Coverage for src/pqlattice/random/_lwe.py: 31%

1 import numpy as np

2

3 from ..integer._modring import cmodl, mod

4 from ..typing import Matrix, Vector

5 from ._distribution import DiscreteGaussian, Uniform

6

7

8 class LWE:

9 def __init__(self, n: int, q: int, sigma: float, secret_distribution: str, seed: int):

10 """

11 Creates LWE sampler with DiscreteGuassianDistribution centered at 0 as noise sampler

12

13 Parameters

14 ----------

15 n : int

16 length of secret vector

17 q : int

18 modulus

19 sigma : float

20 sigma value for DiscreteGaussianDistribution

21 seed : int

22 seed for random number generator

23 """

24 self.n = n

25 self.q = q

26 self.U = Uniform(0, q - 1, seed=seed)

27 self.D = DiscreteGaussian(sigma, seed=seed)

28

29 secret = self.U.sample_vector(n)

30 if secret_distribution == "uniform":

31 self._secret = secret

32 elif secret_distribution == "binary":

33 self._secret = mod(secret, 2)

34 elif secret_distribution == "ternary":

35 self._secret = cmodl(secret, 3)

36 else:

37 raise ValueError(f"Unknown distribution {secret_distribution}, expected uniform|binary|ternary")

38

39 @property

40 def secret(self) -> Vector:

41 """

42 Retrieve underlying secret

43

44 Returns

45 -------

46 Vector

47 s: n-vector

48 """

49 return self._secret

50

51 def set_secret(self, secret: Vector) -> None:

52 """

53 Set the underlying secret

54

55 Parameters

56 ----------

57 secret : Vector

58 secret vector to set

59

60 Raises

61 ------

62 ValueError

63 when lenght of the provided vector is not correct with the parameter of the LWE sampler

64 """

65 if secret.shape[0] != self.n:

66 raise ValueError(f"expected {self.n} dimension of secret, got {secret.shape[0]}")

67

68 self._secret = secret

69

70 def sample_matrix(self, m: int) -> tuple[Matrix, Vector]:

71 """

72 Generates a full matrix system (A, b) with 'm' samples.

73

74 Parameters

75 ----------

76 m : int

77 how many samples should the resulting matrix have

78

79 Returns

80 -------

81 tuple[Matrix, Vector]:

82

83 A (Matrix): m x n matrix (Uniform mod q)

84 b (Vector): m-vector (As + e mod q)

85 """

86 A = self.U.sample_matrix(m, self.n)

87 e = self.D.sample_vector(m)

88

89 b = mod(A @ self.secret + e, self.q)

90

91 return A, b

92

93 def next_sample(self) -> tuple[Vector, int]:

94 """

95 Generates a single sample pair (a, b).

96

97 Returns

98 -------

99 tuple[Vector, int]

100 a (Vector): n-vector (Uniform mod q)

101 b (int): as + e mod q

102 """

103 a = self.U.sample_vector(self.n)

104 e = self.D.sample_int()

105

106 _as: int = np.dot(a, self.secret)

107 b = mod(_as + e, self.q)

108

109 return a, b