Skip to content

Instantly share code, notes, and snippets.

/sample.py Secret

Created May 10, 2013 15:19
Show Gist options
  • Save anonymous/da91bd30fa068ae4a3bb to your computer and use it in GitHub Desktop.
Save anonymous/da91bd30fa068ae4a3bb to your computer and use it in GitHub Desktop.
some code from pybitaddress
# coding=utf-8
# Donation: 1HFhtvzNEiy9eooLSJRMyZ1XB2FXgYGvCH
#
import hashlib, re
from binascii import unhexlify as hexdecode, hexlify as hexencode
import binascii
import base64
if str != bytes:
# Python 3.x
def ord(c):
return c
def chr(n):
return bytes( (n,) )
P = 2**256-2**32-2**9-2**8-2**7-2**6-2**4-1
A = 0
B = 7
Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240
Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424
N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
G = (Gx,Gy)
def inv(a,n):
lm, hm = 1,0
low, high = a%n,n
while low > 1:
r = high // low
nm, new = hm-lm*r, high-low*r
lm, low, hm, high = nm, new, lm, low
return lm % n
def get_code_string(base):
if base == 2: return '01'
elif base == 10: return '0123456789'
elif base == 16: return '0123456789abcdef'
elif base == 58: return '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
else: raise ValueError("Invalid base!")
def encode(val,base,minlen=0):
if val < 0: raise Exception('Minus?!')
if base in [16, 256]:
s = hex(val)[2:].rstrip('L')
if len(s) % 2: s = '0' + s
if base == 256: # code_string is a hack...
result = binascii.unhexlify(s)
code_string = b'\x00'
elif base == 16:
result = s
code_string = '0'
else:
code_string = get_code_string(base)
result = ''
while val > 0:
result = code_string[val % base] + result
val //= base # py3 division
if len(result) < minlen:
result = chr(ord(code_string[0]))*(minlen-len(result))+result
return result
def decode(string,base):
if not string: return 0
if base in [2,8,10,16]: return int(string, base)
if base == 256: return int(hexencode(string), 16)
code_string = str(get_code_string(base))
if base == 16: string = string.lower()
result = 0
for c in string:
result *= base
result += code_string.find(c)
return result
def changebase(string,frm,to,minlen=0):
return encode(decode(string,frm),to,minlen)
def base10_add(a,b):
if a == None: return (b[0],b[1])
if b == None: return (a[0],a[1])
if a[0] == b[0]:
if a[1] == b[1]: return base10_double((a[0],a[1]))
else: return None
m = ((b[1]-a[1]) * inv(b[0]-a[0],P)) % P
x = (m*m-a[0]-b[0]) % P
y = (m*(a[0]-x)-a[1]) % P
return (x,y)
def base10_double(a):
if a == None: return None
m = ((3*a[0]*a[0]+A)*inv(2*a[1],P)) % P
x = (m*m-2*a[0]) % P
y = (m*(a[0]-x)-a[1]) % P
return (x,y)
def base10_multiply(a,n):
if n == 0: return G
if n == 1: return a
d = base10_double(base10_multiply(a,n//2)) # py3 division
if (n%2) == 0: return d
if (n%2) == 1: return base10_add(d,a)
def hex_to_point(h):
if len(h) == 130:
return (decode(h[2:66],16),decode(h[66:],16))
elif len(h) == 128:
return (decode(h[0:64],16),decode(h[64:],16))
else:
raise Exception('What kind of point (you cant use compressed one)?!')
def point_to_hex(p):
if p[0] == 0 and p[1] == 0: raise Exception('Bad point?!')
return '04%064x%064x' % (p[0], p[1])
def pubkey_compress(p):
if len(p) != 130 or p[0:2] != '04': raise Exception('Bad pubkey')
if int(p[-1],16) & 1: # y & 1 is 03 type
return '03' + p[2:66]
else:
return '02' + p[2:66]
def add(p1,p2):
if len(p1)==64:
return '%032x' % (decode(p1,16) + decode(p2,16) % N)
else:
return point_to_hex(base10_add(hex_to_point(p1),hex_to_point(p2)))
# Note: timing attack? actually i * j % N is also ok I think.
def multiply(i, j):
n = i
r = 0
for bit in range(256):
if (j & (1 << bit)):
r = (r + n) % N
n = (n + n) % N
return r
def decodepriv(priv):
if len(priv) != 64: raise Exception('Short privkey?')
p = decode(priv,16) # you cant use any privkey >= N
if p >= N or p == 0: raise Exception('Insecure privkey!')
return p
# x -> Gx
def privtopub(privkey, compressed=False):
if len(privkey) == 51:
privkey = keytopriv(privkey)
pub = point_to_hex(base10_multiply(G,decodepriv(privkey)))
if compressed: pub = pubkey_compress(pub)
return pub
def hash160(string):
intermed = hashlib.sha256(string).digest()
ripemd160 = hashlib.new('ripemd160') # Note: may require openssl
ripemd160.update(intermed)
return ripemd160.digest()
class SHA256:
new = hashlib.sha256
def dbl_sha256(string):
return SHA256.new(SHA256.new(string).digest()).digest()
def b58encode(inp):
leadingzbytes = len(inp) - len(inp.lstrip(b'\x00'))
return '1' * leadingzbytes + changebase(inp,256,58)
def b58decode(inp):
leadingzbytes = len(inp) - len(inp.lstrip('1'))
if leadingzbytes == len(inp): return '\x00' * leadingzbytes
return b'\x00' * leadingzbytes + changebase(inp, 58, 256)
# see https://en.bitcoin.it/wiki/List_of_address_prefixes
def bin_to_b58check(inp, prefix=0, compressed=False):
if prefix < 0 or prefix > 255: raise Exception('Bad address type')
inp_fmtd = chr(prefix) + inp
if compressed: inp_fmtd += b'\x01'
checksum = dbl_sha256(inp_fmtd)[:4]
return b58encode(inp_fmtd+checksum)
# 25 is required as 00 is the address type number.
# when do the priv key validation, 25 is not required.
def verify_b58(inp, length=25, decode=False):
dec = b58decode(inp)
if len(dec) < length: dec = '\x00' * (length - len(dec)) + dec
checksum1 = dec[-4:]
checksum2 = dbl_sha256(dec[:-4])[:4]
if not decode:
return checksum1 == checksum2
if checksum1 == checksum2:
return dec[:-4]
return None
def addrtohash160(inp):
r = verify_b58(inp, length=25, decode=True)
if r: r = hexencode(r[1:25]) # hash160 is 24 bytes.
return r
# the compressed '\x01' should only appear in private key
def privtokey(inp, addrtype=0, compressed=False):
decodepriv(inp) # do a test
return bin_to_b58check(hexdecode(inp), addrtype+128, compressed)
def validate_pub(pubkey, compressed=False):
l = len(pubkey)
if l == 128: pubkey = '04' + pubkey
if l == 66 and pubkey[0:2] in ['02', '03']:
compressed = True
elif l == 130:
if compressed:
pubkey = pubkey_compress(pubkey)
else:
raise Exception('Bad pubkey length?')
return pubkey, compressed
def pubtoaddr(pubkey, prefix=0, compressed=False):
pubkey, compressed = validate_pub(pubkey, compressed)
return bin_to_b58check(hash160(hexdecode(pubkey)), prefix)
def keytopriv(inp, toaddr=False, addrtype=None):
r = verify_b58(inp, 33, True)
if not r: return None
if len(r) == 34 and ord(r[33]) != 1: # compressed key is key + 0x01
raise Exception('Bad compressed key?')
if len(r) in [33, 34]:
priv = changebase(r[1:33],256,16,64) # compressed key are stripped
if toaddr:
if addrtype is None: addrtype = ord(r[0])
return pubtoaddr(privtopub(priv, len(r) == 34), addrtype)
return priv
return None
def keytoaddr(inp, forceaddrtype=None):
return keytopriv(inp, True, forceaddrtype)
try:
from Crypto.Random import random
except ImportError:
import random
print('WARNING: Weak Random Source is Used')
# return r,s
def sign_sig(h, priv, k=0):
if len(priv) == [51, 52]:
priv = keytopriv(priv)
# Note: we should not have random imported... :(
if not random: raise Exception('No cryptographically strong random found')
if k == 0: k = random.randrange(1,N)
h = decode(h, 16)
gk = base10_multiply(G, k)
s = multiply(inv(k, N), (h + multiply(gk[0], decodepriv(priv))) % N)
if gk[0] == 0 or s == 0: return sign_sig(s, priv) # regenerate
return encode(gk[0],16,32), encode(s,16,32)
def verify_sig(h, pub, r, s):
p_pub = hex_to_point(pub)
h = decode(h, 16)
r = decode(r, 16)
s = decode(s, 16)
if r < 1 or r >= N: return False
if s < 1 or s >= N: return False
c = inv(s, N)
u1, u2 = (h * c) % N, (r * c) % N
p = base10_add(base10_multiply(G, u1), base10_multiply(p_pub, u2))
return (p[0] % N) == r
# actually it extract pubkey / compressed from sig
def verify_message_raw(sig, h):
nV = ord(sig[0])
r = decode(sig[1:33], 256)
s = decode(sig[33:65], 256)
if nV < 27 or nV >= 35: return False
compressed = False
if nV >= 31:
compressed = True
nV -= 4
recid = nV - 27
# Note: as y^2 = x^3 + Ax + B, there are two possible y.
# and x is also possible in negative?
x = r + (recid // 2) * N
alpha = (x ** 3 + A * x + B) % P
# Note: should be sqrt(a) of mod P
# as long as P % 4 == 3 in secp256k1, so this can be used.
# otherwise use http://eli.thegreenplace.net/2009/03/07/computing-modular-square-roots-in-python/
beta = pow(alpha, (P + 1) // 4, P)
if (beta - recid) & 1:
y = P - beta
else:
y = beta
R = (x, y)
e = decode(h, 16)
# Q = inv_r * ( s * R + minus_e * G)
Q = base10_multiply(base10_add(base10_multiply(G, -e % N),
base10_multiply(R, s)), inv(r, N))
pub = point_to_hex(Q)
res = verify_sig(h, pub, encode(r, 16), encode(s, 16))
if res: return (pub, compressed)
return None
def verify_message(sig, h, addrtype=0):
try:
sig = base64.b64decode(sig)
if len(sig) != 65: return False
except ValueError:
return False
res = verify_message_raw(sig, h) # pub, compressed
if res is None: return False
return pubtoaddr(res[0], addrtype, res[1])
def varint(i):
if i < 0xfd: return chr(i)
if i < 0xffff: return b'\xfd' + encode(i, 256, 2)[::-1]
if i < 0xffffffff: return b'\xfe' + encode32(i)
return b'\xff' + encode64(i)
# TODO: verify large message
def msg_magic(message):
if isinstance(message, unicode):
message = message.encode('utf-8')
return b"\x18Bitcoin Signed Message:\n" + varint( len(message) ) + message
def hash_msg(message):
return hexencode(dbl_sha256(msg_magic(message)))
# Make sure you verify it is True
def verify_message_addr(sig, message, addr):
sign_addr = verify_message(sig, hash_msg(message))
if sign_addr is None or sign_addr is False: return False
if sign_addr == addr: return True
return sign_addr
# return addr, sig
def sign_message(message, priv, compressed=False, addrtype=0):
h = hash_msg(message)
r, s = sign_sig(h, priv)
# res1 = verify_sig(h, privtopub(priv), r, s)
# if not res1: return False # WTF?!
sign = hexdecode(r) + hexdecode(s) # hex -> bin
addr = pubtoaddr(privtopub(priv), addrtype, compressed)
for nV in range(27, 31): # 27 to 30
if compressed: nV += 4
sig = base64.b64encode( chr(nV) + sign)
res = verify_message(sig, h)
if res == addr: return addr, sig
#raise Exception('Cant sign')
return None
if __name__ == '__main__':
print verify_message_addr('Gzt1sEVYktwv2Tm4nHHvUL+hQ6/ujgzbIoTPBX97g+lA+3Ry5NvDVJTblTpHdO/4IgUpWrOa3BtLxrqha5RypSo=', "C'est par mon ordre et pour le bien de l'Etat que le porteur du présent a fait ce qu'il a fait.", '17mDAmveV5wBwxajBsY7g1trbMW1DVWcgL')
print sign_message("C'est par mon ordre et pour le bien de l'Etat que le porteur du présent a fait ce qu'il a fait.",keytopriv('5JeWZ1z6sRcLTJXdQEDdB986E6XfLAkj9CgNE4EHzr5GmjrVFpf'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment