def _pubkey_recovery(ec: Curve, c: int, sig: ECDS) -> Sequence[Point]: """Private function provided for testing purposes only.""" # ECDSA public key recovery operation according to SEC 1 # http://www.secg.org/sec1-v2.pdf # See SEC 1 v.2 section 4.1.6 r, s = _to_sig(ec, sig) # precomputations r1 = mod_inv(r, ec.n) r1s = r1 * s r1e = -r1 * c keys: Sequence[Point] = list() for j in range(ec.h): # 1 x = r + j * ec.n # 1.1 try: #TODO: check test reporting 1, 2, 3, or 4 keys x %= ec._p R = x, ec.y_odd(x, 1) # 1.2, 1.3, and 1.4 # skip 1.5: in this function, c is an input Q = double_mult(ec, r1s, R, r1e, ec.G) # 1.6.1 if Q[1] != 0 and _verhlp(ec, c, Q, sig): # 1.6.2 keys.append(Q) R = ec.opposite(R) # 1.6.3 Q = double_mult(ec, r1s, R, r1e, ec.G) if Q[1] != 0 and _verhlp(ec, c, Q, sig): # 1.6.2 keys.append(Q) # 1.6.2 except Exception: # R is not a curve point pass return keys
def point_from_octets(ec: Curve, o: octets) -> Point: """Return a tuple (Px, Py) that belongs to the curve SEC 1 v.2, section 2.3.4 """ if isinstance(o, str): o = bytes.fromhex(o) bsize = len(o) # bytes if bsize == 1 and o[0] == 0x00: # infinity point return Point() if bsize == ec.psize + 1: # compressed point if o[0] not in (0x02, 0x03): m = f"{ec.psize+1} bytes, but not a compressed point" raise ValueError(m) Px = int.from_bytes(o[1:], 'big') try: Py = ec.y_odd(Px, o[0] % 2) # also check Px validity return Point(Px, Py) except: raise ValueError("point not on curve") else: # uncompressed point if bsize != 2 * ec.psize + 1: m = f"wrong byte-size ({bsize}) for a point: it " m += f"should have be {ec.psize+1} or {2*ec.psize+1}" raise ValueError(m) if o[0] != 0x04: raise ValueError("not an uncompressed point") Px = int.from_bytes(o[1:ec.psize + 1], 'big') P = Point(Px, int.from_bytes(o[ec.psize + 1:], 'big')) if ec.is_on_curve(P): return P else: raise ValueError("point not on curve")
def second_generator(ec: Curve, hf) -> Point: """Nothing-Up-My-Sleeve (NUMS) second generator H wrt ec.G source: https://github.com/ElementsProject/secp256k1-zkp/blob/secp256k1-zkp/src/modules/rangeproof/main_impl.h idea: https://crypto.stackexchange.com/questions/25581/second-generator-for-secp256k1-curve Get the hash of G, then coerce it to a point (hx, hy). The resulting point could not be a curvepoint: in this case keep on incrementing hx until a valid curve point (hx, hy) is obtained. """ G_bytes = octets_from_point(ec, ec.G, False) hd = hf(G_bytes).digest() hx = int_from_bits(ec, hd) isCurvePoint = False while not isCurvePoint: try: hy = ec.y_odd(hx, False) isCurvePoint = True except: hx += 1 return Point(hx, hy)