Skip to content

Instantly share code, notes, and snippets.

@TheRayTracer
Created August 18, 2016 11:35
Show Gist options
  • Save TheRayTracer/0dcfe80fe51e5e60b1658932676c7972 to your computer and use it in GitHub Desktop.
Save TheRayTracer/0dcfe80fe51e5e60b1658932676c7972 to your computer and use it in GitHub Desktop.
This is a reference implementation for generating an offline Bitcoin address.
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)))
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