Ejemplo n.º 1
0
def test_recover_pub_key_input_type() -> None:
    msg = "test message".encode()
    wif, _ = bms.gen_keys()
    bms_sig = bms.sign(msg, wif)

    key_id = bms_sig.rf - 27 & 0b11
    magic_msg = magic_message(msg)
    Q = dsa.recover_pub_key(key_id, magic_msg, bms_sig.dsa_sig.serialize(),
                            True, sha256)
    Q2 = dsa.recover_pub_key(key_id, magic_msg, bms_sig.dsa_sig, True, sha256)
    assert Q == Q2
Ejemplo n.º 2
0
def assert_as_valid(msg: Octets,
                    addr: String,
                    sig: Union[Sig, String],
                    lower_s: bool = True) -> None:
    # Private function for test/dev purposes
    # It raises Errors, while verify should always return True or False

    if isinstance(sig, Sig):
        sig.assert_valid()
    else:
        sig = Sig.b64decode(sig)

    # first two bits in rf are reserved for key_id
    #    key_id = 00;     key_id = 01;     key_id = 10;     key_id = 11
    # 27-27 = 000000;  28-27 = 000001;  29-27 = 000010;  30-27 = 000011
    # 31-27 = 000100;  32-27 = 000101;  33-27 = 000110;  34-27 = 000111
    # 35-27 = 001000;  36-27 = 001001;  37-27 = 001010;  38-27 = 001011
    # 39-27 = 001100;  40-27 = 001101;  41-27 = 001110;  42-27 = 001111
    key_id = sig.rf - 27 & 0b11
    magic_msg = magic_message(msg)
    Q = dsa.recover_pub_key(key_id, magic_msg, sig.dsa_sig, lower_s, sha256)
    compressed = sig.rf > 30
    # signature is valid only if the provided address is matched
    pub_key = bytes_from_point(Q, compressed=compressed)

    if has_segwit_prefix(addr):
        wit_ver, h160, _ = witness_from_address(addr)
        if wit_ver != 0 or len(h160) != 20:
            raise BTClibValueError(f"not a p2wpkh address: {addr!r}")
        if not (30 < sig.rf < 35 or sig.rf > 38):
            raise BTClibValueError(
                f"invalid p2wpkh address recovery flag: {sig.rf}")
        if hash160(pub_key) != h160:
            raise BTClibValueError(f"invalid p2wpkh address: {addr!r}")
        return

    script_type, h160, _ = h160_from_address(addr)

    if script_type == "p2pkh":
        if sig.rf > 34:
            raise BTClibValueError(
                f"invalid p2pkh address recovery flag: {sig.rf}")
        if hash160(pub_key) != h160:
            raise BTClibValueError(f"invalid p2pkh address: {addr!r}")
        return

    # must be P2WPKH-P2SH
    if not 30 < sig.rf < 39:
        raise BTClibValueError(
            f"invalid p2wpkh-p2sh address recovery flag: {sig.rf}")
    script_pk = b"\x00\x14" + hash160(pub_key)
    if hash160(script_pk) != h160:
        raise BTClibValueError(f"invalid p2wpkh-p2sh address: {addr!r}")
Ejemplo n.º 3
0
def sign(msg: Octets, prv_key: PrvKey, addr: Optional[String] = None) -> Sig:
    "Generate address-based compact signature for the provided message."

    # first sign the message
    magic_msg = magic_message(msg)
    q, network, compressed = prv_keyinfo_from_prv_key(prv_key)
    dsa_sig = dsa.sign(magic_msg, q)

    # now calculate the key_id
    # TODO do the match in Jacobian coordinates avoiding mod_inv
    pub_keys = dsa.recover_pub_keys(magic_msg, dsa_sig)
    Q = mult(q)
    # key_id is in [0, 3]
    # first two bits in rf are reserved for it
    key_id = pub_keys.index(Q)
    pub_key = bytes_from_point(Q, compressed=compressed)

    if isinstance(addr, str):
        addr = addr.strip()
    elif isinstance(addr, bytes):
        addr = addr.decode("ascii")

    # finally, calculate the recovery flag
    if addr is None or addr == p2pkh(pub_key, network, compressed):
        rf = key_id + 27
        # third bit in rf is reserved for the 'compressed' boolean
        rf += 4 if compressed else 0
    # BIP137
    elif addr == p2wpkh_p2sh(pub_key, network):
        rf = key_id + 35
    elif addr == p2wpkh(pub_key, network):
        rf = key_id + 39
    else:
        raise BTClibValueError("mismatch between private key and address")

    return Sig(rf, dsa_sig)
