예제 #1
0
def test_mult_recursive_aff() -> None:
    for ec in all_curves.values():
        assert mult_recursive_aff(0, ec.G, ec) == INF
        assert mult_recursive_aff(0, INF, ec) == INF

        assert mult_recursive_aff(1, INF, ec) == INF
        assert mult_aff(1, ec.G, ec) == ec.G

        Q = ec.add_aff(ec.G, ec.G)
        assert Q == mult_recursive_aff(2, ec.G, ec)

        Q = mult_recursive_aff(ec.n - 1, ec.G, ec)
        assert ec.negate(ec.G) == Q
        assert mult_recursive_aff(ec.n - 1, INF, ec) == INF

        assert ec.add_aff(Q, ec.G) == INF
        assert mult_recursive_aff(ec.n, ec.G, ec) == INF
        assert mult_recursive_aff(ec.n, INF, ec) == INF

        with pytest.raises(BTClibValueError, match="negative m: "):
            mult_recursive_aff(-1, ec.G, ec)

    for ec in low_card_curves.values():
        for q in range(ec.n):
            Q = mult_recursive_aff(q, ec.G, ec)
            assert ec.is_on_curve(Q), f"{q}, {ec}"
            QJ = _mult(q, ec.GJ, ec)
            assert ec.is_on_curve(ec.aff_from_jac(QJ)), f"{q}, {ec}"
            assert Q == ec.aff_from_jac(QJ), f"{q}, {ec}"
            assert INF == mult_recursive_aff(q, INF, ec), f"{q}, {ec}"
            assert ec.jac_equality(INFJ, _mult(q, INFJ, ec)), f"{q}, {ec}"
예제 #2
0
def test_mult_recursive_jac() -> None:
    for ec in all_curves.values():
        assert ec.jac_equality(mult_recursive_jac(0, ec.GJ, ec), INFJ)
        assert ec.jac_equality(mult_recursive_jac(0, INFJ, ec), INFJ)

        assert ec.jac_equality(mult_recursive_jac(1, INFJ, ec), INFJ)
        assert ec.jac_equality(mult_recursive_jac(1, ec.GJ, ec), ec.GJ)

        PJ = ec.add_jac(ec.GJ, ec.GJ)
        assert ec.jac_equality(PJ, mult_recursive_jac(2, ec.GJ, ec))

        PJ = mult_recursive_jac(ec.n - 1, ec.GJ, ec)
        assert ec.jac_equality(ec.negate_jac(ec.GJ), PJ)
        assert ec.jac_equality(mult_recursive_jac(ec.n - 1, INFJ, ec), INFJ)

        assert ec.jac_equality(ec.add_jac(PJ, ec.GJ), INFJ)
        assert ec.jac_equality(mult_recursive_jac(ec.n, ec.GJ, ec), INFJ)
        assert ec.jac_equality(mult_recursive_jac(ec.n, INFJ, ec), INFJ)

        with pytest.raises(BTClibValueError, match="negative m: "):
            mult_recursive_jac(-1, ec.GJ, ec)

    ec = ec23_31
    for k1 in range(ec.n):
        K1 = mult_recursive_jac(k1, ec.GJ, ec)
        assert ec.jac_equality(K1, _mult(k1, ec.GJ, ec))
예제 #3
0
파일: dsa.py 프로젝트: btclib-org/btclib
def _sign_(c: int, q: int, nonce: int, lower_s: bool, ec: Curve) -> Sig:
    # Private function for testing purposes: it allows to explore all
    # possible value of the challenge c (for low-cardinality curves).
    # It assume that c is in [0, n-1], while q and nonce are in [1, n-1]

    # Steps numbering follows SEC 1 v.2 section 4.1.3

    KJ = _mult(nonce, ec.GJ, ec)  # 1

    # affine x_K-coordinate of K (field element)
    x_K = (KJ[0] * mod_inv(KJ[2] * KJ[2], ec.p)) % ec.p
    # mod n makes it a scalar
    r = x_K % ec.n  # 2, 3
    if r == 0:  # r≠0 required as it multiplies the public key
        raise BTClibRuntimeError("failed to sign: r = 0")

    s = mod_inv(nonce, ec.n) * (c + r * q) % ec.n  # 6
    if s == 0:  # s≠0 required as verify will need the inverse of s
        raise BTClibRuntimeError("failed to sign: s = 0")

    # bitcoin canonical 'low-s' encoding for ECDSA signatures
    # it removes signature malleability as cause of transaction malleability
    # see https://github.com/bitcoin/bitcoin/pull/6769
    if lower_s and s > ec.n / 2:
        s = ec.n - s  # s = - s % ec.n

    return Sig(r, s, ec)
