示例#1
0
    def perform_hw1_preflight(self):
        try:
            firmwareInfo = self.dongleObject.getFirmwareVersion()
            firmware = firmwareInfo['version']
            self.multiOutputSupported = versiontuple(firmware) >= versiontuple(
                MULTI_OUTPUT_SUPPORT)
            self.nativeSegwitSupported = versiontuple(
                firmware) >= versiontuple(SEGWIT_SUPPORT)
            self.segwitSupported = self.nativeSegwitSupported or (
                firmwareInfo['specialVersion'] == 0x20 and
                versiontuple(firmware) >= versiontuple(SEGWIT_SUPPORT_SPECIAL))

            if not checkFirmware(firmwareInfo):
                self.dongleObject.dongle.close()
                raise Exception(MSG_NEEDS_FW_UPDATE_GENERIC)
            try:
                self.dongleObject.getOperationMode()
            except BTChipException as e:
                print_error('perform_hw1_preflight ex1', e)
                if (e.sw == 0x6985):
                    self.dongleObject.dongle.close()
                    self.handler.get_setup()
                    # Acquire the new client on the next run
                else:
                    raise e
            if self.has_detached_pin_support(
                    self.dongleObject) and not self.is_pin_validated(
                        self.dongleObject) and (self.handler is not None):
                remaining_attempts = self.dongleObject.getVerifyPinRemainingAttempts(
                )
                if remaining_attempts != 1:
                    msg = "Enter your Ledger PIN - remaining attempts : " + str(
                        remaining_attempts)
                else:
                    msg = "Enter your Ledger PIN - WARNING : LAST ATTEMPT. If the PIN is not correct, the dongle will be wiped."
                confirmed, p, pin = self.password_dialog(msg)
                if not confirmed:
                    raise Exception(
                        'Aborted by user - please unplug the dongle and plug it again before retrying'
                    )
                pin = pin.encode()
                self.dongleObject.verifyPin(pin)
        except BTChipException as e:
            if (e.sw == 0x6faa):
                raise Exception(
                    "Dongle is temporarily locked - please unplug it and replug it again"
                )
            if ((e.sw & 0xFFF0) == 0x63c0):
                raise Exception(
                    "Invalid PIN - please unplug the dongle and plug it again before retrying"
                )
            if e.sw == 0x6f00 and e.message == 'Invalid channel':
                # based on docs 0x6f00 might be a more general error, hence we also compare message to be sure
                raise Exception(
                    "Invalid channel.\n"
                    "Please make sure that 'Browser support' is disabled on your device."
                )
            print('perform_hw1_preflight ex2', e)
            raise e
示例#2
0
 def give_error(self, message, clear_client=False):
     print_error('give_error', message)
     if not self.signing:
         self.handler.show_error(message)
     else:
         self.signing = False
     if clear_client:
         self.client = None
     raise Exception(message)
示例#3
0
 def has_detached_pin_support(self, client):
     try:
         client.getVerifyPinRemainingAttempts()
         return True
     except BTChipException as e:
         if e.sw == 0x6d00:
             return False
         print_error('has_detached_pin_support exception', e, e.sw)
         raise e
示例#4
0
 def checkDevice(self):
     if not self.preflightDone:
         try:
             self.perform_hw1_preflight()
         except BTChipException as e:
             print_error('checkDevice', e)
             if (e.sw == 0x6d00 or e.sw == 0x6700):
                 raise Exception(_("Device not in Btn mode")) from e
             raise e
         self.preflightDone = True
示例#5
0
 def is_pin_validated(self, client):
     try:
         # Invalid SET OPERATION MODE to verify the PIN status
         client.dongle.exchange(
             bytearray([0xe0, 0x26, 0x00, 0x00, 0x01, 0xAB]))
     except BTChipException as e:
         print_error('is_pin_validated exception', e, e.sw)
         if (e.sw == 0x6982):
             return False
         if (e.sw == 0x6A80):
             return True
         raise e
