Пример #1
0
def point_from_key(key: Key, ec: Curve = secp256k1) -> Point:
    """Return a point tuple from any possible key representation.

    It supports:

    - BIP32 extended keys (bytes, string, or BIP32KeyData)
    - SEC Octets (bytes or hex-string, with 02, 03, or 04 prefix)
    - native tuple
    """

    if isinstance(key, tuple):
        return point_from_pub_key(key, ec)
    if isinstance(key, int):
        q, _, _ = prv_keyinfo_from_prv_key(key)
        return mult(q, ec.G, ec)
    try:
        q, net, _ = prv_keyinfo_from_prv_key(key)
    except BTClibValueError:
        pass
    else:
        if ec != NETWORKS[net].curve:
            raise BTClibValueError("Curve mismatch")
        return mult(q, ec.G, ec)

    return point_from_pub_key(key, ec)
Пример #2
0
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"
Пример #3
0
def test_taproot_key_tweaking() -> None:
    prvkey = 123456
    pubkey = mult(prvkey)

    script_trees = [
        None,
        [(0xC0, ["OP_1"])],
        [[(0xC0, ["OP_2"])], [(0xC0, ["OP_3"])]],
    ]

    for script_tree in script_trees:
        tweaked_prvkey = output_prvkey(prvkey, script_tree)
        tweaked_pubkey = output_pubkey(pubkey, script_tree)[0]

        assert tweaked_pubkey == mult(tweaked_prvkey)[0].to_bytes(32, "big")
Пример #4
0
def test_assorted_mult() -> None:
    ec = ec23_31
    H = second_generator(ec)
    for k1 in range(-ec.n + 1, ec.n):
        K1 = mult(k1, ec.G, ec)
        for k2 in range(ec.n):
            K2 = mult(k2, H, ec)

            shamir = double_mult(k1, ec.G, k2, ec.G, ec)
            assert shamir == mult(k1 + k2, ec.G, ec)

            shamir = double_mult(k1, INF, k2, H, ec)
            assert ec.is_on_curve(shamir)
            assert shamir == K2

            shamir = double_mult(k1, ec.G, k2, INF, ec)
            assert ec.is_on_curve(shamir)
            assert shamir == K1

            shamir = double_mult(k1, ec.G, k2, H, ec)
            assert ec.is_on_curve(shamir)
            K1K2 = ec.add(K1, K2)
            assert K1K2 == shamir

            k3 = 1 + secrets.randbelow(ec.n - 1)
            K3 = mult(k3, ec.G, ec)
            K1K2K3 = ec.add(K1K2, K3)
            assert ec.is_on_curve(K1K2K3)
            boscoster = multi_mult([k1, k2, k3], [ec.G, H, ec.G], ec)
            assert ec.is_on_curve(boscoster)
            assert K1K2K3 == boscoster, k3

            k4 = 1 + secrets.randbelow(ec.n - 1)
            K4 = mult(k4, H, ec)
            K1K2K3K4 = ec.add(K1K2K3, K4)
            assert ec.is_on_curve(K1K2K3K4)
            points = [ec.G, H, ec.G, H]
            boscoster = multi_mult([k1, k2, k3, k4], points, ec)
            assert ec.is_on_curve(boscoster)
            assert K1K2K3K4 == boscoster, k4
            assert K1K2K3 == multi_mult([k1, k2, k3, 0], points, ec)
            assert K1K2 == multi_mult([k1, k2, 0, 0], points, ec)
            assert K1 == multi_mult([k1, 0, 0, 0], points, ec)
            assert INF == multi_mult([0, 0, 0, 0], points, ec)

            err_msg = "mismatch between number of scalars and points: "
            with pytest.raises(BTClibValueError, match=err_msg):
                multi_mult([k1, k2, k3, k4], [ec.G, H, ec.G], ec)
Пример #5
0
def pubkey_bytes_from_prvkey(prvkey, compressed=True):
    PubKey = mult(prvkey)
    if compressed:
        prefix = b"\x02" if (PubKey[1] % 2 == 0) else b"\x03"
        return prefix + PubKey[0].to_bytes(32, byteorder="big")
    return (b"\x04" + PubKey[0].to_bytes(32, byteorder="big") +
            PubKey[1].to_bytes(32, byteorder="big"))