예제 #4
0
def test_mult_base_3() -> None:
    for ec in low_card_curves.values():
        assert ec.jac_equality(mult_base_3(0, ec.GJ, ec), INFJ)
        assert ec.jac_equality(mult_base_3(0, INFJ, ec), INFJ)

        assert ec.jac_equality(mult_base_3(1, INFJ, ec), INFJ)
        assert ec.jac_equality(mult_base_3(1, ec.GJ, ec), ec.GJ)

        PJ = mult_base_3(2, ec.GJ, ec)
        assert ec.jac_equality(PJ, ec.add_jac(ec.GJ, ec.GJ))

        PJ = mult_base_3(ec.n - 1, ec.GJ, ec)
        assert ec.jac_equality(ec.negate_jac(ec.GJ), PJ)
        assert ec.jac_equality(mult_base_3(ec.n - 1, INFJ, ec), INFJ)

        assert ec.jac_equality(ec.add_jac(PJ, ec.GJ), INFJ)
        assert ec.jac_equality(mult_base_3(ec.n, ec.GJ, ec), INFJ)
        assert ec.jac_equality(mult_mont_ladder(ec.n, INFJ, ec), INFJ)

        with pytest.raises(BTClibValueError, match="negative m: "):
            mult_base_3(-1, ec.GJ, ec)

    ec = ec23_31
    for k1 in range(ec.n):
        K1 = mult_base_3(k1, ec.GJ, ec)
        assert ec.jac_equality(K1, _mult(k1, ec.GJ, ec))
def test_mult_w_NAF() -> None:
    for w in range(1, 6):
        for ec in low_card_curves.values():
            assert ec.jac_equality(mult_w_NAF(0, ec.GJ, ec, w), INFJ)
            assert ec.jac_equality(mult_w_NAF(0, INFJ, ec, w), INFJ)

            assert ec.jac_equality(mult_w_NAF(1, INFJ, ec, w), INFJ)
            assert ec.jac_equality(mult_w_NAF(1, ec.GJ, ec, w), ec.GJ)

            PJ = mult_w_NAF(2, ec.GJ, ec, w)
            assert ec.jac_equality(PJ, ec.add_jac(ec.GJ, ec.GJ))

            PJ = mult_w_NAF(ec.n - 1, ec.GJ, ec, w)
            assert ec.jac_equality(ec.negate_jac(ec.GJ), PJ)

            assert ec.jac_equality(mult_w_NAF(ec.n - 1, INFJ, ec, w), INFJ)
            assert ec.jac_equality(ec.add_jac(PJ, ec.GJ), INFJ)
            assert ec.jac_equality(mult_w_NAF(ec.n, ec.GJ, ec, w), INFJ)

            with pytest.raises(BTClibValueError, match="negative m: "):
                mult_w_NAF(-1, ec.GJ, ec, w)

            with pytest.raises(BTClibValueError, match="non positive w: "):
                mult_w_NAF(1, ec.GJ, ec, -w)

    ec = ec23_31
    for w in range(1, 10):
        for k1 in range(ec.n):
            K1 = mult_w_NAF(k1, ec.GJ, ec, w)
            assert ec.jac_equality(K1, _mult(k1, ec.GJ, ec))
