Beispiel #1
0
def test_point_from_bip340pub_key() -> None:

    q, x_Q = ssa.gen_keys()
    Q = mult(q)
    # Integer (int)
    assert ssa.point_from_bip340pub_key(x_Q) == Q
    # Integer (bytes)
    x_Q_bytes = x_Q.to_bytes(32, "big", signed=False)
    assert ssa.point_from_bip340pub_key(x_Q_bytes) == Q
    # Integer (hex-str)
    assert ssa.point_from_bip340pub_key(x_Q_bytes.hex()) == Q
    # tuple Point
    assert ssa.point_from_bip340pub_key(Q) == Q
    # 33 bytes
    assert ssa.point_from_bip340pub_key(bytes_from_point(Q)) == Q
    # 33 bytes hex-string
    assert ssa.point_from_bip340pub_key(bytes_from_point(Q).hex()) == Q
    # 65 bytes
    assert ssa.point_from_bip340pub_key(bytes_from_point(
        Q, compressed=False)) == Q
    # 65 bytes hex-string
    assert (ssa.point_from_bip340pub_key(
        bytes_from_point(Q, compressed=False).hex()) == Q)

    xpub_data = BIP32KeyData.b58decode(
        "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"
    )
    xpub_data.key = bytes_from_point(Q)
    # BIP32KeyData
    assert ssa.point_from_bip340pub_key(xpub_data) == Q
    # BIP32Key encoded str
    xpub = xpub_data.b58encode()
    assert ssa.point_from_bip340pub_key(xpub) == Q
    # BIP32Key str
    assert ssa.point_from_bip340pub_key(xpub.encode("ascii")) == Q
def test_gec_2() -> None:
    """GEC 2: Test Vectors for SEC 1, section 4.1

    http://read.pudn.com/downloads168/doc/772358/TestVectorsforSEC%201-gec2.pdf
    """

    # 4.1.1
    ec = CURVES["secp160r1"]
    hf = sha1

    # 4.1.2
    dU = 971761939728640320549601132085879836204587084162
    assert dU == 0xAA374FFC3CE144E6B073307972CB6D57B2A4E982
    QU = mult(dU, ec.G, ec)
    assert QU == (
        466448783855397898016055842232266600516272889280,
        1110706324081757720403272427311003102474457754220,
    )
    assert (
        bytes_from_point(QU, ec).hex() == "0251b4496fecc406ed0e75a24a3c03206251419dc0"
    )

    # 4.1.3
    dV = 399525573676508631577122671218044116107572676710
    assert dV == 0x45FB58A92A17AD4B15101C66E74F277E2B460866
    QV = mult(dV, ec.G, ec)
    assert QV == (
        420773078745784176406965940076771545932416607676,
        221937774842090227911893783570676792435918278531,
    )
    assert (
        bytes_from_point(QV, ec).hex() == "0349b41e0e9c0369c2328739d90f63d56707c6e5bc"
    )

    # expected results
    z_exp = 1155982782519895915997745984453282631351432623114
    assert z_exp == 0xCA7C0F8C3FFA87A96E1B74AC8E6AF594347BB40A
    size = 20

    # 4.1.4
    z, _ = mult(dU, QV, ec)  # x coordinate only
    assert z == z_exp
    keyingdata = ansi_x9_63_kdf(
        z.to_bytes(ec.p_size, byteorder="big", signed=False), size, hf, None
    )
    assert keyingdata.hex() == "744ab703f5bc082e59185f6d049d2d367db245c2"

    # 4.1.5
    z, _ = mult(dV, QU, ec)  # x coordinate only
    assert z == z_exp
    keyingdata = ansi_x9_63_kdf(
        z.to_bytes(ec.p_size, byteorder="big", signed=False), size, hf, None
    )
    assert keyingdata.hex() == "744ab703f5bc082e59185f6d049d2d367db245c2"
def test_gec() -> None:
    """GEC 2: Test Vectors for SEC 1, section 2

    http://read.pudn.com/downloads168/doc/772358/TestVectorsforSEC%201-gec2.pdf
    """
    # 2.1.1 Scheme setup
    ec = CURVES["secp160r1"]
    hf = sha1

    # 2.1.2 Key Deployment for U
    dU = 971761939728640320549601132085879836204587084162
    dU, QU = dsa.gen_keys(dU, ec)
    assert (format(dU,
                   str(ec.n_size) +
                   "x") == "aa374ffc3ce144e6b073307972cb6d57b2a4e982")
    assert QU == (
        466448783855397898016055842232266600516272889280,
        1110706324081757720403272427311003102474457754220,
    )
    assert (bytes_from_point(
        QU, ec).hex() == "0251b4496fecc406ed0e75a24a3c03206251419dc0")

    # 2.1.3 Signing Operation for U
    msg = b"abc"
    k = 702232148019446860144825009548118511996283736794
    lower_s = False
    sig = dsa.sign_(reduce_to_hlen(msg, hf), dU, k, lower_s, ec, hf)
    assert sig.r == 0xCE2873E5BE449563391FEB47DDCBA2DC16379191
    assert sig.s == 0x3480EC1371A091A464B31CE47DF0CB8AA2D98B54
    assert sig.ec == ec

    # 2.1.4 Verifying Operation for V
    dsa.assert_as_valid(msg, QU, sig, lower_s, hf)
    assert dsa.verify(msg, QU, sig, lower_s, hf)