Пример #6
0
def test_negate() -> None:
    for ec in all_curves.values():

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

        # Jacobian coordinates
        QJ = jac_from_aff(Q)
        minus_QJ = ec.negate_jac(QJ)
        assert ec.jac_equality(ec.add_jac(QJ, minus_QJ), INFJ)

        # negate of INF is INF
        minus_INF = ec.negate(INF)
        assert minus_INF == INF

        # negate of INFJ is INFJ
        minus_INFJ = ec.negate_jac(INFJ)
        assert ec.jac_equality(minus_INFJ, INFJ)

        with pytest.raises(BTClibTypeError, match="not a point"):
            ec.negate(ec.GJ)  # type: ignore

        with pytest.raises(BTClibTypeError, match="not a Jacobian point"):
            ec.negate_jac(ec.G)  # type: ignore
Пример #7
0
def check_output_pubkey(q: Octets,
                        script: Octets,
                        control: Octets,
                        ec: Curve = secp256k1) -> bool:
    q = bytes_from_octets(q)
    script = bytes_from_octets(script)
    control = bytes_from_octets(control)
    if len(control) > 4129:  # 33 + 32 * 128
        raise BTClibValueError("Control block too long")
    m = (len(control) - 33) // 32
    if len(control) != 33 + 32 * m:
        raise BTClibValueError("Invalid control block length")
    leaf_version = control[0] & 0xFE
    preimage = leaf_version.to_bytes(1, "big") + var_bytes.serialize(script)
    k = tagged_hash(b"TapLeaf", preimage)
    for j in range(m):
        e = control[33 + 32 * j:65 + 32 * j]
        if k < e:
            k = tagged_hash(b"TapBranch", k + e)
        else:
            k = tagged_hash(b"TapBranch", e + k)
    p_bytes = control[1:33]
    t_bytes = tagged_hash(b"TapTweak", p_bytes + k)
    p = int.from_bytes(p_bytes, "big")
    t = int.from_bytes(t_bytes, "big")
    # edge case that cannot be reproduced in the test suite
    if t >= ec.n:
        raise BTClibValueError("Invalid script tree hash")  # pragma: no cover
    P = (p, secp256k1.y_even(p))
    Q = secp256k1.add(P, mult(t))
    return Q[0] == int.from_bytes(q, "big") and control[0] & 1 == Q[1] % 2
Пример #8
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
Пример #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
Пример #10
0
def test_ecdh() -> None:
    ec = CURVES["secp256k1"]
    hf = sha256

    a, A = dsa.gen_keys()  # Alice
    b, B = dsa.gen_keys()  # Bob

    # Alice computes the shared secret using Bob's public key
    shared_secret_a = mult(a, B)

    # Bob computes the shared secret using Alice's public key
    shared_secret_b = mult(b, A)

    assert shared_secret_a == shared_secret_b
    assert shared_secret_a == mult(a * b, ec.G)

    # hash the shared secret to remove weak bits
    shared_secret_field_element = shared_secret_a[0]
    z = shared_secret_field_element.to_bytes(ec.p_size,
                                             byteorder="big",
                                             signed=False)

    shared_info = b"deadbeef"

    hf_size = hf().digest_size
    for size in (hf_size - 1, hf_size, hf_size + 1):
        shared_key = ansi_x9_63_kdf(z, size, hf, None)
        assert len(shared_key) == size
        assert shared_key == diffie_hellman(a, B, size, None, ec, hf)
        assert shared_key == diffie_hellman(b, A, size, None, ec, hf)
        shared_key = ansi_x9_63_kdf(z, size, hf, shared_info)
        assert len(shared_key) == size
        assert shared_key == diffie_hellman(a, B, size, shared_info, ec, hf)
        assert shared_key == diffie_hellman(b, A, size, shared_info, ec, hf)

    max_size = hf_size * (2**32 - 1)
    size = max_size + 1
    with pytest.raises(BTClibValueError,
                       match="cannot derive a key larger than "):
        ansi_x9_63_kdf(z, size, hf, None)
