Skip to content

Commit

Permalink
add message signing/decryption for segwit addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
ecdsa committed Oct 5, 2017
1 parent 0bc53d3 commit e299df7
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 61 deletions.
8 changes: 3 additions & 5 deletions gui/qt/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -1898,7 +1898,8 @@ def do_sign(self, address, message, signature, password):
if not bitcoin.is_address(address):
self.show_message('Invalid Bitcoin address.')
return
if not bitcoin.is_p2pkh(address):
txin_type = self.wallet.get_txin_type(address)
if txin_type not in ['p2pkh', 'p2wpkh', 'p2wpkh-p2sh']:
self.show_message('Cannot sign messages with this type of address.' + '\n\n' + self.msg_sign)
return
if not self.wallet.is_mine(address):
Expand All @@ -1916,9 +1917,6 @@ def do_verify(self, address, message, signature):
if not bitcoin.is_address(address):
self.show_message('Invalid Bitcoin address.')
return
if not bitcoin.is_p2pkh(address):
self.show_message('Cannot verify messages with this type of address.' + '\n\n' + self.msg_sign)
return
try:
# This can throw on invalid base64
sig = base64.b64decode(str(signature.toPlainText()))
Expand All @@ -1932,7 +1930,7 @@ def do_verify(self, address, message, signature):

def sign_verify_message(self, address=''):
d = WindowModalDialog(self, _('Sign/verify Message'))
d.setMinimumSize(410, 290)
d.setMinimumSize(610, 290)

layout = QGridLayout(d)

Expand Down
10 changes: 6 additions & 4 deletions lib/bitcoin.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,8 +536,7 @@ def public_key_from_private_key(pk, compressed):
def address_from_private_key(sec):
txin_type, privkey, compressed = deserialize_privkey(sec)
public_key = public_key_from_private_key(privkey, compressed)
address = pubkey_to_address(txin_type, public_key)
return address
return pubkey_to_address(txin_type, public_key)

def is_segwit_address(addr):
witver, witprog = segwit_addr.decode(SEGWIT_HRP, addr)
Expand Down Expand Up @@ -607,8 +606,11 @@ def verify_message(address, sig, message):
public_key, compressed = pubkey_from_signature(sig, h)
# check public key using the address
pubkey = point_to_ser(public_key.pubkey.point, compressed)
addr = public_key_to_p2pkh(pubkey)
if address != addr:
for txin_type in ['p2pkh','p2wpkh','p2wpkh-p2sh']:
addr = pubkey_to_address(txin_type, bh2u(pubkey))
if address == addr:
break
else:
raise Exception("Bad signature")
# check message
public_key.verify_digest(sig[1:], h, sigdecode = ecdsa.util.sigdecode_string)
Expand Down
17 changes: 7 additions & 10 deletions lib/keystore.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,13 @@ def may_have_password(self):
return not self.is_watching_only()

def sign_message(self, sequence, message, password):
privkey = self.get_private_key(sequence, password)
compressed = self.use_compressed_pubkeys
privkey, compressed = self.get_private_key(sequence, password)
key = regenerate_key(privkey)
return key.sign_message(message, compressed)

def decrypt_message(self, sequence, message, password):
sec = self.get_private_key(sequence, password)
ec = regenerate_key(sec)
privkey, compressed = self.get_private_key(sequence, password)
ec = regenerate_key(privkey)
decrypted = ec.decrypt_message(message)
return decrypted

Expand All @@ -106,7 +105,7 @@ def sign_transaction(self, tx, password):
# Add private keys
keypairs = self.get_tx_derivations(tx)
for k, v in keypairs.items():
keypairs[k] = self.get_private_key(v, password)
keypairs[k] = self.get_private_key(v, password)[0]
# Sign
if keypairs:
tx.sign(keypairs)
Expand Down Expand Up @@ -156,7 +155,7 @@ def get_private_key(self, pubkey, password):
# this checks the password
if pubkey != public_key_from_private_key(privkey, compressed):
raise InvalidPassword()
return privkey
return privkey, compressed

def get_pubkey_derivation(self, x_pubkey):
if x_pubkey[0:2] in ['02', '03', '04']:
Expand Down Expand Up @@ -279,7 +278,6 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
def __init__(self, d):
Xpub.__init__(self)
Deterministic_KeyStore.__init__(self, d)
self.use_compressed_pubkeys = True
self.xpub = d.get('xpub')
self.xprv = d.get('xprv')

Expand Down Expand Up @@ -331,7 +329,7 @@ def get_private_key(self, sequence, password):
xprv = self.get_master_private_key(password)
_, _, _, _, c, k = deserialize_xprv(xprv)
pk = bip32_private_key(sequence, k, c)
return pk
return pk, True



Expand All @@ -340,7 +338,6 @@ class Old_KeyStore(Deterministic_KeyStore):
def __init__(self, d):
Deterministic_KeyStore.__init__(self, d)
self.mpk = d.get('mpk')
self.use_compressed_pubkeys = False

def get_hex_seed(self, password):
return pw_decode(self.seed, password).encode('utf8')
Expand Down Expand Up @@ -421,7 +418,7 @@ def get_private_key(self, sequence, password):
for_change, n = sequence
secexp = self.stretch_key(seed)
pk = self.get_private_key_from_stretched_exponent(for_change, n, secexp)
return pk
return pk, False

