From bde2edf1e9204aae06e27487068c90e4c451a02f Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Wed, 24 Aug 2022 08:04:34 +0200 Subject: [PATCH] Fixes OKP keys --- src/Component/KeyManagement/JWKFactory.php | 6 +- .../KeyEncryption/ECDHES/AbstractECDH.php | 4 +- .../ECDHES/ECDHESKeyAgreementTest.php | 72 +++++++++++++++++- .../ECDHSS/ECDHSSKeyAgreementTest.php | 76 +++++++++++++++++++ 4 files changed, 153 insertions(+), 5 deletions(-) diff --git a/src/Component/KeyManagement/JWKFactory.php b/src/Component/KeyManagement/JWKFactory.php index 0e0b528a..88e483d5 100644 --- a/src/Component/KeyManagement/JWKFactory.php +++ b/src/Component/KeyManagement/JWKFactory.php @@ -106,7 +106,7 @@ public static function createOKPKey(string $curve, array $values = []): JWK switch ($curve) { case 'X25519': $keyPair = sodium_crypto_box_keypair(); - $secret = $keyPair; + $d = sodium_crypto_box_secretkey($keyPair); $x = sodium_crypto_box_publickey($keyPair); break; @@ -114,6 +114,8 @@ public static function createOKPKey(string $curve, array $values = []): JWK case 'Ed25519': $keyPair = sodium_crypto_sign_keypair(); $secret = sodium_crypto_sign_secretkey($keyPair); + $secretLength = mb_strlen($secret, '8bit'); + $d = mb_substr($secret, 0, -$secretLength / 2, '8bit'); $x = sodium_crypto_sign_publickey($keyPair); break; @@ -121,8 +123,6 @@ public static function createOKPKey(string $curve, array $values = []): JWK default: throw new InvalidArgumentException(sprintf('Unsupported "%s" curve', $curve)); } - $secretLength = mb_strlen($secret, '8bit'); - $d = mb_substr($secret, 0, -$secretLength / 2, '8bit'); $values = array_merge( $values, diff --git a/src/EncryptionAlgorithm/KeyEncryption/ECDHES/AbstractECDH.php b/src/EncryptionAlgorithm/KeyEncryption/ECDHES/AbstractECDH.php index 80bab7fa..105048c6 100644 --- a/src/EncryptionAlgorithm/KeyEncryption/ECDHES/AbstractECDH.php +++ b/src/EncryptionAlgorithm/KeyEncryption/ECDHES/AbstractECDH.php @@ -293,7 +293,9 @@ private function createOKPKey(string $curve): JWK case 'Ed25519': $keyPair = sodium_crypto_sign_keypair(); - $d = sodium_crypto_sign_secretkey($keyPair); + $secret = sodium_crypto_sign_secretkey($keyPair); + $secretLength = mb_strlen($secret, '8bit'); + $d = mb_substr($secret, 0, -$secretLength / 2, '8bit'); $x = sodium_crypto_sign_publickey($keyPair); break; diff --git a/tests/EncryptionAlgorithm/KeyEncryption/ECDHES/ECDHESKeyAgreementTest.php b/tests/EncryptionAlgorithm/KeyEncryption/ECDHES/ECDHESKeyAgreementTest.php index 94c7bdf4..5ae4d9c3 100644 --- a/tests/EncryptionAlgorithm/KeyEncryption/ECDHES/ECDHESKeyAgreementTest.php +++ b/tests/EncryptionAlgorithm/KeyEncryption/ECDHES/ECDHESKeyAgreementTest.php @@ -26,7 +26,7 @@ final class ECDHESKeyAgreementTest extends TestCase * * @test */ - public function getAgreementKey(): void + public function getAgreementKeyWithEllipticCurveKey(): void { $receiver = new JWK([ 'kty' => 'EC', @@ -51,6 +51,76 @@ public function getAgreementKey(): void static::assertArrayHasKey('y', $additional_header_values['epk']); } + /** + * @see https://tools.ietf.org/html/rfc7518#appendix-C + * + * @test + */ + public function getAgreementKeyWithA128KeyWrapAndWithOctetKeyPairKey(): void + { + $header = [ + 'enc' => 'A128GCM', + ]; + + $private = new JWK([ + 'kty' => 'OKP', + 'crv' => 'X25519', + 'd' => 'uns2Byv3po_cjjG8XRCtU-lEOrOgLbsDr5cXHmgjVvA', + 'x' => 'k8IkMMO9I0foCYqEcbfM49DjEoWpHdho_GKNMXk1rFw', + ]); + $public = $private->toPublic(); + + $cek = [ + 4, + 211, + 31, + 197, + 84, + 157, + 252, + 254, + 11, + 100, + 157, + 250, + 63, + 170, + 106, + 206, + 107, + 124, + 212, + 45, + 111, + 107, + 9, + 219, + 200, + 177, + 0, + 240, + 143, + 156, + 44, + 207, + ]; + foreach ($cek as $key => $value) { + $cek[$key] = str_pad(dechex($value), 2, '0', STR_PAD_LEFT); + } + $cek = hex2bin(implode('', $cek)); + + $ecdh_es = new ECDHESA128KW(); + $encrypted_cek = $ecdh_es->wrapAgreementKey($public, null, $cek, 128, $header, $header); + static::assertArrayHasKey('epk', $header); + static::assertArrayHasKey('crv', $header['epk']); + static::assertArrayHasKey('kty', $header['epk']); + static::assertArrayHasKey('x', $header['epk']); + static::assertArrayNotHasKey('y', $header['epk']); + static::assertSame('X25519', $header['epk']['crv']); + static::assertSame('OKP', $header['epk']['kty']); + static::assertSame($cek, $ecdh_es->unwrapAgreementKey($private, null, $encrypted_cek, 128, $header)); + } + /** * @test */ diff --git a/tests/EncryptionAlgorithm/KeyEncryption/ECDHSS/ECDHSSKeyAgreementTest.php b/tests/EncryptionAlgorithm/KeyEncryption/ECDHSS/ECDHSSKeyAgreementTest.php index 3c7d5ea5..2ee6dd08 100644 --- a/tests/EncryptionAlgorithm/KeyEncryption/ECDHSS/ECDHSSKeyAgreementTest.php +++ b/tests/EncryptionAlgorithm/KeyEncryption/ECDHSS/ECDHSSKeyAgreementTest.php @@ -52,6 +52,82 @@ public function getAgreementKey(): void static::assertArrayNotHasKey('epk', $additional_header_values); } + /** + * @test + */ + public function getAgreementKeyWithA128KeyWrapAndOctetKeyPairKey(): void + { + $additional_header_values = [ + 'enc' => 'A128GCM', + ]; + $sender = new JWK([ + 'kty' => 'OKP', + 'crv' => 'X25519', + 'd' => 'uns2Byv3po_cjjG8XRCtU-lEOrOgLbsDr5cXHmgjVvA', + 'x' => 'k8IkMMO9I0foCYqEcbfM49DjEoWpHdho_GKNMXk1rFw', + ]); + $receiver = new JWK([ + 'kty' => 'EC', + 'crv' => 'X25519', + 'd' => 'tzF6dJQtUYj2G60lzzw70A8BGeE_KDDofUdwwm9qIEU', + 'x' => 'q9mRLLKfK-_SosZoBFs5LaDxSB9KaqbRaenvzy1_lAA', + ]); + + $cek = [ + 4, + 211, + 31, + 197, + 84, + 157, + 252, + 254, + 11, + 100, + 157, + 250, + 63, + 170, + 106, + 206, + 107, + 124, + 212, + 45, + 111, + 107, + 9, + 219, + 200, + 177, + 0, + 240, + 143, + 156, + 44, + 207, + ]; + foreach ($cek as $key => $value) { + $cek[$key] = str_pad(dechex($value), 2, '0', STR_PAD_LEFT); + } + $cek = hex2bin(implode('', $cek)); + + $ecdh_ss = new ECDHSSA128KW(); + $encrypted_cek = $ecdh_ss->wrapAgreementKey( + $receiver->toPublic(), + $sender, + $cek, + 128, + $additional_header_values, + $additional_header_values + ); + static::assertArrayNotHasKey('epk', $additional_header_values); + static::assertSame( + $cek, + $ecdh_ss->unwrapAgreementKey($sender->toPublic(), $receiver, $encrypted_cek, 128, $additional_header_values) + ); + } + /** * @test */