Beispiel #4
0
def test_p2pkh_from_pub_key() -> None:
    # https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses
    pub_key = "02 50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352"
    address = "1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs"
    assert address == b58.p2pkh(pub_key)
    assert address == b58.p2pkh(pub_key, compressed=True)
    _, h160, _ = b58.h160_from_address(address)
    assert h160 == hash160(pub_key)

    # trailing/leading spaces in address string
    assert address == b58.p2pkh(" " + pub_key)
    assert h160 == hash160(" " + pub_key)
    assert address == b58.p2pkh(pub_key + " ")
    assert h160 == hash160(pub_key + " ")

    uncompr_pub_key = bytes_from_point(point_from_octets(pub_key),
                                       compressed=False)
    uncompr_address = "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM"
    assert uncompr_address == b58.p2pkh(uncompr_pub_key, compressed=False)
    assert uncompr_address == b58.p2pkh(uncompr_pub_key)
    _, uncompr_h160, _ = b58.h160_from_address(uncompr_address)
    assert uncompr_h160 == hash160(uncompr_pub_key)

    err_msg = "not a private or uncompressed public key: "
    with pytest.raises(BTClibValueError, match=err_msg):
        assert uncompr_address == b58.p2pkh(pub_key, compressed=False)

    err_msg = "not a private or compressed public key: "
    with pytest.raises(BTClibValueError, match=err_msg):
        assert address == b58.p2pkh(uncompr_pub_key, compressed=True)
Beispiel #5
0
def _get_msg_format(msg: bytes, pubk_rings: PubkeyRing) -> bytes:

    t = b"".join(
        b"".join(bytes_from_point(Q, ec) for Q in pubk_ring)
        for pubk_ring in pubk_rings.values()
    )
    return hf(msg + t).digest()
Beispiel #6
0
def assert_as_valid(msg: Octets, e0: bytes, s: SValues, pubk_rings: PubkeyRing) -> bool:

    msg = bytes_from_octets(msg)
    m = _get_msg_format(msg, pubk_rings)

    ring_size = len(pubk_rings)
    e: SValues = defaultdict(list)
    e0bytes = m
    for i in range(ring_size):
        keys_size = len(pubk_rings[i])
        e[i] = [0] * keys_size
        e[i][0] = int_from_bits(_hash(m, e0, i, 0), ec.nlen) % ec.n
        # edge case that cannot be reproduced in the test suite
        if e[i][0] == 0:
            err_msg = "implausibile signature failure"  # pragma: no cover
            raise BTClibRuntimeError(err_msg)  # pragma: no cover
        r = b"\0x00"
        for j in range(keys_size):
            t = double_mult(-e[i][j], pubk_rings[i][j], s[i][j], ec.G)
            r = bytes_from_point(t, ec)
            if j != len(pubk_rings[i]) - 1:
                h = _hash(m, r, i, j + 1)
                e[i][j + 1] = int_from_bits(h, ec.nlen) % ec.n
                # edge case that cannot be reproduced in the test suite
                if e[i][j + 1] == 0:
                    err_msg = "implausibile signature failure"  # pragma: no cover
                    raise BTClibRuntimeError(err_msg)  # pragma: no cover
            else:
                e0bytes += r
    e0_prime = hf(e0bytes).digest()
    return e0_prime == e0
Beispiel #7
0
def second_generator(ec: Curve = secp256k1, hf: HashF = sha256) -> Point:
    """Second (with respect to G) elliptic curve generator.

    Second (with respect to G) Nothing-Up-My-Sleeve (NUMS)
    elliptic curve generator.

    The hash of G is coerced it to a point (x_H, y_H).
    If the resulting point is not on the curve, keep on
    incrementing x_H until a valid curve point (x_H, y_H) is obtained.

    idea:
    https://crypto.stackexchange.com/questions/25581/second-generator-for-secp256k1-curve

    source:
    https://github.com/ElementsProject/secp256k1-zkp/blob/secp256k1-zkp/src/modules/rangeproof/main_impl.h
    """

    G_bytes = bytes_from_point(ec.G, ec, compressed=False)
    hash_ = hf()
    hash_.update(G_bytes)
    hash_digest = hash_.digest()
    x_H = int_from_bits(hash_digest, ec.nlen) % ec.n
    while True:
        try:
            y_H = ec.y_even(x_H)
            return x_H, y_H
        except BTClibValueError:
            x_H += 1
            x_H %= ec.p
