decrypt.js 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import { createDecipheriv, 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 { JOSENotSupported, JWEDecryptionFailed } from '../util/errors.js';
  6. import timingSafeEqual from './timing_safe_equal.js';
  7. import cbcTag from './cbc_tag.js';
  8. import { isCryptoKey } from './webcrypto.js';
  9. import { checkEncCryptoKey } from '../lib/crypto_key.js';
  10. import isKeyObject from './is_key_object.js';
  11. import invalidKeyInput from '../lib/invalid_key_input.js';
  12. import supported from './ciphers.js';
  13. import { types } from './is_key_like.js';
  14. function cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) {
  15. const keySize = parseInt(enc.slice(1, 4), 10);
  16. if (isKeyObject(cek)) {
  17. cek = cek.export();
  18. }
  19. const encKey = cek.subarray(keySize >> 3);
  20. const macKey = cek.subarray(0, keySize >> 3);
  21. const macSize = parseInt(enc.slice(-3), 10);
  22. const algorithm = `aes-${keySize}-cbc`;
  23. if (!supported(algorithm)) {
  24. throw new JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`);
  25. }
  26. const expectedTag = cbcTag(aad, iv, ciphertext, macSize, macKey, keySize);
  27. let macCheckPassed;
  28. try {
  29. macCheckPassed = timingSafeEqual(tag, expectedTag);
  30. }
  31. catch {
  32. }
  33. if (!macCheckPassed) {
  34. throw new JWEDecryptionFailed();
  35. }
  36. let plaintext;
  37. try {
  38. const decipher = createDecipheriv(algorithm, encKey, iv);
  39. plaintext = concat(decipher.update(ciphertext), decipher.final());
  40. }
  41. catch {
  42. }
  43. if (!plaintext) {
  44. throw new JWEDecryptionFailed();
  45. }
  46. return plaintext;
  47. }
  48. function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) {
  49. const keySize = parseInt(enc.slice(1, 4), 10);
  50. const algorithm = `aes-${keySize}-gcm`;
  51. if (!supported(algorithm)) {
  52. throw new JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`);
  53. }
  54. try {
  55. const decipher = createDecipheriv(algorithm, cek, iv, { authTagLength: 16 });
  56. decipher.setAuthTag(tag);
  57. if (aad.byteLength) {
  58. decipher.setAAD(aad, { plaintextLength: ciphertext.length });
  59. }
  60. const plaintext = decipher.update(ciphertext);
  61. decipher.final();
  62. return plaintext;
  63. }
  64. catch {
  65. throw new JWEDecryptionFailed();
  66. }
  67. }
  68. const decrypt = (enc, cek, ciphertext, iv, tag, aad) => {
  69. let key;
  70. if (isCryptoKey(cek)) {
  71. checkEncCryptoKey(cek, enc, 'decrypt');
  72. key = KeyObject.from(cek);
  73. }
  74. else if (cek instanceof Uint8Array || isKeyObject(cek)) {
  75. key = cek;
  76. }
  77. else {
  78. throw new TypeError(invalidKeyInput(cek, ...types, 'Uint8Array'));
  79. }
  80. checkCekLength(enc, key);
  81. checkIvLength(enc, iv);
  82. switch (enc) {
  83. case 'A128CBC-HS256':
  84. case 'A192CBC-HS384':
  85. case 'A256CBC-HS512':
  86. return cbcDecrypt(enc, key, ciphertext, iv, tag, aad);
  87. case 'A128GCM':
  88. case 'A192GCM':
  89. case 'A256GCM':
  90. return gcmDecrypt(enc, key, ciphertext, iv, tag, aad);
  91. default:
  92. throw new JOSENotSupported('Unsupported JWE Content Encryption Algorithm');
  93. }
  94. };
  95. export default decrypt;