def recover_message( self, message: bytes, signature: str, is_deprecated_mode: bool = False) -> Tuple[Address, ...]: """ Recover the addresses from the hash. :param message: the message we expect :param signature: the transaction signature :param is_deprecated_mode: if the deprecated signing was used :return: the recovered addresses """ signature_b64 = base64.b64decode(signature) verifying_keys = VerifyingKey.from_public_key_recovery( signature_b64, message, SECP256k1, hashfunc=hashlib.sha256, ) public_keys = [ verifying_key.to_string("compressed").hex() for verifying_key in verifying_keys ] addresses = [ self.get_address_from_public_key(public_key) for public_key in public_keys ] return tuple(addresses)
def recover_public_keys_from_message( cls, message: bytes, signature: str, is_deprecated_mode: bool = False) -> Tuple[str, ...]: """ Get the public key used to produce the `signature` of the `message` :param message: raw bytes used to produce signature :param signature: signature of the message :param is_deprecated_mode: if the deprecated signing was used :return: the recovered public keys """ signature_b64 = base64.b64decode(signature) verifying_keys = VerifyingKey.from_public_key_recovery( signature_b64, message, SECP256k1, hashfunc=hashlib.sha256, ) public_keys = [ verifying_key.to_string("compressed").hex() for verifying_key in verifying_keys ] return tuple(public_keys)
def recoverPubKey() -> VerifyingKey: signature1 = unhexlify(signMessage(MSG, 0)) signature2 = unhexlify(signMessage(MS2, 0)) vk_1 = VerifyingKey.from_public_key_recovery( signature1, MSG, curve = NIST192p ) vk_2 = VerifyingKey.from_public_key_recovery( signature2, MS2, curve = NIST192p ) verifyingKey = None for key in vk_1: if key in vk_2: verifyingKey = key return verifyingKey
def verifyTxSignature(txHash: str, signature: str, pkh: str) -> bool: try: pks = VerifyingKey.from_public_key_recovery(bytes.fromhex(signature), bytes.fromhex(txHash), SECP256k1) for pk in pks: recoveredPk = pkToPkh(pk.to_string().hex()) if (recoveredPk.decode() == pkh): return pk.verify(bytes.fromhex(signature), bytes.fromhex(txHash)) return False except BaseException: return False
def decode(pr: str) -> Invoice: """bolt11 decoder, based on https://github.com/rustyrussell/lightning-payencode/blob/master/lnaddr.py """ hrp, decoded_data = bech32_decode(pr) if hrp is None or decoded_data is None: raise ValueError("Bad bech32 checksum") if not hrp.startswith("ln"): raise ValueError("Does not start with ln") bitarray = _u5_to_bitarray(decoded_data) # final signature 65 bytes, split it off. if len(bitarray) < 65 * 8: raise ValueError("Too short to contain signature") # extract the signature signature = bitarray[-65 * 8 :].tobytes() # the tagged fields as a bitstream data = bitstring.ConstBitStream(bitarray[: -65 * 8]) # build the invoice object invoice = Invoice() # decode the amount from the hrp m = re.search("[^\d]+", hrp[2:]) if m: amountstr = hrp[2 + m.end() :] if amountstr != "": invoice.amount_msat = _unshorten_amount(amountstr) # pull out date invoice.date = data.read(35).uint while data.pos != data.len: tag, tagdata, data = _pull_tagged(data) data_length = len(tagdata) / 5 if tag == "d": invoice.description = _trim_to_bytes(tagdata).decode("utf-8") elif tag == "h" and data_length == 52: invoice.description_hash = _trim_to_bytes(tagdata).hex() elif tag == "p" and data_length == 52: invoice.payment_hash = _trim_to_bytes(tagdata).hex() elif tag == "x": invoice.expiry = tagdata.uint elif tag == "n": invoice.payee = _trim_to_bytes(tagdata).hex() # this won't work in most cases, we must extract the payee # from the signature elif tag == "s": invoice.secret = _trim_to_bytes(tagdata).hex() elif tag == "r": s = bitstring.ConstBitStream(tagdata) while s.pos + 264 + 64 + 32 + 32 + 16 < s.len: route = Route( pubkey=s.read(264).tobytes().hex(), short_channel_id=_readable_scid(s.read(64).intbe), base_fee_msat=s.read(32).intbe, ppm_fee=s.read(32).intbe, cltv=s.read(16).intbe, ) invoice.route_hints.append(route) # BOLT #11: # A reader MUST check that the `signature` is valid (see the `n` tagged # field specified below). # A reader MUST use the `n` field to validate the signature instead of # performing signature recovery if a valid `n` field is provided. message = bytearray([ord(c) for c in hrp]) + data.tobytes() sig = signature[0:64] if invoice.payee: key = VerifyingKey.from_string(unhexlify(invoice.payee), curve=SECP256k1) key.verify(sig, message, hashlib.sha256, sigdecode=sigdecode_string) else: keys = VerifyingKey.from_public_key_recovery( sig, message, SECP256k1, hashlib.sha256 ) signaling_byte = signature[64] key = keys[int(signaling_byte)] invoice.payee = key.to_string("compressed").hex() return invoice
print('Signature valid: ', valid) except BadSignatureError: print('Signature verification failed!') # ECDSA verify tampered signature (using the curve secp256k1 + SHA3-256) msg = 'Tampered ECDSA Message'.encode('utf8') print('\nMessage: ', msg) try: valid = verifying_key.verify(signature, msg, hashfunc=hashlib.sha3_256) print('Signature valid: ', valid) except BadSignatureError: print('Signature verification failed!') # Identify possible public keys from signature and hash msg = 'Message for ECDSA signing'.encode('utf8') keys = VerifyingKey.from_public_key_recovery(signature, msg, SECP256k1, hashfunc=hashlib.sha3_256) print('Possible public keys:') for key in keys: print(key.to_string().hex()) # Write keys to file pem = signing_key.to_pem() with open('private.pem', 'wb') as file: file.write(pem) pem = verifying_key.to_pem() with open('public.pem', 'wb') as file: file.write(pem)
def decode(bech32_pr: str) -> LightningInvoice: hrp, bech32_data = bech32_decode(bech32_pr) route_hints = [] tags = {} if not hrp or not bech32_data or not hrp.startswith("ln"): raise ValueError("Bech32 is not valid.") matches = re.match(r"ln(bc|bcrt|tb)(\w+)?", hrp) assert matches, "Human readable part is not valid." currency, amount_str = matches.groups() data_part = u5_to_bitarray(bech32_data) # Final signature is 65 bytes, split it off. # signature => # "a valid 512-bit secp256k1 signature of the SHA2 256-bit hash of the human-readable part # represented as UTF-8 bytes, concatenated with the data part (excluding the signature) with 0 bits appended # to pad the data to the next byte boundary, with a trailing byte containing the recovery ID (0, 1, 2, or 3)" if len(data_part) < 65 * 8: raise ValueError("Too short to contain signature") signature_data = data_part[-65 * 8:].tobytes() data = ConstBitStream(data_part[:-65 * 8]) signature = Signature(data=signature_data, signing_data=(hrp.encode("utf-8") + data.tobytes())) timestamp = data.read(35).uint # Look for tags in data while data.pos != data.len: tag, tagdata, data = _pull_tagged(data) data_length = len(tagdata) // 5 if tag == "p" and data_length == 52: # p (1): data_length 52. 256-bit SHA256 payment_hash. Preimage of this provides proof of payment. tags["p"] = trim_to_bytes(tagdata).hex() elif tag == "x": # x (6): data_length variable. expiry time in seconds (big-endian). Default is 3600. tags["x"] = tagdata.uint elif tag == "d": # d (13): data_length variable. Short description of purpose of payment (UTF-8). tags["d"] = trim_to_bytes(tagdata).decode("utf-8") elif tag == "h" and data_length == 52: # h (23): data_length 52. 256-bit description of purpose of payment (SHA256). tags["h"] = trim_to_bytes(tagdata).hex() elif tag == "s" and data_length == 52: # s (16): data_length 52. This 256-bit secret prevents forwarding nodes from probing the payment recipient. tags["s"] = trim_to_bytes(tagdata).hex() elif tag == "c": # c (24): data_length variable. min_final_cltv_expiry to use for the last HTLC in the route. Default is 9. tags["c"] = tagdata.uint elif tag == "n" and data_length == 53: # n (19): data_length 53. 33-byte public key of the payee node. tags["n"] = trim_to_bytes(tagdata).hex() elif tag == "f": # f (9): data_length variable, depending on version. Fallback on-chain address. tags["f"] = _parse_fallback(tagdata, currency) elif tag == "r": # r (3): `data_length` variable. # One or more entries containing extra routing information for a private route; # there may be more than one `r` field, too. # * `pubkey` (264 bits) # * `short_channel_id` (64 bits) # * `feebase` (32 bits, big-endian) # * `feerate` (32 bits, big-endian) # * `cltv_expiry_delta` (16 bits, big-endian) s = ConstBitStream(tagdata) while s.pos + 264 + 64 + 32 + 32 + 16 < s.len: route_hints.append( Route( public_key=s.read(264).tobytes().hex(), short_channel_id=_readable_scid(s.read(64).intbe), base_fee=MilliSatoshi(s.read(32).intbe), ppm_fee=s.read(32).intbe, cltv_expiry_delta=s.read(16).intbe, )) # A reader MUST use the `n` field to validate the signature instead of # performing signature recovery if a valid `n` field is provided. message = bytearray([ord(c) for c in hrp]) + data.tobytes() sig = signature_data[0:64] if "n" in tags: payee_public_key = tags["n"] vk = VerifyingKey.from_string(unhexlify(payee_public_key), curve=SECP256k1) if not vk.verify(sig, message, sha256, sigdecode=sigdecode_string): raise ValueError("Could not verify public key") else: vk = VerifyingKey.from_public_key_recovery(sig, message, SECP256k1, sha256) signaling_byte = signature_data[64] key = vk[int(signaling_byte)] payee_public_key = key.to_string("compressed").hex() return LightningInvoice( amount=amount_to_msat(amount_str) if amount_str else None, currency=currency, timestamp=timestamp, payee_public_key=payee_public_key, route_hints=route_hints, signature=signature, tags=tags, )