| 
<?phpdeclare(strict_types=1);
 namespace ParagonIE\EasyECC;
 
 use FG\ASN1\Exception\ParserException;
 use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface;
 use Mdanter\Ecc\Crypto\Key\PublicKeyInterface;
 use Mdanter\Ecc\Crypto\Signature\Signer;
 use Mdanter\Ecc\Crypto\Signature\SignHasher;
 use Mdanter\Ecc\Curves\CurveFactory;
 use Mdanter\Ecc\EccFactory;
 use Mdanter\Ecc\Math\GmpMathInterface;
 use Mdanter\Ecc\Primitives\GeneratorPoint;
 use Mdanter\Ecc\Random\RandomGeneratorFactory;
 use Mdanter\Ecc\Serializer\PublicKey\DerPublicKeySerializer;
 use Mdanter\Ecc\Serializer\Signature\DerSignatureSerializer;
 use Mdanter\Ecc\Util\NumberSize;
 use ParagonIE\EasyECC\Curve25519\EdwardsPublicKey;
 use ParagonIE\EasyECC\Curve25519\EdwardsSecretKey;
 use ParagonIE\EasyECC\Curve25519\X25519;
 use ParagonIE\EasyECC\ECDSA\SecretKey;
 use ParagonIE\EasyECC\ECDSA\Signature;
 use ParagonIE\EasyECC\Exception\ConfigException;
 use ParagonIE\EasyECC\Exception\NotImplementedException;
 
 /**
 * Class EasyECC
 * @package ParagonIE\EasyECC
 */
 class EasyECC
 {
 const CURVES = ['sodium', 'P256', 'P384', 'K256'];
 const DEFAULT_ECDSA_CURVE = 'P256';
 const DEFAULT_CURVE = 'sodium';
 const SIGNATURE_SIZES = [
 'K256' => 64,
 'P256' => 64,
 'P384' => 96
 ];
 
 /** @var string string */
 protected $curve;
 
 /** @var GmpMathInterface $adapter */
 protected $adapter;
 
 /** @var GeneratorPoint $generator */
 protected $generator;
 
 /** @var string $hashAlgo */
 protected $hashAlgo;
 
 /** @var SignHasher $hasher */
 protected $hasher;
 
 /**
 * EasyECC constructor.
 * @param string $curve
 * @throws ConfigException
 */
 public function __construct(string $curve = self::DEFAULT_CURVE)
 {
 if (!\in_array($curve, self::CURVES, true)) {
 throw new ConfigException('Invalid curve choice');
 }
 $this->curve = $curve;
 switch ($curve) {
 case 'K256':
 $this->adapter = EccFactory::getAdapter();
 $this->generator = CurveFactory::getGeneratorByName('secp256k1');
 $this->hashAlgo = 'sha256';
 $this->hasher = new SignHasher($this->hashAlgo, $this->adapter);
 break;
 case 'P256':
 $this->adapter = EccFactory::getAdapter();
 $this->generator = EccFactory::getNistCurves()->generator256();
 $this->hashAlgo = 'sha256';
 $this->hasher = new SignHasher($this->hashAlgo, $this->adapter);
 break;
 case 'P384':
 $this->adapter = EccFactory::getAdapter();
 $this->generator = EccFactory::getNistCurves()->generator384();
 $this->hashAlgo = 'sha384';
 $this->hasher = new SignHasher($this->hashAlgo, $this->adapter);
 break;
 case 'sodium':
 break;
 }
 }
 
 /**
 * @return PrivateKeyInterface
 * @throws NotImplementedException
 * @throws \SodiumException
 */
 public function generatePrivateKey(): PrivateKeyInterface
 {
 if ($this->curve === 'sodium') {
 return new EdwardsSecretKey(\sodium_crypto_sign_keypair());
 }
 return SecretKey::generate($this->curve);
 }
 
 /**
 * @param PrivateKeyInterface $private
 * @param PublicKeyInterface $public
 * @param bool $isClient
 * @param string $hashAlgo
 * @return string
 * @throws \SodiumException
 * @throws \TypeError
 */
 public function keyExchange(
 PrivateKeyInterface $private,
 PublicKeyInterface $public,
 bool $isClient,
 string $hashAlgo = ''
 ): string {
 if ($this->curve === 'sodium') {
 $ecdh = new X25519();
 $ecdh->setSenderKey($private);
 $ecdh->setRecipientKey($public);
 return $ecdh->keyExchange($isClient);
 }
 if (empty($hashAlgo)) {
 // Use the default
 $hashAlgo = $this->hashAlgo;
 }
 $ss = $this->scalarMult($private, $public);
 $derSer = new DerPublicKeySerializer();
 
 $recip_pk = $derSer->serialize($public);
 $sender_pk = $derSer->serialize($private->getPublicKey());
 
 if ($isClient) {
 return hash(
 $hashAlgo,
 $ss . $sender_pk . $recip_pk,
 true
 );
 } else {
 return hash(
 $hashAlgo,
 $ss . $recip_pk . $sender_pk,
 true
 );
 }
 }
 
 /**
 * @param PrivateKeyInterface $private
 * @param PublicKeyInterface $public
 * @return string
 * @throws \SodiumException
 * @throws \TypeError
 */
 public function scalarmult(
 PrivateKeyInterface $private,
 PublicKeyInterface $public
 ): string {
 if ($this->curve === 'sodium') {
 $ecdh = new X25519();
 $ecdh->setSenderKey($private);
 $ecdh->setRecipientKey($public);
 return $ecdh->scalarMult();
 }
 
 $scalarmult = $private
 ->createExchange($public)
 ->calculateSharedKey();
 return $this->adapter->intToFixedSizeString(
 $scalarmult,
 NumberSize::bnNumBytes($this->adapter, $this->generator->getOrder())
 );
 }
 
 /**
 * @param string $message
 * @param PrivateKeyInterface $privateKey
 * @param bool $ieeeFormat Set to TRUE for IEEE-P1363 formatted signatures
 * @return string
 *
 * @throws \SodiumException
 * @throws \TypeError
 */
 public function sign(
 string $message,
 PrivateKeyInterface $privateKey,
 bool $ieeeFormat = false
 ): string {
 if ($this->curve === 'sodium') {
 if ($privateKey instanceof EdwardsSecretKey) {
 return \sodium_crypto_sign_detached(
 $message,
 $privateKey->getAsString()
 );
 } else {
 throw new \TypeError('Only Ed25519 secret keys can be used to sign');
 }
 }
 $hash = $this->hasher->makeHash($message, $this->generator);
 
 // RFC 6979
 $kGen = RandomGeneratorFactory::getHmacRandomGenerator($privateKey, $hash, $this->hashAlgo);
 $k = $kGen->generate($this->generator->getOrder());
 
 $signer = new Signer($this->adapter);
 $signature = $signer->sign($privateKey, $hash, $k);
 
 if ($ieeeFormat) {
 return (Signature::promote($signature))
 ->toString(self::SIGNATURE_SIZES[$this->curve]);
 }
 $serializer = new DerSignatureSerializer();
 return $serializer->serialize($signature);
 }
 
 /**
 * @param string $message
 * @param PublicKeyInterface $publicKey
 * @param string $signature
 * @param bool $ieeeFormat Set to TRUE for IEEE-P1363 formatted signatures
 * @return bool
 *
 * @throws ParserException
 * @throws \SodiumException
 * @throws \TypeError
 */
 public function verify(
 string $message,
 PublicKeyInterface $publicKey,
 string $signature,
 bool $ieeeFormat = false
 ): bool {
 if ($this->curve === 'sodium') {
 if ($publicKey instanceof EdwardsPublicKey) {
 return \sodium_crypto_sign_verify_detached(
 $signature,
 $message,
 $publicKey->getAsString()
 );
 } else {
 throw new \TypeError('Only Ed25519 secret keys can be used to sign');
 }
 }
 
 if ($ieeeFormat) {
 $sig = Signature::fromString($signature);
 } else {
 $sigSerializer = new DerSignatureSerializer();
 $sig = $sigSerializer->parse($signature);
 }
 
 $hash = $this->hasher->makeHash($message, $this->generator);
 $signer = new Signer($this->adapter);
 
 return $signer->verify($publicKey, $sig, $hash);
 }
 
 /**
 * @param string $curve
 * @return GeneratorPoint
 * @throws NotImplementedException
 */
 public static function getGenerator(string $curve = self::DEFAULT_ECDSA_CURVE): GeneratorPoint
 {
 switch ($curve) {
 case 'K256':
 return CurveFactory::getGeneratorByName('secp256k1');
 case 'P256':
 return EccFactory::getNistCurves()->generator256();
 case 'P384':
 return EccFactory::getNistCurves()->generator384();
 default:
 throw new NotImplementedException('This curve is not supported');
 }
 }
 }
 
 |