Пример #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
Пример #12
0
def test_control_block() -> None:

    script_tree = [[(0xC0, ["OP_2"])], [(0xC0, ["OP_3"])]]
    pubkey = output_pubkey(None, script_tree)[0]
    script, control = input_script_sig(None, script_tree, 0)
    assert check_output_pubkey(pubkey, serialize(script), control)

    prvkey = 123456
    internal_pubkey = mult(prvkey)
    script_tree = [[(0xC0, ["OP_2"])], [(0xC0, ["OP_3"])]]
    pubkey = output_pubkey(internal_pubkey, script_tree)[0]
    script, control = input_script_sig(internal_pubkey, script_tree, 0)
    assert check_output_pubkey(pubkey, serialize(script), control)
Пример #13
0
def ssa_verify_commit_(
    commit_hash: Octets,
    R: Point,
    msg_hash: Octets,
    pub_key: ssa.BIP340PubKey,
    sig: ssa.Sig,
    hf: HashF = sha256,
) -> bool:
    "Open the commitment associated to an EC SSA signature."

    tweak = _tweak(commit_hash, R, sig.ec, hf)
    W = sig.ec.add(R, mult(tweak, sig.ec.G, sig.ec))

    # sig.r is in [1..p-1]
    return (sig.r == W[0]) and ssa.verify_(msg_hash, pub_key, sig, hf)
Пример #14
0
def test_is_on_curve() -> None:
    for ec in all_curves.values():

        with pytest.raises(BTClibValueError, match="point must be a tuple"):
            ec.is_on_curve("not a point")  # type: ignore

        with pytest.raises(BTClibValueError,
                           match="x-coordinate not in 0..p-1: "):
            ec.y(ec.p)

        # just a random point, not INF
        q = 1 + secrets.randbelow(ec.n - 1)
        Q = mult(q, ec.G, ec)
        with pytest.raises(BTClibValueError,
                           match="y-coordinate not in 1..p-1: "):
            ec.is_on_curve((Q[0], ec.p))
Пример #15
0
def dsa_verify_commit_(
    commit_hash: Octets,
    R: Point,
    msg_hash: Octets,
    key: dsa.Key,
    sig: dsa.Sig,
    lower_s: bool = True,
    hf: HashF = sha256,
) -> bool:
    "Open the commitment associated to an EC DSA signature."

    tweak = _tweak(commit_hash, R, sig.ec, hf)
    W = sig.ec.add(R, mult(tweak, sig.ec.G, sig.ec))

    # sig.r is in [1..n-1]
    return (sig.r == W[0] % sig.ec.n) and dsa.verify_(msg_hash, key, sig,
                                                      lower_s, hf)
Пример #16
0
def test_pub_key_recovery() -> None:

    ec = CURVES["secp112r2"]

    q = 0x10
    Q = mult(q, ec.G, ec)

    msg = "Satoshi Nakamoto".encode()
    sig = dsa.sign(msg, q, ec=ec)
    dsa.assert_as_valid(msg, Q, sig)
    assert dsa.verify(msg, Q, sig)

    keys = dsa.recover_pub_keys(msg, sig)
    assert len(keys) == 4
    assert Q in keys
    for Q in keys:
        assert dsa.verify(msg, Q, sig)
Пример #17
0
def ssa_commit_sign_(
    commit_hash: Octets,
    msg_hash: Octets,
    prv_key: PrvKey,
    nonce: Optional[PrvKey] = None,
    ec: Curve = secp256k1,
    hf: HashF = sha256,
) -> Tuple[ssa.Sig, Point]:
    "Include a commitment inside an EC SSA signature."

    nonce = (ssa.det_nonce_(msg_hash, prv_key, aux=None, ec=ec, hf=hf)
             if nonce is None else int_from_prv_key(nonce, ec))
    R = mult(nonce, ec.G, ec)

    tweaked_nonce = (nonce + _tweak(commit_hash, R, ec, hf)) % ec.n
    tweaked_sig = ssa.sign_(msg_hash, prv_key, tweaked_nonce, ec, hf)

    return tweaked_sig, R
Пример #18
0
def output_prvkey(
    internal_prvkey: PrvKey,
    script_tree: Optional[TaprootScriptTree] = None,
    ec: Curve = secp256k1,
) -> int:
    internal_prvkey = int_from_prv_key(internal_prvkey)
    P = mult(internal_prvkey)
    if script_tree:
        _, h = tree_helper(script_tree)
    else:
        h = b""
    has_even_y = ec.y_even(P[0]) == P[1]
    internal_prvkey = internal_prvkey if has_even_y else ec.n - internal_prvkey
    t = int.from_bytes(tagged_hash(b"TapTweak", P[0].to_bytes(32, "big") + h), "big")
    # edge case that cannot be reproduced in the test suite
    if t >= ec.n:
        raise BTClibValueError("Invalid script tree hash")  # pragma: no cover
    return (internal_prvkey + t) % ec.n
