pqlattice 0.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pqlattice/__init__.py +6 -0
- pqlattice/_backends/__init__.py +0 -0
- pqlattice/_backends/_fast.py +75 -0
- pqlattice/_backends/_native.py +33 -0
- pqlattice/_backends/_protocol.py +15 -0
- pqlattice/_utils.py +201 -0
- pqlattice/integer/__init__.py +8 -0
- pqlattice/integer/_integer.py +55 -0
- pqlattice/integer/_modintring.py +246 -0
- pqlattice/integer/_modring.py +165 -0
- pqlattice/integer/_primality.py +78 -0
- pqlattice/integer/_primes.py +57 -0
- pqlattice/lattice/__init__.py +44 -0
- pqlattice/lattice/_bkz.py +87 -0
- pqlattice/lattice/_cvp.py +62 -0
- pqlattice/lattice/_embeddings.py +149 -0
- pqlattice/lattice/_gso.py +43 -0
- pqlattice/lattice/_hkz.py +20 -0
- pqlattice/lattice/_lattice.py +137 -0
- pqlattice/lattice/_lll.py +93 -0
- pqlattice/lattice/_svp.py +89 -0
- pqlattice/lattice/embeddings.py +3 -0
- pqlattice/linalg/__init__.py +37 -0
- pqlattice/linalg/_linalg.py +306 -0
- pqlattice/linalg/_modint.py +209 -0
- pqlattice/linalg/_utils.py +167 -0
- pqlattice/polynomial/__init__.py +5 -0
- pqlattice/polynomial/_modpolyqring.py +185 -0
- pqlattice/polynomial/_modpolyring.py +267 -0
- pqlattice/polynomial/_poly.py +250 -0
- pqlattice/polynomial/poly.py +3 -0
- pqlattice/py.typed +0 -0
- pqlattice/random/__init__.py +7 -0
- pqlattice/random/_distribution.py +303 -0
- pqlattice/random/_lattice.py +53 -0
- pqlattice/random/_lwe.py +109 -0
- pqlattice/random/_lwr.py +41 -0
- pqlattice/random/_prime.py +53 -0
- pqlattice/random/distribution.py +3 -0
- pqlattice/settings.py +66 -0
- pqlattice/typing/__init__.py +4 -0
- pqlattice/typing/_types.py +18 -0
- pqlattice/typing/_types_validator.py +57 -0
- pqlattice-0.1.2.dist-info/METADATA +33 -0
- pqlattice-0.1.2.dist-info/RECORD +47 -0
- pqlattice-0.1.2.dist-info/WHEEL +4 -0
- pqlattice-0.1.2.dist-info/licenses/LICENSE +7 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from typing import overload
|
|
2
|
+
|
|
3
|
+
from ..typing import Array
|
|
4
|
+
from ._integer import eea
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@overload
|
|
8
|
+
def mod(a: int, modulus: int) -> int: ...
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@overload
|
|
12
|
+
def mod(a: Array, modulus: int) -> Array: ...
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def mod(a: int | Array, modulus: int) -> int | Array:
|
|
16
|
+
"""_summary_
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
a : int | Array
|
|
21
|
+
_description_
|
|
22
|
+
modulus : int
|
|
23
|
+
_description_
|
|
24
|
+
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
int | Array
|
|
28
|
+
_description_
|
|
29
|
+
"""
|
|
30
|
+
return a % abs(modulus)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@overload
|
|
34
|
+
def cmodl(a: int, modulus: int) -> int: ...
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@overload
|
|
38
|
+
def cmodl(a: Array, modulus: int) -> Array: ...
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def cmodl(a: int | Array, modulus: int) -> int | Array:
|
|
42
|
+
"""_summary_
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
a : int | Array
|
|
47
|
+
_description_
|
|
48
|
+
modulus : int
|
|
49
|
+
_description_
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
int | Array
|
|
54
|
+
_description_
|
|
55
|
+
"""
|
|
56
|
+
return mod(a, modulus) - modulus // 2
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@overload
|
|
60
|
+
def cmodr(a: int, modulus: int) -> int: ...
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@overload
|
|
64
|
+
def cmodr(a: Array, modulus: int) -> Array: ...
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def cmodr(a: int | Array, modulus: int) -> int | Array:
|
|
68
|
+
"""_summary_
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
a : int | Array
|
|
73
|
+
_description_
|
|
74
|
+
modulus : int
|
|
75
|
+
_description_
|
|
76
|
+
|
|
77
|
+
Returns
|
|
78
|
+
-------
|
|
79
|
+
int | Array
|
|
80
|
+
_description_
|
|
81
|
+
"""
|
|
82
|
+
return mod(a, modulus) - int(modulus / 2 - 0.1)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@overload
|
|
86
|
+
def modinv(a: int, modulus: int) -> int: ...
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@overload
|
|
90
|
+
def modinv(a: Array, modulus: int) -> Array: ...
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def modinv(a: int | Array, modulus: int) -> int | Array:
|
|
94
|
+
"""_summary_
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
a : int | Array
|
|
99
|
+
_description_
|
|
100
|
+
modulus : int
|
|
101
|
+
_description_
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
int | Array
|
|
106
|
+
_description_
|
|
107
|
+
|
|
108
|
+
Raises
|
|
109
|
+
------
|
|
110
|
+
ValueError
|
|
111
|
+
_description_
|
|
112
|
+
ValueError
|
|
113
|
+
_description_
|
|
114
|
+
"""
|
|
115
|
+
if isinstance(a, int):
|
|
116
|
+
if mod(a, modulus) == 0:
|
|
117
|
+
raise ValueError(f"{a} mod {modulus} is zero; Modular inverse does not exist")
|
|
118
|
+
gcd, a_inv, _ = eea(a, modulus)
|
|
119
|
+
if gcd != 1:
|
|
120
|
+
raise ValueError(f"Modular inverse of {a} mod {modulus} does not exist; gcd is equal to {gcd}")
|
|
121
|
+
else:
|
|
122
|
+
if (mod(a, modulus) == 0).all():
|
|
123
|
+
raise ValueError(f"{a} mod {modulus} is zero; Modular inverse does not exist")
|
|
124
|
+
gcd, a_inv, _ = eea(a, modulus)
|
|
125
|
+
if (gcd != 1).any():
|
|
126
|
+
raise ValueError(f"Modular inverse of {a} mod {modulus} does not exist; gcd is equal to {gcd}")
|
|
127
|
+
|
|
128
|
+
return mod(a_inv, modulus)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@overload
|
|
132
|
+
def modpow(a: Array, r: int, modulus: int) -> Array: ...
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@overload
|
|
136
|
+
def modpow(a: int, r: int, modulus: int) -> int: ...
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def modpow(a: int | Array, r: int, modulus: int) -> int | Array:
|
|
140
|
+
"""_summary_
|
|
141
|
+
|
|
142
|
+
Parameters
|
|
143
|
+
----------
|
|
144
|
+
a : int | Array
|
|
145
|
+
_description_
|
|
146
|
+
r : int
|
|
147
|
+
_description_
|
|
148
|
+
modulus : int
|
|
149
|
+
_description_
|
|
150
|
+
|
|
151
|
+
Returns
|
|
152
|
+
-------
|
|
153
|
+
int | Array
|
|
154
|
+
_description_
|
|
155
|
+
"""
|
|
156
|
+
if r < 0:
|
|
157
|
+
return modpow(modinv(a, modulus), -r, modulus)
|
|
158
|
+
|
|
159
|
+
y, z = 1, a
|
|
160
|
+
while r != 0:
|
|
161
|
+
if r % 2 == 1:
|
|
162
|
+
y = mod(y * z, modulus)
|
|
163
|
+
r //= 2
|
|
164
|
+
z = mod(z * z, modulus)
|
|
165
|
+
return y
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import random
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
|
|
5
|
+
from . import _modring as mr # type: ignore
|
|
6
|
+
from . import _primes as primes
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def fermat_primality_test(p: int, s: int, int_gen: Callable[[int, int], int] | None = None) -> bool:
|
|
12
|
+
if p <= 1:
|
|
13
|
+
return False
|
|
14
|
+
|
|
15
|
+
if int_gen is None:
|
|
16
|
+
int_gen = lambda a, b: random.randint(a, b - 1)
|
|
17
|
+
|
|
18
|
+
for _ in range(s):
|
|
19
|
+
a = int_gen(2, p - 2)
|
|
20
|
+
if mr.modpow(a, p - 1, p) == 1:
|
|
21
|
+
return False
|
|
22
|
+
return True
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def miller_rabin_primality_test(n: int, s: int, int_gen: Callable[[int, int], int] | None = None) -> bool:
|
|
26
|
+
if int_gen is None:
|
|
27
|
+
int_gen = lambda a, b: random.randint(a, b)
|
|
28
|
+
|
|
29
|
+
# n - 1 = r * 2 ** u
|
|
30
|
+
u = 0
|
|
31
|
+
r = n - 1
|
|
32
|
+
while r % 2 == 0:
|
|
33
|
+
u += 1
|
|
34
|
+
r //= 2
|
|
35
|
+
|
|
36
|
+
# assert n - 1 == r * 2 ** u, f"{n - 1=}, {r=}, {u=}, {2**u=}, {r * 2 ** u=}"
|
|
37
|
+
|
|
38
|
+
for _ in range(s):
|
|
39
|
+
a = int_gen(2, n - 2)
|
|
40
|
+
z = mr.modpow(a, r, n)
|
|
41
|
+
for _ in range(u):
|
|
42
|
+
y = mr.mod(z * z, n)
|
|
43
|
+
if y == 1 and z != 1 and z != n - 1:
|
|
44
|
+
# composite
|
|
45
|
+
return False
|
|
46
|
+
z = y
|
|
47
|
+
if z != 1:
|
|
48
|
+
# composite
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
# likely prime
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def is_prime(p: int) -> bool:
|
|
56
|
+
"""_summary_
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
p : int
|
|
61
|
+
_description_
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
bool
|
|
66
|
+
_description_
|
|
67
|
+
"""
|
|
68
|
+
if p <= 1:
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
for prime in primes.SMALL_PRIMES:
|
|
72
|
+
if p == prime:
|
|
73
|
+
return True
|
|
74
|
+
|
|
75
|
+
if p % prime == 0:
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
return miller_rabin_primality_test(p, 20)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# All primes less than 256
|
|
2
|
+
SMALL_PRIMES = (
|
|
3
|
+
2,
|
|
4
|
+
3,
|
|
5
|
+
5,
|
|
6
|
+
7,
|
|
7
|
+
11,
|
|
8
|
+
13,
|
|
9
|
+
17,
|
|
10
|
+
19,
|
|
11
|
+
23,
|
|
12
|
+
29,
|
|
13
|
+
31,
|
|
14
|
+
37,
|
|
15
|
+
41,
|
|
16
|
+
43,
|
|
17
|
+
47,
|
|
18
|
+
53,
|
|
19
|
+
59,
|
|
20
|
+
61,
|
|
21
|
+
67,
|
|
22
|
+
71,
|
|
23
|
+
73,
|
|
24
|
+
79,
|
|
25
|
+
83,
|
|
26
|
+
89,
|
|
27
|
+
97,
|
|
28
|
+
101,
|
|
29
|
+
103,
|
|
30
|
+
107,
|
|
31
|
+
109,
|
|
32
|
+
113,
|
|
33
|
+
127,
|
|
34
|
+
131,
|
|
35
|
+
137,
|
|
36
|
+
139,
|
|
37
|
+
149,
|
|
38
|
+
151,
|
|
39
|
+
157,
|
|
40
|
+
163,
|
|
41
|
+
167,
|
|
42
|
+
173,
|
|
43
|
+
179,
|
|
44
|
+
181,
|
|
45
|
+
191,
|
|
46
|
+
193,
|
|
47
|
+
197,
|
|
48
|
+
199,
|
|
49
|
+
211,
|
|
50
|
+
223,
|
|
51
|
+
227,
|
|
52
|
+
229,
|
|
53
|
+
233,
|
|
54
|
+
239,
|
|
55
|
+
241,
|
|
56
|
+
251,
|
|
57
|
+
)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from .. import settings
|
|
2
|
+
from ..typing import SquareMatrix, Vector
|
|
3
|
+
from . import embeddings
|
|
4
|
+
from ._cvp import babai_closest_vector, babai_nearest_plane, closest_vector
|
|
5
|
+
from ._gso import gso
|
|
6
|
+
from ._lattice import discriminant, gaussian_heuristic, glr_2dim, hadamard_ratio, rank, volume
|
|
7
|
+
from ._lll import is_lll_reduced, is_size_reduced
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def lll(lattice_basis: SquareMatrix, delta: float = 0.99) -> SquareMatrix:
|
|
11
|
+
return settings.get_backend().lll(lattice_basis, delta)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def bkz(lattice_basis: SquareMatrix, block_size: int = 10, delta: float = 0.99) -> SquareMatrix:
|
|
15
|
+
return settings.get_backend().bkz(lattice_basis, block_size, delta)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def hkz(lattice_basis: SquareMatrix, delta: float = 0.99) -> SquareMatrix:
|
|
19
|
+
return settings.get_backend().hkz(lattice_basis, delta)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def shortest_vector(lattice_basis: SquareMatrix) -> Vector:
|
|
23
|
+
return settings.get_backend().shortest_vector(lattice_basis)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"volume",
|
|
28
|
+
"rank",
|
|
29
|
+
"hadamard_ratio",
|
|
30
|
+
"discriminant",
|
|
31
|
+
"gaussian_heuristic",
|
|
32
|
+
"glr_2dim",
|
|
33
|
+
"gso",
|
|
34
|
+
"lll",
|
|
35
|
+
"is_lll_reduced",
|
|
36
|
+
"is_size_reduced",
|
|
37
|
+
"bkz",
|
|
38
|
+
"hkz",
|
|
39
|
+
"shortest_vector",
|
|
40
|
+
"closest_vector",
|
|
41
|
+
"babai_closest_vector",
|
|
42
|
+
"babai_nearest_plane",
|
|
43
|
+
"embeddings",
|
|
44
|
+
]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from .._utils import as_integer, as_rational
|
|
4
|
+
from ..typing import Matrix, SquareMatrix, Vector, validate_aliases
|
|
5
|
+
from ._gso import gso
|
|
6
|
+
from ._lll import lll
|
|
7
|
+
from ._svp import schnorr_euchner_svp
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger("BKZ")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@validate_aliases
|
|
13
|
+
def update_block(lattice_basis: SquareMatrix, new_vector: Vector, start_index: int, block_size: int) -> SquareMatrix:
|
|
14
|
+
B = as_integer(lattice_basis)
|
|
15
|
+
local_basis = as_integer([new_vector] + B[start_index : start_index + block_size].tolist())
|
|
16
|
+
reduced_local = lll(local_basis)
|
|
17
|
+
|
|
18
|
+
zero_vec = reduced_local[0]
|
|
19
|
+
if any(s != 0 for s in zero_vec):
|
|
20
|
+
raise ValueError("block update failed")
|
|
21
|
+
|
|
22
|
+
cleaned_basis = as_integer(reduced_local[1:])
|
|
23
|
+
|
|
24
|
+
for i in range(block_size):
|
|
25
|
+
B[start_index + i] = cleaned_basis[i]
|
|
26
|
+
|
|
27
|
+
return B
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@validate_aliases
|
|
31
|
+
def bkz(lattice_basis: SquareMatrix, block_size: int = 10) -> SquareMatrix:
|
|
32
|
+
"""_summary_
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
lattice_basis : SquareMatrix
|
|
37
|
+
_description_
|
|
38
|
+
block_size : int, optional
|
|
39
|
+
_description_, by default 10
|
|
40
|
+
|
|
41
|
+
Returns
|
|
42
|
+
-------
|
|
43
|
+
SquareMatrix
|
|
44
|
+
_description_
|
|
45
|
+
"""
|
|
46
|
+
n, m = lattice_basis.shape
|
|
47
|
+
|
|
48
|
+
B = lll(lattice_basis)
|
|
49
|
+
|
|
50
|
+
is_changed = True
|
|
51
|
+
# iteration = 0
|
|
52
|
+
# logger.info("starting bkz loop")
|
|
53
|
+
while is_changed:
|
|
54
|
+
is_changed = False
|
|
55
|
+
# iteration += 1
|
|
56
|
+
# logger.info(f"iter {iteration}")
|
|
57
|
+
for k in range(n - 1):
|
|
58
|
+
h = min(block_size, n - k)
|
|
59
|
+
local_basis: Matrix = B[k : k + h]
|
|
60
|
+
# logger.info(f"block {local_basis}")
|
|
61
|
+
B_star, U = gso(local_basis)
|
|
62
|
+
B_norms2 = as_rational([sum(x * x for x in v) for v in B_star])
|
|
63
|
+
mu = U.T
|
|
64
|
+
coeffs = schnorr_euchner_svp(mu, B_norms2)
|
|
65
|
+
|
|
66
|
+
is_trivial = True
|
|
67
|
+
if abs(coeffs[0]) == 1:
|
|
68
|
+
is_trivial = all(c == 0 for c in coeffs[1:])
|
|
69
|
+
elif coeffs[0] == 0:
|
|
70
|
+
is_trivial = False
|
|
71
|
+
else:
|
|
72
|
+
is_trivial = False
|
|
73
|
+
|
|
74
|
+
if is_trivial:
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
# logger.info(f"shortest vector in block norm: {norm(coeffs @ local_basis)}")
|
|
78
|
+
new_vector = as_integer([0] * m)
|
|
79
|
+
for i in range(h):
|
|
80
|
+
if coeffs[i] == 0:
|
|
81
|
+
continue
|
|
82
|
+
for d in range(m):
|
|
83
|
+
new_vector[d] += coeffs[i] * local_basis[i][d]
|
|
84
|
+
|
|
85
|
+
B = update_block(B, new_vector, k, h)
|
|
86
|
+
is_changed = True
|
|
87
|
+
return B
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from .._utils import as_integer, as_rational
|
|
4
|
+
from ..typing import SquareMatrix, Vector, validate_aliases
|
|
5
|
+
from ._gso import gso, project_coeffs
|
|
6
|
+
from ._lll import lll
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@validate_aliases
|
|
10
|
+
def schnorr_euchner_cvp(mu: SquareMatrix, B: Vector, target_coeffs: Vector) -> Vector:
|
|
11
|
+
raise NotImplementedError()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@validate_aliases
|
|
15
|
+
def closest_vector(lattice_basis: SquareMatrix, target_vector: Vector) -> Vector:
|
|
16
|
+
raise NotImplementedError()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@validate_aliases
|
|
20
|
+
def babai_nearest_plane(lattice_basis: SquareMatrix, target_vector: Vector) -> Vector:
|
|
21
|
+
"""_summary_
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
lattice_basis : SquareMatrix
|
|
26
|
+
_description_
|
|
27
|
+
target_vector : Vector
|
|
28
|
+
_description_
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
Vector
|
|
33
|
+
_description_
|
|
34
|
+
"""
|
|
35
|
+
n, _ = lattice_basis.shape
|
|
36
|
+
B = lll(lattice_basis)
|
|
37
|
+
b = as_rational(target_vector)
|
|
38
|
+
for j in range(n - 1, -1, -1):
|
|
39
|
+
B_star, _ = gso(B)
|
|
40
|
+
cj = round(project_coeffs(B_star[j], b))
|
|
41
|
+
b -= cj * B[j]
|
|
42
|
+
|
|
43
|
+
return as_integer(as_rational(target_vector) - b)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@validate_aliases
|
|
47
|
+
def babai_closest_vector(lattice_basis: SquareMatrix, target_vector: Vector) -> Vector:
|
|
48
|
+
"""_summary_
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
lattice_basis : SquareMatrix
|
|
53
|
+
_description_
|
|
54
|
+
target_vector : Vector
|
|
55
|
+
_description_
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
Vector
|
|
60
|
+
_description_
|
|
61
|
+
"""
|
|
62
|
+
return as_integer(np.rint(target_vector.astype(float) @ np.linalg.inv(lattice_basis.astype(float))))
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from .._utils import as_integer, zeros_mat
|
|
4
|
+
from ..linalg import hnf
|
|
5
|
+
from ..typing import Matrix, SquareMatrix, Vector, validate_aliases
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def lwe_basis(A: Matrix, q: int) -> SquareMatrix:
|
|
9
|
+
"""
|
|
10
|
+
_summary_
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
A : Matrix
|
|
15
|
+
_description_
|
|
16
|
+
q : int
|
|
17
|
+
_description_
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
SquareMatrix
|
|
22
|
+
_description_
|
|
23
|
+
"""
|
|
24
|
+
# lattice: L = { x | Ax = 0 mod q }
|
|
25
|
+
m, _ = A.shape
|
|
26
|
+
Im = q * as_integer(np.identity(m))
|
|
27
|
+
G = np.vstack((A.T, Im))
|
|
28
|
+
H, _ = hnf(G)
|
|
29
|
+
|
|
30
|
+
return H[:m]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def sis_basis(A: Matrix, q: int) -> SquareMatrix:
|
|
34
|
+
"""
|
|
35
|
+
_summary_
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
A : Matrix
|
|
40
|
+
_description_
|
|
41
|
+
q : int
|
|
42
|
+
_description_
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
SquareMatrix
|
|
47
|
+
_description_
|
|
48
|
+
"""
|
|
49
|
+
# lattice: L = { y | y = xA mod q }
|
|
50
|
+
B_p = lwe_basis(A, q)
|
|
51
|
+
B_inv = np.linalg.inv(B_p.astype(float))
|
|
52
|
+
B_dual = np.round(q * B_inv.T).astype(int)
|
|
53
|
+
return B_dual
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@validate_aliases
|
|
57
|
+
def kannan(A: Matrix, b: Vector, q: int) -> SquareMatrix:
|
|
58
|
+
"""
|
|
59
|
+
_summary_
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
A : Matrix
|
|
64
|
+
_description_
|
|
65
|
+
b : Vector
|
|
66
|
+
_description_
|
|
67
|
+
q : int
|
|
68
|
+
_description_
|
|
69
|
+
|
|
70
|
+
Returns
|
|
71
|
+
-------
|
|
72
|
+
SquareMatrix
|
|
73
|
+
_description_
|
|
74
|
+
"""
|
|
75
|
+
return bai_galbraith(A, b, q, 1)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@validate_aliases
|
|
79
|
+
def bai_galbraith(A: Matrix, b: Vector, q: int, M: int) -> SquareMatrix:
|
|
80
|
+
"""
|
|
81
|
+
_summary_
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
A : Matrix
|
|
86
|
+
_description_
|
|
87
|
+
b : Vector
|
|
88
|
+
_description_
|
|
89
|
+
q : int
|
|
90
|
+
_description_
|
|
91
|
+
M : int
|
|
92
|
+
_description_
|
|
93
|
+
|
|
94
|
+
Returns
|
|
95
|
+
-------
|
|
96
|
+
SquareMatrix
|
|
97
|
+
_description_
|
|
98
|
+
"""
|
|
99
|
+
m, n = A.shape
|
|
100
|
+
|
|
101
|
+
Im = as_integer(np.identity(m))
|
|
102
|
+
In = as_integer(np.identity(n))
|
|
103
|
+
|
|
104
|
+
Zmxn = zeros_mat(m, n)
|
|
105
|
+
Zmx1 = zeros_mat(m, 1)
|
|
106
|
+
Znx1 = zeros_mat(n, 1)
|
|
107
|
+
Z1xn = zeros_mat(1, n)
|
|
108
|
+
|
|
109
|
+
bT = b.reshape(1, -1)
|
|
110
|
+
IM = M * (zeros_mat(1, 1) + 1)
|
|
111
|
+
|
|
112
|
+
return np.block(
|
|
113
|
+
[
|
|
114
|
+
[q * Im, Zmxn, Zmx1],
|
|
115
|
+
[A.T, In, Znx1],
|
|
116
|
+
[-bT, Z1xn, IM],
|
|
117
|
+
]
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@validate_aliases
|
|
122
|
+
def subset_sum(sequence: Vector, S: int) -> SquareMatrix:
|
|
123
|
+
"""
|
|
124
|
+
_summary_
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
sequence : Vector
|
|
129
|
+
_description_
|
|
130
|
+
S : int
|
|
131
|
+
_description_
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
SquareMatrix
|
|
136
|
+
_description_
|
|
137
|
+
"""
|
|
138
|
+
n = len(sequence)
|
|
139
|
+
A = as_integer(np.identity(n + 1) * 2)
|
|
140
|
+
A[-1] = 1
|
|
141
|
+
A[:-1, -1] = sequence
|
|
142
|
+
A[-1, -1] = S
|
|
143
|
+
|
|
144
|
+
return A
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@validate_aliases
|
|
148
|
+
def ntru() -> SquareMatrix:
|
|
149
|
+
raise NotImplementedError()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from fractions import Fraction
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from .._utils import as_rational
|
|
6
|
+
from ..typing import Matrix, SquareMatrix, Vector, validate_aliases
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@validate_aliases
|
|
10
|
+
def project_coeffs(q: Vector, b: Vector) -> Fraction:
|
|
11
|
+
if np.dot(q, q) == 0:
|
|
12
|
+
return Fraction(0, 1)
|
|
13
|
+
|
|
14
|
+
return np.dot(b, q) / np.dot(q, q)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@validate_aliases
|
|
18
|
+
def gso(B: Matrix) -> tuple[Matrix, SquareMatrix]:
|
|
19
|
+
"""_summary_
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
B : Matrix
|
|
24
|
+
_description_
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
tuple[Matrix, SquareMatrix]
|
|
29
|
+
_description_
|
|
30
|
+
"""
|
|
31
|
+
rows, _ = B.shape
|
|
32
|
+
|
|
33
|
+
B_star: Matrix = as_rational(B)
|
|
34
|
+
U: SquareMatrix = as_rational(np.identity(rows))
|
|
35
|
+
|
|
36
|
+
for j in range(1, rows):
|
|
37
|
+
b: Vector = B_star[j].copy()
|
|
38
|
+
for i in range(j):
|
|
39
|
+
U[i, j] = project_coeffs(B_star[i], b)
|
|
40
|
+
B_star[j] -= U[i][j] * B_star[i]
|
|
41
|
+
|
|
42
|
+
# B = U.T @ B_star
|
|
43
|
+
return B_star, U
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from ..typing import SquareMatrix, validate_aliases
|
|
2
|
+
from ._bkz import bkz
|
|
3
|
+
from ._lattice import rank
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@validate_aliases
|
|
7
|
+
def hkz(lattice_basis: SquareMatrix) -> SquareMatrix:
|
|
8
|
+
"""_summary_
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
lattice_basis : SquareMatrix
|
|
13
|
+
_description_
|
|
14
|
+
|
|
15
|
+
Returns
|
|
16
|
+
-------
|
|
17
|
+
SquareMatrix
|
|
18
|
+
_description_
|
|
19
|
+
"""
|
|
20
|
+
return bkz(lattice_basis, rank(lattice_basis))
|