first commit
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
/doc export-ignore
|
||||
/examples export-ignore
|
||||
/tests export-ignore
|
||||
@@ -0,0 +1,82 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
strategy:
|
||||
matrix:
|
||||
operating-system: ['ubuntu-20.04', 'ubuntu-18.04']
|
||||
php-versions: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0']
|
||||
phpunit-versions: ['latest']
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Checkout submodules
|
||||
uses: textbook/git-checkout-submodule-action@master
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
extensions: gmp
|
||||
coverage: none
|
||||
|
||||
- name: Get composer cache directory
|
||||
id: composer-cache
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: ${{ runner.os }}-composer-
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --prefer-dist
|
||||
|
||||
- name: Run tests
|
||||
run: make phpunit-full-ci
|
||||
|
||||
coverage:
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
strategy:
|
||||
matrix:
|
||||
operating-system: ['ubuntu-20.04']
|
||||
php-versions: ['7.4']
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Checkout submodules
|
||||
uses: textbook/git-checkout-submodule-action@master
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
extensions: gmp, xdebug
|
||||
|
||||
- name: Get composer cache directory
|
||||
id: composer-cache
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: ${{ runner.os }}-composer-
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --prefer-dist
|
||||
|
||||
- name: Run tests
|
||||
run: make phpunit-ci
|
||||
|
||||
- name: Upload coverage to Scrutinizer
|
||||
run: make scrutinizer
|
||||
@@ -0,0 +1,4 @@
|
||||
tests/output
|
||||
vendor/
|
||||
|
||||
composer.lock
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "tests/import/wycheproof"]
|
||||
path = tests/import/wycheproof
|
||||
url = https://github.com/google/wycheproof
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
build:
|
||||
nodes:
|
||||
php-scrutinizer-run:
|
||||
project_setup:
|
||||
override: true
|
||||
tests:
|
||||
override: [php-scrutinizer-run]
|
||||
|
||||
environment:
|
||||
mysql: false
|
||||
redis: false
|
||||
postgresql: false
|
||||
php:
|
||||
version: 7.2
|
||||
ini:
|
||||
'zend.assertions': '1'
|
||||
'assert.exception': '1'
|
||||
dependencies:
|
||||
override:
|
||||
-
|
||||
command: 'composer install'
|
||||
tests:
|
||||
override: true
|
||||
project_setup:
|
||||
override: true
|
||||
|
||||
filter:
|
||||
paths:
|
||||
- src/*
|
||||
excluded_paths:
|
||||
- examples/*
|
||||
- tests/*
|
||||
- vendor/*
|
||||
|
||||
tools:
|
||||
php_cpd: true
|
||||
php_pdepend: true
|
||||
php_analyzer: true
|
||||
php_sim: true
|
||||
php_changetracking: true
|
||||
php_mess_detector: true
|
||||
php_code_sniffer: true
|
||||
sensiolabs_security_checker: true
|
||||
php_code_coverage: true
|
||||
php_pdepend:
|
||||
excluded_dirs: [vendor, doc, examples, build, tests]
|
||||
external_code_coverage:
|
||||
timeout: 1300
|
||||
runs: 1
|
||||
|
||||
changetracking:
|
||||
bug_patterns: ["\bfix(?:es|ed)?\b"]
|
||||
feature_patterns: ["\badd(?:s|ed)?\b", "\bimplement(?:s|ed)?\b"]
|
||||
|
||||
build_failure_conditions:
|
||||
# No new critical issue is introduced (existing ones are tolerated)
|
||||
- 'issues.severity(>= CRITICAL).new.exists'
|
||||
|
||||
# Code Coverage drops below 60%
|
||||
- 'project.metric("scrutinizer.test_coverage", < 0.80)'
|
||||
|
||||
# Code Coverage decreased from previous inspection by more than 1%
|
||||
- 'project.metric_change("scrutinizer.test_coverage", < -0.01)'
|
||||
|
||||
checks:
|
||||
php:
|
||||
simplify_boolean_return: true
|
||||
return_doc_comments: true
|
||||
return_doc_comment_if_not_inferrable: true
|
||||
remove_extra_empty_lines: true
|
||||
properties_in_camelcaps: true
|
||||
phpunit_assertions: true
|
||||
parameters_in_camelcaps: true
|
||||
parameter_doc_comments: true
|
||||
param_doc_comment_if_not_inferrable: true
|
||||
overriding_parameter: true
|
||||
no_short_variable_names:
|
||||
minimum: '3'
|
||||
no_short_method_names:
|
||||
minimum: '3'
|
||||
no_long_variable_names:
|
||||
maximum: '20'
|
||||
no_goto: true
|
||||
fix_use_statements:
|
||||
remove_unused: true
|
||||
preserve_multiple: false
|
||||
preserve_blanklines: false
|
||||
order_alphabetically: false
|
||||
fix_line_ending: true
|
||||
check_method_contracts:
|
||||
verify_interface_like_constraints: true
|
||||
verify_documented_constraints: true
|
||||
verify_parent_constraints: true
|
||||
|
||||
Vendored
+55
@@ -0,0 +1,55 @@
|
||||
test: phpunit phpcs
|
||||
|
||||
.PHONY: test phpunit phpcs
|
||||
|
||||
pretest:
|
||||
if [ ! -d vendor ] || [ ! -f composer.lock ]; then composer install; else echo "Already have dependencies"; fi
|
||||
|
||||
phpunit: pretest
|
||||
mkdir -p tests/output
|
||||
vendor/bin/phpunit --coverage-text --coverage-clover=tests/output/coverage.clover --coverage-html=tests/output/Results
|
||||
|
||||
test-examples:
|
||||
./validate_examples.sh
|
||||
|
||||
phpunit-ci: pretest
|
||||
vendor/bin/phpunit --coverage-text --coverage-clover=tests/output/coverage.clover
|
||||
|
||||
phpunit-full-ci: pretest
|
||||
vendor/bin/phpunit -c phpunit.full.xml --coverage-text --coverage-clover=tests/output/coverage.clover
|
||||
|
||||
ifndef STRICT
|
||||
STRICT = 0
|
||||
endif
|
||||
|
||||
ifeq "$(STRICT)" "1"
|
||||
phpcs: pretest
|
||||
vendor/bin/phpcs --standard=PSR2 src tests/unit/
|
||||
else
|
||||
phpcs: pretest
|
||||
vendor/bin/phpcs --standard=PSR2 -n src tests/unit/
|
||||
endif
|
||||
|
||||
phpcbf: pretest
|
||||
vendor/bin/phpcbf --standard=PSR2 -n src tests/unit/
|
||||
|
||||
ocular:
|
||||
wget https://scrutinizer-ci.com/ocular.phar
|
||||
|
||||
ifdef OCULAR_TOKEN
|
||||
scrutinizer: ocular
|
||||
@php ocular.phar code-coverage:upload --format=php-clover tests/output/coverage.clover --access-token=$(OCULAR_TOKEN);
|
||||
else
|
||||
scrutinizer: ocular
|
||||
php ocular.phar code-coverage:upload --format=php-clover tests/output/coverage.clover;
|
||||
endif
|
||||
|
||||
clean: clean-env clean-deps
|
||||
|
||||
clean-env:
|
||||
rm -rf coverage.clover
|
||||
rm -rf ocular.phar
|
||||
rm -rf tests/output/
|
||||
|
||||
clean-deps:
|
||||
rm -rf vendor/
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
## Pure PHP Elliptic Curve DSA and DH
|
||||
|
||||
[](https://travis-ci.org/phpecc/phpecc)
|
||||
|
||||
[](https://scrutinizer-ci.com/g/phpecc/phpecc?branch=master)
|
||||
[](https://scrutinizer-ci.com/g/phpecc/phpecc/?branch=master)
|
||||
|
||||
[](https://packagist.org/packages/mdanter/ecc)
|
||||
[](https://packagist.org/packages/mdanter/ecc)
|
||||
[](https://packagist.org/packages/mdanter/ecc)
|
||||
[](https://packagist.org/packages/mdanter/ecc)
|
||||
|
||||
### Information
|
||||
|
||||
This library is a rewrite/update of Matyas Danter's ECC library. All credit goes to him.
|
||||
|
||||
For more information on Elliptic Curve Cryptography please read [this fine article](http://www.matyasdanter.com/2010/12/elliptic-curve-php-oop-dsa-and-diffie-hellman/).
|
||||
|
||||
The library supports the following curves:
|
||||
|
||||
- secp112r1
|
||||
- secp256k1
|
||||
- nistp192
|
||||
- nistp224
|
||||
- nistp256 / secp256r1
|
||||
- nistp384 / secp384r1
|
||||
- nistp521
|
||||
|
||||
During ECDSA, a random value `k` is required. It is acceptable to use a true RNG to generate this value, but
|
||||
should the same `k` value ever be repeatedly used for a key, an attacker can recover that signing key.
|
||||
The HMAC random generator can derive a deterministic k value from the message hash and private key, voiding
|
||||
this concern.
|
||||
|
||||
The library uses a non-branching Montgomery ladder for scalar multiplication, as it's constant time and avoids secret
|
||||
dependant branches.
|
||||
|
||||
### License
|
||||
|
||||
This package is released under the MIT license.
|
||||
|
||||
### Requirements
|
||||
|
||||
* PHP 7.0+ or PHP 8.0+
|
||||
* composer
|
||||
* ext-gmp
|
||||
|
||||
Support for older PHP versions:
|
||||
* v0.4.x: php ^5.6|<7.2
|
||||
* v0.5.x: php ^7.0
|
||||
* v1.0.x: php ^7.0|^8.0
|
||||
|
||||
### Installation
|
||||
|
||||
You can install this library via Composer :
|
||||
|
||||
`composer require mdanter/ecc:^1.0`
|
||||
|
||||
### Contribute
|
||||
|
||||
When sending in pull requests, please make sure to run the `make` command.
|
||||
|
||||
The default target runs all PHPUnit and PHPCS tests. All tests
|
||||
must validate for your contribution to be accepted.
|
||||
|
||||
It's also always a good idea to check the results of the [Scrutinizer analysis](https://scrutinizer-ci.com/g/phpecc/phpecc/) for your pull requests.
|
||||
|
||||
### Usage
|
||||
|
||||
Examples:
|
||||
* [Key generation](./examples/key_generation.php)
|
||||
* [ECDH exchange](./examples/ecdh_exchange.php)
|
||||
* [Signature creation](./examples/creating_signature.php)
|
||||
* [Signature verification](./examples/verify_signature.php)
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "mdanter/ecc",
|
||||
"description": "PHP Elliptic Curve Cryptography library",
|
||||
"type": "library",
|
||||
"homepage": "https://github.com/phpecc/phpecc",
|
||||
"keywords": ["secp256k1", "secp256r1", "nistp192", "nistp224", "nistp256", "nistp384", "nistp521", "ECDSA", "diffie", "hellman", "ECDH", "elliptic", "curve", "phpecc"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Matyas Danter",
|
||||
"homepage": "http://matejdanter.com/",
|
||||
"role": "Author"
|
||||
},
|
||||
{
|
||||
"name": "Thibaud Fabre",
|
||||
"email": "thibaud@aztech.io",
|
||||
"homepage": "http://aztech.io",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Thomas Kerin",
|
||||
"email": "afk11@users.noreply.github.com",
|
||||
"role": "Maintainer"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.0||^8.0",
|
||||
"ext-gmp": "*",
|
||||
"fgrosse/phpasn1": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.0||^8.0||^9.0",
|
||||
"squizlabs/php_codesniffer": "^2.0",
|
||||
"symfony/yaml": "^2.6|^3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Mdanter\\Ecc\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Mdanter\\Ecc\\Tests\\": "tests/unit",
|
||||
"Mdanter\\Ecc\\WycheProof\\": "tests/wycheproof"
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit bootstrap="tests/bootstrap.php" colors="true"
|
||||
convertErrorsToExceptions="true" convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true" processIsolation="false"
|
||||
stopOnFailure="false">
|
||||
<php>
|
||||
<const name="PHPUNIT_DEBUG" value="false" />
|
||||
<ini name="display_errors" value="On" />
|
||||
<ini name="error_reporting" value="32767" />
|
||||
</php>
|
||||
<testsuites>
|
||||
<testsuite name="WycheProof tests">
|
||||
<directory suffix="Test.php">./tests/wycheproof</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Unit tests">
|
||||
<directory suffix="Test.php">./tests/unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<logging>
|
||||
<log type="coverage-html" target="./tests/output/Coverage/"
|
||||
charset="UTF-8" yui="true" highlight="true" />
|
||||
<log type="junit" target="./tests/output/Results/Results.xml"
|
||||
logIncompleteSkipped="true" />
|
||||
</logging>
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit bootstrap="tests/bootstrap.php" colors="true"
|
||||
convertErrorsToExceptions="true" convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true" processIsolation="false"
|
||||
stopOnFailure="false">
|
||||
<php>
|
||||
<const name="PHPUNIT_DEBUG" value="false" />
|
||||
<ini name="display_errors" value="On" />
|
||||
<ini name="error_reporting" value="32767" />
|
||||
</php>
|
||||
<testsuites>
|
||||
<testsuite name="All">
|
||||
<directory suffix="Test.php">./tests/unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<logging>
|
||||
<log type="coverage-html" target="./tests/output/Coverage/"
|
||||
charset="UTF-8" yui="true" highlight="true" />
|
||||
<log type="junit" target="./tests/output/Results/Results.xml"
|
||||
logIncompleteSkipped="true" />
|
||||
</logging>
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Crypto\EcDH;
|
||||
|
||||
/**
|
||||
* *********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
|
||||
use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface;
|
||||
use Mdanter\Ecc\Crypto\Key\PublicKeyInterface;
|
||||
use Mdanter\Ecc\Exception\ExchangeException;
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
|
||||
/**
|
||||
* This class is the implementation of ECDH.
|
||||
* EcDH is safe key exchange and achieves
|
||||
* that a key is transported securely between two parties.
|
||||
* The key then can be hashed and used as a basis in
|
||||
* a dual encryption scheme, along with AES for faster
|
||||
* two- way encryption.
|
||||
*/
|
||||
class EcDH implements EcDHInterface
|
||||
{
|
||||
/**
|
||||
* Adapter used for math calculations
|
||||
*
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* Secret key between the two parties
|
||||
*
|
||||
* @var PublicKeyInterface
|
||||
*/
|
||||
private $secretKey = null;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var PublicKeyInterface
|
||||
*/
|
||||
private $recipientKey;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var PrivateKeyInterface
|
||||
*/
|
||||
private $senderKey;
|
||||
|
||||
/**
|
||||
* Initialize a new exchange from a generator point.
|
||||
*
|
||||
* @param GmpMathInterface $adapter A math adapter instance.
|
||||
*/
|
||||
public function __construct(GmpMathInterface $adapter)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Crypto\EcDH\EcDHInterface::calculateSharedKey()
|
||||
*/
|
||||
public function calculateSharedKey(): \GMP
|
||||
{
|
||||
$this->calculateKey();
|
||||
|
||||
return $this->secretKey->getPoint()->getX();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Crypto\EcDH\EcDHInterface::createMultiPartyKey()
|
||||
*/
|
||||
public function createMultiPartyKey(): PublicKeyInterface
|
||||
{
|
||||
$this->calculateKey();
|
||||
|
||||
return $this->secretKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Crypto\EcDH\EcDHInterface::setRecipientKey()
|
||||
*/
|
||||
public function setRecipientKey(PublicKeyInterface $key = null)
|
||||
{
|
||||
$this->recipientKey = $key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Crypto\EcDH\EcDHInterface::setSenderKey()
|
||||
*/
|
||||
public function setSenderKey(PrivateKeyInterface $key)
|
||||
{
|
||||
$this->senderKey = $key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function calculateKey()
|
||||
{
|
||||
$this->checkExchangeState();
|
||||
|
||||
if ($this->secretKey === null) {
|
||||
try {
|
||||
// Multiply our secret with recipients public key
|
||||
$point = $this->recipientKey->getPoint()->mul($this->senderKey->getSecret());
|
||||
|
||||
// Ensure we completed a valid exchange, ensure we can create a
|
||||
// public key instance for the shared secret using our generator.
|
||||
$this->secretKey = $this->senderKey->getPoint()->getPublicKeyFrom($point->getX(), $point->getY());
|
||||
} catch (\Exception $e) {
|
||||
throw new ExchangeException("Invalid ECDH exchange", 0, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the shared secret is known, or that the required keys are available
|
||||
* to calculate the shared secret.
|
||||
* @throws \RuntimeException when the exchange has not been made.
|
||||
*/
|
||||
private function checkExchangeState()
|
||||
{
|
||||
if ($this->secretKey !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->senderKey === null) {
|
||||
throw new ExchangeException('Sender key not set.');
|
||||
}
|
||||
|
||||
if ($this->recipientKey === null) {
|
||||
throw new ExchangeException('Recipient key not set.');
|
||||
}
|
||||
|
||||
// Check the point exists on our curve.
|
||||
$point = $this->recipientKey->getPoint();
|
||||
if (!$this->senderKey->getPoint()->getCurve()->contains($point->getX(), $point->getY())) {
|
||||
throw new ExchangeException("Invalid ECDH exchange - Point does not exist on our curve");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Crypto\EcDH;
|
||||
|
||||
/**
|
||||
* *********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
use Mdanter\Ecc\Crypto\Key\PublicKeyInterface;
|
||||
use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface;
|
||||
|
||||
/**
|
||||
* This is the contract for implementing EcDH (EC Diffie Hellman).
|
||||
*/
|
||||
interface EcDHInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Calculates and returns the shared key for the exchange.
|
||||
*
|
||||
* @return \GMP
|
||||
*/
|
||||
public function calculateSharedKey(): \GMP;
|
||||
|
||||
/**
|
||||
* @return PublicKeyInterface
|
||||
*/
|
||||
public function createMultiPartyKey(): PublicKeyInterface;
|
||||
|
||||
/**
|
||||
* Sets the sender's key.
|
||||
*
|
||||
* @param PrivateKeyInterface $key
|
||||
*/
|
||||
public function setSenderKey(PrivateKeyInterface $key);
|
||||
|
||||
/**
|
||||
* Sets the recipient key.
|
||||
*
|
||||
* @param PublicKeyInterface $key
|
||||
* @return void
|
||||
*/
|
||||
public function setRecipientKey(PublicKeyInterface $key);
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Crypto\Key;
|
||||
|
||||
/**
|
||||
* *********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
|
||||
use Mdanter\Ecc\Crypto\EcDH\EcDH;
|
||||
use Mdanter\Ecc\Crypto\EcDH\EcDHInterface;
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Primitives\CurveFpInterface;
|
||||
use Mdanter\Ecc\Primitives\GeneratorPoint;
|
||||
use Mdanter\Ecc\Primitives\PointInterface;
|
||||
|
||||
/**
|
||||
* This class serves as public - private key exchange for signature verification.
|
||||
*/
|
||||
class PrivateKey implements PrivateKeyInterface
|
||||
{
|
||||
/**
|
||||
* @var GeneratorPoint
|
||||
*/
|
||||
private $generator;
|
||||
|
||||
/**
|
||||
* @var \GMP
|
||||
*/
|
||||
private $secretMultiplier;
|
||||
|
||||
/**
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface $adapter
|
||||
* @param GeneratorPoint $generator
|
||||
* @param \GMP $secretMultiplier
|
||||
*/
|
||||
public function __construct(GmpMathInterface $adapter, GeneratorPoint $generator, \GMP $secretMultiplier)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
$this->generator = $generator;
|
||||
$this->secretMultiplier = $secretMultiplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Crypto\Key\PrivateKeyInterface::getPublicKey()
|
||||
*/
|
||||
public function getPublicKey(): PublicKeyInterface
|
||||
{
|
||||
return new PublicKey($this->adapter, $this->generator, $this->generator->mul($this->secretMultiplier));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Crypto\Key\PrivateKeyInterface::getPoint()
|
||||
*/
|
||||
public function getPoint(): GeneratorPoint
|
||||
{
|
||||
return $this->generator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Crypto\Key\PrivateKeyInterface::getCurve()
|
||||
*/
|
||||
public function getCurve(): CurveFpInterface
|
||||
{
|
||||
return $this->generator->getCurve();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Crypto\Key\PrivateKeyInterface::getSecret()
|
||||
*/
|
||||
public function getSecret(): \GMP
|
||||
{
|
||||
return $this->secretMultiplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Crypto\Key\PrivateKeyInterface::createExchange()
|
||||
*/
|
||||
public function createExchange(PublicKeyInterface $recipient = null): EcDHInterface
|
||||
{
|
||||
$ecdh = new EcDH($this->adapter);
|
||||
$ecdh
|
||||
->setSenderKey($this)
|
||||
->setRecipientKey($recipient);
|
||||
|
||||
return $ecdh;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Crypto\Key;
|
||||
|
||||
/**
|
||||
* *********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
|
||||
use Mdanter\Ecc\Crypto\EcDH\EcDHInterface;
|
||||
use Mdanter\Ecc\Primitives\GeneratorPoint;
|
||||
|
||||
/**
|
||||
* This is a contract for the PrivateKey portion of ECDSA.
|
||||
*/
|
||||
interface PrivateKeyInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return PublicKeyInterface
|
||||
*/
|
||||
public function getPublicKey(): PublicKeyInterface;
|
||||
|
||||
/**
|
||||
* @return GeneratorPoint
|
||||
*/
|
||||
public function getPoint(): GeneratorPoint;
|
||||
|
||||
/**
|
||||
* @return \GMP
|
||||
*/
|
||||
public function getSecret(): \GMP;
|
||||
|
||||
/**
|
||||
* @param PublicKeyInterface $recipient
|
||||
* @return EcDHInterface
|
||||
*/
|
||||
public function createExchange(PublicKeyInterface $recipient): EcDHInterface;
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Crypto\Key;
|
||||
|
||||
/**
|
||||
* *********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
|
||||
use Mdanter\Ecc\Exception\PublicKeyException;
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Primitives\CurveFpInterface;
|
||||
use Mdanter\Ecc\Primitives\GeneratorPoint;
|
||||
use Mdanter\Ecc\Primitives\PointInterface;
|
||||
|
||||
/**
|
||||
* This class serves as public- private key exchange for signature verification
|
||||
*/
|
||||
class PublicKey implements PublicKeyInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var CurveFpInterface
|
||||
*/
|
||||
private $curve;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var GeneratorPoint
|
||||
*/
|
||||
private $generator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var PointInterface
|
||||
*/
|
||||
private $point;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* Initialize a new PublicKey instance.
|
||||
*
|
||||
* @param GmpMathInterface $adapter
|
||||
* @param GeneratorPoint $generator
|
||||
* @param PointInterface $point
|
||||
*/
|
||||
public function __construct(GmpMathInterface $adapter, GeneratorPoint $generator, PointInterface $point)
|
||||
{
|
||||
$this->curve = $generator->getCurve();
|
||||
$this->generator = $generator;
|
||||
$this->point = $point;
|
||||
$this->adapter = $adapter;
|
||||
|
||||
// step 1: not point at infinity.
|
||||
if ($point->isInfinity()) {
|
||||
throw new PublicKeyException($generator, $point, "Cannot use point at infinity for public key");
|
||||
}
|
||||
|
||||
// step 2 full & partial public key validation routine
|
||||
if ($adapter->cmp($point->getX(), gmp_init(0, 10)) < 0 || $adapter->cmp($this->curve->getPrime(), $point->getX()) < 0
|
||||
|| $adapter->cmp($point->getY(), gmp_init(0, 10)) < 0 || $adapter->cmp($this->curve->getPrime(), $point->getY()) < 0
|
||||
) {
|
||||
throw new PublicKeyException($generator, $point, "Point has x and y out of range.");
|
||||
}
|
||||
|
||||
// Sanity check. Point (x,y) values are qualified against it's
|
||||
// generator and curve. Here we ensure the Point and Generator
|
||||
// are the same.
|
||||
if (!$generator->getCurve()->equals($point->getCurve())) {
|
||||
throw new PublicKeyException($generator, $point, "Curve for given point not in common with GeneratorPoint");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Crypto\Key\PublicKeyInterface::getCurve()
|
||||
*/
|
||||
public function getCurve(): CurveFpInterface
|
||||
{
|
||||
return $this->curve;
|
||||
}
|
||||
|
||||
/**
|
||||
* {$inheritDoc}
|
||||
* @see \Mdanter\Ecc\Crypto\Key\PublicKeyInterface::getGenerator()
|
||||
*/
|
||||
public function getGenerator(): GeneratorPoint
|
||||
{
|
||||
return $this->generator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Crypto\Key\PublicKeyInterface::getPoint()
|
||||
*/
|
||||
public function getPoint(): PointInterface
|
||||
{
|
||||
return $this->point;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Crypto\Key;
|
||||
|
||||
/**
|
||||
* *********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
use Mdanter\Ecc\Primitives\CurveFpInterface;
|
||||
use Mdanter\Ecc\Primitives\PointInterface;
|
||||
use Mdanter\Ecc\Primitives\GeneratorPoint;
|
||||
|
||||
/**
|
||||
* This is the contract for the PublicKey portion of ECDSA.
|
||||
*/
|
||||
interface PublicKeyInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @return CurveFpInterface
|
||||
*/
|
||||
public function getCurve(): CurveFpInterface;
|
||||
|
||||
/**
|
||||
* @return PointInterface
|
||||
*/
|
||||
public function getPoint(): PointInterface;
|
||||
|
||||
/**
|
||||
* @return GeneratorPoint
|
||||
*/
|
||||
public function getGenerator(): GeneratorPoint;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Mdanter\Ecc\Crypto\Signature;
|
||||
|
||||
use Mdanter\Ecc\Primitives\GeneratorPoint;
|
||||
|
||||
interface HasherInterface
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAlgorithm(): string;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLengthInBytes(): int;
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @return string
|
||||
*/
|
||||
public function makeRawHash(string $data): string;
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param GeneratorPoint $G
|
||||
* @return \GMP
|
||||
*/
|
||||
public function makeHash(string $data, GeneratorPoint $G): \GMP;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Mdanter\Ecc\Crypto\Signature;
|
||||
|
||||
use Mdanter\Ecc\EccFactory;
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Primitives\GeneratorPoint;
|
||||
use Mdanter\Ecc\Util\BinaryString;
|
||||
use Mdanter\Ecc\Util\NumberSize;
|
||||
|
||||
class SignHasher implements HasherInterface
|
||||
{
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
protected static $sizeMap = [
|
||||
'sha1' => 20,
|
||||
'sha224' => 28,
|
||||
'sha256' => 32,
|
||||
'sha384' => 48,
|
||||
'sha512' => 64,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $algorithm;
|
||||
|
||||
/**
|
||||
* SignHasher constructor.
|
||||
* @param string $algorithm
|
||||
* @param GmpMathInterface|null $math
|
||||
*/
|
||||
public function __construct(string $algorithm, GmpMathInterface $math = null)
|
||||
{
|
||||
if (!array_key_exists($algorithm, self::$sizeMap)) {
|
||||
throw new \InvalidArgumentException("Unsupported hashing algorithm");
|
||||
}
|
||||
|
||||
$this->algorithm = $algorithm;
|
||||
$this->adapter = $math ?: EccFactory::getAdapter();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAlgorithm(): string
|
||||
{
|
||||
return $this->algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLengthInBytes(): int
|
||||
{
|
||||
return self::$sizeMap[$this->algorithm];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @return string
|
||||
*/
|
||||
public function makeRawHash(string $data): string
|
||||
{
|
||||
return hash($this->algorithm, $data, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $hash
|
||||
* @param GeneratorPoint $G
|
||||
* @return \GMP
|
||||
*/
|
||||
public function truncateForECDSA(\GMP $hash, GeneratorPoint $G)
|
||||
{
|
||||
$hashBits = gmp_strval($hash, 2);
|
||||
if (BinaryString::length($hashBits) < self::$sizeMap[$this->algorithm] * 8) {
|
||||
$hashBits = str_pad($hashBits, self::$sizeMap[$this->algorithm] * 8, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
return gmp_init(BinaryString::substring($hashBits, 0, NumberSize::bnNumBits($this->adapter, $G->getOrder())), 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param GeneratorPoint $G
|
||||
* @return \GMP
|
||||
*/
|
||||
public function makeHash(string $data, GeneratorPoint $G): \GMP
|
||||
{
|
||||
$hash = gmp_init($this->makeRawHash($data), 16);
|
||||
return $this->truncateForECDSA($hash, $G);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Crypto\Signature;
|
||||
|
||||
/**
|
||||
* *********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* Plain Old PHP Object that stores the signature r,s for ECDSA
|
||||
*/
|
||||
class Signature implements SignatureInterface
|
||||
{
|
||||
/**
|
||||
* @var \GMP
|
||||
*/
|
||||
private $r;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var \GMP
|
||||
*/
|
||||
private $s;
|
||||
|
||||
/**
|
||||
* Initialize a new instance with values
|
||||
*
|
||||
* @param \GMP $r
|
||||
* @param \GMP $s
|
||||
*/
|
||||
public function __construct(\GMP $r, \GMP $s)
|
||||
{
|
||||
$this->r = $r;
|
||||
$this->s = $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Crypto\Signature\SignatureInterface::getR()
|
||||
*/
|
||||
public function getR(): \GMP
|
||||
{
|
||||
return $this->r;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Crypto\Signature\SignatureInterface::getS()
|
||||
*/
|
||||
public function getS(): \GMP
|
||||
{
|
||||
return $this->s;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Crypto\Signature;
|
||||
|
||||
/**
|
||||
* *********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is the contract for describing a signature used in ECDSA.
|
||||
*/
|
||||
interface SignatureInterface
|
||||
{
|
||||
/**
|
||||
* Returns the r parameter of the signature.
|
||||
*
|
||||
* @return \GMP
|
||||
*/
|
||||
public function getR(): \GMP;
|
||||
|
||||
/**
|
||||
* Returns the s parameter of the signature.
|
||||
*
|
||||
* @return \GMP
|
||||
*/
|
||||
public function getS(): \GMP;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Crypto\Signature;
|
||||
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface;
|
||||
use Mdanter\Ecc\Crypto\Key\PublicKeyInterface;
|
||||
use Mdanter\Ecc\Util\BinaryString;
|
||||
|
||||
class Signer
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param GmpMathInterface $adapter
|
||||
*/
|
||||
public function __construct(GmpMathInterface $adapter)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PrivateKeyInterface $key
|
||||
* @param \GMP $truncatedHash - hash truncated for use in ECDSA
|
||||
* @param \GMP $randomK
|
||||
* @return SignatureInterface
|
||||
*/
|
||||
public function sign(PrivateKeyInterface $key, \GMP $truncatedHash, \GMP $randomK): SignatureInterface
|
||||
{
|
||||
$math = $this->adapter;
|
||||
$generator = $key->getPoint();
|
||||
$modMath = $math->getModularArithmetic($generator->getOrder());
|
||||
|
||||
$k = $math->mod($randomK, $generator->getOrder());
|
||||
$p1 = $generator->mul($k);
|
||||
$r = $p1->getX();
|
||||
$zero = gmp_init(0, 10);
|
||||
if ($math->equals($r, $zero)) {
|
||||
throw new \RuntimeException("Error: random number R = 0");
|
||||
}
|
||||
|
||||
$s = $modMath->div($modMath->add($truncatedHash, $math->mul($key->getSecret(), $r)), $k);
|
||||
if ($math->equals($s, $zero)) {
|
||||
throw new \RuntimeException("Error: random number S = 0");
|
||||
}
|
||||
|
||||
return new Signature($r, $s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PublicKeyInterface $key
|
||||
* @param SignatureInterface $signature
|
||||
* @param \GMP $hash
|
||||
* @return bool
|
||||
*/
|
||||
public function verify(PublicKeyInterface $key, SignatureInterface $signature, \GMP $hash): bool
|
||||
{
|
||||
$generator = $key->getGenerator();
|
||||
$n = $generator->getOrder();
|
||||
$r = $signature->getR();
|
||||
$s = $signature->getS();
|
||||
|
||||
$math = $this->adapter;
|
||||
$one = gmp_init(1, 10);
|
||||
if ($math->cmp($r, $one) < 0 || $math->cmp($r, $math->sub($n, $one)) > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($math->cmp($s, $one) < 0 || $math->cmp($s, $math->sub($n, $one)) > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$modMath = $math->getModularArithmetic($n);
|
||||
$c = $math->inverseMod($s, $n);
|
||||
$u1 = $modMath->mul($hash, $c);
|
||||
$u2 = $modMath->mul($r, $c);
|
||||
$xy = $generator->mul($u1)->add($key->getPoint()->mul($u2));
|
||||
$v = $math->mod($xy->getX(), $n);
|
||||
|
||||
return BinaryString::constantTimeCompare($math->toString($v), $math->toString($r));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Curves;
|
||||
|
||||
use Mdanter\Ecc\Exception\UnknownCurveException;
|
||||
use Mdanter\Ecc\Exception\UnsupportedCurveException;
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Math\MathAdapterFactory;
|
||||
use Mdanter\Ecc\Primitives\GeneratorPoint;
|
||||
|
||||
class CurveFactory
|
||||
{
|
||||
/**
|
||||
* @param string $name
|
||||
* @return NamedCurveFp
|
||||
*/
|
||||
public static function getCurveByName(string $name): NamedCurveFp
|
||||
{
|
||||
$adapter = MathAdapterFactory::getAdapter();
|
||||
$nistFactory = self::getNistFactory($adapter);
|
||||
$secpFactory = self::getSecpFactory($adapter);
|
||||
|
||||
switch ($name) {
|
||||
case NistCurve::NAME_P192:
|
||||
return $nistFactory->curve192();
|
||||
case NistCurve::NAME_P224:
|
||||
return $nistFactory->curve224();
|
||||
case NistCurve::NAME_P256:
|
||||
return $nistFactory->curve256();
|
||||
case NistCurve::NAME_P384:
|
||||
return $nistFactory->curve384();
|
||||
case NistCurve::NAME_P521:
|
||||
return $nistFactory->curve521();
|
||||
case SecgCurve::NAME_SECP_112R1:
|
||||
return $secpFactory->curve112r1();
|
||||
case SecgCurve::NAME_SECP_192K1:
|
||||
return $secpFactory->curve192k1();
|
||||
case SecgCurve::NAME_SECP_256K1:
|
||||
return $secpFactory->curve256k1();
|
||||
case SecgCurve::NAME_SECP_256R1:
|
||||
return $secpFactory->curve256r1();
|
||||
case SecgCurve::NAME_SECP_384R1:
|
||||
return $secpFactory->curve384r1();
|
||||
default:
|
||||
$error = new UnsupportedCurveException('Unknown curve.');
|
||||
$error->setCurveName($name);
|
||||
throw $error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return GeneratorPoint
|
||||
*/
|
||||
public static function getGeneratorByName(string $name): GeneratorPoint
|
||||
{
|
||||
$adapter = MathAdapterFactory::getAdapter();
|
||||
$nistFactory = self::getNistFactory($adapter);
|
||||
$secpFactory = self::getSecpFactory($adapter);
|
||||
|
||||
switch ($name) {
|
||||
case NistCurve::NAME_P192:
|
||||
return $nistFactory->generator192();
|
||||
case NistCurve::NAME_P224:
|
||||
return $nistFactory->generator224();
|
||||
case NistCurve::NAME_P256:
|
||||
return $nistFactory->generator256();
|
||||
case NistCurve::NAME_P384:
|
||||
return $nistFactory->generator384();
|
||||
case NistCurve::NAME_P521:
|
||||
return $nistFactory->generator521();
|
||||
case SecgCurve::NAME_SECP_112R1:
|
||||
return $secpFactory->generator112r1();
|
||||
case SecgCurve::NAME_SECP_192K1:
|
||||
return $secpFactory->generator192k1();
|
||||
case SecgCurve::NAME_SECP_256K1:
|
||||
return $secpFactory->generator256k1();
|
||||
case SecgCurve::NAME_SECP_256R1:
|
||||
return $secpFactory->generator256r1();
|
||||
case SecgCurve::NAME_SECP_384R1:
|
||||
return $secpFactory->generator384r1();
|
||||
default:
|
||||
$error = new UnsupportedCurveException('Unknown generator.');
|
||||
$error->setCurveName($name);
|
||||
throw $error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface $math
|
||||
* @return NistCurve
|
||||
*/
|
||||
private static function getNistFactory(GmpMathInterface $math): NistCurve
|
||||
{
|
||||
return new NistCurve($math);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface $math
|
||||
* @return SecgCurve
|
||||
*/
|
||||
private static function getSecpFactory(GmpMathInterface $math): SecgCurve
|
||||
{
|
||||
return new SecgCurve($math);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Curves;
|
||||
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Primitives\CurveFp;
|
||||
use Mdanter\Ecc\Primitives\CurveParameters;
|
||||
|
||||
class NamedCurveFp extends CurveFp
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param CurveParameters $parameters
|
||||
* @param GmpMathInterface $adapter
|
||||
*/
|
||||
public function __construct(string $name, CurveParameters $parameters, GmpMathInterface $adapter)
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
parent::__construct($parameters, $adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Curves;
|
||||
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Primitives\CurveParameters;
|
||||
use Mdanter\Ecc\Primitives\GeneratorPoint;
|
||||
use Mdanter\Ecc\Random\RandomNumberGeneratorInterface;
|
||||
|
||||
/**
|
||||
* *********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class encapsulates the NIST recommended curves
|
||||
* - fields are Mersenne primes, i.e.
|
||||
* for some p, Mersenne_prine = 2^p - 1
|
||||
*/
|
||||
class NistCurve
|
||||
{
|
||||
|
||||
const NAME_P192 = 'nistp192';
|
||||
const NAME_P224 = 'nistp224';
|
||||
const NAME_P256 = 'nistp256';
|
||||
const NAME_P384 = 'nistp384';
|
||||
const NAME_P521 = 'nistp521';
|
||||
|
||||
/**
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface $adapter
|
||||
*/
|
||||
public function __construct(GmpMathInterface $adapter)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an NIST P-192 curve.
|
||||
*
|
||||
* @return NamedCurveFp
|
||||
*/
|
||||
public function curve192(): NamedCurveFp
|
||||
{
|
||||
$p = gmp_init('6277101735386680763835789423207666416083908700390324961279', 10);
|
||||
$b = gmp_init('64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1', 16);
|
||||
|
||||
$parameters = new CurveParameters(192, $p, gmp_init('-3', 10), $b);
|
||||
|
||||
return new NamedCurveFp(self::NAME_P192, $parameters, $this->adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an NIST P-192 generator.
|
||||
*
|
||||
* @param RandomNumberGeneratorInterface $randomGenerator
|
||||
* @return GeneratorPoint
|
||||
*/
|
||||
public function generator192(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint
|
||||
{
|
||||
$curve = $this->curve192();
|
||||
$order = gmp_init('6277101735386680763835789423176059013767194773182842284081', 10);
|
||||
|
||||
$x = gmp_init('188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012', 16);
|
||||
$y = gmp_init('07192b95ffc8da78631011ed6b24cdd573f977a11e794811', 16);
|
||||
|
||||
return $curve->getGenerator($x, $y, $order, $randomGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an NIST P-224 curve
|
||||
*
|
||||
* @return NamedCurveFp
|
||||
*/
|
||||
public function curve224(): NamedCurveFp
|
||||
{
|
||||
$p = gmp_init('26959946667150639794667015087019630673557916260026308143510066298881', 10);
|
||||
$b = gmp_init('b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4', 16);
|
||||
|
||||
$parameters = new CurveParameters(224, $p, gmp_init(-3, 10), $b);
|
||||
|
||||
return new NamedCurveFp(self::NAME_P224, $parameters, $this->adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an NIST P-224 generator.
|
||||
*
|
||||
* @param RandomNumberGeneratorInterface $randomGenerator
|
||||
* @return GeneratorPoint
|
||||
*/
|
||||
public function generator224(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint
|
||||
{
|
||||
$curve = $this->curve224();
|
||||
$order = gmp_init('26959946667150639794667015087019625940457807714424391721682722368061', 10);
|
||||
|
||||
$x = gmp_init('b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21', 16);
|
||||
$y = gmp_init('bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34', 16);
|
||||
|
||||
return $curve->getGenerator($x, $y, $order, $randomGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an NIST P-256 curve.
|
||||
*
|
||||
* @return NamedCurveFp
|
||||
*/
|
||||
public function curve256(): NamedCurveFp
|
||||
{
|
||||
$p = gmp_init('115792089210356248762697446949407573530086143415290314195533631308867097853951', 10);
|
||||
$b = gmp_init('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b', 16);
|
||||
|
||||
$parameters = new CurveParameters(256, $p, gmp_init(-3, 10), $b);
|
||||
|
||||
return new NamedCurveFp(self::NAME_P256, $parameters, $this->adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an NIST P-256 generator.
|
||||
*
|
||||
* @param RandomNumberGeneratorInterface $randomGenerator
|
||||
* @return GeneratorPoint
|
||||
*/
|
||||
public function generator256(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint
|
||||
{
|
||||
$curve = $this->curve256();
|
||||
$order = gmp_init('115792089210356248762697446949407573529996955224135760342422259061068512044369', 10);
|
||||
|
||||
$x = gmp_init('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296', 16);
|
||||
$y = gmp_init('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5', 16);
|
||||
|
||||
return $curve->getGenerator($x, $y, $order, $randomGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an NIST P-384 curve.
|
||||
*
|
||||
* @return NamedCurveFp
|
||||
*/
|
||||
public function curve384(): NamedCurveFp
|
||||
{
|
||||
$p = gmp_init('39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319', 10);
|
||||
$b = gmp_init('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef', 16);
|
||||
|
||||
$parameters = new CurveParameters(384, $p, gmp_init(-3, 10), $b);
|
||||
|
||||
return new NamedCurveFp(self::NAME_P384, $parameters, $this->adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an NIST P-384 generator.
|
||||
*
|
||||
* @param RandomNumberGeneratorInterface $randomGenerator
|
||||
* @return GeneratorPoint
|
||||
*/
|
||||
public function generator384(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint
|
||||
{
|
||||
$curve = $this->curve384();
|
||||
$order = gmp_init('39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643', 10);
|
||||
|
||||
$x = gmp_init('0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7', 16);
|
||||
$y = gmp_init('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f', 16);
|
||||
|
||||
return $curve->getGenerator($x, $y, $order, $randomGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an NIST P-521 curve.
|
||||
*
|
||||
* @return NamedCurveFp
|
||||
*/
|
||||
public function curve521(): NamedCurveFp
|
||||
{
|
||||
$p = gmp_init('6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151', 10);
|
||||
$b = gmp_init('0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00', 16);
|
||||
|
||||
$parameters = new CurveParameters(521, $p, gmp_init(-3, 10), $b);
|
||||
|
||||
return new NamedCurveFp(self::NAME_P521, $parameters, $this->adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an NIST P-521 generator.
|
||||
*
|
||||
* @param RandomNumberGeneratorInterface $randomGenerator
|
||||
* @return GeneratorPoint
|
||||
*/
|
||||
public function generator521(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint
|
||||
{
|
||||
$curve = $this->curve521();
|
||||
$order = gmp_init('6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449', 10);
|
||||
|
||||
$x = gmp_init('0xc6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66', 16);
|
||||
$y = gmp_init('0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650', 16);
|
||||
|
||||
return $curve->getGenerator($x, $y, $order, $randomGenerator);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Curves;
|
||||
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Primitives\CurveParameters;
|
||||
use Mdanter\Ecc\Primitives\GeneratorPoint;
|
||||
use Mdanter\Ecc\Random\RandomNumberGeneratorInterface;
|
||||
|
||||
/**
|
||||
* *********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class SecgCurve
|
||||
{
|
||||
/**
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
const NAME_SECP_112R1 = 'secp112r1';
|
||||
const NAME_SECP_192K1 = 'secp192k1';
|
||||
const NAME_SECP_256K1 = 'secp256k1';
|
||||
const NAME_SECP_256R1 = 'secp256r1';
|
||||
const NAME_SECP_384R1 = 'secp384r1';
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface $adapter
|
||||
*/
|
||||
public function __construct(GmpMathInterface $adapter)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NamedCurveFp
|
||||
*/
|
||||
public function curve112r1(): NamedCurveFp
|
||||
{
|
||||
$p = gmp_init('0xDB7C2ABF62E35E668076BEAD208B', 16);
|
||||
$a = gmp_init('0xDB7C2ABF62E35E668076BEAD2088', 16);
|
||||
$b = gmp_init('0x659EF8BA043916EEDE8911702B22', 16);
|
||||
|
||||
$parameters = new CurveParameters(112, $p, $a, $b);
|
||||
|
||||
return new NamedCurveFp(self::NAME_SECP_112R1, $parameters, $this->adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RandomNumberGeneratorInterface $randomGenerator
|
||||
* @return GeneratorPoint
|
||||
*/
|
||||
public function generator112r1(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint
|
||||
{
|
||||
$curve = $this->curve112r1();
|
||||
|
||||
$order = gmp_init('0xDB7C2ABF62E35E7628DFAC6561C5', 16);
|
||||
$x = gmp_init('0x09487239995A5EE76B55F9C2F098', 16);
|
||||
$y = gmp_init('0xA89CE5AF8724C0A23E0E0FF77500', 16);
|
||||
|
||||
return $curve->getGenerator($x, $y, $order, $randomGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NamedCurveFp
|
||||
*/
|
||||
public function curve192k1(): NamedCurveFp
|
||||
{
|
||||
$p = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37', 16);
|
||||
$a = gmp_init(0, 10);
|
||||
$b = gmp_init(3, 10);
|
||||
|
||||
$parameters = new CurveParameters(192, $p, $a, $b);
|
||||
|
||||
return new NamedCurveFp(self::NAME_SECP_192K1, $parameters, $this->adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RandomNumberGeneratorInterface $randomGenerator
|
||||
* @return \Mdanter\Ecc\Primitives\GeneratorPoint
|
||||
*/
|
||||
public function generator192k1(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint
|
||||
{
|
||||
$curve = $this->curve192k1();
|
||||
|
||||
$order = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D', 16);
|
||||
$x = gmp_init('0xDB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D', 16);
|
||||
$y = gmp_init('0x9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D', 16);
|
||||
|
||||
return $curve->getGenerator($x, $y, $order, $randomGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NamedCurveFp
|
||||
*/
|
||||
public function curve256k1(): NamedCurveFp
|
||||
{
|
||||
$p = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 16);
|
||||
$a = gmp_init(0, 10);
|
||||
$b = gmp_init(7, 10);
|
||||
|
||||
$parameters = new CurveParameters(256, $p, $a, $b);
|
||||
|
||||
return new NamedCurveFp(self::NAME_SECP_256K1, $parameters, $this->adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RandomNumberGeneratorInterface $randomGenerator
|
||||
* @return GeneratorPoint
|
||||
*/
|
||||
public function generator256k1(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint
|
||||
{
|
||||
$curve = $this->curve256k1();
|
||||
|
||||
$order = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16);
|
||||
$x = gmp_init('0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798', 16);
|
||||
$y = gmp_init('0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8', 16);
|
||||
|
||||
return $curve->getGenerator($x, $y, $order, $randomGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NamedCurveFp
|
||||
*/
|
||||
public function curve256r1(): NamedCurveFp
|
||||
{
|
||||
$p = gmp_init('0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF', 16);
|
||||
$a = gmp_init('0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC', 16);
|
||||
$b = gmp_init('0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B', 16);
|
||||
|
||||
$parameters = new CurveParameters(256, $p, $a, $b);
|
||||
|
||||
return new NamedCurveFp(self::NAME_SECP_256R1, $parameters, $this->adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RandomNumberGeneratorInterface $randomGenerator
|
||||
* @return GeneratorPoint
|
||||
*/
|
||||
public function generator256r1(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint
|
||||
{
|
||||
$curve = $this->curve256r1();
|
||||
|
||||
$order = gmp_init('0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551', 16);
|
||||
$x = gmp_init('0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296', 16);
|
||||
$y = gmp_init('0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5', 16);
|
||||
|
||||
return $curve->getGenerator($x, $y, $order, $randomGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NamedCurveFp
|
||||
*/
|
||||
public function curve384r1(): NamedCurveFp
|
||||
{
|
||||
$p = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF', 16);
|
||||
$a = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC', 16);
|
||||
$b = gmp_init('0xB3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF', 16);
|
||||
|
||||
$parameters = new CurveParameters(384, $p, $a, $b);
|
||||
|
||||
return new NamedCurveFp(self::NAME_SECP_384R1, $parameters, $this->adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RandomNumberGeneratorInterface $randomGenerator
|
||||
* @return GeneratorPoint
|
||||
*/
|
||||
public function generator384r1(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint
|
||||
{
|
||||
$curve = $this->curve384r1();
|
||||
|
||||
$order = gmp_init('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973', 16);
|
||||
$x = gmp_init('0xAA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7', 16);
|
||||
$y = gmp_init('0x3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F', 16);
|
||||
|
||||
return $curve->getGenerator($x, $y, $order, $randomGenerator);
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc;
|
||||
|
||||
use Mdanter\Ecc\Crypto\Signature\Signer;
|
||||
use Mdanter\Ecc\Curves\NistCurve;
|
||||
use Mdanter\Ecc\Curves\SecgCurve;
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Math\MathAdapterFactory;
|
||||
use Mdanter\Ecc\Primitives\CurveFp;
|
||||
use Mdanter\Ecc\Primitives\CurveFpInterface;
|
||||
use Mdanter\Ecc\Primitives\CurveParameters;
|
||||
|
||||
/**
|
||||
* Static factory class providing factory methods to work with NIST and SECG recommended curves.
|
||||
*/
|
||||
class EccFactory
|
||||
{
|
||||
/**
|
||||
* Selects and creates the most appropriate adapter for the running environment.
|
||||
*
|
||||
* @param bool $debug [optional] Set to true to get a trace of all mathematical operations
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @return GmpMathInterface
|
||||
*/
|
||||
public static function getAdapter(bool $debug = false): GmpMathInterface
|
||||
{
|
||||
return MathAdapterFactory::getAdapter($debug);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a factory to create NIST Recommended curves and generators.
|
||||
*
|
||||
* @param GmpMathInterface $adapter [optional] Defaults to the return value of EccFactory::getAdapter().
|
||||
* @return NistCurve
|
||||
*/
|
||||
public static function getNistCurves(GmpMathInterface $adapter = null): NistCurve
|
||||
{
|
||||
return new NistCurve($adapter ?: self::getAdapter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a factory to return SECG Recommended curves and generators.
|
||||
*
|
||||
* @param GmpMathInterface $adapter [optional] Defaults to the return value of EccFactory::getAdapter().
|
||||
* @return SecgCurve
|
||||
*/
|
||||
public static function getSecgCurves(GmpMathInterface $adapter = null): SecgCurve
|
||||
{
|
||||
return new SecgCurve($adapter ?: self::getAdapter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new curve from arbitrary parameters.
|
||||
*
|
||||
* @param int $bitSize
|
||||
* @param \GMP $prime
|
||||
* @param \GMP $a
|
||||
* @param \GMP $b
|
||||
* @param GmpMathInterface $adapter [optional] Defaults to the return value of EccFactory::getAdapter().
|
||||
* @return CurveFpInterface
|
||||
*/
|
||||
public static function createCurve(int $bitSize, \GMP $prime, \GMP $a, \GMP $b, GmpMathInterface $adapter = null): CurveFpInterface
|
||||
{
|
||||
return new CurveFp(new CurveParameters($bitSize, $prime, $a, $b), $adapter ?: self::getAdapter());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface $adapter [optional] Defaults to the return value of EccFactory::getAdapteR()
|
||||
* @return Signer
|
||||
*/
|
||||
public static function getSigner(GmpMathInterface $adapter = null): Signer
|
||||
{
|
||||
return new Signer($adapter ?: self::getAdapter());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Exception;
|
||||
|
||||
class ExchangeException extends \RuntimeException
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Exception;
|
||||
|
||||
class NumberTheoryException extends \RuntimeException
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Exception;
|
||||
|
||||
class PointException extends \RuntimeException
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Exception;
|
||||
|
||||
use Mdanter\Ecc\Primitives\CurveFpInterface;
|
||||
|
||||
class PointNotOnCurveException extends PointException
|
||||
{
|
||||
public function __construct(\GMP $x, \GMP $y, CurveFpInterface $curve)
|
||||
{
|
||||
parent::__construct("Curve " . $curve . " does not contain point (" . gmp_strval($x, 10) . ", " . gmp_strval($y, 10) . ")");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Exception;
|
||||
|
||||
class PointRecoveryException extends PointException
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Exception;
|
||||
|
||||
use Mdanter\Ecc\Primitives\GeneratorPoint;
|
||||
use Mdanter\Ecc\Primitives\PointInterface;
|
||||
use Throwable;
|
||||
|
||||
class PublicKeyException extends \RuntimeException
|
||||
{
|
||||
/**
|
||||
* @var GeneratorPoint
|
||||
*/
|
||||
private $G;
|
||||
|
||||
/**
|
||||
* @var PointInterface
|
||||
*/
|
||||
private $point;
|
||||
|
||||
public function __construct(GeneratorPoint $G, PointInterface $point, string $message = "", int $code = 0, Throwable $previous = null)
|
||||
{
|
||||
$this->G = $G;
|
||||
$this->point = $point;
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
public function getGenerator(): GeneratorPoint
|
||||
{
|
||||
return $this->G;
|
||||
}
|
||||
|
||||
public function getPoint(): PointInterface
|
||||
{
|
||||
return $this->point;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Exception;
|
||||
|
||||
class SignatureDecodeException extends \RuntimeException
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Exception;
|
||||
|
||||
class SquareRootException extends NumberTheoryException
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Exception;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class UnsupportedCurveException extends \RuntimeException
|
||||
{
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
private $oid;
|
||||
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
private $curveName;
|
||||
|
||||
public function __construct(string $message = "", int $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
public function setCurveName(string $curveName)
|
||||
{
|
||||
$this->curveName = $curveName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setOid(string $oid)
|
||||
{
|
||||
$this->oid = $oid;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasCurveName(): bool
|
||||
{
|
||||
return is_string($this->curveName);
|
||||
}
|
||||
|
||||
public function hasOid(): bool
|
||||
{
|
||||
return is_string($this->oid);
|
||||
}
|
||||
|
||||
public function getCurveName(): string
|
||||
{
|
||||
return $this->curveName;
|
||||
}
|
||||
|
||||
public function getOid(): string
|
||||
{
|
||||
return $this->oid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,620 @@
|
||||
<?php
|
||||
|
||||
namespace Mdanter\Ecc\Math;
|
||||
|
||||
use Mdanter\Ecc\Primitives\GeneratorPoint;
|
||||
|
||||
/**
|
||||
* Debug helper class to trace all calls to math functions along with the provided params and result.
|
||||
*/
|
||||
class DebugDecorator implements GmpMathInterface
|
||||
{
|
||||
/**
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $writer;
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface $adapter
|
||||
* @param callable|null $callback
|
||||
*/
|
||||
public function __construct(GmpMathInterface $adapter, callable $callback = null)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
$this->writer = $callback ?: function ($message) {
|
||||
echo $message;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
private function write($message)
|
||||
{
|
||||
call_user_func($this->writer, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $func
|
||||
* @param array $args
|
||||
* @return mixed
|
||||
*/
|
||||
private function call($func, $args)
|
||||
{
|
||||
$strArgs = array_map(
|
||||
function ($arg) {
|
||||
if ($arg instanceof \GMP) {
|
||||
return var_export($this->adapter->toString($arg), true);
|
||||
} else {
|
||||
return var_export($arg, true);
|
||||
}
|
||||
},
|
||||
$args
|
||||
);
|
||||
|
||||
if (strpos($func, '::')) {
|
||||
list(, $func) = explode('::', $func);
|
||||
}
|
||||
|
||||
$this->write($func.'('.implode(', ', $strArgs).')');
|
||||
|
||||
$res = call_user_func_array([ $this->adapter, $func ], $args);
|
||||
|
||||
if ($res instanceof \GMP) {
|
||||
$this->write(' => ' . var_export($this->adapter->toString($res), true) . PHP_EOL);
|
||||
} else {
|
||||
$this->write(' => ' . var_export($res, true) . PHP_EOL);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::cmp()
|
||||
*/
|
||||
public function cmp(\GMP $first, \GMP $other): int
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::cmp()
|
||||
*/
|
||||
public function equals(\GMP $first, \GMP $other): bool
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::mod()
|
||||
*/
|
||||
public function mod(\GMP $number, \GMP $modulus): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::add()
|
||||
*/
|
||||
public function add(\GMP $augend, \GMP $addend): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::sub()
|
||||
*/
|
||||
public function sub(\GMP $minuend, \GMP $subtrahend): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::mul()
|
||||
*/
|
||||
public function mul(\GMP $multiplier, \GMP $multiplicand): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::div()
|
||||
*/
|
||||
public function div(\GMP $dividend, \GMP $divisor): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::pow()
|
||||
*/
|
||||
public function pow(\GMP $base, int $exponent): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::bitwiseAnd()
|
||||
*/
|
||||
public function bitwiseAnd(\GMP $first, \GMP $other): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\MathAdapter::toString()
|
||||
*/
|
||||
public function toString(\GMP $value): string
|
||||
{
|
||||
return $this->adapter->toString($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::hexDec()
|
||||
*/
|
||||
public function hexDec(string $hexString): string
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::decHex()
|
||||
*/
|
||||
public function decHex(string $decString): string
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::powmod()
|
||||
*/
|
||||
public function powmod(\GMP $base, \GMP $exponent, \GMP $modulus): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::isPrime()
|
||||
*/
|
||||
public function isPrime(\GMP $n): bool
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::nextPrime()
|
||||
*/
|
||||
public function nextPrime(\GMP $currentPrime): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::inverseMod()
|
||||
*/
|
||||
public function inverseMod(\GMP $a, \GMP $m): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::jacobi()
|
||||
*/
|
||||
public function jacobi(\GMP $a, \GMP $p): int
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::intToString()
|
||||
*/
|
||||
public function intToFixedSizeString(\GMP $x, int $byteSize): string
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::intToString()
|
||||
*/
|
||||
public function intToString(\GMP $x): string
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::stringToInt()
|
||||
*/
|
||||
public function stringToInt(string $s): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::digestInteger()
|
||||
*/
|
||||
public function digestInteger(\GMP $m): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::gcd2()
|
||||
*/
|
||||
public function gcd2(\GMP $a, \GMP $m): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::rightShift()
|
||||
*/
|
||||
public function rightShift(\GMP $number, int $positions): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::leftShift()
|
||||
*/
|
||||
public function leftShift(\GMP $number, int $positions): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call',
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::bitwiseXor()
|
||||
*/
|
||||
public function bitwiseXor(\GMP $first, \GMP $other): \GMP
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call'
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::baseConvert()
|
||||
*/
|
||||
public function baseConvert(string $value, int $fromBase, int $toBase): string
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call'
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::getEcMath()
|
||||
*/
|
||||
public function getEcMath(GeneratorPoint $generatorPoint, $input)
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call'
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::getModularArithmetic()
|
||||
*/
|
||||
public function getModularArithmetic(\GMP $modulus): ModularArithmetic
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call'
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Math\GmpMathInterface::getNumberTheory()
|
||||
*/
|
||||
public function getNumberTheory(): NumberTheory
|
||||
{
|
||||
$func = __METHOD__;
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func(
|
||||
array(
|
||||
$this,
|
||||
'call'
|
||||
),
|
||||
$func,
|
||||
$args
|
||||
);
|
||||
}
|
||||
}
|
||||
+333
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
|
||||
namespace Mdanter\Ecc\Math;
|
||||
|
||||
use Mdanter\Ecc\Util\BinaryString;
|
||||
use Mdanter\Ecc\Util\NumberSize;
|
||||
|
||||
class GmpMath implements GmpMathInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::cmp()
|
||||
*/
|
||||
public function cmp(\GMP $first, \GMP $other): int
|
||||
{
|
||||
return gmp_cmp($first, $other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $first
|
||||
* @param \GMP $other
|
||||
* @return bool
|
||||
*/
|
||||
public function equals(\GMP $first, \GMP $other): bool
|
||||
{
|
||||
return gmp_cmp($first, $other) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::mod()
|
||||
*/
|
||||
public function mod(\GMP $number, \GMP $modulus): \GMP
|
||||
{
|
||||
return gmp_mod($number, $modulus);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::add()
|
||||
*/
|
||||
public function add(\GMP $augend, \GMP $addend): \GMP
|
||||
{
|
||||
return gmp_add($augend, $addend);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::sub()
|
||||
*/
|
||||
public function sub(\GMP $minuend, \GMP $subtrahend): \GMP
|
||||
{
|
||||
return gmp_sub($minuend, $subtrahend);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::mul()
|
||||
*/
|
||||
public function mul(\GMP $multiplier, \GMP $multiplicand): \GMP
|
||||
{
|
||||
return gmp_mul($multiplier, $multiplicand);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::div()
|
||||
*/
|
||||
public function div(\GMP $dividend, \GMP $divisor): \GMP
|
||||
{
|
||||
return gmp_div($dividend, $divisor);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::pow()
|
||||
*/
|
||||
public function pow(\GMP $base, int $exponent): \GMP
|
||||
{
|
||||
return gmp_pow($base, $exponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::bitwiseAnd()
|
||||
*/
|
||||
public function bitwiseAnd(\GMP $first, \GMP $other): \GMP
|
||||
{
|
||||
return gmp_and($first, $other);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::rightShift()
|
||||
*/
|
||||
public function rightShift(\GMP $number, int $positions): \GMP
|
||||
{
|
||||
// Shift 1 right = div / 2
|
||||
return gmp_div($number, gmp_pow(gmp_init(2, 10), $positions));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::bitwiseXor()
|
||||
*/
|
||||
public function bitwiseXor(\GMP $first, \GMP $other): \GMP
|
||||
{
|
||||
return gmp_xor($first, $other);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::leftShift()
|
||||
*/
|
||||
public function leftShift(\GMP $number, int $positions): \GMP
|
||||
{
|
||||
// Shift 1 left = mul by 2
|
||||
return gmp_mul($number, gmp_pow(2, $positions));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::toString()
|
||||
*/
|
||||
public function toString(\GMP $value): string
|
||||
{
|
||||
return gmp_strval($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::hexDec()
|
||||
*/
|
||||
public function hexDec(string $hex): string
|
||||
{
|
||||
return gmp_strval(gmp_init($hex, 16), 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::decHex()
|
||||
*/
|
||||
public function decHex(string $dec): string
|
||||
{
|
||||
$dec = gmp_init($dec, 10);
|
||||
|
||||
if (gmp_cmp($dec, 0) < 0) {
|
||||
throw new \InvalidArgumentException('Unable to convert negative integer to string');
|
||||
}
|
||||
|
||||
$hex = gmp_strval($dec, 16);
|
||||
|
||||
if (BinaryString::length($hex) % 2 != 0) {
|
||||
$hex = '0'.$hex;
|
||||
}
|
||||
|
||||
return $hex;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::powmod()
|
||||
*/
|
||||
public function powmod(\GMP $base, \GMP $exponent, \GMP $modulus): \GMP
|
||||
{
|
||||
if ($this->cmp($exponent, gmp_init(0, 10)) < 0) {
|
||||
throw new \InvalidArgumentException("Negative exponents (" . $this->toString($exponent) . ") not allowed.");
|
||||
}
|
||||
|
||||
return gmp_powm($base, $exponent, $modulus);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::isPrime()
|
||||
*/
|
||||
public function isPrime(\GMP $n): bool
|
||||
{
|
||||
$prob = gmp_prob_prime($n);
|
||||
|
||||
if ($prob > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::nextPrime()
|
||||
*/
|
||||
public function nextPrime(\GMP $starting_value): \GMP
|
||||
{
|
||||
return gmp_nextprime($starting_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::inverseMod()
|
||||
*/
|
||||
public function inverseMod(\GMP $a, \GMP $m): \GMP
|
||||
{
|
||||
return gmp_invert($a, $m);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::jacobi()
|
||||
*/
|
||||
public function jacobi(\GMP $a, \GMP $n): int
|
||||
{
|
||||
return gmp_jacobi($a, $n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $x
|
||||
* @param int $byteSize
|
||||
* @return string
|
||||
*/
|
||||
public function intToFixedSizeString(\GMP $x, int $byteSize): string
|
||||
{
|
||||
if ($byteSize < 0) {
|
||||
throw new \RuntimeException("Byte size cannot be negative");
|
||||
}
|
||||
|
||||
if (gmp_cmp($x, 0) < 0) {
|
||||
throw new \RuntimeException("x was negative - not yet supported");
|
||||
}
|
||||
|
||||
$two = gmp_init(2);
|
||||
$range = gmp_pow($two, $byteSize * 8);
|
||||
if (NumberSize::bnNumBits($this, $x) >= NumberSize::bnNumBits($this, $range)) {
|
||||
throw new \RuntimeException("Number overflows byte size");
|
||||
}
|
||||
|
||||
$maskShift = gmp_pow($two, 8);
|
||||
$mask = gmp_mul(gmp_init(255), $range);
|
||||
|
||||
$binary = '';
|
||||
for ($i = $byteSize - 1; $i >= 0; $i--) {
|
||||
$mask = gmp_div($mask, $maskShift);
|
||||
$binary .= pack('C', gmp_strval(gmp_div(gmp_and($x, $mask), gmp_pow($two, $i * 8)), 10));
|
||||
}
|
||||
|
||||
return $binary;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::intToString()
|
||||
*/
|
||||
public function intToString(\GMP $x): string
|
||||
{
|
||||
if (gmp_cmp($x, 0) < 0) {
|
||||
throw new \InvalidArgumentException('Unable to convert negative integer to string');
|
||||
}
|
||||
|
||||
$hex = gmp_strval($x, 16);
|
||||
|
||||
if (BinaryString::length($hex) % 2 != 0) {
|
||||
$hex = '0'.$hex;
|
||||
}
|
||||
|
||||
return pack('H*', $hex);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::stringToInt()
|
||||
*/
|
||||
public function stringToInt(string $s): \GMP
|
||||
{
|
||||
$result = gmp_init(0, 10);
|
||||
$sLen = BinaryString::length($s);
|
||||
|
||||
for ($c = 0; $c < $sLen; $c ++) {
|
||||
$result = gmp_add(gmp_mul(256, $result), gmp_init(ord($s[$c]), 10));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::digestInteger()
|
||||
*/
|
||||
public function digestInteger(\GMP $m): \GMP
|
||||
{
|
||||
return $this->stringToInt(hash('sha1', $this->intToString($m), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::gcd2()
|
||||
*/
|
||||
public function gcd2(\GMP $a, \GMP $b): \GMP
|
||||
{
|
||||
while ($this->cmp($a, gmp_init(0)) > 0) {
|
||||
$temp = $a;
|
||||
$a = $this->mod($b, $a);
|
||||
$b = $temp;
|
||||
}
|
||||
|
||||
return $b;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::baseConvert()
|
||||
*/
|
||||
public function baseConvert(string $number, int $from, int $to): string
|
||||
{
|
||||
return gmp_strval(gmp_init($number, $from), $to);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see GmpMathInterface::getNumberTheory()
|
||||
*/
|
||||
public function getNumberTheory(): NumberTheory
|
||||
{
|
||||
return new NumberTheory($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $modulus
|
||||
* @return ModularArithmetic
|
||||
*/
|
||||
public function getModularArithmetic(\GMP $modulus): ModularArithmetic
|
||||
{
|
||||
return new ModularArithmetic($this, $modulus);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
|
||||
namespace Mdanter\Ecc\Math;
|
||||
|
||||
interface GmpMathInterface
|
||||
{
|
||||
/**
|
||||
* Compares two numbers
|
||||
*
|
||||
* @param \GMP $first
|
||||
* @param \GMP $other
|
||||
* @return int less than 0 if first is less than second, 0 if equal, greater than 0 if greater than.
|
||||
*/
|
||||
public function cmp(\GMP $first, \GMP $other): int;
|
||||
|
||||
/**
|
||||
* @param \GMP $first
|
||||
* @param \GMP $other
|
||||
* @return bool
|
||||
*/
|
||||
public function equals(\GMP $first, \GMP $other): bool;
|
||||
|
||||
/**
|
||||
* Returns the remainder of a division
|
||||
*
|
||||
* @param \GMP $number
|
||||
* @param \GMP $modulus
|
||||
* @return \GMP
|
||||
*/
|
||||
public function mod(\GMP $number, \GMP $modulus): \GMP;
|
||||
|
||||
/**
|
||||
* Adds two numbers
|
||||
*
|
||||
* @param \GMP $augend
|
||||
* @param \GMP $addend
|
||||
* @return \GMP
|
||||
*/
|
||||
public function add(\GMP $augend, \GMP $addend): \GMP;
|
||||
|
||||
/**
|
||||
* Substract one number from another
|
||||
*
|
||||
* @param \GMP $minuend
|
||||
* @param \GMP $subtrahend
|
||||
* @return \GMP
|
||||
*/
|
||||
public function sub(\GMP $minuend, \GMP $subtrahend): \GMP;
|
||||
|
||||
/**
|
||||
* Multiplies a number by another.
|
||||
*
|
||||
* @param \GMP $multiplier
|
||||
* @param \GMP $multiplicand
|
||||
* @return \GMP
|
||||
*/
|
||||
public function mul(\GMP $multiplier, \GMP $multiplicand): \GMP;
|
||||
|
||||
/**
|
||||
* Divides a number by another.
|
||||
*
|
||||
* @param \GMP $dividend
|
||||
* @param \GMP $divisor
|
||||
* @return \GMP
|
||||
*/
|
||||
public function div(\GMP $dividend, \GMP $divisor): \GMP;
|
||||
|
||||
/**
|
||||
* Raises a number to a power.
|
||||
*
|
||||
* @param \GMP $base The number to raise.
|
||||
* @param int $exponent The power to raise the number to.
|
||||
* @return \GMP
|
||||
*/
|
||||
public function pow(\GMP $base, int $exponent): \GMP;
|
||||
|
||||
/**
|
||||
* Performs a logical AND between two values.
|
||||
*
|
||||
* @param \GMP $first
|
||||
* @param \GMP $other
|
||||
* @return \GMP
|
||||
*/
|
||||
public function bitwiseAnd(\GMP $first, \GMP $other): \GMP;
|
||||
|
||||
/**
|
||||
* Performs a logical XOR between two values.
|
||||
*
|
||||
* @param \GMP $first
|
||||
* @param \GMP $other
|
||||
* @return \GMP
|
||||
*/
|
||||
public function bitwiseXor(\GMP $first, \GMP $other): \GMP;
|
||||
|
||||
/**
|
||||
* Shifts bits to the right
|
||||
* @param \GMP $number Number to shift
|
||||
* @param int $positions Number of positions to shift
|
||||
* @return \GMP
|
||||
*/
|
||||
public function rightShift(\GMP $number, int $positions): \GMP;
|
||||
|
||||
/**
|
||||
* Shifts bits to the left
|
||||
* @param \GMP $number Number to shift
|
||||
* @param int $positions Number of positions to shift
|
||||
* @return \GMP
|
||||
*/
|
||||
public function leftShift(\GMP $number, int $positions): \GMP;
|
||||
|
||||
/**
|
||||
* Returns the string representation of a returned value.
|
||||
*
|
||||
* @param \GMP $value
|
||||
* @return string
|
||||
*/
|
||||
public function toString(\GMP $value): string;
|
||||
|
||||
/**
|
||||
* Converts an hexadecimal string to decimal.
|
||||
*
|
||||
* @param string $hexString
|
||||
* @return int|string
|
||||
*/
|
||||
public function hexDec(string $hexString): string;
|
||||
|
||||
/**
|
||||
* Converts a decimal string to hexadecimal.
|
||||
*
|
||||
* @param int|string $decString
|
||||
* @return string
|
||||
*/
|
||||
public function decHex(string $decString): string;
|
||||
|
||||
/**
|
||||
* Calculates the modular exponent of a number.
|
||||
*
|
||||
* @param \GMP $base
|
||||
* @param \GMP $exponent
|
||||
* @param \GMP $modulus
|
||||
* @return \GMP
|
||||
*/
|
||||
public function powmod(\GMP $base, \GMP $exponent, \GMP $modulus): \GMP;
|
||||
|
||||
/**
|
||||
* Checks whether a number is a prime.
|
||||
*
|
||||
* @param \GMP $n
|
||||
* @return boolean
|
||||
*/
|
||||
public function isPrime(\GMP $n): bool;
|
||||
|
||||
/**
|
||||
* Gets the next known prime that is greater than a given prime.
|
||||
*
|
||||
* @param \GMP $currentPrime
|
||||
* @return \GMP
|
||||
*/
|
||||
public function nextPrime(\GMP $currentPrime): \GMP;
|
||||
|
||||
/**
|
||||
* @param \GMP $a
|
||||
* @param \GMP $m
|
||||
* @return \GMP
|
||||
*/
|
||||
public function inverseMod(\GMP $a, \GMP $m): \GMP;
|
||||
|
||||
/**
|
||||
* @param \GMP $a
|
||||
* @param \GMP $p
|
||||
* @return int
|
||||
*/
|
||||
public function jacobi(\GMP $a, \GMP $p): int;
|
||||
|
||||
/**
|
||||
* @param \GMP $x
|
||||
* @return string
|
||||
*/
|
||||
public function intToString(\GMP $x): string;
|
||||
|
||||
/**
|
||||
* @param \GMP $x
|
||||
* @param int $byteSize
|
||||
* @return string
|
||||
*/
|
||||
public function intToFixedSizeString(\GMP $x, int $byteSize): string;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int|string $s
|
||||
* @return \GMP
|
||||
*/
|
||||
public function stringToInt(string $s): \GMP;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param \GMP $m
|
||||
* @return \GMP
|
||||
*/
|
||||
public function digestInteger(\GMP $m): \GMP;
|
||||
|
||||
/**
|
||||
* @param \GMP $a
|
||||
* @param \GMP $m
|
||||
* @return \GMP
|
||||
*/
|
||||
public function gcd2(\GMP $a, \GMP $m): \GMP;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @param int $fromBase
|
||||
* @param int $toBase
|
||||
* @return string
|
||||
*/
|
||||
public function baseConvert(string $value, int $fromBase, int $toBase): string;
|
||||
|
||||
/**
|
||||
* @return NumberTheory
|
||||
*/
|
||||
public function getNumberTheory(): NumberTheory;
|
||||
|
||||
/**
|
||||
* @param \GMP $modulus
|
||||
* @return ModularArithmetic
|
||||
*/
|
||||
public function getModularArithmetic(\GMP $modulus): ModularArithmetic;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Mdanter\Ecc\Math;
|
||||
|
||||
class MathAdapterFactory
|
||||
{
|
||||
/**
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private static $forcedAdapter = null;
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface $adapter
|
||||
*/
|
||||
public static function forceAdapter(GmpMathInterface $adapter = null)
|
||||
{
|
||||
self::$forcedAdapter = $adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $debug
|
||||
* @return DebugDecorator|GmpMathInterface|null
|
||||
*/
|
||||
public static function getAdapter(bool $debug = false): GmpMathInterface
|
||||
{
|
||||
if (self::$forcedAdapter !== null) {
|
||||
return self::$forcedAdapter;
|
||||
}
|
||||
|
||||
$adapter = new GmpMath();
|
||||
|
||||
return self::wrapAdapter($adapter, $debug);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface $adapter
|
||||
* @param bool $debug
|
||||
* @return DebugDecorator|GmpMathInterface
|
||||
*/
|
||||
private static function wrapAdapter(GmpMathInterface $adapter, bool $debug): GmpMathInterface
|
||||
{
|
||||
if ($debug === true) {
|
||||
return new DebugDecorator($adapter);
|
||||
}
|
||||
|
||||
return $adapter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Mdanter\Ecc\Math;
|
||||
|
||||
class ModularArithmetic
|
||||
{
|
||||
/**
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* @var \GMP
|
||||
*/
|
||||
private $modulus;
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface $adapter
|
||||
* @param \GMP $modulus
|
||||
*/
|
||||
public function __construct(GmpMathInterface $adapter, \GMP $modulus)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
$this->modulus = $modulus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $augend
|
||||
* @param \GMP $addend
|
||||
* @return \GMP
|
||||
*/
|
||||
public function add(\GMP $augend, \GMP $addend): \GMP
|
||||
{
|
||||
return $this->adapter->mod($this->adapter->add($augend, $addend), $this->modulus);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $minuend
|
||||
* @param \GMP $subtrahend
|
||||
* @return \GMP
|
||||
*/
|
||||
public function sub(\GMP $minuend, \GMP $subtrahend): \GMP
|
||||
{
|
||||
return $this->adapter->mod($this->adapter->sub($minuend, $subtrahend), $this->modulus);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $multiplier
|
||||
* @param \GMP $muliplicand
|
||||
* @return \GMP
|
||||
*/
|
||||
public function mul(\GMP $multiplier, \GMP $muliplicand): \GMP
|
||||
{
|
||||
return $this->adapter->mod($this->adapter->mul($multiplier, $muliplicand), $this->modulus);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $dividend
|
||||
* @param \GMP $divisor
|
||||
* @return \GMP
|
||||
*/
|
||||
public function div(\GMP $dividend, \GMP $divisor): \GMP
|
||||
{
|
||||
return $this->mul($dividend, $this->adapter->inverseMod($divisor, $this->modulus));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $base
|
||||
* @param \GMP $exponent
|
||||
* @return \GMP
|
||||
*/
|
||||
public function pow(\GMP $base, \GMP $exponent): \GMP
|
||||
{
|
||||
return $this->adapter->powmod($base, $exponent, $this->modulus);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
|
||||
namespace Mdanter\Ecc\Math;
|
||||
|
||||
/***********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
************************************************************************/
|
||||
|
||||
/**
|
||||
* Implementation of some number theoretic algorithms
|
||||
*
|
||||
* @author Matyas Danter
|
||||
*/
|
||||
|
||||
use Mdanter\Ecc\Exception\NumberTheoryException;
|
||||
use Mdanter\Ecc\Exception\SquareRootException;
|
||||
|
||||
/**
|
||||
* Rewritten to take a MathAdaptor to handle different environments. Has
|
||||
* some desireable functions for public key compression/recovery.
|
||||
*/
|
||||
class NumberTheory
|
||||
{
|
||||
/**
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface $adapter
|
||||
*/
|
||||
public function __construct(GmpMathInterface $adapter)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
$this->zero = gmp_init(0, 10);
|
||||
$this->one = gmp_init(1, 10);
|
||||
$this->two = gmp_init(2, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP[] $poly
|
||||
* @param \GMP[] $polymod
|
||||
* @param \GMP $p
|
||||
* @return \GMP[]
|
||||
*/
|
||||
public function polynomialReduceMod(array $poly, array $polymod, \GMP $p): array
|
||||
{
|
||||
$adapter = $this->adapter;
|
||||
|
||||
// Only enter if last value is set, implying count > 0
|
||||
if ((($last = end($polymod)) instanceof \GMP) && $adapter->equals($last, $this->one)) {
|
||||
$count_polymod = count($polymod);
|
||||
while (count($poly) >= $count_polymod) {
|
||||
if (!$adapter->equals(end($poly), $this->zero)) {
|
||||
for ($i = 2; $i < $count_polymod + 1; $i++) {
|
||||
$poly[count($poly) - $i] =
|
||||
$adapter->mod(
|
||||
$adapter->sub(
|
||||
$poly[count($poly) - $i],
|
||||
$adapter->mul(
|
||||
end($poly),
|
||||
$polymod[$count_polymod - $i]
|
||||
)
|
||||
),
|
||||
$p
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$poly = array_slice($poly, 0, count($poly) - 1);
|
||||
}
|
||||
|
||||
return $poly;
|
||||
}
|
||||
|
||||
throw new NumberTheoryException('Unable to calculate polynomialReduceMod');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP[] $m1
|
||||
* @param \GMP[] $m2
|
||||
* @param \GMP[] $polymod
|
||||
* @param \GMP $p
|
||||
* @return \GMP[]
|
||||
*/
|
||||
public function polynomialMultiplyMod(array $m1, array $m2, array $polymod, \GMP $p): array
|
||||
{
|
||||
$prod = array();
|
||||
$cm1 = count($m1);
|
||||
$cm2 = count($m2);
|
||||
|
||||
for ($i = 0; $i < $cm1; $i++) {
|
||||
for ($j = 0; $j < $cm2; $j++) {
|
||||
$index = $i + $j;
|
||||
if (!isset($prod[$index])) {
|
||||
$prod[$index] = $this->zero;
|
||||
}
|
||||
$prod[$index] =
|
||||
$this->adapter->mod(
|
||||
$this->adapter->add(
|
||||
$prod[$index],
|
||||
$this->adapter->mul(
|
||||
$m1[$i],
|
||||
$m2[$j]
|
||||
)
|
||||
),
|
||||
$p
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->polynomialReduceMod($prod, $polymod, $p);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP[] $base
|
||||
* @param \GMP $exponent
|
||||
* @param \GMP[] $polymod
|
||||
* @param \GMP $p
|
||||
* @return \GMP[]
|
||||
*/
|
||||
public function polynomialPowMod(array $base, \GMP $exponent, array $polymod, \GMP $p): array
|
||||
{
|
||||
$adapter = $this->adapter;
|
||||
|
||||
if ($adapter->cmp($exponent, $p) < 0) {
|
||||
if ($adapter->equals($exponent, $this->zero)) {
|
||||
return $this->one;
|
||||
}
|
||||
|
||||
$G = $base;
|
||||
$k = $exponent;
|
||||
|
||||
if ($adapter->equals($adapter->mod($k, $this->two), $this->one)) {
|
||||
$s = $G;
|
||||
} else {
|
||||
$s = array($this->one);
|
||||
}
|
||||
|
||||
while ($adapter->cmp($k, $this->one) > 0) {
|
||||
$k = $adapter->div($k, $this->two);
|
||||
|
||||
$G = $this->polynomialMultiplyMod($G, $G, $polymod, $p);
|
||||
if ($adapter->equals($adapter->mod($k, $this->two), $this->one)) {
|
||||
$s = $this->polynomialMultiplyMod($G, $s, $polymod, $p);
|
||||
}
|
||||
}
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
throw new NumberTheoryException('Unable to calculate polynomialPowMod');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $a
|
||||
* @param \GMP $p
|
||||
* @return \GMP
|
||||
*/
|
||||
public function squareRootModP(\GMP $a, \GMP $p): \GMP
|
||||
{
|
||||
$math = $this->adapter;
|
||||
$four = gmp_init(4, 10);
|
||||
$eight = gmp_init(8, 10);
|
||||
|
||||
$modMath = $math->getModularArithmetic($p);
|
||||
if ($math->cmp($this->one, $p) < 0) {
|
||||
if ($math->equals($a, $this->zero)) {
|
||||
return $this->zero;
|
||||
}
|
||||
|
||||
if ($math->equals($p, $this->two)) {
|
||||
return $a;
|
||||
}
|
||||
|
||||
$jac = $math->jacobi($a, $p);
|
||||
if ($jac === -1) {
|
||||
throw new SquareRootException("{$math->toString($a)} has no square root modulo {$math->toString($p)}");
|
||||
}
|
||||
|
||||
if ($math->equals($math->mod($p, $four), gmp_init(3, 10))) {
|
||||
return $modMath->pow($a, $math->div($math->add($p, $this->one), $four));
|
||||
}
|
||||
|
||||
if ($math->equals($math->mod($p, $eight), gmp_init(5, 10))) {
|
||||
$d = $modMath->pow($a, $math->div($math->sub($p, $this->one), $four));
|
||||
if ($math->equals($d, $this->one)) {
|
||||
return $modMath->pow($a, $math->div($math->add($p, gmp_init(3, 10)), $eight));
|
||||
}
|
||||
|
||||
if ($math->equals($d, $math->sub($p, $this->one))) {
|
||||
return $modMath->mul(
|
||||
$math->mul(
|
||||
$this->two,
|
||||
$a
|
||||
),
|
||||
$modMath->pow(
|
||||
$math->mul(
|
||||
$four,
|
||||
$a
|
||||
),
|
||||
$math->div(
|
||||
$math->sub(
|
||||
$p,
|
||||
gmp_init(5, 10)
|
||||
),
|
||||
$eight
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
//shouldn't get here
|
||||
}
|
||||
|
||||
for ($b = $this->two; $math->cmp($b, $p) < 0; $b = gmp_add($b, $this->one)) {
|
||||
if ($math->jacobi(
|
||||
$math->sub(
|
||||
$math->mul($b, $b),
|
||||
$math->mul($four, $a)
|
||||
),
|
||||
$p
|
||||
) == -1
|
||||
) {
|
||||
$f = array($a, $math->sub($this->zero, $b), $this->one);
|
||||
|
||||
$ff = $this->polynomialPowMod(
|
||||
array($this->zero, $this->one),
|
||||
$math->div(
|
||||
$math->add(
|
||||
$p,
|
||||
$this->one
|
||||
),
|
||||
$this->two
|
||||
),
|
||||
$f,
|
||||
$p
|
||||
);
|
||||
|
||||
if ($math->equals($ff[1], $this->zero)) {
|
||||
return $ff[0];
|
||||
}
|
||||
// if we got here no b was found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new SquareRootException('Unable to calculate square root mod p!');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/***********************************************************************
|
||||
Copyright (C) 2012 Matyas Danter
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
*************************************************************************/
|
||||
namespace Mdanter\Ecc\Primitives;
|
||||
|
||||
use Mdanter\Ecc\Exception\PointRecoveryException;
|
||||
use Mdanter\Ecc\Exception\SquareRootException;
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Math\ModularArithmetic;
|
||||
use Mdanter\Ecc\Random\RandomNumberGeneratorInterface;
|
||||
|
||||
/**
|
||||
* This class is a representation of an EC over a field modulo a prime number
|
||||
*
|
||||
* Important objectives for this class are:
|
||||
* - Does the curve contain a point?
|
||||
* - Comparison of two curves.
|
||||
*/
|
||||
class CurveFp implements CurveFpInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var CurveParameters
|
||||
*/
|
||||
protected $parameters;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
protected $adapter = null;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var ModularArithmetic
|
||||
*/
|
||||
protected $modAdapter = null;
|
||||
|
||||
/**
|
||||
* Constructor that sets up the instance variables.
|
||||
*
|
||||
* @param CurveParameters $parameters
|
||||
* @param GmpMathInterface $adapter
|
||||
*/
|
||||
public function __construct(CurveParameters $parameters, GmpMathInterface $adapter)
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
$this->adapter = $adapter;
|
||||
$this->modAdapter = new ModularArithmetic($this->adapter, $this->parameters->getPrime());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\CurveFpInterface::getModAdapter()
|
||||
*/
|
||||
public function getModAdapter(): ModularArithmetic
|
||||
{
|
||||
return $this->modAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\CurveFpInterface::getPoint()
|
||||
*/
|
||||
public function getPoint(\GMP $x, \GMP $y, \GMP $order = null): PointInterface
|
||||
{
|
||||
return new Point($this->adapter, $this, $x, $y, $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\CurveFpInterface::getInfinity()
|
||||
*/
|
||||
public function getInfinity(): PointInterface
|
||||
{
|
||||
return new Point($this->adapter, $this, gmp_init(0, 10), gmp_init(0, 10), null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\CurveFpInterface::getGenerator()
|
||||
*/
|
||||
public function getGenerator(\GMP $x, \GMP $y, \GMP $order, RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint
|
||||
{
|
||||
return new GeneratorPoint($this->adapter, $this, $x, $y, $order, $randomGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $wasOdd
|
||||
* @param \GMP $xCoord
|
||||
* @return \GMP
|
||||
*/
|
||||
public function recoverYfromX(bool $wasOdd, \GMP $xCoord): \GMP
|
||||
{
|
||||
$math = $this->adapter;
|
||||
$prime = $this->getPrime();
|
||||
|
||||
try {
|
||||
$root = $this->adapter->getNumberTheory()->squareRootModP(
|
||||
$math->add(
|
||||
$math->add(
|
||||
$this->modAdapter->pow($xCoord, gmp_init(3, 10)),
|
||||
$math->mul($this->getA(), $xCoord)
|
||||
),
|
||||
$this->getB()
|
||||
),
|
||||
$prime
|
||||
);
|
||||
} catch (SquareRootException $e) {
|
||||
throw new PointRecoveryException("Failed to recover y coordinate for point", 0, $e);
|
||||
}
|
||||
|
||||
if ($math->equals($math->mod($root, gmp_init(2, 10)), gmp_init(1)) === $wasOdd) {
|
||||
return $root;
|
||||
} else {
|
||||
return $math->sub($prime, $root);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\CurveFpInterface::contains()
|
||||
*/
|
||||
public function contains(\GMP $x, \GMP $y): bool
|
||||
{
|
||||
$math = $this->adapter;
|
||||
|
||||
$eq_zero = $math->equals(
|
||||
$this->modAdapter->sub(
|
||||
$math->pow($y, 2),
|
||||
$math->add(
|
||||
$math->add(
|
||||
$math->pow($x, 3),
|
||||
$math->mul($this->getA(), $x)
|
||||
),
|
||||
$this->getB()
|
||||
)
|
||||
),
|
||||
gmp_init(0, 10)
|
||||
);
|
||||
|
||||
return $eq_zero;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\CurveFpInterface::getA()
|
||||
*/
|
||||
public function getA(): \GMP
|
||||
{
|
||||
return $this->parameters->getA();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\CurveFpInterface::getB()
|
||||
*/
|
||||
public function getB(): \GMP
|
||||
{
|
||||
return $this->parameters->getB();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\CurveFpInterface::getPrime()
|
||||
*/
|
||||
public function getPrime(): \GMP
|
||||
{
|
||||
return $this->parameters->getPrime();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSize(): int
|
||||
{
|
||||
return $this->parameters->getSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\CurveFpInterface::cmp()
|
||||
*/
|
||||
public function cmp(CurveFpInterface $other): int
|
||||
{
|
||||
$math = $this->adapter;
|
||||
|
||||
$equal = $math->equals($this->getA(), $other->getA());
|
||||
$equal &= $math->equals($this->getB(), $other->getB());
|
||||
$equal &= $math->equals($this->getPrime(), $other->getPrime());
|
||||
|
||||
return ($equal) ? 0 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\CurveFpInterface::equals()
|
||||
*/
|
||||
public function equals(CurveFpInterface $other): bool
|
||||
{
|
||||
return $this->cmp($other) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\CurveFpInterface::__toString()
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return 'curve(' . $this->adapter->toString($this->getA()) . ', ' . $this->adapter->toString($this->getB()) . ', ' . $this->adapter->toString($this->getPrime()) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo()
|
||||
{
|
||||
return [
|
||||
'a' => $this->adapter->toString($this->getA()),
|
||||
'b' => $this->adapter->toString($this->getB()),
|
||||
'prime' => $this->adapter->toString($this->getPrime())
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Primitives;
|
||||
|
||||
use Mdanter\Ecc\Math\ModularArithmetic;
|
||||
use Mdanter\Ecc\Random\RandomNumberGeneratorInterface;
|
||||
|
||||
/**
|
||||
* *********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is the contract for implementing CurveFp (EC prime finite-field).
|
||||
*/
|
||||
interface CurveFpInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns a modular arithmetic adapter.
|
||||
*
|
||||
* @return ModularArithmetic
|
||||
*/
|
||||
public function getModAdapter(): ModularArithmetic;
|
||||
|
||||
/**
|
||||
* Returns the point identified by given coordinates.
|
||||
*
|
||||
* @param \GMP $x
|
||||
* @param \GMP $y
|
||||
* @param \GMP $order
|
||||
* @return PointInterface
|
||||
*/
|
||||
public function getPoint(\GMP $x, \GMP $y, \GMP $order = null): PointInterface;
|
||||
|
||||
/**
|
||||
* @param bool $wasOdd
|
||||
* @param \GMP $x
|
||||
* @return \GMP
|
||||
*/
|
||||
public function recoverYfromX(bool $wasOdd, \GMP $x): \GMP;
|
||||
|
||||
/**
|
||||
* Returns a point representing infinity on the curve.
|
||||
*
|
||||
* @return PointInterface
|
||||
*/
|
||||
public function getInfinity(): PointInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param \GMP $x
|
||||
* @param \GMP $y
|
||||
* @param \GMP $order
|
||||
* @param RandomNumberGeneratorInterface $randomGenerator
|
||||
* @return GeneratorPoint
|
||||
*/
|
||||
public function getGenerator(\GMP $x, \GMP $y, \GMP $order, RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint;
|
||||
|
||||
/**
|
||||
* Checks whether the curve contains the given coordinates.
|
||||
*
|
||||
* @param \GMP $x
|
||||
* @param \GMP $y
|
||||
* @return bool
|
||||
*/
|
||||
public function contains(\GMP $x, \GMP $y): bool;
|
||||
|
||||
/**
|
||||
* Returns the a parameter of the curve.
|
||||
*
|
||||
* @return \GMP
|
||||
*/
|
||||
public function getA(): \GMP;
|
||||
|
||||
/**
|
||||
* Returns the b parameter of the curve.
|
||||
*
|
||||
* @return \GMP
|
||||
*/
|
||||
public function getB(): \GMP;
|
||||
|
||||
/**
|
||||
* Returns the prime associated with the curve.
|
||||
*
|
||||
* @return \GMP
|
||||
*/
|
||||
public function getPrime(): \GMP;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSize(): int;
|
||||
|
||||
/**
|
||||
* Compares the curve to another.
|
||||
*
|
||||
* @param CurveFpInterface $other
|
||||
* @return int < 0 if $this < $other, 0 if $other == $this, > 0 if $this > $other
|
||||
*/
|
||||
public function cmp(CurveFpInterface $other): int;
|
||||
|
||||
/**
|
||||
* Checks whether the curve is equal to another.
|
||||
*
|
||||
* @param CurveFpInterface $other
|
||||
* @return bool
|
||||
*/
|
||||
public function equals(CurveFpInterface $other): bool;
|
||||
|
||||
/**
|
||||
* Return string representation of curve for debugging
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Primitives;
|
||||
|
||||
class CurveParameters
|
||||
{
|
||||
/**
|
||||
* Elliptic curve over the field of integers modulo a prime.
|
||||
*
|
||||
* @var \GMP
|
||||
*/
|
||||
protected $a;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var \GMP
|
||||
*/
|
||||
protected $b;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var \GMP
|
||||
*/
|
||||
protected $prime;
|
||||
|
||||
/**
|
||||
* Binary length of keys associated with these curve parameters
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $size;
|
||||
|
||||
/**
|
||||
* @param int $size
|
||||
* @param \GMP $prime
|
||||
* @param \GMP $a
|
||||
* @param \GMP $b
|
||||
*/
|
||||
public function __construct(int $size, \GMP $prime, \GMP $a, \GMP $b)
|
||||
{
|
||||
$this->size = $size;
|
||||
$this->prime = $prime;
|
||||
$this->a = $a;
|
||||
$this->b = $b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \GMP
|
||||
*/
|
||||
public function getA(): \GMP
|
||||
{
|
||||
return $this->a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \GMP
|
||||
*/
|
||||
public function getB(): \GMP
|
||||
{
|
||||
return $this->b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \GMP
|
||||
*/
|
||||
public function getPrime(): \GMP
|
||||
{
|
||||
return $this->prime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSize(): int
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Primitives;
|
||||
|
||||
use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface;
|
||||
use Mdanter\Ecc\Crypto\Key\PublicKeyInterface;
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Crypto\Key\PrivateKey;
|
||||
use Mdanter\Ecc\Crypto\Key\PublicKey;
|
||||
use Mdanter\Ecc\Random\RandomGeneratorFactory;
|
||||
use Mdanter\Ecc\Random\RandomNumberGeneratorInterface;
|
||||
|
||||
/**
|
||||
* Curve point from which public and private keys can be derived.
|
||||
*/
|
||||
class GeneratorPoint extends Point
|
||||
{
|
||||
/**
|
||||
* @var RandomNumberGeneratorInterface
|
||||
*/
|
||||
private $generator;
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface $adapter
|
||||
* @param CurveFpInterface $curve
|
||||
* @param \GMP $x
|
||||
* @param \GMP $y
|
||||
* @param \GMP $order
|
||||
* @param RandomNumberGeneratorInterface $generator
|
||||
*/
|
||||
public function __construct(
|
||||
GmpMathInterface $adapter,
|
||||
CurveFpInterface $curve,
|
||||
\GMP $x,
|
||||
\GMP $y,
|
||||
\GMP $order,
|
||||
RandomNumberGeneratorInterface $generator = null
|
||||
) {
|
||||
$this->generator = $generator ?: RandomGeneratorFactory::getRandomGenerator();
|
||||
parent::__construct($adapter, $curve, $x, $y, $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies validity of given coordinates against the current point and its point.
|
||||
*
|
||||
* @todo Check if really necessary here (only used for testing in lib)
|
||||
* @param \GMP $x
|
||||
* @param \GMP $y
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(\GMP $x, \GMP $y): bool
|
||||
{
|
||||
|
||||
$math = $this->getAdapter();
|
||||
|
||||
$n = $this->getOrder();
|
||||
$zero = gmp_init(0, 10);
|
||||
$curve = $this->getCurve();
|
||||
|
||||
if ($math->cmp($x, $zero) < 0 || $math->cmp($n, $x) <= 0 || $math->cmp($y, $zero) < 0 || $math->cmp($n, $y) <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $curve->contains($x, $y)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$point = $curve->getPoint($x, $y)->mul($n);
|
||||
|
||||
if (! $point->isInfinity()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PrivateKeyInterface
|
||||
*/
|
||||
public function createPrivateKey(): PrivateKeyInterface
|
||||
{
|
||||
$secret = $this->generator->generate($this->getOrder());
|
||||
|
||||
return new PrivateKey($this->getAdapter(), $this, $secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $x
|
||||
* @param \GMP $y
|
||||
* @return PublicKeyInterface
|
||||
*/
|
||||
public function getPublicKeyFrom(\GMP $x, \GMP $y): PublicKeyInterface
|
||||
{
|
||||
$pubPoint = $this->getCurve()->getPoint($x, $y, $this->getOrder());
|
||||
return new PublicKey($this->getAdapter(), $this, $pubPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $secretMultiplier
|
||||
* @return PrivateKeyInterface
|
||||
*/
|
||||
public function getPrivateKeyFrom(\GMP $secretMultiplier): PrivateKeyInterface
|
||||
{
|
||||
return new PrivateKey($this->getAdapter(), $this, $secretMultiplier);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Primitives;
|
||||
|
||||
use Mdanter\Ecc\Exception\PointException;
|
||||
use Mdanter\Ecc\Exception\PointNotOnCurveException;
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Math\ModularArithmetic;
|
||||
|
||||
/**
|
||||
* *********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class is where the elliptic curve arithmetic takes place.
|
||||
* The important methods are:
|
||||
* - add: adds two points according to ec arithmetic
|
||||
* - double: doubles a point on the ec field mod p
|
||||
* - mul: uses double and add to achieve multiplication The rest of the methods are there for supporting the ones above.
|
||||
*/
|
||||
class Point implements PointInterface
|
||||
{
|
||||
/**
|
||||
* @var CurveFpInterface
|
||||
*/
|
||||
private $curve;
|
||||
|
||||
/**
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* @var ModularArithmetic
|
||||
*/
|
||||
private $modAdapter;
|
||||
|
||||
/**
|
||||
* @var \GMP
|
||||
*/
|
||||
private $x;
|
||||
|
||||
/**
|
||||
* @var \GMP
|
||||
*/
|
||||
private $y;
|
||||
|
||||
/**
|
||||
* @var \GMP
|
||||
*/
|
||||
private $order;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $infinity = false;
|
||||
|
||||
/**
|
||||
* Initialize a new instance
|
||||
*
|
||||
* @param GmpMathInterface $adapter
|
||||
* @param CurveFpInterface $curve
|
||||
* @param \GMP $x
|
||||
* @param \GMP $y
|
||||
* @param \GMP $order
|
||||
* @param bool $infinity
|
||||
*
|
||||
* @throws \RuntimeException when either the curve does not contain the given coordinates or
|
||||
* when order is not null and P(x, y) * order is not equal to infinity.
|
||||
*/
|
||||
public function __construct(GmpMathInterface $adapter, CurveFpInterface $curve, \GMP $x, \GMP $y, \GMP $order = null, bool $infinity = false)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
$this->modAdapter = $curve->getModAdapter();
|
||||
$this->curve = $curve;
|
||||
$this->x = $x;
|
||||
$this->y = $y;
|
||||
$this->order = $order !== null ? $order : gmp_init(0, 10);
|
||||
$this->infinity = (bool) $infinity;
|
||||
if (! $infinity && ! $curve->contains($x, $y)) {
|
||||
throw new PointNotOnCurveException($x, $y, $curve);
|
||||
}
|
||||
|
||||
if (!is_null($order)) {
|
||||
$mul = $this->mul($order);
|
||||
if (!$mul->isInfinity()) {
|
||||
throw new PointException("SELF * ORDER MUST EQUAL INFINITY. (" . (string)$mul . " found instead)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return GmpMathInterface
|
||||
*/
|
||||
public function getAdapter(): GmpMathInterface
|
||||
{
|
||||
return $this->adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\PointInterface::isInfinity()
|
||||
*/
|
||||
public function isInfinity(): bool
|
||||
{
|
||||
return (bool) $this->infinity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\PointInterface::getCurve()
|
||||
*/
|
||||
public function getCurve(): CurveFpInterface
|
||||
{
|
||||
return $this->curve;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\PointInterface::getOrder()
|
||||
*/
|
||||
public function getOrder(): \GMP
|
||||
{
|
||||
return $this->order;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\PointInterface::getX()
|
||||
*/
|
||||
public function getX(): \GMP
|
||||
{
|
||||
return $this->x;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\PointInterface::getY()
|
||||
*/
|
||||
public function getY(): \GMP
|
||||
{
|
||||
return $this->y;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\PointInterface::add()
|
||||
* @return self
|
||||
*/
|
||||
public function add(PointInterface $addend): PointInterface
|
||||
{
|
||||
if (! $this->curve->equals($addend->getCurve())) {
|
||||
throw new \RuntimeException("The Elliptic Curves do not match.");
|
||||
}
|
||||
|
||||
if ($addend->isInfinity()) {
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
if ($this->isInfinity()) {
|
||||
return clone $addend;
|
||||
}
|
||||
|
||||
$math = $this->adapter;
|
||||
$modMath = $this->modAdapter;
|
||||
|
||||
if ($math->equals($addend->getX(), $this->x)) {
|
||||
if ($math->equals($addend->getY(), $this->y)) {
|
||||
return $this->getDouble();
|
||||
} else {
|
||||
return $this->curve->getInfinity();
|
||||
}
|
||||
}
|
||||
|
||||
$slope = $modMath->div(
|
||||
$math->sub($addend->getY(), $this->y),
|
||||
$math->sub($addend->getX(), $this->x)
|
||||
);
|
||||
|
||||
$xR = $modMath->sub(
|
||||
$math->sub($math->pow($slope, 2), $this->x),
|
||||
$addend->getX()
|
||||
);
|
||||
|
||||
$yR = $modMath->sub(
|
||||
$math->mul($slope, $math->sub($this->x, $xR)),
|
||||
$this->y
|
||||
);
|
||||
|
||||
return $this->curve->getPoint($xR, $yR, $this->order);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\PointInterface::cmp()
|
||||
*/
|
||||
public function cmp(PointInterface $other): int
|
||||
{
|
||||
if ($other->isInfinity() && $this->isInfinity()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($other->isInfinity() || $this->isInfinity()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$math = $this->adapter;
|
||||
$equal = ($math->equals($this->x, $other->getX()));
|
||||
$equal &= ($math->equals($this->y, $other->getY()));
|
||||
$equal &= $this->isInfinity() == $other->isInfinity();
|
||||
$equal &= $this->curve->equals($other->getCurve());
|
||||
|
||||
if ($equal) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\PointInterface::equals()
|
||||
*/
|
||||
public function equals(PointInterface $other): bool
|
||||
{
|
||||
return $this->cmp($other) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\PointInterface::mul()
|
||||
*/
|
||||
public function mul(\GMP $n): PointInterface
|
||||
{
|
||||
if ($this->isInfinity()) {
|
||||
return $this->curve->getInfinity();
|
||||
}
|
||||
|
||||
$zero = gmp_init(0, 10);
|
||||
if ($this->adapter->cmp($this->order, $zero) > 0) {
|
||||
$n = $this->adapter->mod($n, $this->order);
|
||||
}
|
||||
|
||||
if ($this->adapter->equals($n, $zero)) {
|
||||
return $this->curve->getInfinity();
|
||||
}
|
||||
|
||||
/** @var Point[] $r */
|
||||
$r = [
|
||||
$this->curve->getInfinity(),
|
||||
clone $this
|
||||
];
|
||||
|
||||
$k = $this->curve->getSize();
|
||||
$n = str_pad($this->adapter->baseConvert($this->adapter->toString($n), 10, 2), $k, '0', STR_PAD_LEFT);
|
||||
|
||||
for ($i = 0; $i < $k; $i++) {
|
||||
$j = $n[$i];
|
||||
|
||||
$this->cswap($r[0], $r[1], $j ^ 1, $k);
|
||||
|
||||
$r[0] = $r[0]->add($r[1]);
|
||||
$r[1] = $r[1]->getDouble();
|
||||
|
||||
$this->cswap($r[0], $r[1], $j ^ 1, $k);
|
||||
}
|
||||
|
||||
$r[0]->validate();
|
||||
|
||||
return $r[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Point $a
|
||||
* @param Point $b
|
||||
* @param int $cond
|
||||
* @param int $curveSize
|
||||
*/
|
||||
private function cswap(self $a, self $b, int $cond, int $curveSize)
|
||||
{
|
||||
$this->cswapValue($a->x, $b->x, $cond, $curveSize);
|
||||
$this->cswapValue($a->y, $b->y, $cond, $curveSize);
|
||||
$this->cswapValue($a->order, $b->order, $cond, $curveSize);
|
||||
$this->cswapValue($a->infinity, $b->infinity, $cond, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|\GMP $a
|
||||
* @param bool|\GMP $b
|
||||
* @param int $cond
|
||||
* @param int $maskBitSize
|
||||
*/
|
||||
public function cswapValue(& $a, & $b, int $cond, int $maskBitSize)
|
||||
{
|
||||
$isGMP = is_object($a) && $a instanceof \GMP;
|
||||
|
||||
$sa = $isGMP ? $a : gmp_init(intval($a), 10);
|
||||
$sb = $isGMP ? $b : gmp_init(intval($b), 10);
|
||||
|
||||
$mask = str_pad('', $maskBitSize, (string) (1 - intval($cond)), STR_PAD_LEFT);
|
||||
$mask = gmp_init($mask, 2);
|
||||
|
||||
$taA = $this->adapter->bitwiseAnd($sa, $mask);
|
||||
$taB = $this->adapter->bitwiseAnd($sb, $mask);
|
||||
|
||||
$sa = $this->adapter->bitwiseXor($this->adapter->bitwiseXor($sa, $sb), $taB);
|
||||
$sb = $this->adapter->bitwiseXor($this->adapter->bitwiseXor($sa, $sb), $taA);
|
||||
$sa = $this->adapter->bitwiseXor($this->adapter->bitwiseXor($sa, $sb), $taB);
|
||||
|
||||
$a = $isGMP ? $sa : (bool) gmp_strval($sa, 10);
|
||||
$b = $isGMP ? $sb : (bool) gmp_strval($sb, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function validate()
|
||||
{
|
||||
if (! $this->infinity && ! $this->curve->contains($this->x, $this->y)) {
|
||||
throw new \RuntimeException('Invalid point');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\PointInterface::getDouble()
|
||||
* @return self
|
||||
*/
|
||||
public function getDouble(): PointInterface
|
||||
{
|
||||
if ($this->isInfinity()) {
|
||||
return $this->curve->getInfinity();
|
||||
}
|
||||
|
||||
$math = $this->adapter;
|
||||
$modMath = $this->modAdapter;
|
||||
|
||||
$a = $this->curve->getA();
|
||||
$threeX2 = $math->mul(gmp_init(3, 10), $math->pow($this->x, 2));
|
||||
|
||||
$tangent = $modMath->div(
|
||||
$math->add($threeX2, $a),
|
||||
$math->mul(gmp_init(2, 10), $this->y)
|
||||
);
|
||||
|
||||
$x3 = $modMath->sub(
|
||||
$math->pow($tangent, 2),
|
||||
$math->mul(gmp_init(2, 10), $this->x)
|
||||
);
|
||||
|
||||
$y3 = $modMath->sub(
|
||||
$math->mul($tangent, $math->sub($this->x, $x3)),
|
||||
$this->y
|
||||
);
|
||||
|
||||
return $this->curve->getPoint($x3, $y3, $this->order);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Primitives\PointInterface::__toString()
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
if ($this->infinity) {
|
||||
return '[ (infinity) on ' . (string) $this->curve . ' ]';
|
||||
}
|
||||
|
||||
return "[ (" . $this->adapter->toString($this->x) . "," . $this->adapter->toString($this->y) . ') on ' . (string) $this->curve . ' ]';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
$info = [
|
||||
'x' => $this->adapter->toString($this->x),
|
||||
'y' => $this->adapter->toString($this->y),
|
||||
'z' => $this->adapter->toString($this->order),
|
||||
'curve' => $this->curve
|
||||
];
|
||||
|
||||
if ($this->infinity) {
|
||||
$info['x'] = 'inf (' . $info['x'] . ')';
|
||||
$info['y'] = 'inf (' . $info['y'] . ')';
|
||||
$info['z'] = 'inf (' . $info['z'] . ')';
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Primitives;
|
||||
|
||||
/**
|
||||
* *********************************************************************
|
||||
* Copyright (C) 2012 Matyas Danter
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ***********************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is the contract for implementing Point, which encapsulates entities
|
||||
* and operations over the points on the Elliptic Curve. Implementations must be immutable.
|
||||
*
|
||||
* Implementors must be wary of the special "Infinity" implementation, which breaks LSP, and should always
|
||||
* be checked against (and properly handled) when receiving a PointInterface as an argument or when a method indicates it can
|
||||
* return infinity.
|
||||
*
|
||||
* @todo Fix LSP break (possibly derive an extra interface, FinitePointInterface from current one, and move
|
||||
* coordinate-related ops to sub-interface).
|
||||
*/
|
||||
interface PointInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns true if instance is an non-finite point.
|
||||
*/
|
||||
public function isInfinity(): bool;
|
||||
|
||||
/**
|
||||
* Adds another point to the current one and returns the resulting point.
|
||||
*
|
||||
* @param PointInterface $addend
|
||||
* @return PointInterface
|
||||
*/
|
||||
public function add(PointInterface $addend): PointInterface;
|
||||
|
||||
/**
|
||||
* Compares the current instance to another point.
|
||||
*
|
||||
* @param PointInterface $other
|
||||
* @return int A number different than 0 when current instance is less than the given point, 0 when they are equal.
|
||||
*/
|
||||
public function cmp(PointInterface $other): int;
|
||||
|
||||
/**
|
||||
* Checks whether the current instance is equal to the given point.
|
||||
*
|
||||
* @param PointInterface $other
|
||||
* @return bool true when points are equal, false otherwise.
|
||||
*/
|
||||
public function equals(PointInterface $other): bool;
|
||||
|
||||
/**
|
||||
* Multiplies the point by a scalar value and returns the resulting point.
|
||||
*
|
||||
* @param \GMP $multiplier
|
||||
* @return PointInterface
|
||||
*/
|
||||
public function mul(\GMP $multiplier): PointInterface;
|
||||
|
||||
/**
|
||||
* Returns the curve to which the point belongs.
|
||||
*
|
||||
* @return CurveFpInterface
|
||||
*/
|
||||
public function getCurve(): CurveFpInterface;
|
||||
|
||||
/**
|
||||
* Doubles the current point and returns the resulting point.
|
||||
*
|
||||
* @return PointInterface
|
||||
*/
|
||||
public function getDouble(): PointInterface;
|
||||
|
||||
/**
|
||||
* Returns the order of the point.
|
||||
*
|
||||
* @return \GMP
|
||||
*/
|
||||
public function getOrder(): \GMP;
|
||||
|
||||
/**
|
||||
* Returns the X coordinate of the point.
|
||||
*
|
||||
* @return \GMP
|
||||
*/
|
||||
public function getX(): \GMP;
|
||||
|
||||
/**
|
||||
* Returns the Y coordinate of the point.
|
||||
*
|
||||
* @return \GMP
|
||||
*/
|
||||
public function getY(): \GMP;
|
||||
|
||||
/**
|
||||
* Returns the string representation of the point.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Random;
|
||||
|
||||
class DebugDecorator implements RandomNumberGeneratorInterface
|
||||
{
|
||||
/**
|
||||
* @var RandomNumberGeneratorInterface
|
||||
*/
|
||||
private $generator;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $generatorName;
|
||||
|
||||
/**
|
||||
* @param RandomNumberGeneratorInterface $generator
|
||||
* @param string $name
|
||||
*/
|
||||
public function __construct(RandomNumberGeneratorInterface $generator, string $name)
|
||||
{
|
||||
$this->generator = $generator;
|
||||
$this->generatorName = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $max
|
||||
* @return \GMP
|
||||
*/
|
||||
public function generate(\GMP $max): \GMP
|
||||
{
|
||||
echo $this->generatorName.'::rand() = ';
|
||||
|
||||
$result = $this->generator->generate($max);
|
||||
|
||||
echo gmp_strval($result, 10).PHP_EOL;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Random;
|
||||
|
||||
use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface;
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Util\BinaryString;
|
||||
use Mdanter\Ecc\Util\NumberSize;
|
||||
|
||||
class HmacRandomNumberGenerator implements RandomNumberGeneratorInterface
|
||||
{
|
||||
/**
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $math;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $algorithm;
|
||||
|
||||
/**
|
||||
* @var PrivateKeyInterface
|
||||
*/
|
||||
private $privateKey;
|
||||
|
||||
/**
|
||||
* @var \GMP
|
||||
*/
|
||||
private $messageHash;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $algSize = array(
|
||||
'sha1' => 160,
|
||||
'sha224' => 224,
|
||||
'sha256' => 256,
|
||||
'sha384' => 384,
|
||||
'sha512' => 512
|
||||
);
|
||||
|
||||
/**
|
||||
* Hmac constructor.
|
||||
* @param GmpMathInterface $math
|
||||
* @param PrivateKeyInterface $privateKey
|
||||
* @param \GMP $messageHash - decimal hash of the message (*may* be truncated)
|
||||
* @param string $algorithm - hashing algorithm
|
||||
*/
|
||||
public function __construct(GmpMathInterface $math, PrivateKeyInterface $privateKey, \GMP $messageHash, string $algorithm)
|
||||
{
|
||||
if (!isset($this->algSize[$algorithm])) {
|
||||
throw new \InvalidArgumentException('Unsupported hashing algorithm');
|
||||
}
|
||||
|
||||
$this->math = $math;
|
||||
$this->algorithm = $algorithm;
|
||||
$this->privateKey = $privateKey;
|
||||
$this->messageHash = $messageHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $bits - binary string of bits
|
||||
* @param \GMP $qlen - length of q in bits
|
||||
* @return \GMP
|
||||
*/
|
||||
public function bits2int(string $bits, \GMP $qlen): \GMP
|
||||
{
|
||||
$vlen = gmp_init(BinaryString::length($bits) * 8, 10);
|
||||
$hex = bin2hex($bits);
|
||||
$v = gmp_init($hex, 16);
|
||||
|
||||
if ($this->math->cmp($vlen, $qlen) > 0) {
|
||||
$v = $this->math->rightShift($v, (int) $this->math->toString($this->math->sub($vlen, $qlen)));
|
||||
}
|
||||
|
||||
return $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $int
|
||||
* @param \GMP $rlen - rounded octet length
|
||||
* @return string
|
||||
*/
|
||||
public function int2octets(\GMP $int, \GMP $rlen): string
|
||||
{
|
||||
$out = pack("H*", $this->math->decHex(gmp_strval($int, 10)));
|
||||
$length = gmp_init(BinaryString::length($out), 10);
|
||||
if ($this->math->cmp($length, $rlen) < 0) {
|
||||
return str_pad('', (int) $this->math->toString($this->math->sub($rlen, $length)), "\x00") . $out;
|
||||
}
|
||||
|
||||
if ($this->math->cmp($length, $rlen) > 0) {
|
||||
return BinaryString::substring($out, 0, (int) $this->math->toString($rlen));
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $algorithm
|
||||
* @return int
|
||||
*/
|
||||
private function getHashLength(string $algorithm): int
|
||||
{
|
||||
return $this->algSize[$algorithm];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $q
|
||||
* @return \GMP
|
||||
*/
|
||||
public function generate(\GMP $q): \GMP
|
||||
{
|
||||
$qlen = gmp_init(NumberSize::bnNumBits($this->math, $q), 10);
|
||||
$rlen = $this->math->rightShift($this->math->add($qlen, gmp_init(7, 10)), 3);
|
||||
$hlen = $this->getHashLength($this->algorithm);
|
||||
$bx = $this->int2octets($this->privateKey->getSecret(), $rlen) . $this->int2octets($this->messageHash, $rlen);
|
||||
|
||||
$v = str_pad('', $hlen >> 3, "\x01", STR_PAD_LEFT);
|
||||
$k = str_pad('', $hlen >> 3, "\x00", STR_PAD_LEFT);
|
||||
|
||||
$k = hash_hmac($this->algorithm, $v . "\x00" . $bx, $k, true);
|
||||
$v = hash_hmac($this->algorithm, $v, $k, true);
|
||||
|
||||
$k = hash_hmac($this->algorithm, $v . "\x01" . $bx, $k, true);
|
||||
$v = hash_hmac($this->algorithm, $v, $k, true);
|
||||
|
||||
$t = '';
|
||||
for (;;) {
|
||||
$toff = gmp_init(0, 10);
|
||||
while ($this->math->cmp($toff, $rlen) < 0) {
|
||||
$v = hash_hmac($this->algorithm, $v, $k, true);
|
||||
|
||||
$cc = min(BinaryString::length($v), (int) gmp_strval(gmp_sub($rlen, $toff), 10));
|
||||
$t .= BinaryString::substring($v, 0, $cc);
|
||||
$toff = gmp_add($toff, $cc);
|
||||
}
|
||||
|
||||
$k = $this->bits2int($t, $qlen);
|
||||
if ($this->math->cmp($k, gmp_init(0, 10)) > 0 && $this->math->cmp($k, $q) < 0) {
|
||||
return $k;
|
||||
}
|
||||
|
||||
$k = hash_hmac($this->algorithm, $v . "\x00", $k, true);
|
||||
$v = hash_hmac($this->algorithm, $v, $k, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace Mdanter\Ecc\Random;
|
||||
|
||||
use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface;
|
||||
|
||||
use Mdanter\Ecc\Math\MathAdapterFactory;
|
||||
|
||||
class RandomGeneratorFactory
|
||||
{
|
||||
/**
|
||||
* @param bool $debug
|
||||
* @return DebugDecorator|RandomNumberGeneratorInterface
|
||||
*/
|
||||
public static function getRandomGenerator(bool $debug = false): RandomNumberGeneratorInterface
|
||||
{
|
||||
return self::wrapAdapter(
|
||||
new RandomNumberGenerator(
|
||||
MathAdapterFactory::getAdapter($debug)
|
||||
),
|
||||
'random_bytes',
|
||||
$debug
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PrivateKeyInterface $privateKey
|
||||
* @param \GMP $messageHash
|
||||
* @param string $algorithm
|
||||
* @param bool $debug
|
||||
* @return DebugDecorator|RandomNumberGeneratorInterface
|
||||
*/
|
||||
public static function getHmacRandomGenerator(PrivateKeyInterface $privateKey, \GMP $messageHash, string $algorithm, bool $debug = false): RandomNumberGeneratorInterface
|
||||
{
|
||||
return self::wrapAdapter(
|
||||
new HmacRandomNumberGenerator(
|
||||
MathAdapterFactory::getAdapter($debug),
|
||||
$privateKey,
|
||||
$messageHash,
|
||||
$algorithm
|
||||
),
|
||||
'rfc6979',
|
||||
$debug
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RandomNumberGeneratorInterface $generator
|
||||
* @param string $name
|
||||
* @param bool $debug
|
||||
* @return DebugDecorator|RandomNumberGeneratorInterface
|
||||
*/
|
||||
private static function wrapAdapter(RandomNumberGeneratorInterface $generator, string $name, bool $debug = false): RandomNumberGeneratorInterface
|
||||
{
|
||||
if ($debug === true) {
|
||||
return new DebugDecorator($generator, $name);
|
||||
}
|
||||
|
||||
return $generator;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Random;
|
||||
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Util\NumberSize;
|
||||
|
||||
class RandomNumberGenerator implements RandomNumberGeneratorInterface
|
||||
{
|
||||
/**
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* RandomNumberGenerator constructor.
|
||||
* @param GmpMathInterface $adapter
|
||||
*/
|
||||
public function __construct(GmpMathInterface $adapter)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GMP $max
|
||||
* @return \GMP
|
||||
*/
|
||||
public function generate(\GMP $max): \GMP
|
||||
{
|
||||
$numBits = NumberSize::bnNumBits($this->adapter, $max);
|
||||
$numBytes = (int) ceil($numBits / 8);
|
||||
// Generate an integer of size >= $numBits
|
||||
$bytes = random_bytes($numBytes);
|
||||
$value = $this->adapter->stringToInt($bytes);
|
||||
|
||||
$mask = gmp_sub(gmp_pow(2, $numBits), 1);
|
||||
$integer = gmp_and($value, $mask);
|
||||
|
||||
return $integer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Random;
|
||||
|
||||
interface RandomNumberGeneratorInterface
|
||||
{
|
||||
/**
|
||||
* Generate a random number between 0 and the specified upper boundary.
|
||||
* @param \GMP $max - Upper boundary, inclusive
|
||||
* @return \GMP
|
||||
*/
|
||||
public function generate(\GMP $max): \GMP;
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\Point;
|
||||
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Primitives\CurveFpInterface;
|
||||
use Mdanter\Ecc\Primitives\PointInterface;
|
||||
use Mdanter\Ecc\Serializer\Util\CurveOidMapper;
|
||||
|
||||
class CompressedPointSerializer implements PointSerializerInterface
|
||||
{
|
||||
/**
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* @var \Mdanter\Ecc\Math\NumberTheory
|
||||
*/
|
||||
private $theory;
|
||||
|
||||
/**
|
||||
* CompressedPointSerializer constructor.
|
||||
* @param GmpMathInterface $adapter
|
||||
*/
|
||||
public function __construct(GmpMathInterface $adapter)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
$this->theory = $adapter->getNumberTheory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PointInterface $point
|
||||
* @return string
|
||||
*/
|
||||
public function getPrefix(PointInterface $point): string
|
||||
{
|
||||
if ($this->adapter->equals($this->adapter->mod($point->getY(), gmp_init(2, 10)), gmp_init(0))) {
|
||||
return '02';
|
||||
} else {
|
||||
return '03';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PointInterface $point
|
||||
* @return string
|
||||
*/
|
||||
public function serialize(PointInterface $point): string
|
||||
{
|
||||
$length = CurveOidMapper::getByteSize($point->getCurve()) * 2;
|
||||
|
||||
$hexString = $this->getPrefix($point);
|
||||
$hexString .= str_pad(gmp_strval($point->getX(), 16), $length, '0', STR_PAD_LEFT);
|
||||
|
||||
return $hexString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CurveFpInterface $curve
|
||||
* @param string $data - hex serialized compressed point
|
||||
* @return PointInterface
|
||||
*/
|
||||
public function unserialize(CurveFpInterface $curve, string $data): PointInterface
|
||||
{
|
||||
$prefix = substr($data, 0, 2);
|
||||
if ($prefix !== '03' && $prefix !== '02') {
|
||||
throw new \InvalidArgumentException('Invalid data: only compressed keys are supported.');
|
||||
}
|
||||
|
||||
$x = gmp_init(substr($data, 2), 16);
|
||||
$y = $curve->recoverYfromX($prefix === '03', $x);
|
||||
|
||||
return $curve->getPoint($x, $y);
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\Point;
|
||||
|
||||
use Mdanter\Ecc\Primitives\PointInterface;
|
||||
use Mdanter\Ecc\Primitives\CurveFpInterface;
|
||||
|
||||
interface PointSerializerInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @param PointInterface $point
|
||||
* @return string
|
||||
*/
|
||||
public function serialize(PointInterface $point): string;
|
||||
|
||||
/**
|
||||
* @param CurveFpInterface $curve Curve that contains the serialized point
|
||||
* @param string $string
|
||||
* @return PointInterface
|
||||
*/
|
||||
public function unserialize(CurveFpInterface $curve, string $string): PointInterface;
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\Point;
|
||||
|
||||
use Mdanter\Ecc\Primitives\PointInterface;
|
||||
use Mdanter\Ecc\Primitives\CurveFpInterface;
|
||||
use Mdanter\Ecc\Serializer\Util\CurveOidMapper;
|
||||
use Mdanter\Ecc\Util\BinaryString;
|
||||
|
||||
class UncompressedPointSerializer implements PointSerializerInterface
|
||||
{
|
||||
/**
|
||||
* @param PointInterface $point
|
||||
* @return string
|
||||
*/
|
||||
public function serialize(PointInterface $point): string
|
||||
{
|
||||
$length = CurveOidMapper::getByteSize($point->getCurve()) * 2;
|
||||
|
||||
$hexString = '04';
|
||||
$hexString .= str_pad(gmp_strval($point->getX(), 16), $length, '0', STR_PAD_LEFT);
|
||||
$hexString .= str_pad(gmp_strval($point->getY(), 16), $length, '0', STR_PAD_LEFT);
|
||||
|
||||
return $hexString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CurveFpInterface $curve
|
||||
* @param string $data
|
||||
* @return PointInterface
|
||||
*/
|
||||
public function unserialize(CurveFpInterface $curve, string $data): PointInterface
|
||||
{
|
||||
if (BinaryString::substring($data, 0, 2) != '04') {
|
||||
throw new \InvalidArgumentException('Invalid data: only uncompressed keys are supported.');
|
||||
}
|
||||
|
||||
$data = BinaryString::substring($data, 2);
|
||||
$dataLength = BinaryString::length($data);
|
||||
|
||||
$x = gmp_init(BinaryString::substring($data, 0, $dataLength / 2), 16);
|
||||
$y = gmp_init(BinaryString::substring($data, $dataLength / 2), 16);
|
||||
|
||||
return $curve->getPoint($x, $y);
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\PrivateKey;
|
||||
|
||||
use FG\ASN1\ASNObject;
|
||||
use FG\ASN1\Universal\Sequence;
|
||||
use FG\ASN1\Universal\Integer;
|
||||
use FG\ASN1\Universal\BitString;
|
||||
use FG\ASN1\Universal\OctetString;
|
||||
use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface;
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Math\MathAdapterFactory;
|
||||
use Mdanter\Ecc\Serializer\Util\CurveOidMapper;
|
||||
use Mdanter\Ecc\Serializer\PublicKey\DerPublicKeySerializer;
|
||||
use FG\ASN1\ExplicitlyTaggedObject;
|
||||
|
||||
/**
|
||||
* PEM Private key formatter
|
||||
*
|
||||
* @link https://tools.ietf.org/html/rfc5915
|
||||
*/
|
||||
class DerPrivateKeySerializer implements PrivateKeySerializerInterface
|
||||
{
|
||||
|
||||
const VERSION = 1;
|
||||
|
||||
/**
|
||||
* @var GmpMathInterface|null
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* @var DerPublicKeySerializer
|
||||
*/
|
||||
private $pubKeySerializer;
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface $adapter
|
||||
* @param DerPublicKeySerializer $pubKeySerializer
|
||||
*/
|
||||
public function __construct(GmpMathInterface $adapter = null, DerPublicKeySerializer $pubKeySerializer = null)
|
||||
{
|
||||
$this->adapter = $adapter ?: MathAdapterFactory::getAdapter();
|
||||
$this->pubKeySerializer = $pubKeySerializer ?: new DerPublicKeySerializer($this->adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Serializer\PrivateKey\PrivateKeySerializerInterface::serialize()
|
||||
*/
|
||||
public function serialize(PrivateKeyInterface $key): string
|
||||
{
|
||||
$privateKeyInfo = new Sequence(
|
||||
new Integer(self::VERSION),
|
||||
new OctetString($this->formatKey($key)),
|
||||
new ExplicitlyTaggedObject(0, CurveOidMapper::getCurveOid($key->getPoint()->getCurve())),
|
||||
new ExplicitlyTaggedObject(1, $this->encodePubKey($key))
|
||||
);
|
||||
|
||||
return $privateKeyInfo->getBinary();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PrivateKeyInterface $key
|
||||
* @return BitString
|
||||
*/
|
||||
private function encodePubKey(PrivateKeyInterface $key): BitString
|
||||
{
|
||||
return new BitString(
|
||||
$this->pubKeySerializer->getUncompressedKey($key->getPublicKey())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PrivateKeyInterface $key
|
||||
* @return string
|
||||
*/
|
||||
private function formatKey(PrivateKeyInterface $key): string
|
||||
{
|
||||
return gmp_strval($key->getSecret(), 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Serializer\PrivateKey\PrivateKeySerializerInterface::parse()
|
||||
* @throws \FG\ASN1\Exception\ParserException
|
||||
*/
|
||||
public function parse(string $data): PrivateKeyInterface
|
||||
{
|
||||
$asnObject = ASNObject::fromBinary($data);
|
||||
|
||||
if (! ($asnObject instanceof Sequence) || $asnObject->getNumberofChildren() !== 4) {
|
||||
throw new \RuntimeException('Invalid data.');
|
||||
}
|
||||
|
||||
$children = $asnObject->getChildren();
|
||||
|
||||
$version = $children[0];
|
||||
|
||||
if ($version->getContent() != 1) {
|
||||
throw new \RuntimeException('Invalid data: only version 1 (RFC5915) keys are supported.');
|
||||
}
|
||||
|
||||
$key = gmp_init($children[1]->getContent(), 16);
|
||||
$oid = $children[2]->getContent()[0];
|
||||
$generator = CurveOidMapper::getGeneratorFromOid($oid);
|
||||
|
||||
return $generator->getPrivateKeyFrom($key);
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\PrivateKey;
|
||||
|
||||
use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface;
|
||||
|
||||
/**
|
||||
* PEM Private key formatter
|
||||
*
|
||||
* @link https://tools.ietf.org/html/rfc5915
|
||||
*/
|
||||
class PemPrivateKeySerializer implements PrivateKeySerializerInterface
|
||||
{
|
||||
/**
|
||||
* @var DerPrivateKeySerializer
|
||||
*/
|
||||
private $derSerializer;
|
||||
|
||||
/**
|
||||
* @param DerPrivateKeySerializer $derSerializer
|
||||
*/
|
||||
public function __construct(DerPrivateKeySerializer $derSerializer)
|
||||
{
|
||||
$this->derSerializer = $derSerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Serializer\PrivateKey\PrivateKeySerializerInterface::serialize()
|
||||
*/
|
||||
public function serialize(PrivateKeyInterface $key): string
|
||||
{
|
||||
$privateKeyInfo = $this->derSerializer->serialize($key);
|
||||
|
||||
$content = '-----BEGIN EC PRIVATE KEY-----'.PHP_EOL;
|
||||
$content .= trim(chunk_split(base64_encode($privateKeyInfo), 64, PHP_EOL)).PHP_EOL;
|
||||
$content .= '-----END EC PRIVATE KEY-----';
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Serializer\PrivateKey\PrivateKeySerializerInterface::parse()
|
||||
*/
|
||||
public function parse(string $formattedKey): PrivateKeyInterface
|
||||
{
|
||||
$formattedKey = str_replace('-----BEGIN EC PRIVATE KEY-----', '', $formattedKey);
|
||||
$formattedKey = str_replace('-----END EC PRIVATE KEY-----', '', $formattedKey);
|
||||
|
||||
$data = base64_decode($formattedKey);
|
||||
|
||||
return $this->derSerializer->parse($data);
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\PrivateKey;
|
||||
|
||||
use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface;
|
||||
|
||||
interface PrivateKeySerializerInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @param PrivateKeyInterface $key
|
||||
* @return string
|
||||
*/
|
||||
public function serialize(PrivateKeyInterface $key): string;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $formattedKey
|
||||
* @return PrivateKeyInterface
|
||||
*/
|
||||
public function parse(string $formattedKey): PrivateKeyInterface;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\PublicKey\Der;
|
||||
|
||||
use FG\ASN1\Universal\Sequence;
|
||||
use FG\ASN1\Universal\ObjectIdentifier;
|
||||
use FG\ASN1\Universal\BitString;
|
||||
use Mdanter\Ecc\Primitives\PointInterface;
|
||||
use Mdanter\Ecc\Crypto\Key\PublicKeyInterface;
|
||||
use Mdanter\Ecc\Curves\NamedCurveFp;
|
||||
use Mdanter\Ecc\Serializer\Util\CurveOidMapper;
|
||||
use Mdanter\Ecc\Serializer\PublicKey\DerPublicKeySerializer;
|
||||
use Mdanter\Ecc\Serializer\Point\PointSerializerInterface;
|
||||
use Mdanter\Ecc\Serializer\Point\UncompressedPointSerializer;
|
||||
|
||||
class Formatter
|
||||
{
|
||||
/**
|
||||
* @var UncompressedPointSerializer
|
||||
*/
|
||||
private $pointSerializer;
|
||||
|
||||
/**
|
||||
* Formatter constructor.
|
||||
* @param PointSerializerInterface|null $pointSerializer
|
||||
*/
|
||||
public function __construct(PointSerializerInterface $pointSerializer = null)
|
||||
{
|
||||
$this->pointSerializer = $pointSerializer ?: new UncompressedPointSerializer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PublicKeyInterface $key
|
||||
* @return string
|
||||
*/
|
||||
public function format(PublicKeyInterface $key): string
|
||||
{
|
||||
if (! ($key->getCurve() instanceof NamedCurveFp)) {
|
||||
throw new \RuntimeException('Not implemented for unnamed curves');
|
||||
}
|
||||
|
||||
$sequence = new Sequence(
|
||||
new Sequence(
|
||||
new ObjectIdentifier(DerPublicKeySerializer::X509_ECDSA_OID),
|
||||
CurveOidMapper::getCurveOid($key->getCurve())
|
||||
),
|
||||
new BitString($this->encodePoint($key->getPoint()))
|
||||
);
|
||||
|
||||
return $sequence->getBinary();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PointInterface $point
|
||||
* @return string
|
||||
*/
|
||||
public function encodePoint(PointInterface $point): string
|
||||
{
|
||||
return $this->pointSerializer->serialize($point);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\PublicKey\Der;
|
||||
|
||||
use FG\ASN1\ASNObject;
|
||||
use FG\ASN1\Identifier;
|
||||
use FG\ASN1\Universal\Sequence;
|
||||
use Mdanter\Ecc\Crypto\Key\PublicKeyInterface;
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Serializer\Util\CurveOidMapper;
|
||||
use Mdanter\Ecc\Primitives\GeneratorPoint;
|
||||
use Mdanter\Ecc\Serializer\PublicKey\DerPublicKeySerializer;
|
||||
use Mdanter\Ecc\Serializer\Point\PointSerializerInterface;
|
||||
use Mdanter\Ecc\Serializer\Point\UncompressedPointSerializer;
|
||||
use Mdanter\Ecc\Crypto\Key\PublicKey;
|
||||
|
||||
class Parser
|
||||
{
|
||||
|
||||
/**
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
* @var UncompressedPointSerializer
|
||||
*/
|
||||
private $pointSerializer;
|
||||
|
||||
/**
|
||||
* Parser constructor.
|
||||
* @param GmpMathInterface $adapter
|
||||
* @param PointSerializerInterface|null $pointSerializer
|
||||
*/
|
||||
public function __construct(GmpMathInterface $adapter, PointSerializerInterface $pointSerializer = null)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
$this->pointSerializer = $pointSerializer ?: new UncompressedPointSerializer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $binaryData
|
||||
* @return PublicKeyInterface
|
||||
* @throws \FG\ASN1\Exception\ParserException
|
||||
*/
|
||||
public function parse(string $binaryData): PublicKeyInterface
|
||||
{
|
||||
$asnObject = ASNObject::fromBinary($binaryData);
|
||||
if ($asnObject->getType() !== Identifier::SEQUENCE) {
|
||||
throw new \RuntimeException('Invalid data.');
|
||||
}
|
||||
|
||||
/** @var Sequence $asnObject */
|
||||
if ($asnObject->getNumberofChildren() != 2) {
|
||||
throw new \RuntimeException('Invalid data.');
|
||||
}
|
||||
|
||||
$children = $asnObject->getChildren();
|
||||
if (count($children) != 2) {
|
||||
throw new \RuntimeException('Invalid data.');
|
||||
}
|
||||
|
||||
if (count($children) != 2) {
|
||||
throw new \RuntimeException('Invalid data.');
|
||||
}
|
||||
|
||||
if ($children[0]->getType() !== Identifier::SEQUENCE) {
|
||||
throw new \RuntimeException('Invalid data.');
|
||||
}
|
||||
|
||||
if (count($children[0]->getChildren()) != 2) {
|
||||
throw new \RuntimeException('Invalid data.');
|
||||
}
|
||||
|
||||
if ($children[0]->getChildren()[0]->getType() !== Identifier::OBJECT_IDENTIFIER) {
|
||||
throw new \RuntimeException('Invalid data.');
|
||||
}
|
||||
|
||||
if ($children[0]->getChildren()[1]->getType() !== Identifier::OBJECT_IDENTIFIER) {
|
||||
throw new \RuntimeException('Invalid data.');
|
||||
}
|
||||
|
||||
if ($children[1]->getType() !== Identifier::BITSTRING) {
|
||||
throw new \RuntimeException('Invalid data.');
|
||||
}
|
||||
|
||||
$oid = $children[0]->getChildren()[0];
|
||||
$curveOid = $children[0]->getChildren()[1];
|
||||
$encodedKey = $children[1];
|
||||
if ($oid->getContent() !== DerPublicKeySerializer::X509_ECDSA_OID) {
|
||||
throw new \RuntimeException('Invalid data: non X509 data.');
|
||||
}
|
||||
|
||||
$generator = CurveOidMapper::getGeneratorFromOid($curveOid);
|
||||
|
||||
return $this->parseKey($generator, $encodedKey->getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GeneratorPoint $generator
|
||||
* @param string $data
|
||||
* @return PublicKeyInterface
|
||||
*/
|
||||
public function parseKey(GeneratorPoint $generator, string $data): PublicKeyInterface
|
||||
{
|
||||
$point = $this->pointSerializer->unserialize($generator->getCurve(), $data);
|
||||
|
||||
return new PublicKey($this->adapter, $generator, $point);
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\PublicKey;
|
||||
|
||||
use Mdanter\Ecc\Crypto\Key\PublicKeyInterface;
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
use Mdanter\Ecc\Math\MathAdapterFactory;
|
||||
use Mdanter\Ecc\Serializer\Point\PointSerializerInterface;
|
||||
use Mdanter\Ecc\Serializer\Point\UncompressedPointSerializer;
|
||||
use Mdanter\Ecc\Serializer\PublicKey\Der\Formatter;
|
||||
use Mdanter\Ecc\Serializer\PublicKey\Der\Parser;
|
||||
|
||||
/**
|
||||
*
|
||||
* @link https://tools.ietf.org/html/rfc5480#page-3
|
||||
* @todo: review for full spec, should we support all prefixes here?
|
||||
*/
|
||||
class DerPublicKeySerializer implements PublicKeySerializerInterface
|
||||
{
|
||||
|
||||
const X509_ECDSA_OID = '1.2.840.10045.2.1';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var GmpMathInterface
|
||||
*/
|
||||
private $adapter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var Formatter
|
||||
*/
|
||||
private $formatter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var Parser
|
||||
*/
|
||||
private $parser;
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface|null $adapter
|
||||
* @param PointSerializerInterface|null $pointSerializer
|
||||
*/
|
||||
public function __construct(GmpMathInterface $adapter = null, PointSerializerInterface $pointSerializer = null)
|
||||
{
|
||||
$this->adapter = $adapter ?: MathAdapterFactory::getAdapter();
|
||||
|
||||
$this->formatter = new Formatter();
|
||||
$this->parser = new Parser($this->adapter, $pointSerializer ?: new UncompressedPointSerializer());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param PublicKeyInterface $key
|
||||
* @return string
|
||||
*/
|
||||
public function serialize(PublicKeyInterface $key): string
|
||||
{
|
||||
return $this->formatter->format($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PublicKeyInterface $key
|
||||
* @return string
|
||||
*/
|
||||
public function getUncompressedKey(PublicKeyInterface $key): string
|
||||
{
|
||||
return $this->formatter->encodePoint($key->getPoint());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Serializer\PublicKey\PublicKeySerializerInterface::parse()
|
||||
*/
|
||||
public function parse(string $string): PublicKeyInterface
|
||||
{
|
||||
return $this->parser->parse($string);
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\PublicKey;
|
||||
|
||||
use Mdanter\Ecc\Crypto\Key\PublicKeyInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
* @link https://tools.ietf.org/html/rfc5480#page-3
|
||||
*/
|
||||
class PemPublicKeySerializer implements PublicKeySerializerInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var DerPublicKeySerializer
|
||||
*/
|
||||
private $derSerializer;
|
||||
|
||||
/**
|
||||
* @param DerPublicKeySerializer $serializer
|
||||
*/
|
||||
public function __construct(DerPublicKeySerializer $serializer)
|
||||
{
|
||||
$this->derSerializer = $serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Serializer\PublicKey\PublicKeySerializerInterface::serialize()
|
||||
*/
|
||||
public function serialize(PublicKeyInterface $key): string
|
||||
{
|
||||
$publicKeyInfo = $this->derSerializer->serialize($key);
|
||||
|
||||
$content = '-----BEGIN PUBLIC KEY-----'.PHP_EOL;
|
||||
$content .= trim(chunk_split(base64_encode($publicKeyInfo), 64, PHP_EOL)).PHP_EOL;
|
||||
$content .= '-----END PUBLIC KEY-----';
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see \Mdanter\Ecc\Serializer\PublicKey\PublicKeySerializerInterface::parse()
|
||||
*/
|
||||
public function parse(string $formattedKey): PublicKeyInterface
|
||||
{
|
||||
$formattedKey = str_replace('-----BEGIN PUBLIC KEY-----', '', $formattedKey);
|
||||
$formattedKey = str_replace('-----END PUBLIC KEY-----', '', $formattedKey);
|
||||
|
||||
$data = base64_decode($formattedKey);
|
||||
|
||||
return $this->derSerializer->parse($data);
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\PublicKey;
|
||||
|
||||
use Mdanter\Ecc\Crypto\Key\PublicKeyInterface;
|
||||
|
||||
interface PublicKeySerializerInterface
|
||||
{
|
||||
/**
|
||||
* @param PublicKeyInterface $key
|
||||
* @return string
|
||||
*/
|
||||
public function serialize(PublicKeyInterface $key): string;
|
||||
|
||||
/**
|
||||
* @param string $formattedKey
|
||||
* @return PublicKeyInterface
|
||||
*/
|
||||
public function parse(string $formattedKey): PublicKeyInterface;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\Signature\Der;
|
||||
|
||||
use FG\ASN1\Universal\Integer;
|
||||
use FG\ASN1\Universal\Sequence;
|
||||
use Mdanter\Ecc\Crypto\Signature\SignatureInterface;
|
||||
|
||||
class Formatter
|
||||
{
|
||||
/**
|
||||
* @param SignatureInterface $signature
|
||||
* @return Sequence
|
||||
*/
|
||||
public function toAsn(SignatureInterface $signature): Sequence
|
||||
{
|
||||
return new Sequence(
|
||||
new Integer(gmp_strval($signature->getR(), 10)),
|
||||
new Integer(gmp_strval($signature->getS(), 10))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SignatureInterface $signature
|
||||
* @return string
|
||||
*/
|
||||
public function serialize(SignatureInterface $signature): string
|
||||
{
|
||||
return $this->toAsn($signature)->getBinary();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\Signature\Der;
|
||||
|
||||
use FG\ASN1\ASNObject;
|
||||
use FG\ASN1\Identifier;
|
||||
use FG\ASN1\Universal\Integer;
|
||||
use Mdanter\Ecc\Crypto\Signature\Signature;
|
||||
use Mdanter\Ecc\Crypto\Signature\SignatureInterface;
|
||||
use Mdanter\Ecc\Exception\SignatureDecodeException;
|
||||
|
||||
class Parser
|
||||
{
|
||||
/**
|
||||
* @param string $binary
|
||||
* @return SignatureInterface
|
||||
* @throws \FG\ASN1\Exception\ParserException
|
||||
*/
|
||||
public function parse(string $binary): SignatureInterface
|
||||
{
|
||||
$offsetIndex = 0;
|
||||
$asnObject = ASNObject::fromBinary($binary, $offsetIndex);
|
||||
|
||||
if ($offsetIndex != strlen($binary)) {
|
||||
throw new SignatureDecodeException('Invalid data.');
|
||||
}
|
||||
|
||||
// Set inherits from Sequence, so use getType!
|
||||
if ($asnObject->getType() !== Identifier::SEQUENCE) {
|
||||
throw new SignatureDecodeException('Invalid tag for sequence.');
|
||||
}
|
||||
|
||||
if ($asnObject->getNumberofChildren() !== 2) {
|
||||
throw new SignatureDecodeException('Invalid data.');
|
||||
}
|
||||
|
||||
if (!($asnObject[0] instanceof Integer && $asnObject[1] instanceof Integer)) {
|
||||
throw new SignatureDecodeException('Invalid data.');
|
||||
}
|
||||
|
||||
return new Signature(
|
||||
gmp_init($asnObject[0]->getContent(), 10),
|
||||
gmp_init($asnObject[1]->getContent(), 10)
|
||||
);
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\Signature;
|
||||
|
||||
use Mdanter\Ecc\Crypto\Signature\SignatureInterface;
|
||||
|
||||
class DerSignatureSerializer implements DerSignatureSerializerInterface
|
||||
{
|
||||
/**
|
||||
* @var Der\Parser
|
||||
*/
|
||||
private $parser;
|
||||
|
||||
/**
|
||||
* @var Der\Formatter
|
||||
*/
|
||||
private $formatter;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->parser = new Der\Parser();
|
||||
$this->formatter = new Der\Formatter();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SignatureInterface $signature
|
||||
* @return string
|
||||
*/
|
||||
public function serialize(SignatureInterface $signature): string
|
||||
{
|
||||
return $this->formatter->serialize($signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $binary
|
||||
* @return SignatureInterface
|
||||
* @throws \FG\ASN1\Exception\ParserException
|
||||
*/
|
||||
public function parse(string $binary): SignatureInterface
|
||||
{
|
||||
return $this->parser->parse($binary);
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\Signature;
|
||||
|
||||
use Mdanter\Ecc\Crypto\Signature\SignatureInterface;
|
||||
|
||||
interface DerSignatureSerializerInterface
|
||||
{
|
||||
/**
|
||||
* @param SignatureInterface $signature
|
||||
* @return string
|
||||
*/
|
||||
public function serialize(SignatureInterface $signature): string;
|
||||
|
||||
/**
|
||||
* @param string $binary
|
||||
* @return SignatureInterface
|
||||
* @throws \FG\ASN1\Exception\ParserException
|
||||
*/
|
||||
public function parse(string $binary): SignatureInterface;
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Serializer\Util;
|
||||
|
||||
use FG\ASN1\Universal\ObjectIdentifier;
|
||||
use Mdanter\Ecc\Curves\NamedCurveFp;
|
||||
use Mdanter\Ecc\Curves\CurveFactory;
|
||||
use Mdanter\Ecc\Curves\NistCurve;
|
||||
use Mdanter\Ecc\Curves\SecgCurve;
|
||||
use Mdanter\Ecc\Exception\UnsupportedCurveException;
|
||||
use Mdanter\Ecc\Primitives\CurveFpInterface;
|
||||
use Mdanter\Ecc\Primitives\GeneratorPoint;
|
||||
|
||||
class CurveOidMapper
|
||||
{
|
||||
|
||||
const NIST_P192_OID = '1.2.840.10045.3.1.1';
|
||||
|
||||
const NIST_P224_OID = '1.3.132.0.33';
|
||||
|
||||
const NIST_P256_OID = '1.2.840.10045.3.1.7';
|
||||
|
||||
const NIST_P384_OID = '1.3.132.0.34';
|
||||
|
||||
const NIST_P521_OID = '1.3.132.0.35';
|
||||
|
||||
const SECP_112R1_OID = '1.3.132.0.6';
|
||||
|
||||
const SECP_192K1_OID = '1.3.132.0.31';
|
||||
|
||||
const SECP_256K1_OID = '1.3.132.0.10';
|
||||
|
||||
const SECP_256R1_OID = '1.2.840.10045.3.1.7';
|
||||
|
||||
const SECP_384R1_OID = '1.3.132.0.34';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $oidMap = array(
|
||||
NistCurve::NAME_P192 => self::NIST_P192_OID,
|
||||
NistCurve::NAME_P224 => self::NIST_P224_OID,
|
||||
NistCurve::NAME_P256 => self::NIST_P256_OID,
|
||||
NistCurve::NAME_P384 => self::NIST_P384_OID,
|
||||
NistCurve::NAME_P521 => self::NIST_P521_OID,
|
||||
SecgCurve::NAME_SECP_112R1 => self::SECP_112R1_OID,
|
||||
SecgCurve::NAME_SECP_192K1 => self::SECP_192K1_OID,
|
||||
SecgCurve::NAME_SECP_256K1 => self::SECP_256K1_OID,
|
||||
SecgCurve::NAME_SECP_256R1 => self::SECP_256R1_OID,
|
||||
SecgCurve::NAME_SECP_384R1 => self::SECP_384R1_OID,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $sizeMap = array(
|
||||
NistCurve::NAME_P192 => 24,
|
||||
NistCurve::NAME_P224 => 28,
|
||||
NistCurve::NAME_P256 => 32,
|
||||
NistCurve::NAME_P384 => 48,
|
||||
NistCurve::NAME_P521 => 66,
|
||||
SecgCurve::NAME_SECP_112R1 => 14,
|
||||
SecgCurve::NAME_SECP_192K1 => 24,
|
||||
SecgCurve::NAME_SECP_256K1 => 32,
|
||||
SecgCurve::NAME_SECP_256R1 => 32,
|
||||
SecgCurve::NAME_SECP_384R1 => 48,
|
||||
);
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getNames(): array
|
||||
{
|
||||
return array_keys(self::$oidMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CurveFpInterface $curve
|
||||
* @return int
|
||||
*/
|
||||
public static function getByteSize(CurveFpInterface $curve): int
|
||||
{
|
||||
if ($curve instanceof NamedCurveFp && array_key_exists($curve->getName(), self::$sizeMap)) {
|
||||
return self::$sizeMap[$curve->getName()];
|
||||
}
|
||||
|
||||
throw new UnsupportedCurveException('Unsupported curve type');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NamedCurveFp $curve
|
||||
* @return ObjectIdentifier
|
||||
*/
|
||||
public static function getCurveOid(NamedCurveFp $curve): ObjectIdentifier
|
||||
{
|
||||
if (array_key_exists($curve->getName(), self::$oidMap)) {
|
||||
$oidString = self::$oidMap[$curve->getName()];
|
||||
|
||||
return new ObjectIdentifier($oidString);
|
||||
}
|
||||
|
||||
throw new UnsupportedCurveException('Unsupported curve type');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectIdentifier $oid
|
||||
* @return NamedCurveFp
|
||||
*/
|
||||
public static function getCurveFromOid(ObjectIdentifier $oid): NamedCurveFp
|
||||
{
|
||||
$oidString = $oid->getContent();
|
||||
$invertedMap = array_flip(self::$oidMap);
|
||||
|
||||
if (array_key_exists($oidString, $invertedMap)) {
|
||||
return CurveFactory::getCurveByName($invertedMap[$oidString]);
|
||||
}
|
||||
|
||||
$error = new UnsupportedCurveException('Invalid data: unsupported curve.');
|
||||
$error->setOid($oidString);
|
||||
throw $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectIdentifier $oid
|
||||
* @return GeneratorPoint
|
||||
*/
|
||||
public static function getGeneratorFromOid(ObjectIdentifier $oid): GeneratorPoint
|
||||
{
|
||||
$oidString = $oid->getContent();
|
||||
$invertedMap = array_flip(self::$oidMap);
|
||||
|
||||
if (array_key_exists($oidString, $invertedMap)) {
|
||||
return CurveFactory::getGeneratorByName($invertedMap[$oidString]);
|
||||
}
|
||||
|
||||
$error = new UnsupportedCurveException('Invalid data: unsupported generator.');
|
||||
$error->setOid($oidString);
|
||||
throw $error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Util;
|
||||
|
||||
class BinaryString
|
||||
{
|
||||
/**
|
||||
* Multi-byte-safe string length calculation
|
||||
*
|
||||
* @param string $str
|
||||
* @return int
|
||||
*/
|
||||
public static function length(string $str): int
|
||||
{
|
||||
// Premature optimization: cache the function_exists() result
|
||||
static $exists = null;
|
||||
if ($exists === null) {
|
||||
$exists = function_exists('mb_strlen');
|
||||
}
|
||||
|
||||
// If it exists, we need to make sure we're using 8bit mode
|
||||
if ($exists) {
|
||||
return mb_strlen($str, '8bit');
|
||||
}
|
||||
return strlen($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-byte-safe substring calculation
|
||||
*
|
||||
* @param string $str
|
||||
* @param int $start
|
||||
* @param int $length (optional)
|
||||
* @return string
|
||||
*/
|
||||
public static function substring(string $str, int $start = 0, int $length = null): string
|
||||
{
|
||||
// Premature optimization: cache the function_exists() result
|
||||
static $exists = null;
|
||||
if ($exists === null) {
|
||||
$exists = function_exists('mb_substr');
|
||||
}
|
||||
|
||||
// If it exists, we need to make sure we're using 8bit mode
|
||||
if ($exists) {
|
||||
return mb_substr($str, $start, $length, '8bit');
|
||||
} elseif ($length !== null) {
|
||||
return substr($str, $start, $length);
|
||||
}
|
||||
return substr($str, $start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to hash_equals() in PHP 5.6
|
||||
*
|
||||
* @param string $knownString
|
||||
* @param string $userString
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function constantTimeCompare(string $knownString, string $userString): bool
|
||||
{
|
||||
return hash_equals($knownString, $userString);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mdanter\Ecc\Util;
|
||||
|
||||
use Mdanter\Ecc\Math\GmpMathInterface;
|
||||
|
||||
class NumberSize
|
||||
{
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface $adapter
|
||||
* @param \GMP $x
|
||||
* @return float
|
||||
*/
|
||||
public static function getCeiledByteSize(GmpMathInterface $adapter, \GMP $x): float
|
||||
{
|
||||
$log2 = self::bnNumBits($adapter, $x);
|
||||
return ceil($log2 / 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GmpMathInterface $adapter
|
||||
* @param \GMP $x
|
||||
* @return float
|
||||
*/
|
||||
public static function getFlooredByteSize(GmpMathInterface $adapter, \GMP $x): float
|
||||
{
|
||||
$log2 = self::bnNumBits($adapter, $x);
|
||||
return floor($log2 / 8) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of minimum required bytes to store a given number. Non-significant upper bits are not counted.
|
||||
*
|
||||
* @param GmpMathInterface $adapter
|
||||
* @param \GMP $x
|
||||
* @return int
|
||||
*
|
||||
* @link https://www.openssl.org/docs/crypto/BN_num_bytes.html
|
||||
*/
|
||||
public static function bnNumBytes(GmpMathInterface $adapter, \GMP $x): int
|
||||
{
|
||||
// https://github.com/luvit/openssl/blob/master/openssl/crypto/bn/bn.h#L402
|
||||
return (int) floor((self::bnNumBits($adapter, $x) + 7) / 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bits used to store this number. Non-significant upper bits are not counted.
|
||||
*
|
||||
* @param GmpMathInterface $adapter
|
||||
* @param \GMP $x
|
||||
* @return int
|
||||
*
|
||||
* @link https://www.openssl.org/docs/crypto/BN_num_bytes.html
|
||||
*/
|
||||
public static function bnNumBits(GmpMathInterface $adapter, \GMP $x): int
|
||||
{
|
||||
$zero = gmp_init(0, 10);
|
||||
if ($adapter->equals($x, $zero)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$log2 = 0;
|
||||
while (false === $adapter->equals($x, $zero)) {
|
||||
$x = $adapter->rightShift($x, 1);
|
||||
$log2++;
|
||||
}
|
||||
|
||||
return $log2 ;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
for i in examples/*.php; do
|
||||
php $i
|
||||
if [ $? != 0 ]; then
|
||||
echo "Error running example code";
|
||||
exit -1
|
||||
fi;
|
||||
done
|
||||
Reference in New Issue
Block a user