Skip to content

Commit 02c2456

Browse files
committed
lib,crypto: migrate algorithm modules to native CryptoKey
Every Web Crypto algorithm module drops `key[kKeyObject][kHandle]` symbol access in favor of `getCryptoKey*` accessors and stores the raw `KeyObjectHandle` directly (no more `SecretKeyObject`/`PublicKeyObject`/ `PrivateKeyObject` wrappers on the CryptoKey slot). Key generation moves off the promisified `generateKey`/`generateKeyPair` paths onto ones that return `KeyObjectHandle` (i.e. not wrapped in KeyObject): - `SecretKeyGenJob` for AES, ChaCha20-Poly1305, HMAC/CMAC (aes.js, chacha20_poly1305.js, mac.js). - `EcKeyPairGenJob` for ECDSA, ECDH (ec.js). - `NidKeyPairGenJob` for Ed25519/Ed448/X25519/X448, ML-DSA-{44,65,87}, ML-KEM-{512,768,1024} (cfrg.js, ml_dsa.js, ml_kem.js). - `RsaKeyPairGenJob` for RSASSA-PKCS1-v1_5, RSA-PSS, RSA-OAEP (rsa.js). `webcrypto_util.js` switches its import helpers (`importDerKey`, `importJwkKey`, `importRawKey`) to return raw `KeyObjectHandle`s and adds `importSecretKey` / `importJwkSecretKey` helpers for symmetric imports. `webcrypto.js` follows the same pattern; its `getPublicKey` builds a temporary wrapper and carries a TODO for future refactor. `hkdf.js` switches from `promisify(hkdf)` to `jobPromise(() => new HKDFJob(...))` directly. `diffiehellman.js`, `argon2.js`, and `pbkdf2.js` are accessor-only migrations (the `TODO(panva)` notes on argon2/pbkdf2 are orthogonal and left for a later cleanup). Signed-off-by: Filip Skokan <[email protected]>
1 parent 27230ec commit 02c2456

15 files changed

Lines changed: 368 additions & 443 deletions

File tree

lib/internal/crypto/aes.js

