first commit

This commit is contained in:
annnj-company
2026-04-17 18:29:53 +08:00
parent e49fa5a215
commit 130c1026c4
5615 changed files with 1639145 additions and 0 deletions

View File

@@ -0,0 +1,620 @@
<?php
namespace Mdanter\Ecc\Math;
use Mdanter\Ecc\Primitives\GeneratorPoint;
/**
* Debug helper class to trace all calls to math functions along with the provided params and result.
*/
class DebugDecorator implements GmpMathInterface
{
/**
* @var GmpMathInterface
*/
private $adapter;
/**
* @var callable
*/
private $writer;
/**
* @param GmpMathInterface $adapter
* @param callable|null $callback
*/
public function __construct(GmpMathInterface $adapter, callable $callback = null)
{
$this->adapter = $adapter;
$this->writer = $callback ?: function ($message) {
echo $message;
};
}
/**
*
* @param string $message
*/
private function write($message)
{
call_user_func($this->writer, $message);
}
/**
*
* @param string $func
* @param array $args
* @return mixed
*/
private function call($func, $args)
{
$strArgs = array_map(
function ($arg) {
if ($arg instanceof \GMP) {
return var_export($this->adapter->toString($arg), true);
} else {
return var_export($arg, true);
}
},
$args
);
if (strpos($func, '::')) {
list(, $func) = explode('::', $func);
}
$this->write($func.'('.implode(', ', $strArgs).')');
$res = call_user_func_array([ $this->adapter, $func ], $args);
if ($res instanceof \GMP) {
$this->write(' => ' . var_export($this->adapter->toString($res), true) . PHP_EOL);
} else {
$this->write(' => ' . var_export($res, true) . PHP_EOL);
}
return $res;
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::cmp()
*/
public function cmp(\GMP $first, \GMP $other): int
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::cmp()
*/
public function equals(\GMP $first, \GMP $other): bool
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::mod()
*/
public function mod(\GMP $number, \GMP $modulus): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::add()
*/
public function add(\GMP $augend, \GMP $addend): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::sub()
*/
public function sub(\GMP $minuend, \GMP $subtrahend): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::mul()
*/
public function mul(\GMP $multiplier, \GMP $multiplicand): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::div()
*/
public function div(\GMP $dividend, \GMP $divisor): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::pow()
*/
public function pow(\GMP $base, int $exponent): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::bitwiseAnd()
*/
public function bitwiseAnd(\GMP $first, \GMP $other): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\MathAdapter::toString()
*/
public function toString(\GMP $value): string
{
return $this->adapter->toString($value);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::hexDec()
*/
public function hexDec(string $hexString): string
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::decHex()
*/
public function decHex(string $decString): string
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::powmod()
*/
public function powmod(\GMP $base, \GMP $exponent, \GMP $modulus): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::isPrime()
*/
public function isPrime(\GMP $n): bool
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::nextPrime()
*/
public function nextPrime(\GMP $currentPrime): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::inverseMod()
*/
public function inverseMod(\GMP $a, \GMP $m): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::jacobi()
*/
public function jacobi(\GMP $a, \GMP $p): int
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::intToString()
*/
public function intToFixedSizeString(\GMP $x, int $byteSize): string
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::intToString()
*/
public function intToString(\GMP $x): string
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::stringToInt()
*/
public function stringToInt(string $s): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::digestInteger()
*/
public function digestInteger(\GMP $m): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::gcd2()
*/
public function gcd2(\GMP $a, \GMP $m): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::rightShift()
*/
public function rightShift(\GMP $number, int $positions): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::leftShift()
*/
public function leftShift(\GMP $number, int $positions): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call',
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::bitwiseXor()
*/
public function bitwiseXor(\GMP $first, \GMP $other): \GMP
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call'
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::baseConvert()
*/
public function baseConvert(string $value, int $fromBase, int $toBase): string
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call'
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::getEcMath()
*/
public function getEcMath(GeneratorPoint $generatorPoint, $input)
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call'
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::getModularArithmetic()
*/
public function getModularArithmetic(\GMP $modulus): ModularArithmetic
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call'
),
$func,
$args
);
}
/**
* {@inheritDoc}
* @see \Mdanter\Ecc\Math\GmpMathInterface::getNumberTheory()
*/
public function getNumberTheory(): NumberTheory
{
$func = __METHOD__;
$args = func_get_args();
return call_user_func(
array(
$this,
'call'
),
$func,
$args
);
}
}

