Ejemplo n.º 1
0
Archivo: lwe.py Proyecto: 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())
Ejemplo n.º 2
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())
Ejemplo n.º 3
0
Archivo: lwe.py Proyecto: sagemath/sage
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())
Ejemplo n.º 4
0
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 type(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())