Skip to content

Commit e0c2225

Browse files
panvaRafaelGSS
authored andcommitted
crypto: allow length=0 for HKDF and PBKDF2 in SubtleCrypto.deriveBits
PR-URL: #55866 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 d41cb49 commit e0c2225

10 files changed

+76
-31
lines changed

lib/internal/crypto/hkdf.js

+2-1
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

+3-4
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

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Last update:
3232
- user-timing: https://github.com/web-platform-tests/wpt/tree/5ae85bf826/user-timing
3333
- wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/cde25e7e3c/wasm/jsapi
3434
- wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi
35-
- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/5f0f4ac1af/WebCryptoAPI
35+
- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/b81831169b/WebCryptoAPI
3636
- webidl/ecmascript-binding/es-exceptions: https://github.com/web-platform-tests/wpt/tree/a370aad338/webidl/ecmascript-binding/es-exceptions
3737
- webmessaging/broadcastchannel: https://github.com/web-platform-tests/wpt/tree/6495c91853/webmessaging/broadcastchannel
3838
- webstorage: https://github.com/web-platform-tests/wpt/tree/9dafa89214/webstorage
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/derived_bits_length_testcases.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ var testCases = {
33
{length: 256, expected: algorithms["HKDF"].derivation},
44
{length: 384, expected: algorithms["HKDF"].derivation384},
55
{length: 230, expected: undefined}, // should throw an exception, not multiple of 8
6-
{length: 0, expected: undefined}, // explicitly disallowed, so should throw
6+
{length: 0, expected: emptyArray},
77
{length: null, expected: undefined }, // should throw an exception
88
{length: undefined, expected: undefined }, // should throw an exception
99
{length: "omitted", expected: undefined }, // default value is null, so should throw
@@ -12,7 +12,7 @@ var testCases = {
1212
{length: 256, expected: algorithms["PBKDF2"].derivation},
1313
{length: 384, expected: algorithms["PBKDF2"].derivation384},
1414
{length: 230, expected: undefined}, // should throw an exception, not multiple of 8
15-
{length: 0, expected: undefined}, // explicitly disallowed, so should throw
15+
{length: 0, expected: emptyArray},
1616
{length: null, expected: undefined }, // should throw an exception
1717
{length: undefined, expected: undefined }, // should throw an exception
1818
{length: "omitted", expected: undefined }, // default value is null, so should throw

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@ function define_tests() {
4545
});
4646
}, testName);
4747

48-
// 0 length (OperationError)
48+
// 0 length
4949
subsetTest(promise_test, function(test) {
5050
return subtle.deriveBits(algorithm, baseKeys[derivedKeySize], 0)
5151
.then(function(derivation) {
5252
assert_equals(derivation.byteLength, 0, "Derived correctly empty key");
5353
}, function(err) {
54-
assert_equals(err.name, "OperationError", "deriveBits with 0 length correctly threw OperationError: " + err.message);
54+
assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
5555
});
5656
}, testName + " with 0 length");
5757

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

+10-10
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ function define_tests() {
4242
});
4343
}, testName);
4444

45+
// 0 length
46+
subsetTest(promise_test, function(test) {
47+
return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 0)
48+
.then(function(derivation) {
49+
assert_true(equalBuffers(derivation.byteLength, 0, "Derived correctly empty key"));
50+
}, function(err) {
51+
assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
52+
});
53+
}, testName + " with 0 length");
54+
4555
// Check for correct deriveKey results for every kind of
4656
// key that can be created by the deriveKeys operation.
4757
derivedKeyTypes.forEach(function(derivedKeyType) {
@@ -103,16 +113,6 @@ function define_tests() {
103113

104114
});
105115

106-
// 0 length (OperationError)
107-
subsetTest(promise_test, function(test) {
108-
return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 0)
109-
.then(function(derivation) {
110-
assert_unreached("0 length should have thrown an OperationError");
111-
}, function(err) {
112-
assert_equals(err.name, "OperationError", "deriveBits with 0 length correctly threw OperationError: " + err.message);
113-
});
114-
}, testName + " with 0 length");
115-
116116
// length not multiple of 8 (OperationError)
117117
subsetTest(promise_test, function(test) {
118118
return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 44)

test/fixtures/wpt/versions.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
"path": "wasm/webapi"
8989
},
9090
"WebCryptoAPI": {
91-
"commit": "5f0f4ac1af4848480406621fac99163c8ba0e242",
91+
"commit": "b81831169b8527a6c569a4ad92cf8a1baf4a7118",
9292
"path": "WebCryptoAPI"
9393
},
9494
"webidl/ecmascript-binding/es-exceptions": {

test/parallel/test-webcrypto-derivebits-hkdf.js

+15-5
Original file line numberDiff line numberDiff line change
@@ -261,11 +261,6 @@ async function testDeriveBitsBadLengths(
261261
subtle.deriveBits(algorithm, baseKeys[size], undefined), {
262262
name: 'OperationError',
263263
}),
264-
assert.rejects(
265-
subtle.deriveBits(algorithm, baseKeys[size], 0), {
266-
message: /length cannot be zero/,
267-
name: 'OperationError',
268-
}),
269264
assert.rejects(
270265
subtle.deriveBits(algorithm, baseKeys[size], null), {
271266
message: 'length cannot be null',
@@ -562,3 +557,18 @@ async function testWrongKeyType(
562557
await Promise.all(variations);
563558

564559
})().then(common.mustCall());
560+
561+
// https://github.com/w3c/webcrypto/pull/380
562+
{
563+
crypto.subtle.importKey('raw', new Uint8Array(0), 'HKDF', false, ['deriveBits']).then((key) => {
564+
return crypto.subtle.deriveBits({
565+
name: 'HKDF',
566+
hash: { name: 'SHA-256' },
567+
info: new Uint8Array(0),
568+
salt: new Uint8Array(0),
569+
}, key, 0);
570+
}).then((bits) => {
571+
assert.deepStrictEqual(bits, new ArrayBuffer(0));
572+
})
573+
.then(common.mustCall());
574+
}

test/pummel/test-webcrypto-derivebits-pbkdf2.js

+16-5
Original file line numberDiff line numberDiff line change
@@ -449,11 +449,6 @@ async function testDeriveBitsBadLengths(
449449
subtle.deriveBits(algorithm, baseKeys[size], undefined), {
450450
name: 'OperationError',
451451
}),
452-
assert.rejects(
453-
subtle.deriveBits(algorithm, baseKeys[size], 0), {
454-
message: /length cannot be zero/,
455-
name: 'OperationError',
456-
}),
457452
assert.rejects(
458453
subtle.deriveBits(algorithm, baseKeys[size], null), {
459454
message: 'length cannot be null',
@@ -693,3 +688,19 @@ async function testWrongKeyType(
693688

694689
await Promise.all(variations);
695690
})().then(common.mustCall());
691+
692+
693+
// https://github.com/w3c/webcrypto/pull/380
694+
{
695+
crypto.subtle.importKey('raw', new Uint8Array(0), 'PBKDF2', false, ['deriveBits']).then((key) => {
696+
return crypto.subtle.deriveBits({
697+
name: 'PBKDF2',
698+
hash: { name: 'SHA-256' },
699+
iterations: 10,
700+
salt: new Uint8Array(0),
701+
}, key, 0);
702+
}).then((bits) => {
703+
assert.deepStrictEqual(bits, new ArrayBuffer(0));
704+
})
705+
.then(common.mustCall());
706+
}

0 commit comments

Comments
 (0)