diff --git a/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php b/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php index 0ec7742f..7dab2193 100644 --- a/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php +++ b/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php @@ -129,12 +129,21 @@ abstract class PKCS8 extends Progenitor $components = []; if (isset($key['privateKey'])) { - $components['curve'] = $key['privateKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448(); - - // 0x04 == octet string - // 0x20 == length (32 bytes) - if (substr($key['privateKey'], 0, 2) != "\x04\x20") { - throw new \RuntimeException('The first two bytes of the private key field should be 0x0420'); + if ($key['privateKeyAlgorithm']['algorithm'] == 'id-Ed25519') { + $components['curve'] = new Ed25519(); + // 0x04 == octet string + // 0x20 == length (32 bytes) + if (substr($key['privateKey'], 0, 2) != "\x04\x20") { + throw new \RuntimeException('The first two bytes of the Ed25519 private key field should be 0x0420'); + } + } else { + // Assume Ed448 + $components['curve'] = new Ed448(); + // 0x04 == octet string + // 0x39 == length (57 bytes) + if (substr($key['privateKey'], 0, 2) != "\x04\x39") { + throw new \RuntimeException('The first two bytes of the Ed448 private key field should be 0x0439'); + } } $arr = $components['curve']->extractSecret(substr($key['privateKey'], 2)); $components['dA'] = $arr['dA']; @@ -207,13 +216,24 @@ abstract class PKCS8 extends Progenitor } if ($curve instanceof TwistedEdwardsCurve) { - return self::wrapPrivateKey( - "\x04\x20" . $secret, - [], - null, - $password, - $curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448' - ); + if ($curve instanceof Ed25519) { + return self::wrapPrivateKey( + "\x04\x20" . $secret, + [], + null, + $password, + 'id-Ed25519' + ); + } else { + // Assume Ed448 + return self::wrapPrivateKey( + "\x04\x39" . $secret, + [], + null, + $password, + 'id-Ed448' + ); + } } $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes(); diff --git a/tests/Unit/Crypt/EC/KeyTest.php b/tests/Unit/Crypt/EC/KeyTest.php index adbb8f42..83ea2e8d 100644 --- a/tests/Unit/Crypt/EC/KeyTest.php +++ b/tests/Unit/Crypt/EC/KeyTest.php @@ -297,6 +297,33 @@ WEKBIQAZv0QJaYTN/oVBusFn3DuWyFCGqjC2tssMXDitcDFm4Q== $this->assertSameNL('Ed25519', $key->getPublicKey()->getCurve()); } + // Generate with: + // openssl genpkey -algorithm ed448 | openssl ec -pubout + public function testEd448PublicKey() + { + $expected = '-----BEGIN PUBLIC KEY----- +MEMwBQYDK2VxAzoAsA7zbld48IfDhm7Qd6FYrvnljtjhPRRqZi04NWyj8VXrWe1x +BMLQFJEE0JDmKayUWpUWsRXwmb6A +-----END PUBLIC KEY-----'; + $key = PublicKeyLoader::load($expected); + $this->assertSameNL('Ed448', $key->getCurve()); + $this->assertSameNL($expected, $key->toString('PKCS8')); + } + + // Generate with: + // openssl genpkey -algorithm ed448 + public function testEd448PrivateKey() + { + $expected = '-----BEGIN PRIVATE KEY----- +MEcCAQAwBQYDK2VxBDsEOettXaJYob4hJNKJNOD+FfMvdesLKNp0KwochI6AKmAb +tWhtkn99WOjd1PsGMh9zz2Vhdg3MwasOMQ== +-----END PRIVATE KEY-----'; + $key = PublicKeyLoader::load($expected); + $this->assertSameNL($expected, $key->toString('PKCS8')); + $this->assertSameNL('Ed448', $key->getCurve()); + $this->assertSameNL('Ed448', $key->getPublicKey()->getCurve()); + } + public function testPuTTYnistp256() { $key = PublicKeyLoader::load($expected = 'PuTTY-User-Key-File-2: ecdsa-sha2-nistp256