Code Diff
diff --git a/lib/internal/crypto/aes.js b/lib/internal/crypto/aes.js
index 0474060d394c99..c0765f75642189 100644
--- a/lib/internal/crypto/aes.js
+++ b/lib/internal/crypto/aes.js
@@ -1,12 +1,9 @@
'use strict';
const {
- ArrayBufferIsView,
- ArrayBufferPrototypeSlice,
ArrayFrom,
ArrayPrototypePush,
SafeSet,
- TypedArrayPrototypeSlice,
} = primordials;
const {
@@ -28,8 +25,6 @@ const {
kKeyVariantAES_GCM_256,
kKeyVariantAES_KW_256,
kKeyVariantAES_OCB_256,
- kWebCryptoCipherDecrypt,
- kWebCryptoCipherEncrypt,
} = internalBinding('crypto');
const {
@@ -143,80 +138,33 @@ function asyncAesKwCipher(mode, key, data) {
getVariant('AES-KW', key[kAlgorithm].length)));
}
-async function asyncAesGcmCipher(mode, key, data, algorithm) {
+function asyncAesGcmCipher(mode, key, data, algorithm) {
const { tagLength = 128 } = algorithm;
-
const tagByteLength = tagLength / 8;
- let tag;
- switch (mode) {
- case kWebCryptoCipherDecrypt: {
- 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:
- tag = tagByteLength;
- break;
- }
-
- return await jobPromise(() => new AESCipherJob(
+ return jobPromise(() => new AESCipherJob(
kCryptoJobAsync,
mode,
key[kKeyObject][kHandle],
data,
getVariant('AES-GCM', key[kAlgorithm].length),
algorithm.iv,
- tag,
+ tagByteLength,
algorithm.additionalData));
}
-async function asyncAesOcbCipher(mode, key, data, algorithm) {
+function asyncAesOcbCipher(mode, key, data, algorithm) {
const { tagLength = 128 } = algorithm;
-
const tagByteLength = tagLength / 8;
- let tag;
- switch (mode) {
- case kWebCryptoCipherDecrypt: {
- const slice = ArrayBufferIsView(data) ?
- TypedArrayPrototypeSlice : ArrayBufferPrototypeSlice;
- tag = slice(data, -tagByteLength);
-
- // Similar to GCM, OCB requires the tag to be present for decryption
- if (tagByteLength > tag.byteLength) {
- throw lazyDOMException(
- 'The provided data is too small.',
- 'OperationError');
- }
- data = slice(data, 0, -tagByteLength);
- break;
- }
- case kWebCryptoCipherEncrypt:
- tag = tagByteLength;
- break;
- }
-
- return await jobPromise(() => new AESCipherJob(
+ return jobPromise(() => new AESCipherJob(
kCryptoJobAsync,
mode,
key[kKeyObject][kHandle],
data,
getVariant('AES-OCB', key.algorithm.length),
algorithm.iv,
- tag,
+ tagByteLength,
algorithm.additionalData));
}
diff --git a/lib/internal/crypto/chacha20_poly1305.js b/lib/internal/crypto/chacha20_poly1305.js
index 0979d7aaddbb61..a2b7c1fb04fb89 100644
--- a/lib/internal/crypto/chacha20_poly1305.js
+++ b/lib/internal/crypto/chacha20_poly1305.js
@@ -1,19 +1,14 @@
'use strict';
const {
- ArrayBufferIsView,
- ArrayBufferPrototypeSlice,
ArrayFrom,
SafeSet,
- TypedArrayPrototypeSlice,
} = primordials;
const {
ChaCha20Poly1305CipherJob,
KeyObjectHandle,
kCryptoJobAsync,
- kWebCryptoCipherDecrypt,
- kWebCryptoCipherEncrypt,
} = internalBinding('crypto');
const {
@@ -46,35 +41,13 @@ function validateKeyLength(length) {
throw lazyDOMException('Invalid key length', 'DataError');
}
-async function c20pCipher(mode, key, data, algorithm) {
- let tag;
- switch (mode) {
- case kWebCryptoCipherDecrypt: {
- const slice = ArrayBufferIsView(data) ?
- TypedArrayPrototypeSlice : ArrayBufferPrototypeSlice;
-
- if (data.byteLength < 16) {
- throw lazyDOMException(
- 'The provided data is too small.',
- 'OperationError');
- }
-
- tag = slice(data, -16);
- data = slice(data, 0, -16);
- break;
- }
- case kWebCryptoCipherEncrypt:
- tag = 16;
- break;
- }
-
- return await jobPromise(() => new ChaCha20Poly1305CipherJob(
+function c20pCipher(mode, key, data, algorithm) {
+ return jobPromise(() => new ChaCha20Poly1305CipherJob(
kCryptoJobAsync,
mode,
key[kKeyObject][kHandle],
data,
algorithm.iv,
- tag,
algorithm.additionalData));
}
diff --git a/src/crypto/crypto_aes.cc b/src/crypto/crypto_aes.cc
index b5495e59737eb6..fa619696ffd5b2 100644
--- a/src/crypto/crypto_aes.cc
+++ b/src/crypto/crypto_aes.cc
@@ -76,24 +76,28 @@ WebCryptoCipherStatus AES_Cipher(Environment* env,
}
size_t tag_len = 0;
+ size_t data_len = in.size();
if (params.cipher.isGcmMode() || params.cipher.isOcbMode()) {
+ tag_len = params.length;
switch (cipher_mode) {
case kWebCryptoCipherDecrypt: {
- // If in decrypt mode, the auth tag must be set in the params.tag.
- CHECK(params.tag);
+ // In decrypt mode, the auth tag is appended to the end of the
+ // ciphertext. Split it off and set it on the cipher context.
+ if (data_len < tag_len) {
+ return WebCryptoCipherStatus::FAILED;
+ }
+ data_len -= tag_len;
- // For OCB mode, we need to set the auth tag length before setting the
- // tag
if (params.cipher.isOcbMode()) {
- if (!ctx.setAeadTagLength(params.tag.size())) {
+ if (!ctx.setAeadTagLength(tag_len)) {
return WebCryptoCipherStatus::FAILED;
}
}
ncrypto::Buffer<const char> buffer = {
- .data = params.tag.data<char>(),
- .len = params.tag.size(),
+ .data = in.data<char>() + data_len,
+ .len = tag_len,
};
if (!ctx.setAeadTag(buffer)) {
return WebCryptoCipherStatus::FAILED;
@@ -101,14 +105,6 @@ WebCryptoCipherStatus AES_Cipher(Environment* env,
break;
}
case kWebCryptoCipherEncrypt: {
- // In encrypt mode, we grab the tag length here. We'll use it to
- // ensure that that allocated buffer has enough room for both the
- // final block and the auth tag. Unlike our other AES-GCM implementation
- // in CipherBase, in WebCrypto, the auth tag is concatenated to the end
- // of the generated ciphertext and returned in the same ArrayBuffer.
- tag_len = params.length;
-
- // For OCB mode, we need to set the auth tag length
if (params.cipher.isOcbMode()) {
if (!ctx.setAeadTagLength(tag_len)) {
return WebCryptoCipherStatus::FAILED;
@@ -122,7 +118,7 @@ WebCryptoCipherStatus AES_Cipher(Environment* env,
}
size_t total = 0;
- int buf_len = in.size() + ctx.getBlockSize() + tag_len;
+ int buf_len = data_len + ctx.getBlockSize() + (encrypt ? tag_len : 0);
int out_len;
ncrypto::Buffer<const unsigned char> buffer = {
@@ -148,9 +144,9 @@ WebCryptoCipherStatus AES_Cipher(Environment* env,
// Refs: https://github.com/nodejs/node/pull/38913#issuecomment-866505244
buffer = {
.data = in.data<unsigned char>(),
- .len = in.size(),
+ .len = data_len,
};
- if (in.empty()) {
+ if (data_len == 0) {
out_len = 0;
} else if (!ctx.update(buffer, ptr, &out_len)) {
return WebCryptoCipherStatus::FAILED;
@@ -381,42 +377,17 @@ bool ValidateCounter(
return true;
}
-bool ValidateAuthTag(
- Environment* env,
- CryptoJobMode mode,
- WebCryptoCipherMode cipher_mode,
- Local<Value> value,
- AESCipherConfig* params) {
- switch (cipher_mode) {
- case kWebCryptoCipherDecrypt: {
- if (!IsAnyBufferSource(value)) {
- THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(env);
- return false;
- }
- ArrayBufferOrViewContents<char> tag_contents(value);
- if (!tag_contents.CheckSizeInt32()) [[unlikely]] {
- THROW_ERR_OUT_OF_RANGE(env, "tagLength is too big");
- return false;
- }
- params->tag = mode == kCryptoJobAsync
- ? tag_contents.ToCopy()
- : tag_contents.ToByteSource();
- break;
- }
- case kWebCryptoCipherEncrypt: {
- if (!value->IsUint32()) {
- THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(env);
- return false;
- }
- params->length = value.As<Uint32>()->Value();
- if (params->length > 128) {
- THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(env);
- return false;
- }
- break;
- }
- default:
- UNREACHABLE();
+bool ValidateAuthTag(Environment* env,
+ Local<Value> value,
+ AESCipherConfig* params) {
+ if (!value->IsUint32()) {
+ THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(env);
+ return false;
+ }
+ params->length = value.As<Uint32>()->Value();
+ if (params->length > 128) {
+ THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(env);
+ return false;
}
return true;
}
@@ -451,8 +422,7 @@ AESCipherConfig::AESCipherConfig(AESCipherConfig&& other) noexcept
cipher(other.cipher),
length(other.length),
iv(std::move(other.iv)),
- additional_data(std::move(other.additional_data)),
- tag(std::move(other.tag)) {}
+ additional_data(std::move(other.additional_data)) {}
AESCipherConfig& AESCipherConfig::operator=(AESCipherConfig&& other) noexcept {
if (&other == this) return *this;
@@ -466,7 +436,6 @@ void AESCipherConfig::MemoryInfo(MemoryTracker* tracker) const {
if (mode == kCryptoJobAsync) {
tracker->TrackFieldWithSize("iv", iv.size());
tracker->TrackFieldWithSize("additional_data", additional_data.size());
- tracker->TrackFieldWithSize("tag", tag.size());
}
}
@@ -510,7 +479,7 @@ Maybe<void> AESCipherTraits::AdditionalConfig(
return Nothing<void>();
}
} else if (params->cipher.isGcmMode() || params->cipher.isOcbMode()) {
- if (!ValidateAuthTag(env, mode, cipher_mode, args[offset + 2], params) ||
+ if (!ValidateAuthTag(env, args[offset + 2], params) ||
!ValidateAdditionalData(env, mode, args[offset + 3], params)) {
return Nothing<void>();
}
diff --git a/src/crypto/crypto_aes.h b/src/crypto/crypto_aes.h
index 401ef70a5eba9f..5627f9020bad54 100644
--- a/src/crypto/crypto_aes.h
+++ b/src/crypto/crypto_aes.h
@@ -52,7 +52,6 @@ struct AESCipherConfig final : public MemoryRetainer {
size_t length;
ByteSource iv; // Used for both iv or counter
ByteSource additional_data;
- ByteSource tag; // Used only for authenticated modes (GCM)
AESCipherConfig() = default;
diff --git a/src/crypto/crypto_chacha20_poly1305.cc b/src/crypto/crypto_chacha20_poly1305.cc
index bfe904c49ad771..0fd3e0517317ca 100644
--- a/src/crypto/crypto_chacha20_poly1305.cc
+++ b/src/crypto/crypto_chacha20_poly1305.cc
@@ -54,63 +54,6 @@ bool ValidateIV(Environment* env,
return true;
}
-bool ValidateAuthTag(Environment* env,
- CryptoJobMode mode,
- WebCryptoCipherMode cipher_mode,
- Local<Value> value,
- ChaCha20Poly1305CipherConfig* params) {
- switch (cipher_mode) {
- case kWebCryptoCipherDecrypt: {
- if (!IsAnyBufferSource(value)) {
- THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(
- env, "Authentication tag must be a buffer");
- return false;
- }
-
- ArrayBufferOrViewContents<unsigned char> tag(value);
- if (!tag.CheckSizeInt32()) [[unlikely]] {
- THROW_ERR_OUT_OF_RANGE(env, "tag is too large");
- return false;
- }
-
- if (tag.size() != kChaCha20Poly1305TagSize) {
- THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(
- env, "Invalid authentication tag length");
- return false;
- }
-
- if (mode == kCryptoJobAsync) {
- params->tag = tag.ToCopy();
- } else {
- params->tag = tag.ToByteSource();
- }
- break;
- }
- case kWebCryptoCipherEncrypt: {
- // For encryption, the value should be the tag length (passed from
- // JavaScript) We expect it to be the tag size constant for
- // ChaCha20-Poly1305
- if (!value->IsUint32()) {
- THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(env, "Tag length must be a number");
- return false;
- }
-
- uint32_t tag_length = value.As<v8::Uint32>()->Value();
- if (tag_length != kChaCha20Poly1305TagSize) {
- THROW_ERR_CRYPTO_INVALID_TAG_LENGTH(
- env, "Invalid tag length for ChaCha20-Poly1305");
- return false;
- }
- // Tag is generated during encryption, not provided
- break;
- }
- default:
- UNREACHABLE();
- }
-
- return true;
-}
-
bool ValidateAdditionalData(Environment* env,
CryptoJobMode mode,
Local<Value> value,
@@ -138,8 +81,7 @@ ChaCha20Poly1305CipherConfig::ChaCha20Poly1305CipherConfig(
: mode(other.mode),
cipher(other.cipher),
iv(std::move(other.iv)),
- additional_data(std::move(other.additional_data)),
- tag(std::move(other.tag)) {}
+ additional_data(std::move(other.additional_data)) {}
ChaCha20Poly1305CipherConfig& ChaCha20Poly1305CipherConfig::operator=(
ChaCha20Poly1305CipherConfig&& other) noexcept {
@@ -154,7 +96,6 @@ void ChaCha20Poly1305CipherConfig::MemoryInfo(MemoryTracker* tracker) const {
if (mode == kCryptoJobAsync) {
tracker->TrackFieldWithSize("iv", iv.size());
tracker->TrackFieldWithSize("additional_data", additional_data.size());
- tracker->TrackFieldWithSize("tag", tag.size());
}
}
@@ -179,17 +120,9 @@ Maybe<void> ChaCha20Poly1305CipherTraits::AdditionalConfig(
return Nothing<void>();
}
- // Authentication tag parameter (only for decryption) or tag length (for
- // encryption)
- if (static_cast<unsigned int>(args.Length()) > offset + 1) {
- if (!ValidateAuthTag(env, mode, cipher_mode, args[offset + 1], params)) {
- return Nothing<void>();
- }
- }
-
// Additional authenticated data parameter (optional)
- if (static_cast<unsigned int>(args.Length()) > offset + 2) {
- if (!ValidateAdditionalData(env, mode, args[offset + 2], params)) {
+ if (static_cast<unsigned int>(args.Length()) > offset + 1) {
+ if (!ValidateAdditionalData(env, mode, args[offset + 1], params)) {
return Nothing<void>();
}
}
@@ -229,23 +162,25 @@ WebCryptoCipherStatus ChaCha20Poly1305CipherTraits::DoCipher(
return WebCryptoCipherStatus::FAILED;
}
- size_t tag_len = 0;
+ size_t tag_len = kChaCha20Poly1305TagSize;
+ size_t data_len = in.size();
switch (cipher_mode) {
case kWebCryptoCipherDecrypt: {
- if (params.tag.size() != kChaCha20Poly1305TagSize) {
+ if (data_len < tag_len) {
... [truncated]