View File

@@ -0,0 +1,333 @@
<?php
namespace Mdanter\Ecc\Math;
use Mdanter\Ecc\Util\BinaryString;
use Mdanter\Ecc\Util\NumberSize;
class GmpMath implements GmpMathInterface
{
/**
* {@inheritDoc}
* @see GmpMathInterface::cmp()
*/
public function cmp(\GMP $first, \GMP $other): int
{
return gmp_cmp($first, $other);
}
/**
* @param \GMP $first
* @param \GMP $other
* @return bool
*/
public function equals(\GMP $first, \GMP $other): bool
{
return gmp_cmp($first, $other) === 0;
}
/**
* {@inheritDoc}
* @see GmpMathInterface::mod()
*/
public function mod(\GMP $number, \GMP $modulus): \GMP
{
return gmp_mod($number, $modulus);
}
/**
* {@inheritDoc}
* @see GmpMathInterface::add()
*/
public function add(\GMP $augend, \GMP $addend): \GMP
{
return gmp_add($augend, $addend);
}
/**
* {@inheritDoc}
* @see GmpMathInterface::sub()
*/
public function sub(\GMP $minuend, \GMP $subtrahend): \GMP
{
return gmp_sub($minuend, $subtrahend);
}
/**
* {@inheritDoc}
* @see GmpMathInterface::mul()
*/
public function mul(\GMP $multiplier, \GMP $multiplicand): \GMP
{
return gmp_mul($multiplier, $multiplicand);
}
/**
* {@inheritDoc}
* @see GmpMathInterface::div()
*/
public function div(\GMP $dividend, \GMP $divisor): \GMP
{
return gmp_div($dividend, $divisor);
}
/**
* {@inheritDoc}
* @see GmpMathInterface::pow()
*/
public function pow(\GMP $base, int $exponent): \GMP
{
return gmp_pow($base, $exponent);
}
/**
* {@inheritDoc}
* @see GmpMathInterface::bitwiseAnd()
*/
public function bitwiseAnd(\GMP $first, \GMP $other): \GMP
{
return gmp_and($first, $other);
}
/**
* {@inheritDoc}
* @see GmpMathInterface::rightShift()
*/
public function rightShift(\GMP $number, int $positions): \GMP
{
// Shift 1 right = div / 2
return gmp_div($number, gmp_pow(gmp_init(2, 10), $positions));
}
/**
* {@inheritDoc}
* @see GmpMathInterface::bitwiseXor()
*/
public function bitwiseXor(\GMP $first, \GMP $other): \GMP
{
return gmp_xor($first, $other);
}
/**
* {@inheritDoc}
* @see GmpMathInterface::leftShift()
*/
public function leftShift(\GMP $number, int $positions): \GMP
{
// Shift 1 left = mul by 2
return gmp_mul($number, gmp_pow(2, $positions));
}
/**
* {@inheritDoc}
* @see GmpMathInterface::toString()
*/
public function toString(\GMP $value): string
{
return gmp_strval($value);
}
/**
* {@inheritDoc}
* @see GmpMathInterface::hexDec()
*/
public function hexDec(string $hex): string
{
return gmp_strval(gmp_init($hex, 16), 10);
}
/**
* {@inheritDoc}
* @see GmpMathInterface::decHex()
*/
public function decHex(string $dec): string
{
$dec = gmp_init($dec, 10);
if (gmp_cmp($dec, 0) < 0) {
throw new \InvalidArgumentException('Unable to convert negative integer to string');
}
$hex = gmp_strval($dec, 16);
if (BinaryString::length($hex) % 2 != 0) {
$hex = '0'.$hex;
}
return $hex;
}
/**
* {@inheritDoc}
* @see GmpMathInterface::powmod()
*/
public function powmod(\GMP $base, \GMP $exponent, \GMP $modulus): \GMP
{
if ($this->cmp($exponent, gmp_init(0, 10)) < 0) {
throw new \InvalidArgumentException("Negative exponents (" . $this->toString($exponent) . ") not allowed.");
}
return gmp_powm($base, $exponent, $modulus);
}
/**
* {@inheritDoc}
* @see GmpMathInterface::isPrime()
*/
public function isPrime(\GMP $n): bool
{
$prob = gmp_prob_prime($n);
if ($prob > 0) {
return true;
}
return false;
}
/**
* {@inheritDoc}
* @see GmpMathInterface::nextPrime()
*/
public function nextPrime(\GMP $starting_value): \GMP
{
return gmp_nextprime($starting_value);
}
/**
* {@inheritDoc}
* @see GmpMathInterface::inverseMod()
*/
public function inverseMod(\GMP $a, \GMP $m): \GMP
{
return gmp_invert($a, $m);
}
/**
* {@inheritDoc}
* @see GmpMathInterface::jacobi()
*/
public function jacobi(\GMP $a, \GMP $n): int
{
return gmp_jacobi($a, $n);
}
/**
* @param \GMP $x
* @param int $byteSize
* @return string
*/
public function intToFixedSizeString(\GMP $x, int $byteSize): string
{
if ($byteSize < 0) {
throw new \RuntimeException("Byte size cannot be negative");
}
if (gmp_cmp($x, 0) < 0) {
throw new \RuntimeException("x was negative - not yet supported");
}
$two = gmp_init(2);
$range = gmp_pow($two, $byteSize * 8);
if (NumberSize::bnNumBits($this, $x) >= NumberSize::bnNumBits($this, $range)) {
throw new \RuntimeException("Number overflows byte size");
}
$maskShift = gmp_pow($two, 8);
$mask = gmp_mul(gmp_init(255), $range);
$binary = '';
for ($i = $byteSize - 1; $i >= 0; $i--) {
$mask = gmp_div($mask, $maskShift);
$binary .= pack('C', gmp_strval(gmp_div(gmp_and($x, $mask), gmp_pow($two, $i * 8)), 10));
}
return $binary;
}
/**
* {@inheritDoc}
* @see GmpMathInterface::intToString()
*/
public function intToString(\GMP $x): string
{
if (gmp_cmp($x, 0) < 0) {
throw new \InvalidArgumentException('Unable to convert negative integer to string');
}
$hex = gmp_strval($x, 16);
if (BinaryString::length($hex) % 2 != 0) {
$hex = '0'.$hex;
}
return pack('H*', $hex);
}
/**
* {@inheritDoc}
* @see GmpMathInterface::stringToInt()
*/
public function stringToInt(string $s): \GMP
{
$result = gmp_init(0, 10);
$sLen = BinaryString::length($s);
for ($c = 0; $c < $sLen; $c ++) {
$result = gmp_add(gmp_mul(256, $result), gmp_init(ord($s[$c]), 10));
}
return $result;
}
/**
* {@inheritDoc}
* @see GmpMathInterface::digestInteger()
*/
public function digestInteger(\GMP $m): \GMP
{
return $this->stringToInt(hash('sha1', $this->intToString($m), true));
}
/**
* {@inheritDoc}
* @see GmpMathInterface::gcd2()
*/
public function gcd2(\GMP $a, \GMP $b): \GMP
{
while ($this->cmp($a, gmp_init(0)) > 0) {
$temp = $a;
$a = $this->mod($b, $a);
$b = $temp;
}
return $b;
}
/**
* {@inheritDoc}
* @see GmpMathInterface::baseConvert()
*/
public function baseConvert(string $number, int $from, int $to): string
{
return gmp_strval(gmp_init($number, $from), $to);
}
/**
* {@inheritDoc}
* @see GmpMathInterface::getNumberTheory()
*/
public function getNumberTheory(): NumberTheory
{
return new NumberTheory($this);
}
/**
* @param \GMP $modulus
* @return ModularArithmetic
*/
public function getModularArithmetic(\GMP $modulus): ModularArithmetic
{
return new ModularArithmetic($this, $modulus);
}
}