Ejemplo n.º 4
0
def test_ledger() -> None:
    """Hybrid ECDSA Bitcoin message signature generated by Ledger"""

    mnemonic = (
        "barely sun snack this snack relief pipe attack disease boss enlist lawsuit"
    )

    # non-standard leading 31 in DER serialization
    derivation_path = "m/1"
    msg = b"\xfb\xa3\x1f\x8cd\x85\xe29#K\xb3{\xfd\xa7<?\x95oL\xee\x19\xb2'oh\xa7]\xd9A\xfeU\xd8"
    dersig_hex_str = "3144022012ec0c174936c2a46dc657252340b2e6e6dd8c31dd059b6f9f33a90c21af2fba022030e6305b3ccf88009d419bf7651afcfcc0a30898b93ae9de9aa6ac03cf8ec56b"

    # pub_key derivation
    rprv = bip39.mxprv_from_mnemonic(mnemonic)
    xprv = bip32.derive(rprv, derivation_path)

    # the actual message being signed
    magic_msg = magic_message(msg)

    # save key_id and patch dersig
    dersig = bytes.fromhex(dersig_hex_str)
    key_id = dersig[0]
    dsa_sig = dsa.Sig.parse(b"\x30" + dersig[1:])

    # ECDSA signature verification of the patched dersig
    dsa.assert_as_valid(magic_msg, xprv, dsa_sig)
    assert dsa.verify(magic_msg, xprv, dsa_sig)

    # compressed address
    addr = b58.p2pkh(xprv)

    # equivalent Bitcoin Message Signature
    rec_flag = 27 + 4 + (key_id & 0x01)
    bms_sig = bms.Sig(rec_flag, dsa_sig)

    # Bitcoin Message Signature verification
    bms.assert_as_valid(msg, addr, bms_sig)
    assert bms.verify(msg, addr, bms_sig)
    assert not bms.verify(magic_msg, addr, bms_sig)

    bms.sign(msg, xprv)

    # standard leading 30 in DER serialization
    derivation_path = "m/0/0"
    msg_str = "hello world".encode()
    dersig_hex_str = "3045022100967dac3262b4686e89638c8219c5761017f05cd87a855edf034f4a3ec6b59d3d0220108a4ef9682b71a45979d8c75c393382d9ccb8eb561d73b8c5fc0b87a47e7d27"

    # pub_key derivation
    rprv = bip39.mxprv_from_mnemonic(mnemonic)
    xprv = bip32.derive(rprv, derivation_path)

    # the actual message being signed
    magic_msg = magic_message(msg_str)

    # save key_id and patch dersig
    dersig = bytes.fromhex(dersig_hex_str)
    key_id = dersig[0]
    dsa_sig = dsa.Sig.parse(b"\x30" + dersig[1:])

    # ECDSA signature verification of the patched dersig
    dsa.assert_as_valid(magic_msg, xprv, dsa_sig, lower_s=True)
    assert dsa.verify(magic_msg, xprv, dsa_sig)

    # compressed address
    addr = b58.p2pkh(xprv)

    # equivalent Bitcoin Message Signature
    rec_flag = 27 + 4 + (key_id & 0x01)
    bms_sig = bms.Sig(rec_flag, dsa_sig)

    # Bitcoin Message Signature verification
    bms.assert_as_valid(msg_str, addr, bms_sig)
    assert bms.verify(msg_str, addr, bms_sig)
    assert not bms.verify(magic_msg, addr, bms_sig)