|
| 1 | +#!/usr/bin/env python |
| 2 | +# https://pastebin.com/4Lmf25gK |
| 3 | +import ctypes |
| 4 | +import hashlib |
| 5 | +from base58 import encode as base58_encode |
| 6 | + |
| 7 | +################################################################################ |
| 8 | +################################################################################ |
| 9 | +ssl_library = ctypes.cdll.LoadLibrary('libssl.so') |
| 10 | + |
| 11 | +def gen_ecdsa_pair(): |
| 12 | + NID_secp160k1 = 708 |
| 13 | + NID_secp256k1 = 714 |
| 14 | + k = ssl_library.EC_KEY_new_by_curve_name(NID_secp256k1) |
| 15 | + |
| 16 | + if ssl_library.EC_KEY_generate_key(k) != 1: |
| 17 | + raise Exception("internal error?") |
| 18 | + |
| 19 | + bignum_private_key = ssl_library.EC_KEY_get0_private_key(k) |
| 20 | + size = (ssl_library.BN_num_bits(bignum_private_key)+7)//8 |
| 21 | + #print("Key size is {} bytes".format(size)) |
| 22 | + storage = ctypes.create_string_buffer(size) |
| 23 | + ssl_library.BN_bn2bin(bignum_private_key, storage) |
| 24 | + private_key = storage.raw |
| 25 | + |
| 26 | + size = ssl_library.i2o_ECPublicKey(k, 0) |
| 27 | + #print("Pubkey size is {} bytes".format(size)) |
| 28 | + storage = ctypes.create_string_buffer(size) |
| 29 | + ssl_library.i2o_ECPublicKey(k, ctypes.byref(ctypes.pointer(storage))) |
| 30 | + public_key = storage.raw |
| 31 | + |
| 32 | + ssl_library.EC_KEY_free(k) |
| 33 | + return public_key, private_key |
| 34 | + |
| 35 | +def ecdsa_get_coordinates(public_key): |
| 36 | + x = bytes(public_key[1:33]) |
| 37 | + y = bytes(public_key[33:65]) |
| 38 | + return x, y |
| 39 | + |
| 40 | +def generate_address(public_key): |
| 41 | + assert isinstance(public_key, bytes) |
| 42 | + |
| 43 | + x, y = ecdsa_get_coordinates(public_key) |
| 44 | + |
| 45 | + s = b'\x04' + x + y |
| 46 | + #print('0x04 + x + y:', ''.join(['{:02X}'.format(i) for i in s])) |
| 47 | + |
| 48 | + hasher = hashlib.sha256() |
| 49 | + hasher.update(s) |
| 50 | + r = hasher.digest() |
| 51 | + #print('SHA256(0x04 + x + y):', ''.join(['{:02X}'.format(i) for i in r])) |
| 52 | + |
| 53 | + hasher = hashlib.new('ripemd160') |
| 54 | + hasher.update(r) |
| 55 | + r = hasher.digest() |
| 56 | + #print('RIPEMD160(SHA256(0x04 + x + y)):', ''.join(['{:02X}'.format(i) for i in r])) |
| 57 | + |
| 58 | + # Since '1' is a zero byte, it won't be present in the output address. |
| 59 | + return '1' + base58_check(r, version=0) |
| 60 | + |
| 61 | +def base58_check(src, version=0): |
| 62 | + src = bytes([version]) + src |
| 63 | + hasher = hashlib.sha256() |
| 64 | + hasher.update(src) |
| 65 | + r = hasher.digest() |
| 66 | + #print('SHA256(0x00 + r):', ''.join(['{:02X}'.format(i) for i in r])) |
| 67 | + |
| 68 | + hasher = hashlib.sha256() |
| 69 | + hasher.update(r) |
| 70 | + r = hasher.digest() |
| 71 | + #print('SHA256(SHA256(0x00 + r)):', ''.join(['{:02X}'.format(i) for i in r])) |
| 72 | + |
| 73 | + checksum = r[:4] |
| 74 | + s = src + checksum |
| 75 | + #print('src + checksum:', ''.join(['{:02X}'.format(i) for i in s])) |
| 76 | + |
| 77 | + return base58_encode(int.from_bytes(s, 'big')) |
| 78 | + |
| 79 | +def test(): |
| 80 | + public_key, private_key = gen_ecdsa_pair() |
| 81 | + |
| 82 | + hex_private_key = ''.join(["{:02x}".format(i) for i in private_key]) |
| 83 | + assert len(hex_private_key) == 64 |
| 84 | + |
| 85 | + print("ECDSA private key (random number / secret exponent) = {}".format(hex_private_key)) |
| 86 | + print("ECDSA public key = {}".format(''.join(['{:02x}'.format(i) for i in public_key]))) |
| 87 | + print("Bitcoin private key (Base58Check) = {}".format(base58_check(private_key, version=128))) |
| 88 | + |
| 89 | + addr = generate_address(public_key) |
| 90 | + print("Bitcoin Address: {} (length={})".format(addr, len(addr))) |
| 91 | + |
| 92 | +if __name__ == "__main__": |
| 93 | + test() |
0 commit comments