Skip to content
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
lib: refactor kSupportedAlgorithms
  • Loading branch information
panva committed Aug 18, 2025
commit 82c8daedb2fba2d881da5549229fa01fb8e2383e
365 changes: 205 additions & 160 deletions lib/internal/crypto/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,185 +171,230 @@ const kNamedCurveAliases = {
'P-521': 'secp521r1',
};

const kSupportedAlgorithms = {
'digest': {
'SHA-1': null,
'SHA-256': null,
'SHA-384': null,
'SHA-512': null,
// Algorithm definitions organized by algorithm name
const kAlgorithmDefinitions = {
'AES-CBC': {
'generateKey': 'AesKeyGenParams',
'exportKey': null,
'importKey': null,
'encrypt': 'AesCbcParams',
'decrypt': 'AesCbcParams',
'get key length': 'AesDerivedKeyParams',
},
'AES-CTR': {
'generateKey': 'AesKeyGenParams',
'exportKey': null,
'importKey': null,
'encrypt': 'AesCtrParams',
'decrypt': 'AesCtrParams',
'get key length': 'AesDerivedKeyParams',
},
'generateKey': {
'RSASSA-PKCS1-v1_5': 'RsaHashedKeyGenParams',
'RSA-PSS': 'RsaHashedKeyGenParams',
'RSA-OAEP': 'RsaHashedKeyGenParams',
'ECDSA': 'EcKeyGenParams',
'ECDH': 'EcKeyGenParams',
'AES-CTR': 'AesKeyGenParams',
'AES-CBC': 'AesKeyGenParams',
'AES-GCM': 'AesKeyGenParams',
'HMAC': 'HmacKeyGenParams',
'Ed25519': null,
'X25519': null,
'AES-GCM': {
'generateKey': 'AesKeyGenParams',
'exportKey': null,
'importKey': null,
'encrypt': 'AeadParams',
'decrypt': 'AeadParams',
'get key length': 'AesDerivedKeyParams',
},
'exportKey': {
'RSASSA-PKCS1-v1_5': null,
'RSA-PSS': null,
'RSA-OAEP': null,
'ECDSA': null,
'ECDH': null,
'HMAC': null,
'AES-CTR': null,
'AES-CBC': null,
'AES-GCM': null,
'Ed25519': null,
'X25519': null,
'AES-KW': {
'generateKey': 'AesKeyGenParams',
'exportKey': null,
'importKey': null,
'get key length': 'AesDerivedKeyParams',
'wrapKey': null,
'unwrapKey': null,
},
'sign': {
'RSASSA-PKCS1-v1_5': null,
'RSA-PSS': 'RsaPssParams',
'ECDSA': 'EcdsaParams',
'HMAC': null,
'Ed25519': null,
'ChaCha20-Poly1305': {
'generateKey': null,
'exportKey': null,
'importKey': null,
'encrypt': 'AeadParams',
'decrypt': 'AeadParams',
'get key length': null,
},
'verify': {
'RSASSA-PKCS1-v1_5': null,
'RSA-PSS': 'RsaPssParams',
'ECDSA': 'EcdsaParams',
'HMAC': null,
'Ed25519': null,
'cSHAKE128': { 'digest': 'CShakeParams' },
'cSHAKE256': { 'digest': 'CShakeParams' },
'ECDH': {
'generateKey': 'EcKeyGenParams',
'exportKey': null,
'importKey': 'EcKeyImportParams',
'deriveBits': 'EcdhKeyDeriveParams',
},
'importKey': {
'RSASSA-PKCS1-v1_5': 'RsaHashedImportParams',
'RSA-PSS': 'RsaHashedImportParams',
'RSA-OAEP': 'RsaHashedImportParams',
'ECDSA': 'EcKeyImportParams',
'ECDH': 'EcKeyImportParams',
'HMAC': 'HmacImportParams',
'HKDF': null,
'PBKDF2': null,
'AES-CTR': null,
'AES-CBC': null,
'AES-GCM': null,
'Ed25519': null,
'X25519': null,
'ECDSA': {
'generateKey': 'EcKeyGenParams',
'exportKey': null,
'importKey': 'EcKeyImportParams',
'sign': 'EcdsaParams',
'verify': 'EcdsaParams',
},
'deriveBits': {
'HKDF': 'HkdfParams',
'PBKDF2': 'Pbkdf2Params',
'ECDH': 'EcdhKeyDeriveParams',
'X25519': 'EcdhKeyDeriveParams',
'Ed25519': {
'generateKey': null,
'exportKey': null,
'importKey': null,
'sign': null,
'verify': null,
},
'encrypt': {
'RSA-OAEP': 'RsaOaepParams',
'AES-CBC': 'AesCbcParams',
'AES-GCM': 'AeadParams',
'AES-CTR': 'AesCtrParams',
'Ed448': {
'generateKey': null,
'exportKey': null,
'importKey': null,
'sign': 'Ed448Params',
'verify': 'Ed448Params',
},
'decrypt': {
'RSA-OAEP': 'RsaOaepParams',
'AES-CBC': 'AesCbcParams',
'AES-GCM': 'AeadParams',
'AES-CTR': 'AesCtrParams',
'HKDF': {
'importKey': null,
'deriveBits': 'HkdfParams',
'get key length': null,
},
'get key length': {
'AES-CBC': 'AesDerivedKeyParams',
'AES-CTR': 'AesDerivedKeyParams',
'AES-GCM': 'AesDerivedKeyParams',
'HMAC': 'HmacImportParams',
'HKDF': null,
'PBKDF2': null,
'HMAC': {
'generateKey': 'HmacKeyGenParams',
'exportKey': null,
'importKey': 'HmacImportParams',
'sign': null,
'verify': null,
'get key length': 'HmacImportParams',
},
'wrapKey': {},
'unwrapKey': {},
};

const conditionalAlgorithms = ObjectEntries({
'AES-KW': [{
'generateKey': 'AesKeyGenParams',
'ML-DSA-44': {
'generateKey': null,
'exportKey': null,
'importKey': null,
'get key length': 'AesDerivedKeyParams',
'wrapKey': null,
'unwrapKey': null,
}, !process.features.openssl_is_boringssl],
});
'sign': 'ContextParams',
'verify': 'ContextParams',
},
'ML-DSA-65': {
'generateKey': null,
'exportKey': null,
'importKey': null,
'sign': 'ContextParams',
'verify': 'ContextParams',
},
'ML-DSA-87': {
'generateKey': null,
'exportKey': null,
'importKey': null,
'sign': 'ContextParams',
'verify': 'ContextParams',
},
'PBKDF2': {
'importKey': null,
'deriveBits': 'Pbkdf2Params',
'get key length': null,
},
'RSA-OAEP': {
'generateKey': 'RsaHashedKeyGenParams',
'exportKey': null,
'importKey': 'RsaHashedImportParams',
'encrypt': 'RsaOaepParams',
'decrypt': 'RsaOaepParams',
},
'RSA-PSS': {
'generateKey': 'RsaHashedKeyGenParams',
'exportKey': null,
'importKey': 'RsaHashedImportParams',
'sign': 'RsaPssParams',
'verify': 'RsaPssParams',
},
'RSASSA-PKCS1-v1_5': {
'generateKey': 'RsaHashedKeyGenParams',
'exportKey': null,
'importKey': 'RsaHashedImportParams',
'sign': null,
'verify': null,
},
'SHA-1': { 'digest': null },
'SHA-256': { 'digest': null },
'SHA-384': { 'digest': null },
'SHA-512': { 'digest': null },
'SHA3-256': { 'digest': null },
'SHA3-384': { 'digest': null },
'SHA3-512': { 'digest': null },
'X25519': {
'generateKey': null,
'exportKey': null,
'importKey': null,
'deriveBits': 'EcdhKeyDeriveParams',
},
'X448': {
'generateKey': null,
'exportKey': null,
'importKey': null,
'deriveBits': 'EcdhKeyDeriveParams',
},
};

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;
}
}
}
// Conditionally supported algorithms
const conditionalAlgorithms = {
'AES-KW': !process.features.openssl_is_boringssl,
'ChaCha20-Poly1305': !process.features.openssl_is_boringssl ||
ArrayPrototypeIncludes(getCiphers(), 'chacha20-poly1305'),
'cSHAKE128': !process.features.openssl_is_boringssl ||
ArrayPrototypeIncludes(getHashes(), 'shake128'),
'cSHAKE256': !process.features.openssl_is_boringssl ||
ArrayPrototypeIncludes(getHashes(), 'shake256'),
'Ed448': !process.features.openssl_is_boringssl,
'ML-DSA-44': !!EVP_PKEY_ML_DSA_44,
'ML-DSA-65': !!EVP_PKEY_ML_DSA_65,
'ML-DSA-87': !!EVP_PKEY_ML_DSA_87,
'SHA3-256': !process.features.openssl_is_boringssl ||
ArrayPrototypeIncludes(getHashes(), 'sha3-256'),
'SHA3-384': !process.features.openssl_is_boringssl ||
ArrayPrototypeIncludes(getHashes(), 'sha3-384'),
'SHA3-512': !process.features.openssl_is_boringssl ||
ArrayPrototypeIncludes(getHashes(), 'sha3-512'),
'X448': !process.features.openssl_is_boringssl,
};

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 }],
);
}
// Experimental algorithms
const experimentalAlgorithms = [
'ChaCha20-Poly1305',
'cSHAKE128',
'cSHAKE256',
'Ed448',
'ML-DSA-44',
'ML-DSA-65',
'ML-DSA-87',
'SHA3-256',
'SHA3-384',
'SHA3-512',
'X448',
];

// Transform the algorithm definitions into the operation-keyed structure
function createSupportedAlgorithms(algorithmDefs) {
const result = {};

for (const { 0: algorithmName, 1: operations } of ObjectEntries(algorithmDefs)) {
// Skip algorithms that are conditionally not supported
if (ObjectPrototypeHasOwnProperty(conditionalAlgorithms, algorithmName) &&
!conditionalAlgorithms[algorithmName]) {
continue;
}

for (const { 0: algorithm, 1: nid } of [
['ML-DSA-44', EVP_PKEY_ML_DSA_44],
['ML-DSA-65', EVP_PKEY_ML_DSA_65],
['ML-DSA-87', EVP_PKEY_ML_DSA_87],
]) {
if (nid) {
ArrayPrototypePush(experimentalAlgorithms, [algorithm, {
generateKey: null,
sign: 'ContextParams',
verify: 'ContextParams',
importKey: null,
exportKey: null,
}]);
for (const { 0: operation, 1: dict } of ObjectEntries(operations)) {
result[operation] ||= {};

// Add experimental warnings for experimental algorithms
if (ArrayPrototypeIncludes(experimentalAlgorithms, algorithmName)) {
ObjectDefineProperty(result[operation], algorithmName, {
get() {
emitExperimentalWarning(`The ${algorithmName} Web Crypto API algorithm`);
return dict;
},
__proto__: null,
enumerable: true,
});
} else {
result[operation][algorithmName] = dict;
}
}
}
}

for (let i = 0; i < experimentalAlgorithms.length; i++) {
const name = experimentalAlgorithms[i][0];
const ops = ObjectEntries(experimentalAlgorithms[i][1]);
for (let j = 0; j < ops.length; j++) {
const { 0: op, 1: dict } = ops[j];
ObjectDefineProperty(kSupportedAlgorithms[op], name, {
get() {
emitExperimentalWarning(`The ${name} Web Crypto API algorithm`);
return dict;
},
__proto__: null,
enumerable: true,
});
}
return result;
}

const kSupportedAlgorithms = createSupportedAlgorithms(kAlgorithmDefinitions);

const simpleAlgorithmDictionaries = {
AeadParams: { iv: 'BufferSource', additionalData: 'BufferSource' },
RsaHashedKeyGenParams: { hash: 'HashAlgorithmIdentifier' },
Expand Down