Skip to content

Commit

Permalink
Merge pull request #22 from paragonie/oepnssl-ecdh
Browse files Browse the repository at this point in the history
Use OpenSSL for better security/perf if possible
  • Loading branch information
paragonie-security authored Apr 30, 2024
2 parents 197bb7f + 279bec4 commit fdafc54
Show file tree
Hide file tree
Showing 13 changed files with 548 additions and 1 deletion.
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"Mdanter\\Ecc\\": "src/"
}
},
"suggest": {
"ext-openssl": "(PHP 8.1, OpenSSL 3+) Improved performance, less worries about side-channels"
},
"autoload-dev": {
"psr-4": {
"Mdanter\\Ecc\\Tests\\": "tests/unit",
Expand Down
14 changes: 13 additions & 1 deletion src/Crypto/EcDH/EcDH.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@

use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface;
use Mdanter\Ecc\Crypto\Key\PublicKeyInterface;
use Mdanter\Ecc\Curves\NamedCurveFp;
use Mdanter\Ecc\Exception\ExchangeException;
use Mdanter\Ecc\Exception\OpensslException;
use Mdanter\Ecc\Math\GmpMathInterface;
use Mdanter\Ecc\OpensslFallbackTrait;
use Mdanter\Ecc\Primitives\OptimizedCurveInterface;

/**
Expand All @@ -43,6 +46,8 @@
*/
class EcDH implements EcDHInterface
{
use OpensslFallbackTrait;

/**
* Adapter used for math calculations
*
Expand Down Expand Up @@ -134,7 +139,14 @@ private function calculateKey()
try {
// Multiply our secret with recipients public key
$curve = $this->recipientKey->getCurve();
if ($curve instanceof OptimizedCurveInterface) {
if ($curve instanceof NamedCurveFp && $curve->shouldUseOpenssl() && !$this->disableOpenssl) {
// Defer to OpenSSL for ECDH computation
try {
$point = $curve->computeSharedSecret($this->senderKey, $this->recipientKey);
} catch (OpensslException $ex) {
}
}
if (empty($point) && $curve instanceof OptimizedCurveInterface) {
// Use an optimized implementation if one exists:
$optimized = $curve->getOptimizedCurveOps();
$point = $optimized->scalarMult(
Expand Down
101 changes: 101 additions & 0 deletions src/Crypto/Signature/Signer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,26 @@
namespace Mdanter\Ecc\Crypto\Signature;

use GMP;
use Mdanter\Ecc\Curves\CurveFactory;
use Mdanter\Ecc\Curves\NamedCurveFp;
use Mdanter\Ecc\Curves\NistCurve;
use Mdanter\Ecc\Curves\SecgCurve;
use Mdanter\Ecc\Math\ConstantTimeMath;
use Mdanter\Ecc\Math\GmpMathInterface;
use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface;
use Mdanter\Ecc\Crypto\Key\PublicKeyInterface;
use Mdanter\Ecc\OpensslFallbackTrait;
use Mdanter\Ecc\Primitives\CurveFpInterface;
use Mdanter\Ecc\Primitives\GeneratorPoint;
use Mdanter\Ecc\Primitives\OptimizedCurveInterface;
use Mdanter\Ecc\Random\RandomGeneratorFactory;
use Mdanter\Ecc\Serializer\Signature\DerSignatureSerializer;
use Mdanter\Ecc\Util\BinaryString;
use TypeError;

class Signer
{
use OpensslFallbackTrait;

/**
*
Expand All @@ -37,6 +48,66 @@ public function __construct(GmpMathInterface $adapter, bool $disallowMalleableSi
$this->disallowMalleableSig = $disallowMalleableSig;
}

/**
* @param CurveFpInterface $curve
* @return string
*/
protected function getDefaultHashAlgorithm(CurveFpInterface $curve): string
{
if ($curve instanceof NamedCurveFp) {
switch ($curve->getName()) {
case NistCurve::NAME_P256:
case SecgCurve::NAME_SECP_256K1:
return 'sha256';
case NistCurve::NAME_P384:
return 'sha394';
case NistCurve::NAME_P521:
return 'sha512';
}
}
$size = $curve->getSize();
if ($size <= 256) {
return 'sha256';
}
if ($size <= 384) {
return 'sha384';
}
return 'sha512';
}

/**
* @param PrivateKeyInterface $key
* @param string $message
* @param string|null $hashAlgo
*
* @return SignatureInterface
*/
public function signMessage(
#[\SensitiveParameter]
PrivateKeyInterface $key,
#[\SensitiveParameter]
string $message,
?string $hashAlgo = null
): SignatureInterface {
$generator = $key->getPoint();
$curve = $generator->getCurve();
if (is_null($hashAlgo)) {
$hashAlgo = $this->getDefaultHashAlgorithm($curve);
}
if ($curve instanceof NamedCurveFp && $curve->shouldUseOpenssl()&& !$this->disableOpenssl) {
/* Note: OpenSSL disregards $randomK. */
return $curve->signMessage($key, $message, $hashAlgo);
}
$hasher = new SignHasher($hashAlgo, $this->adapter);
$hash = $hasher->makeHash($message, $generator);
$rng = RandomGeneratorFactory::getRandomGenerator();
return $this->sign(
$key,
$hash,
$rng->generate($generator->getOrder())
);
}

/**
* @param PrivateKeyInterface $key
* @param GMP $truncatedHash - hash truncated for use in ECDSA
Expand Down Expand Up @@ -92,6 +163,36 @@ public function sign(
return new Signature($r, $s);
}

/**
* @param PublicKeyInterface $key
* @param SignatureInterface $sig
* @param string $message
* @param string|null $hashAlgo
* @return bool
*/
public function verifyMessage(
PublicKeyInterface $key,
SignatureInterface $sig,
#[\SensitiveParameter]
string $message,
?string $hashAlgo = null
): bool {
$generator = $key->getGenerator();
$curve = $generator->getCurve();
if (is_null($hashAlgo)) {
$hashAlgo = $this->getDefaultHashAlgorithm($curve);
}
if ($curve instanceof NamedCurveFp && $curve->shouldUseOpenssl()) {
/* Note: OpenSSL disregards $randomK. */
$encoder = new DerSignatureSerializer();
$encodedSig = $encoder->serialize($sig);
return $curve->verifyMessage($key, $encodedSig, $message, $hashAlgo);
}
$hasher = new SignHasher($hashAlgo, $this->adapter);
$hash = $hasher->makeHash($message, $generator);
return $this->verify($key, $sig, $hash);
}

/**
* @param PublicKeyInterface $key
* @param SignatureInterface $signature
Expand Down
2 changes: 2 additions & 0 deletions src/Curves/NamedCurveFp.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

class NamedCurveFp extends CurveFp
{
use OpensslTrait;

/**
* @var string
*/
Expand Down
Loading

0 comments on commit fdafc54

Please sign in to comment.