Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SIS Fixes #100

Merged
merged 10 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions estimator/sis.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@

from .sis_lattice import lattice
from .sis_parameters import SISParameters as Parameters # noqa
from .conf import (red_cost_model as red_cost_model_default,
red_shape_model as red_shape_model_default)
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


class Estimate:

def rough(self, params, jobs=1, catch_exceptions=True):
"""
This function makes the following somewhat routine assumptions:
Expand Down Expand Up @@ -47,7 +48,8 @@ def rough(self, params, jobs=1, catch_exceptions=True):
)
res_raw = res_raw[params]
res = {
algorithm: v for algorithm, attack in algorithms.items()
algorithm: v
for algorithm, attack in algorithms.items()
for k, v in res_raw.items()
if f_name(attack) == k
}
Expand Down Expand Up @@ -91,8 +93,8 @@ def __call__(
>>> _ = SIS.estimate(params)
lattice :: rop: ≈2^47.0, red: ≈2^47.0, δ: 1.011391, β: 61, d: 276, tag: euclidean

>>> _ = SIS.estimate(params.updated(norm=oo), red_shape_model="cn11")
lattice :: rop: ≈2^43.6, red: ≈2^42.6, sieve: ≈2^42.7, β: 40, η: 67, ζ: 112, d: 750, ...
>>> _ = SIS.estimate(params.updated(length_bound=16, norm=oo), red_shape_model="cn11")
lattice :: rop: ≈2^65.9, red: ≈2^64.9, sieve: ≈2^64.9, β: 113, η: 142, ζ: 0, d: 2486, ...
"""

algorithms = {}
Expand Down
42 changes: 27 additions & 15 deletions estimator/sis_lattice.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ class SISLattice:
"""
Estimate cost of solving SIS via lattice reduction.
"""

@staticmethod
def _solve_for_delta_euclidean(params, d):
# root_volume = params.q**(params.n/d)
# delta = (params.length_bound / root_volume)**(1/(d - 1))
root_volume = (params.n/d) * log(params.q, 2)
log_delta = (1/(d - 1)) * (log(params.length_bound, 2) - root_volume)
root_volume = (params.n / d) * log(params.q, 2)
log_delta = (1 / (d - 1)) * (log(params.length_bound, 2) - root_volume)
return RR(2**log_delta)

