Example #1
0
 def do_send_static(d):
     ''' Decouples button slot from running instance in case user stops/restarts the plugin while TxDialogs are up. '''
     plugin = Plugin.Instance_ref()
     if plugin:
         plugin.do_send(d)
     else:
         print_error("[cosigner_pool] No plugin.")
Example #2
0
 def getDevice2FAMode(self):
     apdu = [0xe0, 0x24, 0x01, 0x00, 0x00, 0x01]  # get 2fa mode
     try:
         mode = self.dongle.exchange(bytearray(apdu))
         return mode
     except BTChipException as e:
         print_error('Device getMode Failed')
     return 0x11
Example #3
0
 def give_error(self, message, clear_client=False):
     print_error(message)
     if not self.signing:
         self.handler.show_error(message)
     else:
         self.signing = False
     if clear_client:
         self.client = None
     raise Exception(message)
Example #4
0
 def __init__(self, config):
     super().__init__(None)  # Top-level Object
     if Exception_Hook._instance:
         return  # This is ok, we will be GC'd later.
     Exception_Hook._instance = self  # strong reference to self should keep us alive until uninstall() is called
     self.config = config
     sys.excepthook = self.handler  # yet another strong reference. We really won't die unless uninstall() is called
     self._report_exception.connect(_show_window)
     print_error("[{}] Installed.".format(__class__.__qualname__))
     finalization_print_error(
         self, "[{}] Finalized.".format(__class__.__qualname__))
     destroyed_print_error(self)
Example #5
0
    def qr_input(self, callback=None):
        if self.qr_dialog:
            # Re-entrancy prevention -- there is some lag between when the user
            # taps the QR button and the modal dialog appears.  We want to
            # prevent multiple instances of the dialog from appearing, so we
            # must do this.
            util.print_error(
                "[ScanQRTextEdit] Warning: QR dialog is already presented, ignoring."
            )
            return
        from . import ElectrumGui
        if ElectrumGui.instance.warn_if_cant_import_qrreader(self):
            return
        from oregano import get_config
        from .qrreader import QrReaderCameraDialog
        try:
            self.qr_dialog = QrReaderCameraDialog(
                parent=self.top_level_window())

            def _on_qr_reader_finished(success: bool, error: str, result):
                if self.qr_dialog:
                    self.qr_dialog.deleteLater()
                    self.qr_dialog = None
                if not success:
                    if error:
                        self.show_error(error)
                    return
                if not result:
                    result = ''
                if self.allow_multi:
                    new_text = self.text() + result + '\n'
                else:
                    new_text = result
                self.setText(new_text)
                if callback and success:
                    callback(result)

            self.qr_dialog.qr_finished.connect(_on_qr_reader_finished)
            self.qr_dialog.start_scan(get_config().get_video_device())
        except Exception as e:
            if util.is_verbose:
                import traceback
                traceback.print_exc()
            self.qr_dialog = None
            self.show_error(str(e))
Example #6
0
def verify_ssl_socket(host, port, timeout=5.0):
    path = (Network.get_instance() and Network.get_instance().config
            and Network.get_instance().config.path) or None
    if not path:
        print_error("verify_ssl_socket: no config path!")
        return False
    server = "{}:{}:s".format(host, port)
    q = queue.Queue()
    c = Connection(server, q, path)
    socket = None
    try:
        server, socket = q.get(timeout=timeout)
    except queue.Empty:
        pass
    ret = bool(socket and socket.fileno() > -1)
    if socket: socket.close()
    del (q, c, socket, server)
    return ret