Beispiel #8
0
def test_p2wpkh() -> None:

    # https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
    # leading/trailing spaces should be tolerated
    pub = " 02 79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"
    addr = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
    assert addr == b32.p2wpkh(pub)
    addr = "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx"
    assert addr == b32.p2wpkh(pub, "testnet")

    # http://bitcoinscri.pt/pages/segwit_native_p2wpkh
    pub = "02 530c548d402670b13ad8887ff99c294e67fc18097d236d57880c69261b42def7"
    addr = "bc1qg9stkxrszkdqsuj92lm4c7akvk36zvhqw7p6ck"
    assert addr == b32.p2wpkh(pub)

    _, wit_prg, _ = b32.witness_from_address(addr)
    assert wit_prg == hash160(pub)

    uncompr_pub = bytes_from_point(point_from_octets(pub), compressed=False)
    err_msg = "not a private or compressed public key: "
    with pytest.raises(BTClibValueError, match=err_msg):
        b32.p2wpkh(uncompr_pub)
    with pytest.raises(BTClibValueError, match=err_msg):
        b32.p2wpkh(pub + "00")

    err_msg = "invalid size: "
    with pytest.raises(BTClibValueError, match=err_msg):
        b32.address_from_witness(0, hash160(pub) + b"\x00")
Beispiel #9
0
def pub_keyinfo_from_prv_key(prv_key: PrvKey,
                             network: Optional[str] = None,
                             compressed: Optional[bool] = None) -> PubkeyInfo:
    "Return the pub key tuple (SEC-bytes, network) from a private key."

    q, net, compr = prv_keyinfo_from_prv_key(prv_key, network, compressed)
    ec = NETWORKS[net].curve
    pub_key = mult(q, ec.G, ec)
    return bytes_from_point(pub_key, ec, compr), net
Beispiel #10
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}")
Beispiel #11
0
def __ckd(xkey: _ExtendedBIP32KeyData, index: int) -> None:

    xkey.depth += 1
    xkey.index = index
    if xkey.is_private:
        Q_bytes = bytes_from_point(mult(xkey.prv_key_int))
        xkey.parent_fingerprint = hash160(Q_bytes)[:4]
        if xkey.is_hardened:  # hardened derivation
            hmac_ = hmac.new(
                xkey.chain_code,
                xkey.key + index.to_bytes(4, byteorder="big", signed=False),
                "sha512",
            ).digest()
        else:  # normal derivation
            hmac_ = hmac.new(
                xkey.chain_code,
                Q_bytes + index.to_bytes(4, byteorder="big", signed=False),
                "sha512",
            ).digest()
        xkey.chain_code = hmac_[32:]
        offset = int.from_bytes(hmac_[:32], byteorder="big", signed=False)
        xkey.prv_key_int = (xkey.prv_key_int + offset) % ec.n
        xkey.key = b"\x00" + xkey.prv_key_int.to_bytes(
            32, byteorder="big", signed=False
        )
        xkey.pub_key_point = INF
    else:  # public key
        xkey.parent_fingerprint = hash160(xkey.key)[:4]
        if xkey.is_hardened:
            raise BTClibValueError("invalid hardened derivation from public key")
        hmac_ = hmac.new(
            xkey.chain_code,
            xkey.key + index.to_bytes(4, byteorder="big", signed=False),
            "sha512",
        ).digest()
        xkey.chain_code = hmac_[32:]
        offset = int.from_bytes(hmac_[:32], byteorder="big", signed=False)
        xkey.pub_key_point = ec.add(xkey.pub_key_point, mult(offset))
        xkey.key = bytes_from_point(xkey.pub_key_point)
        xkey.prv_key_int = 0
Beispiel #12
0
def pub_keyinfo_from_pub_key(pub_key: PubKey,
                             network: Optional[str] = None,
                             compressed: Optional[bool] = None) -> PubkeyInfo:
    "Return the pub key tuple (SEC-bytes, network) from a public key."

    compr = True if compressed is None else compressed
    net = "mainnet" if network is None else network
    ec = NETWORKS[net].curve

    if isinstance(pub_key, tuple):
        return bytes_from_point(pub_key, ec, compr), net
    if isinstance(pub_key, BIP32KeyData):
        return _pub_keyinfo_from_xpub(pub_key, network, compressed)
    try:
        return _pub_keyinfo_from_xpub(pub_key, network, compressed)
    except (TypeError, BTClibValueError):
        pass

    # it must be octets
    try:
        if compressed is None:
            pub_key = bytes_from_octets(pub_key,
                                        (ec.p_size + 1, 2 * ec.p_size + 1))
            compr = False
            if len(pub_key) == ec.p_size + 1:
                compr = True
        else:
            size = ec.p_size + 1 if compressed else 2 * ec.p_size + 1
            pub_key = bytes_from_octets(pub_key, size)
            compr = compressed
    except (TypeError, ValueError) as e:
        err_msg = f"not a public key: {pub_key!r}"
        raise BTClibValueError(err_msg) from e

    # verify that it is a valid point
    Q = point_from_octets(pub_key, ec)

    return bytes_from_point(Q, ec, compr), net
