From 0ce31fb8f3ef0cf8948db04e74235c395c38701d Mon Sep 17 00:00:00 2001 From: XadillaX Date: Thu, 3 Jun 2021 15:40:50 +0800 Subject: [PATCH 1/4] crypto: fix aes crash when tag length too small Refs: https://github.com/nodejs/node/issues/38883 --- lib/internal/crypto/aes.js | 5 +++ ...pto-webcrypto-aes-decrypt-tag-too-small.js | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 test/parallel/test-crypto-webcrypto-aes-decrypt-tag-too-small.js diff --git a/lib/internal/crypto/aes.js b/lib/internal/crypto/aes.js index a35e3469a514bc..6dc79ff6d5b418 100644 --- a/lib/internal/crypto/aes.js +++ b/lib/internal/crypto/aes.js @@ -188,6 +188,11 @@ function asyncAesGcmCipher( const slice = ArrayBufferIsView(data) ? TypedArrayPrototypeSlice : ArrayBufferPrototypeSlice; tag = slice(data, -tagByteLength); + if (tagByteLength > tag.byteLength) { + throw lazyDOMException( + 'The provided data is too small.', + 'OperationError'); + } data = slice(data, 0, -tagByteLength); break; case kWebCryptoCipherEncrypt: diff --git a/test/parallel/test-crypto-webcrypto-aes-decrypt-tag-too-small.js b/test/parallel/test-crypto-webcrypto-aes-decrypt-tag-too-small.js new file mode 100644 index 00000000000000..2bcf0f13b2b20e --- /dev/null +++ b/test/parallel/test-crypto-webcrypto-aes-decrypt-tag-too-small.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto').webcrypto; + +crypto.subtle.importKey( + 'raw', + new Uint8Array(32), + { + name: 'AES-GCM' + }, + false, + [ 'encrypt', 'decrypt' ]) + .then((k) => { + assert.rejects(async () => { + return crypto.subtle.decrypt({ + name: 'AES-GCM', + iv: new Uint8Array(12), + }, + k, + new Uint8Array(0)); + }, { + name: 'OperationError', + message: /The provided data is too small/, + }); + }); From 9a4d39ca9f62571f955fffd9faf3b019021a9f9c Mon Sep 17 00:00:00 2001 From: XadillaX Date: Thu, 3 Jun 2021 15:56:34 +0800 Subject: [PATCH 2/4] f --- lib/internal/crypto/aes.js | 6 ++++++ .../test-crypto-webcrypto-aes-decrypt-tag-too-small.js | 4 +--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/internal/crypto/aes.js b/lib/internal/crypto/aes.js index 6dc79ff6d5b418..3d14268c07b628 100644 --- a/lib/internal/crypto/aes.js +++ b/lib/internal/crypto/aes.js @@ -188,11 +188,17 @@ function asyncAesGcmCipher( const slice = ArrayBufferIsView(data) ? TypedArrayPrototypeSlice : ArrayBufferPrototypeSlice; tag = slice(data, -tagByteLength); + + // Refs: https://www.w3.org/TR/WebCryptoAPI/#aes-gcm-operations + // + // > If *plaintext* has a length less than *tagLength* bits, then `throw` + // > an `OperationError`. if (tagByteLength > tag.byteLength) { throw lazyDOMException( 'The provided data is too small.', 'OperationError'); } + data = slice(data, 0, -tagByteLength); break; case kWebCryptoCipherEncrypt: diff --git a/test/parallel/test-crypto-webcrypto-aes-decrypt-tag-too-small.js b/test/parallel/test-crypto-webcrypto-aes-decrypt-tag-too-small.js index 2bcf0f13b2b20e..86683904acc112 100644 --- a/test/parallel/test-crypto-webcrypto-aes-decrypt-tag-too-small.js +++ b/test/parallel/test-crypto-webcrypto-aes-decrypt-tag-too-small.js @@ -21,9 +21,7 @@ crypto.subtle.importKey( return crypto.subtle.decrypt({ name: 'AES-GCM', iv: new Uint8Array(12), - }, - k, - new Uint8Array(0)); + }, k, new Uint8Array(0)); }, { name: 'OperationError', message: /The provided data is too small/, From 98a407b86321b725964264dd1d135a4beecbd60a Mon Sep 17 00:00:00 2001 From: XadillaX Date: Thu, 10 Jun 2021 14:52:16 +0800 Subject: [PATCH 3/4] f --- lib/internal/crypto/aes.js | 8 ++++---- .../test-crypto-webcrypto-aes-decrypt-tag-too-small.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/internal/crypto/aes.js b/lib/internal/crypto/aes.js index 3d14268c07b628..2c4247ef7ca8f2 100644 --- a/lib/internal/crypto/aes.js +++ b/lib/internal/crypto/aes.js @@ -167,9 +167,9 @@ function asyncAesGcmCipher( data, { iv, additionalData, tagLength = 128 }) { if (!ArrayPrototypeIncludes(kTagLengths, tagLength)) { - throw lazyDOMException( + return Promise.reject(lazyDOMException( `${tagLength} is not a valid AES-GCM tag length`, - 'OperationError'); + 'OperationError')); } iv = getArrayBufferOrView(iv, 'algorithm.iv'); @@ -194,9 +194,9 @@ function asyncAesGcmCipher( // > If *plaintext* has a length less than *tagLength* bits, then `throw` // > an `OperationError`. if (tagByteLength > tag.byteLength) { - throw lazyDOMException( + return Promise.reject(lazyDOMException( 'The provided data is too small.', - 'OperationError'); + 'OperationError')); } data = slice(data, 0, -tagByteLength); diff --git a/test/parallel/test-crypto-webcrypto-aes-decrypt-tag-too-small.js b/test/parallel/test-crypto-webcrypto-aes-decrypt-tag-too-small.js index 86683904acc112..b9f9c74b2623ed 100644 --- a/test/parallel/test-crypto-webcrypto-aes-decrypt-tag-too-small.js +++ b/test/parallel/test-crypto-webcrypto-aes-decrypt-tag-too-small.js @@ -17,7 +17,7 @@ crypto.subtle.importKey( false, [ 'encrypt', 'decrypt' ]) .then((k) => { - assert.rejects(async () => { + assert.rejects(() => { return crypto.subtle.decrypt({ name: 'AES-GCM', iv: new Uint8Array(12), From e5c781a283369065fe2bb3f0079ef2e22f74e58a Mon Sep 17 00:00:00 2001 From: XadillaX Date: Thu, 10 Jun 2021 15:07:36 +0800 Subject: [PATCH 4/4] f --- lib/internal/crypto/aes.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/internal/crypto/aes.js b/lib/internal/crypto/aes.js index 2c4247ef7ca8f2..dd6aa49ff454ab 100644 --- a/lib/internal/crypto/aes.js +++ b/lib/internal/crypto/aes.js @@ -45,6 +45,8 @@ const { kKeyObject, } = require('internal/crypto/util'); +const { PromiseReject } = primordials; + const { codes: { ERR_INVALID_ARG_TYPE, @@ -167,7 +169,7 @@ function asyncAesGcmCipher( data, { iv, additionalData, tagLength = 128 }) { if (!ArrayPrototypeIncludes(kTagLengths, tagLength)) { - return Promise.reject(lazyDOMException( + return PromiseReject(lazyDOMException( `${tagLength} is not a valid AES-GCM tag length`, 'OperationError')); } @@ -194,7 +196,7 @@ function asyncAesGcmCipher( // > If *plaintext* has a length less than *tagLength* bits, then `throw` // > an `OperationError`. if (tagByteLength > tag.byteLength) { - return Promise.reject(lazyDOMException( + return PromiseReject(lazyDOMException( 'The provided data is too small.', 'OperationError')); }