sign.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import { encode as base64url } from '../../runtime/base64url.js';
  2. import sign from '../../runtime/sign.js';
  3. import isDisjoint from '../../lib/is_disjoint.js';
  4. import { JWSInvalid } from '../../util/errors.js';
  5. import { encoder, decoder, concat } from '../../lib/buffer_utils.js';
  6. import checkKeyType from '../../lib/check_key_type.js';
  7. import validateCrit from '../../lib/validate_crit.js';
  8. export class FlattenedSign {
  9. constructor(payload) {
  10. if (!(payload instanceof Uint8Array)) {
  11. throw new TypeError('payload must be an instance of Uint8Array');
  12. }
  13. this._payload = payload;
  14. }
  15. setProtectedHeader(protectedHeader) {
  16. if (this._protectedHeader) {
  17. throw new TypeError('setProtectedHeader can only be called once');
  18. }
  19. this._protectedHeader = protectedHeader;
  20. return this;
  21. }
  22. setUnprotectedHeader(unprotectedHeader) {
  23. if (this._unprotectedHeader) {
  24. throw new TypeError('setUnprotectedHeader can only be called once');
  25. }
  26. this._unprotectedHeader = unprotectedHeader;
  27. return this;
  28. }
  29. async sign(key, options) {
  30. if (!this._protectedHeader && !this._unprotectedHeader) {
  31. throw new JWSInvalid('either setProtectedHeader or setUnprotectedHeader must be called before #sign()');
  32. }
  33. if (!isDisjoint(this._protectedHeader, this._unprotectedHeader)) {
  34. throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint');
  35. }
  36. const joseHeader = {
  37. ...this._protectedHeader,
  38. ...this._unprotectedHeader,
  39. };
  40. const extensions = validateCrit(JWSInvalid, new Map([['b64', true]]), options === null || options === void 0 ? void 0 : options.crit, this._protectedHeader, joseHeader);
  41. let b64 = true;
  42. if (extensions.has('b64')) {
  43. b64 = this._protectedHeader.b64;
  44. if (typeof b64 !== 'boolean') {
  45. throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
  46. }
  47. }
  48. const { alg } = joseHeader;
  49. if (typeof alg !== 'string' || !alg) {
  50. throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
  51. }
  52. checkKeyType(alg, key, 'sign');
  53. let payload = this._payload;
  54. if (b64) {
  55. payload = encoder.encode(base64url(payload));
  56. }
  57. let protectedHeader;
  58. if (this._protectedHeader) {
  59. protectedHeader = encoder.encode(base64url(JSON.stringify(this._protectedHeader)));
  60. }
  61. else {
  62. protectedHeader = encoder.encode('');
  63. }
  64. const data = concat(protectedHeader, encoder.encode('.'), payload);
  65. const signature = await sign(alg, key, data);
  66. const jws = {
  67. signature: base64url(signature),
  68. payload: '',
  69. };
  70. if (b64) {
  71. jws.payload = decoder.decode(payload);
  72. }
  73. if (this._unprotectedHeader) {
  74. jws.header = this._unprotectedHeader;
  75. }
  76. if (this._protectedHeader) {
  77. jws.protected = decoder.decode(protectedHeader);
  78. }
  79. return jws;
  80. }
  81. }