(Ring-)LWE oracle generators - Cryptography

Skip to content
Back to top
View this page
Edit this page

# (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

    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)

Python

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

    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)

[Python]

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

    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)

Python

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

    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)

[Python]

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

    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)

Python

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

- [Reg09]

- [LP2011]

- [LPR2010]

- [CGW2013]

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

  Bases: `SageObject`

  Learning with Errors (LWE) oracle.

  __init__(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 `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

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

    Python

        >>> from sage.all import *
        >>> from sage.stats.distributions.discrete_gaussian_integer import DiscreteGaussianDistributionIntegerSampler
        >>> D = DiscreteGaussianDistributionIntegerSampler(RealNumber('3.0'))

    Next, we construct our oracle:

    Sage

        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)

    [Python]

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

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

    Python

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

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

    Sage

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

    [Python]

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

        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
        ....:     add_samples()

    Python

        >>> 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
        ...     add_samples()

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

    Sage

        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.

    [Python]

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

    EXAMPLES:

    Sage

        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

    Python

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

  Bases: `LWE`

  LWE oracle with parameters as in [LP2011].

  __init__(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

        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)

    Python

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

  Bases: `LWE`

  LWE oracle with parameters as in [Reg09].

  __init__(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 `LWE` for details (default=’uniform’)

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

    EXAMPLES:

    Sage

        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)

    Python

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

  Bases: `SageObject`

  Ring Learning with Errors oracle.

  __init__(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 `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

        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)

    Python

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

    EXAMPLES:

    Sage

        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

    Python

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

  Bases: `SageObject`

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

  __init__(ringlwe)

    INPUT:

    - `ringlwe` – an instance of a `RingLWE`

    EXAMPLES:

    Sage

        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()[0]
        (86, 136, 117, 249, 7, 110, 64, 222)

    Python

        >>> 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()[Integer(0)]
        (86, 136, 117, 249, 7, 110, 64, 222)

  __call__()

    EXAMPLES:

    Sage

        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()[0]
        (86, 136, 117, 249, 7, 110, 64, 222)

    Python

        >>> 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()[Integer(0)]
        (86, 136, 117, 249, 7, 110, 64, 222)

class sage.crypto.lwe.RingLindnerPeikert(N, delta=0.01, m=None)

  Bases: `RingLWE`

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

  __init__(N, delta=0.01, m=None)

    Construct a Ring-LWE oracle in dimension `n=phi(N)` where the modulus `q` and the `stddev` of the noise is chosen as in [LP2011].

    INPUT:

    - `N` – index of cyclotomic polynomial (integer > 0, must be power of 2)

    - `delta` – error probability per symbol (default: 0.01)

    - `m` – number of allowed samples or `None` in which case `3*n` is used (default: `None`)

    EXAMPLES:

    Sage

        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)

    Python

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

  Bases: `LWE`

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

  __init__(n, instance='key', m=None)

    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

        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)

    Python

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

  Bases: `SageObject`

  Uniform sampler for polynomials.

  EXAMPLES:

  Sage

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

  Python

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

    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

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

    Python

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

  __call__()

    Return a new sample.

    EXAMPLES:

    Sage

        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

    Python

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

  Bases: `SageObject`

  Uniform sampling in a range of integers.

  EXAMPLES:

  Sage

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

  Python

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

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

    INPUT:

    - `lower_bound` – integer

    - `upper_bound` – integer

    EXAMPLES:

    Sage

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

    Python

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

  __call__()

    Return a new sample.

    EXAMPLES:

    Sage

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

    Python

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

  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

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

  Python

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

  Return `m` LWE samples.

  INPUT:

  - `m` – the number of samples (integer > 0)

  - `n` – the security parameter (integer > 0)

  - `lwe` – either

    - a subclass of `LWE` such as `Regev` or `LindnerPeikert`

    - an instance of `LWE` or any subclass

    - the name of any such class (e.g., “Regev”, “LindnerPeikert”)

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

      sage: from sage.crypto.lwe import samples, Regev
      sage: samples(2, 20, Regev, seed=1337)
      [((249, 207, 156, 188, 114, 74, 173, 68, 308, 302, 177, 225, 132, 96, 281, 248, 266, 299, 373, 285), 323),
       ((358, 314, 180, 66, 243, 279, 38, 70, 157, 289, 118, 271, 87, 97, 311, 368, 92, 386, 220, 238), 141)]

      sage: from sage.crypto.lwe import samples, Regev
      sage: samples(2, 20, Regev, balanced=True, seed=1337)
      [((-152, -194, 156, 188, 114, 74, 173, 68, -93, -99, 177, -176, 132, 96, -120, -153, -135, -102, -28, -116), -78),
       ((-43, -87, 180, 66, -158, -122, 38, 70, 157, -112, 118, -130, 87, 97, -90, -33, 92, -15, -181, -163), 141)]

      sage: from sage.crypto.lwe import samples
      sage: samples(2, 20, 'LindnerPeikert')
      [((1792, 938, 1941, 1492, 1164, 790, 1258, 684, 1394, 1116, 1666, 1434, 825, 672, 622, 1140, 1469, 151, 271, 1281), 213),
       ((1677, 1743, 941, 1135, 953, 1554, 1826, 128, 327, 363, 601, 1504, 112, 659, 1861, 1127, 171, 1622, 182, 653), 1401)]

  Python

      >>> from sage.all import *
      >>> from sage.crypto.lwe import samples, Regev
      >>> samples(Integer(2), Integer(20), Regev, seed=Integer(1337))
      [((249, 207, 156, 188, 114, 74, 173, 68, 308, 302, 177, 225, 132, 96, 281, 248, 266, 299, 373, 285), 323),
       ((358, 314, 180, 66, 243, 279, 38, 70, 157, 289, 118, 271, 87, 97, 311, 368, 92, 386, 220, 238), 141)]

      >>> from sage.crypto.lwe import samples, Regev
      >>> samples(Integer(2), Integer(20), Regev, balanced=True, seed=Integer(1337))
      [((-152, -194, 156, 188, 114, 74, 173, 68, -93, -99, 177, -176, 132, 96, -120, -153, -135, -102, -28, -116), -78),
       ((-43, -87, 180, 66, -158, -122, 38, 70, 157, -112, 118, -130, 87, 97, -90, -33, 92, -15, -181, -163), 141)]

      >>> from sage.crypto.lwe import samples
      >>> samples(Integer(2), Integer(20), 'LindnerPeikert')
      [((1792, 938, 1941, 1492, 1164, 790, 1258, 684, 1394, 1116, 1666, 1434, 825, 672, 622, 1140, 1469, 151, 271, 1281), 213),
       ((1677, 1743, 941, 1135, 953, 1554, 1826, 128, 327, 363, 601, 1504, 112, 659, 1861, 1127, 171, 1622, 182, 653), 1401)]