Skip to content

Commit 44d6475

Browse files
committed
doc: warn about using strings as inputs in crypto
1 parent 2ff1c83 commit 44d6475

File tree

1 file changed

+67
-0
lines changed

1 file changed

+67
-0
lines changed

doc/api/crypto.md

+67
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,9 @@ of the ciphertext in bytes. See [CCM mode][].
537537

538538
The `decipher.setAAD()` method must be called before [`decipher.update()`][].
539539

540+
When passing a string as the `buffer`, please consider
541+
[Caveats when using strings as inputs to cryptographic APIs][].
542+
540543
### `decipher.setAuthTag(buffer[, encoding])`
541544
<!-- YAML
542545
added: v1.0.0
@@ -569,6 +572,9 @@ The `decipher.setAuthTag()` method must be called before [`decipher.update()`][]
569572
for `CCM` mode or before [`decipher.final()`][] for `GCM` and `OCB` modes.
570573
`decipher.setAuthTag()` can only be called once.
571574

575+
When passing a string as the authentication tag, please consider
576+
[Caveats when using strings as inputs to cryptographic APIs][].
577+
572578
### `decipher.setAutoPadding([autoPadding])`
573579
<!-- YAML
574580
added: v0.7.1
@@ -2161,6 +2167,9 @@ The `key` is the raw key used by the `algorithm` and `iv` is an
21612167
a [`KeyObject`][] of type `secret`. If the cipher does not need
21622168
an initialization vector, `iv` may be `null`.
21632169

2170+
When passing strings for `key` or `iv`, please consider
2171+
[Caveats when using strings as inputs to cryptographic APIs][].
2172+
21642173
Initialization vectors should be unpredictable and unique; ideally, they will be
21652174
cryptographically random. They do not have to be secret: IVs are typically just
21662175
added to ciphertext messages unencrypted. It may sound contradictory that
@@ -2257,6 +2266,9 @@ The `key` is the raw key used by the `algorithm` and `iv` is an
22572266
a [`KeyObject`][] of type `secret`. If the cipher does not need
22582267
an initialization vector, `iv` may be `null`.
22592268

2269+
When passing strings for `key` or `iv`, please consider
2270+
[Caveats when using strings as inputs to cryptographic APIs][].
2271+
22602272
Initialization vectors should be unpredictable and unique; ideally, they will be
22612273
cryptographically random. They do not have to be secret: IVs are typically just
22622274
added to ciphertext messages unencrypted. It may sound contradictory that
@@ -3097,6 +3109,9 @@ but will take a longer amount of time to complete.
30973109
The `salt` should be as unique as possible. It is recommended that a salt is
30983110
random and at least 16 bytes long. See [NIST SP 800-132][] for details.
30993111

3112+
When passing strings for `password` or `salt`, please consider
3113+
[Caveats when using strings as inputs to cryptographic APIs][].
3114+
31003115
```js
31013116
const crypto = require('crypto');
31023117
crypto.pbkdf2('secret', 'salt', 100000, 64, 'sha512', (err, derivedKey) => {
@@ -3168,6 +3183,9 @@ but will take a longer amount of time to complete.
31683183
The `salt` should be as unique as possible. It is recommended that a salt is
31693184
random and at least 16 bytes long. See [NIST SP 800-132][] for details.
31703185

3186+
When passing strings for `password` or `salt`, please consider
3187+
[Caveats when using strings as inputs to cryptographic APIs][].
3188+
31713189
```js
31723190
const crypto = require('crypto');
31733191
const key = crypto.pbkdf2Sync('secret', 'salt', 100000, 64, 'sha512');
@@ -3656,6 +3674,9 @@ memory-wise in order to make brute-force attacks unrewarding.
36563674
The `salt` should be as unique as possible. It is recommended that a salt is
36573675
random and at least 16 bytes long. See [NIST SP 800-132][] for details.
36583676

3677+
When passing strings for `password` or `salt`, please consider
3678+
[Caveats when using strings as inputs to cryptographic APIs][].
3679+
36593680
The `callback` function is called with two arguments: `err` and `derivedKey`.
36603681
`err` is an exception object when key derivation fails, otherwise `err` is
36613682
`null`. `derivedKey` is passed to the callback as a [`Buffer`][].
@@ -3714,6 +3735,9 @@ memory-wise in order to make brute-force attacks unrewarding.
37143735
The `salt` should be as unique as possible. It is recommended that a salt is
37153736
random and at least 16 bytes long. See [NIST SP 800-132][] for details.
37163737

