Skip to content

Commit 247d017

Browse files
panvatargos
authored andcommitted
crypto: add SHAKE Web Cryptography digest algorithms
PR-URL: #59365 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ethan Arrowood <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Joyee Cheung <[email protected]>
1 parent f4fbcca commit 247d017

File tree

12 files changed

+197
-46
lines changed

12 files changed

+197
-46
lines changed

‎doc/api/webcrypto.md‎

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
<!-- YAML
44
changes:
5+
- version: REPLACEME
6+
pr-url: https://github.com/nodejs/node/pull/59365
7+
description: SHAKE algorithms are now supported.
58
- version: REPLACEME
69
pr-url: https://github.com/nodejs/node/pull/59365
710
description: ML-DSA algorithms are now supported.
@@ -91,6 +94,8 @@ WICG proposal:
9194

9295
Algorithms:
9396

97+
*`'cSHAKE128'`
98+
*`'cSHAKE256'`
9499
*`'ML-DSA-44'`[^openssl35]
95100
*`'ML-DSA-65'`[^openssl35]
96101
*`'ML-DSA-87'`[^openssl35]
@@ -472,6 +477,8 @@ implementation and the APIs supported for each:
472477
| `'AES-CTR'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | |
473478
| `'AES-GCM'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | |
474479
| `'AES-KW'` | ✔ | ✔ | ✔ | | | ✔ | ✔ | | | | | |
480+
| `'cSHAKE128'`[^modern-algos] | | | | | | | | | | | | ✔ |
481+
| `'cSHAKE256'`[^modern-algos] | | | | | | | | | | | | ✔ |
475482
| `'ECDH'` | ✔ | ✔ | ✔ | | | | | ✔ | ✔ | | | |
476483
| `'ECDSA'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
477484
| `'Ed25519'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
@@ -800,9 +807,13 @@ The algorithms currently supported include:
800807
801808
<!-- YAML
802809
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.
803814
-->
804815
805-
* `algorithm`{string|Algorithm}
816+
* `algorithm`{string|Algorithm|CShakeParams}
806817
* `data`{ArrayBuffer|TypedArray|DataView|Buffer}
807818
* Returns:{Promise} Fulfills with an{ArrayBuffer} upon success.
808819
@@ -812,6 +823,8 @@ with an{ArrayBuffer} containing the computed digest.
812823
813824
If `algorithm` is provided as a{string}, it must be one of:
814825
826+
* `'cSHAKE128'`[^modern-algos]
827+
* `'cSHAKE256'`[^modern-algos]
815828
* `'SHA-1'`
816829
* `'SHA-256'`
817830
* `'SHA-384'`
@@ -1423,6 +1436,53 @@ the message.
14231436
The Node.js Web Crypto API implementation only supports zero-length context
14241437
which is equivalent to not providing context at all.
14251438
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+
14261486
### Class: `EcdhKeyDeriveParams`
14271487
14281488
<!-- YAML

‎lib/internal/crypto/hash.js‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,15 @@ async function asyncDigest(algorithm, data){
211211
case'SHA-384':
212212
// Fall through
213213
case'SHA-512':
214+
// Fall through
215+
case'cSHAKE128':
216+
// Fall through
217+
case'cSHAKE256':
214218
returnjobPromise(()=>newHashJob(
215219
kCryptoJobAsync,
216220
normalizeHashName(algorithm.name),
217-
data));
221+
data,
222+
algorithm.length));
218223
}
219224

220225
throwlazyDOMException('Unrecognized algorithm name','NotSupportedError');

‎lib/internal/crypto/hashnames.js‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ const kHashNames ={
4949
[kHashContextJwkRsaOaep]: 'RSA-OAEP-512',
5050
[kHashContextJwkHmac]: 'HS512',
5151
},
52+
shake128: {
53+
[kHashContextNode]: 'shake128',
54+
[kHashContextWebCrypto]: 'cSHAKE128',
55+
},
56+
shake256: {
57+
[kHashContextNode]: 'shake256',
58+
[kHashContextWebCrypto]: 'cSHAKE256',
59+
},
5260
};
5361

5462
{

‎lib/internal/crypto/mac.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ async function hmacGenerateKey(algorithm, extractable, keyUsages){
6262

6363
returnnewInternalCryptoKey(
6464
key,
65-
{ name, length,hash: {name: hash.name}},
65+
{ name, length, hash },
6666
ArrayFrom(usageSet),
6767
extractable);
6868
}

‎lib/internal/crypto/rsa.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ async function rsaKeyGenerate(
161161
name,
162162
modulusLength,
163163
publicExponent,
164-
hash: {name: hash.name},
164+
hash,
165165
};
166166

167167
letpublicUsages;

‎lib/internal/crypto/util.js‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ const experimentalAlgorithms = ObjectEntries({
285285
importKey: null,
286286
exportKey: null,
287287
},
288+
'cSHAKE128': {digest: 'CShakeParams'},
289+
'cSHAKE256': {digest: 'CShakeParams'},
288290
});
289291

