verify-stream.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. /*global module*/
  2. var Buffer = require('safe-buffer').Buffer;
  3. var DataStream = require('./data-stream');
  4. var jwa = require('jwa');
  5. var Stream = require('stream');
  6. var toString = require('./tostring');
  7. var util = require('util');
  8. var JWS_REGEX = /^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/;
  9. function isObject(thing) {
  10. return Object.prototype.toString.call(thing) === '[object Object]';
  11. }
  12. function safeJsonParse(thing) {
  13. if (isObject(thing))
  14. return thing;
  15. try { return JSON.parse(thing); }
  16. catch (e) { return undefined; }
  17. }
  18. function headerFromJWS(jwsSig) {
  19. var encodedHeader = jwsSig.split('.', 1)[0];
  20. return safeJsonParse(Buffer.from(encodedHeader, 'base64').toString('binary'));
  21. }
  22. function securedInputFromJWS(jwsSig) {
  23. return jwsSig.split('.', 2).join('.');
  24. }
  25. function signatureFromJWS(jwsSig) {
  26. return jwsSig.split('.')[2];
  27. }
  28. function payloadFromJWS(jwsSig, encoding) {
  29. encoding = encoding || 'utf8';
  30. var payload = jwsSig.split('.')[1];
  31. return Buffer.from(payload, 'base64').toString(encoding);
  32. }
  33. function isValidJws(string) {
  34. return JWS_REGEX.test(string) && !!headerFromJWS(string);
  35. }
  36. function jwsVerify(jwsSig, algorithm, secretOrKey) {
  37. if (!algorithm) {
  38. var err = new Error("Missing algorithm parameter for jws.verify");
  39. err.code = "MISSING_ALGORITHM";
  40. throw err;
  41. }
  42. jwsSig = toString(jwsSig);
  43. var signature = signatureFromJWS(jwsSig);
  44. var securedInput = securedInputFromJWS(jwsSig);
  45. var algo = jwa(algorithm);
  46. return algo.verify(securedInput, signature, secretOrKey);
  47. }
  48. function jwsDecode(jwsSig, opts) {
  49. opts = opts || {};
  50. jwsSig = toString(jwsSig);
  51. if (!isValidJws(jwsSig))
  52. return null;
  53. var header = headerFromJWS(jwsSig);
  54. if (!header)
  55. return null;
  56. var payload = payloadFromJWS(jwsSig);
  57. if (header.typ === 'JWT' || opts.json)
  58. payload = JSON.parse(payload, opts.encoding);
  59. return {
  60. header: header,
  61. payload: payload,
  62. signature: signatureFromJWS(jwsSig)
  63. };
  64. }
  65. function VerifyStream(opts) {
  66. opts = opts || {};
  67. var secretOrKey = opts.secret||opts.publicKey||opts.key;
  68. var secretStream = new DataStream(secretOrKey);
  69. this.readable = true;
  70. this.algorithm = opts.algorithm;
  71. this.encoding = opts.encoding;
  72. this.secret = this.publicKey = this.key = secretStream;
  73. this.signature = new DataStream(opts.signature);
  74. this.secret.once('close', function () {
  75. if (!this.signature.writable && this.readable)
  76. this.verify();
  77. }.bind(this));
  78. this.signature.once('close', function () {
  79. if (!this.secret.writable && this.readable)
  80. this.verify();
  81. }.bind(this));
  82. }
  83. util.inherits(VerifyStream, Stream);
  84. VerifyStream.prototype.verify = function verify() {
  85. try {
  86. var valid = jwsVerify(this.signature.buffer, this.algorithm, this.key.buffer);
  87. var obj = jwsDecode(this.signature.buffer, this.encoding);
  88. this.emit('done', valid, obj);
  89. this.emit('data', valid);
  90. this.emit('end');
  91. this.readable = false;
  92. return valid;
  93. } catch (e) {
  94. this.readable = false;
  95. this.emit('error', e);
  96. this.emit('close');
  97. }
  98. };
  99. VerifyStream.decode = jwsDecode;
  100. VerifyStream.isValid = isValidJws;
  101. VerifyStream.verify = jwsVerify;
  102. module.exports = VerifyStream;