Skip to content

Commit

Permalink
Make deriveBits length argument optional per spec change
Browse files Browse the repository at this point in the history
  • Loading branch information
jasnell authored and ns476 committed Aug 2, 2024
1 parent 047ca62 commit 1b8f610
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 7 deletions.
14 changes: 8 additions & 6 deletions src/workerd/api/crypto/crypto.c++
Original file line number Diff line number Diff line change
Expand Up @@ -447,16 +447,18 @@ jsg::Promise<kj::Array<kj::byte>> SubtleCrypto::deriveBits(
jsg::Lock& js,
kj::OneOf<kj::String, DeriveKeyAlgorithm> algorithmParam,
const CryptoKey& baseKey,
kj::Maybe<int> lengthParam) {
jsg::Optional<kj::Maybe<int>> lengthParam) {
auto algorithm = interpretAlgorithmParam(kj::mv(algorithmParam));

auto checkErrorsOnFinish = webCryptoOperationBegin(__func__, algorithm);

auto length = lengthParam.map([&](int l) {
// TODO(cleanup): Use the type jsg wrapper for uint32_t?
JSG_REQUIRE(l >= 0, TypeError, "deriveBits length must be an unsigned long integer.");
return uint32_t(l);
});
kj::Maybe<uint32_t> length = kj::none;
KJ_IF_SOME(maybeLength, lengthParam) {
KJ_IF_SOME(l, maybeLength) {
JSG_REQUIRE(l >= 0, TypeError, "deriveBits length must be an unsigned long integer.");
length = uint32_t(l);
}
}

return js.evalNow([&] {
validateOperation(baseKey, algorithm.name, CryptoKeyUsageSet::deriveBits());
Expand Down
7 changes: 6 additions & 1 deletion src/workerd/api/crypto/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,12 @@ class SubtleCrypto: public jsg::Object {
jsg::Lock& js,
kj::OneOf<kj::String, DeriveKeyAlgorithm> algorithm,
const CryptoKey& baseKey,
kj::Maybe<int> length);
// The operation needs to be able to take both undefined and null
// and handle them equivalently... if we just used jsg::Optional<int>
// here, null would be coerced to 0. If we just used kj::Maybe<int>
// here, undefined would be an error. So we need to use an optional
// maybe int in order to treat undefined and null as being equivalent.
jsg::Optional<kj::Maybe<int>> length);

jsg::Promise<jsg::Ref<CryptoKey>> importKey(
jsg::Lock& js,
Expand Down
57 changes: 57 additions & 0 deletions src/workerd/api/tests/crypto-extras-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,60 @@ export const cryptoZeroLength = {
}
};

export const deriveBitsNullLength = {
async test() {

// Tests that deriveBits can take a null or undefined length
// argument and still return the correct number of bits if
// the algorithm supports it. This is a recent spec change.

const pair = await crypto.subtle.generateKey(
{
name: "ECDH",
namedCurve: "P-384",
},
false,
["deriveBits"],
);

{
const bits = await crypto.subtle.deriveBits(
{
name: 'ECDH',
namedCurve: 'P-384',
public: pair.publicKey,
},
pair.privateKey, undefined
);

strictEqual(bits.byteLength, 48);
}

{
const bits = await crypto.subtle.deriveBits(
{
name: 'ECDH',
namedCurve: 'P-384',
public: pair.publicKey,
},
pair.privateKey, null
);

strictEqual(bits.byteLength, 48);
}

{
const bits = await crypto.subtle.deriveBits(
{
name: 'ECDH',
namedCurve: 'P-384',
public: pair.publicKey,
},
pair.privateKey
);

strictEqual(bits.byteLength, 48);
}

}
};

0 comments on commit 1b8f610

Please sign in to comment.