Skip to content

Commit

Permalink
crypto: support authTagLength in GCM encryption
Browse files Browse the repository at this point in the history
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 <jasnell@gmail.com>
Reviewed-By: Yihong Wang <yh.wang@ibm.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
  • Loading branch information
tniessen committed Apr 26, 2018
1 parent 6606a26 commit f9b9974
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 5 deletions.
17 changes: 15 additions & 2 deletions doc/api/crypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -1321,6 +1321,11 @@ This property is deprecated. Please use `crypto.setFips()` and
<!-- YAML
added: v0.1.94
deprecated: v10.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/20235
description: The `authTagLength` option can now be used to produce shorter
authentication tags in GCM mode and defaults to 16 bytes.
-->

> Stability: 0 - Deprecated: Use [`crypto.createCipheriv()`][] instead.
Expand All @@ -1336,7 +1341,9 @@ Creates and returns a `Cipher` object that uses the given `algorithm` and
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 set the length of the authentication
tag that will be returned by `getAuthTag()` and defaults to 16 bytes.

The `algorithm` is dependent on OpenSSL, examples are `'aes192'`, etc. On
recent OpenSSL releases, `openssl list-cipher-algorithms` will display the
Expand Down Expand Up @@ -1366,6 +1373,10 @@ Adversaries][] for details.
<!-- YAML
added: v0.1.94
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/20235
description: The `authTagLength` option can now be used to produce shorter
authentication tags in GCM mode and defaults to 16 bytes.
- 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
Expand All @@ -1383,7 +1394,9 @@ 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 set the length of the authentication
tag that will be returned by `getAuthTag()` and defaults to 16 bytes.

The `algorithm` is dependent on OpenSSL, examples are `'aes192'`, etc. On
recent OpenSSL releases, `openssl list-cipher-algorithms` will display the
Expand Down
7 changes: 4 additions & 3 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3106,9 +3106,10 @@ bool CipherBase::Final(unsigned char** out, int *out_len) {
ok = EVP_CipherFinal_ex(ctx_, *out, out_len) == 1;

if (ok && kind_ == kCipher && IsAuthenticatedMode()) {
// For GCM, the tag length is static (16 bytes), while the CCM tag length
// must be specified in advance.
if (mode == EVP_CIPH_GCM_MODE)
// In GCM mode, the authentication tag length can be specified in advance,
// but defaults to 16 bytes when encrypting. In CCM mode, it must always
// be given by the user.
if (mode == EVP_CIPH_GCM_MODE && auth_tag_len_ == kNoAuthTagLength)
auth_tag_len_ = sizeof(auth_tag_);
// TOOD(tniessen) Use EVP_CTRL_AEAP_GET_TAG in OpenSSL 1.1.0
static_assert(EVP_CTRL_CCM_GET_TAG == EVP_CTRL_GCM_GET_TAG,
Expand Down
29 changes: 29 additions & 0 deletions test/parallel/test-crypto-authenticated.js
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,18 @@ for (const test of TEST_CASES) {
message: `Invalid GCM authentication tag length: ${length}`
});

common.expectsError(() => {
crypto.createCipheriv('aes-256-gcm',
'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8',
'qkuZpJWCewa6Szih',
{
authTagLength: length
});
}, {
type: Error,
message: `Invalid GCM authentication tag length: ${length}`
});

common.expectsError(() => {
crypto.createDecipheriv('aes-256-gcm',
'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8',
Expand All @@ -741,6 +753,23 @@ for (const test of TEST_CASES) {
}
}

// Test that GCM can produce shorter authentication tags than 16 bytes.
{
const fullTag = '1debb47b2c91ba2cea16fad021703070';
for (const [authTagLength, e] of [[undefined, 16], [12, 12], [4, 4]]) {
const cipher = crypto.createCipheriv('aes-256-gcm',
'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8',
'qkuZpJWCewa6Szih', {
authTagLength
});
cipher.setAAD(Buffer.from('abcd'));
cipher.update('01234567', 'hex');
cipher.final();
const tag = cipher.getAuthTag();
assert.strictEqual(tag.toString('hex'), fullTag.substr(0, 2 * e));
}
}

// Test that users can manually restrict the GCM tag length to a single value.
{
const decipher = crypto.createDecipheriv('aes-256-gcm',
Expand Down

0 comments on commit f9b9974

Please sign in to comment.