Skip to content

Commit e431d93

Browse files
panvasxa
authored andcommitted
crypto: add WebCrypto CryptoJob mode
Add a WebCrypto-specific CryptoJob mode that returns a promise from run() and resolves it when native work is finished. Encode job output directly as Web Crypto values, including CryptoKey instances and CryptoKeyPair dictionaries. Convert operation-specific setup failures from AdditionalConfig into OperationError rejections. Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #63363 Backport-PR-URL: #63563 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: René <contact.9a5d6388@renegade334.me.uk>
1 parent 56e2505 commit e431d93

39 files changed

Lines changed: 929 additions & 455 deletions

‎lib/internal/crypto/aes.js‎

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const {
77

88
const{
99
AESCipherJob,
10-
kCryptoJobAsync,
10+
kCryptoJobWebCrypto,
1111
kKeyVariantAES_CTR_128,
1212
kKeyVariantAES_CBC_128,
1313
kKeyVariantAES_GCM_128,
@@ -107,7 +107,7 @@ function getVariant(name, length) {
107107

108108
functionasyncAesCtrCipher(mode,key,data,algorithm){
109109
returnjobPromise(()=>newAESCipherJob(
110-
kCryptoJobAsync,
110+
kCryptoJobWebCrypto,
111111
mode,
112112
getCryptoKeyHandle(key),
113113
data,
@@ -118,7 +118,7 @@ function asyncAesCtrCipher(mode, key, data, algorithm) {
118118

119119
functionasyncAesCbcCipher(mode,key,data,algorithm){
120120
returnjobPromise(()=>newAESCipherJob(
121-
kCryptoJobAsync,
121+
kCryptoJobWebCrypto,
122122
mode,
123123
getCryptoKeyHandle(key),
124124
data,
@@ -128,7 +128,7 @@ function asyncAesCbcCipher(mode, key, data, algorithm) {
128128

129129
functionasyncAesKwCipher(mode,key,data){
130130
returnjobPromise(()=>newAESCipherJob(
131-
kCryptoJobAsync,
131+
kCryptoJobWebCrypto,
132132
mode,
133133
getCryptoKeyHandle(key),
134134
data,
@@ -140,7 +140,7 @@ function asyncAesGcmCipher(mode, key, data, algorithm) {
140140
consttagByteLength=tagLength/8;
141141

142142
returnjobPromise(()=>newAESCipherJob(
143-
kCryptoJobAsync,
143+
kCryptoJobWebCrypto,
144144
mode,
145145
getCryptoKeyHandle(key),
146146
data,
@@ -155,7 +155,7 @@ function asyncAesOcbCipher(mode, key, data, algorithm) {
155155
consttagByteLength=tagLength/8;
156156

157157
returnjobPromise(()=>newAESCipherJob(
158-
kCryptoJobAsync,
158+
kCryptoJobWebCrypto,
159159
mode,
160160
getCryptoKeyHandle(key),
161161
data,
@@ -175,7 +175,7 @@ function aesCipher(mode, key, data, algorithm) {
175175
}
176176
}
177177

178-
asyncfunctionaesGenerateKey(algorithm,extractable,keyUsages){
178+
functionaesGenerateKey(algorithm,extractable,keyUsages){
179179
const{ name, length }=algorithm;
180180

181181
constcheckUsages=['wrapKey','unwrapKey'];
@@ -188,14 +188,18 @@ async function aesGenerateKey(algorithm, extractable, keyUsages) {
188188
'Unsupported key usage for an AES key',
189189
'SyntaxError');
190190
}
191+
if(usagesSet.size===0){
192+
throwlazyDOMException(
193+
'Usages cannot be empty when creating a key.',
194+
'SyntaxError');
195+
}
191196

192-
consthandle=awaitjobPromise(()=>newSecretKeyGenJob(kCryptoJobAsync,length));
193-
194-
returnnewInternalCryptoKey(
195-
handle,
197+
returnjobPromise(()=>newSecretKeyGenJob(
198+
kCryptoJobWebCrypto,
199+
length,
196200
{ name, length },
197201
getUsagesMask(usagesSet),
198-
extractable);
202+
extractable));
199203
}
200204

201205
functionaesImportKey(

‎lib/internal/crypto/argon2.js‎

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
const{
44
FunctionPrototypeCall,
55
MathPow,
6-
StringPrototypeToLowerCase,
7-
TypedArrayPrototypeGetBuffer,
86
Uint8Array,
97
}=primordials;
108

@@ -14,14 +12,14 @@ const {
1412
Argon2Job,
1513
kCryptoJobAsync,
1614
kCryptoJobSync,
15+
kCryptoJobWebCrypto,
1716
kTypeArgon2d,
1817
kTypeArgon2i,
1918
kTypeArgon2id,
2019
}=internalBinding('crypto');
2120

2221
const{
2322
lazyDOMException,
24-
promisify,
2523
}=require('internal/util');
2624

2725
const{
@@ -30,6 +28,7 @@ const {
3028

3129
const{
3230
getArrayBufferOrView,
31+
jobPromise,
3332
}=require('internal/crypto/util');
3433

3534
const{
@@ -143,20 +142,12 @@ function check(algorithm, parameters) {
143142
validateString(algorithm,'algorithm');
144143
validateOneOf(algorithm,'algorithm',['argon2d','argon2i','argon2id']);
145144

146-
lettype;
147-
switch(algorithm){
148-
case'argon2d':
149-
type=kTypeArgon2d;
150-
break;
151-
case'argon2i':
152-
type=kTypeArgon2i;
153-
break;
154-
case'argon2id':
155-
type=kTypeArgon2id;
156-
break;
157-
default: // unreachable
158-
thrownewERR_CRYPTO_ARGON2_NOT_SUPPORTED();
159-
}
145+
consttype={
146+
'__proto__': null,
147+
'argon2d': kTypeArgon2d,
148+
'argon2i': kTypeArgon2i,
149+
'argon2id': kTypeArgon2id,
150+
}[algorithm];
160151

161152
validateObject(parameters,'parameters');
162153

@@ -193,7 +184,6 @@ function check(algorithm, parameters) {
193184
return{ message, nonce, secret, associatedData, tagLength, passes, parallelism, memory, type };
194185
}
195186

196-
constargon2Promise=promisify(argon2);
197187
functionvalidateArgon2DeriveBitsLength(length){
198188
if(length===null)
199189
throwlazyDOMException('length cannot be null','OperationError');
@@ -211,32 +201,39 @@ function validateArgon2DeriveBitsLength(length) {
211201
}
212202
}
213203

214-
asyncfunctionargon2DeriveBits(algorithm,baseKey,length){
204+
functionargon2DeriveBits(algorithm,baseKey,length){
215205
validateArgon2DeriveBitsLength(length);
216206

217-
letresult;
207+
consttype={
208+
'__proto__': null,
209+
'Argon2d': kTypeArgon2d,
210+
'Argon2i': kTypeArgon2i,
211+
'Argon2id': kTypeArgon2id,
212+
}[algorithm.name];
213+
214+
letmessage;
218215
try{
219-
result=awaitargon2Promise(
220-
StringPrototypeToLowerCase(algorithm.name),
221-
{
222-
// TODO(panva): call the job directly without needing to re-export the handle
223-
message: getCryptoKeyHandle(baseKey).export(),
224-
nonce: algorithm.nonce,
225-
parallelism: algorithm.parallelism,
226-
tagLength: length/8,
227-
memory: algorithm.memory,
228-
passes: algorithm.passes,
229-
secret: algorithm.secretValue,
230-
associatedData: algorithm.associatedData,
231-
},
232-
);
216+
// TODO(panva): call the job directly without needing to re-export the handle
217+
message=getCryptoKeyHandle(baseKey).export();
233218
}catch(err){
234219
throwlazyDOMException(
235220
'The operation failed for an operation-specific reason',
236221
{name: 'OperationError',cause: err});
237222
}
238223

239-
returnTypedArrayPrototypeGetBuffer(result);
224+
constempty=newUint8Array(0);
225+
226+
returnjobPromise(()=>newArgon2Job(
227+
kCryptoJobWebCrypto,
228+
message,
229+
algorithm.nonce,
230+
algorithm.parallelism,
231+
length/8,
232+
algorithm.memory,
233+
algorithm.passes,
234+
algorithm.secretValue===undefined ? empty : algorithm.secretValue,
235+
algorithm.associatedData===undefined ? empty : algorithm.associatedData,
236+
type));
240237
}
241238

242239
module.exports={

‎lib/internal/crypto/cfrg.js‎

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const { Buffer } = require('buffer');
1010

1111
const{
1212
SignJob,
13-
kCryptoJobAsync,
13+
kCryptoJobWebCrypto,
1414
kKeyFormatDER,
1515
kKeyFormatRawPublic,
1616
kSignJobModeSign,
@@ -75,7 +75,7 @@ function verifyAcceptableCfrgKeyUse(name, isPublic, usages) {
7575
}
7676
}
7777

78-
asyncfunctioncfrgGenerateKey(algorithm,extractable,keyUsages){
78+
functioncfrgGenerateKey(algorithm,extractable,keyUsages){
7979
const{ name }=algorithm;
8080

8181
constusageSet=newSafeSet(keyUsages);
@@ -99,23 +99,13 @@ async function cfrgGenerateKey(algorithm, extractable, keyUsages) {
9999
}
100100
break;
101101
}
102-
letnid;
103-
switch(name){
104-
case'Ed25519':
105-
nid=EVP_PKEY_ED25519;
106-
break;
107-
case'Ed448':
108-
nid=EVP_PKEY_ED448;
109-
break;
110-
case'X25519':
111-
nid=EVP_PKEY_X25519;
112-
break;
113-
case'X448':
114-
nid=EVP_PKEY_X448;
115-
break;
116-
}
117-
118-
consthandles=awaitjobPromise(()=>newNidKeyPairGenJob(kCryptoJobAsync,nid));
102+
constnid={
103+
'__proto__': null,
104+
'Ed25519': EVP_PKEY_ED25519,
105+
'Ed448': EVP_PKEY_ED448,
106+
'X25519': EVP_PKEY_X25519,
107+
'X448': EVP_PKEY_X448,
108+
}[name];
119109

120110
letpublicUsages;
121111
letprivateUsages;
@@ -136,21 +126,19 @@ async function cfrgGenerateKey(algorithm, extractable, keyUsages) {
136126

137127
constkeyAlgorithm={ name };
138128

139-
constpublicKey=
140-
newInternalCryptoKey(
141-
handles[0],
142-
keyAlgorithm,
143-
getUsagesMask(publicUsages),
144-
true);
145-
146-
constprivateKey=
147-
newInternalCryptoKey(
148-
handles[1],
149-
keyAlgorithm,
150-
getUsagesMask(privateUsages),
151-
extractable);
129+
if(privateUsages.size===0){
130+
throwlazyDOMException(
131+
'Usages cannot be empty when creating a key.',
132+
'SyntaxError');
133+
}
152134

153-
return{__proto__: null, privateKey, publicKey };
135+
returnjobPromise(()=>newNidKeyPairGenJob(
136+
kCryptoJobWebCrypto,
137+
nid,
138+
keyAlgorithm,
139+
getUsagesMask(publicUsages),
140+
getUsagesMask(privateUsages),
141+
extractable));
154142
}
155143

156144
functioncfrgExportKey(key,format){
@@ -251,15 +239,15 @@ function cfrgImportKey(
251239
extractable);
252240
}
253241

254-
asyncfunctioneddsaSignVerify(key,data,algorithm,signature){
242+
functioneddsaSignVerify(key,data,algorithm,signature){
255243
constmode=signature===undefined ? kSignJobModeSign : kSignJobModeVerify;
256244
consttype=mode===kSignJobModeSign ? 'private' : 'public';
257245

258246
if(getCryptoKeyType(key)!==type)
259247
throwlazyDOMException(`Key must be a ${type} key`,'InvalidAccessError');
260248

261-
returnawaitjobPromise(()=>newSignJob(
262-
kCryptoJobAsync,
249+
returnjobPromise(()=>newSignJob(
250+
kCryptoJobWebCrypto,
263251
mode,
264252
getCryptoKeyHandle(key),
265253
undefined,

‎lib/internal/crypto/chacha20_poly1305.js‎

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const {
77
const{
88
ChaCha20Poly1305CipherJob,
99
SecretKeyGenJob,
10-
kCryptoJobAsync,
10+
kCryptoJobWebCrypto,
1111
}=internalBinding('crypto');
1212

1313
const{
@@ -39,15 +39,15 @@ function validateKeyLength(length) {
3939

4040
functionc20pCipher(mode,key,data,algorithm){
4141
returnjobPromise(()=>newChaCha20Poly1305CipherJob(
42-
kCryptoJobAsync,
42+
kCryptoJobWebCrypto,
4343
mode,
4444
getCryptoKeyHandle(key),
4545
data,
4646
algorithm.iv,
4747
algorithm.additionalData));
4848
}
4949

50-
asyncfunctionc20pGenerateKey(algorithm,extractable,keyUsages){
50+
functionc20pGenerateKey(algorithm,extractable,keyUsages){
5151
const{ name }=algorithm;
5252

5353
constcheckUsages=['encrypt','decrypt','wrapKey','unwrapKey'];
@@ -58,14 +58,18 @@ async function c20pGenerateKey(algorithm, extractable, keyUsages) {
5858
`Unsupported key usage for a ${algorithm.name} key`,
5959
'SyntaxError');
6060
}
61+
if(usagesSet.size===0){
62+
throwlazyDOMException(
63+
'Usages cannot be empty when creating a key.',
64+
'SyntaxError');
65+
}
6166

62-
consthandle=awaitjobPromise(()=>newSecretKeyGenJob(kCryptoJobAsync,256));
63-
64-
returnnewInternalCryptoKey(
65-
handle,
67+
returnjobPromise(()=>newSecretKeyGenJob(
68+
kCryptoJobWebCrypto,
69+
256,
6670
{ name },
6771
getUsagesMask(usagesSet),
68-
extractable);
72+
extractable));
6973
}
7074

7175
functionc20pImportKey(

0 commit comments

Comments
 (0)