示例#6
0
    def start_new_window(self, path, uri):
        '''Raises the window for the wallet if it is open.  Otherwise
        opens the wallet and creates a new window for it'''
        try:
            wallet = self.daemon.load_wallet(path, None)
        except BaseException as e:
            traceback.print_exc(file=sys.stdout)
            d = QMessageBox(QMessageBox.Warning, _('Error'),
                            _('Cannot load wallet:') + '\n' + str(e))
            d.exec_()
            return
        if not wallet:
            storage = WalletStorage(path)
            wizard = InstallWizard(self.config, self.app, self.plugins,
                                   storage)
            try:
                wallet = wizard.run_and_get_wallet(self.daemon.get_wallet)
            except UserCancelled:
                pass
            except GoBack as e:
                print_error('[start_new_window] Exception caught (GoBack)', e)
            wizard.terminate()
            if not wallet:
                return

            if not self.daemon.get_wallet(wallet.storage.path):
                # wallet was not in memory
                wallet.start_threads(self.daemon.network)
                self.daemon.add_wallet(wallet)
        try:
            for w in self.windows:
                if w.wallet.storage.path == wallet.storage.path:
                    w.bring_to_top()
                    return
            w = self.create_window_for_wallet(wallet)
        except BaseException as e:
            traceback.print_exc(file=sys.stdout)
            d = QMessageBox(
                QMessageBox.Warning, _('Error'),
                _('Cannot create window for wallet:') + '\n' + str(e))
            d.exec_()
            return
        if uri:
            w.pay_to_URI(uri)
        w.bring_to_top()
        w.setWindowState(w.windowState() & ~QtCore.Qt.WindowMinimized
                         | QtCore.Qt.WindowActive)

        # this will activate the window
        w.activateWindow()
        return w
示例#7
0
 def hid_send_encrypt(self, msg):
     reply = ""
     try:
         secret = Hash(self.password)
         msg = EncodeAES(secret, msg)
         reply = self.hid_send_plain(msg)
         if 'ciphertext' in reply:
             reply = DecodeAES(secret, ''.join(reply["ciphertext"]))
             reply = json.loads(reply)
         if 'error' in reply:
             self.password = None
     except Exception as e:
         print_error('Exception caught ' + str(e))
     return reply
示例#8
0
 def sign_message(self, sequence, message, password):
     message = message.encode('utf8')
     message_hash = hashlib.sha256(message).hexdigest().upper()
     # prompt for the PIN before displaying the dialog if necessary
     client = self.get_client()
     address_path = self.get_derivation()[2:] + "/%d/%d" % sequence
     self.handler.show_message("Signing message ...\r\nMessage hash: " +
                               message_hash)
     try:
         info = self.get_client().signMessagePrepare(address_path, message)
         pin = ""
         if info['confirmationNeeded']:
             pin = self.handler.get_auth(
                 info)  # does the authenticate dialog and returns pin
             if not pin:
                 raise UserWarning(_('Cancelled by user'))
             pin = str(pin).encode()
         signature = self.get_client().signMessageSign(pin)
     except BTChipException as e:
         print_error('ledger sign_message', e)
         if e.sw == 0x6a80:
             self.give_error(
                 "Unfortunately, this message cannot be signed by the Ledger wallet. Only alphanumerical messages shorter than 140 characters are supported. Please remove any extra characters (tab, carriage return) and retry."
             )
         elif e.sw == 0x6985:  # cancelled by user
             return b''
         elif e.sw == 0x6982:
             raise  # pin lock. decorator will catch it
         else:
             self.give_error(e, True)
     except UserWarning:
         self.handler.show_error(_('Cancelled by user'))
         return b''
     except Exception as e:
         self.give_error(e, True)
     finally:
         self.handler.finished()
     # Parse the ASN.1 signature
     rLength = signature[3]
     r = signature[4:4 + rLength]
     sLength = signature[4 + rLength + 1]
     s = signature[4 + rLength + 2:]
     if rLength == 33:
         r = r[1:]
     if sLength == 33:
         s = s[1:]
     # And convert it
     return bytes([27 + 4 + (signature[0] & 0x01)]) + r + s
 def use_tor_proxy(self, use_it):
     if not use_it:
         self.proxy_cb.setChecked(False)
     else:
         socks5_mode_index = self.proxy_mode.findText('SOCKS5')
         if socks5_mode_index == -1:
             print_error("[network_dialog] can't find proxy_mode 'SOCKS5'")
             return
         self.proxy_mode.setCurrentIndex(socks5_mode_index)
         self.proxy_host.setText("127.0.0.1")
         self.proxy_port.setText(str(self.tor_proxy[1]))
         self.proxy_user.setText("")
         self.proxy_password.setText("")
         self.tor_cb.setChecked(True)
         self.proxy_cb.setChecked(True)
     self.check_disable_proxy(use_it)
     self.set_proxy()