Lines changed: 27 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ const {
88

99
const {
1010
AESCipherJob,
11-
KeyObjectHandle,
1211
kCryptoJobAsync,
13-
kKeyFormatJWK,
14-
kKeyTypeSecret,
1512
kKeyVariantAES_CTR_128,
1613
kKeyVariantAES_CBC_128,
1714
kKeyVariantAES_GCM_128,
@@ -27,37 +24,31 @@ const {
2724
kKeyVariantAES_GCM_256,
2825
kKeyVariantAES_KW_256,
2926
kKeyVariantAES_OCB_256,
27+
SecretKeyGenJob,
3028
} = internalBinding('crypto');
3129

3230
const {
3331
hasAnyNotIn,
3432
jobPromise,
3533
kHandle,
36-
kKeyObject,
3734
} = require('internal/crypto/util');
3835

3936
const {
4037
lazyDOMException,
41-
promisify,
4238
} = require('internal/util');
4339

4440
const {
4541
InternalCryptoKey,
46-
SecretKeyObject,
47-
createSecretKey,
48-
kAlgorithm,
42+
getCryptoKeyAlgorithm,
43+
getCryptoKeyHandle,
4944
} = require('internal/crypto/keys');
5045

5146
const {
47+
importJwkSecretKey,
48+
importSecretKey,
5249
validateJwk,
5350
} = require('internal/crypto/webcrypto_util');
5451

55-
const {
56-
generateKey: _generateKey,
57-
} = require('internal/crypto/keygen');
58-
59-
const generateKey = promisify(_generateKey);
60-
6152
function getAlgorithmName(name, length) {
6253
switch (name) {
6354
case 'AES-CBC': return `A${length}CBC`;
@@ -117,9 +108,9 @@ function asyncAesCtrCipher(mode, key, data, algorithm) {
117108
return jobPromise(() => new AESCipherJob(
118109
kCryptoJobAsync,
119110
mode,
120-
key[kKeyObject][kHandle],
111+
getCryptoKeyHandle(key),
121112
data,
122-
getVariant('AES-CTR', key[kAlgorithm].length),
113+
getVariant('AES-CTR', getCryptoKeyAlgorithm(key).length),
123114
algorithm.counter,
124115
algorithm.length));
125116
}
@@ -128,19 +119,19 @@ function asyncAesCbcCipher(mode, key, data, algorithm) {
128119
return jobPromise(() => new AESCipherJob(
129120
kCryptoJobAsync,
130121
mode,
131-
key[kKeyObject][kHandle],
122+
getCryptoKeyHandle(key),
132123
data,
133-
getVariant('AES-CBC', key[kAlgorithm].length),
124+
getVariant('AES-CBC', getCryptoKeyAlgorithm(key).length),
134125
algorithm.iv));
135126
}
136127

137128
function asyncAesKwCipher(mode, key, data) {
138129
return jobPromise(() => new AESCipherJob(
139130
kCryptoJobAsync,
140131
mode,
141-
key[kKeyObject][kHandle],
132+
getCryptoKeyHandle(key),
142133
data,
143-
getVariant('AES-KW', key[kAlgorithm].length)));
134+
getVariant('AES-KW', getCryptoKeyAlgorithm(key).length)));
144135
}
145136

146137
function asyncAesGcmCipher(mode, key, data, algorithm) {
@@ -150,9 +141,9 @@ function asyncAesGcmCipher(mode, key, data, algorithm) {
150141
return jobPromise(() => new AESCipherJob(
151142
kCryptoJobAsync,
152143
mode,
153-
key[kKeyObject][kHandle],
144+
getCryptoKeyHandle(key),
154145
data,
155-
getVariant('AES-GCM', key[kAlgorithm].length),
146+
getVariant('AES-GCM', getCryptoKeyAlgorithm(key).length),
156147
algorithm.iv,
157148
tagByteLength,
158149
algorithm.additionalData));
@@ -165,9 +156,9 @@ function asyncAesOcbCipher(mode, key, data, algorithm) {
165156
return jobPromise(() => new AESCipherJob(
166157
kCryptoJobAsync,
167158
mode,
168-
key[kKeyObject][kHandle],
159+
getCryptoKeyHandle(key),
169160
data,
170-
getVariant('AES-OCB', key.algorithm.length),
161+
getVariant('AES-OCB', getCryptoKeyAlgorithm(key).length),
171162
algorithm.iv,
172163
tagByteLength,
173164
algorithm.additionalData));
@@ -197,18 +188,10 @@ async function aesGenerateKey(algorithm, extractable, keyUsages) {
197188
'SyntaxError');
198189
}
199190

200-
let key;
201-
try {
202-
key = await generateKey('aes', { length });
203-
} catch (err) {
204-
throw lazyDOMException(
205-
'The operation failed for an operation-specific reason' +
206-
`[${err.message}]`,
207-
{ name: 'OperationError', cause: err });
208-
}
191+
const handle = await jobPromise(() => new SecretKeyGenJob(kCryptoJobAsync, length));
209192

210193
return new InternalCryptoKey(
211-
key,
194+
handle,
212195
{ name, length },
213196
ArrayFrom(usagesSet),
214197
extractable);
@@ -232,58 +215,44 @@ function aesImportKey(
232215
'SyntaxError');
233216
}
234217

235-
let keyObject;
218+
let handle;
236219
let length;
237220
switch (format) {
238221
case 'KeyObject': {
239-
validateKeyLength(keyData.symmetricKeySize * 8);
240-
keyObject = keyData;
222+
length = keyData.symmetricKeySize * 8;
223+
validateKeyLength(length);
224+
handle = keyData[kHandle];
241225
break;
242226
}
243227
case 'raw-secret':
244228
case 'raw': {
245229
if (format === 'raw' && name === 'AES-OCB') {
246230
return undefined;
247231
}
248-
validateKeyLength(keyData.byteLength * 8);
249-
keyObject = createSecretKey(keyData);
232+
length = keyData.byteLength * 8;
233+
validateKeyLength(length);
234+
handle = importSecretKey(keyData);
250235
break;
251236
}
252237
case 'jwk': {
253238
validateJwk(keyData, 'oct', extractable, usagesSet, 'enc');
254-
255-
const handle = new KeyObjectHandle();
256-
try {
257-
handle.init(kKeyTypeSecret, keyData, kKeyFormatJWK, null, null);
258-
} catch (err) {
259-
throw lazyDOMException(
260-
'Invalid keyData', { name: 'DataError', cause: err });
261-
}
262-
263-
({ length } = handle.keyDetail({ }));
239+
handle = importJwkSecretKey(keyData);
240+
length = handle.getSymmetricKeySize() * 8;
264241
validateKeyLength(length);
265-
266242
if (keyData.alg !== undefined) {
267243
if (keyData.alg !== getAlgorithmName(algorithm.name, length))
268244
throw lazyDOMException(
269245
'JWK "alg" does not match the requested algorithm',
270246
'DataError');
271247
}
272-
273-
keyObject = new SecretKeyObject(handle);
274248
break;
275249
}
276250
default:
277251
return undefined;
278252
}
279253

280-
if (length === undefined) {
281-
({ length } = keyObject[kHandle].keyDetail({ }));
282-
validateKeyLength(length);
283-
}
284-
285254
return new InternalCryptoKey(
286-
keyObject,
255+
handle,
287256
{ name, length },
288257
keyUsages,
289258
extractable);

lib/internal/crypto/argon2.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ const {
2424
promisify,
2525
} = require('internal/util');
2626

27+
const {
28+
getCryptoKeyHandle,
29+
} = require('internal/crypto/keys');
30+
2731
const {
2832
getArrayBufferOrView,
29-
kKeyObject,
3033
} = require('internal/crypto/util');
3134

3235
const {
@@ -216,7 +219,8 @@ async function argon2DeriveBits(algorithm, baseKey, length) {
216219
result = await argon2Promise(
217220
StringPrototypeToLowerCase(algorithm.name),
218221
{
219-
message: baseKey[kKeyObject].export(),
222+
// TODO(panva): call the job directly without needing to re-export the handle
223+
message: getCryptoKeyHandle(baseKey).export(),
220224
nonce: algorithm.nonce,
221225
parallelism: algorithm.parallelism,
222226
tagLength: length / 8,

0 commit comments

Comments
 (0)