Skip to content

Commit 4fe383e

Browse files
panvatargos
authored andcommitted
crypto: support ML-DSA spki/pkcs8 key formats in Web Cryptography
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 93fc80a commit 4fe383e

File tree

4 files changed

+280
-22
lines changed

4 files changed

+280
-22
lines changed

‎doc/api/webcrypto.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -939,9 +939,9 @@ specification.
939939
| `'Ed25519'` | ✔ | ✔ | ✔ | ✔ | | ✔ | |
940940
| `'Ed448'`[^secure-curves] | ✔ | ✔ | ✔ | ✔ | | ✔ | |
941941
| `'HMAC'` | | | ✔ | ✔ | ✔ | | |
942-
| `'ML-DSA-44'`[^modern-algos] | | | ✔ | | | ✔ | ✔ |
943-
| `'ML-DSA-65'`[^modern-algos] | | | ✔ | | | ✔ | ✔ |
944-
| `'ML-DSA-87'`[^modern-algos] | | | ✔ | | | ✔ | ✔ |
942+
| `'ML-DSA-44'`[^modern-algos] | | ✔ | ✔ | | | ✔ | ✔ |
943+
| `'ML-DSA-65'`[^modern-algos] | | ✔ | ✔ | | | ✔ | ✔ |
944+
| `'ML-DSA-87'`[^modern-algos] | | ✔ | ✔ | | | ✔ | ✔ |
945945
| `'RSA-OAEP'` | ✔ | ✔ | ✔ | | | | |
946946
| `'RSA-PSS'` | ✔ | ✔ | ✔ | | | | |
947947
| `'RSASSA-PKCS1-v1_5'` | ✔ | ✔ | ✔ | | | | |
@@ -1070,9 +1070,9 @@ The algorithms currently supported include:
10701070
| `'Ed448'`[^secure-curves] | ✔ | ✔ | ✔ | ✔ | | ✔ | |
10711071
| `'HDKF'` | | | | ✔ | ✔ | | |
10721072
| `'HMAC'` | | | ✔ | ✔ | ✔ | | |
1073-
| `'ML-DSA-44'`[^modern-algos] | | | ✔ | | | ✔ | ✔ |
1074-
| `'ML-DSA-65'`[^modern-algos] | | | ✔ | | | ✔ | ✔ |
1075-
| `'ML-DSA-87'`[^modern-algos] | | | ✔ | | | ✔ | ✔ |
1073+
| `'ML-DSA-44'`[^modern-algos] | | ✔ | ✔ | | | ✔ | ✔ |
1074+
| `'ML-DSA-65'`[^modern-algos] | | ✔ | ✔ | | | ✔ | ✔ |
1075+
| `'ML-DSA-87'`[^modern-algos] | | ✔ | ✔ | | | ✔ | ✔ |
10761076
| `'PBKDF2'` | | | | ✔ | ✔ | | |
10771077
| `'RSA-OAEP'` | ✔ | ✔ | ✔ | | | | |
10781078
| `'RSA-PSS'` | ✔ | ✔ | ✔ | | | | |

‎lib/internal/crypto/ml_dsa.js‎

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const{
44
SafeSet,
5+
Uint8Array,
56
}=primordials;
67

78
const{ Buffer }=require('buffer');
@@ -14,6 +15,10 @@ const{
1415
kKeyTypePublic,
1516
kSignJobModeSign,
1617
kSignJobModeVerify,
18+
kKeyFormatDER,
19+
kWebCryptoKeyFormatRaw,
20+
kWebCryptoKeyFormatPKCS8,
21+
kWebCryptoKeyFormatSPKI,
1722
}=internalBinding('crypto');
1823

