sign-stream.js 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  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. function base64url(string, encoding) {
  9. return Buffer
  10. .from(string, encoding)
  11. .toString('base64')
  12. .replace(/=/g, '')
  13. .replace(/\+/g, '-')
  14. .replace(/\//g, '_');
  15. }
  16. function jwsSecuredInput(header, payload, encoding) {
  17. encoding = encoding || 'utf8';
  18. var encodedHeader = base64url(toString(header), 'binary');
  19. var encodedPayload = base64url(toString(payload), encoding);
  20. return util.format('%s.%s', encodedHeader, encodedPayload);
  21. }
  22. function jwsSign(opts) {
  23. var header = opts.header;
  24. var payload = opts.payload;
  25. var secretOrKey = opts.secret || opts.privateKey;
  26. var encoding = opts.encoding;
  27. var algo = jwa(header.alg);
  28. var securedInput = jwsSecuredInput(header, payload, encoding);
  29. var signature = algo.sign(securedInput, secretOrKey);
  30. return util.format('%s.%s', securedInput, signature);
  31. }
  32. function SignStream(opts) {
  33. var secret = opts.secret;
  34. secret = secret == null ? opts.privateKey : secret;
  35. secret = secret == null ? opts.key : secret;
  36. if (/^hs/i.test(opts.header.alg) === true && secret == null) {
  37. throw new TypeError('secret must be a string or buffer or a KeyObject')
  38. }
  39. var secretStream = new DataStream(secret);
  40. this.readable = true;
  41. this.header = opts.header;
  42. this.encoding = opts.encoding;
  43. this.secret = this.privateKey = this.key = secretStream;
  44. this.payload = new DataStream(opts.payload);
  45. this.secret.once('close', function () {
  46. if (!this.payload.writable && this.readable)
  47. this.sign();
  48. }.bind(this));
  49. this.payload.once('close', function () {
  50. if (!this.secret.writable && this.readable)
  51. this.sign();
  52. }.bind(this));
  53. }
  54. util.inherits(SignStream, Stream);
  55. SignStream.prototype.sign = function sign() {
  56. try {
  57. var signature = jwsSign({
  58. header: this.header,
  59. payload: this.payload.buffer,
  60. secret: this.secret.buffer,
  61. encoding: this.encoding
  62. });
  63. this.emit('done', signature);
  64. this.emit('data', signature);
  65. this.emit('end');
  66. this.readable = false;
  67. return signature;
  68. } catch (e) {
  69. this.readable = false;
  70. this.emit('error', e);
  71. this.emit('close');
  72. }
  73. };
  74. SignStream.sign = jwsSign;
  75. module.exports = SignStream;