Пример #19
0
def test_aff_jac_conversions() -> None:
    for ec in all_curves.values():

        # just a random point, not INF
        q = 1 + secrets.randbelow(ec.n - 1)
        Q = mult(q, ec.G, ec)
        QJ = jac_from_aff(Q)
        assert Q == ec.aff_from_jac(QJ)
        x_Q = ec.x_aff_from_jac(QJ)
        assert Q[0] == x_Q
        y_Q = ec.y_aff_from_jac(QJ)
        assert Q[1] == y_Q

        assert INF == ec.aff_from_jac(jac_from_aff(INF))

        with pytest.raises(BTClibValueError, match="INF has no x-coordinate"):
            ec.x_aff_from_jac(INFJ)

        with pytest.raises(BTClibValueError, match="INF has no y-coordinate"):
            ec.y_aff_from_jac(INFJ)
Пример #20
0
def test_add_double_aff_jac() -> None:
    "Test consistency between affine and Jacobian add/double methods."
    for ec in all_curves.values():

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

        # add Q and G
        R = ec.add_aff(Q, ec.G)
        RJ = ec.add_jac(QJ, ec.GJ)
        assert R == ec.aff_from_jac(RJ)

        # double Q
        R = ec.double_aff(Q)
        RJ = ec.double_jac(QJ)
        assert R == ec.aff_from_jac(RJ)
        assert R == ec.add_aff(Q, Q)
        assert ec.jac_equality(RJ, ec.add_jac(QJ, QJ))
Пример #21
0
def output_pubkey(
    internal_pubkey: Optional[Key] = None,
    script_tree: Optional[TaprootScriptTree] = None,
    ec: Curve = secp256k1,
) -> Tuple[bytes, int]:
    if not internal_pubkey and not script_tree:
        raise BTClibValueError("Missing data")
    if internal_pubkey:
        pubkey = pub_keyinfo_from_key(internal_pubkey, compressed=True)[0][1:]
    else:
        h_str = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"
        pubkey = bytes.fromhex(h_str)
    if script_tree:
        _, h = tree_helper(script_tree)
    else:
        h = tagged_hash(b"TapTweak", pubkey)
    t = int.from_bytes(tagged_hash(b"TapTweak", pubkey + h), "big")
    # edge case that cannot be reproduced in the test suite
    if t >= ec.n:
        raise BTClibValueError("Invalid script tree hash")  # pragma: no cover
    x = int.from_bytes(pubkey, "big")
    Q = ec.add((x, ec.y_even(x)), mult(t))
    return Q[0].to_bytes(32, "big"), Q[1] % 2
Пример #22
0
def diffie_hellman(
    dU: int,
    QV: Point,
    size: int,
    shared_info: Optional[bytes] = None,
    ec: Curve = secp256k1,
    hf: HashF = sha256,
) -> bytes:
    """Diffie-Hellman elliptic curve key agreement scheme.

    http://www.secg.org/sec1-v2.pdf, section 6.1
    """

    shared_secret_point = mult(dU, QV, ec)
    # edge case that cannot be reproduced in the test suite
    if shared_secret_point[1] == 0:
        err_msg = "invalid (INF) key"  # pragma: no cover
        raise BTClibRuntimeError(err_msg)  # pragma: no cover
    shared_secret_field_element = shared_secret_point[0]
    z = shared_secret_field_element.to_bytes(ec.p_size,
                                             byteorder="big",
                                             signed=False)
    return ansi_x9_63_kdf(z, size, hf, shared_info)
