crypto: make deriveBits length parameter optional and nullable

PR-URL: https://github.com/nodejs/node/pull/53601
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
This commit is contained in:
Filip Skokan 2024-06-29 00:21:29 +02:00 committed by GitHub
parent 0062d5a076
commit d65b17082b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 51 additions and 9 deletions

View File

@ -569,11 +569,15 @@ The algorithms currently supported include:
* `'AES-CBC'` * `'AES-CBC'`
* `'AES-GCM`' * `'AES-GCM`'
### `subtle.deriveBits(algorithm, baseKey, length)` ### `subtle.deriveBits(algorithm, baseKey[, length])`
<!-- YAML <!-- YAML
added: v15.0.0 added: v15.0.0
changes: changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/53601
description: The length parameter is now optional for `'ECDH'`, `'X25519'`,
and `'X448'`.
- version: - version:
- v18.4.0 - v18.4.0
- v16.17.0 - v16.17.0
@ -585,7 +589,7 @@ changes:
* `algorithm`: {AlgorithmIdentifier|EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params} * `algorithm`: {AlgorithmIdentifier|EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params}
* `baseKey`: {CryptoKey} * `baseKey`: {CryptoKey}
* `length`: {number|null} * `length`: {number|null} **Default:** `null`
* Returns: {Promise} Fulfills with an {ArrayBuffer} * Returns: {Promise} Fulfills with an {ArrayBuffer}
<!--lint enable maximum-line-length remark-lint--> <!--lint enable maximum-line-length remark-lint-->
@ -594,12 +598,12 @@ Using the method and parameters specified in `algorithm` and the keying
material provided by `baseKey`, `subtle.deriveBits()` attempts to generate material provided by `baseKey`, `subtle.deriveBits()` attempts to generate
`length` bits. `length` bits.
The Node.js implementation requires that when `length` is a The Node.js implementation requires that `length`, when a number, is a multiple
number it must be multiple of `8`. of `8`.
When `length` is `null` the maximum number of bits for a given algorithm is When `length` is not provided or `null` the maximum number of bits for a given
generated. This is allowed for the `'ECDH'`, `'X25519'`, and `'X448'` algorithm is generated. This is allowed for the `'ECDH'`, `'X25519'`, and `'X448'`
algorithms. algorithms, for other algorithms `length` is required to be a number.
If successful, the returned promise will be resolved with an {ArrayBuffer} If successful, the returned promise will be resolved with an {ArrayBuffer}
containing the generated data. containing the generated data.

View File

@ -173,12 +173,12 @@ async function generateKey(
return result; return result;
} }
async function deriveBits(algorithm, baseKey, length) { async function deriveBits(algorithm, baseKey, length = null) {
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto'); if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
webidl ??= require('internal/crypto/webidl'); webidl ??= require('internal/crypto/webidl');
const prefix = "Failed to execute 'deriveBits' on 'SubtleCrypto'"; const prefix = "Failed to execute 'deriveBits' on 'SubtleCrypto'";
webidl.requiredArguments(arguments.length, 3, { prefix }); webidl.requiredArguments(arguments.length, 2, { prefix });
algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
prefix, prefix,
context: '1st argument', context: '1st argument',

View File

@ -101,6 +101,16 @@ async function prepareKeys() {
assert.strictEqual(Buffer.from(bits).toString('hex'), result); assert.strictEqual(Buffer.from(bits).toString('hex'), result);
} }
{
// Default length
const bits = await subtle.deriveBits({
name,
public: publicKey
}, privateKey);
assert.strictEqual(Buffer.from(bits).toString('hex'), result);
}
{ {
// Short Result // Short Result
const bits = await subtle.deriveBits({ const bits = await subtle.deriveBits({

View File

@ -122,6 +122,16 @@ async function prepareKeys() {
assert.strictEqual(Buffer.from(bits).toString('hex'), result); assert.strictEqual(Buffer.from(bits).toString('hex'), result);
} }
{
// Default length
const bits = await subtle.deriveBits({
name: 'ECDH',
public: publicKey
}, privateKey);
assert.strictEqual(Buffer.from(bits).toString('hex'), result);
}
{ {
// Short Result // Short Result
const bits = await subtle.deriveBits({ const bits = await subtle.deriveBits({

View File

@ -271,6 +271,11 @@ async function testDeriveBitsBadLengths(
message: 'length cannot be null', message: 'length cannot be null',
name: 'OperationError', name: 'OperationError',
}), }),
assert.rejects(
subtle.deriveBits(algorithm, baseKeys[size]), {
message: 'length cannot be null',
name: 'OperationError',
}),
assert.rejects( assert.rejects(
subtle.deriveBits(algorithm, baseKeys[size], 15), { subtle.deriveBits(algorithm, baseKeys[size], 15), {
message: /length must be a multiple of 8/, message: /length must be a multiple of 8/,

View File

@ -459,6 +459,11 @@ async function testDeriveBitsBadLengths(
message: 'length cannot be null', message: 'length cannot be null',
name: 'OperationError', name: 'OperationError',
}), }),
assert.rejects(
subtle.deriveBits(algorithm, baseKeys[size]), {
message: 'length cannot be null',
name: 'OperationError',
}),
assert.rejects( assert.rejects(
subtle.deriveBits(algorithm, baseKeys[size], 15), { subtle.deriveBits(algorithm, baseKeys[size], 15), {
message: /length must be a multiple of 8/, message: /length must be a multiple of 8/,

View File

@ -4,5 +4,13 @@
}, },
"historical.any.js": { "historical.any.js": {
"skip": "Not relevant in Node.js context" "skip": "Not relevant in Node.js context"
},
"idlharness.https.any.js": {
"fail": {
"note": "WPT not updated for https://github.com/w3c/webcrypto/pull/345 yet",
"expected": [
"SubtleCrypto interface: operation deriveBits(AlgorithmIdentifier, CryptoKey, unsigned long)"
]
}
} }
} }