Browse Source

keystore.sign_message: add optional script_type argument

this is used by trezor
(and also by bitbox02, which was using a workaround previously)

fixes https://github.com/spesmilo/electrum/issues/7670
patch-4
SomberNight 3 years ago
parent
commit
376fc01b27
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 7
      electrum/ecc.py
  2. 11
      electrum/keystore.py
  3. 13
      electrum/plugins/bitbox02/bitbox02.py
  4. 2
      electrum/plugins/coldcard/coldcard.py
  5. 2
      electrum/plugins/digitalbitbox/digitalbitbox.py
  6. 2
      electrum/plugins/keepkey/keepkey.py
  7. 2
      electrum/plugins/ledger/ledger.py
  8. 2
      electrum/plugins/safe_t/safe_t.py
  9. 6
      electrum/plugins/trezor/clientbase.py
  10. 5
      electrum/plugins/trezor/trezor.py
  11. 6
      electrum/wallet.py

7
electrum/ecc.py

@ -499,7 +499,12 @@ class ECPrivkey(ECPubkey):
def sign_transaction(self, hashed_preimage: bytes) -> bytes:
return self.sign(hashed_preimage, sigencode=der_sig_from_r_and_s)
def sign_message(self, message: bytes, is_compressed: bool, algo=lambda x: sha256d(msg_magic(x))) -> bytes:
def sign_message(
self,
message: Union[bytes, str],
is_compressed: bool,
algo=lambda x: sha256d(msg_magic(x)),
) -> bytes:
def bruteforce_recid(sig_string):
for recid in range(4):
sig65 = construct_sig65(sig_string, recid, is_compressed)

11
electrum/keystore.py

@ -127,7 +127,14 @@ class KeyStore(Logger, ABC):
pass
@abstractmethod
def sign_message(self, sequence: 'AddressIndexGeneric', message, password) -> bytes:
def sign_message(
self,
sequence: 'AddressIndexGeneric',
message: str,
password,
*,
script_type: Optional[str] = None,
) -> bytes:
pass
@abstractmethod
@ -175,7 +182,7 @@ class Software_KeyStore(KeyStore):
def may_have_password(self):
return not self.is_watching_only()
def sign_message(self, sequence, message, password) -> bytes:
def sign_message(self, sequence, message, password, *, script_type=None) -> bytes:
privkey, compressed = self.get_private_key(sequence, password)
key = ecc.ECPrivkey(privkey)
return key.sign_message(message, compressed)

13
electrum/plugins/bitbox02/bitbox02.py

@ -524,7 +524,7 @@ class BitBox02Client(HardwareClientBase):
signatures = [bh2u(ecc.der_sig_from_sig_string(x[1])) + "01" for x in sigs]
tx.update_signatures(signatures)
def sign_message(self, keypath: str, message: bytes, xtype: str) -> bytes:
def sign_message(self, keypath: str, message: bytes, script_type: str) -> bytes:
if self.bitbox02_device is None:
raise Exception(
"Need to setup communication first before attempting any BitBox02 calls"
@ -534,9 +534,9 @@ class BitBox02Client(HardwareClientBase):
simple_type = {
"p2wpkh-p2sh":bitbox02.btc.BTCScriptConfig.P2WPKH_P2SH,
"p2wpkh": bitbox02.btc.BTCScriptConfig.P2WPKH,
}[xtype]
}[script_type]
except KeyError:
raise UserFacingException("The BitBox02 does not support signing messages for this address type: {}".format(xtype))
raise UserFacingException("The BitBox02 does not support signing messages for this address type: {}".format(script_type))
_, _, signature = self.bitbox02_device.btc_sign_msg(
self._get_coin(),
@ -560,7 +560,7 @@ class BitBox02_KeyStore(Hardware_KeyStore):
self.force_watching_only = False
self.ux_busy = False
def get_client(self):
def get_client(self) -> Optional['BitBox02Client']:
return self.plugin.get_client(self)
def give_error(self, message: Exception, clear_client: bool = False):
@ -580,13 +580,12 @@ class BitBox02_KeyStore(Hardware_KeyStore):
).format(self.device)
)
def sign_message(self, sequence, message, password):
def sign_message(self, sequence, message, password, *, script_type=None):
if password:
raise Exception("BitBox02 does not accept a password from the host")
client = self.get_client()
keypath = self.get_derivation_prefix() + "/%d/%d" % sequence
xtype = self.get_bip32_node_for_xpub().xtype
return client.sign_message(keypath, message.encode("utf-8"), xtype)
return client.sign_message(keypath, message.encode("utf-8"), script_type)
@runs_in_hwd_thread

