Skip to content

Commit 39d2e18

Browse files
committed
feat: add ed25519 support to JWK (public keys)
Reference documentation: https://datatracker.ietf.org/doc/html/rfc8037
1 parent 018dfc4 commit 39d2e18

File tree

4 files changed

+48
-6
lines changed

4 files changed

+48
-6
lines changed

src/JWK.php

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ class JWK
3030
// 'P-521' => '1.3.132.0.35', // Len: 132 (not supported)
3131
];
3232

33+
// 'crv' identifier => JWT 'alg'
34+
private const OKP_CURVES = [
35+
'Ed25519' => 'EdDSA',
36+
];
37+
3338
/**
3439
* Parse a set of JWK keys
3540
*
@@ -96,11 +101,17 @@ public static function parseKey(array $jwk, string $defaultAlg = null): ?Key
96101
throw new UnexpectedValueException('JWK must contain a "kty" parameter');
97102
}
98103

99-
if (!isset($jwk['alg'])) {
104+
$ktyNotRequiringAlg = [
105+
// In Octet Key Pair (OKP) keys, the signing algorithm (alg) will not be read from the
106+
// JWK, as it can be inferred directly from the curve type (crv).
107+
// @see https://datatracker.ietf.org/doc/html/rfc8037#section-3.1
108+
'OKP',
109+
];
110+
if (!isset($jwk['alg']) && !\in_array($jwk['kty'], $ktyNotRequiringAlg, true)) {
100111
if (\is_null($defaultAlg)) {
101112
// The "alg" parameter is optional in a KTY, but an algorithm is required
102-
// for parsing in this library. Use the $defaultAlg parameter when parsing the
103-
// key set in order to prevent this error.
113+
// for parsing certain key types in this library. Use the $defaultAlg parameter
114+
// when parsing the key set in order to prevent this error.
104115
// @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4
105116
throw new UnexpectedValueException('JWK must contain an "alg" parameter');
106117
}
@@ -144,8 +155,28 @@ public static function parseKey(array $jwk, string $defaultAlg = null): ?Key
144155

145156
$publicKey = self::createPemFromCrvAndXYCoordinates($jwk['crv'], $jwk['x'], $jwk['y']);
146157
return new Key($publicKey, $jwk['alg']);
158+
case 'OKP':
159+
if (isset($jwk['d'])) {
160+
// The key is actually a private key
161+
throw new UnexpectedValueException('Key data must be for a public key');
162+
}
163+
164+
if (! isset($jwk['crv'])) {
165+
throw new UnexpectedValueException('crv not set');
166+
}
167+
168+
if (!isset(self::OKP_CURVES[$jwk['crv']])) {
169+
throw new DomainException('Unrecognised or unsupported OKP curve');
170+
}
171+
172+
if (empty($jwk['x'])) {
173+
throw new UnexpectedValueException('x not set');
174+
}
175+
176+
$publicKey = \base64_encode(JWT::urlsafeB64Decode($jwk['x']));
177+
$alg = self::OKP_CURVES[$jwk['crv']];
178+
return new Key($publicKey, $alg);
147179
default:
148-
// Currently only RSA is supported
149180
break;
150181
}
151182

src/JWT.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ public static function encode(
210210
*
211211
* @param string $msg The message to sign
212212
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
213-
* @param string $alg Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
213+
* @param string $alg Supported algorithms are 'EdDSA', 'ES384', 'ES256', 'HS256', 'HS384',
214214
* 'HS512', 'RS256', 'RS384', and 'RS512'
215215
*
216216
* @return string An encrypted message
@@ -270,7 +270,7 @@ public static function sign(
270270
*
271271
* @param string $msg The original message (header and body)
272272
* @param string $signature The original signature
273-
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
273+
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For Ed*, ES*, HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
274274
* @param string $alg The algorithm
275275
*
276276
* @return bool

tests/JWKTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ public function provideDecodeByJwkKeySet()
151151
return [
152152
['rsa1-private.pem', 'rsa-jwkset.json', 'RS256'],
153153
['ecdsa256-private.pem', 'ec-jwkset.json', 'ES256'],
154+
['ed25519-1.sec', 'ed25519-jwkset.json', 'EdDSA'],
154155
];
155156
}
156157

tests/data/ed25519-jwkset.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"keys": [
3+
{
4+
"kid": "jwk1",
5+
"kty": "OKP",
6+
"crv": "Ed25519",
7+
"x": "uOSJMhbKSG4V5xUHS7B9YHmVg_1yVd-G-Io6oBFhSfY"
8+
}
9+
]
10+
}

0 commit comments

Comments
 (0)