Skip to content

Commit df9940b

Browse files
committed
fixup! Implement Web Crypto X25519
1 parent 30596e9 commit df9940b

File tree

1 file changed

+32
-9
lines changed

1 file changed

+32
-9
lines changed

src/workerd/api/crypto-impl-asymmetric.c++

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ public:
5050
// Add salt to digest context in order to generate or verify salted signature.
5151
// Currently only used for RSA-PSS sign and verify operations.
5252

53-
5453
// ---------------------------------------------------------------------------
5554
// Implementation of CryptoKey
5655

@@ -1175,7 +1174,7 @@ public:
11751174

11761175
auto outputBitLength = resultBitLength.orDefault(sharedSecret.size() * 8);
11771176
JSG_REQUIRE(outputBitLength <= sharedSecret.size() * 8, DOMOperationError,
1178-
"Derived key length (", outputBitLength, " bits) is too long (should be less than ",
1177+
"Derived key length (", outputBitLength, " bits) is too long (should be at most ",
11791178
sharedSecret.size() * 8, " bits).");
11801179

11811180
// Round up since outputBitLength may not be a perfect multiple of 8.
@@ -1479,6 +1478,15 @@ kj::Own<EVP_PKEY> ellipticJwkReader(int curveId, SubtleCrypto::JsonWebKey keyDat
14791478
"Missing field \"crv\" for ", curveName, " key.");
14801479
JSG_REQUIRE(crv == curveName, DOMNotSupportedError,
14811480
"Only ", curveName, " is supported but \"", crv, "\" was requested.");
1481+
KJ_IF_MAYBE(alg, keyDataJwk.alg) {
1482+
// If this JWK specifies an algorithm, make sure it jives with the hash we were passed via
1483+
// importKey().
1484+
if (curveId == NID_ED25519) {
1485+
JSG_REQUIRE(*alg == "EdDSA", DOMDataError,
1486+
"JSON Web Key Algorithm parameter \"alg\" (\"", *alg, "\") does not match requested "
1487+
"Ed25519 curve.");
1488+
}
1489+
}
14821490

14831491
auto x = UNWRAP_JWK_BIGNUM(kj::mv(keyDataJwk.x), DOMDataError,
14841492
"Invalid ", crv, " key in JSON WebKey; missing or invalid public key component (\"x\").");
@@ -1526,7 +1534,7 @@ kj::Own<EVP_PKEY> ellipticJwkReader(int curveId, SubtleCrypto::JsonWebKey keyDat
15261534
"\" listed in JSON Web Key Algorithm parameter.");
15271535

15281536
JSG_REQUIRE(iter->second == curveId, DOMDataError,
1529-
"JSON Web Key Algorithm parameter \"alg\" \"", *alg, "\" does not match requested EC curve.");
1537+
"JSON Web Key Algorithm parameter \"alg\" (\"", *alg, "\") does not match requested curve.");
15301538
}
15311539