Beispiel #13
0
def test_libsecp256k1() -> None:

    try:
        import btclib_libsecp256k1.dsa  # pylint: disable=import-outside-toplevel
    except ImportError:  # pragma: no cover
        pytest.skip()

    prvkey, Q = dsa.gen_keys(0x1)
    pubkey_bytes = bytes_from_point(Q)
    msg = "Satoshi Nakamoto".encode()
    msg_hash = reduce_to_hlen(msg)

    libsecp256k1_sig = btclib_libsecp256k1.dsa.sign(msg_hash, prvkey)
    btclib_sig = dsa.sign_(msg_hash, prvkey)

    assert btclib_libsecp256k1.dsa.verify(msg_hash, pubkey_bytes,
                                          btclib_sig.serialize())
    assert dsa.verify(msg, prvkey, libsecp256k1_sig)
Beispiel #14
0
def _tweak(commit_hash: Octets, R: Point, ec: Curve, hf: HashF) -> int:
    "Return the hash(R||commit_hash) tweak for the provided R."

    t = bytes_from_point(R, ec) + bytes_from_octets(commit_hash)
    while True:
        h = hf()
        h.update(t)
        t = h.digest()
        # The following lines would introduce a bias
        # nonce = int.from_bytes(t, 'big') % ec.n
        # nonce = int_from_bits(t, ec.nlen) % ec.n
        # In general, taking a uniformly random integer (like those
        # obtained from a hash function in the random oracle model)
        # modulo the curve order n would produce a biased result.
        # However, if the order n is sufficiently close to 2^hf_len,
        # then the bias is not observable: e.g.
        # for secp256k1 and sha256 1-n/2^256 it is about 1.27*2^-128
        tweak = int_from_bits(t, ec.nlen)  # candidate tweak
        if 0 < tweak < ec.n:  # acceptable value for tweak
            return tweak  # successful candidate
Beispiel #15
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)
Beispiel #16
0
def _xpub_from_xprv(xprv: BIP32Key) -> BIP32KeyData:
    """Neutered Derivation (ND).

    Derivation of the extended public key corresponding to an extended
    private key (“neutered” as it removes the ability to sign transactions).
    """

    if isinstance(xprv, BIP32KeyData):
        xkey = copy.copy(xprv)
    else:
        xkey = BIP32KeyData.b58decode(xprv)

    if xkey.key[0] != 0:
        err_msg = f"not a private key: {xkey.b58encode()}"
        raise BTClibValueError(err_msg)

    i = XPRV_VERSIONS_ALL.index(xkey.version)
    xkey.version = XPUB_VERSIONS_ALL[i]

    q = int.from_bytes(xkey.key[1:], byteorder="big", signed=False)
    Q = mult(q)
    xkey.key = bytes_from_point(Q)

    return xkey
Beispiel #17
0
def test_infinity_point_bytes() -> None:
    with pytest.raises(BTClibValueError,
                       match="no bytes representation for infinity point"):
        bytes_from_point(INF)