예제 #6
0
def test_low_cardinality() -> None:
    """test low-cardinality curves for all msg/key pairs."""
    # pylint: disable=protected-access

    # ec.n has to be prime to sign
    test_curves = [
        low_card_curves["ec13_11"],
        # low_card_curves["ec13_19"],
        # low_card_curves["ec17_13"],
        low_card_curves["ec17_23"],
        low_card_curves["ec19_13"],
        # low_card_curves["ec19_23"],
        low_card_curves["ec23_19"],
        low_card_curves["ec23_31"],
    ]

    lower_s = True
    # only low cardinality test curves or it would take forever
    for ec in test_curves:
        for q in range(1, ec.n):  # all possible private keys
            QJ = _mult(q, ec.GJ, ec)  # public key
            for k in range(1, ec.n):  # all possible ephemeral keys
                RJ = _mult(k, ec.GJ, ec)
                r = ec.x_aff_from_jac(RJ) % ec.n
                k_inv = mod_inv(k, ec.n)
                for e in range(ec.n):  # all possible challenges
                    s = k_inv * (e + q * r) % ec.n
                    # bitcoin canonical 'low-s' encoding for ECDSA
                    if lower_s and s > ec.n / 2:
                        s = ec.n - s
                    if r == 0 or s == 0:
                        err_msg = "failed to sign: "
                        with pytest.raises(BTClibRuntimeError, match=err_msg):
                            dsa._sign_(e, q, k, lower_s, ec)
                    else:
                        sig = dsa._sign_(e, q, k, lower_s, ec)
                        assert r == sig.r
                        assert s == sig.s
                        assert ec == sig.ec
                        # valid signature must pass verification
                        dsa._assert_as_valid_(e, QJ, r, s, lower_s, ec)

                        jac_keys = dsa._recover_pub_keys_(e, r, s, lower_s, ec)
                        # FIXME speed this up
                        Qs = [ec.aff_from_jac(key) for key in jac_keys]
                        assert ec.aff_from_jac(QJ) in Qs
                        assert len(jac_keys) in (2, 4)
예제 #7
0
def test_jac_equality() -> None:

    ec = ec23_31
    assert ec.jac_equality(ec.GJ, jac_from_aff(ec.G))

    # q in [2, n-1], as the difference with ec.GJ is checked below
    q = 2 + secrets.randbelow(ec.n - 2)
    Q = mult_aff(q, ec.G, ec)
    QJ = _mult(q, ec.GJ, ec)
    assert ec.jac_equality(QJ, jac_from_aff(Q))
    assert not ec.jac_equality(QJ, ec.negate_jac(QJ))
    assert not ec.jac_equality(QJ, ec.GJ)
예제 #8
0
파일: dsa.py 프로젝트: btclib-org/btclib
def gen_keys(prv_key: Optional[PrvKey] = None,
             ec: Curve = secp256k1) -> Tuple[int, Point]:
    "Return a private/public (int, Point) key-pair."

    if prv_key is None:
        # q in the range [1, ec.n-1]
        q = 1 + secrets.randbelow(ec.n - 1)
    else:
        q = int_from_prv_key(prv_key, ec)

    QJ = _mult(q, ec.GJ, ec)
    Q = ec.aff_from_jac(QJ)
    return q, Q
예제 #9
0
파일: curve.py 프로젝트: btclib-org/btclib
def mult(m: Integer,
         Q: Optional[Point] = None,
         ec: Curve = secp256k1) -> Point:
    "Elliptic curve scalar multiplication."
    if Q is None:
        QJ = ec.GJ
    else:
        ec.require_on_curve(Q)
        QJ = jac_from_aff(Q)

    m = int_from_integer(m) % ec.n
    R = _mult(m, QJ, ec)
    return ec.aff_from_jac(R)
예제 #10
0
def gen_keys_(prv_key: Optional[PrvKey] = None,
              ec: Curve = secp256k1) -> Tuple[int, int, JacPoint]:
    "Return a BIP340 private/public (int, JacPoint) key-pair."

    if prv_key is None:
        q = 1 + secrets.randbelow(ec.n - 1)
    else:
        q = int_from_prv_key(prv_key, ec)

    QJ = _mult(q, ec.GJ, ec)
    x_Q, y_Q = ec.aff_from_jac(QJ)
    if y_Q % 2:
        q = ec.n - q
        QJ = ec.negate_jac(QJ)

    return q, x_Q, QJ