@staticmethod
Expand All @@ -40,7 +41,7 @@ def _opt_sis_d(params):
Optimizes SIS dimension for the given parameters, assuming the optimal
d \approx sqrt(n*log(q)/log(delta))
"""
log_delta = log(params.length_bound, 2)**2 / (4 * params.n * log(params.q, 2))
log_delta = log(params.length_bound, 2) ** 2 / (4 * params.n * log(params.q, 2))
d = sqrt(params.n * log(params.q, 2) / log_delta)
return d

Expand Down Expand Up @@ -71,8 +72,10 @@ def cost_euclidean(
beta = d
reduction_possible = False

lb = min(RR(sqrt(params.n * log(params.q))), RR(sqrt(d) * params.q**(params.n/d)))
return costf(red_cost_model, beta, d, predicate=params.length_bound > lb and reduction_possible)
lb = min(RR(sqrt(params.n * log(params.q))), RR(sqrt(d) * params.q ** (params.n / d)))
return costf(
red_cost_model, beta, d, predicate=params.length_bound > lb and reduction_possible
)

@staticmethod
@cached_function
Expand Down Expand Up @@ -110,6 +113,9 @@ def cost_infinity(
# Calculate the basis shape to aid in both styles of analysis
d_ = d - zeta

if d_ < beta:
return Cost(rop=oo, mem=oo)

r = simulator(d=d_, n=d_ - params.n, q=params.q, beta=beta, xi=1, tau=False)

# Cost the sampling of short vectors.
Expand All @@ -121,7 +127,7 @@ def cost_infinity(
vector_length = rho * sqrt(r[0])
# Find probability that all coordinates meet norm bound
sigma = vector_length / sqrt(d_)
log_trial_prob = RR(d_*log(1 - 2*gaussian_cdf(0, sigma, -params.length_bound), 2))
log_trial_prob = RR(d_ * log(1 - 2 * gaussian_cdf(0, sigma, -params.length_bound), 2))

else: # Dilithium style analysis
# Find first non-q-vector in r
Expand All @@ -142,10 +148,14 @@ def cost_infinity(
gaussian_coords = max(idx_end - idx_start + 1, sieve_dim)
sigma = vector_length / sqrt(gaussian_coords)

log_trial_prob = RR(log(1 - 2*gaussian_cdf(0, sigma, -params.length_bound), 2)*(gaussian_coords))
log_trial_prob += RR(log((2*params.length_bound + 1)/params.q, 2)*(idx_start))
log_trial_prob = RR(
log(1 - 2 * gaussian_cdf(0, sigma, -params.length_bound), 2) * (gaussian_coords)
)
log_trial_prob += RR(log((2 * params.length_bound + 1) / params.q, 2) * (idx_start))

probability = 2**min(0, log_trial_prob + RR(log(N, 2))) # expected number of solutions (max 1)
probability = 2 ** min(
0, log_trial_prob + RR(log(N, 2))
) # expected number of solutions (max 1)
ret = Cost()
ret["rop"] = cost_red
ret["red"] = bkz_cost["rop"]
Expand Down Expand Up @@ -272,11 +282,11 @@ def __call__(
>>> SIS.lattice(params)
rop: ≈2^47.0, red: ≈2^47.0, δ: 1.011391, β: 61, d: 276, tag: euclidean

>>> SIS.lattice(params.updated(norm=oo), red_shape_model="lgsa")
rop: ≈2^43.6, red: ≈2^42.6, sieve: ≈2^42.7, β: 40, η: 67, ζ: 112, d: 750, prob: 1, ↻: 1, tag: infinity
>>> SIS.lattice(params.updated(norm=oo, length_bound=16), red_shape_model="lgsa")
rop: ≈2^61.0, red: ≈2^59.9, sieve: ≈2^60.1, β: 95, η: 126, ζ: 0, d: 2486, prob: 1, ↻: 1, tag: infinity

>>> SIS.lattice(params.updated(norm=oo), red_shape_model="cn11")
rop: ≈2^43.6, red: ≈2^42.6, sieve: ≈2^42.7, β: 40, η: 67, ζ: 112, d: 750, prob: 1, ↻: 1, tag: infinity
>>> SIS.lattice(params.updated(norm=oo, length_bound=16), red_shape_model="cn11")
rop: ≈2^65.9, red: ≈2^64.9, sieve: ≈2^64.9, β: 113, η: 142, ζ: 0, d: 2486, prob: 1, ↻: 1, tag: infinity

The success condition for euclidean norm bound is derived by determining the root hermite factor required for
BKZ to produce the required output. For infinity norm bounds, the success conditions are derived using a
Expand All @@ -291,7 +301,9 @@ def __call__(
elif params.norm == oo:
tag = "infinity"
else:
raise NotImplementedError("SIS attack estimation currently only supports euclidean and infinity norms")
raise NotImplementedError(
"SIS attack estimation currently only supports euclidean and infinity norms"
)

if tag == "infinity":
red_shape_model = simulator_normalize(red_shape_model)
Expand All @@ -305,7 +317,7 @@ def __call__(
)

if zeta is None:
with local_minimum(0, params.n, log_level=log_level) as it:
with local_minimum(0, params.m, log_level=log_level) as it:
for zeta in it:
it.update(
f(
Expand Down
8 changes: 6 additions & 2 deletions estimator/sis_parameters.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from dataclasses import dataclass

from sage.all import log, ceil
from sage.all import log, ceil, oo


@dataclass
Expand All @@ -19,7 +19,11 @@ class SISParameters:

def __post_init__(self, **kwds):
if not self.m:
self.m = ceil(self.n*log(self.q)) #: Set m to be the minimum required for a solution to exist.
#: Set m to be the minimum required for a solution to exist.
if self.norm == oo:
self.m = 2 * ceil(self.n * log(self.q, (2 * self.length_bound + 1)))
else:
self.m = 2 * ceil(self.n * log(self.q, 2))

@property
def _homogeneous(self):
Expand Down
Loading