From 21f1351f134c2709aded281575d9447121e0b665 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Sun, 14 Jun 2026 11:26:47 +0200 Subject: [PATCH] crypto: make webcrypto aliasKeyFormat directional The helper no longer treats every raw alias the same way and unintended values were accepted for some algorithms. Signed-off-by: Filip Skokan --- lib/internal/crypto/webcrypto.js | 17 ++--- .../test-webcrypto-raw-format-aliases.js | 72 +++++++++++++++++++ 2 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 test/parallel/test-webcrypto-raw-format-aliases.js diff --git a/lib/internal/crypto/webcrypto.js b/lib/internal/crypto/webcrypto.js index e4700da3959324..f86ded93312859 100644 --- a/lib/internal/crypto/webcrypto.js +++ b/lib/internal/crypto/webcrypto.js @@ -837,14 +837,8 @@ function parseJwk(data) { return key; } -function aliasKeyFormat(format) { - switch (format) { - case 'raw-public': - case 'raw-secret': - return 'raw'; - default: - return format; - } +function aliasKeyFormat(format, alias) { + return format === alias ? 'raw' : format; } function importKeySync(format, keyData, algorithm, extractable, usages) { @@ -855,7 +849,6 @@ function importKeySync(format, keyData, algorithm, extractable, usages) { case 'RSA-PSS': // Fall through case 'RSA-OAEP': - format = aliasKeyFormat(format); result = require('internal/crypto/rsa') .rsaImportKey( format, @@ -867,7 +860,7 @@ function importKeySync(format, keyData, algorithm, extractable, usages) { case 'ECDSA': // Fall through case 'ECDH': - format = aliasKeyFormat(format); + format = aliasKeyFormat(format, 'raw-public'); result = require('internal/crypto/ec') .ecImportKey( format, @@ -883,7 +876,7 @@ function importKeySync(format, keyData, algorithm, extractable, usages) { case 'X25519': // Fall through case 'X448': - format = aliasKeyFormat(format); + format = aliasKeyFormat(format, 'raw-public'); result = require('internal/crypto/cfrg') .cfrgImportKey( format, @@ -934,7 +927,7 @@ function importKeySync(format, keyData, algorithm, extractable, usages) { case 'HKDF': // Fall through case 'PBKDF2': - format = aliasKeyFormat(format); + format = aliasKeyFormat(format, 'raw-secret'); result = importGenericSecretKey( algorithm, format, diff --git a/test/parallel/test-webcrypto-raw-format-aliases.js b/test/parallel/test-webcrypto-raw-format-aliases.js new file mode 100644 index 00000000000000..94e9474fde13ff --- /dev/null +++ b/test/parallel/test-webcrypto-raw-format-aliases.js @@ -0,0 +1,72 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { subtle } = globalThis.crypto; + +function getAlgorithmName(algorithm) { + return typeof algorithm === 'string' ? algorithm : algorithm.name; +} + +function assertImportNotSupported(format, keyData, algorithm, extractable, usages) { + return assert.rejects( + subtle.importKey(format, keyData, algorithm, extractable, usages), + { + name: 'NotSupportedError', + message: `Unable to import ${getAlgorithmName(algorithm)} using ${format} format`, + }); +} + +async function assertSecretKeyDoesNotAcceptRawPublic(algorithm) { + const keyData = new Uint8Array(32); + await assertImportNotSupported( + 'raw-public', + keyData, + algorithm, + false, + ['deriveBits']); +} + +async function assertPublicKeyDoesNotAcceptRawSecret( + algorithm, + generateUsages, + importUsages, +) { + const { publicKey } = await subtle.generateKey( + algorithm, + true, + generateUsages); + const keyData = await subtle.exportKey('raw', publicKey); + + await assertImportNotSupported( + 'raw-secret', + keyData, + algorithm, + true, + importUsages); +} + +Promise.all([ + assertSecretKeyDoesNotAcceptRawPublic('HKDF'), + assertSecretKeyDoesNotAcceptRawPublic('PBKDF2'), + assertPublicKeyDoesNotAcceptRawSecret( + { name: 'ECDSA', namedCurve: 'P-256' }, + ['sign', 'verify'], + ['verify']), + assertPublicKeyDoesNotAcceptRawSecret( + { name: 'ECDH', namedCurve: 'P-256' }, + ['deriveBits'], + []), + assertPublicKeyDoesNotAcceptRawSecret( + 'Ed25519', + ['sign', 'verify'], + ['verify']), + assertPublicKeyDoesNotAcceptRawSecret( + 'X25519', + ['deriveBits'], + []), +]).then(common.mustCall());