From 5329f73816a84e806db6b052e22d6622ce9e2e62 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Sat, 9 Aug 2025 13:41:45 +0200 Subject: [PATCH] lib: refactor kSupportedAlgorithms PR-URL: https://github.com/nodejs/node/pull/59365 Reviewed-By: James M Snell Reviewed-By: Ethan Arrowood Reviewed-By: Yagiz Nizipli Reviewed-By: Joyee Cheung --- lib/internal/crypto/util.js | 373 ++++++++++++++++++++---------------- 1 file changed, 209 insertions(+), 164 deletions(-) diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js index 3020682b86b..685637ab743 100644 --- a/lib/internal/crypto/util.js +++ b/lib/internal/crypto/util.js @@ -171,184 +171,229 @@ 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', }, - '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-CTR': { + 'generateKey': 'AesKeyGenParams', + 'exportKey': null, + 'importKey': null, + 'encrypt': 'AesCtrParams', + 'decrypt': 'AesCtrParams', + '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-GCM': { + 'generateKey': 'AesKeyGenParams', + 'exportKey': null, + 'importKey': null, + 'encrypt': 'AeadParams', + 'decrypt': 'AeadParams', + 'get key length': 'AesDerivedKeyParams', }, - 'sign': { - 'RSASSA-PKCS1-v1_5': null, - 'RSA-PSS': 'RsaPssParams', - 'ECDSA': 'EcdsaParams', - 'HMAC': null, - 'Ed25519': null, - }, - 'verify': { - 'RSASSA-PKCS1-v1_5': null, - 'RSA-PSS': 'RsaPssParams', - 'ECDSA': 'EcdsaParams', - 'HMAC': null, - 'Ed25519': null, - }, - '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, - }, - 'deriveBits': { - 'HKDF': 'HkdfParams', - 'PBKDF2': 'Pbkdf2Params', - 'ECDH': 'EcdhKeyDeriveParams', - 'X25519': 'EcdhKeyDeriveParams', - }, - 'encrypt': { - 'RSA-OAEP': 'RsaOaepParams', - 'AES-CBC': 'AesCbcParams', - 'AES-GCM': 'AeadParams', - 'AES-CTR': 'AesCtrParams', - }, - 'decrypt': { - 'RSA-OAEP': 'RsaOaepParams', - 'AES-CBC': 'AesCbcParams', - 'AES-GCM': 'AeadParams', - 'AES-CTR': 'AesCtrParams', - }, - 'get key length': { - 'AES-CBC': 'AesDerivedKeyParams', - 'AES-CTR': 'AesDerivedKeyParams', - 'AES-GCM': 'AesDerivedKeyParams', - 'HMAC': 'HmacImportParams', - 'HKDF': null, - 'PBKDF2': null, - }, - 'wrapKey': {}, - 'unwrapKey': {}, -}; - -const conditionalAlgorithms = ObjectEntries({ - 'AES-KW': [{ + 'AES-KW': { 'generateKey': 'AesKeyGenParams', 'exportKey': null, 'importKey': null, 'get key length': 'AesDerivedKeyParams', 'wrapKey': null, 'unwrapKey': null, - }, !process.features.openssl_is_boringssl], -}); + }, + 'ChaCha20-Poly1305': { + 'generateKey': null, + 'exportKey': null, + 'importKey': null, + 'encrypt': 'AeadParams', + 'decrypt': 'AeadParams', + 'get key length': null, + }, + 'cSHAKE128': { 'digest': 'CShakeParams' }, + 'cSHAKE256': { 'digest': 'CShakeParams' }, + 'ECDH': { + 'generateKey': 'EcKeyGenParams', + 'exportKey': null, + 'importKey': 'EcKeyImportParams', + 'deriveBits': 'EcdhKeyDeriveParams', + }, + 'ECDSA': { + 'generateKey': 'EcKeyGenParams', + 'exportKey': null, + 'importKey': 'EcKeyImportParams', + 'sign': 'EcdsaParams', + 'verify': 'EcdsaParams', + }, + 'Ed25519': { + 'generateKey': null, + 'exportKey': null, + 'importKey': null, + 'sign': null, + 'verify': null, + }, + 'Ed448': { + 'generateKey': null, + 'exportKey': null, + 'importKey': null, + 'sign': 'Ed448Params', + 'verify': 'Ed448Params', + }, + 'HKDF': { + 'importKey': null, + 'deriveBits': 'HkdfParams', + 'get key length': null, + }, + 'HMAC': { + 'generateKey': 'HmacKeyGenParams', + 'exportKey': null, + 'importKey': 'HmacImportParams', + 'sign': null, + 'verify': null, + 'get key length': 'HmacImportParams', + }, + 'ML-DSA-44': { + 'generateKey': null, + 'exportKey': null, + 'importKey': null, + '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, +}; + +// 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: 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; + } } } + + return result; } -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], - ['ML-DSA-87', EVP_PKEY_ML_DSA_87], -]) { - if (nid) { - ArrayPrototypePush(experimentalAlgorithms, [algorithm, { - generateKey: null, - sign: 'ContextParams', - verify: 'ContextParams', - importKey: null, - exportKey: null, - }]); - } -} - -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, - }); - } -} +const kSupportedAlgorithms = createSupportedAlgorithms(kAlgorithmDefinitions); const simpleAlgorithmDictionaries = { AeadParams: { iv: 'BufferSource', additionalData: 'BufferSource' },