示例#10
0
 def hid_send_plain(self, msg):
     reply = ""
     try:
         serial_number = self.dbb_hid.get_serial_number_string()
         if "v2.0." in serial_number or "v1." in serial_number:
             hidBufSize = 4096
             self.dbb_hid.write('\0' + msg + '\0' * (hidBufSize - len(msg)))
             r = bytearray()
             while len(r) < hidBufSize:
                 r += bytearray(self.dbb_hid.read(hidBufSize))
         else:
             self.hid_send_frame(msg)
             r = self.hid_read_frame()
         r = r.rstrip(b' \t\r\n\0')
         r = r.replace(b"\0", b'')
         reply = json.loads(r)
     except Exception as e:
         print_error('Exception caught ' + str(e))
     return reply
示例#11
0
 def interface_changed(self):
     interface_text = self.interface_e.text()
     try:
         interface = json.loads(interface_text)
         constructor = {}
         for abi in interface:
             if abi.get('type') == 'constructor':
                 constructor = abi
                 break
         self.constructor = constructor
         if not constructor:
             self.args_e.setPlaceholderText('')
             return
         signature = '{}'.format(', '.join([
             '{} {}'.format(i.get('type'), i.get('name'))
             for i in constructor.get('inputs', [])
         ]))
         self.args_e.setPlaceholderText(signature)
     except (BaseException, ) as e:
         self.constructor = {}
         self.args_e.setPlaceholderText('')
         print_error('[interface_changed]', str(e))
示例#12
0
import platform

from btn_electrum.plugins import BasePlugin, hook
from btn_electrum_gui.qt.util import WaitingDialog, EnterButton, WindowModalDialog
from btn_electrum.util import print_msg, print_error
from btn_electrum.i18n import _

from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import (QComboBox, QGridLayout, QLabel, QPushButton)

try:
    import amodem.audio
    import amodem.main
    import amodem.config
    print_error('Audio MODEM is available.')
    amodem.log.addHandler(amodem.logging.StreamHandler(sys.stderr))
    amodem.log.setLevel(amodem.logging.INFO)
except ImportError:
    amodem = None
    print_error('Audio MODEM is not found.')


class Plugin(BasePlugin):
    def __init__(self, parent, config, name):
        BasePlugin.__init__(self, parent, config, name)
        if self.is_available():
            self.modem_config = amodem.config.slowest()
            self.library_name = {'Linux': 'libportaudio.so'}[platform.system()]

    def is_available(self):
示例#13
0
 def update_status(self, b):
     print_error('hw device status', b)
