def test_basic(self): self.assertFalse(gmpy.is_prime(1)) self.assertTrue(gmpy.is_prime(2)) self.assertTrue(gmpy.is_prime(101)) self.assertFalse(gmpy.is_prime(561)) self.assertTrue(gmpy.is_prime(2**16 + 1)) self.assertFalse(gmpy.is_prime(41041)) self.assertEqual(gmpy.next_prime(1), 2) self.assertEqual(gmpy.next_prime(2), 3) self.assertEqual(gmpy.next_prime(256), 257) self.assertEqual(gmpy.powmod(3, 256, 257), 1) self.assertEqual(gmpy.invert(3, 257), 86) self.assertRaises(ZeroDivisionError, gmpy.invert, 2, 4) self.assertEqual(gmpy.legendre(0, 101), 0) self.assertEqual(gmpy.legendre(42, 101), -1) self.assertEqual(gmpy.legendre(54, 101), 1) self.assertTrue(gmpy.is_square(625)) self.assertFalse(gmpy.is_square(652)) self.assertEqual(gmpy.isqrt(0), 0) self.assertEqual(gmpy.isqrt(1225), 35) self.assertTrue(gmpy.iroot(0, 10)[1]) self.assertFalse(gmpy.iroot(1226, 2)[1]) self.assertEqual(gmpy.iroot(1226, 2)[0], 35) self.assertEqual(gmpy.iroot(3**10 + 42, 10)[0], 3)
def encode(cls, m): """Encode message m in a quadratic residue.""" gap = cls.gap field = cls.field modulus = field.modulus for i in range(1, gap): if legendre(i, modulus) == 1: a = m * gap + i if legendre(a, modulus) == 1: M = cls(field(a), check=False) Z = cls(field(i), check=False) return M, Z raise ValueError('message encoding failed, try larger gap')
async def bsgn_1(a): """Compute binary sign of a securely. Binary sign of a (1 if a>=0 else -1) is obtained by securely computing (u+v+w - u*v*w)/2 with u=(2a-1 | p), v=(2a+1 | p), and w=(2a+3 | p). """ stype = type(a) await mpc.returnType(stype) Zp = stype.field p = Zp.modulus legendre_p = lambda a: gmpy2.legendre(a.value, p) s = mpc.random_bits(Zp, 3, signed=True) # 3 random signs r = mpc._randoms(Zp, 3) r = mpc.schur_prod(r, r) # 3 random squares modulo p a, s, r = await mpc.gather(a, s, r) y = [b + 2 * i for b in (2 * a + 1, ) for i in (-1, 0, 1)] y.append(s[0]) s.append(s[1]) r.append(s[2]) y = await mpc.schur_prod(y, s) y = await mpc.schur_prod(y, r) y = await mpc.output(y) h = [legendre_p(y[i]) for i in range(3)] u, v, w = [s[i] * h[i] for i in range(3)] uvw = h[0] * h[1] * h[2] * y[3] return (u + v + w - uvw) / 2
async def vector_bsgn_1(x): """Compute bsgn_1(a) for all elements a of x in parallel.""" stype = type(x[0]) n = len(x) await mpc.returnType(stype, n) Zp = stype.field p = Zp.modulus legendre_p = lambda a: gmpy2.legendre(a.value, p) s = mpc.random_bits(Zp, 3 * n, signed=True) # 3n random signs r = mpc._randoms(Zp, 3 * n) r = mpc.schur_prod(r, r) # 3n random squares modulo p x, s, r = await mpc.gather(x, s, r) y = [b + 2 * i for b in (2 * a + 1 for a in x) for i in (-1, 0, 1)] y.extend(s[:n]) s.extend(s[n:2 * n]) r.extend(s[-n:]) y = await mpc.schur_prod(y, s) y = await mpc.schur_prod(y, r) y = await mpc.output(y) h = [legendre_p(y[j]) for j in range(3 * n)] t = [s[j] * h[j] for j in range(3 * n)] z = [ h[3 * j] * h[3 * j + 1] * h[3 * j + 2] * y[3 * n + j] for j in range(n) ] q = (p + 1) >> 1 # q = 1/2 mod p return [ Zp((u.value + v.value + w.value - uvw.value) * q) for u, v, w, uvw in zip(*[iter(t)] * 3, z) ]
def sqrt(self, INV=False): """Modular (inverse) square roots.""" a = self.value p = type(self).modulus if p == 2: return type(self)(a) if p & 3 == 3: if INV: q = (3 * p - 5) // 4 # a**q == a**(-1/2) == 1/sqrt(a) mod p else: q = (p + 1) // 4 return type(self)(int(gmpy2.powmod(a, q, p))) # 1 (mod 4) primes are covered using Cipolla-Lehmer's algorithm. # find b s.t. b^2 - 4*a is not a square (maybe cache this for p) b = 1 while gmpy2.legendre(b * b - 4 * a, p) != -1: b += 1 # compute u*X + v = X^{(p+1)/2} mod f, for f = X^2 - b*X + a u, v = 0, 1 e = (p + 1) // 2 for i in range(e.bit_length() - 1, -1, -1): u2 = (u * u) % p u = ((u << 1) * v + b * u2) % p v = (v * v - a * u2) % p if (e >> i) & 1: u, v = (v + b * u) % p, (-a * u) % p if INV: return type(self)(v)._reciprocal() else: return type(self)(v)
def is_sqr(self): """Test for quadratic residuosity (0 is also square).""" p = type(self).modulus if p == 2: return True else: return gmpy2.legendre(self.value, p) != -1
def sqrt(self, INV=False): a = self.value p = self.modulus if p == 2: return type(self)(a) if p & 3 == 3: if INV: p4 = (p * 3 - 5) >> 2 # a**p4 == a**(-1/2) == 1/sqrt(a) mod p else: p4 = (p + 1) >> 2 return type(self)(int(gmpy2.powmod(a, p4, p))) # 1 (mod 4) primes are covered using Cipolla-Lehmer's algorithm. # find b s.t. b^2 - 4*a is not a square b = 1 while gmpy2.legendre(b * b - 4 * a, p) != -1: b += 1 # compute u*X + v = X^{(p+1)/2} mod f, for f = X^2 - b*X + a u, v = 0, 1 e = (p + 1) >> 1 for i in range(e.bit_length() - 1, -1, -1): u2 = (u * u) % p u = ((u << 1) * v + b * u2) % p v = (v * v - a * u2) % p if (e >> i) & 1: u, v = (v + b * u) % p, (-a * u) % p if INV: return type(self)(v).reciprocal() return type(self)(v)
def encode(cls, m): """Encode message m in x-coordinate of a point on the curve.""" field = cls.field # TODO: extend this to non-prime fields gap = cls.gap modulus = field.modulus for i in range(gap): x_0 = field(i) ysquared_0 = cls.ysquared(x_0) if legendre(int(ysquared_0), modulus) == 1: x_m = field(m * gap + i) ysquared_m = cls.ysquared(x_m) if legendre(int(ysquared_m), modulus) == 1: M = cls((x_m, ysquared_m.sqrt()), check=False) Z = cls((x_0, ysquared_0.sqrt()), check=False) return M, Z raise ValueError('message encoding failed, try larger gap')
def _QuadraticResidues(p): field = GF(p) # raises if p is not prime g = 2 while legendre(g, p) != 1: g += 1 # g is generator if p is a safe prime l = p.bit_length() name = f'QR{l}({p})' QR = type(name, (QuadraticResidue,), {'__slots__': ()}) QR.field = field QR.gap = 128 # TODO: calculate gap as a function of bit length of p QR.order = p >> 1 QR.identity = QR() QR.generator = QR(g) globals()[name] = QR # NB: exploit (almost?) unique name dynamic QR type return QR
async def vector_bsgn_0(x): """Compute bsgn_0(a) for all elements a of x in parallel.""" stype = type(x[0]) n = len(x) await mpc.returnType(stype, n) Zp = stype.field p = Zp.modulus legendre_p = lambda a: gmpy2.legendre(a.value, p) s = mpc.random_bits(Zp, n, signed=True) # n random signs r = mpc._randoms(Zp, n) r = mpc.schur_prod(r, r) # n random squares modulo p x, s, r = await mpc.gather(x, s, r) y = [2 * a + 1 for a in x] y = await mpc.schur_prod(y, s) y = await mpc.schur_prod(y, r) y = await mpc.output(y) return [s[j] * legendre_p(y[j]) for j in range(n)]
async def vector_bsgn_2(x): """Compute bsgn_2(a) for all elements a of x in parallel.""" stype = type(x[0]) n = len(x) await mpc.returnType(stype, n) Zp = stype.field p = Zp.modulus legendre_p = lambda a: gmpy2.legendre(a.value, p) s = mpc.random_bits(Zp, 6*n, signed=True) # 6n random signs r = mpc._randoms(Zp, 6*n) r = mpc.schur_prod(r, r) # 6n random squares modulo p x, s, r = await mpc.gather(x, s, r) y = [b + 2 * i for b in (2 * a + 1 for a in x) for i in (-2, -1, 0, 1, 2)] y = await mpc.schur_prod(y, s[:-n]) y.extend(s[-n:]) y = await mpc.schur_prod(y, r) y = await mpc.output(y) t = [sum(s[5*j + i] * legendre_p(y[5*j + i]) for i in range(5)) for j in range(n)] t = await mpc.output(await mpc.schur_prod(t, y[-n:])) return [c * legendre_p(d) for c, d in zip(s[-n:], t)]
async def bsgn_0(a): """Compute binary sign of a securely. Binary sign of a (1 if a>=0 else -1) is obtained by securely computing (2a+1 | p). Legendre symbols (a | p) for secret a are computed securely by evaluating (a s r^2 | p) in the clear for secret random sign s and secret random r modulo p, and outputting secret s * (a s r^2 | p). """ stype = type(a) await mpc.returnType(stype) Zp = stype.field p = Zp.modulus legendre_p = lambda a: gmpy2.legendre(a.value, p) s = mpc.random_bits(Zp, 1, signed=True) # random sign r = mpc._random(Zp) r = mpc.prod([r, r]) # random square modulo p a, s, r = await mpc.gather(a, s, r) b = await mpc.prod([2 * a + 1, s[0], r]) b = await mpc.output(b) return s[0] * legendre_p(b)
def _sqrt(cls, a, INV=False): p = cls.modulus if a == 0: if INV: raise ZeroDivisionError('no inverse sqrt of 0') return a if p == 2: return a if p & 3 == 3: if INV: p4 = (p * 3 - 5) >> 2 # a**p4 == a**(-1/2) == 1/sqrt(a) mod p else: p4 = (p + 1) >> 2 return int(gmpy2.powmod(a, p4, p)) # 1 (mod 4) primes are covered using Cipolla-Lehmer's algorithm. # find b s.t. b^2 - 4*a is not a square b = 1 while gmpy2.legendre(b * b - 4 * a, p) != -1: b += 1 # compute u*X + v = X^{(p+1)/2} mod f, for f = X^2 - b*X + a u, v = 0, 1 e = (p + 1) >> 1 for i in range(e.bit_length() - 1, -1, -1): u2 = (u * u) % p u = ((u << 1) * v + b * u2) % p v = (v * v - a * u2) % p if (e >> i) & 1: u, v = (v + b * u) % p, (-a * u) % p if INV: v = cls._reciprocal(v) return v
async def bsgn_2(a): """Compute binary sign of a securely. Binary sign of a (1 if a>=0 else -1) is obtained by securely computing (t | p), with t = sum((2a+1+2i | p) for i=-2,-1,0,1,2). """ stype = type(a) await mpc.returnType(stype) Zp = stype.field p = Zp.modulus legendre_p = lambda a: gmpy2.legendre(a.value, p) s = mpc.random_bits(Zp, 6, signed=True) # 6 random signs r = mpc._randoms(Zp, 6) r = mpc.schur_prod(r, r) # 6 random squares modulo p a, s, r = await mpc.gather(a, s, r) y = [b + 2 * i for b in (2 * a + 1, ) for i in (-2, -1, 0, 1, 2)] y = await mpc.schur_prod(y, s[:-1]) y.append(s[-1]) y = await mpc.schur_prod(y, r) y = await mpc.output(y) t = sum(s[i] * legendre_p(y[i]) for i in range(5)) t = await mpc.output(t * y[-1]) return s[-1] * legendre_p(t)
def _is_sqr(cls, a): p = cls.modulus if p == 2: return True return gmpy2.legendre(a, p) != -1
def test_basic(self): self.assertFalse(gmpy.is_prime(1)) self.assertTrue(gmpy.is_prime(2)) self.assertTrue(gmpy.is_prime(101)) self.assertFalse(gmpy.is_prime(561)) self.assertTrue(gmpy.is_prime(2**16 + 1)) self.assertFalse(gmpy.is_prime(41041)) self.assertEqual(gmpy.next_prime(1), 2) self.assertEqual(gmpy.next_prime(2), 3) self.assertEqual(gmpy.next_prime(256), 257) self.assertEqual(gmpy.powmod(3, 256, 257), 1) self.assertEqual(gmpy.gcdext(3, 257), (1, 86, -1)) self.assertEqual(gmpy.gcdext(1234, 257), (1, -126, 605)) self.assertEqual(gmpy.gcdext(-1234 * 3, -257 * 3), (3, 126, -605)) def te_s_t(a, b): g, s, t = gmpy.gcdext(a, b) if abs(a) == abs(b): test_s = s == 0 elif b == 0 or abs(b) == 2 * g: test_s = s == bool(a > 0) - bool(a < 0) # sign of a else: test_s = abs(s) < abs(b) / (2 * g) if abs(a) == abs(b) or a == 0 or abs(a) == 2 * g: test_t = t == bool(b > 0) - bool(b < 0) # sign of b else: test_t = abs(t) < abs(a) / (2 * g) return g == a * s + b * t and test_s and test_t self.assertTrue( all((te_s_t(0, 0), te_s_t(0, -1), te_s_t(1, 0), te_s_t(-1, 1)))) self.assertTrue(te_s_t(-1234, 257)) self.assertTrue(te_s_t(-12537, -257)) self.assertTrue(te_s_t(-11 * 1234, -11 * 2567)) self.assertTrue(te_s_t(1234, -2 * 1234)) self.assertTrue(te_s_t(-2 * 12364, 12364)) # self.assertEqual(gmpy.invert(3, -1), 0) # pending gmpy2 issue if modulus is 1 or -1 self.assertEqual(gmpy.invert(3, 257), 86) self.assertRaises(ZeroDivisionError, gmpy.invert, 2, 0) self.assertRaises(ZeroDivisionError, gmpy.invert, 2, 4) self.assertEqual(gmpy.legendre(0, 101), 0) self.assertEqual(gmpy.legendre(42, 101), -1) self.assertEqual(gmpy.legendre(54, 101), 1) self.assertRaises(ValueError, gmpy.legendre, 1, 2) self.assertEqual(gmpy.jacobi(9, 99), 0) self.assertEqual(gmpy.jacobi(43, 99), -1) self.assertEqual(gmpy.jacobi(53, 99), 1) self.assertRaises(ValueError, gmpy.jacobi, 1, 20) self.assertEqual(gmpy.kronecker(0, 0), 0) self.assertEqual(gmpy.kronecker(-1, 0), 1) self.assertEqual(gmpy.kronecker(43, -98), -1) self.assertEqual(gmpy.kronecker(-53, -98), 1) self.assertEqual(gmpy.kronecker(-54, -98), 0) self.assertTrue(gmpy.is_square(625)) self.assertFalse(gmpy.is_square(652)) self.assertEqual(gmpy.isqrt(0), 0) self.assertEqual(gmpy.isqrt(1225), 35) self.assertTrue(gmpy.iroot(0, 10)[1]) self.assertFalse(gmpy.iroot(1226, 2)[1]) self.assertEqual(gmpy.iroot(1226, 2)[0], 35) self.assertEqual(gmpy.iroot(3**10 + 42, 10)[0], 3)
def is_sqr(self): p = self.modulus if p == 2: return True return gmpy2.legendre(self.value, p) != -1