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)
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
def multi_mult(scalars: Sequence[Integer], points: Sequence[Point], ec: Curve = secp256k1) -> Point: """Return the multi scalar multiplication u1*Q1 + ... + un*Qn. Use Bos-Coster's algorithm for efficient computation. """ if len(scalars) != len(points): err_msg = "mismatch between number of scalars and points: " err_msg += f"{len(scalars)} vs {len(points)}" raise BTClibValueError(err_msg) jac_points: List[JacPoint] = [] ints: List[int] = [] for Q, i in zip(points, scalars): i = int_from_integer(i) % ec.n if i == 0: # early optimization, even if not strictly necessary continue ints.append(i) ec.require_on_curve(Q) jac_points.append(jac_from_aff(Q)) R = _multi_mult(ints, jac_points, ec) return ec.aff_from_jac(R)
def double_mult(u: Integer, H: Point, v: Integer, Q: Point, ec: Curve = secp256k1) -> Point: "Double scalar multiplication (u*H + v*Q)." ec.require_on_curve(H) HJ = jac_from_aff(H) ec.require_on_curve(Q) QJ = jac_from_aff(Q) u = int_from_integer(u) % ec.n v = int_from_integer(v) % ec.n R = _double_mult(u, HJ, v, QJ, ec) return ec.aff_from_jac(R)
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)
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)
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))
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)