Skip to content

Commit 0259d30

Browse files
committed
crypto: support authTagLength in GCM encryption
The authTagLength option can now be used to produce GCM authentication tags with a specific length. PR-URL: #20235 Refs: #20039 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Yihong Wang <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
1 parent f57543b commit 0259d30

File tree

3 files changed

+48
-5
lines changed

3 files changed

+48
-5
lines changed

doc/api/crypto.md

+15-2
Original file line numberDiff line numberDiff line change
@@ -1316,6 +1316,11 @@ This property is deprecated. Please use `crypto.setFips()` and
13161316
<!-- YAML
13171317
added: v0.1.94
13181318
deprecated: v10.0.0
1319+
changes:
1320+
- version: REPLACEME
1321+
pr-url: https://github.com/nodejs/node/pull/20235
1322+
description: The `authTagLength` option can now be used to produce shorter
1323+
authentication tags in GCM mode and defaults to 16 bytes.
13191324
-->
13201325

13211326
> Stability: 0 - Deprecated: Use [`crypto.createCipheriv()`][] instead.
@@ -1331,7 +1336,9 @@ Creates and returns a `Cipher` object that uses the given `algorithm` and
13311336
The `options` argument controls stream behavior and is optional except when a
13321337
cipher in CCM mode is used (e.g. `'aes-128-ccm'`). In that case, the
13331338
`authTagLength` option is required and specifies the length of the
1334-
authentication tag in bytes, see [CCM mode][].
1339+
authentication tag in bytes, see [CCM mode][]. In GCM mode, the `authTagLength`
1340+
option is not required but can be used to set the length of the authentication
1341+
tag that will be returned by `getAuthTag()` and defaults to 16 bytes.
13351342

13361343
The `algorithm` is dependent on OpenSSL, examples are `'aes192'`, etc. On
13371344
recent OpenSSL releases, `openssl list -cipher-algorithms`
@@ -1362,6 +1369,10 @@ Adversaries][] for details.
13621369
<!-- YAML
13631370
added: v0.1.94
13641371
changes:
1372+
- version: REPLACEME
1373+
pr-url: https://github.com/nodejs/node/pull/20235
1374+
description: The `authTagLength` option can now be used to produce shorter
1375+
authentication tags in GCM mode and defaults to 16 bytes.
13651376
- version: v9.9.0
13661377
pr-url: https://github.com/nodejs/node/pull/18644
13671378
description: The `iv` parameter may now be `null` for ciphers which do not
@@ -1379,7 +1390,9 @@ initialization vector (`iv`).
13791390
The `options` argument controls stream behavior and is optional except when a
13801391
cipher in CCM mode is used (e.g. `'aes-128-ccm'`). In that case, the
13811392
`authTagLength` option is required and specifies the length of the
1382-
authentication tag in bytes, see [CCM mode][].
1393+
authentication tag in bytes, see [CCM mode][]. In GCM mode, the `authTagLength`
1394+
option is not required but can be used to set the length of the authentication
1395+
tag that will be returned by `getAuthTag()` and defaults to 16 bytes.
13831396

13841397
The `algorithm` is dependent on OpenSSL, examples are `'aes192'`, etc. On
13851398
recent OpenSSL releases, `openssl list -cipher-algorithms`

src/node_crypto.cc

+4-3
Original file line numberDiff line numberDiff line change
@@ -3123,9 +3123,10 @@ bool CipherBase::Final(unsigned char** out, int *out_len) {
31233123
ok = EVP_CipherFinal_ex(ctx_, *out, out_len) == 1;
31243124

31253125
if (ok && kind_ == kCipher && IsAuthenticatedMode()) {
3126-
// For GCM, the tag length is static (16 bytes), while the CCM tag length
3127-
// must be specified in advance.
3128-
if (mode == EVP_CIPH_GCM_MODE)
3126+
// In GCM mode, the authentication tag length can be specified in advance,
3127+
// but defaults to 16 bytes when encrypting. In CCM mode, it must always
3128+
// be given by the user.
3129+
if (mode == EVP_CIPH_GCM_MODE && auth_tag_len_ == kNoAuthTagLength)
31293130
auth_tag_len_ = sizeof(auth_tag_);
31303131
CHECK_EQ(1, EVP_CIPHER_CTX_ctrl(ctx_, EVP_CTRL_AEAD_GET_TAG,
31313132
auth_tag_len_,

test/parallel/test-crypto-authenticated.js

+29
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,18 @@ for (const test of TEST_CASES) {
730730

731731
// Explicitely passing invalid lengths should throw.
732732
for (const length of [0, 1, 2, 6, 9, 10, 11, 17]) {
733+
common.expectsError(() => {
734+
crypto.createCipheriv('aes-256-gcm',
735+
'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8',
736+
'qkuZpJWCewa6Szih',
737+
{
738+
authTagLength: length
739+
});
740+
}, {
741+
type: Error,
742+
message: `Invalid GCM authentication tag length: ${length}`
743+
});
744+
733745
common.expectsError(() => {
734746
crypto.createDecipheriv('aes-256-gcm',
735747
'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8',
@@ -744,6 +756,23 @@ for (const test of TEST_CASES) {
744756
}
745757
}
746758

759+
// Test that GCM can produce shorter authentication tags than 16 bytes.
760+
{
761+
const fullTag = '1debb47b2c91ba2cea16fad021703070';
762+
for (const [authTagLength, e] of [[undefined, 16], [12, 12], [4, 4]]) {
763+
const cipher = crypto.createCipheriv('aes-256-gcm',
764+
'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8',
765+
'qkuZpJWCewa6Szih', {
766+
authTagLength
767+
});
768+
cipher.setAAD(Buffer.from('abcd'));
769+
cipher.update('01234567', 'hex');
770+
cipher.final();
771+
const tag = cipher.getAuthTag();
772+
assert.strictEqual(tag.toString('hex'), fullTag.substr(0, 2 * e));
773+
}
774+
}
775+
747776
// Test that users can manually restrict the GCM tag length to a single value.
748777
{
749778
const decipher = crypto.createDecipheriv('aes-256-gcm',

0 commit comments

Comments
 (0)