Example #7
0
def patch(dark: bool = False, darkstyle_ver: tuple = None):
    if not dark:
        return

    app = QtWidgets.QApplication.instance()

    if darkstyle_ver is None or darkstyle_ver < (2, 6, 8):
        # only apply this patch to qdarkstyle < 2.6.8.
        # 2.6.8 and above seem to not need it.

        style_sheet = app.styleSheet()
        style_sheet = style_sheet + '''
        QWidget:disabled {
            color: hsl(0, 0, 50%);
        }
        QPushButton:disabled {
            border-color: hsl(0, 0, 50%);
            color: hsl(0, 0, 50%);
        }
        '''
        app.setStyleSheet(style_sheet)
        print_error(
            "[style_patcher] qdarkstyle < 2.6.8 detected; stylesheet patch #1 applied"
        )
    else:
        # This patch is for qdarkstyle >= 2.6.8.

        style_sheet = app.styleSheet()
        style_sheet = style_sheet + '''
        /* PayToEdit was being cut off on QDatkStyle >= 2.6.8. This fixes that. */
        ButtonsTextEdit {
            padding: 0px;
        }
        /* History List labels, when editing, were looking terrible in Windows and were cut off. This fixes that.*/
        QTreeWidget QLineEdit {
            show-decoration-selected: 1;
            padding: 0px;
        }
        '''
        app.setStyleSheet(style_sheet)
        print_error(
            "[style_patcher] qdarkstyle >= 2.6.8 detected; stylesheet patch #2 applied"
        )
Example #8
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'')
         r = to_string(r, 'utf8')
         reply = json.loads(r)
     except Exception as e:
         print_error('Exception caught ' + str(e))
     return reply
Example #9
0
    def reset_seed_dialog(self, msg):
        print_error("In reset_seed_dialog")
        parent = self.top_level_window()
        d = WindowModalDialog(parent, _("Enter PIN"))
        pw = QLineEdit()
        pw.setEchoMode(2)
        pw.setMinimumWidth(200)

        cb_reset_2FA = QCheckBox(_('Also reset 2FA'))

        vbox = QVBoxLayout()
        vbox.addWidget(WWLabel(msg))
        vbox.addWidget(pw)
        vbox.addWidget(cb_reset_2FA)
        vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
        d.setLayout(vbox)

        passphrase = pw.text() if d.exec_() else None

        reset_2FA= cb_reset_2FA.isChecked()
        return (passphrase, reset_2FA)
Example #10
0
    def change_pin(self, client):
        print_error("In change_pin")
        msg_oldpin = _("Enter the current PIN for your Satochip:")
        msg_newpin = _("Enter a new PIN for your Satochip:")
        msg_confirm = _("Please confirm the new PIN for your Satochip:")
        msg_error= _("The PIN values do not match! Please type PIN again!")
        msg_cancel= _("PIN Change cancelled!")
        (is_pin, oldpin, newpin) = client.PIN_change_dialog(msg_oldpin, msg_newpin, msg_confirm, msg_error, msg_cancel)
        if (not is_pin):
            return

        
        oldpin= list(oldpin)    
        newpin= list(newpin)  
        (response, sw1, sw2)= client.cc.card_change_PIN(0, oldpin, newpin)
        if (sw1==0x90 and sw2==0x00):
            msg= _("PIN changed successfully!")
            client.handler.show_message(msg)
        else:
            msg= _("Failed to change PIN!")
            client.handler.show_error(msg)
Example #11
0
    def show_values(self, client):
        print_error("Show value!")
        sw_rel= 'v' + str(SATOCHIP_PROTOCOL_MAJOR_VERSION) + '.' + str(SATOCHIP_PROTOCOL_MINOR_VERSION)
        self.sw_version.setText('<tt>%s' % sw_rel)

        (response, sw1, sw2, d)=client.cc.card_get_status()
        if (sw1==0x90 and sw2==0x00):
            fw_rel= 'v' + str(d["protocol_major_version"]) + '.' + str(d["protocol_minor_version"])
            self.fw_version.setText('<tt>%s' % fw_rel)

            #is_seeded?
            if len(response) >=10:
                self.is_seeded.setText('<tt>%s' % "yes") if d["is_seeded"] else self.is_seeded.setText('<tt>%s' % "no")
            else: #for earlier versions
                try:
                    client.cc.card_bip32_get_authentikey()
                    self.is_seeded.setText('<tt>%s' % "yes")
                except Exception:
                    self.is_seeded.setText('<tt>%s' % "no")

            # needs2FA?
            if d["needs2FA"]:
                self.needs_2FA.setText('<tt>%s' % "yes")
            else:
                self.needs_2FA.setText('<tt>%s' % "no")
            
            # needs secure channel
            if d["needs_secure_channel"]:
                self.needs_SC.setText('<tt>%s' % "yes")
            else:
                self.needs_SC.setText('<tt>%s' % "no")

        else:
            fw_rel= "(unitialized)"
            self.fw_version.setText('<tt>%s' % fw_rel)
            self.needs_2FA.setText('<tt>%s' % "(unitialized)")
            self.is_seeded.setText('<tt>%s' % "no")
            self.needs_SC.setText('<tt>%s' % "(unknown)")
