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.

Backport-PR-URL: #20706
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 authored and addaleax committed May 14, 2018
1 parent 1e5de6f commit 2b2ccae
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 @@ -1316,6 +1316,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 @@ -1331,7 +1336,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`
Expand Down Expand Up @@ -1362,6 +1369,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 @@ -1379,7 +1390,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`
Expand Down
7 changes: 4 additions & 3 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3123,9 +3123,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_);
CHECK_EQ(1, EVP_CIPHER_CTX_ctrl(ctx_, EVP_CTRL_AEAD_GET_TAG,
auth_tag_len_,
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 @@ -730,6 +730,18 @@ for (const test of TEST_CASES) {

// Explicitely passing invalid lengths should throw.
for (const length of [0, 1, 2, 6, 9, 10, 11, 17]) {
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 @@ -744,6 +756,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 2b2ccae

Please sign in to comment.