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}"
def test_mult() -> None: for ec in low_card_curves.values(): for q in range(ec.n): Q = _mult_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_aff(q, INF, ec), f"{q}, {ec}" assert ec._jac_equality(INFJ, _mult(q, INFJ, ec)), f"{q}, {ec}"
def test_low_cardinality() -> None: "test low-cardinality curves for all msg/key pairs." # 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"], ] # only low cardinality test curves or it would take forever for ec in test_curves: # BIP340 Schnorr only applies to curve whose prime p = 3 %4 if not ec.pIsThreeModFour: err_msg = "field prime is not equal to 3 mod 4: " with pytest.raises(ValueError, match=err_msg): ssa._sign(32 * b"\x00", 1, None, ec) continue for q in range(1, ec.n // 2): # all possible private keys QJ = _mult(q, ec.GJ, ec) # public key x_Q = ec._x_aff_from_jac(QJ) if not ec.has_square_y(QJ): q = ec.n - q QJ = ec.negate_jac(QJ) for k in range(1, ec.n // 2): # all possible ephemeral keys RJ = _mult(k, ec.GJ, ec) r = ec._x_aff_from_jac(RJ) if not ec.has_square_y(RJ): k = ec.n - k for e in range(ec.n): # all possible challenges s = (k + e * q) % ec.n sig = ssa.__sign(e, q, k, r, ec) assert (r, s) == sig # valid signature must validate ssa.__assert_as_valid(e, QJ, r, s, ec) # if e == 0 then the sig is valid for all {q, Q} # no public key can be recovered if e == 0: err_msg = "invalid zero challenge" with pytest.raises(ValueError, match=err_msg): ssa.__recover_pubkey(e, r, s, ec) else: assert x_Q == ssa.__recover_pubkey(e, r, s, 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))
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))
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_low_cardinality() -> None: """test low-cardinality curves for all msg/key pairs.""" # 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"], ] low_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 low_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, low_s, ec) continue sig = dsa.__sign(e, q, k, low_s, ec) assert (r, s) == sig # valid signature must pass verification dsa.__assert_as_valid(e, QJ, r, s, ec) jacobian_keys = dsa.__recover_pubkeys(e, r, s, ec) # FIXME speed this up Qs = [ec._aff_from_jac(key) for key in jacobian_keys] assert ec._aff_from_jac(QJ) in Qs assert len(jacobian_keys) in (2, 4)
def test_low_cardinality() -> None: "test low-cardinality curves for all msg/key pairs." # 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"], ] # only low cardinality test curves or it would take forever for ec in test_curves: for q in range(1, ec.n // 2): # all possible private keys QJ = _mult(q, ec.GJ, ec) # public key x_Q, y_Q = ec._aff_from_jac(QJ) if y_Q % 2: q = ec.n - q QJ = ec.negate_jac(QJ) for k in range(1, ec.n // 2): # all possible ephemeral keys RJ = _mult(k, ec.GJ, ec) r, ry = ec._aff_from_jac(RJ) if ry % 2: k = ec.n - k for e in range(ec.n): # all possible challenges s = (k + e * q) % ec.n sig = ssa.__sign(e, q, k, r, ec) assert (r, s) == sig # valid signature must validate ssa.__assert_as_valid(e, QJ, r, s, ec) # if e == 0 then the sig is valid for all {q, Q} # no public key can be recovered if e == 0: err_msg = "invalid zero challenge" with pytest.raises(BTClibValueError, match=err_msg): ssa.__recover_pubkey(e, r, s, ec) else: assert x_Q == ssa.__recover_pubkey(e, r, s, ec)
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)
_mult_w_NAF, ) # setup random.seed(42) qs = [random.getrandbits(ec.nlen) % ec.n for _ in range(300)] gen_only = True print("generator only") if gen_only else print("random points") cached_multiples.cache_clear() cached_multiples(ec.GJ, ec) T = ec.GJ start = time.time() for q in qs: T = _mult(q, ec.GJ, ec) if gen_only else _mult(q, T, ec) benchmark = time.time() - start print("Benchmark completed", cached_multiples.cache_info()) T = ec.GJ start = time.time() for q in qs: T = _mult_jac(q, ec.GJ, ec) if gen_only else _mult_jac(q, T, ec) double_and_add = time.time() - start print(f"Double & add : {double_and_add / benchmark:.0%}") T = ec.GJ start = time.time() for q in qs: T = _mult_mont_ladder(q, ec.GJ, ec) if gen_only else _mult_mont_ladder( q, T, ec)
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)
import time from btclib.curve import secp256k1 as ec from btclib.curvegroup import _double_mult, _mult random.seed(42) # setup us = [] vs = [] QJs = [] for _ in range(500): us.append(random.getrandbits(ec.nlen) % ec.n) vs.append(random.getrandbits(ec.nlen) % ec.n) q = random.getrandbits(ec.nlen) % ec.n QJs.append(_mult(q, ec.GJ, ec)) """ for u, v, QJ in zip(us, vs, QJs): t1 = ec._add_jac(_mult(u, ec.GJ, ec), _mult(v, QJ, ec)) t2 = _double_mult(u, ec.GJ, v, QJ, ec) assert ec._jac_equality(t1, t2) """ start = time.time() for u, v, QJ in zip(us, vs, QJs): ec._add_jac(_mult(u, ec.GJ, ec), _mult(v, QJ, ec)) elapsed1 = time.time() - start start = time.time() for u, v, QJ in zip(us, vs, QJs): _double_mult(u, ec.GJ, v, QJ, ec)