Skip to content

Commit 291d9e9

Browse files
panvatargos
authored andcommitted
crypto: check ed/x webcrypto key import algorithm names
PR-URL: #37305 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent bb81acc commit 291d9e9

File tree

4 files changed

+125
-14
lines changed

4 files changed

+125
-14
lines changed

doc/api/webcrypto.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1697,8 +1697,8 @@ added: v15.8.0
16971697

16981698
* Type: {boolean}
16991699

1700-
The `public` parameter is used to specify that the key is to be interpreted
1701-
as a public key.
1700+
The `public` parameter is used to specify that the `'raw'` format key is to be
1701+
interpreted as a public key. **Default:** `false`.
17021702

17031703
### `NODE-SCRYPT` Algorithm
17041704
<!-- YAML

lib/internal/crypto/ec.js

+46-8
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,6 @@ async function ecImportKey(
253253
namedCurve,
254254
'algorithm.namedCurve',
255255
ObjectKeys(kNamedCurveAliases));
256-
// Only used for NODE-EDnnnn key variants to distinguish between
257-
// importing a raw public key or raw private key.
258-
if (algorithm.public !== undefined)
259-
validateBoolean(algorithm.public, 'algorithm.public');
260256
let keyObject;
261257
const usagesSet = new SafeSet(keyUsages);
262258
let checkNamedCurve = true;
@@ -266,6 +262,24 @@ async function ecImportKey(
266262
throw new ERR_INVALID_ARG_TYPE('keyData', 'KeyObject', keyData);
267263
if (keyData.type === 'secret')
268264
throw lazyDOMException('Invalid key type', 'InvalidAccessException');
265+
266+
switch (namedCurve) {
267+
case 'NODE-X25519':
268+
// Fall through
269+
case 'NODE-X448':
270+
checkNamedCurve = false;
271+
if (algorithm.name !== 'ECDH')
272+
throw lazyDOMException('Invalid algorithm name.', 'DataError');
273+
break;
274+
case 'NODE-ED25519':
275+
// Fall through
276+
case 'NODE-ED448':
277+
checkNamedCurve = false;
278+
if (algorithm.name !== namedCurve)
279+
throw lazyDOMException('Invalid algorithm name.', 'DataError');
280+
break;
281+
}
282+
269283
verifyAcceptableEcKeyUse(name, keyData.type, usagesSet);
270284
keyObject = keyData;
271285
break;
@@ -296,8 +310,24 @@ async function ecImportKey(
296310
case 'OKP': {
297311
checkNamedCurve = false;
298312
const isPublic = keyData.d === undefined;
299-
const type =
300-
namedCurve === 'NODE-X25519' || 'NODE-X448' ? 'ECDH' : 'ECDSA';
313+
314+
let type;
315+
switch (namedCurve) {
316+
case 'NODE-ED25519':
317+
// Fall through
318+
case 'NODE-ED448':
319+
type = `NODE-${keyData.crv.toUpperCase()}`;
320+
break;
321+
case 'NODE-X25519':
322+
// Fall through
323+
case 'NODE-X448':
324+
type = 'ECDH';
325+
break;
326+
}
327+
328+
if (algorithm.name !== type)
329+
throw lazyDOMException('Invalid algorithm name.', 'DataError');
330+
301331
verifyAcceptableEcKeyUse(
302332
type,
303333
isPublic ? 'public' : 'private',
@@ -364,8 +394,12 @@ async function ecImportKey(
364394
// Fall through
365395
case 'NODE-X448':
366396
checkNamedCurve = false;
397+
if (algorithm.public !== undefined)
398+
validateBoolean(algorithm.public, 'algorithm.public');
399+
if (algorithm.name !== 'ECDH')
400+
throw lazyDOMException('Invalid algorithm name.', 'DataError');
367401
verifyAcceptableEcKeyUse(
368-
'ECDH',
402+
algorithm.name,
369403
algorithm.public === true ? 'public' : 'private',
370404
usagesSet);
371405
keyObject = createECRawKey(namedCurve, keyData, algorithm.public);
@@ -374,8 +408,12 @@ async function ecImportKey(
374408
// Fall through
375409
case 'NODE-ED448':
376410
checkNamedCurve = false;
411+
if (algorithm.public !== undefined)
412+
validateBoolean(algorithm.public, 'algorithm.public');
413+
if (algorithm.name !== namedCurve)
414+
throw lazyDOMException('Invalid algorithm name.', 'DataError');
377415
verifyAcceptableEcKeyUse(
378-
'ECDSA',
416+
algorithm.name,
379417
algorithm.public === true ? 'public' : 'private',
380418
usagesSet);
381419
keyObject = createECRawKey(namedCurve, keyData, algorithm.public);

test/parallel/test-webcrypto-ed25519-ed448.js

+53-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ if (!common.hasCrypto)
66
common.skip('missing crypto');
77

88
const assert = require('assert');
9-
const { subtle } = require('crypto').webcrypto;
9+
const {
10+
generateKeyPairSync,
11+
webcrypto: { subtle }
12+
} = require('crypto');
1013

1114
async function generateKey(namedCurve) {
1215
return subtle.generateKey(
@@ -365,3 +368,52 @@ assert.rejects(
365368
{
366369
message: /Unsupported named curves for ECDSA/
367370
});
371+
372+
{
373+
for (const asymmetricKeyType of ['ed25519', 'ed448']) {
374+
const { publicKey, privateKey } = generateKeyPairSync(asymmetricKeyType);
375+
for (const keyObject of [publicKey, privateKey]) {
376+
const namedCurve = `NODE-${asymmetricKeyType.toUpperCase()}`;
377+
subtle.importKey(
378+
'node.keyObject',
379+
keyObject,
380+
{ name: namedCurve, namedCurve },
381+
true,
382+
keyObject.type === 'private' ? ['sign'] : ['verify'],
383+
).then((cryptoKey) => {
384+
assert.strictEqual(cryptoKey.type, keyObject.type);
385+
assert.strictEqual(cryptoKey.algorithm.name, namedCurve);
386+
}, common.mustNotCall());
387+
388+
assert.rejects(
389+
subtle.importKey(
390+
'node.keyObject',
391+
keyObject,
392+
{
393+
name: 'ECDSA',
394+
namedCurve,
395+
},
396+
true,
397+
keyObject.type === 'private' ? ['sign'] : ['verify']
398+
),
399+
{
400+
message: /Invalid algorithm name/
401+
});
402+
403+
assert.rejects(
404+
subtle.importKey(
405+
'node.keyObject',
406+
keyObject,
407+
{
408+
name: 'ECDH',
409+
namedCurve,
410+
},
411+
true,
412+
keyObject.type === 'private' ? ['deriveBits', 'deriveKey'] : [],
413+
),
414+
{
415+
message: /Invalid algorithm name/
416+
});
417+
}
418+
}
419+
}

test/parallel/test-webcrypto-x25519-x448.js

+24-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ if (!common.hasCrypto)
66
common.skip('missing crypto');
77

88
const assert = require('assert');
9-
const { subtle } = require('crypto').webcrypto;
9+
10+
const {
11+
generateKeyPairSync,
12+
webcrypto: { subtle }
13+
} = require('crypto');
1014

1115
// X25519 and X448 are ECDH named curves that should work
1216
// with the existing ECDH mechanisms with no additional
@@ -260,7 +264,7 @@ assert.rejects(
260264
namedCurve: 'NODE-X25519'
261265
},
262266
true,
263-
['deriveBits']).then(common.mustCall());
267+
['deriveBits']).then(common.mustCall(), common.mustNotCall());
264268

265269
// Public JWK import
266270
subtle.importKey(
@@ -275,5 +279,22 @@ assert.rejects(
275279
namedCurve: 'NODE-X25519'
276280
},
277281
true,
278-
[]).then(common.mustCall());
282+
[]).then(common.mustCall(), common.mustNotCall());
283+
284+
for (const asymmetricKeyType of ['x25519', 'x448']) {
285+
const { publicKey, privateKey } = generateKeyPairSync(asymmetricKeyType);
286+
for (const keyObject of [publicKey, privateKey]) {
287+
const namedCurve = `NODE-${asymmetricKeyType.toUpperCase()}`;
288+
subtle.importKey(
289+
'node.keyObject',
290+
keyObject,
291+
{ name: 'ECDH', namedCurve },
292+
true,
293+
keyObject.type === 'private' ? ['deriveBits', 'deriveKey'] : [],
294+
).then((cryptoKey) => {
295+
assert.strictEqual(cryptoKey.type, keyObject.type);
296+
assert.strictEqual(cryptoKey.algorithm.name, 'ECDH');
297+
}, common.mustNotCall());
298+
}
299+
}
279300
}

0 commit comments

Comments
 (0)