mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 10:15:07 +08:00
Delete test-crypto-ecb.js, which can no longer run anywhere: Blowfish is available only from OpenSSL's legacy provider and is absent from BoringSSL. The addon and cctest version guards become OPENSSL_IS_BORINGSSL checks. Remove legacy digest-alias coverage and unreachable OpenSSL 1.x branches. JavaScript test and benchmark gates, skip reasons, and TLS error expectations that distinguished OpenSSL 1.x from provider-backed OpenSSL now distinguish BoringSSL, including PKCS12 parsing, named DH groups, private-key serialization, RSA-PSS parameters, consolidated provider caches, FIPS availability, and empty KMAC output. Keep the CCM alternate finalization error only for BoringSSL. Provider-only test skips use version-neutral terminology or state the actual minimum OpenSSL version. Signed-off-by: Filip Skokan <panva.ip@gmail.com> Assisted-by: Codex PR-URL: https://github.com/nodejs/node/pull/64777 Fixes: https://github.com/nodejs/node/issues/56733 Refs: https://github.com/nodejs/node/pull/64211 Refs: https://github.com/nodejs/node/pull/63966 Refs: https://github.com/nodejs/node/pull/53329 Refs: https://github.com/nodejs/node/pull/48950 Refs: https://github.com/nodejs/node/issues/45465 Refs: https://github.com/nodejs/node/issues/42827 Refs: https://github.com/nodejs/node/issues/47076 Refs: https://github.com/nodejs/node/pull/62862 Refs: https://github.com/nodejs/node/pull/38512 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Richard Lau <richard.lau@ibm.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
253 lines
6.6 KiB
JavaScript
253 lines
6.6 KiB
JavaScript
// Flags: --expose-internals
|
|
'use strict';
|
|
|
|
const common = require('../common');
|
|
|
|
if (!common.hasCrypto) {
|
|
common.skip('missing crypto');
|
|
}
|
|
|
|
const assert = require('node:assert');
|
|
const {
|
|
createHash,
|
|
createHmac,
|
|
createSign,
|
|
createVerify,
|
|
generateKeyPair,
|
|
generateKeyPairSync,
|
|
getHashes,
|
|
hash,
|
|
hkdf,
|
|
hkdfSync,
|
|
pbkdf2,
|
|
pbkdf2Sync,
|
|
privateDecrypt,
|
|
publicEncrypt,
|
|
sign,
|
|
verify,
|
|
} = require('node:crypto');
|
|
const { isBoringSSL } = require('../common/crypto');
|
|
|
|
if (isBoringSSL) {
|
|
common.skip('OpenSSL provider support is required');
|
|
}
|
|
|
|
const { internalBinding } = require('internal/test/binding');
|
|
const {
|
|
HashJob,
|
|
kCryptoJobSync,
|
|
kCryptoJobWebCrypto,
|
|
} = internalBinding('crypto');
|
|
|
|
const hashes = getHashes();
|
|
const lowercaseHashes = hashes.map((name) => name.toLowerCase());
|
|
if (lowercaseHashes.includes('sha1')) {
|
|
assert(hashes.includes('RSA-SHA1'));
|
|
}
|
|
assert(!lowercaseHashes.includes('null'));
|
|
assert(!lowercaseHashes.includes('ml-dsa-mu'));
|
|
assert(!hashes.some((name) => /^\d+(?:\.\d+)+$/.test(name)));
|
|
|
|
for (const name of hashes) {
|
|
try {
|
|
createHash(name);
|
|
} catch (err) {
|
|
assert.strictEqual(err.code, 'ERR_OSSL_EVP_NOT_XOF_OR_INVALID_LENGTH');
|
|
createHash(name, { outputLength: 32 });
|
|
}
|
|
}
|
|
|
|
const input = Buffer.alloc(0);
|
|
assert.throws(
|
|
() => createHash('ml-dsa-mu'),
|
|
/Digest method not supported/,
|
|
);
|
|
assert.throws(
|
|
() => hash('ml-dsa-mu', input),
|
|
{ message: 'Digest method ml-dsa-mu is not supported' },
|
|
);
|
|
|
|
const providerVectors = {
|
|
'keccak-kmac-128': {
|
|
aliases: ['keccak-kmac-128', 'keccak-kmac128'],
|
|
expected: '83aa04c211dc19d16912571ed0a75130' +
|
|
'd36aebd58562dd080c1ea84a8c7d73f7',
|
|
options: { outputLength: 32 },
|
|
},
|
|
'keccak-256': {
|
|
aliases: ['keccak-256'],
|
|
expected: 'c5d2460186f7233c927e7db2dcc703c0' +
|
|
'e500b653ca82273b7bfad8045d85a470',
|
|
},
|
|
'sha256-192': {
|
|
aliases: ['sha2-256/192', 'sha-256/192', 'sha256-192'],
|
|
expected: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934c',
|
|
},
|
|
};
|
|
|
|
function testHashVector({ aliases, expected, options }) {
|
|
for (const alias of aliases) {
|
|
if (!hashes.includes(alias)) continue;
|
|
assert(!hashes.includes(alias.toUpperCase()));
|
|
|
|
for (const name of [alias, alias.toUpperCase()]) {
|
|
const streaming = createHash(name, options).update(input).digest('hex');
|
|
assert.strictEqual(streaming, expected);
|
|
assert.strictEqual(hash(name, input, options), expected);
|
|
}
|
|
}
|
|
}
|
|
|
|
// These digests are tested when the active provider advertises them.
|
|
for (const name of ['keccak-kmac-128', 'keccak-256', 'sha256-192']) {
|
|
const vector = providerVectors[name];
|
|
if (vector.aliases.some((alias) => hashes.includes(alias))) {
|
|
testHashVector(vector);
|
|
} else {
|
|
common.printSkipMessage(`${name} is not available from the active provider`);
|
|
}
|
|
}
|
|
|
|
const keccakKmacName = providerVectors['keccak-kmac-128'].aliases
|
|
.find((alias) => hashes.includes(alias));
|
|
if (keccakKmacName !== undefined) {
|
|
(async () => {
|
|
const { expected } = providerVectors['keccak-kmac-128'];
|
|
const { 0: err, 1: syncResult } = new HashJob(
|
|
kCryptoJobSync,
|
|
keccakKmacName,
|
|
input,
|
|
256,
|
|
).run();
|
|
assert.strictEqual(err, undefined);
|
|
assert.strictEqual(Buffer.from(syncResult).toString('hex'), expected);
|
|
|
|
const result = await new HashJob(
|
|
kCryptoJobWebCrypto,
|
|
keccakKmacName,
|
|
input,
|
|
256,
|
|
).run();
|
|
assert.strictEqual(Buffer.from(result).toString('hex'), expected);
|
|
})().then(common.mustCall());
|
|
}
|
|
|
|
if (hashes.includes('sha256-192')) {
|
|
const operationInput = Buffer.from('abc');
|
|
|
|
assert.strictEqual(
|
|
createHmac('sha256-192', 'key').update(operationInput).digest('hex'),
|
|
'd7774e586190fa2d2f4d4be4bc86ccd459a9170d52c38809',
|
|
);
|
|
|
|
const hkdfExpected = 'ef23757b94b5e1e46c3f981d87828d7aeb0207733ab5c78' +
|
|
'c60df321c9e8c88e0ad54b4eecfef8c258ccd';
|
|
assert.strictEqual(
|
|
Buffer.from(hkdfSync('sha256-192', 'key', 'salt', 'info', 42))
|
|
.toString('hex'),
|
|
hkdfExpected,
|
|
);
|
|
hkdf(
|
|
'sha256-192',
|
|
'key',
|
|
'salt',
|
|
'info',
|
|
42,
|
|
common.mustSucceed((result) => {
|
|
assert.strictEqual(Buffer.from(result).toString('hex'), hkdfExpected);
|
|
}),
|
|
);
|
|
|
|
const pbkdf2Expected = '1fee3dd5ea13d5b563d3cc88fbc6dcf7' +
|
|
'3497aeffc3b3e6358ab3d3d1aa2aa0ee';
|
|
assert.strictEqual(
|
|
pbkdf2Sync('password', 'salt', 2, 32, 'sha256-192').toString('hex'),
|
|
pbkdf2Expected,
|
|
);
|
|
pbkdf2(
|
|
'password',
|
|
'salt',
|
|
2,
|
|
32,
|
|
'sha256-192',
|
|
common.mustSucceed((result) => {
|
|
assert.strictEqual(result.toString('hex'), pbkdf2Expected);
|
|
}),
|
|
);
|
|
|
|
const { privateKey: ecPrivateKey, publicKey: ecPublicKey } =
|
|
generateKeyPairSync('ec', { namedCurve: 'prime256v1' });
|
|
const signature = sign('sha256-192', operationInput, ecPrivateKey);
|
|
assert(verify('sha256-192', operationInput, ecPublicKey, signature));
|
|
|
|
const streamingSignature = createSign('sha256-192')
|
|
.update(operationInput)
|
|
.sign(ecPrivateKey);
|
|
const verifier = createVerify('sha256-192');
|
|
verifier.update(operationInput);
|
|
assert(verifier.verify(ecPublicKey, streamingSignature));
|
|
|
|
sign(
|
|
'sha256-192',
|
|
operationInput,
|
|
ecPrivateKey,
|
|
common.mustSucceed((asyncSignature) => {
|
|
verify(
|
|
'sha256-192',
|
|
operationInput,
|
|
ecPublicKey,
|
|
asyncSignature,
|
|
common.mustSucceed((result) => assert(result)),
|
|
);
|
|
}),
|
|
);
|
|
|
|
const { privateKey: rsaPrivateKey, publicKey: rsaPublicKey } =
|
|
generateKeyPairSync('rsa', { modulusLength: 2048 });
|
|
const plaintext = Buffer.from('provider digest');
|
|
|
|
assert.throws(
|
|
() => sign('sha256-192', plaintext, rsaPrivateKey),
|
|
{ code: 'ERR_OSSL_DIGEST_NOT_ALLOWED' },
|
|
);
|
|
assert.deepStrictEqual(
|
|
privateDecrypt(
|
|
{ key: rsaPrivateKey, oaepHash: 'sha256-192' },
|
|
publicEncrypt(
|
|
{ key: rsaPublicKey, oaepHash: 'sha256-192' },
|
|
plaintext,
|
|
),
|
|
),
|
|
plaintext,
|
|
);
|
|
|
|
const pssOptions = [
|
|
{
|
|
hashAlgorithm: 'sha256-192',
|
|
modulusLength: 2048,
|
|
},
|
|
{
|
|
hashAlgorithm: 'sha256',
|
|
mgf1HashAlgorithm: 'sha256-192',
|
|
modulusLength: 2048,
|
|
},
|
|
];
|
|
const keyGenerationFailed = { message: 'Key generation job failed' };
|
|
|
|
for (const options of pssOptions) {
|
|
assert.throws(
|
|
() => generateKeyPairSync('rsa-pss', options),
|
|
keyGenerationFailed,
|
|
);
|
|
generateKeyPair(
|
|
'rsa-pss',
|
|
options,
|
|
common.mustCall((err, publicKey, privateKey) => {
|
|
assert.strictEqual(err?.message, keyGenerationFailed.message);
|
|
assert.strictEqual(publicKey, undefined);
|
|
assert.strictEqual(privateKey, undefined);
|
|
}),
|
|
);
|
|
}
|
|
}
|