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
def save_qrcode(self, qrw, name): p = qrw and qrw.grab() filename = os.path.join(self.working_directory, name) if p and not p.isNull(): if filename: print_error("saving") p.save(filename, 'png')
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.")
def detect_smartcard_reader(self): print_error("[satochip] SatochipPlugin: detect_smartcard_reader" ) #debugSatochip self.cardtype = AnyCardType() try: cardrequest = CardRequest(timeout=5, cardType=self.cardtype) cardservice = cardrequest.waitforcard() print_error( "[satochip] SatochipPlugin: detect_smartcard_reader: found card!" ) #debugSatochip return [ Device(path="/satochip", interface_number=-1, id_="/satochip", product_key=(SATOCHIP_VID, SATOCHIP_PID), usage_page=0) ] #transport_ui_string='ccid')] except CardRequestTimeoutException: print('time-out: no card inserted during last 5s') return [] except Exception as exc: print("Error during connection:", exc) return [] return []
def set_2FA(self, client): if not client.cc.needs_2FA: use_2FA = client.handler.yes_no_question(MSG_USE_2FA) if (use_2FA): secret_2FA = urandom(20) secret_2FA_hex = secret_2FA.hex() # the secret must be shared with the second factor app (eg on a smartphone) try: d = QRDialog(secret_2FA_hex, None, "Scan secret 2FA and save a copy", True) d.exec_() except Exception as e: print_error("SatochipPlugin: setup 2FA error: " + str(e)) return # further communications will require an id and an encryption key (for privacy). # Both are derived from the secret_2FA using a one-way function inside the Satochip amount_limit = 0 # i.e. always use (response, sw1, sw2) = client.cc.card_set_2FA_key(secret_2FA, amount_limit) if sw1 != 0x90 or sw2 != 0x00: print_error( f"Unable to set 2FA with error code:= {hex(256*sw1+sw2)}" ) #debugSatochip raise RuntimeError( f'Unable to setup 2FA with error code: {hex(256*sw1+sw2)}' ) else: client.handler.show_message("2FA enabled successfully!") else: msg = _(f"2FA is already enabled!") client.handler.show_error(msg)
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
def set_2FA(self, client): if not client.cc.needs_2FA: use_2FA = client.handler.yes_no_question(MSG_USE_2FA) if (use_2FA): secret_2FA = urandom(20) secret_2FA_hex = secret_2FA.hex() # the secret must be shared with the second factor app (eg on a smartphone) try: help_txt = "Scan the QR-code with your Satochip-2FA app and make a backup of the following secret: " + secret_2FA_hex d = QRDialog(data=secret_2FA_hex, parent=None, title="Secret_2FA", show_text=False, help_text=help_txt) d.exec_() except Exception as e: print_error("SatochipPlugin: setup 2FA error: " + str(e)) return # further communications will require an id and an encryption key (for privacy). # Both are derived from the secret_2FA using a one-way function inside the Satochip amount_limit = 0 # i.e. always use (response, sw1, sw2) = client.cc.card_set_2FA_key(secret_2FA, amount_limit) if sw1 != 0x90 or sw2 != 0x00: print_error( f"Unable to set 2FA with error code:= {hex(256*sw1+sw2)}" ) #debugSatochip raise RuntimeError( f'Unable to setup 2FA with error code: {hex(256*sw1+sw2)}' ) else: client.handler.show_message("2FA enabled successfully!")
def __init__(self, client): # request any card type self.client = client self.parser = client.parser self.cardtype = AnyCardType() self.needs_2FA = None try: # request card insertion self.cardrequest = CardRequest(timeout=10, cardType=self.cardtype) self.cardservice = self.cardrequest.waitforcard() # attach the console tracer self.observer = LogCardConnectionObserver( ) #ConsoleCardConnectionObserver() self.cardservice.connection.addObserver(self.observer) # attach the card removal observer self.cardmonitor = CardMonitor() self.cardobserver = RemovalObserver(self) self.cardmonitor.addObserver(self.cardobserver) # connect to the card and perform a few transmits self.cardservice.connection.connect() # cache PIN self.pin_nbr = None self.pin = None except CardRequestTimeoutException: print_error('time-out: no card inserted during last 10s') except Exception as exc: print_error("Error during connection:", exc)
def card_bip32_get_authentikey(self): cla = JCconstants.CardEdge_CLA ins = JCconstants.INS_BIP32_GET_AUTHENTIKEY p1 = 0x00 p2 = 0x00 le = 0x00 apdu = [cla, ins, p1, p2, le] # send apdu response, sw1, sw2 = self.card_transmit(apdu) if sw1 == 0x9c and sw2 == 0x14: print_error( "[CardConnector] card_bip32_get_authentikey(): Seed is not initialized => Raising error!" ) raise UninitializedSeedError('Seed is not initialized') if sw1 == 0x9c and sw2 == 0x04: print_error( "[CardConnector] card_bip32_get_authentikey(): Satochip is not initialized => Raising error!" ) raise UninitializedSeedError( 'Satochip is not initialized! You should create a new wallet!') # compute corresponding pubkey and send to chip for future use if (sw1 == 0x90) and (sw2 == 0x00): authentikey = self.card_bip32_set_authentikey_pubkey(response) return authentikey
def _parse_version(cls, version_msg): try: return version.parse_package_version(version_msg) except: print_error("[{}] Error parsing version '{}': {}".format( cls.__name__, version_msg, repr(sys.exc_info()[1]))) raise
def card_transmit(self, apdu): try: (response, sw1, sw2) = self.cardservice.connection.transmit(apdu) if (sw1 == 0x9C) and (sw2 == 0x06): (response, sw1, sw2) = self.card_verify_PIN() (response, sw1, sw2) = self.cardservice.connection.transmit(apdu) return (response, sw1, sw2) except CardConnectionException: # maybe the card has been removed try: self.cardrequest = CardRequest(timeout=10, cardType=self.cardtype) self.cardservice = self.cardrequest.waitforcard() # attach the console tracer self.observer = LogCardConnectionObserver( ) #ConsoleCardConnectionObserver() self.cardservice.connection.addObserver(self.observer) # connect to the card and perform a few transmits self.cardservice.connection.connect() # retransmit apdu (response, sw1, sw2) = self.cardservice.connection.transmit(apdu) if (sw1 == 0x9C) and (sw2 == 0x06): (response, sw1, sw2) = self.card_verify_PIN() (response, sw1, sw2) = self.cardservice.connection.transmit(apdu) return (response, sw1, sw2) except CardRequestTimeoutException: print_error('time-out: no card inserted during last 10s') except Exception as exc: print_error("Error during connection:", exc)
def show_values(self, client): print_error("Show value!") v_supported = (CardConnector.SATOCHIP_PROTOCOL_MAJOR_VERSION << 8) + CardConnector.SATOCHIP_PROTOCOL_MINOR_VERSION sw_rel = hex(v_supported) self.sw_version.setText('<tt>%s' % sw_rel) (response, sw1, sw2, d) = client.cc.card_get_status() if (sw1 == 0x90 and sw2 == 0x00): v_applet = ( d["protocol_major_version"] << 8) + d["protocol_minor_version"] fw_rel = hex(v_applet) self.fw_version.setText('<tt>%s' % fw_rel) #is_seeded? 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 len(response) >= 9 and response[8] == 0X01: self.needs_2FA.setText('<tt>%s' % "yes") elif len(response) >= 9 and response[8] == 0X00: self.needs_2FA.setText('<tt>%s' % "no") else: self.needs_2FA.setText('<tt>%s' % "(unknown)") 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")
def verify_message_with_address(address: str, sig65: bytes, message: bytes, *, net=None): from electroncash.bitcoin import pubkey_to_address assert_bytes(sig65, message) if net is None: net = constants.net try: h = Hash(msg_magic(message)) public_key, compressed = ECPubkey.from_signature65(sig65, h) # check public key using the address pubkey_hex = public_key.get_public_key_hex(compressed) for txin_type in ['p2pkh', 'p2wpkh', 'p2wpkh-p2sh']: addr = pubkey_to_address(txin_type, pubkey_hex, net=net) if address == addr: break else: raise Exception("Bad signature") # check message public_key.verify_message_hash(sig65[1:], h) return True except Exception as e: print_error(f"Verification error: {repr(e)}") return False
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
def __del__(self): stale = True if __class__.instance is self: stale = False __class__.instance = None print_error("[{}] finalized{}".format(__class__.__name__, ' (stale instance)' if stale else '')) if hasattr(super(), '__del__'): super().__del__()
def resolve(self): self.is_alias, self.validated = False, False if self.hasFocus(): return if self.is_multiline(): # only supports single line entries atm return if self.is_pr: return key = str(self.toPlainText()) key = key.strip() # strip whitespaces if key == self.previous_payto: return self.previous_payto = key if not (('.' in key) and (not '<' in key) and (not ' ' in key)): return parts = key.split(sep=',') # assuming single line if parts and len(parts) > 0 and Address.is_valid(parts[0]): return try: data = self.win.contacts.resolve(key) except Exception as e: print_error(f'error resolving alias: {repr(e)}') return if not data: return address = data.get('address') name = data.get('name') _type = data.get('type') if _type != 'openalias': return address_str = None if isinstance(address, str): address_str = address elif isinstance(address, Address): address_str = address.to_ui_string() else: raise RuntimeError('unknown address type') self.is_alias = True new_url = key + ' <' + address_str + '>' self.setText(new_url) self.previous_payto = new_url self.win.contacts[key] = ('openalias', name) self.win.contact_list.update() self.setFrozen(True) self.validated = bool(data.get('validated')) if self.validated: self.setGreen() else: self.setExpired()
def delete_temp_wallet_file(file): ''' deletes the wallet file ''' if file and os.path.exists(file): try: os.remove(file) print_error("[InterWalletTransfer] Removed temp file", file) except Exception as e: print_error("[InterWalletTransfer] Failed to remove temp file", file, "error: ", repr(e))
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)
def resolve(self): self.is_alias = False if self.hasFocus(): return if self.is_multiline(): # only supports single line entries atm return if self.is_pr: return key = str(self.toPlainText()) if key == self.previous_payto: return self.previous_payto = key if not (('.' in key) and (not '<' in key) and (not ' ' in key)): return parts = key.split(sep=',') # assuming single lie if parts and len(parts) > 0 and Address.is_valid(parts[0]): return try: data = self.win.contacts.resolve(key) except: return if not data: return self.is_alias = True address = data.get('address') name = data.get('name') new_url = key + ' <' + address + '>' self.setText(new_url) self.previous_payto = new_url if isinstance(self.win.contacts, dict): # old contacts API self.win.contacts[key] = ('openalias', name) else: try: from electroncash.contacts import Contact self.win.contacts.add(Contact(name=name, address=key, type='openalias'), unique=True) except Exception as e: print_error( "[Custom PayToEdit] Could not determine contacts API, giving up:", repr(e)) self.win.contact_list.on_update() self.setFrozen(True) if data.get('type') == 'openalias': self.validated = data.get('validated') if self.validated: self.setGreen() else: self.setExpired() else: self.validated = None
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)
def update(self, observable, actions): (addedcards, removedcards) = actions for card in addedcards: print_error("+Inserted: ", toHexString(card.atr)) self.parent.client.handler.update_status(True) for card in removedcards: print_error("-Removed: ", toHexString(card.atr)) self.parent.pin = None #reset PIN self.parent.pin_nbr = None self.parent.client.handler.update_status(False)
def start_new_window(self, path, uri, daemon): '''Raises the window for the wallet if it is open. Otherwise opens the wallet and creates a new window for it.''' for w in self.windows[daemon.currency]: if w.wallet.storage.path == path: w.bring_to_top() break else: try: wallet = daemon.load_wallet(path, None) if not wallet: storage = WalletStorage(path, daemon.currency, manual_upgrades=True) wizard = InstallWizard(self.config, self.app, self.plugins, daemon.currency, storage) try: wallet = wizard.run_and_get_wallet() except UserCancelled: pass except GoBack as e: print_error( '[start_new_window] Exception caught (GoBack)', e) finally: wizard.terminate() if not wallet: return wallet.start_threads(daemon.network) daemon.add_wallet(wallet) except BaseException as e: traceback.print_exc(file=sys.stdout) if '2fa' in str(e): d = QMessageBox( QMessageBox.Warning, _('Error'), '2FA wallets for Bitcoin Cash are currently unsupported by <a href="https://api.trustedcoin.com/#/">TrustedCoin</a>. Follow <a href="https://github.com/Electron-Cash/Electron-Cash/issues/41#issuecomment-357468208">this guide</a> in order to recover your funds.' ) d.exec_() else: d = QMessageBox(QMessageBox.Warning, _('Error'), 'Cannot load wallet:\n' + str(e)) d.exec_() return w = self.create_window_for_wallet(wallet, daemon.currency) 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
def callback(response): err = response.get('error') if err: try: print_stderr("Transaction broadcast error", err['code'], err['message']) except: print_stderr("Transaction broadcast error:", err) else: print_error("Transaction broadcast result:", response) # --verbose only
def reset_2FA(self, client): if 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!", 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) elif (sw1 == 0x9c and sw2 == 0x17): msg = _( f"Failed to reset 2FA: \nyou must reset the seed first (error code {hex(256*sw1+sw2)})" ) client.handler.show_error(msg) elif (sw1 == 0x9c and sw2 == 0x0b): msg = _( f"Failed to reset 2FA: \nrequest rejected by 2FA device (error code: {hex(256*sw1+sw2)})" ) client.handler.show_message(msg) else: msg = _( f"Failed to reset 2FA with error code: {hex(256*sw1+sw2)}") client.handler.show_error(msg) else: msg = _(f"2FA is already disabled!") client.handler.show_error(msg)
def __init__(self, config): super().__init__(None) # Top-level Object if Exception_Hook._instance: return # This is ok, we will be GC'd later. if not _is_enabled(config): print_error("[{}] Not installed due to user config.".format(__class__.__qualname__)) return # self will get auto-gc'd 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__)) Exception_Hook._weak_instances.append(Weak.ref(self, Exception_Hook.finalized))
def __init__(self, parent, config, name): print_error("[satochip] SatochipPlugin: init()") #debugSatochip HW_PluginBase.__init__(self, parent, config, name) #self.libraries_available = self.check_libraries_available() #debugSatochip #if not self.libraries_available: # return #self.device_manager().register_devices(self.DEVICE_IDS) self.device_manager().register_enumerate_func( self.detect_smartcard_reader)
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"]) + '-' + str( d["applet_major_version"]) + '.' + str( d["applet_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") # card label (response, sw1, sw2, label) = client.cc.card_get_label() if (label == ""): label = "(none)" self.card_label.setText('<tt>%s' % label) 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)") self.card_label.setText('<tt>%s' % "(none)")
def get_xpub(self, device_id, derivation, xtype, wizard): # this seems to be part of the pairing process only, not during normal ops? # base_wizard:on_hw_derivation print_error("[satochip] SatochipPlugin: get_xpub()") #debugSatochip #if xtype not in self.SUPPORTED_XTYPES: # raise ScriptTypeNotSupported(_('This type of script is not supported with {}.').format(self.device)) devmgr = self.device_manager() client = devmgr.client_by_id(device_id) client.handler = self.create_handler(wizard) client.ping_check() xpub = client.get_xpub(derivation, xtype) return xpub
def card_bip32_get_extendedkey(self, path): cla = JCconstants.CardEdge_CLA ins = JCconstants.INS_BIP32_GET_EXTENDED_KEY p1 = len(path) // 4 p2 = 0x40 #option flags: 0x80:erase cache memory - 0x40: optimization for non-hardened child derivation le = len(path) apdu = [cla, ins, p1, p2, le] apdu += path if self.parser.authentikey is None: self.card_bip32_get_authentikey() # send apdu while (True): (response, sw1, sw2) = self.card_transmit(apdu) # if there is no more memory available, erase cache... #if self.get_sw12(sw1,sw2)==JCconstants.SW_NO_MEMORY_LEFT: if (sw1 == 0x9C) and (sw2 == 0x01): print_error( "[CardConnector] CardConnector: card_bip32_get_extendedkey(): Reset memory..." ) #debugSatochip apdu[3] = apdu[3] ^ 0x80 response, sw1, sw2 = self.card_transmit(apdu) apdu[3] = apdu[3] & 0x7f # reset the flag # other (unexpected) error if (sw1 != 0x90) or (sw2 != 0x00): raise UnexpectedSW12Error('Unexpected error code SW12=' + hex(sw1) + " " + hex(sw2)) # check for non-hardened child derivation optimization elif ((response[32] & 0x80) == 0x80): print_error( "[CardConnector] CardConnector: card_bip32_get_extendedkey(): Child Derivation optimization..." ) #debugSatochip (pubkey, chaincode) = self.parser.parse_bip32_get_extendedkey(response) coordy = pubkey.get_public_key_bytes(compressed=False) coordy = list(coordy[33:]) authcoordy = self.parser.authentikey.get_public_key_bytes( compressed=False) authcoordy = list(authcoordy[33:]) data = response + [len(coordy) & 0xFF00, len(coordy) & 0x00FF] + coordy apdu_opt = [cla, 0x74, 0x00, 0x00, len(data)] apdu_opt = apdu_opt + data response_opt, sw1_opt, sw2_opt = self.card_transmit(apdu_opt) #at this point, we have successfully received a response from the card else: (key, chaincode) = self.parser.parse_bip32_get_extendedkey(response) return (key, chaincode)
def sign_broadcast_tx_from_partner(self, tx, my_wallet_index): wallet1 = self.getContractWallet(my_wallet_index) c = commands.Commands(None, wallet1, self.network) txSigned = c.signtransaction(tx) #print_msg("size is: %s" % self.get_tx_size(txSigned)) tx = c.deserialize(txSigned) for i in tx['inputs']: if None in i['signatures']: return False c.broadcast(txSigned) print_error( "Cash Rip********************************Transaction of size {} bytes has been broadcast." .format(self.get_tx_size(txSigned))) return True