crypto: add WebCrypto CryptoJob mode · nodejs/node@e431d93 · GitHub
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

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-
let type;
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-
throw new ERR_CRYPTO_ARGON2_NOT_SUPPORTED();
159-
}
145+
const type = {
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-
const argon2Promise = promisify(argon2);
197187
function validateArgon2DeriveBitsLength(length) {
198188
if (length === null)
199189
throw lazyDOMException('length cannot be null', 'OperationError');
@@ -211,32 +201,39 @@ function validateArgon2DeriveBitsLength(length) {
211201
}
212202
}
213203

214-
async function argon2DeriveBits(algorithm, baseKey, length) {
204+
function argon2DeriveBits(algorithm, baseKey, length) {
215205
validateArgon2DeriveBitsLength(length);
216206

217-
let result;
207+
const type = {
208+
'__proto__': null,
209+
'Argon2d': kTypeArgon2d,
210+
'Argon2i': kTypeArgon2i,
211+
'Argon2id': kTypeArgon2id,
212+
}[algorithm.name];
213+
214+
let message;
218215
try {
219-
result = await argon2Promise(
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
throw lazyDOMException(
235220
'The operation failed for an operation-specific reason',
236221
{ name: 'OperationError', cause: err });
237222
}
238223

239-
return TypedArrayPrototypeGetBuffer(result);
224+
const empty = new Uint8Array(0);
225+
226+
return jobPromise(() => new Argon2Job(
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-
async function cfrgGenerateKey(algorithm, extractable, keyUsages) {
78+
function cfrgGenerateKey(algorithm, extractable, keyUsages) {
7979
const { name } = algorithm;
8080

8181
const usageSet = new SafeSet(keyUsages);
@@ -99,23 +99,13 @@ async function cfrgGenerateKey(algorithm, extractable, keyUsages) {
9999
}
100100
break;
101101
}
102-
let nid;
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-
const handles = await jobPromise(() => new NidKeyPairGenJob(kCryptoJobAsync, nid));
102+
const nid = {
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
let publicUsages;
121111
let privateUsages;
@@ -136,21 +126,19 @@ async function cfrgGenerateKey(algorithm, extractable, keyUsages) {
136126

137127
const keyAlgorithm = { name };
138128

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

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

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

254-
async function eddsaSignVerify(key, data, algorithm, signature) {
242+
function eddsaSignVerify(key, data, algorithm, signature) {
255243
const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify;
256244
const type = mode === kSignJobModeSign ? 'private' : 'public';
257245

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

261-
return await jobPromise(() => new SignJob(
262-
kCryptoJobAsync,
249+
return jobPromise(() => new SignJob(
250+
kCryptoJobWebCrypto,
263251
mode,
264252
getCryptoKeyHandle(key),
265253
undefined,

lib/internal/crypto/chacha20_poly1305.js

Lines changed: 12 additions & 8 deletions

0 commit comments

Comments
 (0)