mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 10:15:07 +08:00
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>
131 lines
3.5 KiB
JavaScript
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' });
|
|
}
|
|
}
|