GeezerVison is showing github.com — crypto: add SHAKE Web Cryptography digest algorithms · nodejs/node@247d017
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: doc/api/webcrypto.md +61-1Lines changed: 61 additions & 1 deletion - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
| 2 | 2 |
| 3 | 3 | <!-- YAML | 4 | 4 | changes: | | 5 | + - version: REPLACEME | | 6 | + pr-url: https://github.com/nodejs/node/pull/59365 | | 7 | + description: SHAKE algorithms are now supported. | 5 | 8 | - version: REPLACEME | 6 | 9 | pr-url: https://github.com/nodejs/node/pull/59365 | 7 | 10 | description: ML-DSA algorithms are now supported. | @@ -91,6 +94,8 @@ WICG proposal: | 91 | 94 |
| 92 | 95 | Algorithms: | 93 | 96 |
| | 97 | +* `'cSHAKE128'` | | 98 | +* `'cSHAKE256'` | 94 | 99 | * `'ML-DSA-44'`[^openssl35] | 95 | 100 | * `'ML-DSA-65'`[^openssl35] | 96 | 101 | * `'ML-DSA-87'`[^openssl35] | @@ -472,6 +477,8 @@ implementation and the APIs supported for each: | 472 | 477 | | `'AES-CTR'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | | | 473 | 478 | | `'AES-GCM'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | | | 474 | 479 | | `'AES-KW'` | ✔ | ✔ | ✔ | | | ✔ | ✔ | | | | | | | | 480 | +| `'cSHAKE128'`[^modern-algos] | | | | | | | | | | | | ✔ | | | 481 | +| `'cSHAKE256'`[^modern-algos] | | | | | | | | | | | | ✔ | | 475 | 482 | | `'ECDH'` | ✔ | ✔ | ✔ | | | | | ✔ | ✔ | | | | | 476 | 483 | | `'ECDSA'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | | | 477 | 484 | | `'Ed25519'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | | | @@ -800,9 +807,13 @@ The algorithms currently supported include: | 800 | 807 |
| 801 | 808 | <!-- YAML | 802 | 809 | added: v15.0.0 | | 810 | +changes: | | 811 | + - version: REPLACEME | | 812 | + pr-url: https://github.com/nodejs/node/pull/59365 | | 813 | + description: SHAKE algorithms are now supported. | 803 | 814 | --> | 804 | 815 |
| 805 | | -* `algorithm` {string|Algorithm} | | 816 | +* `algorithm` {string|Algorithm|CShakeParams} | 806 | 817 | * `data` {ArrayBuffer|TypedArray|DataView|Buffer} | 807 | 818 | * Returns: {Promise} Fulfills with an {ArrayBuffer} upon success. | 808 | 819 |
| @@ -812,6 +823,8 @@ with an {ArrayBuffer} containing the computed digest. | 812 | 823 |
| 813 | 824 | If `algorithm` is provided as a {string}, it must be one of: | 814 | 825 |
| | 826 | +* `'cSHAKE128'`[^modern-algos] | | 827 | +* `'cSHAKE256'`[^modern-algos] | 815 | 828 | * `'SHA-1'` | 816 | 829 | * `'SHA-256'` | 817 | 830 | * `'SHA-384'` | @@ -1423,6 +1436,53 @@ the message. | 1423 | 1436 | The Node.js Web Crypto API implementation only supports zero-length context | 1424 | 1437 | which is equivalent to not providing context at all. | 1425 | 1438 |
| | 1439 | +### Class: `CShakeParams` | | 1440 | +
| | 1441 | +<!-- YAML | | 1442 | +added: REPLACEME | | 1443 | +--> | | 1444 | +
| | 1445 | +#### `cShakeParams.customization` | | 1446 | +
| | 1447 | +<!-- YAML | | 1448 | +added: REPLACEME | | 1449 | +--> | | 1450 | +
| | 1451 | +* Type: {ArrayBuffer|TypedArray|DataView|Buffer|undefined} | | 1452 | +
| | 1453 | +The `customization` member represents the customization string. | | 1454 | +The Node.js Web Crypto API implementation only supports zero-length customization | | 1455 | +which is equivalent to not providing customization at all. | | 1456 | +
| | 1457 | +#### `cShakeParams.functionName` | | 1458 | +
| | 1459 | +<!-- YAML | | 1460 | +added: REPLACEME | | 1461 | +--> | | 1462 | +
| | 1463 | +* Type: {ArrayBuffer|TypedArray|DataView|Buffer|undefined} | | 1464 | +
| | 1465 | +The `functionName` member represents represents the function name, used by NIST to define | | 1466 | +functions based on cSHAKE. | | 1467 | +The Node.js Web Crypto API implementation only supports zero-length functionName | | 1468 | +which is equivalent to not providing functionName at all. | | 1469 | +
| | 1470 | +#### `cShakeParams.length` | | 1471 | +
| | 1472 | +<!-- YAML | | 1473 | +added: REPLACEME | | 1474 | +--> | | 1475 | +
| | 1476 | +* Type: {number} represents the requested output length in bits. | | 1477 | +
| | 1478 | +#### `cShakeParams.name` | | 1479 | +
| | 1480 | +<!-- YAML | | 1481 | +added: REPLACEME | | 1482 | +--> | | 1483 | +
| | 1484 | +* Type: {string} Must be `'cSHAKE128'`[^modern-algos] or `'cSHAKE256'`[^modern-algos] | | 1485 | +
| 1426 | 1486 | ### Class: `EcdhKeyDeriveParams` | 1427 | 1487 |
| 1428 | 1488 | <!-- YAML | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/crypto/hash.js +6-1Lines changed: 6 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -211,10 +211,15 @@ async function asyncDigest(algorithm, data) { | 211 | 211 | case 'SHA-384': | 212 | 212 | // Fall through | 213 | 213 | case 'SHA-512': | | 214 | + // Fall through | | 215 | + case 'cSHAKE128': | | 216 | + // Fall through | | 217 | + case 'cSHAKE256': | 214 | 218 | return jobPromise(() => new HashJob( | 215 | 219 | kCryptoJobAsync, | 216 | 220 | normalizeHashName(algorithm.name), | 217 | | - data)); | | 221 | + data, | | 222 | + algorithm.length)); | 218 | 223 | } | 219 | 224 |
| 220 | 225 | throw lazyDOMException('Unrecognized algorithm name', 'NotSupportedError'); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/crypto/hashnames.js +8Lines changed: 8 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -49,6 +49,14 @@ const kHashNames = { | 49 | 49 | [kHashContextJwkRsaOaep]: 'RSA-OAEP-512', | 50 | 50 | [kHashContextJwkHmac]: 'HS512', | 51 | 51 | }, | | 52 | + shake128: { | | 53 | + [kHashContextNode]: 'shake128', | | 54 | + [kHashContextWebCrypto]: 'cSHAKE128', | | 55 | + }, | | 56 | + shake256: { | | 57 | + [kHashContextNode]: 'shake256', | | 58 | + [kHashContextWebCrypto]: 'cSHAKE256', | | 59 | + }, | 52 | 60 | }; | 53 | 61 |
| 54 | 62 | { | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/crypto/mac.js +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -62,7 +62,7 @@ async function hmacGenerateKey(algorithm, extractable, keyUsages) { | 62 | 62 |
| 63 | 63 | return new InternalCryptoKey( | 64 | 64 | key, | 65 | | - { name, length, hash: { name: hash.name } }, | | 65 | + { name, length, hash }, | 66 | 66 | ArrayFrom(usageSet), | 67 | 67 | extractable); | 68 | 68 | } | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/crypto/rsa.js +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -161,7 +161,7 @@ async function rsaKeyGenerate( | 161 | 161 | name, | 162 | 162 | modulusLength, | 163 | 163 | publicExponent, | 164 | | - hash: { name: hash.name }, | | 164 | + hash, | 165 | 165 | }; | 166 | 166 |
| 167 | 167 | let publicUsages; | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/crypto/util.js +6Lines changed: 6 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -285,6 +285,8 @@ const experimentalAlgorithms = ObjectEntries({ | 285 | 285 | importKey: null, | 286 | 286 | exportKey: null, | 287 | 287 | }, | | 288 | + 'cSHAKE128': { digest: 'CShakeParams' }, | | 289 | + 'cSHAKE256': { digest: 'CShakeParams' }, | 288 | 290 | }); | 289 | 291 |
| 290 | 292 | for (const { 0: algorithm, 1: nid } of [ | @@ -338,6 +340,10 @@ const simpleAlgorithmDictionaries = { | 338 | 340 | RsaOaepParams: { label: 'BufferSource' }, | 339 | 341 | RsaHashedImportParams: { hash: 'HashAlgorithmIdentifier' }, | 340 | 342 | EcKeyImportParams: {}, | | 343 | + CShakeParams: { | | 344 | + functionName: 'BufferSource', | | 345 | + customization: 'BufferSource', | | 346 | + }, | 341 | 347 | }; | 342 | 348 |
| 343 | 349 | function validateMaxBufferLength(data, name) { | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/crypto/webidl.js +44Lines changed: 44 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -192,6 +192,16 @@ converters.object = (V, opts) => { | 192 | 192 |
| 193 | 193 | const isNonSharedArrayBuffer = isArrayBuffer; | 194 | 194 |
| | 195 | +function ensureSHA(V, label) { | | 196 | + if ( | | 197 | + typeof V === 'string' ? | | 198 | + !V.toLowerCase().startsWith('sha') : | | 199 | + V.name?.toLowerCase?.().startsWith('sha') === false | | 200 | + ) | | 201 | + throw lazyDOMException( | | 202 | + `Only SHA hashes are supported in ${label}`, 'NotSupportedError'); | | 203 | +} | | 204 | + | 195 | 205 | converters.Uint8Array = (V, opts = kEmptyObject) => { | 196 | 206 | if (!ArrayBufferIsView(V) || | 197 | 207 | TypedArrayPrototypeGetSymbolToStringTag(V) !== 'Uint8Array') { | @@ -393,6 +403,7 @@ converters.RsaHashedKeyGenParams = createDictionaryConverter( | 393 | 403 | { | 394 | 404 | key: 'hash', | 395 | 405 | converter: converters.HashAlgorithmIdentifier, | | 406 | + validator: (V, dict) => ensureSHA(V, 'RsaHashedKeyGenParams'), | 396 | 407 | required: true, | 397 | 408 | }, | 398 | 409 | ]); | @@ -403,6 +414,7 @@ converters.RsaHashedImportParams = createDictionaryConverter( | 403 | 414 | { | 404 | 415 | key: 'hash', | 405 | 416 | converter: converters.HashAlgorithmIdentifier, | | 417 | + validator: (V, dict) => ensureSHA(V, 'RsaHashedImportParams'), | 406 | 418 | required: true, | 407 | 419 | }, | 408 | 420 | ]); | @@ -449,6 +461,7 @@ converters.HmacKeyGenParams = createDictionaryConverter( | 449 | 461 | { | 450 | 462 | key: 'hash', | 451 | 463 | converter: converters.HashAlgorithmIdentifier, | | 464 | + validator: (V, dict) => ensureSHA(V, 'HmacKeyGenParams'), | 452 | 465 | required: true, | 453 | 466 | }, | 454 | 467 | { | @@ -503,6 +516,7 @@ converters.EcdsaParams = createDictionaryConverter( | 503 | 516 | { | 504 | 517 | key: 'hash', | 505 | 518 | converter: converters.HashAlgorithmIdentifier, | | 519 | + validator: (V, dict) => ensureSHA(V, 'EcdsaParams'), | 506 | 520 | required: true, | 507 | 521 | }, | 508 | 522 | ]); | @@ -513,6 +527,7 @@ converters.HmacImportParams = createDictionaryConverter( | 513 | 527 | { | 514 | 528 | key: 'hash', | 515 | 529 | converter: converters.HashAlgorithmIdentifier, | | 530 | + validator: (V, dict) => ensureSHA(V, 'HmacImportParams'), | 516 | 531 | required: true, | 517 | 532 | }, | 518 | 533 | { | @@ -573,6 +588,7 @@ converters.HkdfParams = createDictionaryConverter( | 573 | 588 | { | 574 | 589 | key: 'hash', | 575 | 590 | converter: converters.HashAlgorithmIdentifier, | | 591 | + validator: (V, dict) => ensureSHA(V, 'HkdfParams'), | 576 | 592 | required: true, | 577 | 593 | }, | 578 | 594 | { | @@ -587,12 +603,40 @@ converters.HkdfParams = createDictionaryConverter( | 587 | 603 | }, | 588 | 604 | ]); | 589 | 605 |
| | 606 | +converters.CShakeParams = createDictionaryConverter( | | 607 | + 'CShakeParams', [ | | 608 | + ...new SafeArrayIterator(dictAlgorithm), | | 609 | + { | | 610 | + key: 'length', | | 611 | + converter: (V, opts) => | | 612 | + converters['unsigned long'](V, { ...opts, enforceRange: true }), | | 613 | + validator: (V, opts) => { | | 614 | + // The Web Crypto spec allows for SHAKE output length that are not multiples of | | 615 | + // 8. We don't. | | 616 | + if (V % 8) | | 617 | + throw lazyDOMException('Unsupported CShakeParams length', 'NotSupportedError'); | | 618 | + }, | | 619 | + required: true, | | 620 | + }, | | 621 | + { | | 622 | + key: 'functionName', | | 623 | + converter: converters.BufferSource, | | 624 | + validator: validateZeroLength('CShakeParams.functionName'), | | 625 | + }, | | 626 | + { | | 627 | + key: 'customization', | | 628 | + converter: converters.BufferSource, | | 629 | + validator: validateZeroLength('CShakeParams.customization'), | | 630 | + }, | | 631 | + ]); | | 632 | + | 590 | 633 | converters.Pbkdf2Params = createDictionaryConverter( | 591 | 634 | 'Pbkdf2Params', [ | 592 | 635 | ...new SafeArrayIterator(dictAlgorithm), | 593 | 636 | { | 594 | 637 | key: 'hash', | 595 | 638 | converter: converters.HashAlgorithmIdentifier, | | 639 | + validator: (V, dict) => ensureSHA(V, 'Pbkdf2Params'), | 596 | 640 | required: true, | 597 | 641 | }, | 598 | 642 | { | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-webcrypto-derivebits-hkdf.js +2-9Lines changed: 2 additions & 9 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -306,7 +306,6 @@ async function testDeriveBitsBadHash( | 306 | 306 | hash: 'PBKDF2' | 307 | 307 | }, | 308 | 308 | baseKeys[size], 256), { | 309 | | - message: /Unrecognized algorithm name/, | 310 | 309 | name: 'NotSupportedError', | 311 | 310 | }), | 312 | 311 | ]); | @@ -437,10 +436,7 @@ async function testDeriveKeyBadHash( | 437 | 436 | keyType, | 438 | 437 | true, | 439 | 438 | usages), | 440 | | - { | 441 | | - message: /Unrecognized algorithm name/, | 442 | | - name: 'NotSupportedError', | 443 | | - }), | | 439 | + { name: 'NotSupportedError' }), | 444 | 440 | assert.rejects( | 445 | 441 | subtle.deriveKey( | 446 | 442 | { | @@ -451,10 +447,7 @@ async function testDeriveKeyBadHash( | 451 | 447 | keyType, | 452 | 448 | true, | 453 | 449 | usages), | 454 | | - { | 455 | | - message: /Unrecognized algorithm name/, | 456 | | - name: 'NotSupportedError', | 457 | | - }), | | 450 | + { name: 'NotSupportedError' }), | 458 | 451 | ]); | 459 | 452 | } | 460 | 453 |
| |
|