예제 #11
0
def test_assorted_jac_mult() -> None:
    ec = ec23_31
    H = second_generator(ec)
    HJ = jac_from_aff(H)
    for k1 in range(ec.n):
        K1J = _mult(k1, ec.GJ, ec)
        for k2 in range(ec.n):
            K2J = _mult(k2, HJ, ec)

            shamir = _double_mult(k1, ec.GJ, k2, ec.GJ, ec)
            assert ec.is_on_curve(ec.aff_from_jac(shamir))
            assert ec.jac_equality(shamir, _mult(k1 + k2, ec.GJ, ec))

            shamir = _double_mult(k1, INFJ, k2, HJ, ec)
            assert ec.is_on_curve(ec.aff_from_jac(shamir))
            assert ec.jac_equality(shamir, K2J)

            shamir = _double_mult(k1, ec.GJ, k2, INFJ, ec)
            assert ec.is_on_curve(ec.aff_from_jac(shamir))
            assert ec.jac_equality(shamir, K1J)

            shamir = _double_mult(k1, ec.GJ, k2, HJ, ec)
            assert ec.is_on_curve(ec.aff_from_jac(shamir))
            K1JK2J = ec.add_jac(K1J, K2J)
            assert ec.jac_equality(K1JK2J, shamir)

            k3 = 1 + secrets.randbelow(ec.n - 1)
            K3J = _mult(k3, ec.GJ, ec)
            K1JK2JK3J = ec.add_jac(K1JK2J, K3J)
            assert ec.is_on_curve(ec.aff_from_jac(K1JK2JK3J))
            boscoster = _multi_mult([k1, k2, k3], [ec.GJ, HJ, ec.GJ], ec)
            assert ec.is_on_curve(ec.aff_from_jac(boscoster))
            assert ec.aff_from_jac(K1JK2JK3J) == ec.aff_from_jac(boscoster), k3
            assert ec.jac_equality(K1JK2JK3J, boscoster)

            k4 = 1 + secrets.randbelow(ec.n - 1)
            K4J = _mult(k4, HJ, ec)
            K1JK2JK3JK4J = ec.add_jac(K1JK2JK3J, K4J)
            assert ec.is_on_curve(ec.aff_from_jac(K1JK2JK3JK4J))
            points = [ec.GJ, HJ, ec.GJ, HJ]
            boscoster = _multi_mult([k1, k2, k3, k4], points, ec)
            assert ec.is_on_curve(ec.aff_from_jac(boscoster))
            assert ec.aff_from_jac(K1JK2JK3JK4J) == ec.aff_from_jac(
                boscoster), k4
            assert ec.jac_equality(K1JK2JK3JK4J, boscoster)
            assert ec.jac_equality(K1JK2JK3J,
                                   _multi_mult([k1, k2, k3, 0], points, ec))
            assert ec.jac_equality(K1JK2J,
                                   _multi_mult([k1, k2, 0, 0], points, ec))
            assert ec.jac_equality(K1J, _multi_mult([k1, 0, 0, 0], points, ec))
            assert ec.jac_equality(INFJ, _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.GJ, HJ, ec.GJ], ec)

            err_msg = "negative coefficient: "
            with pytest.raises(BTClibValueError, match=err_msg):
                _multi_mult([k1, k2, -k3], [ec.GJ, HJ, ec.GJ], ec)

    with pytest.raises(BTClibValueError, match="negative first coefficient: "):
        _double_mult(-5, HJ, 1, ec.GJ, ec)
    with pytest.raises(BTClibValueError,
                       match="negative second coefficient: "):
        _double_mult(1, HJ, -5, ec.GJ, ec)