Beispiel #18
0
def test_exceptions() -> None:
    # from creator example
    psbt_str = "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA="

    psbt = Psbt.b64decode(psbt_str)
    psbt.outputs[0].redeem_script = "bad script"  # type: ignore
    with pytest.raises(TypeError):
        psbt.serialize()

    psbt = Psbt.b64decode(psbt_str)
    psbt.inputs[0].witness_script = "bad script"  # type: ignore
    with pytest.raises(TypeError):
        psbt.serialize()

    psbt = Psbt.b64decode(psbt_str)
    psbt.outputs[0].unknown = {"bad key": b""}  # type: ignore
    with pytest.raises(TypeError):
        psbt.serialize()

    psbt = Psbt.b64decode(psbt_str)
    psbt.outputs[0].unknown = {b"deadbeef": "bad value"}  # type: ignore
    with pytest.raises(TypeError):
        psbt.serialize()

    psbt = Psbt.b64decode(psbt_str)
    psbt.inputs[0].sig_hash_type = 101
    with pytest.raises(BTClibValueError, match="invalid sign_hash type: "):
        psbt.serialize()

    psbt = Psbt.b64decode(psbt_str)
    psbt.inputs[0].final_script_sig = "bad script"  # type: ignore
    with pytest.raises(TypeError):
        psbt.serialize()

    psbt = Psbt.b64decode(psbt_str)
    _, Q = dsa.gen_keys()
    pub_key = sec_point.bytes_from_point(Q)
    r = s = int.from_bytes(bytes.fromhex("FF" * 32),
                           byteorder="big",
                           signed=False)
    sig_bytes = der.Sig(r, s,
                        check_validity=False).serialize(check_validity=False)
    psbt.inputs[0].partial_sigs = {pub_key: sig_bytes}
    with pytest.raises(BTClibValueError, match="invalid partial signature: "):
        psbt.serialize()

    pub_key = bytes.fromhex("02" + 31 * "00" + "07")
    psbt.inputs[0].partial_sigs = {pub_key: sig_bytes}
    with pytest.raises(BTClibValueError,
                       match="invalid partial signature pub_key: "):
        psbt.serialize()

    psbt = Psbt.b64decode(psbt_str)
    err_msg = "invalid version: "
    psbt.version = -1
    with pytest.raises(BTClibValueError, match=err_msg):
        psbt.serialize()
    psbt.version = 0xFFFFFFFF + 1
    with pytest.raises(BTClibValueError, match=err_msg):
        psbt.serialize()
    psbt.version = 1
    # TODO: add to test vectors
    with pytest.raises(BTClibValueError, match="invalid non-zero version: "):
        psbt.serialize()

    psbt = Psbt.b64decode(psbt_str)
    psbt_bin = psbt.serialize()
    psbt_bin = psbt_bin.replace(PSBT_SEPARATOR, PSBT_DELIMITER)
    # TODO: add to test vectors
    with pytest.raises(BTClibValueError,
                       match="malformed psbt: missing separator"):
        Psbt.parse(psbt_bin)

    psbt = Psbt.b64decode(psbt_str)
    psbt.inputs.pop()
    err_msg = "mismatched number of psb.tx.vin and psb.inputs: "
    # TODO: add to test vectors
    with pytest.raises(BTClibValueError, match=err_msg):
        psbt.serialize()

    psbt = Psbt.b64decode(psbt_str)
    psbt.tx.vin[0].script_witness = Witness([b""])
    err_msg = "non empty script_sig or witness"
    # TODO: add to test vectors
    with pytest.raises(BTClibValueError, match=err_msg):
        psbt.serialize()

    psbt = Psbt.b64decode(psbt_str)
    psbt.outputs.pop()
    err_msg = "mismatched number of psb.tx.vout and psbt.outputs: "
    # TODO: add to test vectors
    with pytest.raises(BTClibValueError, match=err_msg):
        psbt.serialize()

    psbt_str = "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA"
    psbt = Psbt.b64decode(psbt_str)
    psbt.tx.vin[0].prev_out.tx_id, psbt.tx.vin[1].prev_out.tx_id = (
        psbt.tx.vin[1].prev_out.tx_id,
        psbt.tx.vin[0].prev_out.tx_id,
    )
    err_msg = "mismatched non-witness utxo / outpoint tx_id"
    with pytest.raises(BTClibValueError, match=err_msg):
        psbt.assert_valid()