Example #12
0
 def hid_send_encrypt(self, msg):
     sha256_byte_len = 32
     reply = ""
     try:
         encryption_key, authentication_key = derive_keys(self.password)
         msg = EncodeAES_bytes(encryption_key, msg)
         hmac_digest = hmac_oneshot(authentication_key, msg, hashlib.sha256)
         authenticated_msg = base64.b64encode(msg + hmac_digest)
         reply = self.hid_send_plain(authenticated_msg)
         if 'ciphertext' in reply:
             b64_unencoded = bytes(base64.b64decode(''.join(reply["ciphertext"])))
             reply_hmac = b64_unencoded[-sha256_byte_len:]
             hmac_calculated = hmac_oneshot(authentication_key, b64_unencoded[:-sha256_byte_len], hashlib.sha256)
             if not hmac.compare_digest(reply_hmac, hmac_calculated):
                 raise Exception("Failed to validate HMAC")
             reply = DecodeAES_bytes(encryption_key, b64_unencoded[:-sha256_byte_len])
             reply = to_string(reply, 'utf8')
             reply = json.loads(reply)
         if 'error' in reply:
             self.password = None
     except Exception as e:
         print_error('Exception caught ' + str(e))
     return reply
Example #13
0
    def present(self, frame: QVideoFrame) -> bool:
        if not frame.isValid():
            return False

        image_format = QVideoFrame.imageFormatFromPixelFormat(
            frame.pixelFormat())
        if image_format == QVideoFrame.Format_Invalid:
            print_error(
                _('QR code scanner for video frame with invalid pixel format'))
            return False

        if not frame.map(QAbstractVideoBuffer.ReadOnly):
            print_error(_('QR code scanner failed to map video frame'))
            return False

        try:
            img = QImage(frame.bits(), frame.width(), frame.height(),
                         image_format)

            # Check whether we need to flip the image on any axis
            surface_format = self.surfaceFormat()
            flip_x = surface_format.isMirrored()
            flip_y = surface_format.scanLineDirection(
            ) == QVideoSurfaceFormat.BottomToTop

            # Mirror the image if needed
            if flip_x or flip_y:
                img = img.mirrored(flip_x, flip_y)

            # Create a copy of the image so the original frame data can be freed
            img = img.copy()
        finally:
            frame.unmap()

        self.frame_available.emit(img)

        return True
