secp256k1 - andreydiveev/wiki GitHub Wiki
secp256k1 pub key (not an address):
https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses
https://en.bitcoin.it/wiki/Secp256k1
<?php
ini_set('error_reporting', E_ALL);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
// secp256k1 domain parameters
// @link https://en.bitcoin.it/wiki/Secp256k1
// @link https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses
const P_CURVE = "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"; // 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
const A_CURVE = 0x0000000000000000000000000000000000000000000000000000000000000000;
const B_CURVE = 0x0000000000000000000000000000000000000000000000000000000000000007;
const G_X = "0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"; // 55066263022277343669578718895168534326250603453777594175500187360389116729240
const G_Y = "0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"; // 32670510020758816978083085130507043184471273380659243275938904335757337482424
const N = "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141";
const H = 0x01;
$GPoint = [gmp_init(G_X), gmp_init(G_Y)];
$privateKey = "18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725";
function p_curve()
{
return gmp_init(convBase(P_CURVE, "0123456789ABCDEF", "0123456789"));
}
function N()
{
return gmp_init(convBase(N, "0123456789ABCDEF", "0123456789"));
}
function EccMultiply($GPoint, $ScalarHex)
{
if (gmp_init($ScalarHex, 16) == 0 or gmp_init($ScalarHex, 16) >= N()) throw new \Exception("Invalid Scalar/Private Key");
// 1100011100001010010100111101101101010001100000111111101000010011010101001010011111000000100010100011100000001111001111100100011100111011101001110011111111001101001000111111000101100001000000011010111011011001010011010001000000110001100100001011100100101
$ScalarBin = convBase(strtoupper($ScalarHex), "0123456789ABCDEF", "01");
$Q=$GPoint;
foreach (str_split(substr($ScalarBin, 1), 1) as $bit)
{
$Q = ECdouble($Q);
// print "DUB [$bit]" . $Q[0] . "\n";
if ($bit == "1") {
$Q = ECadd($Q, $GPoint);
// print "ADD [$bit]" . $Q[0] . "\n";
}
}
return $Q;
}
function ECdouble($a)
{
$Lambda = ((3 * $a[0] * $a[0] + A_CURVE) * gmp_invert((2 * $a[1]), p_curve())) % p_curve();
$x = ($Lambda * $Lambda - 2 * $a[0]) % p_curve();
$y = ($Lambda * ($a[0] - $x) - $a[1]) % p_curve();
return [$x, $y];
}
function ECadd($a,$b)
{
$Lambda = (($b[1] - $a[1]) * gmp_invert($b[0] - $a[0], p_curve())) % p_curve();
$x = ($Lambda * $Lambda - $a[0] - $b[0]) % p_curve();
$y = ($Lambda * ($a[0] - $x) - $a[1]) % p_curve();
return [$x, $y];
}
function convBase($numberInput, $fromBaseInput, $toBaseInput)
{
if ($fromBaseInput==$toBaseInput) return $numberInput;
$fromBase = str_split($fromBaseInput,1);
$toBase = str_split($toBaseInput,1);
$number = str_split($numberInput,1);
$fromLen=strlen($fromBaseInput);
$toLen=strlen($toBaseInput);
$numberLen=strlen($numberInput);
$retval='';
if ($toBaseInput == '0123456789')
{
$retval=0;
for ($i = 1;$i <= $numberLen; $i++)
$retval = bcadd($retval, bcmul(array_search($number[$i-1], $fromBase),bcpow($fromLen,$numberLen-$i)));
return $retval;
}
if ($fromBaseInput != '0123456789')
$base10=convBase($numberInput, $fromBaseInput, '0123456789');
else
$base10 = $numberInput;
if ($base10<strlen($toBaseInput))
return $toBase[$base10];
while($base10 != '0')
{
$retval = $toBase[bcmod($base10,$toLen)].$retval;
$base10 = bcdiv($base10,$toLen,0);
}
return $retval;
}
$PublicKey = EccMultiply($GPoint, $privateKey);
$xHex = convBase(strtoupper($PublicKey[0]), "0123456789", "0123456789ABCDEF"); // 36422191471907241029883925342251831624200921388586025344128047678873736520530
$yHex = convBase(strtoupper($PublicKey[1]), "0123456789", "0123456789ABCDEF"); // 20277110887056303803699431755396003735040374760118964734768299847012543114150
$prefixUncompressed = "04";
$prefixCompressed = gmp_mod($PublicKey[1], 2) == 0 ? "02" : "03"; // y is odd or even
// private Key: 18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725
// public Key (compressed): 0250863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B2352
// public Key (uncompressed): 0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6
header('Content-Type: text/plain');
echo <<<END_OF_SEQ
*** secp256k1 ***
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private Key: $privateKey
public Key (compressed): $prefixCompressed$xHex
public Key (uncompressed): $prefixUncompressed$xHex$yHex
END_OF_SEQ;