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;