-
-
Save anonymous/da91bd30fa068ae4a3bb to your computer and use it in GitHub Desktop.
some code from pybitaddress
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
# 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