def sign_message(self, sequence, message, password): sig = None try: inputPath = self.get_derivation() + "/%d/%d" % sequence inputHash = Hash(msg_magic(message)).encode('hex') hasharray = [] hasharray.append({'hash': inputHash, 'keypath': inputPath}) hasharray = json.dumps(hasharray) msg = '{"sign":{"meta":"sign message", "data":%s}}' % (hasharray) dbb_client = self.plugin.get_client(self) if not dbb_client.is_paired(): raise Exception("Could not sign message.") reply = dbb_client.hid_send_encrypt(msg) self.handler.show_message(_("Signing message ...\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.")) reply = dbb_client.hid_send_encrypt( msg ) # Send twice, first returns an echo for smart verification (not implemented) self.handler.clear_dialog() if 'error' in reply: raise Exception(reply['error']['message']) if 'sign' not in reply: raise Exception("Could not sign message.") if 'recid' in reply['sign'][0]: # firmware > v2.1.1 sig = chr(27 + int(reply['sign'][0]['recid'], 16) + 4) + reply['sign'][0]['sig'].decode('hex') h = Hash(msg_magic(message)) pk, compressed = pubkey_from_signature(sig, h) pk = point_to_ser(pk.pubkey.point, compressed) addr = public_key_to_p2pkh(pk) if verify_message(addr, sig, message) is False: raise Exception("Could not sign message") elif 'pubkey' in reply['sign'][0]: # firmware <= v2.1.1 for i in range(4): sig = chr(27 + i + 4) + reply['sign'][0]['sig'].decode('hex') try: addr = public_key_to_p2pkh( reply['sign'][0]['pubkey'].decode('hex')) if verify_message(addr, sig, message): break except Exception: continue else: raise Exception("Could not sign message") except BaseException as e: self.give_error(e) return sig
def doVerify(self) -> None: addrtf = self.tf address_str = str(addrtf.text).strip() message = str(self.topTvDel.text) signature = str(self.botTvDel.text).strip() if not signature: parent().show_message( _("Please provide both a signature and a message to verify")) return try: address = Address.from_string(address_str) except: parent().show_error(_('Invalid Vitae address.')) return message = message.encode('utf-8') try: # This can raise on invalid base64 sig = base64.b64decode(signature) verified = bitcoin.verify_message( address, sig, message) # this raises too on failure except: verified = False if verified: parent().show_message(_("Signature verified"), title=_("Success")) else: parent().show_error(_("Wrong signature"))
def _process_server_reply(self, signed_version_dict): ''' Returns: - the new package version string if new version found from server, e.g. '3.3.5', '3.3.5CS', etc - or the current version (version.PACKAGE_VERSION) if no new version found. - None on failure (such as bad signature). May also raise on error. ''' # example signed_version_dict: # { # "3.9.9": { # "bitcoincash:qphax4cg8sxuc0qnzk6sx25939ma7y877uz04s2z82": "IA+2QG3xPRn4HAIFdpu9eeaCYC7S5wS/sDxn54LJx6BdUTBpse3ibtfq8C43M7M1VfpGkD5tsdwl5C6IfpZD/gQ=" # }, # "3.9.9CS": { # "bitcoincash:qphax4cg8sxuc0qnzk6sx25939ma7y877uz04s2z82": "IA+2QG3xPRn4HAIFdpu9eeaCYC7S5wS/sDxn54LJx6BdUTBpse3ibtfq8C43M7M1VfpGkD5tsdwl5C6IfpZD/gQ=" # }, # "3.9.9SLP": { # "bitcoincash:qphax4cg8sxuc0qnzk6sx25939ma7y877uz04s2z82": "IA+2QG3xPRn4HAIFdpu9eeaCYC7S5wS/sDxn54LJx6BdUTBpse3ibtfq8C43M7M1VfpGkD5tsdwl5C6IfpZD/gQ=" # }, # } # All signed messages above are signed with the address in the dict, and the message is the "3.9.9" or "3.9.9CS" etc string ct_matching = 0 for version_msg, sigdict in signed_version_dict.items(): # This looks quadratic, and it is. But the expected results are small. # We needed to do it this way to detect when there was no matching variant and/or no known-key match. if self.is_matching_variant(version_msg): for adr, sig in sigdict.items(): adr = address.Address.from_string(adr, net=MainNet) # may raise if adr in self.VERSION_ANNOUNCEMENT_SIGNING_ADDRESSES: ct_matching += 1 if self.is_newer(version_msg): # may raise try: is_verified = bitcoin.verify_message(adr, base64.b64decode(sig), version_msg.encode('utf-8'), net=MainNet) except: self.print_error("Exception when verifying version signature for", version_msg, ":", repr(sys.exc_info()[1])) return None if is_verified: self.print_error("Got new version", version_msg) return version_msg.strip() else: self.print_error("Got new version", version_msg, "but sigcheck failed!") return None if 0 == ct_matching: # Hmm. None of the versions we saw matched our variant. # And/Or, none of the keys we saw matched keys we knew about. # This is an error condition, so return None self.print_error("Error: Got a valid reply from server but none of the variants match us and/or none of the signing keys are known!") return None return version.PACKAGE_VERSION
def verify_signature_with_address(address: str, message: str, signature: str) -> bool: """Verify a message signature using a bitcoin address. :param address: Bitcoin address, either legacy or cashaddr :param message: Message to verify against the signature :param signature: Base64 encoded signature string. """ try: sig = base64.b64decode(signature) except binascii.Error: return False addr = Address.from_string(address) message_bytes = message.encode("utf-8") return bitcoin.verify_message(addr, sig, message_bytes)
def do_verify(self): try: address = Address.from_string(self.address_e.text().strip()) except Exception: self.show_message(_(f"Invalid {CURRENCY} address.")) return message = self.message_e.toPlainText().strip().encode("utf-8") try: # This can throw on invalid base64 sig = base64.b64decode(self.signature_e.toPlainText()) verified = verify_message(address, sig, message, sigtype=self.get_sigtype()) except Exception: verified = False if verified: self.show_message(_("Signature verified")) else: self.show_error(_("Wrong signature"))
def sign_message(self, sequence, message, password, sigtype=SignatureType.BITCOIN): if sigtype == SignatureType.ECASH: raise RuntimeError( _('eCash message signing is not available for {}').format( self.device)) sig = None try: message = message.encode('utf8') inputPath = self.get_derivation() + "/%d/%d" % sequence msg_hash = Hash(msg_magic(message)) inputHash = to_hexstr(msg_hash) hasharray = [] hasharray.append({'hash': inputHash, 'keypath': inputPath}) hasharray = json.dumps(hasharray) msg = b'{"sign":{"meta":"sign message", "data":%s}}' % hasharray.encode( 'utf8') dbb_client = self.plugin.get_client(self) if not dbb_client.is_paired(): raise Exception(_("Could not sign message.")) reply = dbb_client.hid_send_encrypt(msg) self.handler.show_message( _("Signing message ...") + "\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." )) reply = dbb_client.hid_send_encrypt( msg ) # Send twice, first returns an echo for smart verification (not implemented) self.handler.finished() if 'error' in reply: raise Exception(reply['error']['message']) if 'sign' not in reply: raise Exception(_("Could not sign message.")) if 'recid' in reply['sign'][0]: # firmware > v2.1.1 sig = bytes([27 + int(reply['sign'][0]['recid'], 16) + 4 ]) + binascii.unhexlify(reply['sign'][0]['sig']) pk, compressed = pubkey_from_signature(sig, msg_hash) pk = point_to_ser(pk.pubkey.point, compressed) addr = public_key_to_p2pkh(pk) if verify_message(addr, sig, message) is False: raise Exception(_("Could not sign message")) elif 'pubkey' in reply['sign'][0]: # firmware <= v2.1.1 for i in range(4): sig = bytes([27 + i + 4]) + binascii.unhexlify( reply['sign'][0]['sig']) try: addr = public_key_to_p2pkh( binascii.unhexlify(reply['sign'][0]['pubkey'])) if verify_message(addr, sig, message): break except Exception: continue else: raise Exception(_("Could not sign message")) except BaseException as e: self.give_error(e) return sig