hash_encode = lambda x: x[::-1].encode('hex')
hash_decode = lambda x: x.decode('hex')[::-1]
-############ functions from pywallet #####################
+# pywallet openssl private key implementation
+
+def i2d_ECPrivateKey(pkey, compressed=False):
+ if compressed:
+ key = '3081d30201010420' + \
+ '%064x' % pkey.secret + \
+ 'a081a53081a2020101302c06072a8648ce3d0101022100' + \
+ '%064x' % _p + \
+ '3006040100040107042102' + \
+ '%064x' % _Gx + \
+ '022100' + \
+ '%064x' % _r + \
+ '020101a124032200'
+ else:
+ key = '308201130201010420' + \
+ '%064x' % pkey.secret + \
+ 'a081a53081a2020101302c06072a8648ce3d0101022100' + \
+ '%064x' % _p + \
+ '3006040100040107044104' + \
+ '%064x' % _Gx + \
+ '%064x' % _Gy + \
+ '022100' + \
+ '%064x' % _r + \
+ '020101a144034200'
+
+ return key.decode('hex') + i2o_ECPublicKey(pkey, compressed)
+
+def i2o_ECPublicKey(pkey, compressed=False):
+ # public keys are 65 bytes long (520 bits)
+ # 0x04 + 32-byte X-coordinate + 32-byte Y-coordinate
+ # 0x00 = point at infinity, 0x02 and 0x03 = compressed, 0x04 = uncompressed
+ # compressed keys: <sign> <x> where <sign> is 0x02 if y is even and 0x03 if y is odd
+ if compressed:
+ if pkey.pubkey.point.y() & 1:
+ key = '03' + '%064x' % pkey.pubkey.point.x()
+ else:
+ key = '02' + '%064x' % pkey.pubkey.point.x()
+ else:
+ key = '04' + \
+ '%064x' % pkey.pubkey.point.x() + \
+ '%064x' % pkey.pubkey.point.y()
+
+ return key.decode('hex')
+
+# end pywallet openssl private key implementation
+
+
+
+############ functions from pywallet #####################
+
addrtype = 0
def hash_160(public_key):
def PrivKeyToSecret(privkey):
return privkey[9:9+32]
-def SecretToASecret(secret):
- vchIn = chr(addrtype+128) + secret
+def SecretToASecret(secret, compressed=False):
+ vchIn = chr((addrtype+128)&255) + secret
+ if compressed: vchIn += '\01'
return EncodeBase58Check(vchIn)
def ASecretToSecret(key):
vch = DecodeBase58Check(key)
- if vch and vch[0] == chr(addrtype+128):
+ if vch and vch[0] == chr((addrtype+128)&255):
return vch[1:]
else:
return False
+def regenerate_key(sec):
+ b = ASecretToSecret(sec)
+ if not b:
+ return False
+ b = b[0:32]
+ secret = int('0x' + b.encode('hex'), 16)
+ return EC_KEY(secret)
+
+def GetPubKey(pkey, compressed=False):
+ return i2o_ECPublicKey(pkey, 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
+
########### end pywallet functions #######################
# secp256k1, http://www.oid-info.com/get/1.3.132.0.10
oid_secp256k1 = (1,3,132,0,10)
SECP256k1 = ecdsa.curves.Curve("SECP256k1", curve_secp256k1, generator_secp256k1, oid_secp256k1 )
+class EC_KEY(object):
+ def __init__( self, secret ):
+ self.pubkey = ecdsa.ecdsa.Public_key( generator_secp256k1, generator_secp256k1 * secret )
+ self.privkey = ecdsa.ecdsa.Private_key( self.pubkey, secret )
+ self.secret = secret
+
+
def filter(s):
out = re.sub('( [^\n]*|)\n','',s)
sig = sig + chr(1) # hashtype
script = int_to_hex( len(sig)) + ' push %d bytes\n'%len(sig)
script += sig.encode('hex') + ' sig\n'
- pubkey = chr(4) + pubkey
script += int_to_hex( len(pubkey)) + ' push %d bytes\n'%len(pubkey)
script += pubkey.encode('hex') + ' pubkey\n'
elif for_sig==i:
while not self.is_up_to_date(): time.sleep(0.1)
def import_key(self, keypair, password):
- address, key = keypair.split(':')
+
+ address, sec = keypair.split(':')
if not self.is_valid(address):
raise BaseException('Invalid Bitcoin address')
if address in self.all_addresses():
raise BaseException('Address already in wallet')
- b = ASecretToSecret( key )
- if not b:
- raise BaseException('Unsupported key format')
- secexp = int( b.encode('hex'), 16)
- private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve=SECP256k1 )
+
+ # rebuild public key from private key, compressed or uncompressed
+ pkey = regenerate_key(sec)
+ if not pkey:
+ return False
+
+ # figure out if private key is compressed
+ compressed = is_compressed(sec)
+
+ # rebuild private and public key from regenerated secret
+ private_key = GetPrivKey(pkey, compressed)
+ public_key = GetPubKey(pkey, compressed)
+ addr = public_key_to_bc_address(public_key)
+
# sanity check
- public_key = private_key.get_verifying_key()
- if not address == public_key_to_bc_address( '04'.decode('hex') + public_key.to_string() ):
+ if not address == addr :
raise BaseException('Address does not match private key')
- self.imported_keys[address] = self.pw_encode( key, password )
-
+
+ # store the originally requested keypair into the imported keys table
+ self.imported_keys[address] = self.pw_encode(sec, password )
+
def new_seed(self, password):
seed = "%032x"%ecdsa.util.randrange( pow(2,128) )
return string_to_number( Hash( "%d:%d:"%(n,for_change) + self.master_public_key.decode('hex') ) )
def get_private_key_base58(self, address, password):
- pk = self.get_private_key(address, password)
- if pk is None: return None
- return SecretToASecret( pk )
+ secexp, compressed = self.get_private_key(address, password)
+ if secexp is None: return None
+ pk = number_to_string( secexp, generator_secp256k1.order() )
+ return SecretToASecret( pk, compressed )
def get_private_key(self, address, password):
""" Privatekey(type,n) = Master_private_key + H(n|S|type) """
order = generator_secp256k1.order()
if address in self.imported_keys.keys():
- b = self.pw_decode( self.imported_keys[address], password )
- if not b: return None
- b = ASecretToSecret( b )
- secexp = int( b.encode('hex'), 16)
+ sec = self.pw_decode( self.imported_keys[address], password )
+ if not sec: return None, None
+
+ pkey = regenerate_key(sec)
+ compressed = is_compressed(sec)
+ secexp = pkey.secret
+
else:
if address in self.addresses:
n = self.addresses.index(address)
if not seed: return None
secexp = self.stretch_key(seed)
secexp = ( secexp + self.get_sequence(n,for_change) ) % order
+ compressed = False
- pk = number_to_string(secexp,order)
- return pk
+ return secexp, compressed
def msg_magic(self, message):
return "\x18Bitcoin Signed Message:\n" + chr( len(message) ) + message
def sign_message(self, address, message, password):
- private_key = ecdsa.SigningKey.from_string( self.get_private_key(address, password), curve = SECP256k1 )
+ secexp, compressed = self.get_private_key(address, password)
+ private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
public_key = private_key.get_verifying_key()
signature = private_key.sign_digest( Hash( self.msg_magic( message ) ), sigencode = ecdsa.util.sigencode_string )
assert public_key.verify_digest( signature, Hash( self.msg_magic( message ) ), sigdecode = ecdsa.util.sigdecode_string)
for i in range(4):
- sig = base64.b64encode( chr(27+i) + signature )
+ sig = base64.b64encode( chr(27 + i + (4 if compressed else 0)) + signature )
try:
self.verify_message( address, sig, message)
return sig
s_inputs = []
for i in range(len(inputs)):
addr, v, p_hash, p_pos, p_scriptPubKey, _, _ = inputs[i]
- private_key = ecdsa.SigningKey.from_string( self.get_private_key(addr, password), curve = SECP256k1 )
+ secexp, compressed = self.get_private_key(addr, password)
+ private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
public_key = private_key.get_verifying_key()
- pubkey = public_key.to_string()
+
+ pkey = EC_KEY(secexp)
+ pubkey = GetPubKey(pkey, compressed)
+
tx = filter( raw_tx( inputs, outputs, for_sig = i ) )
sig = private_key.sign_digest( Hash( tx.decode('hex') ), sigencode = ecdsa.util.sigencode_der )
assert public_key.verify_digest( sig, Hash( tx.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)