Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
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
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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
Exemplo n.º 6
0
    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)
Exemplo n.º 7
0
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,
    )