Пример #23
0
def test_rfc6979_tv() -> None:

    fname = "rfc6979.json"
    filename = path.join(path.dirname(__file__), "_data", fname)
    with open(filename, "r") as file_:
        test_dict = json.load(file_)

    lower_s = False
    for ec_name in test_dict:
        ec = CURVES[ec_name]
        test_vectors = test_dict[ec_name]
        for x, x_U, y_U, hf, msg, k, r, s in test_vectors:
            x = int(x, 16)
            msg = msg.encode()
            m = reduce_to_hlen(msg, hf=getattr(hashlib, hf))
            # test RFC6979 implementation
            k2 = rfc6979_(m, x, ec, getattr(hashlib, hf))
            assert int(k, 16) == k2
            # test RFC6979 usage in DSA
            sig = dsa.sign_(m, x, k2, lower_s, ec=ec, hf=getattr(hashlib, hf))
            assert int(r, 16) == sig.r
            assert int(s, 16) == sig.s
            # test that RFC6979 is the default nonce for DSA
            sig = dsa.sign_(m,
                            x,
                            None,
                            lower_s,
                            ec=ec,
                            hf=getattr(hashlib, hf))
            assert int(r, 16) == sig.r
            assert int(s, 16) == sig.s
            # test key-pair coherence
            U = mult(x, ec.G, ec)
            assert int(x_U, 16), int(y_U, 16) == U
            # test signature validity
            dsa.assert_as_valid(msg, U, sig, lower_s, getattr(hashlib, hf))
Пример #24
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
Пример #25
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)
Пример #26
0
def test_double_mult() -> None:
    H = second_generator(secp256k1)
    G = secp256k1.G

    # 0*G + 1*H
    T = double_mult(1, H, 0, G)
    assert T == H
    T = multi_mult([1, 0], [H, G])
    assert T == H

    # 0*G + 2*H
    exp = mult(2, H)
    T = double_mult(2, H, 0, G)
    assert T == exp
    T = multi_mult([2, 0], [H, G])
    assert T == exp

    # 0*G + 3*H
    exp = mult(3, H)
    T = double_mult(3, H, 0, G)
    assert T == exp
    T = multi_mult([3, 0], [H, G])
    assert T == exp

    # 1*G + 0*H
    T = double_mult(0, H, 1, G)
    assert T == G
    T = multi_mult([0, 1], [H, G])
    assert T == G

    # 2*G + 0*H
    exp = mult(2, G)
    T = double_mult(0, H, 2, G)
    assert T == exp
    T = multi_mult([0, 2], [H, G])
    assert T == exp

    # 3*G + 0*H
    exp = mult(3, G)
    T = double_mult(0, H, 3, G)
    assert T == exp
    T = multi_mult([0, 3], [H, G])
    assert T == exp

    # 0*G + 5*H
    exp = mult(5, H)
    T = double_mult(5, H, 0, G)
    assert T == exp
    T = multi_mult([5, 0], [H, G])
    assert T == exp

    # 0*G - 5*H
    exp = mult(-5, H)
    T = double_mult(-5, H, 0, G)
    assert T == exp
    T = multi_mult([-5, 0], [H, G])
    assert T == exp

    # 1*G - 5*H
    exp = secp256k1.add(G, T)
    T = double_mult(-5, H, 1, G)
    assert T == exp
Пример #27
0
def test_symmetry() -> None:
    "Methods to break simmetry: quadratic residue, even/odd, low/high."
    for ec in low_card_curves.values():

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

        assert not ec.y_even(x_Q) % 2
        assert ec.y_low(x_Q) <= ec.p // 2

        # compute quadratic residues
        hasRoot = {1}
        for i in range(2, ec.p):
            hasRoot.add(i * i % ec.p)

        if ec.p % 4 == 3:
            quad_res = ec.y_quadratic_residue(x_Q)

            # in this case only quad_res is a quadratic residue
            assert quad_res in hasRoot
            root = mod_sqrt(quad_res, ec.p)
            assert quad_res == (root * root) % ec.p
            root = ec.p - root
            assert quad_res == (root * root) % ec.p

            assert ec.p - quad_res not in hasRoot
            with pytest.raises(BTClibValueError, match="no root for "):
                mod_sqrt(ec.p - quad_res, ec.p)
        else:
            assert ec.p % 4 == 1
            # cannot use y_quadratic_residue in this case
            err_msg = "field prime is not equal to 3 mod 4: "
            with pytest.raises(BTClibValueError, match=err_msg):
                ec.y_quadratic_residue(x_Q)

            y_even = ec.y_even(x_Q)
            y_odd = ec.p - y_even
            # in this case neither or both y_Q are quadratic residues
            neither = y_odd not in hasRoot and y_even not in hasRoot
            both = y_odd in hasRoot and y_even in hasRoot
            assert neither or both
            if y_odd in hasRoot:  # both have roots
                root = mod_sqrt(y_odd, ec.p)
                assert y_odd == (root * root) % ec.p
                root = ec.p - root
                assert y_odd == (root * root) % ec.p
                root = mod_sqrt(y_even, ec.p)
                assert y_even == (root * root) % ec.p
                root = ec.p - root
                assert y_even == (root * root) % ec.p
            else:
                err_msg = "no root for "
                with pytest.raises(BTClibValueError, match=err_msg):
                    mod_sqrt(y_odd, ec.p)
                with pytest.raises(BTClibValueError, match=err_msg):
                    mod_sqrt(y_even, ec.p)

    with pytest.raises(BTClibValueError):
        secp256k1.y_even(INF[0])
    with pytest.raises(BTClibValueError):
        secp256k1.y_low(INF[0])
    with pytest.raises(BTClibValueError):
        secp256k1.y_quadratic_residue(INF[0])
