.gitattributes 0000644 00000000146 15073675000 0007443 0 ustar 00 /doc export-ignore
/examples export-ignore
/tests export-ignore
.gitignore 0000755 00000000044 15073675000 0006540 0 ustar 00 tests/output
vendor/
composer.lock
.gitmodules 0000644 00000000162 15073675000 0006723 0 ustar 00 [submodule "tests/import/wycheproof"]
path = tests/import/wycheproof
url = https://github.com/google/wycheproof
.scrutinizer.yml 0000644 00000004771 15073675000 0007742 0 ustar 00 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
.travis.yml 0000644 00000002466 15073675000 0006670 0 ustar 00 language: php
php:
- 7.0
- 7.1
- 7.2
- nightly
env:
# The default env behaviour: phpunit, no coverage, no style checks
- PHPUNIT=true
matrix:
fast_finish: true
exclude:
# Disable standard phpunit test on 7.0, reenable with FULL, and with COVERAGE
- php: 7.2
env: PHPUNIT=true
include:
# This reenables php7.0/phpunit test with COVERAGE
- php: 7.2
env: PHPUNIT=true COVERAGE_SHARDS=5 COVERAGE=true
# This reenables php7.0/phpunit test with extended tests (wycheproof)
- php: 7.2
env: FULL=true
# This runs phpcs on php7.0 - only code style and examples
- php: 7.0
env: CODESTYLE=true EXAMPLES=true
sudo: false
before_script:
- travis_retry composer selfupdate
- travis_retry composer install --prefer-source
- if [ "${COVERAGE}" != "true" ] && [ "${TRAVIS_PHP_VERSION}" != "nightly" ]; then phpenv config-rm xdebug.ini && echo "xdebug disabled"; fi
script:
- if [ "${PHPUNIT}" = "true" ]; then make phpunit-ci; fi
- if [ "${FULL}" = "true" ]; then make phpunit-full-ci; fi
- if [ "${CODESTYLE}" = "true" ]; then make phpcs && echo "PHPCS OK"; fi
- if [ "${EXAMPLES}" = "true" ]; then make test-examples && echo "Examples OK"; fi
after_success:
- if [ "${COVERAGE}" = "true" ]; then make scrutinizer; fi
Makefile 0000644 00000002516 15073675000 0006213 0 ustar 00 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/
README.md 0000644 00000005217 15073675000 0006033 0 ustar 00 ## 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+
* composer
* ext-gmp
For PHP5.6 compatibility, use the v0.4.x releases.
### Installation
You can install this library via Composer :
`composer require mdanter/ecc`
### 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)
composer.json 0000644 00000002513 15073675000 0007272 0 ustar 00 {
"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",
"ext-gmp": "*",
"fgrosse/phpasn1": "^2.0"
},
"require-dev": {
"phpunit/phpunit": "^6.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"
}
}
}
phpunit.full.xml 0000644 00000002242 15073675000 0007721 0 ustar 00
./tests/wycheproof
./tests/unit
src/
phpunit.xml 0000755 00000001657 15073675000 0006774 0 ustar 00
./tests/unit
src/
src/Crypto/EcDH/EcDH.php 0000644 00000011775 15073675000 0010650 0 ustar 00 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");
}
}
}
src/Crypto/EcDH/EcDHInterface.php 0000644 00000004124 15073675000 0012457 0 ustar 00 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;
}
}
src/Crypto/Key/PrivateKeyInterface.php 0000644 00000003666 15073675000 0013776 0 ustar 00 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;
}
}
src/Crypto/Key/PublicKeyInterface.php 0000644 00000003504 15073675000 0013571 0 ustar 00 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);
}
}
src/Crypto/Signature/Signature.php 0000644 00000004140 15073675000 0013230 0 ustar 00 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;
}
}
src/Crypto/Signature/SignatureInterface.php 0000644 00000003245 15073675000 0015056 0 ustar 00 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));
}
}
src/Curves/CurveFactory.php 0000644 00000007115 15073675000 0011736 0 ustar 00 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);
}
}
src/Curves/NamedCurveFp.php 0000644 00000001326 15073675000 0011637 0 ustar 00 name = $name;
parent::__construct($parameters, $adapter);
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
}
src/Curves/NistCurve.php 0000644 00000020373 15073675000 0011245 0 ustar 00 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);
}
}
src/Curves/SecgCurve.php 0000755 00000016550 15073675000 0011216 0 ustar 00 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);
}
}
src/EccFactory.php 0000644 00000005103 15073675000 0010070 0 ustar 00 G = $G;
$this->point = $point;
parent::__construct($message, $code, $previous);
}
public function getGenerator(): GeneratorPoint
{
return $this->G;
}
public function getPoint(): PointInterface
{
return $this->point;
}
}
src/Exception/SignatureDecodeException.php 0000644 00000000201 15073675000 0014722 0 ustar 00 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;
}
}
src/Math/DebugDecorator.php 0000644 00000030705 15073675000 0011636 0 ustar 00 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
);
}
}
src/Math/GmpMath.php 0000644 00000016724 15073675000 0010307 0 ustar 00 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);
}
}
src/Math/GmpMathInterface.php 0000644 00000012153 15073675000 0012120 0 ustar 00 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);
}
}
src/Math/NumberTheory.php 0000644 00000021351 15073675000 0011365 0 ustar 00 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!');
}
}
src/Primitives/CurveFp.php 0000644 00000015647 15073675000 0011571 0 ustar 00 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())
];
}
}
src/Primitives/CurveFpInterface.php 0000644 00000007435 15073675000 0013406 0 ustar 00 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;
}
src/Primitives/CurveParameters.php 0000644 00000002272 15073675000 0013315 0 ustar 00 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;
}
}
src/Primitives/GeneratorPoint.php 0000644 00000005647 15073675000 0013156 0 ustar 00 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);
}
}
src/Primitives/Point.php 0000644 00000026377 15073675000 0011312 0 ustar 00 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;
}
}
src/Primitives/PointInterface.php 0000644 00000007542 15073675000 0013124 0 ustar 00 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;
}
}
src/Random/HmacRandomNumberGenerator.php 0000644 00000010414 15073675000 0014320 0 ustar 00 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);
}
}
}
src/Random/RandomGeneratorFactory.php 0000644 00000003501 15073675000 0013705 0 ustar 00 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;
}
}
src/Random/RandomNumberGeneratorInterface.php 0000644 00000000477 15073675000 0015360 0 ustar 00 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);
}
}
src/Serializer/Point/PointSerializerInterface.php 0000644 00000001133 15073675000 0016213 0 ustar 00 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);
}
}
src/Serializer/PrivateKey/DerPrivateKeySerializer.php 0000644 00000006242 15073675000 0017017 0 ustar 00 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);
}
}
src/Serializer/PrivateKey/PemPrivateKeySerializer.php 0000644 00000003020 15073675000 0017015 0 ustar 00 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);
}
}
src/Serializer/PrivateKey/PrivateKeySerializerInterface.php 0000644 00000000746 15073675000 0020210 0 ustar 00 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);
}
}
src/Serializer/PublicKey/Der/Parser.php 0000644 00000006605 15073675000 0014024 0 ustar 00 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);
}
}
src/Serializer/PublicKey/DerPublicKeySerializer.php 0000644 00000003766 15073675000 0016437 0 ustar 00 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);
}
}
src/Serializer/PublicKey/PemPublicKeySerializer.php 0000644 00000002733 15073675000 0016437 0 ustar 00 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);
}
}
src/Serializer/PublicKey/PublicKeySerializerInterface.php 0000644 00000000721 15073675000 0017611 0 ustar 00 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();
}
}
src/Serializer/Signature/Der/Parser.php 0000644 00000002563 15073675000 0014075 0 ustar 00 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)
);
}
}
src/Serializer/Signature/DerSignatureSerializer.php 0000644 00000001655 15073675000 0016556 0 ustar 00 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);
}
}
src/Serializer/Signature/DerSignatureSerializerInterface.php 0000644 00000001013 15073675000 0020363 0 ustar 00 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;
}
}
src/Util/BinaryString.php 0000644 00000003304 15073675000 0011377 0 ustar 00 equals($x, $zero)) {
return 0;
}
$log2 = 0;
while (false === $adapter->equals($x, $zero)) {
$x = $adapter->rightShift($x, 1);
$log2++;
}
return $log2 ;
}
}
validate_examples.sh 0000755 00000000224 15073675000 0010573 0 ustar 00 #!/bin/bash
for i in examples/*.php; do
php $i
if [ $? != 0 ]; then
echo "Error running example code";
exit -1
fi;
done