3738+
When passing strings for `password` or `salt`, please consider
3739+
[Caveats when using strings as inputs to cryptographic APIs][].
3740+
37173741
An exception is thrown when key derivation fails, otherwise the derived key is
37183742
returned as a [`Buffer`][].
37193743

@@ -3923,6 +3947,47 @@ See the [Web Crypto API documentation][] for details.
39233947

39243948
## Notes
39253949

3950+
### Using strings as inputs to cryptographic APIs
3951+
3952+
For historical reasons, many cryptographic APIs provided by Node.js accept
3953+
strings as inputs where the underlying cryptographic algorithm works on byte
3954+
sequences. These instances include plaintexts, ciphertexts, symmetric keys,
3955+
initialization vectors, passphrases, salts, authentication tags,
3956+
and additional authenticated data.
3957+
3958+
When passing strings to cryptographic APIs, consider the following factors.
3959+
3960+
- Not all byte sequences are valid UTF-8 strings. Therefore, when a byte
3961+
sequence of length `n` is derived from a string, its entropy is generally
3962+
lower than the entropy of a random or pseudo-random `n` byte sequence.
3963+
For example, no UTF-8 string will result in the byte sequence `c0 af`. Secret
3964+
keys should almost exclusively be random or pseudo-random byte sequencs.
3965+
- Similarly, when converting random or pseudo-random byte sequences to UTF-8
3966+
strings, subsequences that do not represent valid code points may be replaced
3967+
by the Unicode replacement character (`U+FFFD`). The byte representation of
3968+
the resulting Unicode string may, therefore, not be equal to the byte sequence
3969+
that the string was created from.
3970+
3971+
```js
3972+
const original = [0xc0, 0xaf];
3973+
const bytesAsString = Buffer.from(original).toString('utf8');
3974+
const stringAsBytes = Buffer.from(bytesAsString, 'utf8');
3975+
console.log(stringAsBytes);
3976+
// Prints '<Buffer ef bf bd ef bf bd>'.
3977+
```
3978+
3979+
The outputs of ciphers, hash functions, signature algorithms, and key
3980+
derivation functions are pseudo-random byte sequences and should not be
3981+
used as Unicode strings.
3982+
- When strings are obtained from user input, some Unicode characters can be
3983+
represented in multiple equivalent ways that result in different byte
3984+
sequences. For example, when passing a user passphrase to a key derivation
3985+
function, such as PBKDF2 or scrypt, the result of the key derivation function
3986+
depends on whether the string uses composed or decomposed characters. Node.js
3987+
does not normalize character representations. Developers should consider using
3988+
[`String.prototype.normalize()`][] on user inputs before passing them to
3989+
cryptographic APIs.
3990+
39263991
### Legacy streams API (prior to Node.js 0.10)
39273992

39283993
The Crypto module was added to Node.js before there was the concept of a
@@ -4384,6 +4449,7 @@ See the [list of SSL OP Flags][] for details.
43844449
[AEAD algorithms]: https://en.wikipedia.org/wiki/Authenticated_encryption
43854450
[CCM mode]: #crypto_ccm_mode
43864451
[Caveats]: #crypto_support_for_weak_or_compromised_algorithms
4452+
[Caveats when using strings as inputs to cryptographic APIs]: #crypto_using_strings_as_inputs_to_cryptographic_apis
43874453
[Crypto constants]: #crypto_crypto_constants_1
43884454
[HTML 5.2]: https://www.w3.org/TR/html52/changes.html#features-removed
43894455
[HTML5's `keygen` element]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/keygen
@@ -4406,6 +4472,7 @@ See the [list of SSL OP Flags][] for details.
44064472
[`EVP_BytesToKey`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_BytesToKey.html
44074473
[`KeyObject`]: #crypto_class_keyobject
44084474
[`Sign`]: #crypto_class_sign
4475+
[`String.prototype.normalize()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize
44094476
[`UV_THREADPOOL_SIZE`]: cli.md#cli_uv_threadpool_size_size
44104477
[`Verify`]: #crypto_class_verify
44114478
[`cipher.final()`]: #crypto_cipher_final_outputencoding

0 commit comments

Comments
 (0)