Example #14
0
    def reset_seed(self, client):
        print_error("In reset_seed")
        # pin
        msg = ''.join([
            _("WARNING!\n"),
            _("You are about to reset the seed of your Satochip. This process is irreversible!\n"),
            _("Please be sure that your wallet is empty and that you have a backup of the seed as a precaution.\n\n"),
            _("To proceed, enter the PIN for your Satochip:")
        ])
        (password, reset_2FA)= self.reset_seed_dialog(msg)
        if (password is None):
            return
        pin = password.encode('utf8')
        pin= list(pin)

        # if 2FA is enabled, get challenge-response
        hmac=[]
        if (client.cc.needs_2FA==None):
            (response, sw1, sw2, d)=client.cc.card_get_status()
        if client.cc.needs_2FA:
            # challenge based on authentikey
            authentikeyx= bytearray(client.cc.parser.authentikey_coordx).hex()

            # format & encrypt msg
            import json
            msg= {'action':"reset_seed", 'authentikeyx':authentikeyx}
            msg=  json.dumps(msg)
            (id_2FA, msg_out)= client.cc.card_crypt_transaction_2FA(msg, True)
            d={}
            d['msg_encrypt']= msg_out
            d['id_2FA']= id_2FA
            # print_error("encrypted message: "+msg_out)
            #print_error("id_2FA: "+id_2FA)

            #do challenge-response with 2FA device...
            client.handler.show_message('2FA request sent! Approve or reject request on your second device.')
            Satochip2FA.do_challenge_response(d)
            # decrypt and parse reply to extract challenge response
            try:
                reply_encrypt= d['reply_encrypt']
            except Exception as e:
                self.give_error("No response received from 2FA.\nPlease ensure that the Satochip-2FA plugin is enabled in Tools>Optional Features", True)
            reply_decrypt= client.cc.card_crypt_transaction_2FA(reply_encrypt, False)
            print_error("challenge:response= "+ reply_decrypt)
            reply_decrypt= reply_decrypt.split(":")
            chalresponse=reply_decrypt[1]
            hmac= list(bytes.fromhex(chalresponse))

        # send request
        (response, sw1, sw2) = client.cc.card_reset_seed(pin, hmac)
        if (sw1==0x90 and sw2==0x00):
            msg= _("Seed reset successfully!\nYou should close this wallet and launch the wizard to generate a new wallet.")
            client.handler.show_message(msg)
            #to do: close client?
        else:
            msg= _(f"Failed to reset seed with error code: {hex(sw1)}{hex(sw2)}")
            client.handler.show_error(msg)

        if reset_2FA and client.cc.needs_2FA:
            # challenge based on ID_2FA
            # format & encrypt msg
            import json
            msg= {'action':"reset_2FA"}
            msg=  json.dumps(msg)
            (id_2FA, msg_out)= client.cc.card_crypt_transaction_2FA(msg, True)
            d={}
            d['msg_encrypt']= msg_out
            d['id_2FA']= id_2FA
            # print_error("encrypted message: "+msg_out)
            print_error("id_2FA: "+id_2FA)

            #do challenge-response with 2FA device...
            client.handler.show_message('2FA request sent! Approve or reject request on your second device.')
            Satochip2FA.do_challenge_response(d)
            # decrypt and parse reply to extract challenge response
            try:
                reply_encrypt= d['reply_encrypt']
            except Exception as e:
                self.give_error("No response received from 2FA.\nPlease ensure that the Satochip-2FA plugin is enabled in Tools>Optional Features", True)
            reply_decrypt= client.cc.card_crypt_transaction_2FA(reply_encrypt, False)
            print_error("challenge:response= "+ reply_decrypt)
            reply_decrypt= reply_decrypt.split(":")
            chalresponse=reply_decrypt[1]
            hmac= list(bytes.fromhex(chalresponse))

            # send request
            (response, sw1, sw2) = client.cc.card_reset_2FA_key(hmac)
            if (sw1==0x90 and sw2==0x00):
                msg= _("2FA reset successfully!")
                client.cc.needs_2FA= False
                client.handler.show_message(msg)
            else:
                msg= _(f"Failed to reset 2FA with error code: {hex(sw1)}{hex(sw2)}")
                client.handler.show_error(msg)
Example #15
0
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import (QComboBox, QGridLayout, QLabel, QPushButton)

PLATFORM_LIBS = {
    'Linux': 'libportaudio.so',
}

err_reason = 'Audio MODEM is available.'

if platform.system() in PLATFORM_LIBS:
    try:
        import amodem.audio
        import amodem.main
        import amodem.config
        print_error(err_reason)
        amodem.log.addHandler(amodem.logging.StreamHandler(sys.stderr))
        amodem.log.setLevel(amodem.logging.INFO)
    except ImportError:
        amodem = None
        err_reason = 'Audio MODEM is not found.'
        print_error(err_reason)
else:
    amodem = None
    err_reason = "Audio modem plugin is not available for this platform"
    print_error(err_reason)


class Plugin(BasePlugin):
    def __init__(self, parent, config, name):
        BasePlugin.__init__(self, parent, config, name)
Example #16
0
 def update_status(self, b):
     print_error('trezor status', b)
Example #17
0
 def uninstall():
     sys.excepthook = sys.__excepthook__
     if Exception_Hook._instance:
         print_error("[{}] Uninstalled.".format(__class__.__qualname__))
         Exception_Hook._instance = None
