Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement secp256k1 in constant-time #15

Merged
merged 2 commits into from
Apr 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions src/Curves/SecgCurve.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use GMP;
use Mdanter\Ecc\Math\GmpMathInterface;
use Mdanter\Ecc\Optimized\K256;
use Mdanter\Ecc\Primitives\CurveParameters;
use Mdanter\Ecc\Primitives\GeneratorPoint;
use Mdanter\Ecc\Random\RandomNumberGeneratorInterface;
Expand Down Expand Up @@ -144,13 +145,36 @@ public function curve256k1(): NamedCurveFp
return new NamedCurveFp(self::NAME_SECP_256K1, $parameters, $this->adapter);
}

/**
* @return NamedCurveFp
*/
public function optimizedCurve256k1(): NamedCurveFp
{
/** @var GMP $p */
$p = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 16);
/** @var GMP $a */
$a = gmp_init(0, 10);
/** @var GMP $b */
$b = gmp_init(7, 10);

$parameters = new CurveParameters(256, $p, $a, $b);

return (new OptimizedCurveFp(self::NAME_SECP_256K1, $parameters, $this->adapter))
->setOptimizedCurveOps(new K256());
}

/**
* @param ?RandomNumberGeneratorInterface $randomGenerator
* @return GeneratorPoint
*/
public function generator256k1(?RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint
public function generator256k1(?RandomNumberGeneratorInterface $randomGenerator = null, bool $optimized = false): GeneratorPoint
{
$curve = $this->curve256k1();

if ($optimized) {
$curve = $this->optimizedCurve256k1();
} else {
$curve = $this->curve256k1();
}

/** @var GMP $order */
$order = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16);
Expand Down
188 changes: 188 additions & 0 deletions src/Optimized/Common/AbstractOptimized.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
<?php
declare(strict_types=1);
namespace Mdanter\Ecc\Optimized\Common;

use GMP;
use Mdanter\Ecc\Math\ConstantTimeMath;
use Mdanter\Ecc\Primitives\CurveFpInterface;
use Mdanter\Ecc\Primitives\Point;
use Mdanter\Ecc\Primitives\PointInterface;

/**
* @property CurveFpInterface $curveByName
* @property GMP $order
* @property GMP $p
*
*/
abstract class AbstractOptimized
{
use BarretReductionTrait;

abstract public function addInternal(JacobiPoint $p1, JacobiPoint $p2): JacobiPoint;
abstract public function doubleInternal(JacobiPoint $p): JacobiPoint;

/**
* @param PointInterface $a
* @param PointInterface $b
* @return PointInterface
*/
public function addPoints(PointInterface $a, PointInterface $b): PointInterface
{
$jA = $this->projectPoint($a);
$jB = $this->projectPoint($b);
$jC = $this->addInternal($jA, $jB);
return $this->toBasicPoint($jC);
}

/**
* @param PointInterface $point
* @return PointInterface
*/
public function doublePoint(PointInterface $point): PointInterface
{
$projected = $this->projectPoint($point);
$doubled = $this->doubleInternal($projected);
return $this->toBasicPoint($doubled);
}

/**
* Computes the reciprocal of the group order
*
* @param GMP $scalar
* @return GMP
*/
public function modInverse(GMP $scalar): GMP
{
// For now, we'll just use ConstantTimeMath.
return $this->ctMath->inverseMod($scalar, $this->order);
}

/**
* @param PointInterface $point
* @return JacobiPoint
*/
public function projectPoint(PointInterface $point): JacobiPoint
{
$ret = new JacobiPoint();
$ret->x = clone $point->getX();
$ret->y = clone $point->getY();
$ret->z = gmp_init(1, 10);
return $ret;
}

/**
* Return a new point at infintiy
*
* @return JacobiPoint
*/
public function newPoint(): JacobiPoint
{
$ret = new JacobiPoint();
$ret->x = gmp_init(0, 10);
$ret->y = gmp_init(1, 10);
$ret->z = gmp_init(0, 10);
return $ret;
}

public function jacobiToAffine(JacobiPoint $p): JacobiPoint
{
$q = clone $p;
$zInv = $this->ctMath->inverseMod($q->z, $this->p);
$q->x = $this->mulElements($zInv, $q->x);
$q->y = $this->mulElements($zInv, $q->y);
$q->z = gmp_init(1, 10);
return $q;
}

/**
* Convert from a projected point to a basic point used by PHPECC
*
* @param JacobiPoint $p
* @return Point
*/
public function toBasicPoint(JacobiPoint $p): Point
{
$q = $this->jacobiToAffine($p);
return new Point(
new ConstantTimeMath(),
$this->curveByName,
$q->x,
$q->y,
clone $this->order
);
}

/**
* Return (a + b) mod p
*
* @param GMP $a
* @param GMP $b
* @param bool $reduce
* @return GMP
*/
public function addElements(GMP $a, GMP $b, bool $reduce = true): GMP
{
/** @var GMP $r */
$r = gmp_add($a, $b);
if (!$reduce) {
return $r;
}

$cmp = $this->ctMath->cmp($r, $this->p);
// $cmp = < -1, 0, 1 >
// lt, eq, gt
//
// ($cmp - 1) = < -2, -1, 0 >
// lt, eq, gt
//
// (($cmp - 1) >> 1) & 1 = < 1, 1, 0 >
// lt, eq, gt
//
// $swap = < 0, 0, 1 >
// lt, eq, gt
$swap = (~(($cmp - 1) >> 1)) & 1;
$rPrime = $r - $this->p;
return $this->ctMath->select($swap, $rPrime, $r);
}

/**
* Calculate (a x b) mod p, using Barrett Reduction
*
* @param GMP $a
* @param GMP $b
* @return GMP
*/
public function mulElements(GMP $a, GMP $b): GMP
{
return $this->barrettReduce(gmp_mul($a, $b));
}

/**
* Return (a - b) mod p
*
* @param GMP $a
* @param GMP $b
* @return GMP
*/
public function subElements(GMP $a, GMP $b): GMP
{
$c = gmp_sub($a, $b);
$swap = ((gmp_sign($c) >> 1)) & 1;
$cPrime = $c + $this->p;
return $this->ctMath->select($swap, $cPrime, $c);
}

/**
* @param JacobiPoint $q
* @return JacobiPoint[]
*/
public function makeTable(JacobiPoint $q): array
{
$table = [clone $q];
for ($i = 1; $i < 15; $i += 2) {
$table[$i] = $this->doubleInternal($table[$i >> 1]);
$table[$i + 1] = $this->addInternal($table[$i], $q);
}
return $table;
}
}
1 change: 1 addition & 0 deletions src/Optimized/Common/BarretReductionTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Mdanter\Ecc\Math\ConstantTimeMath;

/**
* @property GMP R
* @property GMP p
* @property int N
*/
Expand Down
113 changes: 113 additions & 0 deletions src/Optimized/Common/KoblitzTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
namespace Mdanter\Ecc\Optimized\Common;

use GMP;

/**
* Point addition and point doubling for short Weierstrass curves of form
* y^2 = x^3 + b
*
* That is to say, a = 0
*
* @property GMP $b3
* @property GMP $p
*
* @method GMP addElements(GMP $a, GMP $b, bool $reduce = true)
* @method GMP mulElements(GMP $a, GMP $b)
* @method GMP subElements(GMP $a, GMP $b)
*/
trait KoblitzTrait
{
/**
* https://eprint.iacr.org/2015/1060
* Algorithm 4
*
* @param JacobiPoint $p1
* @param JacobiPoint $p2
* @return JacobiPoint
*/
public function addInternal(JacobiPoint $p1, JacobiPoint $p2): JacobiPoint
{
$X1 = $p1->x;
$Y1 = $p1->y;
$Z1 = $p1->z;
$X2 = $p2->x;
$Y2 = $p2->y;
$Z2 = $p2->z;

$t0 = $this->mulElements($X1, $X2); // t0 := X1 * X2
$t1 = $this->mulElements($Y1, $Y2); // t1 := Y1 * Y2
$t2 = $this->mulElements($Z1, $Z2); // t2 := Z1 * Z2
$t3 = $this->addElements($X1, $Y1); // t3 := X1 + Y1
$t4 = $this->addElements($X2, $Y2); // t4 := X2 + Y2
$t3 = $this->mulElements($t3, $t4); // t3 := t3 * t4
$t4 = $this->addElements($t0, $t1); // t4 := t0 + t1
$t3 = $this->subElements($t3, $t4); // t3 := t3 - t4
$t4 = $this->addElements($Y1, $Z1); // t4 := Y1 + Z1
$X3 = $this->addElements($Y2, $Z2); // X3 := Y2 + Z2
$t4 = $this->mulElements($t4, $X3); // t4 := t4 * X3
$X3 = $this->addElements($t1, $t2); // X3 := t1 + t2
$t4 = $this->subElements($t4, $X3); // t4 := t4 - X3
$X3 = $this->addElements($X1, $Z1); // X3 := X1 + Z1
$Y3 = $this->addElements($X2, $Z2); // Y3 := X2 + Z2
$X3 = $this->mulElements($X3, $Y3); // X3 := X3 * Y3
$Y3 = $this->addElements($t0, $t2); // Y3 := t0 + t2
$Y3 = $this->subElements($X3, $Y3); // Y3 := X3 - Y3
$X3 = $this->addElements($t0, $t0); // X3 := t0 + t0
$t0 = $this->addElements($X3, $t0); // t0 := X3 + t0
$t2 = $this->mulElements($this->b3, $t2); // t2 := b3 * t2
$Z3 = $this->addElements($t1, $t2); // Z3 := t1 + t2
$t1 = $this->subElements($t1, $t2); // t1 := t1 - t2
$Y3 = $this->mulElements($this->b3, $Y3); // Y3 := b3 * Y3
$X3 = $this->mulElements($t4, $Y3); // X3 := t4 * Y3
$t2 = $this->mulElements($t3, $t1); // t2 := t3 * t1
$X3 = $this->subElements($t2, $X3); // X3 := t2 - X3
$Y3 = $this->mulElements($Y3, $t0); // Y3 := Y3 * t0
$t1 = $this->mulElements($t1, $Z3); // t1 := t1 * Z3
$Y3 = $this->addElements($t1, $Y3); // Y3 := t1 + Y3
$t0 = $this->mulElements($t0, $t3); // t0 := t0 * t3
$Z3 = $this->mulElements($Z3, $t4); // Z3 := Z3 * t4
$Z3 = $this->addElements($Z3, $t0); // Z3 := Z3 + t0

// Return (X3, Y3, Z3)
return JacobiPoint::init($X3, $Y3, $Z3);
}


/**
* https://eprint.iacr.org/2015/1060
* Algorithm 6
*
* @param JacobiPoint $p
* @return JacobiPoint
*/
public function doubleInternal(JacobiPoint $p): JacobiPoint
{
$X = $p->x;
$Y = $p->y;
$Z = $p->z;

$t0 = $this->mulElements($Y, $Y); // t0 := Y^2
$Z3 = $this->addElements($t0, $t0); // Z3 := t0 + t0
$Z3 = $this->addElements($Z3, $Z3); // Z3 := Z3 + Z3
$Z3 = $this->addElements($Z3, $Z3); // Z3 := Z3 + Z3
$t1 = $this->mulElements($Y, $Z); // t1 := Y * Z
$t2 = $this->mulElements($Z, $Z); // t2 := Z^2
$t2 = $this->mulElements($this->b3, $t2); // t2 := b3 * t2
$X3 = $this->mulElements($t2, $Z3); // X3 := t2 * Z3
$Y3 = $this->addElements($t0, $t2); // Y3 := t0 + t2
$Z3 = $this->mulElements($t1, $Z3); // Z3 := t1 * Z3
$t1 = $this->addElements($t2, $t2); // t1 := t2 + t2
$t2 = $this->addElements($t1, $t2); // t2 := t1 + t2
$t0 = $this->subElements($t0, $t2); // t0 := t0 - t2
$Y3 = $this->mulElements($t0, $Y3); // Y3 := t0 * Y3
$Y3 = $this->addElements($X3, $Y3); // Y3 := X3 + Y3
$t1 = $this->mulElements($X, $Y); // t1 := X * Y
$X3 = $this->mulElements($t0, $t1); // X3 := t0 * t1
$X3 = $this->addElements($X3, $X3); // X3 := X3 + X3

// Return (X3, Y3, Z3)
return JacobiPoint::init($X3, $Y3, $Z3);
}
}
2 changes: 1 addition & 1 deletion src/Optimized/Common/ShortWeierstrassTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* @property GMP $p
*
* @method JacobiPoint newPoint()
* @method GMP addElements(GMP $a, GMP $b)
* @method GMP addElements(GMP $a, GMP $b, bool $reduce = true)
* @method GMP mulElements(GMP $a, GMP $b)
* @method GMP subElements(GMP $a, GMP $b)
*/
Expand Down
Loading
Loading