def ncusps(self): r""" Return the number of orbits of cusps (regular or otherwise) for this subgroup. EXAMPLES:: sage: GammaH(33,[2]).ncusps() 8 sage: GammaH(32079, [21676]).ncusps() 28800 AUTHORS: - Jordi Quer """ N = self.level() H = self._list_of_elements_in_H() c = ZZ(0) for d in [d for d in N.divisors() if d**2 <= N]: Nd = lcm(d, N // d) Hd = set([x % Nd for x in H]) lenHd = len(Hd) if Nd - 1 not in Hd: lenHd *= 2 summand = euler_phi(d) * euler_phi(N // d) // lenHd if d**2 == N: c = c + summand else: c = c + 2 * summand return c
def ncusps(self): r""" Return the number of orbits of cusps (regular or otherwise) for this subgroup. EXAMPLE:: sage: GammaH(33,[2]).ncusps() 8 sage: GammaH(32079, [21676]).ncusps() 28800 AUTHORS: - Jordi Quer """ N = self.level() H = self._list_of_elements_in_H() c = ZZ(0) for d in [d for d in N.divisors() if d**2 <= N]: Nd = lcm(d,N//d) Hd = set([x % Nd for x in H]) lenHd = len(Hd) if Nd-1 not in Hd: lenHd *= 2 summand = euler_phi(d)*euler_phi(N//d)//lenHd if d**2 == N: c = c + summand else: c = c + 2*summand return c
def eisenstein_series_at_inf(phi, psi, k, prec=10, t=1, base_ring=None): r""" Return Fourier expansion of Eistenstein series at a cusp. INPUT: - ``phi`` -- Dirichlet character. - ``psi`` -- Dirichlet character. - ``k`` -- integer, the weight of the Eistenstein series. - ``prec`` -- integer (default: 10). - ``t`` -- integer (default: 1). OUTPUT: The Fourier expansion of the Eisenstein series $E_k^{\phi,\psi, t}$ (as defined by [Diamond-Shurman]) at the specific cusp. EXAMPLES: sage: phi = DirichletGroup(3)[1] sage: psi = DirichletGroup(5)[1] sage: E = eisenstein_series_at_inf(phi, psi, 4) """ N1, N2 = phi.level(), psi.level() N = N1 * N2 #The Fourier expansion of the Eisenstein series at infinity is in the field Q(zeta_Ncyc) Ncyc = lcm([euler_phi(N1), euler_phi(N2)]) if base_ring == None: base_ring = CyclotomicField(Ncyc) Q = PowerSeriesRing(base_ring, 'q') q = Q.gen() s = O(q**prec) #Weight 2 with trivial characters is calculated separately if k == 2 and phi.conductor() == 1 and psi.conductor() == 1: if t == 1: raise TypeError('E_2 is not a modular form.') s = 1 / 24 * (t - 1) for m in srange(1, prec): for n in srange(1, prec / m): s += n * (q**(m * n) - t * q**(m * n * t)) return s + O(q**prec) if psi.level() == 1 and k == 1: s -= phi.bernoulli(k) / k elif phi.level() == 1: s -= psi.bernoulli(k) / k for m in srange(1, prec / t): for n in srange(1, prec / t / m + 1): s += 2 * base_ring(phi(m)) * base_ring( psi(n)) * n**(k - 1) * q**(m * n * t) return s + O(q**prec)
def num_cusps_of_width(N, d): r""" Return the number of cusps on `X_0(N)` of width d. INPUT: - ``N`` - (integer): the level - ``d`` - (integer): an integer dividing N, the cusp width EXAMPLES:: sage: [num_cusps_of_width(18,d) for d in divisors(18)] [1, 1, 2, 2, 1, 1] sage: num_cusps_of_width(4,8) Traceback (most recent call last): ... ValueError: N and d must be positive integers with d|N """ N = ZZ(N) d = ZZ(d) if N <= 0 or d <= 0 or (N % d) != 0: raise ValueError("N and d must be positive integers with d|N") return euler_phi(gcd(d, N//d))
def num_cusps_of_width(N, d): r""" Return the number of cusps on `X_0(N)` of width d. INPUT: - ``N`` - (integer): the level - ``d`` - (integer): an integer dividing N, the cusp width EXAMPLES:: sage: [num_cusps_of_width(18,d) for d in divisors(18)] [1, 1, 2, 2, 1, 1] sage: num_cusps_of_width(4,8) Traceback (most recent call last): ... ValueError: N and d must be positive integers with d|N """ N = ZZ(N) d = ZZ(d) if N <= 0 or d <= 0 or (N % d) != 0: raise ValueError("N and d must be positive integers with d|N") return euler_phi(gcd(d, N // d))
def nu2(self): r""" Return the number of orbits of elliptic points of order 2 for this group. EXAMPLES:: sage: [H.nu2() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()] [1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0] sage: GammaH(33,[2]).nu2() 0 sage: GammaH(5,[2]).nu2() 2 AUTHORS: - Jordi Quer """ N = self.level() H = self._list_of_elements_in_H() if N % 4 == 0: return ZZ(0) for p, r in N.factor(): if p % 4 == 3: return ZZ(0) return (euler_phi(N) // len(H)) * len( [x for x in H if (x**2 + 1) % N == 0])
def nu3(self): r""" Return the number of orbits of elliptic points of order 3 for this group. EXAMPLE:: sage: [H.nu3() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()] [1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] sage: GammaH(33,[2]).nu3() 0 sage: GammaH(7,[2]).nu3() 2 AUTHORS: - Jordi Quer """ N = self.level() H = self._list_of_elements_in_H() if N % 9 == 0: return ZZ(0) for p, r in N.factor(): if p % 3 == 2: return ZZ(0) lenHpm = len(H) if N - ZZ(1) not in H: lenHpm*=2 return (euler_phi(N)//lenHpm)*len([x for x in H if (x**2+x+1) % N == 0])
def nu2(self): r""" Return the number of orbits of elliptic points of order 2 for this group. EXAMPLE:: sage: [H.nu2() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()] [1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0] sage: GammaH(33,[2]).nu2() 0 sage: GammaH(5,[2]).nu2() 2 AUTHORS: - Jordi Quer """ N = self.level() H = self._list_of_elements_in_H() if N % 4 == 0: return ZZ(0) for p, r in N.factor(): if p % 4 == 3: return ZZ(0) return (euler_phi(N) // len(H))*len([x for x in H if (x**2 + 1) % N == 0])
def nu3(self): r""" Return the number of orbits of elliptic points of order 3 for this group. EXAMPLES:: sage: [H.nu3() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()] [1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] sage: GammaH(33,[2]).nu3() 0 sage: GammaH(7,[2]).nu3() 2 AUTHORS: - Jordi Quer """ N = self.level() H = self._list_of_elements_in_H() if N % 9 == 0: return ZZ(0) for p, r in N.factor(): if p % 3 == 2: return ZZ(0) lenHpm = len(H) if N - ZZ(1) not in H: lenHpm *= 2 return (euler_phi(N) // lenHpm) * len( [x for x in H if (x**2 + x + 1) % N == 0])
def GammaH_constructor(level, H): r""" Return the congruence subgroup `\Gamma_H(N)`, which is the subgroup of `SL_2(\ZZ)` consisting of matrices of the form `\begin{pmatrix} a & b \\ c & d \end{pmatrix}` with `N | c` and `a, b \in H`, for `H` a specified subgroup of `(\ZZ/N\ZZ)^\times`. INPUT: - level -- an integer - H -- either 0, 1, or a list * If H is a list, return `\Gamma_H(N)`, where `H` is the subgroup of `(\ZZ/N\ZZ)^*` **generated** by the elements of the list. * If H = 0, returns `\Gamma_0(N)`. * If H = 1, returns `\Gamma_1(N)`. EXAMPLES:: sage: GammaH(11,0) # indirect doctest Congruence Subgroup Gamma0(11) sage: GammaH(11,1) Congruence Subgroup Gamma1(11) sage: GammaH(11,[10]) Congruence Subgroup Gamma_H(11) with H generated by [10] sage: GammaH(11,[10,1]) Congruence Subgroup Gamma_H(11) with H generated by [10] sage: GammaH(14,[10]) Traceback (most recent call last): ... ArithmeticError: The generators [10] must be units modulo 14 """ from .all import Gamma0, Gamma1, SL2Z if level == 1: return SL2Z elif H == 0: return Gamma0(level) elif H == 1: return Gamma1(level) H = _normalize_H(H, level) if H == []: return Gamma1(level) Hlist = _list_subgroup(level, H) if len(Hlist) == euler_phi(level): return Gamma0(level) key = (level, tuple(H)) try: return _gammaH_cache[key] except KeyError: _gammaH_cache[key] = GammaH_class(level, H, Hlist) return _gammaH_cache[key]
def __init__(self, N, q, D, poly=None, secret_dist='uniform', m=None): """ Construct a Ring-LWE oracle in dimension ``n=phi(N)`` over a ring of order ``q`` with noise distribution ``D``. INPUT: - ``N`` - index of cyclotomic polynomial (integer > 0, must be power of 2) - ``q`` - modulus typically > N (integer > 0) - ``D`` - an error distribution such as an instance of :class:`DiscreteGaussianDistributionPolynomialSampler` or :class:`UniformSampler` - ``poly`` - a polynomial of degree ``phi(N)``. If ``None`` the cyclotomic polynomial used (default: ``None``). - ``secret_dist`` - distribution of the secret. See documentation of :class:`LWE` for details (default='uniform') - ``m`` - number of allowed samples or ``None`` if no such limit exists (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import RingLWE sage: from sage.stats.distributions.discrete_gaussian_polynomial import DiscreteGaussianDistributionPolynomialSampler sage: D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n=euler_phi(20), sigma=3.0) sage: RingLWE(N=20, q=next_prime(800), D=D) RingLWE(20, 809, Discrete Gaussian sampler for polynomials of degree < 8 with σ=3.000000 in each component, x^8 - x^6 + x^4 - x^2 + 1, 'uniform', None) """ self.N = ZZ(N) self.n = euler_phi(N) self.m = m self.__i = 0 self.K = IntegerModRing(q) if self.n != D.n: raise ValueError("Noise distribution has dimensions %d != %d" % (D.n, self.n)) self.D = D self.q = q if poly is not None: self.poly = poly else: self.poly = cyclotomic_polynomial(self.N, 'x') self.R_q = self.K['x'].quotient(self.poly, 'x') self.secret_dist = secret_dist if secret_dist == 'uniform': self.__s = self.R_q.random_element() # uniform sampling of secret elif secret_dist == 'noise': self.__s = self.D() else: raise TypeError("Parameter secret_dist=%s not understood." % (secret_dist))
def ncusps(self): r""" Return the number of cusps of this subgroup `\Gamma_0(N)`. EXAMPLES:: sage: [Gamma0(n).ncusps() for n in [1..19]] [1, 2, 2, 3, 2, 4, 2, 4, 4, 4, 2, 6, 2, 4, 4, 6, 2, 8, 2] sage: [Gamma0(n).ncusps() for n in prime_range(2,100)] [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] """ n = self.level() return sum([arith.euler_phi(arith.gcd(d,n//d)) for d in n.divisors()])
def nregcusps(self): r""" Return the number of orbits of regular cusps for this subgroup. A cusp is regular if we may find a parabolic element generating the stabiliser of that cusp whose eigenvalues are both +1 rather than -1. If G contains -1, all cusps are regular. EXAMPLES:: sage: GammaH(20, [17]).nregcusps() 4 sage: GammaH(20, [17]).nirregcusps() 2 sage: GammaH(3212, [2045, 2773]).nregcusps() 1440 sage: GammaH(3212, [2045, 2773]).nirregcusps() 720 AUTHOR: - Jordi Quer """ if self.is_even(): return self.ncusps() N = self.level() H = self._list_of_elements_in_H() c = ZZ(0) for d in [d for d in divisors(N) if d**2 <= N]: Nd = lcm(d, N // d) Hd = set([x % Nd for x in H]) if Nd - 1 not in Hd: summand = euler_phi(d) * euler_phi(N // d) // (2 * len(Hd)) if d**2 == N: c = c + summand else: c = c + 2 * summand return c
def nregcusps(self): r""" Return the number of orbits of regular cusps for this subgroup. A cusp is regular if we may find a parabolic element generating the stabiliser of that cusp whose eigenvalues are both +1 rather than -1. If G contains -1, all cusps are regular. EXAMPLES:: sage: GammaH(20, [17]).nregcusps() 4 sage: GammaH(20, [17]).nirregcusps() 2 sage: GammaH(3212, [2045, 2773]).nregcusps() 1440 sage: GammaH(3212, [2045, 2773]).nirregcusps() 720 AUTHOR: - Jordi Quer """ if self.is_even(): return self.ncusps() N = self.level() H = self._list_of_elements_in_H() c = ZZ(0) for d in [d for d in divisors(N) if d**2 <= N]: Nd = lcm(d,N//d) Hd = set([x%Nd for x in H]) if Nd - 1 not in Hd: summand = euler_phi(d)*euler_phi(N//d)//(2*len(Hd)) if d**2==N: c = c + summand else: c = c + 2*summand return c
def __init__(self, N, q, D, poly=None, secret_dist='uniform', m=None): """ Construct a Ring-LWE oracle in dimension ``n=phi(N)`` over a ring of order ``q`` with noise distribution ``D``. INPUT: - ``N`` - index of cyclotomic polynomial (integer > 0, must be power of 2) - ``q`` - modulus typically > N (integer > 0) - ``D`` - an error distribution such as an instance of :class:`DiscreteGaussianDistributionPolynomialSampler` or :class:`UniformSampler` - ``poly`` - a polynomial of degree ``phi(N)``. If ``None`` the cyclotomic polynomial used (default: ``None``). - ``secret_dist`` - distribution of the secret. See documentation of :class:`LWE` for details (default='uniform') - ``m`` - number of allowed samples or ``None`` if no such limit exists (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import RingLWE sage: from sage.stats.distributions.discrete_gaussian_polynomial import DiscreteGaussianDistributionPolynomialSampler sage: D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n=euler_phi(20), sigma=3.0) sage: RingLWE(N=20, q=next_prime(800), D=D) RingLWE(20, 809, Discrete Gaussian sampler for polynomials of degree < 8 with σ=3.000000 in each component, x^8 - x^6 + x^4 - x^2 + 1, 'uniform', None) """ self.N = ZZ(N) self.n = euler_phi(N) self.m = m self.__i = 0 self.K = IntegerModRing(q) if self.n != D.n: raise ValueError("Noise distribution has dimensions %d != %d"%(D.n, self.n)) self.D = D self.q = q if poly is not None: self.poly = poly else: self.poly = cyclotomic_polynomial(self.N, 'x') self.R_q = self.K['x'].quotient(self.poly, 'x') self.secret_dist = secret_dist if secret_dist == 'uniform': self.__s = self.R_q.random_element() # uniform sampling of secret elif secret_dist == 'noise': self.__s = self.D() else: raise TypeError("Parameter secret_dist=%s not understood."%(secret_dist))
def cardinality(self): r""" Return the number of integer necklaces with the evaluation ``content``. The formula for the number of necklaces of content `\alpha` a composition of `n` is: .. MATH:: \sum_{d|gcd(\alpha)} \phi(d) \binom{n/d}{\alpha_1/d, \ldots, \alpha_\ell/d}, where `\phi(d)` is the Euler `\phi` function. EXAMPLES:: sage: Necklaces([]).cardinality() 0 sage: Necklaces([2,2]).cardinality() 2 sage: Necklaces([2,3,2]).cardinality() 30 sage: Necklaces([0,3,2]).cardinality() 2 Check to make sure that the count matches up with the number of necklace words generated. :: sage: comps = [[],[2,2],[3,2,7],[4,2],[0,4,2],[2,0,4]]+Compositions(4).list() sage: ns = [ Necklaces(comp) for comp in comps] sage: all( [ n.cardinality() == len(n.list()) for n in ns] ) True """ evaluation = self._content le = list(evaluation) if not le: return 0 n = sum(le) return sum( euler_phi(j) * factorial(n / j) / prod(factorial(ni / j) for ni in evaluation) for j in divisors(gcd(le))) / n
def cardinality(self): r""" Return the number of integer necklaces with the evaluation ``content``. The formula for the number of necklaces of content `\alpha` a composition of `n` is: .. MATH:: \sum_{d|gcd(\alpha)} \phi(d) \binom{n/d}{\alpha_1/d, \ldots, \alpha_\ell/d}, where `\phi(d)` is the Euler `\phi` function. EXAMPLES:: sage: Necklaces([]).cardinality() 0 sage: Necklaces([2,2]).cardinality() 2 sage: Necklaces([2,3,2]).cardinality() 30 sage: Necklaces([0,3,2]).cardinality() 2 Check to make sure that the count matches up with the number of necklace words generated. :: sage: comps = [[],[2,2],[3,2,7],[4,2],[0,4,2],[2,0,4]]+Compositions(4).list() sage: ns = [Necklaces(comp) for comp in comps] sage: all(n.cardinality() == len(n.list()) for n in ns) True """ evaluation = self._content le = list(evaluation) if not le: return ZZ.zero() n = sum(le) return ZZ.sum(euler_phi(j) * factorial(n // j) // prod(factorial(ni // j) for ni in evaluation) for j in divisors(gcd(le))) // n
def _cis_iterator(self, base_ring): r""" The cycle index series of the species of cyclic permutations is given by .. MATH:: -\sum_{k=1}^\infty \phi(k)/k * log(1 - x_k) which is equal to .. MATH:: \sum_{n=1}^\infty \frac{1}{n} * \sum_{k|n} \phi(k) * x_k^{n/k} . EXAMPLES:: sage: P = species.CycleSpecies() sage: cis = P.cycle_index_series() sage: cis.coefficients(7) [0, p[1], 1/2*p[1, 1] + 1/2*p[2], 1/3*p[1, 1, 1] + 2/3*p[3], 1/4*p[1, 1, 1, 1] + 1/4*p[2, 2] + 1/2*p[4], 1/5*p[1, 1, 1, 1, 1] + 4/5*p[5], 1/6*p[1, 1, 1, 1, 1, 1] + 1/6*p[2, 2, 2] + 1/3*p[3, 3] + 1/3*p[6]] """ from sage.combinat.sf.sf import SymmetricFunctions p = SymmetricFunctions(base_ring).power() zero = base_ring(0) yield zero for n in _integers_from(1): res = zero for k in divisors(n): res += euler_phi(k) * p([k])**(n // k) res /= n yield self._weight * res
def __init__(self, N, delta=0.01, m=None): """ Construct a Ring-LWE oracle in dimension ``n=phi(N)`` where the modulus ``q`` and the ``stddev`` of the noise is chosen as in [LP2011]_. INPUT: - ``N`` - index of cyclotomic polynomial (integer > 0, must be power of 2) - ``delta`` - error probability per symbol (default: 0.01) - ``m`` - number of allowed samples or ``None`` in which case ``3*n`` is used (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import RingLindnerPeikert sage: RingLindnerPeikert(N=16) RingLWE(16, 1031, Discrete Gaussian sampler for polynomials of degree < 8 with σ=2.803372 in each component, x^8 + 1, 'noise', 24) """ n = euler_phi(N) if m is None: m = 3 * n # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40 # i.e c>=1 such that 2*n*log(c)+n*(1-c**2) + 40*log(2) == 0 c = SR.var('c') c = find_root(2 * n * log(c) + n * (1 - c**2) + 40 * log(2) == 0, 1, 10) # Upper bound on s**2/t s_t_bound = (sqrt(2) * pi / c / sqrt(2 * n * log(2 / delta))).n() # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP2011]_ q = next_prime(floor(2**round(log(256 / s_t_bound, 2)))) # Gaussian parameter as defined in [LP2011]_ s = sqrt(s_t_bound * floor(q / 4)) # Transform s into stddev stddev = s / sqrt(2 * pi.n()) D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n, stddev) RingLWE.__init__(self, N=N, q=q, D=D, poly=None, secret_dist='noise', m=m)
def _cis_iterator(self, base_ring): r""" The cycle index series of the species of cyclic permutations is given by .. MATH:: -\sum_{k=1}^\infty \phi(k)/k * log(1 - x_k) which is equal to .. MATH:: \sum_{n=1}^\infty \frac{1}{n} * \sum_{k|n} \phi(k) * x_k^{n/k} . EXAMPLES:: sage: P = species.CycleSpecies() sage: cis = P.cycle_index_series() sage: cis.coefficients(7) [0, p[1], 1/2*p[1, 1] + 1/2*p[2], 1/3*p[1, 1, 1] + 2/3*p[3], 1/4*p[1, 1, 1, 1] + 1/4*p[2, 2] + 1/2*p[4], 1/5*p[1, 1, 1, 1, 1] + 4/5*p[5], 1/6*p[1, 1, 1, 1, 1, 1] + 1/6*p[2, 2, 2] + 1/3*p[3, 3] + 1/3*p[6]] """ from sage.combinat.sf.sf import SymmetricFunctions p = SymmetricFunctions(base_ring).power() zero = base_ring(0) yield zero for n in _integers_from(1): res = zero for k in divisors(n): res += euler_phi(k)*p([k])**(n//k) res /= n yield self._weight*res
def _GammaH_coset_helper(N, H): r""" Return a list of coset representatives for H in (Z / NZ)^*. EXAMPLES:: sage: from sage.modular.arithgroup.congroup_gammaH import _GammaH_coset_helper sage: _GammaH_coset_helper(108, [1, 107]) [1, 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49, 53] """ t = [Zmod(N)(1)] W = [Zmod(N)(h) for h in H] HH = [Zmod(N)(h) for h in H] k = euler_phi(N) for i in range(1, N): if gcd(i, N) != 1: continue if not i in W: t.append(t[0] * i) W = W + [i * h for h in HH] if len(W) == k: break return t
def _GammaH_coset_helper(N, H): r""" Return a list of coset representatives for H in (Z / NZ)^*. EXAMPLE:: sage: from sage.modular.arithgroup.congroup_gammaH import _GammaH_coset_helper sage: _GammaH_coset_helper(108, [1, 107]) [1, 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49, 53] """ t = [Zmod(N)(1)] W = [Zmod(N)(h) for h in H] HH = [Zmod(N)(h) for h in H] k = euler_phi(N) for i in range(1, N): if gcd(i, N) != 1: continue if not i in W: t.append(t[0]*i) W = W + [i*h for h in HH] if len(W) == k: break return t
def __init__(self, N, delta=0.01, m=None): """ Construct a Ring-LWE oracle in dimension ``n=phi(N)`` where the modulus ``q`` and the ``stddev`` of the noise is chosen as in [LP2011]_. INPUT: - ``N`` - index of cyclotomic polynomial (integer > 0, must be power of 2) - ``delta`` - error probability per symbol (default: 0.01) - ``m`` - number of allowed samples or ``None`` in which case ``3*n`` is used (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import RingLindnerPeikert sage: RingLindnerPeikert(N=16) RingLWE(16, 1031, Discrete Gaussian sampler for polynomials of degree < 8 with σ=2.803372 in each component, x^8 + 1, 'noise', 24) """ n = euler_phi(N) if m is None: m = 3*n # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40 # i.e c>=1 such that 2*n*log(c)+n*(1-c**2) + 40*log(2) == 0 c = SR.var('c') c = find_root(2*n*log(c)+n*(1-c**2) + 40*log(2) == 0, 1, 10) # Upper bound on s**2/t s_t_bound = (sqrt(2) * pi / c / sqrt(2*n*log(2/delta))).n() # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP2011]_ q = next_prime(floor(2**round(log(256 / s_t_bound, 2)))) # Gaussian parameter as defined in [LP2011]_ s = sqrt(s_t_bound*floor(q/4)) # Transform s into stddev stddev = s/sqrt(2*pi.n()) D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n, stddev) RingLWE.__init__(self, N=N, q=q, D=D, poly=None, secret_dist='noise', m=m)
def cyclotomic_restriction(L, K): r""" Given two cyclotomic fields L and K, compute the compositum M of K and L, and return a function and the index [M:K]. The function is a map that acts as follows (here `M = Q(\zeta_m)`): INPUT: element alpha in L OUTPUT: a polynomial `f(x)` in `K[x]` such that `f(\zeta_m) = \alpha`, where we view alpha as living in `M`. (Note that `\zeta_m` generates `M`, not `L`.) EXAMPLES:: sage: L = CyclotomicField(12) ; N = CyclotomicField(33) ; M = CyclotomicField(132) sage: z, n = sage.modular.modform.eisenstein_submodule.cyclotomic_restriction(L,N) sage: n 2 sage: z(L.0) -zeta33^19*x sage: z(L.0)(M.0) zeta132^11 sage: z(L.0^3-L.0+1) (zeta33^19 + zeta33^8)*x + 1 sage: z(L.0^3-L.0+1)(M.0) zeta132^33 - zeta132^11 + 1 sage: z(L.0^3-L.0+1)(M.0) - M(L.0^3-L.0+1) 0 """ if not L.has_coerce_map_from(K): M = CyclotomicField(lcm(L.zeta_order(), K.zeta_order())) f = cyclotomic_restriction_tower(M, K) def g(x): r""" Function returned by cyclotomic restriction. INPUT: element alpha in L OUTPUT: a polynomial `f(x)` in `K[x]` such that `f(\zeta_m) = \alpha`, where we view alpha as living in `M`. (Note that `\zeta_m` generates `M`, not `L`.) EXAMPLES:: sage: L = CyclotomicField(12) sage: N = CyclotomicField(33) sage: g, n = sage.modular.modform.eisenstein_submodule.cyclotomic_restriction(L,N) sage: g(L.0) -zeta33^19*x """ return f(M(x)) return g, euler_phi(M.zeta_order()) // euler_phi(K.zeta_order()) else: return cyclotomic_restriction_tower(L,K), \ euler_phi(L.zeta_order())//euler_phi(K.zeta_order())
def cyclotomic_restriction(L,K): r""" Given two cyclotomic fields L and K, compute the compositum M of K and L, and return a function and the index [M:K]. The function is a map that acts as follows (here `M = Q(\zeta_m)`): INPUT: element alpha in L OUTPUT: a polynomial `f(x)` in `K[x]` such that `f(\zeta_m) = \alpha`, where we view alpha as living in `M`. (Note that `\zeta_m` generates `M`, not `L`.) EXAMPLES:: sage: L = CyclotomicField(12) ; N = CyclotomicField(33) ; M = CyclotomicField(132) sage: z, n = sage.modular.modform.eisenstein_submodule.cyclotomic_restriction(L,N) sage: n 2 sage: z(L.0) -zeta33^19*x sage: z(L.0)(M.0) zeta132^11 sage: z(L.0^3-L.0+1) (zeta33^19 + zeta33^8)*x + 1 sage: z(L.0^3-L.0+1)(M.0) zeta132^33 - zeta132^11 + 1 sage: z(L.0^3-L.0+1)(M.0) - M(L.0^3-L.0+1) 0 """ if not L.has_coerce_map_from(K): M = CyclotomicField(lcm(L.zeta_order(), K.zeta_order())) f = cyclotomic_restriction_tower(M,K) def g(x): """ Function returned by cyclotomic restriction. INPUT: element alpha in L OUTPUT: a polynomial `f(x)` in `K[x]` such that `f(\zeta_m) = \alpha`, where we view alpha as living in `M`. (Note that `\zeta_m` generates `M`, not `L`.) EXAMPLES:: sage: L = CyclotomicField(12) sage: N = CyclotomicField(33) sage: g, n = sage.modular.modform.eisenstein_submodule.cyclotomic_restriction(L,N) sage: g(L.0) -zeta33^19*x """ return f(M(x)) return g, euler_phi(M.zeta_order())//euler_phi(K.zeta_order()) else: return cyclotomic_restriction_tower(L,K), \ euler_phi(L.zeta_order())//euler_phi(K.zeta_order())
def my_gen_lattice2(n=4, q=11, seed=None, quotient=None, dual=False, ntl=False, lattice=False, GuessStuff=True): """ This is a modification of the code for the gen_lattice function from Sage Randomness can be set either with ``seed``, or by using :func:`sage.misc.randstate.set_random_seed`. INPUT: - ``type`` -- one of the following strings - ``'cyclotomic'`` -- Special case of ideal. Allows for efficient processing proposed by [LM2006]_. - ``n`` -- Determinant size, primal:`det(L) = q^n`, dual:`det(L) = q^{m-n}`. For ideal lattices this is also the degree of the quotient polynomial. - ``m`` -- Lattice dimension, `L \subseteq Z^m`. - ``q`` -- Coefficient size, `q-Z^m \subseteq L`. - ``t`` -- BKZ Block Size - ``seed`` -- Randomness seed. - ``quotient`` -- For the type ideal, this determines the quotient polynomial. Ignored for all other types. - ``dual`` -- Set this flag if you want a basis for `q-dual(L)`, for example for Regev's LWE bases [Reg2005]_. - ``ntl`` -- Set this flag if you want the lattice basis in NTL readable format. - ``lattice`` -- Set this flag if you want a :class:`FreeModule_submodule_with_basis_integer` object instead of an integer matrix representing the basis. OUTPUT: ``B`` a unique size-reduced triangular (primal: lower_left, dual: lower_right) basis of row vectors for the lattice in question. EXAMPLES: Cyclotomic bases with n=2^k are SWIFFT bases:: sage: sage.crypto.gen_lattice(type='cyclotomic', seed=42) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [ 4 -2 -3 -3 1 0 0 0] [ 3 4 -2 -3 0 1 0 0] [ 3 3 4 -2 0 0 1 0] [ 2 3 3 4 0 0 0 1] Dual modular bases are related to Regev's famous public-key encryption [Reg2005]_:: sage: sage.crypto.gen_lattice(type='modular', m=10, seed=42, dual=True) [ 0 0 0 0 0 0 0 0 0 11] [ 0 0 0 0 0 0 0 0 11 0] [ 0 0 0 0 0 0 0 11 0 0] [ 0 0 0 0 0 0 11 0 0 0] [ 0 0 0 0 0 11 0 0 0 0] [ 0 0 0 0 11 0 0 0 0 0] [ 0 0 0 1 -5 -2 -1 1 -3 5] [ 0 0 1 0 -3 4 1 4 -3 -2] [ 0 1 0 0 -4 5 -3 3 5 3] [ 1 0 0 0 -2 -1 4 2 5 4] """ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.matrix.constructor import identity_matrix, block_matrix from sage.matrix.matrix_space import MatrixSpace from sage.rings.integer_ring import IntegerRing from sage.modules.free_module_integer import IntegerLattice if seed is not None: from sage.misc.randstate import set_random_seed set_random_seed(seed) m=n+1 ZZ = IntegerRing() ZZ_q = IntegerModRing(q) from sage.arith.all import euler_phi from sage.misc.functional import cyclotomic_polynomial # we assume that n+1 <= min( euler_phi^{-1}(n) ) <= 2*n found = False for k in range(2*n,n,-1): if euler_phi(k) == n: found = True break if not found: raise ValueError("cyclotomic bases require that n " "is an image of Euler's totient function") R = ZZ_q['x'].quotient(cyclotomic_polynomial(2*n, 'x'), 'x') g=x**(n/2)+1 T=ZZ_q['x'].quotient(x**(n/2)+1) a_pol=R.random_element() s_pol=sample_noise(R) e_pol=sample_noise(R) s_pol2=T((s_pol)) e_pol2=T((e_pol)) print("s={0},e={1}".format(T(s_pol),T(e_pol))) Z_mat=e_pol2.matrix().augment(s_pol2.matrix()) Z_mattop=Z_mat[0:1].augment(matrix(1,1,[ZZ.one()*-1])) b_pol=(a_pol*s_pol+e_pol) print("s_pol={0}\ne_pol={1}".format((s_pol2).list(),(e_pol2).list())) # Does a linear mapping change the shortest vector size for the rest?/ a_pol=a_pol#*x_pol b_pol=b_pol#*x_pol a_pol2 = T(a_pol.list())# % S(g.list()) b_pol2 = T((b_pol).list())# % S(g.list()) # print("a={0}\nb={1}".format(a_pol2,b_pol2)) A=identity_matrix(ZZ_q,n/2) A=A.stack(a_pol2.matrix()) b_prime=b_pol2.matrix()[0:1] b_prime=b_prime - 11*A[8:9] A=A.stack(b_pol2.matrix()[0:1]) # print("X=\n{0}".format(X)) # A = A.stack(identity_matrix(ZZ_q, n/2)) print("A=\n{0}\n".format(A)) # switch from representatives 0,...,(q-1) to (1-q)/2,....,(q-1)/2 def minrepnegative(a): if abs(a-q) < abs(a): return (a-q)*-1 else: return a*-1 def minrep(a): if abs(a-q) < abs(a): return (a-q) else: return a A_prime = A[(n/2):(n+1)].lift().apply_map(minrep) # b_neg= A[(n):(n+1)].lift().apply_map(minrepnegative) Z_fixed=Z_mattop.lift().apply_map(minrep) print("Z_fixed={0}\n||Z_fixed||={1}".format(Z_fixed,float(Z_fixed[0].norm()))) print('Z_fixed*A={0}\n\n'.format(Z_fixed*A)) print("z_fixed[0].norm()={0}".format(float(Z_fixed[0].norm()))) # B=block_matrix([[ZZ(q),ZZ.zero()],[A_neg,ZZ.one()]], subdivide=False) # B = block_matrix([[ZZ.one(), -A_prime.transpose()], # [ZZ.zero(), ZZ(q)]], subdivide=False) B = block_matrix([[ZZ(q), ZZ.zero()], [-A_prime, ZZ.one()]], subdivide=False) # for i in range(m//2): # B.swap_rows(i,m-i-1) # print("{0}\n".format(A_neg)) # B=block_matrix([[ZZ(q), ZZ.zero(),ZZ.zero()],[ZZ.one(),A_neg,ZZ.zero() ],[ZZ.zero(),b_neg,ZZ.one()]], # subdivide=False) #print("B=\n{0}".format(B)) print("B*A=\n{0}\n\n".format(B*A)) #print("A=\n{0}\n".format(A)) def remap(x): return minrep((x*251)%251) BL=B.BKZ(block_size=n/2.) y=(BL.solve_left(Z_fixed))#.apply_map(remap)) # print("y*B={0}".format(y*B)) print("y:=B.solve_left(Z_fixed)={0}".format(y)) # BL=B.BKZ(block_size=n/2.) print(BL[0]) print("shortest norm={0}".format(float(BL[0].norm()))) # L = IntegerLattice(B) # p # v=L.shortest_vector() # print("L.shortest_vector={0}, norm={1}".format(v,float(v.norm()))) if ntl and lattice: raise ValueError("Cannot specify ntl=True and lattice=True ") if ntl: return B._ntl_() elif lattice: from sage.modules.free_module_integer import IntegerLattice return IntegerLattice(B) else: return B
def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, quotient=None, dual=False, ntl=False, lattice=False): """ This function generates different types of integral lattice bases of row vectors relevant in cryptography. Randomness can be set either with ``seed``, or by using :func:`sage.misc.randstate.set_random_seed`. INPUT: - ``type`` -- one of the following strings - ``'modular'`` (default) -- A class of lattices for which asymptotic worst-case to average-case connections hold. For more refer to [A96]_. - ``'random'`` -- Special case of modular (n=1). A dense class of lattice used for testing basis reduction algorithms proposed by Goldstein and Mayer [GM02]_. - ``'ideal'`` -- Special case of modular. Allows for a more compact representation proposed by [LM06]_. - ``'cyclotomic'`` -- Special case of ideal. Allows for efficient processing proposed by [LM06]_. - ``n`` -- Determinant size, primal:`det(L) = q^n`, dual:`det(L) = q^{m-n}`. For ideal lattices this is also the degree of the quotient polynomial. - ``m`` -- Lattice dimension, `L \subseteq Z^m`. - ``q`` -- Coefficient size, `q-Z^m \subseteq L`. - ``seed`` -- Randomness seed. - ``quotient`` -- For the type ideal, this determines the quotient polynomial. Ignored for all other types. - ``dual`` -- Set this flag if you want a basis for `q-dual(L)`, for example for Regev's LWE bases [R05]_. - ``ntl`` -- Set this flag if you want the lattice basis in NTL readable format. - ``lattice`` -- Set this flag if you want a :class:`FreeModule_submodule_with_basis_integer` object instead of an integer matrix representing the basis. OUTPUT: ``B`` a unique size-reduced triangular (primal: lower_left, dual: lower_right) basis of row vectors for the lattice in question. EXAMPLES: Modular basis:: sage: sage.crypto.gen_lattice(m=10, seed=42) [11 0 0 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0 0 0] [ 0 0 0 11 0 0 0 0 0 0] [ 2 4 3 5 1 0 0 0 0 0] [ 1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] Random basis:: sage: sage.crypto.gen_lattice(type='random', n=1, m=10, q=11^4, seed=42) [14641 0 0 0 0 0 0 0 0 0] [ 431 1 0 0 0 0 0 0 0 0] [-4792 0 1 0 0 0 0 0 0 0] [ 1015 0 0 1 0 0 0 0 0 0] [-3086 0 0 0 1 0 0 0 0 0] [-5378 0 0 0 0 1 0 0 0 0] [ 4769 0 0 0 0 0 1 0 0 0] [-1159 0 0 0 0 0 0 1 0 0] [ 3082 0 0 0 0 0 0 0 1 0] [-4580 0 0 0 0 0 0 0 0 1] Ideal bases with quotient x^n-1, m=2*n are NTRU bases:: sage: sage.crypto.gen_lattice(type='ideal', seed=42, quotient=x^4-1) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [ 4 -2 -3 -3 1 0 0 0] [-3 4 -2 -3 0 1 0 0] [-3 -3 4 -2 0 0 1 0] [-2 -3 -3 4 0 0 0 1] Ideal bases also work with polynomials:: sage: R.<t> = PolynomialRing(ZZ) sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=t^4-1) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [ 4 1 4 -3 1 0 0 0] [-3 4 1 4 0 1 0 0] [ 4 -3 4 1 0 0 1 0] [ 1 4 -3 4 0 0 0 1] Cyclotomic bases with n=2^k are SWIFFT bases:: sage: sage.crypto.gen_lattice(type='cyclotomic', seed=42) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [ 4 -2 -3 -3 1 0 0 0] [ 3 4 -2 -3 0 1 0 0] [ 3 3 4 -2 0 0 1 0] [ 2 3 3 4 0 0 0 1] Dual modular bases are related to Regev's famous public-key encryption [R05]_:: sage: sage.crypto.gen_lattice(type='modular', m=10, seed=42, dual=True) [ 0 0 0 0 0 0 0 0 0 11] [ 0 0 0 0 0 0 0 0 11 0] [ 0 0 0 0 0 0 0 11 0 0] [ 0 0 0 0 0 0 11 0 0 0] [ 0 0 0 0 0 11 0 0 0 0] [ 0 0 0 0 11 0 0 0 0 0] [ 0 0 0 1 -5 -2 -1 1 -3 5] [ 0 0 1 0 -3 4 1 4 -3 -2] [ 0 1 0 0 -4 5 -3 3 5 3] [ 1 0 0 0 -2 -1 4 2 5 4] Relation of primal and dual bases:: sage: B_primal=sage.crypto.gen_lattice(m=10, q=11, seed=42) sage: B_dual=sage.crypto.gen_lattice(m=10, q=11, seed=42, dual=True) sage: B_dual_alt=transpose(11*B_primal.inverse()).change_ring(ZZ) sage: B_dual_alt.hermite_form() == B_dual.hermite_form() True TESTS: Test some bad quotient polynomials:: sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=cos(x)) Traceback (most recent call last): ... TypeError: unable to convert cos(x) to an integer sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=x^23-1) Traceback (most recent call last): ... ValueError: ideal basis requires n = quotient.degree() sage: R.<u,v> = ZZ[] sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=u+v) Traceback (most recent call last): ... TypeError: quotient should be a univariate polynomial We are testing output format choices:: sage: sage.crypto.gen_lattice(m=10, q=11, seed=42) [11 0 0 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0 0 0] [ 0 0 0 11 0 0 0 0 0 0] [ 2 4 3 5 1 0 0 0 0 0] [ 1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, ntl=True) [ [11 0 0 0 0 0 0 0 0 0] [0 11 0 0 0 0 0 0 0 0] [0 0 11 0 0 0 0 0 0 0] [0 0 0 11 0 0 0 0 0 0] [2 4 3 5 1 0 0 0 0 0] [1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] ] sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, lattice=True) Free module of degree 10 and rank 10 over Integer Ring User basis matrix: [ 0 0 1 1 0 -1 -1 -1 1 0] [-1 1 0 1 0 1 1 0 1 1] [-1 0 0 0 -1 1 1 -2 0 0] [-1 -1 0 1 1 0 0 1 1 -1] [ 1 0 -1 0 0 0 -2 -2 0 0] [ 2 -1 0 0 1 0 1 0 0 -1] [-1 1 -1 0 1 -1 1 0 -1 -2] [ 0 0 -1 3 0 0 0 -1 -1 -1] [ 0 -1 0 -1 2 0 -1 0 0 2] [ 0 1 1 0 1 1 -2 1 -1 -2] REFERENCES: .. [A96] Miklos Ajtai. Generating hard instances of lattice problems (extended abstract). STOC, pp. 99--108, ACM, 1996. .. [GM02] Daniel Goldstein and Andrew Mayer. On the equidistribution of Hecke points. Forum Mathematicum, 15:2, pp. 165--189, De Gruyter, 2003. .. [LM06] Vadim Lyubashevsky and Daniele Micciancio. Generalized compact knapsacks are collision resistant. ICALP, pp. 144--155, Springer, 2006. .. [R05] Oded Regev. On lattices, learning with errors, random linear codes, and cryptography. STOC, pp. 84--93, ACM, 2005. """ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.matrix.constructor import identity_matrix, block_matrix from sage.matrix.matrix_space import MatrixSpace from sage.rings.integer_ring import IntegerRing if seed is not None: from sage.misc.randstate import set_random_seed set_random_seed(seed) if type == 'random': if n != 1: raise ValueError('random bases require n = 1') ZZ = IntegerRing() ZZ_q = IntegerModRing(q) A = identity_matrix(ZZ_q, n) if type == 'random' or type == 'modular': R = MatrixSpace(ZZ_q, m-n, n) A = A.stack(R.random_element()) elif type == 'ideal': if quotient is None: raise ValueError('ideal bases require a quotient polynomial') try: quotient = quotient.change_ring(ZZ_q) except (AttributeError, TypeError): quotient = quotient.polynomial(base_ring=ZZ_q) P = quotient.parent() # P should be a univariate polynomial ring over ZZ_q if not is_PolynomialRing(P): raise TypeError("quotient should be a univariate polynomial") assert P.base_ring() is ZZ_q if quotient.degree() != n: raise ValueError('ideal basis requires n = quotient.degree()') R = P.quotient(quotient) for i in range(m//n): A = A.stack(R.random_element().matrix()) elif type == 'cyclotomic': from sage.arith.all import euler_phi from sage.misc.functional import cyclotomic_polynomial # we assume that n+1 <= min( euler_phi^{-1}(n) ) <= 2*n found = False for k in range(2*n,n,-1): if euler_phi(k) == n: found = True break if not found: raise ValueError("cyclotomic bases require that n " "is an image of Euler's totient function") R = ZZ_q['x'].quotient(cyclotomic_polynomial(k, 'x'), 'x') for i in range(m//n): A = A.stack(R.random_element().matrix()) # switch from representatives 0,...,(q-1) to (1-q)/2,....,(q-1)/2 def minrep(a): if abs(a-q) < abs(a): return a-q else: return a A_prime = A[n:m].lift().apply_map(minrep) if not dual: B = block_matrix([[ZZ(q), ZZ.zero()], [A_prime, ZZ.one()] ], subdivide=False) else: B = block_matrix([[ZZ.one(), -A_prime.transpose()], [ZZ.zero(), ZZ(q)]], subdivide=False) for i in range(m//2): B.swap_rows(i,m-i-1) if ntl and lattice: raise ValueError("Cannot specify ntl=True and lattice=True " "at the same time") if ntl: return B._ntl_() elif lattice: from sage.modules.free_module_integer import IntegerLattice return IntegerLattice(B) else: return B
def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, quotient=None, dual=False, ntl=False, lattice=False): r""" This function generates different types of integral lattice bases of row vectors relevant in cryptography. Randomness can be set either with ``seed``, or by using :func:`sage.misc.randstate.set_random_seed`. INPUT: - ``type`` -- one of the following strings - ``'modular'`` (default) -- A class of lattices for which asymptotic worst-case to average-case connections hold. For more refer to [Aj1996]_. - ``'random'`` -- Special case of modular (n=1). A dense class of lattice used for testing basis reduction algorithms proposed by Goldstein and Mayer [GM2002]_. - ``'ideal'`` -- Special case of modular. Allows for a more compact representation proposed by [LM2006]_. - ``'cyclotomic'`` -- Special case of ideal. Allows for efficient processing proposed by [LM2006]_. - ``n`` -- Determinant size, primal:`det(L) = q^n`, dual:`det(L) = q^{m-n}`. For ideal lattices this is also the degree of the quotient polynomial. - ``m`` -- Lattice dimension, `L \subseteq Z^m`. - ``q`` -- Coefficient size, `q-Z^m \subseteq L`. - ``seed`` -- Randomness seed. - ``quotient`` -- For the type ideal, this determines the quotient polynomial. Ignored for all other types. - ``dual`` -- Set this flag if you want a basis for `q-dual(L)`, for example for Regev's LWE bases [Reg2005]_. - ``ntl`` -- Set this flag if you want the lattice basis in NTL readable format. - ``lattice`` -- Set this flag if you want a :class:`FreeModule_submodule_with_basis_integer` object instead of an integer matrix representing the basis. OUTPUT: ``B`` a unique size-reduced triangular (primal: lower_left, dual: lower_right) basis of row vectors for the lattice in question. EXAMPLES: Modular basis:: sage: sage.crypto.gen_lattice(m=10, seed=42) [11 0 0 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0 0 0] [ 0 0 0 11 0 0 0 0 0 0] [ 2 4 3 5 1 0 0 0 0 0] [ 1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] Random basis:: sage: sage.crypto.gen_lattice(type='random', n=1, m=10, q=11^4, seed=42) [14641 0 0 0 0 0 0 0 0 0] [ 431 1 0 0 0 0 0 0 0 0] [-4792 0 1 0 0 0 0 0 0 0] [ 1015 0 0 1 0 0 0 0 0 0] [-3086 0 0 0 1 0 0 0 0 0] [-5378 0 0 0 0 1 0 0 0 0] [ 4769 0 0 0 0 0 1 0 0 0] [-1159 0 0 0 0 0 0 1 0 0] [ 3082 0 0 0 0 0 0 0 1 0] [-4580 0 0 0 0 0 0 0 0 1] Ideal bases with quotient x^n-1, m=2*n are NTRU bases:: sage: sage.crypto.gen_lattice(type='ideal', seed=42, quotient=x^4-1) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [-2 -3 -3 4 1 0 0 0] [ 4 -2 -3 -3 0 1 0 0] [-3 4 -2 -3 0 0 1 0] [-3 -3 4 -2 0 0 0 1] Ideal bases also work with polynomials:: sage: R.<t> = PolynomialRing(ZZ) sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=t^4-1) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [ 1 4 -3 3 1 0 0 0] [ 3 1 4 -3 0 1 0 0] [-3 3 1 4 0 0 1 0] [ 4 -3 3 1 0 0 0 1] Cyclotomic bases with n=2^k are SWIFFT bases:: sage: sage.crypto.gen_lattice(type='cyclotomic', seed=42) [11 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0] [ 0 0 0 11 0 0 0 0] [-2 -3 -3 4 1 0 0 0] [-4 -2 -3 -3 0 1 0 0] [ 3 -4 -2 -3 0 0 1 0] [ 3 3 -4 -2 0 0 0 1] Dual modular bases are related to Regev's famous public-key encryption [Reg2005]_:: sage: sage.crypto.gen_lattice(type='modular', m=10, seed=42, dual=True) [ 0 0 0 0 0 0 0 0 0 11] [ 0 0 0 0 0 0 0 0 11 0] [ 0 0 0 0 0 0 0 11 0 0] [ 0 0 0 0 0 0 11 0 0 0] [ 0 0 0 0 0 11 0 0 0 0] [ 0 0 0 0 11 0 0 0 0 0] [ 0 0 0 1 -5 -2 -1 1 -3 5] [ 0 0 1 0 -3 4 1 4 -3 -2] [ 0 1 0 0 -4 5 -3 3 5 3] [ 1 0 0 0 -2 -1 4 2 5 4] Relation of primal and dual bases:: sage: B_primal=sage.crypto.gen_lattice(m=10, q=11, seed=42) sage: B_dual=sage.crypto.gen_lattice(m=10, q=11, seed=42, dual=True) sage: B_dual_alt=transpose(11*B_primal.inverse()).change_ring(ZZ) sage: B_dual_alt.hermite_form() == B_dual.hermite_form() True TESTS: Test some bad quotient polynomials:: sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=cos(x)) Traceback (most recent call last): ... TypeError: unable to convert cos(x) to an integer sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=x^23-1) Traceback (most recent call last): ... ValueError: ideal basis requires n = quotient.degree() sage: R.<u,v> = ZZ[] sage: sage.crypto.gen_lattice(type='ideal', seed=1234, quotient=u+v) Traceback (most recent call last): ... TypeError: quotient should be a univariate polynomial We are testing output format choices:: sage: sage.crypto.gen_lattice(m=10, q=11, seed=42) [11 0 0 0 0 0 0 0 0 0] [ 0 11 0 0 0 0 0 0 0 0] [ 0 0 11 0 0 0 0 0 0 0] [ 0 0 0 11 0 0 0 0 0 0] [ 2 4 3 5 1 0 0 0 0 0] [ 1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, ntl=True) [ [11 0 0 0 0 0 0 0 0 0] [0 11 0 0 0 0 0 0 0 0] [0 0 11 0 0 0 0 0 0 0] [0 0 0 11 0 0 0 0 0 0] [2 4 3 5 1 0 0 0 0 0] [1 -5 -4 2 0 1 0 0 0 0] [-4 3 -1 1 0 0 1 0 0 0] [-2 -3 -4 -1 0 0 0 1 0 0] [-5 -5 3 3 0 0 0 0 1 0] [-4 -3 2 -5 0 0 0 0 0 1] ] sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, lattice=True) Free module of degree 10 and rank 10 over Integer Ring User basis matrix: [ 0 0 1 1 0 -1 -1 -1 1 0] [-1 1 0 1 0 1 1 0 1 1] [-1 0 0 0 -1 1 1 -2 0 0] [-1 -1 0 1 1 0 0 1 1 -1] [ 1 0 -1 0 0 0 -2 -2 0 0] [ 2 -1 0 0 1 0 1 0 0 -1] [-1 1 -1 0 1 -1 1 0 -1 -2] [ 0 0 -1 3 0 0 0 -1 -1 -1] [ 0 -1 0 -1 2 0 -1 0 0 2] [ 0 1 1 0 1 1 -2 1 -1 -2] """ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.matrix.constructor import identity_matrix, block_matrix from sage.matrix.matrix_space import MatrixSpace from sage.rings.integer_ring import IntegerRing if seed is not None: from sage.misc.randstate import set_random_seed set_random_seed(seed) if type == 'random': if n != 1: raise ValueError('random bases require n = 1') ZZ = IntegerRing() ZZ_q = IntegerModRing(q) A = identity_matrix(ZZ_q, n) if type == 'random' or type == 'modular': R = MatrixSpace(ZZ_q, m-n, n) A = A.stack(R.random_element()) elif type == 'ideal': if quotient is None: raise ValueError('ideal bases require a quotient polynomial') try: quotient = quotient.change_ring(ZZ_q) except (AttributeError, TypeError): quotient = quotient.polynomial(base_ring=ZZ_q) P = quotient.parent() # P should be a univariate polynomial ring over ZZ_q if not is_PolynomialRing(P): raise TypeError("quotient should be a univariate polynomial") assert P.base_ring() is ZZ_q if quotient.degree() != n: raise ValueError('ideal basis requires n = quotient.degree()') R = P.quotient(quotient) for i in range(m//n): A = A.stack(R.random_element().matrix()) elif type == 'cyclotomic': from sage.arith.all import euler_phi from sage.misc.functional import cyclotomic_polynomial # we assume that n+1 <= min( euler_phi^{-1}(n) ) <= 2*n found = False for k in range(2*n,n,-1): if euler_phi(k) == n: found = True break if not found: raise ValueError("cyclotomic bases require that n " "is an image of Euler's totient function") R = ZZ_q['x'].quotient(cyclotomic_polynomial(k, 'x'), 'x') for i in range(m//n): A = A.stack(R.random_element().matrix()) # switch from representatives 0,...,(q-1) to (1-q)/2,....,(q-1)/2 def minrep(a): if abs(a-q) < abs(a): return a-q else: return a A_prime = A[n:m].lift().apply_map(minrep) if not dual: B = block_matrix([[ZZ(q), ZZ.zero()], [A_prime, ZZ.one()] ], subdivide=False) else: B = block_matrix([[ZZ.one(), -A_prime.transpose()], [ZZ.zero(), ZZ(q)]], subdivide=False) for i in range(m//2): B.swap_rows(i,m-i-1) if ntl and lattice: raise ValueError("Cannot specify ntl=True and lattice=True " "at the same time") if ntl: return B._ntl_() elif lattice: from sage.modules.free_module_integer import IntegerLattice return IntegerLattice(B) else: return B
m2 = ZZ(input("m2 = ")) m1 = 2*m2 m0 = 2*m1 M = [m0, m1, m2] M0BS, M0BSL = m0.quo_rem(8) POWM1 = 2**m1 POWM1D2 = 2**(m1-1) POWM2 = 2**m2 POWM2T2 = 2**(m2+1) POWM2M2D3 = ZZ((2**m2-2)/3) POWM2P1D3 = ZZ((2**m2+1)/3) K1MASK = 2**m1 - 1 K1HIGHBIT = 2**(m1-1) K0MASK = 2**m0 - 1 K0HIGHBIT = 2**(m0-1) NECKLACES = 1/m1*sum(euler_phi(d)*2**(ZZ(m1/d)) for d in m1.divisors()) # 9*2*2*2 > 64 if m2 > 8: USE_DWORD = 1 else: USE_DWORD = 0 GF2 = GF(2) x = polygen(GF2) K2 = GF(2**m2, name='g2', modulus=modulus) g2 = K2.gen() z2 = K2.multiplicative_generator() K1 = GF(2**m1, name='g1', modulus=modulus) g1 = K1.gen() z1 = K1.multiplicative_generator() Z1 = z1.polynomial().change_ring(ZZ)(2)
set_random_seed(seed) m=n+1 ZZ = IntegerRing() ZZ_q = IntegerModRing(q) from sage.arith.all import euler_phi from sage.misc.functional import cyclotomic_polynomial # we assume that n+1 <= min( euler_phi^{-1}(n) ) <= 2*n found = False for k in range(2*n,n,-1): if euler_phi(k) == n: found = True break if not found: raise ValueError("cyclotomic bases require that n " "is an image of Euler's totient function") R = ZZ_q['x'].quotient(cyclotomic_polynomial(2*n, 'x'), 'x') g=x**(n/2)+1 T=ZZ_q['x'].quotient(x**(n/2)+1) a_pol=R.random_element() s_pol=sample_noise(R) e_pol=sample_noise(R)
def product_space(chi, k, weights=False, base_ring=None, verbose=False): r""" Computes all eisenstein series, and products of pairs of eisenstein series of lower weight, lying in the space of modular forms of weight $k$ and nebentypus $\chi$. INPUT: - chi - Dirichlet character, the nebentypus of the target space - k - an integer, the weight of the target space OUTPUT: - a matrix of coefficients of q-expansions, which are the products of Eisenstein series in M_k(chi). WARNING: It is only for principal chi that we know that the resulting space is the whole space of modular forms. """ if weights == False: weights = srange(1, k / 2 + 1) weight_dict = {} weight_dict[-1] = [w for w in weights if w % 2] # Odd weights weight_dict[1] = [w for w in weights if not w % 2] # Even weights try: N = chi.modulus() except AttributeError: if chi.parent() == ZZ: N = chi chi = DirichletGroup(N)[0] Id = DirichletGroup(1)[0] if chi(-1) != (-1)**k: raise ValueError('chi(-1)!=(-1)^k') sturm = ModularForms(N, k).sturm_bound() + 1 if N > 1: target_dim = dimension_modular_forms(chi, k) else: target_dim = dimension_modular_forms(1, k) D = DirichletGroup(N) # product_space should ideally be called over number fields. Over complex # numbers the exact linear algebra solutions might not exist. if base_ring == None: base_ring = CyclotomicField(euler_phi(N)) Q = PowerSeriesRing(base_ring, 'q') q = Q.gen() d = len(D) prim_chars = [phi.primitive_character() for phi in D] divs = divisors(N) products = Matrix(base_ring, []) indexlist = [] rank = 0 if verbose: print(D) print('Sturm bound', sturm) #TODO: target_dim needs refinment in the case of weight 2. print('Target dimension', target_dim) for i in srange(0, d): # First character phi = prim_chars[i] M1 = phi.conductor() for j in srange(0, d): # Second character psi = prim_chars[j] M2 = psi.conductor() if not M1 * M2 in divs: continue parity = psi(-1) * phi(-1) for t1 in divs: if not M1 * M2 * t1 in divs: continue #TODO: THE NEXT CONDITION NEEDS TO BE CORRECTED. THIS IS JUST A TEST if phi.bar() == psi and not ( k == 2): #and i==0 and j==0 and t1==1): E = eisenstein_series_at_inf(phi, psi, k, sturm, t1, base_ring).padded_list() try: products.T.solve_right(vector(base_ring, E)) except ValueError: products = Matrix(products.rows() + [E]) indexlist.append([k, i, j, t1]) rank += 1 if verbose: print('Added ', [k, i, j, t1]) print('Rank is now', rank) if rank == target_dim: return products, indexlist for t in divs: if not M1 * M2 * t1 * t in divs: continue for t2 in divs: if not M1 * M2 * t1 * t2 * t in divs: continue for l in weight_dict[parity]: if l == 1 and phi.is_odd(): continue if i == 0 and j == 0 and (l == 2 or l == k - 2): continue #TODO: THE NEXT CONDITION NEEDS TO BE REMOVED. THIS IS JUST A TEST if l == 2 or l == k - 2: continue E1 = eisenstein_series_at_inf( phi, psi, l, sturm, t1 * t, base_ring) E2 = eisenstein_series_at_inf( phi**(-1), psi**(-1), k - l, sturm, t2 * t, base_ring) #If chi is non-principal this needs to be changed to be something like chi*phi^(-1) instead of phi^(-1) E = (E1 * E2 + O(q**sturm)).padded_list() try: products.T.solve_right(vector(base_ring, E)) except ValueError: products = Matrix(products.rows() + [E]) indexlist.append([l, k - l, i, j, t1, t2, t]) rank += 1 if verbose: print('Added ', [l, k - l, i, j, t1, t2, t]) print('Rank', rank) if rank == target_dim: return products, indexlist return products, indexlist