Exemplo n.º 1
0
    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))
Exemplo n.º 2
0
    def gamma_h_subgroups(self):
        r"""
        Return the subgroups of the form `\Gamma_H(N)` contained
        in self, where `N` is the level of self.

        EXAMPLES::

            sage: G = Gamma0(11)
            sage: G.gamma_h_subgroups()
            [Congruence Subgroup Gamma0(11), Congruence Subgroup Gamma_H(11) with H generated by [4], Congruence Subgroup Gamma_H(11) with H generated by [10], Congruence Subgroup Gamma1(11)]
            sage: G = Gamma0(12)
            sage: G.gamma_h_subgroups()
            [Congruence Subgroup Gamma0(12), Congruence Subgroup Gamma_H(12) with H generated by [7], Congruence Subgroup Gamma_H(12) with H generated by [11], Congruence Subgroup Gamma_H(12) with H generated by [5], Congruence Subgroup Gamma1(12)]
        """
        from all import GammaH
        N = self.level()
        R = IntegerModRing(N)
        return [GammaH(N, H) for H in R.multiplicative_subgroups()]
Exemplo n.º 3
0
    def gamma_h_subgroups(self):
        r"""
        Return the subgroups of the form `\Gamma_H(N)` contained
        in self, where `N` is the level of self.

        EXAMPLES::

            sage: G = Gamma0(11)
            sage: G.gamma_h_subgroups()
            [Congruence Subgroup Gamma0(11), Congruence Subgroup Gamma_H(11) with H generated by [4], Congruence Subgroup Gamma_H(11) with H generated by [10], Congruence Subgroup Gamma1(11)]
            sage: G = Gamma0(12)
            sage: G.gamma_h_subgroups()
            [Congruence Subgroup Gamma0(12), Congruence Subgroup Gamma_H(12) with H generated by [7], Congruence Subgroup Gamma_H(12) with H generated by [11], Congruence Subgroup Gamma_H(12) with H generated by [5], Congruence Subgroup Gamma1(12)]
        """
        from .all import GammaH
        N = self.level()
        R = IntegerModRing(N)
        return [GammaH(N, H) for H in R.multiplicative_subgroups()]
Exemplo n.º 4
0
    def teichmuller_type(self):
        r"""
        Return the Teichmuller type of this weight-character `\kappa`, which is
        the unique `t \in \ZZ/(p-1)\ZZ` such that `\kappa(\mu) =
        \mu^t` for \mu a `(p-1)`-st root of 1.

        For `p = 2` this doesn't make sense, but we still want the Teichmuller
        type to correspond to the index of the component of weight space in
        which `\kappa` lies, so we return 1 if `\kappa` is odd and 0 otherwise.

        EXAMPLES::

            sage: pAdicWeightSpace(11)(2, DirichletGroup(11,QQ).0).teichmuller_type()
            7
            sage: pAdicWeightSpace(29)(13, DirichletGroup(29, Qp(29)).0).teichmuller_type()
            14
            sage: pAdicWeightSpace(2)(3, DirichletGroup(4,QQ).0).teichmuller_type()
            0
        """
        # Special case p == 2
        if self._p == 2:
            if self.is_even():
                return IntegerModRing(2)(0)
            else:
                return IntegerModRing(2)(1)
        m = IntegerModRing(self._p).multiplicative_generator()
        x = [
            y for y in IntegerModRing(self._chi.modulus())
            if y == m and y**(self._p - 1) == 1
        ]
        if len(x) != 1: raise ArithmeticError
        x = x[0]
        f = IntegerModRing(self._p)(self._chi(x)).log(m)
        return IntegerModRing(self._p - 1)(self._k + f)
Exemplo n.º 5
0
    def _generators_for_H(self):
        """
        Return generators for the subgroup H of the units mod
        self.level() that defines self.

        EXAMPLES::

            sage: Gamma0(15)._generators_for_H()
            [11, 7]
        """
        if self.level() in [1, 2]:
            return []
        return [ZZ(x) for x in IntegerModRing(self.level()).unit_gens()]
Exemplo n.º 6
0
Arquivo: lwe.py Projeto: sagemath/sage
    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))
