Created
August 18, 2016 11:35
-
-
Save TheRayTracer/0dcfe80fe51e5e60b1658932676c7972 to your computer and use it in GitHub Desktop.
This is a reference implementation for generating an offline Bitcoin address.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
import re | |
import binascii | |
import ecdsa | |
import hashlib | |
# import random | |
import Crypto.Random.random as rand | |
b58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" | |
def Base58Encode(n): | |
r = "" | |
while n > 0: | |
r = b58[n % 58] + r | |
n = n // 58 | |
assert n == 0, "Base58 encoding error" | |
return r | |
def Base58Decode(s): | |
r = 0 | |
for i in range(0, len(s), 1): | |
j = b58.index(s[i]) | |
r = r * 58 + j | |
return r | |
def IsStringValidWIF(t, testnet = False): | |
r = True | |
if testnet == False: | |
p = re.compile("^5[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{50}") | |
if p.match(t) == None: | |
r = False | |
else: | |
p = re.compile("^9[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{50}") | |
if p.match(t) == None: | |
r = False | |
return r | |
def PrivateKeyToWIF(s, testnet = False): | |
if testnet == False: | |
wif = bytes([0x80]) + s.to_bytes(32, byteorder = "big") | |
else: | |
wif = bytes([0xef]) + s.to_bytes(32, byteorder = "big") | |
# Calculate checksum by using the first 4 bytes... | |
chk = hashlib.sha256(hashlib.sha256(wif).digest()).digest()[0:4] | |
# Append checksum... | |
wif = wif + chk | |
t = Base58Encode(int.from_bytes(wif, byteorder = "big")) | |
if IsStringValidWIF(t, testnet) == False: | |
raise ValueError("Invalid WIF - Invalid private key format") | |
return t | |
def WIFToPrivateKey(t, testnet = False): | |
if IsStringValidWIF(t, testnet) == False: | |
raise ValueError("Invalid WIF - Invalid private key format") | |
s = Base58Decode(t).to_bytes(37, byteorder = "big") | |
if testnet == False: | |
if s[0:1] != bytes([0x80]): | |
raise ValueError("Invalid WIF - Mainnet prefix not found") | |
else: | |
if s[0:1] != bytes([0xef]): | |
raise ValueError("Invalid WIF - Testnet prefix not found") | |
given_chk = s[-4:] | |
fresh_chk = hashlib.sha256(hashlib.sha256(s[:-4]).digest()).digest()[0:4] | |
if given_chk != fresh_chk: | |
raise ValueError("Invalid WIF - Checksum mismatch") | |
return int(s[1:-4].hex(), 16) | |
def PrivateKeyToPublicKey(n): | |
sky = ecdsa.SigningKey.from_secret_exponent(n, curve = ecdsa.SECP256k1) | |
pub = bytes([0x04]) + sky.verifying_key.to_string() | |
return pub.hex() | |
def PublicKeyToAddressV0(s): | |
ripemd160 = hashlib.new("ripemd160") | |
# Calulate SHA-256 hash on public key... | |
sha = hashlib.sha256(binascii.unhexlify(s)).digest() | |
# Calulate RIPEMD-160 hash... | |
ripemd160.update(sha) | |
# Prepend 0x00 for main network... | |
man = bytes([0x00]) + ripemd160.digest() | |
# Calulate SHA-256 hash from SHA-256 hash from the extended RIPEMD-160 result... | |
chk = hashlib.sha256(hashlib.sha256(man).digest()).digest()[0:4] | |
# Prepare the Bitcoin address... | |
add = bytearray(man) + bytearray(chk) | |
r = "" | |
# Add leading 1's... | |
i = 0 | |
while add[i] == 0 and i < len(add): | |
r = b58[0] + r | |
i = i + 1 | |
r = r + Base58Encode(int.from_bytes(add, byteorder = "big")) | |
return r | |
if __name__ == "__main__": | |
test = False | |
generate = False | |
for arg in sys.argv: | |
if arg == "-t": | |
test = True | |
if arg == "-g": | |
generate = True | |
if test == True: | |
# Walkthrough example from https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses | |
private_key = int("18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725", 16) | |
wif = "5J1F7GHadZG3sCCKHCwg8Jvys9xUbFsjLnGec4H125Ny1V9nR6V" | |
public_key = "0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6" | |
address = "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM" | |
assert PrivateKeyToWIF(private_key) == wif, "PrivateKeyToWIF test failed" | |
assert WIFToPrivateKey(wif) == private_key, "WIFToPrivateKey test failed" | |
assert PrivateKeyToPublicKey(private_key) == public_key, "PrivateKeyToPublicKey test failed" | |
assert PublicKeyToAddressV0(public_key) == address, "PublicKeyToAddressV0 test failed" | |
print("Tests passed.") | |
if generate == True: | |
# private_key = int(''.join(random.SystemRandom().choice("0123456789abcdef") for _ in range(64)), 16) | |
private_key = 0 | |
while private_key < 256: # Ensure we have at least more than a byte of bits. | |
private_key = rand.getrandbits(256) | |
print("Private key:", hex(private_key)[2:].lower()) | |
print("WIF:", PrivateKeyToWIF(private_key)) | |
print("Address:", PublicKeyToAddressV0(PrivateKeyToPublicKey(private_key))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The following was used to build and install pycrypto v2.6.1 for Python v3.5. | |
1. Download from https://www.dlitz.net/software/pycrypto/ | |
2. Unpack to a location | |
3. Go to the location directory | |
4. Run the following command to build an installation package for the module: python setup.py bdist_wininst | |
5. Run the installation package build in the previous step fround in the /dist/ directory | |
Attempt to use pycrypto | |
If using the pycrypto fails with "ImportError: No module named 'winrandom'" then: | |
5. Go to \lib\Crypto\Random\OSRNG | |
6. Edit the nt.py file by changing the line: "import winrandom" to "from . import winrandom" | |
7. Go to step 4 to rebuild the installation package (and try again) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment