def test_add(self): for ec in all_curves.values(): Q1 = mult(ec.p, ec.G, ec) # just a random point, not INF Q1J = _jac_from_aff(Q1) # distinct points Q3 = ec._add_aff(Q1, ec.G) Q3jac = ec._add_jac(Q1J, ec.GJ) self.assertEqual(Q3, ec._aff_from_jac(Q3jac)) # point at infinity Q3 = ec._add_aff(ec.G, INF) Q3jac = ec._add_jac(ec.GJ, INFJ) self.assertEqual(Q3, ec._aff_from_jac(Q3jac)) Q3 = ec._add_aff(INF, ec.G) Q3jac = ec._add_jac(INFJ, ec.GJ) self.assertEqual(Q3, ec._aff_from_jac(Q3jac)) # point doubling Q3 = ec._add_aff(Q1, Q1) Q3jac = ec._add_jac(Q1J, Q1J) self.assertEqual(Q3, ec._aff_from_jac(Q3jac)) # negate points Q1opp = ec.negate(Q1) Q3 = ec._add_aff(Q1, Q1opp) Q3jac = ec._add_jac(Q1J, _jac_from_aff(Q1opp)) self.assertEqual(Q3, ec._aff_from_jac(Q3jac))
def test_add(self): for ec in all_curves: Q1 = mult(ec, ec._p, ec.G) # just a random point, not Inf Q1J = _jac_from_aff(Q1) # distinct points Q3 = ec._add_aff(Q1, ec.G) Q3jac = ec._add_jac(Q1J, ec.GJ) self.assertEqual(Q3, ec._aff_from_jac(Q3jac)) # point at infinity Q3 = ec._add_aff(ec.G, Inf) Q3jac = ec._add_jac(ec.GJ, InfJ) self.assertEqual(Q3, ec._aff_from_jac(Q3jac)) Q3 = ec._add_aff(Inf, ec.G) Q3jac = ec._add_jac(InfJ, ec.GJ) self.assertEqual(Q3, ec._aff_from_jac(Q3jac)) # point doubling Q3 = ec._add_aff(Q1, Q1) Q3jac = ec._add_jac(Q1J, Q1J) self.assertEqual(Q3, ec._aff_from_jac(Q3jac)) # opposite points Q1opp = ec.opposite(Q1) Q3 = ec._add_aff(Q1, Q1opp) Q3jac = ec._add_jac(Q1J, _jac_from_aff(Q1opp)) self.assertEqual(Q3, ec._aff_from_jac(Q3jac))
def _batch_verify(ec: Curve, hf, ms: List[bytes], P: List[Point], sig: List[ECSS]) -> bool: t = 0 scalars: List(int) = list() points: List[Point] = list() for i in range(len(P)): _ensure_msg_size(hf, ms[i]) ec.require_on_curve(P[i]) r, s = _to_sig(ec, sig[i]) e = _e(ec, hf, r, P[i], ms[i]) y = ec.y(r) # raises an error if y does not exist # 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 FIXME a = (1 if i == 0 else random.getrandbits(ec.nlen) % ec.n) scalars.append(a) points.append(_jac_from_aff((r, y))) scalars.append(a * e % ec.n) points.append(_jac_from_aff(P[i])) t += a * s % ec.n TJ = _mult_jac(ec, t, ec.GJ) RHSJ = _multi_mult(ec, scalars, points) # 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: return False return (TJ[1] * RHSZ2 * RHSJ[2]) % ec._p == (RHSJ[1] * TZ2 * TJ[2]) % ec._p
def test_aff_jac_conversions(self): for ec in all_curves: Q = mult(ec, ec._p, ec.G) # random point checkQ = ec._aff_from_jac(_jac_from_aff(Q)) self.assertEqual(Q, checkQ) # with only the last curve checkInf = ec._aff_from_jac(_jac_from_aff(Inf)) self.assertEqual(Inf, checkInf)
def _batch_verify(ec: Curve, hf: Callable[[Any], Any], ms: Sequence[bytes], P: Sequence[Point], sig: Sequence[ECSS]) -> bool: # the bitcoin proposed standard is only valid for curves # whose prime p = 3 % 4 if not ec.pIsThreeModFour: errmsg = 'curve prime p must be equal to 3 (mod 4)' raise ValueError(errmsg) batch_size = len(P) if len(ms) != batch_size: errMsg = f"mismatch between number of pubkeys ({batch_size}) " errMsg += f"and number of messages ({len(ms)})" raise ValueError(errMsg) if len(sig) != batch_size: errMsg = f"mismatch between number of pubkeys ({batch_size}) " errMsg += f"and number of signatures ({len(sig)})" raise ValueError(errMsg) if batch_size == 1: return _verify(ec, hf, ms[0], P[0], sig[0]) t = 0 scalars: Sequence(int) = list() points: Sequence[Point] = list() for i in range(batch_size): r, s = _to_sig(ec, sig[i]) _ensure_msg_size(hf, ms[i]) ec.require_on_curve(P[i]) e = _e(ec, hf, r, P[i], ms[i]) # raises an error if y does not exist # no need to check for quadratic residue y = ec.y(r) # a 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 a = (1 if i == 0 else (1 + random.getrandbits(ec.nlen)) % ec.n) scalars.append(a) points.append(_jac_from_aff((r, y))) scalars.append(a * e % ec.n) points.append(_jac_from_aff(P[i])) t += a * s TJ = _mult_jac(ec, t, ec.GJ) RHSJ = _multi_mult(ec, scalars, points) # 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: return False return (TJ[1] * RHSZ2 * RHSJ[2]) % ec._p == (RHSJ[1] * TZ2 * TJ[2]) % ec._p
def test_jac(): ec = Curve(13, 0, 2, (1, 9), 19, 1, False) assert ec._jac_equality(ec.GJ, _jac_from_aff(ec.G)) # q in [2, n-1] q = 2 + secrets.randbelow(ec.n - 2) Q = _mult_aff(q, ec.G, ec) QJ = _mult_jac(q, ec.GJ, ec) assert ec._jac_equality(QJ, _jac_from_aff(Q)) assert not ec._jac_equality(QJ, ec.negate(QJ)) assert not ec._jac_equality(QJ, ec.GJ)
def test_opposite(self): for ec in all_curves: Q = mult(ec, ec._p, ec.G) # just a random point, not Inf minus_Q = ec.opposite(Q) self.assertEqual(ec.add(Q, minus_Q), Inf) # jacobian coordinates Qjac = _jac_from_aff(Q) minus_Qjac = _jac_from_aff(minus_Q) self.assertEqual(ec._add_jac(Qjac, minus_Qjac)[2], 0) # opposite of Inf is Inf minus_Inf = ec.opposite(Inf) self.assertEqual(minus_Inf, Inf)
def test_opposite(self): for ec in all_curves.values(): Q = mult(ec.p, ec.G, ec) # just a random point, not INF minus_Q = ec.negate(Q) self.assertEqual(ec.add(Q, minus_Q), INF) # jacobian coordinates Qjac = _jac_from_aff(Q) minus_Qjac = _jac_from_aff(minus_Q) self.assertEqual(ec._add_jac(Qjac, minus_Qjac)[2], 0) # negate of INF is INF minus_Inf = ec.negate(INF) self.assertEqual(minus_Inf, INF)
def test_aff_jac_conversions(self): for ec in all_curves.values(): Q = mult(ec.p, ec.G, ec) # just a random point, not INF QJ = _jac_from_aff(Q) checkQ = ec._aff_from_jac(QJ) self.assertEqual(Q, checkQ) x = ec._x_aff_from_jac(QJ) self.assertEqual(Q[0], x) checkInf = ec._aff_from_jac(_jac_from_aff(INF)) self.assertEqual(INF, checkInf) # relevant for BIP340-Schnorr signature verification self.assertFalse(ec.has_square_y(INF)) self.assertRaises(ValueError, ec._x_aff_from_jac, INFJ) self.assertRaises(ValueError, ec.has_square_y, "Not a Point")
def test_aff_jac_conversions(): for ec in all_curves.values(): # just a random point, not INF q = 1 + secrets.randbelow(ec.n - 1) Q = _mult_aff(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 assert INF == ec._aff_from_jac(_jac_from_aff(INF)) # relevant for BIP340-Schnorr signature verification assert not ec.has_square_y(INF) with pytest.raises(ValueError, match="infinity point has no x-coordinate"): ec._x_aff_from_jac(INFJ) with pytest.raises(TypeError, match="not a point"): ec.has_square_y("notapoint")
def test_assorted_mult(): ec = ec23_31 H = second_generator(ec) HJ = _jac_from_aff(H) for k1 in range(ec.n): K1J = _mult_jac(k1, ec.GJ, ec) for k2 in range(ec.n): K2J = _mult_jac(k2, HJ, ec) shamir = _double_mult(k1, ec.GJ, k2, ec.GJ, ec) assert ec._jac_equality(shamir, _mult_jac(k1 + k2, ec.GJ, ec)) shamir = _double_mult(k1, INFJ, k2, HJ, ec) assert ec._jac_equality(shamir, K2J) shamir = _double_mult(k1, ec.GJ, k2, INFJ, ec) assert ec._jac_equality(shamir, K1J) shamir = _double_mult(k1, ec.GJ, k2, HJ, ec) K1JK2J = ec._add_jac(K1J, K2J) assert ec._jac_equality(K1JK2J, shamir) k3 = 1 + secrets.randbelow(ec.n - 1) K3J = _mult_jac(k3, ec.GJ, ec) K1JK2JK3J = ec._add_jac(K1JK2J, K3J) boscoster = _multi_mult([k1, k2, k3], [ec.GJ, HJ, ec.GJ], ec) assert ec._jac_equality(K1JK2JK3J, boscoster) k4 = 1 + secrets.randbelow(ec.n - 1) K4J = _mult_jac(k4, HJ, ec) K1JK2JK3JK4J = ec._add_jac(K1JK2JK3J, K4J) points = [ec.GJ, HJ, ec.GJ, HJ] boscoster = _multi_mult([k1, k2, k3, k4], points, ec) 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(ValueError, match=err_msg): _multi_mult([k1, k2, k3, k4], [ec.GJ, HJ, ec.GJ], ec) with pytest.raises(ValueError, match="negative first coefficient: "): _double_mult(-5, HJ, 1, ec.GJ, ec) with pytest.raises(ValueError, match="negative second coefficient: "): _double_mult(1, HJ, -5, ec.GJ, ec)
def test_add(): for ec in all_curves.values(): # just a random point, not INF q = 1 + secrets.randbelow(ec.n - 1) Q = _mult_aff(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._add_aff(Q, Q) RJ = ec._add_jac(QJ, QJ) assert R == ec._aff_from_jac(RJ)
def test_negate(): for ec in all_curves.values(): # just a random point, not INF q = 1 + secrets.randbelow(ec.n - 1) Q = _mult_aff(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(QJ) assert 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(INFJ) assert minus_INFJ == INFJ with pytest.raises(TypeError, match="not a point"): ec.negate("notapoint")
# No part of btclib including this file, may be copied, modified, propagated, # or distributed except according to the terms contained in the LICENSE file. import random import time from btclib.curve import _jac_from_aff from btclib.curvemult import _mult_jac from btclib.curves import secp256k1 as ec random.seed(42) # setup qs = [] for _ in range(50): qs.append(random.getrandbits(ec.nlen) % ec.n) start = time.time() for q in qs: ec._mult_aff(q, ec.G) elapsed1 = time.time() - start start = time.time() for q in qs: # starts from affine coordinates, ends with affine coordinates GJ = _jac_from_aff(ec.G) ec._aff_from_jac(_mult_jac(q, GJ)) elapsed2 = time.time() - start print(elapsed2 / elapsed1)