15321540
auto ecKey = OSSLCALL_OWN(EC_KEY, EC_KEY_new_by_curve_name(curveId), DOMOperationError,
@@ -1728,7 +1736,6 @@ kj::Own<CryptoKey::Impl> CryptoKey::Impl::importEcdh(
17281736
// =====================================================================================
17291737
// EDDSA & EDDH
17301738

1731-
17321739
namespace {
17331740

17341741
// Abstract base class for EDDSA and EDDH. Unfortunately, the legacy NODE-ED25519 identifier has a
@@ -1837,6 +1844,11 @@ public:
18371844
"Private key for derivation is using \"", getAlgorithmName(),
18381845
"\" while public key is using \"", publicKey->getAlgorithmName(), "\".");
18391846

1847+
auto outputBitLength = resultBitLength.orDefault(X25519_SHARED_KEY_LEN * 8);
1848+
JSG_REQUIRE(outputBitLength <= X25519_SHARED_KEY_LEN * 8, DOMOperationError,
1849+
"Derived key length (", outputBitLength, " bits) is too long (should be at most ",
1850+
X25519_SHARED_KEY_LEN * 8, " bits).");
1851+
18401852
// The check above for the algorithm `which` equality ensures that the impl can be downcast to
18411853
// EdDsaKey (assuming we don't accidentally create a class that doesn't inherit this one that
18421854
// for some reason returns an EdDsaKey).
@@ -1849,13 +1861,12 @@ public:
18491861
JSG_REQUIRE(1 == EVP_PKEY_derive_set_peer(ctx, publicKeyImpl.getEvpPkey()),
18501862
InternalDOMOperationError, "Failed to set EDDH peer", internalDescribeOpensslErrors());
18511863

1852-
kj::Vector<kj::byte> sharedSecret(X25519_SHARED_KEY_LEN);
1853-
size_t skeylen = 0;
1854-
JSG_REQUIRE(1 == EVP_PKEY_derive(ctx, NULL, &skeylen), DOMOperationError,
1855-
"Failed to derive EDDH key", internalDescribeOpensslErrors());
1856-
KJ_ASSERT(skeylen == X25519_SHARED_KEY_LEN);
1864+
kj::Vector<kj::byte> sharedSecret;
1865+
sharedSecret.resize(X25519_SHARED_KEY_LEN);
1866+
size_t skeylen = X25519_SHARED_KEY_LEN;
18571867
JSG_REQUIRE(1 == EVP_PKEY_derive(ctx, sharedSecret.begin(), &skeylen), DOMOperationError,
18581868
"Failed to derive EDDH key", internalDescribeOpensslErrors());
1869+
KJ_ASSERT(skeylen == X25519_SHARED_KEY_LEN);
18591870

18601871
// Check for all-zero value as mandated by spec
18611872
kj::byte isNonZeroSecret = 0;
@@ -1864,6 +1875,14 @@ public:
18641875
}
18651876
JSG_REQUIRE(isNonZeroSecret, DOMOperationError,
18661877
"Detected small order secure curve points, aborting EDDH derivation");
1878+
1879+
// mask off bits like in ECDH's deriveBits()
1880+
auto resultByteLength = integerCeilDivision(outputBitLength, 8u);
1881+
sharedSecret.truncate(resultByteLength);
1882+
auto numBitsToMaskOff = resultByteLength * 8 - outputBitLength;
1883+
KJ_DASSERT(numBitsToMaskOff < 8, numBitsToMaskOff);
1884+
uint8_t mask = ~((1 << numBitsToMaskOff) - 1);
1885+
sharedSecret.back() &= mask;
18671886
return sharedSecret.releaseAsArray();
18681887
}
18691888

@@ -1884,6 +1903,9 @@ private:
18841903
jwk.kty = kj::str("OKP");
18851904
jwk.crv = kj::str(getAlgorithmName() == "X25519"_kj ? "X25519"_kj : "Ed25519"_kj);
18861905
jwk.x = kj::encodeBase64Url(kj::arrayPtr(rawPublicKey, publicKeyLen));
1906+
if (getAlgorithmName() == "Ed25519"_kj) {
1907+
jwk.alg = kj::str("EdDSA");
1908+
}
18871909

18881910
if (getType() == "private"_kj) {
18891911
// Deliberately use ED25519_PUBLIC_KEY_LEN here.
@@ -1972,6 +1994,7 @@ kj::OneOf<jsg::Ref<CryptoKey>, CryptoKeyPair> EdDsaKeyBase::generateKey(
19721994
uint8_t rawPublicKey[keylen];
19731995
uint8_t rawPrivateKey[keylen * 2];
19741996
keypair(rawPublicKey, rawPrivateKey);
1997+
19751998
// The private key technically also contains the public key. Why does the keypair function bother
19761999
// writing out the public key to a separate buffer?
19772000

0 commit comments

Comments
 (0)