key_management.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import * as aeskw from './aeskw.js';
  2. import * as ecdhes from './ecdhes.js';
  3. import * as pbes2kw from './pbes2kw.js';
  4. import * as rsaes from './rsaes.js';
  5. import { encode as b64u } from '../util/base64url.js';
  6. import { normalizeKey } from './normalize_key.js';
  7. import { JOSENotSupported, JWEInvalid } from '../util/errors.js';
  8. import { decodeBase64url } from './helpers.js';
  9. import { generateCek, cekLength } from './content_encryption.js';
  10. import { importJWK } from '../key/import.js';
  11. import { exportJWK } from '../key/export.js';
  12. import { isObject } from './type_checks.js';
  13. import { wrap as aesGcmKwWrap, unwrap as aesGcmKwUnwrap } from './aesgcmkw.js';
  14. import { assertCryptoKey } from './is_key_like.js';
  15. const unsupportedAlgHeader = 'Invalid or unsupported "alg" (JWE Algorithm) header value';
  16. function assertEncryptedKey(encryptedKey) {
  17. if (encryptedKey === undefined)
  18. throw new JWEInvalid('JWE Encrypted Key missing');
  19. }
  20. export async function decryptKeyManagement(alg, key, encryptedKey, joseHeader, options) {
  21. switch (alg) {
  22. case 'dir': {
  23. if (encryptedKey !== undefined)
  24. throw new JWEInvalid('Encountered unexpected JWE Encrypted Key');
  25. return key;
  26. }
  27. case 'ECDH-ES':
  28. if (encryptedKey !== undefined)
  29. throw new JWEInvalid('Encountered unexpected JWE Encrypted Key');
  30. case 'ECDH-ES+A128KW':
  31. case 'ECDH-ES+A192KW':
  32. case 'ECDH-ES+A256KW': {
  33. if (!isObject(joseHeader.epk))
  34. throw new JWEInvalid(`JOSE Header "epk" (Ephemeral Public Key) missing or invalid`);
  35. assertCryptoKey(key);
  36. if (!ecdhes.allowed(key))
  37. throw new JOSENotSupported('ECDH with the provided key is not allowed or not supported by your javascript runtime');
  38. const epk = await importJWK(joseHeader.epk, alg);
  39. assertCryptoKey(epk);
  40. let partyUInfo;
  41. let partyVInfo;
  42. if (joseHeader.apu !== undefined) {
  43. if (typeof joseHeader.apu !== 'string')
  44. throw new JWEInvalid(`JOSE Header "apu" (Agreement PartyUInfo) invalid`);
  45. partyUInfo = decodeBase64url(joseHeader.apu, 'apu', JWEInvalid);
  46. }
  47. if (joseHeader.apv !== undefined) {
  48. if (typeof joseHeader.apv !== 'string')
  49. throw new JWEInvalid(`JOSE Header "apv" (Agreement PartyVInfo) invalid`);
  50. partyVInfo = decodeBase64url(joseHeader.apv, 'apv', JWEInvalid);
  51. }
  52. const sharedSecret = await ecdhes.deriveKey(epk, key, alg === 'ECDH-ES' ? joseHeader.enc : alg, alg === 'ECDH-ES' ? cekLength(joseHeader.enc) : parseInt(alg.slice(-5, -2), 10), partyUInfo, partyVInfo);
  53. if (alg === 'ECDH-ES')
  54. return sharedSecret;
  55. assertEncryptedKey(encryptedKey);
  56. return aeskw.unwrap(alg.slice(-6), sharedSecret, encryptedKey);
  57. }
  58. case 'RSA-OAEP':
  59. case 'RSA-OAEP-256':
  60. case 'RSA-OAEP-384':
  61. case 'RSA-OAEP-512': {
  62. assertEncryptedKey(encryptedKey);
  63. assertCryptoKey(key);
  64. return rsaes.decrypt(alg, key, encryptedKey);
  65. }
  66. case 'PBES2-HS256+A128KW':
  67. case 'PBES2-HS384+A192KW':
  68. case 'PBES2-HS512+A256KW': {
  69. assertEncryptedKey(encryptedKey);
  70. if (typeof joseHeader.p2c !== 'number')
  71. throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) missing or invalid`);
  72. const p2cLimit = options?.maxPBES2Count || 10_000;
  73. if (joseHeader.p2c > p2cLimit)
  74. throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds`);
  75. if (typeof joseHeader.p2s !== 'string')
  76. throw new JWEInvalid(`JOSE Header "p2s" (PBES2 Salt) missing or invalid`);
  77. let p2s;
  78. p2s = decodeBase64url(joseHeader.p2s, 'p2s', JWEInvalid);
  79. return pbes2kw.unwrap(alg, key, encryptedKey, joseHeader.p2c, p2s);
  80. }
  81. case 'A128KW':
  82. case 'A192KW':
  83. case 'A256KW': {
  84. assertEncryptedKey(encryptedKey);
  85. return aeskw.unwrap(alg, key, encryptedKey);
  86. }
  87. case 'A128GCMKW':
  88. case 'A192GCMKW':
  89. case 'A256GCMKW': {
  90. assertEncryptedKey(encryptedKey);
  91. if (typeof joseHeader.iv !== 'string')
  92. throw new JWEInvalid(`JOSE Header "iv" (Initialization Vector) missing or invalid`);
  93. if (typeof joseHeader.tag !== 'string')
  94. throw new JWEInvalid(`JOSE Header "tag" (Authentication Tag) missing or invalid`);
  95. let iv;
  96. iv = decodeBase64url(joseHeader.iv, 'iv', JWEInvalid);
  97. let tag;
  98. tag = decodeBase64url(joseHeader.tag, 'tag', JWEInvalid);
  99. return aesGcmKwUnwrap(alg, key, encryptedKey, iv, tag);
  100. }
  101. default: {
  102. throw new JOSENotSupported(unsupportedAlgHeader);
  103. }
  104. }
  105. }
  106. export async function encryptKeyManagement(alg, enc, key, providedCek, providedParameters = {}) {
  107. let encryptedKey;
  108. let parameters;
  109. let cek;
  110. switch (alg) {
  111. case 'dir': {
  112. cek = key;
  113. break;
  114. }
  115. case 'ECDH-ES':
  116. case 'ECDH-ES+A128KW':
  117. case 'ECDH-ES+A192KW':
  118. case 'ECDH-ES+A256KW': {
  119. assertCryptoKey(key);
  120. if (!ecdhes.allowed(key)) {
  121. throw new JOSENotSupported('ECDH with the provided key is not allowed or not supported by your javascript runtime');
  122. }
  123. const { apu, apv } = providedParameters;
  124. let ephemeralKey;
  125. if (providedParameters.epk) {
  126. ephemeralKey = (await normalizeKey(providedParameters.epk, alg));
  127. }
  128. else {
  129. ephemeralKey = (await crypto.subtle.generateKey(key.algorithm, true, ['deriveBits'])).privateKey;
  130. }
  131. const { x, y, crv, kty } = await exportJWK(ephemeralKey);
  132. const sharedSecret = await ecdhes.deriveKey(key, ephemeralKey, alg === 'ECDH-ES' ? enc : alg, alg === 'ECDH-ES' ? cekLength(enc) : parseInt(alg.slice(-5, -2), 10), apu, apv);
  133. parameters = { epk: { x, crv, kty } };
  134. if (kty === 'EC')
  135. parameters.epk.y = y;
  136. if (apu)
  137. parameters.apu = b64u(apu);
  138. if (apv)
  139. parameters.apv = b64u(apv);
  140. if (alg === 'ECDH-ES') {
  141. cek = sharedSecret;
  142. break;
  143. }
  144. cek = providedCek || generateCek(enc);
  145. const kwAlg = alg.slice(-6);
  146. encryptedKey = await aeskw.wrap(kwAlg, sharedSecret, cek);
  147. break;
  148. }
  149. case 'RSA-OAEP':
  150. case 'RSA-OAEP-256':
  151. case 'RSA-OAEP-384':
  152. case 'RSA-OAEP-512': {
  153. cek = providedCek || generateCek(enc);
  154. assertCryptoKey(key);
  155. encryptedKey = await rsaes.encrypt(alg, key, cek);
  156. break;
  157. }
  158. case 'PBES2-HS256+A128KW':
  159. case 'PBES2-HS384+A192KW':
  160. case 'PBES2-HS512+A256KW': {
  161. cek = providedCek || generateCek(enc);
  162. const { p2c, p2s } = providedParameters;
  163. ({ encryptedKey, ...parameters } = await pbes2kw.wrap(alg, key, cek, p2c, p2s));
  164. break;
  165. }
  166. case 'A128KW':
  167. case 'A192KW':
  168. case 'A256KW': {
  169. cek = providedCek || generateCek(enc);
  170. encryptedKey = await aeskw.wrap(alg, key, cek);
  171. break;
  172. }
  173. case 'A128GCMKW':
  174. case 'A192GCMKW':
  175. case 'A256GCMKW': {
  176. cek = providedCek || generateCek(enc);
  177. const { iv } = providedParameters;
  178. ({ encryptedKey, ...parameters } = await aesGcmKwWrap(alg, key, cek, iv));
  179. break;
  180. }
  181. default: {
  182. throw new JOSENotSupported(unsupportedAlgHeader);
  183. }
  184. }
  185. return { cek, encryptedKey, parameters };
  186. }