def check_seed(self, seed):
secexp = self.stretch_key(seed)
Expand Down
80 changes: 38 additions & 42 deletions lib/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,22 +273,14 @@ def export_private_key(self, address, password):
if self.is_watching_only():
return []
index = self.get_address_index(address)
pk = self.keystore.get_private_key(index, password)
compressed = self.keystore.use_compressed_pubkeys
pk, compressed = self.keystore.get_private_key(index, password)
if self.txin_type in ['p2sh', 'p2wsh', 'p2wsh-p2sh']:
pubkeys = self.get_public_keys(address)
redeem_script = self.pubkeys_to_redeem_script(pubkeys)
else:
redeem_script = None
return bitcoin.serialize_privkey(pk, compressed, self.txin_type), redeem_script

def get_public_key(self, address):
if self.keystore.can_import():
pubkey = self.get_address_index(address)
else:
sequence = self.get_address_index(address)
pubkey = self.get_pubkey(*sequence)
return pubkey

def get_public_keys(self, address):
sequence = self.get_address_index(address)
Expand Down Expand Up @@ -1336,6 +1328,15 @@ def add_address(self, address):
def has_password(self):
return self.storage.get('use_encryption', False)

def sign_message(self, address, message, password):
index = self.get_address_index(address)
return self.keystore.sign_message(index, message, password)

def decrypt_message(self, pubkey, message, password):
addr = self.pubkeys_to_address(pubkey)
index = self.get_address_index(addr)
return self.keystore.decrypt_message(index, message, password)


class Imported_Wallet(Abstract_Wallet):
# wallet made of imported addresses
Expand Down Expand Up @@ -1429,8 +1430,10 @@ def delete_address(self, address):
self.storage.write()

def get_address_index(self, address):
if self.keystore.can_import():
return self.addresses[address]['pubkey']
return self.get_public_key(address)

def get_public_key(self, address):
return self.addresses[address].get('pubkey')

def import_private_key(self, sec, pw, redeem_script=None):
try:
Expand Down Expand Up @@ -1461,16 +1464,18 @@ def export_private_key(self, address, password):
sec = pw_decode(self.keystore.keypairs[pubkey], password)
return sec, redeem_script

def get_txin_type(self, address):
return self.addresses[address].get('type', 'address')

def add_input_sig_info(self, txin, address):
txin['type'] = self.get_txin_type(address)
if self.is_watching_only():
addrtype, hash160 = b58_address_to_hash160(address)
x_pubkey = 'fd' + bh2u(bytes([addrtype]) + hash160)
txin['x_pubkeys'] = [x_pubkey]
txin['signatures'] = [None]
return

txin_type = self.addresses[address]['type']
txin['type'] = txin_type
if txin_type in ['p2pkh', 'p2wkh', 'p2wkh-p2sh']:
pubkey = self.addresses[address]['pubkey']
txin['num_sig'] = 1
Expand All @@ -1484,7 +1489,10 @@ def add_input_sig_info(self, txin, address):
txin['redeem_script'] = redeem_script
txin['signatures'] = [None] * num_keys


def pubkeys_to_address(self, pubkey):
for addr, v in self.addresses.items():
if v.get('pubkey') == pubkey:
return addr

class Deterministic_Wallet(Abstract_Wallet):

Expand Down Expand Up @@ -1604,10 +1612,21 @@ def get_master_public_keys(self):
def get_fingerprint(self):
return self.get_master_public_key()

def get_txin_type(self, address):
return self.txin_type


class Simple_Wallet(Abstract_Wallet):
class Simple_Deterministic_Wallet(Deterministic_Wallet):

""" Wallet with a single pubkey per address """
""" Deterministic Wallet with a single pubkey per address """

def __init__(self, storage):
Deterministic_Wallet.__init__(self, storage)

def get_public_key(self, address):
sequence = self.get_address_index(address)
pubkey = self.get_pubkey(*sequence)
return pubkey

def load_keystore(self):
self.keystore = load_keystore(self.storage, 'keystore')
Expand All @@ -1631,30 +1650,12 @@ def get_public_keys(self, address):
return [self.get_public_key(address)]

def add_input_sig_info(self, txin, address):
if not self.keystore.can_import():
derivation = self.get_address_index(address)
x_pubkey = self.keystore.get_xpubkey(*derivation)
else:
x_pubkey = self.get_public_key(address)
derivation = self.get_address_index(address)
x_pubkey = self.keystore.get_xpubkey(*derivation)
txin['x_pubkeys'] = [x_pubkey]
txin['signatures'] = [None]
txin['num_sig'] = 1

def sign_message(self, address, message, password):
index = self.get_address_index(address)
return self.keystore.sign_message(index, message, password)

def decrypt_message(self, pubkey, message, password):
addr = self.pubkeys_to_address(pubkey)
index = self.get_address_index(addr)
return self.keystore.decrypt_message(index, message, password)


class Simple_Deterministic_Wallet(Deterministic_Wallet, Simple_Wallet):

def __init__(self, storage):
Deterministic_Wallet.__init__(self, storage)

def get_master_public_key(self):
return self.keystore.get_master_public_key()

Expand Down Expand Up @@ -1687,9 +1688,6 @@ def update_password(self, old_pw, new_pw, encrypt=False):
def save_keystore(self):
self.storage.put('keystore', self.keystore.dump())

def can_delete_address(self):
return self.keystore.can_import()

def delete_address(self, address):
pubkey = self.get_public_key(address)
self.keystore.delete_imported_key(pubkey)
Expand All @@ -1698,9 +1696,6 @@ def delete_address(self, address):
self.save_addresses()
self.storage.write()

def can_import_privkey(self):
return self.keystore.can_import()




Expand All @@ -1710,6 +1705,7 @@ class Standard_Wallet(Simple_Deterministic_Wallet):
def pubkeys_to_address(self, pubkey):
return bitcoin.pubkey_to_address(self.txin_type, pubkey)


class Multisig_Wallet(Deterministic_Wallet):
# generic m of n
gap_limit = 20
Expand Down

0 comments on commit e299df7

Please sign in to comment.