# (Ring-)LWE oracle generators#

The Learning with Errors problem (LWE) is solving linear systems of equations where the right hand side has been disturbed ‘slightly’ where ‘slightly’ is made precise by a noise distribution - typically a discrete Gaussian distribution. See [Reg09] for details.

The Ring Learning with Errors problem (LWE) is solving a set of univariate polynomial equations - typically in a cyclotomic field - where the right hand side was disturbed ‘slightly’. See [LPR2010] for details.

This module implements generators of LWE samples where parameters are chosen following proposals in the cryptographic literature.

EXAMPLES:

We get 30 samples from an LWE oracle parameterised by security parameter n=20 and where the modulus and the standard deviation of the noise are chosen as in [Reg09]:

sage: from sage.crypto.lwe import samples
sage: S = samples(30, 20, 'Regev')
sage: len(S)
30
sage: S[0][0].parent(), S[0][1].parent()
(Vector space of dimension 20 over Ring of integers modulo 401,
Ring of integers modulo 401)

>>> from sage.all import *
>>> from sage.crypto.lwe import samples
>>> S = samples(Integer(30), Integer(20), 'Regev')
>>> len(S)
30
>>> S[Integer(0)][Integer(0)].parent(), S[Integer(0)][Integer(1)].parent()
(Vector space of dimension 20 over Ring of integers modulo 401,
Ring of integers modulo 401)


We may also pass classes to the samples function, which is useful for users implementing their own oracles:

sage: from sage.crypto.lwe import samples, LindnerPeikert
sage: S = samples(30, 20, LindnerPeikert)
sage: len(S)
30
sage: S[0][0].parent(), S[0][1].parent()
(Vector space of dimension 20 over Ring of integers modulo 2053,
Ring of integers modulo 2053)

>>> from sage.all import *
>>> from sage.crypto.lwe import samples, LindnerPeikert
>>> S = samples(Integer(30), Integer(20), LindnerPeikert)
>>> len(S)
30
>>> S[Integer(0)][Integer(0)].parent(), S[Integer(0)][Integer(1)].parent()
(Vector space of dimension 20 over Ring of integers modulo 2053,
Ring of integers modulo 2053)


Finally, samples() also accepts instances of classes:

sage: from sage.crypto.lwe import LindnerPeikert
sage: lwe = LindnerPeikert(20)
sage: S = samples(30, 20, lwe)
sage: len(S)
30
sage: S[0][0].parent(), S[0][1].parent()
(Vector space of dimension 20 over Ring of integers modulo 2053,
Ring of integers modulo 2053)

>>> from sage.all import *
>>> from sage.crypto.lwe import LindnerPeikert
>>> lwe = LindnerPeikert(Integer(20))
>>> S = samples(Integer(30), Integer(20), lwe)
>>> len(S)
30
>>> S[Integer(0)][Integer(0)].parent(), S[Integer(0)][Integer(1)].parent()
(Vector space of dimension 20 over Ring of integers modulo 2053,
Ring of integers modulo 2053)


Note that Ring-LWE samples are returned as vectors:

sage: # needs sage.libs.pari
sage: from sage.crypto.lwe import RingLWE
sage: from sage.stats.distributions.discrete_gaussian_polynomial import DiscreteGaussianDistributionPolynomialSampler
sage: D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], euler_phi(16), 5)
sage: ringlwe = RingLWE(16, 257, D, secret_dist='uniform')
sage: p = samples(30, euler_phi(16), ringlwe)[0][0].parent(); p
Vector space of dimension 8 over Ring of integers modulo 257
sage: assert all(c.parent() is p for b in samples(30, euler_phi(16), ringlwe) for c in b)

>>> from sage.all import *
>>> # needs sage.libs.pari
>>> from sage.crypto.lwe import RingLWE
>>> from sage.stats.distributions.discrete_gaussian_polynomial import DiscreteGaussianDistributionPolynomialSampler
>>> D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], euler_phi(Integer(16)), Integer(5))
>>> ringlwe = RingLWE(Integer(16), Integer(257), D, secret_dist='uniform')
>>> p = samples(Integer(30), euler_phi(Integer(16)), ringlwe)[Integer(0)][Integer(0)].parent(); p
Vector space of dimension 8 over Ring of integers modulo 257
>>> assert all(c.parent() is p for b in samples(Integer(30), euler_phi(Integer(16)), ringlwe) for c in b)