Beispiel #19
0
def test_from_key() -> None:

    secp256r1 = CURVES["secp256r1"]
    m_c = bytes_from_point(Q, compressed=True), "mainnet"
    m_unc = bytes_from_point(Q, compressed=False), "mainnet"
    t_c = bytes_from_point(Q, compressed=True), "testnet"
    t_unc = bytes_from_point(Q, compressed=False), "testnet"
    for pub_key in [Q, *plain_pub_keys]:
        assert Q == point_from_pub_key(pub_key)
        with pytest.raises(BTClibValueError):
            point_from_pub_key(pub_key, secp256r1)
        assert m_c == pub_keyinfo_from_pub_key(pub_key)
        assert m_c == pub_keyinfo_from_pub_key(pub_key, "mainnet")
        assert m_c == pub_keyinfo_from_pub_key(pub_key,
                                               "mainnet",
                                               compressed=True)
        assert m_c == pub_keyinfo_from_pub_key(pub_key, compressed=True)
        assert m_unc == pub_keyinfo_from_pub_key(pub_key,
                                                 "mainnet",
                                                 compressed=False)
        assert m_unc == pub_keyinfo_from_pub_key(pub_key, compressed=False)
        assert t_c == pub_keyinfo_from_pub_key(pub_key, "testnet")
        assert t_c == pub_keyinfo_from_pub_key(pub_key,
                                               "testnet",
                                               compressed=True)
        assert t_unc == pub_keyinfo_from_pub_key(pub_key,
                                                 "testnet",
                                                 compressed=False)

    for prv_key2 in [xpub_data, *compressed_pub_keys]:
        assert Q == point_from_pub_key(prv_key2)
        with pytest.raises(BTClibValueError):
            point_from_pub_key(prv_key2, secp256r1)
        assert m_c == pub_keyinfo_from_pub_key(prv_key2)
        assert m_c == pub_keyinfo_from_pub_key(prv_key2, "mainnet")
        assert m_c == pub_keyinfo_from_pub_key(prv_key2,
                                               "mainnet",
                                               compressed=True)
        assert m_c == pub_keyinfo_from_pub_key(prv_key2, compressed=True)
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_pub_key(prv_key2, "mainnet", compressed=False)
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_pub_key(prv_key2, compressed=False)
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_pub_key(prv_key2, "testnet", compressed=False)

    for prv_key3 in uncompressed_pub_keys:
        assert Q == point_from_pub_key(prv_key3)
        with pytest.raises(BTClibValueError):
            point_from_pub_key(prv_key3, secp256r1)
        assert m_unc == pub_keyinfo_from_pub_key(prv_key3)
        assert m_unc == pub_keyinfo_from_pub_key(prv_key3, "mainnet")
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_pub_key(prv_key3, "mainnet", compressed=True)
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_pub_key(prv_key3, compressed=True)
        assert m_unc == pub_keyinfo_from_pub_key(prv_key3,
                                                 "mainnet",
                                                 compressed=False)
        assert m_unc == pub_keyinfo_from_pub_key(prv_key3, compressed=False)
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_pub_key(prv_key3, "testnet", compressed=True)

    for prv_key4 in [xpub_data, *net_aware_pub_keys]:
        assert Q == point_from_pub_key(prv_key4)
        with pytest.raises(BTClibValueError):
            point_from_pub_key(prv_key4, secp256r1)
        assert pub_keyinfo_from_pub_key(prv_key4) in (m_c, m_unc)
        assert pub_keyinfo_from_pub_key(prv_key4, "mainnet") in (m_c, m_unc)
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_pub_key(prv_key4, "testnet")

    for prv_key5 in net_unaware_pub_keys:
        assert Q == point_from_pub_key(prv_key5)
        with pytest.raises(BTClibValueError):
            point_from_pub_key(prv_key5, secp256r1)
        assert pub_keyinfo_from_pub_key(prv_key5) in (m_c, m_unc)
        assert pub_keyinfo_from_pub_key(prv_key5, "mainnet") in (m_c, m_unc)
        assert pub_keyinfo_from_pub_key(prv_key5, "testnet") in (t_c, t_unc)

    for invalid_pub_key in [INF, INF_xpub_data, *invalid_pub_keys]:
        with pytest.raises(BTClibValueError):
            point_from_pub_key(invalid_pub_key)  # type: ignore
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_pub_key(invalid_pub_key)  # type: ignore

    for not_a_pub_key in [
            INF,
            INF_xpub_data,
            *not_a_pub_keys,
            q,
            q0,
            qn,
            *plain_prv_keys,
            xprv_data,
            xprv0_data,
            xprvn_data,
            *compressed_prv_keys,
            *uncompressed_prv_keys,
    ]:
        with pytest.raises(BTClibValueError):
            point_from_pub_key(not_a_pub_key)  # type: ignore
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_pub_key(not_a_pub_key)  # type: ignore

    for key in [Q, *plain_pub_keys, q, *plain_prv_keys]:
        assert Q == point_from_key(key)
        assert m_c == pub_keyinfo_from_key(key)
        assert m_c == pub_keyinfo_from_key(key, "mainnet")
        assert m_c == pub_keyinfo_from_key(key, "mainnet", compressed=True)
        assert m_c == pub_keyinfo_from_key(key, compressed=True)
        assert m_unc == pub_keyinfo_from_key(key, "mainnet", compressed=False)
        assert m_unc == pub_keyinfo_from_key(key, compressed=False)
        assert t_c == pub_keyinfo_from_key(key, "testnet")
        assert t_c == pub_keyinfo_from_key(key, "testnet", compressed=True)
        assert t_unc == pub_keyinfo_from_key(key, "testnet", compressed=False)

    for key2 in [
            *compressed_pub_keys, xpub_data, xprv_data, *compressed_prv_keys
    ]:
        assert Q == point_from_key(key2)
        with pytest.raises(BTClibValueError):
            point_from_key(key2, secp256r1)
        assert m_c == pub_keyinfo_from_key(key2)
        assert m_c == pub_keyinfo_from_key(key2, "mainnet")
        assert m_c == pub_keyinfo_from_key(key2, "mainnet", compressed=True)
        assert m_c == pub_keyinfo_from_key(key2, compressed=True)
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_key(key2, "mainnet", compressed=False)
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_key(key2, compressed=False)

    for key3 in [*uncompressed_pub_keys, *uncompressed_prv_keys]:
        assert Q == point_from_key(key3)
        with pytest.raises(BTClibValueError):
            point_from_key(key3, secp256r1)
        assert m_unc == pub_keyinfo_from_key(key3)
        assert m_unc == pub_keyinfo_from_key(key3, "mainnet")
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_key(key3, "mainnet", compressed=True)
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_key(key3, compressed=True)
        assert m_unc == pub_keyinfo_from_key(key3, "mainnet", compressed=False)
        assert m_unc == pub_keyinfo_from_key(key3, compressed=False)

    for key4 in [
            *net_aware_pub_keys, xpub_data, xprv_data, *net_aware_prv_keys
    ]:
        assert Q == point_from_key(key4)
        with pytest.raises(BTClibValueError):
            point_from_key(key4, secp256r1)
        assert pub_keyinfo_from_key(key4) in (m_c, m_unc)
        assert pub_keyinfo_from_key(key4, "mainnet") in (m_c, m_unc)
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_key(key4, "testnet")

    for key5 in [q, *net_unaware_prv_keys, *net_unaware_pub_keys]:
        assert Q == point_from_key(key5)
        assert pub_keyinfo_from_key(key5) in (m_c, m_unc)
        assert pub_keyinfo_from_key(key5, "mainnet") in (m_c, m_unc)
        assert pub_keyinfo_from_key(key5, "testnet") in (t_c, t_unc)

    for invalid_key in [
            INF,
            INF_xpub_data,
            *invalid_pub_keys,
            q0,
            qn,
            xprv0_data,
            xprvn_data,
            *invalid_prv_keys,
    ]:
        with pytest.raises(BTClibValueError):
            point_from_key(invalid_key)  # type: ignore
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_key(invalid_key)  # type: ignore

    for not_a_key in [
            q0,
            qn,
            xprv0_data,
            xprvn_data,
            INF,
            INF_xpub_data,
            *not_a_pub_keys,
    ]:
        with pytest.raises(BTClibValueError):
            point_from_key(not_a_key)  # type: ignore
        with pytest.raises(BTClibValueError):
            pub_keyinfo_from_key(not_a_key)  # type: ignore
