first commit

This commit is contained in:
annnj-company
2026-04-17 18:29:53 +08:00
parent e49fa5a215
commit 130c1026c4
5615 changed files with 1639145 additions and 0 deletions

View File

@@ -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");
}
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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));
}
}