From cf9d686c353c5658048e8cfa999363cc848d43a4 Mon Sep 17 00:00:00 2001 From: XadillaX Date: Thu, 3 Jun 2021 15:40:50 +0800 Subject: [PATCH] crypto: fix aes crash when tag length too small MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/nodejs/node/issues/38883 PR-URL: https://github.com/nodejs/node/pull/38914 Reviewed-By: Tobias Nießen Reviewed-By: James M Snell --- lib/internal/crypto/aes.js | 17 +++++++++-- ...pto-webcrypto-aes-decrypt-tag-too-small.js | 29 +++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) 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..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,9 +169,9 @@ function asyncAesGcmCipher( data, { iv, additionalData, tagLength = 128 }) { if (!ArrayPrototypeIncludes(kTagLengths, tagLength)) { - throw lazyDOMException( + return PromiseReject(lazyDOMException( `${tagLength} is not a valid AES-GCM tag length`, - 'OperationError'); + 'OperationError')); } iv = getArrayBufferOrView(iv, 'algorithm.iv'); @@ -188,6 +190,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) { + return PromiseReject(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..b9f9c74b2623ed --- /dev/null +++ b/test/parallel/test-crypto-webcrypto-aes-decrypt-tag-too-small.js @@ -0,0 +1,29 @@ +'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(() => { + return crypto.subtle.decrypt({ + name: 'AES-GCM', + iv: new Uint8Array(12), + }, k, new Uint8Array(0)); + }, { + name: 'OperationError', + message: /The provided data is too small/, + }); + });