Skip to content

Commit

Permalink
make pure php library for elliptic curve as default;
Browse files Browse the repository at this point in the history
  • Loading branch information
grkamil committed Aug 16, 2018
1 parent 10a9816 commit bd53259
Show file tree
Hide file tree
Showing 8 changed files with 263 additions and 99 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ This is a pure PHP SDK for working with <b>Minter</b> blockhain

## Installing

Before using SDK you need to install secp256k1-php extension for PHP.
To install extension follow these [instructions](https://github.com/Bit-Wasp/secp256k1-php/blob/master/README.md).

After that you can require SDK.
```bash
composer require minter/minter-php-sdk
```

For <b>fast sign and decoding</b> you need to install secp256k1-php extension for PHP
To install extension follow these [instructions](https://github.com/Bit-Wasp/secp256k1-php/blob/master/README.md).

## Using MinterAPI

You can get all valid responses and full documentation at [Minter Node Api](https://minter-go-node.readthedocs.io/en/latest/api.html)
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"guzzlehttp/guzzle": "6.3",
"minter/minter-php-bip-44": "dev-master",
"bitwasp/bitcoin-lib": "1.0.4",
"bitwasp/secp256k1-php": "0.1.3"
"bitwasp/secp256k1-php": "0.1.3",
"simplito/elliptic-php": "1.0.3"
},
"minimum-stability": "dev",
"prefer-stable": true,
Expand Down
239 changes: 239 additions & 0 deletions src/Minter/Library/ECDSA.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
<?php

namespace Minter\Library;

use Elliptic\EC;
use Elliptic\EC\KeyPair;

/**
* Class ECDSA
* @package Minter\Library
*/
class ECDSA
{
/**
* bits for recovery param in elliptic curve
*/
const V_BITS = 27;

/**
* Sign message
*
* @param string $message
* @param string $privateKey
* @return array
*/
public static function sign(string $message, string $privateKey): array
{
if(function_exists('secp256k1_context_create')) {
return self::signViaExtensions($message, $privateKey);
}

return self::signViaPurePHP($message, $privateKey);
}

/**
* Recover public key
*
* @param string $msg
* @param string $r
* @param string $s
* @param int $recovery
* @return string
*/
public static function recover(string $msg, string $r, string $s, int $recovery): string
{
// define the recovery param
$recovery = $recovery === self::V_BITS ? 0 : 1;

if(function_exists('secp256k1_context_create')) {
return self::recoverViaExtension($msg, $r, $s, $recovery);
}

return self::recoverViaPurePHP($msg, $r, $s, $recovery);
}

/**
* Convert private key to public key
*
* @param string $privateKey
* @return string
*/
public static function privateToPublic(string $privateKey): string
{
$publicKey = null;

if(function_exists('secp256k1_context_create')) {
$context = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);

// convert key to binary
$privateKey = hex2bin($privateKey);

/** @var $publicKeyResource */
$publicKeyResource = null;
secp256k1_ec_pubkey_create($context, $publicKeyResource, $privateKey);

$publicKey = null;
secp256k1_ec_pubkey_serialize($context, $publicKey, $publicKeyResource, false);

$publicKey = bin2hex($publicKey);
}
else {
// create elliptic curve and get public key
$ellipticCurve = new EC('secp256k1');
$keyPair = new KeyPair($ellipticCurve, [
'priv' => $privateKey,
'privEnc' => 'hex'
]);

$publicKey = $keyPair->getPublic('hex');
}

return substr($publicKey, 2, 130);
}

/**
* Sign via PHP extension
*
* @param $message
* @param $privateKey
* @return array
*/
public static function signViaExtensions($message, $privateKey): array
{
// convert params to binary
$privateKey = hex2bin($privateKey);
$message = hex2bin($message);

// create curve context
$context = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);

/** @var resource $signatureSource */
$signatureSource = '';
secp256k1_ecdsa_sign_recoverable($context, $signatureSource, $message, $privateKey);

$signature = null;
$recoveryParam = null;
secp256k1_ecdsa_recoverable_signature_serialize_compact($context, $signatureSource, $signature, $recoveryParam);

$signature = bin2hex($signature);

$r = ltrim(substr($signature, 0, 64), '0');
$s = ltrim(substr($signature, 64, 64), '0');

return self::encodeSign($r, $s, $recoveryParam);
}

/**
* Sign using pure PHP library
*
* @param $message
* @param $privateKey
* @return array
*/
public static function signViaPurePHP($message, $privateKey): array
{
// create elliptic curve and sign
$ellipticCurve = new EC('secp256k1');
$signature = $ellipticCurve->sign($message, $privateKey, 'hex', ['canonical' => true]);

$r = $signature->r->toString('hex');
$s = $signature->s->toString('hex');
$recovery = $signature->recoveryParam;

return self::encodeSign($r, $s, $recovery);
}

/**
* Recover public key via PHP extension
*
* @param string $msg
* @param $r
* @param $s
* @param int $recovery
* @return string
*/
public static function recoverViaExtension(string $msg, $r, $s, int $recovery): string
{
// conver to binary
$msg = hex2bin($msg);

// define the signature
$signature = [
'r' => hex2bin(str_repeat('0', 64 - strlen($r)) . $r),
's' => hex2bin(str_repeat('0', 64 - strlen($s)) . $s),
'recoveryParam' => $recovery
];

// create context for curve
$context = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);

/** @var resource $signatureSource */
$signatureResource = null;
secp256k1_ecdsa_recoverable_signature_parse_compact(
$context,
$signatureResource,
$signature['r'] . $signature['s'],
$signature['recoveryParam']
);

/** @var resource $publicKeyResource */
$publicKeyResource = null;
secp256k1_ecdsa_recover($context, $publicKeyResource, $signatureResource, $msg);

$publicKey = '';
secp256k1_ec_pubkey_serialize($context, $publicKey, $publicKeyResource, false);

return substr(bin2hex($publicKey), 2, 130);
}

