goldrat/vendor/simplito/bn-php/lib/Red.php
2025-10-09 17:41:57 +00:00

222 lines
5.6 KiB
PHP

<?php
namespace BN;
use \Exception;
use \BI\BigInteger;
class Red
{
public static $ASSERT_ENABLED;
public $m;
function __construct($m) {
if( is_string($m) )
$this->m = Red::primeByName($m);
else
$this->m = $m;
if( !$this->m->gtn(1) )
throw new Exception("Modulus must be greater than 1");
}
public static function primeByName($name)
{
switch($name) {
case "k256":
return new BN("ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f", 16);
case "p224":
return new BN("ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001", 16);
case "p192":
return new BN("ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff", 16);
case "p25519":
return new BN("7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed", 16);
default:
throw new Exception("Unknown prime name " . $name);
}
}
public function verify1(BN $num)
{
if (Red::$ASSERT_ENABLED) assert(!$num->negative()); //,"red works only with positives");
assert($num->red); //, "red works only with red numbers");
}
public function verify2(BN $a, BN $b)
{
if (Red::$ASSERT_ENABLED) assert(!$a->negative() && !$b->negative()); //, "red works only with positives");
assert($a->red && ($a->red == $b->red)); //, "red works only with red numbers");
}
public function imod(BN &$a) {
return $a->umod($this->m)->_forceRed($this);
}
public function neg(BN $a)
{
if( $a->isZero() )
return $a->_clone();
return $this->m->sub($a)->_forceRed($this);
}
public function add(BN $a, BN $b)
{
$this->verify2($a, $b);
$res = $a->add($b);
if( $res->cmp($this->m) >= 0 )
$res->isub($this->m);
return $res->_forceRed($this);
}
public function iadd(BN &$a, BN $b)
{
$this->verify2($a, $b);
$a->iadd($b);
if( $a->cmp($this->m) >= 0 )
$a->isub($this->m);
return $a;
}
public function sub(BN $a, BN $b)
{
$this->verify2($a, $b);
$res = $a->sub($b);
if( $res->negative() )
$res->iadd($this->m);
return $res->_forceRed($this);
}
public function isub(BN &$a, $b)
{
$this->verify2($a, $b);
$a->isub($b);
if( $a->negative() )
$a->iadd($this->m);
return $a;
}
public function shl(BN $a, $num) {
$this->verify1($a);
return $this->imod($a->ushln($num));
}
public function imul(BN &$a, BN $b) {
$this->verify2($a, $b);
$res = $a->imul($b);
return $this->imod($res);
}
public function mul(BN $a, BN $b) {
$this->verify2($a, $b);
$res = $a->mul($b);
return $this->imod($res);
}
public function sqr(BN $a) {
$res = $a->_clone();
return $this->imul($res, $a);
}
public function isqr(BN &$a) {
return $this->imul($a, $a);
}
public function sqrt(BN $a) {
if ($a->isZero())
return $a->_clone();
$mod3 = $this->m->andln(3);
assert($mod3 % 2 == 1);
// Fast case
if ($mod3 == 3) {
$pow = $this->m->add(new BN(1))->iushrn(2);
return $this->pow($a, $pow);
}
// Tonelli-Shanks algorithm (Totally unoptimized and slow)
//
// Find Q and S, that Q * 2 ^ S = (P - 1)
$q = $this->m->subn(1);
$s = 0;
while (!$q->isZero() && $q->andln(1) == 0) {
$s++;
$q->iushrn(1);
}
if (Red::$ASSERT_ENABLED) assert(!$q->isZero());
$one = (new BN(1))->toRed($this);
$nOne = $one->redNeg();
// Find quadratic non-residue
// NOTE: Max is such because of generalized Riemann hypothesis.
$lpow = $this->m->subn(1)->iushrn(1);
$z = $this->m->bitLength();
$z = (new BN(2 * $z * $z))->toRed($this);
while ($this->pow($z, $lpow)->cmp($nOne) != 0) {
$z->redIAdd($nOne);
}
$c = $this->pow($z, $q);
$r = $this->pow($a, $q->addn(1)->iushrn(1));
$t = $this->pow($a, $q);
$m = $s;
while ($t->cmp($one) != 0) {
$tmp = $t;
for ($i = 0; $tmp->cmp($one) != 0; $i++) {
$tmp = $tmp->redSqr();
}
if ($i >= $m) {
throw new \Exception("Assertion failed");
}
if ($m - $i - 1 > 54) {
$b = $this->pow($c, (new BN(1))->iushln($m - $i - 1));
} else {
$b = clone($c);
$b->bi = $c->bi->powMod(1 << ($m - $i - 1), $this->m->bi);
}
$r = $r->redMul($b);
$c = $b->redSqr();
$t = $t->redMul($c);
$m = $i;
}
return $r;
}
public function invm(BN &$a) {
$res = $a->invm($this->m);
return $this->imod($res);
}
public function pow(BN $a, BN $num) {
$r = clone($a);
$r->bi = $a->bi->powMod($num->bi, $this->m->bi);
return $r;
}
public function convertTo(BN $num) {
$r = $num->umod($this->m);
return $r === $num ? $r->_clone() : $r;
}
public function convertFrom(BN $num) {
$res = $num->_clone();
$res->red = null;
return $res;
}
}
Red::$ASSERT_ENABLED = ini_get("zend.assertions") === "1";
?>