GeezerVison is showing github.com — crypto: ensure expected JWK alg in SubtleCrypto.importKey RSA imports · nodejs/node@cc42ee8
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/crypto/hashnames.js +3-8Lines changed: 3 additions & 8 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 2 | 2 |
| 3 | 3 | const { | 4 | 4 | ObjectKeys, | 5 | | - StringPrototypeToLowerCase, | 6 | 5 | } = primordials; | 7 | 6 |
| 8 | 7 | const kHashContextNode = 1; | @@ -58,22 +57,18 @@ const kHashNames = { | 58 | 57 | for (let n = 0; n < keys.length; n++) { | 59 | 58 | const contexts = ObjectKeys(kHashNames[keys[n]]); | 60 | 59 | 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]]; | 63 | 61 | if (kHashNames[alias] === undefined) | 64 | 62 | kHashNames[alias] = kHashNames[keys[n]]; | 65 | 63 | } | 66 | 64 | } | 67 | 65 | } | 68 | 66 |
| 69 | 67 | 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]; | 75 | 69 | } | 76 | 70 |
| | 71 | + | 77 | 72 | normalizeHashName.kContextNode = kHashContextNode; | 78 | 73 | normalizeHashName.kContextWebCrypto = kHashContextWebCrypto; | 79 | 74 | normalizeHashName.kContextJwkRsa = kHashContextJwkRsa; | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/crypto/rsa.js +7-3Lines changed: 7 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -275,9 +275,13 @@ function rsaImportKey( | 275 | 275 | } | 276 | 276 |
| 277 | 277 | 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) | 281 | 285 | throw lazyDOMException( | 282 | 286 | 'JWK "alg" does not match the requested algorithm', | 283 | 287 | 'DataError'); | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-webcrypto-export-import-rsa.js +64-8Lines changed: 64 additions & 8 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -384,6 +384,19 @@ async function testImportJwk( | 384 | 384 |
| 385 | 385 | const jwk = keyData[size].jwk; | 386 | 386 |
| | 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 | + | 387 | 400 | const [ | 388 | 401 | publicKey, | 389 | 402 | privateKey, | @@ -394,14 +407,14 @@ async function testImportJwk( | 394 | 407 | kty: jwk.kty, | 395 | 408 | n: jwk.n, | 396 | 409 | e: jwk.e, | 397 | | - alg: `PS${hash.substring(4)}` | | 410 | + alg, | 398 | 411 | }, | 399 | 412 | { name, hash }, | 400 | 413 | extractable, | 401 | 414 | publicUsages), | 402 | 415 | subtle.importKey( | 403 | 416 | 'jwk', | 404 | | - { ...jwk, alg: `PS${hash.substring(4)}` }, | | 417 | + { ...jwk, alg }, | 405 | 418 | { name, hash }, | 406 | 419 | extractable, | 407 | 420 | privateUsages), | @@ -435,6 +448,8 @@ async function testImportJwk( | 435 | 448 |
| 436 | 449 | assert.strictEqual(pubJwk.kty, 'RSA'); | 437 | 450 | assert.strictEqual(pvtJwk.kty, 'RSA'); | | 451 | + assert.strictEqual(pubJwk.alg, alg); | | 452 | + assert.strictEqual(pvtJwk.alg, alg); | 438 | 453 | assert.strictEqual(pubJwk.n, jwk.n); | 439 | 454 | assert.strictEqual(pvtJwk.n, jwk.n); | 440 | 455 | assert.strictEqual(pubJwk.e, jwk.e); | @@ -483,30 +498,71 @@ async function testImportJwk( | 483 | 498 | } | 484 | 499 |
| 485 | 500 | { | 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'; | 487 | 521 | switch (name) { | 488 | 522 | case 'RSA-OAEP': | 489 | 523 | if (hash === 'SHA-1') | 490 | | - invalidAlg += '-256'; | | 524 | + invalidAlgHash += '-256'; | 491 | 525 | break; | 492 | 526 | default: | 493 | 527 | if (hash === 'SHA-256') | 494 | | - invalidAlg += '384'; | | 528 | + invalidAlgHash += '384'; | 495 | 529 | else | 496 | | - invalidAlg += '256'; | | 530 | + invalidAlgHash += '256'; | 497 | 531 | } | 498 | 532 | await assert.rejects( | 499 | 533 | subtle.importKey( | 500 | 534 | '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 }, | 502 | 536 | { name, hash }, | 503 | 537 | extractable, | 504 | 538 | publicUsages), | 505 | 539 | { message: 'JWK "alg" does not match the requested algorithm' }); | 506 | 540 | await assert.rejects( | 507 | 541 | subtle.importKey( | 508 | 542 | '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 }, | 510 | 566 | { name, hash }, | 511 | 567 | extractable, | 512 | 568 | privateUsages), | |
|