def _recover_pub_keys_(c: int, r: int, s: int, lower_s: bool, ec: Curve) -> List[JacPoint]: # Private function provided for testing purposes only. # precomputations r_1 = mod_inv(r, ec.n) r1s = r_1 * s % ec.n r1e = -r_1 * c % ec.n keys: List[JacPoint] = [] # r = K[0] % ec.n # if ec.n < K[0] < ec.p (likely when cofactor ec.cofactor > 1) # then both x_K=r and x_K=r+ec.n must be tested for j in range(ec.cofactor + 1): # 1 # affine x_K-coordinate of K (field element) x_K = (r + j * ec.n) % ec.p # 1.1 # two possible y_K-coordinates, i.e. two possible keys for each cycle try: # even root first for bitcoin message signing compatibility yodd = ec.y_even(x_K) KJ = x_K, yodd, 1 # 1.2, 1.3, and 1.4 # 1.5 has been performed in the recover_pub_keys calling function QJ = _double_mult(r1s, KJ, r1e, ec.GJ, ec) # 1.6.1 try: _assert_as_valid_(c, QJ, r, s, lower_s, ec) # 1.6.2 except (BTClibValueError, BTClibRuntimeError): pass else: keys.append(QJ) # 1.6.2 KJ = x_K, ec.p - yodd, 1 # 1.6.3 QJ = _double_mult(r1s, KJ, r1e, ec.GJ, ec) try: _assert_as_valid_(c, QJ, r, s, lower_s, ec) # 1.6.2 except (BTClibValueError, BTClibRuntimeError): pass else: keys.append(QJ) # 1.6.2 except (BTClibValueError, BTClibRuntimeError): # K is not a curve point pass return keys
def mult_endomorphism_secp256k1(m: int, Q: JacPoint, ec: CurveGroup) -> JacPoint: "Scalar multiplication in Jacobian coordinates using efficient endomorphism." m1, m2 = multiplier_decomposer(m, ec) # Values for the efficient endomorphism multiplication # see D. Hankerson, 'Guide to Elliptic Curve Cryptography' chapter 3.5 # lam = 0x5363AD4CC05C30E0A5261C028812645A122E22EA20816678DF02967C1B23BD72 beta = 0x7AE96A2B657C07106E64479EAC3434E99CF0497512F58995C1396C28719501EE K = ((Q[0] * beta) % ec.p), Q[1], Q[2] # K = lambda*Q, direct calculation # FIXME: Change double mult (?) with alghoritm 3.77 return _double_mult(m1, Q, m2, K, ec)
def _recover_pub_key_(c: int, r: int, s: int, ec: Curve) -> int: # Private function provided for testing purposes only. if c == 0: raise BTClibRuntimeError("invalid zero challenge") KJ = r, ec.y_even(r), 1 e1 = mod_inv(c, ec.n) QJ = _double_mult(ec.n - e1, KJ, e1 * s, ec.GJ, ec) # edge case that cannot be reproduced in the test suite if QJ[2] == 0: err_msg = "invalid (INF) key" # pragma: no cover raise BTClibRuntimeError(err_msg) # pragma: no cover return ec.x_aff_from_jac(QJ)
def _assert_as_valid_(c: int, QJ: JacPoint, r: int, s: int, ec: Curve) -> None: # Private function for test/dev purposes # It raises Errors, while verify should always return True or False # Let K = sG - eQ. # in Jacobian coordinates KJ = _double_mult(ec.n - c, QJ, s, ec.GJ, ec) # Fail if infinite(KJ). # Fail if y_K is odd. if ec.y_aff_from_jac(KJ) % 2: raise BTClibRuntimeError("y_K is odd") # Fail if x_K ≠ r if KJ[0] != KJ[2] * KJ[2] * r % ec.p: raise BTClibRuntimeError("signature verification failed")
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 _recover_pub_key_(key_id: int, c: int, r: int, s: int, lower_s: bool, ec: Curve) -> JacPoint: # Private function provided for testing purposes only. # precomputations r_1 = mod_inv(r, ec.n) r1s = r_1 * s % ec.n r1e = -r_1 * c % ec.n # r = K[0] % ec.n # if ec.n < K[0] < ec.p (likely when cofactor ec.cofactor > 1) # then both x_K=r and x_K=r+ec.n must be tested j = key_id & 0b110 # allow for key_id in [0, 7] x_K = (r + j * ec.n) % ec.p # 1.1 # even root first for Bitcoin Core compatibility i = key_id & 0b01 y_even = ec.y_even(x_K) y_K = ec.p - y_even if i else y_even KJ = x_K, y_K, 1 # 1.2, 1.3, and 1.4 # 1.5 has been performed in the recover_pub_keys calling function QJ = _double_mult(r1s, KJ, r1e, ec.GJ, ec) # 1.6.1 _assert_as_valid_(c, QJ, r, s, lower_s, ec) # 1.6.2 return QJ
def _assert_as_valid_(c: int, QJ: JacPoint, r: int, s: int, lower_s: bool, ec: Curve) -> None: # Private function for test/dev purposes if lower_s and s > ec.n / 2: raise BTClibValueError("not a low s") w = mod_inv(s, ec.n) u = c * w % ec.n v = r * w % ec.n # 4 # Let K = u*G + v*Q. KJ = _double_mult(v, QJ, u, ec.GJ, ec) # 5 # Fail if infinite(K). # edge case that cannot be reproduced in the test suite if KJ[2] == 0: # 5 err_msg = "invalid (INF) key" # pragma: no cover raise BTClibRuntimeError(err_msg) # pragma: no cover # affine x_K-coordinate of K x_K = (KJ[0] * mod_inv(KJ[2] * KJ[2], ec.p)) % ec.p # Fail if r ≠ x_K %n. if r != x_K % ec.n: # 6, 7, 8 raise BTClibRuntimeError("signature verification failed")
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)