This commit is contained in:
2025-10-09 17:41:57 +00:00
commit 7a3b2960f8
146 changed files with 34948 additions and 0 deletions
+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