/**
* Recover public key via pure PHP library
*
* @param string $msg
* @param string $r
* @param string $s
* @param int $recovery
* @return string
*/
public static function recoverViaPurePHP(string $msg, string $r, string $s, int $recovery): string
{
// define the signature
$signature = [
'r' => $r,
's' => $s,
'recoveryParam' => $recovery
];

// create elliptic curve
$ellipticCurve = new EC('secp256k1');
$point = $ellipticCurve->recoverPubKey($msg, $signature, $recovery, 'hex');

$keyPair = new KeyPair($ellipticCurve, [
'pub' => $point,
'pubEnc' => 'hex'
]);

return substr($keyPair->getPublic('hex'), 2, 130);
}

/**
* Encore result params (V, R , S)
*
* @param string $r
* @param string $s
* @param int $recovery
* @return array
*/
protected static function encodeSign(string $r, string $s, int $recovery): array
{
$r = Helper::padToEven($r);
$s = Helper::padToEven($s);

return [
'v' => $recovery + self::V_BITS,
'r' => hex2bin($r),
's' => hex2bin($s)
];
}
}
48 changes: 4 additions & 44 deletions src/Minter/Library/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@
use kornrunner\Keccak;
use Minter\SDK\MinterPrefix;

/**
* Class Helper
* @package Minter\Library
*/
class Helper
{
/**
* bits for recovery param in elliptic curve
*/
const V_BITS = 27;

/**
* Decode from hex
*
Expand Down Expand Up @@ -123,43 +122,4 @@ public static function createKeccakHash(string $dataString): string

return Keccak::hash($binaryTx, 256);
}

/**
* Format signature V R S parameters
*
* @param string $message
* @param string $privateKey
* @return array
*/
public static function ecdsaSign(string $message, string $privateKey): array
{
// convert params to binary
$privateKey = hex2bin($privateKey);
$message = hex2bin($message);

// create curve context
$context = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);

/** @var resource $signatureSource */
$signatureSource = '';
secp256k1_ecdsa_sign_recoverable($context, $signatureSource, $message, $privateKey);

$signature = null;
$recoveryParam = null;
secp256k1_ecdsa_recoverable_signature_serialize_compact($context, $signatureSource, $signature, $recoveryParam);

$signature = bin2hex($signature);

$r = ltrim(substr($signature, 0, 64), '0');
$s = ltrim(substr($signature, 64, 64), '0');

if(strlen($r) % 2 !== 0) $r = '0' . $r;
if(strlen($s) % 2 !== 0) $s = '0' . $s;

return [
'v' => $recoveryParam + self::V_BITS,
'r' => hex2bin($r),
's' => hex2bin($s)
];
}
}
4 changes: 4 additions & 0 deletions src/Minter/Library/Http.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

/**
* Trait Http
* @package Minter\Library
*/
Trait Http
{
/**
Expand Down
8 changes: 5 additions & 3 deletions src/Minter/SDK/MinterCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Minter\SDK;

use Elliptic\EC;
use Minter\Library\ECDSA;
use Minter\Library\Helper;
use Web3p\RLP\RLP;

Expand Down Expand Up @@ -93,7 +95,7 @@ public function sign(string $privateKey): string
$passphrase = hash('sha256', $this->passphrase);

// create elliptic curve and sign
$signature = Helper::ecdsaSign($msgHash, $passphrase);
$signature = ECDSA::sign($msgHash, $passphrase);

// define lock field
$this->structure['lock'] = $this->formatLockFromSignature($signature);
Expand All @@ -103,7 +105,7 @@ public function sign(string $privateKey): string
array_slice($this->structure, 0, 5)
);

$this->structure = array_merge($this->structure, Helper::ecdsaSign($msgHashWithLock, $privateKey));
$this->structure = array_merge($this->structure, ECDSA::sign($msgHashWithLock, $privateKey));

// rlp encode data and add Minter wallet prefix
return MinterPrefix::CHECK . $this->rlp->encode($this->structure)->toString('hex');
Expand All @@ -128,7 +130,7 @@ public function createProof(): string

// get SHA 256 hash of password and create EC signature
$passphrase = hash('sha256', $this->passphrase);
$signature = Helper::ecdsaSign($addressHash, $passphrase);
$signature = ECDSA::sign($addressHash, $passphrase);

// return formatted proof
return bin2hex(
Expand Down
Loading

0 comments on commit bd53259

Please sign in to comment.