encrypt.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import { FlattenedEncrypt, unprotected } from '../flattened/encrypt.js';
  2. import { JWEInvalid } from '../../util/errors.js';
  3. import generateCek from '../../lib/cek.js';
  4. import isDisjoint from '../../lib/is_disjoint.js';
  5. import encryptKeyManagement from '../../lib/encrypt_key_management.js';
  6. import { encode as base64url } from '../../runtime/base64url.js';
  7. import validateCrit from '../../lib/validate_crit.js';
  8. class IndividualRecipient {
  9. constructor(enc, key, options) {
  10. this.parent = enc;
  11. this.key = key;
  12. this.options = options;
  13. }
  14. setUnprotectedHeader(unprotectedHeader) {
  15. if (this.unprotectedHeader) {
  16. throw new TypeError('setUnprotectedHeader can only be called once');
  17. }
  18. this.unprotectedHeader = unprotectedHeader;
  19. return this;
  20. }
  21. addRecipient(...args) {
  22. return this.parent.addRecipient(...args);
  23. }
  24. encrypt(...args) {
  25. return this.parent.encrypt(...args);
  26. }
  27. done() {
  28. return this.parent;
  29. }
  30. }
  31. export class GeneralEncrypt {
  32. constructor(plaintext) {
  33. this._recipients = [];
  34. this._plaintext = plaintext;
  35. }
  36. addRecipient(key, options) {
  37. const recipient = new IndividualRecipient(this, key, { crit: options === null || options === void 0 ? void 0 : options.crit });
  38. this._recipients.push(recipient);
  39. return recipient;
  40. }
  41. setProtectedHeader(protectedHeader) {
  42. if (this._protectedHeader) {
  43. throw new TypeError('setProtectedHeader can only be called once');
  44. }
  45. this._protectedHeader = protectedHeader;
  46. return this;
  47. }
  48. setSharedUnprotectedHeader(sharedUnprotectedHeader) {
  49. if (this._unprotectedHeader) {
  50. throw new TypeError('setSharedUnprotectedHeader can only be called once');
  51. }
  52. this._unprotectedHeader = sharedUnprotectedHeader;
  53. return this;
  54. }
  55. setAdditionalAuthenticatedData(aad) {
  56. this._aad = aad;
  57. return this;
  58. }
  59. async encrypt(options) {
  60. var _a, _b, _c;
  61. if (!this._recipients.length) {
  62. throw new JWEInvalid('at least one recipient must be added');
  63. }
  64. options = { deflateRaw: options === null || options === void 0 ? void 0 : options.deflateRaw };
  65. if (this._recipients.length === 1) {
  66. const [recipient] = this._recipients;
  67. const flattened = await new FlattenedEncrypt(this._plaintext)
  68. .setAdditionalAuthenticatedData(this._aad)
  69. .setProtectedHeader(this._protectedHeader)
  70. .setSharedUnprotectedHeader(this._unprotectedHeader)
  71. .setUnprotectedHeader(recipient.unprotectedHeader)
  72. .encrypt(recipient.key, { ...recipient.options, ...options });
  73. let jwe = {
  74. ciphertext: flattened.ciphertext,
  75. iv: flattened.iv,
  76. recipients: [{}],
  77. tag: flattened.tag,
  78. };
  79. if (flattened.aad)
  80. jwe.aad = flattened.aad;
  81. if (flattened.protected)
  82. jwe.protected = flattened.protected;
  83. if (flattened.unprotected)
  84. jwe.unprotected = flattened.unprotected;
  85. if (flattened.encrypted_key)
  86. jwe.recipients[0].encrypted_key = flattened.encrypted_key;
  87. if (flattened.header)
  88. jwe.recipients[0].header = flattened.header;
  89. return jwe;
  90. }
  91. let enc;
  92. for (let i = 0; i < this._recipients.length; i++) {
  93. const recipient = this._recipients[i];
  94. if (!isDisjoint(this._protectedHeader, this._unprotectedHeader, recipient.unprotectedHeader)) {
  95. throw new JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint');
  96. }
  97. const joseHeader = {
  98. ...this._protectedHeader,
  99. ...this._unprotectedHeader,
  100. ...recipient.unprotectedHeader,
  101. };
  102. const { alg } = joseHeader;
  103. if (typeof alg !== 'string' || !alg) {
  104. throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid');
  105. }
  106. if (alg === 'dir' || alg === 'ECDH-ES') {
  107. throw new JWEInvalid('"dir" and "ECDH-ES" alg may only be used with a single recipient');
  108. }
  109. if (typeof joseHeader.enc !== 'string' || !joseHeader.enc) {
  110. throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid');
  111. }
  112. if (!enc) {
  113. enc = joseHeader.enc;
  114. }
  115. else if (enc !== joseHeader.enc) {
  116. throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter must be the same for all recipients');
  117. }
  118. validateCrit(JWEInvalid, new Map(), recipient.options.crit, this._protectedHeader, joseHeader);
  119. if (joseHeader.zip !== undefined) {
  120. if (!this._protectedHeader || !this._protectedHeader.zip) {
  121. throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected');
  122. }
  123. }
  124. }
  125. const cek = generateCek(enc);
  126. let jwe = {
  127. ciphertext: '',
  128. iv: '',
  129. recipients: [],
  130. tag: '',
  131. };
  132. for (let i = 0; i < this._recipients.length; i++) {
  133. const recipient = this._recipients[i];
  134. const target = {};
  135. jwe.recipients.push(target);
  136. const joseHeader = {
  137. ...this._protectedHeader,
  138. ...this._unprotectedHeader,
  139. ...recipient.unprotectedHeader,
  140. };
  141. const p2c = joseHeader.alg.startsWith('PBES2') ? 2048 + i : undefined;
  142. if (i === 0) {
  143. const flattened = await new FlattenedEncrypt(this._plaintext)
  144. .setAdditionalAuthenticatedData(this._aad)
  145. .setContentEncryptionKey(cek)
  146. .setProtectedHeader(this._protectedHeader)
  147. .setSharedUnprotectedHeader(this._unprotectedHeader)
  148. .setUnprotectedHeader(recipient.unprotectedHeader)
  149. .setKeyManagementParameters({ p2c })
  150. .encrypt(recipient.key, {
  151. ...recipient.options,
  152. ...options,
  153. [unprotected]: true,
  154. });
  155. jwe.ciphertext = flattened.ciphertext;
  156. jwe.iv = flattened.iv;
  157. jwe.tag = flattened.tag;
  158. if (flattened.aad)
  159. jwe.aad = flattened.aad;
  160. if (flattened.protected)
  161. jwe.protected = flattened.protected;
  162. if (flattened.unprotected)
  163. jwe.unprotected = flattened.unprotected;
  164. target.encrypted_key = flattened.encrypted_key;
  165. if (flattened.header)
  166. target.header = flattened.header;
  167. continue;
  168. }
  169. const { encryptedKey, parameters } = await encryptKeyManagement(((_a = recipient.unprotectedHeader) === null || _a === void 0 ? void 0 : _a.alg) ||
  170. ((_b = this._protectedHeader) === null || _b === void 0 ? void 0 : _b.alg) ||
  171. ((_c = this._unprotectedHeader) === null || _c === void 0 ? void 0 : _c.alg), enc, recipient.key, cek, { p2c });
  172. target.encrypted_key = base64url(encryptedKey);
  173. if (recipient.unprotectedHeader || parameters)
  174. target.header = { ...recipient.unprotectedHeader, ...parameters };
  175. }
  176. return jwe;
  177. }
  178. }