Skip to content

Commit f56e628

Browse files
panvamarco-ippolito
authored andcommitted
crypto: allow length=0 for HKDF and PBKDF2 in SubtleCrypto.deriveBits
PR-URL: #55866 Backport-PR-URL: #58589 Reviewed-By: Matthew Aitken <maitken033380023@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Jason Zhang <xzha4350@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent 8237346 commit f56e628

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1288
-794
lines changed

lib/internal/crypto/hkdf.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const {
4+
ArrayBuffer,
45
FunctionPrototypeCall,
56
} = primordials;
67

@@ -141,7 +142,7 @@ async function hkdfDeriveBits(algorithm, baseKey, length) {
141142
const { hash, salt, info } = algorithm;
142143

143144
if (length === 0)
144-
throw lazyDOMException('length cannot be zero', 'OperationError');
145+
return new ArrayBuffer(0);
145146
if (length === null)
146147
throw lazyDOMException('length cannot be null', 'OperationError');
147148
if (length % 8) {

lib/internal/crypto/pbkdf2.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const {
4+
ArrayBuffer,
45
FunctionPrototypeCall,
56
} = primordials;
67

@@ -98,10 +99,8 @@ async function pbkdf2DeriveBits(algorithm, baseKey, length) {
9899
'iterations cannot be zero',
99100
'OperationError');
100101

101-
const raw = baseKey[kKeyObject].export();
102-
103102
if (length === 0)
104-
throw lazyDOMException('length cannot be zero', 'OperationError');
103+
return new ArrayBuffer(0);
105104
if (length === null)
106105
throw lazyDOMException('length cannot be null', 'OperationError');
107106
if (length % 8) {
@@ -113,7 +112,7 @@ async function pbkdf2DeriveBits(algorithm, baseKey, length) {
113112
let result;
114113
try {
115114
result = await pbkdf2Promise(
116-
raw, salt, iterations, length / 8, normalizeHashName(hash.name),
115+
baseKey[kKeyObject].export(), salt, iterations, length / 8, normalizeHashName(hash.name),
117116
);
118117
} catch (err) {
119118
throw lazyDOMException(

test/fixtures/wpt/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ Last update:
2525
- interfaces: https://github.com/web-platform-tests/wpt/tree/df731dab88/interfaces
2626
- performance-timeline: https://github.com/web-platform-tests/wpt/tree/17ebc3aea0/performance-timeline
2727
- resource-timing: https://github.com/web-platform-tests/wpt/tree/22d38586d0/resource-timing
28-
- resources: https://github.com/web-platform-tests/wpt/tree/1e140d63ec/resources
28+
- resources: https://github.com/web-platform-tests/wpt/tree/919874f84f/resources
2929
- streams: https://github.com/web-platform-tests/wpt/tree/2bd26e124c/streams
3030
- url: https://github.com/web-platform-tests/wpt/tree/67880a4eb8/url
3131
- user-timing: https://github.com/web-platform-tests/wpt/tree/5ae85bf826/user-timing
3232
- wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/cde25e7e3c/wasm/jsapi
3333
- wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi
34-
- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/6748a0a246/WebCryptoAPI
34+
- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/b81831169b/WebCryptoAPI
3535
- webidl/ecmascript-binding/es-exceptions: https://github.com/web-platform-tests/wpt/tree/a370aad338/webidl/ecmascript-binding/es-exceptions
3636
- webmessaging/broadcastchannel: https://github.com/web-platform-tests/wpt/tree/e97fac4791/webmessaging/broadcastchannel
3737

3838
[Web Platform Tests]: https://github.com/web-platform-tests/wpt
39-
[`git node wpt`]: https://github.com/nodejs/node-core-utils/blob/main/docs/git-node.md#git-node-wpt
39+
[`git node wpt`]: https://github.com/nodejs/node-core-utils/blob/main/docs/git-node.md#git-node-wpt
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// META: title=WebCryptoAPI: CryptoKey.algorithm getter returns cached object
2+
3+
// https://w3c.github.io/webcrypto/#dom-cryptokey-algorithm
4+
// https://github.com/servo/servo/issues/33908
5+
6+
promise_test(function() {
7+
return self.crypto.subtle.generateKey(
8+
{
9+
name: "AES-CTR",
10+
length: 256,
11+
},
12+
true,
13+
["encrypt"],
14+
).then(
15+
function(key) {
16+
let a = key.algorithm;
17+
let b = key.algorithm;
18+
assert_true(a === b);
19+
},
20+
function(err) {
21+
assert_unreached("generateKey threw an unexpected error: " + err.toString());
22+
}
23+
);
24+
}, "CryptoKey.algorithm getter returns cached object");

test/fixtures/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.js

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1+
function define_tests_25519() {
2+
return define_tests("X25519");
3+
}
4+
5+
function define_tests_448() {
6+
return define_tests("X448");
7+
}
18

2-
function define_tests() {
9+
function define_tests(algorithmName) {
310
// May want to test prefixed implementations.
411
var subtle = self.crypto.subtle;
512

613
// Verify the derive functions perform checks against the all-zero value results,
714
// ensuring small-order points are rejected.
815
// https://www.rfc-editor.org/rfc/rfc7748#section-6.1
9-
Object.keys(kSmallOrderPoint).forEach(function(algorithmName) {
16+
{
1017
kSmallOrderPoint[algorithmName].forEach(function(test) {
1118
promise_test(async() => {
1219
let derived;
@@ -28,15 +35,16 @@ function define_tests() {
2835
assert_equals(derived, undefined, "Operation succeeded, but should not have.");
2936
}, algorithmName + " key derivation checks for all-zero value result with a key of order " + test.order);
3037
});
31-
});
38+
}
3239

3340
return importKeys(pkcs8, spki, sizes)
3441
.then(function(results) {
3542
publicKeys = results.publicKeys;
3643
privateKeys = results.privateKeys;
3744
noDeriveBitsKeys = results.noDeriveBitsKeys;
45+
ecdhKeys = results.ecdhKeys;
3846

39-
Object.keys(sizes).forEach(function(algorithmName) {
47+
{
4048
// Basic success case
4149
promise_test(function(test) {
4250
return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName])
@@ -101,11 +109,7 @@ function define_tests() {
101109

102110
// - wrong algorithm
103111
promise_test(function(test) {
104-
publicKey = publicKeys["X25519"];
105-
if (algorithmName === "X25519") {
106-
publicKey = publicKeys["X448"];
107-
}
108-
return subtle.deriveBits({name: algorithmName, public: publicKey}, privateKeys[algorithmName], 8 * sizes[algorithmName])
112+
return subtle.deriveBits({name: algorithmName, public: ecdhKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName])
109113
.then(function(derivation) {
110114
assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
111115
}, function(err) {
@@ -165,16 +169,17 @@ function define_tests() {
165169
assert_equals(err.name, "OperationError", "Should throw correct error, not " + err.name + ": " + err.message);
166170
});
167171
}, algorithmName + " asking for too many bits");
168-
});
172+
}
169173
});
170174

171175
function importKeys(pkcs8, spki, sizes) {
172176
var privateKeys = {};
173177
var publicKeys = {};
174178
var noDeriveBitsKeys = {};
179+
var ecdhPublicKeys = {};
175180

176181
var promises = [];
177-
Object.keys(pkcs8).forEach(function(algorithmName) {
182+
{
178183
var operation = subtle.importKey("pkcs8", pkcs8[algorithmName],
179184
{name: algorithmName},
180185
false, ["deriveBits", "deriveKey"])
@@ -184,8 +189,8 @@ function define_tests() {
184189
privateKeys[algorithmName] = null;
185190
});
186191
promises.push(operation);
187-
});
188-
Object.keys(pkcs8).forEach(function(algorithmName) {
192+
}
193+
{
189194
var operation = subtle.importKey("pkcs8", pkcs8[algorithmName],
190195
{name: algorithmName},
191196
false, ["deriveKey"])
@@ -195,8 +200,8 @@ function define_tests() {
195200
noDeriveBitsKeys[algorithmName] = null;
196201
});
197202
promises.push(operation);
198-
});
199-
Object.keys(spki).forEach(function(algorithmName) {
203+
}
204+
{
200205
var operation = subtle.importKey("spki", spki[algorithmName],
201206
{name: algorithmName},
202207
false, [])
@@ -206,10 +211,17 @@ function define_tests() {
206211
publicKeys[algorithmName] = null;
207212
});
208213
promises.push(operation);
209-
});
210-
214+
}
215+
{
216+
var operation = subtle.importKey("spki", ecSPKI,
217+
{name: "ECDH", namedCurve: "P-256"},
218+
false, [])
219+
.then(function(key) {
220+
ecdhPublicKeys[algorithmName] = key;
221+
});
222+
}
211223
return Promise.all(promises)
212-
.then(function(results) {return {privateKeys: privateKeys, publicKeys: publicKeys, noDeriveBitsKeys: noDeriveBitsKeys}});
224+
.then(function(results) {return {privateKeys: privateKeys, publicKeys: publicKeys, noDeriveBitsKeys: noDeriveBitsKeys, ecdhKeys: ecdhPublicKeys}});
213225
}
214226

215227
// Compares two ArrayBuffer or ArrayBufferView objects. If bitCount is
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// META: title=WebCryptoAPI: deriveKey() Using ECDH with CFRG Elliptic Curves
2+
// META: script=cfrg_curves_bits_fixtures.js
3+
// META: script=cfrg_curves_bits.js
4+
5+
// Define subtests from a `promise_test` to ensure the harness does not
6+
// complete before the subtests are available. `explicit_done` cannot be used
7+
// for this purpose because the global `done` function is automatically invoked
8+
// by the WPT infrastructure in dedicated worker tests defined using the
9+
// "multi-global" pattern.
10+
promise_test(define_tests_25519, 'setup - define tests');
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// META: title=WebCryptoAPI: deriveBits() Using ECDH with CFRG Elliptic Curves
1+
// META: title=WebCryptoAPI: deriveKey() Using ECDH with CFRG Elliptic Curves
22
// META: script=cfrg_curves_bits_fixtures.js
33
// META: script=cfrg_curves_bits.js
44

@@ -7,4 +7,4 @@
77
// for this purpose because the global `done` function is automatically invoked
88
// by the WPT infrastructure in dedicated worker tests defined using the
99
// "multi-global" pattern.
10-
promise_test(define_tests, 'setup - define tests');
10+
promise_test(define_tests_448, 'setup - define tests');

test/fixtures/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_fixtures.js

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys.js

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1+
function define_tests_25519() {
2+
return define_tests("X25519");
3+
}
4+
5+
function define_tests_448() {
6+
return define_tests("X448");
7+
}
18

2-
function define_tests() {
9+
function define_tests(algorithmName) {
310
// May want to test prefixed implementations.
411
var subtle = self.crypto.subtle;
512

@@ -8,7 +15,7 @@ function define_tests() {
815
// https://www.rfc-editor.org/rfc/rfc7748#section-6.1
916
// TODO: The spec states that the check must be done on use, but there is discussion about doing it on import.
1017
// https://github.com/WICG/webcrypto-secure-curves/pull/13
11-
Object.keys(kSmallOrderPoint).forEach(function(algorithmName) {
18+
{
1219
kSmallOrderPoint[algorithmName].forEach(function(test) {
1320
promise_test(async() => {
1421
let derived;
@@ -32,10 +39,10 @@ function define_tests() {
3239
assert_equals(derived, undefined, "Operation succeeded, but should not have.");
3340
}, algorithmName + " deriveBits checks for all-zero value result with a key of order " + test.order);
3441
});
35-
});
42+
}
3643

3744
// Ensure the keys generated by each algorithm are valid for key derivation.
38-
Object.keys(sizes).forEach(function(algorithmName) {
45+
{
3946
promise_test(async() => {
4047
let derived;
4148
try {
@@ -46,15 +53,16 @@ function define_tests() {
4653
}
4754
assert_false (derived === undefined, "Key derivation failed.");
4855
}, "Key derivation using a " + algorithmName + " generated keys.");
49-
});
56+
}
5057

5158
return importKeys(pkcs8, spki, sizes)
5259
.then(function(results) {
5360
publicKeys = results.publicKeys;
5461
privateKeys = results.privateKeys;
5562
noDeriveKeyKeys = results.noDeriveKeyKeys;
63+
ecdhKeys = results.ecdhKeys;
5664

57-
Object.keys(sizes).forEach(function(algorithmName) {
65+
{
5866
// Basic success case
5967
promise_test(function(test) {
6068
return subtle.deriveKey({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
@@ -102,11 +110,7 @@ function define_tests() {
102110

103111
// - wrong algorithm
104112
promise_test(function(test) {
105-
publicKey = publicKeys["X25519"];
106-
if (algorithmName === "X25519") {
107-
publicKey = publicKeys["X448"];
108-
}
109-
return subtle.deriveKey({name: algorithmName, public: publicKey}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
113+
return subtle.deriveKey({name: algorithmName, public: ecdhKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
110114
.then(function(key) {return crypto.subtle.exportKey("raw", key);})
111115
.then(function(exportedKey) {
112116
assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
@@ -161,16 +165,17 @@ function define_tests() {
161165
});
162166
});
163167
}, algorithmName + " public property value is a secret key");
164-
});
168+
}
165169
});
166170

167171
function importKeys(pkcs8, spki, sizes) {
168172
var privateKeys = {};
169173
var publicKeys = {};
170174
var noDeriveKeyKeys = {};
175+
var ecdhPublicKeys = {};
171176

172177
var promises = [];
173-
Object.keys(pkcs8).forEach(function(algorithmName) {
178+
{
174179
var operation = subtle.importKey("pkcs8", pkcs8[algorithmName],
175180
{name: algorithmName},
176181
false, ["deriveBits", "deriveKey"])
@@ -180,8 +185,8 @@ function define_tests() {
180185
privateKeys[algorithmName] = null;
181186
});
182187
promises.push(operation);
183-
});
184-
Object.keys(pkcs8).forEach(function(algorithmName) {
188+
}
189+
{
185190
var operation = subtle.importKey("pkcs8", pkcs8[algorithmName],
186191
{name: algorithmName},
187192
false, ["deriveBits"])
@@ -191,8 +196,8 @@ function define_tests() {
191196
noDeriveKeyKeys[algorithmName] = null;
192197
});
193198
promises.push(operation);
194-
});
195-
Object.keys(spki).forEach(function(algorithmName) {
199+
}
200+
{
196201
var operation = subtle.importKey("spki", spki[algorithmName],
197202
{name: algorithmName},
198203
false, [])
@@ -202,10 +207,18 @@ function define_tests() {
202207
publicKeys[algorithmName] = null;
203208
});
204209
promises.push(operation);
205-
});
210+
}
211+
{
212+
var operation = subtle.importKey("spki", ecSPKI,
213+
{name: "ECDH", namedCurve: "P-256"},
214+
false, [])
215+
.then(function(key) {
216+
ecdhPublicKeys[algorithmName] = key;
217+
});
218+
}
206219

207220
return Promise.all(promises)
208-
.then(function(results) {return {privateKeys: privateKeys, publicKeys: publicKeys, noDeriveKeyKeys: noDeriveKeyKeys}});
221+
.then(function(results) {return {privateKeys: privateKeys, publicKeys: publicKeys, noDeriveKeyKeys: noDeriveKeyKeys, ecdhKeys: ecdhPublicKeys}});
209222
}
210223

211224
// Compares two ArrayBuffer or ArrayBufferView objects. If bitCount is
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// META: title=WebCryptoAPI: deriveKey() Using ECDH with CFRG Elliptic Curves
2+
// META: script=cfrg_curves_bits_fixtures.js
3+
// META: script=cfrg_curves_keys.js
4+
5+
// Define subtests from a `promise_test` to ensure the harness does not
6+
// complete before the subtests are available. `explicit_done` cannot be used
7+
// for this purpose because the global `done` function is automatically invoked
8+
// by the WPT infrastructure in dedicated worker tests defined using the
9+
// "multi-global" pattern.
10+
promise_test(define_tests_25519, 'setup - define tests');

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy