Skip to content

Commit

Permalink
doc,test: clarify ChaCha20-Poly1305 usage
Browse files Browse the repository at this point in the history
PR-URL: #42323
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
tniessen authored and juanarbol committed May 31, 2022
1 parent 0cd6bfa commit 2b80754
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 9 deletions.
27 changes: 18 additions & 9 deletions doc/api/crypto.md
Expand Up @@ -546,7 +546,8 @@ added: v1.0.0
-->

* Returns: {Buffer} When using an authenticated encryption mode (`GCM`, `CCM`,
and `OCB` are currently supported), the `cipher.getAuthTag()` method returns a
`OCB`, and `chacha20-poly1305` are currently supported), the
`cipher.getAuthTag()` method returns a
[`Buffer`][] containing the _authentication tag_ that has been computed from
the given data.

Expand All @@ -568,7 +569,8 @@ added: v1.0.0
* `encoding` {string} The string encoding to use when `buffer` is a string.
* Returns: {Cipher} for method chaining.

When using an authenticated encryption mode (`GCM`, `CCM`, and `OCB` are
When using an authenticated encryption mode (`GCM`, `CCM`, `OCB`, and
`chacha20-poly1305` are
currently supported), the `cipher.setAAD()` method sets the value used for the
_additional authenticated data_ (AAD) input parameter.

Expand Down Expand Up @@ -865,7 +867,8 @@ changes:
* `encoding` {string} String encoding to use when `buffer` is a string.
* Returns: {Decipher} for method chaining.

When using an authenticated encryption mode (`GCM`, `CCM`, and `OCB` are
When using an authenticated encryption mode (`GCM`, `CCM`, `OCB`, and
`chacha20-poly1305` are
currently supported), the `decipher.setAAD()` method sets the value used for the
_additional authenticated data_ (AAD) input parameter.

Expand Down Expand Up @@ -899,7 +902,8 @@ changes:
* `encoding` {string} String encoding to use when `buffer` is a string.
* Returns: {Decipher} for method chaining.

When using an authenticated encryption mode (`GCM`, `CCM`, and `OCB` are
When using an authenticated encryption mode (`GCM`, `CCM`, `OCB`, and
`chacha20-poly1305` are
currently supported), the `decipher.setAuthTag()` method is used to pass in the
received _authentication tag_. If no tag is provided, or if the cipher text
has been tampered with, [`decipher.final()`][] will throw, indicating that the
Expand All @@ -908,7 +912,8 @@ is invalid according to [NIST SP 800-38D][] or does not match the value of the
`authTagLength` option, `decipher.setAuthTag()` will throw an error.

The `decipher.setAuthTag()` method must be called before [`decipher.update()`][]
for `CCM` mode or before [`decipher.final()`][] for `GCM` and `OCB` modes.
for `CCM` mode or before [`decipher.final()`][] for `GCM` and `OCB` modes and
`chacha20-poly1305`.
`decipher.setAuthTag()` can only be called once.

When passing a string as the authentication tag, please consider
Expand Down Expand Up @@ -2949,7 +2954,8 @@ Creates and returns a `Cipher` object that uses the given `algorithm` and
`password`.

The `options` argument controls stream behavior and is optional except when a
cipher in CCM or OCB mode is used (e.g. `'aes-128-ccm'`). In that case, the
cipher in CCM or OCB mode (e.g. `'aes-128-ccm'`) or `chacha20-poly1305` is used.
In that case, the
`authTagLength` option is required and specifies the length of the
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
Expand Down Expand Up @@ -3020,7 +3026,8 @@ Creates and returns a `Cipher` object, with the given `algorithm`, `key` and
initialization vector (`iv`).

The `options` argument controls stream behavior and is optional except when a
cipher in CCM or OCB mode is used (e.g. `'aes-128-ccm'`). In that case, the
cipher in CCM or OCB mode (e.g. `'aes-128-ccm'`) or `chacha20-poly1305` is used.
In that case, the
`authTagLength` option is required and specifies the length of the
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
Expand Down Expand Up @@ -3068,7 +3075,8 @@ Creates and returns a `Decipher` object that uses the given `algorithm` and
`password` (key).

The `options` argument controls stream behavior and is optional except when a
cipher in CCM or OCB mode is used (e.g. `'aes-128-ccm'`). In that case, the
cipher in CCM or OCB mode (e.g. `'aes-128-ccm'`) or `chacha20-poly1305` is used.
In that case, the
`authTagLength` option is required and specifies the length of the
authentication tag in bytes, see [CCM mode][].

Expand Down Expand Up @@ -3121,7 +3129,8 @@ Creates and returns a `Decipher` object that uses the given `algorithm`, `key`
and initialization vector (`iv`).

The `options` argument controls stream behavior and is optional except when a
cipher in CCM or OCB mode is used (e.g. `'aes-128-ccm'`). In that case, the
cipher in CCM or OCB mode (e.g. `'aes-128-ccm'`) or `chacha20-poly1305` is used.
In that case, the
`authTagLength` option is required and specifies the length of the
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
Expand Down
44 changes: 44 additions & 0 deletions test/parallel/test-crypto-authenticated.js
Expand Up @@ -701,3 +701,47 @@ for (const test of TEST_CASES) {
});
}
}

// ChaCha20-Poly1305 should respect the authTagLength option and should not
// require the authentication tag before calls to update() during decryption.
{
const key = Buffer.alloc(32);
const iv = Buffer.alloc(12);

for (let authTagLength = 1; authTagLength <= 16; authTagLength++) {
const cipher =
crypto.createCipheriv('chacha20-poly1305', key, iv, { authTagLength });
const ciphertext = Buffer.concat([cipher.update('foo'), cipher.final()]);
const authTag = cipher.getAuthTag();
assert.strictEqual(authTag.length, authTagLength);

// The decipher operation should reject all authentication tags other than
// that of the expected length.
for (let other = 1; other <= 16; other++) {
const decipher = crypto.createDecipheriv('chacha20-poly1305', key, iv, {
authTagLength: other
});
// ChaCha20 is a stream cipher so we do not need to call final() to obtain
// the full plaintext.
const plaintext = decipher.update(ciphertext);
assert.strictEqual(plaintext.toString(), 'foo');
if (other === authTagLength) {
// The authentication tag length is as expected and the tag itself is
// correct, so this should work.
decipher.setAuthTag(authTag);
decipher.final();
} else {
// The authentication tag that we are going to pass to setAuthTag is
// either too short or too long. If other < authTagLength, the
// authentication tag is still correct, but it should still be rejected
// because its security assurance is lower than expected.
assert.throws(() => {
decipher.setAuthTag(authTag);
}, {
code: 'ERR_CRYPTO_INVALID_AUTH_TAG',
message: `Invalid authentication tag length: ${authTagLength}`
});
}
}
}
}

0 comments on commit 2b80754

Please sign in to comment.