Beispiel #20
0
def test_octets2point() -> None:
    for ec in all_curves.values():

        G_bytes = bytes_from_point(ec.G, ec)
        G_point = point_from_octets(G_bytes, ec)
        assert ec.G == G_point

        G_bytes = bytes_from_point(ec.G, ec, False)
        G_point = point_from_octets(G_bytes, ec)
        assert ec.G == G_point

        # just a random point, not INF
        q = 1 + secrets.randbelow(ec.n - 1)
        Q = mult(q, ec.G, ec)

        Q_bytes = b"\x03" if Q[1] & 1 else b"\x02"
        Q_bytes += Q[0].to_bytes(ec.p_size, byteorder="big", signed=False)
        Q_point = point_from_octets(Q_bytes, ec)
        assert Q_point == Q
        assert bytes_from_point(Q_point, ec) == Q_bytes

        Q_hex_str = Q_bytes.hex()
        Q_point = point_from_octets(Q_hex_str, ec)
        assert Q_point == Q

        Q_bytes = b"\x04" + Q[0].to_bytes(
            ec.p_size, byteorder="big", signed=False)
        Q_bytes += Q[1].to_bytes(ec.p_size, byteorder="big", signed=False)
        Q_point = point_from_octets(Q_bytes, ec)
        assert Q_point == Q
        assert bytes_from_point(Q_point, ec, False) == Q_bytes

        Q_hex_str = Q_bytes.hex()
        Q_point = point_from_octets(Q_hex_str, ec)
        assert Q_point == Q

        Q_bytes = b"\x01" + b"\x01" * ec.p_size
        with pytest.raises(BTClibValueError, match="not a point: "):
            point_from_octets(Q_bytes, ec)

        Q_bytes = b"\x01" + b"\x01" * 2 * ec.p_size
        with pytest.raises(BTClibValueError, match="not a point: "):
            point_from_octets(Q_bytes, ec)

        Q_bytes = b"\x04" + b"\x01" * ec.p_size
        with pytest.raises(BTClibValueError,
                           match="invalid size for uncompressed point: "):
            point_from_octets(Q_bytes, ec)

        Q_bytes = b"\x02" + b"\x01" * 2 * ec.p_size
        with pytest.raises(BTClibValueError,
                           match="invalid size for compressed point: "):
            point_from_octets(Q_bytes, ec)

        Q_bytes = b"\x03" + b"\x01" * 2 * ec.p_size
        with pytest.raises(BTClibValueError,
                           match="invalid size for compressed point: "):
            point_from_octets(Q_bytes, ec)

    # invalid x_Q coordinate
    ec = CURVES["secp256k1"]
    x_Q = 0xEEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34
    xstr = format(x_Q, "32X")
    with pytest.raises(BTClibValueError, match="invalid x-coordinate: "):
        point_from_octets("03" + xstr, ec)
    with pytest.raises(BTClibValueError, match="point not on curve: "):
        point_from_octets("04" + 2 * xstr, ec)
    with pytest.raises(BTClibValueError, match="point not on curve"):
        bytes_from_point((x_Q, x_Q), ec)
    with pytest.raises(BTClibValueError, match="point not on curve"):
        bytes_from_point((x_Q, x_Q), ec, False)
