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
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

преди 4 години
преди 4 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. from fpylll import *
  2. # current version fpylll raises a lot of Deprecation warnings. Silence that.
  3. import warnings
  4. warnings.filterwarnings("ignore", category=DeprecationWarning)
  5. load("../framework/proba_utils.sage")
  6. load("../framework/utils.sage")
  7. load("../framework/geometry.sage")
  8. # Issues with hint that are caught, and only raise a warning
  9. class RejectedHint(Exception):
  10. def __init__(self, message):
  11. self.message = message
  12. def __str__(self):
  13. return self.message
  14. # Issues with hint that are caught, and only raise a warning
  15. class InvalidHint(Exception):
  16. def __init__(self, message):
  17. self.message = message
  18. def __str__(self):
  19. return self.message
  20. def not_after_projections(fn):
  21. def decorated(self, *args, **kwargs):
  22. # if self.projections:
  23. # self.logging("You can't integrate more hints after projection. Sorry.", )
  24. # raise ValueError
  25. return fn(self, *args, **kwargs)
  26. return decorated
  27. def without_dependencies(fn):
  28. def decorated(self, *args, **kwargs):
  29. has_been_reduced = False
  30. if self.B is not None:
  31. has_been_reduced = has_been_reduced or (self.B.nrows() > self._dim)
  32. self.B = remove_linear_dependencies(self.B, self._dim)
  33. if self.D is not None:
  34. if has_been_reduced and (self.D.nrows() > self._dim):
  35. self.logging("(Warning, double computation with LLL)", priority=0, style='WARNING', newline=False)
  36. has_been_reduced = has_been_reduced or (self.D.nrows() > self._dim)
  37. self.D = remove_linear_dependencies(self.D, self._dim)
  38. return fn(self, *args, **kwargs)
  39. return decorated
  40. def hint_integration_wrapper(force=False,
  41. non_primitive_action=None,
  42. requires=[],
  43. invalidates=[],
  44. catch_invalid_hint=True,
  45. estimate=True):
  46. def decorator(fn):
  47. def decorated(self, *args, **kwargs):
  48. if self.verbosity:
  49. self.logging(fn.__name__.replace("_", " "),
  50. style='ACTION', priority=1, newline=False)
  51. if fn.__name__ == "integrate_perfect_hint":
  52. self.logging(hint_to_string(
  53. args[0], args[1]), style='DATA',
  54. priority=2, newline=False)
  55. if fn.__name__ == "integrate_modular_hint":
  56. self.logging("(smooth)" if kwargs.get(
  57. "smooth", True) else "(nonsmooth)",
  58. priority=1, newline=False)
  59. self.logging(hint_to_string(
  60. args[0], args[1]) + " MOD %d" % args[2], style='DATA',
  61. priority=2, newline=False)
  62. if fn.__name__ == "integrate_approx_hint":
  63. self.logging("(aposteriori)" if kwargs.get(
  64. "aposteriori", False) else "(conditionning)",
  65. priority=1, newline=False)
  66. self.logging(hint_to_string(
  67. args[0], args[1]) + " + χ(σ²=%.3f)" % args[2],
  68. style='DATA', priority=2, newline=False)
  69. if fn.__name__ == "integrate_short_vector_hint":
  70. self.logging(hint_to_string(
  71. args[0], None, lit="c") + "∈ Λ", style='DATA',
  72. priority=2, newline=False)
  73. if "primal" in requires and self.B is None:
  74. self.D = remove_linear_dependencies(self.D, self._dim)
  75. self.B = dual_basis(self.D)
  76. if "dual" in requires and self.D is None:
  77. self.B = remove_linear_dependencies(self.B, self._dim)
  78. self.D = dual_basis(self.B)
  79. if "force" in kwargs:
  80. _force = kwargs["force"]
  81. del kwargs["force"]
  82. else:
  83. _force = force
  84. if "estimate" in kwargs:
  85. _estimate = kwargs["estimate"]
  86. del kwargs["estimate"]
  87. else:
  88. _estimate = estimate
  89. if (not _force) or _estimate:
  90. self.stash()
  91. if "catch_invalid_hint" in kwargs:
  92. _catch_invalid_hint = kwargs["catch_invalid_hint"]
  93. del kwargs["catch_invalid_hint"]
  94. else:
  95. _catch_invalid_hint = catch_invalid_hint
  96. if "non_primitive_action" in kwargs:
  97. _non_primitive_action = kwargs["non_primitive_action"]
  98. del kwargs["non_primitive_action"]
  99. else:
  100. _non_primitive_action = non_primitive_action
  101. try:
  102. if _non_primitive_action is not None:
  103. self.test_primitive_dual(concatenate(
  104. args[0], -args[1]), _non_primitive_action)
  105. fn(self, *args, **kwargs)
  106. self.beta = None
  107. except RejectedHint as err:
  108. logging(str(err) + ", Rejected.", style="REJECT", newline=True)
  109. return False
  110. except InvalidHint as err:
  111. if _catch_invalid_hint:
  112. logging(str(err) + ", Invalid.",
  113. style="REJECT", newline=True)
  114. return False
  115. else:
  116. raise err
  117. if "primal" in invalidates:
  118. self.B = None
  119. if "dual" in invalidates:
  120. self.D = None
  121. if (not _force) or _estimate:
  122. return self.undo_if_unworthy(_force)
  123. else:
  124. self.logging("", newline=True)
  125. return True
  126. return decorated
  127. return decorator
  128. class DBDD_generic:
  129. """
  130. This class defines all the elements defining a DBDD instance
  131. """
  132. def __init__():
  133. raise NotImplementedError(
  134. "The generic class is not meant to be used directly.")
  135. def leak(self, v):
  136. value = scal(self.u * concatenate([v, [0]]).T)
  137. return value
  138. def stash(self):
  139. if self.beta is None:
  140. self.estimate_attack(silent=True)
  141. for key, val in self.__dict__.items():
  142. if key != "save":
  143. self.save[key] = copy(val)
  144. def pop(self):
  145. for key, val in self.save.items():
  146. if key != "save":
  147. self.__dict__[key] = val
  148. def undo_if_unworthy(self, force):
  149. self.estimate_attack(silent=True)
  150. if (-self.beta, self.delta)\
  151. <= (-self.save["beta"], self.save["delta"]):
  152. if force:
  153. self.logging("\t Unworthy hint, Forced it.", style="REJECT")
  154. return True
  155. else:
  156. self.logging("\t Unworthy hint, Rejected.", style="REJECT")
  157. self.pop()
  158. return False
  159. self.logging("\t Worthy hint !", style="ACCEPT", newline=False)
  160. self.logging("dim=%3d, δ=%.8f, β=%3.2f" %
  161. (self.dim(), self.delta, self.beta), style="VALUE")
  162. return True
  163. def logging(self, message, priority=1, style='NORMAL', newline=True):
  164. if priority > self.verbosity:
  165. return
  166. logging(message, style=style, newline=newline)
  167. def check_solution(self, solution):
  168. """ Checks wether the solution is correct
  169. If the private attributes of the instance are not None,
  170. the solution is compared to them. It outputs True
  171. if the solution is indeed the same as the private s and e,
  172. False otherwise.
  173. If the private e and s are not stored, we check that the
  174. solution is small enough.
  175. :solution: a vector
  176. """
  177. if self.u == solution or self.u == - solution:
  178. return True
  179. if scal(solution * solution.T) > 1.2 * self.expected_length:
  180. #print(scal(solution * solution.T), 1.2 * self.expected_length)
  181. return False
  182. if self.u is None:
  183. return True
  184. if self.verbosity:
  185. self.logging("Found an incorrect short solution.",
  186. priority=-1, style="WARNING")
  187. #print(solution)
  188. return False
  189. @not_after_projections
  190. @hint_integration_wrapper()
  191. def integrate_perfect_hint(self, v, l):
  192. raise NotImplementedError("This method is not generic.")
  193. @not_after_projections
  194. @hint_integration_wrapper()
  195. def integrate_modular_hint(self, v, l, k, smooth=True):
  196. raise NotImplementedError("This method is not generic.")
  197. @not_after_projections
  198. @hint_integration_wrapper()
  199. def integrate_approx_hint(self, v, l, variance, aposteriori=False):
  200. raise NotImplementedError("This method is not generic.")
  201. @not_after_projections
  202. @hint_integration_wrapper()
  203. def integrate_approx_hint_fulldim(self, center, covariance):
  204. raise NotImplementedError("This method is not generic.")
  205. @hint_integration_wrapper()
  206. def integrate_short_vector_hint(self, v):
  207. raise NotImplementedError("This method is not generic.")
  208. def volumes(self):
  209. raise NotImplementedError("This method is not generic.")
  210. def dim(self):
  211. raise NotImplementedError("This method is not generic.")
  212. def test_primitive_dual(self, V, action):
  213. raise NotImplementedError("This method is not generic.")
  214. def estimate_attack(self, probabilistic=False, tours=1, silent=False):
  215. """ Assesses the complexity of the lattice attack on the instance.
  216. Return value in Bikz
  217. """
  218. (Bvol, Svol, dvol) = self.volumes()
  219. dim_ = self.dim()
  220. beta, delta = compute_beta_delta(
  221. dim_, dvol, probabilistic=probabilistic, tours=tours)
  222. self.dvol = dvol
  223. self.delta = delta
  224. self.beta = beta
  225. if self.verbosity and not silent:
  226. self.logging(" Attack Estimation ", style="HEADER")
  227. self.logging("ln(dvol)=%4.7f \t ln(Bvol)=%4.7f \t ln(Svol)=%4.7f \t"
  228. % (dvol, Bvol, Svol) +
  229. "δ(β)=%.6f" % compute_delta(beta),
  230. style="DATA", priority=2)
  231. self.logging("dim=%3d \t δ=%.6f \t β=%3.2f " %
  232. (dim_, delta, beta), style="VALUE")
  233. self.logging("")
  234. return (beta, delta)
  235. def attack(self, beta_max=None, beta_pre=None, randomize=False, tours=1):
  236. raise NotImplementedError(
  237. "The generic class is not meant to be used directly.")
  238. def S_diag(self):
  239. raise NotImplementedError(
  240. "The generic class is not meant to be used directly.")
  241. def integrate_q_vectors(self, q, min_dim=0, report_every=1, indices=None):
  242. self.logging(" Integrating q-vectors ", style="HEADER")
  243. Sd = self.S_diag()
  244. n = len(Sd)
  245. I = []
  246. J = []
  247. M = q * identity_matrix(n - 1)
  248. it = 0
  249. verbosity = self.verbosity
  250. if indices is None:
  251. indices = range(n - 1)
  252. while self.dim() > min_dim:
  253. if (it % report_every == 0) and report_every > 1:
  254. self.logging("[...%d]" % report_every, newline=False)
  255. Sd = self.S_diag()
  256. L = [(Sd[i], i) for i in indices if i not in I]
  257. if len(L) == 0:
  258. break
  259. _, i = max(L)
  260. I += [i]
  261. try:
  262. didit = self.integrate_short_vector_hint(
  263. vec(M[i]), catch_invalid_hint=False)
  264. if not didit:
  265. break
  266. J += [i]
  267. except InvalidHint as err:
  268. self.logging(str(err) + ", Invalid.",
  269. style="REJECT", priority=1, newline=True)
  270. it += 1
  271. self.verbosity = verbosity if (it % report_every == 0) else 0
  272. self.verbosity = verbosity
  273. return [vec(M[i]) for i in J]