One technical issue when working with these generators is that by default they return vectors and scalars over/in rings modulo some $$q$$. These are represented as elements in $$(0,q-1)$$ by Sage. However, it usually is more natural to think of these entries as integers in $$(-q//2,q//2)$$. To allow for this, this module provides the option to balance the representation. In this case vectors and scalars over/in the integers are returned:

sage: from sage.crypto.lwe import samples
sage: for s in samples(30, 20, 'Regev', balanced=True):
....:     s1 = list(s[0]) + [s[1]]
....:     assert all(-401//2 <= b <= 401//2 for b in s1)

>>> from sage.all import *
>>> from sage.crypto.lwe import samples
>>> for s in samples(Integer(30), Integer(20), 'Regev', balanced=True):
...     s1 = list(s[Integer(0)]) + [s[Integer(1)]]
...     assert all(-Integer(401)//Integer(2) <= b <= Integer(401)//Integer(2) for b in s1)


AUTHORS:

• Martin Albrecht

• Robert Fitzpatrick

• Daniel Cabracas

• Florian Göpfert

• Michael Schneider

REFERENCES:

class sage.crypto.lwe.LWE(n, q, D, secret_dist='uniform', m=None)[source]#

Bases: SageObject

Learning with Errors (LWE) oracle.

__init__(n, q, D, secret_dist='uniform', m=None)[source]#

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 DiscreteGaussianDistributionIntegerSampler or 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)

>>> from sage.all import *
>>> from sage.stats.distributions.discrete_gaussian_integer import DiscreteGaussianDistributionIntegerSampler
>>> D = DiscreteGaussianDistributionIntegerSampler(RealNumber('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.000000, 'uniform', None)

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


and sample 1000 samples:

sage: L = []
....:     global L
....:     L += [lwe() for _ in range(100)]

>>> from sage.all import *
>>> L = []
...     global L
...     L += [lwe() for _ in range(Integer(100))]


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

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

>>> from sage.all import *
>>> S = lambda : [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: from numpy import std                                                             # needs numpy
sage: while abs(std([e if e <= 200 else e-401 for e in S()]) - 3.0) > 0.01:             # needs numpy
....:     L = []  # reset L to avoid quadratic behaviour

>>> from sage.all import *
>>> from numpy import std                                                             # needs numpy
>>> while abs(std([e if e <= Integer(200) else e-Integer(401) for e in S()]) - RealNumber('3.0')) > RealNumber('0.01'):             # needs numpy
...     L = []  # reset L to avoid quadratic behaviour


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.

>>> from sage.all import *
>>> from sage.crypto.lwe import LWE
>>> lwe = LWE(n=Integer(20), q=next_prime(Integer(400)), D=D, m=Integer(30))
>>> _ = [lwe() for _ in range(Integer(30))]
>>> lwe()  # 31
Traceback (most recent call last):
...
IndexError: Number of available samples exhausted.

__call__()[source]#

EXAMPLES:

sage: from sage.crypto.lwe import DiscreteGaussianDistributionIntegerSampler, LWE
sage: LWE(10, 401, DiscreteGaussianDistributionIntegerSampler(3))()[0].parent()
Vector space of dimension 10 over Ring of integers modulo 401
sage: LWE(10, 401, DiscreteGaussianDistributionIntegerSampler(3))()[1].parent()
Ring of integers modulo 401

>>> from sage.all import *
>>> from sage.crypto.lwe import DiscreteGaussianDistributionIntegerSampler, LWE
>>> LWE(Integer(10), Integer(401), DiscreteGaussianDistributionIntegerSampler(Integer(3)))()[Integer(0)].parent()
Vector space of dimension 10 over Ring of integers modulo 401
>>> LWE(Integer(10), Integer(401), DiscreteGaussianDistributionIntegerSampler(Integer(3)))()[Integer(1)].parent()
Ring of integers modulo 401

class sage.crypto.lwe.LindnerPeikert(n, delta=0.01, m=None)[source]#

Bases: LWE

LWE oracle with parameters as in [LP2011].

__init__(n, delta=0.01, m=None)[source]#

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.000000, 'noise', 168)

>>> from sage.all import *
>>> from sage.crypto.lwe import LindnerPeikert
>>> LindnerPeikert(n=Integer(20))
LWE(20, 2053, Discrete Gaussian sampler over the Integers with sigma = 3.600954 and c = 0.000000, 'noise', 168)

class sage.crypto.lwe.Regev(n, secret_dist='uniform', m=None)[source]#

Bases: LWE

LWE oracle with parameters as in [Reg09].

__init__(n, secret_dist='uniform', m=None)[source]#

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 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.000000, 'uniform', None)

>>> from sage.all import *
>>> from sage.crypto.lwe import Regev
>>> Regev(n=Integer(20))
LWE(20, 401, Discrete Gaussian sampler over the Integers with sigma = 1.915069 and c = 401.000000, 'uniform', None)

class sage.crypto.lwe.RingLWE(N, q, D, poly=None, secret_dist='uniform', m=None)[source]#

Bases: SageObject

Ring Learning with Errors oracle.

__init__(N, q, D, poly=None, secret_dist='uniform', m=None)[source]#

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 DiscreteGaussianDistributionPolynomialSampler or 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 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)                # needs sage.libs.pari
sage: RingLWE(N=20, q=next_prime(800), D=D)                                 # needs sage.libs.pari
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)

>>> from sage.all import *
>>> from sage.crypto.lwe import RingLWE
>>> from sage.stats.distributions.discrete_gaussian_polynomial import DiscreteGaussianDistributionPolynomialSampler
>>> D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n=euler_phi(Integer(20)), sigma=RealNumber('3.0'))                # needs sage.libs.pari
>>> RingLWE(N=Integer(20), q=next_prime(Integer(800)), D=D)                                 # needs sage.libs.pari
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)

