summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Nießen <tniessen@tnie.de>2018-04-13 18:02:46 +0200
committerTobias Nießen <tniessen@tnie.de>2018-04-23 01:55:09 +0200
commit358d8ffad650b6fb966082e7bd1460b0d6a4eacc (patch)
treeff6262822e193167ec4758a92cea0bf03216d33f
parent854f840243b0b531f9569f9304a6810b01bd9778 (diff)
downloadandroid-node-v8-358d8ffad650b6fb966082e7bd1460b0d6a4eacc.tar.gz
android-node-v8-358d8ffad650b6fb966082e7bd1460b0d6a4eacc.tar.bz2
android-node-v8-358d8ffad650b6fb966082e7bd1460b0d6a4eacc.zip
crypto: allow to restrict valid GCM tag length
This change allows users to restrict accepted GCM authentication tag lengths to a single value. PR-URL: https://github.com/nodejs/node/pull/20039 Fixes: https://github.com/nodejs/node/issues/17523 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yihong Wang <yh.wang@ibm.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
-rw-r--r--doc/api/crypto.md8
-rw-r--r--src/node_crypto.cc33
-rw-r--r--src/node_crypto.h3
-rw-r--r--test/parallel/test-crypto-authenticated.js37
4 files changed, 74 insertions, 7 deletions
diff --git a/doc/api/crypto.md b/doc/api/crypto.md
index b4ff52d89d..47181f6a2c 100644
--- a/doc/api/crypto.md
+++ b/doc/api/crypto.md
@@ -1457,6 +1457,10 @@ to create the `Decipher` object.
<!-- YAML
added: v0.1.94
changes:
+ - version: REPLACEME
+ pr-url: https://github.com/nodejs/node/pull/20039
+ description: The `authTagLength` option can now be used to restrict accepted
+ GCM authentication tag lengths.
- version: v9.9.0
pr-url: https://github.com/nodejs/node/pull/18644
description: The `iv` parameter may now be `null` for ciphers which do not
@@ -1474,7 +1478,9 @@ and initialization vector (`iv`).
The `options` argument controls stream behavior and is optional except when a
cipher in CCM mode is used (e.g. `'aes-128-ccm'`). In that case, the
`authTagLength` option is required and specifies the length of the
-authentication tag in bytes, see [CCM mode][].
+authentication tag in bytes, see [CCM mode][]. In GCM mode, the `authTagLength`
+option is not required but can be used to restrict accepted authentication tags
+to those with the specified length.
The `algorithm` is dependent on OpenSSL, examples are `'aes192'`, etc. On
recent OpenSSL releases, `openssl list-cipher-algorithms` will display the
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index 2bdda4566d..255dc6e2d4 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -2797,6 +2797,10 @@ void CipherBase::InitIv(const FunctionCallbackInfo<Value>& args) {
}
+static bool IsValidGCMTagLength(unsigned int tag_len) {
+ return tag_len == 4 || tag_len == 8 || tag_len >= 12 && tag_len <= 16;
+}
+
bool CipherBase::InitAuthenticated(const char *cipher_type, int iv_len,
int auth_tag_len) {
CHECK(IsAuthenticatedMode());
@@ -2809,7 +2813,8 @@ bool CipherBase::InitAuthenticated(const char *cipher_type, int iv_len,
return false;
}
- if (EVP_CIPHER_CTX_mode(ctx_) == EVP_CIPH_CCM_MODE) {
+ const int mode = EVP_CIPHER_CTX_mode(ctx_);
+ if (mode == EVP_CIPH_CCM_MODE) {
if (auth_tag_len < 0) {
char msg[128];
snprintf(msg, sizeof(msg), "authTagLength required for %s", cipher_type);
@@ -2842,6 +2847,21 @@ bool CipherBase::InitAuthenticated(const char *cipher_type, int iv_len,
} else {
max_message_size_ = INT_MAX;
}
+ } else {
+ CHECK_EQ(mode, EVP_CIPH_GCM_MODE);
+
+ if (auth_tag_len >= 0) {
+ if (!IsValidGCMTagLength(auth_tag_len)) {
+ char msg[50];
+ snprintf(msg, sizeof(msg),
+ "Invalid GCM authentication tag length: %u", auth_tag_len);
+ env()->ThrowError(msg);
+ return false;
+ }
+
+ // Remember the given authentication tag length for later.
+ auth_tag_len_ = auth_tag_len;
+ }
}
return true;
@@ -2877,7 +2897,7 @@ void CipherBase::GetAuthTag(const FunctionCallbackInfo<Value>& args) {
// Only callable after Final and if encrypting.
if (cipher->ctx_ != nullptr ||
cipher->kind_ != kCipher ||
- cipher->auth_tag_len_ == 0) {
+ cipher->auth_tag_len_ == kNoAuthTagLength) {
return args.GetReturnValue().SetUndefined();
}
@@ -2902,7 +2922,9 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) {
unsigned int tag_len = Buffer::Length(args[0]);
const int mode = EVP_CIPHER_CTX_mode(cipher->ctx_);
if (mode == EVP_CIPH_GCM_MODE) {
- if (tag_len > 16 || (tag_len < 12 && tag_len != 8 && tag_len != 4)) {
+ if (cipher->auth_tag_len_ != kNoAuthTagLength &&
+ cipher->auth_tag_len_ != tag_len ||
+ !IsValidGCMTagLength(tag_len)) {
char msg[50];
snprintf(msg, sizeof(msg),
"Invalid GCM authentication tag length: %u", tag_len);
@@ -2938,7 +2960,8 @@ bool CipherBase::SetAAD(const char* data, unsigned int len, int plaintext_len) {
if (!CheckCCMMessageLength(plaintext_len))
return false;
- if (kind_ == kDecipher && !auth_tag_set_ && auth_tag_len_ > 0) {
+ if (kind_ == kDecipher && !auth_tag_set_ && auth_tag_len_ > 0 &&
+ auth_tag_len_ != kNoAuthTagLength) {
if (!EVP_CIPHER_CTX_ctrl(ctx_,
EVP_CTRL_CCM_SET_TAG,
auth_tag_len_,
@@ -2991,7 +3014,7 @@ CipherBase::UpdateResult CipherBase::Update(const char* data,
// on first update:
if (kind_ == kDecipher && IsAuthenticatedMode() && auth_tag_len_ > 0 &&
- !auth_tag_set_) {
+ auth_tag_len_ != kNoAuthTagLength && !auth_tag_set_) {
EVP_CIPHER_CTX_ctrl(ctx_,
EVP_CTRL_GCM_SET_TAG,
auth_tag_len_,
diff --git a/src/node_crypto.h b/src/node_crypto.h
index 2f7c904ee9..3c166f5dcc 100644
--- a/src/node_crypto.h
+++ b/src/node_crypto.h
@@ -359,6 +359,7 @@ class CipherBase : public BaseObject {
kErrorMessageSize,
kErrorState
};
+ static const unsigned kNoAuthTagLength = static_cast<unsigned>(-1);
void Init(const char* cipher_type,
const char* key_buf,
@@ -398,7 +399,7 @@ class CipherBase : public BaseObject {
ctx_(nullptr),
kind_(kind),
auth_tag_set_(false),
- auth_tag_len_(0),
+ auth_tag_len_(kNoAuthTagLength),
pending_auth_failed_(false) {
MakeWeak<CipherBase>(this);
}
diff --git a/test/parallel/test-crypto-authenticated.js b/test/parallel/test-crypto-authenticated.js
index df5ba03923..6c41538752 100644
--- a/test/parallel/test-crypto-authenticated.js
+++ b/test/parallel/test-crypto-authenticated.js
@@ -726,9 +726,46 @@ for (const test of TEST_CASES) {
type: Error,
message: `Invalid GCM authentication tag length: ${length}`
});
+
+ common.expectsError(() => {
+ crypto.createDecipheriv('aes-256-gcm',
+ 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8',
+ 'qkuZpJWCewa6Szih',
+ {
+ authTagLength: length
+ });
+ }, {
+ type: Error,
+ message: `Invalid GCM authentication tag length: ${length}`
+ });
}
}
+// Test that users can manually restrict the GCM tag length to a single value.
+{
+ const decipher = crypto.createDecipheriv('aes-256-gcm',
+ 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8',
+ 'qkuZpJWCewa6Szih', {
+ authTagLength: 8
+ });
+
+ common.expectsError(() => {
+ // This tag would normally be allowed.
+ decipher.setAuthTag(Buffer.from('1'.repeat(12)));
+ }, {
+ type: Error,
+ message: 'Invalid GCM authentication tag length: 12'
+ });
+
+ // The Decipher object should be left intact.
+ decipher.setAuthTag(Buffer.from('445352d3ff85cf94', 'hex'));
+ const text = Buffer.concat([
+ decipher.update('3a2a3647', 'hex'),
+ decipher.final()
+ ]);
+ assert.strictEqual(text.toString('utf8'), 'node');
+}
+
// Test that create(De|C)ipher(iv)? throws if the mode is CCM and an invalid
// authentication tag length has been specified.
{