def find_prime_root(l, blum=True, n=1): """Find smallest prime of bit length at least l satisfying given constraints. Default is to return Blum primes (primes p with p % 4 == 3). Also, a primitive root w is returned of prime order at least n (0 < w < p). """ if l <= 2: if not blum: p = 2 assert n == 1 w = 1 else: p = 3 n, w = 2, p - 1 elif n <= 2: p = gmpy2.next_prime(1 << l - 1) if blum: while p % 4 != 3: p = gmpy2.next_prime(p) p = int(p) w = p - 1 if n == 2 else 1 else: assert blum if not gmpy2.is_prime(n): n = gmpy2.next_prime(n) p = 1 + 2 * n * (3 + 2 * ((1 << l - 3) // n)) while not gmpy2.is_prime(p): p += 4 * n a = 2 while (w := gmpy2.powmod(a, (p - 1) // n, p)) == 1: a += 1 p, w = int(p), int(w)
def find_prime_root(l, blum=True, n=1): """Find smallest prime of bit length l satisfying given constraints. Default is to return Blum primes (primes p with p % 4 == 3). Also, a primitive root w is returned of prime order at least n. """ if l == 1: assert not blum assert n == 1 p = 2 w = 1 elif n <= 2: n = 2 w = -1 p = gmpy2.next_prime(2**(l - 1)) if blum: while p % 4 != 3: p = gmpy2.next_prime(p) p = int(p) else: assert blum if not gmpy2.is_prime(n): n = int(gmpy2.next_prime(n)) p = 1 + n * (1 + (n**2) % 4 + 4 * ((2**(l - 2)) // n)) while not gmpy2.is_prime(p): p += 4 * n a = 1 w = 1 while w == 1: a += 1 w = gmpy2.powmod(a, (p - 1) // n, p) p, w = int(p), int(w) return p, n, w
def SchnorrGroup(p=None, q=None, g=None, l=None, n=None): """Create type for Schnorr group of odd prime order q. If q is not given, q will be the smallest n-bit prime, n>=2. If p is not given, p will be the least l-bit prime, l>n, such that q divides p-1. If l and/or n are not given, default bit lengths will be set (2<=n<l). """ n_l = ((160, 1024), (192, 1536), (224, 2048), (256, 3072), (384, 7680)) if p is None: if q is None: if n is None: if l is None: l = 2048 n = next((n for n, _ in n_l if _ >= l), 512) q = next_prime(1 << n-1) else: if n is None: n = q.bit_length() assert q%2 and is_prime(q) if l is None: l = next((l for _, l in n_l if _ >= n), 15360) # n-bit prime q w = (1 << l-2) // q + 1 # w*q >= 2^(l-2), so p = 2*w*q + 1 > 2^(l-1) p = 2*w*q + 1 while not is_prime(p): p += 2*q # 2^(l-1) < p < 2^l else: assert q is not None # if p is given, q must be given as well assert (p - 1) % q == 0 assert q%2 and is_prime(q) assert is_prime(p) if l is None: l = p.bit_length() if n is None: n = q.bit_length() assert l == p.bit_length() assert n == q.bit_length() p = int(p) q = int(q) if g is None: w = (p-1) // q i = 2 while True: g = powmod(i, w, p) if g != 1 and powmod(g, q, p) == 1: break i += 1 g = int(g) return _SchnorrGroup(p, q, g)
def GF(modulus, f=0): """Create a Galois (finite) field for given prime modulus.""" if isinstance(modulus, tuple): p, n, w = modulus else: p = modulus if p == 2: n, w = 1, 1 else: n, w = 2, -1 if (p, f) in _field_cache: return _field_cache[(p, f)] if not gmpy2.is_prime(p): raise ValueError(f'{p} is not a prime') GFElement = type(f'GF({p})', (PrimeFieldElement, ), {'__slots__': ()}) GFElement.modulus = p GFElement.order = p GFElement.is_signed = True GFElement.nth = n GFElement.root = w % p GFElement.frac_length = f GFElement.rshift_factor = int(gmpy2.invert(1 << f, p)) # cache (1/2)^f mod p _field_cache[(p, f)] = GFElement return GFElement
def ClassGroup(Delta=None, l=None): """Create type for class group, given (bit length l of) discriminant Delta. The following conditions are imposed on discriminant Delta: - Delta < 0, only supporting class groups of imaginary quadratic field - Delta = 1 (mod 4), preferably Delta = 1 (mod 8) - -Delta is prime This implies that Delta is a fundamental discriminant. """ if l is not None: if Delta is None: # find fundamental discriminant Delta of bit length l >= 2 p = next_prime(1 << l-1) while p != 3 and p != 11 and p%8 != 7: p = next_prime(p) Delta = int(-p) # D = 1 mod 4, and even D = 1 mod 8 if possible (and -D is prime) elif Delta is None: Delta = -3 if Delta%4 != 1: raise ValueError('discriminant required to be 1 modulo 4, preferably 1 modulo 8') if Delta >= 0 or not is_prime(-Delta): raise ValueError('negative prime discriminant required') return _ClassGroup(Delta)
def pGF(modulus, f=0): """Create a finite field for given prime modulus.""" if isinstance(modulus, tuple): p, n, w = modulus else: p = modulus if p == 2: n, w = 1, 1 else: n, w = 2, -1 if not gmpy2.is_prime(p): raise ValueError('modulus is not a prime') GFElement = type(f'GF({p})', (PrimeFieldElement, ), {'__slots__': ()}) GFElement.modulus = p GFElement.order = p GFElement.characteristic = p GFElement.ext_deg = 1 GFElement.byte_length = (GFElement.order.bit_length() + 7) >> 3 GFElement.frac_length = f GFElement.rshift_factor = int(gmpy2.invert(1 << f, p)) # cache (1/2)^f mod p GFElement.is_signed = True GFElement.nth = n GFElement.root = w % p return GFElement
def GF(modulus, f=0): """Create a Galois (finite) field for given prime modulus.""" if isinstance(modulus, tuple): p, n, w = modulus else: p = modulus if p == 2: n, w = 1, 1 else: n, w = 2, -1 if (p, f) in _field_cache: return _field_cache[(p, f)] if not gmpy2.is_prime(p): raise ValueError("%d is not a prime" % p) GFElement = type('GF(' + str(p) + ')', (PrimeFieldElement, ), {'__slots__': ()}) GFElement.modulus = p GFElement.is_signed = True GFElement.nth = n GFElement.root = w % p GFElement.frac_length = f _field_cache[(p, f)] = GFElement return GFElement
def GFpX(p): """Create type for polynomials over GF(p).""" if not gmpy2.is_prime(p): raise ValueError('number is not prime') BasePolynomial = BinaryPolynomial if p == 2 else Polynomial GFpPolynomial = type(f'GF({p})[{X}]', (BasePolynomial,), {'__slots__': ()}) GFpPolynomial.p = p return GFpPolynomial
def GFpX(p): """Create type for polynomials over GF(p).""" if not gmpy2.is_prime(p): raise ValueError('number is not prime') BasePolynomial = BinaryPolynomial if p == 2 else Polynomial GFpPolynomial = type(f'GF({p})[{X}]', (BasePolynomial,), {'__slots__': ()}) GFpPolynomial.p = p globals()[f'GF({p})[{X}]'] = GFpPolynomial # NB: exploit unique name dynamic Polynomial type return GFpPolynomial
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 SecFld(order=None, modulus=None, char2=None, l=None): """Secure prime or binary field of (l+1)-bit order. Field is prime by default, and if order (or modulus) is prime. Field is binary if order is a power of 2, if modulus is a polynomial, or if char2 is True. """ if isinstance(modulus, str): modulus = gf2x.Polynomial(modulus) if isinstance(modulus, gf2x.Polynomial): char2 = char2 or (char2 is None) assert char2 # binary field modulus = int(modulus) if order is not None: if order == 2: assert modulus is None or modulus == 2 or modulus == 3 if modulus is None or modulus == 2: # default: prime field char2 = char2 or False else: char2 = char2 or (char2 is None) assert char2 # binary field elif gmpy.is_prime(order): modulus = modulus or order assert modulus == order char2 = char2 or False assert not char2 # prime field elif order % 2 == 0: assert modulus is None or modulus.bit_length() == order.bit_length( ) char2 = char2 or (char2 is None) assert char2 # binary field else: raise ValueError('only prime fields and binary fields supported') l = l or order.bit_length() - 1 assert l == order.bit_length() - 1 if modulus is None: l = l or 1 if char2: modulus = int(bfield.find_irreducible(l)) else: modulus = pfield.find_prime_root(l + 1, blum=False)[0] l = modulus.bit_length() - 1 if char2: field = bfield.GF(modulus) else: field = pfield.GF(modulus) assert runtime.threshold == 0 or field.order > len(runtime.parties), \ 'Field order must exceed number of parties, unless threshold is 0.' # field.order >= number of parties for MDS field.is_signed = False return _SecFld(l, field)
def pGF(p, n, w): """Create a finite field for given prime modulus p.""" if not gmpy2.is_prime(p): raise ValueError('modulus is not a prime') GFp = type(f'GF({p})', (PrimeFieldElement, ), {'__slots__': ()}) GFp.__doc__ = 'Class of prime field elements.' GFp.modulus = p GFp.order = p GFp.characteristic = p GFp.ext_deg = 1 GFp.byte_length = (GFp.order.bit_length() + 7) >> 3 GFp.is_signed = True GFp.nth = n GFp.root = w % p return GFp
def _find_safe_prime(l): """Find safe prime p of bit length l, l>=2. Hence, q=(p-1)/2 is also prime (except when l=2 and p=3). It is also ensured that p=3 (mod 4), hence p is a Blum prime. """ IKE_options_l_k = { 768: 149686, 1024: 129093, 1536: 741804, 2048: 124476, 3072: 1690314, 4096: 240904, 6144: 929484, 8192: 4743158 } if l in IKE_options_l_k: # Compute pi to the required precision. decimal.setcontext(decimal.Context(prec=round(l / math.log2(10)))) # See https://docs.python.org/3/library/decimal.html for following recipe: decimal.getcontext().prec += 2 # extra digits for intermediate steps three = decimal.Decimal(3) lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24 while s != lasts: lasts = s n, na = n + na, na + 8 d, da = d + da, da + 32 t = (t * n) / d s += t decimal.getcontext().prec -= 2 pi_l = +s # NB: unary plus applies the new precision # Following https://kivinen.iki.fi/primes to compute IKE prime p: k = IKE_options_l_k[l] fixedbits = 64 epi = math.floor(pi_l * 2**(l - 2 * fixedbits - 2)) + k p = 2**l - 2**(l - fixedbits) - 1 + epi * 2**fixedbits else: if l == 2: p = 3 else: q = next_prime(1 << l - 2) while not is_prime(2 * q + 1): q = next_prime(q) # q is a Sophie Germain prime p = int(2 * q + 1) return p
async def keygen(g): """Threshold ElGamal key generation.""" group = type(g) secgrp = mpc.SecGrp(group) n = group.order if n is not None and is_prime(n): secnum = mpc.SecFld(n) else: l = isqrt(-group.discriminant).bit_length() secnum = mpc.SecInt(l) while True: x = mpc._random(secnum) h = await secgrp.repeat_public(g, x) # g^x if h != group.identity: # NB: this branch will always be followed unless n is artificially small return x, h
def pGF(p, f, n, w): """Create a finite field for given prime modulus p.""" if not gmpy2.is_prime(p): raise ValueError('modulus is not a prime') GFtype = type(f'GF({p})', (PrimeFieldElement, ), {'__slots__': ()}) GFtype.__doc__ = 'Class of prime field elements.' GFtype.modulus = p GFtype.order = p GFtype.characteristic = p GFtype.ext_deg = 1 GFtype.byte_length = (GFtype.order.bit_length() + 7) >> 3 GFtype._frac_length = f GFtype._rshift_factor = int(gmpy2.invert(1 << f, p)) # cache (1/2)^f mod p GFtype.is_signed = True GFtype.nth = n GFtype.root = w % p return GFtype
def SecFld(order=None, modulus=None, char2=None, l=None): """Secure prime or binary field of (l+1)-bit order. Field is prime by default, and if order (or modulus) is prime. Field is binary if order is a power of 2, if modulus is a polynomial, or if char2 is True. """ if isinstance(modulus, str): modulus = gf2x.Polynomial(modulus) if isinstance(modulus, gf2x.Polynomial): char2 = char2 or (char2 is None) assert char2 # binary field modulus = int(modulus) if order is not None: if order == 2: assert modulus is None or modulus == 2 or modulus == 3 if modulus is None or modulus == 2: # default: prime field char2 = char2 or False else: char2 = char2 or (char2 is None) assert char2 # binary field elif gmpy.is_prime(order): modulus = modulus or order assert modulus == order char2 = char2 or False assert not char2 # prime field elif order % 2 == 0: assert modulus is None or modulus.bit_length() == order.bit_length( ) char2 = char2 or (char2 is None) assert char2 # binary field else: raise ValueError('only prime fields and binary fields supported') l = l or order.bit_length() - 1 assert l == order.bit_length() - 1 if modulus is None: l = l or 1 if char2: modulus = int(bfield.find_irreducible(l)) else: modulus = pfield.find_prime_root(l + 1, blum=False)[0] l = modulus.bit_length() - 1 if char2: field = bfield.GF(modulus) else: field = pfield.GF(modulus) assert runtime.threshold == 0 or field.order > len(runtime.parties), \ 'Field order must exceed number of parties, unless threshold is 0.' # field.order >= number of parties for MDS field.is_signed = False if (modulus, char2) not in _sectypes: class SecureFld(Share): __slots__ = () def __init__(self, value=None): super().__init__(field, value) SecureFld.field = field SecureFld.bit_length = l name = f'SecFld{SecureFld.bit_length}({SecureFld.field.modulus})' _sectypes[(modulus, char2)] = type(name, (SecureFld, ), {'__slots__': ()}) return _sectypes[(modulus, char2)]
def _EllipticCurve(curvename, coordinates): if curvename.startswith('Ed'): if curvename == 'Ed25519': p = 2**255 - 19 gf = GF(p) elif curvename == 'Ed448': p = 2**448 - 2**224 - 1 gf = GF(p) else: raise ValueError('invalid curvename') name = f'E({gf.__name__}){curvename}{coordinates}' if coordinates == 'extended': base = EdwardsExtended elif coordinates == 'affine': base = EdwardsAffine elif coordinates == 'projective': base = EdwardsProjective else: raise ValueError('invalid coordinates') EC = type(name, (base,), {'__slots__': ()}) EC.field = gf if curvename == 'Ed25519': EC.a = gf(-1) # twisted EC.d = gf(-121665) / gf(121666) y = gf(4) / gf(5) x2 = (1 - y**2) / (EC.a - EC.d * y**2) x = x2.sqrt() x = x if x.value%2 == 0 else -x # enforce "positive" (even) x coordinate base_pt = (x, y) EC.order = 2**252 + 27742317777372353535851937790883648493 else: # 'Ed448' EC.a = gf(1) EC.d = gf(-39081) y = gf(19) x2 = (1 - y**2) / (EC.a - EC.d * y**2) x = x2.sqrt() x = x if 2*x.value < p else -x # enforce principal root base_pt = (x, y) EC.order = 2**446 - int('8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d', 16) elif curvename.startswith('BN'): u = 1868033**3 p = 36*u**4 + 36*u**3 + 24*u**2 + 6*u + 1 # p = 3 (mod 4) if curvename == 'BN256': gf = GF(p) elif curvename == 'BN256_twist': gf = GF(GFpX(p)('x^2+1')) # i^2 == -1 else: raise ValueError('invalid curvename') name = f'E({gf.__name__}){curvename}{coordinates}' if coordinates == 'jacobian': base = WeierstrassJacobian elif coordinates == 'affine': base = WeierstrassAffine elif coordinates == 'projective': base = WeierstrassProjective else: raise ValueError('invalid coordinates') EC = type(name, (base,), {'__slots__': ()}) EC.field = gf if curvename == 'BN256': EC.a = gf(0) EC.b = gf(3) base_pt = (gf(1), gf(-2)) else: # 'BN256_twist' EC.a = gf('0') xi = gf('x+3') EC.b = gf('3') / xi x = gf([64746500191241794695844075326670126197795977525365406531717464316923369116492, 21167961636542580255011770066570541300993051739349375019639421053990175267184]) y = gf([17778617556404439934652658462602675281523610326338642107814333856843981424549, 20666913350058776956210519119118544732556678129809273996262322366050359951122]) base_pt = (x, y) EC.order = p - 6*u**2 else: raise ValueError('curve not supported') assert is_prime(EC.order) EC.curvename = curvename EC.field.is_signed = False # for consistency between sectypes and regular types EC.is_cyclic = True # these EC groups are cyclic (but not all EC groups are) EC.gap = 256 # TODO: optimize gap value EC.identity = EC(check=False) EC.generator = EC(base_pt, check=False) globals()[name] = EC # NB: exploit (almost?) unique name dynamic EC type return EC
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)