crypto: check ed/x webcrypto key import algorithm names

PR-URL: https://github.com/nodejs/node/pull/37305
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Filip Skokan
2021-02-10 12:22:52 +01:00
committed by Antoine du Hamel
parent 83cce8717f
commit 8ddee73efc
4 changed files with 125 additions and 14 deletions

View File

@@ -1697,8 +1697,8 @@ added: v15.8.0
* Type: {boolean}
The `public` parameter is used to specify that the key is to be interpreted
as a public key.
The `public` parameter is used to specify that the `'raw'` format key is to be
interpreted as a public key. **Default:** `false`.
### `NODE-SCRYPT` Algorithm
<!-- YAML

View File

@@ -253,10 +253,6 @@ async function ecImportKey(
namedCurve,
'algorithm.namedCurve',
ObjectKeys(kNamedCurveAliases));
// Only used for NODE-EDnnnn key variants to distinguish between
// importing a raw public key or raw private key.
if (algorithm.public !== undefined)
validateBoolean(algorithm.public, 'algorithm.public');
let keyObject;
const usagesSet = new SafeSet(keyUsages);
let checkNamedCurve = true;
@@ -266,6 +262,24 @@ async function ecImportKey(
throw new ERR_INVALID_ARG_TYPE('keyData', 'KeyObject', keyData);
if (keyData.type === 'secret')
throw lazyDOMException('Invalid key type', 'InvalidAccessException');
switch (namedCurve) {
case 'NODE-X25519':
// Fall through
case 'NODE-X448':
checkNamedCurve = false;
if (algorithm.name !== 'ECDH')
throw lazyDOMException('Invalid algorithm name.', 'DataError');
break;
case 'NODE-ED25519':
// Fall through
case 'NODE-ED448':
checkNamedCurve = false;
if (algorithm.name !== namedCurve)
throw lazyDOMException('Invalid algorithm name.', 'DataError');
break;
}
verifyAcceptableEcKeyUse(name, keyData.type, usagesSet);
keyObject = keyData;
break;
@@ -296,8 +310,24 @@ async function ecImportKey(
case 'OKP': {
checkNamedCurve = false;
const isPublic = keyData.d === undefined;
const type =
namedCurve === 'NODE-X25519' || 'NODE-X448' ? 'ECDH' : 'ECDSA';
let type;
switch (namedCurve) {
case 'NODE-ED25519':
// Fall through
case 'NODE-ED448':
type = `NODE-${keyData.crv.toUpperCase()}`;
break;
case 'NODE-X25519':
// Fall through
case 'NODE-X448':
type = 'ECDH';
break;
}
if (algorithm.name !== type)
throw lazyDOMException('Invalid algorithm name.', 'DataError');
verifyAcceptableEcKeyUse(
type,
isPublic ? 'public' : 'private',
@@ -364,8 +394,12 @@ async function ecImportKey(
// Fall through
case 'NODE-X448':
checkNamedCurve = false;
if (algorithm.public !== undefined)
validateBoolean(algorithm.public, 'algorithm.public');
if (algorithm.name !== 'ECDH')
throw lazyDOMException('Invalid algorithm name.', 'DataError');
verifyAcceptableEcKeyUse(
'ECDH',
algorithm.name,
algorithm.public === true ? 'public' : 'private',
usagesSet);
keyObject = createECRawKey(namedCurve, keyData, algorithm.public);
@@ -374,8 +408,12 @@ async function ecImportKey(
// Fall through
case 'NODE-ED448':
checkNamedCurve = false;
if (algorithm.public !== undefined)
validateBoolean(algorithm.public, 'algorithm.public');
if (algorithm.name !== namedCurve)
throw lazyDOMException('Invalid algorithm name.', 'DataError');
verifyAcceptableEcKeyUse(
'ECDSA',
algorithm.name,
algorithm.public === true ? 'public' : 'private',
usagesSet);
keyObject = createECRawKey(namedCurve, keyData, algorithm.public);

View File

@@ -6,7 +6,10 @@ if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const { subtle } = require('crypto').webcrypto;
const {
generateKeyPairSync,
webcrypto: { subtle }
} = require('crypto');
async function generateKey(namedCurve) {
return subtle.generateKey(
@@ -365,3 +368,52 @@ assert.rejects(
{
message: /Unsupported named curves for ECDSA/
});
{
for (const asymmetricKeyType of ['ed25519', 'ed448']) {
const { publicKey, privateKey } = generateKeyPairSync(asymmetricKeyType);
for (const keyObject of [publicKey, privateKey]) {
const namedCurve = `NODE-${asymmetricKeyType.toUpperCase()}`;
subtle.importKey(
'node.keyObject',
keyObject,
{ name: namedCurve, namedCurve },
true,
keyObject.type === 'private' ? ['sign'] : ['verify'],
).then((cryptoKey) => {
assert.strictEqual(cryptoKey.type, keyObject.type);
assert.strictEqual(cryptoKey.algorithm.name, namedCurve);
}, common.mustNotCall());
assert.rejects(
subtle.importKey(
'node.keyObject',
keyObject,
{
name: 'ECDSA',
namedCurve,
},
true,
keyObject.type === 'private' ? ['sign'] : ['verify']
),
{
message: /Invalid algorithm name/
});
assert.rejects(
subtle.importKey(
'node.keyObject',
keyObject,
{
name: 'ECDH',
namedCurve,
},
true,
keyObject.type === 'private' ? ['deriveBits', 'deriveKey'] : [],
),
{
message: /Invalid algorithm name/
});
}
}
}

View File

@@ -6,7 +6,11 @@ if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const { subtle } = require('crypto').webcrypto;
const {
generateKeyPairSync,
webcrypto: { subtle }
} = require('crypto');
// X25519 and X448 are ECDH named curves that should work
// with the existing ECDH mechanisms with no additional
@@ -260,7 +264,7 @@ assert.rejects(
namedCurve: 'NODE-X25519'
},
true,
['deriveBits']).then(common.mustCall());
['deriveBits']).then(common.mustCall(), common.mustNotCall());
// Public JWK import
subtle.importKey(
@@ -275,5 +279,22 @@ assert.rejects(
namedCurve: 'NODE-X25519'
},
true,
[]).then(common.mustCall());
[]).then(common.mustCall(), common.mustNotCall());
for (const asymmetricKeyType of ['x25519', 'x448']) {
const { publicKey, privateKey } = generateKeyPairSync(asymmetricKeyType);
for (const keyObject of [publicKey, privateKey]) {
const namedCurve = `NODE-${asymmetricKeyType.toUpperCase()}`;
subtle.importKey(
'node.keyObject',
keyObject,
{ name: 'ECDH', namedCurve },
true,
keyObject.type === 'private' ? ['deriveBits', 'deriveKey'] : [],
).then((cryptoKey) => {
assert.strictEqual(cryptoKey.type, keyObject.type);
assert.strictEqual(cryptoKey.algorithm.name, 'ECDH');
}, common.mustNotCall());
}
}
}