Skip to content
This repository has been archived by the owner on Dec 30, 2020. It is now read-only.

Commit

Permalink
OpenSSL EC support added
Browse files Browse the repository at this point in the history
  • Loading branch information
Florent Morselli committed Jul 7, 2016
1 parent 1bb8ab7 commit 34d7f4b
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 9 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
"symfony/polyfill-mbstring": "^1.1",
"symfony/polyfill-php70": "^1.1",
"fgrosse/phpasn1": "^1.5",
"mdanter/ecc": "0.4"
"mdanter/ecc": "0.4",
"psr/cache": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^5.0",
Expand Down
15 changes: 10 additions & 5 deletions doc/Performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The conclusions reached regarding these results are:
* Signature operations:
* The HMAC signature performances are very good.
* The RSA signature performances are good.
* The ECC signature performances are poor. This is due to the use of a pure PHP library.
* The ECC signature performances very good **only if OpenSSL supports EC signatures**.
* Key Encryption operations:
* The algorithms based on RSA are very good.
* The AES GCM Key Wrapping algorithms are very good if the extension is installed, else performances are bad.
Expand All @@ -25,7 +25,7 @@ The conclusions reached regarding these results are:
To conclude, if you use shared keys, you will prefer HMAC signature algorithms and AES/AES GCM key wrapping algorithms.
If you use public/private key pairs, you will prefer RSA algorithms for signature and key encryption.

**At this moment, we do not recommend the use of ECC algorithms with our library.**
**At this moment, we do not recommend the use of ECC algorithms for encryption/decryption with our library.**

# Signature/Verification Operations

Expand All @@ -43,11 +43,16 @@ Hereafter a table with all signature/verification test results.
| PS256 | 2.711060 msec | 0.338850 msec |
| PS384 | 2.658789 msec | 0.305960 msec |
| PS512 | 2.691140 msec | 0.352941 msec |
| ES256 | 46.056359 msec | 85.660450 msec |
| ES384 | 70.218148 msec | 143.770418 msec |
| ES512 | 110.474162 msec | 202.372239 msec |
| ES256 | 1.375458 msec | 0.685260 msec |
| ES256* | 46.056359 msec | 85.660450 msec |
| ES384 | 1.336381 msec | 1.702900 msec |
| ES384* | 70.218148 msec | 143.770418 msec |
| ES512 | 1.124258 msec | 1.578491 msec |
| ES512* | 110.474162 msec | 202.372239 msec |
| EdDSA (Ed25519) | 0.042379 msec | 0.109930 msec |

* *(1) Tests using the PHPECC library in case the EC signature is not supported by OpenSSL*

# Key Encryption Operations

## Direct Key
Expand Down
95 changes: 92 additions & 3 deletions src/Algorithm/Signature/ECDSA.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@

use Assert\Assertion;
use Base64Url\Base64Url;
use FG\ASN1\Object;
use FG\ASN1\Universal\Integer;
use FG\ASN1\Universal\Sequence;
use Jose\Algorithm\SignatureAlgorithmInterface;
use Jose\KeyConverter\ECKey;
use Jose\Object\JWKInterface;
use Mdanter\Ecc\Crypto\Signature\Signature;
use Mdanter\Ecc\EccFactory;
Expand All @@ -32,6 +36,46 @@ public function sign(JWKInterface $key, $data)
$this->checkKey($key);
Assertion::true($key->has('d'), 'The EC key is not private');

if (defined('OPENSSL_KEYTYPE_EC')) {
return $this->getOpenSSLSignature($key, $data);
}

return $this->getPHPECCSignature($key, $data);
}

/**
* @param \Jose\Object\JWKInterface $key
* @param string $data
*
* @return string
*/
private function getOpenSSLSignature(JWKInterface $key, $data)
{
$pem = (new ECKey($key))->toPEM();
$result = openssl_sign($data, $signature, $pem, $this->getHashAlgorithm());

Assertion::true($result, 'Signature failed');

$asn = Object::fromBinary($signature);
Assertion::isInstanceOf($asn, Sequence::class, 'Invalid signature');

$res = '';
foreach ($asn->getChildren() as $child) {
Assertion::isInstanceOf($child, Integer::class, 'Invalid signature');
$res .= str_pad($this->convertDecToHex($child->getContent()), $this->getSignaturePartLength(), '0', STR_PAD_LEFT);
}

return $this->convertHexToBin($res);
}

/**
* @param \Jose\Object\JWKInterface $key
* @param string $data
*
* @return string
*/
private function getPHPECCSignature(JWKInterface $key, $data)
{
$p = $this->getGenerator();
$d = $this->convertBase64ToGmp($key->get('d'));
$hash = $this->convertHexToGmp(hash($this->getHashAlgorithm(), $data));
Expand Down Expand Up @@ -63,19 +107,64 @@ public function verify(JWKInterface $key, $data, $signature)
if (mb_strlen($signature, '8bit') !== 2 * $part_length) {
return false;
}
$R = mb_substr($signature, 0, $part_length, '8bit');
$S = mb_substr($signature, $part_length, null, '8bit');

if (defined('OPENSSL_KEYTYPE_EC')) {
return $this->verifyOpenSSLSignature($key, $data, $R, $S);
}

return $this->verifyPHPECCSignature($key, $data, $R, $S);
}

/**
* @param \Jose\Object\JWKInterface $key
* @param string $data
* @param string $R
* @param string $S
*
* @return string
*/
private function verifyOpenSSLSignature(JWKInterface $key, $data, $R, $S)
{
$pem = ECKey::toPublic(new ECKey($key))->toPEM();

$oid_sequence = new Sequence();
$oid_sequence->addChildren([
new Integer(gmp_strval($this->convertHexToGmp($R), 10)),
new Integer(gmp_strval($this->convertHexToGmp($S), 10)),
]);

return 1 === openssl_verify($data, $oid_sequence->getBinary(), $pem, $this->getHashAlgorithm());
}

/**
* @param \Jose\Object\JWKInterface $key
* @param string $data
* @param string $R
* @param string $S
*
* @return string
*/
private function verifyPHPECCSignature(JWKInterface $key, $data, $R, $S)
{
$p = $this->getGenerator();
$x = $this->convertBase64ToGmp($key->get('x'));
$y = $this->convertBase64ToGmp($key->get('y'));
$R = $this->convertHexToGmp(mb_substr($signature, 0, $part_length, '8bit'));
$S = $this->convertHexToGmp(mb_substr($signature, $part_length, null, '8bit'));
$hash = $this->convertHexToGmp(hash($this->getHashAlgorithm(), $data));

$public_key = $p->getPublicKeyFrom($x, $y);

$signer = EccFactory::getSigner();

return $signer->verify($public_key, new Signature($R, $S), $hash);
return $signer->verify(
$public_key,
new Signature(
$this->convertHexToGmp($R),
$this->convertHexToGmp($S)
),
$hash
);
}

/**
Expand Down

0 comments on commit 34d7f4b

Please sign in to comment.