This commit is contained in:
2025-10-09 17:41:57 +00:00
commit 7a3b2960f8
146 changed files with 34948 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
## MIT LICENSE
Copyright (C) 2018 Simplito
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.
+190
View File
@@ -0,0 +1,190 @@
# BigInteger wrapper library for PHP
## Information
This library is a common interface for php_gmp and php_bcmath modules. It automatically detects supported modules and uses the best of them (gmp>bcmath). Gmp is a lot faster, but is also missing on many hosting services -- that is why this wrapper has been created. It is used for example in encryption functions of the [PrivMX WebMail](https://privmx.com) software.
## Installation
You can install this library via Composer:
```
composer require simplito/bigint-wrapper-php
```
## Documentation
If you want to force using a specific implementation, then define constant S_MATH_BIGINTEGER_MODE - set it to "gmp" or "bcmath". If you do not do this, mode of operation and the constant will be set automatically.
If there are no gmp and bcmath modules, an exception will be thrown. If you want to prevent this, then simply define S_MATH_BIGINTEGER_QUIET constant.
All functions of this library are implemented as members of class BigInteger, which is located under BI namespace. Instances of BigInteger are immutable - member functions usually return new instances of the BigInteger class.
### ConvertibleToBi - a placeholder type
To make the below documentation more readable we use the "ConvertibleToBi" type symbol, which in reality can be one of the following types:
- an instance of the BigInteger class
- an integer
- a decimal string
- a gmp resource or class (only when you are in gmp mode)
If you have a non-decimal string and want to use it -- first you have to convert it to BigInteger class using:
```
new BigInteger($myNonDecimalString, $baseOfMyNonDecimalString)
```
### BI\BigInteger class members
#### construct(ConvertibleToBi $value = 0, int $base = 10)
Creates a new instance of BigInteger. If you pass an invalid value, an exception will be thrown. If $base === true then passed $value will be used without any check and conversion. Supported bases: 2, 10, 16, 256.
- **GMP implementation:** gmp_init + bin2hex for 256 base
- **Bcmath implementation:** custom(bcadd + bcmul)
#### static BigInteger|false createSafe(ConvertibleToBi $value = 0, int $base = 10)
Creates a new BigInteger instance in the same way as constructor, but if there is an error, false will be returned instead of throwing an exception.
#### BigInteger add(ConvertibleToBi $x)
Adds numbers
- **GMP implementation:** gmp_add
- **Bcmath implementation:** bcadd
#### BigInteger sub(ConvertibleToBi $x)
Subtracts numbers
- **GMP implementation:** gmp_sub
- **Bcmath implementation:** bcsub
#### BigInteger mul(ConvertibleToBi $x)
Multiplies numbers
- **GMP implementation:** gmp_mul
- **Bcmath implementation:** bcmul
#### BigInteger div(ConvertibleToBi $x)
Divides numbers
- **GMP implementation:** gmp_div_q
- **Bcmath implementation:** bcdiv
#### BigInteger divR(ConvertibleToBi $x)
Returns a remainder of the division of numbers. The remainder has the sign of the divided number.
- **GMP implementation:** gmp_div_r
- **Bcmath implementation:** bcmod
#### array(BigInteger, BigInteger) divQR(ConvertibleToBi $x)
Divides numbers and returns quotient and remainder. Returns an array(), with the first element being quotient, and the second being remainder.
- **GMP implementation:** gmp_div_qr
- **Bcmath implementation:** div + divR
#### BigInteger mod(ConvertibleToBi $x)
The "division modulo" operation. The result is always non-negative, the sign of divider is ignored.
- **GMP implementation:** gmp_mod
- **Bcmath implementation:** custom (bcmod + bcadd)
#### BigInteger gcd(ConvertibleToBi $x)
Calculates greatest common divisor
- **GMP implementation:** gmp_gcd
- **Bcmath implementation:** custom (bccomp + bcdiv + bcsub + bcmul)
#### BigInteger|false modInverse(ConvertibleToBi $x)
Inverses by modulo, returns false if inversion does not exist.
- **GMP implementation:** gmp_invert
- **Bcmath implementation:** custom (gcd)
#### BigInteger pow(ConvertibleToBi $x)
The power function.
- **GMP implementation:** gmp_pow
- **Bcmath implementation:** bcpow
#### BigInteger powMod(ConvertibleToBi $x, ConvertibleToBi $n)
The modular power function.
- **GMP implementation:** gmp_powm
- **Bcmath implementation:** bcpowmod
#### BigInteger abs()
Returns absolute value.
- **GMP implementation:** gmp_abs
- **Bcmath implementation:** check first character
#### BigInteger neg()
Negates the number
- **GMP implementation:** gmp_neg
- **Bcmath implementation:** check first character
#### BigInteger binaryAnd(ConvertibleToBi $x)
Bitwise AND.
- **GMP implementation:** gmp_and
- **Bcmath implementation:** custom (toBytes + php string and)
#### BigInteger binaryOr(ConvertibleToBi $x)
Bitwise OR
- **GMP implementation:** gmp_or
- **Bcmath implementation:** custom (toBytes + php string or)
#### BigInteger binaryXor(ConvertibleToBi $x)
Bitwise XOR
- **GMP implementation:** gmp_xor
- **Bcmath implementation:** custom (toBytes + php string xor)
#### BigInteger setbit($index, $bitOn = true)
Sets bit at given index
- **GMP implementation:** gmp_setbit
- **Bcmath implementation:** custom (toBits)
#### bool testbit($index)
Tests if a bit at given index is set
- **GMP implementation:** gmp_testbit
- **Bcmath implementation:** custom (toBits)
#### int scan0($start)
Scans for 0, and returns index of first found bit
- **GMP implementation:** gmp_scan0
- **Bcmath implementation:** custom (toBits)
#### int scan1($start)
Scans for 1, and returns index of first found bit
- **GMP implementation:** gmp_scan1
- **Bcmath implementation:** custom (toBits)
#### int cmp(ConvertibleToBi $x)
Compares numbers, returns <0, 0, >0
- **GMP implementation:** gmp_cmp
- **Bcmath implementation:** bccomp
#### bool equals(ConvertibleToBi $x)
Checks if numbers are equal
- **GMP implementation:** gmp_cmp
- **Bcmath implementation:** bccomp
#### int sign()
Sign of number, returns -1, 0, 1
- **GMP implementation:** gmp_sign
- **Bcmath implementation:** check first character
#### int toNumber()
Converts to number (use only with small 32/64bit numbers)
- **GMP implementation:** gmp_intval
- **Bcmath implementation:** intval
#### string toDec()
Converts to decimal string
- **GMP implementation:** gmp_strval
- **Bcmath implementation:** just the value
#### string toHex()
Converts to hex string
- **GMP implementation:** gmp_strval
- **Bcmath implementation:** toBytes + bin2hex
#### string toBytes
Converts to binary string
- **GMP implementation:** gmp_strval + hex2bin
- **Bcmath implementation:** custom (bcmod + bcdiv + bccomp)
#### string toBits()
Converts to bits string (0 and 1 characters)
- **GMP implementation:** gmp_strval
- **Bcmath implementation:** toBytes + decbin
#### string toString(int $base = 10)
Converts to string using given base (supported bases 2-62, 256)
- **GMP implementation:** all above toX functions, and for non standard gmp_strval
- **Bcmath implementation:** all above toX functions, and for non standard bcmod + bcdiv + bccomp
+17
View File
@@ -0,0 +1,17 @@
{
"name": "simplito/bigint-wrapper-php",
"description": "Common interface for php_gmp and php_bcmath modules",
"license": "MIT",
"authors": [
{
"name": "Simplito Team",
"email": "s.smyczynski@simplito.com",
"homepage": "https://simplito.com"
}
],
"autoload": {
"psr-4": {
"BI\\": "lib/"
}
}
}
+635
View File
@@ -0,0 +1,635 @@
<?php
namespace BI;
if (!defined("S_MATH_BIGINTEGER_MODE")) {
if (extension_loaded("gmp")) {
define("S_MATH_BIGINTEGER_MODE", "gmp");
}
else if (extension_loaded("bcmath")) {
define("S_MATH_BIGINTEGER_MODE", "bcmath");
}
else {
if (!defined("S_MATH_BIGINTEGER_QUIET")) {
throw new \Exception("Cannot use BigInteger. Neither gmp nor bcmath module is loaded");
}
}
}
if (S_MATH_BIGINTEGER_MODE == "gmp") {
if (!extension_loaded("gmp")) {
throw new \Exception("Extension gmp not loaded");
}
class BigInteger {
public $value;
public function __construct($value = 0, $base = 10) {
$this->value = $base === true ? $value : BigInteger::getGmp($value, $base);
}
public static function createSafe($value = 0, $base = 10) {
try {
return new BigInteger($value, $base);
}
catch (\Exception $e) {
return false;
}
}
public static function isGmp($var) {
if (is_resource($var)) {
return get_resource_type($var) == "GMP integer";
}
if (class_exists("GMP") && $var instanceof \GMP) {
return true;
}
return false;
}
public static function getGmp($value = 0, $base = 10) {
if ($value instanceof BigInteger) {
return $value->value;
}
if (BigInteger::isGmp($value)) {
return $value;
}
$type = gettype($value);
if ($type == "integer") {
$gmp = gmp_init($value);
if ($gmp === false) {
throw new \Exception("Cannot initialize");
}
return $gmp;
}
if ($type == "string") {
if ($base != 2 && $base != 10 && $base != 16 && $base != 256) {
throw new \Exception("Unsupported BigInteger base");
}
if ($base == 256) {
$value = bin2hex($value);
$base = 16;
}
$level = error_reporting();
error_reporting(0);
$gmp = gmp_init($value, $base);
error_reporting($level);
if ($gmp === false) {
throw new \Exception("Cannot initialize");
}
return $gmp;
}
throw new \Exception("Unsupported value, only string and integer are allowed, receive " . $type . ($type == "object" ? ", class: " . get_class($value) : ""));
}
public function toDec() {
return gmp_strval($this->value, 10);
}
public function toHex() {
$hex = gmp_strval($this->value, 16);
return strlen($hex) % 2 == 1 ? "0". $hex : $hex;
}
public function toBytes() {
return hex2bin($this->toHex());
}
public function toBase($base) {
if ($base < 2 || $base > 62) {
throw new \Exception("Invalid base");
}
return gmp_strval($this->value, $base);
}
public function toBits() {
return gmp_strval($this->value, 2);
}
public function toString($base = 10) {
if ($base == 2) {
return $this->toBits();
}
if ($base == 10) {
return $this->toDec();
}
if ($base == 16) {
return $this->toHex();
}
if ($base == 256) {
return $this->toBytes();
}
return $this->toBase($base);
}
public function __toString() {
return $this->toString();
}
public function toNumber() {
return gmp_intval($this->value);
}
public function add($x) {
return new BigInteger(gmp_add($this->value, BigInteger::getGmp($x)), true);
}
public function sub($x) {
return new BigInteger(gmp_sub($this->value, BigInteger::getGmp($x)), true);
}
public function mul($x) {
return new BigInteger(gmp_mul($this->value, BigInteger::getGmp($x)), true);
}
public function div($x) {
return new BigInteger(gmp_div_q($this->value, BigInteger::getGmp($x)), true);
}
public function divR($x) {
return new BigInteger(gmp_div_r($this->value, BigInteger::getGmp($x)), true);
}
public function divQR($x) {
$res = gmp_div_qr($this->value, BigInteger::getGmp($x));
return array(new BigInteger($res[0], true), new BigInteger($res[1], true));
}
public function mod($x) {
return new BigInteger(gmp_mod($this->value, BigInteger::getGmp($x)), true);
}
public function gcd($x) {
return new BigInteger(gmp_gcd($this->value, BigInteger::getGmp($x)), true);
}
public function modInverse($x) {
$res = gmp_invert($this->value, BigInteger::getGmp($x));
return $res === false ? false : new BigInteger($res, true);
}
public function pow($x) {
return new BigInteger(gmp_pow($this->value, (new BigInteger($x))->toNumber()), true);
}
public function powMod($x, $n) {
return new BigInteger(gmp_powm($this->value, BigInteger::getGmp($x), BigInteger::getGmp($n)), true);
}
public function abs() {
return new BigInteger(gmp_abs($this->value), true);
}
public function neg() {
return new BigInteger(gmp_neg($this->value), true);
}
public function binaryAnd($x) {
return new BigInteger(gmp_and($this->value, BigInteger::getGmp($x)), true);
}
public function binaryOr($x) {
return new BigInteger(gmp_or($this->value, BigInteger::getGmp($x)), true);
}
public function binaryXor($x) {
return new BigInteger(gmp_xor($this->value, BigInteger::getGmp($x)), true);
}
public function setbit($index, $bitOn = true) {
$cpy = gmp_init(gmp_strval($this->value, 16), 16);
gmp_setbit($cpy, $index, $bitOn);
return new BigInteger($cpy, true);
}
public function testbit($index) {
return gmp_testbit($this->value, $index);
}
public function scan0($start) {
return gmp_scan0($this->value, $start);
}
public function scan1($start) {
return gmp_scan1($this->value, $start);
}
public function cmp($x) {
return gmp_cmp($this->value, BigInteger::getGmp($x));
}
public function equals($x) {
return $this->cmp($x) === 0;
}
public function sign() {
return gmp_sign($this->value);
}
}
}
else if (S_MATH_BIGINTEGER_MODE == "bcmath") {
if (!extension_loaded("bcmath")) {
throw new \Exception("Extension bcmath not loaded");
}
class BigInteger{
public static $chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv";
public $value;
public function __construct($value = 0, $base = 10) {
$this->value = $base === true ? $value : BigInteger::getBC($value, $base);
}
public static function createSafe($value = 0, $base = 10) {
try {
return new BigInteger($value, $base);
}
catch (\Exception $e) {
return false;
}
}
public static function checkBinary($str) {
$len = strlen($str);
for ($i = 0; $i < $len; $i++) {
$c = ord($str[$i]);
if (($i != 0 || $c != 45) && ($c < 48 || $c > 49)) {
return false;
}
}
return true;
}
public static function checkDecimal($str) {
$len = strlen($str);
for ($i = 0; $i < $len; $i++) {
$c = ord($str[$i]);
if (($i != 0 || $c != 45) && ($c < 48 || $c > 57)) {
return false;
}
}
return true;
}
public static function checkHex($str) {
$len = strlen($str);
for ($i = 0; $i < $len; $i++) {
$c = ord($str[$i]);
if (($i != 0 || $c != 45) && ($c < 48 || $c > 57) && ($c < 65 || $c > 70) && ($c < 97 || $c > 102)) {
return false;
}
}
return true;
}
public static function getBC($value = 0, $base = 10) {
if ($value instanceof BigInteger) {
return $value->value;
}
$type = gettype($value);
if ($type == "integer") {
return strval($value);
}
if ($type == "string") {
if ($base == 2) {
$value = str_replace(" ", "", $value);
if (!BigInteger::checkBinary($value)) {
throw new \Exception("Invalid characters");
}
$minus = $value[0] == "-";
if ($minus) {
$value = substr($value, 1);
}
$len = strlen($value);
$m = 1;
$res = "0";
for ($i = $len - 1; $i >= 0; $i -= 8) {
$h = $i - 7 < 0 ? substr($value, 0, $i + 1) : substr($value, $i - 7, 8);
$res = bcadd($res, bcmul(bindec($h), $m, 0), 0);
$m = bcmul($m, "256", 0);
}
return ($minus ? "-" : "") . $res;
}
if ($base == 10) {
$value = str_replace(" ", "", $value);
if (!BigInteger::checkDecimal($value)) {
throw new \Exception("Invalid characters");
}
return $value;
}
if ($base == 16) {
$value = str_replace(" ", "", $value);
if (!BigInteger::checkHex($value)) {
throw new \Exception("Invalid characters");
}
$minus = $value[0] == "-";
if ($minus) {
$value = substr($value, 1);
}
$len = strlen($value);
$m = 1;
$res = "0";
for ($i = $len - 1; $i >= 0; $i -= 2) {
$h = $i == 0 ? "0" . substr($value, 0, 1) : substr($value, $i - 1, 2);
$res = bcadd($res, bcmul(hexdec($h), $m, 0), 0);
$m = bcmul($m, "256", 0);
}
return ($minus ? "-" : "") . $res;
}
if ($base == 256) {
$len = strlen($value);
$m = 1;
$res = "0";
for ($i = $len - 1; $i >= 0; $i -= 6) {
$h = $i - 5 < 0 ? substr($value, 0, $i + 1) : substr($value, $i - 5, 6);
$res = bcadd($res, bcmul(base_convert(bin2hex($h), 16, 10), $m, 0), 0);
$m = bcmul($m, "281474976710656", 0);
}
return $res;
}
throw new \Exception("Unsupported BigInteger base");
}
throw new \Exception("Unsupported value, only string and integer are allowed, receive " . $type . ($type == "object" ? ", class: " . get_class($value) : ""));
}
public function toDec() {
return $this->value;
}
public function toHex() {
return bin2hex($this->toBytes());
}
public function toBytes() {
$value = "";
$current = $this->value;
if ($current[0] == "-") {
$current = substr($current, 1);
}
while (bccomp($current, "0", 0) > 0) {
$temp = bcmod($current, "281474976710656");
$value = hex2bin(str_pad(base_convert($temp, 10, 16), 12, "0", STR_PAD_LEFT)) . $value;
$current = bcdiv($current, "281474976710656", 0);
}
return ltrim($value, chr(0));
}
public function toBase($base) {
if ($base < 2 || $base > 62) {
throw new \Exception("Invalid base");
}
$value = '';
$current = $this->value;
$base = BigInteger::getBC($base);
if ($current[0] == '-') {
$current = substr($current, 1);
}
while (bccomp($current, '0', 0) > 0) {
$v = bcmod($current, $base);
$value = BigInteger::$chars[$v] . $value;
$current = bcdiv($current, $base, 0);
}
return $value;
}
public function toBits() {
$bytes = $this->toBytes();
$res = "";
$len = strlen($bytes);
for ($i = 0; $i < $len; $i++) {
$b = decbin(ord($bytes[$i]));
$res .= strlen($b) != 8 ? str_pad($b, 8, "0", STR_PAD_LEFT) : $b;
}
$res = ltrim($res, "0");
return strlen($res) == 0 ? "0" : $res;
}
public function toString($base = 10) {
if ($base == 2) {
return $this->toBits();
}
if ($base == 10) {
return $this->toDec();
}
if ($base == 16) {
return $this->toHex();
}
if ($base == 256) {
return $this->toBytes();
}
return $this->toBase($base);
}
public function __toString() {
return $this->toString();
}
public function toNumber() {
return intval($this->value);
}
public function add($x) {
return new BigInteger(bcadd($this->value, BigInteger::getBC($x), 0), true);
}
public function sub($x) {
return new BigInteger(bcsub($this->value, BigInteger::getBC($x), 0), true);
}
public function mul($x) {
return new BigInteger(bcmul($this->value, BigInteger::getBC($x), 0), true);
}
public function div($x) {
return new BigInteger(bcdiv($this->value, BigInteger::getBC($x), 0), true);
}
public function divR($x) {
return new BigInteger(bcmod($this->value, BigInteger::getBC($x)), true);
}
public function divQR($x) {
return array(
$this->div($x),
$this->divR($x)
);
}
public function mod($x) {
$xv = BigInteger::getBC($x);
$mod = bcmod($this->value, $xv);
if ($mod[0] == "-") {
$mod = bcadd($mod, $xv[0] == "-" ? substr($xv, 1) : $xv, 0);
}
return new BigInteger($mod, true);
}
public function extendedGcd($n) {
$u = $this->value;
$v = (new BigInteger($n))->abs()->value;
$a = "1";
$b = "0";
$c = "0";
$d = "1";
while (bccomp($v, "0", 0) != 0) {
$q = bcdiv($u, $v, 0);
$temp = $u;
$u = $v;
$v = bcsub($temp, bcmul($v, $q, 0), 0);
$temp = $a;
$a = $c;
$c = bcsub($temp, bcmul($a, $q, 0), 0);
$temp = $b;
$b = $d;
$d = bcsub($temp, bcmul($b, $q, 0), 0);
}
return array(
"gcd" => new BigInteger($u, true),
"x" => new BigInteger($a, true),
"y" => new BigInteger($b, true)
);
}
public function gcd($x) {
return $this->extendedGcd($x)["gcd"];
}
public function modInverse($n) {
$n = (new BigInteger($n))->abs();
if ($this->sign() < 0) {
$temp = $this->abs();
$temp = $temp->modInverse($n);
return $n->sub($temp);
}
extract($this->extendedGcd($n));
if (!$gcd->equals(1)) {
return false;
}
$x = $x->sign() < 0 ? $x->add($n) : $x;
return $this->sign() < 0 ? $n->sub($x) : $x;
}
public function pow($x) {
return new BigInteger(bcpow($this->value, BigInteger::getBC($x), 0), true);
}
public function powMod($x, $n) {
return new BigInteger(bcpowmod($this->value, BigInteger::getBC($x), BigInteger::getBC($n), 0), true);
}
public function abs() {
return new BigInteger($this->value[0] == "-" ? substr($this->value, 1) : $this->value, true);
}
public function neg() {
return new BigInteger($this->value[0] == "-" ? substr($this->value, 1) : "-" . $this->value, true);
}
public function binaryAnd($x) {
$left = $this->toBytes();
$right = (new BigInteger($x))->toBytes();
$length = max(strlen($left), strlen($right));
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
return new BigInteger($left & $right, 256);
}
public function binaryOr($x) {
$left = $this->toBytes();
$right = (new BigInteger($x))->toBytes();
$length = max(strlen($left), strlen($right));
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
return new BigInteger($left | $right, 256);
}
public function binaryXor($x) {
$left = $this->toBytes();
$right = (new BigInteger($x))->toBytes();
$length = max(strlen($left), strlen($right));
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
return new BigInteger($left ^ $right, 256);
}
public function setbit($index, $bitOn = true) {
$bits = $this->toBits();
$bits[strlen($bits) - $index - 1] = $bitOn ? "1" : "0";
return new BigInteger($bits, 2);
}
public function testbit($index) {
$bytes = $this->toBytes();
$bytesIndex = intval($index / 8);
$len = strlen($bytes);
$b = $bytesIndex >= $len ? 0 : ord($bytes[$len - $bytesIndex - 1]);
$v = 1 << ($index % 8);
return ($b & $v) === $v;
}
public function scan0($start) {
$bits = $this->toBits();
$len = strlen($bits);
if ($start < 0 || $start >= $len) {
return -1;
}
$pos = strrpos($bits, "0", -1 - $start);
return $pos === false ? -1 : $len - $pos - 1;
}
public function scan1($start) {
$bits = $this->toBits();
$len = strlen($bits);
if ($start < 0 || $start >= $len) {
return -1;
}
$pos = strrpos($bits, "1", -1 - $start);
return $pos === false ? -1 : $len - $pos - 1;
}
public function cmp($x) {
return bccomp($this->value, BigInteger::getBC($x));
}
public function equals($x) {
return $this->value === BigInteger::getBC($x);
}
public function sign() {
return $this->value[0] === "-" ? -1 : ($this->value === "0" ? 0 : 1);
}
}
}
else {
if (!defined("S_MATH_BIGINTEGER_QUIET")) {
throw new \Exception("Unsupported S_MATH_BIGINTEGER_MODE " . S_MATH_BIGINTEGER_MODE);
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
define('S_MATH_BIGINTEGER_MODE', "bcmath");
require(__DIR__ . "/../lib/BigInteger.php");
use BI\BigInteger;
$a = new BigInteger("4547395333333333333333333333333333333333333343587493875493579375498759837593574935739857");
$b = new BigInteger("-4547395333333333333333333333333333333333333343587493875493579375498759837593574935739857");
$c = new BigInteger("0");
$hex = "eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3";
$bytes = hex2bin($hex);
function test($v, $b) {
$start = microtime(true);
$count = 10000;
for ($i = 0; $i < $count; $i++) {
$v->binaryAnd($b);
//$v->toBytes();
//BigInteger::getBC($v, 256);
}
$end = microtime(true);
error_log($end - $start);
}
test($a, $a);
test($b, $b);
//test($bytes, $b);
+119
View File
@@ -0,0 +1,119 @@
<?php
use BI\BigInteger;
function test($a, $b, $message = "") {
error_log(($a === $b ? "PASS" : "FAIL get: " . $a . ", expected: " . $b) . " " . $message);
}
function testB($a, $b, $message = "") {
error_log(($a->toString() === $b ? "PASS" : "FAIL get: " . $a . ", expected: " . $b) . " " . $message);
}
function testSerialization($b, $msg = "") {
test($b->toBits(), "1010000", $msg . " toBits");
test($b->toBytes(), hex2bin("50"), $msg . " toBytes");
test($b->toHex(), "50", $msg . " toHex");
test($b->toDec(), "80", $msg . " toDec");
test($b->toNumber(), 80, $msg . " toNumber");
test($b->toBase(58), "1M", $msg . " to58");
}
function testCreate() {
error_log("=============\nTest serialization\n=============");
testSerialization(new BigInteger("1010000", 2), "bits");
testSerialization(new BigInteger(hex2bin("50"), 256), "bytes");
testSerialization(new BigInteger("50", 16), "hex");
testSerialization(new BigInteger("80", 10), "dec");
testSerialization(new BigInteger("80"), "dec2");
testSerialization(new BigInteger(80), "number");
}
function testCreateSafeSingle($value, $base, $msg) {
try {
$z = new BigInteger($value, $base);
error_log("FAIL exception during create " . $msg);
}
catch (\Exception $e) {
error_log("PASS exception during create " . $msg);
}
test(BigInteger::createSafe($value, $base), false, "createSafe " . $msg);
}
function testCreateSafe() {
error_log("=============\nTest create safe\n=============");
testCreateSafeSingle("zz", 2, "bin");
testCreateSafeSingle("zz", 10, "dec");
testCreateSafeSingle("zz", 16, "hex");
}
function testSpaces() {
error_log("=============\nTest spaces\n=============");
test((new BigInteger("11 0 1", 2))->toBits(), "1101", "bin");
test((new BigInteger("6 2 0 6", 10))->toDec(), "6206", "dec");
test((new BigInteger("f3 5 12 ac 0", 16))->toHex(), "f3512ac0", "hex");
}
function testOp() {
error_log("=============\nTest op\n=============");
testB((new BigInteger(20))->add(34), "54", "add");
testB((new BigInteger(20))->sub(14), "6", "sub");
testB((new BigInteger(20))->mul(12), "240", "mul");
testB((new BigInteger(20))->div(4), "5", "div");
testB((new BigInteger(20))->divR(7), "6", "divR");
$qr = (new BigInteger(20))->divQR(6);
testB($qr[0], "3", "divQR[0]");
testB($qr[1], "2", "divQR[1]");
testB((new BigInteger(20))->mod(3), "2", "mod");
testB((new BigInteger(54))->gcd(81), "27", "gcd");
testB((new BigInteger(3))->modInverse(10), "7", "modInverse");
testB((new BigInteger(3))->pow(4), "81", "pow");
testB((new BigInteger(3))->powMod(4, 10), "1", "powMod");
testB((new BigInteger(20))->abs(), "20", "abs");
testB((new BigInteger(20))->neg(), "-20", "neg");
testB((new BigInteger(20))->binaryAnd(18), "16", "binaryAnd");
testB((new BigInteger(20))->binaryOr(18), "22", "binaryOr");
testB((new BigInteger(20))->binaryXor(18), "6", "binaryXor");
testB((new BigInteger(20))->setbit(3), "28", "setbit");
test((new BigInteger(20))->testbit(4), true, "testbit true");
test((new BigInteger(20))->testbit(3), false, "testbit false");
test((new BigInteger(5))->testbit(0), true, "testbit 0 true");
test((new BigInteger(6))->testbit(0), false, "testbit 0 false");
test((new BigInteger(6))->testbit(1), true, "testbit 1 true");
test((new BigInteger(5))->testbit(1), false, "testbit 1 false");
test((new BigInteger(132))->testbit(7), true, "testbit 7 true");
test((new BigInteger(81))->testbit(7), false, "testbit 7 false");
test((new BigInteger(258))->testbit(8), true, "testbit 8 true");
test((new BigInteger(253))->testbit(8), false, "testbit 8 false");
test((new BigInteger(20))->scan0(2), 3, "scan0");
test((new BigInteger(20))->scan1(3), 4, "scan1");
test((new BigInteger(20))->cmp(22), -1, "cmp -1");
test((new BigInteger(20))->cmp(20), 0, "cmp 0");
test((new BigInteger(20))->cmp(18), 1, "cmp 1");
test((new BigInteger(20))->equals(20), true, "equals true");
test((new BigInteger(20))->equals(21), false, "equals false");
test((new BigInteger(-20))->sign(), -1, "sign -1");
test((new BigInteger(0))->sign(), 0, "sign 0");
test((new BigInteger(20))->sign(), 1, "sign 1");
testB(new BigInteger("-20"), "-20", "minus");
testB(new BigInteger("-14", 16), "-20", "minus");
testB(new BigInteger("-10100", 2), "-20", "minus");
}
function testBig() {
error_log("=============\nTest big\n=============");
$bits = "1001010111010010100001000101110110100001000101101000110101010101001";
$hex = "eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3";
$dec = "436529472098746319073192837123683467019263172846";
$bytes = hex2bin($hex);
test((new BigInteger($bits, 2))->toBits(), $bits, "init big from binary");
test((new BigInteger($dec, 10))->toDec(), $dec, "init big from dec");
test((new BigInteger($hex, 16))->toHex(), $hex, "init big from hex");
test((new BigInteger($bytes, 256))->toBytes(), $bytes, "init big from buffer");
}
testCreate();
testCreateSafe();
testSpaces();
testOp();
testBig();
+6
View File
@@ -0,0 +1,6 @@
<?php
define('S_MATH_BIGINTEGER_MODE', "bcmath");
require(__DIR__ . "/../lib/BigInteger.php");
require(__DIR__ . "/test.php");
+6
View File
@@ -0,0 +1,6 @@
<?php
define('S_MATH_BIGINTEGER_MODE', "gmp");
require(__DIR__ . "/../lib/BigInteger.php");
require(__DIR__ . "/test.php");
+4
View File
@@ -0,0 +1,4 @@
/vendor/
.settings
.project
.buildpath
+23
View File
@@ -0,0 +1,23 @@
## MIT LICENSE
Copyright (C) 2018 Simplito
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.
+17
View File
@@ -0,0 +1,17 @@
# BigNum library for PHP
## Information
This library provides a PHP Big Number API compatible with [bn.js](https://github.com/indutny/bn.js) and is used in Fast PHP ECC library [elliptic-php](https://github.com/simplito/elliptic-php).
This software is licensed under the MIT License.
## Installation
You can install this library via Composer:
```
composer require simplito/bn-php
```
+23
View File
@@ -0,0 +1,23 @@
{
"name": "simplito/bn-php",
"description": "Big number implementation compatible with bn.js",
"license": "MIT",
"authors": [
{
"name": "Simplito Team",
"email": "s.smyczynski@simplito.com",
"homepage": "https://simplito.com"
}
],
"require": {
"simplito/bigint-wrapper-php": "~1.0.0"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"autoload": {
"psr-4": {
"BN\\": "lib/"
}
}
}
+768
View File
@@ -0,0 +1,768 @@
<?php
namespace BN;
use \JsonSerializable;
use \Exception;
use \BI\BigInteger;
class BN implements JsonSerializable
{
public static $ASSERT_ENABLED;
public $bi;
public $red;
function __construct($number, $base = 10, $endian = null)
{
if( $number instanceof BN ) {
$this->bi = $number->bi;
$this->red = $number->red;
return;
}
// Reduction context
$this->red = null;
if ( $number instanceof BigInteger ) {
$this->bi = $number;
return;
}
if( is_array($number) )
{
$number = call_user_func_array("pack", array_merge(array("C*"), $number));
$number = bin2hex($number);
$base = 16;
}
if( $base == "hex" )
$base = 16;
if ($endian == 'le') {
if ($base != 16)
throw new \Exception("Not implemented");
$number = bin2hex(strrev(hex2bin($number)));
}
$this->bi = new BigInteger($number, $base);
}
public function negative() {
return $this->bi->sign() < 0 ? 1 : 0;
}
public static function isBN($num) {
return ($num instanceof BN);
}
public static function max($left, $right) {
return ( $left->cmp($right) > 0 ) ? $left : $right;
}
public static function min($left, $right) {
return ( $left->cmp($right) < 0 ) ? $left : $right;
}
public function copy($dest)
{
$dest->bi = $this->bi;
$dest->red = $this->red;
}
public function _clone() {
return clone($this);
}
public function toString($base = 10, $padding = 0)
{
if( $base == "hex" )
$base = 16;
$str = $this->bi->abs()->toString($base);
if ($padding > 0) {
$len = strlen($str);
$mod = $len % $padding;
if ($mod > 0)
$len = $len + $padding - $mod;
$str = str_pad($str, $len, "0", STR_PAD_LEFT);
}
if( $this->negative() )
return "-" . $str;
return $str;
}
public function toNumber() {
return $this->bi->toNumber();
}
#[\ReturnTypeWillChange]
public function jsonSerialize() {
return $this->toString(16);
}
public function toArray($endian = "be", $length = -1)
{
$hex = $this->toString(16);
if( $hex[0] === "-" )
$hex = substr($hex, 1);
if( strlen($hex) % 2 )
$hex = "0" . $hex;
$bytes = array_map(
function($v) { return hexdec($v); },
str_split($hex, 2)
);
if( $length > 0 )
{
$count = count($bytes);
if( $count > $length )
throw new Exception("Byte array longer than desired length");
for($i = $count; $i < $length; $i++)
array_unshift($bytes, 0);
}
if( $endian === "le" )
$bytes = array_reverse($bytes);
return $bytes;
}
public function bitLength() {
$bin = $this->toString(2);
return strlen($bin) - ( $bin[0] === "-" ? 1 : 0 );
}
public function zeroBits() {
return $this->bi->scan1(0);
}
public function byteLength() {
return ceil($this->bitLength() / 8);
}
//TODO toTwos, fromTwos
public function isNeg() {
return $this->negative() !== 0;
}
// Return negative clone of `this`
public function neg() {
return $this->_clone()->ineg();
}
public function ineg() {
$this->bi = $this->bi->neg();
return $this;
}
// Or `num` with `this` in-place
public function iuor(BN $num) {
$this->bi = $this->bi->binaryOr($num->bi);
return $this;
}
public function ior(BN $num) {
if (BN::$ASSERT_ENABLED) assert(!$this->negative() && !$num->negative());
return $this->iuor($num);
}
// Or `num` with `this`
public function _or(BN $num) {
if( $this->ucmp($num) > 0 )
return $this->_clone()->ior($num);
return $num->_clone()->ior($this);
}
public function uor(BN $num) {
if( $this->ucmp($num) > 0 )
return $this->_clone()->iuor($num);
return $num->_clone()->ior($this);
}
// And `num` with `this` in-place
public function iuand(BN $num) {
$this->bi = $this->bi->binaryAnd($num->bi);
return $this;
}
public function iand(BN $num) {
if (BN::$ASSERT_ENABLED) assert(!$this->negative() && !$num->negative());
return $this->iuand($num);
}
// And `num` with `this`
public function _and(BN $num) {
if( $this->ucmp($num) > 0 )
return $this->_clone()->iand($num);
return $num->_clone()->iand($this);
}
public function uand(BN $num) {
if( $this->ucmp($num) > 0 )
return $this->_clone()->iuand($num);
return $num->_clone()->iuand($this);
}
// Xor `num` with `this` in-place
public function iuxor(BN $num) {
$this->bi = $this->bi->binaryXor($num->bi);
return $this;
}
public function ixor(BN $num) {
if (BN::$ASSERT_ENABLED) assert(!$this->negative() && !$num->negative());
return $this->iuxor($num);
}
// Xor `num` with `this`
public function _xor(BN $num) {
if( $this->ucmp($num) > 0 )
return $this->_clone()->ixor($num);
return $num->_clone()->ixor($this);
}
public function uxor(BN $num) {
if( $this->ucmp($num) > 0 )
return $this->_clone()->iuxor($num);
return $num->_clone()->iuxor($this);
}
// Not ``this`` with ``width`` bitwidth
public function inotn($width)
{
assert(is_integer($width) && $width >= 0);
$neg = false;
if( $this->isNeg() )
{
$this->negi();
$neg = true;
}
for($i = 0; $i < $width; $i++)
$this->bi = $this->bi->setbit($i, !$this->bi->testbit($i));
return $neg ? $this->negi() : $this;
}
public function notn($width) {
return $this->_clone()->inotn($width);
}
// Set `bit` of `this`
public function setn($bit, $val) {
assert(is_integer($bit) && $bit > 0);
$this->bi = $this->bi->setbit($bit, !!$val);
return $this;
}
// Add `num` to `this` in-place
public function iadd(BN $num) {
$this->bi = $this->bi->add($num->bi);
return $this;
}
// Add `num` to `this`
public function add(BN $num) {
return $this->_clone()->iadd($num);
}
// Subtract `num` from `this` in-place
public function isub(BN $num) {
$this->bi = $this->bi->sub($num->bi);
return $this;
}
// Subtract `num` from `this`
public function sub(BN $num) {
return $this->_clone()->isub($num);
}
// Multiply `this` by `num`
public function mul(BN $num) {
return $this->_clone()->imul($num);
}
// In-place Multiplication
public function imul(BN $num) {
$this->bi = $this->bi->mul($num->bi);
return $this;
}
public function imuln($num)
{
assert(is_numeric($num));
$int = intval($num);
$res = $this->bi->mul($int);
if( ($num - $int) > 0 )
{
$mul = 10;
$frac = ($num - $int) * $mul;
$int = intval($frac);
while( ($frac - $int) > 0 )
{
$mul *= 10;
$frac *= 10;
$int = intval($frac);
}
$tmp = $this->bi->mul($int);
$tmp = $tmp->div($mul);
$res = $res->add($tmp);
}
$this->bi = $res;
return $this;
}
public function muln($num) {
return $this->_clone()->imuln($num);
}
// `this` * `this`
public function sqr() {
return $this->mul($this);
}
// `this` * `this` in-place
public function isqr() {
return $this->imul($this);
}
// Math.pow(`this`, `num`)
public function pow(BN $num) {
$res = clone($this);
$res->bi = $res->bi->pow($num->bi);
return $res;
}
// Shift-left in-place
public function iushln($bits) {
assert(is_integer($bits) && $bits >= 0);
if ($bits < 54) {
$this->bi = $this->bi->mul(1 << $bits);
} else {
$this->bi = $this->bi->mul((new BigInteger(2))->pow($bits));
}
return $this;
}
public function ishln($bits) {
if (BN::$ASSERT_ENABLED) assert(!$this->negative());
return $this->iushln($bits);
}
// Shift-right in-place
// NOTE: `hint` is a lowest bit before trailing zeroes
// NOTE: if `extended` is present - it will be filled with destroyed bits
public function iushrn($bits, $hint = 0, &$extended = null) {
if( $hint != 0 )
throw new Exception("Not implemented");
assert(is_integer($bits) && $bits >= 0);
if( $extended != null )
$extended = $this->maskn($bits);
if ($bits < 54) {
$this->bi = $this->bi->div(1 << $bits);
} else {
$this->bi = $this->bi->div((new BigInteger(2))->pow($bits));
}
return $this;
}
public function ishrn($bits, $hint = null, $extended = null) {
if (BN::$ASSERT_ENABLED) assert(!$this->negative());
return $this->iushrn($bits, $hint, $extended);
}
// Shift-left
public function shln($bits) {
return $this->_clone()->ishln($bits);
}
public function ushln($bits) {
return $this->_clone()->iushln($bits);
}
// Shift-right
public function shrn($bits) {
return $this->_clone()->ishrn($bits);
}
public function ushrn($bits) {
return $this->_clone()->iushrn($bits);
}
// Test if n bit is set
public function testn($bit) {
assert(is_integer($bit) && $bit >= 0);
return $this->bi->testbit($bit);
}
// Return only lowers bits of number (in-place)
public function imaskn($bits) {
assert(is_integer($bits) && $bits >= 0);
if (BN::$ASSERT_ENABLED) assert(!$this->negative());
$mask = "";
for($i = 0; $i < $bits; $i++)
$mask .= "1";
return $this->iand(new BN($mask, 2));
}
// Return only lowers bits of number
public function maskn($bits) {
return $this->_clone()->imaskn($bits);
}
// Add plain number `num` to `this`
public function iaddn($num) {
assert(is_numeric($num));
$this->bi = $this->bi->add(intval($num));
return $this;
}
// Subtract plain number `num` from `this`
public function isubn($num) {
assert(is_numeric($num));
$this->bi = $this->bi->sub(intval($num));
return $this;
}
public function addn($num) {
return $this->_clone()->iaddn($num);
}
public function subn($num) {
return $this->_clone()->isubn($num);
}
public function iabs() {
if ($this->bi->sign() < 0) {
$this->bi = $this->bi->abs();
}
return $this;
}
public function abs() {
$res = clone($this);
if ($res->bi->sign() < 0)
$res->bi = $res->bi->abs();
return $res;
}
// Find `this` / `num`
public function div(BN $num) {
if (BN::$ASSERT_ENABLED) assert(!$num->isZero());
$res = clone($this);
$res->bi = $res->bi->div($num->bi);
return $res;
}
// Find `this` % `num`
public function mod(BN $num) {
if (BN::$ASSERT_ENABLED) assert(!$num->isZero());
$res = clone($this);
$res->bi = $res->bi->divR($num->bi);
return $res;
}
public function umod(BN $num) {
if (BN::$ASSERT_ENABLED) assert(!$num->isZero());
$tmp = $num->bi->sign() < 0 ? $num->bi->abs() : $num->bi;
$res = clone($this);
$res->bi = $this->bi->mod($tmp);
return $res;
}
// Find Round(`this` / `num`)
public function divRound(BN $num)
{
if (BN::$ASSERT_ENABLED) assert(!$num->isZero());
$negative = $this->negative() !== $num->negative();
$res = $this->_clone()->abs();
$arr = $res->bi->divQR($num->bi->abs());
$res->bi = $arr[0];
$tmp = $num->bi->sub($arr[1]->mul(2));
if( $tmp->cmp(0) <= 0 && (!$negative || $this->negative() === 0) )
$res->iaddn(1);
return $negative ? $res->negi() : $res;
}
public function modn($num) {
assert(is_numeric($num) && $num != 0);
return $this->bi->divR(intval($num))->toNumber();
}
// In-place division by number
public function idivn($num) {
assert(is_numeric($num) && $num != 0);
$this->bi = $this->bi->div(intval($num));
return $this;
}
public function divn($num) {
return $this->_clone()->idivn($num);
}
public function gcd(BN $num) {
$res = clone($this);
$res->bi = $this->bi->gcd($num->bi);
return $res;
}
public function invm(BN $num) {
$res = clone($this);
$res->bi = $res->bi->modInverse($num->bi);
return $res;
}
public function isEven() {
return !$this->bi->testbit(0);
}
public function isOdd() {
return $this->bi->testbit(0);
}
public function andln($num) {
assert(is_numeric($num));
return $this->bi->binaryAnd($num)->toNumber();
}
public function bincn($num) {
$tmp = (new BN(1))->iushln($num);
return $this->add($tmp);
}
public function isZero() {
return $this->bi->sign() == 0;
}
public function cmpn($num) {
assert(is_numeric($num));
return $this->bi->cmp($num);
}
// Compare two numbers and return:
// 1 - if `this` > `num`
// 0 - if `this` == `num`
// -1 - if `this` < `num`
public function cmp(BN $num) {
return $this->bi->cmp($num->bi);
}
public function ucmp(BN $num) {
return $this->bi->abs()->cmp($num->bi->abs());
}
public function gtn($num) {
return $this->cmpn($num) > 0;
}
public function gt(BN $num) {
return $this->cmp($num) > 0;
}
public function gten($num) {
return $this->cmpn($num) >= 0;
}
public function gte(BN $num) {
return $this->cmp($num) >= 0;
}
public function ltn($num) {
return $this->cmpn($num) < 0;
}
public function lt(BN $num) {
return $this->cmp($num) < 0;
}
public function lten($num) {
return $this->cmpn($num) <= 0;
}
public function lte(BN $num) {
return $this->cmp($num) <= 0;
}
public function eqn($num) {
return $this->cmpn($num) === 0;
}
public function eq(BN $num) {
return $this->cmp($num) === 0;
}
public function toRed(Red &$ctx) {
if( $this->red !== null )
throw new Exception("Already a number in reduction context");
if( $this->negative() !== 0 )
throw new Exception("red works only with positives");
return $ctx->convertTo($this)->_forceRed($ctx);
}
public function fromRed() {
if( $this->red === null )
throw new Exception("fromRed works only with numbers in reduction context");
return $this->red->convertFrom($this);
}
public function _forceRed(Red &$ctx) {
$this->red = $ctx;
return $this;
}
public function forceRed(Red &$ctx) {
if( $this->red !== null )
throw new Exception("Already a number in reduction context");
return $this->_forceRed($ctx);
}
public function redAdd(BN $num) {
if( $this->red === null )
throw new Exception("redAdd works only with red numbers");
$res = clone($this);
$res->bi = $res->bi->add($num->bi);
if ($res->bi->cmp($this->red->m->bi) >= 0)
$res->bi = $res->bi->sub($this->red->m->bi);
return $res;
// return $this->red->add($this, $num);
}
public function redIAdd(BN $num) {
if( $this->red === null )
throw new Exception("redIAdd works only with red numbers");
$res = $this;
$res->bi = $res->bi->add($num->bi);
if ($res->bi->cmp($this->red->m->bi) >= 0)
$res->bi = $res->bi->sub($this->red->m->bi);
return $res;
//return $this->red->iadd($this, $num);
}
public function redSub(BN $num) {
if( $this->red === null )
throw new Exception("redSub works only with red numbers");
$res = clone($this);
$res->bi = $this->bi->sub($num->bi);
if ($res->bi->sign() < 0)
$res->bi = $res->bi->add($this->red->m->bi);
return $res;
//return $this->red->sub($this, $num);
}
public function redISub(BN $num) {
if( $this->red === null )
throw new Exception("redISub works only with red numbers");
$this->bi = $this->bi->sub($num->bi);
if ($this->bi->sign() < 0)
$this->bi = $this->bi->add($this->red->m->bi);
return $this;
// return $this->red->isub($this, $num);
}
public function redShl(BN $num) {
if( $this->red === null )
throw new Exception("redShl works only with red numbers");
return $this->red->shl($this, $num);
}
public function redMul(BN $num) {
if( $this->red === null )
throw new Exception("redMul works only with red numbers");
$res = clone($this);
$res->bi = $this->bi->mul($num->bi)->mod($this->red->m->bi);
return $res;
/*
return $this->red->mul($this, $num);
*/
}
public function redIMul(BN $num) {
if( $this->red === null )
throw new Exception("redIMul works only with red numbers");
$this->bi = $this->bi->mul($num->bi)->mod($this->red->m->bi);
return $this;
//return $this->red->imul($this, $num);
}
public function redSqr() {
if( $this->red === null )
throw new Exception("redSqr works only with red numbers");
$res = clone($this);
$res->bi = $this->bi->mul($this->bi)->mod($this->red->m->bi);
return $res;
/*
$this->red->verify1($this);
return $this->red->sqr($this);
*/
}
public function redISqr() {
if( $this->red === null )
throw new Exception("redISqr works only with red numbers");
$res = $this;
$res->bi = $this->bi->mul($this->bi)->mod($this->red->m->bi);
return $res;
/* $this->red->verify1($this);
return $this->red->isqr($this);
*/
}
public function redSqrt() {
if( $this->red === null )
throw new Exception("redSqrt works only with red numbers");
$this->red->verify1($this);
return $this->red->sqrt($this);
}
public function redInvm() {
if( $this->red === null )
throw new Exception("redInvm works only with red numbers");
$this->red->verify1($this);
return $this->red->invm($this);
}
public function redNeg() {
if( $this->red === null )
throw new Exception("redNeg works only with red numbers");
$this->red->verify1($this);
return $this->red->neg($this);
}
public function redPow(BN $num) {
$this->red->verify2($this, $num);
return $this->red->pow($this, $num);
}
public static function red($num) {
return new Red($num);
}
public static function mont($num) {
return new Red($num);
}
public function inspect() {
return ($this->red == null ? "<BN: " : "<BN-R: ") . $this->toString(16) . ">";
}
public function __debugInfo() {
if ($this->red != null) {
return ["BN-R" => $this->toString(16)];
} else {
return ["BN" => $this->toString(16)];
}
}
}
BN::$ASSERT_ENABLED = ini_get("zend.assertions") === "1";
+221
View File
@@ -0,0 +1,221 @@
<?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";
?>
+8
View File
@@ -0,0 +1,8 @@
.vscode
/vendor/
node_modules
composer.lock
.project
.settings
.buildpath
/.idea
+23
View File
@@ -0,0 +1,23 @@
## MIT LICENSE
Copyright (C) 2016 Simplito
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.
+13
View File
@@ -0,0 +1,13 @@
all: test bench coverage
test:
vendor/bin/phpunit --testdox
bench:
vendor/bin/phpbench run --report=simple
coverage:
sudo php5enmod xdebug
vendor/bin/phpunit --coverage-html=coverage
sudo php5dismod xdebug
xdg-open coverage/index.html
+350
View File
@@ -0,0 +1,350 @@
# Fast Elliptic Curve Cryptography in PHP
## Information
This library is a PHP port of [elliptic](https://github.com/indutny/elliptic), a great JavaScript ECC library.
* Supported curve types: Short Weierstrass, Montgomery, Edwards, Twisted Edwards.
* Curve 'presets': `secp256k1`, `p192`, `p224`, `p256`, `p384`, `p521`, `curve25519`, `ed25519`.
This software is licensed under the MIT License.
Projects which use Fast ECC PHP library: [PrivMX WebMail](https://privmx.com), ...
## Benchmarks
```
+------------------------+----------------+--------+-----+------+
| subject | mode | rstdev | its | revs |
+------------------------+----------------+--------+-----+------+
| elliptic#genKeyPair | 323.682ops/s | 2.72% | 5 | 50 |
| mdanter#genKeyPair | 13.794ops/s | 3.18% | 5 | 50 |
+------------------------+----------------+--------+-----+------+
| elliptic#sign | 307.228ops/s | 3.82% | 5 | 50 |
| mdanter#sign | 14.118ops/s | 2.12% | 5 | 50 |
+------------------------+----------------+--------+-----+------+
| elliptic#verify | 93.913ops/s | 5.93% | 5 | 50 |
| mdanter#verify | 6.859ops/s | 2.95% | 5 | 50 |
+------------------------+----------------+--------+-----+------+
| elliptic#dh | 135.166ops/s | 1.67% | 5 | 50 |
| mdanter#dh | 14.302ops/s | 0.89% | 5 | 50 |
+------------------------+----------------+--------+-----+------+
| elliptic#EdDSASign | 296.756ops/s | 1.09% | 5 | 50 |
+------------------------+----------------+--------+-----+------+
| elliptic#EdDSAVerify | 67.481ops/s | 2.76% | 5 | 50 |
+------------------------+----------------+--------+-----+------+
```
## Installation
You can install this library via Composer:
```
composer require simplito/elliptic-php
```
## Implementation details
ECDSA is using deterministic `k` value generation as per [RFC6979][0]. Most of
the curve operations are performed on non-affine coordinates (either projective
or extended), various windowing techniques are used for different cases.
NOTE: `curve25519` could not be used for ECDSA, use `ed25519` instead.
All operations are performed in reduction context using [bn-php][1].
## API
### ECDSA
```php
<?php
use Elliptic\EC;
// Create and initialize EC context
// (better do it once and reuse it)
$ec = new EC('secp256k1');
// Generate keys
$key = $ec->genKeyPair();
// Sign message (can be hex sequence or array)
$msg = 'ab4c3451';
$signature = $key->sign($msg);
// Export DER encoded signature to hex string
$derSign = $signature->toDER('hex');
// Verify signature
echo "Verified: " . (($key->verify($msg, $derSign) == TRUE) ? "true" : "false") . "\n";
// CHECK WITH NO PRIVATE KEY
// Public key as '04 + x + y'
$pub = "049a1eedae838f2f8ad94597dc4368899ecc751342b464862da80c280d841875ab4607fb6ce14100e71dd7648dd6b417c7872a6ff1ff29195dabd99f15eff023e5";
// Signature MUST be either:
// 1) hex-string of DER-encoded signature; or
// 2) DER-encoded signature as byte array; or
// 3) object with two hex-string properties (r and s)
// case 1
$sig = '30450220233f8bab3f5df09e3d02f45914b0b519d2c04d13ac6964495623806a015df1cd022100c0c279c989b79885b3cc0f117643317bc59414bfb581f38e03557b8532f06603';
// case 2
$sig = [48,69,2,32,35,63,139,171,63,93,240,158,61,2,244,89,20,176,181,25,210,192,77,19,172,105,100,73,86,35,128,106,1,93,241,205,2,33,0,192,194,121,201,137,183,152,133,179,204,15,17,118,67,49,123,197,148,20,191,181,129,243,142,3,85,123,133,50,240,102,3];
// case 3
$sig = ['r' => '233f8bab3f5df09e3d02f45914b0b519d2c04d13ac6964495623806a015df1cd', 's' => 'c0c279c989b79885b3cc0f117643317bc59414bfb581f38e03557b8532f06603'];
// Import public key
$key = $ec->keyFromPublic($pub, 'hex');
// Verify signature
echo "Verified: " . (($key->verify($msg, $sig) == TRUE) ? "true" : "false") . "\n";
```
### EdDSA
```php
<?php
use Elliptic\EdDSA;
// Create and initialize EdDSA context
// (better do it once and reuse it)
$ec = new EdDSA('ed25519');
// Create key pair from secret
$key = $ec->keyFromSecret('61233ca4590acd'); // hex string or array of bytes
// Sign message (can be hex sequence or array)
$msg = 'ab4c3451';
$signature = $key->sign($msg)->toHex();
// Verify signature
echo "Verified: " . (($key->verify($msg, $signature) == TRUE) ? "true" : "false") . "\n";
// CHECK WITH NO PRIVATE KEY
// Import public key
$pub = '2763d01c334250d3e2dda459e5e3f949f667c6bbf0a35012c77ad40b00f0374d';
$key = $ec->keyFromPublic($pub, 'hex');
// Verify signature
$signature = '93899915C2919181A3D244AAAC032CE78EF76D2FFC0355D4BE2C70F48202EBC5F2BB0541D236182F55B11AC6346B524150695E5DE1FEA570786E1CC1F7999404';
echo "Verified: " . (($key->verify($msg, $signature) == TRUE) ? "true" : "false") . "\n";
```
### ECDH
```php
<?php
use Elliptic\EC;
$ec = new EC('curve25519');
// Generate keys
$key1 = $ec->genKeyPair();
$key2 = $ec->genKeyPair();
$shared1 = $key1->derive($key2->getPublic());
$shared2 = $key2->derive($key1->getPublic());
echo "Both shared secrets are BN instances\n";
echo $shared1->toString(16) . "\n";
echo $shared2->toString(16) . "\n";
```
NOTE: `.derive()` returns a [BN][1] instance. The resulting hex string is not zero-padded to constant size. Note that when interoperating with other libraries or using the result in a hash function.
### Using EC directly
Use case examples:
#### Computing public key from private
```php
use Elliptic\EC;
$ec = new EC('secp256k1');
$priv_hex = "751ce088f64404e5889bf7e9e5c280b200b2dc158461e96b921df39a1dbc6635";
$pub_hex = "03a319a1d10a91ada9a01ab121b81ae5f14580083a976e74945cdb014a4a52bae6";
$priv = $ec->keyFromPrivate($priv_hex);
if ($pub_hex == $priv->getPublic(true, "hex")) {
echo "Success\n";
} else {
echo "Fail\n";
}
```
#### Verifying Bitcoin Message Signature
```php
use Elliptic\EC;
use StephenHill\Base58;
// see: https://en.bitcoin.it/wiki/List_of_address_prefixes
const MainNetId = "\x00";
const TestNetId = "\x6F";
const PrefixNetIdMap = [ "1" => MainNetId, "m" => TestNetId ];
function pubKeyAddress($pubkey, $netid = MainNetId) {
$b58 = new Base58();
$pubenc = hex2bin($pubkey->encode("hex", true));
$pubhash = $netid . hash('ripemd160', hash('sha256', $pubenc, true), true);
$checksum = substr( hash('sha256', hash('sha256', $pubhash, true), true), 0, 4);
return $b58->encode($pubhash . $checksum);
}
function verifySignature($message, $signature, $address) {
$signbin = base64_decode($signature);
$signarr = [ "r" => bin2hex(substr($signbin, 1, 32)),
"s" => bin2hex(substr($signbin, 33, 32)) ];
$nv = ord(substr($signbin, 0, 1)) - 27;
if ($nv != ($nv & 7))
return false;
$recid = ($nv & 3);
$compressed = ($nv & 4) != 0;
$msglen = strlen($message);
$hash = hash('sha256', hash('sha256', "\x18Bitcoin Signed Message:\n" . chr($msglen) . $message, true));
$ec = new EC('secp256k1');
$pub = $ec->recoverPubKey($hash, $signarr, $recid);
$result = pubKeyAddress($pub, PrefixNetIdMap[$address[0]]);
return $result == $address;
}
$message = "I like signatures";
$signature = "H/zugYITIQTk8ZFWeXkbGCV2MzvMtbh+CnKBctbM9tP2UCb1B4LdyWFQuTZKxLdIDgP8Vsvl+0AEkBlY1HoyVw8=";
$address = "mxQadqtYQXYeUsSqdMdJxZwkzxbd2tuMdc";
if (verifySignature($message, $signature, $address)) {
echo "Success\n";
} else {
echo "Fail\n";
}
```
#### Verifying Ethereum Signature
```php
use Elliptic\EC;
use kornrunner\Keccak;
function pubKeyToAddress($pubkey) {
return "0x" . substr(Keccak::hash(substr(hex2bin($pubkey->encode("hex")), 1), 256), 24);
}
function verifySignature($message, $signature, $address) {
$msglen = strlen($message);
$hash = Keccak::hash("\x19Ethereum Signed Message:\n{$msglen}{$message}", 256);
$sign = ["r" => substr($signature, 2, 64),
"s" => substr($signature, 66, 64)];
$recid = ord(hex2bin(substr($signature, 130, 2))) - 27;
if ($recid != ($recid & 1))
return false;
$ec = new EC('secp256k1');
$pubkey = $ec->recoverPubKey($hash, $sign, $recid);
return $address == pubKeyToAddress($pubkey);
}
$address = "0x5a214a45585b336a776b62a3a61dbafd39f9fa2a";
$message = "I like signatures";
// signature returned by eth.sign(address, message)
$signature = "0xacb175089543ac060ed48c3e25ada5ffeed6f008da9eaca3806e4acb707b9481401409ae1f5f9f290f54f29684e7bac1d79b2964e0edcb7f083bacd5fc48882e1b";
if (verifySignature($message, $signature, $address)) {
echo "Success\n";
} else {
echo "Fail\n";
}
```
#### ECDH (secret based, base58 format)
For usage in ed25519 oriented platforms like e.g. BigChainDB who use base58 encoded public / private keys.
```php
use Elliptic\EdDSA;
use StephenHill\Base58;
$mnemonic = "scheme spot photo card baby mountain device kick cradle pact join borrow";
$secret = hash_pbkdf2('sha512', $mnemonic, 'mnemonic', 2048);
$ec = new EdDSA('ed25519');
$kp = $ec->keyFromSecret($secret);
assert($secret == $kp->getSecret('hex'));
echo "Secret: " . $kp->getSecret('hex') . PHP_EOL;
echo "Private: " . $kp->priv()->toString('hex') . PHP_EOL;
echo "Public: " . $kp->getPublic('hex') . PHP_EOL;
$b58 = new Base58();
echo PHP_EOL;
echo "B58 Private: " . $b58->encode(hex2bin($kp->priv()->toString('hex'))) . PHP_EOL;
echo "B58 Public: " . $b58->encode(hex2bin($kp->getPublic('hex'))) . PHP_EOL;
```
#### BIP32 Public Parent Key -> Public Child Key derivation example
```php
<?php
use Elliptic\EC;
use BN\BN;
$ec = new EC('secp256k1');
// See: http://bip32.org using Derive From BIP32 Key
// xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8
$c_par = "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508";
$K_par = "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2";
// Derived public child key
// Derivation path Simple: m/i
// Keypair index i: 2018
// xpub68Gmy5EVb2Begkah8BxugKchT5SExW5p9gEHBLnEvYSuwVppt2TzD3WTjxNk14R8pmHbz3MHB9n75M2zNYgkJUCwV9pYwU9Z21Awj7Cr5U9
$expected_c_child = "a7470737ffde1458292e19e838534f400ad3c0f72e12f08eff79dee4fce11bed";
$expected_K_child = "0376499d06f9e9df71d7ee08d13a91337fa2b92182d4afcddf917b8d9983eb4615";
$i = 2018;
$I_key = hex2bin($c_par);
$I_data = hex2bin($K_par) . pack("N", $i);
$I = hash_hmac("sha512", $I_data, $I_key);
$I_L = substr($I, 0, 64);
$I_R = substr($I, 64, 64);
$c_i = $I_R;
$K_par_point = $ec->curve->decodePoint($K_par, "hex");
$I_L_point = $ec->g->mul(new BN($I_L, 16));
$K_i = $K_par_point->add($I_L_point);
$K_i = $K_i->encodeCompressed("hex");
if ($expected_c_child == $c_i && $expected_K_child == $K_i) {
echo "Success!\n";
} else {
echo "Failure!\n";
}
```
[0]: http://tools.ietf.org/html/rfc6979
[1]: https://github.com/simplito/bn-php
@@ -0,0 +1,64 @@
<?php
require __DIR__ . "/../vendor/autoload.php";
/**
* @BeforeMethods({"init"})
*
* @Iterations(5)
* @Revs(50)
* @OutputTimeUnit("seconds")
* @OutputMode("throughput")
*/
class EllipticBench {
private $ec;
private $keys;
private $hash;
static $msg = [ 0xB, 0xE, 0xE, 0xF ];
public function init() {
$this->ec = new \Elliptic\EC('secp256k1');
$this->priv = $this->ec->genKeyPair();
$this->pub = $this->priv->getPublic();
$this->hash = hash('sha256', 'hello world');
$this->sign = $this->priv->sign($this->hash);
$this->priv2 = $this->ec->genKeyPair();
$this->pub2 = $this->priv2->getPublic();
$this->ed25519 = new \Elliptic\EdDSA('ed25519');
$secret = array_fill(0, 32, 0);
$this->edkey = $this->ed25519->keyFromSecret($secret);
$this->edsig = $this->edkey->sign(self::$msg);
}
public function benchGenKeyPair() {
$this->ec->genKeyPair();
}
public function benchGenKeyPairWithPublicKey() {
$priv = $this->ec->genKeyPair();
$pub = $priv->getPublic();
}
public function benchSign() {
$this->priv->sign($this->hash);
}
public function benchVerify() {
if ( !$this->ec->verify($this->hash, $this->sign, $this->pub) )
throw new \Exception("unexpected");
}
public function benchDH() {
$this->priv->derive($this->pub2);
}
public function benchEdDSASign() {
$this->edkey->sign(self::$msg);
}
public function benchEdDSAVerify() {
if ( !$this->edkey->verify(self::$msg, $this->edsig) )
throw new \Exception("unexpected");
}
}
+34
View File
@@ -0,0 +1,34 @@
{
"name": "simplito/elliptic-php",
"description": "Fast elliptic curve cryptography",
"type": "library",
"homepage": "https://github.com/simplito/elliptic-php",
"keywords": ["elliptic", "curve", "cryptography", "ECC",
"ECDH", "ECDSA", "EdDSA",
"secp256k1", "curve25519", "curve25519-weier", "ed25519",
"nistp192", "nistp224", "nistp256", "nistp384", "nistp521"],
"license": "MIT",
"authors": [
{
"name": "Simplito Team",
"email": "s.smyczynski@simplito.com",
"homepage": "https://simplito.com"
}
],
"require": {
"ext-gmp": "*",
"simplito/bn-php": "~1.1.0"
},
"require-dev": {
"phpunit/phpunit": "*",
"phpbench/phpbench": "@dev"
},
"autoload": {
"psr-4": {
"Elliptic\\": "lib/"
}
},
"scripts": {
"test": "phpunit --verbose"
}
}
+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);
}
}
?>
+990
View File
@@ -0,0 +1,990 @@
<?php
namespace Elliptic;
use Elliptic\Curve\PresetCurve;
class Curves
{
private static $curves;
public static function hasCurve($name) {
return isset(self::$curves[$name]);
}
public static function getCurve($name) {
if (!isset(self::$curves[$name])) {
throw new \Exception('Unknown curve ' . $name);
}
return self::$curves[$name];
}
public static function defineCurve($name, $options)
{
self::$curves[$name] = new PresetCurve($options);
}
}
$sha256 = [ "blockSize" => 512, "outSize" => 256, "hmacStrength" => 192, "padLength" => 64, "algo" => 'sha256' ];
$sha224 = [ "blockSize" => 512, "outSize" => 224, "hmacStrength" => 192, "padLength" => 64, "algo" => 'sha224' ];
$sha512 = [ "blockSize" => 1024, "outSize" => 512, "hmacStrength" => 192, "padLength" => 128, "algo" => 'sha512' ];
$sha384 = [ "blockSize" => 1024, "outSize" => 384, "hmacStrength" => 192, "padLength" => 128, "algo" => 'sha384' ];
$sha1 = [ "blockSize" => 512, "outSize" => 160, "hmacStrength" => 80, "padLength" => 64, "algo" => 'sha1' ];
Curves::defineCurve("p192", array(
"type" => "short",
"prime" => "p192",
"p" => "ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff",
"a" => "ffffffff ffffffff ffffffff fffffffe ffffffff fffffffc",
"b" => "64210519 e59c80e7 0fa7e9ab 72243049 feb8deec c146b9b1",
"n" => "ffffffff ffffffff ffffffff 99def836 146bc9b1 b4d22831",
"hash" => $sha256,
"gRed" => false,
"g" => array(
"188da80e b03090f6 7cbf20eb 43a18800 f4ff0afd 82ff1012",
"07192b95 ffc8da78 631011ed 6b24cdd5 73f977a1 1e794811"
)
));
Curves::defineCurve("p224", array(
"type" => "short",
"prime" => "p224",
"p" => "ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001",
"a" => "ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff fffffffe",
"b" => "b4050a85 0c04b3ab f5413256 5044b0b7 d7bfd8ba 270b3943 2355ffb4",
"n" => "ffffffff ffffffff ffffffff ffff16a2 e0b8f03e 13dd2945 5c5c2a3d",
"hash" => $sha256,
"gRed" => false,
"g" => array(
"b70e0cbd 6bb4bf7f 321390b9 4a03c1d3 56c21122 343280d6 115c1d21",
"bd376388 b5f723fb 4c22dfe6 cd4375a0 5a074764 44d58199 85007e34"
)
));
Curves::defineCurve("p256", array(
"type" => "short",
"prime" => null,
"p" => "ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff ffffffff",
"a" => "ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff fffffffc",
"b" => "5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f6 3bce3c3e 27d2604b",
"n" => "ffffffff 00000000 ffffffff ffffffff bce6faad a7179e84 f3b9cac2 fc632551",
"hash" => $sha256,
"gRed" => false,
"g" => array(
"6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0 f4a13945 d898c296",
"4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece cbb64068 37bf51f5"
)
));
Curves::defineCurve("p384", array(
"type" => "short",
"prime" => null,
"p" => "ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff " .
"fffffffe ffffffff 00000000 00000000 ffffffff",
"a" => "ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff " .
"fffffffe ffffffff 00000000 00000000 fffffffc",
"b" => "b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112 0314088f " .
"5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef",
"n" => "ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff c7634d81 " .
"f4372ddf 581a0db2 48b0a77a ecec196a ccc52973",
"hash" => $sha384,
"gRed" => false,
"g" => array(
"aa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98 59f741e0 82542a38 " .
"5502f25d bf55296c 3a545e38 72760ab7",
"3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c e9da3113 b5f0b8c0 " .
"0a60b1ce 1d7e819d 7a431d7c 90ea0e5f"
)
));
Curves::defineCurve("p521", array(
"type" => "short",
"prime" => null,
"p" => "000001ff ffffffff ffffffff ffffffff ffffffff ffffffff " .
"ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff " .
"ffffffff ffffffff ffffffff ffffffff ffffffff",
"a" => "000001ff ffffffff ffffffff ffffffff ffffffff ffffffff " .
"ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff " .
"ffffffff ffffffff ffffffff ffffffff fffffffc",
"b" => "00000051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b " .
"99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd " .
"3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00",
"n" => "000001ff ffffffff ffffffff ffffffff ffffffff ffffffff " .
"ffffffff ffffffff fffffffa 51868783 bf2f966b 7fcc0148 " .
"f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409",
"hash" => $sha512,
"gRed" => false,
"g" => array(
"000000c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139 " .
"053fb521 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127 " .
"a2ffa8de 3348b3c1 856a429b f97e7e31 c2e5bd66",
"00000118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449 " .
"579b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901 " .
"3fad0761 353c7086 a272c240 88be9476 9fd16650"
)
));
Curves::defineCurve("curve25519", array(
"type" => "mont",
"prime" => "p25519",
"p" => "7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed",
"a" => "76d06",
"b" => "0",
"n" => "1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed",
"hash" => $sha256,
"gRed" => false,
"g" => array(
"9"
)
));
Curves:: defineCurve("curve25519-weier", array(
"type" => "short",
"prime" => "p25519",
"p" => "7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed",
"a" => "2aaaaaaaaaaaaaaa aaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaa aaaaaa984914a144",
"b" => "7b425ed097b425ed 097b425ed097b425 ed097b425ed097b4 260b5e9c7710c864",
"n" => "1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed",
"hash" => $sha256,
"gRed" => false,
"g" => array(
"2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad245a",
"20ae19a1b8a086b4e01edd2c7748d14c923d4d7e6d7c61b229e9c5a27eced3d9"
)
));
Curves::defineCurve("ed25519", array(
"type" => "edwards",
"prime" => "p25519",
"p" => "7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed",
"a" => "-1",
"c" => "1",
// -121665 * (121666^(-1)) (mod P)
"d" => "52036cee2b6ffe73 8cc740797779e898 00700a4d4141d8ab 75eb4dca135978a3",
"n" => "1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed",
"hash" => $sha256,
"gRed" => false,
"g" => array(
"216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a",
// 4/5
"6666666666666666666666666666666666666666666666666666666666666658"
)
));
$pre = array(
"doubles" => array(
"step" => 4,
"points" => array(
array(
"e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a",
"f7e3507399e595929db99f34f57937101296891e44d23f0be1f32cce69616821"
),
array(
"8282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508",
"11f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf"
),
array(
"175e159f728b865a72f99cc6c6fc846de0b93833fd2222ed73fce5b551e5b739",
"d3506e0d9e3c79eba4ef97a51ff71f5eacb5955add24345c6efa6ffee9fed695"
),
array(
"363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640",
"4e273adfc732221953b445397f3363145b9a89008199ecb62003c7f3bee9de9"
),
array(
"8b4b5f165df3c2be8c6244b5b745638843e4a781a15bcd1b69f79a55dffdf80c",
"4aad0a6f68d308b4b3fbd7813ab0da04f9e336546162ee56b3eff0c65fd4fd36"
),
array(
"723cbaa6e5db996d6bf771c00bd548c7b700dbffa6c0e77bcb6115925232fcda",
"96e867b5595cc498a921137488824d6e2660a0653779494801dc069d9eb39f5f"
),
array(
"eebfa4d493bebf98ba5feec812c2d3b50947961237a919839a533eca0e7dd7fa",
"5d9a8ca3970ef0f269ee7edaf178089d9ae4cdc3a711f712ddfd4fdae1de8999"
),
array(
"100f44da696e71672791d0a09b7bde459f1215a29b3c03bfefd7835b39a48db0",
"cdd9e13192a00b772ec8f3300c090666b7ff4a18ff5195ac0fbd5cd62bc65a09"
),
array(
"e1031be262c7ed1b1dc9227a4a04c017a77f8d4464f3b3852c8acde6e534fd2d",
"9d7061928940405e6bb6a4176597535af292dd419e1ced79a44f18f29456a00d"
),
array(
"feea6cae46d55b530ac2839f143bd7ec5cf8b266a41d6af52d5e688d9094696d",
"e57c6b6c97dce1bab06e4e12bf3ecd5c981c8957cc41442d3155debf18090088"
),
array(
"da67a91d91049cdcb367be4be6ffca3cfeed657d808583de33fa978bc1ec6cb1",
"9bacaa35481642bc41f463f7ec9780e5dec7adc508f740a17e9ea8e27a68be1d"
),
array(
"53904faa0b334cdda6e000935ef22151ec08d0f7bb11069f57545ccc1a37b7c0",
"5bc087d0bc80106d88c9eccac20d3c1c13999981e14434699dcb096b022771c8"
),
array(
"8e7bcd0bd35983a7719cca7764ca906779b53a043a9b8bcaeff959f43ad86047",
"10b7770b2a3da4b3940310420ca9514579e88e2e47fd68b3ea10047e8460372a"
),
array(
"385eed34c1cdff21e6d0818689b81bde71a7f4f18397e6690a841e1599c43862",
"283bebc3e8ea23f56701de19e9ebf4576b304eec2086dc8cc0458fe5542e5453"
),
array(
"6f9d9b803ecf191637c73a4413dfa180fddf84a5947fbc9c606ed86c3fac3a7",
"7c80c68e603059ba69b8e2a30e45c4d47ea4dd2f5c281002d86890603a842160"
),
array(
"3322d401243c4e2582a2147c104d6ecbf774d163db0f5e5313b7e0e742d0e6bd",
"56e70797e9664ef5bfb019bc4ddaf9b72805f63ea2873af624f3a2e96c28b2a0"
),
array(
"85672c7d2de0b7da2bd1770d89665868741b3f9af7643397721d74d28134ab83",
"7c481b9b5b43b2eb6374049bfa62c2e5e77f17fcc5298f44c8e3094f790313a6"
),
array(
"948bf809b1988a46b06c9f1919413b10f9226c60f668832ffd959af60c82a0a",
"53a562856dcb6646dc6b74c5d1c3418c6d4dff08c97cd2bed4cb7f88d8c8e589"
),
array(
"6260ce7f461801c34f067ce0f02873a8f1b0e44dfc69752accecd819f38fd8e8",
"bc2da82b6fa5b571a7f09049776a1ef7ecd292238051c198c1a84e95b2b4ae17"
),
array(
"e5037de0afc1d8d43d8348414bbf4103043ec8f575bfdc432953cc8d2037fa2d",
"4571534baa94d3b5f9f98d09fb990bddbd5f5b03ec481f10e0e5dc841d755bda"
),
array(
"e06372b0f4a207adf5ea905e8f1771b4e7e8dbd1c6a6c5b725866a0ae4fce725",
"7a908974bce18cfe12a27bb2ad5a488cd7484a7787104870b27034f94eee31dd"
),
array(
"213c7a715cd5d45358d0bbf9dc0ce02204b10bdde2a3f58540ad6908d0559754",
"4b6dad0b5ae462507013ad06245ba190bb4850f5f36a7eeddff2c27534b458f2"
),
array(
"4e7c272a7af4b34e8dbb9352a5419a87e2838c70adc62cddf0cc3a3b08fbd53c",
"17749c766c9d0b18e16fd09f6def681b530b9614bff7dd33e0b3941817dcaae6"
),
array(
"fea74e3dbe778b1b10f238ad61686aa5c76e3db2be43057632427e2840fb27b6",
"6e0568db9b0b13297cf674deccb6af93126b596b973f7b77701d3db7f23cb96f"
),
array(
"76e64113f677cf0e10a2570d599968d31544e179b760432952c02a4417bdde39",
"c90ddf8dee4e95cf577066d70681f0d35e2a33d2b56d2032b4b1752d1901ac01"
),
array(
"c738c56b03b2abe1e8281baa743f8f9a8f7cc643df26cbee3ab150242bcbb891",
"893fb578951ad2537f718f2eacbfbbbb82314eef7880cfe917e735d9699a84c3"
),
array(
"d895626548b65b81e264c7637c972877d1d72e5f3a925014372e9f6588f6c14b",
"febfaa38f2bc7eae728ec60818c340eb03428d632bb067e179363ed75d7d991f"
),
array(
"b8da94032a957518eb0f6433571e8761ceffc73693e84edd49150a564f676e03",
"2804dfa44805a1e4d7c99cc9762808b092cc584d95ff3b511488e4e74efdf6e7"
),
array(
"e80fea14441fb33a7d8adab9475d7fab2019effb5156a792f1a11778e3c0df5d",
"eed1de7f638e00771e89768ca3ca94472d155e80af322ea9fcb4291b6ac9ec78"
),
array(
"a301697bdfcd704313ba48e51d567543f2a182031efd6915ddc07bbcc4e16070",
"7370f91cfb67e4f5081809fa25d40f9b1735dbf7c0a11a130c0d1a041e177ea1"
),
array(
"90ad85b389d6b936463f9d0512678de208cc330b11307fffab7ac63e3fb04ed4",
"e507a3620a38261affdcbd9427222b839aefabe1582894d991d4d48cb6ef150"
),
array(
"8f68b9d2f63b5f339239c1ad981f162ee88c5678723ea3351b7b444c9ec4c0da",
"662a9f2dba063986de1d90c2b6be215dbbea2cfe95510bfdf23cbf79501fff82"
),
array(
"e4f3fb0176af85d65ff99ff9198c36091f48e86503681e3e6686fd5053231e11",
"1e63633ad0ef4f1c1661a6d0ea02b7286cc7e74ec951d1c9822c38576feb73bc"
),
array(
"8c00fa9b18ebf331eb961537a45a4266c7034f2f0d4e1d0716fb6eae20eae29e",
"efa47267fea521a1a9dc343a3736c974c2fadafa81e36c54e7d2a4c66702414b"
),
array(
"e7a26ce69dd4829f3e10cec0a9e98ed3143d084f308b92c0997fddfc60cb3e41",
"2a758e300fa7984b471b006a1aafbb18d0a6b2c0420e83e20e8a9421cf2cfd51"
),
array(
"b6459e0ee3662ec8d23540c223bcbdc571cbcb967d79424f3cf29eb3de6b80ef",
"67c876d06f3e06de1dadf16e5661db3c4b3ae6d48e35b2ff30bf0b61a71ba45"
),
array(
"d68a80c8280bb840793234aa118f06231d6f1fc67e73c5a5deda0f5b496943e8",
"db8ba9fff4b586d00c4b1f9177b0e28b5b0e7b8f7845295a294c84266b133120"
),
array(
"324aed7df65c804252dc0270907a30b09612aeb973449cea4095980fc28d3d5d",
"648a365774b61f2ff130c0c35aec1f4f19213b0c7e332843967224af96ab7c84"
),
array(
"4df9c14919cde61f6d51dfdbe5fee5dceec4143ba8d1ca888e8bd373fd054c96",
"35ec51092d8728050974c23a1d85d4b5d506cdc288490192ebac06cad10d5d"
),
array(
"9c3919a84a474870faed8a9c1cc66021523489054d7f0308cbfc99c8ac1f98cd",
"ddb84f0f4a4ddd57584f044bf260e641905326f76c64c8e6be7e5e03d4fc599d"
),
array(
"6057170b1dd12fdf8de05f281d8e06bb91e1493a8b91d4cc5a21382120a959e5",
"9a1af0b26a6a4807add9a2daf71df262465152bc3ee24c65e899be932385a2a8"
),
array(
"a576df8e23a08411421439a4518da31880cef0fba7d4df12b1a6973eecb94266",
"40a6bf20e76640b2c92b97afe58cd82c432e10a7f514d9f3ee8be11ae1b28ec8"
),
array(
"7778a78c28dec3e30a05fe9629de8c38bb30d1f5cf9a3a208f763889be58ad71",
"34626d9ab5a5b22ff7098e12f2ff580087b38411ff24ac563b513fc1fd9f43ac"
),
array(
"928955ee637a84463729fd30e7afd2ed5f96274e5ad7e5cb09eda9c06d903ac",
"c25621003d3f42a827b78a13093a95eeac3d26efa8a8d83fc5180e935bcd091f"
),
array(
"85d0fef3ec6db109399064f3a0e3b2855645b4a907ad354527aae75163d82751",
"1f03648413a38c0be29d496e582cf5663e8751e96877331582c237a24eb1f962"
),
array(
"ff2b0dce97eece97c1c9b6041798b85dfdfb6d8882da20308f5404824526087e",
"493d13fef524ba188af4c4dc54d07936c7b7ed6fb90e2ceb2c951e01f0c29907"
),
array(
"827fbbe4b1e880ea9ed2b2e6301b212b57f1ee148cd6dd28780e5e2cf856e241",
"c60f9c923c727b0b71bef2c67d1d12687ff7a63186903166d605b68baec293ec"
),
array(
"eaa649f21f51bdbae7be4ae34ce6e5217a58fdce7f47f9aa7f3b58fa2120e2b3",
"be3279ed5bbbb03ac69a80f89879aa5a01a6b965f13f7e59d47a5305ba5ad93d"
),
array(
"e4a42d43c5cf169d9391df6decf42ee541b6d8f0c9a137401e23632dda34d24f",
"4d9f92e716d1c73526fc99ccfb8ad34ce886eedfa8d8e4f13a7f7131deba9414"
),
array(
"1ec80fef360cbdd954160fadab352b6b92b53576a88fea4947173b9d4300bf19",
"aeefe93756b5340d2f3a4958a7abbf5e0146e77f6295a07b671cdc1cc107cefd"
),
array(
"146a778c04670c2f91b00af4680dfa8bce3490717d58ba889ddb5928366642be",
"b318e0ec3354028add669827f9d4b2870aaa971d2f7e5ed1d0b297483d83efd0"
),
array(
"fa50c0f61d22e5f07e3acebb1aa07b128d0012209a28b9776d76a8793180eef9",
"6b84c6922397eba9b72cd2872281a68a5e683293a57a213b38cd8d7d3f4f2811"
),
array(
"da1d61d0ca721a11b1a5bf6b7d88e8421a288ab5d5bba5220e53d32b5f067ec2",
"8157f55a7c99306c79c0766161c91e2966a73899d279b48a655fba0f1ad836f1"
),
array(
"a8e282ff0c9706907215ff98e8fd416615311de0446f1e062a73b0610d064e13",
"7f97355b8db81c09abfb7f3c5b2515888b679a3e50dd6bd6cef7c73111f4cc0c"
),
array(
"174a53b9c9a285872d39e56e6913cab15d59b1fa512508c022f382de8319497c",
"ccc9dc37abfc9c1657b4155f2c47f9e6646b3a1d8cb9854383da13ac079afa73"
),
array(
"959396981943785c3d3e57edf5018cdbe039e730e4918b3d884fdff09475b7ba",
"2e7e552888c331dd8ba0386a4b9cd6849c653f64c8709385e9b8abf87524f2fd"
),
array(
"d2a63a50ae401e56d645a1153b109a8fcca0a43d561fba2dbb51340c9d82b151",
"e82d86fb6443fcb7565aee58b2948220a70f750af484ca52d4142174dcf89405"
),
array(
"64587e2335471eb890ee7896d7cfdc866bacbdbd3839317b3436f9b45617e073",
"d99fcdd5bf6902e2ae96dd6447c299a185b90a39133aeab358299e5e9faf6589"
),
array(
"8481bde0e4e4d885b3a546d3e549de042f0aa6cea250e7fd358d6c86dd45e458",
"38ee7b8cba5404dd84a25bf39cecb2ca900a79c42b262e556d64b1b59779057e"
),
array(
"13464a57a78102aa62b6979ae817f4637ffcfed3c4b1ce30bcd6303f6caf666b",
"69be159004614580ef7e433453ccb0ca48f300a81d0942e13f495a907f6ecc27"
),
array(
"bc4a9df5b713fe2e9aef430bcc1dc97a0cd9ccede2f28588cada3a0d2d83f366",
"d3a81ca6e785c06383937adf4b798caa6e8a9fbfa547b16d758d666581f33c1"
),
array(
"8c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa",
"40a30463a3305193378fedf31f7cc0eb7ae784f0451cb9459e71dc73cbef9482"
),
array(
"8ea9666139527a8c1dd94ce4f071fd23c8b350c5a4bb33748c4ba111faccae0",
"620efabbc8ee2782e24e7c0cfb95c5d735b783be9cf0f8e955af34a30e62b945"
),
array(
"dd3625faef5ba06074669716bbd3788d89bdde815959968092f76cc4eb9a9787",
"7a188fa3520e30d461da2501045731ca941461982883395937f68d00c644a573"
),
array(
"f710d79d9eb962297e4f6232b40e8f7feb2bc63814614d692c12de752408221e",
"ea98e67232d3b3295d3b535532115ccac8612c721851617526ae47a9c77bfc82"
)
)
),
"naf" => array(
"wnd" => 7,
"points" => array(
array(
"f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9",
"388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672"
),
array(
"2f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4",
"d8ac222636e5e3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6"
),
array(
"5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc",
"6aebca40ba255960a3178d6d861a54dba813d0b813fde7b5a5082628087264da"
),
array(
"acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe",
"cc338921b0a7d9fd64380971763b61e9add888a4375f8e0f05cc262ac64f9c37"
),
array(
"774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb",
"d984a032eb6b5e190243dd56d7b7b365372db1e2dff9d6a8301d74c9c953c61b"
),
array(
"f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8",
"ab0902e8d880a89758212eb65cdaf473a1a06da521fa91f29b5cb52db03ed81"
),
array(
"d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e",
"581e2872a86c72a683842ec228cc6defea40af2bd896d3a5c504dc9ff6a26b58"
),
array(
"defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34",
"4211ab0694635168e997b0ead2a93daeced1f4a04a95c0f6cfb199f69e56eb77"
),
array(
"2b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6c",
"85e89bc037945d93b343083b5a1c86131a01f60c50269763b570c854e5c09b7a"
),
array(
"352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5",
"321eb4075348f534d59c18259dda3e1f4a1b3b2e71b1039c67bd3d8bcf81998c"
),
array(
"2fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f",
"2de1068295dd865b64569335bd5dd80181d70ecfc882648423ba76b532b7d67"
),
array(
"9248279b09b4d68dab21a9b066edda83263c3d84e09572e269ca0cd7f5453714",
"73016f7bf234aade5d1aa71bdea2b1ff3fc0de2a887912ffe54a32ce97cb3402"
),
array(
"daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729",
"a69dce4a7d6c98e8d4a1aca87ef8d7003f83c230f3afa726ab40e52290be1c55"
),
array(
"c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db",
"2119a460ce326cdc76c45926c982fdac0e106e861edf61c5a039063f0e0e6482"
),
array(
"6a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4",
"e022cf42c2bd4a708b3f5126f16a24ad8b33ba48d0423b6efd5e6348100d8a82"
),
array(
"1697ffa6fd9de627c077e3d2fe541084ce13300b0bec1146f95ae57f0d0bd6a5",
"b9c398f186806f5d27561506e4557433a2cf15009e498ae7adee9d63d01b2396"
),
array(
"605bdb019981718b986d0f07e834cb0d9deb8360ffb7f61df982345ef27a7479",
"2972d2de4f8d20681a78d93ec96fe23c26bfae84fb14db43b01e1e9056b8c49"
),
array(
"62d14dab4150bf497402fdc45a215e10dcb01c354959b10cfe31c7e9d87ff33d",
"80fc06bd8cc5b01098088a1950eed0db01aa132967ab472235f5642483b25eaf"
),
array(
"80c60ad0040f27dade5b4b06c408e56b2c50e9f56b9b8b425e555c2f86308b6f",
"1c38303f1cc5c30f26e66bad7fe72f70a65eed4cbe7024eb1aa01f56430bd57a"
),
array(
"7a9375ad6167ad54aa74c6348cc54d344cc5dc9487d847049d5eabb0fa03c8fb",
"d0e3fa9eca8726909559e0d79269046bdc59ea10c70ce2b02d499ec224dc7f7"
),
array(
"d528ecd9b696b54c907a9ed045447a79bb408ec39b68df504bb51f459bc3ffc9",
"eecf41253136e5f99966f21881fd656ebc4345405c520dbc063465b521409933"
),
array(
"49370a4b5f43412ea25f514e8ecdad05266115e4a7ecb1387231808f8b45963",
"758f3f41afd6ed428b3081b0512fd62a54c3f3afbb5b6764b653052a12949c9a"
),
array(
"77f230936ee88cbbd73df930d64702ef881d811e0e1498e2f1c13eb1fc345d74",
"958ef42a7886b6400a08266e9ba1b37896c95330d97077cbbe8eb3c7671c60d6"
),
array(
"f2dac991cc4ce4b9ea44887e5c7c0bce58c80074ab9d4dbaeb28531b7739f530",
"e0dedc9b3b2f8dad4da1f32dec2531df9eb5fbeb0598e4fd1a117dba703a3c37"
),
array(
"463b3d9f662621fb1b4be8fbbe2520125a216cdfc9dae3debcba4850c690d45b",
"5ed430d78c296c3543114306dd8622d7c622e27c970a1de31cb377b01af7307e"
),
array(
"f16f804244e46e2a09232d4aff3b59976b98fac14328a2d1a32496b49998f247",
"cedabd9b82203f7e13d206fcdf4e33d92a6c53c26e5cce26d6579962c4e31df6"
),
array(
"caf754272dc84563b0352b7a14311af55d245315ace27c65369e15f7151d41d1",
"cb474660ef35f5f2a41b643fa5e460575f4fa9b7962232a5c32f908318a04476"
),
array(
"2600ca4b282cb986f85d0f1709979d8b44a09c07cb86d7c124497bc86f082120",
"4119b88753c15bd6a693b03fcddbb45d5ac6be74ab5f0ef44b0be9475a7e4b40"
),
array(
"7635ca72d7e8432c338ec53cd12220bc01c48685e24f7dc8c602a7746998e435",
"91b649609489d613d1d5e590f78e6d74ecfc061d57048bad9e76f302c5b9c61"
),
array(
"754e3239f325570cdbbf4a87deee8a66b7f2b33479d468fbc1a50743bf56cc18",
"673fb86e5bda30fb3cd0ed304ea49a023ee33d0197a695d0c5d98093c536683"
),
array(
"e3e6bd1071a1e96aff57859c82d570f0330800661d1c952f9fe2694691d9b9e8",
"59c9e0bba394e76f40c0aa58379a3cb6a5a2283993e90c4167002af4920e37f5"
),
array(
"186b483d056a033826ae73d88f732985c4ccb1f32ba35f4b4cc47fdcf04aa6eb",
"3b952d32c67cf77e2e17446e204180ab21fb8090895138b4a4a797f86e80888b"
),
array(
"df9d70a6b9876ce544c98561f4be4f725442e6d2b737d9c91a8321724ce0963f",
"55eb2dafd84d6ccd5f862b785dc39d4ab157222720ef9da217b8c45cf2ba2417"
),
array(
"5edd5cc23c51e87a497ca815d5dce0f8ab52554f849ed8995de64c5f34ce7143",
"efae9c8dbc14130661e8cec030c89ad0c13c66c0d17a2905cdc706ab7399a868"
),
array(
"290798c2b6476830da12fe02287e9e777aa3fba1c355b17a722d362f84614fba",
"e38da76dcd440621988d00bcf79af25d5b29c094db2a23146d003afd41943e7a"
),
array(
"af3c423a95d9f5b3054754efa150ac39cd29552fe360257362dfdecef4053b45",
"f98a3fd831eb2b749a93b0e6f35cfb40c8cd5aa667a15581bc2feded498fd9c6"
),
array(
"766dbb24d134e745cccaa28c99bf274906bb66b26dcf98df8d2fed50d884249a",
"744b1152eacbe5e38dcc887980da38b897584a65fa06cedd2c924f97cbac5996"
),
array(
"59dbf46f8c94759ba21277c33784f41645f7b44f6c596a58ce92e666191abe3e",
"c534ad44175fbc300f4ea6ce648309a042ce739a7919798cd85e216c4a307f6e"
),
array(
"f13ada95103c4537305e691e74e9a4a8dd647e711a95e73cb62dc6018cfd87b8",
"e13817b44ee14de663bf4bc808341f326949e21a6a75c2570778419bdaf5733d"
),
array(
"7754b4fa0e8aced06d4167a2c59cca4cda1869c06ebadfb6488550015a88522c",
"30e93e864e669d82224b967c3020b8fa8d1e4e350b6cbcc537a48b57841163a2"
),
array(
"948dcadf5990e048aa3874d46abef9d701858f95de8041d2a6828c99e2262519",
"e491a42537f6e597d5d28a3224b1bc25df9154efbd2ef1d2cbba2cae5347d57e"
),
array(
"7962414450c76c1689c7b48f8202ec37fb224cf5ac0bfa1570328a8a3d7c77ab",
"100b610ec4ffb4760d5c1fc133ef6f6b12507a051f04ac5760afa5b29db83437"
),
array(
"3514087834964b54b15b160644d915485a16977225b8847bb0dd085137ec47ca",
"ef0afbb2056205448e1652c48e8127fc6039e77c15c2378b7e7d15a0de293311"
),
array(
"d3cc30ad6b483e4bc79ce2c9dd8bc54993e947eb8df787b442943d3f7b527eaf",
"8b378a22d827278d89c5e9be8f9508ae3c2ad46290358630afb34db04eede0a4"
),
array(
"1624d84780732860ce1c78fcbfefe08b2b29823db913f6493975ba0ff4847610",
"68651cf9b6da903e0914448c6cd9d4ca896878f5282be4c8cc06e2a404078575"
),
array(
"733ce80da955a8a26902c95633e62a985192474b5af207da6df7b4fd5fc61cd4",
"f5435a2bd2badf7d485a4d8b8db9fcce3e1ef8e0201e4578c54673bc1dc5ea1d"
),
array(
"15d9441254945064cf1a1c33bbd3b49f8966c5092171e699ef258dfab81c045c",
"d56eb30b69463e7234f5137b73b84177434800bacebfc685fc37bbe9efe4070d"
),
array(
"a1d0fcf2ec9de675b612136e5ce70d271c21417c9d2b8aaaac138599d0717940",
"edd77f50bcb5a3cab2e90737309667f2641462a54070f3d519212d39c197a629"
),
array(
"e22fbe15c0af8ccc5780c0735f84dbe9a790badee8245c06c7ca37331cb36980",
"a855babad5cd60c88b430a69f53a1a7a38289154964799be43d06d77d31da06"
),
array(
"311091dd9860e8e20ee13473c1155f5f69635e394704eaa74009452246cfa9b3",
"66db656f87d1f04fffd1f04788c06830871ec5a64feee685bd80f0b1286d8374"
),
array(
"34c1fd04d301be89b31c0442d3e6ac24883928b45a9340781867d4232ec2dbdf",
"9414685e97b1b5954bd46f730174136d57f1ceeb487443dc5321857ba73abee"
),
array(
"f219ea5d6b54701c1c14de5b557eb42a8d13f3abbcd08affcc2a5e6b049b8d63",
"4cb95957e83d40b0f73af4544cccf6b1f4b08d3c07b27fb8d8c2962a400766d1"
),
array(
"d7b8740f74a8fbaab1f683db8f45de26543a5490bca627087236912469a0b448",
"fa77968128d9c92ee1010f337ad4717eff15db5ed3c049b3411e0315eaa4593b"
),
array(
"32d31c222f8f6f0ef86f7c98d3a3335ead5bcd32abdd94289fe4d3091aa824bf",
"5f3032f5892156e39ccd3d7915b9e1da2e6dac9e6f26e961118d14b8462e1661"
),
array(
"7461f371914ab32671045a155d9831ea8793d77cd59592c4340f86cbc18347b5",
"8ec0ba238b96bec0cbdddcae0aa442542eee1ff50c986ea6b39847b3cc092ff6"
),
array(
"ee079adb1df1860074356a25aa38206a6d716b2c3e67453d287698bad7b2b2d6",
"8dc2412aafe3be5c4c5f37e0ecc5f9f6a446989af04c4e25ebaac479ec1c8c1e"
),
array(
"16ec93e447ec83f0467b18302ee620f7e65de331874c9dc72bfd8616ba9da6b5",
"5e4631150e62fb40d0e8c2a7ca5804a39d58186a50e497139626778e25b0674d"
),
array(
"eaa5f980c245f6f038978290afa70b6bd8855897f98b6aa485b96065d537bd99",
"f65f5d3e292c2e0819a528391c994624d784869d7e6ea67fb18041024edc07dc"
),
array(
"78c9407544ac132692ee1910a02439958ae04877151342ea96c4b6b35a49f51",
"f3e0319169eb9b85d5404795539a5e68fa1fbd583c064d2462b675f194a3ddb4"
),
array(
"494f4be219a1a77016dcd838431aea0001cdc8ae7a6fc688726578d9702857a5",
"42242a969283a5f339ba7f075e36ba2af925ce30d767ed6e55f4b031880d562c"
),
array(
"a598a8030da6d86c6bc7f2f5144ea549d28211ea58faa70ebf4c1e665c1fe9b5",
"204b5d6f84822c307e4b4a7140737aec23fc63b65b35f86a10026dbd2d864e6b"
),
array(
"c41916365abb2b5d09192f5f2dbeafec208f020f12570a184dbadc3e58595997",
"4f14351d0087efa49d245b328984989d5caf9450f34bfc0ed16e96b58fa9913"
),
array(
"841d6063a586fa475a724604da03bc5b92a2e0d2e0a36acfe4c73a5514742881",
"73867f59c0659e81904f9a1c7543698e62562d6744c169ce7a36de01a8d6154"
),
array(
"5e95bb399a6971d376026947f89bde2f282b33810928be4ded112ac4d70e20d5",
"39f23f366809085beebfc71181313775a99c9aed7d8ba38b161384c746012865"
),
array(
"36e4641a53948fd476c39f8a99fd974e5ec07564b5315d8bf99471bca0ef2f66",
"d2424b1b1abe4eb8164227b085c9aa9456ea13493fd563e06fd51cf5694c78fc"
),
array(
"336581ea7bfbbb290c191a2f507a41cf5643842170e914faeab27c2c579f726",
"ead12168595fe1be99252129b6e56b3391f7ab1410cd1e0ef3dcdcabd2fda224"
),
array(
"8ab89816dadfd6b6a1f2634fcf00ec8403781025ed6890c4849742706bd43ede",
"6fdcef09f2f6d0a044e654aef624136f503d459c3e89845858a47a9129cdd24e"
),
array(
"1e33f1a746c9c5778133344d9299fcaa20b0938e8acff2544bb40284b8c5fb94",
"60660257dd11b3aa9c8ed618d24edff2306d320f1d03010e33a7d2057f3b3b6"
),
array(
"85b7c1dcb3cec1b7ee7f30ded79dd20a0ed1f4cc18cbcfcfa410361fd8f08f31",
"3d98a9cdd026dd43f39048f25a8847f4fcafad1895d7a633c6fed3c35e999511"
),
array(
"29df9fbd8d9e46509275f4b125d6d45d7fbe9a3b878a7af872a2800661ac5f51",
"b4c4fe99c775a606e2d8862179139ffda61dc861c019e55cd2876eb2a27d84b"
),
array(
"a0b1cae06b0a847a3fea6e671aaf8adfdfe58ca2f768105c8082b2e449fce252",
"ae434102edde0958ec4b19d917a6a28e6b72da1834aff0e650f049503a296cf2"
),
array(
"4e8ceafb9b3e9a136dc7ff67e840295b499dfb3b2133e4ba113f2e4c0e121e5",
"cf2174118c8b6d7a4b48f6d534ce5c79422c086a63460502b827ce62a326683c"
),
array(
"d24a44e047e19b6f5afb81c7ca2f69080a5076689a010919f42725c2b789a33b",
"6fb8d5591b466f8fc63db50f1c0f1c69013f996887b8244d2cdec417afea8fa3"
),
array(
"ea01606a7a6c9cdd249fdfcfacb99584001edd28abbab77b5104e98e8e3b35d4",
"322af4908c7312b0cfbfe369f7a7b3cdb7d4494bc2823700cfd652188a3ea98d"
),
array(
"af8addbf2b661c8a6c6328655eb96651252007d8c5ea31be4ad196de8ce2131f",
"6749e67c029b85f52a034eafd096836b2520818680e26ac8f3dfbcdb71749700"
),
array(
"e3ae1974566ca06cc516d47e0fb165a674a3dabcfca15e722f0e3450f45889",
"2aeabe7e4531510116217f07bf4d07300de97e4874f81f533420a72eeb0bd6a4"
),
array(
"591ee355313d99721cf6993ffed1e3e301993ff3ed258802075ea8ced397e246",
"b0ea558a113c30bea60fc4775460c7901ff0b053d25ca2bdeee98f1a4be5d196"
),
array(
"11396d55fda54c49f19aa97318d8da61fa8584e47b084945077cf03255b52984",
"998c74a8cd45ac01289d5833a7beb4744ff536b01b257be4c5767bea93ea57a4"
),
array(
"3c5d2a1ba39c5a1790000738c9e0c40b8dcdfd5468754b6405540157e017aa7a",
"b2284279995a34e2f9d4de7396fc18b80f9b8b9fdd270f6661f79ca4c81bd257"
),
array(
"cc8704b8a60a0defa3a99a7299f2e9c3fbc395afb04ac078425ef8a1793cc030",
"bdd46039feed17881d1e0862db347f8cf395b74fc4bcdc4e940b74e3ac1f1b13"
),
array(
"c533e4f7ea8555aacd9777ac5cad29b97dd4defccc53ee7ea204119b2889b197",
"6f0a256bc5efdf429a2fb6242f1a43a2d9b925bb4a4b3a26bb8e0f45eb596096"
),
array(
"c14f8f2ccb27d6f109f6d08d03cc96a69ba8c34eec07bbcf566d48e33da6593",
"c359d6923bb398f7fd4473e16fe1c28475b740dd098075e6c0e8649113dc3a38"
),
array(
"a6cbc3046bc6a450bac24789fa17115a4c9739ed75f8f21ce441f72e0b90e6ef",
"21ae7f4680e889bb130619e2c0f95a360ceb573c70603139862afd617fa9b9f"
),
array(
"347d6d9a02c48927ebfb86c1359b1caf130a3c0267d11ce6344b39f99d43cc38",
"60ea7f61a353524d1c987f6ecec92f086d565ab687870cb12689ff1e31c74448"
),
array(
"da6545d2181db8d983f7dcb375ef5866d47c67b1bf31c8cf855ef7437b72656a",
"49b96715ab6878a79e78f07ce5680c5d6673051b4935bd897fea824b77dc208a"
),
array(
"c40747cc9d012cb1a13b8148309c6de7ec25d6945d657146b9d5994b8feb1111",
"5ca560753be2a12fc6de6caf2cb489565db936156b9514e1bb5e83037e0fa2d4"
),
array(
"4e42c8ec82c99798ccf3a610be870e78338c7f713348bd34c8203ef4037f3502",
"7571d74ee5e0fb92a7a8b33a07783341a5492144cc54bcc40a94473693606437"
),
array(
"3775ab7089bc6af823aba2e1af70b236d251cadb0c86743287522a1b3b0dedea",
"be52d107bcfa09d8bcb9736a828cfa7fac8db17bf7a76a2c42ad961409018cf7"
),
array(
"cee31cbf7e34ec379d94fb814d3d775ad954595d1314ba8846959e3e82f74e26",
"8fd64a14c06b589c26b947ae2bcf6bfa0149ef0be14ed4d80f448a01c43b1c6d"
),
array(
"b4f9eaea09b6917619f6ea6a4eb5464efddb58fd45b1ebefcdc1a01d08b47986",
"39e5c9925b5a54b07433a4f18c61726f8bb131c012ca542eb24a8ac07200682a"
),
array(
"d4263dfc3d2df923a0179a48966d30ce84e2515afc3dccc1b77907792ebcc60e",
"62dfaf07a0f78feb30e30d6295853ce189e127760ad6cf7fae164e122a208d54"
),
array(
"48457524820fa65a4f8d35eb6930857c0032acc0a4a2de422233eeda897612c4",
"25a748ab367979d98733c38a1fa1c2e7dc6cc07db2d60a9ae7a76aaa49bd0f77"
),
array(
"dfeeef1881101f2cb11644f3a2afdfc2045e19919152923f367a1767c11cceda",
"ecfb7056cf1de042f9420bab396793c0c390bde74b4bbdff16a83ae09a9a7517"
),
array(
"6d7ef6b17543f8373c573f44e1f389835d89bcbc6062ced36c82df83b8fae859",
"cd450ec335438986dfefa10c57fea9bcc521a0959b2d80bbf74b190dca712d10"
),
array(
"e75605d59102a5a2684500d3b991f2e3f3c88b93225547035af25af66e04541f",
"f5c54754a8f71ee540b9b48728473e314f729ac5308b06938360990e2bfad125"
),
array(
"eb98660f4c4dfaa06a2be453d5020bc99a0c2e60abe388457dd43fefb1ed620c",
"6cb9a8876d9cb8520609af3add26cd20a0a7cd8a9411131ce85f44100099223e"
),
array(
"13e87b027d8514d35939f2e6892b19922154596941888336dc3563e3b8dba942",
"fef5a3c68059a6dec5d624114bf1e91aac2b9da568d6abeb2570d55646b8adf1"
),
array(
"ee163026e9fd6fe017c38f06a5be6fc125424b371ce2708e7bf4491691e5764a",
"1acb250f255dd61c43d94ccc670d0f58f49ae3fa15b96623e5430da0ad6c62b2"
),
array(
"b268f5ef9ad51e4d78de3a750c2dc89b1e626d43505867999932e5db33af3d80",
"5f310d4b3c99b9ebb19f77d41c1dee018cf0d34fd4191614003e945a1216e423"
),
array(
"ff07f3118a9df035e9fad85eb6c7bfe42b02f01ca99ceea3bf7ffdba93c4750d",
"438136d603e858a3a5c440c38eccbaddc1d2942114e2eddd4740d098ced1f0d8"
),
array(
"8d8b9855c7c052a34146fd20ffb658bea4b9f69e0d825ebec16e8c3ce2b526a1",
"cdb559eedc2d79f926baf44fb84ea4d44bcf50fee51d7ceb30e2e7f463036758"
),
array(
"52db0b5384dfbf05bfa9d472d7ae26dfe4b851ceca91b1eba54263180da32b63",
"c3b997d050ee5d423ebaf66a6db9f57b3180c902875679de924b69d84a7b375"
),
array(
"e62f9490d3d51da6395efd24e80919cc7d0f29c3f3fa48c6fff543becbd43352",
"6d89ad7ba4876b0b22c2ca280c682862f342c8591f1daf5170e07bfd9ccafa7d"
),
array(
"7f30ea2476b399b4957509c88f77d0191afa2ff5cb7b14fd6d8e7d65aaab1193",
"ca5ef7d4b231c94c3b15389a5f6311e9daff7bb67b103e9880ef4bff637acaec"
),
array(
"5098ff1e1d9f14fb46a210fada6c903fef0fb7b4a1dd1d9ac60a0361800b7a00",
"9731141d81fc8f8084d37c6e7542006b3ee1b40d60dfe5362a5b132fd17ddc0"
),
array(
"32b78c7de9ee512a72895be6b9cbefa6e2f3c4ccce445c96b9f2c81e2778ad58",
"ee1849f513df71e32efc3896ee28260c73bb80547ae2275ba497237794c8753c"
),
array(
"e2cb74fddc8e9fbcd076eef2a7c72b0ce37d50f08269dfc074b581550547a4f7",
"d3aa2ed71c9dd2247a62df062736eb0baddea9e36122d2be8641abcb005cc4a4"
),
array(
"8438447566d4d7bedadc299496ab357426009a35f235cb141be0d99cd10ae3a8",
"c4e1020916980a4da5d01ac5e6ad330734ef0d7906631c4f2390426b2edd791f"
),
array(
"4162d488b89402039b584c6fc6c308870587d9c46f660b878ab65c82c711d67e",
"67163e903236289f776f22c25fb8a3afc1732f2b84b4e95dbda47ae5a0852649"
),
array(
"3fad3fa84caf0f34f0f89bfd2dcf54fc175d767aec3e50684f3ba4a4bf5f683d",
"cd1bc7cb6cc407bb2f0ca647c718a730cf71872e7d0d2a53fa20efcdfe61826"
),
array(
"674f2600a3007a00568c1a7ce05d0816c1fb84bf1370798f1c69532faeb1a86b",
"299d21f9413f33b3edf43b257004580b70db57da0b182259e09eecc69e0d38a5"
),
array(
"d32f4da54ade74abb81b815ad1fb3b263d82d6c692714bcff87d29bd5ee9f08f",
"f9429e738b8e53b968e99016c059707782e14f4535359d582fc416910b3eea87"
),
array(
"30e4e670435385556e593657135845d36fbb6931f72b08cb1ed954f1e3ce3ff6",
"462f9bce619898638499350113bbc9b10a878d35da70740dc695a559eb88db7b"
),
array(
"be2062003c51cc3004682904330e4dee7f3dcd10b01e580bf1971b04d4cad297",
"62188bc49d61e5428573d48a74e1c655b1c61090905682a0d5558ed72dccb9bc"
),
array(
"93144423ace3451ed29e0fb9ac2af211cb6e84a601df5993c419859fff5df04a",
"7c10dfb164c3425f5c71a3f9d7992038f1065224f72bb9d1d902a6d13037b47c"
),
array(
"b015f8044f5fcbdcf21ca26d6c34fb8197829205c7b7d2a7cb66418c157b112c",
"ab8c1e086d04e813744a655b2df8d5f83b3cdc6faa3088c1d3aea1454e3a1d5f"
),
array(
"d5e9e1da649d97d89e4868117a465a3a4f8a18de57a140d36b3f2af341a21b52",
"4cb04437f391ed73111a13cc1d4dd0db1693465c2240480d8955e8592f27447a"
),
array(
"d3ae41047dd7ca065dbf8ed77b992439983005cd72e16d6f996a5316d36966bb",
"bd1aeb21ad22ebb22a10f0303417c6d964f8cdd7df0aca614b10dc14d125ac46"
),
array(
"463e2763d885f958fc66cdd22800f0a487197d0a82e377b49f80af87c897b065",
"bfefacdb0e5d0fd7df3a311a94de062b26b80c61fbc97508b79992671ef7ca7f"
),
array(
"7985fdfd127c0567c6f53ec1bb63ec3158e597c40bfe747c83cddfc910641917",
"603c12daf3d9862ef2b25fe1de289aed24ed291e0ec6708703a5bd567f32ed03"
),
array(
"74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9",
"cc6157ef18c9c63cd6193d83631bbea0093e0968942e8c33d5737fd790e0db08"
),
array(
"30682a50703375f602d416664ba19b7fc9bab42c72747463a71d0896b22f6da3",
"553e04f6b018b4fa6c8f39e7f311d3176290d0e0f19ca73f17714d9977a22ff8"
),
array(
"9e2158f0d7c0d5f26c3791efefa79597654e7a2b2464f52b1ee6c1347769ef57",
"712fcdd1b9053f09003a3481fa7762e9ffd7c8ef35a38509e2fbf2629008373"
),
array(
"176e26989a43c9cfeba4029c202538c28172e566e3c4fce7322857f3be327d66",
"ed8cc9d04b29eb877d270b4878dc43c19aefd31f4eee09ee7b47834c1fa4b1c3"
),
array(
"75d46efea3771e6e68abb89a13ad747ecf1892393dfc4f1b7004788c50374da8",
"9852390a99507679fd0b86fd2b39a868d7efc22151346e1a3ca4726586a6bed8"
),
array(
"809a20c67d64900ffb698c4c825f6d5f2310fb0451c869345b7319f645605721",
"9e994980d9917e22b76b061927fa04143d096ccc54963e6a5ebfa5f3f8e286c1"
),
array(
"1b38903a43f7f114ed4500b4eac7083fdefece1cf29c63528d563446f972c180",
"4036edc931a60ae889353f77fd53de4a2708b26b6f5da72ad3394119daf408f9"
)
)
)
);
Curves::defineCurve("secp256k1", array(
"type" => "short",
"prime" => "k256",
"p" => "ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f",
"a" => "0",
"b" => "7",
"n" => "ffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b bfd25e8c d0364141",
"h" => "1",
"hash" => array(
"outSize" => 256,
"hmacStrength" => 192,
"algo" => "sha256"
),
// Precomputed endomorphism
"beta" => "7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee",
"lambda" => "5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72",
"basis" => array(
array(
"a" => "3086d221a7d46bcde86c90e49284eb15",
"b" => "-e4437ed6010e88286f547fa90abfe4c3"
),
array(
"a" => "114ca50f7a8e2f3f657c1108d9d44cfd8",
"b" => "3086d221a7d46bcde86c90e49284eb15"
)
),
"gRed" => false,
"g" => array(
"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
$pre
)
));
?>
+278
View File
@@ -0,0 +1,278 @@
<?php
namespace Elliptic;
use Elliptic\Curve\PresetCurve;
use Elliptic\EC\KeyPair;
use Elliptic\EC\Signature;
use BN\BN;
class EC
{
public $curve;
public $n;
public $nh;
public $g;
public $hash;
function __construct($options)
{
if( is_string($options) )
{
$options = Curves::getCurve($options);
}
if( $options instanceof PresetCurve )
$options = array("curve" => $options);
$this->curve = $options["curve"]->curve;
$this->n = $this->curve->n;
$this->nh = $this->n->ushrn(1);
//Point on curve
$this->g = $options["curve"]->g;
$this->g->precompute($options["curve"]->n->bitLength() + 1);
//Hash for function for DRBG
if( isset($options["hash"]) )
$this->hash = $options["hash"];
else
$this->hash = $options["curve"]->hash;
}
public function keyPair(#[\SensitiveParameter]
$options) {
return new KeyPair($this, $options);
}
public function keyFromPrivate(#[\SensitiveParameter]
$priv, $enc = false) {
return KeyPair::fromPrivate($this, $priv, $enc);
}
public function keyFromPublic($pub, $enc = false) {
return KeyPair::fromPublic($this, $pub, $enc);
}
public function genKeyPair($options = null)
{
// Instantiate HmacDRBG
$drbg = new HmacDRBG(array(
"hash" => $this->hash,
"pers" => isset($options["pers"]) ? $options["pers"] : "",
"entropy" => isset($options["entropy"]) ? $options["entropy"] : Utils::randBytes($this->hash["hmacStrength"]),
"nonce" => $this->n->toArray()
));
$bytes = $this->n->byteLength();
$ns2 = $this->n->sub(new BN(2));
while(true)
{
$priv = new BN($drbg->generate($bytes));
if( $priv->cmp($ns2) > 0 )
continue;
$priv->iaddn(1);
return $this->keyFromPrivate($priv);
}
}
private function _truncateToN($msg, $truncOnly = false)
{
$delta = intval(($msg->byteLength() * 8) - $this->n->bitLength());
if( $delta > 0 ) {
$msg = $msg->ushrn($delta);
}
if( $truncOnly || $msg->cmp($this->n) < 0 )
return $msg;
return $msg->sub($this->n);
}
public function sign($msg, #[\SensitiveParameter]
$key, $enc = null, $options = null)
{
if( !is_string($enc) )
{
$options = $enc;
$enc = null;
}
$key = $this->keyFromPrivate($key, $enc);
$msg = $this->_truncateToN(new BN($msg, 16));
// Zero-extend key to provide enough entropy
$bytes = $this->n->byteLength();
$bkey = $key->getPrivate()->toArray("be", $bytes);
// Zero-extend nonce to have the same byte size as N
$nonce = $msg->toArray("be", $bytes);
$kFunc = null;
if( isset($options["k"]) )
$kFunc = $options["k"];
else
{
// Instatiate HmacDRBG
$drbg = new HmacDRBG(array(
"hash" => $this->hash,
"entropy" => $bkey,
"nonce" => $nonce,
"pers" => isset($options["pers"]) ? $options["pers"] : "",
"persEnc" => isset($options["persEnc"]) ? $options["persEnc"] : false
));
$kFunc = function($iter) use ($drbg, $bytes) {
return new BN($drbg->generate($bytes));
};
}
// Number of bytes to generate
$ns1 = $this->n->sub(new BN(1));
$canonical = isset($options["canonical"]) ? $options["canonical"] : false;
for($iter = 0; true; $iter++)
{
$k = $kFunc($iter);
$k = $this->_truncateToN($k, true);
if( $k->cmpn(1) <= 0 || $k->cmp($ns1) >= 0 )
continue;
// Fix the bit-length of the random nonce,
// so that it doesn't leak via timing.
// This does not change that ks = k mod k
$ks = $k->add($this->n);
$kt = $ks->add($this->n);
if ($ks->bitLength() === $this->n->bitLength()) {
$kp = $this->g->mul($kt);
} else {
$kp = $this->g->mul($ks);
}
if( $kp->isInfinity() )
continue;
$kpX = $kp->getX();
$r = $kpX->umod($this->n);
if( $r->isZero() )
continue;
$s = $k->invm($this->n)->mul($r->mul($key->getPrivate())->iadd($msg));
$s = $s->umod($this->n);
if( $s->isZero() )
continue;
$recoveryParam = ($kp->getY()->isOdd() ? 1 : 0) | ($kpX->cmp($r) !== 0 ? 2 : 0);
// Use complement of `s`, if it is > `n / 2`
if( $canonical && $s->cmp($this->nh) > 0 )
{
$s = $this->n->sub($s);
$recoveryParam ^= 1;
}
return new Signature(array(
"r" => $r,
"s" => $s,
"recoveryParam" => $recoveryParam
));
}
}
public function verify($msg, $signature, $key, $enc = false)
{
$msg = $this->_truncateToN(new BN($msg, 16));
$key = $this->keyFromPublic($key, $enc);
$signature = new Signature($signature, "hex");
// Perform primitive values validation
$r = $signature->r;
$s = $signature->s;
if( $r->cmpn(1) < 0 || $r->cmp($this->n) >= 0 )
return false;
if( $s->cmpn(1) < 0 || $s->cmp($this->n) >= 0 )
return false;
// Validate signature
$sinv = $s->invm($this->n);
$u1 = $sinv->mul($msg)->umod($this->n);
$u2 = $sinv->mul($r)->umod($this->n);
if( !$this->curve->_maxwellTrick )
{
$p = $this->g->mulAdd($u1, $key->getPublic(), $u2);
if( $p->isInfinity() )
return false;
return $p->getX()->umod($this->n)->cmp($r) === 0;
}
// NOTE: Greg Maxwell's trick, inspired by:
// https://git.io/vad3K
$p = $this->g->jmulAdd($u1, $key->getPublic(), $u2);
if( $p->isInfinity() )
return false;
// Compare `p.x` of Jacobian point with `r`,
// this will do `p.x == r * p.z^2` instead of multiplying `p.x` by the
// inverse of `p.z^2`
return $p->eqXToP($r);
}
public function recoverPubKey($msg, $signature, $j, $enc = false)
{
assert((3 & $j) === $j); //, "The recovery param is more than two bits");
$signature = new Signature($signature, $enc);
$e = new BN($msg, 16);
$r = $signature->r;
$s = $signature->s;
// A set LSB signifies that the y-coordinate is odd
$isYOdd = ($j & 1) == 1;
$isSecondKey = $j >> 1;
if ($r->cmp($this->curve->p->umod($this->curve->n)) >= 0 && $isSecondKey)
throw new \Exception("Unable to find second key candinate");
// 1.1. Let x = r + jn.
if( $isSecondKey )
$r = $this->curve->pointFromX($r->add($this->curve->n), $isYOdd);
else
$r = $this->curve->pointFromX($r, $isYOdd);
$eNeg = $this->n->sub($e);
// 1.6.1 Compute Q = r^-1 (sR - eG)
// Q = r^-1 (sR + -eG)
$rInv = $signature->r->invm($this->n);
return $this->g->mulAdd($eNeg, $r, $s)->mul($rInv);
}
public function getKeyRecoveryParam($e, $signature, $Q, $enc = false)
{
$signature = new Signature($signature, $enc);
if( $signature->recoveryParam != null )
return $signature->recoveryParam;
for($i = 0; $i < 4; $i++)
{
$Qprime = null;
try {
$Qprime = $this->recoverPubKey($e, $signature, $i);
}
catch(\Exception $e) {
continue;
}
if( $Qprime->eq($Q))
return $i;
}
throw new \Exception("Unable to find valid recovery factor");
}
}
?>
+144
View File
@@ -0,0 +1,144 @@
<?php
namespace Elliptic\EC;
use BN\BN;
class KeyPair
{
public $ec;
public $pub;
public $priv;
function __construct($ec, #[\SensitiveParameter]
$options)
{
$this->ec = $ec;
$this->priv = null;
$this->pub = null;
if( isset($options["priv"]) )
$this->_importPrivate($options["priv"], $options["privEnc"]);
if( isset($options["pub"]) )
$this->_importPublic($options["pub"], $options["pubEnc"]);
}
public static function fromPublic($ec, $pub, $enc)
{
if( $pub instanceof KeyPair )
return $pub;
return new KeyPair($ec, array(
"pub" => $pub,
"pubEnc" => $enc
));
}
public static function fromPrivate($ec, #[\SensitiveParameter]
$priv, $enc)
{
if( $priv instanceof KeyPair )
return $priv;
return new KeyPair($ec, array(
"priv" => $priv,
"privEnc" => $enc
));
}
public function validate()
{
$pub = $this->getPublic();
if( $pub->isInfinity() )
return array( "result" => false, "reason" => "Invalid public key" );
if( !$pub->validate() )
return array( "result" => false, "reason" => "Public key is not a point" );
if( !$pub->mul($this->ec->curve->n)->isInfinity() )
return array( "result" => false, "reason" => "Public key * N != O" );
return array( "result" => true, "reason" => null );
}
public function getPublic($compact = false, $enc = "")
{
//compact is optional argument
if( is_string($compact) )
{
$enc = $compact;
$compact = false;
}
if( $this->pub === null )
$this->pub = $this->ec->g->mul($this->priv);
if( !$enc )
return $this->pub;
return $this->pub->encode($enc, $compact);
}
public function getPrivate($enc = false)
{
if( $enc === "hex" )
return $this->priv->toString(16, 2);
return $this->priv;
}
private function _importPrivate(#[\SensitiveParameter]
$key, $enc)
{
$this->priv = new BN($key, (isset($enc) && $enc) ? $enc : 16);
// Ensure that the priv won't be bigger than n, otherwise we may fail
// in fixed multiplication method
$this->priv = $this->priv->umod($this->ec->curve->n);
}
private function _importPublic($key, $enc)
{
$x = $y = null;
if ( is_object($key) ) {
$x = $key->x;
$y = $key->y;
} elseif ( is_array($key) ) {
$x = isset($key["x"]) ? $key["x"] : null;
$y = isset($key["y"]) ? $key["y"] : null;
}
if( $x != null || $y != null )
$this->pub = $this->ec->curve->point($x, $y);
else
$this->pub = $this->ec->curve->decodePoint($key, $enc);
}
//ECDH
public function derive($pub) {
return $pub->mul($this->priv)->getX();
}
//ECDSA
public function sign($msg, $enc = false, $options = false) {
return $this->ec->sign($msg, $this, $enc, $options);
}
public function verify($msg, $signature) {
return $this->ec->verify($msg, $signature, $this);
}
public function inspect() {
return "<Key priv: " . (isset($this->priv) ? $this->priv->toString(16, 2) : "") .
" pub: " . (isset($this->pub) ? $this->pub->inspect() : "") . ">";
}
public function __debugInfo() {
return ["priv" => $this->priv, "pub" => $this->pub];
}
}
?>
+155
View File
@@ -0,0 +1,155 @@
<?php
namespace Elliptic\EC;
use Elliptic\Utils;
use BN\BN;
class Signature
{
public $r;
public $s;
public $recoveryParam;
function __construct($options, $enc = false)
{
if ($options instanceof Signature) {
$this->r = $options->r;
$this->s = $options->s;
$this->recoveryParam = $options->recoveryParam;
return;
}
if (isset($options['r'])) {
assert(isset($options["r"]) && isset($options["s"])); //, "Signature without r or s");
$this->r = new BN($options["r"], 16);
$this->s = new BN($options["s"], 16);
if( isset($options["recoveryParam"]) )
$this->recoveryParam = $options["recoveryParam"];
else
$this->recoveryParam = null;
return;
}
if (!$this->_importDER($options, $enc))
throw new \Exception('Unknown signature format');
}
private static function getLength($buf, &$pos)
{
$initial = $buf[$pos++];
if( !($initial & 0x80) )
return $initial;
$octetLen = $initial & 0xf;
$val = 0;
for($i = 0; $i < $octetLen; $i++)
{
$val = $val << 8;
$val = $val | $buf[$pos];
$pos++;
}
return $val;
}
private static function rmPadding(&$buf)
{
$i = 0;
$len = count($buf) - 1;
while($i < $len && !$buf[$i] && !($buf[$i+1] & 0x80) )
$i++;
if( $i === 0 )
return $buf;
return array_slice($buf, $i);
}
private function _importDER($data, $enc)
{
$data = Utils::toArray($data, $enc);
$dataLen = count($data);
$place = 0;
if( $data[$place++] !== 0x30)
return false;
$len = self::getLength($data, $place);
if( ($len + $place) !== $dataLen )
return false;
if( $data[$place++] !== 0x02 )
return false;
$rlen = self::getLength($data, $place);
$r = array_slice($data, $place, $rlen);
$place += $rlen;
if( $data[$place++] !== 0x02 )
return false;
$slen = self::getLength($data, $place);
if( $dataLen !== $slen + $place )
return false;
$s = array_slice($data, $place, $slen);
if( $r[0] === 0 && ($r[1] & 0x80 ) )
$r = array_slice($r, 1);
if( $s[0] === 0 && ($s[1] & 0x80 ) )
$s = array_slice($s, 1);
$this->r = new BN($r);
$this->s = new BN($s);
$this->recoveryParam = null;
return true;
}
private static function constructLength(&$arr, $len)
{
if( $len < 0x80 )
{
array_push($arr, $len);
return;
}
$octets = 1 + (log($len) / M_LN2 >> 3);
array_push($arr, $octets | 0x80);
while(--$octets)
array_push($arr, ($len >> ($octets << 3)) & 0xff);
array_push($arr, $len);
}
public function toDER($enc = false)
{
$r = $this->r->toArray();
$s = $this->s->toArray();
//Pad values
if( $r[0] & 0x80 )
array_unshift($r, 0);
if( $s[0] & 0x80 )
array_unshift($s, 0);
$r = self::rmPadding($r);
$s = self::rmPadding($s);
while(!$s[0] && !($s[1] & 0x80))
array_slice($s, 1);
$arr = array(0x02);
self::constructLength($arr, count($r));
$arr = array_merge($arr, $r, array(0x02));
self::constructLength($arr, count($s));
$backHalf = array_merge($arr, $s);
$res = array(0x30);
self::constructLength($res, count($backHalf));
$res = array_merge($res, $backHalf);
return Utils::encode($res, $enc);
}
}
?>
+124
View File
@@ -0,0 +1,124 @@
<?php
namespace Elliptic;
use Elliptic\EdDSA\KeyPair;
use Elliptic\EdDSA\Signature;
class EdDSA {
public $curve;
public $g;
public $pointClass;
public $encodingLength;
public $hash;
function __construct($curve) {
assert($curve == "ed25519"); //, 'only tested with ed25519 so far');
$curve = \Elliptic\Curves::getCurve($curve)->curve;
$this->curve = $curve;
$this->g = $curve->g;
$this->g->precompute($curve->n->bitLength() + 1);
$this->pointClass = get_class($curve->point());
$this->encodingLength = intval(ceil($curve->n->bitLength() / 8));
// TODO: !!!
$this->hash = [ "algo" => "sha512" ];
}
/**
* @param {Array|String} message - message bytes
* @param {Array|String|KeyPair} secret - secret bytes or a keypair
* @returns {Signature} - signature
*/
public function sign($message, #[\SensitiveParameter]
$secret) {
$message = Utils::parseBytes($message);
$key = $this->keyFromSecret($secret);
$r = $this->hashInt($key->messagePrefix(), $message);
$R = $this->g->mul($r);
$Rencoded = $this->encodePoint($R);
$s_ = $this->hashInt($Rencoded, $key->pubBytes(), $message)
->mul($key->priv());
$S = $r->add($s_)->umod($this->curve->n);
return $this->makeSignature([ "R" => $R, "S" => $S, "Rencoded" => $Rencoded ]);
}
/**
* @param {Array} message - message bytes
* @param {Array|String|Signature} sig - sig bytes
* @param {Array|String|Point|KeyPair} pub - public key
* @returns {Boolean} - true if public key matches sig of message
*/
public function verify($message, $sig, $pub) {
$message = Utils::parseBytes($message);
$sig = $this->makeSignature($sig);
$key = $this->keyFromPublic($pub);
$h = $this->hashInt($sig->Rencoded(), $key->pubBytes(), $message);
$SG = $this->g->mul($sig->S());
$RplusAh = $sig->R()->add($key->pub()->mul($h));
return $RplusAh->eq($SG);
}
public function hashInt() {
$arguments = func_get_args();
// TODO: refactor when hash-php is ready
$hash = hash_init($this->hash["algo"]);
for ($i = 0; $i < count($arguments); $i++)
hash_update($hash, Utils::toBin($arguments[$i]));
return Utils::intFromLE(hash_final($hash))->umod($this->curve->n);
}
public function keyFromPublic($pub) {
return KeyPair::fromPublic($this, $pub);
}
public function keyFromSecret(#[\SensitiveParameter]
$secret) {
return KeyPair::fromSecret($this, $secret);
}
public function makeSignature($sig) {
if ($sig instanceof Signature)
return $sig;
return new Signature($this, $sig);
}
/**
* * https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-03#section-5.2
*
* EdDSA defines methods for encoding and decoding points and integers. These are
* helper convenience methods, that pass along to utility functions implied
* parameters.
*
*/
public function encodePoint($point) {
$enc = $point->getY()->toArray('le', $this->encodingLength);
$enc[$this->encodingLength - 1] |= $point->getX()->isOdd() ? 0x80 : 0;
return $enc;
}
public function decodePoint($bytes) {
$bytes = Utils::parseBytes($bytes);
$lastIx = count($bytes) - 1;
$normed = $bytes;
$normed[$lastIx] = $bytes[$lastIx] & ~0x80;
$xIsOdd = ($bytes[$lastIx] & 0x80) !== 0;
$y = Utils::intFromLE($normed);
return $this->curve->pointFromY($y, $xIsOdd);
}
public function encodeInt($num) {
return $num->toArray('le', $this->encodingLength);
}
public function decodeInt($bytes) {
return Utils::intFromLE($bytes);
}
public function isPoint($val) {
return is_a($val, $this->pointClass);
}
}
+128
View File
@@ -0,0 +1,128 @@
<?php
namespace Elliptic\EdDSA;
use Elliptic\Utils;
class KeyPair {
public $eddsa;
public $_pubBytes;
/**
* @param {\Elliptic\EdDSA} eddsa - instance
* @param {Object} params - public/private key parameters
*
* @param {Array<Byte>} [params.secret] - secret seed bytes
* @param {Point} [params.pub] - public key point (aka `A` in eddsa terms)
* @param {Array<Byte>} [params.pub] - public key point encoded as bytes
*
*/
function __construct($eddsa, #[\SensitiveParameter]
$params) {
$this->eddsa = $eddsa;
$this->_secret = isset($params["secret"]) ? Utils::parseBytes($params["secret"]) : null;
if (!isset($params["pub"])) {
$this->_pub = null;
$this->_pubBytes = null;
return;
}
if ($eddsa->isPoint($params["pub"]))
$this->_pub = $params["pub"];
else
$this->_pubBytes = Utils::parseBytes($params["pub"]);
}
public static function fromPublic($eddsa, $pub) {
if ($pub instanceof KeyPair)
return $pub;
return new KeyPair($eddsa, [ "pub" => $pub ]);
}
public static function fromSecret($eddsa, #[\SensitiveParameter]
$secret) {
if ($secret instanceof KeyPair)
return $secret;
return new KeyPair($eddsa, [ "secret" => $secret ]);
}
private $_secret;
public function secret() {
return $this->_secret;
}
public function pubBytes() {
if (!$this->_pubBytes)
$this->_pubBytes = $this->eddsa->encodePoint($this->pub());
return $this->_pubBytes;
}
private $_pub;
public function pub() {
if (!$this->_pub) {
if ($this->_pubBytes)
$this->_pub = $this->eddsa->decodePoint($this->_pubBytes);
else
$this->_pub = $this->eddsa->g->mul($this->priv());
}
return $this->_pub;
}
private $_privBytes;
public function privBytes() {
if (!$this->_privBytes) {
$eddsa = $this->eddsa;
$hash = $this->hash();
$lastIx = $eddsa->encodingLength - 1;
$a = array_slice($hash, 0, $eddsa->encodingLength);
$a[0] &= 248;
$a[$lastIx] &= 127;
$a[$lastIx] |= 64;
$this->_privBytes = $a;
}
return $this->_privBytes;
}
private $_priv;
public function priv() {
if (!$this->_priv) {
$this->_priv = $this->eddsa->decodeInt($this->privBytes());
}
return $this->_priv;
}
private $_hash;
public function hash() {
if (!$this->_hash) {
// TODO: !!!
$hash = hash_init('sha512');
hash_update($hash, Utils::toBin($this->secret()));
$this->_hash = Utils::toArray( hash_final($hash), 'hex' );
}
return $this->_hash;
}
private $_messagePrefix;
public function messagePrefix() {
if (!$this->_messagePrefix) {
$this->_messagePrefix = array_slice($this->hash(), $this->eddsa->encodingLength);
}
return $this->_messagePrefix;
}
public function sign($message) {
assert($this->_secret); //, 'KeyPair can only verify');
return $this->eddsa->sign($message, $this);
}
public function verify($message, $sig) {
return $this->eddsa->verify($message, $sig, $this);
}
public function getSecret($enc = false) {
assert($this->_secret); //, 'KeyPair is public only');
return Utils::encode($this->secret(), $enc);
}
public function getPublic($enc = false) {
return Utils::encode($this->pubBytes(), $enc);
}
}
+82
View File
@@ -0,0 +1,82 @@
<?php
namespace Elliptic\EdDSA;
use Elliptic\Utils;
use BN\BN;
class Signature {
public $eddsa;
/**
* @param {EdDSA} eddsa - eddsa instance
* @param {Array<Bytes>|Object} sig -
* @param {Array<Bytes>|Point} [sig.R] - R point as Point or bytes
* @param {Array<Bytes>|bn} [sig.S] - S scalar as bn or bytes
* @param {Array<Bytes>} [sig.Rencoded] - R point encoded
* @param {Array<Bytes>} [sig.Sencoded] - S scalar encoded
*/
function __construct($eddsa, $sig) {
$this->eddsa = $eddsa;
if (is_string($sig))
$sig = Utils::parseBytes($sig);
if (is_array($sig) && !isset($sig["R"])) {
$sig = [
"R" => array_slice($sig, 0, $eddsa->encodingLength),
"S" => array_slice($sig, $eddsa->encodingLength)
];
}
assert($sig["R"] && $sig["S"]); //, 'Signature without R or S');
if ($eddsa->isPoint($sig["R"]))
$this->_R = $sig["R"];
if ($sig["S"] instanceof BN)
$this->_S = $sig["S"];
$this->_Rencoded = is_array($sig["R"]) ? $sig["R"] : (isset($sig["Rencoded"]) ?$sig["Rencoded"] : null);
$this->_Sencoded = is_array($sig["S"]) ? $sig["S"] : (isset($sig["Sencoded"]) ?$sig["Sencoded"] : null);
}
private $_S;
public function S() {
if (!$this->_S) {
$this->_S = $this->eddsa->decodeInt($this->Sencoded());
}
return $this->_S;
}
private $_R;
public function R() {
if (!$this->_R) {
$this->_R = $this->eddsa->decodePoint($this->Rencoded());
}
return $this->_R;
}
private $_Rencoded;
public function Rencoded() {
if (!$this->_Rencoded) {
$this->_Rencoded = $this->eddsa->encodePoint($this->R());
}
return $this->_Rencoded;
}
private $_Sencoded;
public function Sencoded() {
if (!$this->_Sencoded) {
$this->_Sencoded = $this->eddsa->encodeInt($this->S());
}
return $this->_Sencoded;
}
public function toBytes() {
return array_merge($this->Rencoded(), $this->Sencoded());
}
public function toHex() {
return strtoupper(Utils::encode($this->toBytes(), 'hex'));
}
}
+135
View File
@@ -0,0 +1,135 @@
<?php
namespace Elliptic;
class HmacDRBG
{
private $hash;
private $predResist;
private $outLen;
private $minEntropy;
private $reseed;
private $reseedInterval;
private $K;
private $V;
function __construct($options)
{
Utils::optionAssert($options, "predResist");
Utils::optionAssert($options, "hash", null, true);
Utils::optionAssert($options["hash"], "outSize", null, true);
Utils::optionAssert($options["hash"], "hmacStrength", null, true);
Utils::optionAssert($options["hash"], "algo", null, true);
Utils::optionAssert($options, "minEntropy");
Utils::optionAssert($options, "entropy", null, true);
Utils::optionAssert($options, "entropyEnc");
Utils::optionAssert($options, "nonce", "");
Utils::optionAssert($options, "nonceEnc");
Utils::optionAssert($options, "pers", "");
Utils::optionAssert($options, "persEnc");
$this->hash = $options["hash"];
$this->predResist = $options["predResist"];
$this->outLen = $this->hash["outSize"];
$this->minEntropy = $options["minEntropy"] ?: $this->hash["hmacStrength"];
$this->reseed = null;
$this->reseedInterval = null;
$this->K = null;
$this->V = null;
$entropy = Utils::toBin($options["entropy"], $options["entropyEnc"]);
$nonce = Utils::toBin($options["nonce"], $options["nonceEnc"]);
$pers = Utils::toBin($options["pers"], $options["persEnc"]);
if (Utils::$ASSERT_ENABLED) {
assert(strlen($entropy) >= ($this->minEntropy / 8));
}
$this->_init($entropy, $nonce, $pers);
}
private function _init($entropy, $nonce, $pers)
{
$seed = $entropy . $nonce . $pers;
$this->K = str_repeat(chr(0x00), $this->outLen / 8);
$this->V = str_repeat(chr(0x01), $this->outLen / 8);
$this->_update($seed);
$this->reseed = 1;
$this->reseedInterval = 0x1000000000000; // 2^48
}
private function _hmac()
{
return hash_init($this->hash["algo"], HASH_HMAC, $this->K);
}
private function _update($seed = false)
{
$kmac = $this->_hmac();
hash_update($kmac, $this->V);
hash_update($kmac, chr(0x00));
if( $seed )
hash_update($kmac, $seed);
$this->K = hash_final($kmac, true);
$kmac = $this->_hmac();
hash_update($kmac, $this->V);
$this->V = hash_final($kmac, true);
if(!$seed)
return;
$kmac = $this->_hmac();
hash_update($kmac, $this->V);
hash_update($kmac, chr(0x01));
hash_update($kmac, $seed);
$this->K = hash_final($kmac, true);
$kmac = $this->_hmac();
hash_update($kmac, $this->V);
$this->V = hash_final($kmac, true);
}
// TODO: reseed()
public function generate($len, $enc = null, $add = null, $addEnc = null)
{
if ($this->reseed > $this->reseedInterval)
throw new \Exception("Reseed is required");
// Optional encoding
if( !is_string($enc) )
{
$addEnc = $enc;
$add = $enc;
$enc = null;
}
// Optional additional data
if( $add != null ) {
$add = Utils::toBin($add, $addEnc);
$this->_update($add);
}
$temp = "";
while( strlen($temp) < $len )
{
$hmac = $this->_hmac();
hash_update($hmac, $this->V);
$this->V = hash_final($hmac, true);
$temp .= $this->V;
}
$res = substr($temp, 0, $len);
$this->_update($add);
$this->reseed++;
return Utils::encode(Utils::toArray($res), $enc);
}
}
?>
+176
View File
@@ -0,0 +1,176 @@
<?php
namespace Elliptic;
use \Exception;
use BN\BN;
if (!function_exists("random_int")) {
function random_int($a, $b) {
return rand($a, $b);
}
}
class Utils
{
public static $ASSERT_ENABLED;
public static function toArray($msg, $enc = false)
{
if( is_array($msg) )
return array_slice($msg, 0);
if( !$msg )
return array();
if( !is_string($msg) )
throw new Exception("Not implemented");
if( !$enc )
return array_slice(unpack("C*", $msg), 0);
if( $enc === "hex" )
return array_slice(unpack("C*", hex2bin($msg)), 0);
return $msg;
}
public static function toHex($msg)
{
if( is_string($msg) )
return bin2hex($msg);
if( !is_array($msg) )
throw new Exception("Not implemented");
$binary = call_user_func_array("pack", array_merge(["C*"], $msg));
return bin2hex($binary);
}
public static function toBin($msg, $enc = false)
{
if( is_array($msg) )
return call_user_func_array("pack", array_merge(["C*"], $msg));
if( $enc === "hex" )
return hex2bin($msg);
return $msg;
}
public static function encode($arr, $enc)
{
if( $enc === "hex" )
return self::toHex($arr);
return $arr;
}
// Represent num in a w-NAF form
public static function getNAF($num, $w)
{
$naf = array();
$ws = 1 << ($w + 1);
$k = clone($num);
while( $k->cmpn(1) >= 0 )
{
if( !$k->isOdd() )
array_push($naf, 0);
else
{
$mod = $k->andln($ws - 1);
$z = $mod;
if( $mod > (($ws >> 1) - 1))
$z = ($ws >> 1) - $mod;
$k->isubn($z);
array_push($naf, $z);
}
// Optimization, shift by word if possible
$shift = (!$k->isZero() && $k->andln($ws - 1) === 0) ? ($w + 1) : 1;
for($i = 1; $i < $shift; $i++)
array_push($naf, 0);
$k->iushrn($shift);
}
return $naf;
}
// Represent k1, k2 in a Joint Sparse Form
public static function getJSF($k1, $k2)
{
$jsf = array( array(), array() );
$k1 = $k1->_clone();
$k2 = $k2->_clone();
$d1 = 0;
$d2 = 0;
while( $k1->cmpn(-$d1) > 0 || $k2->cmpn(-$d2) > 0 )
{
// First phase
$m14 = ($k1->andln(3) + $d1) & 3;
$m24 = ($k2->andln(3) + $d2) & 3;
if( $m14 === 3 )
$m14 = -1;
if( $m24 === 3 )
$m24 = -1;
$u1 = 0;
if( ($m14 & 1) !== 0 )
{
$m8 = ($k1->andln(7) + $d1) & 7;
$u1 = ( ($m8 === 3 || $m8 === 5) && $m24 === 2 ) ? -$m14 : $m14;
}
array_push($jsf[0], $u1);
$u2 = 0;
if( ($m24 & 1) !== 0 )
{
$m8 = ($k2->andln(7) + $d2) & 7;
$u2 = ( ($m8 === 3 || $m8 === 5) && $m14 === 2 ) ? -$m24 : $m24;
}
array_push($jsf[1], $u2);
// Second phase
if( (2 * $d1) === ($u1 + 1) )
$d1 = 1 - $d1;
if( (2 * $d2) === ($u2 + 1) )
$d2 = 1 - $d2;
$k1->iushrn(1);
$k2->iushrn(1);
}
return $jsf;
}
public static function intFromLE($bytes) {
return new BN($bytes, 'hex', 'le');
}
public static function parseBytes($bytes) {
if (is_string($bytes))
return self::toArray($bytes, 'hex');
return $bytes;
}
public static function randBytes($count)
{
$res = "";
for($i = 0; $i < $count; $i++)
$res .= chr(random_int(0, 255));
return $res;
}
public static function optionAssert(&$array, $key, $value = false, $required = false)
{
if( isset($array[$key]) )
return;
if( $required )
throw new Exception("Missing option " . $key);
$array[$key] = $value;
}
}
Utils::$ASSERT_ENABLED = ini_get("zend.assertions") === "1";
?>
+10
View File
@@ -0,0 +1,10 @@
{
"bootstrap": "vendor/autoload.php",
"path": "benchmarks",
"reports": {
"simple": {
"generator": "table",
"cols": ["subject", "mode", "rstdev", "its", "revs"]
}
}
}
+21
View File
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnFailure="false"
verbose="true"
>
<testsuites>
<testsuite name="AllTests">
<directory>tests</directory>
</testsuite>
</testsuites>
<coverage>
<exclude>
<directory suffix=".php">lib/</directory>
</exclude>
</coverage>
</phpunit>
+17
View File
@@ -0,0 +1,17 @@
<?php
require_once __DIR__ . "/../vendor/autoload.php";
class ApiTest extends \PHPUnit\Framework\TestCase {
public function test_should_instatiate_with_valid_curve_secp256k1() {
$ec = new \Elliptic\EC('secp256k1');
$this->assertNotNull($ec);
$this->assertInstanceOf(\Elliptic\EC::class, $ec);
}
public function test_should_throw_error_with_invalid_curve() {
$this->expectException(\Exception::class);
$ec = new \Elliptic\EC('nonexistent-curve');
}
}
+165
View File
@@ -0,0 +1,165 @@
<?php
require_once __DIR__ . "/../vendor/autoload.php";
use BN\BN;
class CurveTest extends \PHPUnit\Framework\TestCase {
public function test_should_work_with_example_curve() {
$curve = new \Elliptic\Curve\ShortCurve(array(
"p" => '1d',
"a" => '4',
"b" => '14'
));
$p = $curve->point('18', '16');
$this->assertTrue($p->validate());
$this->assertTrue($p->dbl()->validate());
$this->assertTrue($p->dbl()->add($p)->validate());
$this->assertTrue($p->dbl()->add($p->dbl())->validate());
$this->assertTrue($p->dbl()->add($p->dbl())->eq($p->add($p)->add($p)->add($p)));
}
public function test_should_work_with_secp112k1() {
$curve = new \Elliptic\Curve\ShortCurve(array(
"p" => 'db7c 2abf62e3 5e668076 bead208b',
"a" => 'db7c 2abf62e3 5e668076 bead2088',
"b" => '659e f8ba0439 16eede89 11702b22'
));
$p = $curve->point(
'0948 7239995a 5ee76b55 f9c2f098',
'a89c e5af8724 c0a23e0e 0ff77500');
$this->assertTrue($p->validate());
$this->assertTrue($p->dbl()->validate());
}
public function test_should_work_with_secp256k1() {
$curve = new \Elliptic\Curve\ShortCurve(array(
"p" => 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe ' .
'fffffc2f',
"a" => '0',
"b" => '7',
"n" => 'ffffffff ffffffff ffffffff fffffffe ' .
'baaedce6 af48a03b bfd25e8c d0364141',
"g" => [
'79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
'483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'
]
));
$p = $curve->point(
'79be667e f9dcbbac 55a06295 ce870b07 029bfcdb 2dce28d9 59f2815b 16f81798',
'483ada77 26a3c465 5da4fbfc 0e1108a8 fd17b448 a6855419 9c47d08f fb10d4b8'
);
$this->assertTrue($p->validate());
$this->assertTrue($p->dbl()->validate());
$this->assertTrue($p->toJ()->dbl()->toP()->validate());
$this->assertTrue($p->mul(new BN('79be667e f9dcbbac 55a06295 ce870b07', 16))->validate());
$j = $p->toJ();
$this->assertTrue($j->trpl()->eq($j->dbl()->add($j)));
// Endomorphism test
$this->assertNotNull($curve->endo);
$this->assertEquals(
$curve->endo["beta"]->fromRed()->toString(16),
'7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee');
$this->assertEquals(
$curve->endo["lambda"]->toString(16),
'5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72');
$k = new BN('1234567890123456789012345678901234', 16);
$split = $curve->_endoSplit($k);
$testK = $split["k1"]->add($split["k2"]->mul($curve->endo["lambda"]))->umod($curve->n);
$this->assertEquals($testK->toString(16), $k->toString(16));
}
public function test_should_compute_this_problematic_secp256k1_multiplication() {
$curve = \Elliptic\Curves::getCurve("secp256k1")->curve;
$g1 = $curve->g; // precomputed g
$this->assertNotNull($g1->precomputed);
$g2 = $curve->point($g1->getX(), $g1->getY()); // not precomputed g
$this->assertNull($g2->precomputed);
$a = new BN(
'6d1229a6b24c2e775c062870ad26bc261051e0198c67203167273c7c62538846', 16);
$p1 = $g1->mul($a);
$p2 = $g2->mul($a);
$this->assertTrue($p1->eq($p2));
}
public function test_should_not_use_fixed_NAF_when_k_is_too_large() {
$curve = \Elliptic\Curves::getCurve("secp256k1")->curve;
$g1 = $curve->g; // precomputed g
$this->assertNotNull($g1->precomputed);
$g2 = $curve->point($g1->getX(), $g1->getY()); // not precomputed g
$this->assertNull($g2->precomputed);
$a = new BN(
'6d1229a6b24c2e775c062870ad26bc26' .
'1051e0198c67203167273c7c6253884612345678',
16);
$p1 = $g1->mul($a);
$p2 = $g2->mul($a);
$this->assertTrue($p1->eq($p2));
}
public function test_should_not_fail_on_secp256k1_regression() {
$curve = \Elliptic\Curves::getCurve("secp256k1")->curve;
$k1 = new BN(
'32efeba414cd0c830aed727749e816a01c471831536fd2fce28c56b54f5a3bb1', 16);
$k2 = new BN(
'5f2e49b5d64e53f9811545434706cde4de528af97bfd49fde1f6cf792ee37a8c', 16);
$p1 = $curve->g->mul($k1);
$p2 = $curve->g->mul($k2);
// 2 + 2 + 1 = 2 + 1 + 2
$two = $p2->dbl();
$five = $two->dbl()->add($p2);
$three = $two->add($p2);
$maybeFive = $three->add($two);
$this->assertTrue($maybeFive->eq($five));
$p1 = $p1->mul($k2);
$p2 = $p2->mul($k1);
$this->assertTrue($p1->validate());
$this->assertTrue($p2->validate());
$this->assertTrue($p1->eq($p2));
}
public function test_should_correctly_double_the_affine_point_on_secp256k1() {
$bad = new ArrayObject(array(
"x" => '026a2073b1ef6fab47ace18e60e728a05180a82755bbcec9a0abc08ad9f7a3d4',
"y" => '9cd8cb48c3281596139f147c1364a3ede88d3f310fdb0eb98c924e599ca1b3c9',
"z" => 'd78587ad45e4102f48b54b5d85598296e069ce6085002e169c6bad78ddc6d9bd'
), ArrayObject::ARRAY_AS_PROPS);
$good = new ArrayObject(array(
"x" => 'e7789226739ac2eb3c7ccb2a9a910066beeed86cdb4e0f8a7fee8eeb29dc7016',
"y" => '4b76b191fd6d47d07828ea965e275b76d0e3e0196cd5056d38384fbb819f9fcb',
"z" => 'cbf8d99056618ba132d6145b904eee1ce566e0feedb9595139c45f84e90cfa7d'
), ArrayObject::ARRAY_AS_PROPS);
$curve = \Elliptic\Curves::getCurve("secp256k1")->curve;
$bad = $curve->jpoint($bad->x, $bad->y, $bad->z);
$good = $curve->jpoint($good->x, $good->y, $good->z);
// They are the same points
$this->assertTrue($bad->add($good->neg())->isInfinity());
// But doubling borks them out
$this->assertTrue($bad->dbl()->add($good->dbl()->neg())->isInfinity());
}
public function test_should_store_precomputed_values_correctly_on_negation() {
$curve = \Elliptic\Curves::getCurve("secp256k1")->curve;
$p = $curve->g->mul('2');
$p->precompute();
$neg = $p->neg(true);
$neg2 = $neg->neg(true);
$this->assertTrue($p->eq($neg2));
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
require_once __DIR__ . "/../vendor/autoload.php";
class ECDHTest extends \PHPUnit\Framework\TestCase {
public function test_should_work_with_secp256k1_curve() {
$this->doTest('secp256k1');
}
public function test_should_work_with_p256_curve() {
$this->doTest('p256');
}
public function test_should_work_with_curve25519_curve() {
$this->doTest('curve25519');
}
public function test_should_work_with_ed25519_curve() {
$this->doTest('ed25519');
}
function doTest($name) {
$ecdh = new \Elliptic\EC($name);
$s1 = $ecdh->genKeyPair();
$s2 = $ecdh->genKeyPair();
$sh1 = $s1->derive($s2->getPublic());
$sh2 = $s2->derive($s1->getPublic());
$this->assertEquals($sh1->toString(16), $sh2->toString(16));
$sh1 = $s1->derive($ecdh->keyFromPublic($s2->getPublic('hex'), 'hex')
->getPublic());
$sh2 = $s2->derive($ecdh->keyFromPublic($s1->getPublic('hex'), 'hex')
->getPublic());
$this->assertEquals($sh1->toString(16), $sh2->toString(16));
}
}
+321
View File
@@ -0,0 +1,321 @@
<?php
require_once __DIR__ . "/../vendor/autoload.php";
use BN\BN;
class ECDSATest extends \PHPUnit\Framework\TestCase {
function ECDSACurveNames() {
return [
['secp256k1']
, ['ed25519']
, ['p256']
, ['p384']
, ['p521']
];
}
static $entropy = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25
];
static $msg = 'deadbeef';
protected $curve;
protected $ecdsa;
protected $keys;
public function prepare($name) {
$this->curve = \Elliptic\Curves::getCurve($name);
$this->assertNotNull($this->curve);
$this->ecdsa = new \Elliptic\EC($this->curve);
$this->keys = $this->ecdsa->genKeyPair([ "entropy" => self::$entropy ]);
return [$this->curve, $this->ecdsa, $this->keys];
}
/**
* @dataProvider ECDSACurveNames
*/
public function test_should_generate_proper_key_pair($name) {
list($curve, $ecdsa, $keys) = $this->prepare($name);
$keylen = 64;
if ($name == 'p384') {
$keylen = 96;
} else if ($name == 'p521') {
$keylen = 132;
}
// Get keys out of pair
$this->assertTrue( $keys->getPublic()->x && $keys->getPublic()->y );
$this->assertTrue( $keys->getPrivate()->byteLength() > 0);
$this->assertEquals( strlen($keys->getPrivate('hex')), $keylen);
$this->assertTrue( strlen($keys->getPublic('hex')) > 0);
$this->assertTrue( strlen($keys->getPrivate('hex')) > 0);
$this->assertTrue( $keys->validate()["result"], 'key validate' );
}
/**
* @dataProvider ECDSACurveNames
*/
public function test_should_sign_and_verify($name) {
list($curve, $ecdsa, $keys) = $this->prepare($name);
$signature = $ecdsa->sign(self::$msg, $keys);
$this->assertTrue($ecdsa->verify(self::$msg, $signature, $keys), 'Normal verify');
}
/**
* @dataProvider ECDSACurveNames
*/
public function test_should_sign_and_verify_using_keys_methods($name) {
list($curve, $ecdsa, $keys) = $this->prepare($name);
$signature = $keys->sign(self::$msg);
$this->assertTrue($keys->verify(self::$msg, $signature), 'On-key verify');
}
/**
* @dataProvider ECDSACurveNames
*/
public function test_should_load_private_key_from_the_hex_value($name) {
list($curve, $ecdsa, $keys) = $this->prepare($name);
$copy = $ecdsa->keyFromPrivate($keys->getPrivate('hex'), 'hex');
$signature = $ecdsa->sign(self::$msg, $copy);
$this->assertTrue($ecdsa->verify(self::$msg, $signature, $copy), 'hex-private verify');
}
/**
* @dataProvider ECDSACurveNames
*/
public function test_should_have_signature_s_leq_keys_ec_nh($name) {
list($curve, $ecdsa, $keys) = $this->prepare($name);
// key.sign(msg, options)
$sign = $keys->sign('deadbeef', [ "canonical" => true ]);
$this->assertTrue($sign->s->cmp($keys->ec->nh) <= 0);
}
/**
* @dataProvider ECDSACurveNames
*/
public function test_should_support_options_k($name) {
list($curve, $ecdsa, $keys) = $this->prepare($name);
$sign = $keys->sign(self::$msg, [
"k" => function($iter) {
$this->assertTrue($iter >= 0);
return new BN(1358);
}
]);
$this->assertTrue($ecdsa->verify(self::$msg, $sign, $keys), 'custom-k verify');
}
/**
* @dataProvider ECDSACurveNames
*/
public function test_should_have_another_signature_with_pers($name) {
list($curve, $ecdsa, $keys) = $this->prepare($name);
$sign1 = $keys->sign(self::$msg);
$sign2 = $keys->sign(self::$msg, [ "pers" => '1234', "persEnc" => 'hex' ]);
$this->assertNotEquals($sign1->r->toString('hex') . $sign1->s->toString('hex'),
$sign2->r->toString('hex') . $sign2->s->toString('hex'));
}
/**
* @dataProvider ECDSACurveNames
*/
public function test_should_load_public_key_from_compact_hex_value($name) {
list($curve, $ecdsa, $keys) = $this->prepare($name);
$pub = $keys->getPublic(true, 'hex');
$copy = $ecdsa->keyFromPublic($pub, 'hex');
$this->assertEquals($copy->getPublic(true, 'hex'), $pub);
}
/**
* @dataProvider ECDSACurveNames
*/
public function test_should_load_public_key_from_hex_value($name) {
list($curve, $ecdsa, $keys) = $this->prepare($name);
$pub = $keys->getPublic('hex');
$copy = $ecdsa->keyFromPublic($pub, 'hex');
$this->assertEquals($copy->getPublic('hex'), $pub);
}
/**
* @dataProvider ECDSACurveNames
*/
public function test_should_support_hex_DER_encoding_of_signatures($name) {
list($curve, $ecdsa, $keys) = $this->prepare($name);
$signature = $ecdsa->sign(self::$msg, $keys);
$dsign = $signature->toDER('hex');
$this->assertTrue($ecdsa->verify(self::$msg, $dsign, $keys), 'hex-DER encoded verify');
}
/**
* @dataProvider ECDSACurveNames
*/
public function test_should_support_DER_encoding_of_signatures($name) {
list($curve, $ecdsa, $keys) = $this->prepare($name);
$signature = $ecdsa->sign(self::$msg, $keys);
$dsign = $signature->toDER();
$this->assertTrue($ecdsa->verify(self::$msg, $dsign, $keys), 'DER encoded verify');
}
/**
* @dataProvider ECDSACurveNames
*/
public function test_should_not_verify_signature_with_wrong_public_key($name) {
list($curve, $ecdsa, $keys) = $this->prepare($name);
$signature = $ecdsa->sign(self::$msg, $keys);
$wrong = $ecdsa->genKeyPair();
$this->assertNotTrue($ecdsa->verify(self::$msg, $signature, $wrong), 'Wrong key verify');
}
/**
* @dataProvider ECDSACurveNames
*/
public function test_should_not_verify_signature_with_wrong_private_key($name) {
list($curve, $ecdsa, $keys) = $this->prepare($name);
$signature = $ecdsa->sign(self::$msg, $keys);
$wrong = $ecdsa->keyFromPrivate($keys->getPrivate('hex') .
$keys->getPrivate('hex'), 'hex');
$this->assertNotTrue($ecdsa->verify(self::$msg, $signature, $wrong), 'Wrong key verify');
}
// TODO: Implement RFC6979 vectors test
function MaxwellsTrickVector() {
$p256 = \Elliptic\Curves::getCurve("p256");
$p384 = \Elliptic\Curves::getCurve("p384");
$msg = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
return [
[[
"curve" => $p256,
"pub" => '041548fc88953e06cd34d4b300804c5322cb48c24aaaa4d0' .
'7a541b0f0ccfeedeb0ae4991b90519ea405588bdf699f5e6' .
'd0c6b2d5217a5c16e8371062737aa1dae1',
"message" => $msg,
"sig" => '3006020106020104',
"result" => true
]],
[[
"curve" => $p256,
"pub" => '04ad8f60e4ec1ebdb6a260b559cb55b1e9d2c5ddd43a41a2' .
'd11b0741ef2567d84e166737664104ebbc337af3d861d352' .
'4cfbc761c12edae974a0759750c8324f9a',
"message" => $msg,
"sig" => '3006020106020104',
"result" => true
]],
[[
"curve" => $p256,
"pub" => '0445bd879143a64af5746e2e82aa65fd2ea07bba4e355940' .
'95a981b59984dacb219d59697387ac721b1f1eccf4b11f43' .
'ddc39e8367147abab3084142ed3ea170e4',
"message" => $msg,
"sig" => '301502104319055358e8617b0c46353d039cdaae020104',
"result" => true
]],
[[
"curve" => $p256,
"pub" => '040feb5df4cc78b35ec9c180cc0de5842f75f088b4845697' .
'8ffa98e716d94883e1e6500b2a1f6c1d9d493428d7ae7d9a' .
'8a560fff30a3d14aa160be0c5e7edcd887',
"message" => $msg,
"sig" => '301502104319055358e8617b0c46353d039cdaae020104',
"result" => false
]],
[[
"curve" => $p384,
"pub" => '0425e299eea9927b39fa92417705391bf17e8110b4615e9e' .
'b5da471b57be0c30e7d89dbdc3e5da4eae029b300344d385' .
'1548b59ed8be668813905105e673319d59d32f574e180568' .
'463c6186864888f6c0b67b304441f82aab031279e48f047c31',
"message" => $msg,
"sig" => '3006020103020104',
"result" => true
]],
[[
"curve" => $p384,
"pub" => '04a328f65c22307188b4af65779c1d2ec821c6748c6bd8dc' .
'0e6a008135f048f832df501f7f3f79966b03d5bef2f187ec' .
'34d85f6a934af465656fb4eea8dd9176ab80fbb4a27a649f' .
'526a7dfe616091b78d293552bc093dfde9b31cae69d51d3afb',
"message" => $msg,
"sig" => '3006020103020104',
"result" => true
]],
[[
"curve" => $p384,
"pub" => '04242e8585eaa7a28cc6062cab4c9c5fd536f46b17be1728' .
'288a2cda5951df4941aed1d712defda023d10aca1c5ee014' .
'43e8beacd821f7efa27847418ab95ce2c514b2b6b395ee73' .
'417c83dbcad631421f360d84d64658c98a62d685b220f5aad4',
"message" => $msg,
"sig" => '301d0218389cb27e0bc8d21fa7e5f24cb74f58851313e696333ad68e020104',
"result" => true
]],
[[
"curve" => $p384,
"pub" => '04cdf865dd743fe1c23757ec5e65fd5e4038b472ded2af26' .
'1e3d8343c595c8b69147df46379c7ca40e60e80170d34a11' .
'88dbb2b6f7d3934c23d2f78cfb0db3f3219959fad63c9b61' .
'2ef2f20d679777b84192ce86e781c14b1bbb77eacd6e0520e2',
"message" => $msg,
"sig" => '301d0218389cb27e0bc8d21fa7e5f24cb74f58851313e696333ad68e020104',
"result" => false
]]
];
}
/**
* @dataProvider MaxwellsTrickVector
*/
public function test_should_pass_on_Maxwells_trick_vectors($vector) {
$ecdsa = new \Elliptic\EC($vector["curve"]);
$key = $ecdsa->keyFromPublic($vector["pub"], 'hex');
$msg = $vector["message"];
$sig = $vector["sig"];
$actual = $ecdsa->verify($msg, $sig, $key);
$this->assertEquals($actual, $vector["result"]);
}
public function test_should_deterministically_generate_private_key() {
$curve = \Elliptic\Curves::getCurve("secp256k1");
$this->assertNotNull($curve);
$ecdsa = new \Elliptic\EC($curve);
$keys = $ecdsa->genKeyPair(array(
"pers" => 'my.pers.string',
"entropy" => hash('sha256', 'hello world', true)
));
$this->assertEquals(
$keys->getPrivate('hex'),
'6160edb2b218b7f1394b9ca8eb65a72831032a1f2f3dc2d99291c2f7950ed887');
}
public function test_should_recover_the_public_key_from_a_signature() {
$ec = new \Elliptic\EC('secp256k1');
$key = $ec->genKeyPair();
$msg = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
$signature = $key->sign($msg);
$recid = $ec->getKeyRecoveryParam($msg, $signature, $key->getPublic());
$r = $ec->recoverPubKey($msg, $signature, $recid);
$this->assertTrue($key->getPublic()->eq($r), 'the keys should match');
}
public function test_should_fail_to_recover_key_when_no_quadratic_residue_available() {
$ec = new \Elliptic\EC('secp256k1');
$message =
'f75c6b18a72fabc0f0b888c3da58e004f0af1fe14f7ca5d8c897fe164925d5e9';
$this->expectException(\Exception::class);
$ec->recoverPubKey($message, [
"r" => 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140',
"s" => '8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3'
], 0);
}
}
+110
View File
@@ -0,0 +1,110 @@
<?php
require_once __DIR__ . "/../vendor/autoload.php";
use \Elliptic\EdDSA;
use \Elliptic\Utils;
function toHex($arg) { return strtoupper(Utils::toHex($arg)); }
class ED25519Test extends \PHPUnit\Framework\TestCase {
public function derivations() {
$data = json_decode( file_get_contents(__DIR__ . "/fixtures/derivation-fixtures"), true);
$data = array_slice($data, 0, 50);
return array_map(function($set) { return [$set]; }, $data);
}
/**
* @dataProvider derivations
*/
public function test_derivations_can_compute_correct_a_and_A_from_secret($test) {
$ed25519 = new EdDSA("ed25519");
$secret = Utils::toArray($test["secret_hex"], 'hex');
$key = $ed25519->keyFromSecret($secret);
$this->assertEquals( toHex($key->privBytes()), $test["a_hex"] );
$xRecovered = toHex( $ed25519->encodeInt(
$ed25519->decodePoint( $key->pubBytes() )->getX()) );
$this->assertEquals( $xRecovered, $test["A_P"]["x"] );
$this->assertEquals( toHex( $key->pubBytes() ), $test["A_hex"] );
}
public function signLines() {
$data = file_get_contents(__DIR__ . "/fixtures/sign.input");
$lines = array_filter( explode("\n", $data), function($line) { return strlen($line) > 0; });
$lines = array_slice($lines, 0, 50);
return array_map(function($line) { return [$line]; }, $lines);
}
/**
* @dataProvider signLines
*/
public function test_sign_input_test_vectors($line) {
$split = explode(':', strtoupper($line));
$ed25519 = new EdDSA("ed25519");
$key = $ed25519->keyFromSecret(substr($split[0], 0, 64));
$expectedPk = substr($split[0], 64);
$this->assertEquals( toHex($key->pubBytes()), $expectedPk);
$msg = Utils::toArray($split[2], 'hex');
$sig = $key->sign($msg)->toHex();
$sigR = substr($sig, 0, 64);
$sigS = substr($sig, 64);
$this->assertEquals($sigR, substr($split[3], 0, 64));
$this->assertEquals($sigS, substr($split[3], 64, 64));
$this->assertTrue($key->verify($msg, $sig));
if (count($msg) == 0) {
$forged = [ 0x78 ] /* ord('x') */;
} else {
$forged = $msg;
$forged[count($msg) - 1] = ($msg[count($msg) - 1] + 1) % 256;
}
$this->assertNotTrue($key->verify($forged, $sig));
}
public function test_eddsa_has_encodingLength_of_32() {
$ed25519 = new EdDSA("ed25519");
$this->assertEquals(32, $ed25519->encodingLength);
}
public function test_eddsa_can_sign_and_verify_messages() {
$ed25519 = new EdDSA("ed25519");
$secret = array_fill(0, 32, 0);
$msg = [ 0xB, 0xE, 0xE, 0xF ];
$key = $ed25519->keyFromSecret($secret);
$sig = $key->sign($msg)->toHex();
$R = '8F1B9A7FDB22BCD2C15D4695B1CE2B063CBFAEC9B00BE360427BAC9533943F6C';
$S = '5F0B380FD7F2E43B70AB2FA29F6C6E3FFC1012710E174786814012324BF19B0C';
$this->assertEquals(substr($sig, 0, 64), $R);
$this->assertEquals(substr($sig, 64), $S);
$this->assertTrue($key->verify($msg, $sig));
}
static $secret = '0000000000000000000000000000000000000000000000000000000000000000';
public function test_eddsa_keypair_can_be_created_with_keyFromSecret_or_keyFromPublic() {
$ed25519 = new EdDSA("ed25519");
$pair = $ed25519->keyFromSecret(self::$secret);
$pubKey = $ed25519->keyFromPublic( toHex($pair->pubBytes()) );
$this->assertTrue( is_a($pubKey->pub(), $ed25519->pointClass) );
$this->assertTrue( $pubKey->pub()->eq($pair->pub()));
}
public function test_eddsa_keypair_getSecret_returns_bytes_with_optional_encoding() {
$ed25519 = new EdDSA("ed25519");
$pair = $ed25519->keyFromSecret(self::$secret);
$this->assertTrue( is_array($pair->getSecret()) );
$this->assertTrue( $pair->getSecret('hex') == self::$secret);
}
public function test_eddsa_keypair_getPub_returns_bytes_with_optional_encoding() {
$ed25519 = new EdDSA("ed25519");
$pair = $ed25519->keyFromSecret(self::$secret);
$this->assertTrue( is_array($pair->getPublic()) );
$this->assertEquals( $pair->getPublic('hex'),
'3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29');
}
}
+68
View File
@@ -0,0 +1,68 @@
<?php
require_once __DIR__ . "/../vendor/autoload.php";
class HmacDRBGTest extends \PHPUnit\Framework\TestCase {
public function test_should_support_hmac_drbg_sha256() {
function doDrbg($opt) {
$drbg = new \Elliptic\HmacDRBG([
"hash" => ["algo" => 'sha256', 'outSize' => 256, 'hmacStrength' => 192],//hash.sha256,
"entropy" => $opt["entropy"],
"nonce" => $opt["nonce"],
"pers" => $opt["pers"]
]);
return $drbg->generate($opt["size"], 'hex');
}
$test = [
[
"entropy" => 'totally random0123456789',
"nonce" => 'secret nonce',
"pers" => 'my drbg',
"size" => 32,
"res" => '018ec5f8e08c41e5ac974eb129ac297c5388ee1864324fa13d9b15cf98d9a157'
],
[
"entropy" => 'totally random0123456789',
"nonce" => 'secret nonce',
"pers" => null,
"size" => 32,
"res" => 'ed5d61ecf0ef38258e62f03bbb49f19f2cd07ba5145a840d83b134d5963b3633'
]
];
for ($i = 0; $i < count($test); $i++)
$this->assertEquals(doDrbg($test[$i]), $test[$i]["res"]);
}
/**
* @dataProvider NISTVector
*/
public function test_should_not_fail_at_NIST_vector($opt) {
$drbg = new \Elliptic\HmacDRBG([
"hash" => ["algo" => 'sha256', 'outSize' => 256, 'hmacStrength' => 192],//hash.sha256,
"entropy" => $opt["entropy"],
"entropyEnc" => 'hex',
"nonce" => $opt["nonce"],
"nonceEnc" => 'hex',
"pers" => $opt["pers"],
"persEnc" => 'hex'
]);
for ($i = 0; $i < count($opt["add"]); $i++) {
$last = $drbg->generate(strlen($opt["expected"]) / 2,
'hex',
$opt["add"][$i],
'hex');
}
$this->assertEquals($last, $opt["expected"]);
}
function NISTVector() {
$data = json_decode(file_get_contents(__DIR__."/fixtures/hmac-drbg-nist.json"), true);
$cases = array();
for($i = 0; $i < count($data); ++$i) {
$cases[ $data[$i]["name"] ] = [ $data[$i] ];
}
return $cases;
}
}
+104
View File
@@ -0,0 +1,104 @@
<?php
require_once __DIR__ . "/../vendor/autoload.php";
class PointCodecTest extends \PHPUnit\Framework\TestCase {
function makeShortTest($definition) {
$curve = \Elliptic\Curves::getCurve("secp256k1")->curve;
return function() use($curve, $definition) {
$co = $definition["coordinates"];
$p = $curve->point($co["x"], $co["y"]);
// Encodes as expected
$this->assertEquals($p->encode('hex'), $definition["encoded"]);
$this->assertEquals($p->encodeCompressed('hex'), $definition["compactEncoded"]);
// Decodes as expected
$this->assertTrue($curve->decodePoint($definition["encoded"], 'hex')->eq($p));
$this->assertTrue($curve->decodePoint($definition["compactEncoded"], 'hex')->eq($p));
$this->assertTrue($curve->decodePoint($definition["hybrid"], 'hex')->eq($p));
};
}
function makeMontTest($definition) {
$curve = \Elliptic\Curves::getCurve("curve25519")->curve;
return function() use ($definition, $curve) {
$co = $definition["coordinates"];
$p = $curve->point($co["x"], $co["z"]);
$encoded = $p->encode('hex');
$decoded = $curve->decodePoint($encoded, 'hex');
$this->assertTrue($decoded->eq($p));
$this->assertEquals($encoded, $definition["encoded"]);
};
}
static $shortPointEvenY;
static $shortPointOddY;
public function test_should_throw_when_trying_to_decode_random_bytes() {
$this->expectException(\Exception::class);
\Elliptic\Curves::getCurve("secp256k1")->curve->decodePoint(
'05' .
'79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798');
}
public function test_should_be_able_to_encode_and_decode_a_short_curve_point_with_even_Y() {
$f = $this->makeShortTest(self::$shortPointEvenY);
$f();
}
public function test_should_be_able_to_encode_and_decode_a_short_curve_point_with_odd_Y() {
$f = $this->makeShortTest(self::$shortPointOddY);
$f();
}
public function test_should_be_able_to_encode_and_decode_a_mont_curve_point() {
$f = $this->makeMontTest([
"coordinates" => [
// curve25519.curve.g.mul(new BN('6')).getX().toString(16, 2)
"x" => '26954ccdc99ebf34f8f1dde5e6bb080685fec73640494c28f9fe0bfa8c794531',
"z" => '1'
],
"encoded" =>
'26954ccdc99ebf34f8f1dde5e6bb080685fec73640494c28f9fe0bfa8c794531'
]);
$f();
}
}
PointCodecTest::$shortPointEvenY = [
"coordinates" => [
"x" => '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
"y" => '483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'
],
"compactEncoded" =>
'02' .
'79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
"encoded" =>
'04' .
'79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798' .
'483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8',
"hybrid" =>
'06' .
'79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798' .
'483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'
];
PointCodecTest::$shortPointOddY = [
"coordinates" => [
"x" => 'fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556',
"y" => 'ae12777aacfbb620f3be96017f45c560de80f0f6518fe4a03c870c36b075f297'
],
"compactEncoded" =>
'03' .
'fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556',
"encoded" =>
'04' .
'fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556' .
'ae12777aacfbb620f3be96017f45c560de80f0f6518fe4a03c870c36b075f297',
"hybrid" =>
'07' .
'fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556' .
'ae12777aacfbb620f3be96017f45c560de80f0f6518fe4a03c870c36b075f297'
];
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,332 @@
[
{
"name": "0",
"entropy": "ca851911349384bffe89de1cbdc46e6831e44d34a4fb935ee285dd14b71a7488",
"nonce": "659ba96c601dc69fc902940805ec0ca8",
"pers": null,
"add": [
null,
null
],
"expected": "e528e9abf2dece54d47c7e75e5fe302149f817ea9fb4bee6f4199697d04d5b89d54fbb978a15b5c443c9ec21036d2460b6f73ebad0dc2aba6e624abf07745bc107694bb7547bb0995f70de25d6b29e2d3011bb19d27676c07162c8b5ccde0668961df86803482cb37ed6d5c0bb8d50cf1f50d476aa0458bdaba806f48be9dcb8"
},
{
"name": "1",
"entropy": "79737479ba4e7642a221fcfd1b820b134e9e3540a35bb48ffae29c20f5418ea3",
"nonce": "3593259c092bef4129bc2c6c9e19f343",
"pers": null,
"add": [
null,
null
],
"expected": "cf5ad5984f9e43917aa9087380dac46e410ddc8a7731859c84e9d0f31bd43655b924159413e2293b17610f211e09f770f172b8fb693a35b85d3b9e5e63b1dc252ac0e115002e9bedfb4b5b6fd43f33b8e0eafb2d072e1a6fee1f159df9b51e6c8da737e60d5032dd30544ec51558c6f080bdbdab1de8a939e961e06b5f1aca37"
},
{
"name": "2",
"entropy": "b340907445b97a8b589264de4a17c0bea11bb53ad72f9f33297f05d2879d898d",
"nonce": "65cb27735d83c0708f72684ea58f7ee5",
"pers": null,
"add": [
null,
null
],
"expected": "75183aaaf3574bc68003352ad655d0e9ce9dd17552723b47fab0e84ef903694a32987eeddbdc48efd24195dbdac8a46ba2d972f5808f23a869e71343140361f58b243e62722088fe10a98e43372d252b144e00c89c215a76a121734bdc485486f65c0b16b8963524a3a70e6f38f169c12f6cbdd169dd48fe4421a235847a23ff"
},
{
"name": "3",
"entropy": "8e159f60060a7d6a7e6fe7c9f769c30b98acb1240b25e7ee33f1da834c0858e7",
"nonce": "c39d35052201bdcce4e127a04f04d644",
"pers": null,
"add": [
null,
null
],
"expected": "62910a77213967ea93d6457e255af51fc79d49629af2fccd81840cdfbb4910991f50a477cbd29edd8a47c4fec9d141f50dfde7c4d8fcab473eff3cc2ee9e7cc90871f180777a97841597b0dd7e779eff9784b9cc33689fd7d48c0dcd341515ac8fecf5c55a6327aea8d58f97220b7462373e84e3b7417a57e80ce946d6120db5"
},
{
"name": "4",
"entropy": "74755f196305f7fb6689b2fe6835dc1d81484fc481a6b8087f649a1952f4df6a",
"nonce": "c36387a544a5f2b78007651a7b74b749",
"pers": null,
"add": [
null,
null
],
"expected": "b2896f3af4375dab67e8062d82c1a005ef4ed119d13a9f18371b1b873774418684805fd659bfd69964f83a5cfe08667ddad672cafd16befffa9faed49865214f703951b443e6dca22edb636f3308380144b9333de4bcb0735710e4d9266786342fc53babe7bdbe3c01a3addb7f23c63ce2834729fabbd419b47beceb4a460236"
},
{
"name": "5",
"entropy": "4b222718f56a3260b3c2625a4cf80950b7d6c1250f170bd5c28b118abdf23b2f",
"nonce": "7aed52d0016fcaef0b6492bc40bbe0e9",
"pers": null,
"add": [
null,
null
],
"expected": "a6da029b3665cd39fd50a54c553f99fed3626f4902ffe322dc51f0670dfe8742ed48415cf04bbad5ed3b23b18b7892d170a7dcf3ef8052d5717cb0c1a8b3010d9a9ea5de70ae5356249c0e098946030c46d9d3d209864539444374d8fbcae068e1d6548fa59e6562e6b2d1acbda8da0318c23752ebc9be0c1c1c5b3cf66dd967"
},
{
"name": "6",
"entropy": "b512633f27fb182a076917e39888ba3ff35d23c3742eb8f3c635a044163768e0",
"nonce": "e2c39b84629a3de5c301db5643af1c21",
"pers": null,
"add": [
null,
null
],
"expected": "fb931d0d0194a97b48d5d4c231fdad5c61aedf1c3a55ac24983ecbf38487b1c93396c6b86ff3920cfa8c77e0146de835ea5809676e702dee6a78100da9aa43d8ec0bf5720befa71f82193205ac2ea403e8d7e0e6270b366dc4200be26afd9f63b7e79286a35c688c57cbff55ac747d4c28bb80a2b2097b3b62ea439950d75dff"
},
{
"name": "7",
"entropy": "aae3ffc8605a975befefcea0a7a286642bc3b95fb37bd0eb0585a4cabf8b3d1e",
"nonce": "9504c3c0c4310c1c0746a036c91d9034",
"pers": null,
"add": [
null,
null
],
"expected": "2819bd3b0d216dad59ddd6c354c4518153a2b04374b07c49e64a8e4d055575dfbc9a8fcde68bd257ff1ba5c6000564b46d6dd7ecd9c5d684fd757df62d85211575d3562d7814008ab5c8bc00e7b5a649eae2318665b55d762de36eba00c2906c0e0ec8706edb493e51ca5eb4b9f015dc932f262f52a86b11c41e9a6d5b3bd431"
},
{
"name": "8",
"entropy": "b9475210b79b87180e746df704b3cbc7bf8424750e416a7fbb5ce3ef25a82cc6",
"nonce": "24baf03599c10df6ef44065d715a93f7",
"pers": null,
"add": [
null,
null
],
"expected": "ae12d784f796183c50db5a1a283aa35ed9a2b685dacea97c596ff8c294906d1b1305ba1f80254eb062b874a8dfffa3378c809ab2869aa51a4e6a489692284a25038908a347342175c38401193b8afc498077e10522bec5c70882b7f760ea5946870bd9fc72961eedbe8bff4fd58c7cc1589bb4f369ed0d3bf26c5bbc62e0b2b2"
},
{
"name": "9",
"entropy": "27838eb44ceccb4e36210703ebf38f659bc39dd3277cd76b7a9bcd6bc964b628",
"nonce": "39cfe0210db2e7b0eb52a387476e7ea1",
"pers": null,
"add": [
null,
null
],
"expected": "e5e72a53605d2aaa67832f97536445ab774dd9bff7f13a0d11fd27bf6593bfb52309f2d4f09d147192199ea584503181de87002f4ee085c7dc18bf32ce5315647a3708e6f404d6588c92b2dda599c131aa350d18c747b33dc8eda15cf40e95263d1231e1b4b68f8d829f86054d49cfdb1b8d96ab0465110569c8583a424a099a"
},
{
"name": "10",
"entropy": "d7129e4f47008ad60c9b5d081ff4ca8eb821a6e4deb91608bf4e2647835373a5",
"nonce": "a72882773f78c2fc4878295840a53012",
"pers": null,
"add": [
null,
null
],
"expected": "0cbf48585c5de9183b7ff76557f8fc9ebcfdfde07e588a8641156f61b7952725bbee954f87e9b937513b16bba0f2e523d095114658e00f0f3772175acfcb3240a01de631c19c5a834c94cc58d04a6837f0d2782fa53d2f9f65178ee9c837222494c799e64c60406069bd319549b889fa00a0032dd7ba5b1cc9edbf58de82bfcd"
},
{
"name": "11",
"entropy": "67fe5e300c513371976c80de4b20d4473889c9f1214bce718bc32d1da3ab7532",
"nonce": "e256d88497738a33923aa003a8d7845c",
"pers": null,
"add": [
null,
null
],
"expected": "b44660d64ef7bcebc7a1ab71f8407a02285c7592d755ae6766059e894f694373ed9c776c0cfc8594413eefb400ed427e158d687e28da3ecc205e0f7370fb089676bbb0fa591ec8d916c3d5f18a3eb4a417120705f3e2198154cd60648dbfcfc901242e15711cacd501b2c2826abe870ba32da785ed6f1fdc68f203d1ab43a64f"
},
{
"name": "12",
"entropy": "de8142541255c46d66efc6173b0fe3ffaf5936c897a3ce2e9d5835616aafa2cb",
"nonce": "d01f9002c407127bc3297a561d89b81d",
"pers": null,
"add": [
null,
null
],
"expected": "64d1020929d74716446d8a4e17205d0756b5264867811aa24d0d0da8644db25d5cde474143c57d12482f6bf0f31d10af9d1da4eb6d701bdd605a8db74fb4e77f79aaa9e450afda50b18d19fae68f03db1d7b5f1738d2fdce9ad3ee9461b58ee242daf7a1d72c45c9213eca34e14810a9fca5208d5c56d8066bab1586f1513de7"
},
{
"name": "13",
"entropy": "4a8e0bd90bdb12f7748ad5f147b115d7385bb1b06aee7d8b76136a25d779bcb7",
"nonce": "7f3cce4af8c8ce3c45bdf23c6b181a00",
"pers": null,
"add": [
null,
null
],
"expected": "320c7ca4bbeb7af977bc054f604b5086a3f237aa5501658112f3e7a33d2231f5536d2c85c1dad9d9b0bf7f619c81be4854661626839c8c10ae7fdc0c0b571be34b58d66da553676167b00e7d8e49f416aacb2926c6eb2c66ec98bffae20864cf92496db15e3b09e530b7b9648be8d3916b3c20a3a779bec7d66da63396849aaf"
},
{
"name": "14",
"entropy": "451ed024bc4b95f1025b14ec3616f5e42e80824541dc795a2f07500f92adc665",
"nonce": "2f28e6ee8de5879db1eccd58c994e5f0",
"pers": null,
"add": [
null,
null
],
"expected": "3fb637085ab75f4e95655faae95885166a5fbb423bb03dbf0543be063bcd48799c4f05d4e522634d9275fe02e1edd920e26d9accd43709cb0d8f6e50aa54a5f3bdd618be23cf73ef736ed0ef7524b0d14d5bef8c8aec1cf1ed3e1c38a808b35e61a44078127c7cb3a8fd7addfa50fcf3ff3bc6d6bc355d5436fe9b71eb44f7fd"
},
{
"name": "0 with additional data",
"entropy": "d3cc4d1acf3dde0c4bd2290d262337042dc632948223d3a2eaab87da44295fbd",
"nonce": "0109b0e729f457328aa18569a9224921",
"pers": null,
"add": [
"3c311848183c9a212a26f27f8c6647e40375e466a0857cc39c4e47575d53f1f6",
"fcb9abd19ccfbccef88c9c39bfb3dd7b1c12266c9808992e305bc3cff566e4e4"
],
"expected": "9c7b758b212cd0fcecd5daa489821712e3cdea4467b560ef5ddc24ab47749a1f1ffdbbb118f4e62fcfca3371b8fbfc5b0646b83e06bfbbab5fac30ea09ea2bc76f1ea568c9be0444b2cc90517b20ca825f2d0eccd88e7175538b85d90ab390183ca6395535d34473af6b5a5b88f5a59ee7561573337ea819da0dcc3573a22974"
},
{
"name": "1 with additional data",
"entropy": "f97a3cfd91faa046b9e61b9493d436c4931f604b22f1081521b3419151e8ff06",
"nonce": "11f3a7d43595357d58120bd1e2dd8aed",
"pers": null,
"add": [
"517289afe444a0fe5ed1a41dbbb5eb17150079bdd31e29cf2ff30034d8268e3b",
"88028d29ef80b4e6f0fe12f91d7449fe75062682e89c571440c0c9b52c42a6e0"
],
"expected": "c6871cff0824fe55ea7689a52229886730450e5d362da5bf590dcf9acd67fed4cb32107df5d03969a66b1f6494fdf5d63d5b4d0d34ea7399a07d0116126d0d518c7c55ba46e12f62efc8fe28a51c9d428e6d371d7397ab319fc73ded4722e5b4f30004032a6128df5e7497ecf82ca7b0a50e867ef6728a4f509a8c859087039c"
},
{
"name": "2 with additional data",
"entropy": "0f2f23d64f481cabec7abb01db3aabf125c3173a044b9bf26844300b69dcac8b",
"nonce": "9a5ae13232b43aa19cfe8d7958b4b590",
"pers": null,
"add": [
"ec4c7a62acab73385f567da10e892ff395a0929f959231a5628188ce0c26e818",
"6b97b8c6b6bb8935e676c410c17caa8042aa3145f856d0a32b641e4ae5298648"
],
"expected": "7480a361058bd9afa3db82c9d7586e42269102013f6ec5c269b6d05f17987847748684766b44918fd4b65e1648622fc0e0954178b0279dfc9fa99b66c6f53e51c4860131e9e0644287a4afe4ca8e480417e070db68008a97c3397e4b320b5d1a1d7e1d18a95cfedd7d1e74997052bf649d132deb9ec53aae7dafdab55e6dae93"
},
{
"name": "3 with additional data",
"entropy": "53c56660c78481be9c63284e005fcc14fbc7fb27732c9bf1366d01a426765a31",
"nonce": "dc7a14d0eb5b0b3534e717a0b3c64614",
"pers": null,
"add": [
"3aa848706ecb877f5bedf4ffc332d57c22e08747a47e75cff6f0fd1316861c95",
"9a401afa739b8f752fddacd291e0b854f5eff4a55b515e20cb319852189d3722"
],
"expected": "5c0eb420e0bf41ce9323e815310e4e8303cd677a8a8b023f31f0d79f0ca15aeb636099a369fd074d69889865eac1b72ab3cbfebdb8cf460b00072802e2ec648b1349a5303be4ccaadd729f1a9ea17482fd026aaeb93f1602bc1404b9853adde40d6c34b844cf148bc088941ecfc1642c8c0b9778e45f3b07e06e21ee2c9e0300"
},
{
"name": "4 with additional data",
"entropy": "f63c804404902db334c54bb298fc271a21d7acd9f770278e089775710bf4fdd7",
"nonce": "3e45009ea9cb2a36ba1aa4bf39178200",
"pers": null,
"add": [
"d165a13dc8cc43f3f0952c3f5d3de4136954d983683d4a3e6d2dc4c89bf23423",
"75106bc86d0336df85097f6af8e80e2da59046a03fa65b06706b8bbc7ffc6785"
],
"expected": "6363139bba32c22a0f5cd23ca6d437b5669b7d432f786b8af445471bee0b2d24c9d5f2f93717cbe00d1f010cc3b9c515fc9f7336d53d4d26ba5c0d76a90186663c8582eb739c7b6578a3328bf68dc2cec2cd89b3a90201f6993adcc854df0f5c6974d0f5570765a15fe03dbce28942dd2fd16ba2027e68abac83926969349af8"
},
{
"name": "5 with additional data",
"entropy": "2aaca9147da66c176615726b69e3e851cc3537f5f279fe7344233d8e44cfc99d",
"nonce": "4e171f080af9a6081bee9f183ac9e340",
"pers": null,
"add": [
"d75a2a6eb66c3833e50f5ec3d2e434cf791448d618026d0c360806d120ded669",
"b643b74c15b37612e6577ed7ca2a4c67a78d560af9eb50a4108fca742e87b8d6"
],
"expected": "501dcdc977f4ba856f24eaa4968b374bebb3166b280334cb510232c31ebffde10fa47b7840ef3fe3b77725c2272d3a1d4219baf23e0290c622271edcced58838cf428f0517425d2e19e0d8c89377eecfc378245f283236fafa466c914b99672ceafab369e8889a0c866d8bd639db9fb797254262c6fd44cfa9045ad6340a60ef"
},
{
"name": "6 with additional data",
"entropy": "a2e4cd48a5cf918d6f55942d95fcb4e8465cdc4f77b7c52b6fae5b16a25ca306",
"nonce": "bef036716440db6e6d333d9d760b7ca8",
"pers": null,
"add": [
"bfa591c7287f3f931168f95e38869441d1f9a11035ad8ea625bb61b9ea17591c",
"c00c735463bca215adc372cb892b05e939bf669583341c06d4e31d0e5b363a37"
],
"expected": "e7d136af69926a5421d4266ee0420fd729f2a4f7c295d3c966bdfa05268180b508b8a2852d1b3a06fd2ab3e13c54005123ef319f42d0c6d3a575e6e7e1496cb28aacadbcf83740fba8f35fcee04bb2ed8a51db3d3362b01094a62fb57e33c99a432f29fce6676cffbbcc05107e794e75e44a02d5e6d9d748c5fbff00a0178d65"
},
{
"name": "7 with additional data",
"entropy": "95a67771cba69011a79776e713145d309edae56fad5fd6d41d83eaff89df6e5e",
"nonce": "be5b5164e31ecc51ba6f7c3c5199eb33",
"pers": null,
"add": [
"065f693b229a7c4fd373cd15b3807552dd9bf98c5485cef361949d4e7d774b53",
"9afb62406f0e812c4f156d58b19a656c904813c1b4a45a0029ae7f50731f8014"
],
"expected": "f61b61a6e79a41183e8ed6647899d2dc85cdaf5c3abf5c7f3bf37685946dc28f4923dc842f2d4326bd6ce0d50a84cb3ba869d72a36e246910eba6512ba36cd7ed3a5437c9245b00a344308c792b668b458d3c3e16dee2fbec41867da31084d46d8ec168de2148ef64fc5b72069abf5a6ada1ead2b7146bb793ff1c9c3690fa56"
},
{
"name": "8 with additional data",
"entropy": "a459e1815cbca4514ec8094d5ab2414a557ba6fe10e613c345338d0521e4bf90",
"nonce": "62221392e2552e76cd0d36df6e6068eb",
"pers": null,
"add": [
"0a3642b02b23b3ef62c701a63401124022f5b896de86dab6e6c7451497aa1dcc",
"c80514865901371c45ba92d9f95d50bb7c9dd1768cb3dfbc45b968da94965c6e"
],
"expected": "464e6977b8adaef307c9623e41c357013249c9ffd77f405f3925cebb69f151ce8fbb6a277164002aee7858fc224f6499042aa1e6322deee9a5d133c31d640e12a7487c731ba03ad866a24675badb1d79220c40be689f79c2a0be93cb4dada3e0eac4ab140cb91998b6f11953e68f2319b050c40f71c34de9905ae41b2de1c2f6"
},
{
"name": "9 with additional data",
"entropy": "252c2cad613e002478162861880979ee4e323025eebb6fb2e0aa9f200e28e0a1",
"nonce": "d001bc9a8f2c8c242e4369df0c191989",
"pers": null,
"add": [
"9bcfc61cb2bc000034bb3db980eb47c76fb5ecdd40553eff113368d639b947fd",
"8b0565c767c2610ee0014582e9fbecb96e173005b60e9581503a6dca5637a26e"
],
"expected": "e96c15fe8a60692b0a7d67171e0195ff6e1c87aab844221e71700d1bbee75feea695f6a740c9760bbe0e812ecf4061d8f0955bc0195e18c4fd1516ebca50ba6a6db86881737dbab8321707675479b87611db6af2c97ea361a5484555ead454defb1a64335de964fc803d40f3a6f057893d2afc25725754f4f00abc51920743dc"
},
{
"name": "10 with additional data",
"entropy": "8be0ca6adc8b3870c9d69d6021bc1f1d8eb9e649073d35ee6c5aa0b7e56ad8a5",
"nonce": "9d1265f7d51fdb65377f1e6edd6ae0e4",
"pers": null,
"add": [
"da86167ac997c406bb7979f423986a84ec6614d6caa7afc10aff0699a9b2cf7f",
"e4baa3c555950b53e2bfdba480cb4c94b59381bac1e33947e0c22e838a9534cf"
],
"expected": "64384ecc4ea6b458efc227ca697eac5510092265520c0a0d8a0ccf9ed3ca9d58074671188c6a7ad16d0b050cdc072c125d7298d3a31d9f044a9ee40da0089a84fea28cc7f05f1716db952fad29a0e779635cb7a912a959be67be2f0a4170aace2981802e2ff6467e5b46f0ffbff3b42ba5935fd553c82482ac266acf1cd247d7"
},
{
"name": "11 with additional data",
"entropy": "d43a75b6adf26d60322284cb12ac38327792442aa8f040f60a2f331b33ac4a8f",
"nonce": "0682f8b091f811afacaacaec9b04d279",
"pers": null,
"add": [
"7fd3b8f512940da7de5d80199d9a7b42670c04a945775a3dba869546cbb9bc65",
"2575db20bc7aafc2a90a5dabab760db851d754777bc9f05616af1858b24ff3da"
],
"expected": "0da7a8dc73c163014bf0841913d3067806456bbca6d5de92b85534c6545467313648d71ef17c923d090dc92cff8d4d1a9a2bb63e001dc2e8ab1a597999be3d6cf70ff63fee9985801395fbd4f4990430c4259fcae4fa1fcd73dc3187ccc102d04af7c07532885e5a226fc42809c48f22eecf4f6ab996ae4fcb144786957d9f41"
},
{
"name": "12 with additional data",
"entropy": "64352f236af5d32067a529a8fd05ba00a338c9de306371a0b00c36e610a48d18",
"nonce": "df99ed2c7608c870624b962a5dc68acd",
"pers": null,
"add": [
"da416335e7aaf60cf3d06fb438735ce796aad09034f8969c8f8c3f81e32fef24",
"a28c07c21a2297311adf172c19e83ca0a87731bdffb80548978d2d1cd82cf8a3"
],
"expected": "132b9f25868729e3853d3c51f99a3b5fae6d4204bea70890daf62e042b776a526c8fb831b80a6d5d3f153237df1fd39b6fd9137963f5516d9cdd4e3f9195c46e9972c15d3edc6606e3368bde1594977fb88d0ca6e6f5f3d057ccadc7d7dab77dfc42658a1e972aa446b20d418286386a52dfc1c714d2ac548713268b0b709729"
},
{
"name": "13 with additional data",
"entropy": "282f4d2e05a2cd30e9087f5633089389449f04bac11df718c90bb351cd3653a5",
"nonce": "90a7daf3c0de9ea286081efc4a684dfb",
"pers": null,
"add": [
"2630b4ccc7271cc379cb580b0aaede3d3aa8c1c7ba002cf791f0752c3d739007",
"c31d69de499f1017be44e3d4fa77ecebc6a9b9934749fcf136f267b29115d2cc"
],
"expected": "c899094520e0197c37b91dd50778e20a5b950decfb308d39f1db709447ae48f6101d9abe63a783fbb830eec1d359a5f61a2013728966d349213ee96382614aa4135058a967627183810c6622a2158cababe3b8ab99169c89e362108bf5955b4ffc47440f87e4bad0d36bc738e737e072e64d8842e7619f1be0af1141f05afe2d"
},
{
"name": "14 with additional data",
"entropy": "13c752b9e745ce77bbc7c0dbda982313d3fe66f903e83ebd8dbe4ff0c11380e9",
"nonce": "f1a533095d6174164bd7c82532464ae7",
"pers": null,
"add": [
"4f53db89b9ba7fc00767bc751fb8f3c103fe0f76acd6d5c7891ab15b2b7cf67c",
"582c2a7d34679088cca6bd28723c99aac07db46c332dc0153d1673256903b446"
],
"expected": "6311f4c0c4cd1f86bd48349abb9eb930d4f63df5e5f7217d1d1b91a71d8a6938b0ad2b3e897bd7e3d8703db125fab30e03464fad41e5ddf5bf9aeeb5161b244468cfb26a9d956931a5412c97d64188b0da1bd907819c686f39af82e91cfeef0cbffb5d1e229e383bed26d06412988640706815a6e820796876f416653e464961"
}
]
File diff suppressed because it is too large Load Diff