Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9ee9f52

Browse filesBrowse files
panvaruyadorno
authored andcommitted
crypto: allow non-multiple of 8 in SubtleCrypto.deriveBits
PR-URL: #55296 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent bfe7982 commit 9ee9f52
Copy full SHA for 9ee9f52

File tree

Expand file treeCollapse file tree

5 files changed

+28
-27
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

5 files changed

+28
-27
lines changed
Open diff view settings
Collapse file

‎doc/api/webcrypto.md‎

Copy file name to clipboardExpand all lines: doc/api/webcrypto.md
-3Lines changed: 0 additions & 3 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -598,9 +598,6 @@ Using the method and parameters specified in `algorithm` and the keying
598598
material provided by `baseKey`, `subtle.deriveBits()` attempts to generate
599599
`length` bits.
600600

601-
The Node.js implementation requires that `length`, when a number, is a multiple
602-
of `8`.
603-
604601
When `length` is not provided or `null` the maximum number of bits for a given
605602
algorithm is generated. This is allowed for the `'ECDH'`, `'X25519'`, and `'X448'`
606603
algorithms, for other algorithms `length` is required to be a number.
Collapse file

‎lib/internal/crypto/diffiehellman.js‎

Copy file name to clipboardExpand all lines: lib/internal/crypto/diffiehellman.js
+18-8Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const {
55
MathCeil,
66
ObjectDefineProperty,
77
SafeSet,
8+
Uint8Array,
89
} = primordials;
910

1011
const { Buffer } = require('buffer');
@@ -295,6 +296,8 @@ function diffieHellman(options) {
295296
return statelessDH(privateKey[kHandle], publicKey[kHandle]);
296297
}
297298

299+
let masks;
300+
298301
// The ecdhDeriveBits function is part of the Web Crypto API and serves both
299302
// deriveKeys and deriveBits functions.
300303
async function ecdhDeriveBits(algorithm, baseKey, length) {
@@ -341,18 +344,25 @@ async function ecdhDeriveBits(algorithm, baseKey, length) {
341344

342345
// If the length is not a multiple of 8 the nearest ceiled
343346
// multiple of 8 is sliced.
344-
length = MathCeil(length / 8);
345-
const { byteLength } = bits;
347+
const sliceLength = MathCeil(length / 8);
346348

349+
const { byteLength } = bits;
347350
// If the length is larger than the derived secret, throw.
348-
// Otherwise, we either return the secret or a truncated
349-
// slice.
350-
if (byteLength < length)
351+
if (byteLength < sliceLength)
351352
throw lazyDOMException('derived bit length is too small', 'OperationError');
352353

353-
return length === byteLength ?
354-
bits :
355-
ArrayBufferPrototypeSlice(bits, 0, length);
354+
const slice = ArrayBufferPrototypeSlice(bits, 0, sliceLength);
355+
356+
const mod = length % 8;
357+
if (mod === 0)
358+
return slice;
359+
360+
// eslint-disable-next-line no-sparse-arrays
361+
masks ||= [, 0b10000000, 0b11000000, 0b11100000, 0b11110000, 0b11111000, 0b11111100, 0b11111110];
362+
363+
const masked = new Uint8Array(slice);
364+
masked[sliceLength - 1] = masked[sliceLength - 1] & masks[mod];
365+
return masked.buffer;
356366
}
357367

358368
module.exports = {
Collapse file

‎test/parallel/test-webcrypto-derivebits-cfrg.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-webcrypto-derivebits-cfrg.js
+5-3Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,11 @@ async function prepareKeys() {
140140
public: publicKey
141141
}, privateKey, 8 * size - 11);
142142

143-
assert.strictEqual(
144-
Buffer.from(bits).toString('hex'),
145-
result.slice(0, -2));
143+
const expected = Buffer.from(result.slice(0, -2), 'hex');
144+
expected[size - 2] = expected[size - 2] & 0b11111000;
145+
assert.deepStrictEqual(
146+
Buffer.from(bits),
147+
expected);
146148
}
147149
}));
148150

Collapse file

‎test/parallel/test-webcrypto-derivebits-ecdh.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-webcrypto-derivebits-ecdh.js
+5-3Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,11 @@ async function prepareKeys() {
161161
public: publicKey
162162
}, privateKey, 8 * size - 11);
163163

164-
assert.strictEqual(
165-
Buffer.from(bits).toString('hex'),
166-
result.slice(0, -2));
164+
const expected = Buffer.from(result.slice(0, -2), 'hex');
165+
expected[size - 2] = expected[size - 2] & 0b11111000;
166+
assert.deepStrictEqual(
167+
Buffer.from(bits),
168+
expected);
167169
}
168170
}));
169171

Collapse file

‎test/wpt/status/WebCryptoAPI.cjs‎

Copy file name to clipboardExpand all lines: test/wpt/status/WebCryptoAPI.cjs
-10Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,6 @@ module.exports = {
88
'algorithm-discards-context.https.window.js': {
99
'skip': 'Not relevant in Node.js context',
1010
},
11-
'derive_bits_keys/derived_bits_length.https.any.js': {
12-
'fail': {
13-
// See https://github.com/nodejs/node/pull/55296
14-
// The fix is pending a decision whether truncation in ECDH/X* will be removed from the spec entirely
15-
'expected': [
16-
"ECDH derivation with 230 as 'length' parameter",
17-
"X25519 derivation with 230 as 'length' parameter",
18-
],
19-
},
20-
},
2111
'historical.any.js': {
2212
'skip': 'Not relevant in Node.js context',
2313
},

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.