예제 #12
0
def assert_batch_as_valid_(
    m_hashes: Sequence[Octets],
    Qs: Sequence[BIP340PubKey],
    sigs: Sequence[Sig],
    hf: HashF = sha256,
) -> None:

    batch_size = len(Qs)
    if batch_size == 0:
        raise BTClibValueError("no signatures provided")

    if len(m_hashes) != batch_size:
        err_msg = f"mismatch between number of pub_keys ({batch_size}) "
        err_msg += f"and number of messages ({len(m_hashes)})"
        raise BTClibValueError(err_msg)
    if len(sigs) != batch_size:
        err_msg = f"mismatch between number of pub_keys ({batch_size}) "
        err_msg += f"and number of signatures ({len(sigs)})"
        raise BTClibValueError(err_msg)

    if batch_size == 1:
        assert_as_valid_(m_hashes[0], Qs[0], sigs[0], hf)
        return None

    ec = sigs[0].ec
    if any(sig.ec != ec for sig in sigs):
        raise BTClibValueError("not the same curve for all signatures")
    t = 0
    scalars: List[int] = []
    points: List[JacPoint] = []
    for i, (msg_hash, Q, sig) in enumerate(zip(m_hashes, Qs, sigs)):
        msg_hash = bytes_from_octets(msg_hash, hf().digest_size)

        KJ = sig.r, ec.y_even(sig.r), 1

        x_Q, y_Q = point_from_bip340pub_key(Q, ec)
        QJ = x_Q, y_Q, 1

        c = challenge_(msg_hash, x_Q, sig.r, ec, hf)

        # rand in [1, n-1]
        # deterministically generated using a CSPRNG seeded by a
        # cryptographic hash (e.g., SHA256) of all inputs of the
        # algorithm, or randomly generated independently for each
        # run of the batch verification algorithm
        rand = 1 if i == 0 else 1 + secrets.randbelow(ec.n - 1)
        scalars.append(rand)
        points.append(KJ)
        scalars.append(rand * c % ec.n)
        points.append(QJ)
        t += rand * sig.s

    TJ = _mult(t, ec.GJ, ec)
    RHSJ = _multi_mult(scalars, points, ec)

    # return T == RHS, checked in Jacobian coordinates
    RHSZ2 = RHSJ[2] * RHSJ[2]
    TZ2 = TJ[2] * TJ[2]
    if (TJ[0] * RHSZ2 % ec.p != RHSJ[0] * TZ2 % ec.p) or (
            TJ[1] * RHSZ2 * RHSJ[2] % ec.p != RHSJ[1] * TZ2 * TJ[2] % ec.p):
        raise BTClibRuntimeError("signature verification failed")
    return None
예제 #13
0
파일: curve.py 프로젝트: btclib-org/btclib
    def __init__(
        self,
        p: Integer,
        a: Integer,
        b: Integer,
        G: Point,
        n: Integer,
        cofactor: int,
        weakness_check: bool = True,
        name: Optional[str] = None,
    ) -> None:

        super().__init__(p, a, b, G)
        n = int_from_integer(n)

        # Security level is expressed in bits, where n-bit security
        # means that the attacker would have to perform 2^n operations
        # to break it. Security bits are half the key size for asymmetric
        # elliptic curve cryptography, i.e. half of the number of bits
        # required to express the group order n or, holding Hasse theorem,
        # to express the field prime p

        self.n = n
        self.nlen = n.bit_length()
        self.n_size = (self.nlen + 7) // 8

        # 5. Check that n is prime.
        if n < 2 or n % 2 == 0 or pow(2, n - 1, n) != 1:
            err_msg = "n is not prime: "
            err_msg += f"{hex_string(n)}" if n > HEX_THRESHOLD else f"{n}"
            raise BTClibValueError(err_msg)
        delta = int(2 * sqrt(self.p))
        # also check n with Hasse Theorem
        if cofactor < 2 and not self.p + 1 - delta <= n <= self.p + 1 + delta:
            err_msg = "n not in p+1-delta..p+1+delta: "
            err_msg += f"{hex_string(n)}" if n > HEX_THRESHOLD else f"{n}"
            raise BTClibValueError(err_msg)

        # 7. Check that G ≠ INF, nG = INF
        if self.G[1] == 0:
            err_msg = "INF point cannot be a generator"
            raise BTClibValueError(err_msg)
        jac_inf = _mult(n, self.GJ, self)
        if jac_inf[2] != 0:
            err_msg = "n is not the group order: "
            err_msg += f"{hex_string(n)}" if n > HEX_THRESHOLD else f"{n}"
            raise BTClibValueError(err_msg)

        # 6. Check cofactor
        exp_cofactor = int(1 / n + delta / n + self.p / n)
        if cofactor != exp_cofactor:
            err_msg = f"invalid cofactor: {cofactor}, expected {exp_cofactor}"
            raise BTClibValueError(err_msg)
        self.cofactor = cofactor

        # 8. Check that n ≠ p
        if n == p:
            raise BTClibValueError(
                f"n=p weak curve: {hex_string(n)}")  # pragma: no cover

        if weakness_check:
            # 8. Check that p^i % n ≠ 1 for all 1≤i<100
            for i in range(1, 100):
                if pow(self.p, i, n) == 1:
                    raise UserWarning("weak curve")

        self.name = name