Skip to content

Commit 0cfc471

Browse files
authored
crypto: ensure expected JWK alg in SubtleCrypto.importKey RSA imports
PR-URL: #57450 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
1 parent 8b41272 commit 0cfc471

File tree

3 files changed

+74
-19
lines changed

3 files changed

+74
-19
lines changed

lib/internal/crypto/hashnames.js

+3-8
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
const {
44
ObjectKeys,
5-
StringPrototypeToLowerCase,
65
} = primordials;
76

87
const kHashContextNode = 1;
@@ -58,22 +57,18 @@ const kHashNames = {
5857
for (let n = 0; n < keys.length; n++) {
5958
const contexts = ObjectKeys(kHashNames[keys[n]]);
6059
for (let i = 0; i < contexts.length; i++) {
61-
const alias =
62-
StringPrototypeToLowerCase(kHashNames[keys[n]][contexts[i]]);
60+
const alias = kHashNames[keys[n]][contexts[i]];
6361
if (kHashNames[alias] === undefined)
6462
kHashNames[alias] = kHashNames[keys[n]];
6563
}
6664
}
6765
}
6866

6967
function normalizeHashName(name, context = kHashContextNode) {
70-
if (typeof name !== 'string')
71-
return name;
72-
name = StringPrototypeToLowerCase(name);
73-
const alias = kHashNames[name]?.[context];
74-
return alias || name;
68+
return kHashNames[name]?.[context];
7569
}
7670

71+
7772
normalizeHashName.kContextNode = kHashContextNode;
7873
normalizeHashName.kContextWebCrypto = kHashContextWebCrypto;
7974
normalizeHashName.kContextJwkRsa = kHashContextJwkRsa;

lib/internal/crypto/rsa.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -275,9 +275,13 @@ function rsaImportKey(
275275
}
276276

277277
if (keyData.alg !== undefined) {
278-
const hash =
279-
normalizeHashName(keyData.alg, normalizeHashName.kContextWebCrypto);
280-
if (hash !== algorithm.hash.name)
278+
const expected =
279+
normalizeHashName(algorithm.hash.name,
280+
algorithm.name === 'RSASSA-PKCS1-v1_5' ? normalizeHashName.kContextJwkRsa :
281+
algorithm.name === 'RSA-PSS' ? normalizeHashName.kContextJwkRsaPss :
282+
normalizeHashName.kContextJwkRsaOaep);
283+
284+
if (keyData.alg !== expected)
281285
throw lazyDOMException(
282286
'JWK "alg" does not match the requested algorithm',
283287
'DataError');

test/parallel/test-webcrypto-export-import-rsa.js

+64-8
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,19 @@ async function testImportJwk(
384384

385385
const jwk = keyData[size].jwk;
386386

387+
let alg;
388+
switch (name) {
389+
case 'RSA-PSS':
390+
alg = `PS${hash === 'SHA-1' ? 1 : hash.substring(4)}`;
391+
break;
392+
case 'RSA-OAEP':
393+
alg = `RSA-OAEP${hash === 'SHA-1' ? '' : hash.substring(3)}`;
394+
break;
395+
case 'RSASSA-PKCS1-v1_5':
396+
alg = `RS${hash === 'SHA-1' ? 1 : hash.substring(4)}`;
397+
break;
398+
}
399+
387400
const [
388401
publicKey,
389402
privateKey,
@@ -394,14 +407,14 @@ async function testImportJwk(
394407
kty: jwk.kty,
395408
n: jwk.n,
396409
e: jwk.e,
397-
alg: `PS${hash.substring(4)}`
410+
alg,
398411
},
399412
{ name, hash },
400413
extractable,
401414
publicUsages),
402415
subtle.importKey(
403416
'jwk',
404-
{ ...jwk, alg: `PS${hash.substring(4)}` },
417+
{ ...jwk, alg },
405418
{ name, hash },
406419
extractable,
407420
privateUsages),
@@ -435,6 +448,8 @@ async function testImportJwk(
435448

436449
assert.strictEqual(pubJwk.kty, 'RSA');
437450
assert.strictEqual(pvtJwk.kty, 'RSA');
451+
assert.strictEqual(pubJwk.alg, alg);
452+
assert.strictEqual(pvtJwk.alg, alg);
438453
assert.strictEqual(pubJwk.n, jwk.n);
439454
assert.strictEqual(pvtJwk.n, jwk.n);
440455
assert.strictEqual(pubJwk.e, jwk.e);
@@ -483,30 +498,71 @@ async function testImportJwk(
483498
}
484499

485500
{
486-
let invalidAlg = name === 'RSA-OAEP' ? name : name === 'RSA-PSS' ? 'PS' : 'RS';
501+
await assert.rejects(
502+
subtle.importKey(
503+
'jwk',
504+
{ kty: jwk.kty, n: jwk.n, e: jwk.e, alg: alg.toLowerCase() },
505+
{ name, hash },
506+
extractable,
507+
publicUsages),
508+
{ message: 'JWK "alg" does not match the requested algorithm' });
509+
await assert.rejects(
510+
subtle.importKey(
511+
'jwk',
512+
{ ...jwk, alg: alg.toLowerCase() },
513+
{ name, hash },
514+
extractable,
515+
privateUsages),
516+
{ message: 'JWK "alg" does not match the requested algorithm' });
517+
}
518+
519+
{
520+
let invalidAlgHash = name === 'RSA-OAEP' ? name : name === 'RSA-PSS' ? 'PS' : 'RS';
487521
switch (name) {
488522
case 'RSA-OAEP':
489523
if (hash === 'SHA-1')
490-
invalidAlg += '-256';
524+
invalidAlgHash += '-256';
491525
break;
492526
default:
493527
if (hash === 'SHA-256')
494-
invalidAlg += '384';
528+
invalidAlgHash += '384';
495529
else
496-
invalidAlg += '256';
530+
invalidAlgHash += '256';
497531
}
498532
await assert.rejects(
499533
subtle.importKey(
500534
'jwk',
501-
{ kty: jwk.kty, n: jwk.n, e: jwk.e, alg: invalidAlg },
535+
{ kty: jwk.kty, n: jwk.n, e: jwk.e, alg: invalidAlgHash },
502536
{ name, hash },
503537
extractable,
504538
publicUsages),
505539
{ message: 'JWK "alg" does not match the requested algorithm' });
506540
await assert.rejects(
507541
subtle.importKey(
508542
'jwk',
509-
{ ...jwk, alg: invalidAlg },
543+
{ ...jwk, alg: invalidAlgHash },
544+
{ name, hash },
545+
extractable,
546+
privateUsages),
547+
{ message: 'JWK "alg" does not match the requested algorithm' });
548+
}
549+
550+
{
551+
const invalidAlgType = name === 'RSA-PSS' ? `RS${hash.substring(4)}` : `PS${hash.substring(4)}`;
552+
await assert.rejects(
553+
subtle.importKey(
554+
'jwk',
555+
{ kty: jwk.kty, n: jwk.n, e: jwk.e, alg: invalidAlgType },
556+
{ name, hash },
557+
extractable,
558+
publicUsages),
559+
{ message: 'JWK "alg" does not match the requested algorithm' }).catch((e) => {
560+
throw e;
561+
});
562+
await assert.rejects(
563+
subtle.importKey(
564+
'jwk',
565+
{ ...jwk, alg: invalidAlgType },
510566
{ name, hash },
511567
extractable,
512568
privateUsages),

0 commit comments

Comments
 (0)