Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc,test: clarify ChaCha20-Poly1305 usage #42323

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -2967,7 +2972,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 @@ -3038,7 +3044,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 @@ -3086,7 +3093,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 @@ -3139,7 +3147,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}`
});
}
}
}
}