def add_points( self, p1: EllipticCurvePoint, p2: EllipticCurvePoint, ) -> EllipticCurvePoint: if p1 == EllipticCurveIdentity: return p2 if p2 == EllipticCurveIdentity: return p1 if p1 == self.invert_point(p2): return EllipticCurveIdentity p = self.prime x1, y1 = p1 x2, y2 = p2 if p1 == p2: m = mod_divide(3 * x1 * x1 + self.a, 2 * y1, p) else: m = mod_divide(y2 - y1, x2 - x1, p) x3 = (m * m - x1 - x2) % p y3 = (m * (x1 - x3) - y1) % p return (x3, y3)
def ecdsa_verify(msg: str, sig: ECDSASignature, keypair: ECDHKeypair): curve = keypair.config.curve n = keypair.config.n u1 = mod_divide(hash_msg(msg), sig.s, n) u2 = mod_divide(sig.r, sig.s, n) R = curve.add_points(keypair.config.scalar_mult_point(u1), curve.scalar_mult(keypair.public, u2)) return R[0] == sig.r
def add_points( self, p1: EllipticCurvePoint, p2: EllipticCurvePoint, ) -> EllipticCurvePoint: # https://www.hyperelliptic.org/EFD/g1p/auto-montgom.html x1, y1 = p1 x2, y2 = p2 a, b, p = self.a, self.b, self.prime x3 = b * pow(y2 - y1, 2, p) * pow(x2 - x1, p - 1 - 2, p) - a - x1 - x2 y3_first = mod_divide((2 * x1 + x2 + a) * (y2 - y1), x2 - x1, p) y3_second = mod_divide(b * (y2 - y1)**3, (x2 - x1)**3, p) return mod_point((x3, y3_first - y3_second - y1), p)
def ecdsa_sign(msg: str, keypair: ECDHKeypair): n = keypair.config.n k = keypair.config.rand_scalar() r = keypair.config.scalar_mult_point(k)[0] s = mod_divide(hash_msg(msg) + keypair.secret * r, k, n) return ECDSASignature(r, s)
def ecdsa_sign_biased(msg: str, keypair: ECDHKeypair): n = keypair.config.n k = keypair.config.rand_scalar() k = (k >> 8) << 8 # Mask off the last 8 bits as suggested r = keypair.config.scalar_mult_point(k)[0] s = mod_divide(hash_msg(msg) + keypair.secret * r, k, n) return ECDSASignature(r, s)
def montgomery_point_test(curve: MontgomeryCurve, u: int) -> bool: p = curve.prime a = curve.a b = curve.b rhs = (u**3 + a * (u**2) + u) % p v = mod_divide(rhs, b, p) return kronecker_symbol(v, p) == 1
def test_lll_against_biased_nonce_attack(): dh = create_dh() chars = string.ascii_lowercase + string.digits alice_keypair = dh.generate_keypair() num_signatures = 30 message_length = 40 l = 8 # Number of biased bits q = dh.config.n attacker_pairs: List[Tuple[int, int]] = [] for _ in range(num_signatures): msg = ''.join(random.choices(chars, k=message_length)) sig = ecdsa_sign_biased(msg, alice_keypair) r, s = sig.r, sig.s t = mod_divide(r, s * 2**l, q) u = mod_divide(hash_msg(msg), (-s * 2**l), q) attacker_pairs.append((u, t)) ct = Fraction(1, 2**l) cu = Fraction(q, 2**l) dim = len(attacker_pairs) + 2 basis = [] for i in range(len(attacker_pairs)): vec = [0] * dim vec[i] = q basis.append(vec) basis.append(list(map(itemgetter(1), attacker_pairs)) + [ct, 0]) basis.append(list(map(itemgetter(0), attacker_pairs)) + [0, cu]) reduction = lll_reduction(basis) candidate_rows = [v for v in reduction if v[dim - 1] == cu] candidate_keys = [-1 * v[dim - 2] * 2**l for v in candidate_rows] assert alice_keypair.secret in candidate_keys, \ f'Should have seen {alice_keypair.secret} in {candidate_keys}'
def montgomery_find_points( curve: MontgomeryCurve, u: int, ) -> List[Tuple[int, int]]: p = curve.prime a = curve.a b = curve.b rhs = (u**3 + a * (u**2) + u) % p if rhs == 0: return [(0, 0)] v = mod_sqrt(mod_divide(rhs, b, p), p) if p - v < v: return [(u, p - v), (u, v)] return [(u, v), (u, p - v)]