-
Notifications
You must be signed in to change notification settings - Fork 12
/
btcwif.py
139 lines (125 loc) · 5.38 KB
/
btcwif.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
'''
Title: btcwif
Version: 0.1
Author: Carlo Cervellin
Website: carlo.cervellin.eu
Python version: 3.6.0
=== INFO ===
This library allows you to get the WIF (wallet import format) from a private ECDSA key and reverse.
algorithms from https://en.bitcoin.it/wiki/Wallet_import_format
=== USAGE (example) ===
>>> import btcwif
>>> priv = "0C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D"
>>> wif = btcwif.privToWif(priv)
>>> wif
'5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'
>>> priv1 = btcwif.wifToPriv(wif)
>>> priv1
'0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d'
>>> btcwif.wifChecksum(wif)
True
>>> wif1 = "6HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ" # invalid WIF
>>> btcwif.wifChecksum(wif1)
False
>>> btcwif.wifToPriv(wif1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "E:\Programming\bitcoin\btcwif.py", line 86, in wifToPriv
if not wifChecksum(wif) : raise Exception('The WIF is not correct (does not pass checksum)')
Exception: The WIF is not correct (does not pass checksum)
'''
import hashlib
# base58 alphabet
alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def sha256(arg) :
''' Return a sha256 hash of a hex string '''
byte_array = bytearray.fromhex(arg)
m = hashlib.sha256()
m.update(byte_array)
return m.hexdigest()
def b58encode(hex_string) :
''' Return a base58 encoded string from hex string '''
num = int(hex_string, 16)
encode = ""
base_count = len(alphabet)
while (num > 0) :
num, res = divmod(num,base_count)
encode = alphabet[res] + encode
return encode
def b58decode(v):
''' Decode a Base58 encoded string as an integer and return a hex string '''
if not isinstance(v, str):
v = v.decode('ascii')
decimal = 0
for char in v:
decimal = decimal * 58 + alphabet.index(char)
return hex(decimal)[2:] # (remove "0x" prefix)
def privToWif(priv, verbose=False) :
''' Produce a WIF from a private key in the form of an hex string '''
# 1 - Take a private key
_priv = priv.lower() # just for aesthetics
if verbose : print("Private key: "+_priv)
# 2 - Add a 0x80 byte in front of it
priv_add_x80 = "80" + _priv
if verbose : print("Private with x80 at beginning: "+priv_add_x80)
# 3 - Perform SHA-256 hash on the extended key
first_sha256 = sha256(priv_add_x80)
if verbose : print("sha256: " + first_sha256.upper())
# 4 - Perform SHA-256 hash on result of SHA-256 hash
seconf_sha256 = sha256(first_sha256)
if verbose : print("sha256: " + seconf_sha256.upper())
# 5 - Take the first 4 bytes of the second SHA-256 hash, this is the checksum
first_4_bytes = seconf_sha256[0:8]
if verbose : print("First 4 bytes: " + first_4_bytes)
# 6 - Add the 4 checksum bytes from point 5 at the end of the extended key from point 2
resulting_hex = priv_add_x80 + first_4_bytes
if verbose : print("Resulting WIF in HEX: " + resulting_hex)
# 7 - Convert the result from a byte string into a base58 string using Base58Check encoding. This is the Wallet Import Format
result_wif = b58encode(resulting_hex)
if verbose : print("Resulting WIF: " + result_wif)
return result_wif
def wifToPriv(wif, verbose=False) :
''' Produce the private ECDSA key in the form of a hex string from a WIF string '''
if not wifChecksum(wif) : raise Exception('The WIF is not correct (does not pass checksum)')
# 1 - Take a Wallet Import Format string
if verbose : print("WIF: " + wif)
# 2 - Convert it to a byte string using Base58Check encoding
byte_str = b58decode(wif)
if verbose : print("WIF base58 decoded: " + byte_str)
# 3 - Drop the last 4 checksum bytes from the byte string
byte_str_drop_last_4bytes = byte_str[0:-8]
if verbose : print("Decoded WIF drop last 4 bytes: " + byte_str_drop_last_4bytes)
# 4 - Drop the first byte
byte_str_drop_first_byte = byte_str_drop_last_4bytes[2:]
if verbose : print("ECDSA private key: " + byte_str_drop_first_byte)
return byte_str_drop_first_byte
def wifChecksum(wif, verbose=False) :
''' Returns True if the WIF is positive to the checksum, False otherwise '''
# 1 - Take the Wallet Import Format string
if verbose : print("WIF: " + wif)
# 2 - Convert it to a byte string using Base58Check encoding
byte_str = b58decode(wif)
if verbose : print("WIF base58 decoded: " + byte_str)
# 3 - Drop the last 4 checksum bytes from the byte string
byte_str_drop_last_4bytes = byte_str[0:-8]
if verbose : print("Decoded WIF drop last 4 bytes: " + byte_str_drop_last_4bytes)
# 3 - Perform SHA-256 hash on the shortened string
sha_256_1 = sha256(byte_str_drop_last_4bytes)
if verbose : print("SHA256 1: " + sha_256_1)
# 4 - Perform SHA-256 hash on result of SHA-256 hash
sha_256_2 = sha256(sha_256_1)
if verbose : print("SHA256 2: " + sha_256_2)
# 5 - Take the first 4 bytes of the second SHA-256 hash, this is the checksum
first_4_bytes = sha_256_2[0:8]
if verbose : print("First 4 bytes: " + first_4_bytes)
# 6 - Make sure it is the same, as the last 4 bytes from point 2
last_4_bytes_WIF = byte_str[-8:]
if verbose : print("Last 4 bytes of WIF: " + last_4_bytes_WIF)
bytes_check = False
if first_4_bytes == last_4_bytes_WIF : bytes_check = True
if verbose : print("4 bytes check: " + str(bytes_check))
# 7 - If they are, and the byte string from point 2 starts with 0x80 (0xef for testnet addresses), then there is no error.
check_sum = False
if bytes_check and byte_str[0:2] == "80" : check_sum = True
if verbose : print("Checksum: " + str(check_sum))
return check_sum