290292
for(const{0: algorithm,1: nid}of[
@@ -338,6 +340,10 @@ const simpleAlgorithmDictionaries ={
338340
RsaOaepParams: {label: 'BufferSource'},
339341
RsaHashedImportParams: {hash: 'HashAlgorithmIdentifier'},
340342
EcKeyImportParams: {},
343+
CShakeParams: {
344+
functionName: 'BufferSource',
345+
customization: 'BufferSource',
346+
},
341347
};
342348

343349
functionvalidateMaxBufferLength(data,name){

‎lib/internal/crypto/webidl.js‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,16 @@ converters.object = (V, opts) =>{
192192

193193
constisNonSharedArrayBuffer=isArrayBuffer;
194194

195+
functionensureSHA(V,label){
196+
if(
197+
typeofV==='string' ?
198+
!V.toLowerCase().startsWith('sha') :
199+
V.name?.toLowerCase?.().startsWith('sha')===false
200+
)
201+
throwlazyDOMException(
202+
`Only SHA hashes are supported in ${label}`,'NotSupportedError');
203+
}
204+
195205
converters.Uint8Array=(V,opts=kEmptyObject)=>{
196206
if(!ArrayBufferIsView(V)||
197207
TypedArrayPrototypeGetSymbolToStringTag(V)!=='Uint8Array'){
@@ -393,6 +403,7 @@ converters.RsaHashedKeyGenParams = createDictionaryConverter(
393403
{
394404
key: 'hash',
395405
converter: converters.HashAlgorithmIdentifier,
406+
validator: (V,dict)=>ensureSHA(V,'RsaHashedKeyGenParams'),
396407
required: true,
397408
},
398409
]);
@@ -403,6 +414,7 @@ converters.RsaHashedImportParams = createDictionaryConverter(
403414
{
404415
key: 'hash',
405416
converter: converters.HashAlgorithmIdentifier,
417+
validator: (V,dict)=>ensureSHA(V,'RsaHashedImportParams'),
406418
required: true,
407419
},
408420
]);
@@ -449,6 +461,7 @@ converters.HmacKeyGenParams = createDictionaryConverter(
449461
{
450462
key: 'hash',
451463
converter: converters.HashAlgorithmIdentifier,
464+
validator: (V,dict)=>ensureSHA(V,'HmacKeyGenParams'),
452465
required: true,
453466
},
454467
{
@@ -503,6 +516,7 @@ converters.EcdsaParams = createDictionaryConverter(
503516
{
504517
key: 'hash',
505518
converter: converters.HashAlgorithmIdentifier,
519+
validator: (V,dict)=>ensureSHA(V,'EcdsaParams'),
506520
required: true,
507521
},
508522
]);
@@ -513,6 +527,7 @@ converters.HmacImportParams = createDictionaryConverter(
513527
{
514528
key: 'hash',
515529
converter: converters.HashAlgorithmIdentifier,
530+
validator: (V,dict)=>ensureSHA(V,'HmacImportParams'),
516531
required: true,
517532
},
518533
{
@@ -573,6 +588,7 @@ converters.HkdfParams = createDictionaryConverter(
573588
{
574589
key: 'hash',
575590
converter: converters.HashAlgorithmIdentifier,
591+
validator: (V,dict)=>ensureSHA(V,'HkdfParams'),
576592
required: true,
577593
},
578594
{
@@ -587,12 +603,40 @@ converters.HkdfParams = createDictionaryConverter(
587603
},
588604
]);
589605

606+
converters.CShakeParams=createDictionaryConverter(
607+
'CShakeParams',[
608+
...newSafeArrayIterator(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+
throwlazyDOMException('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+
590633
converters.Pbkdf2Params=createDictionaryConverter(
591634
'Pbkdf2Params',[
592635
...newSafeArrayIterator(dictAlgorithm),
593636
{
594637
key: 'hash',
595638
converter: converters.HashAlgorithmIdentifier,
639+
validator: (V,dict)=>ensureSHA(V,'Pbkdf2Params'),
596640
required: true,
597641
},
598642
{

‎test/parallel/test-webcrypto-derivebits-hkdf.js‎

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,6 @@ async function testDeriveBitsBadHash(
306306
hash: 'PBKDF2'
307307
},
308308
baseKeys[size],256),{
309-
message: /Unrecognizedalgorithmname/,
310309
name: 'NotSupportedError',
311310
}),
312311
]);
@@ -437,10 +436,7 @@ async function testDeriveKeyBadHash(
437436
keyType,
438437
true,
439438
usages),
440-
{
441-
message: /Unrecognizedalgorithmname/,
442-
name: 'NotSupportedError',
443-
}),
439+
{name: 'NotSupportedError'}),
444440
assert.rejects(
445441
subtle.deriveKey(
446442
{
@@ -451,10 +447,7 @@ async function testDeriveKeyBadHash(
451447
keyType,
452448
true,
453449
usages),
454-
{
455-
message: /Unrecognizedalgorithmname/,
456-
name: 'NotSupportedError',
457-
}),
450+
{name: 'NotSupportedError'}),
458451
]);
459452
}
460453

0 commit comments

Comments
(0)