Пример #28
0
# or distributed except according to the terms contained in the LICENSE file.
""" Deterministic Key Sequence (Type-1)"""

import secrets
from hashlib import sha256 as hf

from btclib.ecc.curve import mult
from btclib.ecc.curve import secp256k1 as ec
from btclib.utils import int_from_bits

# master prvkey in [1, n-1]
mprvkey = 1 + secrets.randbelow(ec.n - 1)
print(f"\nmaster prvkey: {hex(mprvkey).upper()}")

# Master Pubkey:
mpubkey = mult(mprvkey, ec.G)
print(f"Master Pubkey: {hex(mpubkey[0]).upper()}")
print(f"               {hex(mpubkey[1]).upper()}")

r = secrets.randbits(ec.nlen)
print(f"\npublic random number: {hex(r).upper()}")

rbytes = r.to_bytes(ec.nsize, "big")
nKeys = 3
for i in range(nKeys):
    ibytes = i.to_bytes(ec.nsize, "big")
    hd = hf(ibytes + rbytes).digest()
    offset = int_from_bits(hd, ec.nlen) % ec.n
    q = (mprvkey + offset) % ec.n
    Q = mult(q, ec.G, ec)
    print(f"\nprvkey #{i}: {hex(q).upper()}")
Пример #29
0
#
# No part of btclib including this file, may be copied, modified, propagated,
# or distributed except according to the terms contained in the LICENSE file.

from btclib.ecc.curve import mult
from btclib.ecc.curve import secp256k1 as ec
from btclib.ecc.dsa import recover_pub_keys, sign, verify
from btclib.ecc.der import Sig

print("\n*** EC:")
print(ec)

print("0. Key generation")
q = 0x18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725
q %= ec.n
Q = mult(q, ec.G)
print(f"prvkey:    {hex(q).upper()}")
print(f"PubKey: {'02' if Q[1] % 2 == 0 else '03'} {hex(Q[0]).upper()}")

print("\n1. Message to be signed")
msg1 = "Paolo is afraid of ephemeral random numbers".encode()
print(msg1.decode())

print("2. Sign message")
sig1 = sign(msg1, q)
print(f"    r1:    {hex(sig1.r).upper()}")
print(f"    s1:    {hex(sig1.s).upper()}")

print("3. Verify signature")
print(verify(msg1, Q, sig1))
Пример #30
0
from hashlib import sha256 as hf

from btclib import dsa
from btclib.ecc.curve import mult
from btclib.ecc.curve import secp256k1 as ec
from btclib.dh import ansi_x9_63_kdf

# Diffie-Hellman
print("\n Diffie-Hellman")

a, A = dsa.gen_keys()  # Alice
b, B = dsa.gen_keys()  # Bob

# Alice calculates the shared secret using Bob's public key
shared_secret_a = mult(a, B)

# Bob calculates the shared secret using Alice's public key
shared_secret_b = mult(b, A)

print("same shared secret:", shared_secret_a == shared_secret_b)
print("as expected:", shared_secret_a == mult(a * b, ec.G))

# hash the shared secret to remove weak bits
shared_secret_field_element = shared_secret_a[0]
z = shared_secret_field_element.to_bytes(ec.psize, "big")
shared_info = None
shared_key = ansi_x9_63_kdf(z, 32, hf, shared_info)
print("shared key:", shared_key.hex())