2
electrum/plugins/coldcard/coldcard.py

@ -308,7 +308,7 @@ class Coldcard_KeyStore(Hardware_KeyStore):
raise UserFacingException(_('Encryption and decryption are currently not supported for {}').format(self.device))
@wrap_busy
def sign_message(self, sequence, message, password):
def sign_message(self, sequence, message, password, *, script_type=None):
# Sign a message on device. Since we have big screen, of course we
# have to show the message unabiguously there first!
try:

2
electrum/plugins/digitalbitbox/digitalbitbox.py

@ -455,7 +455,7 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
raise RuntimeError(_('Encryption and decryption are currently not supported for {}').format(self.device))
def sign_message(self, sequence, message, password):
def sign_message(self, sequence, message, password, *, script_type=None):
sig = None
try:
message = message.encode('utf8')

2
electrum/plugins/keepkey/keepkey.py

@ -38,7 +38,7 @@ class KeepKey_KeyStore(Hardware_KeyStore):
raise UserFacingException(_('Encryption and decryption are not implemented by {}').format(self.device))
@runs_in_hwd_thread
def sign_message(self, sequence, message, password):
def sign_message(self, sequence, message, password, *, script_type=None):
client = self.get_client()
address_path = self.get_derivation_prefix() + "/%d/%d"%sequence
address_n = client.expand_path(address_path)

2
electrum/plugins/ledger/ledger.py

@ -290,7 +290,7 @@ class Ledger_KeyStore(Hardware_KeyStore):
@runs_in_hwd_thread
@test_pin_unlocked
@set_and_unset_signing
def sign_message(self, sequence, message, password):
def sign_message(self, sequence, message, password, *, script_type=None):
message = message.encode('utf8')
message_hash = hashlib.sha256(message).hexdigest().upper()
# prompt for the PIN before displaying the dialog if necessary

2
electrum/plugins/safe_t/safe_t.py

@ -36,7 +36,7 @@ class SafeTKeyStore(Hardware_KeyStore):
raise UserFacingException(_('Encryption and decryption are not implemented by {}').format(self.device))
@runs_in_hwd_thread
def sign_message(self, sequence, message, password):
def sign_message(self, sequence, message, password, *, script_type=None):
client = self.get_client()
address_path = self.get_derivation_prefix() + "/%d/%d"%sequence
address_n = client.expand_path(address_path)

6
electrum/plugins/trezor/clientbase.py

@ -228,7 +228,7 @@ class TrezorClientBase(HardwareClientBase, Logger):
multisig=multisig)
@runs_in_hwd_thread
def sign_message(self, address_str, message):
def sign_message(self, address_str, message, *, script_type):
coin_name = self.plugin.get_coin_name()
address_n = parse_path(address_str)
with self.run_flow():
@ -236,7 +236,9 @@ class TrezorClientBase(HardwareClientBase, Logger):
self.client,
coin_name,
address_n,
message)
message,
script_type=script_type,
no_script_type=True)
@runs_in_hwd_thread
def recover_device(self, recovery_type, *args, **kwargs):

5
electrum/plugins/trezor/trezor.py

@ -77,10 +77,11 @@ class TrezorKeyStore(Hardware_KeyStore):
def decrypt_message(self, sequence, message, password):
raise UserFacingException(_('Encryption and decryption are not implemented by {}').format(self.device))
def sign_message(self, sequence, message, password):
def sign_message(self, sequence, message, password, *, script_type=None):
client = self.get_client()
address_path = self.get_derivation_prefix() + "/%d/%d"%sequence
msg_sig = client.sign_message(address_path, message)
script_type = self.plugin.get_trezor_input_script_type(script_type)
msg_sig = client.sign_message(address_path, message, script_type=script_type)
return msg_sig.signature
def sign_transaction(self, tx, password):

6
electrum/wallet.py

@ -2452,9 +2452,11 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
def _update_password_for_keystore(self, old_pw: Optional[str], new_pw: Optional[str]) -> None:
pass
def sign_message(self, address, message, password):
def sign_message(self, address: str, message: str, password) -> bytes:
index = self.get_address_index(address)
return self.keystore.sign_message(index, message, password)
script_type = self.get_txin_type(address)
assert script_type != "address"
return self.keystore.sign_message(index, message, password, script_type=script_type)
def decrypt_message(self, pubkey: str, message, password) -> bytes:
addr = self.pubkeys_to_address([pubkey])

Loading…
Cancel
Save