Fork of the official github repository of the framework Leaky-LWE-Estimator, a Sage Toolkit to attack and estimate the hardness of LWE with Side Information. https://github.com/lducas/leaky-LWE-Estimator
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

geometry.sage 8.1KB

4 år sedan
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. from numpy.linalg import inv as np_inv
  2. # from numpy.linalg import slogdet as np_slogdet
  3. from numpy import array
  4. import numpy as np
  5. def dual_basis(B):
  6. """
  7. Compute the dual basis of B
  8. """
  9. return B.pseudoinverse().transpose()
  10. def projection_matrix(A):
  11. """
  12. Construct the projection matrix orthogonally to Span(V)
  13. """
  14. S = A * A.T
  15. return A.T * S.inverse() * A
  16. def project_against(v, X):
  17. """ Project matrix X orthonally to vector v"""
  18. # Pv = projection_matrix(v)
  19. # return X - X * Pv
  20. Z = (X * v.T) * v / scal(v * v.T)
  21. return X - Z
  22. # def make_primitive(B, v):
  23. # assert False
  24. # # project and Scale v's in V so that each v
  25. # # is in the lattice, and primitive in it.
  26. # # Note: does not make V primitive as as set of vector !
  27. # # (e.g. linear dep. not eliminated)
  28. # PB = projection_matrix(B)
  29. # DT = dual_basis(B).T
  30. # v = vec(v) * PB
  31. # w = v * DT
  32. # den = lcm([x.denominator() for x in w[0]])
  33. # num = gcd([x for x in w[0] * den])
  34. # if num==0:
  35. # return None
  36. # v *= den/num
  37. # return v
  38. def vol(B):
  39. return sqrt(det(B * B.T))
  40. def project_and_eliminate_dep(B, W):
  41. # Project v on Span(B)
  42. PB = projection_matrix(B)
  43. V = W * PB
  44. rank_loss = V.nrows() - V.rank()
  45. if rank_loss > 0:
  46. print("WARNING: their were %d linear dependencies out of %d " %
  47. (rank_loss, V.nrows()))
  48. V = V.LLL()
  49. V = V[rank_loss:]
  50. return V
  51. def is_cannonical_direction(v):
  52. v = vec(v)
  53. return sum([x != 0 for x in v[0]]) == 1
  54. def cannonical_param(v):
  55. v = vec(v)
  56. assert is_cannonical_direction(v)
  57. i = [x != 0 for x in v[0]].index(True)
  58. return i, v[0, i]
  59. def remove_linear_dependencies(B, dim=None):
  60. nrows = B.nrows()
  61. if dim is None or nrows > dim:
  62. #print(f'Go... [{dim}]')
  63. B = B.LLL()
  64. r = min([i for i in range(B.nrows()) if not B[i].is_zero()]) if dim is None else nrows-dim
  65. B = B[r:]
  66. #print(f'[{nrows}->{B.nrows()}]')
  67. return B
  68. def lattice_orthogonal_section(D, V):
  69. """
  70. Compute the intersection of the lattice L(B)
  71. with the hyperplane orthogonal to Span(V).
  72. (V can be either a vector or a matrix)
  73. INPUT AND OUTPUT DUAL BASIS
  74. Algorithm:
  75. - project V onto Span(B)
  76. - project the dual basis onto orth(V)
  77. - eliminate linear dependencies (LLL)
  78. - go back to the primal.
  79. """
  80. #V = project_and_eliminate_dep(D, V) ## No need because D is full-rank
  81. #r = V.nrows()
  82. # Project the dual basis orthogonally to v
  83. PV = projection_matrix(V)
  84. D = D - D * PV
  85. #print(f'LLL... ({return_basis})', end='')
  86. # Eliminate linear dependencies
  87. #if return_basis:
  88. # D = remove_linear_dependencies(D)
  89. #print(f'{D.nrows()}')
  90. #print('Done.')
  91. # Go back to the primal
  92. return D
  93. def lattice_project_against(B, V):
  94. """
  95. Compute the projection of the lattice L(B) orthogonally to Span(V). All vectors if V
  96. (or at least their projection on Span(B)) must belong to L(B).
  97. Algorithm:
  98. - project V onto Span(B)
  99. - project the basis onto orth(V)
  100. - eliminate linear dependencies (LLL)
  101. """
  102. # Project v on Span(B)
  103. #V = project_and_eliminate_dep(B, V) ## No need because D is full-rank
  104. #r = V.nrows() # Useless
  105. # Check that V belogs to L(B)
  106. D = dual_basis(B)
  107. M = D * V.T
  108. if not lcm([x.denominator() for x in M.list()]) == 1:
  109. raise ValueError("Not in the lattice")
  110. # Project the basis orthogonally to v
  111. PV = projection_matrix(V)
  112. B = B - B * PV
  113. # Eliminate linear dependencies
  114. #if return_basis:
  115. # B = remove_linear_dependencies(B)
  116. # Go back to the primal
  117. return B
  118. def lattice_modular_intersection(D, V, k):
  119. """
  120. Compute the intersection of the lattice L(B) with
  121. the lattice {x | x*V = 0 mod k}
  122. (V can be either a vector or a matrix)
  123. Algorithm:
  124. - project V onto Span(B)
  125. - append the equations in the dual
  126. - eliminate linear dependencies (LLL)
  127. - go back to the primal.
  128. """
  129. # Project v on Span(B)
  130. #V = project_and_eliminate_dep(D, V) ## No need because D is full-rank
  131. #r = V.nrows() # Useless
  132. # append the equation in the dual
  133. V /= k
  134. # D = dual_basis(B)
  135. D = D.stack(V)
  136. # Eliminate linear dependencies
  137. #if return_basis:
  138. # D = remove_linear_dependencies(D)
  139. # Go back to the primal
  140. return D
  141. def is_diagonal(M):
  142. if M.nrows() != M.ncols():
  143. return False
  144. A = M.numpy()
  145. return np.all(A == np.diag(np.diagonal(A)))
  146. def logdet(M, exact=False):
  147. """
  148. Compute the log of the determinant of a large rational matrix,
  149. tryping to avoid overflows.
  150. """
  151. if not exact:
  152. MM = array(M, dtype=float)
  153. _, l = slogdet(MM)
  154. return l
  155. a = abs(M.det())
  156. l = 0
  157. while a > 2**32:
  158. l += RR(32 * ln(2))
  159. a /= 2**32
  160. l += ln(RR(a))
  161. return l
  162. def degen_inverse(S, B=None):
  163. """ Compute the inverse of a symmetric matrix restricted
  164. to its span
  165. """
  166. # Get an orthogonal basis for the Span of B
  167. if B is None:
  168. # Get an orthogonal basis for the Span of B
  169. V = S.echelon_form()
  170. V = V[:V.rank()]
  171. P = projection_matrix(V)
  172. else:
  173. P = projection_matrix(B)
  174. # make S non-degenerated by adding the complement of span(B)
  175. C = identity_matrix(S.ncols()) - P
  176. Sinv = (S + C).inverse() - C
  177. assert S * Sinv == P, "Consistency failed (probably not your fault)."
  178. assert P * Sinv == Sinv, "Consistency failed (probably not your fault)."
  179. return Sinv
  180. def degen_logdet(S, B=None):
  181. """ Compute the determinant of a symmetric matrix
  182. sigma (m x m) restricted to the span of the full-rank
  183. rectangular (k x m, k <= m) matrix V
  184. """
  185. # Get an orthogonal basis for the Span of B
  186. if B is None:
  187. # Get an orthogonal basis for the Span of B
  188. V = S.echelon_form()
  189. V = V[:V.rank()]
  190. P = projection_matrix(V)
  191. else:
  192. P = projection_matrix(B)
  193. # Check that S is indeed supported by span(B)
  194. #assert S == P.T * S * P
  195. assert (S - P.T * S * P).norm() <= 1e-10
  196. # make S non-degenerated by adding the complement of span(B)
  197. C = identity_matrix(S.ncols()) - P
  198. l3 = logdet(S + C)
  199. return l3
  200. def square_root_inverse_degen(S, B=None):
  201. """ Compute the determinant of a symmetric matrix
  202. sigma (m x m) restricted to the span of the full-rank
  203. rectangular (k x m, k <= m) matrix V
  204. """
  205. if B is None:
  206. # Get an orthogonal basis for the Span of B
  207. V = S.echelon_form()
  208. V = V[:V.rank()]
  209. P = projection_matrix(V)
  210. else:
  211. P = projection_matrix(B)
  212. # make S non-degenerated by adding the complement of span(B)
  213. C = identity_matrix(S.ncols()) - P
  214. S_inv = np_inv(array((S + C), dtype=float))
  215. S_inv = array(S_inv, dtype=float)
  216. L_inv = cholesky(S_inv)
  217. L_inv = round_matrix_to_rational(L_inv)
  218. L = L_inv.inverse()
  219. return L, L_inv
  220. def build_standard_substitution_matrix(V, pivot=None, data=None, output_data=False):
  221. _, pivot = V.nonzero_positions()[0] if (pivot is None) else (None, pivot)
  222. assert V[0,pivot] != 0, 'The value of the pivot must be non-zero.'
  223. dim = V.ncols()
  224. V1 = - V[0,:pivot] / V[0,pivot]
  225. V2 = - V[0,pivot+1:] / V[0,pivot]
  226. Gamma = zero_matrix(QQ, dim,dim-1)
  227. Gamma[:pivot,:pivot] = identity_matrix(pivot)
  228. Gamma[pivot,:pivot] = V1
  229. Gamma[pivot,pivot:] = V2
  230. Gamma[pivot+1:,pivot:] = identity_matrix(dim-pivot-1)
  231. data = data if not output_data else {}
  232. if data is not None:
  233. norm2 = 1 + scal(V1*V1.T) + scal(V2*V2.T)
  234. normalization_matrix = zero_matrix(QQ, dim-1,dim-1)
  235. normalization_matrix[:pivot,:pivot] = identity_matrix(pivot) - V1.T*V1 / norm2
  236. normalization_matrix[pivot:,pivot:] = identity_matrix(dim-pivot-1) - V2.T*V2 / norm2
  237. normalization_matrix[:pivot,pivot:] = - V1.T*V2 / norm2
  238. normalization_matrix[pivot:,:pivot] = - V2.T*V1 / norm2
  239. data['det'] = norm2
  240. data['normalization_matrix'] = normalization_matrix
  241. if output_data:
  242. return Gamma, data
  243. else:
  244. return Gamma