__call__()[source]#

EXAMPLES:

sage: # needs sage.libs.pari
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()[0].parent()
Vector space of dimension 8 over Ring of integers modulo 257
sage: ringlwe()[1].parent()
Vector space of dimension 8 over Ring of integers modulo 257

>>> from sage.all import *
>>> # needs sage.libs.pari
>>> from sage.crypto.lwe import DiscreteGaussianDistributionPolynomialSampler, RingLWE
>>> N = Integer(16)
>>> n = euler_phi(N)
>>> D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n, Integer(5))
>>> ringlwe = RingLWE(N, Integer(257), D, secret_dist='uniform')
>>> ringlwe()[Integer(0)].parent()
Vector space of dimension 8 over Ring of integers modulo 257
>>> ringlwe()[Integer(1)].parent()
Vector space of dimension 8 over Ring of integers modulo 257

class sage.crypto.lwe.RingLWEConverter(ringlwe)[source]#

Bases: SageObject

Wrapper callable to convert Ring-LWE oracles into LWE oracles by disregarding the additional structure.

__init__(ringlwe)[source]#

INPUT:

EXAMPLES:

sage: from sage.crypto.lwe import DiscreteGaussianDistributionPolynomialSampler, RingLWE, RingLWEConverter
sage: D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], euler_phi(16), 5)
sage: lwe = RingLWEConverter(RingLWE(16, 257, D, secret_dist='uniform'))
sage: set_random_seed(1337)
sage: lwe()
((171, 197, 58, 125, 3, 216, 32, 130), ...)

>>> from sage.all import *
>>> from sage.crypto.lwe import DiscreteGaussianDistributionPolynomialSampler, RingLWE, RingLWEConverter
>>> D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], euler_phi(Integer(16)), Integer(5))
>>> lwe = RingLWEConverter(RingLWE(Integer(16), Integer(257), D, secret_dist='uniform'))
>>> set_random_seed(Integer(1337))
>>> lwe()
((171, 197, 58, 125, 3, 216, 32, 130), ...)

__call__()[source]#

EXAMPLES:

sage: from sage.crypto.lwe import DiscreteGaussianDistributionPolynomialSampler, RingLWE, RingLWEConverter
sage: D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], euler_phi(16), 5)
sage: lwe = RingLWEConverter(RingLWE(16, 257, D, secret_dist='uniform'))
sage: set_random_seed(1337)
sage: lwe()
((171, 197, 58, 125, 3, 216, 32, 130), ...)

>>> from sage.all import *
>>> from sage.crypto.lwe import DiscreteGaussianDistributionPolynomialSampler, RingLWE, RingLWEConverter
>>> D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], euler_phi(Integer(16)), Integer(5))
>>> lwe = RingLWEConverter(RingLWE(Integer(16), Integer(257), D, secret_dist='uniform'))
>>> set_random_seed(Integer(1337))
>>> lwe()
((171, 197, 58, 125, 3, 216, 32, 130), ...)

class sage.crypto.lwe.RingLindnerPeikert(N, delta=0.01, m=None)[source]#

Bases: RingLWE

Ring-LWE oracle with parameters as in [LP2011].

