X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=lib%2Fbitcoin.py;h=33631ffef3464cfb844d393e83001196e4f78689;hb=54973062bd903bda787bb94a66bc4eff7b35b77b;hp=9493a0d9a8ba4040b6df3b60e3a50612e7391b3d;hpb=501bb92584126b5669ace3072be8612f6027a931;p=electrum-nvc.git diff --git a/lib/bitcoin.py b/lib/bitcoin.py index 9493a0d..33631ff 100644 --- a/lib/bitcoin.py +++ b/lib/bitcoin.py @@ -17,16 +17,35 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import hashlib, base64, ecdsa, re +import hashlib +import base64 +import re +import sys import hmac -import aes + from util import print_error +from version import SEED_PREFIX + +try: + import ecdsa +except ImportError: + sys.exit("Error: python-ecdsa does not seem to be installed. Try 'sudo pip install ecdsa'") + +try: + import aes +except ImportError: + sys.exit("Error: AES does not seem to be installed. Try 'sudo pip install slowaes'") + +################################## transactions + +MIN_RELAY_TX_FEE = 1000 + # AES encryption EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s)) DecodeAES = lambda secret, e: aes.decryptData(secret, base64.b64decode(e)) + def pw_encode(s, password): if password: secret = Hash(password) @@ -34,6 +53,7 @@ def pw_encode(s, password): else: return s + def pw_decode(s, password): if password is not None: secret = Hash(password) @@ -46,17 +66,16 @@ def pw_decode(s, password): return s - - - def rev_hex(s): return s.decode('hex')[::-1].encode('hex') + def int_to_hex(i, length=1): s = hex(i)[2:].rstrip('L') s = "0"*(2*length - len(s)) + s return rev_hex(s) + def var_int(i): # https://en.bitcoin.it/wiki/Protocol_specification#Variable_length_integer if i<0xfd: @@ -68,6 +87,7 @@ def var_int(i): else: return "ff"+int_to_hex(i,8) + def op_push(i): if i<0x4c: return int_to_hex(i) @@ -77,27 +97,29 @@ def op_push(i): return '4d' + int_to_hex(i,2) else: return '4e' + int_to_hex(i,4) - def sha256(x): return hashlib.sha256(x).digest() + def Hash(x): if type(x) is unicode: x=x.encode('utf-8') return sha256(sha256(x)) + hash_encode = lambda x: x[::-1].encode('hex') hash_decode = lambda x: x.decode('hex')[::-1] hmac_sha_512 = lambda x,y: hmac.new(x, y, hashlib.sha512).digest() + def mnemonic_to_seed(mnemonic, passphrase): from pbkdf2 import PBKDF2 import hmac PBKDF2_ROUNDS = 2048 return PBKDF2(mnemonic, 'mnemonic' + passphrase, iterations = PBKDF2_ROUNDS, macmodule = hmac, digestmodule = hashlib.sha512).read(64) -from version import SEED_PREFIX + is_new_seed = lambda x: hmac_sha_512("Seed version", x.encode('utf8')).encode('hex')[0:2].startswith(SEED_PREFIX) def is_old_seed(seed): @@ -114,7 +136,7 @@ def is_old_seed(seed): is_hex = (len(seed) == 32) except Exception: is_hex = False - + return is_hex or (uses_electrum_words and len(words) == 12) @@ -142,9 +164,9 @@ def i2d_ECPrivateKey(pkey, compressed=False): '022100' + \ '%064x' % _r + \ '020101a144034200' - + return key.decode('hex') + i2o_ECPublicKey(pkey.pubkey, compressed) - + def i2o_ECPublicKey(pubkey, compressed=False): # public keys are 65 bytes long (520 bits) # 0x04 + 32-byte X-coordinate + 32-byte Y-coordinate @@ -159,14 +181,14 @@ def i2o_ECPublicKey(pubkey, compressed=False): key = '04' + \ '%064x' % pubkey.point.x() + \ '%064x' % pubkey.point.y() - + return key.decode('hex') - + # end pywallet openssl private key implementation - - -############ functions from pywallet ##################### + + +############ functions from pywallet ##################### def hash_160(public_key): try: @@ -197,6 +219,7 @@ def bc_address_to_hash_160(addr): __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' __b58base = len(__b58chars) + def b58encode(v): """ encode v, which is a string of bytes, to base58.""" @@ -220,6 +243,7 @@ def b58encode(v): return (__b58chars[0]*nPad) + result + def b58decode(v, length): """ decode v into a string of len bytes.""" long_value = 0L @@ -249,6 +273,7 @@ def EncodeBase58Check(vchIn): hash = Hash(vchIn) return b58encode(vchIn + hash[0:4]) + def DecodeBase58Check(psz): vchRet = b58decode(psz, None) key = vchRet[0:-4] @@ -260,9 +285,11 @@ def DecodeBase58Check(psz): else: return key + def PrivKeyToSecret(privkey): return privkey[9:9+32] + def SecretToASecret(secret, compressed=False, addrtype=0): vchIn = chr((addrtype+128)&255) + secret if compressed: vchIn += '\01' @@ -282,15 +309,19 @@ def regenerate_key(sec): b = b[0:32] return EC_KEY(b) + def GetPubKey(pubkey, compressed=False): return i2o_ECPublicKey(pubkey, compressed) + def GetPrivKey(pkey, compressed=False): return i2d_ECPrivateKey(pkey, compressed) + def GetSecret(pkey): return ('%064x' % pkey.secret).decode('hex') + def is_compressed(sec): b = ASecretToSecret(sec) return len(b) == 33 @@ -327,7 +358,7 @@ def is_address(addr): def is_private_key(key): try: - k = ASecretToSecret(key) + k = ASecretToSecret(key) return k is not False except: return False @@ -406,6 +437,34 @@ def ser_to_point(Aser): +class MyVerifyingKey(ecdsa.VerifyingKey): + @classmethod + def from_signature(klass, sig, recid, h, curve): + """ See http://www.secg.org/download/aid-780/sec1-v2.pdf, chapter 4.1.6 """ + from ecdsa import util, numbertheory + import msqr + curveFp = curve.curve + G = curve.generator + order = G.order() + # extract r,s from signature + r, s = util.sigdecode_string(sig, order) + # 1.1 + x = r + (recid/2) * order + # 1.3 + alpha = ( x * x * x + curveFp.a() * x + curveFp.b() ) % curveFp.p() + beta = msqr.modular_sqrt(alpha, curveFp.p()) + y = beta if (beta - recid) % 2 == 0 else curveFp.p() - beta + # 1.4 the constructor checks that nR is at infinity + R = Point(curveFp, x, y, order) + # 1.5 compute e from message: + e = string_to_number(h) + minus_e = -e % order + # 1.6 compute Q = r^-1 (sR - eG) + inv_r = numbertheory.inverse_mod(r,order) + Q = inv_r * ( s * R + minus_e * G ) + return klass.from_public_point( Q, curve ) + + class EC_KEY(object): def __init__( self, k ): secret = string_to_number(k) @@ -434,16 +493,9 @@ class EC_KEY(object): @classmethod def verify_message(self, address, signature, message): - """ See http://www.secg.org/download/aid-780/sec1-v2.pdf for the math """ - from ecdsa import numbertheory, util - import msqr - curve = curve_secp256k1 - G = generator_secp256k1 - order = G.order() - # extract r,s from signature sig = base64.b64decode(signature) if len(sig) != 65: raise Exception("Wrong encoding") - r,s = util.sigdecode_string(sig[1:], order) + nV = ord(sig[0]) if nV < 27 or nV >= 35: raise Exception("Bad encoding") @@ -454,24 +506,12 @@ class EC_KEY(object): compressed = False recid = nV - 27 - # 1.1 - x = r + (recid/2) * order - # 1.3 - alpha = ( x * x * x + curve.a() * x + curve.b() ) % curve.p() - beta = msqr.modular_sqrt(alpha, curve.p()) - y = beta if (beta - recid) % 2 == 0 else curve.p() - beta - # 1.4 the constructor checks that nR is at infinity - R = Point(curve, x, y, order) - # 1.5 compute e from message: h = Hash( msg_magic(message) ) - e = string_to_number(h) - minus_e = -e % order - # 1.6 compute Q = r^-1 (sR - eG) - inv_r = numbertheory.inverse_mod(r,order) - Q = inv_r * ( s * R + minus_e * G ) - public_key = ecdsa.VerifyingKey.from_public_point( Q, curve = SECP256k1 ) - # check that Q is the public key + public_key = MyVerifyingKey.from_signature( sig[1:], recid, h, curve = SECP256k1 ) + + # check public key public_key.verify_digest( sig[1:], h, sigdecode = ecdsa.util.sigdecode_string) + # check that we get the original signing address addr = public_key_to_bc_address( point_to_ser(public_key.pubkey.point, compressed) ) if address != addr: @@ -482,19 +522,19 @@ class EC_KEY(object): @classmethod def encrypt_message(self, message, pubkey): - + pk = ser_to_point(pubkey) if not ecdsa.ecdsa.point_is_valid(generator_secp256k1, pk.x(), pk.y()): raise Exception('invalid pubkey') - + ephemeral_exponent = number_to_string(ecdsa.util.randrange(pow(2,256)), generator_secp256k1.order()) ephemeral = EC_KEY(ephemeral_exponent) - + ecdh_key = (pk * ephemeral.privkey.secret_multiplier).x() ecdh_key = ('%064x' % ecdh_key).decode('hex') key = hashlib.sha512(ecdh_key).digest() key_e, key_m = key[:32], key[32:] - + iv_ciphertext = aes.encryptData(key_e, message) ephemeral_pubkey = ephemeral.get_public_key(compressed=True).decode('hex') @@ -505,20 +545,20 @@ class EC_KEY(object): def decrypt_message(self, encrypted): - + encrypted = base64.b64decode(encrypted) - + if len(encrypted) < 85: raise Exception('invalid ciphertext: length') - + magic = encrypted[:4] ephemeral_pubkey = encrypted[4:37] iv_ciphertext = encrypted[37:-32] mac = encrypted[-32:] - + if magic != 'BIE1': raise Exception('invalid ciphertext: invalid magic bytes') - + try: ephemeral_pubkey = ser_to_point(ephemeral_pubkey) except AssertionError, e: @@ -577,10 +617,10 @@ def _CKD_priv(k, c, s, is_prime): return k_n, c_n # Child public key derivation function (from public key only) -# K = master public key +# K = master public key # c = master chain code # n = index of key we want to derive -# This function allows us to find the nth public key, as long as n is +# This function allows us to find the nth public key, as long as n is # non-negative. If n is negative, we need the master private key to find it. def CKD_pub(cK, c, n): if n & BIP32_PRIME: raise @@ -602,7 +642,7 @@ def _CKD_pub(cK, c, s): def deserialize_xkey(xkey): - xkey = DecodeBase58Check(xkey) + xkey = DecodeBase58Check(xkey) assert len(xkey) == 78 assert xkey[0:4].encode('hex') in ["0488ade4", "0488b21e"] depth = ord(xkey[4]) @@ -616,10 +656,31 @@ def deserialize_xkey(xkey): return depth, fingerprint, child_number, c, K_or_k +def get_xkey_name(xkey): + depth, fingerprint, child_number, c, K = deserialize_xkey(xkey) + n = int(child_number.encode('hex'), 16) + if n & BIP32_PRIME: + child_id = "%d'"%(n - BIP32_PRIME) + else: + child_id = "%d"%n + if depth == 0: + return '' + elif depth == 1: + return child_id + else: + raise BaseException("xpub depth error") + + +def xpub_from_xprv(xprv): + depth, fingerprint, child_number, c, k = deserialize_xkey(xprv) + K, cK = get_pubkeys_from_secret(k) + xpub = "0488B21E".decode('hex') + chr(depth) + fingerprint + child_number + c + cK + return EncodeBase58Check(xpub) + def bip32_root(seed): import hmac - seed = seed.decode('hex') + seed = seed.decode('hex') I = hmac.new("Bitcoin seed", seed, hashlib.sha512).digest() master_k = I[0:32] master_c = I[32:] @@ -668,88 +729,7 @@ def bip32_public_derivation(xpub, branch, sequence): return EncodeBase58Check(xpub) - - def bip32_private_key(sequence, k, chain): for i in sequence: k, chain = CKD_priv(k, chain, i) return SecretToASecret(k, True) - - - - -################################## transactions - -MIN_RELAY_TX_FEE = 1000 - - - -def test_bip32(seed, sequence): - """ - run a test vector, - see https://en.bitcoin.it/wiki/BIP_0032_TestVectors - """ - - xprv, xpub = bip32_root(seed) - print xpub - print xprv - - assert sequence[0:2] == "m/" - path = 'm' - sequence = sequence[2:] - for n in sequence.split('/'): - child_path = path + '/' + n - if n[-1] != "'": - xpub2 = bip32_public_derivation(xpub, path, child_path) - xprv, xpub = bip32_private_derivation(xprv, path, child_path) - if n[-1] != "'": - assert xpub == xpub2 - - - path = child_path - print path - print xpub - print xprv - - print "----" - - - -def test_crypto(message): - G = generator_secp256k1 - _r = G.order() - pvk = ecdsa.util.randrange( pow(2,256) ) %_r - - Pub = pvk*G - pubkey_c = point_to_ser(Pub,True) - pubkey_u = point_to_ser(Pub,False) - addr_c = public_key_to_bc_address(pubkey_c) - addr_u = public_key_to_bc_address(pubkey_u) - - print "Private key ", '%064x'%pvk - eck = EC_KEY(number_to_string(pvk,_r)) - - print "Compressed public key ", pubkey_c.encode('hex') - enc = EC_KEY.encrypt_message(message, pubkey_c) - dec = eck.decrypt_message(enc) - assert dec == message - - print "Uncompressed public key", pubkey_u.encode('hex') - enc2 = EC_KEY.encrypt_message(message, pubkey_u) - dec2 = eck.decrypt_message(enc) - assert dec2 == message - - signature = eck.sign_message(message, True, addr_c) - print signature - EC_KEY.verify_message(addr_c, signature, message) - - -if __name__ == '__main__': - - for message in ["Chancellor on brink of second bailout for banks", chr(255)*512]: - test_crypto(message) - - test_bip32("000102030405060708090a0b0c0d0e0f", "m/0'/1/2'/2/1000000000") - test_bip32("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542","m/0/2147483647'/1/2147483646'/2") - -