Skip to content

crypto: disable ciphers not supported by EVP #43532

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,14 @@ when an error occurs (and is caught) during the creation of the
context, for example, when the allocation fails or the maximum call stack
size is reached when the context is created.

<a id="ERR_CRYPTO_CIPHER_DISABLED"></a>

### `ERR_CRYPTO_CIPHER_DISABLED`

A cipher was requested that does not conform to OpenSSL's standard
EVP interface for AEAD ciphers and requires usage of undocumented
interfaces.

<a id="ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED"></a>

### `ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED`
Expand Down
9 changes: 9 additions & 0 deletions lib/internal/crypto/cipher.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const {

const {
codes: {
ERR_CRYPTO_CIPHER_DISABLED,
ERR_CRYPTO_INVALID_STATE,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
Expand Down Expand Up @@ -108,6 +109,14 @@ function getUIntOption(options, key) {
}

function createCipherBase(cipher, credential, options, decipher, iv) {
switch (cipher.toLowerCase()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a toLowerCase() function in lib/tls.js. It'd be better to move that to some shared file and use that because the prototype method is locale sensitive and vulnerable to prototype pollution.

case 'aes-128-cbc-hmac-sha1':
case 'aes-128-cbc-hmac-sha256':
case 'aes-256-cbc-hmac-sha1':
case 'aes-256-cbc-hmac-sha256':
throw new ERR_CRYPTO_CIPHER_DISABLED(cipher);
}

const authTagLength = getUIntOption(options, 'authTagLength');
this[kHandle] = new CipherBase(decipher);
if (iv === undefined) {
Expand Down
3 changes: 3 additions & 0 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,9 @@ E('ERR_CHILD_PROCESS_STDIO_MAXBUFFER', '%s maxBuffer length exceeded',
E('ERR_CONSOLE_WRITABLE_STREAM',
'Console expects a writable stream instance for %s', TypeError);
E('ERR_CONTEXT_NOT_INITIALIZED', 'context used is not initialized', Error);
E('ERR_CRYPTO_CIPHER_DISABLED',
'Cipher %s is disabled as it\'s not compatible with OpenSSL\'s ' +
'EVP AEAD interfaces', Error);
E('ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED',
'Custom engines not supported by this OpenSSL', Error);
E('ERR_CRYPTO_ECDH_INVALID_FORMAT', 'Invalid ECDH format: %s', TypeError);
Expand Down
20 changes: 16 additions & 4 deletions test/parallel/test-crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,22 @@ for (const algo of cryptoCiphers) {
options = { authTagLength: 8 };
else if (mode === 'ocb' || algo === 'chacha20-poly1305')
options = { authTagLength: 16 };
crypto.createCipheriv(algo,
crypto.randomBytes(keyLength),
crypto.randomBytes(ivLength || 0),
options);

const create_cipher = () =>
crypto.createCipheriv(algo,
crypto.randomBytes(keyLength),
crypto.randomBytes(ivLength || 0),
options);

// Disabled ciphers will throw
if (algo.includes('hmac-sha'))
assert.throws(create_cipher, {
code: 'ERR_CRYPTO_CIPHER_DISABLED',
name: 'Error',
message: `Cipher ${algo} is disabled as it's not compatible with OpenSSL's EVP AEAD interfaces`
});
else
create_cipher();
}

// Assume that we have at least AES256-SHA.
Expand Down