Skip to content

Commit 0bd46c1

Browse files
authored
crypto: improve accuracy of SubtleCrypto.supports
Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #63104 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
1 parent e690df3 commit 0bd46c1

8 files changed

Lines changed: 432 additions & 23 deletions

File tree

lib/internal/crypto/webcrypto.js

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,7 +1658,11 @@ class SubtleCrypto {
16581658
}
16591659
}
16601660

1661-
return check(operation, algorithm, length);
1661+
try {
1662+
return check(operation, algorithm, length);
1663+
} catch {
1664+
return false;
1665+
}
16621666
}
16631667
}
16641668

@@ -1701,25 +1705,35 @@ function check(op, alg, length) {
17011705
return true;
17021706
case 'deriveBits': {
17031707
if (normalizedAlgorithm.name === 'HKDF') {
1704-
try {
1705-
require('internal/crypto/hkdf').validateHkdfDeriveBitsLength(length);
1706-
} catch {
1707-
return false;
1708-
}
1708+
require('internal/crypto/hkdf').validateHkdfDeriveBitsLength(length);
17091709
}
17101710

17111711
if (normalizedAlgorithm.name === 'PBKDF2') {
1712-
try {
1713-
require('internal/crypto/pbkdf2').validatePbkdf2DeriveBitsLength(length);
1714-
} catch {
1715-
return false;
1716-
}
1712+
require('internal/crypto/pbkdf2').validatePbkdf2DeriveBitsLength(length);
17171713
}
17181714

17191715
if (StringPrototypeStartsWith(normalizedAlgorithm.name, 'Argon2')) {
1720-
try {
1721-
require('internal/crypto/argon2').validateArgon2DeriveBitsLength(length);
1722-
} catch {
1716+
require('internal/crypto/argon2').validateArgon2DeriveBitsLength(length);
1717+
}
1718+
1719+
if (normalizedAlgorithm.name === 'X25519' && length > 256) {
1720+
return false;
1721+
}
1722+
1723+
if (normalizedAlgorithm.name === 'X448' && length > 448) {
1724+
return false;
1725+
}
1726+
1727+
if (normalizedAlgorithm.name === 'ECDH') {
1728+
const namedCurve = getCryptoKeyAlgorithm(normalizedAlgorithm.public).namedCurve;
1729+
const maxLength = {
1730+
'__proto__': null,
1731+
'P-256': 256,
1732+
'P-384': 384,
1733+
'P-521': 528,
1734+
}[namedCurve];
1735+
1736+
if (length > maxLength) {
17231737
return false;
17241738
}
17251739
}

test/fixtures/webcrypto/supports-level-2.mjs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,18 +103,36 @@ export const vectors = {
103103
[true,
104104
{ name: 'X25519', public: X25519.publicKey },
105105
{ name: 'AES-CBC', length: 128 }],
106-
[true,
106+
[false,
107107
{ name: 'X25519', public: X25519.publicKey },
108108
{ name: 'HMAC', hash: 'SHA-256' }],
109+
[true,
110+
{ name: 'X25519', public: X25519.publicKey },
111+
{ name: 'HMAC', hash: 'SHA-256', length: 256 }],
112+
[false,
113+
{ name: 'X25519', public: X25519.publicKey },
114+
{ name: 'HMAC', hash: 'SHA-256', length: 257 }],
109115
[true,
110116
{ name: 'X25519', public: X25519.publicKey },
111117
'HKDF'],
112118
[true,
113119
{ name: 'ECDH', public: ECDH.publicKey },
114120
{ name: 'AES-CBC', length: 128 }],
115-
[true,
121+
[false,
116122
{ name: 'ECDH', public: ECDH.publicKey },
117123
{ name: 'HMAC', hash: 'SHA-256' }],
124+
[false,
125+
{ name: 'ECDH', public: ECDH.publicKey },
126+
{ name: 'HMAC', hash: 'SHA-256', length: 255 }],
127+
[true,
128+
{ name: 'ECDH', public: ECDH.publicKey },
129+
{ name: 'HMAC', hash: 'SHA-256', length: 256 }],
130+
[false,
131+
{ name: 'ECDH', public: ECDH.publicKey },
132+
{ name: 'HMAC', hash: 'SHA-256', length: 257 }],
133+
[false,
134+
{ name: 'ECDH', public: ECDH.publicKey },
135+
{ name: 'HMAC', hash: 'SHA-256', length: 264 }],
118136
[true,
119137
{ name: 'ECDH', public: ECDH.publicKey },
120138
'HKDF'],
@@ -143,10 +161,18 @@ export const vectors = {
143161

144162
[true,
145163
{ name: 'ECDH', public: ECDH.publicKey }],
164+
[true,
165+
{ name: 'ECDH', public: ECDH.publicKey }, 256],
166+
[false,
167+
{ name: 'ECDH', public: ECDH.publicKey }, 257],
168+
[false,
169+
{ name: 'ECDH', public: ECDH.publicKey }, 264],
146170
[false, { name: 'ECDH', public: ECDH.privateKey }],
147171
[false, 'ECDH'],
148172

149173
[true, { name: 'X25519', public: X25519.publicKey }],
174+
[true, { name: 'X25519', public: X25519.publicKey }, 256],
175+
[false, { name: 'X25519', public: X25519.publicKey }, 257],
150176
[false, { name: 'X25519', public: X25519.privateKey }],
151177
[false, 'X25519'],
152178
],

test/fixtures/webcrypto/supports-secure-curves.mjs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,23 @@ export const vectors = {
2828
[!boringSSL,
2929
{ name: 'X448', public: X448?.publicKey },
3030
{ name: 'AES-CBC', length: 128 }],
31-
[!boringSSL,
31+
[false,
3232
{ name: 'X448', public: X448?.publicKey },
3333
{ name: 'HMAC', hash: 'SHA-256' }],
34+
[!boringSSL,
35+
{ name: 'X448', public: X448?.publicKey },
36+
{ name: 'HMAC', hash: 'SHA-256', length: 448 }],
37+
[false,
38+
{ name: 'X448', public: X448?.publicKey },
39+
{ name: 'HMAC', hash: 'SHA-256', length: 449 }],
3440
[!boringSSL,
3541
{ name: 'X448', public: X448?.publicKey },
3642
'HKDF'],
3743
],
3844
'deriveBits': [
3945
[!boringSSL, { name: 'X448', public: X448?.publicKey }],
46+
[!boringSSL, { name: 'X448', public: X448?.publicKey }, 448],
47+
[false, { name: 'X448', public: X448?.publicKey }, 449],
4048
[false, { name: 'X448', public: X25519.publicKey }],
4149
[false, { name: 'X448', public: X448?.privateKey }],
4250
[false, 'X448'],

test/fixtures/wpt/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Last update:
3434
- wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/65a2134d50/wasm/jsapi
3535
- wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi
3636
- web-locks: https://github.com/web-platform-tests/wpt/tree/10a122a6bc/web-locks
37-
- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/2cb332d710/WebCryptoAPI
37+
- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/8b5cd267b4/WebCryptoAPI
3838
- webidl: https://github.com/web-platform-tests/wpt/tree/63ca529a02/webidl
3939
- webidl/ecmascript-binding/es-exceptions: https://github.com/web-platform-tests/wpt/tree/2f96fa1996/webidl/ecmascript-binding/es-exceptions
4040
- webmessaging/broadcastchannel: https://github.com/web-platform-tests/wpt/tree/6495c91853/webmessaging/broadcastchannel
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// META: title=WebCrypto API: supports method tests for algorithms in https://wicg.github.io/webcrypto-modern-algos/
2+
// META: script=util/helpers.js
3+
4+
'use strict';
5+
6+
const modernAlgorithms = {
7+
// Asymmetric algorithms
8+
'ML-DSA-44': {
9+
operations: ['generateKey', 'importKey', 'sign', 'verify'],
10+
},
11+
'ML-DSA-65': {
12+
operations: ['generateKey', 'importKey', 'sign', 'verify'],
13+
},
14+
'ML-DSA-87': {
15+
operations: ['generateKey', 'importKey', 'sign', 'verify'],
16+
},
17+
'ML-KEM-512': {
18+
operations: [
19+
'generateKey', 'importKey', 'encapsulateKey', 'encapsulateBits',
20+
'decapsulateKey', 'decapsulateBits'
21+
],
22+
},
23+
'ML-KEM-768': {
24+
operations: [
25+
'generateKey', 'importKey', 'encapsulateKey', 'encapsulateBits',
26+
'decapsulateKey', 'decapsulateBits'
27+
],
28+
},
29+
'ML-KEM-1024': {
30+
operations: [
31+
'generateKey', 'importKey', 'encapsulateKey', 'encapsulateBits',
32+
'decapsulateKey', 'decapsulateBits'
33+
],
34+
},
35+
36+
// Symmetric algorithms
37+
'ChaCha20-Poly1305': {
38+
operations: ['generateKey', 'importKey', 'encrypt', 'decrypt'],
39+
encryptParams: {name: 'ChaCha20-Poly1305', iv: new Uint8Array(12)},
40+
},
41+
42+
};
43+
44+
const operations = [
45+
'generateKey',
46+
'importKey',
47+
'sign',
48+
'verify',
49+
'encrypt',
50+
'decrypt',
51+
'deriveBits',
52+
'digest',
53+
'encapsulateKey',
54+
'encapsulateBits',
55+
'decapsulateKey',
56+
'decapsulateBits',
57+
];
58+
59+
// Test that supports method exists and is a static method
60+
test(() => {
61+
assert_true(
62+
typeof SubtleCrypto.supports === 'function',
63+
'SubtleCrypto.supports should be a function');
64+
}, 'SubtleCrypto.supports method exists');
65+
66+
67+
// Test standard WebCrypto algorithms for requested operations
68+
for (const [algorithmName, algorithmInfo] of Object.entries(modernAlgorithms)) {
69+
for (const operation of operations) {
70+
promise_test(async (t) => {
71+
const isSupported = algorithmInfo.operations.includes(operation);
72+
73+
// Use appropriate algorithm parameters for each operation
74+
let algorithm;
75+
let lengthOrAdditionalAlgorithm;
76+
switch (operation) {
77+
case 'generateKey':
78+
algorithm = algorithmInfo.keyGenParams || algorithmName;
79+
break;
80+
case 'importKey':
81+
algorithm = algorithmInfo.importParams || algorithmName;
82+
break;
83+
case 'sign':
84+
case 'verify':
85+
algorithm = algorithmInfo.signParams || algorithmName;
86+
break;
87+
case 'encrypt':
88+
case 'decrypt':
89+
algorithm = algorithmInfo.encryptParams || algorithmName;
90+
break;
91+
case 'deriveBits':
92+
algorithm = algorithmInfo.deriveBitsParams || algorithmName;
93+
if (algorithm?.public instanceof Promise) {
94+
algorithm.public = (await algorithm.public).publicKey;
95+
}
96+
if (algorithmName === 'PBKDF2' || algorithmName === 'HKDF') {
97+
lengthOrAdditionalAlgorithm = 256;
98+
}
99+
break;
100+
case 'digest':
101+
algorithm = algorithmName;
102+
break;
103+
case 'encapsulateKey':
104+
case 'encapsulateBits':
105+
case 'decapsulateKey':
106+
case 'decapsulateBits':
107+
algorithm = algorithmName;
108+
if (operation === 'encapsulateKey' || operation === 'decapsulateKey') {
109+
lengthOrAdditionalAlgorithm = { name: 'AES-GCM', length: 256 };
110+
}
111+
break;
112+
default:
113+
algorithm = algorithmName;
114+
}
115+
116+
const result = SubtleCrypto.supports(operation, algorithm, lengthOrAdditionalAlgorithm);
117+
118+
if (isSupported) {
119+
assert_true(result, `${algorithmName} should support ${operation}`);
120+
} else {
121+
assert_false(
122+
result, `${algorithmName} should not support ${operation}`);
123+
}
124+
}, `supports(${operation}, ${algorithmName})`);
125+
}
126+
}
127+
128+
// Test some algorithm objects with valid parameters
129+
test(() => {
130+
assert_true(
131+
SubtleCrypto.supports('encrypt', {
132+
name: 'ChaCha20-Poly1305',
133+
iv: new Uint8Array(12),
134+
tagLength: 128,
135+
}),
136+
'ChaCha20-Poly1305 encrypt with valid tagLength');
137+
}, 'supports returns true for algorithm objects with valid parameters');
138+
139+
// Test some algorithm objects with invalid parameters
140+
test(() => {
141+
assert_false(
142+
SubtleCrypto.supports('encrypt', {
143+
name: 'ChaCha20-Poly1305',
144+
iv: new Uint8Array(12),
145+
tagLength: 100,
146+
}),
147+
'ChaCha20-Poly1305 encrypt with invalid tagLength');
148+
149+
assert_false(
150+
SubtleCrypto.supports('encrypt', {
151+
name: 'ChaCha20-Poly1305',
152+
iv: new Uint8Array(10),
153+
tagLength: 128,
154+
}),
155+
'ChaCha20-Poly1305 encrypt with invalid iv');
156+
}, 'supports returns false for algorithm objects with invalid parameters');
157+
158+
159+
done();

0 commit comments

Comments
 (0)