This commit is contained in:
2025-10-09 17:41:57 +00:00
commit 7a3b2960f8
146 changed files with 34948 additions and 0 deletions
+321
View File
@@ -0,0 +1,321 @@
<?php
namespace Elliptic\Curve;
use Elliptic\Utils;
use \Exception;
use BN\BN;
abstract class BaseCurve
{
public $type;
public $p;
public $red;
public $zero;
public $one;
public $two;
public $n;
public $g;
protected $_wnafT1;
protected $_wnafT2;
protected $_wnafT3;
protected $_wnafT4;
public $redN;
public $_maxwellTrick;
function __construct($type, $conf)
{
$this->type = $type;
$this->p = new BN($conf["p"], 16);
//Use Montgomery, when there is no fast reduction for the prime
$this->red = isset($conf["prime"]) ? BN::red($conf["prime"]) : BN::mont($this->p);
//Useful for many curves
$this->zero = (new BN(0))->toRed($this->red);
$this->one = (new BN(1))->toRed($this->red);
$this->two = (new BN(2))->toRed($this->red);
//Curve configuration, optional
$this->n = isset($conf["n"]) ? new BN($conf["n"], 16) : null;
$this->g = isset($conf["g"]) ? $this->pointFromJSON($conf["g"], isset($conf["gRed"]) ? $conf["gRed"] : null) : null;
//Temporary arrays
$this->_wnafT1 = array(0,0,0,0);
$this->_wnafT2 = array(0,0,0,0);
$this->_wnafT3 = array(0,0,0,0);
$this->_wnafT4 = array(0,0,0,0);
//Generalized Greg Maxwell's trick
$adjustCount = $this->n != null ? $this->p->div($this->n) : null;
if( $adjustCount == null || $adjustCount->cmpn(100) > 0 )
{
$this->redN = null;
$this->_maxwellTrick = false;
}
else
{
$this->redN = $this->n->toRed($this->red);
$this->_maxwellTrick = true;
}
}
abstract public function point($x, $z);
abstract public function validate($point);
public function _fixedNafMul($p, $k)
{
assert(isset($p->precomputed));
$doubles = $p->_getDoubles();
$naf = Utils::getNAF($k, 1);
$I = (1 << ($doubles["step"] + 1)) - ($doubles["step"] % 2 == 0 ? 2 : 1);
$I = $I / 3;
//Translate to more windowed form
$repr = array();
for($j = 0; $j < count($naf); $j += $doubles["step"])
{
$nafW = 0;
for($k = $j + $doubles["step"] - 1; $k >= $j; $k--)
$nafW = ($nafW << 1) + (isset($naf[$k]) ? $naf[$k] : 0);
array_push($repr, $nafW);
}
$a = $this->jpoint(null, null, null);
$b = $this->jpoint(null, null, null);
for($i = $I; $i > 0; $i--)
{
for($j = 0; $j < count($repr); $j++)
{
$nafW = $repr[$j];
if ($nafW == $i) {
$b = $b->mixedAdd($doubles["points"][$j]);
} else if($nafW == -$i) {
$b = $b->mixedAdd($doubles["points"][$j]->neg());
}
}
$a = $a->add($b);
}
return $a->toP();
}
public function _wnafMul($p, $k)
{
$w = 4;
//Precompute window
$nafPoints = $p->_getNAFPoints($w);
$w = $nafPoints["wnd"];
$wnd = $nafPoints["points"];
//Get NAF form
$naf = Utils::getNAF($k, $w);
//Add `this`*(N+1) for every w-NAF index
$acc = $this->jpoint(null, null, null);
for($i = count($naf) - 1; $i >= 0; $i--)
{
//Count zeros
for($k = 0; $i >= 0 && $naf[$i] == 0; $i--)
$k++;
if($i >= 0)
$k++;
$acc = $acc->dblp($k);
if($i < 0)
break;
$z = $naf[$i];
assert($z != 0);
if( $p->type == "affine" )
{
//J +- P
if( $z > 0 )
$acc = $acc->mixedAdd($wnd[($z - 1) >> 1]);
else
$acc = $acc->mixedAdd($wnd[(-$z - 1) >> 1]->neg());
}
else
{
//J +- J
if( $z > 0 )
$acc = $acc->add($wnd[($z - 1) >> 1]);
else
$acc = $acc->add($wnd[(-$z - 1) >> 1]->neg());
}
}
return $p->type == "affine" ? $acc->toP() : $acc;
}
public function _wnafMulAdd($defW, $points, $coeffs, $len, $jacobianResult = false)
{
$wndWidth = &$this->_wnafT1;
$wnd = &$this->_wnafT2;
$naf = &$this->_wnafT3;
//Fill all arrays
$max = 0;
for($i = 0; $i < $len; $i++)
{
$p = $points[$i];
$nafPoints = $p->_getNAFPoints($defW);
$wndWidth[$i] = $nafPoints["wnd"];
$wnd[$i] = $nafPoints["points"];
}
//Comb all window NAFs
for($i = $len - 1; $i >= 1; $i -= 2)
{
$a = $i - 1;
$b = $i;
if( $wndWidth[$a] != 1 || $wndWidth[$b] != 1 )
{
$naf[$a] = Utils::getNAF($coeffs[$a], $wndWidth[$a]);
$naf[$b] = Utils::getNAF($coeffs[$b], $wndWidth[$b]);
$max = max(count($naf[$a]), $max);
$max = max(count($naf[$b]), $max);
continue;
}
$comb = array(
$points[$a], /* 1 */
null, /* 3 */
null, /* 5 */
$points[$b] /* 7 */
);
//Try to avoid Projective points, if possible
if( $points[$a]->y->cmp($points[$b]->y) == 0 )
{
$comb[1] = $points[$a]->add($points[$b]);
$comb[2] = $points[$a]->toJ()->mixedAdd($points[$b]->neg());
}
elseif( $points[$a]->y->cmp($points[$b]->y->redNeg()) == 0 )
{
$comb[1] = $points[$a]->toJ()->mixedAdd($points[$b]);
$comb[2] = $points[$a]->add($points[$b]->neg());
}
else
{
$comb[1] = $points[$a]->toJ()->mixedAdd($points[$b]);
$comb[2] = $points[$a]->toJ()->mixedAdd($points[$b]->neg());
}
$index = array(
-3, /* -1 -1 */
-1, /* -1 0 */
-5, /* -1 1 */
-7, /* 0 -1 */
0, /* 0 0 */
7, /* 0 1 */
5, /* 1 -1 */
1, /* 1 0 */
3 /* 1 1 */
);
$jsf = Utils::getJSF($coeffs[$a], $coeffs[$b]);
$max = max(count($jsf[0]), $max);
if ($max > 0) {
$naf[$a] = array_fill(0, $max, 0);
$naf[$b] = array_fill(0, $max, 0);
} else {
$naf[$a] = [];
$naf[$b] = [];
}
for($j = 0; $j < $max; $j++)
{
$ja = isset($jsf[0][$j]) ? $jsf[0][$j] : 0;
$jb = isset($jsf[1][$j]) ? $jsf[1][$j] : 0;
$naf[$a][$j] = $index[($ja + 1) * 3 + ($jb + 1)];
$naf[$b][$j] = 0;
$wnd[$a] = $comb;
}
}
$acc = $this->jpoint(null, null, null);
$tmp = &$this->_wnafT4;
for($i = $max; $i >= 0; $i--)
{
$k = 0;
while($i >= 0)
{
$zero = true;
for($j = 0; $j < $len; $j++)
{
$tmp[$j] = isset($naf[$j][$i]) ? $naf[$j][$i] : 0;
if( $tmp[$j] != 0 )
$zero = false;
}
if( !$zero )
break;
$k++;
$i--;
}
if( $i >=0 )
$k++;
$acc = $acc->dblp($k);
if( $i < 0 )
break;
for($j = 0; $j < $len; $j++)
{
$z = $tmp[$j];
$p = null;
if( $z == 0 )
continue;
elseif( $z > 0 )
$p = $wnd[$j][($z - 1) >> 1];
elseif( $z < 0 )
$p = $wnd[$j][(-$z - 1) >> 1]->neg();
if( $p->type == "affine" )
$acc = $acc->mixedAdd($p);
else
$acc = $acc->add($p);
}
}
//Zeroify references
for($i = 0; $i < $len; $i++)
$wnd[$i] = null;
if( $jacobianResult )
return $acc;
else
return $acc->toP();
}
public function decodePoint($bytes, $enc = false)
{
$bytes = Utils::toArray($bytes, $enc);
$len = $this->p->byteLength();
$count = count($bytes);
//uncompressed, hybrid-odd, hybrid-even
if(($bytes[0] == 0x04 || $bytes[0] == 0x06 || $bytes[0] == 0x07) && ($count - 1) == (2 * $len) )
{
if( $bytes[0] == 0x06 )
assert($bytes[$count - 1] % 2 == 0);
elseif( $bytes[0] == 0x07 )
assert($bytes[$count - 1] % 2 == 1);
return $this->point(array_slice($bytes, 1, $len), array_slice($bytes, 1 + $len, $len));
}
if( ($bytes[0] == 0x02 || $bytes[0] == 0x03) && ($count - 1) == $len )
return $this->pointFromX(array_slice($bytes, 1, $len), $bytes[0] == 0x03);
throw new Exception("Unknown point format");
}
}
?>
@@ -0,0 +1,120 @@
<?php
namespace Elliptic\Curve\BaseCurve;
use Elliptic\Utils;
abstract class Point
{
public $curve;
public $type;
public $precomputed;
function __construct($curve, $type)
{
$this->curve = $curve;
$this->type = $type;
$this->precomputed = null;
}
abstract public function eq($other);
public function validate() {
return $this->curve->validate($this);
}
public function encodeCompressed($enc) {
return $this->encode($enc, true);
}
public function encode($enc, $compact = false) {
return Utils::encode($this->_encode($compact), $enc);
}
protected function _encode($compact)
{
$len = $this->curve->p->byteLength();
$x = $this->getX()->toArray("be", $len);
if( $compact )
{
array_unshift($x, ($this->getY()->isEven() ? 0x02 : 0x03));
return $x;
}
return array_merge(array(0x04), $x, $this->getY()->toArray("be", $len));
}
public function precompute($power = null)
{
if( isset($this->precomputed) )
return $this;
$this->precomputed = array(
"naf" => $this->_getNAFPoints(8),
"doubles" => $this->_getDoubles(4, $power),
"beta" => $this->_getBeta()
);
return $this;
}
protected function _hasDoubles($k)
{
if( !isset($this->precomputed) || !isset($this->precomputed["doubles"]) )
return false;
return count($this->precomputed["doubles"]["points"]) >= ceil(($k->bitLength() + 1) / $this->precomputed["doubles"]["step"]);
}
public function _getDoubles($step = null, $power = null)
{
if( isset($this->precomputed) && isset($this->precomputed["doubles"]) )
return $this->precomputed["doubles"];
$doubles = array( $this );
$acc = $this;
for($i = 0; $i < $power; $i += $step)
{
for($j = 0; $j < $step; $j++)
$acc = $acc->dbl();
array_push($doubles, $acc);
}
return array(
"step" => $step,
"points" => $doubles
);
}
public function _getNAFPoints($wnd)
{
if( isset($this->precomputed) && isset($this->precomputed["naf"]) )
return $this->precomputed["naf"];
$res = array( $this );
$max = (1 << $wnd) - 1;
$dbl = $max === 1 ? null : $this->dbl();
for($i = 1; $i < $max; $i++)
array_push($res, $res[$i - 1]->add($dbl));
return array(
"wnd" => $wnd,
"points" => $res
);
}
public function _getBeta() {
return null;
}
public function dblp($k)
{
$r = $this;
for($i = 0; $i < $k; $i++)
$r = $r->dbl();
return $r;
}
}
?>
+131
View File
@@ -0,0 +1,131 @@
<?php
namespace Elliptic\Curve;
use Elliptic\Curve\EdwardsCurve\Point;
use Elliptic\Utils;
use BN\BN;
class EdwardsCurve extends BaseCurve
{
public $twisted;
public $mOneA;
public $extended;
public $a;
public $c;
public $c2;
public $d;
public $d2;
public $dd;
public $oneC;
function __construct($conf)
{
// NOTE: Important as we are creating point in Base.call()
$this->twisted = ($conf["a"] | 0) != 1;
$this->mOneA = $this->twisted && ($conf["a"] | 0) == -1;
$this->extended = $this->mOneA;
parent::__construct("edward", $conf);
$this->a = (new BN($conf["a"], 16))->umod($this->red->m);
$this->a = $this->a->toRed($this->red);
$this->c = (new BN($conf["c"], 16))->toRed($this->red);
$this->c2 = $this->c->redSqr();
$this->d = (new BN($conf["d"], 16))->toRed($this->red);
$this->dd = $this->d->redAdd($this->d);
if (Utils::$ASSERT_ENABLED) {
assert(!$this->twisted || $this->c->fromRed()->cmpn(1) == 0);
}
$this->oneC = ($conf["c"] | 0) == 1;
}
public function _mulA($num) {
if ($this->mOneA)
return $num->redNeg();
else
return $this->a->redMul($num);
}
public function _mulC($num) {
if ($this->oneC)
return $num;
else
return $this->c->redMul($num);
}
// Just for compatibility with Short curve
public function jpoint($x, $y, $z, $t = null) {
return $this->point($x, $y, $z, $t);
}
public function pointFromX($x, $odd = false) {
$x = new BN($x, 16);
if (!$x->red)
$x = $x->toRed($this->red);
$x2 = $x->redSqr();
$rhs = $this->c2->redSub($this->a->redMul($x2));
$lhs = $this->one->redSub($this->c2->redMul($this->d)->redMul($x2));
$y2 = $rhs->redMul($lhs->redInvm());
$y = $y2->redSqrt();
if ($y->redSqr()->redSub($y2)->cmp($this->zero) != 0)
throw new \Exception('invalid point');
$isOdd = $y->fromRed()->isOdd();
if ($odd && !$isOdd || !$odd && $isOdd)
$y = $y->redNeg();
return $this->point($x, $y);
}
public function pointFromY($y, $odd = false) {
$y = new BN($y, 16);
if (!$y->red)
$y = $y->toRed($this->red);
// x^2 = (y^2 - 1) / (d y^2 + 1)
$y2 = $y->redSqr();
$lhs = $y2->redSub($this->one);
$rhs = $y2->redMul($this->d)->redAdd($this->one);
$x2 = $lhs->redMul($rhs->redInvm());
if ($x2->cmp($this->zero) == 0) {
if ($odd)
throw new \Exception('invalid point');
else
return $this->point($this->zero, $y);
}
$x = $x2->redSqrt();
if ($x->redSqr()->redSub($x2)->cmp($this->zero) != 0)
throw new \Exception('invalid point');
if ($x->isOdd() != $odd)
$x = $x->redNeg();
return $this->point($x, $y);
}
public function validate($point) {
if ($point->isInfinity())
return true;
// Curve: A * X^2 + Y^2 = C^2 * (1 + D * X^2 * Y^2)
$point->normalize();
$x2 = $point->x->redSqr();
$y2 = $point->y->redSqr();
$lhs = $x2->redMul($this->a)->redAdd($y2);
$rhs = $this->c2->redMul($this->one->redAdd($this->d->redMul($x2)->redMul($y2)));
return $lhs->cmp($rhs) == 0;
}
public function pointFromJSON($obj) {
return Point::fromJSON($this, $obj);
}
public function point($x = null, $y = null, $z = null, $t = null) {
return new Point($this, $x, $y, $z, $t);
}
}
@@ -0,0 +1,323 @@
<?php
namespace Elliptic\Curve\EdwardsCurve;
use BN\BN;
class Point extends \Elliptic\Curve\BaseCurve\Point
{
public $x;
public $y;
public $z;
public $t;
public $zOne;
function __construct($curve, $x = null, $y = null, $z = null, $t = null) {
parent::__construct($curve, 'projective');
if ($x == null && $y == null && $z == null) {
$this->x = $this->curve->zero;
$this->y = $this->curve->one;
$this->z = $this->curve->one;
$this->t = $this->curve->zero;
$this->zOne = true;
} else {
$this->x = new BN($x, 16);
$this->y = new BN($y, 16);
$this->z = $z ? new BN($z, 16) : $this->curve->one;
$this->t = $t ? new BN($t, 16) : null;
if (!$this->x->red)
$this->x = $this->x->toRed($this->curve->red);
if (!$this->y->red)
$this->y = $this->y->toRed($this->curve->red);
if (!$this->z->red)
$this->z = $this->z->toRed($this->curve->red);
if ($this->t && !$this->t->red)
$this->t = $this->t->toRed($this->curve->red);
$this->zOne = $this->z == $this->curve->one;
// Use extended coordinates
if ($this->curve->extended && !$this->t) {
$this->t = $this->x->redMul($this->y);
if (!$this->zOne)
$this->t = $this->t->redMul($this->z->redInvm());
}
}
}
public static function fromJSON($curve, $obj) {
return new Point($curve,
isset($obj[0]) ? $obj[0] : null,
isset($obj[1]) ? $obj[1] : null,
isset($obj[2]) ? $obj[2] : null
);
}
public function inspect() {
if ($this->isInfinity())
return '<EC Point Infinity>';
return '<EC Point x: ' . $this->x->fromRed()->toString(16, 2) .
' y: ' . $this->y->fromRed()->toString(16, 2) .
' z: ' . $this->z->fromRed()->toString(16, 2) . '>';
}
public function isInfinity() {
// XXX This code assumes that zero is always zero in red
return $this->x->cmpn(0) == 0 &&
$this->y->cmp($this->z) == 0;
}
public function _extDbl() {
// hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
// #doubling-dbl-2008-hwcd
// 4M + 4S
// A = X1^2
$a = $this->x->redSqr();
// B = Y1^2
$b = $this->y->redSqr();
// C = 2 * Z1^2
$c = $this->z->redSqr();
$c = $c->redIAdd($c);
// D = a * A
$d = $this->curve->_mulA($a);
// E = (X1 + Y1)^2 - A - B
$e = $this->x->redAdd($this->y)->redSqr()->redISub($a)->redISub($b);
// G = D + B
$g = $d->redAdd($b);
// F = G - C
$f = $g->redSub($c);
// H = D - B
$h = $d->redSub($b);
// X3 = E * F
$nx = $e->redMul($f);
// Y3 = G * H
$ny = $g->redMul($h);
// T3 = E * H
$nt = $e->redMul($h);
// Z3 = F * G
$nz = $f->redMul($g);
return $this->curve->point($nx, $ny, $nz, $nt);
}
public function _projDbl() {
// hyperelliptic.org/EFD/g1p/auto-twisted-projective.html
// #doubling-dbl-2008-bbjlp
// #doubling-dbl-2007-bl
// and others
// Generally 3M + 4S or 2M + 4S
// B = (X1 + Y1)^2
$b = $this->x->redAdd($this->y)->redSqr();
// C = X1^2
$c = $this->x->redSqr();
// D = Y1^2
$d = $this->y->redSqr();
if ($this->curve->twisted) {
// E = a * C
$e = $this->curve->_mulA($c);
// F = E + D
$f = $e->redAdd($d);
if ($this->zOne) {
// X3 = (B - C - D) * (F - 2)
$nx = $b->redSub($c)->redSub($d)->redMul($f->redSub($this->curve->two));
// Y3 = F * (E - D)
$ny = $f->redMul($e->redSub($d));
// Z3 = F^2 - 2 * F
$nz = $f->redSqr()->redSub($f)->redSub($f);
} else {
// H = Z1^2
$h = $this->z->redSqr();
// J = F - 2 * H
$j = $f->redSub($h)->redISub($h);
// X3 = (B-C-D)*J
$nx = $b->redSub($c)->redISub($d)->redMul($j);
// Y3 = F * (E - D)
$ny = $f->redMul($e->redSub($d));
// Z3 = F * J
$nz = $f->redMul($j);
}
} else {
// E = C + D
$e = $c->redAdd($d);
// H = (c * Z1)^2
$h = $this->curve->_mulC($this->c->redMul($this->z))->redSqr();
// J = E - 2 * H
$j = $e->redSub($h)->redSub($h);
// X3 = c * (B - E) * J
$nx = $this->curve->_mulC($b->redISub($e))->redMul($j);
// Y3 = c * E * (C - D)
$ny = $this->curve->_mulC($e)->redMul($c->redISub($d));
// Z3 = E * J
$nz = $e->redMul($j);
}
return $this->curve->point($nx, $ny, $nz);
}
public function dbl() {
if ($this->isInfinity())
return $this;
// Double in extended coordinates
if ($this->curve->extended)
return $this->_extDbl();
else
return $this->_projDbl();
}
public function _extAdd($p) {
// hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
// #addition-add-2008-hwcd-3
// 8M
// A = (Y1 - X1) * (Y2 - X2)
$a = $this->y->redSub($this->x)->redMul($p->y->redSub($p->x));
// B = (Y1 + X1) * (Y2 + X2)
$b = $this->y->redAdd($this->x)->redMul($p->y->redAdd($p->x));
// C = T1 * k * T2
$c = $this->t->redMul($this->curve->dd)->redMul($p->t);
// D = Z1 * 2 * Z2
$d = $this->z->redMul($p->z->redAdd($p->z));
// E = B - A
$e = $b->redSub($a);
// F = D - C
$f = $d->redSub($c);
// G = D + C
$g = $d->redAdd($c);
// H = B + A
$h = $b->redAdd($a);
// X3 = E * F
$nx = $e->redMul($f);
// Y3 = G * H
$ny = $g->redMul($h);
// T3 = E * H
$nt = $e->redMul($h);
// Z3 = F * G
$nz = $f->redMul($g);
return $this->curve->point($nx, $ny, $nz, $nt);
}
public function _projAdd($p) {
// hyperelliptic.org/EFD/g1p/auto-twisted-projective.html
// #addition-add-2008-bbjlp
// #addition-add-2007-bl
// 10M + 1S
// A = Z1 * Z2
$a = $this->z->redMul($p->z);
// B = A^2
$b = $a->redSqr();
// C = X1 * X2
$c = $this->x->redMul($p->x);
// D = Y1 * Y2
$d = $this->y->redMul($p->y);
// E = d * C * D
$e = $this->curve->d->redMul($c)->redMul($d);
// F = B - E
$f = $b->redSub($e);
// G = B + E
$g = $b->redAdd($e);
// X3 = A * F * ((X1 + Y1) * (X2 + Y2) - C - D)
$tmp = $this->x->redAdd($this->y)->redMul($p->x->redAdd($p->y))->redISub($c)->redISub($d);
$nx = $a->redMul($f)->redMul($tmp);
if ($this->curve->twisted) {
// Y3 = A * G * (D - a * C)
$ny = $a->redMul($g)->redMul($d->redSub($this->curve->_mulA($c)));
// Z3 = F * G
$nz = $f->redMul($g);
} else {
// Y3 = A * G * (D - C)
$ny = $a->redMul($g)->redMul($d->redSub($c));
// Z3 = c * F * G
$nz = $this->curve->_mulC($f)->redMul($g);
}
return $this->curve->point($nx, $ny, $nz);
}
public function add($p) {
if ($this->isInfinity())
return $p;
if ($p->isInfinity())
return $this;
if ($this->curve->extended)
return $this->_extAdd($p);
else
return $this->_projAdd($p);
}
public function mul($k) {
if ($this->_hasDoubles($k))
return $this->curve->_fixedNafMul($this, $k);
else
return $this->curve->_wnafMul($this, $k);
}
public function mulAdd($k1, $p, $k2) {
return $this->curve->_wnafMulAdd(1, [ $this, $p ], [ $k1, $k2 ], 2, false);
}
public function jmulAdd($k1, $p, $k2) {
return $this->curve->_wnafMulAdd(1, [ $this, $p ], [ $k1, $k2 ], 2, true);
}
public function normalize() {
if ($this->zOne)
return $this;
// Normalize coordinates
$zi = $this->z->redInvm();
$this->x = $this->x->redMul($zi);
$this->y = $this->y->redMul($zi);
if ($this->t)
$this->t = $this->t->redMul($zi);
$this->z = $this->curve->one;
$this->zOne = true;
return $this;
}
public function neg() {
return $this->curve->point($this->x->redNeg(),
$this->y,
$this->z,
($this->t != null) ? $this->t->redNeg() : null);
}
public function getX() {
$this->normalize();
return $this->x->fromRed();
}
public function getY() {
$this->normalize();
return $this->y->fromRed();
}
public function eq($other) {
return $this == $other ||
$this->getX()->cmp($other->getX()) == 0 &&
$this->getY()->cmp($other->getY()) == 0;
}
public function eqXToP($x) {
$rx = $x->toRed($this->curve->red)->redMul($this->z);
if ($this->x->cmp($rx) == 0)
return true;
$xc = $x->_clone();
$t = $this->curve->redN->redMul($this->z);
for (;;) {
$xc->iadd($this->curve->n);
if ($xc->cmp($this->curve->p) >= 0)
return false;
$rx->redIAdd($t);
if ($this->x->cmp($rx) == 0)
return true;
}
return false;
}
// Compatibility with BaseCurve
public function toP() { return $this->normalize(); }
public function mixedAdd($p) { return $this->add($p); }
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace Elliptic\Curve;
use Elliptic\Curve\MontCurve\Point;
use Elliptic\Utils;
use BN\BN;
class MontCurve extends BaseCurve
{
public $a;
public $b;
public $i4;
public $a24;
function __construct($conf)
{
parent::__construct("mont", $conf);
$this->a = (new BN($conf["a"], 16))->toRed($this->red);
$this->b = (new BN($conf["b"], 16))->toRed($this->red);
$this->i4 = (new BN(4))->toRed($this->red)->redInvm();
$this->a24 = $this->i4->redMul($this->a->redAdd($this->two));
}
public function validate($point)
{
$x = $point->normalize()->x;
$x2 = $x->redSqr();
$rhs = $x2->redMul($x)->redAdd($x2->redMul($this->a))->redAdd($x);
$y = $rhs->redSqr();
return $y->redSqr()->cmp($rhs) ===0;
}
public function decodePoint($bytes, $enc = false) {
return $this->point(Utils::toArray($bytes, $enc), 1);
}
public function point($x, $z) {
return new Point($this, $x, $z);
}
public function pointFromJSON($obj) {
return Point::fromJSON($this, $obj);
}
}
?>
@@ -0,0 +1,160 @@
<?php
namespace Elliptic\Curve\MontCurve;
use BN\BN;
class Point extends \Elliptic\Curve\BaseCurve\Point
{
public $x;
public $z;
function __construct($curve, $x, $z)
{
parent::__construct($curve, "projective");
if( $x == null && $z == null )
{
$this->x = $this->curve->one;
$this->z = $this->curve->zero;
}
else
{
$this->x = new BN($x, 16);
$this->z = new BN($z, 16);
if( !$this->x->red )
$this->x = $this->x->toRed($this->curve->red);
if( !$this->z->red )
$this->z = $this->z->toRed($this->curve->red);
}
}
public function precompute($power = null) {
// No-op
}
protected function _encode($compact) {
return $this->getX()->toArray("be", $this->curve->p->byteLength());
}
public static function fromJSON($curve, $obj) {
return new Point($curve, $obj[0], isset($obj[1]) ? $obj[1] : $curve->one);
}
public function inspect()
{
if( $this->isInfinity() )
return "<EC Point Infinity>";
return "<EC Point x: " . $this->x->fromRed()->toString(16, 2) .
" z: " . $this->z->fromRed()->toString(16, 2) . ">";
}
public function isInfinity() {
// XXX This code assumes that zero is always zero in red
return $this->z->isZero();
}
public function dbl()
{
// http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#doubling-dbl-1987-m-3
// 2M + 2S + 4A
// A = X1 + Z1
$a = $this->x->redAdd($this->z);
// AA = A^2
$aa = $a->redSqr();
// B = X1 - Z1
$b = $this->x->redSub($this->z);
// BB = B^2
$bb = $b->redSqr();
// C = AA - BB
$c = $aa->redSub($bb);
// X3 = AA * BB
$nx = $aa->redMul($bb);
// Z3 = C * (BB + A24 * C)
$nz = $c->redMul( $bb->redAdd($this->curve->a24->redMul($c)) );
return $this->curve->point($nx, $nz);
}
public function add($p) {
throw new \Exception('Not supported on Montgomery curve');
}
public function diffAdd($p, $diff)
{
// http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#diffadd-dadd-1987-m-3
// 4M + 2S + 6A
// A = X2 + Z2
$a = $this->x->redAdd($this->z);
// B = X2 - Z2
$b = $this->x->redSub($this->z);
// C = X3 + Z3
$c = $p->x->redAdd($p->z);
// D = X3 - Z3
$d = $p->x->redSub($p->z);
// DA = D * A
$da = $d->redMul($a);
// CB = C * B
$cb = $c->redMul($b);
// X5 = Z1 * (DA + CB)^2
$nx = $diff->z->redMul($da->redAdd($cb)->redSqr());
// Z5 = X1 * (DA - CB)^2
$nz = $diff->x->redMul($da->redSub($cb)->redSqr());
return $this->curve->point($nx, $nz);
}
public function mul($k)
{
$t = $k->_clone();
$a = $this; // (N / 2) * Q + Q
$b = $this->curve->point(null, null); // (N / 2) * Q
$c = $this; // Q
$bits = array();
while( !$t->isZero() )
{
// TODO: Maybe it is faster to use toString(2)?
array_push($bits, $t->andln(1));
$t->iushrn(1);
}
for($i = count($bits) - 1; $i >= 0; $i--)
{
if( $bits[$i] === 0 )
{
// N * Q + Q = ((N / 2) * Q + Q)) + (N / 2) * Q
$a = $a->diffAdd($b, $c);
// N * Q = 2 * ((N / 2) * Q + Q))
$b = $b->dbl();
}
else
{
// N * Q = ((N / 2) * Q + Q) + ((N / 2) * Q)
$b = $a->diffAdd($b, $c);
// N * Q + Q = 2 * ((N / 2) * Q + Q)
$a = $a->dbl();
}
}
return $b;
}
public function eq($other) {
return $this->getX()->cmp($other->getX()) === 0;
}
public function normalize()
{
$this->x = $this->x->redMul($this->z->redInvm());
$this->z = $this->curve->one;
return $this;
}
public function getX() {
$this->normalize();
return $this->x->fromRed();
}
}
?>
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace Elliptic\Curve;
class PresetCurve
{
public $curve;
public $g;
public $n;
public $hash;
function __construct($options)
{
if ( $options["type"] === "short" )
$this->curve = new ShortCurve($options);
elseif ( $options["type"] === "edwards" )
$this->curve = new EdwardsCurve($options);
else
$this->curve = new MontCurve($options);
$this->g = $this->curve->g;
$this->n = $this->curve->n;
$this->hash = isset($options["hash"]) ? $options["hash"] : null;
}
}
?>
+305
View File
@@ -0,0 +1,305 @@
<?php
namespace Elliptic\Curve;
use Elliptic\Curve\ShortCurve\Point;
use Elliptic\Curve\ShortCurve\JPoint;
use Elliptic\Utils;
use BN\BN;
use \Exception;
class ShortCurve extends BaseCurve
{
public $a;
public $b;
public $tinv;
public $zeroA;
public $threeA;
public $endo;
private $_endoWnafT1;
private $_endoWnafT2;
function __construct($conf)
{
parent::__construct("short", $conf);
$this->a = (new BN($conf["a"], 16))->toRed($this->red);
$this->b = (new BN($conf["b"], 16))->toRed($this->red);
$this->tinv = $this->two->redInvm();
$this->zeroA = $this->a->fromRed()->isZero();
$this->threeA = $this->a->fromRed()->sub($this->p)->cmpn(-3) === 0;
// If curve is endomorphic, precalculate beta and lambda
$this->endo = $this->_getEndomorphism($conf);
$this->_endoWnafT1 = array(0,0,0,0);
$this->_endoWnafT2 = array(0,0,0,0);
}
private function _getEndomorphism($conf)
{
// No efficient endomorphism
if( !$this->zeroA || !isset($this->g) || !isset($this->n) || $this->p->modn(3) != 1 )
return null;
// Compute beta and lambda, that lambda * P = (beta * Px; Py)
$beta = null;
$lambda = null;
if( isset($conf["beta"]) )
$beta = (new BN($conf["beta"], 16))->toRed($this->red);
else
{
$betas = $this->_getEndoRoots($this->p);
// Choose smallest beta
$beta = $betas[0]->cmp($betas[1]) < 0 ? $betas[0] : $betas[1];
$beta = $beta->toRed($this->red);
}
if( isset($conf["lambda"]) )
$lambda = new BN($conf["lambda"], 16);
else
{
// Choose the lambda that is matching selected beta
$lambdas = $this->_getEndoRoots($this->n);
if( $this->g->mul($lambdas[0])->x->cmp($this->g->x->redMul($beta)) == 0 )
$lambda = $lambdas[0];
else
{
$lambda = $lambdas[1];
if (Utils::$ASSERT_ENABLED) {
assert($this->g->mul($lambda)->x->cmp($this->g->x->redMul($beta)) === 0);
}
}
}
// Get basis vectors, used for balanced length-two representation
$basis = null;
if( !isset($conf["basis"]) )
$basis = $this->_getEndoBasis($lambda);
else
{
$callback = function($vector) {
return array(
"a" => new BN($vector["a"], 16),
"b" => new BN($vector["b"], 16)
);
};
$basis = array_map($callback, $conf["basis"]);
}
return array(
"beta" => $beta,
"lambda" => $lambda,
"basis" => $basis
);
}
private function _getEndoRoots($num)
{
// Find roots of for x^2 + x + 1 in F
// Root = (-1 +- Sqrt(-3)) / 2
//
$red = $num === $this->p ? $this->red : BN::mont($num);
$tinv = (new BN(2))->toRed($red)->redInvm();
$ntinv = $tinv->redNeg();
$s = (new BN(3))->toRed($red)->redNeg()->redSqrt()->redMul($tinv);
return array(
$ntinv->redAdd($s)->fromRed(),
$ntinv->redSub($s)->fromRed()
);
}
private function _getEndoBasis($lambda)
{
// aprxSqrt >= sqrt(this.n)
$aprxSqrt = $this->n->ushrn(intval($this->n->bitLength() / 2));
// 3.74
// Run EGCD, until r(L + 1) < aprxSqrt
$u = $lambda;
$v = $this->n->_clone();
$x1 = new BN(1);
$y1 = new BN(0);
$x2 = new BN(0);
$y2 = new BN(1);
// NOTE: all vectors are roots of: a + b * lambda = 0 (mod n)
$a0 = 0;
$b0 = 0;
// First vector
$a1 = 0;
$b1 = 0;
// Second vector
$a2 = 0;
$b2 = 0;
$prevR = 0;
$i = 0;
$r = 0;
$x = 0;
while( ! $u->isZero() )
{
$q = $v->div($u);
$r = $v->sub($q->mul($u));
$x = $x2->sub($q->mul($x1));
$y = $y2->sub($q->mul($y2));
if( !$a1 && $r->cmp($aprxSqrt) < 0 )
{
$a0 = $prevR->neg();
$b0 = $x1;
$a1 = $r->neg();
$b1 = $x;
}
elseif($a1 && ++$i === 2)
break;
$prevR = $r;
$v = $u;
$u = $r;
$x2 = $x1;
$x1 = $x;
$y2 = $y1;
$y1 = $y;
}
$a2 = $r->neg();
$b2 = $x;
$len1 = $a1->sqr()->add($b1->sqr());
$len2 = $a2->sqr()->add($b2->sqr());
if( $len2->cmp($len1) >= 0 )
{
$a2 = $a0;
$b2 = $b0;
}
// Normalize signs
if( $a1->negative() )
{
$a1 = $a1->neg();
$b1 = $b1->neg();
}
if( $a2->negative() )
{
$a2 = $a2->neg();
$b2 = $b2->neg();
}
return array(
array( "a" => $a1, "b" => $b1 ),
array( "a" => $a2, "b" => $b2 ),
);
}
public function _endoSplit($k)
{
$basis = $this->endo["basis"];
$v1 = $basis[0];
$v2 = $basis[1];
$c1 = $v2["b"]->mul($k)->divRound($this->n);
$c2 = $v1["b"]->neg()->mul($k)->divRound($this->n);
$p1 = $c1->mul($v1["a"]);
$p2 = $c2->mul($v2["a"]);
$q1 = $c1->mul($v1["b"]);
$q2 = $c2->mul($v2["b"]);
//Calculate answer
$k1 = $k->sub($p1)->sub($p2);
$k2 = $q1->add($q2)->neg();
return array( "k1" => $k1, "k2" => $k2 );
}
public function pointFromX($x, $odd)
{
$x = new BN($x, 16);
if( !$x->red )
$x = $x->toRed($this->red);
$y2 = $x->redSqr()->redMul($x)->redIAdd($x->redMul($this->a))->redIAdd($this->b);
$y = $y2->redSqrt();
if( $y->redSqr()->redSub($y2)->cmp($this->zero) !== 0 )
throw new Exception("Invalid point");
// XXX Is there any way to tell if the number is odd without converting it
// to non-red form?
$isOdd = $y->fromRed()->isOdd();
if( $odd != $isOdd )
$y = $y->redNeg();
return $this->point($x, $y);
}
public function validate($point)
{
if( $point->inf )
return true;
$x = $point->x;
$y = $point->y;
$ax = $this->a->redMul($x);
$rhs = $x->redSqr()->redMul($x)->redIAdd($ax)->redIAdd($this->b);
return $y->redSqr()->redISub($rhs)->isZero();
}
public function _endoWnafMulAdd($points, $coeffs, $jacobianResult = false)
{
$npoints = &$this->_endoWnafT1;
$ncoeffs = &$this->_endoWnafT2;
for($i = 0; $i < count($points); $i++)
{
$split = $this->_endoSplit($coeffs[$i]);
$p = $points[$i];
$beta = $p->_getBeta();
if( $split["k1"]->negative() )
{
$split["k1"]->ineg();
$p = $p->neg(true);
}
if( $split["k2"]->negative() )
{
$split["k2"]->ineg();
$beta = $beta->neg(true);
}
$npoints[$i * 2] = $p;
$npoints[$i * 2 + 1] = $beta;
$ncoeffs[$i * 2] = $split["k1"];
$ncoeffs[$i * 2 + 1] = $split["k2"];
}
$res = $this->_wnafMulAdd(1, $npoints, $ncoeffs, $i * 2, $jacobianResult);
// Clean-up references to points and coefficients
for($j = 0; $j < 2 * $i; $j++)
{
$npoints[$j] = null;
$ncoeffs[$j] = null;
}
return $res;
}
public function point($x, $y, $isRed = false) {
return new Point($this, $x, $y, $isRed);
}
public function pointFromJSON($obj, $red) {
return Point::fromJSON($this, $obj, $red);
}
public function jpoint($x, $y, $z) {
return new JPoint($this, $x, $y, $z);
}
}
?>
@@ -0,0 +1,488 @@
<?php
namespace Elliptic\Curve\ShortCurve;
use BN\BN;
class JPoint extends \Elliptic\Curve\BaseCurve\Point
{
public $x;
public $y;
public $z;
public $zOne;
function __construct($curve, $x, $y, $z)
{
parent::__construct($curve, "jacobian");
if( $x == null && $y == null && $z == null )
{
$this->x = $this->curve->one;
$this->y = $this->curve->one;
$this->z = new BN(0);
}
else
{
$this->x = new BN($x, 16);
$this->y = new BN($y, 16);
$this->z = new BN($z, 16);
}
if( !$this->x->red )
$this->x = $this->x->toRed($this->curve->red);
if( !$this->y->red )
$this->y = $this->y->toRed($this->curve->red);
if( !$this->z->red )
$this->z = $this->z->toRed($this->curve->red);
return $this->zOne = $this->z == $this->curve->one;
}
public function toP()
{
if( $this->isInfinity() )
return $this->curve->point(null, null);
$zinv = $this->z->redInvm();
$zinv2 = $zinv->redSqr();
$ax = $this->x->redMul($zinv2);
$ay = $this->y->redMul($zinv2)->redMul($zinv);
return $this->curve->point($ax, $ay);
}
public function neg() {
return $this->curve->jpoint($this->x, $this->y->redNeg(), $this->z);
}
public function add($p)
{
// O + P = P
if( $this->isInfinity() )
return $p;
// P + O = P
if( $p->isInfinity() )
return $this;
// 12M + 4S + 7A
$pz2 = $p->z->redSqr();
$z2 = $this->z->redSqr();
$u1 = $this->x->redMul($pz2);
$u2 = $p->x->redMul($z2);
$s1 = $this->y->redMul($pz2->redMul($p->z));
$s2 = $p->y->redMul($z2->redMul($this->z));
$h = $u1->redSub($u2);
$r = $s1->redSub($s2);
if( $h->isZero() )
{
if( ! $r->isZero() )
return $this->curve->jpoint(null, null, null);
else
return $this->dbl();
}
$h2 = $h->redSqr();
$h3 = $h2->redMul($h);
$v = $u1->redMul($h2);
$nx = $r->redSqr()->redIAdd($h3)->redISub($v)->redISub($v);
$ny = $r->redMul($v->redISub($nx))->redISub($s1->redMul($h3));
$nz = $this->z->redMul($p->z)->redMul($h);
return $this->curve->jpoint($nx, $ny, $nz);
}
public function mixedAdd($p)
{
// O + P = P
if( $this->isInfinity() )
return $p->toJ();
// P + O = P
if( $p->isInfinity() )
return $this;
// 8M + 3S + 7A
$z2 = $this->z->redSqr();
$u1 = $this->x;
$u2 = $p->x->redMul($z2);
$s1 = $this->y;
$s2 = $p->y->redMul($z2)->redMul($this->z);
$h = $u1->redSub($u2);
$r = $s1->redSub($s2);
if( $h->isZero() )
{
if( ! $r->isZero() )
return $this->curve->jpoint(null, null, null);
else
return $this->dbl();
}
$h2 = $h->redSqr();
$h3 = $h2->redMul($h);
$v = $u1->redMul($h2);
$nx = $r->redSqr()->redIAdd($h3)->redISub($v)->redISub($v);
$ny = $r->redMul($v->redISub($nx))->redISub($s1->redMul($h3));
$nz = $this->z->redMul($h);
return $this->curve->jpoint($nx, $ny, $nz);
}
public function dblp($pow = null)
{
if( $pow == 0 || $this->isInfinity() )
return $this;
if( $pow == null )
return $this->dbl();
if( $this->curve->zeroA || $this->curve->threeA )
{
$r = $this;
for($i = 0; $i < $pow; $i++)
$r = $r->dbl();
return $r;
}
// 1M + 2S + 1A + N * (4S + 5M + 8A)
// N = 1 => 6M + 6S + 9A
$jx = $this->x;
$jy = $this->y;
$jz = $this->z;
$jz4 = $jz->redSqr()->redSqr();
//Reuse results
$jyd = $jy->redAdd($jy);
for($i = 0; $i < $pow; $i++)
{
$jx2 = $jx->redSqr();
$jyd2 = $jyd->redSqr();
$jyd4 = $jyd2->redSqr();
$c = $jx2->redAdd($jx2)->redIAdd($jx2)->redIAdd($this->curve->a->redMul($jz4));
$t1 = $jx->redMul($jyd2);
$nx = $c->redSqr()->redISub($t1->redAdd($t1));
$t2 = $t1->redISub($nx);
$dny = $c->redMul($t2);
$dny = $dny->redIAdd($dny)->redISub($jyd4);
$nz = $jyd->redMul($jz);
if( ($i + 1) < $pow)
$jz4 = $jz4->redMul($jyd4);
$jx = $nx;
$jz = $nz;
$jyd = $dny;
}
return $this->curve->jpoint($jx, $jyd->redMul($this->curve->tinv), $jz);
}
public function dbl()
{
if( $this->isInfinity() )
return $this;
if( $this->curve->zeroA )
return $this->_zeroDbl();
elseif( $this->curve->threeA )
return $this->_threeDbl();
return $this->_dbl();
}
private function _zOneDbl($withA)
{
$xx = $this->x->redSqr();
$yy = $this->y->redSqr();
$yyyy = $yy->redSqr();
// S = 2 * ((X1 + YY)^2 - XX - YYYY)
$s = $this->x->redAdd($yy)->redSqr()->redISub($xx)->redISub($yyyy);
$s = $s->redIAdd($s);
// M = 3 * XX + a; a = 0
$m = null;
if( $withA )
$m = $xx->redAdd($xx)->redIAdd($xx)->redIAdd($this->curve->a);
else
$m = $xx->redAdd($xx)->redIAdd($xx);
// T = M ^ 2 - 2*S
$t = $m->redSqr()->redISub($s)->redISub($s);
$yyyy8 = $yyyy->redIAdd($yyyy);
$yyyy8 = $yyyy8->redIAdd($yyyy8);
$yyyy8 = $yyyy8->redIAdd($yyyy8);
$ny = $m->redMul($s->redISub($t))->redISub($yyyy8);
$nz = $this->y->redAdd($this->y);
return $this->curve->jpoint($t, $ny, $nz);
}
private function _zeroDbl()
{
// Z = 1
if( $this->zOne )
{
// hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html
// #doubling-mdbl-2007-bl
// 1M + 5S + 14A
return $this->_zOneDbl(false);
}
// hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html
// #doubling-dbl-2009-l
// 2M + 5S + 13A
$a = $this->x->redSqr();
$b = $this->y->redSqr();
$c = $b->redSqr();
// D = 2 * ((X1 + B)^2 - A - C)
$d = $this->x->redAdd($b)->redSqr()->redISub($a)->redISub($c);
$d = $d->redIAdd($d);
$e = $a->redAdd($a)->redIAdd($a);
$f = $e->redSqr();
$c8 = $c->redIAdd($c);
$c8 = $c8->redIAdd($c8);
$c8 = $c8->redIAdd($c8);
// X3 = F - 2 * D
$nx = $f->redISub($d)->redISub($d);
// Y3 = E * (D - X3) - 8 * C
$ny = $e->redMul($d->redISub($nx))->redISub($c8);
// Z3 = 2 * Y1 * Z1
$nz = $this->y->redMul($this->z);
$nz = $nz->redIAdd($nz);
return $this->curve->jpoint($nx, $ny, $nz);
}
private function _threeDbl()
{
if( $this->zOne )
{
// hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html
// #doubling-mdbl-2007-bl
// 1M + 5S + 15A
// XX = X1^2
$xx = $this->x->redSqr();
// YY = Y1^2
$yy = $this->y->redSqr();
// YYYY = YY^2
$yyyy = $yy->redSqr();
// S = 2 * ((X1 + YY)^2 - XX - YYYY)
$s = $this->x->redAdd($yy)->redSqr()->redISub($xx)->redISub($yyyy);
$s = $s->redIAdd($s);
// M = 3 * XX + a
$m = $xx->redAdd($xx)->redIAdd($xx)->redIAdd($this->curve->a);
// T = M^2 - 2 * S
$t = $m->redSqr()->redISub($s)->redISub($s);
// X3 = T
$nx = $t;
// Y3 = M * (S - T) - 8 * YYYY
$yyyy8 = $yyyy->redIAdd($yyyy);
$yyyy8 = $yyyy8->redIAdd($yyyy8);
$yyyy8 = $yyyy8->redIAdd($yyyy8);
$ny = $m->redMul($s->redISub($t))->redISub($yyyy8);
// Z3 = 2 * Y1
$nz = $this->y->redAdd($this->y);
} else {
// hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b
// 3M + 5S
// delta = Z1^2
$delta = $this->z->redSqr();
// gamma = Y1^2
$gamma = $this->y->redSqr();
// beta = X1 * gamma
$beta = $this->x->redMul($gamma);
// alpha = 3 * (X1 - delta) * (X1 + delta)
$alpha = $this->x->redSub($delta)->redMul($this->x->redAdd($delta));
$alpha = $alpha->redAdd($alpha)->redIAdd($alpha);
// X3 = alpha^2 - 8 * beta
$beta4 = $beta->redIAdd($beta);
$beta4 = $beta4->redIAdd($beta4);
$beta8 = $beta4->redAdd($beta4);
$nx = $alpha->redSqr()->redISub($beta8);
// Z3 = (Y1 + Z1)^2 - gamma - delta
$nz = $this->y->redAdd($this->z)->redSqr()->redISub($gamma)->redISub($delta);
$ggamma8 = $gamma->redSqr();
$ggamma8 = $ggamma8->redIAdd($ggamma8);
$ggamma8 = $ggamma8->redIAdd($ggamma8);
$ggamma8 = $ggamma8->redIAdd($ggamma8);
// Y3 = alpha * (4 * beta - X3) - 8 * gamma^2
$ny = $alpha->redMul($beta4->redISub($nx))->redISub($ggamma8);
}
return $this->curve->jpoint($nx, $ny, $nz);
}
private function _dbl()
{
// 4M + 6S + 10A
$jx = $this->x;
$jy = $this->y;
$jz = $this->z;
$jz4 = $jz->redSqr()->redSqr();
$jx2 = $jx->redSqr();
$jy2 = $jy->redSqr();
$c = $jx2->redAdd($jx2)->redIAdd($jx2)->redIAdd($this->curve->a->redMul($jz4));
$jxd4 = $jx->redAdd($jx);
$jxd4 = $jxd4->redIAdd($jxd4);
$t1 = $jxd4->redMul($jy2);
$nx = $c->redSqr()->redISub($t1->redAdd($t1));
$t2 = $t1->redISub($nx);
$jyd8 = $jy2->redSqr();
$jyd8 = $jyd8->redIAdd($jyd8);
$jyd8 = $jyd8->redIAdd($jyd8);
$jyd8 = $jyd8->redIAdd($jyd8);
$ny = $c->redMul($t2)->redISub($jyd8);
$nz = $jy->redAdd($jy)->redMul($jz);
return $this->curve->jpoint($nx, $ny, $nz);
}
public function trpl()
{
if( !$this->curve->zeroA )
return $this->dbl()->add($this);
// hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#tripling-tpl-2007-bl
// 5M + 10S + ...
$xx = $this->x->redSqr();
$yy = $this->y->redSqr();
$zz = $this->z->redSqr();
// YYYY = YY^2
$yyyy = $yy->redSqr();
// M = 3 * XX + a * ZZ2; a = 0
$m = $xx->redAdd($xx)->redIAdd($xx);
// MM = M^2
$mm = $m->redSqr();
// E = 6 * ((X1 + YY)^2 - XX - YYYY) - MM
$e = $this->x->redAdd($yy)->redSqr()->redISub($xx)->redISub($yyyy);
$e = $e->redIAdd($e);
$e = $e->redAdd($e)->redIAdd($e);
$e = $e->redISub($mm);
$ee = $e->redSqr();
// T = 16*YYYY
$t = $yyyy->redIAdd($yyyy);
$t = $t->redIAdd($t);
$t = $t->redIAdd($t);
$t = $t->redIAdd($t);
// U = (M + E)^2 - MM - EE - T
$u = $m->redAdd($e)->redSqr()->redISub($mm)->redISub($ee)->redISub($t);
$yyu4 = $yy->redMul($u);
$yyu4 = $yyu4->redIAdd($yyu4);
$yyu4 = $yyu4->redIAdd($yyu4);
// X3 = 4 * (X1 * EE - 4 * YY * U)
$nx = $this->x->redMul($ee)->redISub($yyu4);
$nx = $nx->redIAdd($nx);
$nx = $nx->redIAdd($nx);
// Y3 = 8 * Y1 * (U * (T - U) - E * EE)
$ny = $this->y->redMul($u->redMul($t->redISub($u))->redISub($e->redMul($ee)));
$ny = $ny->redIAdd($ny);
$ny = $ny->redIAdd($ny);
$ny = $ny->redIAdd($ny);
// Z3 = (Z1 + E)^2 - ZZ - EE
$nz = $this->z->redAdd($e)->redSqr()->redISub($zz)->redISub($ee);
return $this->curve->jpoint($nx, $ny, $nz);
}
public function mul($k, $kbase) {
return $this->curve->_wnafMul($this, new BN($k, $kbase));
}
public function eq($p)
{
if( $p->type == "affine" )
return $this->eq($p->toJ());
if( $this == $p )
return true;
// x1 * z2^2 == x2 * z1^2
$z2 = $this->z->redSqr();
$pz2 = $p->z->redSqr();
if( ! $this->x->redMul($pz2)->redISub($p->x->redMul($z2))->isZero() )
return false;
// y1 * z2^3 == y2 * z1^3
$z3 = $z2->redMul($this->z);
$pz3 = $pz2->redMul($p->z);
return $this->y->redMul($pz3)->redISub($p->y->redMul($z3))->isZero();
}
public function eqXToP($x)
{
$zs = $this->z->redSqr();
$rx = $x->toRed($this->curve->red)->redMul($zs);
if( $this->x->cmp($rx) == 0 )
return true;
$xc = $x->_clone();
$t = $this->curve->redN->redMul($zs);
while(true)
{
$xc->iadd($this->curve->n);
if( $xc->cmp($this->curve->p) >= 0 )
return false;
$rx->redIAdd($t);
if( $this->x->cmp($rx) == 0 )
return true;
}
}
public function inspect()
{
if( $this->isInfinity() )
return "<EC JPoint Infinity>";
return "<EC JPoint x: " . $this->x->toString(16, 2) .
" y: " . $this->y->toString(16, 2) .
" z: " . $this->z->toString(16, 2) . ">";
}
public function __debugInfo() {
return [
"EC JPoint" => ($this->isInfinity() ?
"Infinity" :
[
"x" => $this->x->toString(16,2),
"y" => $this->y->toString(16,2),
"z" => $this->z->toString(16,2)
]
)
];
}
public function isInfinity() {
// XXX This code assumes that zero is always zero in red
return $this->z->isZero();
}
}
?>
@@ -0,0 +1,317 @@
<?php
namespace Elliptic\Curve\ShortCurve;
use JsonSerializable;
use BN\BN;
class Point extends \Elliptic\Curve\BaseCurve\Point implements JsonSerializable
{
public $x;
public $y;
public $inf;
function __construct($curve, $x, $y, $isRed)
{
parent::__construct($curve, 'affine');
if( $x == null && $y == null )
{
$this->x = null;
$this->y = null;
$this->inf = true;
}
else
{
$this->x = new BN($x, 16);
$this->y = new BN($y, 16);
// Force redgomery representation when loading from JSON
if( $isRed )
{
$this->x->forceRed($this->curve->red);
$this->y->forceRed($this->curve->red);
}
if( !$this->x->red )
$this->x = $this->x->toRed($this->curve->red);
if( !$this->y->red )
$this->y = $this->y->toRed($this->curve->red);
$this->inf = false;
}
}
public function _getBeta()
{
if( !isset($this->curve->endo) )
return null;
if( isset($this->precomputed) && isset($this->precomputed["beta"]) )
return $this->precomputed["beta"];
$beta = $this->curve->point($this->x->redMul($this->curve->endo["beta"]), $this->y);
if( isset($this->precomputed) )
{
$endoMul = function($p) {
return $this->curve->point($p->x->redMul($this->curve->endo["beta"]), $p->y);
};
$beta->precomputed = array(
"beta" => null,
"naf" => null,
"doubles" => null
);
if( isset($this->precomputed["naf"]) )
{
$beta->precomputed["naf"] = array(
"wnd" => $this->precomputed["naf"]["wnd"],
"points" => array_map($endoMul, $this->precomputed["naf"]["points"])
);
}
if( isset($this->precomputed["doubles"]) )
{
$beta->precomputed["doubles"] = array(
"step" => $this->precomputed["doubles"]["step"],
"points" => array_map($endoMul, $this->precomputed["doubles"]["points"])
);
}
$this->precomputed["beta"] = $beta;
}
return $beta;
}
//toJSON()
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
$res = array($this->x, $this->y);
if( !isset($this->precomputed) )
return $res;
$pre = array();
$addPre = false;
if( isset($this->precomputed["doubles"]) )
{
$pre["doubles"] = array(
"step" => $this->precomputed["doubles"]["step"],
"points" => array_slice($this->precomputed["doubles"]["points"], 1)
);
$addPre = true;
}
if( isset($this->precomputed["naf"]) )
{
$pre["naf"] = array(
"naf" => $this->precomputed["naf"]["wnd"],
"points" => array_slice($this->precomputed["naf"]["points"], 1)
);
$addPre = true;
}
if( $addPre )
array_push($res, $pre);
return $res;
}
public static function fromJSON($curve, $obj, $red)
{
if( is_string($obj) )
$obj = json_decode($obj);
$point = $curve->point($obj[0], $obj[1], $red);
if( count($obj) === 2 )
return $point;
$pre = $obj[2];
$point->precomputed = array("beta" => null);
$obj2point = function($obj) use ($curve, $red) {
return $curve->point($obj[0], $obj[1], $red);
};
if( isset($pre["doubles"]) )
{
$tmp = array_map($obj2point, $pre["doubles"]["points"]);
array_unshift($tmp, $point);
$point->precomputed["doubles"] = array(
"step" => $pre["doubles"]["step"],
"points" => $tmp
);
}
if( isset($pre["naf"]) )
{
$tmp = array_map($obj2point, $pre["naf"]["points"]);
array_unshift($tmp, $point);
$point->precomputed["naf"] = array(
"wnd" => $pre["naf"]["wnd"],
"points" => $tmp
);
}
return $point;
}
public function inspect()
{
if( $this->isInfinity() )
return "<EC Point Infinity>";
return "<EC Point x: " . $this->x->fromRed()->toString(16, 2) .
" y: " . $this->y->fromRed()->toString(16, 2) . ">";
}
public function __debugInfo() {
return [
"EC Point" => ($this->isInfinity() ?
"Infinity" :
[
"x" => $this->x->fromRed()->toString(16, 2),
"y" => $this->y->fromRed()->toString(16, 2)
])
];
}
public function isInfinity() {
return $this->inf;
}
public function add($point)
{
// O + P = P
if( $this->inf )
return $point;
// P + O = P
if( $point->inf )
return $this;
// P + P = 2P
if( $this->eq($point) )
return $this->dbl();
// P + (-P) = O
if( $this->neg()->eq($point) )
return $this->curve->point(null, null);
// P + Q = O
if( $this->x->cmp($point->x) === 0 )
return $this->curve->point(null, null);
$c = $this->y->redSub($point->y);
if( ! $c->isZero() )
$c = $c->redMul($this->x->redSub($point->x)->redInvm());
$nx = $c->redSqr()->redISub($this->x)->redISub($point->x);
$ny = $c->redMul($this->x->redSub($nx))->redISub($this->y);
return $this->curve->point($nx, $ny);
}
public function dbl()
{
if( $this->inf )
return $this;
// 2P = 0
$ys1 = $this->y->redAdd($this->y);
if( $ys1->isZero() )
return $this->curve->point(null, null);
$x2 = $this->x->redSqr();
$dyinv = $ys1->redInvm();
$c = $x2->redAdd($x2)->redIAdd($x2)->redIAdd($this->curve->a)->redMul($dyinv);
$nx = $c->redSqr()->redISub($this->x->redAdd($this->x));
$ny = $c->redMul($this->x->redSub($nx))->redISub($this->y);
return $this->curve->point($nx, $ny);
}
public function getX() {
return $this->x->fromRed();
}
public function getY() {
return $this->y->fromRed();
}
public function mul($k)
{
$k = new BN($k, 16);
if( $this->_hasDoubles($k) )
return $this->curve->_fixedNafMul($this, $k);
elseif( isset($this->curve->endo) )
return $this->curve->_endoWnafMulAdd(array($this), array($k));
return $this->curve->_wnafMul($this, $k);
}
public function mulAdd($k1, $p2, $k2, $j = false)
{
$points = array($this, $p2);
$coeffs = array($k1, $k2);
if( isset($this->curve->endo) )
return $this->curve->_endoWnafMulAdd($points, $coeffs, $j);
return $this->curve->_wnafMulAdd(1, $points, $coeffs, 2, $j);
}
public function jmulAdd($k1, $p2, $k2) {
return $this->mulAdd($k1, $p2, $k2, true);
}
public function eq($point)
{
return (
$this === $point ||
$this->inf === $point->inf &&
($this->inf || $this->x->cmp($point->x) === 0 && $this->y->cmp($point->y) === 0)
);
}
public function neg($precompute = false)
{
if( $this->inf )
return $this;
$res = $this->curve->point($this->x, $this->y->redNeg());
if( $precompute && isset($this->precomputed) )
{
$res->precomputed = array();
$pre = $this->precomputed;
$negate = function($point) {
return $point->neg();
};
if( isset($pre["naf"]) )
{
$res->precomputed["naf"] = array(
"wnd" => $pre["naf"]["wnd"],
"points" => array_map($negate, $pre["naf"]["points"])
);
}
if( isset($pre["doubles"]) )
{
$res->precomputed["doubles"] = array(
"step" => $pre["doubles"]["step"],
"points" => array_map($negate, $pre["doubles"]["points"])
);
}
}
return $res;
}
public function toJ()
{
if( $this->inf )
return $this->curve->jpoint(null, null, null);
return $this->curve->jpoint($this->x, $this->y, $this->curve->one);
}
}
?>