def gen_instance(n, alpha, q, m, t=1): D = DiscreteGaussianDistributionIntegerSampler(alpha * q / sqrt(2 * pi)) lwe = LWE(n, q, D) Ac = [lwe() for _ in range(m)] A = matrix([a_ for a_, c_ in Ac]) c = vector(ZZ, [c_ for a_, c_ in Ac]) B = A.T.echelon_form() e = (c - A * lwe._LWE__s).change_ring(ZZ) def bm(x): return ZZ(x) if ZZ(x) < q // 2 else ZZ(x) - q e = vector(map(bm, e)) N = B.change_ring(ZZ) S = matrix(ZZ, m - n, n).augment(q * identity_matrix(ZZ, m - n)) L = (N.stack(S)).augment(matrix(ZZ, m, 1)) L = L.stack(matrix(c).augment(matrix(ZZ, 1, 1, [t]))) return L, e
def __init__(self, n, delta=0.01, m=None): """ Construct LWE instance parameterised by security parameter ``n`` where the modulus ``q`` and the ``stddev`` of the noise is chosen as in [LP2011]_. INPUT: - ``n`` - security parameter (integer > 0) - ``delta`` - error probability per symbol (default: 0.01) - ``m`` - number of allowed samples or ``None`` in which case ``m=2*n + 128`` as in [LP2011]_ (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import LindnerPeikert sage: LindnerPeikert(n=20) LWE(20, 2053, Discrete Gaussian sampler over the Integers with sigma = 3.600954 and c = 0, 'noise', 168) """ if m is None: m = 2 * n + 128 # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40 # (c*exp((1-c**2)/2))**(2*n) == 2**-40 # log((c*exp((1-c**2)/2))**(2*n)) == -40*log(2) # (2*n)*log(c*exp((1-c**2)/2)) == -40*log(2) # 2*n*(log(c)+log(exp((1-c**2)/2))) == -40*log(2) # 2*n*(log(c)+(1-c**2)/2) == -40*log(2) # 2*n*log(c)+n*(1-c**2) == -40*log(2) # 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 = DiscreteGaussianDistributionIntegerSampler(stddev) LWE.__init__(self, n=n, q=q, D=D, secret_dist='noise', m=m)
def encrypt_lwe(self, m, t, n, q, s): # m : message, t: The parameter of BV11 if t > q: print "t must be smaller than the modulus q.\n Please choose a new t" return 0 if t <= m: print "*the first argument m* must be larger than (the second argument) t " return 0 # Every time that we call the function encrypt_lwe, a new "a" and "e" is generated. # So, for the same message we have a different ciphertext. # That is, the encryption is probabilistic (and not determenistic) S = samples( 1, n, lwe ) # this is a function implemented in sagemath which returns a vector \bf{a} for lwe a = vector(ZZ, S[0][0]) # the vector a, we transform it to a vector over Z D = DiscreteGaussianDistributionIntegerSampler(sigma=sigma) e = int(D()) ### TODO : YOU must allow e to take negative values ### Q : It is the same if we consider emod q instead of e<0 ? if e < 0: e = q - e # e maybe positve or zero or negative dot = int( a.dot_product(s) % q) #dot is integer, this will be the *aux* polynomial in decrypt() dot_q = int((dot + t * e + m) % q) # this is the encryption of m, where m\in (-t/2,t/2), m!=-t/2,t/2 # dot_q always is in {0,1,...,q-1} # if representation_modulo_t(dot_q - dot,t) == representation_modulo_t(t*e + m,t): # print "ok" c = [a, dot_q] return c, a #,e # note that e does not send to Bob, onle c is sent to Bob
def __init__(self, n, secret_dist='uniform', m=None): """ Construct LWE instance parameterised by security parameter ``n`` where the modulus ``q`` and the ``stddev`` of the noise are chosen as in [Reg09]_. INPUT: - ``n`` - security parameter (integer > 0) - ``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 Regev sage: Regev(n=20) LWE(20, 401, Discrete Gaussian sampler over the Integers with sigma = 1.915069 and c = 401, 'uniform', None) """ q = ZZ(next_prime(n**2)) s = RR(1 / (RR(n).sqrt() * log(n, 2)**2) * q) D = DiscreteGaussianDistributionIntegerSampler(s / sqrt(2 * pi.n()), q) LWE.__init__(self, n=n, q=q, D=D, secret_dist=secret_dist, m=m)
q=2^54 d=1024 t=83 T=100 l=floor(log(q,T).n()) P.<x>=PolynomialRing(ZZ) f=x^d+1 R.<X>=P.quotient(f) Zq=Zmod(q) Zt=Zmod(t) Pq.<x>=PolynomialRing(Zq) Pt.<x>=PolynomialRing(Zt) sigma = 1.0 delta=floor(q/t) from sage.stats.distributions.discrete_gaussian_integer import DiscreteGaussianDistributionIntegerSampler D = DiscreteGaussianDistributionIntegerSampler(sigma=sigma) def sample_e(): return R([D() for _ in range(d)]) def sample_2(): return R([randint(0,1) for _ in range(d)]) def sample_r(): return R([randint(0,q-1) for _ in range(d)]) def rq(a): A=a.list() for i in range(len(A)): A[i]=A[i]%q return R(A) def rt(a): A=a.list()
def discrete_gaussian_y(PP, m, n, sigma): D = DiscreteGaussianDistributionIntegerSampler(sigma=sigma) return Matrix(PP.R, m, n, lambda i, j: PP.R(list(D() for _ in range(PP.d))))
def discrete_gaussian_vector_y(PP, n, sigma): D = DiscreteGaussianDistributionIntegerSampler(sigma=sigma) return list(PP.R(list(D() for _ in range(PP.d))) for j in range(n))
def attack(m, q, r=4, sigma=3.0, subfield_only=False): K = CyclotomicField(m, 'z') z = K.gen() OK = K.ring_of_integers() G = K.galois_group() n = euler_phi(m) mprime = m / r nprime = euler_phi(mprime) Gprime = [tau for tau in G if tau(z**r) == z**r] R = PolynomialRing(IntegerRing(), 'a') a = R.gen() phim = a**n + 1 D = DiscreteGaussianDistributionIntegerSampler(sigma) print "sampling f,g" while True: f = sum([D() * z**i for i in range(n)]) fx = sum([f[i] * a**i for i in range(n)]) res = inverse(fx, phim, q) if res[0]: f_inv = sum([res[1][i] * z**i for i in range(n)]) print "f_inv * f = %s (mod %d)" % ((f * f_inv).mod(q), q) break g = sum([D() * z**i for i in range(n)]) print "done sampling f, g" #h = [g*f^{-1)]_q h = (g * f_inv).mod(q) lognorm_f = log(f.vector().norm(), 2) lognorm_g = log(g.vector().norm(), 2) print "f*h - g = %s" % (f * h - g).mod(q) print "log q = ", log(q, 2).n(precision) print "log |f| = %s, log |g| = %s" % (lognorm_f.n(precision), lognorm_g.n(precision)) print "log |(f,g)| = ", log( sqrt(f.vector().norm()**2 + g.vector().norm()**2), 2).n(precision) print "begin computing N(f), N(g), N(h), Tr(h), fbar" fprime = norm(f, Gprime) gprime = norm(g, Gprime) hprime = norm(h, Gprime).mod(q) htr = trace(h, Gprime) fbar = prod([tau(f) for tau in Gprime[1:]]) print "end computing N(f), N(g), N(h), Tr(h), fbar" lognorm_fp = log(fprime.vector().norm(), 2) lognorm_gp = log(gprime.vector().norm(), 2) print "%d * log |f| - log |f'| = %s" % (r, r * lognorm_f.n(precision) - lognorm_fp.n(precision)) print "log |(f', g')| = ", log( sqrt(fprime.vector().norm()**2 + gprime.vector().norm()**2), 2).n(precision) print "log |N(f), Tr(g fbar)| = ", log( sqrt(fprime.vector().norm()**2 + trace(g * fbar, Gprime).vector().norm()**2), 2).n(precision) #(fprime, gprime) lies in the lattice \Lambda_hprime^q print "f'*h' - g' = %s " % (hprime * fprime - gprime).mod(q) print "N(f) Tr(h) - Tr(g fbar) = %s" % (htr * fprime - trace(g * fbar, Gprime)).mod(q) if not subfield_only: ntru_full = NTRU(h, K, q) full_sv = ntru_full.shortest_vector() print "log |v| = %s" % log(full_sv.norm(), 2).n(precision) ntru_subfield = NTRU_subfield(hprime, q, nprime, r) ntru_trace_subfield = NTRU_subfield(htr, q, nprime, r) print "begin computing Shortest Vector of subfield lattice" norm_sv = ntru_subfield.shortest_vector() tr_sv = ntru_trace_subfield.shortest_vector() print "end computing Shortest Vector of subfield lattice" norm_xp = sum( [coerce(Integer, norm_sv[i]) * z**(r * i) for i in range(nprime)]) tr_xp = sum( [coerce(Integer, tr_sv[i]) * z**(r * i) for i in range(nprime)]) print "Norm map: log |(x',y')| = ", log(norm_sv.norm(), 2).n(precision) print "Trace map: log |(x', y')| = ", log(tr_sv.norm(), 2).n(precision) #test if xprime belongs to <fprime> mat = [] for i in range(nprime): coordinate = (fprime * z**(r * i)).vector().list() mat.append([coordinate[r * j] for j in range(nprime)]) FL = IntegerLattice(mat) print norm_sv[:nprime] in FL print tr_sv[:nprime] in FL norm_x = norm_xp norm_y = mod_q(norm_x * h, q) tr_x = tr_xp tr_y = mod_q(tr_x * h, q) print "Norm map: log |(x,y)| = ", log( sqrt(norm_x.vector().norm()**2 + norm_y.vector().norm()**2), 2).n(precision) print "Trace map: log |(x,y)| = ", log( sqrt(tr_x.vector().norm()**2 + tr_y.vector().norm()**2), 2).n(precision)
def initialize_lwe(self, n, q, sigma): D = DiscreteGaussianDistributionIntegerSampler(sigma) lwe = LWE(n, q, D=D) secret_key = lwe._LWE__s return lwe, secret_key # exports the object lwe and the corresponding secret key