From 105c9e6d3ba4e0c6078a0f369384d434c5482421 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Tue, 31 Aug 2021 19:48:29 +0200 Subject: [PATCH] crypto: check webcrypto asymmetric key types during importKey PR-URL: https://github.com/nodejs/node/pull/39962 Reviewed-By: James M Snell --- lib/internal/crypto/dsa.js | 3 ++ lib/internal/crypto/ec.js | 33 +++++++++++++++++ lib/internal/crypto/rsa.js | 11 ++++++ .../test-webcrypto-export-import-dsa.js | 32 +++++++++++++++- .../test-webcrypto-export-import-ec.js | 37 ++++++++++++++++++- .../test-webcrypto-export-import-rsa.js | 37 ++++++++++++++++++- 6 files changed, 150 insertions(+), 3 deletions(-) diff --git a/lib/internal/crypto/dsa.js b/lib/internal/crypto/dsa.js index 54bd70d9e2eac5..12e327c6ed1765 100644 --- a/lib/internal/crypto/dsa.js +++ b/lib/internal/crypto/dsa.js @@ -230,6 +230,9 @@ async function dsaImportKey( 'NotSupportedError'); } + if (keyObject.asymmetricKeyType !== 'dsa') + throw lazyDOMException('Invalid key type', 'DataError'); + const { modulusLength, divisorLength, diff --git a/lib/internal/crypto/ec.js b/lib/internal/crypto/ec.js index 8bc7d9a28a42ea..1ebceadf50f399 100644 --- a/lib/internal/crypto/ec.js +++ b/lib/internal/crypto/ec.js @@ -430,6 +430,39 @@ async function ecImportKey( } } + switch (algorithm.name) { + case 'ECDSA': + if (keyObject.asymmetricKeyType !== 'ec') + throw lazyDOMException('Invalid key type', 'DataError'); + break; + case 'ECDH': + if ( + algorithm.namedCurve === 'NODE-X25519' && + keyObject.asymmetricKeyType !== 'x25519' + ) { + throw lazyDOMException('Invalid key type', 'DataError'); + } else if ( + algorithm.namedCurve === 'NODE-X448' && + keyObject.asymmetricKeyType !== 'x448' + ) { + throw lazyDOMException('Invalid key type', 'DataError'); + } else if ( + algorithm.namedCurve.startsWith('P') && + keyObject.asymmetricKeyType !== 'ec' + ) { + throw lazyDOMException('Invalid key type', 'DataError'); + } + break; + case 'NODE-ED25519': + if (keyObject.asymmetricKeyType !== 'ed25519') + throw lazyDOMException('Invalid key type', 'DataError'); + break; + case 'NODE-ED448': + if (keyObject.asymmetricKeyType !== 'ed448') + throw lazyDOMException('Invalid key type', 'DataError'); + break; + } + if (checkNamedCurve) { const { namedCurve: checkNamedCurve diff --git a/lib/internal/crypto/rsa.js b/lib/internal/crypto/rsa.js index a0d27f3715e211..8e4b6af571dda3 100644 --- a/lib/internal/crypto/rsa.js +++ b/lib/internal/crypto/rsa.js @@ -326,6 +326,17 @@ async function rsaImportKey( 'NotSupportedError'); } + if (algorithm.name === 'RSA-PSS') { + if ( + keyObject.asymmetricKeyType !== 'rsa' && + keyObject.asymmetricKeyType !== 'rsa-pss' + ) { + throw lazyDOMException('Invalid key type', 'DataError'); + } + } else if (keyObject.asymmetricKeyType !== 'rsa') { + throw lazyDOMException('Invalid key type', 'DataError'); + } + const { modulusLength, publicExponent, diff --git a/test/parallel/test-webcrypto-export-import-dsa.js b/test/parallel/test-webcrypto-export-import-dsa.js index 3fddd9dd9c4559..01e31d747942ae 100644 --- a/test/parallel/test-webcrypto-export-import-dsa.js +++ b/test/parallel/test-webcrypto-export-import-dsa.js @@ -1,12 +1,14 @@ 'use strict'; const common = require('../common'); +const fixtures = require('../common/fixtures'); if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const { subtle } = require('crypto').webcrypto; +const crypto = require('crypto'); +const { subtle } = crypto.webcrypto; const sizes = [1024]; @@ -132,3 +134,31 @@ const testVectors = [ }); await Promise.all(variations); })().then(common.mustCall()); + +{ + const ecPublic = crypto.createPublicKey( + fixtures.readKey('ec_p256_public.pem')); + const ecPrivate = crypto.createPrivateKey( + fixtures.readKey('ec_p256_private.pem')); + + assert.rejects(subtle.importKey( + 'node.keyObject', + ecPublic, + { name: 'NODE-DSA', hash: 'SHA-256' }, + true, ['verify']), { message: /Invalid key type/ }); + assert.rejects(subtle.importKey( + 'node.keyObject', + ecPrivate, + { name: 'NODE-DSA', hash: 'SHA-256' }, + true, ['sign']), { message: /Invalid key type/ }); + assert.rejects(subtle.importKey( + 'spki', + ecPublic.export({ format: 'der', type: 'spki' }), + { name: 'NODE-DSA', hash: 'SHA-256' }, + true, ['verify']), { message: /Invalid key type/ }); + assert.rejects(subtle.importKey( + 'pkcs8', + ecPrivate.export({ format: 'der', type: 'pkcs8' }), + { name: 'NODE-DSA', hash: 'SHA-256' }, + true, ['sign']), { message: /Invalid key type/ }); +} diff --git a/test/parallel/test-webcrypto-export-import-ec.js b/test/parallel/test-webcrypto-export-import-ec.js index 31ab2c09cdb1f9..682d5f54be5563 100644 --- a/test/parallel/test-webcrypto-export-import-ec.js +++ b/test/parallel/test-webcrypto-export-import-ec.js @@ -1,12 +1,14 @@ 'use strict'; const common = require('../common'); +const fixtures = require('../common/fixtures'); if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const { subtle } = require('crypto').webcrypto; +const crypto = require('crypto'); +const { subtle } = crypto.webcrypto; const curves = ['P-256', 'P-384', 'P-521']; @@ -274,3 +276,36 @@ async function testImportJwk( await Promise.all(tests); })().then(common.mustCall()); + +{ + const rsaPublic = crypto.createPublicKey( + fixtures.readKey('rsa_public_2048.pem')); + const rsaPrivate = crypto.createPrivateKey( + fixtures.readKey('rsa_private_2048.pem')); + + for (const [name, [publicUsage, privateUsage]] of Object.entries({ + 'ECDSA': ['verify', 'sign'], + 'ECDH': ['deriveBits', 'deriveBits'], + })) { + assert.rejects(subtle.importKey( + 'node.keyObject', + rsaPublic, + { name, hash: 'SHA-256', namedCurve: 'P-256' }, + true, [publicUsage]), { message: /Invalid key type/ }); + assert.rejects(subtle.importKey( + 'node.keyObject', + rsaPrivate, + { name, hash: 'SHA-256', namedCurve: 'P-256' }, + true, [privateUsage]), { message: /Invalid key type/ }); + assert.rejects(subtle.importKey( + 'spki', + rsaPublic.export({ format: 'der', type: 'spki' }), + { name, hash: 'SHA-256', namedCurve: 'P-256' }, + true, [publicUsage]), { message: /Invalid key type/ }); + assert.rejects(subtle.importKey( + 'pkcs8', + rsaPrivate.export({ format: 'der', type: 'pkcs8' }), + { name, hash: 'SHA-256', namedCurve: 'P-256' }, + true, [privateUsage]), { message: /Invalid key type/ }); + } +} diff --git a/test/parallel/test-webcrypto-export-import-rsa.js b/test/parallel/test-webcrypto-export-import-rsa.js index 04cf6388fc739d..ab7aa77394ac99 100644 --- a/test/parallel/test-webcrypto-export-import-rsa.js +++ b/test/parallel/test-webcrypto-export-import-rsa.js @@ -7,7 +7,8 @@ if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const { subtle } = require('crypto').webcrypto; +const crypto = require('crypto'); +const { subtle } = crypto.webcrypto; const sizes = [1024, 2048, 4096]; @@ -521,3 +522,37 @@ const testVectors = [ assert.strictEqual(jwk.alg, 'PS256'); })().then(common.mustCall()); } + +{ + const ecPublic = crypto.createPublicKey( + fixtures.readKey('ec_p256_public.pem')); + const ecPrivate = crypto.createPrivateKey( + fixtures.readKey('ec_p256_private.pem')); + + for (const [name, [publicUsage, privateUsage]] of Object.entries({ + 'RSA-PSS': ['verify', 'sign'], + 'RSASSA-PKCS1-v1_5': ['verify', 'sign'], + 'RSA-OAEP': ['encrypt', 'decrypt'], + })) { + assert.rejects(subtle.importKey( + 'node.keyObject', + ecPublic, + { name, hash: 'SHA-256' }, + true, [publicUsage]), { message: /Invalid key type/ }); + assert.rejects(subtle.importKey( + 'node.keyObject', + ecPrivate, + { name, hash: 'SHA-256' }, + true, [privateUsage]), { message: /Invalid key type/ }); + assert.rejects(subtle.importKey( + 'spki', + ecPublic.export({ format: 'der', type: 'spki' }), + { name, hash: 'SHA-256' }, + true, [publicUsage]), { message: /Invalid key type/ }); + assert.rejects(subtle.importKey( + 'pkcs8', + ecPrivate.export({ format: 'der', type: 'pkcs8' }), + { name, hash: 'SHA-256' }, + true, [privateUsage]), { message: /Invalid key type/ }); + } +}