encrypt.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. import { createCipheriv, KeyObject } from 'crypto';
  2. import checkIvLength from '../lib/check_iv_length.js';
  3. import checkCekLength from './check_cek_length.js';
  4. import { concat } from '../lib/buffer_utils.js';
  5. import cbcTag from './cbc_tag.js';
  6. import { isCryptoKey } from './webcrypto.js';
  7. import { checkEncCryptoKey } from '../lib/crypto_key.js';
  8. import isKeyObject from './is_key_object.js';
  9. import invalidKeyInput from '../lib/invalid_key_input.js';
  10. import { JOSENotSupported } from '../util/errors.js';
  11. import supported from './ciphers.js';
  12. import { types } from './is_key_like.js';
  13. function cbcEncrypt(enc, plaintext, cek, iv, aad) {
  14. const keySize = parseInt(enc.slice(1, 4), 10);
  15. if (isKeyObject(cek)) {
  16. cek = cek.export();
  17. }
  18. const encKey = cek.subarray(keySize >> 3);
  19. const macKey = cek.subarray(0, keySize >> 3);
  20. const algorithm = `aes-${keySize}-cbc`;
  21. if (!supported(algorithm)) {
  22. throw new JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`);
  23. }
  24. const cipher = createCipheriv(algorithm, encKey, iv);
  25. const ciphertext = concat(cipher.update(plaintext), cipher.final());
  26. const macSize = parseInt(enc.slice(-3), 10);
  27. const tag = cbcTag(aad, iv, ciphertext, macSize, macKey, keySize);
  28. return { ciphertext, tag };
  29. }
  30. function gcmEncrypt(enc, plaintext, cek, iv, aad) {
  31. const keySize = parseInt(enc.slice(1, 4), 10);
  32. const algorithm = `aes-${keySize}-gcm`;
  33. if (!supported(algorithm)) {
  34. throw new JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`);
  35. }
  36. const cipher = createCipheriv(algorithm, cek, iv, { authTagLength: 16 });
  37. if (aad.byteLength) {
  38. cipher.setAAD(aad, { plaintextLength: plaintext.length });
  39. }
  40. const ciphertext = cipher.update(plaintext);
  41. cipher.final();
  42. const tag = cipher.getAuthTag();
  43. return { ciphertext, tag };
  44. }
  45. const encrypt = (enc, plaintext, cek, iv, aad) => {
  46. let key;
  47. if (isCryptoKey(cek)) {
  48. checkEncCryptoKey(cek, enc, 'encrypt');
  49. key = KeyObject.from(cek);
  50. }
  51. else if (cek instanceof Uint8Array || isKeyObject(cek)) {
  52. key = cek;
  53. }
  54. else {
  55. throw new TypeError(invalidKeyInput(cek, ...types, 'Uint8Array'));
  56. }
  57. checkCekLength(enc, key);
  58. checkIvLength(enc, iv);
  59. switch (enc) {
  60. case 'A128CBC-HS256':
  61. case 'A192CBC-HS384':
  62. case 'A256CBC-HS512':
  63. return cbcEncrypt(enc, plaintext, key, iv, aad);
  64. case 'A128GCM':
  65. case 'A192GCM':
  66. case 'A256GCM':
  67. return gcmEncrypt(enc, plaintext, key, iv, aad);
  68. default:
  69. throw new JOSENotSupported('Unsupported JWE Content Encryption Algorithm');
  70. }
  71. };
  72. export default encrypt;