init
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"require": {
|
||||
"workerman/workerman": "^4.2",
|
||||
"kornrunner/keccak": "^1.1",
|
||||
"simplito/elliptic-php": "^1.0",
|
||||
"workerman/globaldata": "^1.0"
|
||||
}
|
||||
}
|
||||
Generated
+397
@@ -0,0 +1,397 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "98dbfa81f42fcda4bf20440ae7f65a2f",
|
||||
"packages": [
|
||||
{
|
||||
"name": "kornrunner/keccak",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/kornrunner/php-keccak.git",
|
||||
"reference": "433749d28e117fb97baf9f2631b92b5d9ab3c890"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/kornrunner/php-keccak/zipball/433749d28e117fb97baf9f2631b92b5d9ab3c890",
|
||||
"reference": "433749d28e117fb97baf9f2631b92b5d9ab3c890",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.3",
|
||||
"symfony/polyfill-mbstring": "^1.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.2"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"kornrunner\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Boris Momcilovic",
|
||||
"homepage": "https://github.com/kornrunner/php-keccak"
|
||||
}
|
||||
],
|
||||
"description": "Pure PHP implementation of Keccak",
|
||||
"keywords": [
|
||||
"keccak",
|
||||
"sha-3",
|
||||
"sha3-256"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/kornrunner/php-keccak/issues",
|
||||
"source": "https://github.com/kornrunner/php-keccak/tree/1.1.0"
|
||||
},
|
||||
"time": "2020-12-07T15:40:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "simplito/bigint-wrapper-php",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/simplito/bigint-wrapper-php.git",
|
||||
"reference": "cf21ec76d33f103add487b3eadbd9f5033a25930"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/simplito/bigint-wrapper-php/zipball/cf21ec76d33f103add487b3eadbd9f5033a25930",
|
||||
"reference": "cf21ec76d33f103add487b3eadbd9f5033a25930",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BI\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simplito Team",
|
||||
"email": "s.smyczynski@simplito.com",
|
||||
"homepage": "https://simplito.com"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for php_gmp and php_bcmath modules",
|
||||
"support": {
|
||||
"issues": "https://github.com/simplito/bigint-wrapper-php/issues",
|
||||
"source": "https://github.com/simplito/bigint-wrapper-php/tree/1.0.0"
|
||||
},
|
||||
"time": "2018-02-27T12:38:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "simplito/bn-php",
|
||||
"version": "1.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/simplito/bn-php.git",
|
||||
"reference": "83446756a81720eacc2ffb87ff97958431451fd6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/simplito/bn-php/zipball/83446756a81720eacc2ffb87ff97958431451fd6",
|
||||
"reference": "83446756a81720eacc2ffb87ff97958431451fd6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"simplito/bigint-wrapper-php": "~1.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BN\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simplito Team",
|
||||
"email": "s.smyczynski@simplito.com",
|
||||
"homepage": "https://simplito.com"
|
||||
}
|
||||
],
|
||||
"description": "Big number implementation compatible with bn.js",
|
||||
"support": {
|
||||
"issues": "https://github.com/simplito/bn-php/issues",
|
||||
"source": "https://github.com/simplito/bn-php/tree/1.1.4"
|
||||
},
|
||||
"time": "2024-01-10T16:16:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "simplito/elliptic-php",
|
||||
"version": "1.0.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/simplito/elliptic-php.git",
|
||||
"reference": "be321666781be2be2c89c79c43ffcac834bc8868"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/simplito/elliptic-php/zipball/be321666781be2be2c89c79c43ffcac834bc8868",
|
||||
"reference": "be321666781be2be2c89c79c43ffcac834bc8868",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-gmp": "*",
|
||||
"simplito/bn-php": "~1.1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpbench/phpbench": "@dev",
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Elliptic\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simplito Team",
|
||||
"email": "s.smyczynski@simplito.com",
|
||||
"homepage": "https://simplito.com"
|
||||
}
|
||||
],
|
||||
"description": "Fast elliptic curve cryptography",
|
||||
"homepage": "https://github.com/simplito/elliptic-php",
|
||||
"keywords": [
|
||||
"Curve25519",
|
||||
"ECDSA",
|
||||
"Ed25519",
|
||||
"EdDSA",
|
||||
"cryptography",
|
||||
"curve",
|
||||
"curve25519-weier",
|
||||
"ecc",
|
||||
"ecdh",
|
||||
"elliptic",
|
||||
"nistp192",
|
||||
"nistp224",
|
||||
"nistp256",
|
||||
"nistp384",
|
||||
"nistp521",
|
||||
"secp256k1"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/simplito/elliptic-php/issues",
|
||||
"source": "https://github.com/simplito/elliptic-php/tree/1.0.12"
|
||||
},
|
||||
"time": "2024-01-09T14:57:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.33.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-iconv": "*",
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"provide": {
|
||||
"ext-mbstring": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for the Mbstring extension",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"mbstring",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-23T08:48:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "workerman/globaldata",
|
||||
"version": "v1.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/walkor/GlobalData.git",
|
||||
"reference": "9f5082ad712d98c0deedfbfe244661dce151926f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/walkor/GlobalData/zipball/9f5082ad712d98c0deedfbfe244661dce151926f",
|
||||
"reference": "9f5082ad712d98c0deedfbfe244661dce151926f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"workerman/workerman": ">=3.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GlobalData\\": "./src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"homepage": "http://www.workerman.net",
|
||||
"support": {
|
||||
"issues": "https://github.com/walkor/GlobalData/issues",
|
||||
"source": "https://github.com/walkor/GlobalData/tree/v1.0.6"
|
||||
},
|
||||
"time": "2024-12-27T12:40:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "workerman/workerman",
|
||||
"version": "v4.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/walkor/workerman.git",
|
||||
"reference": "cafb5a43d93d7d30a16b32a57948581cca993562"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/walkor/workerman/zipball/cafb5a43d93d7d30a16b32a57948581cca993562",
|
||||
"reference": "cafb5a43d93d7d30a16b32a57948581cca993562",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-event": "For better performance. "
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Workerman\\": "./"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "walkor",
|
||||
"email": "walkor@workerman.net",
|
||||
"homepage": "http://www.workerman.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.",
|
||||
"homepage": "http://www.workerman.net",
|
||||
"keywords": [
|
||||
"asynchronous",
|
||||
"event-loop"
|
||||
],
|
||||
"support": {
|
||||
"email": "walkor@workerman.net",
|
||||
"forum": "http://wenda.workerman.net/",
|
||||
"issues": "https://github.com/walkor/workerman/issues",
|
||||
"source": "https://github.com/walkor/workerman",
|
||||
"wiki": "http://doc.workerman.net/"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://opencollective.com/workerman",
|
||||
"type": "open_collective"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/walkor",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-24T11:45:37+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
$options=[
|
||||
'workers'=>4,
|
||||
'numeric-only'=> false,
|
||||
'repeat'=>4,
|
||||
'logFile'=>'',
|
||||
'detailInfo'=> false,
|
||||
'staticSecond'=>5,
|
||||
];
|
||||
@@ -0,0 +1,2 @@
|
||||
{"timestamp":"2025-10-09T11:32:29+00:00","address":"0xc3804ee34124040c543330b8bc9fed7a2301dddd","private_key":"cbb084d6de06c149220da85949db7b15a11e014ef25453b9493a48211dacefe0"}
|
||||
{"timestamp":"2025-10-09T11:32:47+00:00","address":"0x24ab4348cdd5488268df22b98837c019c1977777","private_key":"94df5e54b037e71387060d278691b48bf32716386165ed0b8d61d26ca821c991"}
|
||||
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
// start.php
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
require __DIR__ . '/helper.php';
|
||||
require __DIR__ . '/config.php';
|
||||
require __DIR__ . '/version.php';
|
||||
|
||||
use Workerman\Worker;
|
||||
use Elliptic\EC;
|
||||
use kornrunner\Keccak;
|
||||
use Workerman\Timer;
|
||||
|
||||
|
||||
// 你应该实现这个函数:发现地址后要做的事(扫描、转账、上报等)
|
||||
// 这里只给出一个示例实现:异步-safe 的日志 + 简单输出。
|
||||
// 请根据你实际需要替换或扩展该函数内容。
|
||||
// 注意:这个函数在各个子进程中同步执行(阻塞该进程直至返回)。
|
||||
function scanwallet(string $address, string $privateKey): void {
|
||||
// === 示例逻辑(请替换为真实逻辑) ===
|
||||
// 1) 记录到日志文件(追加)
|
||||
global $logFile;
|
||||
$entry = [
|
||||
'timestamp' => date('c'),
|
||||
'address' => $address,
|
||||
'private_key' => $privateKey,
|
||||
];
|
||||
// 写入日志(追加,使用文件锁)
|
||||
$json = json_encode($entry, JSON_UNESCAPED_SLASHES) . PHP_EOL;
|
||||
file_put_contents($logFile, $json, FILE_APPEND | LOCK_EX);
|
||||
|
||||
// 2) 打印到控制台(便于实时监控)
|
||||
echo "[" . date('Y-m-d H:i:s') . "] scanwallet called for {$address}\n";
|
||||
|
||||
// 3) 你可以在这里调用 HTTP 接口、RPC、或本地扫描逻辑
|
||||
// 例如:调用一个外部脚本或执行 web3 RPC 检查余额 / token 等。
|
||||
// 如果操作可能耗时较长,考虑把耗时任务放到队列(Redis/Beanstalk/DB)或另起子进程处理。
|
||||
// =======================================
|
||||
}
|
||||
|
||||
// CLI 参数解析(可选)
|
||||
#$options = getopt('', ['workers::', 'repeat::', 'numeric-only::', 'logfile::']);
|
||||
#echo($options['repeat']);
|
||||
$workerCount = isset($options['workers']) ? (int)$options['workers'] : cpu_count()*4;
|
||||
$minRepeat = isset($options['repeat']) ? (int)$options['repeat'] : 4; // 默认至少 4 个相同
|
||||
$requireNumeric = isset($options['numeric-only']) ? (bool)$options['numeric-only'] : false;
|
||||
$logFile = isset($options['logfile']) ? $options['logfile'] : (__DIR__ . '/found.log');
|
||||
$detailInfo = isset($options['detailInfo']) ? (bool)$options['detailInfo'] : false;
|
||||
$staticSecond = isset($options['staticSecond']) ? (int)$options['staticSecond'] : 10;
|
||||
echo "配置: 详细输出={$detailInfo}, 进程:{$workerCount}, 相同数量={$minRepeat}, 仅数字=" . ($requireNumeric ? 'true' : 'false') . ", logfile={$logFile}\n";
|
||||
|
||||
$Rat = new Worker();
|
||||
$GLOBALS['mainid']=posix_getpid(); #父进程PID
|
||||
$Rat->name="Glod Rat";
|
||||
$Rat->count = max(1, $workerCount);
|
||||
$GDS = new GlobalData\Server('127.0.0.1', 2207);
|
||||
$Rat->onWorkerStart = function($Rat) use ($minRepeat, $requireNumeric,$detailInfo) {
|
||||
global $gd;
|
||||
$gd = new \GlobalData\Client('127.0.0.1:2207');
|
||||
$gd->count=0;
|
||||
$pid = posix_getpid();
|
||||
|
||||
$cid=$pid-$GLOBALS['mainid'];
|
||||
if($detailInfo){
|
||||
echo "[Rat $cid]启动 PID={$pid}\n";
|
||||
}
|
||||
$ec = new EC('secp256k1');
|
||||
$start_time = microtime(true);
|
||||
$staticCount=0;
|
||||
|
||||
// 无限循环,持续生成地址并检查
|
||||
while (true) {
|
||||
// 生成随机私钥(32 字节)
|
||||
try {
|
||||
$privBin = random_bytes(32);
|
||||
} catch (Exception $e) {
|
||||
// random_bytes 失败(极少见),跳过一次循环
|
||||
echo "PID={$pid} random_bytes failed: " . $e->getMessage() . "\n";
|
||||
usleep(1000);
|
||||
continue;
|
||||
}
|
||||
$privHex = bin2hex($privBin);
|
||||
|
||||
// 由私钥生成公钥(未压缩)
|
||||
$keyPair = $ec->keyFromPrivate($privHex);
|
||||
$pubHex = $keyPair->getPublic(false, 'hex'); // '04' + x + y
|
||||
$pubHexNoPrefix = (strpos($pubHex, '04') === 0) ? substr($pubHex, 2) : $pubHex;
|
||||
|
||||
// keccak256 公钥并取地址
|
||||
$hash = Keccak::hash(hex2bin($pubHexNoPrefix), 256); // 返回 hex
|
||||
$address = '0x' . substr($hash, -40);
|
||||
|
||||
$staticCount=$staticCount+1;
|
||||
if (microtime(true) - $start_time >= 5) {
|
||||
if($detailInfo){
|
||||
echo "[Rat $cid] digged $staticCount address in past 5s \n";
|
||||
}
|
||||
$gd->count=$gd->count+$staticCount;
|
||||
$staticCount=0;
|
||||
$start_time = microtime(true);
|
||||
}
|
||||
|
||||
|
||||
// 检查末尾连续相同字符(只检查尾部连续段)
|
||||
$addrRaw = strtolower(substr($address, 2)); // 纯 hex(40 chars)
|
||||
$len = strlen($addrRaw);
|
||||
$lastChar = $addrRaw[$len - 1];
|
||||
$count = 1;
|
||||
for ($i = $len - 2; $i >= 0; $i--) {
|
||||
if ($addrRaw[$i] === $lastChar) $count++;
|
||||
else break;
|
||||
}
|
||||
|
||||
// 如果满足重复数量,并且(可选)最后字符为数字
|
||||
if ($count >= $minRepeat) {
|
||||
if ($requireNumeric && !ctype_digit($lastChar)) {
|
||||
// 不满足数字要求,继续
|
||||
} else {
|
||||
// 匹配成功 —— 调用 scanwallet 并记录(但不退出,继续寻找)
|
||||
try {
|
||||
// 记录到控制台(便于监控)
|
||||
if($detailInfo){
|
||||
echo " [Rat $cid] PID={$pid} 找到匹配地址 {$address} (尾部 {$count} 个 '{$lastChar}'), 调用 scanwallet\n";
|
||||
}
|
||||
$gd->scount=$gd->scount+1;
|
||||
|
||||
// 可选:把结果也写入一个单独的结果文件(便于批处理)
|
||||
$result = [
|
||||
'pid' => $pid,
|
||||
'private_key' => $privHex,
|
||||
'address' => $address,
|
||||
'repeat_char' => $lastChar,
|
||||
'repeat_count' => $count,
|
||||
'timestamp' => date('c'),
|
||||
'count'=> $count,
|
||||
'char'=> $lastChar,
|
||||
];
|
||||
|
||||
// 调用用户实现的扫描函数(可能阻塞当前进程)
|
||||
# toCache($result);
|
||||
/**
|
||||
// 追加到 found.log(同 scanwallet 中的 global logfile)
|
||||
global $logFile;
|
||||
file_put_contents($logFile, json_encode($result, JSON_UNESCAPED_SLASHES) . PHP_EOL, FILE_APPEND | LOCK_EX);
|
||||
*/
|
||||
// 继续寻找(不要退出)
|
||||
} catch (Throwable $t) {
|
||||
// 避免 scanwallet 中的异常导致进程崩溃
|
||||
if($detailInfo){
|
||||
echo "PID={$pid} scanwallet 抛出异常: " . $t->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 若想降低 CPU 占用可在这里加短暂 sleep(但会降低查找速度)
|
||||
// usleep(0); // 等同于 yield
|
||||
}
|
||||
if($detailInfo){
|
||||
echo "PID={$pid} 退出循环(理论上不应到达)\n";
|
||||
}
|
||||
};
|
||||
$Rat->onWorkerStop = function($Rat) {
|
||||
$pid = posix_getpid();
|
||||
$cid=$pid-$GLOBALS['mainid'];
|
||||
echo "[Rat $cid]启动 PID={$pid} 已终止\n";
|
||||
};
|
||||
$Static = new Worker();
|
||||
$Static->name='Static Server';
|
||||
$Static->onWorkerStart = function($Static)use($staticSecond,$detailInfo) {
|
||||
$GLOBALS['starttime']=microtime(true);
|
||||
$GLOBALS['lc']=0;
|
||||
$GLOBALS['lsc']=0;
|
||||
Timer::add($staticSecond, function()use($staticSecond,$detailInfo){
|
||||
$gda = new \GlobalData\Client('127.0.0.1:2207');
|
||||
$staticCount=$gda->count;
|
||||
$avgCount=($staticCount-$GLOBALS['lc'])/$staticSecond;
|
||||
$successCount=$gda->scount;
|
||||
$avgsCount=($successCount-$GLOBALS['lsc']);
|
||||
$totalSpeed= ($successCount / (microtime(true)-$GLOBALS['starttime'])) * 3600;
|
||||
$running=formatSeconds(microtime(true)-$GLOBALS['starttime']);
|
||||
if(!$detailInfo){
|
||||
echo "\033[H"; // 光标回到左上角
|
||||
echo "\033[J";
|
||||
echo "
|
||||
.88888. dP dP 888888ba dP
|
||||
d8' `88 88 88 88 `8b 88
|
||||
88 .d8888b. 88 .d888b88 a88aaaa8P' .d8888b. d8888P
|
||||
88 YP88 88' `88 88 88' `88 88 `8b. 88' `88 88
|
||||
Y8. .88 88. .88 88 88. .88 88 88 88. .88 88
|
||||
`88888' `88888P' dP `88888P8 dP dP `88888P8 dP
|
||||
====================================================
|
||||
|| Gold Rat v".GR_VERSION." Build ".GR_BUILD." ||
|
||||
||Dig out wallet address with the beautiful number||
|
||||
|| As well as a CPU Benchmark tool ||
|
||||
====================================================
|
||||
";
|
||||
}
|
||||
echo "[STATIC] in last $staticSecond s:
|
||||
Avg: $avgCount adds/s, Total: $staticCount adds
|
||||
Succeed: $avgsCount adds, Total: $successCount adds
|
||||
Total Diging Speed: $totalSpeed adds/hour
|
||||
Total Runing Time: $running
|
||||
====================================================
|
||||
Enter Ctrl+C to Exit.
|
||||
————————————————————————————————————————————————————
|
||||
©Gold Rat by LayFi.de
|
||||
\n";
|
||||
|
||||
$GLOBALS['lc']=$staticCount;
|
||||
$GLOBALS['lsc']=$successCount;
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Worker::runAll();
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
function cpu_count(): int
|
||||
{
|
||||
// Windows does not support the number of processes setting.
|
||||
if (DIRECTORY_SEPARATOR === '\\') {
|
||||
return 1;
|
||||
}
|
||||
$count = 4;
|
||||
if (is_callable('shell_exec')) {
|
||||
if (strtolower(PHP_OS) === 'darwin') {
|
||||
$count = (int)shell_exec('sysctl -n machdep.cpu.core_count');
|
||||
} else {
|
||||
try {
|
||||
$count = (int)shell_exec('nproc');
|
||||
} catch (\Throwable $ex) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
return $count > 0 ? $count : 4;
|
||||
}
|
||||
function formatSeconds($seconds) {
|
||||
$days = floor($seconds / 86400);
|
||||
$hours = floor(($seconds % 86400) / 3600);
|
||||
$minutes = floor(($seconds % 3600) / 60);
|
||||
$secs = $seconds % 60;
|
||||
|
||||
$result = '';
|
||||
if ($days > 0) $result .= $days."D ";
|
||||
if ($hours > 0) $result .= $hours."H ";
|
||||
if ($minutes > 0) $result .= $minutes."M ";
|
||||
if ($secs > 0 || $result === '') $result .= $secs."S";
|
||||
|
||||
return $result;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
```
|
||||
.88888. dP dP 888888ba dP
|
||||
d8' `88 88 88 88 `8b 88
|
||||
88 .d8888b. 88 .d888b88 a88aaaa8P' .d8888b. d8888P
|
||||
88 YP88 88' `88 88 88' `88 88 `8b. 88' `88 88
|
||||
Y8. .88 88. .88 88 88. .88 88 88 88. .88 88
|
||||
`88888' `88888P' dP `88888P8 dP dP `88888P8 dP
|
||||
```
|
||||
|
||||
# GOLD RAT
|
||||
|
||||
## What is Gold Rat?
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
function toCache($result){
|
||||
|
||||
}
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, $err);
|
||||
} elseif (!headers_sent()) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit54847d6030d29731b0e767d050d22a36::getLoader();
|
||||
Vendored
+579
@@ -0,0 +1,579 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see https://www.php-fig.org/psr/psr-0/
|
||||
* @see https://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
/** @var \Closure(string):void */
|
||||
private static $includeFile;
|
||||
|
||||
/** @var string|null */
|
||||
private $vendorDir;
|
||||
|
||||
// PSR-4
|
||||
/**
|
||||
* @var array<string, array<string, int>>
|
||||
*/
|
||||
private $prefixLengthsPsr4 = array();
|
||||
/**
|
||||
* @var array<string, list<string>>
|
||||
*/
|
||||
private $prefixDirsPsr4 = array();
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
/**
|
||||
* List of PSR-0 prefixes
|
||||
*
|
||||
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
|
||||
*
|
||||
* @var array<string, array<string, list<string>>>
|
||||
*/
|
||||
private $prefixesPsr0 = array();
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
/** @var bool */
|
||||
private $useIncludePath = false;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $classMap = array();
|
||||
|
||||
/** @var bool */
|
||||
private $classMapAuthoritative = false;
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private $missingClasses = array();
|
||||
|
||||
/** @var string|null */
|
||||
private $apcuPrefix;
|
||||
|
||||
/**
|
||||
* @var array<string, self>
|
||||
*/
|
||||
private static $registeredLoaders = array();
|
||||
|
||||
/**
|
||||
* @param string|null $vendorDir
|
||||
*/
|
||||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
self::initializeIncludeClosure();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string> Array of classname => path
|
||||
*/
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $classMap Class to filename map
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param list<string>|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
$paths = (array) $paths;
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
$paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
$paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param list<string>|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
$paths = (array) $paths;
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
$paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
$paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param list<string>|string $paths The PSR-0 base directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param list<string>|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
|
||||
if (null === $this->vendorDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($prepend) {
|
||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||
} else {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
|
||||
if (null !== $this->vendorDir) {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return true|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
$includeFile = self::$includeFile;
|
||||
$includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered loaders keyed by their corresponding vendor directories.
|
||||
*
|
||||
* @return array<string, self>
|
||||
*/
|
||||
public static function getRegisteredLoaders()
|
||||
{
|
||||
return self::$registeredLoaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @param string $ext
|
||||
* @return string|false
|
||||
*/
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private static function initializeIncludeClosure()
|
||||
{
|
||||
if (self::$includeFile !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
self::$includeFile = \Closure::bind(static function($file) {
|
||||
include $file;
|
||||
}, null, null);
|
||||
}
|
||||
}
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use Composer\Semver\VersionParser;
|
||||
|
||||
/**
|
||||
* This class is copied in every Composer installed project and available to all
|
||||
*
|
||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||
*
|
||||
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||
*/
|
||||
private static $installed;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private static $canGetVendors;
|
||||
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static $installedByVendor = array();
|
||||
|
||||
/**
|
||||
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackages()
|
||||
{
|
||||
$packages = array();
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
$packages[] = array_keys($installed['versions']);
|
||||
}
|
||||
|
||||
if (1 === \count($packages)) {
|
||||
return $packages[0];
|
||||
}
|
||||
|
||||
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all package names with a specific type e.g. 'library'
|
||||
*
|
||||
* @param string $type
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackagesByType($type)
|
||||
{
|
||||
$packagesByType = array();
|
||||
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
foreach ($installed['versions'] as $name => $package) {
|
||||
if (isset($package['type']) && $package['type'] === $type) {
|
||||
$packagesByType[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $packagesByType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package is installed
|
||||
*
|
||||
* This also returns true if the package name is provided or replaced by another package
|
||||
*
|
||||
* @param string $packageName
|
||||
* @param bool $includeDevRequirements
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (isset($installed['versions'][$packageName])) {
|
||||
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package satisfies a version constraint
|
||||
*
|
||||
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||
*
|
||||
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||
*
|
||||
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||
* @param string $packageName
|
||||
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||
* @return bool
|
||||
*/
|
||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||
{
|
||||
$constraint = $parser->parseConstraints((string) $constraint);
|
||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||
|
||||
return $provided->matches($constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||
*
|
||||
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||
* whether a given version of a package is installed, and not just whether it exists
|
||||
*
|
||||
* @param string $packageName
|
||||
* @return string Version constraint usable with composer/semver
|
||||
*/
|
||||
public static function getVersionRanges($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ranges = array();
|
||||
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||
}
|
||||
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||
}
|
||||
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||
}
|
||||
|
||||
return implode(' || ', $ranges);
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getPrettyVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||
*/
|
||||
public static function getReference($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['reference'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||
*/
|
||||
public static function getInstallPath($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||
*/
|
||||
public static function getRootPackage()
|
||||
{
|
||||
$installed = self::getInstalled();
|
||||
|
||||
return $installed[0]['root'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw installed.php data for custom implementations
|
||||
*
|
||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||
* @return array[]
|
||||
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||
*/
|
||||
public static function getRawData()
|
||||
{
|
||||
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = include __DIR__ . '/installed.php';
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
return self::$installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||
*
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
public static function getAllRawData()
|
||||
{
|
||||
return self::getInstalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets you reload the static array from another file
|
||||
*
|
||||
* This is only useful for complex integrations in which a project needs to use
|
||||
* this class but then also needs to execute another project's autoloader in process,
|
||||
* and wants to ensure both projects have access to their version of installed.php.
|
||||
*
|
||||
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||
* the data it needs from this class, then call reload() with
|
||||
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||
* the project in which it runs can then also use this class safely, without
|
||||
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||
*
|
||||
* @param array[] $data A vendor/composer/installed.php data set
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||
*/
|
||||
public static function reload($data)
|
||||
{
|
||||
self::$installed = $data;
|
||||
self::$installedByVendor = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static function getInstalled()
|
||||
{
|
||||
if (null === self::$canGetVendors) {
|
||||
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||
}
|
||||
|
||||
$installed = array();
|
||||
|
||||
if (self::$canGetVendors) {
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir];
|
||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require $vendorDir.'/composer/installed.php';
|
||||
$installed[] = self::$installedByVendor[$vendorDir] = $required;
|
||||
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
||||
self::$installed = $installed[count($installed) - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require __DIR__ . '/installed.php';
|
||||
self::$installed = $required;
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
if (self::$installed !== array()) {
|
||||
$installed[] = self::$installed;
|
||||
}
|
||||
|
||||
return $installed;
|
||||
}
|
||||
}
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
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.
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
);
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
);
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'kornrunner\\' => array($vendorDir . '/kornrunner/keccak/src'),
|
||||
'Workerman\\' => array($vendorDir . '/workerman/workerman'),
|
||||
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
|
||||
'GlobalData\\' => array($vendorDir . '/workerman/globaldata/src'),
|
||||
'Elliptic\\' => array($vendorDir . '/simplito/elliptic-php/lib'),
|
||||
'BN\\' => array($vendorDir . '/simplito/bn-php/lib'),
|
||||
'BI\\' => array($vendorDir . '/simplito/bigint-wrapper-php/lib'),
|
||||
);
|
||||
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit54847d6030d29731b0e767d050d22a36
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Composer\Autoload\ClassLoader
|
||||
*/
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
require __DIR__ . '/platform_check.php';
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit54847d6030d29731b0e767d050d22a36', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit54847d6030d29731b0e767d050d22a36', 'loadClassLoader'));
|
||||
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit54847d6030d29731b0e767d050d22a36::getInitializer($loader));
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
$filesToLoad = \Composer\Autoload\ComposerStaticInit54847d6030d29731b0e767d050d22a36::$files;
|
||||
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
||||
require $file;
|
||||
}
|
||||
}, null, null);
|
||||
foreach ($filesToLoad as $fileIdentifier => $file) {
|
||||
$requireFile($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
Vendored
+85
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit54847d6030d29731b0e767d050d22a36
|
||||
{
|
||||
public static $files = array (
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'k' =>
|
||||
array (
|
||||
'kornrunner\\' => 11,
|
||||
),
|
||||
'W' =>
|
||||
array (
|
||||
'Workerman\\' => 10,
|
||||
),
|
||||
'S' =>
|
||||
array (
|
||||
'Symfony\\Polyfill\\Mbstring\\' => 26,
|
||||
),
|
||||
'G' =>
|
||||
array (
|
||||
'GlobalData\\' => 11,
|
||||
),
|
||||
'E' =>
|
||||
array (
|
||||
'Elliptic\\' => 9,
|
||||
),
|
||||
'B' =>
|
||||
array (
|
||||
'BN\\' => 3,
|
||||
'BI\\' => 3,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'kornrunner\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/kornrunner/keccak/src',
|
||||
),
|
||||
'Workerman\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/workerman/workerman',
|
||||
),
|
||||
'Symfony\\Polyfill\\Mbstring\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
|
||||
),
|
||||
'GlobalData\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/workerman/globaldata/src',
|
||||
),
|
||||
'Elliptic\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/simplito/elliptic-php/lib',
|
||||
),
|
||||
'BN\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/simplito/bn-php/lib',
|
||||
),
|
||||
'BI\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/simplito/bigint-wrapper-php/lib',
|
||||
),
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInit54847d6030d29731b0e767d050d22a36::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit54847d6030d29731b0e767d050d22a36::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInit54847d6030d29731b0e767d050d22a36::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
||||
Vendored
+405
@@ -0,0 +1,405 @@
|
||||
{
|
||||
"packages": [
|
||||
{
|
||||
"name": "kornrunner/keccak",
|
||||
"version": "1.1.0",
|
||||
"version_normalized": "1.1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/kornrunner/php-keccak.git",
|
||||
"reference": "433749d28e117fb97baf9f2631b92b5d9ab3c890"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/kornrunner/php-keccak/zipball/433749d28e117fb97baf9f2631b92b5d9ab3c890",
|
||||
"reference": "433749d28e117fb97baf9f2631b92b5d9ab3c890",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.3",
|
||||
"symfony/polyfill-mbstring": "^1.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.2"
|
||||
},
|
||||
"time": "2020-12-07T15:40:44+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"kornrunner\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Boris Momcilovic",
|
||||
"homepage": "https://github.com/kornrunner/php-keccak"
|
||||
}
|
||||
],
|
||||
"description": "Pure PHP implementation of Keccak",
|
||||
"keywords": [
|
||||
"keccak",
|
||||
"sha-3",
|
||||
"sha3-256"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/kornrunner/php-keccak/issues",
|
||||
"source": "https://github.com/kornrunner/php-keccak/tree/1.1.0"
|
||||
},
|
||||
"install-path": "../kornrunner/keccak"
|
||||
},
|
||||
{
|
||||
"name": "simplito/bigint-wrapper-php",
|
||||
"version": "1.0.0",
|
||||
"version_normalized": "1.0.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/simplito/bigint-wrapper-php.git",
|
||||
"reference": "cf21ec76d33f103add487b3eadbd9f5033a25930"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/simplito/bigint-wrapper-php/zipball/cf21ec76d33f103add487b3eadbd9f5033a25930",
|
||||
"reference": "cf21ec76d33f103add487b3eadbd9f5033a25930",
|
||||
"shasum": ""
|
||||
},
|
||||
"time": "2018-02-27T12:38:08+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BI\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simplito Team",
|
||||
"email": "s.smyczynski@simplito.com",
|
||||
"homepage": "https://simplito.com"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for php_gmp and php_bcmath modules",
|
||||
"support": {
|
||||
"issues": "https://github.com/simplito/bigint-wrapper-php/issues",
|
||||
"source": "https://github.com/simplito/bigint-wrapper-php/tree/1.0.0"
|
||||
},
|
||||
"install-path": "../simplito/bigint-wrapper-php"
|
||||
},
|
||||
{
|
||||
"name": "simplito/bn-php",
|
||||
"version": "1.1.4",
|
||||
"version_normalized": "1.1.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/simplito/bn-php.git",
|
||||
"reference": "83446756a81720eacc2ffb87ff97958431451fd6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/simplito/bn-php/zipball/83446756a81720eacc2ffb87ff97958431451fd6",
|
||||
"reference": "83446756a81720eacc2ffb87ff97958431451fd6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"simplito/bigint-wrapper-php": "~1.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"time": "2024-01-10T16:16:59+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BN\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simplito Team",
|
||||
"email": "s.smyczynski@simplito.com",
|
||||
"homepage": "https://simplito.com"
|
||||
}
|
||||
],
|
||||
"description": "Big number implementation compatible with bn.js",
|
||||
"support": {
|
||||
"issues": "https://github.com/simplito/bn-php/issues",
|
||||
"source": "https://github.com/simplito/bn-php/tree/1.1.4"
|
||||
},
|
||||
"install-path": "../simplito/bn-php"
|
||||
},
|
||||
{
|
||||
"name": "simplito/elliptic-php",
|
||||
"version": "1.0.12",
|
||||
"version_normalized": "1.0.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/simplito/elliptic-php.git",
|
||||
"reference": "be321666781be2be2c89c79c43ffcac834bc8868"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/simplito/elliptic-php/zipball/be321666781be2be2c89c79c43ffcac834bc8868",
|
||||
"reference": "be321666781be2be2c89c79c43ffcac834bc8868",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-gmp": "*",
|
||||
"simplito/bn-php": "~1.1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpbench/phpbench": "@dev",
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"time": "2024-01-09T14:57:04+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Elliptic\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simplito Team",
|
||||
"email": "s.smyczynski@simplito.com",
|
||||
"homepage": "https://simplito.com"
|
||||
}
|
||||
],
|
||||
"description": "Fast elliptic curve cryptography",
|
||||
"homepage": "https://github.com/simplito/elliptic-php",
|
||||
"keywords": [
|
||||
"Curve25519",
|
||||
"ECDSA",
|
||||
"Ed25519",
|
||||
"EdDSA",
|
||||
"cryptography",
|
||||
"curve",
|
||||
"curve25519-weier",
|
||||
"ecc",
|
||||
"ecdh",
|
||||
"elliptic",
|
||||
"nistp192",
|
||||
"nistp224",
|
||||
"nistp256",
|
||||
"nistp384",
|
||||
"nistp521",
|
||||
"secp256k1"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/simplito/elliptic-php/issues",
|
||||
"source": "https://github.com/simplito/elliptic-php/tree/1.0.12"
|
||||
},
|
||||
"install-path": "../simplito/elliptic-php"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.33.0",
|
||||
"version_normalized": "1.33.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-iconv": "*",
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"provide": {
|
||||
"ext-mbstring": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "For best performance"
|
||||
},
|
||||
"time": "2024-12-23T08:48:59+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for the Mbstring extension",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"mbstring",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"install-path": "../symfony/polyfill-mbstring"
|
||||
},
|
||||
{
|
||||
"name": "workerman/globaldata",
|
||||
"version": "v1.0.6",
|
||||
"version_normalized": "1.0.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/walkor/GlobalData.git",
|
||||
"reference": "9f5082ad712d98c0deedfbfe244661dce151926f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/walkor/GlobalData/zipball/9f5082ad712d98c0deedfbfe244661dce151926f",
|
||||
"reference": "9f5082ad712d98c0deedfbfe244661dce151926f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"workerman/workerman": ">=3.3.0"
|
||||
},
|
||||
"time": "2024-12-27T12:40:16+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GlobalData\\": "./src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"homepage": "http://www.workerman.net",
|
||||
"support": {
|
||||
"issues": "https://github.com/walkor/GlobalData/issues",
|
||||
"source": "https://github.com/walkor/GlobalData/tree/v1.0.6"
|
||||
},
|
||||
"install-path": "../workerman/globaldata"
|
||||
},
|
||||
{
|
||||
"name": "workerman/workerman",
|
||||
"version": "v4.2.1",
|
||||
"version_normalized": "4.2.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/walkor/workerman.git",
|
||||
"reference": "cafb5a43d93d7d30a16b32a57948581cca993562"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/walkor/workerman/zipball/cafb5a43d93d7d30a16b32a57948581cca993562",
|
||||
"reference": "cafb5a43d93d7d30a16b32a57948581cca993562",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-event": "For better performance. "
|
||||
},
|
||||
"time": "2024-11-24T11:45:37+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Workerman\\": "./"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "walkor",
|
||||
"email": "walkor@workerman.net",
|
||||
"homepage": "http://www.workerman.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.",
|
||||
"homepage": "http://www.workerman.net",
|
||||
"keywords": [
|
||||
"asynchronous",
|
||||
"event-loop"
|
||||
],
|
||||
"support": {
|
||||
"email": "walkor@workerman.net",
|
||||
"forum": "http://wenda.workerman.net/",
|
||||
"issues": "https://github.com/walkor/workerman/issues",
|
||||
"source": "https://github.com/walkor/workerman",
|
||||
"wiki": "http://doc.workerman.net/"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://opencollective.com/workerman",
|
||||
"type": "open_collective"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/walkor",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"install-path": "../workerman/workerman"
|
||||
}
|
||||
],
|
||||
"dev": true,
|
||||
"dev-package-names": []
|
||||
}
|
||||
Vendored
+86
@@ -0,0 +1,86 @@
|
||||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => '__root__',
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => NULL,
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev' => true,
|
||||
),
|
||||
'versions' => array(
|
||||
'__root__' => array(
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => NULL,
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'kornrunner/keccak' => array(
|
||||
'pretty_version' => '1.1.0',
|
||||
'version' => '1.1.0.0',
|
||||
'reference' => '433749d28e117fb97baf9f2631b92b5d9ab3c890',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../kornrunner/keccak',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'simplito/bigint-wrapper-php' => array(
|
||||
'pretty_version' => '1.0.0',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => 'cf21ec76d33f103add487b3eadbd9f5033a25930',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../simplito/bigint-wrapper-php',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'simplito/bn-php' => array(
|
||||
'pretty_version' => '1.1.4',
|
||||
'version' => '1.1.4.0',
|
||||
'reference' => '83446756a81720eacc2ffb87ff97958431451fd6',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../simplito/bn-php',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'simplito/elliptic-php' => array(
|
||||
'pretty_version' => '1.0.12',
|
||||
'version' => '1.0.12.0',
|
||||
'reference' => 'be321666781be2be2c89c79c43ffcac834bc8868',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../simplito/elliptic-php',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-mbstring' => array(
|
||||
'pretty_version' => 'v1.33.0',
|
||||
'version' => '1.33.0.0',
|
||||
'reference' => '6d857f4d76bd4b343eac26d6b539585d2bc56493',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'workerman/globaldata' => array(
|
||||
'pretty_version' => 'v1.0.6',
|
||||
'version' => '1.0.6.0',
|
||||
'reference' => '9f5082ad712d98c0deedfbfe244661dce151926f',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../workerman/globaldata',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'workerman/workerman' => array(
|
||||
'pretty_version' => 'v4.2.1',
|
||||
'version' => '4.2.1.0',
|
||||
'reference' => 'cafb5a43d93d7d30a16b32a57948581cca993562',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../workerman/workerman',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
),
|
||||
);
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
// platform_check.php @generated by Composer
|
||||
|
||||
$issues = array();
|
||||
|
||||
if (!(PHP_VERSION_ID >= 80000)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.0.0". You are running ' . PHP_VERSION . '.';
|
||||
}
|
||||
|
||||
if ($issues) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||
} elseif (!headers_sent()) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
service_name: travis-ci
|
||||
coverage_clover: build/logs/clover.xml
|
||||
json_path: coveralls-upload.json
|
||||
@@ -0,0 +1,10 @@
|
||||
composer.phar
|
||||
/vendor/
|
||||
|
||||
# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
|
||||
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
|
||||
# composer.lock
|
||||
nbproject/
|
||||
build/
|
||||
.phpunit.result.cache
|
||||
infection.log
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 7.3
|
||||
- 7.4
|
||||
- 8.0
|
||||
- nightly
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: nightly
|
||||
|
||||
install:
|
||||
- phpenv config-rm xdebug.ini || true
|
||||
- travis_retry composer install --prefer-dist -n
|
||||
|
||||
script:
|
||||
- mkdir -p build/logs
|
||||
- phpdbg -qrr vendor/bin/phpunit
|
||||
|
||||
after_success:
|
||||
- composer require php-coveralls/php-coveralls
|
||||
- travis_retry vendor/bin/php-coveralls -v
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Boris Momčilović
|
||||
|
||||
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.
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
# php-keccak [](https://travis-ci.org/kornrunner/php-keccak) [](https://ci.appveyor.com/project/kornrunner/php-keccak/branch/master) [](https://coveralls.io/github/kornrunner/php-keccak?branch=master) [](https://packagist.org/packages/kornrunner/keccak)
|
||||
Pure PHP implementation of Keccak (SHA-3)
|
||||
|
||||
## Usage
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use kornrunner\Keccak;
|
||||
|
||||
Keccak::hash('', 224);
|
||||
// f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd
|
||||
|
||||
Keccak::hash('', 256);
|
||||
// c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
|
||||
|
||||
Keccak::hash('', 384);
|
||||
// 2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff
|
||||
|
||||
Keccak::hash('', 512);
|
||||
// 0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e
|
||||
|
||||
Keccak::shake('', 128, 256);
|
||||
// 7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26
|
||||
|
||||
Keccak::shake('', 256, 512);
|
||||
// 46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762fd75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be
|
||||
```
|
||||
|
||||
[ 0x9c7b7a00972121fb843af7af74526d7eb585b171][Ethereum]
|
||||
|
||||
[Ethereum]: https://etherscan.io/address/0x9c7b7a00972121fb843af7af74526d7eb585b171 "Donate with Ethereum"
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
build: false
|
||||
shallow_clone: true
|
||||
clone_folder: c:\php-keccak
|
||||
platform: x64
|
||||
|
||||
cache:
|
||||
- c:\php -> appveyor.yml
|
||||
- vendor -> composer.lock
|
||||
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- PHP_VERSION: '7.3.7'
|
||||
PHP_DOWNLOAD_URL: 'https://windows.php.net/downloads/releases/archives/php-7.3.7-nts-Win32-VC15-x64.zip'
|
||||
- PHP_VERSION: '7.4.3'
|
||||
PHP_DOWNLOAD_URL: 'https://windows.php.net/downloads/releases/archives/php-7.4.3-nts-Win32-vc15-x64.zip'
|
||||
- PHP_VERSION: '8.0.0'
|
||||
PHP_DOWNLOAD_URL: 'https://windows.php.net/downloads/releases/php-8.0.0-nts-Win32-vs16-x64.zip'
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
init:
|
||||
- SET PATH=c:\php\%PHP_VERSION%;%PATH%
|
||||
- set COMPOSER_NO_INTERACTION=1
|
||||
|
||||
install:
|
||||
- IF NOT EXIST c:\php mkdir c:\php
|
||||
- IF NOT EXIST c:\php\%PHP_VERSION% mkdir c:\php\%PHP_VERSION%
|
||||
- cd c:\php\%PHP_VERSION%
|
||||
- IF NOT EXIST php-installed.txt curl --fail --location --silent --show-error -o php.zip %PHP_DOWNLOAD_URL%
|
||||
- IF NOT EXIST php-installed.txt 7z x php.zip -y
|
||||
- IF NOT EXIST php-installed.txt del /Q *.zip
|
||||
- IF NOT EXIST php-installed.txt copy /Y php.ini-development php.ini
|
||||
- IF NOT EXIST php-installed.txt echo max_execution_time=1200 >> php.ini
|
||||
- IF NOT EXIST php-installed.txt echo date.timezone="UTC" >> php.ini
|
||||
- IF NOT EXIST php-installed.txt echo extension_dir=ext >> php.ini
|
||||
- IF NOT EXIST php-installed.txt echo extension=php_openssl.dll >> php.ini
|
||||
- IF NOT EXIST php-installed.txt echo extension=php_mbstring.dll >> php.ini
|
||||
- IF NOT EXIST php-installed.txt appveyor DownloadFile https://getcomposer.org/composer.phar
|
||||
- IF NOT EXIST php-installed.txt echo @php %%~dp0composer.phar %%* > composer.bat
|
||||
- IF NOT EXIST php-installed.txt type nul >> php-installed.txt
|
||||
- cd c:\php-keccak
|
||||
|
||||
test_script:
|
||||
- composer install
|
||||
- vendor/bin/phpunit.bat --coverage-text
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "kornrunner/keccak",
|
||||
"description": "Pure PHP implementation of Keccak",
|
||||
"keywords": ["keccak", "sha-3", "sha3-256"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Boris Momcilovic",
|
||||
"homepage": "https://github.com/kornrunner/php-keccak"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.3",
|
||||
"symfony/polyfill-mbstring": "^1.8"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"kornrunner\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"kornrunner\\": "test"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.2"
|
||||
}
|
||||
}
|
||||
+1870
File diff suppressed because it is too large
Load Diff
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"timeout": 10,
|
||||
"source": {
|
||||
"directories": [
|
||||
"src"
|
||||
]
|
||||
},
|
||||
"logs": {
|
||||
"text": "infection.log"
|
||||
},
|
||||
"mutators": {
|
||||
"@default": true
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit bootstrap="vendor/autoload.php"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
verbose="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false">
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Keccak Test Suite">
|
||||
<directory>test</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<logging>
|
||||
<log type="tap" target="build/report.tap"/>
|
||||
<log type="junit" target="build/report.junit.xml"/>
|
||||
<log type="coverage-html" target="build/coverage"/>
|
||||
<log type="coverage-text" target="build/coverage.txt"/>
|
||||
<log type="coverage-clover" target="build/logs/clover.xml"/>
|
||||
</logging>
|
||||
</phpunit>
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
<?php
|
||||
|
||||
namespace kornrunner;
|
||||
|
||||
use Exception;
|
||||
use function mb_strlen;
|
||||
use function mb_substr;
|
||||
|
||||
final class Keccak
|
||||
{
|
||||
private const KECCAK_ROUNDS = 24;
|
||||
private const LFSR = 0x01;
|
||||
private const ENCODING = '8bit';
|
||||
private static $keccakf_rotc = [1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44];
|
||||
private static $keccakf_piln = [10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12,2, 20, 14, 22, 9, 6, 1];
|
||||
private static $x64 = (PHP_INT_SIZE === 8);
|
||||
|
||||
private static function keccakf64(&$st, $rounds): void {
|
||||
$keccakf_rndc = [
|
||||
[0x00000000, 0x00000001], [0x00000000, 0x00008082], [0x80000000, 0x0000808a], [0x80000000, 0x80008000],
|
||||
[0x00000000, 0x0000808b], [0x00000000, 0x80000001], [0x80000000, 0x80008081], [0x80000000, 0x00008009],
|
||||
[0x00000000, 0x0000008a], [0x00000000, 0x00000088], [0x00000000, 0x80008009], [0x00000000, 0x8000000a],
|
||||
[0x00000000, 0x8000808b], [0x80000000, 0x0000008b], [0x80000000, 0x00008089], [0x80000000, 0x00008003],
|
||||
[0x80000000, 0x00008002], [0x80000000, 0x00000080], [0x00000000, 0x0000800a], [0x80000000, 0x8000000a],
|
||||
[0x80000000, 0x80008081], [0x80000000, 0x00008080], [0x00000000, 0x80000001], [0x80000000, 0x80008008]
|
||||
];
|
||||
|
||||
$bc = [];
|
||||
for ($round = 0; $round < $rounds; $round++) {
|
||||
|
||||
// Theta
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$bc[$i] = [
|
||||
$st[$i][0] ^ $st[$i + 5][0] ^ $st[$i + 10][0] ^ $st[$i + 15][0] ^ $st[$i + 20][0],
|
||||
$st[$i][1] ^ $st[$i + 5][1] ^ $st[$i + 10][1] ^ $st[$i + 15][1] ^ $st[$i + 20][1]
|
||||
];
|
||||
}
|
||||
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$t = [
|
||||
$bc[($i + 4) % 5][0] ^ (($bc[($i + 1) % 5][0] << 1) | ($bc[($i + 1) % 5][1] >> 31)) & (0xFFFFFFFF),
|
||||
$bc[($i + 4) % 5][1] ^ (($bc[($i + 1) % 5][1] << 1) | ($bc[($i + 1) % 5][0] >> 31)) & (0xFFFFFFFF)
|
||||
];
|
||||
|
||||
for ($j = 0; $j < 25; $j += 5) {
|
||||
$st[$j + $i] = [
|
||||
$st[$j + $i][0] ^ $t[0],
|
||||
$st[$j + $i][1] ^ $t[1]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Rho Pi
|
||||
$t = $st[1];
|
||||
for ($i = 0; $i < 24; $i++) {
|
||||
$j = self::$keccakf_piln[$i];
|
||||
|
||||
$bc[0] = $st[$j];
|
||||
|
||||
$n = self::$keccakf_rotc[$i];
|
||||
$hi = $t[0];
|
||||
$lo = $t[1];
|
||||
if ($n >= 32) {
|
||||
$n -= 32;
|
||||
$hi = $t[1];
|
||||
$lo = $t[0];
|
||||
}
|
||||
|
||||
$st[$j] =[
|
||||
(($hi << $n) | ($lo >> (32 - $n))) & (0xFFFFFFFF),
|
||||
(($lo << $n) | ($hi >> (32 - $n))) & (0xFFFFFFFF)
|
||||
];
|
||||
|
||||
$t = $bc[0];
|
||||
}
|
||||
|
||||
// Chi
|
||||
for ($j = 0; $j < 25; $j += 5) {
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$bc[$i] = $st[$j + $i];
|
||||
}
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$st[$j + $i] = [
|
||||
$st[$j + $i][0] ^ ~$bc[($i + 1) % 5][0] & $bc[($i + 2) % 5][0],
|
||||
$st[$j + $i][1] ^ ~$bc[($i + 1) % 5][1] & $bc[($i + 2) % 5][1]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Iota
|
||||
$st[0] = [
|
||||
$st[0][0] ^ $keccakf_rndc[$round][0],
|
||||
$st[0][1] ^ $keccakf_rndc[$round][1]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private static function keccak64($in_raw, int $capacity, int $outputlength, $suffix, bool $raw_output): string {
|
||||
$capacity /= 8;
|
||||
|
||||
$inlen = mb_strlen($in_raw, self::ENCODING);
|
||||
|
||||
$rsiz = 200 - 2 * $capacity;
|
||||
$rsizw = $rsiz / 8;
|
||||
|
||||
$st = [];
|
||||
for ($i = 0; $i < 25; $i++) {
|
||||
$st[] = [0, 0];
|
||||
}
|
||||
|
||||
for ($in_t = 0; $inlen >= $rsiz; $inlen -= $rsiz, $in_t += $rsiz) {
|
||||
for ($i = 0; $i < $rsizw; $i++) {
|
||||
$t = unpack('V*', mb_substr($in_raw, intval($i * 8 + $in_t), 8, self::ENCODING));
|
||||
|
||||
$st[$i] = [
|
||||
$st[$i][0] ^ $t[2],
|
||||
$st[$i][1] ^ $t[1]
|
||||
];
|
||||
}
|
||||
|
||||
self::keccakf64($st, self::KECCAK_ROUNDS);
|
||||
}
|
||||
|
||||
$temp = mb_substr($in_raw, (int) $in_t, (int) $inlen, self::ENCODING);
|
||||
$temp = str_pad($temp, (int) $rsiz, "\x0", STR_PAD_RIGHT);
|
||||
$temp = substr_replace($temp, chr($suffix), $inlen, 1);
|
||||
$temp = substr_replace($temp, chr(ord($temp[intval($rsiz - 1)]) | 0x80), $rsiz - 1, 1);
|
||||
|
||||
for ($i = 0; $i < $rsizw; $i++) {
|
||||
$t = unpack('V*', mb_substr($temp, $i * 8, 8, self::ENCODING));
|
||||
|
||||
$st[$i] = [
|
||||
$st[$i][0] ^ $t[2],
|
||||
$st[$i][1] ^ $t[1]
|
||||
];
|
||||
}
|
||||
|
||||
self::keccakf64($st, self::KECCAK_ROUNDS);
|
||||
|
||||
$out = '';
|
||||
for ($i = 0; $i < 25; $i++) {
|
||||
$out .= $t = pack('V*', $st[$i][1], $st[$i][0]);
|
||||
}
|
||||
$r = mb_substr($out, 0, intval($outputlength / 8), self::ENCODING);
|
||||
|
||||
return $raw_output ? $r : bin2hex($r);
|
||||
}
|
||||
|
||||
private static function keccakf32(&$st, $rounds): void {
|
||||
$keccakf_rndc = [
|
||||
[0x0000, 0x0000, 0x0000, 0x0001], [0x0000, 0x0000, 0x0000, 0x8082], [0x8000, 0x0000, 0x0000, 0x0808a], [0x8000, 0x0000, 0x8000, 0x8000],
|
||||
[0x0000, 0x0000, 0x0000, 0x808b], [0x0000, 0x0000, 0x8000, 0x0001], [0x8000, 0x0000, 0x8000, 0x08081], [0x8000, 0x0000, 0x0000, 0x8009],
|
||||
[0x0000, 0x0000, 0x0000, 0x008a], [0x0000, 0x0000, 0x0000, 0x0088], [0x0000, 0x0000, 0x8000, 0x08009], [0x0000, 0x0000, 0x8000, 0x000a],
|
||||
[0x0000, 0x0000, 0x8000, 0x808b], [0x8000, 0x0000, 0x0000, 0x008b], [0x8000, 0x0000, 0x0000, 0x08089], [0x8000, 0x0000, 0x0000, 0x8003],
|
||||
[0x8000, 0x0000, 0x0000, 0x8002], [0x8000, 0x0000, 0x0000, 0x0080], [0x0000, 0x0000, 0x0000, 0x0800a], [0x8000, 0x0000, 0x8000, 0x000a],
|
||||
[0x8000, 0x0000, 0x8000, 0x8081], [0x8000, 0x0000, 0x0000, 0x8080], [0x0000, 0x0000, 0x8000, 0x00001], [0x8000, 0x0000, 0x8000, 0x8008]
|
||||
];
|
||||
|
||||
$bc = [];
|
||||
for ($round = 0; $round < $rounds; $round++) {
|
||||
|
||||
// Theta
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$bc[$i] = [
|
||||
$st[$i][0] ^ $st[$i + 5][0] ^ $st[$i + 10][0] ^ $st[$i + 15][0] ^ $st[$i + 20][0],
|
||||
$st[$i][1] ^ $st[$i + 5][1] ^ $st[$i + 10][1] ^ $st[$i + 15][1] ^ $st[$i + 20][1],
|
||||
$st[$i][2] ^ $st[$i + 5][2] ^ $st[$i + 10][2] ^ $st[$i + 15][2] ^ $st[$i + 20][2],
|
||||
$st[$i][3] ^ $st[$i + 5][3] ^ $st[$i + 10][3] ^ $st[$i + 15][3] ^ $st[$i + 20][3]
|
||||
];
|
||||
}
|
||||
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$t = [
|
||||
$bc[($i + 4) % 5][0] ^ ((($bc[($i + 1) % 5][0] << 1) | ($bc[($i + 1) % 5][1] >> 15)) & (0xFFFF)),
|
||||
$bc[($i + 4) % 5][1] ^ ((($bc[($i + 1) % 5][1] << 1) | ($bc[($i + 1) % 5][2] >> 15)) & (0xFFFF)),
|
||||
$bc[($i + 4) % 5][2] ^ ((($bc[($i + 1) % 5][2] << 1) | ($bc[($i + 1) % 5][3] >> 15)) & (0xFFFF)),
|
||||
$bc[($i + 4) % 5][3] ^ ((($bc[($i + 1) % 5][3] << 1) | ($bc[($i + 1) % 5][0] >> 15)) & (0xFFFF))
|
||||
];
|
||||
|
||||
for ($j = 0; $j < 25; $j += 5) {
|
||||
$st[$j + $i] = [
|
||||
$st[$j + $i][0] ^ $t[0],
|
||||
$st[$j + $i][1] ^ $t[1],
|
||||
$st[$j + $i][2] ^ $t[2],
|
||||
$st[$j + $i][3] ^ $t[3]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Rho Pi
|
||||
$t = $st[1];
|
||||
for ($i = 0; $i < 24; $i++) {
|
||||
$j = self::$keccakf_piln[$i];
|
||||
$bc[0] = $st[$j];
|
||||
|
||||
|
||||
$n = self::$keccakf_rotc[$i] >> 4;
|
||||
$m = self::$keccakf_rotc[$i] % 16;
|
||||
|
||||
$st[$j] = [
|
||||
((($t[(0+$n) %4] << $m) | ($t[(1+$n) %4] >> (16-$m))) & (0xFFFF)),
|
||||
((($t[(1+$n) %4] << $m) | ($t[(2+$n) %4] >> (16-$m))) & (0xFFFF)),
|
||||
((($t[(2+$n) %4] << $m) | ($t[(3+$n) %4] >> (16-$m))) & (0xFFFF)),
|
||||
((($t[(3+$n) %4] << $m) | ($t[(0+$n) %4] >> (16-$m))) & (0xFFFF))
|
||||
];
|
||||
|
||||
$t = $bc[0];
|
||||
}
|
||||
|
||||
// Chi
|
||||
for ($j = 0; $j < 25; $j += 5) {
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$bc[$i] = $st[$j + $i];
|
||||
}
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$st[$j + $i] = [
|
||||
$st[$j + $i][0] ^ ~$bc[($i + 1) % 5][0] & $bc[($i + 2) % 5][0],
|
||||
$st[$j + $i][1] ^ ~$bc[($i + 1) % 5][1] & $bc[($i + 2) % 5][1],
|
||||
$st[$j + $i][2] ^ ~$bc[($i + 1) % 5][2] & $bc[($i + 2) % 5][2],
|
||||
$st[$j + $i][3] ^ ~$bc[($i + 1) % 5][3] & $bc[($i + 2) % 5][3]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Iota
|
||||
$st[0] = [
|
||||
$st[0][0] ^ $keccakf_rndc[$round][0],
|
||||
$st[0][1] ^ $keccakf_rndc[$round][1],
|
||||
$st[0][2] ^ $keccakf_rndc[$round][2],
|
||||
$st[0][3] ^ $keccakf_rndc[$round][3]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private static function keccak32($in_raw, int $capacity, int $outputlength, $suffix, bool $raw_output): string {
|
||||
$capacity /= 8;
|
||||
|
||||
$inlen = mb_strlen($in_raw, self::ENCODING);
|
||||
|
||||
$rsiz = 200 - 2 * $capacity;
|
||||
$rsizw = $rsiz / 8;
|
||||
|
||||
$st = [];
|
||||
for ($i = 0; $i < 25; $i++) {
|
||||
$st[] = [0, 0, 0, 0];
|
||||
}
|
||||
|
||||
for ($in_t = 0; $inlen >= $rsiz; $inlen -= $rsiz, $in_t += $rsiz) {
|
||||
for ($i = 0; $i < $rsizw; $i++) {
|
||||
$t = unpack('v*', mb_substr($in_raw, intval($i * 8 + $in_t), 8, self::ENCODING));
|
||||
|
||||
$st[$i] = [
|
||||
$st[$i][0] ^ $t[4],
|
||||
$st[$i][1] ^ $t[3],
|
||||
$st[$i][2] ^ $t[2],
|
||||
$st[$i][3] ^ $t[1]
|
||||
];
|
||||
}
|
||||
|
||||
self::keccakf32($st, self::KECCAK_ROUNDS);
|
||||
}
|
||||
|
||||
$temp = mb_substr($in_raw, (int) $in_t, (int) $inlen, self::ENCODING);
|
||||
$temp = str_pad($temp, (int) $rsiz, "\x0", STR_PAD_RIGHT);
|
||||
$temp = substr_replace($temp, chr($suffix), $inlen, 1);
|
||||
$temp = substr_replace($temp, chr((int) $temp[intval($rsiz - 1)] | 0x80), $rsiz - 1, 1);
|
||||
|
||||
for ($i = 0; $i < $rsizw; $i++) {
|
||||
$t = unpack('v*', mb_substr($temp, $i * 8, 8, self::ENCODING));
|
||||
|
||||
$st[$i] = [
|
||||
$st[$i][0] ^ $t[4],
|
||||
$st[$i][1] ^ $t[3],
|
||||
$st[$i][2] ^ $t[2],
|
||||
$st[$i][3] ^ $t[1]
|
||||
];
|
||||
}
|
||||
|
||||
self::keccakf32($st, self::KECCAK_ROUNDS);
|
||||
|
||||
$out = '';
|
||||
for ($i = 0; $i < 25; $i++) {
|
||||
$out .= $t = pack('v*', $st[$i][3],$st[$i][2], $st[$i][1], $st[$i][0]);
|
||||
}
|
||||
$r = mb_substr($out, 0, intval($outputlength / 8), self::ENCODING);
|
||||
|
||||
return $raw_output ? $r: bin2hex($r);
|
||||
}
|
||||
|
||||
private static function keccak($in_raw, int $capacity, int $outputlength, $suffix, bool $raw_output): string {
|
||||
return self::$x64
|
||||
? self::keccak64($in_raw, $capacity, $outputlength, $suffix, $raw_output)
|
||||
: self::keccak32($in_raw, $capacity, $outputlength, $suffix, $raw_output);
|
||||
}
|
||||
|
||||
public static function hash($in, int $mdlen, bool $raw_output = false): string {
|
||||
if (!in_array($mdlen, [224, 256, 384, 512], true)) {
|
||||
throw new Exception('Unsupported Keccak Hash output size.');
|
||||
}
|
||||
|
||||
return self::keccak($in, $mdlen, $mdlen, self::LFSR, $raw_output);
|
||||
}
|
||||
|
||||
public static function shake($in, int $security_level, int $outlen, bool $raw_output = false): string {
|
||||
if (!in_array($security_level, [128, 256], true)) {
|
||||
throw new Exception('Unsupported Keccak Shake security level.');
|
||||
}
|
||||
|
||||
return self::keccak($in, $security_level, $outlen, 0x1f, $raw_output);
|
||||
}
|
||||
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace kornrunner;
|
||||
|
||||
/**
|
||||
*@see https://gist.github.com/Souptacular/f50128d63b5188490fa2
|
||||
*/
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class KeccakTest extends TestCase
|
||||
{
|
||||
private const SHORT = "52A608AB21CCDD8A4457A57EDE782176";
|
||||
private const LONG = "3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD4574EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F11418690A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED306FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D79DC266F6A5BE6BB0E4A92ECEEBAEB1";
|
||||
private static $x64;
|
||||
|
||||
public static function setUpBeforeClass(): void {
|
||||
parent::setUpBeforeClass();
|
||||
$class = new \ReflectionClass(Keccak::class);
|
||||
self::$x64 = $class->getProperty('x64');
|
||||
self::$x64->setAccessible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider hash
|
||||
*/
|
||||
public function testHash($level, $tests) {
|
||||
$x64_values = [true, false];
|
||||
foreach ($x64_values as $x64_bit) {
|
||||
self::$x64->setValue($x64_bit);
|
||||
|
||||
foreach($tests as $test) {
|
||||
$message = $test[0];
|
||||
$expected = $test[1];
|
||||
$this->assertEquals($expected, Keccak::hash($message, $level));
|
||||
$this->assertEquals(hex2bin($expected), Keccak::hash($message, $level, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function hash(): array {
|
||||
return [
|
||||
/**
|
||||
* @see https://emn178.github.io/online-tools/keccak_512.html
|
||||
*/
|
||||
[512, [
|
||||
['','0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e'],
|
||||
['testing', '9558a7ba9ac74b33b347703ffe33f8d561d86d9fcad1cfd63225fb55dfea50a0953a0efafd6072377f4c396e806d5bda0294cba28762740d8446fee45a276e4a'],
|
||||
['The quick brown fox jumps over the lazy dog', 'd135bb84d0439dbac432247ee573a23ea7d3c9deb2a968eb31d47c4fb45f1ef4422d6c531b5b9bd6f449ebcc449ea94d0a8f05f62130fda612da53c79659f609'],
|
||||
['The quick brown fox jumps over the lazy dog.','ab7192d2b11f51c7dd744e7b3441febf397ca07bf812cceae122ca4ded6387889064f8db9230f173f6d1ab6e24b6e50f065b039f799f5592360a6558eb52d760'],
|
||||
[hex2bin(self::SHORT), '4b39d3da5bcdf4d9b769015995644311c14c435bf72b1009d6dd71b01a63b97cfb596418e8e42342d117e07471a8914314ba7b0e264dadf0cea381868cbd43d1'],
|
||||
[hex2bin(self::LONG), '81950e7096d31d4f22e3db71cac725bf59e81af54c7ca9e6aeee71c010fc5467466312a01aa5c137cfb140646941556796f612c9351268737c7e9a2b9631d1fa']
|
||||
]],
|
||||
/**
|
||||
* @see https://emn178.github.io/online-tools/keccak_384.html
|
||||
*/
|
||||
[384, [
|
||||
['', '2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff'],
|
||||
['testing', '1020b1c91956efe79b89c387b54de4f7a9c187c3970552f9f48c0da176f6326b7aa694795d2c9adcf2bdd20aec605588'],
|
||||
['The quick brown fox jumps over the lazy dog', '283990fa9d5fb731d786c5bbee94ea4db4910f18c62c03d173fc0a5e494422e8a0b3da7574dae7fa0baf005e504063b3'],
|
||||
['The quick brown fox jumps over the lazy dog.', '9ad8e17325408eddb6edee6147f13856ad819bb7532668b605a24a2d958f88bd5c169e56dc4b2f89ffd325f6006d820b'],
|
||||
[hex2bin(self::SHORT), '18422ac1d3a1e54bad876883d2d6dd65f65c1d5f33a7125cc4c186405a12ed64ba96672eedda8c5a6331d28683f488eb'],
|
||||
[hex2bin(self::LONG), '6bff1c8405a3fe594e360e3bccea1ebcd509310dc79b9e45c263783d7a5dd662c6789b18bd567dbdda1554f5bee6a860'],
|
||||
[hex2bin('E35780EB9799AD4C77535D4DDB683CF33EF367715327CF4C4A58ED9CBDCDD486F669F80189D549A9364FA82A51A52654EC721BB3AAB95DCEB4A86A6AFA93826DB923517E928F33E3FBA850D45660EF83B9876ACCAFA2A9987A254B137C6E140A21691E1069413848'), '9fb5700502e01926824f46e9f61894f9487dbcf8ae6217203c85606f975566539376d6239db04aef9bf48ca4f191a90b'],
|
||||
]],
|
||||
/**
|
||||
* @see https://emn178.github.io/online-tools/keccak_256.html
|
||||
*/
|
||||
[256, [
|
||||
['', 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'],
|
||||
['testing', '5f16f4c7f149ac4f9510d9cf8cf384038ad348b3bcdc01915f95de12df9d1b02'],
|
||||
['The quick brown fox jumps over the lazy dog', '4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15'],
|
||||
['The quick brown fox jumps over the lazy dog.', '578951e24efd62a3d63a86f7cd19aaa53c898fe287d2552133220370240b572d'],
|
||||
[hex2bin(self::SHORT), '0e32defa2071f0b5ac0e6a108b842ed0f1d3249712f58ee0ddf956fe332a5f95'],
|
||||
[hex2bin(self::LONG), '348fb774adc970a16b1105669442625e6adaa8257a89effdb5a802f161b862ea'],
|
||||
[hex2bin('9F2FCC7C90DE090D6B87CD7E9718C1EA6CB21118FC2D5DE9F97E5DB6AC1E9C10'), '24dd2ee02482144f539f810d2caa8a7b75d0fa33657e47932122d273c3f6f6d1'],
|
||||
]],
|
||||
/**
|
||||
* @see https://emn178.github.io/online-tools/keccak_224.html
|
||||
*/
|
||||
[224, [
|
||||
['', 'f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd'],
|
||||
['testing', '7b77b0b01d9b669ec7637ae75fd2f0ce234c8c8c835723b6715f4b59'],
|
||||
['The quick brown fox jumps over the lazy dog', '310aee6b30c47350576ac2873fa89fd190cdc488442f3ef654cf23fe'],
|
||||
['The quick brown fox jumps over the lazy dog.', 'c59d4eaeac728671c635ff645014e2afa935bebffdb5fbd207ffdeab'],
|
||||
[hex2bin(self::SHORT), '5679cd509c5120af54795cf477149641cf27b2ebb6a5f90340704e57'],
|
||||
[hex2bin(self::LONG), '5af56987ea9cf11fcd0eac5ebc14b037365e9b1123e31cb2dfc7929a'],
|
||||
]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider Shake
|
||||
*/
|
||||
public function testShake($level, $size, $tests)
|
||||
{
|
||||
$x64_values = [true, false];
|
||||
foreach ($x64_values as $x64_bit) {
|
||||
self::$x64->setValue($x64_bit);
|
||||
|
||||
foreach($tests as $test) {
|
||||
$message = $test[0];
|
||||
$expected = $test[1];
|
||||
$this->assertEquals($expected, Keccak::shake($message, $level, $size));
|
||||
$this->assertEquals(hex2bin($expected), Keccak::shake($message, $level, $size, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function shake(): array {
|
||||
return [
|
||||
[128, 256, [
|
||||
['', '7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26'],
|
||||
[hex2bin(self::SHORT), '3a0faca70c9d2b81d1064d429ea3b05ad27366f64985379ddd75bc73d6a83810'],
|
||||
[hex2bin(self::LONG), '14236e75b9784df4f57935f945356cbe383fe513ed30286f91060759bcb0ef4b'],
|
||||
['The quick brown fox jumps over the lazy dog', 'f4202e3c5852f9182a0430fd8144f0a74b95e7417ecae17db0f8cfeed0e3e66e'],
|
||||
['The quick brown fox jumps over the lazy dof', '853f4538be0db9621a6cea659a06c1107b1f83f02b13d18297bd39d7411cf10c'],
|
||||
]],
|
||||
[256, 512, [
|
||||
['', '46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762fd75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be'],
|
||||
[hex2bin(self::SHORT), '57119c4507f975ad0e9ea4f1166e5f9b590bf2671aaeb41d130d2c570bafc579b0b9ec485cc736a0a848bbc886cbaa79ffcd067ce64b3b410741ab011c544225'],
|
||||
[hex2bin(self::LONG), '8a5199b4a7e133e264a86202720655894d48cff344a928cf8347f48379cef347dfc5bcffab99b27b1f89aa2735e23d30088ffa03b9edb02b9635470ab9f10389'],
|
||||
]],
|
||||
];
|
||||
}
|
||||
|
||||
public function testUnsupportedHashOutputSize()
|
||||
{
|
||||
$this->expectException(\Exception::class);
|
||||
$this->expectExceptionMessage('Unsupported Keccak Hash output size.');
|
||||
Keccak::hash('', 225);
|
||||
}
|
||||
|
||||
public function testUnsupportedShakeSecurityLevel()
|
||||
{
|
||||
$this->expectException(\Exception::class);
|
||||
$this->expectExceptionMessage('Unsupported Keccak Shake security level.');
|
||||
Keccak::shake('', 129, 256);
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
|
||||
## MIT LICENSE
|
||||
|
||||
Copyright (C) 2018 Simplito
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
|
||||
# BigInteger wrapper library for PHP
|
||||
|
||||
## Information
|
||||
|
||||
This library is a common interface for php_gmp and php_bcmath modules. It automatically detects supported modules and uses the best of them (gmp>bcmath). Gmp is a lot faster, but is also missing on many hosting services -- that is why this wrapper has been created. It is used for example in encryption functions of the [PrivMX WebMail](https://privmx.com) software.
|
||||
|
||||
## Installation
|
||||
|
||||
You can install this library via Composer:
|
||||
```
|
||||
composer require simplito/bigint-wrapper-php
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
If you want to force using a specific implementation, then define constant S_MATH_BIGINTEGER_MODE - set it to "gmp" or "bcmath". If you do not do this, mode of operation and the constant will be set automatically.
|
||||
|
||||
If there are no gmp and bcmath modules, an exception will be thrown. If you want to prevent this, then simply define S_MATH_BIGINTEGER_QUIET constant.
|
||||
|
||||
All functions of this library are implemented as members of class BigInteger, which is located under BI namespace. Instances of BigInteger are immutable - member functions usually return new instances of the BigInteger class.
|
||||
|
||||
|
||||
### ConvertibleToBi - a placeholder type
|
||||
|
||||
To make the below documentation more readable we use the "ConvertibleToBi" type symbol, which in reality can be one of the following types:
|
||||
- an instance of the BigInteger class
|
||||
- an integer
|
||||
- a decimal string
|
||||
- a gmp resource or class (only when you are in gmp mode)
|
||||
|
||||
If you have a non-decimal string and want to use it -- first you have to convert it to BigInteger class using:
|
||||
```
|
||||
new BigInteger($myNonDecimalString, $baseOfMyNonDecimalString)
|
||||
```
|
||||
|
||||
### BI\BigInteger class members
|
||||
|
||||
#### construct(ConvertibleToBi $value = 0, int $base = 10)
|
||||
Creates a new instance of BigInteger. If you pass an invalid value, an exception will be thrown. If $base === true then passed $value will be used without any check and conversion. Supported bases: 2, 10, 16, 256.
|
||||
- **GMP implementation:** gmp_init + bin2hex for 256 base
|
||||
- **Bcmath implementation:** custom(bcadd + bcmul)
|
||||
|
||||
#### static BigInteger|false createSafe(ConvertibleToBi $value = 0, int $base = 10)
|
||||
Creates a new BigInteger instance in the same way as constructor, but if there is an error, false will be returned instead of throwing an exception.
|
||||
|
||||
#### BigInteger add(ConvertibleToBi $x)
|
||||
Adds numbers
|
||||
- **GMP implementation:** gmp_add
|
||||
- **Bcmath implementation:** bcadd
|
||||
|
||||
#### BigInteger sub(ConvertibleToBi $x)
|
||||
Subtracts numbers
|
||||
- **GMP implementation:** gmp_sub
|
||||
- **Bcmath implementation:** bcsub
|
||||
|
||||
#### BigInteger mul(ConvertibleToBi $x)
|
||||
Multiplies numbers
|
||||
- **GMP implementation:** gmp_mul
|
||||
- **Bcmath implementation:** bcmul
|
||||
|
||||
#### BigInteger div(ConvertibleToBi $x)
|
||||
Divides numbers
|
||||
- **GMP implementation:** gmp_div_q
|
||||
- **Bcmath implementation:** bcdiv
|
||||
|
||||
#### BigInteger divR(ConvertibleToBi $x)
|
||||
Returns a remainder of the division of numbers. The remainder has the sign of the divided number.
|
||||
- **GMP implementation:** gmp_div_r
|
||||
- **Bcmath implementation:** bcmod
|
||||
|
||||
#### array(BigInteger, BigInteger) divQR(ConvertibleToBi $x)
|
||||
Divides numbers and returns quotient and remainder. Returns an array(), with the first element being quotient, and the second being remainder.
|
||||
- **GMP implementation:** gmp_div_qr
|
||||
- **Bcmath implementation:** div + divR
|
||||
|
||||
#### BigInteger mod(ConvertibleToBi $x)
|
||||
The "division modulo" operation. The result is always non-negative, the sign of divider is ignored.
|
||||
- **GMP implementation:** gmp_mod
|
||||
- **Bcmath implementation:** custom (bcmod + bcadd)
|
||||
|
||||
#### BigInteger gcd(ConvertibleToBi $x)
|
||||
Calculates greatest common divisor
|
||||
- **GMP implementation:** gmp_gcd
|
||||
- **Bcmath implementation:** custom (bccomp + bcdiv + bcsub + bcmul)
|
||||
|
||||
#### BigInteger|false modInverse(ConvertibleToBi $x)
|
||||
Inverses by modulo, returns false if inversion does not exist.
|
||||
- **GMP implementation:** gmp_invert
|
||||
- **Bcmath implementation:** custom (gcd)
|
||||
|
||||
#### BigInteger pow(ConvertibleToBi $x)
|
||||
The power function.
|
||||
- **GMP implementation:** gmp_pow
|
||||
- **Bcmath implementation:** bcpow
|
||||
|
||||
#### BigInteger powMod(ConvertibleToBi $x, ConvertibleToBi $n)
|
||||
The modular power function.
|
||||
- **GMP implementation:** gmp_powm
|
||||
- **Bcmath implementation:** bcpowmod
|
||||
|
||||
#### BigInteger abs()
|
||||
Returns absolute value.
|
||||
- **GMP implementation:** gmp_abs
|
||||
- **Bcmath implementation:** check first character
|
||||
|
||||
#### BigInteger neg()
|
||||
Negates the number
|
||||
- **GMP implementation:** gmp_neg
|
||||
- **Bcmath implementation:** check first character
|
||||
|
||||
#### BigInteger binaryAnd(ConvertibleToBi $x)
|
||||
Bitwise AND.
|
||||
- **GMP implementation:** gmp_and
|
||||
- **Bcmath implementation:** custom (toBytes + php string and)
|
||||
|
||||
#### BigInteger binaryOr(ConvertibleToBi $x)
|
||||
Bitwise OR
|
||||
- **GMP implementation:** gmp_or
|
||||
- **Bcmath implementation:** custom (toBytes + php string or)
|
||||
|
||||
#### BigInteger binaryXor(ConvertibleToBi $x)
|
||||
Bitwise XOR
|
||||
- **GMP implementation:** gmp_xor
|
||||
- **Bcmath implementation:** custom (toBytes + php string xor)
|
||||
|
||||
#### BigInteger setbit($index, $bitOn = true)
|
||||
Sets bit at given index
|
||||
- **GMP implementation:** gmp_setbit
|
||||
- **Bcmath implementation:** custom (toBits)
|
||||
|
||||
#### bool testbit($index)
|
||||
Tests if a bit at given index is set
|
||||
- **GMP implementation:** gmp_testbit
|
||||
- **Bcmath implementation:** custom (toBits)
|
||||
|
||||
#### int scan0($start)
|
||||
Scans for 0, and returns index of first found bit
|
||||
- **GMP implementation:** gmp_scan0
|
||||
- **Bcmath implementation:** custom (toBits)
|
||||
|
||||
#### int scan1($start)
|
||||
Scans for 1, and returns index of first found bit
|
||||
- **GMP implementation:** gmp_scan1
|
||||
- **Bcmath implementation:** custom (toBits)
|
||||
|
||||
#### int cmp(ConvertibleToBi $x)
|
||||
Compares numbers, returns <0, 0, >0
|
||||
- **GMP implementation:** gmp_cmp
|
||||
- **Bcmath implementation:** bccomp
|
||||
|
||||
#### bool equals(ConvertibleToBi $x)
|
||||
Checks if numbers are equal
|
||||
- **GMP implementation:** gmp_cmp
|
||||
- **Bcmath implementation:** bccomp
|
||||
|
||||
#### int sign()
|
||||
Sign of number, returns -1, 0, 1
|
||||
- **GMP implementation:** gmp_sign
|
||||
- **Bcmath implementation:** check first character
|
||||
|
||||
#### int toNumber()
|
||||
Converts to number (use only with small 32/64bit numbers)
|
||||
- **GMP implementation:** gmp_intval
|
||||
- **Bcmath implementation:** intval
|
||||
|
||||
#### string toDec()
|
||||
Converts to decimal string
|
||||
- **GMP implementation:** gmp_strval
|
||||
- **Bcmath implementation:** just the value
|
||||
|
||||
#### string toHex()
|
||||
Converts to hex string
|
||||
- **GMP implementation:** gmp_strval
|
||||
- **Bcmath implementation:** toBytes + bin2hex
|
||||
|
||||
#### string toBytes
|
||||
Converts to binary string
|
||||
- **GMP implementation:** gmp_strval + hex2bin
|
||||
- **Bcmath implementation:** custom (bcmod + bcdiv + bccomp)
|
||||
|
||||
#### string toBits()
|
||||
Converts to bits string (0 and 1 characters)
|
||||
- **GMP implementation:** gmp_strval
|
||||
- **Bcmath implementation:** toBytes + decbin
|
||||
|
||||
#### string toString(int $base = 10)
|
||||
Converts to string using given base (supported bases 2-62, 256)
|
||||
- **GMP implementation:** all above toX functions, and for non standard gmp_strval
|
||||
- **Bcmath implementation:** all above toX functions, and for non standard bcmod + bcdiv + bccomp
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "simplito/bigint-wrapper-php",
|
||||
"description": "Common interface for php_gmp and php_bcmath modules",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simplito Team",
|
||||
"email": "s.smyczynski@simplito.com",
|
||||
"homepage": "https://simplito.com"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BI\\": "lib/"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,635 @@
|
||||
<?php
|
||||
|
||||
namespace BI;
|
||||
|
||||
if (!defined("S_MATH_BIGINTEGER_MODE")) {
|
||||
if (extension_loaded("gmp")) {
|
||||
define("S_MATH_BIGINTEGER_MODE", "gmp");
|
||||
}
|
||||
else if (extension_loaded("bcmath")) {
|
||||
define("S_MATH_BIGINTEGER_MODE", "bcmath");
|
||||
}
|
||||
else {
|
||||
if (!defined("S_MATH_BIGINTEGER_QUIET")) {
|
||||
throw new \Exception("Cannot use BigInteger. Neither gmp nor bcmath module is loaded");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (S_MATH_BIGINTEGER_MODE == "gmp") {
|
||||
|
||||
if (!extension_loaded("gmp")) {
|
||||
throw new \Exception("Extension gmp not loaded");
|
||||
}
|
||||
|
||||
class BigInteger {
|
||||
|
||||
public $value;
|
||||
|
||||
public function __construct($value = 0, $base = 10) {
|
||||
$this->value = $base === true ? $value : BigInteger::getGmp($value, $base);
|
||||
}
|
||||
|
||||
public static function createSafe($value = 0, $base = 10) {
|
||||
try {
|
||||
return new BigInteger($value, $base);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function isGmp($var) {
|
||||
if (is_resource($var)) {
|
||||
return get_resource_type($var) == "GMP integer";
|
||||
}
|
||||
if (class_exists("GMP") && $var instanceof \GMP) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function getGmp($value = 0, $base = 10) {
|
||||
if ($value instanceof BigInteger) {
|
||||
return $value->value;
|
||||
}
|
||||
if (BigInteger::isGmp($value)) {
|
||||
return $value;
|
||||
}
|
||||
$type = gettype($value);
|
||||
if ($type == "integer") {
|
||||
$gmp = gmp_init($value);
|
||||
if ($gmp === false) {
|
||||
throw new \Exception("Cannot initialize");
|
||||
}
|
||||
return $gmp;
|
||||
}
|
||||
if ($type == "string") {
|
||||
if ($base != 2 && $base != 10 && $base != 16 && $base != 256) {
|
||||
throw new \Exception("Unsupported BigInteger base");
|
||||
}
|
||||
if ($base == 256) {
|
||||
$value = bin2hex($value);
|
||||
$base = 16;
|
||||
}
|
||||
$level = error_reporting();
|
||||
error_reporting(0);
|
||||
$gmp = gmp_init($value, $base);
|
||||
error_reporting($level);
|
||||
if ($gmp === false) {
|
||||
throw new \Exception("Cannot initialize");
|
||||
}
|
||||
return $gmp;
|
||||
}
|
||||
throw new \Exception("Unsupported value, only string and integer are allowed, receive " . $type . ($type == "object" ? ", class: " . get_class($value) : ""));
|
||||
}
|
||||
|
||||
public function toDec() {
|
||||
return gmp_strval($this->value, 10);
|
||||
}
|
||||
|
||||
public function toHex() {
|
||||
$hex = gmp_strval($this->value, 16);
|
||||
return strlen($hex) % 2 == 1 ? "0". $hex : $hex;
|
||||
}
|
||||
|
||||
public function toBytes() {
|
||||
return hex2bin($this->toHex());
|
||||
}
|
||||
|
||||
public function toBase($base) {
|
||||
if ($base < 2 || $base > 62) {
|
||||
throw new \Exception("Invalid base");
|
||||
}
|
||||
return gmp_strval($this->value, $base);
|
||||
}
|
||||
|
||||
public function toBits() {
|
||||
return gmp_strval($this->value, 2);
|
||||
}
|
||||
|
||||
public function toString($base = 10) {
|
||||
if ($base == 2) {
|
||||
return $this->toBits();
|
||||
}
|
||||
if ($base == 10) {
|
||||
return $this->toDec();
|
||||
}
|
||||
if ($base == 16) {
|
||||
return $this->toHex();
|
||||
}
|
||||
if ($base == 256) {
|
||||
return $this->toBytes();
|
||||
}
|
||||
return $this->toBase($base);
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
public function toNumber() {
|
||||
return gmp_intval($this->value);
|
||||
}
|
||||
|
||||
public function add($x) {
|
||||
return new BigInteger(gmp_add($this->value, BigInteger::getGmp($x)), true);
|
||||
}
|
||||
|
||||
public function sub($x) {
|
||||
return new BigInteger(gmp_sub($this->value, BigInteger::getGmp($x)), true);
|
||||
}
|
||||
|
||||
public function mul($x) {
|
||||
return new BigInteger(gmp_mul($this->value, BigInteger::getGmp($x)), true);
|
||||
}
|
||||
|
||||
public function div($x) {
|
||||
return new BigInteger(gmp_div_q($this->value, BigInteger::getGmp($x)), true);
|
||||
}
|
||||
|
||||
public function divR($x) {
|
||||
return new BigInteger(gmp_div_r($this->value, BigInteger::getGmp($x)), true);
|
||||
}
|
||||
|
||||
public function divQR($x) {
|
||||
$res = gmp_div_qr($this->value, BigInteger::getGmp($x));
|
||||
return array(new BigInteger($res[0], true), new BigInteger($res[1], true));
|
||||
}
|
||||
|
||||
public function mod($x) {
|
||||
return new BigInteger(gmp_mod($this->value, BigInteger::getGmp($x)), true);
|
||||
}
|
||||
|
||||
public function gcd($x) {
|
||||
return new BigInteger(gmp_gcd($this->value, BigInteger::getGmp($x)), true);
|
||||
}
|
||||
|
||||
public function modInverse($x) {
|
||||
$res = gmp_invert($this->value, BigInteger::getGmp($x));
|
||||
return $res === false ? false : new BigInteger($res, true);
|
||||
}
|
||||
|
||||
public function pow($x) {
|
||||
return new BigInteger(gmp_pow($this->value, (new BigInteger($x))->toNumber()), true);
|
||||
}
|
||||
|
||||
public function powMod($x, $n) {
|
||||
return new BigInteger(gmp_powm($this->value, BigInteger::getGmp($x), BigInteger::getGmp($n)), true);
|
||||
}
|
||||
|
||||
public function abs() {
|
||||
return new BigInteger(gmp_abs($this->value), true);
|
||||
}
|
||||
|
||||
public function neg() {
|
||||
return new BigInteger(gmp_neg($this->value), true);
|
||||
}
|
||||
|
||||
public function binaryAnd($x) {
|
||||
return new BigInteger(gmp_and($this->value, BigInteger::getGmp($x)), true);
|
||||
}
|
||||
|
||||
public function binaryOr($x) {
|
||||
return new BigInteger(gmp_or($this->value, BigInteger::getGmp($x)), true);
|
||||
}
|
||||
|
||||
public function binaryXor($x) {
|
||||
return new BigInteger(gmp_xor($this->value, BigInteger::getGmp($x)), true);
|
||||
}
|
||||
|
||||
public function setbit($index, $bitOn = true) {
|
||||
$cpy = gmp_init(gmp_strval($this->value, 16), 16);
|
||||
gmp_setbit($cpy, $index, $bitOn);
|
||||
return new BigInteger($cpy, true);
|
||||
}
|
||||
|
||||
public function testbit($index) {
|
||||
return gmp_testbit($this->value, $index);
|
||||
}
|
||||
|
||||
public function scan0($start) {
|
||||
return gmp_scan0($this->value, $start);
|
||||
}
|
||||
|
||||
public function scan1($start) {
|
||||
return gmp_scan1($this->value, $start);
|
||||
}
|
||||
|
||||
public function cmp($x) {
|
||||
return gmp_cmp($this->value, BigInteger::getGmp($x));
|
||||
}
|
||||
|
||||
public function equals($x) {
|
||||
return $this->cmp($x) === 0;
|
||||
}
|
||||
|
||||
public function sign() {
|
||||
return gmp_sign($this->value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (S_MATH_BIGINTEGER_MODE == "bcmath") {
|
||||
|
||||
if (!extension_loaded("bcmath")) {
|
||||
throw new \Exception("Extension bcmath not loaded");
|
||||
}
|
||||
|
||||
class BigInteger{
|
||||
|
||||
public static $chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv";
|
||||
public $value;
|
||||
|
||||
public function __construct($value = 0, $base = 10) {
|
||||
$this->value = $base === true ? $value : BigInteger::getBC($value, $base);
|
||||
}
|
||||
|
||||
public static function createSafe($value = 0, $base = 10) {
|
||||
try {
|
||||
return new BigInteger($value, $base);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function checkBinary($str) {
|
||||
$len = strlen($str);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$c = ord($str[$i]);
|
||||
if (($i != 0 || $c != 45) && ($c < 48 || $c > 49)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function checkDecimal($str) {
|
||||
$len = strlen($str);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$c = ord($str[$i]);
|
||||
if (($i != 0 || $c != 45) && ($c < 48 || $c > 57)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function checkHex($str) {
|
||||
$len = strlen($str);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$c = ord($str[$i]);
|
||||
if (($i != 0 || $c != 45) && ($c < 48 || $c > 57) && ($c < 65 || $c > 70) && ($c < 97 || $c > 102)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function getBC($value = 0, $base = 10) {
|
||||
if ($value instanceof BigInteger) {
|
||||
return $value->value;
|
||||
}
|
||||
$type = gettype($value);
|
||||
if ($type == "integer") {
|
||||
return strval($value);
|
||||
}
|
||||
if ($type == "string") {
|
||||
if ($base == 2) {
|
||||
$value = str_replace(" ", "", $value);
|
||||
if (!BigInteger::checkBinary($value)) {
|
||||
throw new \Exception("Invalid characters");
|
||||
}
|
||||
$minus = $value[0] == "-";
|
||||
if ($minus) {
|
||||
$value = substr($value, 1);
|
||||
}
|
||||
$len = strlen($value);
|
||||
$m = 1;
|
||||
$res = "0";
|
||||
for ($i = $len - 1; $i >= 0; $i -= 8) {
|
||||
$h = $i - 7 < 0 ? substr($value, 0, $i + 1) : substr($value, $i - 7, 8);
|
||||
$res = bcadd($res, bcmul(bindec($h), $m, 0), 0);
|
||||
$m = bcmul($m, "256", 0);
|
||||
}
|
||||
return ($minus ? "-" : "") . $res;
|
||||
}
|
||||
if ($base == 10) {
|
||||
$value = str_replace(" ", "", $value);
|
||||
if (!BigInteger::checkDecimal($value)) {
|
||||
throw new \Exception("Invalid characters");
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
if ($base == 16) {
|
||||
$value = str_replace(" ", "", $value);
|
||||
if (!BigInteger::checkHex($value)) {
|
||||
throw new \Exception("Invalid characters");
|
||||
}
|
||||
$minus = $value[0] == "-";
|
||||
if ($minus) {
|
||||
$value = substr($value, 1);
|
||||
}
|
||||
$len = strlen($value);
|
||||
$m = 1;
|
||||
$res = "0";
|
||||
for ($i = $len - 1; $i >= 0; $i -= 2) {
|
||||
$h = $i == 0 ? "0" . substr($value, 0, 1) : substr($value, $i - 1, 2);
|
||||
$res = bcadd($res, bcmul(hexdec($h), $m, 0), 0);
|
||||
$m = bcmul($m, "256", 0);
|
||||
}
|
||||
return ($minus ? "-" : "") . $res;
|
||||
}
|
||||
if ($base == 256) {
|
||||
$len = strlen($value);
|
||||
$m = 1;
|
||||
$res = "0";
|
||||
for ($i = $len - 1; $i >= 0; $i -= 6) {
|
||||
$h = $i - 5 < 0 ? substr($value, 0, $i + 1) : substr($value, $i - 5, 6);
|
||||
$res = bcadd($res, bcmul(base_convert(bin2hex($h), 16, 10), $m, 0), 0);
|
||||
$m = bcmul($m, "281474976710656", 0);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
throw new \Exception("Unsupported BigInteger base");
|
||||
}
|
||||
throw new \Exception("Unsupported value, only string and integer are allowed, receive " . $type . ($type == "object" ? ", class: " . get_class($value) : ""));
|
||||
}
|
||||
|
||||
public function toDec() {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function toHex() {
|
||||
return bin2hex($this->toBytes());
|
||||
}
|
||||
|
||||
public function toBytes() {
|
||||
$value = "";
|
||||
$current = $this->value;
|
||||
if ($current[0] == "-") {
|
||||
$current = substr($current, 1);
|
||||
}
|
||||
while (bccomp($current, "0", 0) > 0) {
|
||||
$temp = bcmod($current, "281474976710656");
|
||||
$value = hex2bin(str_pad(base_convert($temp, 10, 16), 12, "0", STR_PAD_LEFT)) . $value;
|
||||
$current = bcdiv($current, "281474976710656", 0);
|
||||
}
|
||||
return ltrim($value, chr(0));
|
||||
}
|
||||
|
||||
public function toBase($base) {
|
||||
if ($base < 2 || $base > 62) {
|
||||
throw new \Exception("Invalid base");
|
||||
}
|
||||
$value = '';
|
||||
$current = $this->value;
|
||||
$base = BigInteger::getBC($base);
|
||||
|
||||
if ($current[0] == '-') {
|
||||
$current = substr($current, 1);
|
||||
}
|
||||
|
||||
while (bccomp($current, '0', 0) > 0) {
|
||||
$v = bcmod($current, $base);
|
||||
$value = BigInteger::$chars[$v] . $value;
|
||||
$current = bcdiv($current, $base, 0);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function toBits() {
|
||||
$bytes = $this->toBytes();
|
||||
$res = "";
|
||||
$len = strlen($bytes);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$b = decbin(ord($bytes[$i]));
|
||||
$res .= strlen($b) != 8 ? str_pad($b, 8, "0", STR_PAD_LEFT) : $b;
|
||||
}
|
||||
$res = ltrim($res, "0");
|
||||
return strlen($res) == 0 ? "0" : $res;
|
||||
}
|
||||
|
||||
public function toString($base = 10) {
|
||||
if ($base == 2) {
|
||||
return $this->toBits();
|
||||
}
|
||||
if ($base == 10) {
|
||||
return $this->toDec();
|
||||
}
|
||||
if ($base == 16) {
|
||||
return $this->toHex();
|
||||
}
|
||||
if ($base == 256) {
|
||||
return $this->toBytes();
|
||||
}
|
||||
return $this->toBase($base);
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
public function toNumber() {
|
||||
return intval($this->value);
|
||||
}
|
||||
|
||||
public function add($x) {
|
||||
return new BigInteger(bcadd($this->value, BigInteger::getBC($x), 0), true);
|
||||
}
|
||||
|
||||
public function sub($x) {
|
||||
return new BigInteger(bcsub($this->value, BigInteger::getBC($x), 0), true);
|
||||
}
|
||||
|
||||
public function mul($x) {
|
||||
return new BigInteger(bcmul($this->value, BigInteger::getBC($x), 0), true);
|
||||
}
|
||||
|
||||
public function div($x) {
|
||||
return new BigInteger(bcdiv($this->value, BigInteger::getBC($x), 0), true);
|
||||
}
|
||||
|
||||
public function divR($x) {
|
||||
return new BigInteger(bcmod($this->value, BigInteger::getBC($x)), true);
|
||||
}
|
||||
|
||||
public function divQR($x) {
|
||||
return array(
|
||||
$this->div($x),
|
||||
$this->divR($x)
|
||||
);
|
||||
}
|
||||
|
||||
public function mod($x) {
|
||||
$xv = BigInteger::getBC($x);
|
||||
$mod = bcmod($this->value, $xv);
|
||||
if ($mod[0] == "-") {
|
||||
$mod = bcadd($mod, $xv[0] == "-" ? substr($xv, 1) : $xv, 0);
|
||||
}
|
||||
return new BigInteger($mod, true);
|
||||
}
|
||||
|
||||
public function extendedGcd($n) {
|
||||
$u = $this->value;
|
||||
$v = (new BigInteger($n))->abs()->value;
|
||||
|
||||
$a = "1";
|
||||
$b = "0";
|
||||
$c = "0";
|
||||
$d = "1";
|
||||
|
||||
while (bccomp($v, "0", 0) != 0) {
|
||||
$q = bcdiv($u, $v, 0);
|
||||
|
||||
$temp = $u;
|
||||
$u = $v;
|
||||
$v = bcsub($temp, bcmul($v, $q, 0), 0);
|
||||
|
||||
$temp = $a;
|
||||
$a = $c;
|
||||
$c = bcsub($temp, bcmul($a, $q, 0), 0);
|
||||
|
||||
$temp = $b;
|
||||
$b = $d;
|
||||
$d = bcsub($temp, bcmul($b, $q, 0), 0);
|
||||
}
|
||||
|
||||
return array(
|
||||
"gcd" => new BigInteger($u, true),
|
||||
"x" => new BigInteger($a, true),
|
||||
"y" => new BigInteger($b, true)
|
||||
);
|
||||
}
|
||||
|
||||
public function gcd($x) {
|
||||
return $this->extendedGcd($x)["gcd"];
|
||||
}
|
||||
|
||||
public function modInverse($n) {
|
||||
$n = (new BigInteger($n))->abs();
|
||||
|
||||
if ($this->sign() < 0) {
|
||||
$temp = $this->abs();
|
||||
$temp = $temp->modInverse($n);
|
||||
return $n->sub($temp);
|
||||
}
|
||||
|
||||
extract($this->extendedGcd($n));
|
||||
|
||||
if (!$gcd->equals(1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$x = $x->sign() < 0 ? $x->add($n) : $x;
|
||||
|
||||
return $this->sign() < 0 ? $n->sub($x) : $x;
|
||||
}
|
||||
|
||||
public function pow($x) {
|
||||
return new BigInteger(bcpow($this->value, BigInteger::getBC($x), 0), true);
|
||||
}
|
||||
|
||||
public function powMod($x, $n) {
|
||||
return new BigInteger(bcpowmod($this->value, BigInteger::getBC($x), BigInteger::getBC($n), 0), true);
|
||||
}
|
||||
|
||||
public function abs() {
|
||||
return new BigInteger($this->value[0] == "-" ? substr($this->value, 1) : $this->value, true);
|
||||
}
|
||||
|
||||
public function neg() {
|
||||
return new BigInteger($this->value[0] == "-" ? substr($this->value, 1) : "-" . $this->value, true);
|
||||
}
|
||||
|
||||
public function binaryAnd($x) {
|
||||
$left = $this->toBytes();
|
||||
$right = (new BigInteger($x))->toBytes();
|
||||
|
||||
$length = max(strlen($left), strlen($right));
|
||||
|
||||
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
|
||||
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
|
||||
|
||||
return new BigInteger($left & $right, 256);
|
||||
}
|
||||
|
||||
public function binaryOr($x) {
|
||||
$left = $this->toBytes();
|
||||
$right = (new BigInteger($x))->toBytes();
|
||||
|
||||
$length = max(strlen($left), strlen($right));
|
||||
|
||||
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
|
||||
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
|
||||
|
||||
return new BigInteger($left | $right, 256);
|
||||
}
|
||||
|
||||
public function binaryXor($x) {
|
||||
$left = $this->toBytes();
|
||||
$right = (new BigInteger($x))->toBytes();
|
||||
|
||||
$length = max(strlen($left), strlen($right));
|
||||
|
||||
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
|
||||
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
|
||||
|
||||
return new BigInteger($left ^ $right, 256);
|
||||
}
|
||||
|
||||
public function setbit($index, $bitOn = true) {
|
||||
$bits = $this->toBits();
|
||||
$bits[strlen($bits) - $index - 1] = $bitOn ? "1" : "0";
|
||||
return new BigInteger($bits, 2);
|
||||
}
|
||||
|
||||
public function testbit($index) {
|
||||
$bytes = $this->toBytes();
|
||||
$bytesIndex = intval($index / 8);
|
||||
$len = strlen($bytes);
|
||||
$b = $bytesIndex >= $len ? 0 : ord($bytes[$len - $bytesIndex - 1]);
|
||||
$v = 1 << ($index % 8);
|
||||
return ($b & $v) === $v;
|
||||
}
|
||||
|
||||
public function scan0($start) {
|
||||
$bits = $this->toBits();
|
||||
$len = strlen($bits);
|
||||
if ($start < 0 || $start >= $len) {
|
||||
return -1;
|
||||
}
|
||||
$pos = strrpos($bits, "0", -1 - $start);
|
||||
return $pos === false ? -1 : $len - $pos - 1;
|
||||
}
|
||||
|
||||
public function scan1($start) {
|
||||
$bits = $this->toBits();
|
||||
$len = strlen($bits);
|
||||
if ($start < 0 || $start >= $len) {
|
||||
return -1;
|
||||
}
|
||||
$pos = strrpos($bits, "1", -1 - $start);
|
||||
return $pos === false ? -1 : $len - $pos - 1;
|
||||
}
|
||||
|
||||
public function cmp($x) {
|
||||
return bccomp($this->value, BigInteger::getBC($x));
|
||||
}
|
||||
|
||||
public function equals($x) {
|
||||
return $this->value === BigInteger::getBC($x);
|
||||
}
|
||||
|
||||
public function sign() {
|
||||
return $this->value[0] === "-" ? -1 : ($this->value === "0" ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
if (!defined("S_MATH_BIGINTEGER_QUIET")) {
|
||||
throw new \Exception("Unsupported S_MATH_BIGINTEGER_MODE " . S_MATH_BIGINTEGER_MODE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
define('S_MATH_BIGINTEGER_MODE', "bcmath");
|
||||
|
||||
require(__DIR__ . "/../lib/BigInteger.php");
|
||||
|
||||
use BI\BigInteger;
|
||||
|
||||
$a = new BigInteger("4547395333333333333333333333333333333333333343587493875493579375498759837593574935739857");
|
||||
$b = new BigInteger("-4547395333333333333333333333333333333333333343587493875493579375498759837593574935739857");
|
||||
$c = new BigInteger("0");
|
||||
$hex = "eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3";
|
||||
$bytes = hex2bin($hex);
|
||||
|
||||
function test($v, $b) {
|
||||
$start = microtime(true);
|
||||
$count = 10000;
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$v->binaryAnd($b);
|
||||
//$v->toBytes();
|
||||
//BigInteger::getBC($v, 256);
|
||||
}
|
||||
$end = microtime(true);
|
||||
error_log($end - $start);
|
||||
}
|
||||
|
||||
test($a, $a);
|
||||
test($b, $b);
|
||||
//test($bytes, $b);
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
use BI\BigInteger;
|
||||
|
||||
function test($a, $b, $message = "") {
|
||||
error_log(($a === $b ? "PASS" : "FAIL get: " . $a . ", expected: " . $b) . " " . $message);
|
||||
}
|
||||
|
||||
function testB($a, $b, $message = "") {
|
||||
error_log(($a->toString() === $b ? "PASS" : "FAIL get: " . $a . ", expected: " . $b) . " " . $message);
|
||||
}
|
||||
|
||||
function testSerialization($b, $msg = "") {
|
||||
test($b->toBits(), "1010000", $msg . " toBits");
|
||||
test($b->toBytes(), hex2bin("50"), $msg . " toBytes");
|
||||
test($b->toHex(), "50", $msg . " toHex");
|
||||
test($b->toDec(), "80", $msg . " toDec");
|
||||
test($b->toNumber(), 80, $msg . " toNumber");
|
||||
test($b->toBase(58), "1M", $msg . " to58");
|
||||
}
|
||||
|
||||
function testCreate() {
|
||||
error_log("=============\nTest serialization\n=============");
|
||||
testSerialization(new BigInteger("1010000", 2), "bits");
|
||||
testSerialization(new BigInteger(hex2bin("50"), 256), "bytes");
|
||||
testSerialization(new BigInteger("50", 16), "hex");
|
||||
testSerialization(new BigInteger("80", 10), "dec");
|
||||
testSerialization(new BigInteger("80"), "dec2");
|
||||
testSerialization(new BigInteger(80), "number");
|
||||
}
|
||||
|
||||
function testCreateSafeSingle($value, $base, $msg) {
|
||||
try {
|
||||
$z = new BigInteger($value, $base);
|
||||
error_log("FAIL exception during create " . $msg);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
error_log("PASS exception during create " . $msg);
|
||||
}
|
||||
test(BigInteger::createSafe($value, $base), false, "createSafe " . $msg);
|
||||
}
|
||||
|
||||
function testCreateSafe() {
|
||||
error_log("=============\nTest create safe\n=============");
|
||||
testCreateSafeSingle("zz", 2, "bin");
|
||||
testCreateSafeSingle("zz", 10, "dec");
|
||||
testCreateSafeSingle("zz", 16, "hex");
|
||||
}
|
||||
|
||||
function testSpaces() {
|
||||
error_log("=============\nTest spaces\n=============");
|
||||
test((new BigInteger("11 0 1", 2))->toBits(), "1101", "bin");
|
||||
test((new BigInteger("6 2 0 6", 10))->toDec(), "6206", "dec");
|
||||
test((new BigInteger("f3 5 12 ac 0", 16))->toHex(), "f3512ac0", "hex");
|
||||
}
|
||||
|
||||
function testOp() {
|
||||
error_log("=============\nTest op\n=============");
|
||||
testB((new BigInteger(20))->add(34), "54", "add");
|
||||
testB((new BigInteger(20))->sub(14), "6", "sub");
|
||||
testB((new BigInteger(20))->mul(12), "240", "mul");
|
||||
testB((new BigInteger(20))->div(4), "5", "div");
|
||||
testB((new BigInteger(20))->divR(7), "6", "divR");
|
||||
$qr = (new BigInteger(20))->divQR(6);
|
||||
testB($qr[0], "3", "divQR[0]");
|
||||
testB($qr[1], "2", "divQR[1]");
|
||||
testB((new BigInteger(20))->mod(3), "2", "mod");
|
||||
testB((new BigInteger(54))->gcd(81), "27", "gcd");
|
||||
testB((new BigInteger(3))->modInverse(10), "7", "modInverse");
|
||||
testB((new BigInteger(3))->pow(4), "81", "pow");
|
||||
testB((new BigInteger(3))->powMod(4, 10), "1", "powMod");
|
||||
testB((new BigInteger(20))->abs(), "20", "abs");
|
||||
testB((new BigInteger(20))->neg(), "-20", "neg");
|
||||
testB((new BigInteger(20))->binaryAnd(18), "16", "binaryAnd");
|
||||
testB((new BigInteger(20))->binaryOr(18), "22", "binaryOr");
|
||||
testB((new BigInteger(20))->binaryXor(18), "6", "binaryXor");
|
||||
testB((new BigInteger(20))->setbit(3), "28", "setbit");
|
||||
test((new BigInteger(20))->testbit(4), true, "testbit true");
|
||||
test((new BigInteger(20))->testbit(3), false, "testbit false");
|
||||
test((new BigInteger(5))->testbit(0), true, "testbit 0 true");
|
||||
test((new BigInteger(6))->testbit(0), false, "testbit 0 false");
|
||||
test((new BigInteger(6))->testbit(1), true, "testbit 1 true");
|
||||
test((new BigInteger(5))->testbit(1), false, "testbit 1 false");
|
||||
test((new BigInteger(132))->testbit(7), true, "testbit 7 true");
|
||||
test((new BigInteger(81))->testbit(7), false, "testbit 7 false");
|
||||
test((new BigInteger(258))->testbit(8), true, "testbit 8 true");
|
||||
test((new BigInteger(253))->testbit(8), false, "testbit 8 false");
|
||||
test((new BigInteger(20))->scan0(2), 3, "scan0");
|
||||
test((new BigInteger(20))->scan1(3), 4, "scan1");
|
||||
test((new BigInteger(20))->cmp(22), -1, "cmp -1");
|
||||
test((new BigInteger(20))->cmp(20), 0, "cmp 0");
|
||||
test((new BigInteger(20))->cmp(18), 1, "cmp 1");
|
||||
test((new BigInteger(20))->equals(20), true, "equals true");
|
||||
test((new BigInteger(20))->equals(21), false, "equals false");
|
||||
test((new BigInteger(-20))->sign(), -1, "sign -1");
|
||||
test((new BigInteger(0))->sign(), 0, "sign 0");
|
||||
test((new BigInteger(20))->sign(), 1, "sign 1");
|
||||
testB(new BigInteger("-20"), "-20", "minus");
|
||||
testB(new BigInteger("-14", 16), "-20", "minus");
|
||||
testB(new BigInteger("-10100", 2), "-20", "minus");
|
||||
}
|
||||
|
||||
function testBig() {
|
||||
error_log("=============\nTest big\n=============");
|
||||
$bits = "1001010111010010100001000101110110100001000101101000110101010101001";
|
||||
$hex = "eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3";
|
||||
$dec = "436529472098746319073192837123683467019263172846";
|
||||
$bytes = hex2bin($hex);
|
||||
test((new BigInteger($bits, 2))->toBits(), $bits, "init big from binary");
|
||||
test((new BigInteger($dec, 10))->toDec(), $dec, "init big from dec");
|
||||
test((new BigInteger($hex, 16))->toHex(), $hex, "init big from hex");
|
||||
test((new BigInteger($bytes, 256))->toBytes(), $bytes, "init big from buffer");
|
||||
}
|
||||
|
||||
testCreate();
|
||||
testCreateSafe();
|
||||
testSpaces();
|
||||
testOp();
|
||||
testBig();
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
define('S_MATH_BIGINTEGER_MODE', "bcmath");
|
||||
|
||||
require(__DIR__ . "/../lib/BigInteger.php");
|
||||
require(__DIR__ . "/test.php");
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
define('S_MATH_BIGINTEGER_MODE', "gmp");
|
||||
|
||||
require(__DIR__ . "/../lib/BigInteger.php");
|
||||
require(__DIR__ . "/test.php");
|
||||
@@ -0,0 +1,4 @@
|
||||
/vendor/
|
||||
.settings
|
||||
.project
|
||||
.buildpath
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
|
||||
## MIT LICENSE
|
||||
|
||||
Copyright (C) 2018 Simplito
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
|
||||
# BigNum library for PHP
|
||||
|
||||
|
||||
## Information
|
||||
|
||||
This library provides a PHP Big Number API compatible with [bn.js](https://github.com/indutny/bn.js) and is used in Fast PHP ECC library [elliptic-php](https://github.com/simplito/elliptic-php).
|
||||
|
||||
This software is licensed under the MIT License.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
You can install this library via Composer:
|
||||
```
|
||||
composer require simplito/bn-php
|
||||
```
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "simplito/bn-php",
|
||||
"description": "Big number implementation compatible with bn.js",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simplito Team",
|
||||
"email": "s.smyczynski@simplito.com",
|
||||
"homepage": "https://simplito.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"simplito/bigint-wrapper-php": "~1.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BN\\": "lib/"
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+768
@@ -0,0 +1,768 @@
|
||||
<?php
|
||||
namespace BN;
|
||||
|
||||
use \JsonSerializable;
|
||||
use \Exception;
|
||||
use \BI\BigInteger;
|
||||
|
||||
class BN implements JsonSerializable
|
||||
{
|
||||
public static $ASSERT_ENABLED;
|
||||
public $bi;
|
||||
public $red;
|
||||
|
||||
function __construct($number, $base = 10, $endian = null)
|
||||
{
|
||||
if( $number instanceof BN ) {
|
||||
$this->bi = $number->bi;
|
||||
$this->red = $number->red;
|
||||
return;
|
||||
}
|
||||
|
||||
// Reduction context
|
||||
$this->red = null;
|
||||
|
||||
if ( $number instanceof BigInteger ) {
|
||||
$this->bi = $number;
|
||||
return;
|
||||
}
|
||||
|
||||
if( is_array($number) )
|
||||
{
|
||||
$number = call_user_func_array("pack", array_merge(array("C*"), $number));
|
||||
$number = bin2hex($number);
|
||||
$base = 16;
|
||||
}
|
||||
|
||||
if( $base == "hex" )
|
||||
$base = 16;
|
||||
|
||||
if ($endian == 'le') {
|
||||
if ($base != 16)
|
||||
throw new \Exception("Not implemented");
|
||||
$number = bin2hex(strrev(hex2bin($number)));
|
||||
}
|
||||
|
||||
$this->bi = new BigInteger($number, $base);
|
||||
}
|
||||
|
||||
public function negative() {
|
||||
return $this->bi->sign() < 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
public static function isBN($num) {
|
||||
return ($num instanceof BN);
|
||||
}
|
||||
|
||||
public static function max($left, $right) {
|
||||
return ( $left->cmp($right) > 0 ) ? $left : $right;
|
||||
}
|
||||
|
||||
public static function min($left, $right) {
|
||||
return ( $left->cmp($right) < 0 ) ? $left : $right;
|
||||
}
|
||||
|
||||
public function copy($dest)
|
||||
{
|
||||
$dest->bi = $this->bi;
|
||||
$dest->red = $this->red;
|
||||
}
|
||||
|
||||
public function _clone() {
|
||||
return clone($this);
|
||||
}
|
||||
|
||||
public function toString($base = 10, $padding = 0)
|
||||
{
|
||||
if( $base == "hex" )
|
||||
$base = 16;
|
||||
$str = $this->bi->abs()->toString($base);
|
||||
if ($padding > 0) {
|
||||
$len = strlen($str);
|
||||
$mod = $len % $padding;
|
||||
if ($mod > 0)
|
||||
$len = $len + $padding - $mod;
|
||||
$str = str_pad($str, $len, "0", STR_PAD_LEFT);
|
||||
}
|
||||
if( $this->negative() )
|
||||
return "-" . $str;
|
||||
return $str;
|
||||
}
|
||||
|
||||
public function toNumber() {
|
||||
return $this->bi->toNumber();
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize() {
|
||||
return $this->toString(16);
|
||||
}
|
||||
|
||||
public function toArray($endian = "be", $length = -1)
|
||||
{
|
||||
$hex = $this->toString(16);
|
||||
if( $hex[0] === "-" )
|
||||
$hex = substr($hex, 1);
|
||||
|
||||
if( strlen($hex) % 2 )
|
||||
$hex = "0" . $hex;
|
||||
|
||||
$bytes = array_map(
|
||||
function($v) { return hexdec($v); },
|
||||
str_split($hex, 2)
|
||||
);
|
||||
|
||||
if( $length > 0 )
|
||||
{
|
||||
$count = count($bytes);
|
||||
if( $count > $length )
|
||||
throw new Exception("Byte array longer than desired length");
|
||||
|
||||
for($i = $count; $i < $length; $i++)
|
||||
array_unshift($bytes, 0);
|
||||
}
|
||||
|
||||
if( $endian === "le" )
|
||||
$bytes = array_reverse($bytes);
|
||||
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
public function bitLength() {
|
||||
$bin = $this->toString(2);
|
||||
return strlen($bin) - ( $bin[0] === "-" ? 1 : 0 );
|
||||
}
|
||||
|
||||
public function zeroBits() {
|
||||
return $this->bi->scan1(0);
|
||||
}
|
||||
|
||||
public function byteLength() {
|
||||
return ceil($this->bitLength() / 8);
|
||||
}
|
||||
|
||||
//TODO toTwos, fromTwos
|
||||
|
||||
public function isNeg() {
|
||||
return $this->negative() !== 0;
|
||||
}
|
||||
|
||||
// Return negative clone of `this`
|
||||
public function neg() {
|
||||
return $this->_clone()->ineg();
|
||||
}
|
||||
|
||||
public function ineg() {
|
||||
$this->bi = $this->bi->neg();
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Or `num` with `this` in-place
|
||||
public function iuor(BN $num) {
|
||||
$this->bi = $this->bi->binaryOr($num->bi);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function ior(BN $num) {
|
||||
if (BN::$ASSERT_ENABLED) assert(!$this->negative() && !$num->negative());
|
||||
return $this->iuor($num);
|
||||
}
|
||||
|
||||
// Or `num` with `this`
|
||||
public function _or(BN $num) {
|
||||
if( $this->ucmp($num) > 0 )
|
||||
return $this->_clone()->ior($num);
|
||||
return $num->_clone()->ior($this);
|
||||
}
|
||||
|
||||
public function uor(BN $num) {
|
||||
if( $this->ucmp($num) > 0 )
|
||||
return $this->_clone()->iuor($num);
|
||||
return $num->_clone()->ior($this);
|
||||
}
|
||||
|
||||
// And `num` with `this` in-place
|
||||
public function iuand(BN $num) {
|
||||
$this->bi = $this->bi->binaryAnd($num->bi);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function iand(BN $num) {
|
||||
if (BN::$ASSERT_ENABLED) assert(!$this->negative() && !$num->negative());
|
||||
return $this->iuand($num);
|
||||
}
|
||||
|
||||
// And `num` with `this`
|
||||
public function _and(BN $num) {
|
||||
if( $this->ucmp($num) > 0 )
|
||||
return $this->_clone()->iand($num);
|
||||
return $num->_clone()->iand($this);
|
||||
}
|
||||
|
||||
public function uand(BN $num) {
|
||||
if( $this->ucmp($num) > 0 )
|
||||
return $this->_clone()->iuand($num);
|
||||
return $num->_clone()->iuand($this);
|
||||
}
|
||||
|
||||
// Xor `num` with `this` in-place
|
||||
public function iuxor(BN $num) {
|
||||
$this->bi = $this->bi->binaryXor($num->bi);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function ixor(BN $num) {
|
||||
if (BN::$ASSERT_ENABLED) assert(!$this->negative() && !$num->negative());
|
||||
return $this->iuxor($num);
|
||||
}
|
||||
|
||||
// Xor `num` with `this`
|
||||
public function _xor(BN $num) {
|
||||
if( $this->ucmp($num) > 0 )
|
||||
return $this->_clone()->ixor($num);
|
||||
return $num->_clone()->ixor($this);
|
||||
}
|
||||
|
||||
public function uxor(BN $num) {
|
||||
if( $this->ucmp($num) > 0 )
|
||||
return $this->_clone()->iuxor($num);
|
||||
return $num->_clone()->iuxor($this);
|
||||
}
|
||||
|
||||
// Not ``this`` with ``width`` bitwidth
|
||||
public function inotn($width)
|
||||
{
|
||||
assert(is_integer($width) && $width >= 0);
|
||||
$neg = false;
|
||||
if( $this->isNeg() )
|
||||
{
|
||||
$this->negi();
|
||||
$neg = true;
|
||||
}
|
||||
|
||||
for($i = 0; $i < $width; $i++)
|
||||
$this->bi = $this->bi->setbit($i, !$this->bi->testbit($i));
|
||||
|
||||
return $neg ? $this->negi() : $this;
|
||||
}
|
||||
|
||||
public function notn($width) {
|
||||
return $this->_clone()->inotn($width);
|
||||
}
|
||||
|
||||
// Set `bit` of `this`
|
||||
public function setn($bit, $val) {
|
||||
assert(is_integer($bit) && $bit > 0);
|
||||
$this->bi = $this->bi->setbit($bit, !!$val);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Add `num` to `this` in-place
|
||||
public function iadd(BN $num) {
|
||||
$this->bi = $this->bi->add($num->bi);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Add `num` to `this`
|
||||
public function add(BN $num) {
|
||||
return $this->_clone()->iadd($num);
|
||||
}
|
||||
|
||||
// Subtract `num` from `this` in-place
|
||||
public function isub(BN $num) {
|
||||
$this->bi = $this->bi->sub($num->bi);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Subtract `num` from `this`
|
||||
public function sub(BN $num) {
|
||||
return $this->_clone()->isub($num);
|
||||
}
|
||||
|
||||
// Multiply `this` by `num`
|
||||
public function mul(BN $num) {
|
||||
return $this->_clone()->imul($num);
|
||||
}
|
||||
|
||||
// In-place Multiplication
|
||||
public function imul(BN $num) {
|
||||
$this->bi = $this->bi->mul($num->bi);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function imuln($num)
|
||||
{
|
||||
assert(is_numeric($num));
|
||||
$int = intval($num);
|
||||
$res = $this->bi->mul($int);
|
||||
|
||||
if( ($num - $int) > 0 )
|
||||
{
|
||||
$mul = 10;
|
||||
$frac = ($num - $int) * $mul;
|
||||
$int = intval($frac);
|
||||
while( ($frac - $int) > 0 )
|
||||
{
|
||||
$mul *= 10;
|
||||
$frac *= 10;
|
||||
$int = intval($frac);
|
||||
}
|
||||
|
||||
$tmp = $this->bi->mul($int);
|
||||
$tmp = $tmp->div($mul);
|
||||
$res = $res->add($tmp);
|
||||
}
|
||||
|
||||
$this->bi = $res;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function muln($num) {
|
||||
return $this->_clone()->imuln($num);
|
||||
}
|
||||
|
||||
// `this` * `this`
|
||||
public function sqr() {
|
||||
return $this->mul($this);
|
||||
}
|
||||
|
||||
// `this` * `this` in-place
|
||||
public function isqr() {
|
||||
return $this->imul($this);
|
||||
}
|
||||
|
||||
// Math.pow(`this`, `num`)
|
||||
public function pow(BN $num) {
|
||||
$res = clone($this);
|
||||
$res->bi = $res->bi->pow($num->bi);
|
||||
return $res;
|
||||
}
|
||||
|
||||
// Shift-left in-place
|
||||
public function iushln($bits) {
|
||||
assert(is_integer($bits) && $bits >= 0);
|
||||
if ($bits < 54) {
|
||||
$this->bi = $this->bi->mul(1 << $bits);
|
||||
} else {
|
||||
$this->bi = $this->bi->mul((new BigInteger(2))->pow($bits));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function ishln($bits) {
|
||||
if (BN::$ASSERT_ENABLED) assert(!$this->negative());
|
||||
return $this->iushln($bits);
|
||||
}
|
||||
|
||||
// Shift-right in-place
|
||||
// NOTE: `hint` is a lowest bit before trailing zeroes
|
||||
// NOTE: if `extended` is present - it will be filled with destroyed bits
|
||||
public function iushrn($bits, $hint = 0, &$extended = null) {
|
||||
if( $hint != 0 )
|
||||
throw new Exception("Not implemented");
|
||||
|
||||
assert(is_integer($bits) && $bits >= 0);
|
||||
|
||||
if( $extended != null )
|
||||
$extended = $this->maskn($bits);
|
||||
|
||||
if ($bits < 54) {
|
||||
$this->bi = $this->bi->div(1 << $bits);
|
||||
} else {
|
||||
$this->bi = $this->bi->div((new BigInteger(2))->pow($bits));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function ishrn($bits, $hint = null, $extended = null) {
|
||||
if (BN::$ASSERT_ENABLED) assert(!$this->negative());
|
||||
return $this->iushrn($bits, $hint, $extended);
|
||||
}
|
||||
|
||||
// Shift-left
|
||||
public function shln($bits) {
|
||||
return $this->_clone()->ishln($bits);
|
||||
}
|
||||
|
||||
public function ushln($bits) {
|
||||
return $this->_clone()->iushln($bits);
|
||||
}
|
||||
|
||||
// Shift-right
|
||||
public function shrn($bits) {
|
||||
return $this->_clone()->ishrn($bits);
|
||||
}
|
||||
|
||||
public function ushrn($bits) {
|
||||
return $this->_clone()->iushrn($bits);
|
||||
}
|
||||
|
||||
// Test if n bit is set
|
||||
public function testn($bit) {
|
||||
assert(is_integer($bit) && $bit >= 0);
|
||||
return $this->bi->testbit($bit);
|
||||
}
|
||||
|
||||
// Return only lowers bits of number (in-place)
|
||||
public function imaskn($bits) {
|
||||
assert(is_integer($bits) && $bits >= 0);
|
||||
if (BN::$ASSERT_ENABLED) assert(!$this->negative());
|
||||
$mask = "";
|
||||
for($i = 0; $i < $bits; $i++)
|
||||
$mask .= "1";
|
||||
return $this->iand(new BN($mask, 2));
|
||||
}
|
||||
|
||||
// Return only lowers bits of number
|
||||
public function maskn($bits) {
|
||||
return $this->_clone()->imaskn($bits);
|
||||
}
|
||||
|
||||
// Add plain number `num` to `this`
|
||||
public function iaddn($num) {
|
||||
assert(is_numeric($num));
|
||||
$this->bi = $this->bi->add(intval($num));
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Subtract plain number `num` from `this`
|
||||
public function isubn($num) {
|
||||
assert(is_numeric($num));
|
||||
$this->bi = $this->bi->sub(intval($num));
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addn($num) {
|
||||
return $this->_clone()->iaddn($num);
|
||||
}
|
||||
|
||||
public function subn($num) {
|
||||
return $this->_clone()->isubn($num);
|
||||
}
|
||||
|
||||
public function iabs() {
|
||||
if ($this->bi->sign() < 0) {
|
||||
$this->bi = $this->bi->abs();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function abs() {
|
||||
$res = clone($this);
|
||||
if ($res->bi->sign() < 0)
|
||||
$res->bi = $res->bi->abs();
|
||||
return $res;
|
||||
}
|
||||
|
||||
// Find `this` / `num`
|
||||
public function div(BN $num) {
|
||||
if (BN::$ASSERT_ENABLED) assert(!$num->isZero());
|
||||
$res = clone($this);
|
||||
$res->bi = $res->bi->div($num->bi);
|
||||
return $res;
|
||||
}
|
||||
|
||||
// Find `this` % `num`
|
||||
public function mod(BN $num) {
|
||||
if (BN::$ASSERT_ENABLED) assert(!$num->isZero());
|
||||
$res = clone($this);
|
||||
$res->bi = $res->bi->divR($num->bi);
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function umod(BN $num) {
|
||||
if (BN::$ASSERT_ENABLED) assert(!$num->isZero());
|
||||
$tmp = $num->bi->sign() < 0 ? $num->bi->abs() : $num->bi;
|
||||
$res = clone($this);
|
||||
$res->bi = $this->bi->mod($tmp);
|
||||
return $res;
|
||||
}
|
||||
|
||||
// Find Round(`this` / `num`)
|
||||
public function divRound(BN $num)
|
||||
{
|
||||
if (BN::$ASSERT_ENABLED) assert(!$num->isZero());
|
||||
|
||||
$negative = $this->negative() !== $num->negative();
|
||||
|
||||
$res = $this->_clone()->abs();
|
||||
$arr = $res->bi->divQR($num->bi->abs());
|
||||
$res->bi = $arr[0];
|
||||
$tmp = $num->bi->sub($arr[1]->mul(2));
|
||||
if( $tmp->cmp(0) <= 0 && (!$negative || $this->negative() === 0) )
|
||||
$res->iaddn(1);
|
||||
return $negative ? $res->negi() : $res;
|
||||
}
|
||||
|
||||
public function modn($num) {
|
||||
assert(is_numeric($num) && $num != 0);
|
||||
return $this->bi->divR(intval($num))->toNumber();
|
||||
}
|
||||
|
||||
// In-place division by number
|
||||
public function idivn($num) {
|
||||
assert(is_numeric($num) && $num != 0);
|
||||
$this->bi = $this->bi->div(intval($num));
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function divn($num) {
|
||||
return $this->_clone()->idivn($num);
|
||||
}
|
||||
|
||||
public function gcd(BN $num) {
|
||||
$res = clone($this);
|
||||
$res->bi = $this->bi->gcd($num->bi);
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function invm(BN $num) {
|
||||
$res = clone($this);
|
||||
$res->bi = $res->bi->modInverse($num->bi);
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function isEven() {
|
||||
return !$this->bi->testbit(0);
|
||||
}
|
||||
|
||||
public function isOdd() {
|
||||
return $this->bi->testbit(0);
|
||||
}
|
||||
|
||||
public function andln($num) {
|
||||
assert(is_numeric($num));
|
||||
return $this->bi->binaryAnd($num)->toNumber();
|
||||
}
|
||||
|
||||
public function bincn($num) {
|
||||
$tmp = (new BN(1))->iushln($num);
|
||||
return $this->add($tmp);
|
||||
}
|
||||
|
||||
public function isZero() {
|
||||
return $this->bi->sign() == 0;
|
||||
}
|
||||
|
||||
public function cmpn($num) {
|
||||
assert(is_numeric($num));
|
||||
return $this->bi->cmp($num);
|
||||
}
|
||||
|
||||
// Compare two numbers and return:
|
||||
// 1 - if `this` > `num`
|
||||
// 0 - if `this` == `num`
|
||||
// -1 - if `this` < `num`
|
||||
public function cmp(BN $num) {
|
||||
return $this->bi->cmp($num->bi);
|
||||
}
|
||||
|
||||
public function ucmp(BN $num) {
|
||||
return $this->bi->abs()->cmp($num->bi->abs());
|
||||
}
|
||||
|
||||
public function gtn($num) {
|
||||
return $this->cmpn($num) > 0;
|
||||
}
|
||||
|
||||
public function gt(BN $num) {
|
||||
return $this->cmp($num) > 0;
|
||||
}
|
||||
|
||||
public function gten($num) {
|
||||
return $this->cmpn($num) >= 0;
|
||||
}
|
||||
|
||||
public function gte(BN $num) {
|
||||
return $this->cmp($num) >= 0;
|
||||
}
|
||||
|
||||
public function ltn($num) {
|
||||
return $this->cmpn($num) < 0;
|
||||
}
|
||||
|
||||
public function lt(BN $num) {
|
||||
return $this->cmp($num) < 0;
|
||||
}
|
||||
|
||||
public function lten($num) {
|
||||
return $this->cmpn($num) <= 0;
|
||||
}
|
||||
|
||||
public function lte(BN $num) {
|
||||
return $this->cmp($num) <= 0;
|
||||
}
|
||||
|
||||
public function eqn($num) {
|
||||
return $this->cmpn($num) === 0;
|
||||
}
|
||||
|
||||
public function eq(BN $num) {
|
||||
return $this->cmp($num) === 0;
|
||||
}
|
||||
|
||||
public function toRed(Red &$ctx) {
|
||||
if( $this->red !== null )
|
||||
throw new Exception("Already a number in reduction context");
|
||||
if( $this->negative() !== 0 )
|
||||
throw new Exception("red works only with positives");
|
||||
return $ctx->convertTo($this)->_forceRed($ctx);
|
||||
}
|
||||
|
||||
public function fromRed() {
|
||||
if( $this->red === null )
|
||||
throw new Exception("fromRed works only with numbers in reduction context");
|
||||
return $this->red->convertFrom($this);
|
||||
}
|
||||
|
||||
public function _forceRed(Red &$ctx) {
|
||||
$this->red = $ctx;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function forceRed(Red &$ctx) {
|
||||
if( $this->red !== null )
|
||||
throw new Exception("Already a number in reduction context");
|
||||
return $this->_forceRed($ctx);
|
||||
}
|
||||
|
||||
public function redAdd(BN $num) {
|
||||
if( $this->red === null )
|
||||
throw new Exception("redAdd works only with red numbers");
|
||||
|
||||
$res = clone($this);
|
||||
$res->bi = $res->bi->add($num->bi);
|
||||
if ($res->bi->cmp($this->red->m->bi) >= 0)
|
||||
$res->bi = $res->bi->sub($this->red->m->bi);
|
||||
return $res;
|
||||
// return $this->red->add($this, $num);
|
||||
}
|
||||
|
||||
public function redIAdd(BN $num) {
|
||||
if( $this->red === null )
|
||||
throw new Exception("redIAdd works only with red numbers");
|
||||
$res = $this;
|
||||
$res->bi = $res->bi->add($num->bi);
|
||||
if ($res->bi->cmp($this->red->m->bi) >= 0)
|
||||
$res->bi = $res->bi->sub($this->red->m->bi);
|
||||
return $res;
|
||||
//return $this->red->iadd($this, $num);
|
||||
}
|
||||
|
||||
public function redSub(BN $num) {
|
||||
if( $this->red === null )
|
||||
throw new Exception("redSub works only with red numbers");
|
||||
$res = clone($this);
|
||||
$res->bi = $this->bi->sub($num->bi);
|
||||
if ($res->bi->sign() < 0)
|
||||
$res->bi = $res->bi->add($this->red->m->bi);
|
||||
return $res;
|
||||
//return $this->red->sub($this, $num);
|
||||
}
|
||||
|
||||
public function redISub(BN $num) {
|
||||
if( $this->red === null )
|
||||
throw new Exception("redISub works only with red numbers");
|
||||
$this->bi = $this->bi->sub($num->bi);
|
||||
if ($this->bi->sign() < 0)
|
||||
$this->bi = $this->bi->add($this->red->m->bi);
|
||||
return $this;
|
||||
|
||||
// return $this->red->isub($this, $num);
|
||||
}
|
||||
|
||||
public function redShl(BN $num) {
|
||||
if( $this->red === null )
|
||||
throw new Exception("redShl works only with red numbers");
|
||||
return $this->red->shl($this, $num);
|
||||
}
|
||||
|
||||
public function redMul(BN $num) {
|
||||
if( $this->red === null )
|
||||
throw new Exception("redMul works only with red numbers");
|
||||
$res = clone($this);
|
||||
$res->bi = $this->bi->mul($num->bi)->mod($this->red->m->bi);
|
||||
return $res;
|
||||
/*
|
||||
return $this->red->mul($this, $num);
|
||||
*/
|
||||
}
|
||||
|
||||
public function redIMul(BN $num) {
|
||||
if( $this->red === null )
|
||||
throw new Exception("redIMul works only with red numbers");
|
||||
$this->bi = $this->bi->mul($num->bi)->mod($this->red->m->bi);
|
||||
return $this;
|
||||
//return $this->red->imul($this, $num);
|
||||
}
|
||||
|
||||
public function redSqr() {
|
||||
if( $this->red === null )
|
||||
throw new Exception("redSqr works only with red numbers");
|
||||
$res = clone($this);
|
||||
$res->bi = $this->bi->mul($this->bi)->mod($this->red->m->bi);
|
||||
return $res;
|
||||
/*
|
||||
$this->red->verify1($this);
|
||||
return $this->red->sqr($this);
|
||||
*/
|
||||
}
|
||||
|
||||
public function redISqr() {
|
||||
if( $this->red === null )
|
||||
throw new Exception("redISqr works only with red numbers");
|
||||
$res = $this;
|
||||
$res->bi = $this->bi->mul($this->bi)->mod($this->red->m->bi);
|
||||
return $res;
|
||||
/* $this->red->verify1($this);
|
||||
return $this->red->isqr($this);
|
||||
*/
|
||||
}
|
||||
|
||||
public function redSqrt() {
|
||||
if( $this->red === null )
|
||||
throw new Exception("redSqrt works only with red numbers");
|
||||
$this->red->verify1($this);
|
||||
return $this->red->sqrt($this);
|
||||
}
|
||||
|
||||
public function redInvm() {
|
||||
if( $this->red === null )
|
||||
throw new Exception("redInvm works only with red numbers");
|
||||
$this->red->verify1($this);
|
||||
return $this->red->invm($this);
|
||||
}
|
||||
|
||||
public function redNeg() {
|
||||
if( $this->red === null )
|
||||
throw new Exception("redNeg works only with red numbers");
|
||||
$this->red->verify1($this);
|
||||
return $this->red->neg($this);
|
||||
}
|
||||
|
||||
public function redPow(BN $num) {
|
||||
$this->red->verify2($this, $num);
|
||||
return $this->red->pow($this, $num);
|
||||
}
|
||||
|
||||
public static function red($num) {
|
||||
return new Red($num);
|
||||
}
|
||||
|
||||
public static function mont($num) {
|
||||
return new Red($num);
|
||||
}
|
||||
|
||||
public function inspect() {
|
||||
return ($this->red == null ? "<BN: " : "<BN-R: ") . $this->toString(16) . ">";
|
||||
}
|
||||
|
||||
public function __debugInfo() {
|
||||
if ($this->red != null) {
|
||||
return ["BN-R" => $this->toString(16)];
|
||||
} else {
|
||||
return ["BN" => $this->toString(16)];
|
||||
}
|
||||
}
|
||||
}
|
||||
BN::$ASSERT_ENABLED = ini_get("zend.assertions") === "1";
|
||||
Vendored
+221
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
namespace BN;
|
||||
|
||||
use \Exception;
|
||||
use \BI\BigInteger;
|
||||
|
||||
class Red
|
||||
{
|
||||
public static $ASSERT_ENABLED;
|
||||
public $m;
|
||||
|
||||
function __construct($m) {
|
||||
if( is_string($m) )
|
||||
$this->m = Red::primeByName($m);
|
||||
else
|
||||
$this->m = $m;
|
||||
|
||||
if( !$this->m->gtn(1) )
|
||||
throw new Exception("Modulus must be greater than 1");
|
||||
}
|
||||
|
||||
|
||||
public static function primeByName($name)
|
||||
{
|
||||
switch($name) {
|
||||
case "k256":
|
||||
return new BN("ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f", 16);
|
||||
case "p224":
|
||||
return new BN("ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001", 16);
|
||||
case "p192":
|
||||
return new BN("ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff", 16);
|
||||
case "p25519":
|
||||
return new BN("7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed", 16);
|
||||
default:
|
||||
throw new Exception("Unknown prime name " . $name);
|
||||
}
|
||||
}
|
||||
|
||||
public function verify1(BN $num)
|
||||
{
|
||||
if (Red::$ASSERT_ENABLED) assert(!$num->negative()); //,"red works only with positives");
|
||||
assert($num->red); //, "red works only with red numbers");
|
||||
}
|
||||
|
||||
public function verify2(BN $a, BN $b)
|
||||
{
|
||||
if (Red::$ASSERT_ENABLED) assert(!$a->negative() && !$b->negative()); //, "red works only with positives");
|
||||
assert($a->red && ($a->red == $b->red)); //, "red works only with red numbers");
|
||||
}
|
||||
|
||||
public function imod(BN &$a) {
|
||||
return $a->umod($this->m)->_forceRed($this);
|
||||
}
|
||||
|
||||
public function neg(BN $a)
|
||||
{
|
||||
if( $a->isZero() )
|
||||
return $a->_clone();
|
||||
return $this->m->sub($a)->_forceRed($this);
|
||||
}
|
||||
|
||||
public function add(BN $a, BN $b)
|
||||
{
|
||||
$this->verify2($a, $b);
|
||||
|
||||
$res = $a->add($b);
|
||||
if( $res->cmp($this->m) >= 0 )
|
||||
$res->isub($this->m);
|
||||
return $res->_forceRed($this);
|
||||
}
|
||||
|
||||
public function iadd(BN &$a, BN $b)
|
||||
{
|
||||
$this->verify2($a, $b);
|
||||
|
||||
$a->iadd($b);
|
||||
if( $a->cmp($this->m) >= 0 )
|
||||
$a->isub($this->m);
|
||||
|
||||
return $a;
|
||||
}
|
||||
|
||||
public function sub(BN $a, BN $b)
|
||||
{
|
||||
$this->verify2($a, $b);
|
||||
|
||||
$res = $a->sub($b);
|
||||
if( $res->negative() )
|
||||
$res->iadd($this->m);
|
||||
|
||||
return $res->_forceRed($this);
|
||||
}
|
||||
|
||||
public function isub(BN &$a, $b)
|
||||
{
|
||||
$this->verify2($a, $b);
|
||||
|
||||
$a->isub($b);
|
||||
if( $a->negative() )
|
||||
$a->iadd($this->m);
|
||||
|
||||
return $a;
|
||||
}
|
||||
|
||||
public function shl(BN $a, $num) {
|
||||
$this->verify1($a);
|
||||
return $this->imod($a->ushln($num));
|
||||
}
|
||||
|
||||
public function imul(BN &$a, BN $b) {
|
||||
$this->verify2($a, $b);
|
||||
$res = $a->imul($b);
|
||||
return $this->imod($res);
|
||||
}
|
||||
|
||||
public function mul(BN $a, BN $b) {
|
||||
$this->verify2($a, $b);
|
||||
$res = $a->mul($b);
|
||||
return $this->imod($res);
|
||||
}
|
||||
|
||||
public function sqr(BN $a) {
|
||||
$res = $a->_clone();
|
||||
return $this->imul($res, $a);
|
||||
}
|
||||
|
||||
public function isqr(BN &$a) {
|
||||
return $this->imul($a, $a);
|
||||
}
|
||||
|
||||
public function sqrt(BN $a) {
|
||||
if ($a->isZero())
|
||||
return $a->_clone();
|
||||
|
||||
$mod3 = $this->m->andln(3);
|
||||
assert($mod3 % 2 == 1);
|
||||
|
||||
// Fast case
|
||||
if ($mod3 == 3) {
|
||||
$pow = $this->m->add(new BN(1))->iushrn(2);
|
||||
return $this->pow($a, $pow);
|
||||
}
|
||||
|
||||
// Tonelli-Shanks algorithm (Totally unoptimized and slow)
|
||||
//
|
||||
// Find Q and S, that Q * 2 ^ S = (P - 1)
|
||||
$q = $this->m->subn(1);
|
||||
$s = 0;
|
||||
while (!$q->isZero() && $q->andln(1) == 0) {
|
||||
$s++;
|
||||
$q->iushrn(1);
|
||||
}
|
||||
if (Red::$ASSERT_ENABLED) assert(!$q->isZero());
|
||||
|
||||
$one = (new BN(1))->toRed($this);
|
||||
$nOne = $one->redNeg();
|
||||
|
||||
// Find quadratic non-residue
|
||||
// NOTE: Max is such because of generalized Riemann hypothesis.
|
||||
$lpow = $this->m->subn(1)->iushrn(1);
|
||||
$z = $this->m->bitLength();
|
||||
$z = (new BN(2 * $z * $z))->toRed($this);
|
||||
|
||||
while ($this->pow($z, $lpow)->cmp($nOne) != 0) {
|
||||
$z->redIAdd($nOne);
|
||||
}
|
||||
|
||||
$c = $this->pow($z, $q);
|
||||
$r = $this->pow($a, $q->addn(1)->iushrn(1));
|
||||
$t = $this->pow($a, $q);
|
||||
$m = $s;
|
||||
while ($t->cmp($one) != 0) {
|
||||
$tmp = $t;
|
||||
for ($i = 0; $tmp->cmp($one) != 0; $i++) {
|
||||
$tmp = $tmp->redSqr();
|
||||
}
|
||||
if ($i >= $m) {
|
||||
throw new \Exception("Assertion failed");
|
||||
}
|
||||
if ($m - $i - 1 > 54) {
|
||||
$b = $this->pow($c, (new BN(1))->iushln($m - $i - 1));
|
||||
} else {
|
||||
$b = clone($c);
|
||||
$b->bi = $c->bi->powMod(1 << ($m - $i - 1), $this->m->bi);
|
||||
}
|
||||
|
||||
$r = $r->redMul($b);
|
||||
$c = $b->redSqr();
|
||||
$t = $t->redMul($c);
|
||||
$m = $i;
|
||||
}
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
public function invm(BN &$a) {
|
||||
$res = $a->invm($this->m);
|
||||
return $this->imod($res);
|
||||
}
|
||||
|
||||
public function pow(BN $a, BN $num) {
|
||||
$r = clone($a);
|
||||
$r->bi = $a->bi->powMod($num->bi, $this->m->bi);
|
||||
return $r;
|
||||
}
|
||||
|
||||
public function convertTo(BN $num) {
|
||||
$r = $num->umod($this->m);
|
||||
return $r === $num ? $r->_clone() : $r;
|
||||
}
|
||||
|
||||
public function convertFrom(BN $num) {
|
||||
$res = $num->_clone();
|
||||
$res->red = null;
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
Red::$ASSERT_ENABLED = ini_get("zend.assertions") === "1";
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,8 @@
|
||||
.vscode
|
||||
/vendor/
|
||||
node_modules
|
||||
composer.lock
|
||||
.project
|
||||
.settings
|
||||
.buildpath
|
||||
/.idea
|
||||
+23
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -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); }
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -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
|
||||