PHP Classes

File: src/EasyECC.php

Recommend this page to a friend!
  Classes of Scott Arciszewski   Easy PHP ECC   src/EasyECC.php   Download  
File: src/EasyECC.php
Role: Class source
Content type: text/plain
Description: Class source
Class: Easy PHP ECC
Encrypt, decrypt and sign messages with PHPECC
Author: By
Last change:
Date: 2 years ago
Size: 8,754 bytes
 

Contents

Class file image Download
<?php
declare(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');
        }
    }
}