Example #18
0
    def sign_transaction(self, tx, password, *, use_cache=False):
        if tx.is_complete():
            return

        try:
            p2pkhTransaction = True
            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'] != 'p2pkh':
                    p2pkhTransaction = False

                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

            # Build pubkeyarray from outputs
            for _type, address, amount in tx.outputs():
                info = tx.output_info.get(address)
                if info is not None:
                    index, xpubs, m, script_type = 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.
            # At the moment, verification only works for p2pkh transactions.
            if p2pkhTransaction:
                class CustomTXSerialization(Transaction):
                    @classmethod
                    def input_script(self, txin, estimate_size=False, sign_schnorr=False):
                        if txin['type'] == 'p2pkh':
                            return Transaction.get_preimage_script(txin)
                        if txin['type'] == 'p2sh':
                            # Multisig verification has partial support, but is disabled. This is the
                            # expected serialization though, so we leave it here until we activate it.
                            return '00' + push_script(Transaction.get_preimage_script(txin))
                        raise Exception("unsupported type %s" % txin['type'])
                tx_dbb_serialized = CustomTXSerialization(tx.serialize()).serialize()
            else:
                # We only need this for the signing echo / verification.
                tx_dbb_serialized = None

            # 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": {
                        "data": hashes,
                        "checkpub": pubkeyarray,
                    },
                }
                if tx_dbb_serialized is not None:
                    msg["sign"]["meta"] = to_hexstr(Hash(tx_dbb_serialized))
                msg = json.dumps(msg).encode('ascii')
                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.")

                if self.plugin.is_mobile_paired() and tx_dbb_serialized is not None:
                    reply['tx'] = tx_dbb_serialized
                    self.plugin.comserver_post_notification(reply)

                if steps > 1:
                    self.handler.show_message(_("Signing large transaction. Please be patient ...") + "\n\n" +
                                              _("To continue, touch the Digital Bitbox's blinking light for 3 seconds.") + " " +
                                              _("(Touch {} of {})").format((step + 1), steps) + "\n\n" +
                                              _("To cancel, briefly touch the blinking light or wait for the timeout.") + "\n\n")
                else:
                    self.handler.show_message(_("Signing transaction...") + "\n\n" +
                                              _("To continue, touch the Digital Bitbox's blinking light for 3 seconds.") + "\n\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.finished()

                if 'error' in reply:
                    if reply["error"].get('code') in (600, 601):
                        # aborted via LED short touch or timeout
                        raise UserCancelled()
                    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) + '41'
                    tx._inputs[i] = txin
        except UserCancelled:
            raise
        except BaseException as e:
            self.give_error(e, True)
        else:
            print_error("Transaction is_complete", tx.is_complete())
            tx.raw = tx.serialize()
Example #19
0
    from pysatochip.CardConnector import CardConnector, UninitializedSeedError, logger
    from pysatochip.JCconstants import JCconstants
    from pysatochip.TxParser import TxParser
    from pysatochip.Satochip2FA import Satochip2FA
    from pysatochip.ecc import CURVE_ORDER, der_sig_from_r_and_s, get_r_and_s_from_der_sig, ECPubkey
    from pysatochip.version import SATOCHIP_PROTOCOL_MAJOR_VERSION, SATOCHIP_PROTOCOL_MINOR_VERSION, SATOCHIP_PROTOCOL_VERSION

    #pyscard
    from smartcard.sw.SWExceptions import SWException
    from smartcard.Exceptions import CardConnectionException, CardRequestTimeoutException
    from smartcard.CardType import AnyCardType
    from smartcard.CardRequest import CardRequest
    LIBS_AVAILABLE = True
except:
    LIBS_AVAILABLE = False
    print_error("[satochip] failed to import requisite libraries, please install the 'pyscard' and 'pysatochip' libraries")
    print_error("[satochip] satochip will not not be available")
    raise

logging.basicConfig(level=logging.DEBUG, format='%(levelname)s [%(module)s] %(funcName)s | %(message)s') #debugSatochip

# debug: smartcard reader ids
SATOCHIP_VID= 0x096E
SATOCHIP_PID= 0x0503

MSG_USE_2FA= _("Do you want to use 2-Factor-Authentication (2FA)?\n\nWith 2FA, "
               "any transaction must be confirmed on a second device such as your "
               "smartphone. First you have to install the Satochip-2FA android app on "
               "google play. Then you have to pair your 2FA device with your Satochip "
               "by scanning the qr-code on the next screen. Warning: be sure to backup "
               "a copy of the qr-code in a safe place, in case you have to reinstall the app!")