Source code for estimator.ntru

# -*- coding: utf-8 -*-
"""
High-level NTRU interface
"""

from functools import partial
from sage.all import oo

from .ntru_primal import primal_dsd, primal_usvp, primal_bdd, primal_hybrid
from .lwe_bkw import coded_bkw # noqa
from .lwe_guess import exhaustive_search, mitm, distinguish, guess_composition # noqa
from .lwe_dual import dual, dual_hybrid # noqa
from .gb import arora_gb  # noqa
from .ntru_parameters import NTRUParameters as Parameters  # noqa
from .conf import (red_cost_model as red_cost_model_default,
                   red_shape_model as red_shape_model_default)
from .util import batch_estimate, f_name
from .reduction import RC


[docs] class Estimate:
[docs] def rough(self, params, jobs=1, catch_exceptions=True): """ This function makes the following (non-default) somewhat routine assumptions to evaluate the cost of lattice reduction, and to provide comparable numbers with most of the literature: - The ZGSA holds. - The Core-SVP model holds. Provided numbers are notably not directly comparable with the rest of our API, when using the default cost models. This function furthermore assumes the following heuristics: - The primal hybrid attack only applies to sparse secrets. - The dual hybrid MITM attack only applies to sparse secrets. - The dense sublattice attack only applies to possibly overstretched parameters :param params: NTRU parameters. :param jobs: Use multiple threads in parallel. :param catch_exceptions: When an estimate fails, just print a warning. EXAMPLE :: >>> from estimator import * >>> _ = NTRU.estimate.rough(schemes.NTRUHPS2048509Enc) usvp :: rop: ≈2^109.2, red: ≈2^109.2, δ: 1.004171, β: 374, d: 643, tag: usvp bdd_hybrid :: rop: ≈2^108.6, red: ≈2^107.7, svp: ≈2^107.5, β: 369, η: 368, ζ: 0, |S|: 1, ... """ params = params.normalize() algorithms = {} # Only primal attacks apply to NTRU algorithms["usvp"] = partial(primal_usvp, red_cost_model=RC.ADPS16, red_shape_model="zgsa") if params.possibly_overstretched: algorithms["dsd"] = partial( primal_dsd, red_cost_model=RC.ADPS16, red_shape_model="zgsa" ) if params.Xs.is_sparse: algorithms["bdd_hybrid"] = partial( primal_hybrid, mitm=False, babai=False, red_cost_model=RC.ADPS16, red_shape_model="ZGSA", ) res_raw = batch_estimate( params, algorithms.values(), log_level=1, jobs=jobs, catch_exceptions=catch_exceptions ) res_raw = res_raw[params] res = { algorithm: v for algorithm, attack in algorithms.items() for k, v in res_raw.items() if f_name(attack) == k } for algorithm in algorithms: if algorithm not in res: continue result = res[algorithm] if result["rop"] != oo: print(f"{algorithm:20s} :: {result!r}") return res
[docs] def __call__( self, params, red_cost_model=red_cost_model_default, red_shape_model=red_shape_model_default, deny_list=tuple(), add_list=tuple(), jobs=1, catch_exceptions=True, ): """ Run all estimates, based on the default cost and shape models for lattice reduction. :param params: NTRU parameters. :param red_cost_model: How to cost lattice reduction. :param red_shape_model: How to model the shape of a reduced basis (applies to primal attacks) :param deny_list: skip these algorithms :param add_list: add these ``(name, function)`` pairs to the list of algorithms to estimate.a :param jobs: Use multiple threads in parallel. :param catch_exceptions: When an estimate fails, just print a warning. EXAMPLE :: >>> from estimator import * >>> _ = NTRU.estimate(schemes.NTRUHRSS701Enc) usvp :: rop: ≈2^162.1, red: ≈2^162.1, δ: 1.003557, β: 470, d: 1317, tag: usvp bdd :: rop: ≈2^158.7, red: ≈2^157.7, svp: ≈2^157.7, β: 454, η: 489, d: 1306, tag: bdd bdd_hybrid :: rop: ≈2^158.7, red: ≈2^157.7, svp: ≈2^157.7, β: 454, η: 489, ζ: 0, |S|: 1, d: ... bdd_mitm_hybrid :: rop: ≈2^233.0, red: ≈2^232.1, svp: ≈2^232.0, β: 469, η: 2, ζ: 178, |S|: ... >>> params = NTRU.Parameters(n=113, q=512, Xs=ND.UniformMod(3), Xe=ND.UniformMod(3)) >>> _ = NTRU.estimate(params, catch_exceptions=False) usvp :: rop: ≈2^46.0, red: ≈2^46.0, δ: 1.011516, β: 59, d: 221, tag: usvp dsd :: rop: ≈2^37.9, red: ≈2^37.9, δ: 1.013310, β: 31, d: 226, tag: dsd bdd :: rop: ≈2^42.4, red: ≈2^41.0, svp: ≈2^41.8, β: 41, η: 70, d: 225, tag: bdd bdd_hybrid :: rop: ≈2^42.4, red: ≈2^41.0, svp: ≈2^41.8, β: 41, η: 70, ζ: 0, |S|: 1, d: 226, ... bdd_mitm_hybrid :: rop: ≈2^55.6, red: ≈2^54.7, svp: ≈2^54.6, β: 41, η: 2, ζ: 32, |S|: ≈2^50.7, ... """ params = params.normalize() algorithms = {} algorithms["usvp"] = partial( primal_usvp, red_cost_model=red_cost_model, red_shape_model=red_shape_model ) algorithms["dsd"] = partial( primal_dsd, red_cost_model=red_cost_model, red_shape_model=red_shape_model ) algorithms["bdd"] = partial( primal_bdd, red_cost_model=red_cost_model, red_shape_model=red_shape_model ) algorithms["bdd_hybrid"] = partial( primal_hybrid, mitm=False, babai=False, red_cost_model=red_cost_model, red_shape_model=red_shape_model, ) # we ignore the case of mitm=True babai=False for now, due to it being overly-optimistic algorithms["bdd_mitm_hybrid"] = partial( primal_hybrid, mitm=True, babai=True, red_cost_model=red_cost_model, red_shape_model=red_shape_model, ) algorithms = {k: v for k, v in algorithms.items() if k not in deny_list} algorithms.update(add_list) res_raw = batch_estimate( params, algorithms.values(), log_level=1, jobs=jobs, catch_exceptions=catch_exceptions ) res_raw = res_raw[params] res = { algorithm: v for algorithm, attack in algorithms.items() for k, v in res_raw.items() if f_name(attack) == k } for algorithm in algorithms: if algorithm not in res: continue result = res[algorithm] if result["rop"] == oo: continue if algorithm == "hybrid" and res["bdd"]["rop"] < result["rop"]: continue if algorithm == "dsd" and res["usvp"]["rop"] < result["rop"]: continue print(f"{algorithm:20s} :: {result!r}") return res
estimate = Estimate()