__init__(N, delta=0.01, m=None)[source]#

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)

>>> from sage.all import *
>>> from sage.crypto.lwe import RingLindnerPeikert
>>> RingLindnerPeikert(N=Integer(16))
RingLWE(16, 1031, Discrete Gaussian sampler for polynomials of degree < 8 with σ=2.803372 in each component, x^8 + 1, 'noise', 24)

class sage.crypto.lwe.UniformNoiseLWE(n, instance='key', m=None)[source]#

Bases: LWE

LWE oracle with uniform secret with parameters as in [CGW2013].

__init__(n, instance='key', m=None)[source]#

Construct LWE instance parameterised by security parameter n where all other parameters are chosen as in [CGW2013].

INPUT:

• n – security parameter (integer >= 89)

• instance – one of

• “key” – the LWE-instance that hides the secret key is generated

• “encrypt” – the LWE-instance that hides the message is generated (default: key)

• m – number of allowed samples or None in which case m is chosen as in [CGW2013]. (default: None)

EXAMPLES:

sage: from sage.crypto.lwe import UniformNoiseLWE
sage: UniformNoiseLWE(89)
LWE(89, 64311834871, UniformSampler(0, 6577), 'noise', 131)

sage: UniformNoiseLWE(89, instance='encrypt')
LWE(131, 64311834871, UniformSampler(0, 11109), 'noise', 181)

>>> from sage.all import *
>>> from sage.crypto.lwe import UniformNoiseLWE
>>> UniformNoiseLWE(Integer(89))
LWE(89, 64311834871, UniformSampler(0, 6577), 'noise', 131)

>>> UniformNoiseLWE(Integer(89), instance='encrypt')
LWE(131, 64311834871, UniformSampler(0, 11109), 'noise', 181)

class sage.crypto.lwe.UniformPolynomialSampler(P, n, lower_bound, upper_bound)[source]#

Bases: SageObject

Uniform sampler for polynomials.

EXAMPLES:

sage: from sage.crypto.lwe import UniformPolynomialSampler
sage: UniformPolynomialSampler(ZZ['x'], 8, -2, 2)().parent()
Univariate Polynomial Ring in x over Integer Ring

>>> from sage.all import *
>>> from sage.crypto.lwe import UniformPolynomialSampler
>>> UniformPolynomialSampler(ZZ['x'], Integer(8), -Integer(2), Integer(2))().parent()
Univariate Polynomial Ring in x over Integer Ring

__init__(P, n, lower_bound, upper_bound)[source]#

Construct a sampler for univariate polynomials of degree n-1 where coefficients are drawn uniformly at random between lower_bound and upper_bound (both endpoints inclusive).

INPUT:

• P – a univariate polynomial ring over the Integers

• n – number of coefficients to be sampled

• lower_bound – integer

• upper_bound – integer

EXAMPLES:

sage: from sage.crypto.lwe import UniformPolynomialSampler
sage: UniformPolynomialSampler(ZZ['x'], 10, -10, 10)
UniformPolynomialSampler(10, -10, 10)

>>> from sage.all import *
>>> from sage.crypto.lwe import UniformPolynomialSampler
>>> UniformPolynomialSampler(ZZ['x'], Integer(10), -Integer(10), Integer(10))
UniformPolynomialSampler(10, -10, 10)

__call__()[source]#

Return a new sample.

EXAMPLES:

sage: from sage.crypto.lwe import UniformPolynomialSampler
sage: sampler = UniformPolynomialSampler(ZZ['x'], 8, -12, 12)
sage: sampler().parent()
Univariate Polynomial Ring in x over Integer Ring

>>> from sage.all import *
>>> from sage.crypto.lwe import UniformPolynomialSampler
>>> sampler = UniformPolynomialSampler(ZZ['x'], Integer(8), -Integer(12), Integer(12))
>>> sampler().parent()
Univariate Polynomial Ring in x over Integer Ring

class sage.crypto.lwe.UniformSampler(lower_bound, upper_bound)[source]#

Bases: SageObject

Uniform sampling in a range of integers.

EXAMPLES:

sage: from sage.crypto.lwe import UniformSampler
sage: sampler = UniformSampler(-2, 2); sampler
UniformSampler(-2, 2)
sage: sampler() in range(-2, 3)
True

>>> from sage.all import *
>>> from sage.crypto.lwe import UniformSampler
>>> sampler = UniformSampler(-Integer(2), Integer(2)); sampler
UniformSampler(-2, 2)
>>> sampler() in range(-Integer(2), Integer(3))
True