Exemplo n.º 7
0
    def brown_invariant(self):
        r"""
        Return the Brown invariant of this torsion quadratic form.

        Let `(D,q)` be a torsion quadratic module with values in `\QQ / 2 \ZZ`.
        The Brown invariant `Br(D,q) \in \Zmod{8}` is defined by the equation

        .. MATH::

            \exp \left( \frac{2 \pi i }{8} Br(q)\right) =
            \frac{1}{\sqrt{D}} \sum_{x \in D} \exp(i \pi q(x)).

        The Brown invariant is additive with respect to direct sums of
        torsion quadratic modules.

        OUTPUT:

        - an element of `\Zmod{8}`

        EXAMPLES::

            sage: L = IntegralLattice("D4")
            sage: D = L.discriminant_group()
            sage: D.brown_invariant()
            4

        We require the quadratic form to be defined modulo `2 \ZZ`::

            sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule
            sage: V = FreeQuadraticModule(ZZ,3,matrix.identity(3))
            sage: T = TorsionQuadraticModule((1/10)*V, V)
            sage: T.brown_invariant()
            Traceback (most recent call last):
            ...
            ValueError: the torsion quadratic form must have values in QQ / 2 ZZ
        """
        if self._modulus_qf != 2:
            raise ValueError("the torsion quadratic form must have values in "
                             "QQ / 2 ZZ")
        from sage.quadratic_forms.genera.normal_form import collect_small_blocks
        brown = IntegerModRing(8).zero()
        for p in self.annihilator().gen().prime_divisors():
            q = self.primary_part(p).normal_form()
            q = q.gram_matrix_quadratic()
            L = collect_small_blocks(q)
            for qi in L:
                brown += _brown_indecomposable(qi, p)
        return brown
