Files
node/test/parallel/test-crypto-cipherbase-options-fast-path.js
Filip Skokan e7a69c4992 crypto: discover ciphers from OpenSSL providers
Enumerate usable ciphers and aliases from activated OpenSSL 3 providers
instead of maintaining lists of provider-only algorithms. Skip numeric
OID aliases and filter NULL, TLS composite, multiblock, and
encrypt-then-MAC implementations that the Cipher APIs cannot use.
Preserve the OpenSSL 1.1.1 and BoringSSL paths.

Expose CBC-CTS, SM4-GCM, SM4-CCM, SM4-XTS, and additional AES key wrap
implementations. Add `ctsMode` (CS1/CS2/CS3) and `xtsStandard`
(GB/IEEE) options for selecting provider CTS and SM4-XTS variants.

Keep ordinary cipher construction on the original binding and legacy
lookup paths.

Lazily cache successful provider fetches per Environment for
string initialization and `getCipherInfo()`.
Index entries by case-insensitive query, canonical, and alias names.
Deduplicate owners by provider and canonical identity. Return borrowed
pointers on warm hits.

Use the shared process-wide FIPS-state generation to invalidate
per-Environment cipher caches and refresh `getCiphers()` snapshots in
the main thread and workers. Existing cipher contexts retain their
implementation and can finish across a transition. Release provider
owners before unloading worker addon DSOs.

Enforce one-shot updates for CBC-CTS, AES key wrap, SIV/GCM-SIV, and
CCM decryption. Reject finalization without required input or CCM tags,
and defer authentication failures to `final()`. Document streaming and
XTS data-unit constraints.

Add known-answer vectors, option validation, provider round trips,
cache, worker, snapshot, FIPS transition, and construction benchmark
coverage.

Fixes: https://github.com/nodejs/node/issues/43040
Fixes: https://github.com/nodejs/node/issues/64866
Refs: https://github.com/nodejs/node/issues/62982
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65484
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
2026-08-25 23:11:06 +00:00

131 lines
3.5 KiB
JavaScript

'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const {
createCipheriv,
createDecipheriv,
} = require('crypto');
const key = Buffer.alloc(16);
const gcmIv = Buffer.alloc(12);
const cbcIv = Buffer.alloc(16);
for (const create of [createCipheriv, createDecipheriv]) {
// None of these supply either of the extended cipher options.
create('aes-128-gcm', key, gcmIv);
create('aes-128-gcm', key, gcmIv, null);
create('aes-128-gcm', key, gcmIv, {});
create('aes-128-gcm', key, gcmIv, undefined);
create('aes-128-gcm', key, gcmIv, { authTagLength: 16 });
for (const options of [
{ ctsMode: null },
{ ctsMode: undefined },
{ xtsStandard: null },
{ xtsStandard: undefined },
{ ctsMode: null, xtsStandard: undefined },
]) {
create('aes-128-gcm', key, gcmIv, options);
}
const accesses = [];
create('aes-128-gcm', key, gcmIv, {
get authTagLength() {
accesses.push('authTagLength');
return 16;
},
get ctsMode() {
accesses.push('ctsMode');
return null;
},
get xtsStandard() {
accesses.push('xtsStandard');
return undefined;
},
});
assert.deepStrictEqual(
accesses,
['authTagLength', 'ctsMode', 'xtsStandard']);
const extendedAccesses = [];
assert.throws(
() => create('aes-128-cbc', key, cbcIv, {
get authTagLength() {
extendedAccesses.push('authTagLength');
return null;
},
get ctsMode() {
extendedAccesses.push('ctsMode');
return 'CS1';
},
get xtsStandard() {
extendedAccesses.push('xtsStandard');
return null;
},
}),
{ code: 'ERR_CRYPTO_UNSUPPORTED_OPERATION' });
assert.deepStrictEqual(
extendedAccesses,
['authTagLength', 'ctsMode', 'xtsStandard']);
const invalidAuthTagAccesses = [];
assert.throws(() => create('aes-128-cbc', key, cbcIv, {
get authTagLength() {
invalidAuthTagAccesses.push('authTagLength');
return -2;
},
get ctsMode() {
invalidAuthTagAccesses.push('ctsMode');
return undefined;
},
}), { code: 'ERR_INVALID_ARG_VALUE' });
assert.deepStrictEqual(invalidAuthTagAccesses, ['authTagLength']);
const invalidCtsTypeAccesses = [];
assert.throws(() => create('aes-128-cbc', key, cbcIv, {
get ctsMode() {
invalidCtsTypeAccesses.push('ctsMode');
return 1;
},
get xtsStandard() {
invalidCtsTypeAccesses.push('xtsStandard');
return undefined;
},
}), { code: 'ERR_INVALID_ARG_TYPE' });
assert.deepStrictEqual(invalidCtsTypeAccesses, ['ctsMode']);
const invalidCtsValueAccesses = [];
assert.throws(() => create('aes-128-cbc', key, cbcIv, {
get ctsMode() {
invalidCtsValueAccesses.push('ctsMode');
return 'CS4';
},
get xtsStandard() {
invalidCtsValueAccesses.push('xtsStandard');
return undefined;
},
}), { code: 'ERR_INVALID_ARG_VALUE' });
assert.deepStrictEqual(
invalidCtsValueAccesses,
['ctsMode', 'xtsStandard']);
assert.throws(
() => create('aes-128-cbc', key, cbcIv, { xtsStandard: 'GB' }),
{ code: 'ERR_CRYPTO_UNSUPPORTED_OPERATION' });
for (const ctsMode of ['', 'CS4']) {
assert.throws(
() => create('aes-128-cbc', key, cbcIv, { ctsMode }),
{ code: 'ERR_INVALID_ARG_VALUE' });
}
for (const xtsStandard of ['', 'IEEE-1619']) {
assert.throws(
() => create('aes-128-cbc', key, cbcIv, { xtsStandard }),
{ code: 'ERR_INVALID_ARG_VALUE' });
}
}