crypto: throw in setAuthTag on invalid length
The current implementation performs limited checks only and silently ignores superfluous bytes of the authentication tag. This change makes setAuthTag throw when - the user-specified authTagLength does not match the actual tag length, especially when the authentication tag is longer than 16 bytes, and when - the mode is GCM, no authTagLength option has been specified and the tag length is not a valid GCM tag length. This change makes the conditional assignment in SetAuthTag unnecessary, which is replaced with a CHECK. Refs: https://github.com/nodejs/node/pull/17825 PR-URL: https://github.com/nodejs/node/pull/20040 Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Yihong Wang <yh.wang@ibm.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
cb3d049bad
commit
faf449ca04
@ -2806,9 +2806,7 @@ bool CipherBase::InitAuthenticated(const char* cipher_type, int iv_len,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When decrypting in CCM mode, this field will be set in setAuthTag().
|
auth_tag_len_ = auth_tag_len;
|
||||||
if (kind_ == kCipher)
|
|
||||||
auth_tag_len_ = auth_tag_len;
|
|
||||||
|
|
||||||
// The message length is restricted to 2 ^ (8 * (15 - iv_len)) - 1 bytes.
|
// The message length is restricted to 2 ^ (8 * (15 - iv_len)) - 1 bytes.
|
||||||
CHECK(iv_len >= 7 && iv_len <= 13);
|
CHECK(iv_len >= 7 && iv_len <= 13);
|
||||||
@ -2824,7 +2822,7 @@ bool CipherBase::InitAuthenticated(const char* cipher_type, int iv_len,
|
|||||||
if (!IsValidGCMTagLength(auth_tag_len)) {
|
if (!IsValidGCMTagLength(auth_tag_len)) {
|
||||||
char msg[50];
|
char msg[50];
|
||||||
snprintf(msg, sizeof(msg),
|
snprintf(msg, sizeof(msg),
|
||||||
"Invalid GCM authentication tag length: %u", auth_tag_len);
|
"Invalid authentication tag length: %u", auth_tag_len);
|
||||||
env()->ThrowError(msg);
|
env()->ThrowError(msg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2891,21 +2889,26 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) {
|
|||||||
// Restrict GCM tag lengths according to NIST 800-38d, page 9.
|
// Restrict GCM tag lengths according to NIST 800-38d, page 9.
|
||||||
unsigned int tag_len = Buffer::Length(args[0]);
|
unsigned int tag_len = Buffer::Length(args[0]);
|
||||||
const int mode = EVP_CIPHER_CTX_mode(cipher->ctx_.get());
|
const int mode = EVP_CIPHER_CTX_mode(cipher->ctx_.get());
|
||||||
|
bool is_valid;
|
||||||
if (mode == EVP_CIPH_GCM_MODE) {
|
if (mode == EVP_CIPH_GCM_MODE) {
|
||||||
if ((cipher->auth_tag_len_ != kNoAuthTagLength &&
|
is_valid = (cipher->auth_tag_len_ == kNoAuthTagLength ||
|
||||||
cipher->auth_tag_len_ != tag_len) ||
|
cipher->auth_tag_len_ == tag_len) &&
|
||||||
!IsValidGCMTagLength(tag_len)) {
|
IsValidGCMTagLength(tag_len);
|
||||||
char msg[50];
|
} else {
|
||||||
snprintf(msg, sizeof(msg),
|
CHECK_EQ(mode, EVP_CIPH_CCM_MODE);
|
||||||
"Invalid GCM authentication tag length: %u", tag_len);
|
CHECK_NE(cipher->auth_tag_len_, kNoAuthTagLength);
|
||||||
return cipher->env()->ThrowError(msg);
|
is_valid = cipher->auth_tag_len_ == tag_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!is_valid) {
|
||||||
|
char msg[50];
|
||||||
|
snprintf(msg, sizeof(msg),
|
||||||
|
"Invalid authentication tag length: %u", tag_len);
|
||||||
|
return cipher->env()->ThrowError(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: we don't use std::min() here to work around a header conflict.
|
|
||||||
cipher->auth_tag_len_ = tag_len;
|
cipher->auth_tag_len_ = tag_len;
|
||||||
if (cipher->auth_tag_len_ > sizeof(cipher->auth_tag_))
|
CHECK_LE(cipher->auth_tag_len_, sizeof(cipher->auth_tag_));
|
||||||
cipher->auth_tag_len_ = sizeof(cipher->auth_tag_);
|
|
||||||
|
|
||||||
memset(cipher->auth_tag_, 0, sizeof(cipher->auth_tag_));
|
memset(cipher->auth_tag_, 0, sizeof(cipher->auth_tag_));
|
||||||
memcpy(cipher->auth_tag_, Buffer::Data(args[0]), cipher->auth_tag_len_);
|
memcpy(cipher->auth_tag_, Buffer::Data(args[0]), cipher->auth_tag_len_);
|
||||||
|
@ -724,7 +724,7 @@ for (const test of TEST_CASES) {
|
|||||||
decrypt.setAuthTag(Buffer.from('1'.repeat(length)));
|
decrypt.setAuthTag(Buffer.from('1'.repeat(length)));
|
||||||
}, {
|
}, {
|
||||||
type: Error,
|
type: Error,
|
||||||
message: `Invalid GCM authentication tag length: ${length}`
|
message: `Invalid authentication tag length: ${length}`
|
||||||
});
|
});
|
||||||
|
|
||||||
common.expectsError(() => {
|
common.expectsError(() => {
|
||||||
@ -736,7 +736,7 @@ for (const test of TEST_CASES) {
|
|||||||
});
|
});
|
||||||
}, {
|
}, {
|
||||||
type: Error,
|
type: Error,
|
||||||
message: `Invalid GCM authentication tag length: ${length}`
|
message: `Invalid authentication tag length: ${length}`
|
||||||
});
|
});
|
||||||
|
|
||||||
common.expectsError(() => {
|
common.expectsError(() => {
|
||||||
@ -748,7 +748,7 @@ for (const test of TEST_CASES) {
|
|||||||
});
|
});
|
||||||
}, {
|
}, {
|
||||||
type: Error,
|
type: Error,
|
||||||
message: `Invalid GCM authentication tag length: ${length}`
|
message: `Invalid authentication tag length: ${length}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -783,7 +783,7 @@ for (const test of TEST_CASES) {
|
|||||||
decipher.setAuthTag(Buffer.from('1'.repeat(12)));
|
decipher.setAuthTag(Buffer.from('1'.repeat(12)));
|
||||||
}, {
|
}, {
|
||||||
type: Error,
|
type: Error,
|
||||||
message: 'Invalid GCM authentication tag length: 12'
|
message: 'Invalid authentication tag length: 12'
|
||||||
});
|
});
|
||||||
|
|
||||||
// The Decipher object should be left intact.
|
// The Decipher object should be left intact.
|
||||||
@ -985,7 +985,7 @@ for (const test of TEST_CASES) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that setAAD throws in CCM mode when no authentication tag is provided.
|
// Test that final() throws in CCM mode when no authentication tag is provided.
|
||||||
{
|
{
|
||||||
if (!common.hasFipsCrypto) {
|
if (!common.hasFipsCrypto) {
|
||||||
const key = Buffer.from('1ed2233fa2223ef5d7df08546049406c', 'hex');
|
const key = Buffer.from('1ed2233fa2223ef5d7df08546049406c', 'hex');
|
||||||
@ -1000,6 +1000,8 @@ for (const test of TEST_CASES) {
|
|||||||
decrypt.setAAD(Buffer.from('63616c76696e', 'hex'), {
|
decrypt.setAAD(Buffer.from('63616c76696e', 'hex'), {
|
||||||
plaintextLength: ct.length
|
plaintextLength: ct.length
|
||||||
});
|
});
|
||||||
|
decrypt.update(ct);
|
||||||
|
decrypt.final();
|
||||||
}, errMessages.state);
|
}, errMessages.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user