Beispiel #21
0
def sign(
    msg: Octets,
    ks: Sequence[int],
    sign_key_idx: Sequence[int],
    sign_keys: Sequence[int],
    pubk_rings: PubkeyRing,
) -> Tuple[bytes, SValues]:
    """Borromean ring signature - signing algorithm

    https://github.com/ElementsProject/borromean-signatures-writeup
    https://github.com/Blockstream/borromean_paper/blob/master/borromean_draft_0.01_9ade1e49.pdf

    inputs:
    - msg: message to be signed (bytes)
    - sign_key_idx: list of indexes representing each signing key per ring
    - sign_keys: list containing the whole set of signing keys (one per ring)
    - pubk_rings: dictionary of sequences representing single rings of pub_keys
    """

    msg = bytes_from_octets(msg)
    m = _get_msg_format(msg, pubk_rings)

    e0bytes = m
    s: SValues = defaultdict(list)
    e: SValues = defaultdict(list)
    # step 1
    for i, (pubk_ring, j_star, k) in enumerate(
        zip(pubk_rings.values(), sign_key_idx, ks)
    ):
        keys_size = len(pubk_ring)
        s[i] = [0] * keys_size
        e[i] = [0] * keys_size
        start_idx = (j_star + 1) % keys_size
        r = bytes_from_point(mult(k), ec)
        if start_idx != 0:
            for j in range(start_idx, keys_size):
                s[i][j] = secrets.randbits(256)
                e[i][j] = int_from_bits(_hash(m, r, i, j), ec.nlen) % ec.n
                # edge case that cannot be reproduced in the test suite
                if not 0 < e[i][j] < ec.n:
                    err_msg = "implausibile signature failure"  # pragma: no cover
                    raise BTClibRuntimeError(err_msg)  # pragma: no cover
                t = double_mult(-e[i][j], pubk_ring[j], s[i][j], ec.G)
                r = bytes_from_point(t, ec)
        e0bytes += r
    e0 = hf(e0bytes).digest()
    # step 2
    for i, (j_star, k) in enumerate(zip(sign_key_idx, ks)):
        e[i][0] = int_from_bits(_hash(m, e0, i, 0), ec.nlen) % ec.n
        # edge case that cannot be reproduced in the test suite
        if not 0 < e[i][0] < ec.n:
            err_msg = "implausibile signature failure"  # pragma: no cover
            raise BTClibRuntimeError(err_msg)  # pragma: no cover
        for j in range(1, j_star + 1):
            s[i][j - 1] = secrets.randbits(256)
            t = double_mult(-e[i][j - 1], pubk_rings[i][j - 1], s[i][j - 1], ec.G)
            r = bytes_from_point(t, ec)
            e[i][j] = int_from_bits(_hash(m, r, i, j), ec.nlen) % ec.n
            # edge case that cannot be reproduced in the test suite
            if not 0 < e[i][j] < ec.n:
                err_msg = "implausibile signature failure"  # pragma: no cover
                raise BTClibRuntimeError(err_msg)  # pragma: no cover
        s[i][j_star] = k + sign_keys[i] * e[i][j_star]
    return e0, s
Beispiel #22
0
# ==master ext private key==
# depth: 0x00 for master nodes, 0x01 for level-1 derived keys, ...
depth = b"\x00"
# This is ser32(i) for i in xi = xpar/i, with xi the key being serialized. (0x00000000 if master key)
child_number = b"\x00\x00\x00\x00"
# the fingerprint of the parent's public key (0x00000000 if master key)
fingerprint = b"\x00\x00\x00\x00"
idf = depth + fingerprint + child_number

# master private key, master public key, chain code
hd = hmac.digest(b"Bitcoin seed", seed.to_bytes(seed_bytes, "big"), "sha512")
qbytes = hd[:32]
p = int(qbytes.hex(), 16) % ec.n
qbytes = b"\x00" + p.to_bytes(32, "big")
Q = mult(p, ec.G)
Qbytes = bytes_from_point(Q)
chain_code = hd[32:]

# extended keys
ext_prv = b58encode(xprv + idf + chain_code + qbytes)
print("\nm")
print(ext_prv)
ext_pub = b58encode(xpub + idf + chain_code + Qbytes)
print("M")
print(ext_pub)
assert (
    ext_prv
    == b"xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6"
), "failure"
assert (
    ext_pub