def _verhlp(ec: Curve, c: int, P: Point, sig: ECDS) -> bool: # Private function for test/dev purposes # Fail if r is not [1, n-1] # Fail if s is not [1, n-1] r, s = _to_sig(ec, sig) # 1 # Let P = point(pk); fail if point(pk) fails. ec.require_on_curve(P) if P[1] == 0: raise ValueError("public key is infinite") w = mod_inv(s, ec.n) u = c * w v = r * w # 4 # Let R = u*G + v*P. RJ = _double_mult(ec, u, ec.GJ, v, (P[0], P[1], 1)) # 5 # Fail if infinite(R). assert RJ[2] != 0, "how did you do that?!?" # 5 Rx = (RJ[0] * mod_inv(RJ[2] * RJ[2], ec._p)) % ec._p x = Rx % ec.n # 6, 7 # Fail if r ≠ x(R) %n. return r == x # 8
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 _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 octets_from_point(ec: Curve, Q: Point, compressed: bool) -> bytes: """Return a compressed (0x02, 0x03) or uncompressed (0x04) point as octets SEC 1 v.2, section 2.3.3 """ # check that Q is a point and that is on curve ec.require_on_curve(Q) if Q[1] == 0: # infinity point in affine coordinates return b'\x00' bPx = Q[0].to_bytes(ec.psize, byteorder='big') if compressed: return (b'\x03' if (Q[1] & 1) else b'\x02') + bPx return b'\x04' + bPx + Q[1].to_bytes(ec.psize, byteorder='big')
def _verify(ec: Curve, hf: Callable[[Any], Any], mhd: bytes, P: Point, sig: ECSS) -> bool: # This raises Exceptions, while verify should always return True or False # 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) # Let r = int(sig[ 0:32]). # Let s = int(sig[32:64]); fail if s is not [0, n-1]. r, s = _to_sig(ec, sig) # The message mhd: a 32-byte array _ensure_msg_size(hf, mhd) # Let P = point(pk); fail if point(pk) fails. ec.require_on_curve(P) if P[1] == 0: raise ValueError("public key is infinite") # Let e = int(hf(bytes(r) || bytes(P) || mhd)) mod n. e = _e(ec, hf, r, P, mhd) # Let R = sG - eP. # in Jacobian coordinates R = _double_mult(ec, s, ec.GJ, -e, (P[0], P[1], 1)) # Fail if infinite(R). if R[2] == 0: raise ValueError("sG - eP is infinite") # Fail if jacobi(R.y) ≠ 1. if legendre_symbol(R[1] * R[2] % ec._p, ec._p) != 1: raise ValueError("(sG - eP).y is not a quadratic residue") # Fail if R.x ≠ r. return R[0] == (R[2] * R[2] * r % ec._p)
def _verhlp(ec: Curve, e: int, P: Point, sig: ECDS) -> bool: """Private function provided for testing purposes only.""" # Fail if r is not [1, n-1] # Fail if s is not [1, n-1] r, s = _to_sig(ec, sig) # 1 # Let P = point(pk); fail if point(pk) fails. ec.require_on_curve(P) if P[1] == 0: raise ValueError("public key is infinite") s1 = mod_inv(s, ec.n) u1 = e*s1 u2 = r*s1 # 4 # Let R = u*G + v*P. RJ = _double_mult(ec, u1, ec.GJ, u2, (P[0], P[1], 1)) # 5 # Fail if infinite(R). assert RJ[2] != 0, "how did you do that?!?" # 5 Rx = (RJ[0]*mod_inv(RJ[2]*RJ[2], ec._p)) % ec._p v = Rx % ec.n # 6, 7 # Fail if r ≠ x(R) %n. return r == v # 8