Skip to content

Instantly share code, notes, and snippets.

Created August 24, 2015 17:13
Show Gist options
  • Save anonymous/8ad5a5766ba754e57fd5 to your computer and use it in GitHub Desktop.
Save anonymous/8ad5a5766ba754e57fd5 to your computer and use it in GitHub Desktop.
#!/usr/bin/perl
#
# WARNING!
#
# TO,OOO,OOO dirty
#
# YOURE CANT UNSEE IT!
#
use strict;
use warnings;
use Math::BigInt;
use Crypt::RIPEMD160;
use Digest::SHA qw(sha256_hex);
# Private key exponent
# Gives right result: 0284196c3028cbe20c29d0d1c1d5c24a09bd3f028bbc2d1c76dde0816a28365c
# Gives wrong result: 835c6975aa65f95cb55616ace8c8bede83b010f7191c0a6d385be1c95992870d
my $k = "0284196c3028cbe20c29d0d1c1d5c24a09bd3f028bbc2d1c76dde0816a28365c";
# Base58 func was taken from http://lenschulwitz.com/b58/base58perl.txt
my @b58 = qw{
1 2 3 4 5 6 7 8 9
A B C D E F G H J K L M N P Q R S T U V W X Y Z
a b c d e f g h i j k m n o p q r s t u v w x y z
};
my %b58 = map { $b58[$_] => $_ } 0 .. 57;
my %reverseb58 = reverse %b58;
sub base58 {
my @binary_address_to_encode = @{$_[0]};
die "Subroutine base58 needs binary decimal array to encode!\n" unless @binary_address_to_encode;
my $base58_encoded_array_size = 2 * scalar @binary_address_to_encode;
my @base58_encoded_address;
my $leading_zeroes = length $1 if join('', @binary_address_to_encode) =~ /^(0*)/;
for my $dec_char ( @binary_address_to_encode ) {
for (my $encoded_character_index = $base58_encoded_array_size; $encoded_character_index--; ) {
$dec_char += 256 * ($base58_encoded_address[$encoded_character_index] // 0);
$base58_encoded_address[$encoded_character_index] = $dec_char % 58;
$dec_char /= 58;
}
}
my $encoded_address_with_leading_1s = join('', map { $reverseb58{$_} } @base58_encoded_address);
if ($encoded_address_with_leading_1s =~ /(1{$leading_zeroes}[^1].*)/){
return $1;
}
elsif ($encoded_address_with_leading_1s =~ /(1{$leading_zeroes})/){
return $1;
}
else{
die "Unexpected error in subroutine base58!\n";
}
}
sub encodebase58fromhex {
my $hex_binary_address = $_[0];
die "Subroutine encodebase58fromhex needs binary address represented with hex characters as input!" unless (defined $hex_binary_address and length $hex_binary_address != 0);
die "Cannot Encode! Invalid Hexadecimal Character(s)!\n" unless $hex_binary_address =~ /^[a-f0-9]*$/i;
my @binary_address_to_encode = $hex_binary_address =~ /../g;
for( 0 .. scalar(@binary_address_to_encode)-1 ){
$binary_address_to_encode[$_] = hex($binary_address_to_encode[$_]);
}
my $std_bitcoin_address = base58(\@binary_address_to_encode);
return $std_bitcoin_address;
}
# Ok, here we go
&processKey("0x$k");
sub processKey() {
my $k = shift;
my %p = getPubKeyPoints($k);
my ($pkh, $pkhs, $pkhr, $pkhrd, $pkhrde, $pkhrdeh1, $pkhrdeh2, $pkhrdeh2c, $pkhrdec, $pkb);
my ($pkch, $pkchs, $pkchr, $pkchrd, $pkchrde, $pkchrdeh1, $pkchrdeh2, $pkchrdeh2c, $pkchrdec, $pkcb);
$p{"x"} = substr($p{"x"}, 2, length($p{"x"}) - 2);
$p{"y"} = substr($p{"y"}, 2, length($p{"y"}) - 2);
while(length($p{'x'}) < 64) {
$p{'x'} = '0' . $p{'x'};
}
while(length($p{'y'}) < 64) {
$p{'x'} = '0' . $p{'x'};
}
if ((Math::BigInt->new("0x" . $p{"x"})->is_even())) {
$pkch = "02" . $p{"x"};
} else {
$pkch = "03" . $p{"x"};
}
print "X: " . $p{"x"} . "\n";
print "Y: " . $p{"y"} . "\n";
$pkh = "04" . $p{"x"} . $p{"y"};
print "pkh = $pkh\n";
print "pkch = $pkch\n";
$pkhs = sha256_hex(pack('H*', $pkh));
$pkhr = new Crypt::RIPEMD160;
$pkhr->add(pack('H*', $pkhs));
$pkhrd = $pkhr->hexdigest();
$pkhrd =~ s/\s//g;
$pkhrde = '00' . $pkhrd;
$pkhrdeh1 = sha256_hex(pack('H*', $pkhrde));
$pkhrdeh2 = sha256_hex(pack('H*', $pkhrdeh1));
$pkhrdeh2c = substr($pkhrdeh2, 0, 8);
$pkhrdec = $pkhrde . $pkhrdeh2c;
$pkb = encodebase58fromhex($pkhrdec);
print "Uncompressed: $pkb\n";
$pkchs = sha256_hex(pack('H*', $pkch));
$pkchr = new Crypt::RIPEMD160;
$pkchr->add(pack('H*', $pkchs));
$pkchrd = $pkchr->hexdigest();
$pkchrd =~ s/\s//g;
$pkchrde = '00' . $pkchrd;
$pkchrdeh1 = sha256_hex(pack('H*', $pkchrde));
$pkchrdeh2 = sha256_hex(pack('H*', $pkchrdeh1));
$pkchrdeh2c = substr($pkchrdeh2, 0, 8);
$pkchrdec = $pkchrde . $pkchrdeh2c;
$pkcb = encodebase58fromhex($pkchrdec);
print "Compressed: $pkcb\n";
}
sub getPubKeyPoints() {
my $k = Math::BigInt->new(shift);
my $a = Math::BigInt->new("0x0");
my $b = Math::BigInt->new("0x7");
my $p = Math::BigInt->new("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f");
my $n = Math::BigInt->new("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
my %g = (
"x" => Math::BigInt->new("0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"),
"y" => Math::BigInt->new("0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"),
);
my %pubkey = mulPoint(\%g, $k, $a, $b, $p);
$pubkey{"x"} = Math::BigInt->new($pubkey{"x"})->as_hex();
$pubkey{"y"} = Math::BigInt->new($pubkey{"y"})->as_hex();
# ====
$pubkey{"x"} = substr($pubkey{"x"}, 2, length($pubkey{"x"}) - 2);
$pubkey{"y"} = substr($pubkey{"y"}, 2, length($pubkey{"y"}) - 2);
while(length($pubkey{'x'}) < 64) {
$pubkey{'x'} = '00' . $pubkey{'x'};
}
while(length($pubkey{'y'}) < 64) {
$pubkey{'x'} = '00' . $pubkey{'x'};
}
# ====
$pubkey{"x"} = Math::BigInt->new("0x" . $pubkey{"x"})->as_hex();
$pubkey{"y"} = Math::BigInt->new("0x" . $pubkey{"y"})->as_hex();
return %pubkey;
}
sub mulPoint {
my %g = %{shift()};
my $k = shift;
my $a = shift;
my $b = shift;
my $p = shift;
my $kb = $k->copy()->as_bin();
$kb = substr($kb, 2, length($kb) - 2);
my $kbl = length($kb);
my %lastpoint = %g;
my %dpt = ();
my $i = 1;
for ($i = 1; $i < $kbl; $i ++) {
if (substr($kb, $i, 1) eq 1) {
%dpt = doublePoint(\%lastpoint, $a, $p);
%lastpoint = addPoints(\%dpt, \%g, $a, $p);
} else {
%lastpoint = doublePoint(\%lastpoint, $a, $p);
}
}
return %lastpoint;
}
sub addPoints {
my %pt1 = %{shift()};
my %pt2 = %{shift()};
my $a = shift;
my $p = shift;
my $pt1x = $pt1{"x"};
my $pt1y = $pt1{"y"};
my $pt2x = $pt2{"x"};
my $pt2y = $pt2{"y"};
my $number2 = Math::BigInt->new(2);
my $number3 = Math::BigInt->new(3);
if (($pt1x->bcmp($pt2x)) eq 0 && ($pt1y->bcmp($pt2y)) eq 0) {
return doublePoint(\%pt1, $a, $p);
}
my $gcd_sub = $pt1x->copy()->bsub($pt2x);
my @gcd_arr = ($gcd_sub, $p);
my $gcd = Math::BigInt::bgcd(@gcd_arr);
if ($gcd != 1) {
print "Possibly INFINITY there, does not know what to do with that\n";
exit;
}
my $pt_sub1 = $pt1x->copy()->bsub($pt2x);
my $pt_inv = $pt_sub1->copy->bmodinv($p);
my $pt_sub2 = $pt1y->copy()->bsub($pt2y);
my $pt_mul = $pt_sub2->copy()->bmul($pt_inv);
my $slope = $pt_mul->copy()->bmod($p);
my %nPt = ();
my $nPtx_pow = $slope->copy()->bpow($number2);
my $nPtx_sub1 = $nPtx_pow->copy()->bsub($pt1x);
my $nPtx_sub2 = $nPtx_sub1->copy()->bsub($pt2x);
my $nPtx_mod = $nPtx_sub2->copy()->bmod($p);
$nPt{"x"} = $nPtx_mod;
my $nPty_sub1 = $pt1x->copy()->bsub($nPtx_mod);
my $nPty_mul = $slope->copy()->bmul($nPty_sub1);
my $nPty_sub2 = $nPty_mul->copy()->bsub($pt1y);
my $nPty_mod = $nPty_sub2->copy()->bmod($p);
$nPt{"y"} = $nPty_mod;
return %nPt;
}
sub doublePoint {
my %pt = %{shift()};
my $a = shift;
my $p = shift;
my $ptx = $pt{"x"};
my $pty = $pt{"y"};
my $number2 = Math::BigInt->new(2);
my $number3 = Math::BigInt->new(3);
my $pt_ymul = $pty->copy()->bmul($number2);
my $pt_ymod = $pt_ymul->copy()->bmod($p);
my $pt_yinv = $pt_ymod->copy()->bmodinv($p);
my $pt_xpow = $ptx->copy()->bpow($number2);
my $pt_xmul = $pt_xpow->copy()->bmul($number3);
my $pt_xadd = $pt_xmul->copy()->badd($a);
my $pt_xymul = $pt_yinv->copy()->bmul($pt_xadd);
my $slope = $pt_xymul->copy()->bmod($p);
my %nPt = ();
my $nPtx_pow = $slope->copy()->bpow($number2);
my $nPtx_sub1 = $nPtx_pow->copy()->bsub($ptx);
my $nPtx_sub2 = $nPtx_sub1->copy()->bsub($ptx);
my $nPtx_mod = $nPtx_sub2->copy()->bmod($p);
$nPt{"x"} = $nPtx_mod;
my $nPty_sub1 = $ptx->copy()->bsub($nPtx_mod);
my $nPty_mul = $slope->copy()->bmul($nPty_sub1);
my $nPty_sub2 = $nPty_mul->copy()->bsub($pty);
my $nPty_mod = $nPty_sub2->copy()->bmod($p);
$nPt{"y"} = $nPty_mod;
return %nPt;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment