mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-23 17:47:31 +00:00
- fix sign/verify messages
- fix hardware wallet tx_outputs
This commit is contained in:
parent
b436042c89
commit
b87c5d12fa
8 changed files with 95 additions and 55 deletions
|
@ -371,6 +371,16 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):
|
||||||
action = c_values[clayout.selected_index()]
|
action = c_values[clayout.selected_index()]
|
||||||
return action
|
return action
|
||||||
|
|
||||||
|
@wizard_dialog
|
||||||
|
def input_dialog(self, title, message, run_next):
|
||||||
|
line = QLineEdit()
|
||||||
|
vbox = QVBoxLayout()
|
||||||
|
vbox.addWidget(QLabel(message))
|
||||||
|
vbox.addWidget(line)
|
||||||
|
self.set_main_layout(vbox, title)
|
||||||
|
action = line.text()
|
||||||
|
return action
|
||||||
|
|
||||||
@wizard_dialog
|
@wizard_dialog
|
||||||
def show_xpub_dialog(self, xpub, run_next):
|
def show_xpub_dialog(self, xpub, run_next):
|
||||||
msg = ' '.join([
|
msg = ' '.join([
|
||||||
|
|
|
@ -1849,7 +1849,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
||||||
|
|
||||||
pubkey_e = QLineEdit()
|
pubkey_e = QLineEdit()
|
||||||
if address:
|
if address:
|
||||||
pubkey = self.wallet.get_public_keys(address)[0]
|
sequence = self.wallet.get_address_index(address)
|
||||||
|
pubkey = self.wallet.get_pubkey(*sequence)
|
||||||
pubkey_e.setText(pubkey)
|
pubkey_e.setText(pubkey)
|
||||||
layout.addWidget(QLabel(_('Public key')), 2, 0)
|
layout.addWidget(QLabel(_('Public key')), 2, 0)
|
||||||
layout.addWidget(pubkey_e, 2, 1)
|
layout.addWidget(pubkey_e, 2, 1)
|
||||||
|
|
|
@ -176,7 +176,6 @@ class BaseWizard(object):
|
||||||
from keystore import load_keystore
|
from keystore import load_keystore
|
||||||
keystore = load_keystore(self.storage, None)
|
keystore = load_keystore(self.storage, None)
|
||||||
keystore.plugin.on_create_wallet(keystore, self)
|
keystore.plugin.on_create_wallet(keystore, self)
|
||||||
self.create_wallet(keystore, None)
|
|
||||||
|
|
||||||
def on_hardware_seed(self):
|
def on_hardware_seed(self):
|
||||||
from keystore import load_keystore
|
from keystore import load_keystore
|
||||||
|
|
|
@ -410,9 +410,17 @@ def msg_magic(message):
|
||||||
return "\x18Bitcoin Signed Message:\n" + encoded_varint + message
|
return "\x18Bitcoin Signed Message:\n" + encoded_varint + message
|
||||||
|
|
||||||
|
|
||||||
def verify_message(address, signature, message):
|
def verify_message(address, sig, message):
|
||||||
try:
|
try:
|
||||||
EC_KEY.verify_message(address, signature, message)
|
public_key, compressed = pubkey_from_signature(sig, message)
|
||||||
|
# check public key using the address
|
||||||
|
pubkey = point_to_ser(public_key.pubkey.point, compressed)
|
||||||
|
addr = public_key_to_bc_address(pubkey)
|
||||||
|
if address != addr:
|
||||||
|
raise Exception("Bad signature")
|
||||||
|
# check message
|
||||||
|
h = Hash(msg_magic(message))
|
||||||
|
public_key.verify_digest(sig[1:], h, sigdecode = ecdsa.util.sigdecode_string)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print_error("Verification error: {0}".format(e))
|
print_error("Verification error: {0}".format(e))
|
||||||
|
@ -493,6 +501,22 @@ class MyVerifyingKey(ecdsa.VerifyingKey):
|
||||||
return klass.from_public_point( Q, curve )
|
return klass.from_public_point( Q, curve )
|
||||||
|
|
||||||
|
|
||||||
|
def pubkey_from_signature(sig, message):
|
||||||
|
if len(sig) != 65:
|
||||||
|
raise Exception("Wrong encoding")
|
||||||
|
nV = ord(sig[0])
|
||||||
|
if nV < 27 or nV >= 35:
|
||||||
|
raise Exception("Bad encoding")
|
||||||
|
if nV >= 31:
|
||||||
|
compressed = True
|
||||||
|
nV -= 4
|
||||||
|
else:
|
||||||
|
compressed = False
|
||||||
|
recid = nV - 27
|
||||||
|
h = Hash(msg_magic(message))
|
||||||
|
return MyVerifyingKey.from_signature(sig[1:], recid, h, curve = SECP256k1), compressed
|
||||||
|
|
||||||
|
|
||||||
class MySigningKey(ecdsa.SigningKey):
|
class MySigningKey(ecdsa.SigningKey):
|
||||||
"""Enforce low S values in signatures"""
|
"""Enforce low S values in signatures"""
|
||||||
|
|
||||||
|
@ -524,41 +548,27 @@ class EC_KEY(object):
|
||||||
assert public_key.verify_digest(signature, msg_hash, sigdecode = ecdsa.util.sigdecode_string)
|
assert public_key.verify_digest(signature, msg_hash, sigdecode = ecdsa.util.sigdecode_string)
|
||||||
return signature
|
return signature
|
||||||
|
|
||||||
def sign_message(self, message, compressed, address):
|
def sign_message(self, message, is_compressed):
|
||||||
signature = self.sign(Hash(msg_magic(message)))
|
signature = self.sign(Hash(msg_magic(message)))
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
sig = chr(27 + i + (4 if compressed else 0)) + signature
|
sig = chr(27 + i + (4 if is_compressed else 0)) + signature
|
||||||
try:
|
try:
|
||||||
self.verify_message(address, sig, message)
|
self.verify_message(sig, message)
|
||||||
return sig
|
return sig
|
||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
raise Exception("error: cannot sign message")
|
raise Exception("error: cannot sign message")
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def verify_message(self, address, sig, message):
|
|
||||||
if len(sig) != 65:
|
|
||||||
raise Exception("Wrong encoding")
|
|
||||||
nV = ord(sig[0])
|
|
||||||
if nV < 27 or nV >= 35:
|
|
||||||
raise Exception("Bad encoding")
|
|
||||||
if nV >= 31:
|
|
||||||
compressed = True
|
|
||||||
nV -= 4
|
|
||||||
else:
|
|
||||||
compressed = False
|
|
||||||
recid = nV - 27
|
|
||||||
|
|
||||||
h = Hash(msg_magic(message))
|
def verify_message(self, sig, message):
|
||||||
public_key = MyVerifyingKey.from_signature(sig[1:], recid, h, curve = SECP256k1)
|
public_key, compressed = pubkey_from_signature(sig, message)
|
||||||
# check public key
|
# check public key
|
||||||
public_key.verify_digest(sig[1:], h, sigdecode = ecdsa.util.sigdecode_string)
|
if point_to_ser(public_key.pubkey.point, compressed) != point_to_ser(self.pubkey.point, compressed):
|
||||||
pubkey = point_to_ser(public_key.pubkey.point, compressed)
|
|
||||||
# check that we get the original signing address
|
|
||||||
addr = public_key_to_bc_address(pubkey)
|
|
||||||
if address != addr:
|
|
||||||
raise Exception("Bad signature")
|
raise Exception("Bad signature")
|
||||||
|
# check message
|
||||||
|
h = Hash(msg_magic(message))
|
||||||
|
public_key.verify_digest(sig[1:], h, sigdecode = ecdsa.util.sigdecode_string)
|
||||||
|
|
||||||
|
|
||||||
# ECIES encryption/decryption methods; AES-128-CBC with PKCS7 is used as the cipher; hmac-sha256 is used as the mac
|
# ECIES encryption/decryption methods; AES-128-CBC with PKCS7 is used as the cipher; hmac-sha256 is used as the mac
|
||||||
|
|
|
@ -62,6 +62,19 @@ class Software_KeyStore(KeyStore):
|
||||||
def has_password(self):
|
def has_password(self):
|
||||||
return self.use_encryption
|
return self.use_encryption
|
||||||
|
|
||||||
|
def sign_message(self, sequence, message, password):
|
||||||
|
sec = self.get_private_key(sequence, password)
|
||||||
|
key = regenerate_key(sec)
|
||||||
|
compressed = is_compressed(sec)
|
||||||
|
return key.sign_message(message, compressed)
|
||||||
|
|
||||||
|
def decrypt_message(self, sequence, message, password):
|
||||||
|
sec = self.get_private_key(sequence, password)
|
||||||
|
ec = regenerate_key(sec)
|
||||||
|
decrypted = ec.decrypt_message(message)
|
||||||
|
return decrypted
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Imported_KeyStore(Software_KeyStore):
|
class Imported_KeyStore(Software_KeyStore):
|
||||||
# keystore for imported private keys
|
# keystore for imported private keys
|
||||||
|
@ -109,6 +122,11 @@ class Imported_KeyStore(Software_KeyStore):
|
||||||
def delete_imported_key(self, key):
|
def delete_imported_key(self, key):
|
||||||
self.keypairs.pop(key)
|
self.keypairs.pop(key)
|
||||||
|
|
||||||
|
def get_public_key(self, sequence):
|
||||||
|
for_change, i = sequence
|
||||||
|
pubkey = (self.change_pubkeys if for_change else self.receiving_pubkeys)[i]
|
||||||
|
return pubkey
|
||||||
|
|
||||||
def get_private_key(self, sequence, password):
|
def get_private_key(self, sequence, password):
|
||||||
for_change, i = sequence
|
for_change, i = sequence
|
||||||
assert for_change == 0
|
assert for_change == 0
|
||||||
|
@ -296,14 +314,14 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
|
||||||
def make_seed(self, lang=None):
|
def make_seed(self, lang=None):
|
||||||
return Mnemonic(lang).make_seed()
|
return Mnemonic(lang).make_seed()
|
||||||
|
|
||||||
@classmethod
|
#@classmethod
|
||||||
def address_derivation(self, account_id, change, address_index):
|
#def address_derivation(self, account_id, change, address_index):
|
||||||
account_derivation = self.account_derivation(account_id)
|
# account_derivation = self.account_derivation(account_id)
|
||||||
return "%s/%d/%d" % (account_derivation, change, address_index)
|
# return "%s/%d/%d" % (account_derivation, change, address_index)
|
||||||
|
|
||||||
def address_id(self, address):
|
#def address_id(self, address):
|
||||||
acc_id, (change, address_index) = self.get_address_index(address)
|
# acc_id, (change, address_index) = self.get_address_index(address)
|
||||||
return self.address_derivation(acc_id, change, address_index)
|
# return self.address_derivation(acc_id, change, address_index)
|
||||||
|
|
||||||
def add_seed_and_xprv(self, seed, password, passphrase=''):
|
def add_seed_and_xprv(self, seed, password, passphrase=''):
|
||||||
xprv, xpub = bip32_root(self.mnemonic_to_seed(seed, passphrase))
|
xprv, xpub = bip32_root(self.mnemonic_to_seed(seed, passphrase))
|
||||||
|
|
|
@ -276,22 +276,6 @@ class Abstract_Wallet(PrintError):
|
||||||
sequence = self.get_address_index(address)
|
sequence = self.get_address_index(address)
|
||||||
return self.get_pubkeys(*sequence)
|
return self.get_pubkeys(*sequence)
|
||||||
|
|
||||||
def sign_message(self, address, message, password):
|
|
||||||
keys = self.get_private_key(address, password)
|
|
||||||
assert len(keys) == 1
|
|
||||||
sec = keys[0]
|
|
||||||
key = regenerate_key(sec)
|
|
||||||
compressed = is_compressed(sec)
|
|
||||||
return key.sign_message(message, compressed, address)
|
|
||||||
|
|
||||||
def decrypt_message(self, pubkey, message, password):
|
|
||||||
address = public_key_to_bc_address(pubkey.decode('hex'))
|
|
||||||
keys = self.get_private_key(address, password)
|
|
||||||
secret = keys[0]
|
|
||||||
ec = regenerate_key(secret)
|
|
||||||
decrypted = ec.decrypt_message(message)
|
|
||||||
return decrypted
|
|
||||||
|
|
||||||
def add_unverified_tx(self, tx_hash, tx_height):
|
def add_unverified_tx(self, tx_hash, tx_height):
|
||||||
# tx will be verified only if height > 0
|
# tx will be verified only if height > 0
|
||||||
if tx_hash not in self.verified_tx:
|
if tx_hash not in self.verified_tx:
|
||||||
|
@ -1036,7 +1020,7 @@ class Abstract_Wallet(PrintError):
|
||||||
tx.output_info = []
|
tx.output_info = []
|
||||||
for i, txout in enumerate(tx.outputs()):
|
for i, txout in enumerate(tx.outputs()):
|
||||||
_type, addr, amount = txout
|
_type, addr, amount = txout
|
||||||
change, address_index = self.get_address_index(addr) if self.is_change(addr) else None, None
|
change, address_index = self.get_address_index(addr) if self.is_change(addr) else (None, None)
|
||||||
tx.output_info.append((change, address_index))
|
tx.output_info.append((change, address_index))
|
||||||
|
|
||||||
# sign
|
# sign
|
||||||
|
@ -1251,6 +1235,13 @@ class P2PK_Wallet(Abstract_Wallet):
|
||||||
pubkey_list = self.change_pubkeys if c else self.receiving_pubkeys
|
pubkey_list = self.change_pubkeys if c else self.receiving_pubkeys
|
||||||
return pubkey_list[i]
|
return pubkey_list[i]
|
||||||
|
|
||||||
|
def get_pubkey_index(self, pubkey):
|
||||||
|
if pubkey in self.receiving_pubkeys:
|
||||||
|
return False, self.receiving_pubkeys.index(pubkey)
|
||||||
|
if pubkey in self.change_pubkeys:
|
||||||
|
return True, self.change_pubkeys.index(pubkey)
|
||||||
|
raise BaseExeption('pubkey not found')
|
||||||
|
|
||||||
def add_input_sig_info(self, txin, address):
|
def add_input_sig_info(self, txin, address):
|
||||||
txin['derivation'] = derivation = self.get_address_index(address)
|
txin['derivation'] = derivation = self.get_address_index(address)
|
||||||
x_pubkey = self.keystore.get_xpubkey(*derivation)
|
x_pubkey = self.keystore.get_xpubkey(*derivation)
|
||||||
|
@ -1262,6 +1253,14 @@ class P2PK_Wallet(Abstract_Wallet):
|
||||||
txin['num_sig'] = 1
|
txin['num_sig'] = 1
|
||||||
txin['can_sign'] = any([x is None for x in txin['signatures']])
|
txin['can_sign'] = any([x is None for x in txin['signatures']])
|
||||||
|
|
||||||
|
def sign_message(self, address, message, password):
|
||||||
|
sequence = self.get_address_index(address)
|
||||||
|
return self.keystore.sign_message(sequence, message, password)
|
||||||
|
|
||||||
|
def decrypt_message(self, pubkey, message, password):
|
||||||
|
sequence = self.get_pubkey_index(pubkey)
|
||||||
|
return self.keystore.decrypt_message(sequence, message, password)
|
||||||
|
|
||||||
|
|
||||||
class Deterministic_Wallet(Abstract_Wallet):
|
class Deterministic_Wallet(Abstract_Wallet):
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,9 @@ class TrezorCompatibleKeyStore(Hardware_KeyStore):
|
||||||
root = "m/44'/0'"
|
root = "m/44'/0'"
|
||||||
account_id = 0
|
account_id = 0
|
||||||
|
|
||||||
|
def load(self, storage, name):
|
||||||
|
self.xpub = storage.get('master_public_keys', {}).get(name)
|
||||||
|
|
||||||
def get_derivation(self):
|
def get_derivation(self):
|
||||||
return self.root + "/%d'"%self.account_id
|
return self.root + "/%d'"%self.account_id
|
||||||
|
|
||||||
|
@ -46,9 +49,9 @@ class TrezorCompatibleKeyStore(Hardware_KeyStore):
|
||||||
result = client.decrypt_message(address_n, nonce, message, msg_hmac)
|
result = client.decrypt_message(address_n, nonce, message, msg_hmac)
|
||||||
return result.message
|
return result.message
|
||||||
|
|
||||||
def sign_message(self, address, message, password):
|
def sign_message(self, sequence, message, password):
|
||||||
client = self.get_client()
|
client = self.get_client()
|
||||||
address_path = self.address_id(address)
|
address_path = self.get_derivation() + "/%d/%d"%sequence
|
||||||
address_n = client.expand_path(address_path)
|
address_n = client.expand_path(address_path)
|
||||||
msg_sig = client.sign_message('Bitcoin', address_n, message)
|
msg_sig = client.sign_message('Bitcoin', address_n, message)
|
||||||
return msg_sig.signature
|
return msg_sig.signature
|
||||||
|
@ -312,7 +315,7 @@ class TrezorCompatiblePlugin(HW_PluginBase):
|
||||||
txoutputtype.op_return_data = address[2:]
|
txoutputtype.op_return_data = address[2:]
|
||||||
elif _type == TYPE_ADDRESS:
|
elif _type == TYPE_ADDRESS:
|
||||||
if change is not None:
|
if change is not None:
|
||||||
address_path = "%s/%d/%d/"%(derivation, change, index)
|
address_path = "%s/%d/%d"%(derivation, change, index)
|
||||||
address_n = self.client_class.expand_path(address_path)
|
address_n = self.client_class.expand_path(address_path)
|
||||||
txoutputtype.address_n.extend(address_n)
|
txoutputtype.address_n.extend(address_n)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -285,7 +285,6 @@ def qt_plugin_class(base_plugin_class):
|
||||||
keystore.thread.add(partial(self.get_client, keystore))
|
keystore.thread.add(partial(self.get_client, keystore))
|
||||||
|
|
||||||
def on_create_wallet(self, keystore, wizard):
|
def on_create_wallet(self, keystore, wizard):
|
||||||
#assert type(keystore) == self.keystore_class
|
|
||||||
keystore.handler = self.create_handler(wizard)
|
keystore.handler = self.create_handler(wizard)
|
||||||
keystore.thread = TaskThread(wizard, wizard.on_error)
|
keystore.thread = TaskThread(wizard, wizard.on_error)
|
||||||
# Setup device and create accounts in separate thread; wait until done
|
# Setup device and create accounts in separate thread; wait until done
|
||||||
|
@ -298,6 +297,7 @@ def qt_plugin_class(base_plugin_class):
|
||||||
if exc_info:
|
if exc_info:
|
||||||
wizard.on_error(exc_info)
|
wizard.on_error(exc_info)
|
||||||
raise UserCancelled
|
raise UserCancelled
|
||||||
|
wizard.create_wallet(keystore, None)
|
||||||
|
|
||||||
@hook
|
@hook
|
||||||
def receive_menu(self, menu, addrs, wallet):
|
def receive_menu(self, menu, addrs, wallet):
|
||||||
|
|
Loading…
Add table
Reference in a new issue