GeezerVison is showing github.com — crypto: fix webcrypto private/secret import with empty usages · nodejs/node@09fb74a
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/webcrypto.js +22-7Lines changed: 22 additions & 7 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -605,52 +605,67 @@ async function importKey( | 605 | 605 | }); | 606 | 606 |
| 607 | 607 | algorithm = normalizeAlgorithm(algorithm, 'importKey'); | | 608 | + let result; | 608 | 609 | switch (algorithm.name) { | 609 | 610 | case 'RSASSA-PKCS1-v1_5': | 610 | 611 | // Fall through | 611 | 612 | case 'RSA-PSS': | 612 | 613 | // Fall through | 613 | 614 | case 'RSA-OAEP': | 614 | | - return require('internal/crypto/rsa') | | 615 | + result = await require('internal/crypto/rsa') | 615 | 616 | .rsaImportKey(format, keyData, algorithm, extractable, keyUsages); | | 617 | + break; | 616 | 618 | case 'ECDSA': | 617 | 619 | // Fall through | 618 | 620 | case 'ECDH': | 619 | | - return require('internal/crypto/ec') | | 621 | + result = await require('internal/crypto/ec') | 620 | 622 | .ecImportKey(format, keyData, algorithm, extractable, keyUsages); | | 623 | + break; | 621 | 624 | case 'Ed25519': | 622 | 625 | // Fall through | 623 | 626 | case 'Ed448': | 624 | 627 | // Fall through | 625 | 628 | case 'X25519': | 626 | 629 | // Fall through | 627 | 630 | case 'X448': | 628 | | - return require('internal/crypto/cfrg') | | 631 | + result = await require('internal/crypto/cfrg') | 629 | 632 | .cfrgImportKey(format, keyData, algorithm, extractable, keyUsages); | | 633 | + break; | 630 | 634 | case 'HMAC': | 631 | | - return require('internal/crypto/mac') | | 635 | + result = await require('internal/crypto/mac') | 632 | 636 | .hmacImportKey(format, keyData, algorithm, extractable, keyUsages); | | 637 | + break; | 633 | 638 | case 'AES-CTR': | 634 | 639 | // Fall through | 635 | 640 | case 'AES-CBC': | 636 | 641 | // Fall through | 637 | 642 | case 'AES-GCM': | 638 | 643 | // Fall through | 639 | 644 | case 'AES-KW': | 640 | | - return require('internal/crypto/aes') | | 645 | + result = await require('internal/crypto/aes') | 641 | 646 | .aesImportKey(algorithm, format, keyData, extractable, keyUsages); | | 647 | + break; | 642 | 648 | case 'HKDF': | 643 | 649 | // Fall through | 644 | 650 | case 'PBKDF2': | 645 | | - return importGenericSecretKey( | | 651 | + result = await importGenericSecretKey( | 646 | 652 | algorithm, | 647 | 653 | format, | 648 | 654 | keyData, | 649 | 655 | extractable, | 650 | 656 | keyUsages); | | 657 | + break; | | 658 | + default: | | 659 | + throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError'); | 651 | 660 | } | 652 | 661 |
| 653 | | - throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError'); | | 662 | + if ((result.type === 'secret' || result.type === 'private') && result.usages.length === 0) { | | 663 | + throw lazyDOMException( | | 664 | + `Usages cannot be empty when importing a ${result.type} key.`, | | 665 | + 'SyntaxError'); | | 666 | + } | | 667 | + | | 668 | + return result; | 654 | 669 | } | 655 | 670 |
| 656 | 671 | // subtle.wrapKey() is essentially a subtle.exportKey() followed | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-webcrypto-export-import-cfrg.js +18Lines changed: 18 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -164,6 +164,15 @@ async function testImportPkcs8({ name, privateUsages }, extractable) { | 164 | 164 | message: /key is not extractable/ | 165 | 165 | }); | 166 | 166 | } | | 167 | + | | 168 | + await assert.rejects( | | 169 | + subtle.importKey( | | 170 | + 'pkcs8', | | 171 | + keyData[name].pkcs8, | | 172 | + { name }, | | 173 | + extractable, | | 174 | + [/* empty usages */]), | | 175 | + { name: 'SyntaxError', message: 'Usages cannot be empty when importing a private key.' }); | 167 | 176 | } | 168 | 177 |
| 169 | 178 | async function testImportJwk({ name, publicUsages, privateUsages }, extractable) { | @@ -311,6 +320,15 @@ async function testImportJwk({ name, publicUsages, privateUsages }, extractable) | 311 | 320 | publicUsages), | 312 | 321 | { message: 'JWK "crv" Parameter and algorithm name mismatch' }); | 313 | 322 | } | | 323 | + | | 324 | + await assert.rejects( | | 325 | + subtle.importKey( | | 326 | + 'jwk', | | 327 | + { ...jwk }, | | 328 | + { name }, | | 329 | + extractable, | | 330 | + [/* empty usages */]), | | 331 | + { name: 'SyntaxError', message: 'Usages cannot be empty when importing a private key.' }); | 314 | 332 | } | 315 | 333 |
| 316 | 334 | async function testImportRaw({ name, publicUsages }) { | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-webcrypto-export-import-ec.js +18Lines changed: 18 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -164,6 +164,15 @@ async function testImportPkcs8( | 164 | 164 | message: /key is not extractable/ | 165 | 165 | }); | 166 | 166 | } | | 167 | + | | 168 | + await assert.rejects( | | 169 | + subtle.importKey( | | 170 | + 'pkcs8', | | 171 | + keyData[namedCurve].pkcs8, | | 172 | + { name, namedCurve }, | | 173 | + extractable, | | 174 | + [/* empty usages */]), | | 175 | + { name: 'SyntaxError', message: 'Usages cannot be empty when importing a private key.' }); | 167 | 176 | } | 168 | 177 |
| 169 | 178 | async function testImportJwk( | @@ -312,6 +321,15 @@ async function testImportJwk( | 312 | 321 | privateUsages), | 313 | 322 | { message: 'JWK "crv" does not match the requested algorithm' }); | 314 | 323 | } | | 324 | + | | 325 | + await assert.rejects( | | 326 | + subtle.importKey( | | 327 | + 'jwk', | | 328 | + { ...jwk }, | | 329 | + { name, namedCurve }, | | 330 | + extractable, | | 331 | + [/* empty usages */]), | | 332 | + { name: 'SyntaxError', message: 'Usages cannot be empty when importing a private key.' }); | 315 | 333 | } | 316 | 334 |
| 317 | 335 | async function testImportRaw({ name, publicUsages }, namedCurve) { | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-webcrypto-export-import-rsa.js +18Lines changed: 18 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -361,6 +361,15 @@ async function testImportPkcs8( | 361 | 361 | message: /key is not extractable/ | 362 | 362 | }); | 363 | 363 | } | | 364 | + | | 365 | + await assert.rejects( | | 366 | + subtle.importKey( | | 367 | + 'pkcs8', | | 368 | + keyData[size].pkcs8, | | 369 | + { name, hash }, | | 370 | + extractable, | | 371 | + [/* empty usages */]), | | 372 | + { name: 'SyntaxError', message: 'Usages cannot be empty when importing a private key.' }); | 364 | 373 | } | 365 | 374 |
| 366 | 375 | async function testImportJwk( | @@ -495,6 +504,15 @@ async function testImportJwk( | 495 | 504 | privateUsages), | 496 | 505 | { message: 'JWK "alg" does not match the requested algorithm' }); | 497 | 506 | } | | 507 | + | | 508 | + await assert.rejects( | | 509 | + subtle.importKey( | | 510 | + 'jwk', | | 511 | + { ...jwk }, | | 512 | + { name, hash }, | | 513 | + extractable, | | 514 | + [/* empty usages */]), | | 515 | + { name: 'SyntaxError', message: 'Usages cannot be empty when importing a private key.' }); | 498 | 516 | } | 499 | 517 |
| 500 | 518 | // combinations to test | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-webcrypto-export-import.js +24Lines changed: 24 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -95,6 +95,18 @@ const { subtle } = globalThis.crypto; | 95 | 95 | assert.deepStrictEqual( | 96 | 96 | Buffer.from(jwk.k, 'base64').toString('hex'), | 97 | 97 | Buffer.from(raw).toString('hex')); | | 98 | + | | 99 | + await assert.rejects( | | 100 | + subtle.importKey( | | 101 | + 'raw', | | 102 | + keyData, | | 103 | + { | | 104 | + name: 'HMAC', | | 105 | + hash: 'SHA-256' | | 106 | + }, | | 107 | + true, | | 108 | + [/* empty usages */]), | | 109 | + { name: 'SyntaxError', message: 'Usages cannot be empty when importing a secret key.' }); | 98 | 110 | } | 99 | 111 |
| 100 | 112 | test().then(common.mustCall()); | @@ -125,6 +137,18 @@ const { subtle } = globalThis.crypto; | 125 | 137 | assert.deepStrictEqual( | 126 | 138 | Buffer.from(jwk.k, 'base64').toString('hex'), | 127 | 139 | Buffer.from(raw).toString('hex')); | | 140 | + | | 141 | + await assert.rejects( | | 142 | + subtle.importKey( | | 143 | + 'raw', | | 144 | + keyData, | | 145 | + { | | 146 | + name: 'AES-CTR', | | 147 | + length: 256, | | 148 | + }, | | 149 | + true, | | 150 | + [/* empty usages */]), | | 151 | + { name: 'SyntaxError', message: 'Usages cannot be empty when importing a secret key.' }); | 128 | 152 | } | 129 | 153 |
| 130 | 154 | test().then(common.mustCall()); | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-webcrypto-sign-verify-ecdsa.js -13Lines changed: 0 additions & 13 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -149,7 +149,6 @@ async function testSign({ name, | 149 | 149 | plaintext }) { | 150 | 150 | const [ | 151 | 151 | publicKey, | 152 | | - noSignPrivateKey, | 153 | 152 | privateKey, | 154 | 153 | hmacKey, | 155 | 154 | rsaKeys, | @@ -161,12 +160,6 @@ async function testSign({ name, | 161 | 160 | { name, namedCurve }, | 162 | 161 | false, | 163 | 162 | ['verify']), | 164 | | - subtle.importKey( | 165 | | - 'pkcs8', | 166 | | - privateKeyBuffer, | 167 | | - { name, namedCurve }, | 168 | | - false, | 169 | | - [ /* No usages */ ]), | 170 | 163 | subtle.importKey( | 171 | 164 | 'pkcs8', | 172 | 165 | privateKeyBuffer, | @@ -214,12 +207,6 @@ async function testSign({ name, | 214 | 207 | message: /Unable to use this key to sign/ | 215 | 208 | }); | 216 | 209 |
| 217 | | - // Test failure when no sign usage | 218 | | - await assert.rejects( | 219 | | - subtle.sign({ name, hash }, noSignPrivateKey, plaintext), { | 220 | | - message: /Unable to use this key to sign/ | 221 | | - }); | 222 | | - | 223 | 210 | // Test failure when using the wrong algorithms | 224 | 211 | await assert.rejects( | 225 | 212 | subtle.sign({ name, hash }, hmacKey, plaintext), { | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-webcrypto-sign-verify-eddsa.js -13Lines changed: 0 additions & 13 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -131,7 +131,6 @@ async function testSign({ name, | 131 | 131 | data }) { | 132 | 132 | const [ | 133 | 133 | publicKey, | 134 | | - noSignPrivateKey, | 135 | 134 | privateKey, | 136 | 135 | hmacKey, | 137 | 136 | rsaKeys, | @@ -143,12 +142,6 @@ async function testSign({ name, | 143 | 142 | { name }, | 144 | 143 | false, | 145 | 144 | ['verify']), | 146 | | - subtle.importKey( | 147 | | - 'pkcs8', | 148 | | - privateKeyBuffer, | 149 | | - { name }, | 150 | | - false, | 151 | | - [ /* No usages */ ]), | 152 | 145 | subtle.importKey( | 153 | 146 | 'pkcs8', | 154 | 147 | privateKeyBuffer, | @@ -197,12 +190,6 @@ async function testSign({ name, | 197 | 190 | message: /Unable to use this key to sign/ | 198 | 191 | }); | 199 | 192 |
| 200 | | - // Test failure when no sign usage | 201 | | - await assert.rejects( | 202 | | - subtle.sign({ name }, noSignPrivateKey, data), { | 203 | | - message: /Unable to use this key to sign/ | 204 | | - }); | 205 | | - | 206 | 193 | // Test failure when using the wrong algorithms | 207 | 194 | await assert.rejects( | 208 | 195 | subtle.sign({ name }, hmacKey, data), { | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-webcrypto-sign-verify-hmac.js +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -31,7 +31,7 @@ async function testVerify({ hash, | 31 | 31 | keyBuffer, | 32 | 32 | { name, hash }, | 33 | 33 | false, | 34 | | - [ /* No usages */ ]), | | 34 | + ['sign']), | 35 | 35 | subtle.generateKey( | 36 | 36 | { | 37 | 37 | name: 'RSA-PSS', | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-webcrypto-sign-verify-rsa.js -13Lines changed: 0 additions & 13 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -132,7 +132,6 @@ async function testSign({ | 132 | 132 | }) { | 133 | 133 | const [ | 134 | 134 | publicKey, | 135 | | - noSignPrivateKey, | 136 | 135 | privateKey, | 137 | 136 | hmacKey, | 138 | 137 | ecdsaKeys, | @@ -143,12 +142,6 @@ async function testSign({ | 143 | 142 | { name: algorithm.name, hash }, | 144 | 143 | false, | 145 | 144 | ['verify']), | 146 | | - subtle.importKey( | 147 | | - 'pkcs8', | 148 | | - privateKeyBuffer, | 149 | | - { name: algorithm.name, hash }, | 150 | | - false, | 151 | | - [ /* No usages */ ]), | 152 | 145 | subtle.importKey( | 153 | 146 | 'pkcs8', | 154 | 147 | privateKeyBuffer, | @@ -189,12 +182,6 @@ async function testSign({ | 189 | 182 | message: /Unable to use this key to sign/ | 190 | 183 | }); | 191 | 184 |
| 192 | | - // Test failure when no sign usage | 193 | | - await assert.rejects( | 194 | | - subtle.sign(algorithm, noSignPrivateKey, plaintext), { | 195 | | - message: /Unable to use this key to sign/ | 196 | | - }); | 197 | | - | 198 | 185 | // Test failure when using the wrong algorithms | 199 | 186 | await assert.rejects( | 200 | 187 | subtle.sign(algorithm, hmacKey, plaintext), { | |
|