You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

474 lines
16 KiB

#!/usr/bin/python
from __future__ import print_function
import binascii
import hashlib
import re
import sys
import os
import base64
import time
import random
import hmac
import secp256k1
#Required only for PoDLE calculation:
N = 115792089237316195423570985008687907852837564279074904382605163141518161494337
#Global context for secp256k1 operations (helps with performance)
ctx = secp256k1.lib.secp256k1_context_create(secp256k1.ALL_FLAGS)
#required for point addition
dummy_pub = secp256k1.PublicKey(ctx=ctx)
#Standard prefix for Bitcoin message signing.
BITCOIN_MESSAGE_MAGIC = '\x18' + 'Bitcoin Signed Message:\n'
"""A custom nonce function acting as a pass-through.
Only used for reusable donation pubkeys (stealth).
"""
from cffi import FFI
ffi = FFI()
ffi.cdef('static int nonce_function_rand(unsigned char *nonce32,'
'const unsigned char *msg32,const unsigned char *key32,'
'const unsigned char *algo16,void *data,unsigned int attempt);')
ffi.set_source("_noncefunc",
"""
static int nonce_function_rand(unsigned char *nonce32,
const unsigned char *msg32,
const unsigned char *key32,
const unsigned char *algo16,
void *data,
unsigned int attempt)
{
memcpy(nonce32,data,32);
return 1;
}
""")
ffi.compile()
import _noncefunc
from _noncefunc import ffi
if sys.version_info.major == 2:
string_types = (str, unicode)
string_or_bytes_types = string_types
int_types = (int, float, long)
# Base switching
code_strings = {
2: '01',
10: '0123456789',
16: '0123456789abcdef',
32: 'abcdefghijklmnopqrstuvwxyz234567',
58: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
256: ''.join([chr(x) for x in range(256)])
}
def bin_dbl_sha256(s):
bytes_to_hash = from_string_to_bytes(s)
return hashlib.sha256(hashlib.sha256(bytes_to_hash).digest()).digest()
def lpad(msg, symbol, length):
if len(msg) >= length:
return msg
return symbol * (length - len(msg)) + msg
def get_code_string(base):
if base in code_strings:
return code_strings[base]
else:
raise ValueError("Invalid base!")
def changebase(string, frm, to, minlen=0):
if frm == to:
return lpad(string, get_code_string(frm)[0], minlen)
return encode(decode(string, frm), to, minlen)
def bin_to_b58check(inp, magicbyte=0):
inp_fmtd = chr(int(magicbyte)) + inp
leadingzbytes = len(re.match('^\x00*', inp_fmtd).group(0))
checksum = bin_dbl_sha256(inp_fmtd)[:4]
return '1' * leadingzbytes + changebase(inp_fmtd + checksum, 256, 58)
def bytes_to_hex_string(b):
return b.encode('hex')
def safe_from_hex(s):
return s.decode('hex')
def from_int_to_byte(a):
return chr(a)
def from_byte_to_int(a):
return ord(a)
def from_string_to_bytes(a):
return a
def safe_hexlify(a):
return binascii.hexlify(a)
def encode(val, base, minlen=0):
base, minlen = int(base), int(minlen)
code_string = get_code_string(base)
result = ""
while val > 0:
result = code_string[val % base] + result
val //= base
return code_string[0] * max(minlen - len(result), 0) + result
def decode(string, base):
base = int(base)
code_string = get_code_string(base)
result = 0
if base == 16:
string = string.lower()
while len(string) > 0:
result *= base
result += code_string.find(string[0])
string = string[1:]
return result
else:
raise NotImplementedError("Only Python2 currently supported by btc interface")
def tweak_mul(point, scalar):
"""Temporary hack because Windows binding had a bug in tweak_mul.
Can be removed when Windows binding is updated.
"""
return secp256k1._tweak_public(point,
secp256k1.lib.secp256k1_ec_pubkey_tweak_mul,
scalar)
"""PoDLE related primitives
"""
def getG(compressed=True):
"""Returns the public key binary
representation of secp256k1 G
"""
priv = "\x00"*31 + "\x01"
G = secp256k1.PrivateKey(priv, ctx=ctx).pubkey.serialize(compressed)
return G
podle_PublicKey_class = secp256k1.PublicKey
podle_PrivateKey_class = secp256k1.PrivateKey
def podle_PublicKey(P):
"""Returns a PublicKey object from a binary string
"""
return secp256k1.PublicKey(P, raw=True, ctx=ctx)
def podle_PrivateKey(priv):
"""Returns a PrivateKey object from a binary string
"""
return secp256k1.PrivateKey(priv, ctx=ctx)
def privkey_to_address(priv, from_hex=True, magicbyte=0):
return pubkey_to_address(privkey_to_pubkey(priv, from_hex), magicbyte)
privtoaddr = privkey_to_address
# Hashes
def bin_hash160(string):
intermed = hashlib.sha256(string).digest()
return hashlib.new('ripemd160', intermed).digest()
def hash160(string):
return safe_hexlify(bin_hash160(string))
def bin_sha256(string):
binary_data = string if isinstance(string, bytes) else bytes(string,
'utf-8')
return hashlib.sha256(binary_data).digest()
def sha256(string):
return bytes_to_hex_string(bin_sha256(string))
def bin_dbl_sha256(s):
bytes_to_hash = from_string_to_bytes(s)
return hashlib.sha256(hashlib.sha256(bytes_to_hash).digest()).digest()
def dbl_sha256(string):
return safe_hexlify(bin_dbl_sha256(string))
def hash_to_int(x):
if len(x) in [40, 64]:
return decode(x, 16)
return decode(x, 256)
def num_to_var_int(x):
x = int(x)
if x < 253: return from_int_to_byte(x)
elif x < 65536: return from_int_to_byte(253) + encode(x, 256, 2)[::-1]
elif x < 4294967296: return from_int_to_byte(254) + encode(x, 256, 4)[::-1]
else: return from_int_to_byte(255) + encode(x, 256, 8)[::-1]
def message_sig_hash(message):
"""Used for construction of signatures of
messages, intended to be compatible with Bitcoin Core.
"""
padded = BITCOIN_MESSAGE_MAGIC + num_to_var_int(len(
message)) + from_string_to_bytes(message)
return bin_dbl_sha256(padded)
# Encodings
def b58check_to_bin(inp):
leadingzbytes = len(re.match('^1*', inp).group(0))
data = b'\x00' * leadingzbytes + changebase(inp, 58, 256)
assert bin_dbl_sha256(data[:-4])[:4] == data[-4:]
return data[1:-4]
def get_version_byte(inp):
leadingzbytes = len(re.match('^1*', inp).group(0))
data = b'\x00' * leadingzbytes + changebase(inp, 58, 256)
assert bin_dbl_sha256(data[:-4])[:4] == data[-4:]
return ord(data[0])
def hex_to_b58check(inp, magicbyte=0):
return bin_to_b58check(binascii.unhexlify(inp), magicbyte)
def b58check_to_hex(inp):
return safe_hexlify(b58check_to_bin(inp))
def pubkey_to_address(pubkey, magicbyte=0):
if len(pubkey) in [66, 130]:
return bin_to_b58check(
bin_hash160(binascii.unhexlify(pubkey)), magicbyte)
return bin_to_b58check(bin_hash160(pubkey), magicbyte)
pubtoaddr = pubkey_to_address
def wif_compressed_privkey(priv, vbyte=0):
"""Convert privkey in hex compressed to WIF compressed
"""
if len(priv) != 66:
raise Exception("Wrong length of compressed private key")
if priv[-2:] != '01':
raise Exception("Private key has wrong compression byte")
return bin_to_b58check(binascii.unhexlify(priv), 128 + int(vbyte))
def from_wif_privkey(wif_priv, compressed=True, vbyte=0):
"""Convert WIF compressed privkey to hex compressed.
Caller specifies the network version byte (0 for mainnet, 0x6f
for testnet) that the key should correspond to; if there is
a mismatch an error is thrown. WIF encoding uses 128+ this number.
"""
bin_key = b58check_to_bin(wif_priv)
claimed_version_byte = get_version_byte(wif_priv)
if not 128+vbyte == claimed_version_byte:
raise Exception(
"WIF key version byte is wrong network (mainnet/testnet?)")
if compressed and not len(bin_key) == 33:
raise Exception("Compressed private key is not 33 bytes")
if compressed and not bin_key[-1] == '\x01':
raise Exception("Private key has incorrect compression byte")
return safe_hexlify(bin_key)
def ecdsa_sign(msg, priv, usehex=True):
hashed_msg = message_sig_hash(msg)
if usehex:
#arguments to raw sign must be consistently hex or bin
hashed_msg = binascii.hexlify(hashed_msg)
sig = ecdsa_raw_sign(hashed_msg, priv, usehex, rawmsg=True)
#note those functions only handles binary, not hex
if usehex:
sig = binascii.unhexlify(sig)
return base64.b64encode(sig)
def ecdsa_verify(msg, sig, pub, usehex=True):
hashed_msg = message_sig_hash(msg)
sig = base64.b64decode(sig)
if usehex:
#arguments to raw_verify must be consistently hex or bin
hashed_msg = binascii.hexlify(hashed_msg)
sig = binascii.hexlify(sig)
return ecdsa_raw_verify(hashed_msg, pub, sig, usehex, rawmsg=True)
#Use secp256k1 to handle all EC and ECDSA operations.
#Data types: only hex and binary.
#Compressed and uncompressed private and public keys.
def hexbin(func):
'''To enable each function to 'speak' either hex or binary,
requires that the decorated function's final positional argument
is a boolean flag, True for hex and False for binary.
'''
def func_wrapper(*args, **kwargs):
if args[-1]:
newargs = []
for arg in args[:-1]:
if isinstance(arg, (list, tuple)):
newargs += [[x.decode('hex') for x in arg]]
else:
newargs += [arg.decode('hex')]
newargs += [False]
returnval = func(*newargs, **kwargs)
if isinstance(returnval, bool):
return returnval
else:
return binascii.hexlify(returnval)
else:
return func(*args, **kwargs)
return func_wrapper
def read_privkey(priv):
if len(priv) == 33:
if priv[-1] == '\x01':
compressed = True
else:
raise Exception("Invalid private key")
elif len(priv) == 32:
compressed = False
else:
raise Exception("Invalid private key")
return (compressed, priv[:32])
@hexbin
def privkey_to_pubkey_inner(priv, usehex):
'''Take 32/33 byte raw private key as input.
If 32 bytes, return compressed (33 byte) raw public key.
If 33 bytes, read the final byte as compression flag,
and return compressed/uncompressed public key as appropriate.'''
compressed, priv = read_privkey(priv)
#secp256k1 checks for validity of key value.
newpriv = secp256k1.PrivateKey(privkey=priv, ctx=ctx)
return newpriv.pubkey.serialize(compressed=compressed)
def privkey_to_pubkey(priv, usehex=True):
'''To avoid changing the interface from the legacy system,
allow an *optional* hex argument here (called differently from
maker/taker code to how it's called in bip32 code), then
pass to the standard hexbin decorator under the hood.
'''
return privkey_to_pubkey_inner(priv, usehex)
privtopub = privkey_to_pubkey
@hexbin
def multiply(s, pub, usehex, rawpub=True, return_serialized=True):
'''Input binary compressed pubkey P(33 bytes)
and scalar s(32 bytes), return s*P.
The return value is a binary compressed public key,
or a PublicKey object if return_serialized is False.
Note that the called function does the type checking
of the scalar s.
('raw' options passed in)
'''
newpub = secp256k1.PublicKey(pub, raw=rawpub, ctx=ctx)
#see note to "tweak_mul" function in podle.py
res = secp256k1._tweak_public(newpub,
secp256k1.lib.secp256k1_ec_pubkey_tweak_mul,
s)
if not return_serialized:
return res
return res.serialize()
@hexbin
def add_pubkeys(pubkeys, usehex):
'''Input a list of binary compressed pubkeys
and return their sum as a binary compressed pubkey.'''
r = secp256k1.PublicKey(ctx=ctx) #dummy holding object
pubkey_list = [secp256k1.PublicKey(x,
raw=True,
ctx=ctx).public_key for x in pubkeys]
r.combine(pubkey_list)
return r.serialize()
@hexbin
def add_privkeys(priv1, priv2, usehex):
'''Add privkey 1 to privkey 2.
Input keys must be in binary either compressed or not.
Returned key will have the same compression state.
Error if compression state of both input keys is not the same.'''
y, z = [read_privkey(x) for x in [priv1, priv2]]
if y[0] != z[0]:
raise Exception("cannot add privkeys, mixed compression formats")
else:
compressed = y[0]
newpriv1, newpriv2 = (y[1], z[1])
p1 = secp256k1.PrivateKey(newpriv1, raw=True, ctx=ctx)
res = p1.tweak_add(newpriv2)
if compressed:
res += '\x01'
return res
@hexbin
def ecdsa_raw_sign(msg,
priv,
usehex,
rawpriv=True,
rawmsg=False,
usenonce=None):
'''Take the binary message msg and sign it with the private key
priv.
By default priv is just a 32 byte string, if rawpriv is false
it is assumed to be DER encoded.
If rawmsg is True, no sha256 hash is applied to msg before signing.
In this case, msg must be a precalculated hash (256 bit).
If rawmsg is False, the secp256k1 lib will hash the message as part
of the ECDSA-SHA256 signing algo.
If usenonce is not None, its value is passed to the secp256k1 library
sign() function as the ndata value, which is then used in conjunction
with a custom nonce generating function, such that the nonce used in the ECDSA
sign algorithm is exactly that value (ndata there, usenonce here). 32 bytes.
Return value: the calculated signature.'''
if rawmsg and len(msg) != 32:
raise Exception("Invalid hash input to ECDSA raw sign.")
if rawpriv:
compressed, p = read_privkey(priv)
newpriv = secp256k1.PrivateKey(p, raw=True, ctx=ctx)
else:
newpriv = secp256k1.PrivateKey(priv, raw=False, ctx=ctx)
if usenonce:
if len(usenonce) != 32:
raise ValueError("Invalid nonce passed to ecdsa_sign: " + str(
usenonce))
nf = ffi.addressof(_noncefunc.lib, "nonce_function_rand")
ndata = ffi.new("char [32]", usenonce)
usenonce = (nf, ndata)
if usenonce:
sig = newpriv.ecdsa_sign(msg, raw=rawmsg, custom_nonce=usenonce)
else:
#partial fix for secp256k1-transient not including customnonce;
#partial because donations will crash on windows in the "if".
sig = newpriv.ecdsa_sign(msg, raw=rawmsg)
return newpriv.ecdsa_serialize(sig)
@hexbin
def ecdsa_raw_verify(msg, pub, sig, usehex, rawmsg=False):
'''Take the binary message msg and binary signature sig,
and verify it against the pubkey pub.
If rawmsg is True, no sha256 hash is applied to msg before verifying.
In this case, msg must be a precalculated hash (256 bit).
If rawmsg is False, the secp256k1 lib will hash the message as part
of the ECDSA-SHA256 verification algo.
Return value: True if the signature is valid for this pubkey, False
otherwise.
Since the arguments may come from external messages their content is
not guaranteed, so return False on any parsing exception.
'''
try:
if rawmsg:
assert len(msg) == 32
newpub = secp256k1.PublicKey(pubkey=pub, raw=True, ctx=ctx)
sigobj = newpub.ecdsa_deserialize(sig)
retval = newpub.ecdsa_verify(msg, sigobj, raw=rawmsg)
except:
return False
return retval
def estimate_tx_size(ins, outs, txtype='p2pkh'):
'''Estimate transaction size.
Assuming p2pkh:
out: 8+1+3+2+20=34, in: 1+32+4+1+1+~73+1+1+33=147,
ver:4,seq:4, +2 (len in,out)
total ~= 34*len_out + 147*len_in + 10 (sig sizes vary slightly)
'''
if txtype == 'p2pkh':
return 10 + ins * 147 + 34 * outs
else:
raise NotImplementedError("Non p2pkh transaction size estimation not" +
"yet implemented")