Skip to content

Commit 7a842a4

Browse files
committed
fixup! Implement Web Crypto X25519
1 parent 53bc355 commit 7a842a4

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

@@ -1151,7 +1150,7 @@ public:
11511150

11521151
auto outputBitLength = resultBitLength.orDefault(sharedSecret.size() * 8);
11531152
JSG_REQUIRE(outputBitLength <= sharedSecret.size() * 8, DOMOperationError,
1154-
"Derived key length (", outputBitLength, " bits) is too long (should be less than ",
1153+
"Derived key length (", outputBitLength, " bits) is too long (should be at most ",
11551154
sharedSecret.size() * 8, " bits).");
11561155

11571156
// Round up since outputBitLength may not be a perfect multiple of 8.
@@ -1455,6 +1454,15 @@ kj::Own<EVP_PKEY> ellipticJwkReader(int curveId, SubtleCrypto::JsonWebKey keyDat
14551454
"Missing field \"crv\" for ", curveName, " key.");
14561455
JSG_REQUIRE(crv == curveName, DOMNotSupportedError,
14571456
"Only ", curveName, " is supported but \"", crv, "\" was requested.");
1457+
KJ_IF_MAYBE(alg, keyDataJwk.alg) {
1458+
// If this JWK specifies an algorithm, make sure it jives with the hash we were passed via
1459+
// importKey().
1460+
if (curveId == NID_ED25519) {
1461+
JSG_REQUIRE(*alg == "EdDSA", DOMDataError,
1462+
"JSON Web Key Algorithm parameter \"alg\" (\"", *alg, "\") does not match requested "
1463+
"Ed25519 curve.");
1464+
}
1465+
}
14581466

14591467
auto x = UNWRAP_JWK_BIGNUM(kj::mv(keyDataJwk.x), DOMDataError,
14601468
"Invalid ", crv, " key in JSON WebKey; missing or invalid public key component (\"x\").");
@@ -1502,7 +1510,7 @@ kj::Own<EVP_PKEY> ellipticJwkReader(int curveId, SubtleCrypto::JsonWebKey keyDat
15021510
"\" listed in JSON Web Key Algorithm parameter.");
15031511

15041512
JSG_REQUIRE(iter->second == curveId, DOMDataError,
1505-
"JSON Web Key Algorithm parameter \"alg\" \"", *alg, "\" does not match requested EC curve.");
1513+
"JSON Web Key Algorithm parameter \"alg\" (\"", *alg, "\") does not match requested curve.");
15061514
}
15071515

15081516
auto ecKey = OSSLCALL_OWN(EC_KEY, EC_KEY_new_by_curve_name(curveId), DOMOperationError,
@@ -1704,7 +1712,6 @@ kj::Own<CryptoKey::Impl> CryptoKey::Impl::importEcdh(
17041712
// =====================================================================================
17051713
// EDDSA & EDDH
17061714

1707-
17081715
namespace {
17091716

17101717
// Abstract base class for EDDSA and EDDH. Unfortunately, the legacy NODE-ED25519 identifier has a
@@ -1813,6 +1820,11 @@ public:
18131820
"Private key for derivation is using \"", getAlgorithmName(),
18141821
"\" while public key is using \"", publicKey->getAlgorithmName(), "\".");
18151822

1823+
auto outputBitLength = resultBitLength.orDefault(X25519_SHARED_KEY_LEN * 8);
1824+
JSG_REQUIRE(outputBitLength <= X25519_SHARED_KEY_LEN * 8, DOMOperationError,
1825+
"Derived key length (", outputBitLength, " bits) is too long (should be at most ",
1826+
X25519_SHARED_KEY_LEN * 8, " bits).");
1827+
18161828
// The check above for the algorithm `which` equality ensures that the impl can be downcast to
18171829
// EdDsaKey (assuming we don't accidentally create a class that doesn't inherit this one that
18181830
// for some reason returns an EdDsaKey).
@@ -1825,13 +1837,12 @@ public:
18251837
JSG_REQUIRE(1 == EVP_PKEY_derive_set_peer(ctx, publicKeyImpl.getEvpPkey()),
18261838
InternalDOMOperationError, "Failed to set EDDH peer", internalDescribeOpensslErrors());
18271839

1828-
kj::Vector<kj::byte> sharedSecret(X25519_SHARED_KEY_LEN);
1829-
size_t skeylen = 0;
1830-
JSG_REQUIRE(1 == EVP_PKEY_derive(ctx, NULL, &skeylen), DOMOperationError,
1831-
"Failed to derive EDDH key", internalDescribeOpensslErrors());
1832-
KJ_ASSERT(skeylen == X25519_SHARED_KEY_LEN);
1840+
kj::Vector<kj::byte> sharedSecret;
1841+
sharedSecret.resize(X25519_SHARED_KEY_LEN);
1842+
size_t skeylen = X25519_SHARED_KEY_LEN;
18331843
JSG_REQUIRE(1 == EVP_PKEY_derive(ctx, sharedSecret.begin(), &skeylen), DOMOperationError,
18341844
"Failed to derive EDDH key", internalDescribeOpensslErrors());
1845+
KJ_ASSERT(skeylen == X25519_SHARED_KEY_LEN);
18351846

18361847
// Check for all-zero value as mandated by spec
18371848
kj::byte isNonZeroSecret = 0;
@@ -1840,6 +1851,14 @@ public:
18401851
}
18411852
JSG_REQUIRE(isNonZeroSecret, DOMOperationError,
18421853
"Detected small order secure curve points, aborting EDDH derivation");
1854+
1855+
// mask off bits like in ECDH's deriveBits()
1856+
auto resultByteLength = integerCeilDivision(outputBitLength, 8u);
1857+
sharedSecret.truncate(resultByteLength);
1858+
auto numBitsToMaskOff = resultByteLength * 8 - outputBitLength;
1859+
KJ_DASSERT(numBitsToMaskOff < 8, numBitsToMaskOff);
1860+
uint8_t mask = ~((1 << numBitsToMaskOff) - 1);
1861+
sharedSecret.back() &= mask;
18431862
return sharedSecret.releaseAsArray();
18441863
}
18451864

@@ -1860,6 +1879,9 @@ private:
18601879
jwk.kty = kj::str("OKP");
18611880
jwk.crv = kj::str(getAlgorithmName() == "X25519"_kj ? "X25519"_kj : "Ed25519"_kj);
18621881
jwk.x = kj::encodeBase64Url(kj::arrayPtr(rawPublicKey, publicKeyLen));
1882+
if (getAlgorithmName() == "Ed25519"_kj) {
1883+
jwk.alg = kj::str("EdDSA");
1884+
}
18631885

18641886
if (getType() == "private"_kj) {
18651887
// Deliberately use ED25519_PUBLIC_KEY_LEN here.
@@ -1948,6 +1970,7 @@ kj::OneOf<jsg::Ref<CryptoKey>, CryptoKeyPair> EdDsaKeyBase::generateKey(
19481970
uint8_t rawPublicKey[keylen];
19491971
uint8_t rawPrivateKey[keylen * 2];
19501972
keypair(rawPublicKey, rawPrivateKey);
1973+
19511974
// The private key technically also contains the public key. Why does the keypair function bother
19521975
// writing out the public key to a separate buffer?
19531976

0 commit comments

Comments
 (0)