Skip to content

Commit cedc8f4

Browse files
panvaaduh95
authored andcommitted
crypto: share WebCrypto method and usage helpers
Introduce prepareSubtleMethod() and convertSubtleArgument() for the common SubtleCrypto call prelude. Methods now reuse the same receiver check, required-argument check, error prefix construction, argument context selection, and WebIDL conversion path. Introduce WebCrypto key usage helpers for common usage validation and key pair usage splitting. Algorithm modules now define their allowed public, private, and key generation usages once, then call the shared helpers from generateKey() and importKey() paths. This removes repeated function-invocation setup in webcrypto.js and repeated key usage checks across the WebCrypto algorithm modules while preserving the existing validation behavior. Mark normalized-algorithm fallback branches as unreachable assertions. Signed-off-by: Filip Skokan <[email protected]> PR-URL: #63975 Backport-PR-URL: #64629 Reviewed-By: Antoine du Hamel <[email protected]>
1 parent 5a356ac commit cedc8f4

12 files changed

Lines changed: 541 additions & 772 deletions

File tree

lib/internal/crypto/aes.js

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
'use strict';
22

3-
const {
4-
ArrayPrototypePush,
5-
SafeSet,
6-
} = primordials;
7-
83
const {
94
AESCipherJob,
105
kCryptoJobWebCrypto,
@@ -28,7 +23,6 @@ const {
2823

2924
const {
3025
getUsagesMask,
31-
hasAnyNotIn,
3226
jobPromise,
3327
} = require('internal/crypto/util');
3428

@@ -46,8 +40,22 @@ const {
4640
importJwkSecretKey,
4741
importSecretKey,
4842
validateJwk,
43+
validateKeyUsages,
44+
validateUsagesNotEmpty,
4945
} = require('internal/crypto/webcrypto_util');
5046

47+
const kCipherUsages = ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey'];
48+
const kWrapUsages = ['wrapKey', 'unwrapKey'];
49+
50+
const kUsages = {
51+
'__proto__': null,
52+
'AES-CBC': kCipherUsages,
53+
'AES-CTR': kCipherUsages,
54+
'AES-GCM': kCipherUsages,
55+
'AES-KW': kWrapUsages,
56+
'AES-OCB': kCipherUsages,
57+
};
58+
5159
function getAlgorithmName(name, length) {
5260
switch (name) {
5361
case 'AES-CBC': return `A${length}CBC`;
@@ -176,21 +184,8 @@ function aesCipher(mode, key, data, algorithm) {
176184
function aesGenerateKey(algorithm, extractable, usages) {
177185
const { name, length } = algorithm;
178186

179-
const checkUsages = ['wrapKey', 'unwrapKey'];
180-
if (name !== 'AES-KW')
181-
ArrayPrototypePush(checkUsages, 'encrypt', 'decrypt');
182-
183-
const usagesSet = new SafeSet(usages);
184-
if (hasAnyNotIn(usagesSet, checkUsages)) {
185-
throw lazyDOMException(
186-
'Unsupported key usage for an AES key',
187-
'SyntaxError');
188-
}
189-
if (usagesSet.size === 0) {
190-
throw lazyDOMException(
191-
'Usages cannot be empty when creating a key.',
192-
'SyntaxError');
193-
}
187+
const usagesSet = validateUsagesNotEmpty(
188+
validateKeyUsages(usages, kUsages[name], name));
194189

195190
return jobPromise(() => new SecretKeyGenJob(
196191
kCryptoJobWebCrypto,
@@ -207,16 +202,7 @@ function aesImportKey(
207202
extractable,
208203
usages) {
209204
const { name } = algorithm;
210-
const checkUsages = ['wrapKey', 'unwrapKey'];
211-
if (name !== 'AES-KW')
212-
ArrayPrototypePush(checkUsages, 'encrypt', 'decrypt');
213-
214-
const usagesSet = new SafeSet(usages);
215-
if (hasAnyNotIn(usagesSet, checkUsages)) {
216-
throw lazyDOMException(
217-
'Unsupported key usage for an AES key',
218-
'SyntaxError');
219-
}
205+
const usagesSet = validateKeyUsages(usages, kUsages[name], name);
220206

221207
let handle;
222208
let length;

lib/internal/crypto/cfrg.js

Lines changed: 36 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ const {
2828

2929
const {
3030
getUsagesMask,
31-
getUsagesUnion,
32-
hasAnyNotIn,
3331
jobPromise,
3432
} = require('internal/crypto/util');
3533

@@ -44,60 +42,33 @@ const {
4442
} = require('internal/crypto/keys');
4543

4644
const {
45+
createKeyUsages,
46+
getKeyPairUsages,
4747
importDerKey,
4848
importJwkKey,
4949
importRawKey,
5050
validateJwk,
51+
validateKeyUsages,
52+
validateUsagesNotEmpty,
53+
verifyAcceptableKeyUse,
5154
} = require('internal/crypto/webcrypto_util');
5255

53-
function verifyAcceptableCfrgKeyUse(name, isPublic, usages) {
54-
let checkSet;
55-
switch (name) {
56-
case 'X25519':
57-
// Fall through
58-
case 'X448':
59-
checkSet = isPublic ? [] : ['deriveKey', 'deriveBits'];
60-
break;
61-
case 'Ed25519':
62-
// Fall through
63-
case 'Ed448':
64-
checkSet = isPublic ? ['verify'] : ['sign'];
65-
break;
66-
default:
67-
throw lazyDOMException(
68-
'The algorithm is not supported', 'NotSupportedError');
69-
}
70-
if (hasAnyNotIn(usages, checkSet)) {
71-
throw lazyDOMException(
72-
`Unsupported key usage for a ${name} key`,
73-
'SyntaxError');
74-
}
75-
}
56+
const kDeriveUsages = createKeyUsages([], ['deriveKey', 'deriveBits']);
57+
58+
const kSignVerifyUsages = createKeyUsages(['verify'], ['sign']);
59+
60+
const kUsages = {
61+
'__proto__': null,
62+
'X25519': kDeriveUsages,
63+
'X448': kDeriveUsages,
64+
'Ed25519': kSignVerifyUsages,
65+
'Ed448': kSignVerifyUsages,
66+
};
7667

7768
function cfrgGenerateKey(algorithm, extractable, usages) {
7869
const { name } = algorithm;
79-
80-
const usageSet = new SafeSet(usages);
81-
switch (name) {
82-
case 'Ed25519':
83-
// Fall through
84-
case 'Ed448':
85-
if (hasAnyNotIn(usageSet, ['sign', 'verify'])) {
86-
throw lazyDOMException(
87-
`Unsupported key usage for an ${name} key`,
88-
'SyntaxError');
89-
}
90-
break;
91-
case 'X25519':
92-
// Fall through
93-
case 'X448':
94-
if (hasAnyNotIn(usageSet, ['deriveKey', 'deriveBits'])) {
95-
throw lazyDOMException(
96-
`Unsupported key usage for an ${name} key`,
97-
'SyntaxError');
98-
}
99-
break;
100-
}
70+
const allowedUsages = kUsages[name];
71+
const usagesSet = validateKeyUsages(usages, allowedUsages.keygen, name);
10172
const nid = {
10273
'__proto__': null,
10374
'Ed25519': EVP_PKEY_ED25519,
@@ -106,37 +77,16 @@ function cfrgGenerateKey(algorithm, extractable, usages) {
10677
'X448': EVP_PKEY_X448,
10778
}[name];
10879

109-
let publicUsages;
110-
let privateUsages;
111-
switch (name) {
112-
case 'Ed25519':
113-
// Fall through
114-
case 'Ed448':
115-
publicUsages = getUsagesUnion(usageSet, 'verify');
116-
privateUsages = getUsagesUnion(usageSet, 'sign');
117-
break;
118-
case 'X25519':
119-
// Fall through
120-
case 'X448':
121-
publicUsages = new SafeSet();
122-
privateUsages = getUsagesUnion(usageSet, 'deriveKey', 'deriveBits');
123-
break;
124-
}
125-
12680
const keyAlgorithm = { name };
127-
128-
if (privateUsages.size === 0) {
129-
throw lazyDOMException(
130-
'Usages cannot be empty when creating a key.',
131-
'SyntaxError');
132-
}
81+
const keyUsages = getKeyPairUsages(usagesSet, allowedUsages);
82+
validateUsagesNotEmpty(keyUsages.private);
13383

13484
return jobPromise(() => new NidKeyPairGenJob(
13585
kCryptoJobWebCrypto,
13686
nid,
13787
keyAlgorithm,
138-
getUsagesMask(publicUsages),
139-
getUsagesMask(privateUsages),
88+
getUsagesMask(keyUsages.public),
89+
getUsagesMask(keyUsages.private),
14090
extractable));
14191
}
14292

@@ -175,20 +125,25 @@ function cfrgImportKey(
175125

176126
const { name } = algorithm;
177127
let handle;
128+
const allowedUsages = kUsages[name];
178129
const usagesSet = new SafeSet(usages);
179130
switch (format) {
180131
case 'KeyObjectHandle':
181-
verifyAcceptableCfrgKeyUse(
182-
name, keyData.getKeyType() === kKeyTypePublic, usagesSet);
132+
verifyAcceptableKeyUse(
133+
name,
134+
usagesSet,
135+
keyData.getKeyType() === kKeyTypePublic ?
136+
allowedUsages.public :
137+
allowedUsages.private);
183138
handle = keyData;
184139
break;
185140
case 'spki': {
186-
verifyAcceptableCfrgKeyUse(name, true, usagesSet);
141+
verifyAcceptableKeyUse(name, usagesSet, allowedUsages.public);
187142
handle = importDerKey(keyData, true);
188143
break;
189144
}
190145
case 'pkcs8': {
191-
verifyAcceptableCfrgKeyUse(name, false, usagesSet);
146+
verifyAcceptableKeyUse(name, usagesSet, allowedUsages.private);
192147
handle = importDerKey(keyData, false);
193148
break;
194149
}
@@ -207,7 +162,10 @@ function cfrgImportKey(
207162
}
208163

209164
const isPublic = keyData.d === undefined;
210-
verifyAcceptableCfrgKeyUse(name, isPublic, usagesSet);
165+
verifyAcceptableKeyUse(
166+
name,
167+
usagesSet,
168+
isPublic ? allowedUsages.public : allowedUsages.private);
211169
handle = importJwkKey(isPublic, keyData);
212170

213171
if (!isPublic) {
@@ -218,7 +176,7 @@ function cfrgImportKey(
218176
break;
219177
}
220178
case 'raw': {
221-
verifyAcceptableCfrgKeyUse(name, true, usagesSet);
179+
verifyAcceptableKeyUse(name, usagesSet, allowedUsages.public);
222180
handle = importRawKey(true, keyData, kKeyFormatRawPublic, name);
223181
break;
224182
}

lib/internal/crypto/chacha20_poly1305.js

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
'use strict';
22

3-
const {
4-
SafeSet,
5-
} = primordials;
6-
73
const {
84
ChaCha20Poly1305CipherJob,
95
SecretKeyGenJob,
@@ -12,7 +8,6 @@ const {
128

139
const {
1410
getUsagesMask,
15-
hasAnyNotIn,
1611
jobPromise,
1712
} = require('internal/crypto/util');
1813

@@ -29,8 +24,12 @@ const {
2924
importJwkSecretKey,
3025
importSecretKey,
3126
validateJwk,
27+
validateKeyUsages,
28+
validateUsagesNotEmpty,
3229
} = require('internal/crypto/webcrypto_util');
3330

31+
const kUsages = ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey'];
32+
3433
function validateKeyLength(length) {
3534
if (length !== 256)
3635
throw lazyDOMException('Invalid key length', 'DataError');
@@ -49,19 +48,8 @@ function c20pCipher(mode, key, data, algorithm) {
4948
function c20pGenerateKey(algorithm, extractable, usages) {
5049
const { name } = algorithm;
5150

52-
const checkUsages = ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey'];
53-
54-
const usagesSet = new SafeSet(usages);
55-
if (hasAnyNotIn(usagesSet, checkUsages)) {
56-
throw lazyDOMException(
57-
`Unsupported key usage for a ${algorithm.name} key`,
58-
'SyntaxError');
59-
}
60-
if (usagesSet.size === 0) {
61-
throw lazyDOMException(
62-
'Usages cannot be empty when creating a key.',
63-
'SyntaxError');
64-
}
51+
const usagesSet = validateUsagesNotEmpty(
52+
validateKeyUsages(usages, kUsages, name));
6553

6654
return jobPromise(() => new SecretKeyGenJob(
6755
kCryptoJobWebCrypto,
@@ -78,14 +66,9 @@ function c20pImportKey(
7866
extractable,
7967
usages) {
8068
const { name } = algorithm;
81-
const checkUsages = ['encrypt', 'decrypt', 'wrapKey', 'unwrapKey'];
8269

83-
const usagesSet = new SafeSet(usages);
84-
if (hasAnyNotIn(usagesSet, checkUsages)) {
85-
throw lazyDOMException(
86-
`Unsupported key usage for a ${algorithm.name} key`,
87-
'SyntaxError');
88-
}
70+
const usagesSet = validateKeyUsages(
71+
usages, kUsages, name);
8972

9073
let handle;
9174
switch (format) {

0 commit comments

Comments
 (0)