Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5a33a82
test: update WPT resources, interfaces and WebCryptoAPI
nodejs-github-bot Mar 22, 2026
6b28cd5
crypto: add TurboSHAKE and KangarooTwelve Web Cryptography algorithms
panva Mar 10, 2026
790969f
crypto: unify asymmetric key import through KeyObjectHandle::Init
panva Mar 31, 2026
b5a6c71
crypto: accept key data in crypto.diffieHellman() and cleanup DH jobs
panva Mar 30, 2026
1882595
crypto: align key argument names in docs and error messages
panva Apr 7, 2026
b7f9292
crypto: guard against size_t overflow on experimental 32-bit arch
panva Apr 9, 2026
d25f5a5
crypto: add JWK support for ML-KEM and SLH-DSA key types
panva Apr 21, 2026
8e0b3d0
crypto: reject duplicate ML-KEM JWK key_ops
panva Apr 25, 2026
7250959
crypto: add guards and adjust tests for BoringSSL
panva Apr 22, 2026
f040f6c
src: decouple KeyObject and CryptoKey and move CryptoKey to src
panva Apr 24, 2026
87bc18f
crypto: harden KeyObject internal slots
panva May 4, 2026
c882aee
crypto: harden CryptoKey algorithm slots
panva May 4, 2026
54c951e
tools: prevent lib code from reading KeyObject and CryptoKey accessors
panva May 4, 2026
f1986d7
src: add BoringSSL EVP enumeration fallback
panva May 5, 2026
deb2ea8
src: simplify OpenSSL feature gates
panva May 5, 2026
a355fd2
crypto: wire AES-KW in Web Cryptography when using BoringSSL
panva Apr 22, 2026
e1d941d
crypto: wire ChaCha20-Poly1305 in Web Cryptography when using BoringSSL
panva Apr 22, 2026
20811d6
crypto: wire ML-DSA and ML-KEM for use when using BoringSSL
panva May 8, 2026
5760769
crypto: add WebCrypto CryptoJob mode
panva Apr 25, 2026
bc95785
crypto: remove async from WebCrypto methods
panva Apr 25, 2026
b3be75f
crypto: pass CryptoKey handles to KDF jobs
panva Apr 26, 2026
db4f5b9
crypto: harden WebCrypto against prototype pollution
panva May 16, 2026
b310759
test: update WPT for WebCryptoAPI to 97bbc7247a
nodejs-github-bot May 20, 2026
13074f1
lib: refactor internal webidl converters
panva May 4, 2026
809ccfc
crypto: strengthen argument CHECKs in TurboSHAKE
tniessen Apr 17, 2026
1455de4
lib: cleanup stateless diffiehellman key handling
panva Apr 12, 2026
46e6035
test: update tls/crypto behaviour expectations when using BoringSSL
panva May 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions benchmark/crypto/create-keyobject.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const keyFixtures = {

if (hasOpenSSL(3, 5)) {
keyFixtures['ml-dsa-44'] = readKeyPair('ml_dsa_44_public', 'ml_dsa_44_private');
} else if (process.features.openssl_is_boringssl) {
keyFixtures['ml-dsa-44'] = readKeyPair('ml_dsa_44_public', 'ml_dsa_44_private_seed_only');
}

const bench = common.createBenchmark(main, {
Expand Down
6 changes: 3 additions & 3 deletions benchmark/crypto/kem.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ if (hasOpenSSL(3, 5)) {
keyFixtures['ml-kem-512'] = readKeyPair('ml_kem_512_public', 'ml_kem_512_private');
keyFixtures['ml-kem-768'] = readKeyPair('ml_kem_768_public', 'ml_kem_768_private');
keyFixtures['ml-kem-1024'] = readKeyPair('ml_kem_1024_public', 'ml_kem_1024_private');
} else if (process.features.openssl_is_boringssl) {
keyFixtures['ml-kem-768'] = readKeyPair('ml_kem_768_public', 'ml_kem_768_private_seed_only');
keyFixtures['ml-kem-1024'] = readKeyPair('ml_kem_1024_public', 'ml_kem_1024_private_seed_only');
}
if (hasOpenSSL(3, 2)) {
keyFixtures['p-256'] = readKeyPair('ec_p256_public', 'ec_p256_private');
Expand Down Expand Up @@ -54,9 +57,6 @@ const bench = common.createBenchmark(main, {
// assess whether mutexes over the key material impact the operation
if (p.keyFormat === 'keyObject.unique')
return p.mode === 'async-parallel';
// JWK is not supported for ml-kem for now
if (p.keyFormat === 'jwk')
return !p.keyType.startsWith('ml-');
// raw-public is only supported for encapsulate, not rsa
if (p.keyFormat === 'raw-public')
return p.keyType !== 'rsa' && p.op === 'encapsulate';
Expand Down
2 changes: 2 additions & 0 deletions benchmark/crypto/oneshot-sign.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const keyFixtures = {

if (hasOpenSSL(3, 5)) {
keyFixtures['ml-dsa-44'] = readKey('ml_dsa_44_private');
} else if (process.features.openssl_is_boringssl) {
keyFixtures['ml-dsa-44'] = readKey('ml_dsa_44_private_seed_only');
}

const data = crypto.randomBytes(256);
Expand Down
2 changes: 2 additions & 0 deletions benchmark/crypto/oneshot-verify.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const keyFixtures = {

if (hasOpenSSL(3, 5)) {
keyFixtures['ml-dsa-44'] = readKeyPair('ml_dsa_44_public', 'ml_dsa_44_private');
} else if (process.features.openssl_is_boringssl) {
keyFixtures['ml-dsa-44'] = readKeyPair('ml_dsa_44_public', 'ml_dsa_44_private_seed_only');
}

const data = crypto.randomBytes(256);
Expand Down
97 changes: 97 additions & 0 deletions benchmark/crypto/webcrypto-sign.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
'use strict';

const common = require('../common.js');
const { hasOpenSSL } = require('../../test/common/crypto.js');
const { subtle } = globalThis.crypto;

const kAlgorithms = {
'ec': { name: 'ECDSA', namedCurve: 'P-256' },
'rsassa-pkcs1-v1_5': {
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256',
},
'rsa-pss': {
name: 'RSA-PSS',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256',
},
'ed25519': { name: 'Ed25519' },
};

if (hasOpenSSL(3, 5)) {
kAlgorithms['ml-dsa-44'] = { name: 'ML-DSA-44' };
}

const kSignParams = {
'ec': { name: 'ECDSA', hash: 'SHA-256' },
'rsassa-pkcs1-v1_5': { name: 'RSASSA-PKCS1-v1_5' },
'rsa-pss': { name: 'RSA-PSS', saltLength: 32 },
'ed25519': { name: 'Ed25519' },
'ml-dsa-44': { name: 'ML-DSA-44' },
};

const data = globalThis.crypto.getRandomValues(new Uint8Array(256));

let keys;

const bench = common.createBenchmark(main, {
keyType: Object.keys(kAlgorithms),
mode: ['serial', 'parallel'],
keyReuse: ['shared', 'unique'],
n: [1e3],
}, {
combinationFilter(p) {
// Unique only differs from shared when operations overlap (parallel);
// sequential calls have no contention so unique+serial adds no value.
if (p.keyReuse === 'unique') return p.mode === 'parallel';
return true;
},
});

async function measureSerial(n, signParams, sharedKey) {
bench.start();
for (let i = 0; i < n; ++i) {
await subtle.sign(signParams, sharedKey || keys[i], data);
}
bench.end(n);
}

async function measureParallel(n, signParams, sharedKey) {
const promises = new Array(n);
bench.start();
for (let i = 0; i < n; ++i) {
promises[i] = subtle.sign(signParams, sharedKey || keys[i], data);
}
await Promise.all(promises);
bench.end(n);
}

async function main({ n, mode, keyReuse, keyType }) {
const algorithm = kAlgorithms[keyType];
const signParams = kSignParams[keyType];

if (!keys || keys.length !== n || keys[0].algorithm.name !== signParams.name) {
keys = new Array(n);
// Generate one key pair, then import its pkcs8 bytes n times to get
// distinct CryptoKey instances.
const kp = await subtle.generateKey(algorithm, true, ['sign', 'verify']);
const pkcs8 = await subtle.exportKey('pkcs8', kp.privateKey);
for (let i = 0; i < n; ++i) {
keys[i] = await subtle.importKey('pkcs8', pkcs8, algorithm, false, ['sign']);
}
}

const sharedKey = keyReuse === 'shared' ? keys[0] : undefined;

switch (mode) {
case 'serial':
await measureSerial(n, signParams, sharedKey);
break;
case 'parallel':
await measureParallel(n, signParams, sharedKey);
break;
}
}
100 changes: 100 additions & 0 deletions benchmark/crypto/webcrypto-verify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
'use strict';

const common = require('../common.js');
const { hasOpenSSL } = require('../../test/common/crypto.js');
const { subtle } = globalThis.crypto;

const kAlgorithms = {
'ec': { name: 'ECDSA', namedCurve: 'P-256' },
'rsassa-pkcs1-v1_5': {
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256',
},
'rsa-pss': {
name: 'RSA-PSS',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256',
},
'ed25519': { name: 'Ed25519' },
};

if (hasOpenSSL(3, 5)) {
kAlgorithms['ml-dsa-44'] = { name: 'ML-DSA-44' };
}

const kSignParams = {
'ec': { name: 'ECDSA', hash: 'SHA-256' },
'rsassa-pkcs1-v1_5': { name: 'RSASSA-PKCS1-v1_5' },
'rsa-pss': { name: 'RSA-PSS', saltLength: 32 },
'ed25519': { name: 'Ed25519' },
'ml-dsa-44': { name: 'ML-DSA-44' },
};

const data = globalThis.crypto.getRandomValues(new Uint8Array(256));

let publicKeys;
let signature;

const bench = common.createBenchmark(main, {
keyType: Object.keys(kAlgorithms),
mode: ['serial', 'parallel'],
keyReuse: ['shared', 'unique'],
n: [1e3],
}, {
combinationFilter(p) {
// Unique only differs from shared when operations overlap (parallel);
// sequential calls have no contention so unique+serial adds no value.
if (p.keyReuse === 'unique') return p.mode === 'parallel';
return true;
},
});

async function measureSerial(n, verifyParams, sharedKey) {
bench.start();
for (let i = 0; i < n; ++i) {
await subtle.verify(verifyParams, sharedKey || publicKeys[i], signature, data);
}
bench.end(n);
}

async function measureParallel(n, verifyParams, sharedKey) {
const promises = new Array(n);
bench.start();
for (let i = 0; i < n; ++i) {
promises[i] = subtle.verify(verifyParams, sharedKey || publicKeys[i], signature, data);
}
await Promise.all(promises);
bench.end(n);
}

async function main({ n, mode, keyReuse, keyType }) {
const algorithm = kAlgorithms[keyType];
const verifyParams = kSignParams[keyType];

if (!publicKeys || publicKeys.length !== n ||
publicKeys[0].algorithm.name !== verifyParams.name) {
publicKeys = new Array(n);
// Generate one key pair, then import its spki bytes n times to get
// distinct CryptoKey instances.
const kp = await subtle.generateKey(algorithm, true, ['sign', 'verify']);
const spki = await subtle.exportKey('spki', kp.publicKey);
for (let i = 0; i < n; ++i) {
publicKeys[i] = await subtle.importKey('spki', spki, algorithm, false, ['verify']);
}
signature = await subtle.sign(verifyParams, kp.privateKey, data);
}

const sharedKey = keyReuse === 'shared' ? publicKeys[0] : undefined;

switch (mode) {
case 'serial':
await measureSerial(n, verifyParams, sharedKey);
break;
case 'parallel':
await measureParallel(n, verifyParams, sharedKey);
break;
}
}
109 changes: 109 additions & 0 deletions benchmark/misc/webcrypto-webidl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
'use strict';

const common = require('../common.js');

const bench = common.createBenchmark(main, {
op: [
'normalizeAlgorithm-string',
'normalizeAlgorithm-dict',
'webidl-dict',
'webidl-algorithm-identifier-string',
'webidl-algorithm-identifier-object',
'webidl-dict-enforce-range',
'webidl-dict-ensure-sha',
'webidl-dict-null',
],
n: [1e6],
}, { flags: ['--expose-internals'] });

function main({ n, op }) {
const { normalizeAlgorithm } = require('internal/crypto/util');

switch (op) {
case 'normalizeAlgorithm-string': {
// String shortcut + null dictionary (cheapest path).
bench.start();
for (let i = 0; i < n; i++)
normalizeAlgorithm('SHA-256', 'digest');
bench.end(n);
break;
}
case 'normalizeAlgorithm-dict': {
// Object input with a dictionary type and no BufferSource members.
const alg = { name: 'ECDSA', hash: 'SHA-256' };
bench.start();
for (let i = 0; i < n; i++)
normalizeAlgorithm(alg, 'sign');
bench.end(n);
break;
}
case 'webidl-dict': {
// WebIDL dictionary converter in isolation.
const webidl = require('internal/crypto/webidl');
const input = { name: 'AES-GCM', iv: new Uint8Array(12) };
const opts = { prefix: 'test', context: 'test' };
bench.start();
for (let i = 0; i < n; i++)
webidl.converters.AeadParams(input, opts);
bench.end(n);
break;
}
case 'webidl-algorithm-identifier-string': {
// Exercises converters.AlgorithmIdentifier string path.
const webidl = require('internal/crypto/webidl');
const opts = { prefix: 'test', context: 'test' };
bench.start();
for (let i = 0; i < n; i++)
webidl.converters.AlgorithmIdentifier('SHA-256', opts);
bench.end(n);
break;
}
case 'webidl-algorithm-identifier-object': {
// Exercises converters.AlgorithmIdentifier object path.
const webidl = require('internal/crypto/webidl');
const input = { name: 'SHA-256' };
const opts = { prefix: 'test', context: 'test' };
bench.start();
for (let i = 0; i < n; i++)
webidl.converters.AlgorithmIdentifier(input, opts);
bench.end(n);
break;
}
case 'webidl-dict-enforce-range': {
// Exercises [EnforceRange] integer dictionary members.
const webidl = require('internal/crypto/webidl');
const input = {
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
};
const opts = { prefix: 'test', context: 'test' };
bench.start();
for (let i = 0; i < n; i++)
webidl.converters.RsaKeyGenParams(input, opts);
bench.end(n);
break;
}
case 'webidl-dict-ensure-sha': {
// Exercises ensureSHA on a hash member.
const webidl = require('internal/crypto/webidl');
const input = { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' };
const opts = { prefix: 'test', context: 'test' };
bench.start();
for (let i = 0; i < n; i++)
webidl.converters.RsaHashedImportParams(input, opts);
bench.end(n);
break;
}
case 'webidl-dict-null': {
// Exercises the null/undefined path in createDictionaryConverter().
const webidl = require('internal/crypto/webidl');
const opts = { prefix: 'test', context: 'test' };
bench.start();
for (let i = 0; i < n; i++)
webidl.converters.JsonWebKey(undefined, opts);
bench.end(n);
break;
}
}
}
Loading