def test_minus_one_quadr_res() -> None: "Ensure that if p = 3 (mod 4) then p - 1 is not a quadratic residue" for p in primes: if (p % 4) == 3: with pytest.raises(ValueError, match="no root for "): mod_sqrt(p - 1, p) else: assert p == 2 or p % 4 == 1, "something is badly broken" root = mod_sqrt(p - 1, p) assert p - 1 == root * root % p
def y(self, x: int, odd1even0: int) -> int: assert odd1even0 in (0, 1), "must be bool or 0/1" y2 = self.__y2(x) # if root does not exist, mod_sqrt will raise a ValueError root = mod_sqrt(y2, self.__prime) # switch even/odd root when needed return root if (root % 2 + odd1even0) != 1 else self.__prime - root
def test_mod_sqrt(self): for p in primes[:30]: # exhaustable only for small p hasRoot = set() hasRoot.add(0) hasRoot.add(1) for i in range(2, p): hasRoot.add(i * i % p) for i in range(p): if i in hasRoot: root = mod_sqrt(i, p) self.assertEqual(i, (root * root) % p) root = p - root self.assertEqual(i, (root * root) % p) root = mod_sqrt(i + p, p) self.assertEqual(i, (root * root) % p) else: self.assertRaises(ValueError, mod_sqrt, i, p)
def yLow(self, x: int, low1high0: int) -> int: assert low1high0 in (0, 1), "must be bool or 0/1" y2 = self.__y2(x) if y2 == 0: return 0 # if root does not exist, mod_sqrt will raise a ValueError root = mod_sqrt(y2, self.__p) # switch low/high root when needed return root if (root < self.__p / 2) else self.__p - root
def test_minus_one_quadr_res(self): """Ensure that if p = 3 (mod 4) then p - 1 is not a quadratic residue""" for p in primes: if (p % 4) == 3: self.assertRaises(ValueError, mod_sqrt, p - 1, p) else: assert p == 2 or p % 4 == 1, "something is badly broken" root = mod_sqrt(p - 1, p) self.assertEqual(p - 1, root * root % p)
def test_mod_sqrt() -> None: for p in primes[:30]: # exhaustable only for small p has_root = {0, 1} for i in range(2, p): has_root.add(i * i % p) for i in range(p): if i in has_root: root1 = mod_sqrt(i, p) assert i == (root1 * root1) % p root2 = p - root1 assert i == (root2 * root2) % p root = mod_sqrt(i + p, p) assert i == (root * root) % p if p % 4 == 3 or p % 8 == 5: assert tonelli(i, p) in (root1, root2) else: with pytest.raises(ValueError, match="no root for "): mod_sqrt(i, p)
def yQuadraticResidue(self, x: int, quadres: int) -> int: assert quadres in (0, 1), "must be bool or 0/1" y2 = self.__y2(x) if y2 == 0: return 0 # if root does not exist, mod_sqrt will raise a ValueError root = mod_sqrt(y2, self.__p) # switch to the quadratic residue root when needed if quadres: return self.__p - root if (self.jacobi(root) != 1) else root else: return root if (self.jacobi(root) != 1) else self.__p - root
def test_mod_sqrt(self): for p in [3, 5, 7, 11, 13, 17, 19, 23, 29]: hasRoot = set() hasRoot.add(1) for i in range(2, p): hasRoot.add(i * i % p) for i in range(1, p): if i in hasRoot: root = mod_sqrt(i, p) self.assertEqual(i, (root * root) % p) root = p - root self.assertEqual(i, (root * root) % p) else: self.assertRaises(ValueError, mod_sqrt, i, p)
def test_minus_one_quadr_res(self): for p in [3, 5, 7, 11, 13, 17, 19, 23, 29]: hasRoot = set() hasRoot.add(1) for i in range(2, p): hasRoot.add(i * i % p) if (p % 4) == 3: self.assertNotIn(p - 1, hasRoot) self.assertRaises(ValueError, mod_sqrt, p - 1, p) else: assert p % 4 == 1 self.assertIn(p - 1, hasRoot) root = mod_sqrt(p - 1, p) self.assertEqual(p - 1, root * root % p)
def test_symmetry(self): """Methods to break simmetry: quadratic residue, odd/even, low/high""" for ec in low_card_curves: # setup phase # compute quadratic residues hasRoot = set() hasRoot.add(1) for i in range(2, ec._p): hasRoot.add(i*i % ec._p) # test phase Q = mult(ec, ec._p, ec.G) # just a random point, not Inf x = Q[0] if ec._p % 4 == 3: quad_res = ec.y_quadratic_residue(x, True) not_quad_res = ec.y_quadratic_residue(x, False) # in this case only quad_res is a quadratic residue self.assertIn(quad_res, hasRoot) root = mod_sqrt(quad_res, ec._p) self.assertEqual(quad_res, (root*root) % ec._p) root = ec._p - root self.assertEqual(quad_res, (root*root) % ec._p) self.assertTrue(not_quad_res == ec._p - quad_res) self.assertNotIn(not_quad_res, hasRoot) self.assertRaises(ValueError, mod_sqrt, not_quad_res, ec._p) y_odd = ec.y_odd(x, True) self.assertTrue(y_odd in (quad_res, not_quad_res)) self.assertTrue(y_odd % 2 == 1) y_even = ec.y_odd(x, False) self.assertTrue(y_even in (quad_res, not_quad_res)) self.assertTrue(y_even % 2 == 0) y_low = ec.y_low(x, True) self.assertTrue(y_low in (y_odd, y_even)) y_high = ec.y_low(x, False) self.assertTrue(y_high in (y_odd, y_even)) self.assertTrue(y_low < y_high) else: self.assertTrue(ec._p % 4 == 1) # cannot use y_quadratic_residue in this case self.assertRaises(ValueError, ec.y_quadratic_residue, x, True) self.assertRaises(ValueError, ec.y_quadratic_residue, x, False) y_odd = ec.y_odd(x, True) self.assertTrue(y_odd % 2 == 1) y_even = ec.y_odd(x, False) self.assertTrue(y_even % 2 == 0) # in this case neither or both are quadratic residues self.assertTrue((y_odd in hasRoot and y_even in hasRoot) or (y_odd not in hasRoot and y_even not in hasRoot)) if y_odd in hasRoot: # both have roots root = mod_sqrt(y_odd, ec._p) self.assertEqual(y_odd, (root*root) % ec._p) root = ec._p - root self.assertEqual(y_odd, (root*root) % ec._p) root = mod_sqrt(y_even, ec._p) self.assertEqual(y_even, (root*root) % ec._p) root = ec._p - root self.assertEqual(y_even, (root*root) % ec._p) else: self.assertRaises(ValueError, mod_sqrt, y_odd, ec._p) self.assertRaises(ValueError, mod_sqrt, y_even, ec._p) y_low = ec.y_low(x, True) self.assertTrue(y_low in (y_odd, y_even)) y_high = ec.y_low(x, False) self.assertTrue(y_high in (y_odd, y_even)) self.assertTrue(y_low < y_high) # with the last curve self.assertRaises(ValueError, ec.y_low, x, 2) self.assertRaises(ValueError, ec.y_odd, x, 2) self.assertRaises(ValueError, ec.y_quadratic_residue, x, 2)
def test_symmetry() -> None: """Methods to break simmetry: quadratic residue, odd/even, low/high""" for ec in low_card_curves.values(): # just a random point, not INF q = 1 + secrets.randbelow(ec.n - 1) Q = mult(q, ec.G, ec) x_Q = Q[0] y_odd = ec.y_odd(x_Q) assert y_odd % 2 == 1 y_even = ec.y_odd(x_Q, False) assert y_even % 2 == 0 assert y_even == ec.p - y_odd y_low = ec.y_low(x_Q) y_high = ec.y_low(x_Q, False) assert y_low < y_high assert y_high == ec.p - y_low # compute quadratic residues hasRoot = {1} for i in range(2, ec.p): hasRoot.add(i * i % ec.p) if ec.p % 4 == 3: quad_res = ec.y_quadratic_residue(x_Q) not_quad_res = ec.y_quadratic_residue(x_Q, False) # in this case only quad_res is a quadratic residue assert quad_res in hasRoot root = mod_sqrt(quad_res, ec.p) assert quad_res == (root * root) % ec.p root = ec.p - root assert quad_res == (root * root) % ec.p assert not_quad_res == ec.p - quad_res assert not_quad_res not in hasRoot with pytest.raises(ValueError, match="no root for "): mod_sqrt(not_quad_res, ec.p) else: assert ec.p % 4 == 1 # cannot use y_quadratic_residue in this case err_msg = "field prime is not equal to 3 mod 4: " with pytest.raises(ValueError, match=err_msg): ec.y_quadratic_residue(x_Q) with pytest.raises(ValueError, match=err_msg): ec.y_quadratic_residue(x_Q, False) # in this case neither or both y_Q are quadratic residues neither = y_odd not in hasRoot and y_even not in hasRoot both = y_odd in hasRoot and y_even in hasRoot assert neither or both if y_odd in hasRoot: # both have roots root = mod_sqrt(y_odd, ec.p) assert y_odd == (root * root) % ec.p root = ec.p - root assert y_odd == (root * root) % ec.p root = mod_sqrt(y_even, ec.p) assert y_even == (root * root) % ec.p root = ec.p - root assert y_even == (root * root) % ec.p else: err_msg = "no root for " with pytest.raises(ValueError, match=err_msg): mod_sqrt(y_odd, ec.p) with pytest.raises(ValueError, match=err_msg): mod_sqrt(y_even, ec.p) # with the last curve with pytest.raises(ValueError, match="low1high0 must be bool or 1/0"): ec.y_low(x_Q, 2) with pytest.raises(ValueError, match="odd1even0 must be bool or 1/0"): ec.y_odd(x_Q, 2) with pytest.raises(ValueError, match="quad_res must be bool or 1/0"): ec.y_quadratic_residue(x_Q, 2)
def test_symmetry() -> None: "Methods to break simmetry: quadratic residue, even/odd, low/high." for ec in low_card_curves.values(): # just a random point, not INF q = 1 + secrets.randbelow(ec.n - 1) Q = mult(q, ec.G, ec) x_Q = Q[0] assert not ec.y_even(x_Q) % 2 assert ec.y_low(x_Q) <= ec.p // 2 # compute quadratic residues hasRoot = {1} for i in range(2, ec.p): hasRoot.add(i * i % ec.p) if ec.p % 4 == 3: quad_res = ec.y_quadratic_residue(x_Q) # in this case only quad_res is a quadratic residue assert quad_res in hasRoot root = mod_sqrt(quad_res, ec.p) assert quad_res == (root * root) % ec.p root = ec.p - root assert quad_res == (root * root) % ec.p assert ec.p - quad_res not in hasRoot with pytest.raises(BTClibValueError, match="no root for "): mod_sqrt(ec.p - quad_res, ec.p) else: assert ec.p % 4 == 1 # cannot use y_quadratic_residue in this case err_msg = "field prime is not equal to 3 mod 4: " with pytest.raises(BTClibValueError, match=err_msg): ec.y_quadratic_residue(x_Q) y_even = ec.y_even(x_Q) y_odd = ec.p - y_even # in this case neither or both y_Q are quadratic residues neither = y_odd not in hasRoot and y_even not in hasRoot both = y_odd in hasRoot and y_even in hasRoot assert neither or both if y_odd in hasRoot: # both have roots root = mod_sqrt(y_odd, ec.p) assert y_odd == (root * root) % ec.p root = ec.p - root assert y_odd == (root * root) % ec.p root = mod_sqrt(y_even, ec.p) assert y_even == (root * root) % ec.p root = ec.p - root assert y_even == (root * root) % ec.p else: err_msg = "no root for " with pytest.raises(BTClibValueError, match=err_msg): mod_sqrt(y_odd, ec.p) with pytest.raises(BTClibValueError, match=err_msg): mod_sqrt(y_even, ec.p) with pytest.raises(BTClibValueError): secp256k1.y_even(INF[0]) with pytest.raises(BTClibValueError): secp256k1.y_low(INF[0]) with pytest.raises(BTClibValueError): secp256k1.y_quadratic_residue(INF[0])
def y(self, x: int) -> int: if not 0 <= x < self._p: raise ValueError(f"x-coordinate {hex(x)} not in [0, p-1]") y2 = self._y2(x) # mod_sqrt will raise a ValueError if root does not exist return mod_sqrt(y2, self._p)
maxordera = -1 maxorderb = -1 maxorderlessthanprime = 0 maxorderlessthanprimea = -1 maxorderlessthanprimeb = -1 for a in range(200): for b in range(200): order = 0 for x in range(prime): y2 = ((x * x + a) * x + b) % prime if y2 == 0: order += 1 # print("#", order+1, " ", x, ", ", 0, " #####", sep="") continue try: y = mod_sqrt(y2, prime) assert (y * y) % prime == y2 # print("#", order+1, " ", x, ",", y, sep="") # print("#", order+2, " ", x, ",", prime-y, sep="") order += 2 except Exception: continue order += 1 if isprime(order): # print(a, b, prime, "gen", order) if order > maxorder: maxorder = order maxordera = a maxorderb = b if order > maxorderlessthanprime and order < prime: maxorderlessthanprime = order