Skip to content

Commit 03e05b0

Browse files
tniessenmarco-ippolito
authored andcommitted
crypto: deprecate implicitly shortened GCM tags
This introduces a doc-only deprecation of using GCM authentication tags that are shorter than the cipher's block size, unless the user specified the authTagLength option. Refs: #52327 PR-URL: #52345 Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent b9bdb94 commit 03e05b0

File tree

6 files changed

+107
-2
lines changed

6 files changed

+107
-2
lines changed

doc/api/crypto.md

+5
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,11 @@ When passing a string as the `buffer`, please consider
891891
<!-- YAML
892892
added: v1.0.0
893893
changes:
894+
- version: REPLACEME
895+
pr-url: https://github.com/nodejs/node/pull/52345
896+
description: Using GCM tag lengths other than 128 bits without specifying
897+
the `authTagLength` option when creating `decipher` is
898+
deprecated.
894899
- version: v15.0.0
895900
pr-url: https://github.com/nodejs/node/pull/35093
896901
description: The buffer argument can be a string or ArrayBuffer and is

doc/api/deprecations.md

+19
Original file line numberDiff line numberDiff line change
@@ -3507,6 +3507,25 @@ Calling `Hmac` class directly with `Hmac()` or `new Hmac()` is
35073507
deprecated due to being internals, not intended for public use.
35083508
Please use the [`crypto.createHmac()`][] method to create Hmac instances.
35093509

3510+
### DEP0182: Short GCM authentication tags without explicit `authTagLength`
3511+
3512+
<!-- YAML
3513+
changes:
3514+
- version: REPLACEME
3515+
pr-url: https://github.com/nodejs/node/pull/52345
3516+
description: Documentation-only deprecation.
3517+
-->
3518+
3519+
Type: Documentation-only (supports [`--pending-deprecation`][])
3520+
3521+
Applications that intend to use authentication tags that are shorter than the
3522+
default authentication tag length should set the `authTagLength` option of the
3523+
[`crypto.createDecipheriv()`][] function to the appropriate length.
3524+
3525+
For ciphers in GCM mode, the [`decipher.setAuthTag()`][] function accepts
3526+
authentication tags of any valid length (see [DEP0090](#DEP0090)). This behavior
3527+
is deprecated to better align with recommendations per [NIST SP 800-38D][].
3528+
35103529
[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
35113530
[RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3
35123531
[RFC 8247 Section 2.4]: https://www.rfc-editor.org/rfc/rfc8247#section-2.4

src/crypto/crypto_cipher.cc

+13
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,19 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) {
707707
env, "Invalid authentication tag length: %u", tag_len);
708708
}
709709

710+
if (mode == EVP_CIPH_GCM_MODE && cipher->auth_tag_len_ == kNoAuthTagLength &&
711+
tag_len != 16 && env->options()->pending_deprecation &&
712+
env->EmitProcessEnvWarning()) {
713+
if (ProcessEmitDeprecationWarning(
714+
env,
715+
"Using AES-GCM authentication tags of less than 128 bits without "
716+
"specifying the authTagLength option when initializing decryption "
717+
"is deprecated.",
718+
"DEP0182")
719+
.IsNothing())
720+
return;
721+
}
722+
710723
cipher->auth_tag_len_ = tag_len;
711724
cipher->auth_tag_state_ = kAuthTagKnown;
712725
CHECK_LE(cipher->auth_tag_len_, sizeof(cipher->auth_tag_));

test/common/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -654,12 +654,12 @@ function _expectWarning(name, expected, code) {
654654
expected = [[expected, code]];
655655
} else if (!Array.isArray(expected)) {
656656
expected = Object.entries(expected).map(([a, b]) => [b, a]);
657-
} else if (!(Array.isArray(expected[0]))) {
657+
} else if (expected.length !== 0 && !Array.isArray(expected[0])) {
658658
expected = [[expected[0], expected[1]]];
659659
}
660660
// Deprecation codes are mandatory, everything else is not.
661661
if (name === 'DeprecationWarning') {
662-
expected.forEach(([_, code]) => assert(code, expected));
662+
expected.forEach(([_, code]) => assert(code, `Missing deprecation code: ${expected}`));
663663
}
664664
return mustCall((warning) => {
665665
const expectedProperties = expected.shift();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Flags: --pending-deprecation
2+
'use strict';
3+
4+
const common = require('../common');
5+
if (!common.hasCrypto)
6+
common.skip('missing crypto');
7+
8+
const assert = require('assert');
9+
const { createDecipheriv, randomBytes } = require('crypto');
10+
11+
common.expectWarning({
12+
DeprecationWarning: []
13+
});
14+
15+
const key = randomBytes(32);
16+
const iv = randomBytes(16);
17+
18+
{
19+
// Full 128-bit tag.
20+
21+
const tag = randomBytes(16);
22+
createDecipheriv('aes-256-gcm', key, iv).setAuthTag(tag);
23+
}
24+
25+
{
26+
// Shortened tag with explicit length option.
27+
28+
const tag = randomBytes(12);
29+
createDecipheriv('aes-256-gcm', key, iv, {
30+
authTagLength: tag.byteLength
31+
}).setAuthTag(tag);
32+
}
33+
34+
{
35+
// Shortened tag with explicit but incorrect length option.
36+
37+
const tag = randomBytes(12);
38+
assert.throws(() => {
39+
createDecipheriv('aes-256-gcm', key, iv, {
40+
authTagLength: 14
41+
}).setAuthTag(tag);
42+
}, {
43+
name: 'TypeError',
44+
message: 'Invalid authentication tag length: 12',
45+
code: 'ERR_CRYPTO_INVALID_AUTH_TAG'
46+
});
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Flags: --pending-deprecation
2+
'use strict';
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const { createDecipheriv, randomBytes } = require('crypto');
8+
9+
common.expectWarning({
10+
DeprecationWarning: [
11+
['Using AES-GCM authentication tags of less than 128 bits without ' +
12+
'specifying the authTagLength option when initializing decryption is ' +
13+
'deprecated.',
14+
'DEP0182'],
15+
]
16+
});
17+
18+
const key = randomBytes(32);
19+
const iv = randomBytes(16);
20+
const tag = randomBytes(12);
21+
createDecipheriv('aes-256-gcm', key, iv).setAuthTag(tag);

0 commit comments

Comments
 (0)