diff --git a/phpseclib/Crypt/EC/BaseCurves/Montgomery.php b/phpseclib/Crypt/EC/BaseCurves/Montgomery.php index 7c0daa6e..5fccb066 100644 --- a/phpseclib/Crypt/EC/BaseCurves/Montgomery.php +++ b/phpseclib/Crypt/EC/BaseCurves/Montgomery.php @@ -30,6 +30,7 @@ use phpseclib\Math\Common\FiniteField\Integer; use phpseclib\Common\Functions\Strings; use phpseclib\Math\PrimeField; use phpseclib\Math\BigInteger; +use phpseclib\Crypt\EC\Curves\Curve25519; use phpseclib\Math\PrimeField\Integer as PrimeInteger; /** @@ -202,7 +203,8 @@ class Montgomery extends Base $temp = $da->subtract($cb); $z5 = $x1->multiply($temp->multiply($temp)); $x4 = $aa->multiply($bb); - $z4 = $e->multiply($bb->add($this->a24->multiply($e))); + $temp = static::class == Curve25519::class ? $bb : $aa; + $z4 = $e->multiply($temp->add($this->a24->multiply($e))); return [ [$x4, $z4], diff --git a/phpseclib/Crypt/EC/Curves/Curve25519.php b/phpseclib/Crypt/EC/Curves/Curve25519.php index 2eeaf4af..0a90eb0d 100644 --- a/phpseclib/Crypt/EC/Curves/Curve25519.php +++ b/phpseclib/Crypt/EC/Curves/Curve25519.php @@ -36,7 +36,7 @@ class Curve25519 extends Montgomery ); $this->setBasePoint( new BigInteger(9), - new BigInteger('14781619447589544791020593568409986887264606134616475288964881837755586237401', 16) + new BigInteger('14781619447589544791020593568409986887264606134616475288964881837755586237401') ); */ } diff --git a/phpseclib/Crypt/EC/Curves/Curve448.php b/phpseclib/Crypt/EC/Curves/Curve448.php new file mode 100644 index 00000000..d575cb74 --- /dev/null +++ b/phpseclib/Crypt/EC/Curves/Curve448.php @@ -0,0 +1,71 @@ + + * @copyright 2019 Jim Wigginton + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger + */ + +namespace phpseclib\Crypt\EC\Curves; + +use phpseclib\Math\Common\FiniteField\Integer; +use phpseclib\Crypt\EC\BaseCurves\Montgomery; +use phpseclib\Math\BigInteger; + +class Curve448 extends Montgomery +{ + public function __construct() + { + // 2^448 - 2^224 - 1 + $this->setModulo(new BigInteger( + 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE' . + 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 16)); + $this->a24 = $this->factory->newInteger(new BigInteger('39081')); + $this->p = [$this->factory->newInteger(new BigInteger(5))]; + // 2^446 - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d + $this->setOrder(new BigInteger( + '3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' . + '7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3', 16)); + + /* + $this->setCoefficients( + new BigInteger('156326'), // a + ); + $this->setBasePoint( + new BigInteger(5), + new BigInteger( + '355293926785568175264127502063783334808976399387714271831880898' . + '435169088786967410002932673765864550910142774147268105838985595290' . + '606362') + ); + */ + } + + /** + * Multiply a point on the curve by a scalar + * + * Modifies the scalar as described at https://tools.ietf.org/html/rfc7748#page-8 + * + * @return array + */ + public function multiplyPoint(array $p, Integer $d) + { + //$r = strrev(sodium_crypto_scalarmult($d->toBytes(), strrev($p[0]->toBytes()))); + //return [$this->factory->newInteger(new BigInteger($r, 256))]; + + $d = $d->toBytes(); + $d[0] = $d[0] & "\xFC"; + $d = strrev($d); + $d|= "\x80"; + $d = $this->factory->newInteger(new BigInteger($d, 256)); + + return parent::multiplyPoint($p, $d); + } +} \ No newline at end of file diff --git a/phpseclib/Crypt/EC/Formats/Keys/Curve25519Private.php b/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php similarity index 69% rename from phpseclib/Crypt/EC/Formats/Keys/Curve25519Private.php rename to phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php index 79391f02..7ce0a49f 100644 --- a/phpseclib/Crypt/EC/Formats/Keys/Curve25519Private.php +++ b/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php @@ -1,7 +1,7 @@ * @access public */ -abstract class Curve25519Private +abstract class MontgomeryPrivate { /** * Is invisible flag @@ -52,10 +54,19 @@ abstract class Curve25519Private */ public static function load($key, $password = '') { - $curve = new Curve25519(); + switch (strlen($key)) { + case 32: + $curve = new Curve25519; + break; + case 56: + $curve = new Curve448; + break; + default: + throw new \LengthException('The only supported lengths are 32 and 56'); + } $components = ['curve' => $curve]; - $components['dA'] = $components['curve']->convertInteger(new BigInteger($key, -256)); + $components['dA'] = $components['curve']->convertInteger(new BigInteger($key, 256)); // note that EC::getEncodedCoordinates does some additional "magic" (it does strrev on the result) $components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']); @@ -66,13 +77,13 @@ abstract class Curve25519Private * Convert an EC public key to the appropriate format * * @access public - * @param \phpseclib\Crypt\EC\Curves\Curve25519 $curve + * @param \phpseclib\Crypt\EC\Curves\MontgomeryCurve $curve * @param \phpseclib\Math\Common\FiniteField\Integer[] $publicKey * @return string */ - public static function savePublicKey(Curve25519 $curve, array $publicKey) + public static function savePublicKey(MontgomeryCurve $curve, array $publicKey) { - return strrev($publicKey[0]->toBytes(true)); + return strrev($publicKey[0]->toBytes()); } /** @@ -80,13 +91,13 @@ abstract class Curve25519Private * * @access public * @param \phpseclib\Math\Common\FiniteField\Integer $privateKey - * @param \phpseclib\Crypt\EC\Curves\Curve25519 $curve + * @param \phpseclib\Crypt\EC\Curves\Montgomery $curve * @param \phpseclib\Math\Common\FiniteField\Integer[] $publicKey * @param string $password optional * @return string */ - public static function savePrivateKey(Integer $privateKey, Curve25519 $curve, array $publicKey, $password = '') + public static function savePrivateKey(Integer $privateKey, MontgomeryCurve $curve, array $publicKey, $password = '') { - return $privateKey->toBytes(true); + return $privateKey->toBytes(); } } diff --git a/phpseclib/Crypt/EC/Formats/Keys/Curve25519Public.php b/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php similarity index 62% rename from phpseclib/Crypt/EC/Formats/Keys/Curve25519Public.php rename to phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php index 22828f1a..35f2b5b9 100644 --- a/phpseclib/Crypt/EC/Formats/Keys/Curve25519Public.php +++ b/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php @@ -1,7 +1,7 @@ * @access public */ -abstract class Curve25519Public +abstract class MontgomeryPublic { /** * Is invisible flag @@ -45,10 +47,19 @@ abstract class Curve25519Public */ public static function load($key, $password = '') { - $curve = new Curve25519(); + switch (strlen($key)) { + case 32: + $curve = new Curve25519; + break; + case 56: + $curve = new Curve448; + break; + default: + throw new \LengthException('The only supported lengths are 32 and 56'); + } $components = ['curve' => $curve]; - $components['QA'] = [$components['curve']->convertInteger(new BigInteger(strrev($key), -256))]; + $components['QA'] = [$components['curve']->convertInteger(new BigInteger(strrev($key), 256))]; return $components; } @@ -57,12 +68,12 @@ abstract class Curve25519Public * Convert an EC public key to the appropriate format * * @access public - * @param \phpseclib\Crypt\EC\Curves\Curve25519 $curve + * @param \phpseclib\Crypt\EC\Curves\Montgomery $curve * @param \phpseclib\Math\Common\FiniteField\Integer[] $publicKey * @return string */ - public static function savePublicKey(Curve25519 $curve, array $publicKey) + public static function savePublicKey(MontgomeryCurve $curve, array $publicKey) { - return strrev($publicKey[0]->toBytes(true)); + return strrev($publicKey[0]->toBytes()); } } diff --git a/phpseclib/Crypt/EC/PrivateKey.php b/phpseclib/Crypt/EC/PrivateKey.php index e50819fa..4c22a6ed 100644 --- a/phpseclib/Crypt/EC/PrivateKey.php +++ b/phpseclib/Crypt/EC/PrivateKey.php @@ -235,9 +235,7 @@ class PrivateKey extends EC implements Common\PrivateKey { $format = 'PKCS8'; if ($this->curve instanceof MontgomeryCurve) { - $format = $this->curve instanceof Curve25519 ? - 'Curve25519Public' : - 'Curve448Public'; + $format = 'MontgomeryPublic'; } $type = self::validatePlugin('Keys', $format, 'savePublicKey'); diff --git a/tests/Unit/Crypt/DHTest.php b/tests/Unit/Crypt/DHTest.php index 2c90185f..951f1c33 100644 --- a/tests/Unit/Crypt/DHTest.php +++ b/tests/Unit/Crypt/DHTest.php @@ -153,11 +153,11 @@ Q3ADAIcv9LEmTBnSAOsCs1K9ExAmSv/T2/4+9dW28UYb+p/uV477d1wf+nCWS6VU $aes->setIV(substr($key, 16, 16)); $encrypted = - $ourEphemeralPublic->toString('Curve25519Public') . + $ourEphemeralPublic->toString('MontgomeryPublic') . $aes->encrypt($plaintext); $theirPublic = substr($encrypted, 0, 32); - $theirPublic = EC::loadFormat('Curve25519Public', $theirPublic); + $theirPublic = EC::loadFormat('MontgomeryPublic', $theirPublic); $ourPrivate = $theirPrivate; @@ -205,20 +205,20 @@ Q3ADAIcv9LEmTBnSAOsCs1K9ExAmSv/T2/4+9dW28UYb+p/uV477d1wf+nCWS6VU { // utilizing test vector from https://tools.ietf.org/html/rfc7748#section-6.1 - $alicePrivate = EC::loadFormat('Curve25519Private', pack('H*', '77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a')); - $bobPrivate = EC::loadFormat('Curve25519Private', pack('H*', '5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb')); + $alicePrivate = EC::loadFormat('MontgomeryPrivate', pack('H*', '77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a')); + $bobPrivate = EC::loadFormat('MontgomeryPrivate', pack('H*', '5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb')); $alicePublic = $alicePrivate->getPublicKey(); $bobPublic = $bobPrivate->getPublicKey(); $this->assertSame( '8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a', - bin2hex($alicePublic->toString('Curve25519Public')) + bin2hex($alicePublic->toString('MontgomeryPublic')) ); $this->assertSame( 'de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f', - bin2hex($bobPublic->toString('Curve25519Public')) + bin2hex($bobPublic->toString('MontgomeryPublic')) ); $expected = pack('H*', '4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742'); @@ -226,4 +226,41 @@ Q3ADAIcv9LEmTBnSAOsCs1K9ExAmSv/T2/4+9dW28UYb+p/uV477d1wf+nCWS6VU $this->assertSame($expected, DH::computeSecret($alicePrivate, $bobPublic)); $this->assertSame($expected, DH::computeSecret($bobPrivate, $alicePublic)); } + + public function testCurve448() + { + // utilizing test vector from https://tools.ietf.org/html/rfc7748#section-6.2 + + $alicePrivate = EC::loadFormat('MontgomeryPrivate', pack('H*', + '9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28d' . + 'd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b' + )); + $bobPrivate = EC::loadFormat('MontgomeryPrivate', pack('H*', + '1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d' . + '6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d' + )); + + $alicePublic = $alicePrivate->getPublicKey(); + $bobPublic = $bobPrivate->getPublicKey(); + + $this->assertSame( + '9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c' . + '22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0', + bin2hex($alicePublic->toString('MontgomeryPublic')) + ); + + $this->assertSame( + '3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b430' . + '27d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609', + bin2hex($bobPublic->toString('MontgomeryPublic')) + ); + + $expected = pack('H*', + '07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282b' . + 'b60c0b56fd2464c335543936521c24403085d59a449a5037514a879d' + ); + + $this->assertSame($expected, DH::computeSecret($alicePrivate, $bobPublic)); + $this->assertSame($expected, DH::computeSecret($bobPrivate, $alicePublic)); + } }