Exemplo n.º 8
0
def CO_delta(r, p, N, eps):
    r"""
    This is used as an intermediate value in computations related to
    the paper of Cohen-Oesterle.
    
    INPUT:
    
    
    -  ``r`` - positive integer
    
    -  ``p`` - a prime
    
    -  ``N`` - positive integer
    
    -  ``eps`` - character
    
    
    OUTPUT: element of the base ring of the character
    
    EXAMPLES::
    
        sage: G.<eps> = DirichletGroup(7)
        sage: sage.modular.dims.CO_delta(1,5,7,eps^3)
        2
    """
    if not is_prime(p):
        raise ValueError, "p must be prime"
    K = eps.base_ring()
    if p % 4 == 3:
        return K(0)
    if p == 2:
        if r == 1:
            return K(1)
        return K(0)
    # interesting case: p=1(mod 4).
    # omega is a primitive 4th root of unity mod p.
    omega = (IntegerModRing(p).unit_gens()[0])**((p - 1) // 4)
    # this n is within a p-power root of a "local" 4th root of 1 modulo p.
    n = Mod(int(omega.crt(Mod(1, N // (p**r)))), N)
    n = n**(p**(r - 1))  # this is correct now
    t = eps(n)
    if t == K(1):
        return K(2)
    if t == K(-1):
        return K(-2)
    return K(0)
Exemplo n.º 9
0
def CO_nu(r, p, N, eps):
    r"""
    This is used as an intermediate value in computations related to
    the paper of Cohen-Oesterle.
    
    INPUT:
    
    
    -  ``r`` - positive integer
    
    -  ``p`` - a prime
    
    -  ``N`` - positive integer
    
    -  ``eps`` - character
    
    
    OUTPUT: element of the base ring of the character
    
    EXAMPLES::
    
        sage: G.<eps> = DirichletGroup(7)
        sage: G.<eps> = DirichletGroup(7)
        sage: sage.modular.dims.CO_nu(1,7,7,eps)
        -1
    """
    K = eps.base_ring()
    if p % 3 == 2:
        return K(0)
    if p == 3:
        if r == 1:
            return K(1)
        return K(0)
    # interesting case: p=1(mod 3)
    # omega is a cube root of 1 mod p.
    omega = (IntegerModRing(p).unit_gens()[0])**((p - 1) // 3)
    n = Mod(omega.crt(Mod(1, N // (p**r))),
            N)  # within a p-power root of a "local" cube root of 1 mod p.
    n = n**(p**(r - 1))  # this is right now
    t = eps(n)
    if t == K(1):
        return K(2)
    return K(-1)
Exemplo n.º 10
0
Arquivo: lwe.py Projeto: Etn40ff/sage
class LWE(SageObject):
    """
    Learning with Errors (LWE) oracle.

    .. automethod:: __init__
    .. automethod:: __call__
    """
    def __init__(self, n, q, D, secret_dist='uniform', m=None):
        """
        Construct an LWE oracle in dimension ``n`` over a ring of order
        ``q`` with noise distribution ``D``.

        INPUT:

        - ``n`` - dimension (integer > 0)
        - ``q`` - modulus typically > n (integer > 0)
        - ``D`` - an error distribution such as an instance of
          :class:`DiscreteGaussianSamplerRejection` or :class:`UniformSampler`
        - ``secret_dist`` - distribution of the secret (default: 'uniform'); one of

          - "uniform" - secret follows the uniform distribution in `\Zmod{q}`
          - "noise" - secret follows the noise distribution
          - ``(lb,ub)`` - the secret is chosen uniformly from ``[lb,...,ub]`` including both endpoints

        - ``m`` - number of allowed samples or ``None`` if no such limit exists
          (default: ``None``)

        EXAMPLE:

        First, we construct a noise distribution with standard deviation 3.0::

            sage: from sage.crypto.lwe import DiscreteGaussianSampler
            sage: D = DiscreteGaussianSampler(3.0)

        Next, we construct our oracle::

            sage: from sage.crypto.lwe import LWE
            sage: lwe = LWE(n=20, q=next_prime(400), D=D); lwe
            LWE(20, 401, DiscreteGaussianSamplerRejection(3.000000, 53, 4), 'uniform', None)

        and sample 1000 samples::

            sage: L = [lwe() for _ in range(1000)]

        To test the oracle, we use the internal secret to evaluate the samples
        in the secret::

            sage: S = [ZZ(a.dot_product(lwe._LWE__s) - c) for (a,c) in L]

        However, while Sage represents finite field elements between 0 and q-1
        we rely on a balanced representation of those elements here. Hence, we
        fix the representation and recover the correct standard deviation of the
        noise::

            sage: sqrt(variance([e if e <= 200 else e-401 for e in S]).n())
            3.0...

        If ``m`` is not ``None`` the number of available samples is restricted::

            sage: from sage.crypto.lwe import LWE
            sage: lwe = LWE(n=20, q=next_prime(400), D=D, m=30)
            sage: _ = [lwe() for _ in range(30)]
            sage: lwe() # 31
            Traceback (most recent call last):
            ...
            IndexError: Number of available samples exhausted.
        """
        self.n  = ZZ(n)
        self.m =  m
        self.__i = 0
        self.K  = IntegerModRing(q)
        self.FM = FreeModule(self.K, n)
        self.D = D

        self.secret_dist = secret_dist
        if secret_dist == 'uniform':
            self.__s = random_vector(self.K, self.n)
        elif secret_dist == 'noise':
            self.__s = vector(self.K, self.n, [self.D() for _ in range(n)])
        else:
            try:
                lb, ub = map(ZZ,secret_dist)
                self.__s = vector(self.K, self.n, [randint(lb,ub) for _ in range(n)])
            except (IndexError, TypeError):
                raise TypeError("Parameter secret_dist=%s not understood."%(secret_dist))

    def _repr_(self):
        """
        EXAMPLE::

            sage: from sage.crypto.lwe import DiscreteGaussianSampler, LWE
            sage: D = DiscreteGaussianSampler(3.0)
            sage: lwe = LWE(n=20, q=next_prime(400), D=D); lwe
            LWE(20, 401, DiscreteGaussianSamplerRejection(3.000000, 53, 4), 'uniform', None)

            sage: lwe = LWE(n=20, q=next_prime(400), D=D, secret_dist=(-3, 3)); lwe
            LWE(20, 401, DiscreteGaussianSamplerRejection(3.000000, 53, 4), (-3, 3), None)
        """
        if isinstance(self.secret_dist, str):
            return "LWE(%d, %d, %s, '%s', %s)"%(self.n,self.K.order(),self.D,self.secret_dist, self.m)
        else:
            return "LWE(%d, %d, %s, %s, %s)"%(self.n,self.K.order(),self.D,self.secret_dist, self.m)


    def __call__(self):
        """
        EXAMPLE::

            sage: from sage.crypto.lwe import DiscreteGaussianSampler, LWE
            sage: LWE(10, 401, DiscreteGaussianSampler(3))()
            ((309, 347, 198, 194, 336, 360, 264, 123, 368, 398), 198)
        """
        if self.m is not None:
            if self.__i >= self.m:
                raise IndexError("Number of available samples exhausted.")
        self.__i+=1
        a = self.FM.random_element()
        return a, a.dot_product(self.__s) + self.K(self.D())
Exemplo n.º 11
0
class LWE(SageObject):
    """
    Learning with Errors (LWE) oracle.

    .. automethod:: __init__
    .. automethod:: __call__
    """
    def __init__(self, n, q, D, secret_dist='uniform', m=None):
        r"""
        Construct an LWE oracle in dimension ``n`` over a ring of order
        ``q`` with noise distribution ``D``.

        INPUT:

        - ``n`` - dimension (integer > 0)
        - ``q`` - modulus typically > n (integer > 0)
        - ``D`` - an error distribution such as an instance of
          :class:`DiscreteGaussianDistributionIntegerSampler` or :class:`UniformSampler`
        - ``secret_dist`` - distribution of the secret (default: 'uniform'); one of

          - "uniform" - secret follows the uniform distribution in `\Zmod{q}`
          - "noise" - secret follows the noise distribution
          - ``(lb,ub)`` - the secret is chosen uniformly from ``[lb,...,ub]`` including both endpoints

        - ``m`` - number of allowed samples or ``None`` if no such limit exists
          (default: ``None``)

        EXAMPLES:

        First, we construct a noise distribution with standard deviation 3.0::

            sage: from sage.stats.distributions.discrete_gaussian_integer import DiscreteGaussianDistributionIntegerSampler
            sage: D = DiscreteGaussianDistributionIntegerSampler(3.0)

        Next, we construct our oracle::

            sage: from sage.crypto.lwe import LWE
            sage: lwe = LWE(n=20, q=next_prime(400), D=D); lwe
            LWE(20, 401, Discrete Gaussian sampler over the Integers with sigma = 3.000000 and c = 0, 'uniform', None)

        and sample 1000 samples::

            sage: L = [lwe() for _ in range(1000)]

        To test the oracle, we use the internal secret to evaluate the samples
        in the secret::

            sage: S = [ZZ(a.dot_product(lwe._LWE__s) - c) for (a,c) in L]

        However, while Sage represents finite field elements between 0 and q-1
        we rely on a balanced representation of those elements here. Hence, we
        fix the representation and recover the correct standard deviation of the
        noise::

            sage: sqrt(variance([e if e <= 200 else e-401 for e in S]).n())
            3.0...

        If ``m`` is not ``None`` the number of available samples is restricted::

            sage: from sage.crypto.lwe import LWE
            sage: lwe = LWE(n=20, q=next_prime(400), D=D, m=30)
            sage: _ = [lwe() for _ in range(30)]
            sage: lwe() # 31
            Traceback (most recent call last):
            ...
            IndexError: Number of available samples exhausted.
        """
        self.n = ZZ(n)
        self.m = m
        self.__i = 0
        self.K = IntegerModRing(q)
        self.FM = FreeModule(self.K, n)
        self.D = D

        self.secret_dist = secret_dist
        if secret_dist == 'uniform':
            self.__s = random_vector(self.K, self.n)
        elif secret_dist == 'noise':
            self.__s = vector(self.K, self.n, [self.D() for _ in range(n)])
        else:
            try:
                lb, ub = map(ZZ, secret_dist)
                self.__s = vector(self.K, self.n,
                                  [randint(lb, ub) for _ in range(n)])
            except (IndexError, TypeError):
                raise TypeError("Parameter secret_dist=%s not understood." %
                                (secret_dist))

    def _repr_(self):
        """
        EXAMPLES::

            sage: from sage.stats.distributions.discrete_gaussian_integer import DiscreteGaussianDistributionIntegerSampler
            sage: from sage.crypto.lwe import LWE
            sage: D = DiscreteGaussianDistributionIntegerSampler(3.0)
            sage: lwe = LWE(n=20, q=next_prime(400), D=D); lwe
            LWE(20, 401, Discrete Gaussian sampler over the Integers with sigma = 3.000000 and c = 0, 'uniform', None)

            sage: lwe = LWE(n=20, q=next_prime(400), D=D, secret_dist=(-3, 3)); lwe
            LWE(20, 401, Discrete Gaussian sampler over the Integers with sigma = 3.000000 and c = 0, (-3, 3), None)
        """
        if isinstance(self.secret_dist, str):
            return "LWE(%d, %d, %s, '%s', %s)" % (
                self.n, self.K.order(), self.D, self.secret_dist, self.m)
        else:
            return "LWE(%d, %d, %s, %s, %s)" % (self.n, self.K.order(), self.D,
                                                self.secret_dist, self.m)

    def __call__(self):
        """
        EXAMPLES::

            sage: from sage.crypto.lwe import DiscreteGaussianDistributionIntegerSampler, LWE
            sage: LWE(10, 401, DiscreteGaussianDistributionIntegerSampler(3))()
            ((309, 347, 198, 194, 336, 360, 264, 123, 368, 398), 198)
        """
        if self.m is not None:
            if self.__i >= self.m:
                raise IndexError("Number of available samples exhausted.")
        self.__i += 1
        a = self.FM.random_element()
        return a, a.dot_product(self.__s) + self.K(self.D())
Exemplo n.º 12
0
def balance_sample(s, q=None):
    r"""
    Given ``(a,c) = s`` return a tuple ``(a',c')`` where ``a'`` is an integer
    vector with entries between -q//2 and q//2 and ``c`` is also within these
    bounds.

    If ``q`` is given ``(a,c) = s`` may live in the integers. If ``q`` is not
    given, then ``(a,c)`` are assumed to live in `\Zmod{q}`.

    INPUT:

    - ``s`` - sample of the form (a,c) where a is a vector and c is a scalar
    - ``q`` - modulus (default: ``None``)

    EXAMPLES::

        sage: from sage.crypto.lwe import balance_sample, samples, Regev
        sage: [balance_sample(s) for s in samples(10, 5, Regev)]
        [((-9, -4, -4, 4, -4), 4), ((-8, 11, 12, -11, -11), -7),
        ...
        ((-11, 12, 0, -6, -3), 7), ((-7, 14, 8, 11, -8), -12)]


        sage: from sage.crypto.lwe import balance_sample, DiscreteGaussianDistributionPolynomialSampler, RingLWE, samples
        sage: D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], 8, 5)
        sage: rlwe = RingLWE(20, 257, D)
        sage: [balance_sample(s) for s in samples(10, 8, rlwe)]
        [((-64, 107, -91, -24, 120, 54, 38, -35), (-84, 121, 28, -99, 91, 54, -60, 11)),
        ...
        ((-40, -117, 35, -69, -11, 10, 122, 48), (-80, -2, 119, -91, 27, 66, 121, -1))]

    .. note::

        This function is useful to convert between Sage's standard
        representation of elements in `\Zmod{q}` as integers between 0 and q-1
        and the usual representation of such elements in lattice cryptography as
        integers between -q//2 and q//2.
    """
    a, c = s

    try:
        c[0]
        scalar = False
    except TypeError:
        c = vector(c.parent(), [c])
        scalar = True

    if q is None:
        q = parent(c[0]).order()
        a = a.change_ring(ZZ)
        c = c.change_ring(ZZ)
    else:
        K = IntegerModRing(q)
        a = a.change_ring(K).change_ring(ZZ)
        c = c.change_ring(K).change_ring(ZZ)

    q2 = q // 2

    if scalar:
        return vector(ZZ, len(a),
                      [e if e <= q2 else e - q
                       for e in a]), c[0] if c[0] <= q2 else c[0] - q
    else:
        return vector(ZZ, len(a),
                      [e if e <= q2 else e - q for e in a]), vector(
                          ZZ, len(c), [e if e <= q2 else e - q for e in c])
Exemplo n.º 13
0
Arquivo: lwe.py Projeto: sagemath/sage
class RingLWE(SageObject):
    """
    Ring Learning with Errors oracle.

    .. automethod:: __init__
    .. automethod:: __call__
    """
    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 _repr_(self):
        """
        EXAMPLES::

            sage: from sage.crypto.lwe import DiscreteGaussianDistributionPolynomialSampler, RingLWE
            sage: D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n=8, sigma=3.0)
            sage: RingLWE(N=16, q=next_prime(400), D=D)
            RingLWE(16, 401, Discrete Gaussian sampler for polynomials of degree < 8 with σ=3.000000 in each component, x^8 + 1, 'uniform', None)
        """
        if isinstance(self.secret_dist, str):
            return "RingLWE(%d, %d, %s, %s, '%s', %s)"%(self.N, self.K.order(), self.D, self.poly, self.secret_dist, self.m)
        else:
            return "RingLWE(%d, %d, %s, %s, %s, %s)"%(self.N, self.K.order(), self.D, self.poly, self.secret_dist, self.m)


    def __call__(self):
        """
        EXAMPLES::

            sage: from sage.crypto.lwe import DiscreteGaussianDistributionPolynomialSampler, RingLWE
            sage: N = 16
            sage: n = euler_phi(N)
            sage: D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n, 5)
            sage: ringlwe = RingLWE(N, 257, D, secret_dist='uniform')
            sage: ringlwe()
            ((226, 198, 38, 222, 222, 127, 194, 124), (11, 191, 177, 59, 105, 203, 108, 42))
        """
        if self.m is not None:
            if self.__i >= self.m:
                raise IndexError("Number of available samples exhausted.")
        self.__i+=1
        a = self.R_q.random_element()
        return vector(a), vector(a * (self.__s) + self.D())
Exemplo n.º 14
0
    def __init__(self, n, q, D, secret_dist='uniform', m=None):
        """
        Construct an LWE oracle in dimension ``n`` over a ring of order
        ``q`` with noise distribution ``D``.

        INPUT:

        - ``n`` - dimension (integer > 0)
        - ``q`` - modulus typically > n (integer > 0)
        - ``D`` - an error distribution such as an instance of
          :class:`DiscreteGaussianSamplerRejection` or :class:`UniformSampler`
        - ``secret_dist`` - distribution of the secret (default: 'uniform'); one of

          - "uniform" - secret follows the uniform distribution in `\Zmod{q}`
          - "noise" - secret follows the noise distribution
          - ``(lb,ub)`` - the secret is chosen uniformly from ``[lb,...,ub]`` including both endpoints

        - ``m`` - number of allowed samples or ``None`` if no such limit exists
          (default: ``None``)

        EXAMPLE:

        First, we construct a noise distribution with standard deviation 3.0::

            sage: from sage.crypto.lwe import DiscreteGaussianSampler
            sage: D = DiscreteGaussianSampler(3.0)

        Next, we construct our oracle::

            sage: from sage.crypto.lwe import LWE
            sage: lwe = LWE(n=20, q=next_prime(400), D=D); lwe
            LWE(20, 401, DiscreteGaussianSamplerRejection(3.000000, 53, 4), 'uniform', None)

        and sample 1000 samples::

            sage: L = [lwe() for _ in range(1000)]

        To test the oracle, we use the internal secret to evaluate the samples
        in the secret::

            sage: S = [ZZ(a.dot_product(lwe._LWE__s) - c) for (a,c) in L]

        However, while Sage represents finite field elements between 0 and q-1
        we rely on a balanced representation of those elements here. Hence, we
        fix the representation and recover the correct standard deviation of the
        noise::

            sage: sqrt(variance([e if e <= 200 else e-401 for e in S]).n())
            3.0...

        If ``m`` is not ``None`` the number of available samples is restricted::

            sage: from sage.crypto.lwe import LWE
            sage: lwe = LWE(n=20, q=next_prime(400), D=D, m=30)
            sage: _ = [lwe() for _ in range(30)]
            sage: lwe() # 31
            Traceback (most recent call last):
            ...
            IndexError: Number of available samples exhausted.
        """
        self.n = ZZ(n)
        self.m = m
        self.__i = 0
        self.K = IntegerModRing(q)
        self.FM = FreeModule(self.K, n)
        self.D = D

        self.secret_dist = secret_dist
        if secret_dist == 'uniform':
            self.__s = random_vector(self.K, self.n)
        elif secret_dist == 'noise':
            self.__s = vector(self.K, self.n, [self.D() for _ in range(n)])
        else:
            try:
                lb, ub = map(ZZ, secret_dist)
                self.__s = vector(self.K, self.n,
                                  [randint(lb, ub) for _ in range(n)])
            except (IndexError, TypeError):
                raise TypeError("Parameter secret_dist=%s not understood." %
                                (secret_dist))
Exemplo n.º 15
0
Arquivo: lwe.py Projeto: sagemath/sage
    def __init__(self, n, q, D, secret_dist='uniform', m=None):
        r"""
        Construct an LWE oracle in dimension ``n`` over a ring of order
        ``q`` with noise distribution ``D``.

        INPUT:

        - ``n`` - dimension (integer > 0)
        - ``q`` - modulus typically > n (integer > 0)
        - ``D`` - an error distribution such as an instance of
          :class:`DiscreteGaussianDistributionIntegerSampler` or :class:`UniformSampler`
        - ``secret_dist`` - distribution of the secret (default: 'uniform'); one of

          - "uniform" - secret follows the uniform distribution in `\Zmod{q}`
          - "noise" - secret follows the noise distribution
          - ``(lb,ub)`` - the secret is chosen uniformly from ``[lb,...,ub]`` including both endpoints

        - ``m`` - number of allowed samples or ``None`` if no such limit exists
          (default: ``None``)

        EXAMPLES:

        First, we construct a noise distribution with standard deviation 3.0::

            sage: from sage.stats.distributions.discrete_gaussian_integer import DiscreteGaussianDistributionIntegerSampler
            sage: D = DiscreteGaussianDistributionIntegerSampler(3.0)

        Next, we construct our oracle::

            sage: from sage.crypto.lwe import LWE
            sage: lwe = LWE(n=20, q=next_prime(400), D=D); lwe
            LWE(20, 401, Discrete Gaussian sampler over the Integers with sigma = 3.000000 and c = 0, 'uniform', None)

        and sample 1000 samples::

            sage: L = [lwe() for _ in range(1000)]

        To test the oracle, we use the internal secret to evaluate the samples
        in the secret::

            sage: S = [ZZ(a.dot_product(lwe._LWE__s) - c) for (a,c) in L]

        However, while Sage represents finite field elements between 0 and q-1
        we rely on a balanced representation of those elements here. Hence, we
        fix the representation and recover the correct standard deviation of the
        noise::

            sage: sqrt(variance([e if e <= 200 else e-401 for e in S]).n())
            3.0...

        If ``m`` is not ``None`` the number of available samples is restricted::

            sage: from sage.crypto.lwe import LWE
            sage: lwe = LWE(n=20, q=next_prime(400), D=D, m=30)
            sage: _ = [lwe() for _ in range(30)]
            sage: lwe() # 31
            Traceback (most recent call last):
            ...
            IndexError: Number of available samples exhausted.
        """
        self.n  = ZZ(n)
        self.m =  m
        self.__i = 0
        self.K  = IntegerModRing(q)
        self.FM = FreeModule(self.K, n)
        self.D = D

        self.secret_dist = secret_dist
        if secret_dist == 'uniform':
            self.__s = random_vector(self.K, self.n)
        elif secret_dist == 'noise':
            self.__s = vector(self.K, self.n, [self.D() for _ in range(n)])
        else:
            try:
                lb, ub = map(ZZ, secret_dist)
                self.__s = vector(self.K, self.n, [randint(lb,ub) for _ in range(n)])
            except (IndexError, TypeError):
                raise TypeError("Parameter secret_dist=%s not understood."%(secret_dist))
Exemplo n.º 16
0
class RingLWE(SageObject):
    """
    Ring Learning with Errors oracle.

    .. automethod:: __init__
    .. automethod:: __call__
    """
    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:`DiscreteGaussianPolynomialSamplerRejection` 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``)

        EXAMPLE::

            sage: from sage.crypto.lwe import DiscreteGaussianPolynomialSampler, RingLWE
            sage: D = DiscreteGaussianPolynomialSampler(n=euler_phi(20), stddev=3.0)
            sage: RingLWE(N=20, q=next_prime(800), D=D);
            RingLWE(20, 809, DiscreteGaussianPolynomialSamplerRejection(8, 3.000000, 53, 4), 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 _repr_(self):
        """
        EXAMPLE::

            sage: from sage.crypto.lwe import DiscreteGaussianPolynomialSampler, RingLWE
            sage: D = DiscreteGaussianPolynomialSampler(n=8, stddev=3.0)
            sage: RingLWE(N=16, q=next_prime(400), D=D);
            RingLWE(16, 401, DiscreteGaussianPolynomialSamplerRejection(8, 3.000000, 53, 4), x^8 + 1, 'uniform', None)
        """
        if type(self.secret_dist) == str:
            return "RingLWE(%d, %d, %s, %s, '%s', %s)" % (self.N, self.K.order(
            ), self.D, self.poly, self.secret_dist, self.m)
        else:
            return "RingLWE(%d, %d, %s, %s, %s, %s)" % (self.N, self.K.order(
            ), self.D, self.poly, self.secret_dist, self.m)

    def __call__(self):
        """
        EXAMPLE::

            sage: from sage.crypto.lwe import DiscreteGaussianPolynomialSampler, RingLWE
            sage: N = 16
            sage: n = euler_phi(N)
            sage: D = DiscreteGaussianPolynomialSampler(n, 5)
            sage: ringlwe = RingLWE(N, 257, D, secret_dist='uniform')
            sage: ringlwe()
            ((228, 149, 226, 198, 38, 222, 222, 127), (177, 138, 68, 134, 74, 162, 203, 243))
        """
        if self.m is not None:
            if self.__i >= self.m:
                raise IndexError("Number of available samples exhausted.")
        self.__i += 1
        a = self.R_q.random_element()
        return vector(a), vector(a * (self.__s) + self.D())
Exemplo n.º 17
0
def lfun_character(chi):
    """
    Create the L-function of a primitive Dirichlet character.

    If the given character is not primitive, it is replaced by its
    associated primitive character.

    OUTPUT:

    one :pari:`lfun` object

    EXAMPLES::

        sage: from sage.lfunctions.pari import lfun_character, LFunction
        sage: chi = DirichletGroup(6).gen().primitive_character()
        sage: L = LFunction(lfun_character(chi))
        sage: L(3)
        1.20205690315959

    TESTS:

    A non-primitive one::

        sage: L = LFunction(lfun_character(DirichletGroup(6).gen()))
        sage: L(4)
        1.08232323371114

    With complex arguments::

        sage: from sage.lfunctions.pari import lfun_character, LFunction
        sage: chi = DirichletGroup(6, CC).gen().primitive_character()
        sage: L = LFunction(lfun_character(chi))
        sage: L(3)
        1.20205690315959
    """
    if not chi.is_primitive():
        chi = chi.primitive_character()

    conductor = chi.conductor()
    G = pari.znstar(conductor, 1)

    pari_orders = [pari(o) for o in G[2][1]]
    pari_gens = IntegerModRing(conductor).unit_gens(algorithm="pari")
    # should coincide with G[2][2]

    values_on_gens = (chi(x) for x in pari_gens)

    # now compute the input for pari (list of exponents)
    P = chi.parent()
    if is_ComplexField(P.base_ring()):
        zeta = P.zeta()
        zeta_argument = zeta.argument()
        v = [int(x.argument() / zeta_argument) for x in values_on_gens]
    else:
        dlog = P._zeta_dlog
        v = [dlog[x] for x in values_on_gens]

    m = P.zeta_order()
    v = [(vi * oi) // m for vi, oi in zip(v, pari_orders)]

    return pari.lfuncreate([G, v])
Exemplo n.º 18
0
def balance_sample(s, q=None):
    r"""
    Given ``(a,c) = s`` return a tuple ``(a',c')`` where ``a'`` is an integer
    vector with entries between -q//2 and q//2 and ``c`` is also within these
    bounds.

    If ``q`` is given ``(a,c) = s`` may live in the integers. If ``q`` is not
    given, then ``(a,c)`` are assumed to live in `\Zmod{q}`.

    INPUT:

    - ``s`` - sample of the form (a,c) where a is a vector and c is a scalar
    - ``q`` - modulus (default: ``None``)

    EXAMPLE::

        sage: from sage.crypto.lwe import balance_sample, samples, Regev
        sage: map(balance_sample, samples(10, 5, Regev))
        [((-9, -4, -4, 4, -4), 6), ((-3, -10, 8, -3, -1), -10), ((-6, -12, -3, -2, -6), -6),
        ...
        ((-1, -8, -11, 13, 4), -6), ((10, 11, -3, -13, 0), 6), ((6, -1, 2, -11, 14), 2)]


        sage: from sage.crypto.lwe import balance_sample, DiscreteGaussianPolynomialSampler, RingLWE, samples
        sage: D = DiscreteGaussianPolynomialSampler(8, 5)
        sage: rlwe = RingLWE(20, 257, D)
        sage: map(balance_sample, samples(10, 8, rlwe))
        [((5, -55, -31, -90, 6, 100, -46, -107), (6, -64, -40, 117, 27, 54, -98, -56)),
         ((109, -106, 28, 77, -14, -109, 115, 34), (82, 17, -89, 62, 1, -77, 128, 64)),
         ...
         ((-32, 51, -110, -106, 35, -82, 14, -113), (126, -120, 126, 119, 101, 3, -122, -75))]

    .. note::

        This function is useful to convert between Sage's standard
        representation of elements in `\Zmod{q}` as integers between 0 and q-1
        and the usual representation of such elements in lattice cryptography as
        integers between -q//2 and q//2.
    """
    a, c = s

    try:
        c[0]
        scalar = False
    except TypeError:
        c = vector(c.parent(), [c])
        scalar = True

    if q is None:
        q = parent(c[0]).order()
        a = a.change_ring(ZZ)
        c = c.change_ring(ZZ)
    else:
        K = IntegerModRing(q)
        a = a.change_ring(K).change_ring(ZZ)
        c = c.change_ring(K).change_ring(ZZ)

    q2 = q // 2

    if scalar:
        return vector(ZZ, len(a),
                      [e if e <= q2 else e - q
                       for e in a]), c[0] if c[0] <= q2 else c[0] - q
    else:
        return vector(ZZ, len(a),
                      [e if e <= q2 else e - q for e in a]), vector(
                          ZZ, len(c), [e if e <= q2 else e - q for e in c])
Exemplo n.º 19
0
def balance_sample(s, q=None):
    r"""
    Given ``(a,c) = s`` return a tuple ``(a',c')`` where ``a'`` is an integer
    vector with entries between -q//2 and q//2 and ``c`` is also within these
    bounds.

    If ``q`` is given ``(a,c) = s`` may live in the integers. If ``q`` is not
    given, then ``(a,c)`` are assumed to live in `\Zmod{q}`.

    INPUT:

    - ``s`` - sample of the form (a,c) where a is a vector and c is a scalar
    - ``q`` - modulus (default: ``None``)

    EXAMPLES::

        sage: from sage.crypto.lwe import balance_sample, samples, Regev
        sage: for s in samples(10, 5, Regev):
        ....:     b = balance_sample(s)
        ....:     assert all(-29//2 <= c <= 29//2 for c in b[0])
        ....:     assert -29//2 <= b[1] <= 29//2
        ....:     assert all(s[0][j] == b[0][j] % 29 for j in range(5))
        ....:     assert s[1] == b[1] % 29


        sage: from sage.crypto.lwe import balance_sample, DiscreteGaussianDistributionPolynomialSampler, RingLWE, samples
        sage: D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], 8, 5)
        sage: rlwe = RingLWE(20, 257, D)
        sage: for s in samples(10, 8, rlwe):
        ....:     b = balance_sample(s)
        ....:     assert all(-257//2 <= c <= 257//2 for bi in b for c in bi)
        ....:     assert all(s[i][j] == b[i][j] % 257 for i in range(2) for j in range(8))

    .. note::

        This function is useful to convert between Sage's standard
        representation of elements in `\Zmod{q}` as integers between 0 and q-1
        and the usual representation of such elements in lattice cryptography as
        integers between -q//2 and q//2.
    """
    a, c = s

    try:
        c[0]
        scalar = False
    except TypeError:
        c = vector(c.parent(), [c])
        scalar = True

    if q is None:
        q = parent(c[0]).order()
        a = a.change_ring(ZZ)
        c = c.change_ring(ZZ)
    else:
        K = IntegerModRing(q)
        a = a.change_ring(K).change_ring(ZZ)
        c = c.change_ring(K).change_ring(ZZ)

    q2 = q // 2

    if scalar:
        return vector(ZZ, len(a),
                      [e if e <= q2 else e - q
                       for e in a]), c[0] if c[0] <= q2 else c[0] - q
    else:
        return vector(ZZ, len(a),
                      [e if e <= q2 else e - q for e in a]), vector(
                          ZZ, len(c), [e if e <= q2 else e - q for e in c])