示例#14
0
    def sign_transaction(self, tx, password):
        if tx.is_complete():
            return

        try:
            p2shTransaction = False
            derivations = self.get_tx_derivations(tx)
            inputhasharray = []
            hasharray = []
            pubkeyarray = []

            # Build hasharray from inputs
            for i, txin in enumerate(tx.inputs()):
                if txin['type'] == 'coinbase':
                    self.give_error("Coinbase not supported") # should never happen

                if txin['type'] in ['p2sh']:
                    p2shTransaction = True

                for x_pubkey in txin['x_pubkeys']:
                    if x_pubkey in derivations:
                        index = derivations.get(x_pubkey)
                        inputPath = "%s/%d/%d" % (self.get_derivation(), index[0], index[1])
                        inputHash = Hash(binascii.unhexlify(tx.serialize_preimage(i)))
                        hasharray_i = {'hash': to_hexstr(inputHash), 'keypath': inputPath}
                        hasharray.append(hasharray_i)
                        inputhasharray.append(inputHash)
                        break
                else:
                    self.give_error("No matching x_key for sign_transaction") # should never happen

            # Sanity check
            if p2shTransaction:
                for txinput in tx.inputs():
                    if txinput['type'] != 'p2sh':
                        self.give_error("P2SH / regular input mixed in same transaction not supported") # should never happen

            # Build pubkeyarray from outputs
            for _type, address, amount in tx.outputs():
                assert _type == TYPE_ADDRESS
                info = tx.output_info.get(address)
                if info is not None:
                    index, xpubs, m = info
                    changePath = self.get_derivation() + "/%d/%d" % index
                    changePubkey = self.derive_pubkey(index[0], index[1])
                    pubkeyarray_i = {'pubkey': changePubkey, 'keypath': changePath}
                    pubkeyarray.append(pubkeyarray_i)

            # Special serialization of the unsigned transaction for
            # the mobile verification app.
            class CustomTXSerialization(Transaction):
                @classmethod
                def input_script(self, txin, estimate_size=False):
                    if txin['type'] == 'p2pkh':
                        return Transaction.get_preimage_script(txin)
                    if txin['type'] == 'p2sh':
                        return '00' + push_script(Transaction.get_preimage_script(txin))
                    raise Exception("unsupported type %s" % txin['type'])
            tx_dbb_serialized = CustomTXSerialization(tx.serialize()).serialize()

            # Build sign command
            dbb_signatures = []
            steps = math.ceil(1.0 * len(hasharray) / self.maxInputs)
            for step in range(int(steps)):
                hashes = hasharray[step * self.maxInputs : (step + 1) * self.maxInputs]

                msg = ('{"sign": {"meta":"%s", "data":%s, "checkpub":%s} }' % \
                       (to_hexstr(Hash(tx_dbb_serialized)), json.dumps(hashes), json.dumps(pubkeyarray))).encode('utf8')
                dbb_client = self.plugin.get_client(self)

                if not dbb_client.is_paired():
                    raise Exception("Could not sign transaction.")

                reply = dbb_client.hid_send_encrypt(msg)
                if 'error' in reply:
                    raise Exception(reply['error']['message'])

                if 'echo' not in reply:
                    raise Exception("Could not sign transaction.")

                # multisig verification not working correctly yet
                if self.plugin.is_mobile_paired() and not p2shTransaction:
                    reply['tx'] = tx_dbb_serialized
                    self.plugin.comserver_post_notification(reply)

                if steps > 1:
                    self.handler.show_message(_("Signing large transaction. Please be patient ...\r\n\r\n" \
                                                "To continue, touch the Digital Bitbox's blinking light for 3 seconds. " \
                                                "(Touch " + str(step + 1) + " of " + str(int(steps)) + ")\r\n\r\n" \
                                                "To cancel, briefly touch the blinking light or wait for the timeout.\r\n\r\n"))
                else:
                    self.handler.show_message(_("Signing transaction ...\r\n\r\n" \
                                                "To continue, touch the Digital Bitbox's blinking light for 3 seconds.\r\n\r\n" \
                                                "To cancel, briefly touch the blinking light or wait for the timeout."))

                # Send twice, first returns an echo for smart verification
                reply = dbb_client.hid_send_encrypt(msg)
                self.handler.clear_dialog()

                if 'error' in reply:
                    raise Exception(reply['error']['message'])

                if 'sign' not in reply:
                    raise Exception("Could not sign transaction.")

                dbb_signatures.extend(reply['sign'])

            # Fill signatures
            if len(dbb_signatures) != len(tx.inputs()):
                raise Exception("Incorrect number of transactions signed.") # Should never occur
            for i, txin in enumerate(tx.inputs()):
                num = txin['num_sig']
                for pubkey in txin['pubkeys']:
                    signatures = list(filter(None, txin['signatures']))
                    if len(signatures) == num:
                        break # txin is complete
                    ii = txin['pubkeys'].index(pubkey)
                    signed = dbb_signatures[i]
                    if 'recid' in signed:
                        # firmware > v2.1.1
                        recid = int(signed['recid'], 16)
                        s = binascii.unhexlify(signed['sig'])
                        h = inputhasharray[i]
                        pk = MyVerifyingKey.from_signature(s, recid, h, curve = SECP256k1)
                        pk = to_hexstr(point_to_ser(pk.pubkey.point, True))
                    elif 'pubkey' in signed:
                        # firmware <= v2.1.1
                        pk = signed['pubkey']
                    if pk != pubkey:
                        continue
                    sig_r = int(signed['sig'][:64], 16)
                    sig_s = int(signed['sig'][64:], 16)
                    sig = sigencode_der(sig_r, sig_s, generator_secp256k1.order())
                    txin['signatures'][ii] = to_hexstr(sig) + '01'
                    tx._inputs[i] = txin
        except BaseException as e:
            self.give_error(e, True)
        else:
            print_error("Transaction is_complete", tx.is_complete())
            tx.raw = tx.serialize()