add message signing/decryption for segwit addresses
This commit is contained in:
parent
0bc53d34d1
commit
e299df7b82
|
@ -1898,7 +1898,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
||||||
if not bitcoin.is_address(address):
|
if not bitcoin.is_address(address):
|
||||||
self.show_message('Invalid Bitcoin address.')
|
self.show_message('Invalid Bitcoin address.')
|
||||||
return
|
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)
|
self.show_message('Cannot sign messages with this type of address.' + '\n\n' + self.msg_sign)
|
||||||
return
|
return
|
||||||
if not self.wallet.is_mine(address):
|
if not self.wallet.is_mine(address):
|
||||||
|
@ -1916,9 +1917,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
||||||
if not bitcoin.is_address(address):
|
if not bitcoin.is_address(address):
|
||||||
self.show_message('Invalid Bitcoin address.')
|
self.show_message('Invalid Bitcoin address.')
|
||||||
return
|
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:
|
try:
|
||||||
# This can throw on invalid base64
|
# This can throw on invalid base64
|
||||||
sig = base64.b64decode(str(signature.toPlainText()))
|
sig = base64.b64decode(str(signature.toPlainText()))
|
||||||
|
@ -1932,7 +1930,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
||||||
|
|
||||||
def sign_verify_message(self, address=''):
|
def sign_verify_message(self, address=''):
|
||||||
d = WindowModalDialog(self, _('Sign/verify Message'))
|
d = WindowModalDialog(self, _('Sign/verify Message'))
|
||||||
d.setMinimumSize(410, 290)
|
d.setMinimumSize(610, 290)
|
||||||
|
|
||||||
layout = QGridLayout(d)
|
layout = QGridLayout(d)
|
||||||
|
|
||||||
|
|
|
@ -536,8 +536,7 @@ def public_key_from_private_key(pk, compressed):
|
||||||
def address_from_private_key(sec):
|
def address_from_private_key(sec):
|
||||||
txin_type, privkey, compressed = deserialize_privkey(sec)
|
txin_type, privkey, compressed = deserialize_privkey(sec)
|
||||||
public_key = public_key_from_private_key(privkey, compressed)
|
public_key = public_key_from_private_key(privkey, compressed)
|
||||||
address = pubkey_to_address(txin_type, public_key)
|
return pubkey_to_address(txin_type, public_key)
|
||||||
return address
|
|
||||||
|
|
||||||
def is_segwit_address(addr):
|
def is_segwit_address(addr):
|
||||||
witver, witprog = segwit_addr.decode(SEGWIT_HRP, addr)
|
witver, witprog = segwit_addr.decode(SEGWIT_HRP, addr)
|
||||||
|
@ -607,8 +606,11 @@ def verify_message(address, sig, message):
|
||||||
public_key, compressed = pubkey_from_signature(sig, h)
|
public_key, compressed = pubkey_from_signature(sig, h)
|
||||||
# check public key using the address
|
# check public key using the address
|
||||||
pubkey = point_to_ser(public_key.pubkey.point, compressed)
|
pubkey = point_to_ser(public_key.pubkey.point, compressed)
|
||||||
addr = public_key_to_p2pkh(pubkey)
|
for txin_type in ['p2pkh','p2wpkh','p2wpkh-p2sh']:
|
||||||
if address != addr:
|
addr = pubkey_to_address(txin_type, bh2u(pubkey))
|
||||||
|
if address == addr:
|
||||||
|
break
|
||||||
|
else:
|
||||||
raise Exception("Bad signature")
|
raise Exception("Bad signature")
|
||||||
# check message
|
# check message
|
||||||
public_key.verify_digest(sig[1:], h, sigdecode = ecdsa.util.sigdecode_string)
|
public_key.verify_digest(sig[1:], h, sigdecode = ecdsa.util.sigdecode_string)
|
||||||
|
|
|
@ -87,14 +87,13 @@ class Software_KeyStore(KeyStore):
|
||||||
return not self.is_watching_only()
|
return not self.is_watching_only()
|
||||||
|
|
||||||
def sign_message(self, sequence, message, password):
|
def sign_message(self, sequence, message, password):
|
||||||
privkey = self.get_private_key(sequence, password)
|
privkey, compressed = self.get_private_key(sequence, password)
|
||||||
compressed = self.use_compressed_pubkeys
|
|
||||||
key = regenerate_key(privkey)
|
key = regenerate_key(privkey)
|
||||||
return key.sign_message(message, compressed)
|
return key.sign_message(message, compressed)
|
||||||
|
|
||||||
def decrypt_message(self, sequence, message, password):
|
def decrypt_message(self, sequence, message, password):
|
||||||
sec = self.get_private_key(sequence, password)
|
privkey, compressed = self.get_private_key(sequence, password)
|
||||||
ec = regenerate_key(sec)
|
ec = regenerate_key(privkey)
|
||||||
decrypted = ec.decrypt_message(message)
|
decrypted = ec.decrypt_message(message)
|
||||||
return decrypted
|
return decrypted
|
||||||
|
|
||||||
|
@ -106,7 +105,7 @@ class Software_KeyStore(KeyStore):
|
||||||
# Add private keys
|
# Add private keys
|
||||||
keypairs = self.get_tx_derivations(tx)
|
keypairs = self.get_tx_derivations(tx)
|
||||||
for k, v in keypairs.items():
|
for k, v in keypairs.items():
|
||||||
keypairs[k] = self.get_private_key(v, password)
|
keypairs[k] = self.get_private_key(v, password)[0]
|
||||||
# Sign
|
# Sign
|
||||||
if keypairs:
|
if keypairs:
|
||||||
tx.sign(keypairs)
|
tx.sign(keypairs)
|
||||||
|
@ -156,7 +155,7 @@ class Imported_KeyStore(Software_KeyStore):
|
||||||
# this checks the password
|
# this checks the password
|
||||||
if pubkey != public_key_from_private_key(privkey, compressed):
|
if pubkey != public_key_from_private_key(privkey, compressed):
|
||||||
raise InvalidPassword()
|
raise InvalidPassword()
|
||||||
return privkey
|
return privkey, compressed
|
||||||
|
|
||||||
def get_pubkey_derivation(self, x_pubkey):
|
def get_pubkey_derivation(self, x_pubkey):
|
||||||
if x_pubkey[0:2] in ['02', '03', '04']:
|
if x_pubkey[0:2] in ['02', '03', '04']:
|
||||||
|
@ -279,7 +278,6 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
|
||||||
def __init__(self, d):
|
def __init__(self, d):
|
||||||
Xpub.__init__(self)
|
Xpub.__init__(self)
|
||||||
Deterministic_KeyStore.__init__(self, d)
|
Deterministic_KeyStore.__init__(self, d)
|
||||||
self.use_compressed_pubkeys = True
|
|
||||||
self.xpub = d.get('xpub')
|
self.xpub = d.get('xpub')
|
||||||
self.xprv = d.get('xprv')
|
self.xprv = d.get('xprv')
|
||||||
|
|
||||||
|
@ -331,7 +329,7 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
|
||||||
xprv = self.get_master_private_key(password)
|
xprv = self.get_master_private_key(password)
|
||||||
_, _, _, _, c, k = deserialize_xprv(xprv)
|
_, _, _, _, c, k = deserialize_xprv(xprv)
|
||||||
pk = bip32_private_key(sequence, k, c)
|
pk = bip32_private_key(sequence, k, c)
|
||||||
return pk
|
return pk, True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -340,7 +338,6 @@ class Old_KeyStore(Deterministic_KeyStore):
|
||||||
def __init__(self, d):
|
def __init__(self, d):
|
||||||
Deterministic_KeyStore.__init__(self, d)
|
Deterministic_KeyStore.__init__(self, d)
|
||||||
self.mpk = d.get('mpk')
|
self.mpk = d.get('mpk')
|
||||||
self.use_compressed_pubkeys = False
|
|
||||||
|
|
||||||
def get_hex_seed(self, password):
|
def get_hex_seed(self, password):
|
||||||
return pw_decode(self.seed, password).encode('utf8')
|
return pw_decode(self.seed, password).encode('utf8')
|
||||||
|
@ -421,7 +418,7 @@ class Old_KeyStore(Deterministic_KeyStore):
|
||||||
for_change, n = sequence
|
for_change, n = sequence
|
||||||
secexp = self.stretch_key(seed)
|
secexp = self.stretch_key(seed)
|
||||||
pk = self.get_private_key_from_stretched_exponent(for_change, n, secexp)
|
pk = self.get_private_key_from_stretched_exponent(for_change, n, secexp)
|
||||||
return pk
|
return pk, False
|
||||||
|
|
||||||
def check_seed(self, seed):
|
def check_seed(self, seed):
|
||||||
secexp = self.stretch_key(seed)
|
secexp = self.stretch_key(seed)
|
||||||
|
|
|
@ -273,8 +273,7 @@ class Abstract_Wallet(PrintError):
|
||||||
if self.is_watching_only():
|
if self.is_watching_only():
|
||||||
return []
|
return []
|
||||||
index = self.get_address_index(address)
|
index = self.get_address_index(address)
|
||||||
pk = self.keystore.get_private_key(index, password)
|
pk, compressed = self.keystore.get_private_key(index, password)
|
||||||
compressed = self.keystore.use_compressed_pubkeys
|
|
||||||
if self.txin_type in ['p2sh', 'p2wsh', 'p2wsh-p2sh']:
|
if self.txin_type in ['p2sh', 'p2wsh', 'p2wsh-p2sh']:
|
||||||
pubkeys = self.get_public_keys(address)
|
pubkeys = self.get_public_keys(address)
|
||||||
redeem_script = self.pubkeys_to_redeem_script(pubkeys)
|
redeem_script = self.pubkeys_to_redeem_script(pubkeys)
|
||||||
|
@ -282,13 +281,6 @@ class Abstract_Wallet(PrintError):
|
||||||
redeem_script = None
|
redeem_script = None
|
||||||
return bitcoin.serialize_privkey(pk, compressed, self.txin_type), redeem_script
|
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):
|
def get_public_keys(self, address):
|
||||||
sequence = self.get_address_index(address)
|
sequence = self.get_address_index(address)
|
||||||
|
@ -1336,6 +1328,15 @@ class Abstract_Wallet(PrintError):
|
||||||
def has_password(self):
|
def has_password(self):
|
||||||
return self.storage.get('use_encryption', False)
|
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):
|
class Imported_Wallet(Abstract_Wallet):
|
||||||
# wallet made of imported addresses
|
# wallet made of imported addresses
|
||||||
|
@ -1429,8 +1430,10 @@ class Imported_Wallet(Abstract_Wallet):
|
||||||
self.storage.write()
|
self.storage.write()
|
||||||
|
|
||||||
def get_address_index(self, address):
|
def get_address_index(self, address):
|
||||||
if self.keystore.can_import():
|
return self.get_public_key(address)
|
||||||
return self.addresses[address]['pubkey']
|
|
||||||
|
def get_public_key(self, address):
|
||||||
|
return self.addresses[address].get('pubkey')
|
||||||
|
|
||||||
def import_private_key(self, sec, pw, redeem_script=None):
|
def import_private_key(self, sec, pw, redeem_script=None):
|
||||||
try:
|
try:
|
||||||
|
@ -1461,7 +1464,11 @@ class Imported_Wallet(Abstract_Wallet):
|
||||||
sec = pw_decode(self.keystore.keypairs[pubkey], password)
|
sec = pw_decode(self.keystore.keypairs[pubkey], password)
|
||||||
return sec, redeem_script
|
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):
|
def add_input_sig_info(self, txin, address):
|
||||||
|
txin['type'] = self.get_txin_type(address)
|
||||||
if self.is_watching_only():
|
if self.is_watching_only():
|
||||||
addrtype, hash160 = b58_address_to_hash160(address)
|
addrtype, hash160 = b58_address_to_hash160(address)
|
||||||
x_pubkey = 'fd' + bh2u(bytes([addrtype]) + hash160)
|
x_pubkey = 'fd' + bh2u(bytes([addrtype]) + hash160)
|
||||||
|
@ -1469,8 +1476,6 @@ class Imported_Wallet(Abstract_Wallet):
|
||||||
txin['signatures'] = [None]
|
txin['signatures'] = [None]
|
||||||
return
|
return
|
||||||
|
|
||||||
txin_type = self.addresses[address]['type']
|
|
||||||
txin['type'] = txin_type
|
|
||||||
if txin_type in ['p2pkh', 'p2wkh', 'p2wkh-p2sh']:
|
if txin_type in ['p2pkh', 'p2wkh', 'p2wkh-p2sh']:
|
||||||
pubkey = self.addresses[address]['pubkey']
|
pubkey = self.addresses[address]['pubkey']
|
||||||
txin['num_sig'] = 1
|
txin['num_sig'] = 1
|
||||||
|
@ -1484,7 +1489,10 @@ class Imported_Wallet(Abstract_Wallet):
|
||||||
txin['redeem_script'] = redeem_script
|
txin['redeem_script'] = redeem_script
|
||||||
txin['signatures'] = [None] * num_keys
|
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):
|
class Deterministic_Wallet(Abstract_Wallet):
|
||||||
|
|
||||||
|
@ -1604,10 +1612,21 @@ class Deterministic_Wallet(Abstract_Wallet):
|
||||||
def get_fingerprint(self):
|
def get_fingerprint(self):
|
||||||
return self.get_master_public_key()
|
return self.get_master_public_key()
|
||||||
|
|
||||||
|
def get_txin_type(self, address):
|
||||||
|
return self.txin_type
|
||||||
|
|
||||||
class Simple_Wallet(Abstract_Wallet):
|
|
||||||
|
|
||||||
""" Wallet with a single pubkey per address """
|
class Simple_Deterministic_Wallet(Deterministic_Wallet):
|
||||||
|
|
||||||
|
""" 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):
|
def load_keystore(self):
|
||||||
self.keystore = load_keystore(self.storage, 'keystore')
|
self.keystore = load_keystore(self.storage, 'keystore')
|
||||||
|
@ -1631,30 +1650,12 @@ class Simple_Wallet(Abstract_Wallet):
|
||||||
return [self.get_public_key(address)]
|
return [self.get_public_key(address)]
|
||||||
|
|
||||||
def add_input_sig_info(self, txin, address):
|
def add_input_sig_info(self, txin, address):
|
||||||
if not self.keystore.can_import():
|
|
||||||
derivation = self.get_address_index(address)
|
derivation = self.get_address_index(address)
|
||||||
x_pubkey = self.keystore.get_xpubkey(*derivation)
|
x_pubkey = self.keystore.get_xpubkey(*derivation)
|
||||||
else:
|
|
||||||
x_pubkey = self.get_public_key(address)
|
|
||||||
txin['x_pubkeys'] = [x_pubkey]
|
txin['x_pubkeys'] = [x_pubkey]
|
||||||
txin['signatures'] = [None]
|
txin['signatures'] = [None]
|
||||||
txin['num_sig'] = 1
|
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):
|
def get_master_public_key(self):
|
||||||
return self.keystore.get_master_public_key()
|
return self.keystore.get_master_public_key()
|
||||||
|
|
||||||
|
@ -1687,9 +1688,6 @@ class Simple_Deterministic_Wallet(Deterministic_Wallet, Simple_Wallet):
|
||||||
def save_keystore(self):
|
def save_keystore(self):
|
||||||
self.storage.put('keystore', self.keystore.dump())
|
self.storage.put('keystore', self.keystore.dump())
|
||||||
|
|
||||||
def can_delete_address(self):
|
|
||||||
return self.keystore.can_import()
|
|
||||||
|
|
||||||
def delete_address(self, address):
|
def delete_address(self, address):
|
||||||
pubkey = self.get_public_key(address)
|
pubkey = self.get_public_key(address)
|
||||||
self.keystore.delete_imported_key(pubkey)
|
self.keystore.delete_imported_key(pubkey)
|
||||||
|
@ -1698,9 +1696,6 @@ class Simple_Deterministic_Wallet(Deterministic_Wallet, Simple_Wallet):
|
||||||
self.save_addresses()
|
self.save_addresses()
|
||||||
self.storage.write()
|
self.storage.write()
|
||||||
|
|
||||||
def can_import_privkey(self):
|
|
||||||
return self.keystore.can_import()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1710,6 +1705,7 @@ class Standard_Wallet(Simple_Deterministic_Wallet):
|
||||||
def pubkeys_to_address(self, pubkey):
|
def pubkeys_to_address(self, pubkey):
|
||||||
return bitcoin.pubkey_to_address(self.txin_type, pubkey)
|
return bitcoin.pubkey_to_address(self.txin_type, pubkey)
|
||||||
|
|
||||||
|
|
||||||
class Multisig_Wallet(Deterministic_Wallet):
|
class Multisig_Wallet(Deterministic_Wallet):
|
||||||
# generic m of n
|
# generic m of n
|
||||||
gap_limit = 20
|
gap_limit = 20
|
||||||
|
|
Loading…
Reference in New Issue