From 213924f51936291fbbca99158b11bd4ae56c2c95 Mon Sep 17 00:00:00 2001 From: Mitch Flindell <990013+scrummitch@users.noreply.github.com> Date: Tue, 20 Dec 2022 09:15:50 +1100 Subject: [PATCH] feat: add support for W3C ES256K (#462) --- src/JWK.php | 1 + src/JWT.php | 15 ++++++++------- tests/JWTTest.php | 1 + tests/data/ec-jwkset.json | 9 +++++++++ tests/data/secp256k1-private.pem | 5 +++++ tests/data/secp256k1-public.pem | 4 ++++ 6 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 tests/data/secp256k1-private.pem create mode 100644 tests/data/secp256k1-public.pem diff --git a/src/JWK.php b/src/JWK.php index 15631ecc..c7eff8ae 100644 --- a/src/JWK.php +++ b/src/JWK.php @@ -26,6 +26,7 @@ class JWK private const ASN1_BIT_STRING = 0x03; private const EC_CURVES = [ 'P-256' => '1.2.840.10045.3.1.7', // Len: 64 + 'secp256k1' => '1.3.132.0.10', // Len: 64 // 'P-384' => '1.3.132.0.34', // Len: 96 (not yet supported) // 'P-521' => '1.3.132.0.35', // Len: 132 (not supported) ]; diff --git a/src/JWT.php b/src/JWT.php index 623e7055..269e8caf 100644 --- a/src/JWT.php +++ b/src/JWT.php @@ -55,6 +55,7 @@ class JWT public static $supported_algs = [ 'ES384' => ['openssl', 'SHA384'], 'ES256' => ['openssl', 'SHA256'], + 'ES256K' => ['openssl', 'SHA256'], 'HS256' => ['hash_hmac', 'SHA256'], 'HS384' => ['hash_hmac', 'SHA384'], 'HS512' => ['hash_hmac', 'SHA512'], @@ -132,8 +133,8 @@ public static function decode( // See issue #351 throw new UnexpectedValueException('Incorrect key for this algorithm'); } - if ($header->alg === 'ES256' || $header->alg === 'ES384') { - // OpenSSL expects an ASN.1 DER sequence for ES256/ES384 signatures + if (\in_array($header->alg, ['ES256', 'ES256K', 'ES384'], true)) { + // OpenSSL expects an ASN.1 DER sequence for ES256/ES256K/ES384 signatures $sig = self::signatureToDER($sig); } if (!self::verify("{$headb64}.{$bodyb64}", $sig, $key->getKeyMaterial(), $header->alg)) { @@ -170,8 +171,8 @@ public static function decode( * * @param array $payload PHP array * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key. - * @param string $alg Supported algorithms are 'ES384','ES256', 'HS256', 'HS384', - * 'HS512', 'RS256', 'RS384', and 'RS512' + * @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256', + * 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' * @param string $keyId * @param array $head An array with header elements to attach * @@ -210,8 +211,8 @@ public static function encode( * * @param string $msg The message to sign * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key. - * @param string $alg Supported algorithms are 'ES384','ES256', 'HS256', 'HS384', - * 'HS512', 'RS256', 'RS384', and 'RS512' + * @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256', + * 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' * * @return string An encrypted message * @@ -238,7 +239,7 @@ public static function sign( if (!$success) { throw new DomainException('OpenSSL unable to sign data'); } - if ($alg === 'ES256') { + if ($alg === 'ES256' || $alg === 'ES256K') { $signature = self::signatureFromDER($signature, 256); } elseif ($alg === 'ES384') { $signature = self::signatureFromDER($signature, 384); diff --git a/tests/JWTTest.php b/tests/JWTTest.php index 58c334ed..e3b95144 100644 --- a/tests/JWTTest.php +++ b/tests/JWTTest.php @@ -377,6 +377,7 @@ public function provideEncodeDecode() [__DIR__ . '/data/ecdsa384-private.pem', __DIR__ . '/data/ecdsa384-public.pem', 'ES384'], [__DIR__ . '/data/rsa1-private.pem', __DIR__ . '/data/rsa1-public.pub', 'RS512'], [__DIR__ . '/data/ed25519-1.sec', __DIR__ . '/data/ed25519-1.pub', 'EdDSA'], + [__DIR__ . '/data/secp256k1-private.pem', __DIR__ . '/data/secp256k1-public.pem', 'ES256K'], ]; } diff --git a/tests/data/ec-jwkset.json b/tests/data/ec-jwkset.json index 46ed8cf9..213f68ac 100644 --- a/tests/data/ec-jwkset.json +++ b/tests/data/ec-jwkset.json @@ -17,6 +17,15 @@ "x": "mQa0q5FvxPRujxzFazQT1Mo2YJJzuKiXU3svOJ41jhw", "y": "jAz7UwIl2oOFk06kj42ZFMOXmGMFUGjKASvyYtibCH0", "alg": "ES256" + }, + { + "kty": "EC", + "use": "sig", + "crv": "secp256k1", + "kid": "jwk3", + "x": "EFpwNuP322bU3WP1DtJgx67L0CUV1MxNixqPVMH2L9Q", + "y": "_fSTbijIJjpsqL16cIEvxxf3MaYMY8MbqEq066yV9ls", + "alg": "ES256K" } ] } \ No newline at end of file diff --git a/tests/data/secp256k1-private.pem b/tests/data/secp256k1-private.pem new file mode 100644 index 00000000..3e69d585 --- /dev/null +++ b/tests/data/secp256k1-private.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgC8ouvv1ZOmOjh5Nbwx6i +3b35wWN+OEkW2hzm3BKAQJ2hRANCAAT9nYGLVP6Unm/LXOoyWhsKpalffMSr3EHV +iUE8gVmj2/atnPkblx38Yj6bC3z1urERAB+JqgpWOAKaWcEYCUuO +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/tests/data/secp256k1-public.pem b/tests/data/secp256k1-public.pem new file mode 100644 index 00000000..5949135f --- /dev/null +++ b/tests/data/secp256k1-public.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE/Z2Bi1T+lJ5vy1zqMlobCqWpX3zEq9xB +1YlBPIFZo9v2rZz5G5cd/GI+mwt89bqxEQAfiaoKVjgCmlnBGAlLjg== +-----END PUBLIC KEY----- \ No newline at end of file