from fpylll import * # current version fpylll raises a lot of Deprecation warnings. Silence that. import warnings warnings.filterwarnings("ignore", category=DeprecationWarning) load("../framework/proba_utils.sage") load("../framework/utils.sage") load("../framework/geometry.sage") # Issues with hint that are caught, and only raise a warning class RejectedHint(Exception): def __init__(self, message): self.message = message def __str__(self): return self.message # Issues with hint that are caught, and only raise a warning class InvalidHint(Exception): def __init__(self, message): self.message = message def __str__(self): return self.message def not_after_projections(fn): def decorated(self, *args, **kwargs): # if self.projections: # self.logging("You can't integrate more hints after projection. Sorry.", ) # raise ValueError return fn(self, *args, **kwargs) return decorated def without_dependencies(fn): def decorated(self, *args, **kwargs): has_been_reduced = False if self.B is not None: has_been_reduced = has_been_reduced or (self.B.nrows() > self._dim) self.B = remove_linear_dependencies(self.B, self._dim) if self.D is not None: if has_been_reduced and (self.D.nrows() > self._dim): self.logging("(Warning, double computation with LLL)", priority=0, style='WARNING', newline=False) has_been_reduced = has_been_reduced or (self.D.nrows() > self._dim) self.D = remove_linear_dependencies(self.D, self._dim) return fn(self, *args, **kwargs) return decorated def hint_integration_wrapper(force=False, non_primitive_action=None, requires=[], invalidates=[], catch_invalid_hint=True, estimate=True): def decorator(fn): def decorated(self, *args, **kwargs): if self.verbosity: self.logging(fn.__name__.replace("_", " "), style='ACTION', priority=1, newline=False) if fn.__name__ == "integrate_perfect_hint": self.logging(hint_to_string( args[0], args[1]), style='DATA', priority=2, newline=False) if fn.__name__ == "integrate_modular_hint": self.logging("(smooth)" if kwargs.get( "smooth", True) else "(nonsmooth)", priority=1, newline=False) self.logging(hint_to_string( args[0], args[1]) + " MOD %d" % args[2], style='DATA', priority=2, newline=False) if fn.__name__ == "integrate_approx_hint": self.logging("(aposteriori)" if kwargs.get( "aposteriori", False) else "(conditionning)", priority=1, newline=False) self.logging(hint_to_string( args[0], args[1]) + " + χ(σ²=%.3f)" % args[2], style='DATA', priority=2, newline=False) if fn.__name__ == "integrate_short_vector_hint": self.logging(hint_to_string( args[0], None, lit="c") + "∈ Λ", style='DATA', priority=2, newline=False) if "primal" in requires and self.B is None: self.D = remove_linear_dependencies(self.D, self._dim) self.B = dual_basis(self.D) if "dual" in requires and self.D is None: self.B = remove_linear_dependencies(self.B, self._dim) self.D = dual_basis(self.B) if "force" in kwargs: _force = kwargs["force"] del kwargs["force"] else: _force = force if "estimate" in kwargs: _estimate = kwargs["estimate"] del kwargs["estimate"] else: _estimate = estimate if (not _force) or _estimate: self.stash() if "catch_invalid_hint" in kwargs: _catch_invalid_hint = kwargs["catch_invalid_hint"] del kwargs["catch_invalid_hint"] else: _catch_invalid_hint = catch_invalid_hint if "non_primitive_action" in kwargs: _non_primitive_action = kwargs["non_primitive_action"] del kwargs["non_primitive_action"] else: _non_primitive_action = non_primitive_action try: if _non_primitive_action is not None: self.test_primitive_dual(concatenate( args[0], -args[1]), _non_primitive_action) fn(self, *args, **kwargs) self.beta = None except RejectedHint as err: logging(str(err) + ", Rejected.", style="REJECT", newline=True) return False except InvalidHint as err: if _catch_invalid_hint: logging(str(err) + ", Invalid.", style="REJECT", newline=True) return False else: raise err if "primal" in invalidates: self.B = None if "dual" in invalidates: self.D = None if (not _force) or _estimate: return self.undo_if_unworthy(_force) else: self.logging("", newline=True) return True return decorated return decorator class DBDD_generic: """ This class defines all the elements defining a DBDD instance """ def __init__(): raise NotImplementedError( "The generic class is not meant to be used directly.") def leak(self, v): value = scal(self.u * concatenate([v, [0]]).T) return value def stash(self): if self.beta is None: self.estimate_attack(silent=True) for key, val in self.__dict__.items(): if key != "save": self.save[key] = copy(val) def pop(self): for key, val in self.save.items(): if key != "save": self.__dict__[key] = val def undo_if_unworthy(self, force): self.estimate_attack(silent=True) if (-self.beta, self.delta)\ <= (-self.save["beta"], self.save["delta"]): if force: self.logging("\t Unworthy hint, Forced it.", style="REJECT") return True else: self.logging("\t Unworthy hint, Rejected.", style="REJECT") self.pop() return False self.logging("\t Worthy hint !", style="ACCEPT", newline=False) self.logging("dim=%3d, δ=%.8f, β=%3.2f" % (self.dim(), self.delta, self.beta), style="VALUE") return True def logging(self, message, priority=1, style='NORMAL', newline=True): if priority > self.verbosity: return logging(message, style=style, newline=newline) def check_solution(self, solution): """ Checks wether the solution is correct If the private attributes of the instance are not None, the solution is compared to them. It outputs True if the solution is indeed the same as the private s and e, False otherwise. If the private e and s are not stored, we check that the solution is small enough. :solution: a vector """ if self.u == solution or self.u == - solution: return True if scal(solution * solution.T) > 1.2 * self.expected_length: #print(scal(solution * solution.T), 1.2 * self.expected_length) return False if self.u is None: return True if self.verbosity: self.logging("Found an incorrect short solution.", priority=-1, style="WARNING") #print(solution) return False @not_after_projections @hint_integration_wrapper() def integrate_perfect_hint(self, v, l): raise NotImplementedError("This method is not generic.") @not_after_projections @hint_integration_wrapper() def integrate_modular_hint(self, v, l, k, smooth=True): raise NotImplementedError("This method is not generic.") @not_after_projections @hint_integration_wrapper() def integrate_approx_hint(self, v, l, variance, aposteriori=False): raise NotImplementedError("This method is not generic.") @not_after_projections @hint_integration_wrapper() def integrate_approx_hint_fulldim(self, center, covariance): raise NotImplementedError("This method is not generic.") @hint_integration_wrapper() def integrate_short_vector_hint(self, v): raise NotImplementedError("This method is not generic.") def volumes(self): raise NotImplementedError("This method is not generic.") def dim(self): raise NotImplementedError("This method is not generic.") def test_primitive_dual(self, V, action): raise NotImplementedError("This method is not generic.") def estimate_attack(self, probabilistic=False, tours=1, silent=False): """ Assesses the complexity of the lattice attack on the instance. Return value in Bikz """ (Bvol, Svol, dvol) = self.volumes() dim_ = self.dim() beta, delta = compute_beta_delta( dim_, dvol, probabilistic=probabilistic, tours=tours) self.dvol = dvol self.delta = delta self.beta = beta if self.verbosity and not silent: self.logging(" Attack Estimation ", style="HEADER") self.logging("ln(dvol)=%4.7f \t ln(Bvol)=%4.7f \t ln(Svol)=%4.7f \t" % (dvol, Bvol, Svol) + "δ(β)=%.6f" % compute_delta(beta), style="DATA", priority=2) self.logging("dim=%3d \t δ=%.6f \t β=%3.2f " % (dim_, delta, beta), style="VALUE") self.logging("") return (beta, delta) def attack(self, beta_max=None, beta_pre=None, randomize=False, tours=1): raise NotImplementedError( "The generic class is not meant to be used directly.") def S_diag(self): raise NotImplementedError( "The generic class is not meant to be used directly.") def integrate_q_vectors(self, q, min_dim=0, report_every=1, indices=None): self.logging(" Integrating q-vectors ", style="HEADER") Sd = self.S_diag() n = len(Sd) I = [] J = [] M = q * identity_matrix(n - 1) it = 0 verbosity = self.verbosity if indices is None: indices = range(n - 1) while self.dim() > min_dim: if (it % report_every == 0) and report_every > 1: self.logging("[...%d]" % report_every, newline=False) Sd = self.S_diag() L = [(Sd[i], i) for i in indices if i not in I] if len(L) == 0: break _, i = max(L) I += [i] try: didit = self.integrate_short_vector_hint( vec(M[i]), catch_invalid_hint=False) if not didit: break J += [i] except InvalidHint as err: self.logging(str(err) + ", Invalid.", style="REJECT", priority=1, newline=True) it += 1 self.verbosity = verbosity if (it % report_every == 0) else 0 self.verbosity = verbosity return [vec(M[i]) for i in J]