View File

@@ -0,0 +1,227 @@
<?php
namespace Mdanter\Ecc\Math;
interface GmpMathInterface
{
/**
* Compares two numbers
*
* @param \GMP $first
* @param \GMP $other
* @return int less than 0 if first is less than second, 0 if equal, greater than 0 if greater than.
*/
public function cmp(\GMP $first, \GMP $other): int;
/**
* @param \GMP $first
* @param \GMP $other
* @return bool
*/
public function equals(\GMP $first, \GMP $other): bool;
/**
* Returns the remainder of a division
*
* @param \GMP $number
* @param \GMP $modulus
* @return \GMP
*/
public function mod(\GMP $number, \GMP $modulus): \GMP;
/**
* Adds two numbers
*
* @param \GMP $augend
* @param \GMP $addend
* @return \GMP
*/
public function add(\GMP $augend, \GMP $addend): \GMP;
/**
* Substract one number from another
*
* @param \GMP $minuend
* @param \GMP $subtrahend
* @return \GMP
*/
public function sub(\GMP $minuend, \GMP $subtrahend): \GMP;
/**
* Multiplies a number by another.
*
* @param \GMP $multiplier
* @param \GMP $multiplicand
* @return \GMP
*/
public function mul(\GMP $multiplier, \GMP $multiplicand): \GMP;
/**
* Divides a number by another.
*
* @param \GMP $dividend
* @param \GMP $divisor
* @return \GMP
*/
public function div(\GMP $dividend, \GMP $divisor): \GMP;
/**
* Raises a number to a power.
*
* @param \GMP $base The number to raise.
* @param int $exponent The power to raise the number to.
* @return \GMP
*/
public function pow(\GMP $base, int $exponent): \GMP;
/**
* Performs a logical AND between two values.
*
* @param \GMP $first
* @param \GMP $other
* @return \GMP
*/
public function bitwiseAnd(\GMP $first, \GMP $other): \GMP;
/**
* Performs a logical XOR between two values.
*
* @param \GMP $first
* @param \GMP $other
* @return \GMP
*/
public function bitwiseXor(\GMP $first, \GMP $other): \GMP;
/**
* Shifts bits to the right
* @param \GMP $number Number to shift
* @param int $positions Number of positions to shift
* @return \GMP
*/
public function rightShift(\GMP $number, int $positions): \GMP;
/**
* Shifts bits to the left
* @param \GMP $number Number to shift
* @param int $positions Number of positions to shift
* @return \GMP
*/
public function leftShift(\GMP $number, int $positions): \GMP;
/**
* Returns the string representation of a returned value.
*
* @param \GMP $value
* @return string
*/
public function toString(\GMP $value): string;
/**
* Converts an hexadecimal string to decimal.
*
* @param string $hexString
* @return int|string
*/
public function hexDec(string $hexString): string;
/**
* Converts a decimal string to hexadecimal.
*
* @param int|string $decString
* @return string
*/
public function decHex(string $decString): string;
/**
* Calculates the modular exponent of a number.
*
* @param \GMP $base
* @param \GMP $exponent
* @param \GMP $modulus
* @return \GMP
*/
public function powmod(\GMP $base, \GMP $exponent, \GMP $modulus): \GMP;
/**
* Checks whether a number is a prime.
*
* @param \GMP $n
* @return boolean
*/
public function isPrime(\GMP $n): bool;
/**
* Gets the next known prime that is greater than a given prime.
*
* @param \GMP $currentPrime
* @return \GMP
*/
public function nextPrime(\GMP $currentPrime): \GMP;
/**
* @param \GMP $a
* @param \GMP $m
* @return \GMP
*/
public function inverseMod(\GMP $a, \GMP $m): \GMP;
/**
* @param \GMP $a
* @param \GMP $p
* @return int
*/
public function jacobi(\GMP $a, \GMP $p): int;
/**
* @param \GMP $x
* @return string
*/
public function intToString(\GMP $x): string;
/**
* @param \GMP $x
* @param int $byteSize
* @return string
*/
public function intToFixedSizeString(\GMP $x, int $byteSize): string;
/**
*
* @param int|string $s
* @return \GMP
*/
public function stringToInt(string $s): \GMP;
/**
*
* @param \GMP $m
* @return \GMP
*/
public function digestInteger(\GMP $m): \GMP;
/**
* @param \GMP $a
* @param \GMP $m
* @return \GMP
*/
public function gcd2(\GMP $a, \GMP $m): \GMP;
/**
* @param string $value
* @param int $fromBase
* @param int $toBase
* @return string
*/
public function baseConvert(string $value, int $fromBase, int $toBase): string;
/**
* @return NumberTheory
*/
public function getNumberTheory(): NumberTheory;
/**
* @param \GMP $modulus
* @return ModularArithmetic
*/
public function getModularArithmetic(\GMP $modulus): ModularArithmetic;
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Mdanter\Ecc\Math;
class MathAdapterFactory
{
/**
* @var GmpMathInterface
*/
private static $forcedAdapter = null;
/**
* @param GmpMathInterface $adapter
*/
public static function forceAdapter(GmpMathInterface $adapter = null)
{
self::$forcedAdapter = $adapter;
}
/**
* @param bool $debug
* @return DebugDecorator|GmpMathInterface|null
*/
public static function getAdapter(bool $debug = false): GmpMathInterface
{
if (self::$forcedAdapter !== null) {
return self::$forcedAdapter;
}
$adapter = new GmpMath();
return self::wrapAdapter($adapter, $debug);
}
/**
* @param GmpMathInterface $adapter
* @param bool $debug
* @return DebugDecorator|GmpMathInterface
*/
private static function wrapAdapter(GmpMathInterface $adapter, bool $debug): GmpMathInterface
{
if ($debug === true) {
return new DebugDecorator($adapter);
}
return $adapter;
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace Mdanter\Ecc\Math;
class ModularArithmetic
{
/**
* @var GmpMathInterface
*/
private $adapter;
/**
* @var \GMP
*/
private $modulus;
/**
* @param GmpMathInterface $adapter
* @param \GMP $modulus
*/
public function __construct(GmpMathInterface $adapter, \GMP $modulus)
{
$this->adapter = $adapter;
$this->modulus = $modulus;
}
/**
* @param \GMP $augend
* @param \GMP $addend
* @return \GMP
*/
public function add(\GMP $augend, \GMP $addend): \GMP
{
return $this->adapter->mod($this->adapter->add($augend, $addend), $this->modulus);
}
/**
* @param \GMP $minuend
* @param \GMP $subtrahend
* @return \GMP
*/
public function sub(\GMP $minuend, \GMP $subtrahend): \GMP
{
return $this->adapter->mod($this->adapter->sub($minuend, $subtrahend), $this->modulus);
}
/**
* @param \GMP $multiplier
* @param \GMP $muliplicand
* @return \GMP
*/
public function mul(\GMP $multiplier, \GMP $muliplicand): \GMP
{
return $this->adapter->mod($this->adapter->mul($multiplier, $muliplicand), $this->modulus);
}
/**
* @param \GMP $dividend
* @param \GMP $divisor
* @return \GMP
*/
public function div(\GMP $dividend, \GMP $divisor): \GMP
{
return $this->mul($dividend, $this->adapter->inverseMod($divisor, $this->modulus));
}
/**
* @param \GMP $base
* @param \GMP $exponent
* @return \GMP
*/
public function pow(\GMP $base, \GMP $exponent): \GMP
{
return $this->adapter->powmod($base, $exponent, $this->modulus);
}
}

View File

@@ -0,0 +1,267 @@
<?php
namespace Mdanter\Ecc\Math;
/***********************************************************************
* Copyright (C) 2012 Matyas Danter
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
************************************************************************/
/**
* Implementation of some number theoretic algorithms
*
* @author Matyas Danter
*/
use Mdanter\Ecc\Exception\NumberTheoryException;
use Mdanter\Ecc\Exception\SquareRootException;
/**
* Rewritten to take a MathAdaptor to handle different environments. Has
* some desireable functions for public key compression/recovery.
*/
class NumberTheory
{
/**
* @var GmpMathInterface
*/
private $adapter;
/**
* @param GmpMathInterface $adapter
*/
public function __construct(GmpMathInterface $adapter)
{
$this->adapter = $adapter;
$this->zero = gmp_init(0, 10);
$this->one = gmp_init(1, 10);
$this->two = gmp_init(2, 10);
}
/**
* @param \GMP[] $poly
* @param \GMP[] $polymod
* @param \GMP $p
* @return \GMP[]
*/
public function polynomialReduceMod(array $poly, array $polymod, \GMP $p): array
{
$adapter = $this->adapter;
// Only enter if last value is set, implying count > 0
if ((($last = end($polymod)) instanceof \GMP) && $adapter->equals($last, $this->one)) {
$count_polymod = count($polymod);
while (count($poly) >= $count_polymod) {
if (!$adapter->equals(end($poly), $this->zero)) {
for ($i = 2; $i < $count_polymod + 1; $i++) {
$poly[count($poly) - $i] =
$adapter->mod(
$adapter->sub(
$poly[count($poly) - $i],
$adapter->mul(
end($poly),
$polymod[$count_polymod - $i]
)
),
$p
);
}
}
$poly = array_slice($poly, 0, count($poly) - 1);
}
return $poly;
}
throw new NumberTheoryException('Unable to calculate polynomialReduceMod');
}
/**
* @param \GMP[] $m1
* @param \GMP[] $m2
* @param \GMP[] $polymod
* @param \GMP $p
* @return \GMP[]
*/
public function polynomialMultiplyMod(array $m1, array $m2, array $polymod, \GMP $p): array
{
$prod = array();
$cm1 = count($m1);
$cm2 = count($m2);
for ($i = 0; $i < $cm1; $i++) {
for ($j = 0; $j < $cm2; $j++) {
$index = $i + $j;
if (!isset($prod[$index])) {
$prod[$index] = $this->zero;
}
$prod[$index] =
$this->adapter->mod(
$this->adapter->add(
$prod[$index],
$this->adapter->mul(
$m1[$i],
$m2[$j]
)
),
$p
);
}
}
return $this->polynomialReduceMod($prod, $polymod, $p);
}
/**
* @param \GMP[] $base
* @param \GMP $exponent
* @param \GMP[] $polymod
* @param \GMP $p
* @return \GMP[]
*/
public function polynomialPowMod(array $base, \GMP $exponent, array $polymod, \GMP $p): array
{
$adapter = $this->adapter;
if ($adapter->cmp($exponent, $p) < 0) {
if ($adapter->equals($exponent, $this->zero)) {
return $this->one;
}
$G = $base;
$k = $exponent;
if ($adapter->equals($adapter->mod($k, $this->two), $this->one)) {
$s = $G;
} else {
$s = array($this->one);
}
while ($adapter->cmp($k, $this->one) > 0) {
$k = $adapter->div($k, $this->two);
$G = $this->polynomialMultiplyMod($G, $G, $polymod, $p);
if ($adapter->equals($adapter->mod($k, $this->two), $this->one)) {
$s = $this->polynomialMultiplyMod($G, $s, $polymod, $p);
}
}
return $s;
}
throw new NumberTheoryException('Unable to calculate polynomialPowMod');
}
/**
* @param \GMP $a
* @param \GMP $p
* @return \GMP
*/
public function squareRootModP(\GMP $a, \GMP $p): \GMP
{
$math = $this->adapter;
$four = gmp_init(4, 10);
$eight = gmp_init(8, 10);
$modMath = $math->getModularArithmetic($p);
if ($math->cmp($this->one, $p) < 0) {
if ($math->equals($a, $this->zero)) {
return $this->zero;
}
if ($math->equals($p, $this->two)) {
return $a;
}
$jac = $math->jacobi($a, $p);
if ($jac === -1) {
throw new SquareRootException("{$math->toString($a)} has no square root modulo {$math->toString($p)}");
}
if ($math->equals($math->mod($p, $four), gmp_init(3, 10))) {
return $modMath->pow($a, $math->div($math->add($p, $this->one), $four));
}
if ($math->equals($math->mod($p, $eight), gmp_init(5, 10))) {
$d = $modMath->pow($a, $math->div($math->sub($p, $this->one), $four));
if ($math->equals($d, $this->one)) {
return $modMath->pow($a, $math->div($math->add($p, gmp_init(3, 10)), $eight));
}
if ($math->equals($d, $math->sub($p, $this->one))) {
return $modMath->mul(
$math->mul(
$this->two,
$a
),
$modMath->pow(
$math->mul(
$four,
$a
),
$math->div(
$math->sub(
$p,
gmp_init(5, 10)
),
$eight
)
)
);
}
//shouldn't get here
}
for ($b = $this->two; $math->cmp($b, $p) < 0; $b = gmp_add($b, $this->one)) {
if ($math->jacobi(
$math->sub(
$math->mul($b, $b),
$math->mul($four, $a)
),
$p
) == -1
) {
$f = array($a, $math->sub($this->zero, $b), $this->one);
$ff = $this->polynomialPowMod(
array($this->zero, $this->one),
$math->div(
$math->add(
$p,
$this->one
),
$this->two
),
$f,
$p
);
if ($math->equals($ff[1], $this->zero)) {
return $ff[0];
}
// if we got here no b was found
}
}
}
throw new SquareRootException('Unable to calculate square root mod p!');
}
}