Skip to content

Commit

Permalink
Using mdanter/ecc to implement NIST curves (and fix #30).
Browse files Browse the repository at this point in the history
  • Loading branch information
lcobucci committed Sep 10, 2015
1 parent 14bbc7c commit f787525
Show file tree
Hide file tree
Showing 15 changed files with 915 additions and 34 deletions.
6 changes: 5 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@
"squizlabs/php_codesniffer": "~2.3",
"phpmd/phpmd": "~2.2",
"phpunit/php-invoker": "~1.1",
"mikey179/vfsStream": "~1.5"
"mikey179/vfsStream": "~1.5",
"mdanter/ecc": "~0.3"
},
"suggest":{
"mdanter/ecc": "Required to use Elliptic Curves based algorithms."
},
"autoload": {
"psr-4": {
Expand Down
170 changes: 169 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

127 changes: 107 additions & 20 deletions src/Signer/Ecdsa.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@

namespace Lcobucci\JWT\Signer;

use InvalidArgumentException;
use Lcobucci\JWT\Signer\Ecdsa\KeyParser;
use Mdanter\Ecc\Crypto\Signature\Signature;
use Mdanter\Ecc\Crypto\Signature\Signer;
use Mdanter\Ecc\EccFactory;
use Mdanter\Ecc\Math\MathAdapterInterface as Adapter;
use Mdanter\Ecc\Random\RandomGeneratorFactory;
use Mdanter\Ecc\Random\RandomNumberGeneratorInterface;

/**
* Base class for ECDSA signers
Expand All @@ -18,45 +24,126 @@
abstract class Ecdsa extends BaseSigner
{
/**
* {@inheritdoc}
* @var Adapter
*/
private $adapter;

/**
* @var Signer
*/
private $signer;

/**
* @var KeyParser
*/
private $parser;

/**
* @param Adapter $adapter
* @param EcdsaSigner $signer
* @param KeyParser $parser
*/
public function createHash($payload, Key $key)
public function __construct(Adapter $adapter = null, Signer $signer = null, KeyParser $parser = null)
{
$this->validateKey($key);
$this->adapter = $adapter ?: EccFactory::getAdapter();
$this->signer = $signer ?: EccFactory::getSigner($this->adapter);
$this->parser = $parser ?: new KeyParser($this->adapter);
}

$details = openssl_pkey_get_details($key);
/**
* {@inheritdoc}
*/
public function createHash(
$payload,
Key $key,
RandomNumberGeneratorInterface $generator = null
) {
$privateKey = $this->parser->getPrivateKey($key);
$generator = $generator ?: RandomGeneratorFactory::getRandomGenerator();

var_dump($details);
return $this->createSignatureHash(
$this->signer->sign(
$privateKey,
$this->createSigningHash($payload),
$generator->generate($privateKey->getPoint()->getOrder())
)
);
}

/**
* Creates a binary signature with R and S coordinates
*
* @param Signature $signature
*
* @return string
*/
private function createSignatureHash(Signature $signature)
{
$length = $this->getSignatureLength();

$signature = '';
openssl_sign($payload, $signature, $key, $this->getAlgorithm());
return pack(
'H*',
sprintf(
'%s%s',
str_pad($this->adapter->decHex($signature->getR()), $length, '0', STR_PAD_LEFT),
str_pad($this->adapter->decHex($signature->getS()), $length, '0', STR_PAD_LEFT)
)
);
}

return $signature;
/**
* Creates a hash using the signer algorithm with given payload
*
* @param string $payload
*
* @return int|string
*/
private function createSigningHash($payload)
{
return $this->adapter->hexDec(hash($this->getAlgorithm(), $payload));
}

/**
* {@inheritdoc}
*/
public function doVerify($expected, $payload, Key $key)
{
$this->validateKey($key);

return openssl_verify($payload, $expected, $key, $this->getAlgorithm()) === 1;
return $this->signer->verify(
$this->parser->getPublicKey($key),
$this->extractSignature($expected),
$this->createSigningHash($payload)
);
}

/**
* Returns if the key type is equals with expected type
* Extracts R and S values from given data
*
* @param resource $key
* @param string $value
*
* @return boolean
* @return \Mdanter\Ecc\Crypto\Signature\Signature
*/
private function validateKey($key)
private function extractSignature($value)
{
$details = openssl_pkey_get_details($key);
$length = $this->getSignatureLength();
$value = unpack('H*', $value)[1];

if (!isset($details['key']) || $details['type'] !== OPENSSL_KEYTYPE_EC) {
throw new InvalidArgumentException('The type of given key does not match with this signer');
}
return new Signature(
$this->adapter->hexDec(substr($value, 0, $length)),
$this->adapter->hexDec(substr($value, $length))
);
}

/**
* Returns the lenght of signature parts
*
* @return int
*/
abstract public function getSignatureLength();

/**
* Returns the name of algorithm to be used to create the signing hash
*
* @return string
*/
abstract public function getAlgorithm();
}
Loading

0 comments on commit f787525

Please sign in to comment.