From 8d481ffc8dff8b7066f8732b3c2afe8893b0d08e Mon Sep 17 00:00:00 2001 From: Paragon Initiative Enterprises Date: Wed, 1 May 2024 09:41:24 -0400 Subject: [PATCH] Test SecureFactory --- src/Curves/CurveFactory.php | 18 ++--- src/Curves/SecgCurve.php | 48 +++++++++++-- src/Curves/SecureCurveFactory.php | 93 ++++++++++++++++++++++++- src/Curves/SecureSecgCurve.php | 4 +- tests/unit/Curves/SecureFactoryTest.php | 69 ++++++++++++++++++ 5 files changed, 214 insertions(+), 18 deletions(-) create mode 100644 tests/unit/Curves/SecureFactoryTest.php diff --git a/src/Curves/CurveFactory.php b/src/Curves/CurveFactory.php index 55fd19c..7f0b355 100644 --- a/src/Curves/CurveFactory.php +++ b/src/Curves/CurveFactory.php @@ -17,9 +17,9 @@ class CurveFactory public static function getCurveByName(string $name): NamedCurveFp { $adapter = MathAdapterFactory::getAdapter(); - $nistFactory = self::getNistFactory($adapter); - $brainpoolFactory = self::getBrainpoolFactory($adapter); - $secpFactory = self::getSecpFactory($adapter); + $nistFactory = static::getNistFactory($adapter); + $brainpoolFactory = static::getBrainpoolFactory($adapter); + $secpFactory = static::getSecpFactory($adapter); switch ($name) { case NistCurve::NAME_P192: @@ -62,9 +62,9 @@ public static function getCurveByName(string $name): NamedCurveFp public static function getGeneratorByName(string $name): GeneratorPoint { $adapter = MathAdapterFactory::getAdapter(); - $nistFactory = self::getNistFactory($adapter); - $brainpoolFactory = self::getBrainpoolFactory($adapter); - $secpFactory = self::getSecpFactory($adapter); + $nistFactory = static::getNistFactory($adapter); + $brainpoolFactory = static::getBrainpoolFactory($adapter); + $secpFactory = static::getSecpFactory($adapter); switch ($name) { case NistCurve::NAME_P192: @@ -104,7 +104,7 @@ public static function getGeneratorByName(string $name): GeneratorPoint * @param GmpMathInterface $math * @return NistCurve */ - private static function getNistFactory(GmpMathInterface $math): NistCurve + protected static function getNistFactory(GmpMathInterface $math): NistCurve { return new NistCurve($math); } @@ -113,7 +113,7 @@ private static function getNistFactory(GmpMathInterface $math): NistCurve * @param GmpMathInterface $math * @return BrainpoolCurve */ - private static function getBrainpoolFactory(GmpMathInterface $math): BrainpoolCurve + protected static function getBrainpoolFactory(GmpMathInterface $math): BrainpoolCurve { return new BrainpoolCurve($math); } @@ -122,7 +122,7 @@ private static function getBrainpoolFactory(GmpMathInterface $math): BrainpoolCu * @param GmpMathInterface $math * @return SecgCurve */ - private static function getSecpFactory(GmpMathInterface $math): SecgCurve + protected static function getSecpFactory(GmpMathInterface $math): SecgCurve { return new SecgCurve($math); } diff --git a/src/Curves/SecgCurve.php b/src/Curves/SecgCurve.php index 1209755..457143b 100755 --- a/src/Curves/SecgCurve.php +++ b/src/Curves/SecgCurve.php @@ -6,6 +6,8 @@ use GMP; use Mdanter\Ecc\Math\GmpMathInterface; use Mdanter\Ecc\Optimized\K256; +use Mdanter\Ecc\Optimized\P256; +use Mdanter\Ecc\Optimized\P384; use Mdanter\Ecc\Primitives\CurveParameters; use Mdanter\Ecc\Primitives\GeneratorPoint; use Mdanter\Ecc\Random\RandomNumberGeneratorInterface; @@ -203,6 +205,42 @@ public function curve256r1(): NamedCurveFp return new NamedCurveFp(self::NAME_SECP_256R1, $parameters, $this->adapter); } + + /** + * Returns an NIST P-256 curve. + * + * @return NamedCurveFp + */ + public function optimizedCurve256r1(): NamedCurveFp + { + /** @var GMP $p */ + $p = gmp_init('115792089210356248762697446949407573530086143415290314195533631308867097853951', 10); + /** @var GMP $b */ + $b = gmp_init('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b', 16); + + /** @var GMP $minusThree */ + $minusThree = gmp_init(-3, 10); + $parameters = new CurveParameters(256, $p, $minusThree, $b); + + return (new OptimizedCurveFp(self::NAME_SECP_256R1, $parameters, $this->adapter)) + ->setOptimizedCurveOps(new P256()); + } + + public function optimizedCurve384r1(): NamedCurveFp + { + /** @var GMP $p */ + $p = gmp_init('39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319', 10); + /** @var GMP $b */ + $b = gmp_init('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef', 16); + + /** @var GMP $minus3 */ + $minus3 = gmp_init(-3, 10); + $parameters = new CurveParameters(384, $p, $minus3, $b); + + return (new OptimizedCurveFp(self::NAME_SECP_384R1, $parameters, $this->adapter)) + ->setOptimizedCurveOps(new P384()); + } + /** * @param ?RandomNumberGeneratorInterface $randomGenerator * @param bool $optimized @@ -211,9 +249,10 @@ public function curve256r1(): NamedCurveFp public function generator256r1(?RandomNumberGeneratorInterface $randomGenerator = null, bool $optimized = false): GeneratorPoint { if ($optimized) { - return (new NistCurve($this->adapter))->generator256($randomGenerator, true); + $curve = $this->optimizedCurve256r1(); + } else { + $curve = $this->curve256r1(); } - $curve = $this->curve256r1(); /** @var GMP $order */ $order = gmp_init('0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551', 16); @@ -250,9 +289,10 @@ public function curve384r1(): NamedCurveFp public function generator384r1(?RandomNumberGeneratorInterface $randomGenerator = null, bool $optimized = false): GeneratorPoint { if ($optimized) { - return (new NistCurve($this->adapter))->generator256($randomGenerator, true); + $curve = $this->optimizedCurve384r1(); + } else { + $curve = $this->curve384r1(); } - $curve = $this->curve384r1(); /** @var GMP $order */ $order = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973', 16); diff --git a/src/Curves/SecureCurveFactory.php b/src/Curves/SecureCurveFactory.php index 205af77..ec98b4a 100644 --- a/src/Curves/SecureCurveFactory.php +++ b/src/Curves/SecureCurveFactory.php @@ -2,19 +2,106 @@ namespace Mdanter\Ecc\Curves; +use Mdanter\Ecc\Exception\InsecureCurveException; +use Mdanter\Ecc\Exception\UnsupportedCurveException; use Mdanter\Ecc\Math\GmpMathInterface; use Mdanter\Ecc\Math\MathAdapterFactory; +use Mdanter\Ecc\Primitives\GeneratorPoint; /** * Similar to CurveFactory, but only returns secure implementations */ class SecureCurveFactory extends CurveFactory { + /** + * @param string $name + * @return NamedCurveFp + */ + public static function getCurveByName(string $name): NamedCurveFp + { + $adapter = MathAdapterFactory::getAdapter(); + $nistFactory = static::getNistFactory($adapter); + $brainpoolFactory = static::getBrainpoolFactory($adapter); + $secpFactory = static::getSecpFactory($adapter); + + switch ($name) { + case SecgCurve::NAME_SECP_112R1: + case SecgCurve::NAME_SECP_192K1: + case NistCurve::NAME_P192: + case NistCurve::NAME_P224: + throw new InsecureCurveException('This is not a secure curve: '. $name); + case SecgCurve::NAME_SECP_256K1: + return $secpFactory->optimizedCurve256k1(); + case SecgCurve::NAME_SECP_256R1: + return $secpFactory->optimizedCurve256r1(); + case NistCurve::NAME_P256: + return $nistFactory->optimizedCurve256(); + case SecgCurve::NAME_SECP_384R1: + return $secpFactory->optimizedCurve384r1(); + case NistCurve::NAME_P384: + return $nistFactory->optimizedCurve384(); + case NistCurve::NAME_P521: + return $nistFactory->optimizedCurve521(); + case BrainpoolCurve::NAME_P256R1: + return $brainpoolFactory->optimizedCurve256r1(); + case BrainpoolCurve::NAME_P384R1: + return $brainpoolFactory->optimizedCurve384r1(); + case BrainpoolCurve::NAME_P512R1: + return $brainpoolFactory->optimizedCurve512r1(); + default: + $error = new UnsupportedCurveException('Unknown curve.'); + $error->setCurveName($name); + throw $error; + } + } + + /** + * @param string $name + * @return GeneratorPoint + */ + public static function getGeneratorByName(string $name): GeneratorPoint + { + $adapter = MathAdapterFactory::getAdapter(); + $nistFactory = static::getNistFactory($adapter); + $brainpoolFactory = static::getBrainpoolFactory($adapter); + $secpFactory = static::getSecpFactory($adapter); + + switch ($name) { + case SecgCurve::NAME_SECP_112R1: + case SecgCurve::NAME_SECP_192K1: + case NistCurve::NAME_P192: + case NistCurve::NAME_P224: + throw new InsecureCurveException('This is not a secure curve: '. $name); + case NistCurve::NAME_P256: + return $nistFactory->generator256(null, true); + case NistCurve::NAME_P384: + return $nistFactory->generator384(null, true); + case NistCurve::NAME_P521: + return $nistFactory->generator521(null, true); + case BrainpoolCurve::NAME_P256R1: + return $brainpoolFactory->generator256r1(null, true); + case BrainpoolCurve::NAME_P384R1: + return $brainpoolFactory->generator384r1(null, true); + case BrainpoolCurve::NAME_P512R1: + return $brainpoolFactory->generator512r1(null, true); + case SecgCurve::NAME_SECP_256K1: + return $secpFactory->generator256k1(null, true); + case SecgCurve::NAME_SECP_256R1: + return $secpFactory->generator256r1(null, true); + case SecgCurve::NAME_SECP_384R1: + return $secpFactory->generator384r1(null, true); + default: + $error = new UnsupportedCurveException('Unknown generator.'); + $error->setCurveName($name); + throw $error; + } + } + /** * @param GmpMathInterface $math * @return NistCurve */ - private static function getNistFactory(GmpMathInterface $math): NistCurve + protected static function getNistFactory(GmpMathInterface $math): NistCurve { return new SecureNistCurve($math); } @@ -23,7 +110,7 @@ private static function getNistFactory(GmpMathInterface $math): NistCurve * @param GmpMathInterface $math * @return BrainpoolCurve */ - private static function getBrainpoolFactory(GmpMathInterface $math): BrainpoolCurve + protected static function getBrainpoolFactory(GmpMathInterface $math): BrainpoolCurve { return new SecureBrainpoolCurve($math); } @@ -32,7 +119,7 @@ private static function getBrainpoolFactory(GmpMathInterface $math): BrainpoolCu * @param GmpMathInterface $math * @return SecgCurve */ - private static function getSecpFactory(GmpMathInterface $math): SecgCurve + protected static function getSecpFactory(GmpMathInterface $math): SecgCurve { return new SecureSecgCurve($math); } diff --git a/src/Curves/SecureSecgCurve.php b/src/Curves/SecureSecgCurve.php index 73e311a..d61969c 100644 --- a/src/Curves/SecureSecgCurve.php +++ b/src/Curves/SecureSecgCurve.php @@ -38,7 +38,7 @@ public function curve256r1(): NamedCurveFp { $curve = parent::curve256r1(); if (!$curve->isOpensslAvailable()) { - throw new InsecureCurveException('Cannot securely use non-optimized secp256k1 without OpenSSL support'); + throw new InsecureCurveException('Cannot securely use non-optimized secp256r1 without OpenSSL support'); } return $curve; } @@ -50,7 +50,7 @@ public function curve384r1(): NamedCurveFp { $curve = parent::curve384r1(); if (!$curve->isOpensslAvailable()) { - throw new InsecureCurveException('Cannot securely use non-optimized secp256k1 without OpenSSL support'); + throw new InsecureCurveException('Cannot securely use non-optimized secp384r1 without OpenSSL support'); } return $curve; } diff --git a/tests/unit/Curves/SecureFactoryTest.php b/tests/unit/Curves/SecureFactoryTest.php new file mode 100644 index 0000000..f9b1edd --- /dev/null +++ b/tests/unit/Curves/SecureFactoryTest.php @@ -0,0 +1,69 @@ +expectException(InsecureCurveException::class); + SecureCurveFactory::getCurveByName($name); + } + /** + * @param string $name + * @dataProvider getInsecureCurves + */ + public function testThrowsIfInsecureGenerator(string $name): void + { + $this->expectException(InsecureCurveException::class); + SecureCurveFactory::getGeneratorByName($name); + } + + /** + * @param string $name + * @dataProvider getCurveNames + */ + public function testLoadsCurveByName(string $name): void + { + $curve = SecureCurveFactory::getCurveByName($name); + $generator = SecureCurveFactory::getGeneratorByName($name); + $this->assertEquals($name, $curve->getName()); + $this->assertEquals($name, $generator->getCurve()->getName()); + } +}