crypto: subject some algorithms in Web Cryptography on BoringSSL absence

PR-URL: https://github.com/nodejs/node/pull/59365
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ethan Arrowood <ethan@arrowood.dev>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
This commit is contained in:
Filip Skokan 2025-08-09 09:21:18 +02:00
parent 84aaed7597
commit 0cc2c83e32
No known key found for this signature in database
21 changed files with 434 additions and 277 deletions

View File

@ -187,7 +187,6 @@ const kSupportedAlgorithms = {
'AES-CTR': 'AesKeyGenParams',
'AES-CBC': 'AesKeyGenParams',
'AES-GCM': 'AesKeyGenParams',
'AES-KW': 'AesKeyGenParams',
'HMAC': 'HmacKeyGenParams',
'Ed25519': null,
'X25519': null,
@ -202,7 +201,6 @@ const kSupportedAlgorithms = {
'AES-CTR': null,
'AES-CBC': null,
'AES-GCM': null,
'AES-KW': null,
'Ed25519': null,
'X25519': null,
},
@ -232,7 +230,6 @@ const kSupportedAlgorithms = {
'AES-CTR': null,
'AES-CBC': null,
'AES-GCM': null,
'AES-KW': null,
'Ed25519': null,
'X25519': null,
},
@ -258,48 +255,69 @@ const kSupportedAlgorithms = {
'AES-CBC': 'AesDerivedKeyParams',
'AES-CTR': 'AesDerivedKeyParams',
'AES-GCM': 'AesDerivedKeyParams',
'AES-KW': 'AesDerivedKeyParams',
'HMAC': 'HmacImportParams',
'HKDF': null,
'PBKDF2': null,
},
'wrapKey': {
'AES-KW': null,
},
'unwrapKey': {
'AES-KW': null,
},
'wrapKey': {},
'unwrapKey': {},
};
const experimentalAlgorithms = ObjectEntries({
'X448': {
generateKey: null,
importKey: null,
deriveBits: 'EcdhKeyDeriveParams',
exportKey: null,
},
'Ed448': {
generateKey: null,
sign: 'Ed448Params',
verify: 'Ed448Params',
importKey: null,
exportKey: null,
},
'cSHAKE128': { digest: 'CShakeParams' },
'cSHAKE256': { digest: 'CShakeParams' },
'SHA3-256': { digest: null },
'SHA3-384': { digest: null },
'SHA3-512': { digest: null },
'ChaCha20-Poly1305': {
'encrypt': 'AeadParams',
'decrypt': 'AeadParams',
'generateKey': null,
'importKey': null,
const conditionalAlgorithms = ObjectEntries({
'AES-KW': [{
'generateKey': 'AesKeyGenParams',
'exportKey': null,
'get key length': null,
},
'importKey': null,
'get key length': 'AesDerivedKeyParams',
'wrapKey': null,
'unwrapKey': null,
}, !process.features.openssl_is_boringssl],
});
for (let i = 0; i < conditionalAlgorithms.length; i++) {
if (conditionalAlgorithms[i][1][1]) {
const name = conditionalAlgorithms[i][0];
const ops = ObjectEntries(conditionalAlgorithms[i][1][0]);
for (let j = 0; j < ops.length; j++) {
const { 0: op, 1: dict } = ops[j];
kSupportedAlgorithms[op][name] = dict;
}
}
}
const experimentalAlgorithms = ObjectEntries({});
if (!process.features.openssl_is_boringssl) {
ArrayPrototypePush(experimentalAlgorithms,
['Ed448', {
generateKey: null,
sign: 'Ed448Params',
verify: 'Ed448Params',
importKey: null,
exportKey: null,
}],
['X448', {
generateKey: null,
importKey: null,
deriveBits: 'EcdhKeyDeriveParams',
exportKey: null,
}],
['cSHAKE128', { digest: 'CShakeParams' }],
['cSHAKE256', { digest: 'CShakeParams' }],
['ChaCha20-Poly1305', {
'encrypt': 'AeadParams',
'decrypt': 'AeadParams',
'generateKey': null,
'importKey': null,
'exportKey': null,
'get key length': null,
}],
['SHA3-256', { digest: null }],
['SHA3-384', { digest: null }],
['SHA3-512', { digest: null }],
);
}
for (const { 0: algorithm, 1: nid } of [
['ML-DSA-44', EVP_PKEY_ML_DSA_44],
['ML-DSA-65', EVP_PKEY_ML_DSA_65],

View File

@ -1,5 +1,7 @@
'use strict';
const common = require('../../common');
module.exports = function() {
const pkcs8 = {
'Ed25519': Buffer.from(
@ -37,7 +39,13 @@ module.exports = function() {
'025a2a5a572b9d23b0642f00', 'hex')
}
const algorithms = ['Ed25519', 'Ed448'];
const algorithms = ['Ed25519'];
if (!process.features.openssl_is_boringssl) {
algorithms.push('Ed448')
} else {
common.printSkipMessage(`Skipping unsupported Ed448 test cases`);
}
const vectors = algorithms.map((algorithm) => ({
publicKeyBuffer: spki[algorithm],

View File

@ -11,6 +11,8 @@ const [ECDH, X448, X25519] = await Promise.all([
subtle.generateKey('X25519', false, ['deriveBits', 'deriveKey']),
]);
const boringSSL = process.features.openssl_is_boringssl;
export const vectors = {
'encrypt': [
[false, 'Invalid'],
@ -79,7 +81,7 @@ export const vectors = {
[false, { name: 'AES-CBC', length: 25 }],
[true, { name: 'AES-GCM', length: 128 }],
[false, { name: 'AES-GCM', length: 25 }],
[true, { name: 'AES-KW', length: 128 }],
[!boringSSL, { name: 'AES-KW', length: 128 }],
[false, { name: 'AES-KW', length: 25 }],
[true, { name: 'HMAC', hash: 'SHA-256' }],
[true, { name: 'HMAC', hash: 'SHA-256', length: 256 }],
@ -189,7 +191,7 @@ export const vectors = {
[true, 'AES-CTR'],
[true, 'AES-CBC'],
[true, 'AES-GCM'],
[true, 'AES-KW'],
[!boringSSL, 'AES-KW'],
[true, { name: 'HMAC', hash: 'SHA-256' }],
[true, { name: 'HMAC', hash: 'SHA-256', length: 256 }],
[false, { name: 'HMAC', hash: 'SHA-256', length: 25 }],
@ -211,18 +213,18 @@ export const vectors = {
[true, 'AES-CTR'],
[true, 'AES-CBC'],
[true, 'AES-GCM'],
[true, 'AES-KW'],
[!boringSSL, 'AES-KW'],
[true, 'Ed25519'],
[true, 'X25519'],
],
'wrapKey': [
[false, 'AES-KW'],
[true, 'AES-KW', 'AES-CTR'],
[true, 'AES-KW', 'HMAC'],
[!boringSSL, 'AES-KW', 'AES-CTR'],
[!boringSSL, 'AES-KW', 'HMAC'],
],
'unwrapKey': [
[false, 'AES-KW'],
[true, 'AES-KW', 'AES-CTR'],
[!boringSSL, 'AES-KW', 'AES-CTR'],
],
'unsupported operation': [
[false, ''],

View File

@ -1,8 +1,27 @@
import * as crypto from 'node:crypto'
import { hasOpenSSL } from '../../common/crypto.js'
const pqc = hasOpenSSL(3, 5);
const shake128 = crypto.getHashes().includes('shake128');
const shake256 = crypto.getHashes().includes('shake256');
const chacha = crypto.getCiphers().includes('chacha20-poly1305');
export const vectors = {
'digest': [
[false, 'cSHAKE128'],
[shake128, { name: 'cSHAKE128', length: 128 }],
[shake128, { name: 'cSHAKE128', length: 128, functionName: Buffer.alloc(0), customization: Buffer.alloc(0) }],
[false, { name: 'cSHAKE128', length: 128, functionName: Buffer.alloc(1) }],
[false, { name: 'cSHAKE128', length: 128, customization: Buffer.alloc(1) }],
[false, { name: 'cSHAKE128', length: 127 }],
[false, 'cSHAKE256'],
[shake256, { name: 'cSHAKE256', length: 256 }],
[shake256, { name: 'cSHAKE256', length: 256, functionName: Buffer.alloc(0), customization: Buffer.alloc(0) }],
[false, { name: 'cSHAKE256', length: 256, functionName: Buffer.alloc(1) }],
[false, { name: 'cSHAKE256', length: 256, customization: Buffer.alloc(1) }],
[false, { name: 'cSHAKE256', length: 255 }],
],
'sign': [
[pqc, 'ML-DSA-44'],
[pqc, 'ML-DSA-65'],
@ -12,19 +31,19 @@ export const vectors = {
[pqc, 'ML-DSA-44'],
[pqc, 'ML-DSA-65'],
[pqc, 'ML-DSA-87'],
[true, 'ChaCha20-Poly1305'],
[chacha, 'ChaCha20-Poly1305'],
],
'importKey': [
[pqc, 'ML-DSA-44'],
[pqc, 'ML-DSA-65'],
[pqc, 'ML-DSA-87'],
[true, 'ChaCha20-Poly1305'],
[chacha, 'ChaCha20-Poly1305'],
],
'exportKey': [
[pqc, 'ML-DSA-44'],
[pqc, 'ML-DSA-65'],
[pqc, 'ML-DSA-87'],
[true, 'ChaCha20-Poly1305'],
[chacha, 'ChaCha20-Poly1305'],
],
'getPublicKey': [
[true, 'RSA-OAEP'],
@ -44,9 +63,9 @@ export const vectors = {
[false, 'ChaCha20-Poly1305'],
],
'encrypt': [
[true, { name: 'ChaCha20-Poly1305', iv: Buffer.alloc(12) }],
[chacha, { name: 'ChaCha20-Poly1305', iv: Buffer.alloc(12) }],
[false, { name: 'ChaCha20-Poly1305', iv: Buffer.alloc(16) }],
[true, { name: 'ChaCha20-Poly1305', iv: Buffer.alloc(12), tagLength: 128 }],
[chacha, { name: 'ChaCha20-Poly1305', iv: Buffer.alloc(12), tagLength: 128 }],
[false, { name: 'ChaCha20-Poly1305', iv: Buffer.alloc(12), tagLength: 64 }],
[false, 'ChaCha20-Poly1305'],
]

View File

@ -1,38 +1,41 @@
const { subtle } = globalThis.crypto;
const [X448, X25519] = await Promise.all([
subtle.generateKey('X448', false, ['deriveBits', 'deriveKey']),
subtle.generateKey('X25519', false, ['deriveBits', 'deriveKey']),
]);
const boringSSL = process.features.openssl_is_boringssl;
const X25519 = await subtle.generateKey('X25519', false, ['deriveBits', 'deriveKey']);
let X448;
if (!boringSSL) {
X448 = await subtle.generateKey('X448', false, ['deriveBits', 'deriveKey'])
}
export const vectors = {
'generateKey': [
[true, 'X448'],
[true, 'Ed448'],
[!boringSSL, 'X448'],
[!boringSSL, 'Ed448'],
],
'deriveKey': [
[true,
{ name: 'X448', public: X448.publicKey },
[!boringSSL,
{ name: 'X448', public: X448?.publicKey },
{ name: 'AES-CBC', length: 128 }],
[true,
{ name: 'X448', public: X448.publicKey },
[!boringSSL,
{ name: 'X448', public: X448?.publicKey },
{ name: 'HMAC', hash: 'SHA-256' }],
[true,
{ name: 'X448', public: X448.publicKey },
[!boringSSL,
{ name: 'X448', public: X448?.publicKey },
'HKDF'],
],
'deriveBits': [
[true, { name: 'X448', public: X448.publicKey }],
[!boringSSL, { name: 'X448', public: X448?.publicKey }],
[false, { name: 'X448', public: X25519.publicKey }],
[false, { name: 'X448', public: X448.privateKey }],
[false, { name: 'X448', public: X448?.privateKey }],
[false, 'X448'],
],
'importKey': [
[true, 'X448'],
[true, 'Ed448'],
[!boringSSL, 'X448'],
[!boringSSL, 'Ed448'],
],
'exportKey': [
[true, 'Ed448'],
[true, 'X448'],
[!boringSSL, 'Ed448'],
[!boringSSL, 'X448'],
],
};

View File

@ -1,84 +1,82 @@
const { subtle } = globalThis.crypto;
const boringSSL = process.features.openssl_is_boringssl;
const RSA_KEY_GEN = {
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1])
};
const [ECDH, X448, X25519] = await Promise.all([
const [ECDH, X25519] = await Promise.all([
subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, false, ['deriveBits', 'deriveKey']),
subtle.generateKey('X448', false, ['deriveBits', 'deriveKey']),
subtle.generateKey('X25519', false, ['deriveBits', 'deriveKey']),
]);
export const vectors = {
'digest': [
[true, 'SHA3-256'],
[true, 'SHA3-384'],
[true, 'SHA3-512'],
[!boringSSL, 'SHA3-256'],
[!boringSSL, 'SHA3-384'],
[!boringSSL, 'SHA3-512'],
],
'generateKey': [
[true, { name: 'HMAC', hash: 'SHA3-256' }],
[true, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
[!boringSSL, { name: 'HMAC', hash: 'SHA3-256' }],
[!boringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
[false, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
[true, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA3-256', ...RSA_KEY_GEN }],
[true, { name: 'RSA-PSS', hash: 'SHA3-256', ...RSA_KEY_GEN }],
[true, { name: 'RSA-OAEP', hash: 'SHA3-256', ...RSA_KEY_GEN }],
[true, { name: 'HMAC', hash: 'SHA3-256' }],
[true, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
[!boringSSL, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA3-256', ...RSA_KEY_GEN }],
[!boringSSL, { name: 'RSA-PSS', hash: 'SHA3-256', ...RSA_KEY_GEN }],
[!boringSSL, { name: 'RSA-OAEP', hash: 'SHA3-256', ...RSA_KEY_GEN }],
[!boringSSL, { name: 'HMAC', hash: 'SHA3-256' }],
[!boringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
[false, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
[false, { name: 'HMAC', hash: 'SHA3-256', length: 0 }],
],
'deriveKey': [
[true,
[!boringSSL,
{ name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) },
{ name: 'AES-CBC', length: 128 }],
[true,
[!boringSSL,
{ name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) },
{ name: 'HMAC', hash: 'SHA3-256' }],
[false,
{ name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) },
'HKDF'],
[true,
[!boringSSL,
{ name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 },
{ name: 'AES-CBC', length: 128 }],
[true,
[!boringSSL,
{ name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 },
{ name: 'HMAC', hash: 'SHA3-256' }],
[false,
{ name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 },
'HKDF'],
[true,
[!boringSSL,
{ name: 'X25519', public: X25519.publicKey },
{ name: 'HMAC', hash: 'SHA3-256' }],
[true,
{ name: 'X448', public: X448.publicKey },
{ name: 'HMAC', hash: 'SHA3-256' }],
[true,
[!boringSSL,
{ name: 'ECDH', public: ECDH.publicKey },
{ name: 'HMAC', hash: 'SHA3-256' }],
],
'deriveBits': [
[true, { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 8],
[true, { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 0],
[!boringSSL, { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 8],
[!boringSSL, { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 0],
[false, { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, null],
[false, { name: 'HKDF', hash: 'SHA3-256', salt: Buffer.alloc(0), info: Buffer.alloc(0) }, 7],
[true, { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 }, 8],
[true, { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 }, 0],
[!boringSSL, { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 }, 8],
[!boringSSL, { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 }, 0],
[false, { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 0 }, 8],
[false, { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 }, null],
[false, { name: 'PBKDF2', hash: 'SHA3-256', salt: Buffer.alloc(0), iterations: 1 }, 7],
],
'importKey': [
[true, { name: 'HMAC', hash: 'SHA3-256' }],
[true, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
[!boringSSL, { name: 'HMAC', hash: 'SHA3-256' }],
[!boringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
[false, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
[true, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA3-256', ...RSA_KEY_GEN }],
[true, { name: 'RSA-PSS', hash: 'SHA3-256', ...RSA_KEY_GEN }],
[true, { name: 'RSA-OAEP', hash: 'SHA3-256', ...RSA_KEY_GEN }],
[true, { name: 'HMAC', hash: 'SHA3-256' }],
[true, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
[!boringSSL, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA3-256', ...RSA_KEY_GEN }],
[!boringSSL, { name: 'RSA-PSS', hash: 'SHA3-256', ...RSA_KEY_GEN }],
[!boringSSL, { name: 'RSA-OAEP', hash: 'SHA3-256', ...RSA_KEY_GEN }],
[!boringSSL, { name: 'HMAC', hash: 'SHA3-256' }],
[!boringSSL, { name: 'HMAC', hash: 'SHA3-256', length: 256 }],
[false, { name: 'HMAC', hash: 'SHA3-256', length: 25 }],
[false, { name: 'HMAC', hash: 'SHA3-256', length: 0 }],
],

View File

@ -18,19 +18,26 @@ const kTests = [
'64ea51fae5b3307cfe9706',
result: '2768409dfab99ec23b8c89b93ff5880295f76176088f89e43dfebe7ea1950008'
},
{
name: 'X448',
size: 56,
pkcs8: '3046020100300506032b656f043a043858c7d29a3eb519b29d00cfb191bb64fc6' +
];
if (!process.features.openssl_is_boringssl) {
kTests.push(
{
name: 'X448',
size: 56,
pkcs8: '3046020100300506032b656f043a043858c7d29a3eb519b29d00cfb191bb64fc6' +
'd8a42d8f17176272b89f2272d1819295c6525c0829671b052ef0727530f188e31' +
'd0cc53bf26929e',
spki: '3042300506032b656f033900b604a1d1a5cd1d9426d561ef630a9eb16cbe69d5b9' +
spki: '3042300506032b656f033900b604a1d1a5cd1d9426d561ef630a9eb16cbe69d5b9' +
'ca615edc53633efb52ea31e6e6a0a1dbacc6e76cbce6482d7e4ba3d55d9e802765' +
'ce6f',
result: 'f0f6c5f17f94f4291eab7178866d37ec8906dd6c514143dc85be7cf28deff39b' +
result: 'f0f6c5f17f94f4291eab7178866d37ec8906dd6c514143dc85be7cf28deff39b' +
'726e0f6dcf810eb594dca97b4882bd44c43ea7dc67f49a4e',
},
];
}
);
} else {
common.printSkipMessage('Skipping unsupported X448 test case');
}
async function prepareKeys() {
const keys = {};
@ -153,9 +160,9 @@ async function prepareKeys() {
// Missing public property
await assert.rejects(
subtle.deriveBits(
{ name: 'X448' },
keys.X448.privateKey,
8 * keys.X448.size),
{ name: 'X25519' },
keys.X25519.privateKey,
8 * keys.X25519.size),
{ code: 'ERR_MISSING_OPTION' });
}
@ -164,15 +171,15 @@ async function prepareKeys() {
await assert.rejects(
subtle.deriveBits(
{
name: 'X448',
name: 'X25519',
public: { message: 'Not a CryptoKey' }
},
keys.X448.privateKey,
8 * keys.X448.size),
keys.X25519.privateKey,
8 * keys.X25519.size),
{ code: 'ERR_INVALID_ARG_TYPE' });
}
{
if (keys.X25519 && keys.X448) {
// Mismatched types
await assert.rejects(
subtle.deriveBits(
@ -188,9 +195,9 @@ async function prepareKeys() {
{
// Base key is not a private key
await assert.rejects(subtle.deriveBits({
name: 'X448',
public: keys.X448.publicKey
}, keys.X448.publicKey, null), {
name: 'X25519',
public: keys.X25519.publicKey
}, keys.X25519.publicKey, null), {
name: 'InvalidAccessError'
});
}
@ -198,9 +205,9 @@ async function prepareKeys() {
{
// Base key is not a private key
await assert.rejects(subtle.deriveBits({
name: 'X448',
public: keys.X448.privateKey
}, keys.X448.publicKey, null), {
name: 'X25519',
public: keys.X25519.privateKey
}, keys.X25519.publicKey, null), {
name: 'InvalidAccessError'
});
}
@ -215,9 +222,9 @@ async function prepareKeys() {
false, ['encrypt']);
await assert.rejects(subtle.deriveBits({
name: 'X448',
name: 'X25519',
public: key
}, keys.X448.publicKey, null), {
}, keys.X25519.publicKey, null), {
name: 'InvalidAccessError'
});
}

View File

@ -19,17 +19,24 @@ const kDerivedKeyTypes = [
['AES-CTR', 256, undefined, 'encrypt', 'decrypt'],
['AES-GCM', 128, undefined, 'encrypt', 'decrypt'],
['AES-GCM', 256, undefined, 'encrypt', 'decrypt'],
['AES-KW', 128, undefined, 'wrapKey', 'unwrapKey'],
['AES-KW', 256, undefined, 'wrapKey', 'unwrapKey'],
['HMAC', 256, 'SHA-1', 'sign', 'verify'],
['HMAC', 256, 'SHA-256', 'sign', 'verify'],
['HMAC', 256, 'SHA-384', 'sign', 'verify'],
['HMAC', 256, 'SHA-512', 'sign', 'verify'],
['HMAC', 256, 'SHA3-256', 'sign', 'verify'],
['HMAC', 256, 'SHA3-384', 'sign', 'verify'],
['HMAC', 256, 'SHA3-512', 'sign', 'verify'],
];
if (!process.features.openssl_is_boringssl) {
kDerivedKeyTypes.push(
['AES-KW', 128, undefined, 'wrapKey', 'unwrapKey'],
['AES-KW', 256, undefined, 'wrapKey', 'unwrapKey'],
['HMAC', 256, 'SHA3-256', 'sign', 'verify'],
['HMAC', 256, 'SHA3-384', 'sign', 'verify'],
['HMAC', 256, 'SHA3-512', 'sign', 'verify'],
);
} else {
common.printSkipMessage('Skipping unsupported AES-KW test cases');
}
const kDerivedKeys = {
short: '5040737377307264',
long: '55736572732073686f756c64207069636b206c6f6e6720706173737068726' +

View File

@ -123,8 +123,10 @@ const { subtle } = globalThis.crypto;
assert.deepStrictEqual(secret1, secret2);
}
test('X25519').then(common.mustCall());
if (!process.features.openssl_is_boringssl) {
test('X25519').then(common.mustCall());
test('X448').then(common.mustCall());
} else {
common.printSkipMessage('Skipping unsupported X448 test case');
}
}

View File

@ -18,18 +18,25 @@ const kTests = [
'64ea51fae5b3307cfe9706',
result: '2768409dfab99ec23b8c89b93ff5880295f76176088f89e43dfebe7ea1950008'
},
{
name: 'X448',
size: 56,
pkcs8: '3046020100300506032b656f043a043858c7d29a3eb519b29d00cfb191bb64fc6' +
];
if (!process.features.openssl_is_boringssl) {
kTests.push(
{
name: 'X448',
size: 56,
pkcs8: '3046020100300506032b656f043a043858c7d29a3eb519b29d00cfb191bb64fc6' +
'd8a42d8f17176272b89f2272d1819295c6525c0829671b052ef0727530f188e31' +
'd0cc53bf26929e',
spki: '3042300506032b656f033900b604a1d1a5cd1d9426d561ef630a9eb16cbe69d5b9' +
spki: '3042300506032b656f033900b604a1d1a5cd1d9426d561ef630a9eb16cbe69d5b9' +
'ca615edc53633efb52ea31e6e6a0a1dbacc6e76cbce6482d7e4ba3d55d9e802765' +
'ce6f',
result: 'f0f6c5f17f94f4291eab7178866d37ec8906dd6c514143dc85be7cf28deff39b'
},
];
result: 'f0f6c5f17f94f4291eab7178866d37ec8906dd6c514143dc85be7cf28deff39b'
},
);
} else {
common.printSkipMessage('Skipping unsupported X448 test case');
}
async function prepareKeys() {
const keys = {};
@ -107,8 +114,8 @@ async function prepareKeys() {
// Missing public property
await assert.rejects(
subtle.deriveKey(
{ name: 'X448' },
keys.X448.privateKey,
{ name: 'X25519' },
keys.X25519.privateKey,
...otherArgs),
{ code: 'ERR_MISSING_OPTION' });
}
@ -118,15 +125,15 @@ async function prepareKeys() {
await assert.rejects(
subtle.deriveKey(
{
name: 'X448',
name: 'X25519',
public: { message: 'Not a CryptoKey' }
},
keys.X448.privateKey,
keys.X25519.privateKey,
...otherArgs),
{ code: 'ERR_INVALID_ARG_TYPE' });
}
{
if (keys.X25519 && keys.X448) {
// Mismatched named curves
await assert.rejects(
subtle.deriveKey(
@ -144,10 +151,10 @@ async function prepareKeys() {
await assert.rejects(
subtle.deriveKey(
{
name: 'X448',
public: keys.X448.publicKey
name: 'X25519',
public: keys.X25519.publicKey
},
keys.X448.publicKey,
keys.X25519.publicKey,
...otherArgs),
{ name: 'InvalidAccessError' });
}
@ -157,10 +164,10 @@ async function prepareKeys() {
await assert.rejects(
subtle.deriveKey(
{
name: 'X448',
public: keys.X448.privateKey
name: 'X25519',
public: keys.X25519.privateKey
},
keys.X448.privateKey,
keys.X25519.privateKey,
...otherArgs),
{ name: 'InvalidAccessError' });
}
@ -177,10 +184,10 @@ async function prepareKeys() {
await assert.rejects(
subtle.deriveKey(
{
name: 'X448',
name: 'X25519',
public: key
},
keys.X448.publicKey,
keys.X25519.publicKey,
...otherArgs),
{ name: 'InvalidAccessError' });
}

View File

@ -81,14 +81,21 @@ const { KeyObject } = require('crypto');
'e36cf2cf943d8f3a88adb80f478745c336ac811b1a86d03a7d10eb0b6b52295c'],
['hello', 'there', 'my friend', 'SHA-512',
'1e42d43fcacba361716f65853bd5f3c479f679612f0180eab3c51ed6c9d2b47d'],
['hello', 'there', 'my friend', 'SHA3-256',
'2a49a3b6fb219117af9e251c6c65f16600cbca13bd0be6e70d96b0b9fa4cf3fd'],
['hello', 'there', 'my friend', 'SHA3-384',
'0437bb59b95f2db2c7684c0b439028cb0fdd6f0f5d03b9f489066a87ae147221'],
['hello', 'there', 'my friend', 'SHA3-512',
'3bbc469d38214371921e52c6f147e96cb7eb370421a81f53dea8b4851dfb8bce'],
];
if (!process.features.openssl_is_boringssl) {
kTests.push(
['hello', 'there', 'my friend', 'SHA3-256',
'2a49a3b6fb219117af9e251c6c65f16600cbca13bd0be6e70d96b0b9fa4cf3fd'],
['hello', 'there', 'my friend', 'SHA3-384',
'0437bb59b95f2db2c7684c0b439028cb0fdd6f0f5d03b9f489066a87ae147221'],
['hello', 'there', 'my friend', 'SHA3-512',
'3bbc469d38214371921e52c6f147e96cb7eb370421a81f53dea8b4851dfb8bce'],
);
} else {
common.printSkipMessage('Skipping unsupported SHA-3 test cases');
}
const tests = Promise.all(kTests.map((args) => test(...args)));
tests.then(common.mustCall());
@ -239,8 +246,10 @@ const { KeyObject } = require('crypto');
assert.deepStrictEqual(raw1, raw2);
}
test('X25519').then(common.mustCall());
if (!process.features.openssl_is_boringssl) {
test('X25519').then(common.mustCall());
test('X448').then(common.mustCall());
} else {
common.printSkipMessage('Skipping unsupported X448 test case');
}
}

View File

@ -8,20 +8,27 @@ if (!common.hasCrypto)
const assert = require('assert');
const { Buffer } = require('buffer');
const { subtle } = globalThis.crypto;
const { createHash } = require('crypto');
const { createHash, getHashes } = require('crypto');
const kTests = [
['SHA-1', ['sha1'], 160],
['SHA-256', ['sha256'], 256],
['SHA-384', ['sha384'], 384],
['SHA-512', ['sha512'], 512],
['SHA3-256', ['sha3-256'], 256],
['SHA3-384', ['sha3-384'], 384],
['SHA3-512', ['sha3-512'], 512],
[{ name: 'cSHAKE128', length: 256 }, ['shake128', { outputLength: 256 >> 3 }], 256],
[{ name: 'cSHAKE256', length: 512 }, ['shake256', { outputLength: 512 >> 3 }], 512],
];
if (!process.features.openssl_is_boringssl) {
kTests.push(
[{ name: 'cSHAKE128', length: 256 }, ['shake128', { outputLength: 256 >> 3 }], 256],
[{ name: 'cSHAKE256', length: 512 }, ['shake256', { outputLength: 512 >> 3 }], 512],
['SHA3-256', ['sha3-256'], 256],
['SHA3-384', ['sha3-384'], 384],
['SHA3-512', ['sha3-512'], 512],
);
} else {
common.printSkipMessage('Skipping unsupported test cases');
}
// Empty hash just works, not checking result
subtle.digest('SHA-512', Buffer.alloc(0))
.then(common.mustCall());
@ -247,14 +254,16 @@ function applyXOF(name) {
})().then(common.mustCall());
// CShake edge cases
(async () => {
assert.deepStrictEqual(
new Uint8Array(await subtle.digest({ name: 'cSHAKE128', length: 0 }, Buffer.alloc(1))),
new Uint8Array(0),
);
if (getHashes().includes('shake128')) {
(async () => {
assert.deepStrictEqual(
new Uint8Array(await subtle.digest({ name: 'cSHAKE128', length: 0 }, Buffer.alloc(1))),
new Uint8Array(0),
);
await assert.rejects(subtle.digest({ name: 'cSHAKE128', length: 7 }, Buffer.alloc(1)), {
name: 'NotSupportedError',
message: 'Unsupported CShakeParams length',
});
})().then(common.mustCall());
await assert.rejects(subtle.digest({ name: 'cSHAKE128', length: 7 }, Buffer.alloc(1)), {
name: 'NotSupportedError',
message: 'Unsupported CShakeParams length',
});
})().then(common.mustCall());
}

View File

@ -57,7 +57,7 @@ const { subtle } = globalThis.crypto;
}
// Test Encrypt/Decrypt RSA-OAEP w/ SHA-3
{
if (!process.features.openssl_is_boringssl) {
const buf = globalThis.crypto.getRandomValues(new Uint8Array(50));
async function test() {

View File

@ -87,23 +87,30 @@ const testVectors = [
privateUsages: ['sign'],
publicUsages: ['verify']
},
{
name: 'Ed448',
privateUsages: ['sign'],
publicUsages: ['verify']
},
{
name: 'X25519',
privateUsages: ['deriveKey', 'deriveBits'],
publicUsages: []
},
{
name: 'X448',
privateUsages: ['deriveKey', 'deriveBits'],
publicUsages: []
},
];
if (!process.features.openssl_is_boringssl) {
testVectors.push(
{
name: 'Ed448',
privateUsages: ['sign'],
publicUsages: ['verify']
},
{
name: 'X448',
privateUsages: ['deriveKey', 'deriveBits'],
publicUsages: []
},
);
} else {
common.printSkipMessage('Skipping unsupported Curve448 test cases');
}
async function testImportSpki({ name, publicUsages }, extractable) {
const key = await subtle.importKey(
'spki',

View File

@ -17,11 +17,18 @@ const hashes = [
'SHA-256',
'SHA-384',
'SHA-512',
'SHA3-256',
'SHA3-384',
'SHA3-512',
];
if (!process.features.openssl_is_boringssl) {
hashes.push(
'SHA3-256',
'SHA3-384',
'SHA3-512',
);
} else {
common.printSkipMessage('Skipping unsupported SHA-3 test cases');
}
const keyData = {
1024: {
spki: Buffer.from(

View File

@ -295,7 +295,7 @@ const { createPrivateKey, createPublicKey, createSecretKey } = require('crypto')
}
// SHA-3 hashes and JWK "alg"
{
if (!process.features.openssl_is_boringssl) {
const rsa = fixtures.readKey('rsa_private_2048.pem');
const privateKey = createPrivateKey(rsa);
const publicKey = createPublicKey(privateKey);

View File

@ -59,23 +59,6 @@ const vectors = {
'unwrapKey',
],
},
'ChaCha20-Poly1305': {
result: 'CryptoKey',
usages: [
'encrypt',
'decrypt',
'wrapKey',
'unwrapKey',
],
},
'AES-KW': {
algorithm: { length: 256 },
result: 'CryptoKey',
usages: [
'wrapKey',
'unwrapKey',
],
},
'HMAC': {
algorithm: { length: 256, hash: 'SHA-256' },
result: 'CryptoKey',
@ -145,13 +128,6 @@ const vectors = {
'verify',
],
},
'Ed448': {
result: 'CryptoKeyPair',
usages: [
'sign',
'verify',
],
},
'X25519': {
result: 'CryptoKeyPair',
usages: [
@ -159,14 +135,43 @@ const vectors = {
'deriveBits',
],
},
'X448': {
};
if (!process.features.openssl_is_boringssl) {
vectors.Ed448 = {
result: 'CryptoKeyPair',
usages: [
'sign',
'verify',
],
};
vectors.X448 = {
result: 'CryptoKeyPair',
usages: [
'deriveKey',
'deriveBits',
],
},
};
};
vectors['AES-KW'] = {
algorithm: { length: 256 },
result: 'CryptoKey',
usages: [
'wrapKey',
'unwrapKey',
],
};
vectors['ChaCha20-Poly1305'] = {
result: 'CryptoKey',
usages: [
'encrypt',
'decrypt',
'wrapKey',
'unwrapKey',
],
};
} else {
common.printSkipMessage('Skipping unsupported test cases');
}
if (hasOpenSSL(3, 5)) {
for (const name of ['ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87']) {
@ -422,16 +427,24 @@ if (hasOpenSSL(3, 5)) {
['sign'],
['verify'],
],
[
'RSA-OAEP',
1024,
Buffer.from([3]),
'SHA3-256',
['decrypt', 'unwrapKey'],
['encrypt', 'wrapKey'],
],
];
if (!process.features.openssl_is_boringssl) {
kTests.push(
[
'RSA-OAEP',
1024,
Buffer.from([3]),
'SHA3-256',
['decrypt', 'unwrapKey'],
['encrypt', 'wrapKey'],
],
);
} else {
common.printSkipMessage('Skipping unsupported SHA-3 test case');
}
const tests = kTests.map((args) => test(...args));
Promise.all(tests).then(common.mustCall());
@ -564,10 +577,17 @@ if (hasOpenSSL(3, 5)) {
[ 'AES-CBC', 256, ['encrypt', 'decrypt']],
[ 'AES-GCM', 128, ['encrypt', 'decrypt']],
[ 'AES-GCM', 256, ['encrypt', 'decrypt']],
[ 'AES-KW', 128, ['wrapKey', 'unwrapKey']],
[ 'AES-KW', 256, ['wrapKey', 'unwrapKey']],
];
if (!process.features.openssl_is_boringssl) {
kTests.push(
[ 'AES-KW', 128, ['wrapKey', 'unwrapKey']],
[ 'AES-KW', 256, ['wrapKey', 'unwrapKey']],
);
} else {
common.printSkipMessage('Skipping unsupported AES-KW test cases');
}
const tests = Promise.all(kTests.map((args) => test(...args)));
tests.then(common.mustCall());
@ -620,13 +640,21 @@ if (hasOpenSSL(3, 5)) {
[ undefined, 'SHA-256', ['sign', 'verify']],
[ undefined, 'SHA-384', ['sign', 'verify']],
[ undefined, 'SHA-512', ['sign', 'verify']],
[ undefined, 'SHA3-256', ['sign', 'verify']],
[ undefined, 'SHA3-384', ['sign', 'verify']],
[ undefined, 'SHA3-512', ['sign', 'verify']],
[ 128, 'SHA-256', ['sign', 'verify']],
[ 1024, 'SHA-512', ['sign', 'verify']],
];
if (!process.features.openssl_is_boringssl) {
kTests.push(
[ undefined, 'SHA3-256', ['sign', 'verify']],
[ undefined, 'SHA3-384', ['sign', 'verify']],
[ undefined, 'SHA3-512', ['sign', 'verify']],
);
} else {
common.printSkipMessage('Skipping unsupported SHA-3 test cases');
}
const tests = Promise.all(kTests.map((args) => test(...args)));
tests.then(common.mustCall());
@ -684,23 +712,30 @@ assert.throws(() => new CryptoKey(), { code: 'ERR_ILLEGAL_CONSTRUCTOR' });
['sign'],
['verify'],
],
[
'Ed448',
['sign'],
['verify'],
],
[
'X25519',
['deriveKey', 'deriveBits'],
[],
],
[
'X448',
['deriveKey', 'deriveBits'],
[],
],
];
if (!process.features.openssl_is_boringssl) {
kTests.push(
[
'Ed448',
['sign'],
['verify'],
],
[
'X448',
['deriveKey', 'deriveBits'],
[],
],
);
} else {
common.printSkipMessage('Skipping unsupported Curve448 test cases');
}
const tests = kTests.map((args) => test(...args));
Promise.all(tests).then(common.mustCall());

View File

@ -219,7 +219,7 @@ async function testSign({ name,
})().then(common.mustCall());
// Ed448 context
{
if (!process.features.openssl_is_boringssl) {
const vector = vectors.find(({ name }) => name === 'Ed448');
Promise.all([
subtle.importKey(
@ -247,4 +247,6 @@ async function testSign({ name,
message: /Non zero-length Ed448Params\.context is not supported/
});
}).then(common.mustCall());
} else {
common.printSkipMessage('Skipping unsupported Ed448 test case');
}

View File

@ -245,7 +245,9 @@ async function testSaltLength(keyLength, hash, hLen) {
['SHA3-384', 48],
['SHA3-512', 64],
]) {
variations.push(testSaltLength(keyLength, hash, hLen));
if (hash.startsWith('SHA-3') && !process.features.openssl_is_boringssl) {
variations.push(testSaltLength(keyLength, hash, hLen));
}
}
}

View File

@ -123,13 +123,11 @@ const { subtle } = globalThis.crypto;
name: 'Ed25519',
}, publicKey, signature, ec.encode(data)));
}
if (!process.features.openssl_is_boringssl) {
test('hello world').then(common.mustCall());
}
test('hello world').then(common.mustCall());
}
// Test Sign/Verify Ed448
{
if (!process.features.openssl_is_boringssl) {
async function test(data) {
const ec = new TextEncoder();
const { publicKey, privateKey } = await subtle.generateKey({
@ -145,9 +143,9 @@ const { subtle } = globalThis.crypto;
}, publicKey, signature, ec.encode(data)));
}
if (!process.features.openssl_is_boringssl) {
test('hello world').then(common.mustCall());
}
test('hello world').then(common.mustCall());
} else {
common.printSkipMessage('Skipping unsupported Ed448 test case');
}
// Test Sign/Verify ML-DSA

View File

@ -39,20 +39,25 @@ const kWrappingData = {
},
pair: false
},
'ChaCha20-Poly1305': {
};
if (!process.features.openssl_is_boringssl) {
kWrappingData['AES-KW'] = {
generate: { length: 128 },
wrap: { },
pair: false
};
kWrappingData['ChaCha20-Poly1305'] = {
wrap: {
iv: new Uint8Array(12),
additionalData: new Uint8Array(16),
tagLength: 128
},
pair: false
},
'AES-KW': {
generate: { length: 128 },
wrap: { },
pair: false
}
};
};
} else {
common.printSkipMessage('Skipping unsupported AES-KW test case');
}
function generateWrappingKeys() {
return Promise.all(Object.keys(kWrappingData).map(async (name) => {
@ -131,14 +136,6 @@ async function generateKeysToWrap() {
publicUsages: ['verify'],
pair: true,
},
{
algorithm: {
name: 'Ed448',
},
privateUsages: ['sign'],
publicUsages: ['verify'],
pair: true,
},
{
algorithm: {
name: 'X25519',
@ -147,14 +144,6 @@ async function generateKeysToWrap() {
publicUsages: [],
pair: true,
},
{
algorithm: {
name: 'X448',
},
privateUsages: ['deriveBits'],
publicUsages: [],
pair: true,
},
{
algorithm: {
name: 'AES-CTR',
@ -185,14 +174,6 @@ async function generateKeysToWrap() {
usages: ['encrypt', 'decrypt'],
pair: false,
},
{
algorithm: {
name: 'AES-KW',
length: 128
},
usages: ['wrapKey', 'unwrapKey'],
pair: false,
},
{
algorithm: {
name: 'HMAC',
@ -204,6 +185,19 @@ async function generateKeysToWrap() {
},
];
if (!process.features.openssl_is_boringssl) {
parameters.push({
algorithm: {
name: 'AES-KW',
length: 128
},
usages: ['wrapKey', 'unwrapKey'],
pair: false,
});
} else {
common.printSkipMessage('Skipping unsupported AES-KW test case');
}
if (hasOpenSSL(3, 5)) {
for (const name of ['ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87']) {
parameters.push({
@ -215,6 +209,29 @@ async function generateKeysToWrap() {
}
}
if (!process.features.openssl_is_boringssl) {
parameters.push(
{
algorithm: {
name: 'Ed448',
},
privateUsages: ['sign'],
publicUsages: ['verify'],
pair: true,
},
{
algorithm: {
name: 'X448',
},
privateUsages: ['deriveBits'],
publicUsages: [],
pair: true,
},
);
} else {
common.printSkipMessage('Skipping unsupported Curve test cases');
}
const allkeys = await Promise.all(parameters.map(async (params) => {
const usages = 'usages' in params ?
params.usages :