From d3f8c35320053b4138d0d0a7cdf0beebc8db0172 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Mon, 23 Sep 2024 17:28:44 +0200 Subject: [PATCH] crypto: ensure invalid SubtleCrypto JWK data import results in DataError PR-URL: https://github.com/nodejs/node/pull/55041 Reviewed-By: James M Snell Reviewed-By: Yagiz Nizipli --- lib/internal/crypto/aes.js | 7 +++- lib/internal/crypto/cfrg.js | 38 ++++++++++++------- lib/internal/crypto/ec.js | 10 ++++- lib/internal/crypto/mac.js | 7 +++- lib/internal/crypto/rsa.js | 10 ++++- .../test-webcrypto-export-import-cfrg.js | 9 +++++ .../test-webcrypto-export-import-ec.js | 9 +++++ .../test-webcrypto-export-import-rsa.js | 9 +++++ 8 files changed, 79 insertions(+), 20 deletions(-) diff --git a/lib/internal/crypto/aes.js b/lib/internal/crypto/aes.js index bb35d2b21de261..762e8ead4ac46f 100644 --- a/lib/internal/crypto/aes.js +++ b/lib/internal/crypto/aes.js @@ -295,7 +295,12 @@ async function aesImportKey( } const handle = new KeyObjectHandle(); - handle.initJwk(keyData); + try { + handle.initJwk(keyData); + } catch (err) { + throw lazyDOMException( + 'Invalid keyData', { name: 'DataError', cause: err }); + } ({ length } = handle.keyDetail({ })); validateKeyLength(length); diff --git a/lib/internal/crypto/cfrg.js b/lib/internal/crypto/cfrg.js index e7141c04b3f437..9c5e59ebb3bf86 100644 --- a/lib/internal/crypto/cfrg.js +++ b/lib/internal/crypto/cfrg.js @@ -17,6 +17,12 @@ const { kSignJobModeVerify, } = internalBinding('crypto'); +const { + codes: { + ERR_CRYPTO_INVALID_JWK, + }, +} = require('internal/errors'); + const { getUsagesUnion, hasAnyNotIn, @@ -277,22 +283,26 @@ async function cfrgImportKey( isPublic, usagesSet); - const publicKeyObject = createCFRGRawKey( - name, - Buffer.from(keyData.x, 'base64'), - true); - - if (isPublic) { - keyObject = publicKeyObject; - } else { - keyObject = createCFRGRawKey( + try { + const publicKeyObject = createCFRGRawKey( name, - Buffer.from(keyData.d, 'base64'), - false); - - if (!createPublicKey(keyObject).equals(publicKeyObject)) { - throw lazyDOMException('Invalid JWK', 'DataError'); + Buffer.from(keyData.x, 'base64'), + true); + + if (isPublic) { + keyObject = publicKeyObject; + } else { + keyObject = createCFRGRawKey( + name, + Buffer.from(keyData.d, 'base64'), + false); + + if (!createPublicKey(keyObject).equals(publicKeyObject)) { + throw new ERR_CRYPTO_INVALID_JWK(); + } } + } catch (err) { + throw lazyDOMException('Invalid keyData', { name: 'DataError', cause: err }); } break; } diff --git a/lib/internal/crypto/ec.js b/lib/internal/crypto/ec.js index 9f30f6e1a0ba09..e155a5b6610044 100644 --- a/lib/internal/crypto/ec.js +++ b/lib/internal/crypto/ec.js @@ -240,9 +240,15 @@ async function ecImportKey( } const handle = new KeyObjectHandle(); - const type = handle.initJwk(keyData, namedCurve); + let type; + try { + type = handle.initJwk(keyData, namedCurve); + } catch (err) { + throw lazyDOMException( + 'Invalid keyData', { name: 'DataError', cause: err }); + } if (type === undefined) - throw lazyDOMException('Invalid JWK', 'DataError'); + throw lazyDOMException('Invalid keyData', 'DataError'); keyObject = type === kKeyTypePrivate ? new PrivateKeyObject(handle) : new PublicKeyObject(handle); diff --git a/lib/internal/crypto/mac.js b/lib/internal/crypto/mac.js index 91f58a85a9f6dd..112861523c605f 100644 --- a/lib/internal/crypto/mac.js +++ b/lib/internal/crypto/mac.js @@ -145,7 +145,12 @@ async function hmacImportKey( } const handle = new KeyObjectHandle(); - handle.initJwk(keyData); + try { + handle.initJwk(keyData); + } catch (err) { + throw lazyDOMException( + 'Invalid keyData', { name: 'DataError', cause: err }); + } keyObject = new SecretKeyObject(handle); break; } diff --git a/lib/internal/crypto/rsa.js b/lib/internal/crypto/rsa.js index 3b338ce8762135..fd223d3cb632ab 100644 --- a/lib/internal/crypto/rsa.js +++ b/lib/internal/crypto/rsa.js @@ -275,9 +275,15 @@ async function rsaImportKey( } const handle = new KeyObjectHandle(); - const type = handle.initJwk(keyData); + let type; + try { + type = handle.initJwk(keyData); + } catch (err) { + throw lazyDOMException( + 'Invalid keyData', { name: 'DataError', cause: err }); + } if (type === undefined) - throw lazyDOMException('Invalid JWK', 'DataError'); + throw lazyDOMException('Invalid keyData', 'DataError'); keyObject = type === kKeyTypePrivate ? new PrivateKeyObject(handle) : diff --git a/test/parallel/test-webcrypto-export-import-cfrg.js b/test/parallel/test-webcrypto-export-import-cfrg.js index 144424ce01ded2..2c2cb80a31c1bf 100644 --- a/test/parallel/test-webcrypto-export-import-cfrg.js +++ b/test/parallel/test-webcrypto-export-import-cfrg.js @@ -322,6 +322,15 @@ async function testImportJwk({ name, publicUsages, privateUsages }, extractable) extractable, [/* empty usages */]), { name: 'SyntaxError', message: 'Usages cannot be empty when importing a private key.' }); + + await assert.rejects( + subtle.importKey( + 'jwk', + { kty: jwk.kty, /* missing x */ crv: jwk.crv }, + { name }, + extractable, + publicUsages), + { name: 'DataError', message: 'Invalid keyData' }); } async function testImportRaw({ name, publicUsages }) { diff --git a/test/parallel/test-webcrypto-export-import-ec.js b/test/parallel/test-webcrypto-export-import-ec.js index 27c37715be1838..35c657bbbf0ef1 100644 --- a/test/parallel/test-webcrypto-export-import-ec.js +++ b/test/parallel/test-webcrypto-export-import-ec.js @@ -330,6 +330,15 @@ async function testImportJwk( extractable, [/* empty usages */]), { name: 'SyntaxError', message: 'Usages cannot be empty when importing a private key.' }); + + await assert.rejects( + subtle.importKey( + 'jwk', + { kty: jwk.kty, /* missing x */ y: jwk.y, crv: jwk.crv }, + { name, namedCurve }, + extractable, + publicUsages), + { name: 'DataError', message: 'Invalid keyData' }); } async function testImportRaw({ name, publicUsages }, namedCurve) { diff --git a/test/parallel/test-webcrypto-export-import-rsa.js b/test/parallel/test-webcrypto-export-import-rsa.js index fb79184afd6e12..303efef7bb8f84 100644 --- a/test/parallel/test-webcrypto-export-import-rsa.js +++ b/test/parallel/test-webcrypto-export-import-rsa.js @@ -513,6 +513,15 @@ async function testImportJwk( extractable, [/* empty usages */]), { name: 'SyntaxError', message: 'Usages cannot be empty when importing a private key.' }); + + await assert.rejects( + subtle.importKey( + 'jwk', + { kty: jwk.kty, /* missing e */ n: jwk.n }, + { name, hash }, + extractable, + publicUsages), + { name: 'DataError', message: 'Invalid keyData' }); } // combinations to test