__init__(lower_bound, upper_bound)[source]#

Construct a uniform sampler with bounds lower_bound and upper_bound (both endpoints inclusive).

INPUT:

• lower_bound – integer

• upper_bound – integer

EXAMPLES:

sage: from sage.crypto.lwe import UniformSampler
sage: UniformSampler(-2, 2)
UniformSampler(-2, 2)

>>> from sage.all import *
>>> from sage.crypto.lwe import UniformSampler
>>> UniformSampler(-Integer(2), Integer(2))
UniformSampler(-2, 2)

__call__()[source]#

Return a new sample.

EXAMPLES:

sage: from sage.crypto.lwe import UniformSampler
sage: sampler = UniformSampler(-12, 12)
sage: sampler() in range(-12, 13)
True

>>> from sage.all import *
>>> from sage.crypto.lwe import UniformSampler
>>> sampler = UniformSampler(-Integer(12), Integer(12))
>>> sampler() in range(-Integer(12), Integer(13))
True

sage.crypto.lwe.balance_sample(s, q=None)[source]#

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))

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

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

sage.crypto.lwe.samples(m, n, lwe, seed=None, balanced=False, **kwds)[source]#

Return m LWE samples.

INPUT:

• m – the number of samples (integer > 0)

• n – the security parameter (integer > 0)

• lwe – either

• seed – seed to be used for generation or None if no specific seed shall be set (default: None)

• balanced – use function balance_sample() to return balanced representations of finite field elements (default: False)

• **kwds – passed through to LWE constructor

EXAMPLES:

sage: from sage.crypto.lwe import samples, Regev
sage: samples(2, 20, Regev, seed=1337)
[((199, 388, 337, 53, 200, 284, 336, 215, 75, 14, 274, 234, 97, 255, 246, 153, 268, 218, 396, 351), 15),
((365, 227, 333, 165, 76, 328, 288, 206, 286, 42, 175, 155, 190, 275, 114, 280, 45, 218, 304, 386), 143)]

sage: from sage.crypto.lwe import samples, Regev
sage: samples(2, 20, Regev, balanced=True, seed=1337)
[((199, -13, -64, 53, 200, -117, -65, -186, 75, 14, -127, -167, 97, -146, -155, 153, -133, -183, -5, -50), 15),
((-36, -174, -68, 165, 76, -73, -113, -195, -115, 42, 175, 155, 190, -126, 114, -121, 45, -183, -97, -15), 143)]

sage: from sage.crypto.lwe import samples
sage: samples(2, 20, 'LindnerPeikert')
[((506, 1205, 398, 0, 337, 106, 836, 75, 1242, 642, 840, 262, 1823, 1798, 1831, 1658, 1084, 915, 1994, 163), 1447),
((463, 250, 1226, 1906, 330, 933, 1014, 1061, 1322, 2035, 1849, 285, 1993, 1975, 864, 1341, 41, 1955, 1818, 1357), 312)]

>>> from sage.all import *
>>> from sage.crypto.lwe import samples, Regev
>>> samples(Integer(2), Integer(20), Regev, seed=Integer(1337))
[((199, 388, 337, 53, 200, 284, 336, 215, 75, 14, 274, 234, 97, 255, 246, 153, 268, 218, 396, 351), 15),
((365, 227, 333, 165, 76, 328, 288, 206, 286, 42, 175, 155, 190, 275, 114, 280, 45, 218, 304, 386), 143)]

>>> from sage.crypto.lwe import samples, Regev
>>> samples(Integer(2), Integer(20), Regev, balanced=True, seed=Integer(1337))
[((199, -13, -64, 53, 200, -117, -65, -186, 75, 14, -127, -167, 97, -146, -155, 153, -133, -183, -5, -50), 15),
((-36, -174, -68, 165, 76, -73, -113, -195, -115, 42, 175, 155, 190, -126, 114, -121, 45, -183, -97, -15), 143)]

>>> from sage.crypto.lwe import samples
>>> samples(Integer(2), Integer(20), 'LindnerPeikert')
[((506, 1205, 398, 0, 337, 106, 836, 75, 1242, 642, 840, 262, 1823, 1798, 1831, 1658, 1084, 915, 1994, 163), 1447),
((463, 250, 1226, 1906, 330, 933, 1014, 1061, 1322, 2035, 1849, 285, 1993, 1975, 864, 1341, 41, 1955, 1818, 1357), 312)]