1924
const{
@@ -44,6 +49,7 @@ const{
4449
InternalCryptoKey,
4550
PrivateKeyObject,
4651
PublicKeyObject,
52+
createPrivateKey,
4753
createPublicKey,
4854
}=require('internal/crypto/keys');
4955

@@ -106,15 +112,47 @@ async function mlDsaGenerateKey(algorithm, extractable, keyUsages){
106112
return{__proto__: null, privateKey, publicKey };
107113
}
108114

109-
functionmlDsaExportKey(key){
115+
functionmlDsaExportKey(key,format){
110116
try{
111-
if(key.type==='private'){
112-
const{ priv }=key[kKeyObject][kHandle].exportJwk({},false);
113-
returnBuffer.alloc(32,priv,'base64url').buffer;
114-
}
117+
switch(format){
118+
casekWebCryptoKeyFormatRaw: {
119+
if(key.type==='private'){
120+
const{ priv }=key[kKeyObject][kHandle].exportJwk({},false);
121+
returnBuffer.alloc(32,priv,'base64url').buffer;
122+
}
115123

116-
const{ pub }=key[kKeyObject][kHandle].exportJwk({},false);
117-
returnBuffer.alloc(Buffer.byteLength(pub,'base64url'),pub,'base64url').buffer;
124+
const{ pub }=key[kKeyObject][kHandle].exportJwk({},false);
125+
returnBuffer.alloc(Buffer.byteLength(pub,'base64url'),pub,'base64url').buffer;
126+
}
127+
casekWebCryptoKeyFormatSPKI: {
128+
returnkey[kKeyObject][kHandle].export(kKeyFormatDER,kWebCryptoKeyFormatSPKI).buffer;
129+
}
130+
casekWebCryptoKeyFormatPKCS8: {
131+
const{ priv }=key[kKeyObject][kHandle].exportJwk({},false);
132+
constseed=Buffer.alloc(32,priv,'base64url');
133+
constbuffer=newUint8Array(54);
134+
buffer.set([
135+
0x30,0x34,0x02,0x01,0x00,0x30,0x0B,0x06,
136+
0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,
137+
0x03,0x00,0x04,0x22,0x80,0x20,
138+
],0);
139+
switch(key.algorithm.name){
140+
case'ML-DSA-44':
141+
buffer.set([0x11],17);
142+
break;
143+
case'ML-DSA-65':
144+
buffer.set([0x12],17);
145+
break;
146+
case'ML-DSA-87':
147+
buffer.set([0x13],17);
148+
break;
149+
}
150+
buffer.set(seed,22);
151+
returnbuffer.buffer;
152+
}
153+
default:
154+
returnundefined;
155+
}
118156
}catch(err){
119157
throwlazyDOMException(
120158
'The operation failed for an operation-specific reason',
@@ -138,6 +176,34 @@ function mlDsaImportKey(
138176
keyObject=keyData;
139177
break;
140178
}
179+
case'spki': {
180+
verifyAcceptableMlDsaKeyUse(name,true,usagesSet);
181+
try{
182+
keyObject=createPublicKey({
183+
key: keyData,
184+
format: 'der',
185+
type: 'spki',
186+
});
187+
}catch(err){
188+
throwlazyDOMException(
189+
'Invalid keyData',{name: 'DataError',cause: err});
190+
}
191+
break;
192+
}
193+
case'pkcs8': {
194+
verifyAcceptableMlDsaKeyUse(name,false,usagesSet);
195+
try{
196+
keyObject=createPrivateKey({
197+
key: keyData,
198+
format: 'der',
199+
type: 'pkcs8',
200+
});
201+
}catch(err){
202+
throwlazyDOMException(
203+
'Invalid keyData',{name: 'DataError',cause: err});
204+
}
205+
break;
206+
}
141207
case'jwk': {
142208
if(!keyData.kty)
143209
throwlazyDOMException('Invalid keyData','DataError');

‎lib/internal/crypto/webcrypto.js‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,14 @@ async function exportKeySpki(key){
357357
case'X448':
358358
returnrequire('internal/crypto/cfrg')
359359
.cfrgExportKey(key,kWebCryptoKeyFormatSPKI);
360+
case'ML-DSA-44':
361+
// Fall through
362+
case'ML-DSA-65':
363+
// Fall through
364+
case'ML-DSA-87': {
365+
returnrequire('internal/crypto/ml_dsa')
366+
.mlDsaExportKey(key,kWebCryptoKeyFormatSPKI);
367+
}
360368
default:
361369
returnundefined;
362370
}
@@ -385,6 +393,14 @@ async function exportKeyPkcs8(key){
385393
case'X448':
386394
returnrequire('internal/crypto/cfrg')
387395
.cfrgExportKey(key,kWebCryptoKeyFormatPKCS8);
396+
case'ML-DSA-44':
397+
// Fall through
398+
case'ML-DSA-65':
399+
// Fall through
400+
case'ML-DSA-87': {
401+
returnrequire('internal/crypto/ml_dsa')
402+
.mlDsaExportKey(key,kWebCryptoKeyFormatPKCS8);
403+
}
388404
default:
389405
returnundefined;
390406
}

0 commit comments

Comments
(0)