Skip to content
Open
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
124 changes: 93 additions & 31 deletions lwe_challenge.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from fpylll import BKZ as fplll_bkz
from fpylll.algorithms.bkz2 import BKZReduction
from fpylll.tools.bkz_simulator import simulate
from fpylll.tools.quality import basis_quality
from fpylll.util import gaussian_heuristic, set_threads

Expand Down Expand Up @@ -93,6 +94,7 @@ def lwe_kernel(arg0, params=None, seed=None):
# flow of the lwe solver
svp_bkz_time_factor = params.pop("lwe/svp_bkz_time_factor")
goal_margin = params.pop("lwe/goal_margin")
favour_memory_factor = params.pop("lwe/favour_memory_factor")

# generation of lwe instance and Kannan's embedding
alpha = params.pop("lwe/alpha")
Expand All @@ -108,8 +110,8 @@ def lwe_kernel(arg0, params=None, seed=None):
set_threads(threads)

A, c, q = load_lwe_challenge(n=n, alpha=alpha)
print( "-------------------------" )
print( "Primal attack, LWE challenge n=%d, alpha=%.4f" % (n, alpha) )
print("-------------------------")
print("Primal attack, LWE challenge n=%d, alpha=%.4f" % (n, alpha))

if m is None:
try:
Expand All @@ -125,21 +127,20 @@ def lwe_kernel(arg0, params=None, seed=None):
(b, s, _) = min_cost_param
except TypeError:
raise TypeError("No winning parameters.")
print( "Chose %d samples. Predict solution at bkz-%d + svp-%d" % (m, b, s))
print("Chose %d samples. Predict solution at bkz-%d + svp-%d" % (m, b, s))
print("")

target_norm = goal_margin * (alpha*q)**2 * m + 1

if blocksizes is not None:
blocksizes = list(range(10, 40)) + eval("range(%s)" % re.sub(":", ",", blocksizes)) # noqa
else:
# blocksizes = list(range(10, 50)) + list(range(90, b-15, 5)) + [b-15] + list(range(b - 13, b + 25, 2))
blocksizes = list(range(10, 50)) + list(range(70, b-15, 10)) + [b-15] + list(range(b - 13, b + 25, 2))
blocksizes = list(range(10, 50)) + list(range(70, b-15, 10)) + [b-15] + list(range(b - 13, b + 25, 2)) # noqa

B = primal_lattice_basis(A, c, q, m=m)

g6k = Siever(B, params)
print( "GSO precision: ", g6k.M.float_type )
print("GSO precision: ", g6k.M.float_type)

if dont_trace:
tracer = dummy_tracer
Expand All @@ -149,17 +150,19 @@ def lwe_kernel(arg0, params=None, seed=None):
d = g6k.full_n
g6k.lll(0, g6k.full_n)
slope = basis_quality(g6k.M)["/"]
print( "Intial Slope = %.5f\n" % slope )
print("Intial Slope = %.5f\n" % slope)

T0 = time.time()
T0_BKZ = time.time()
T_blocksizes = {}
for blocksize in blocksizes:
for tt in range(tours):
# BKZ tours

T0_this_blocksize = time.time()
if blocksize < fpylll_crossover:
if verbose:
print( "Starting a fpylll BKZ-%d tour. " % (blocksize), end='')
print("Starting a fpylll BKZ-%d tour. " %
(blocksize), end='')
sys.stdout.flush()
bkz = BKZReduction(g6k.M)
par = fplll_bkz.Param(blocksize,
Expand All @@ -169,7 +172,7 @@ def lwe_kernel(arg0, params=None, seed=None):

else:
if verbose:
print( "Starting a pnjBKZ-%d tour. " % (blocksize) )
print("Starting a pnjBKZ-%d tour. " % (blocksize))

pump_n_jump_bkz_tour(g6k, tracer, blocksize, jump=jump,
verbose=verbose,
Expand All @@ -178,53 +181,111 @@ def lwe_kernel(arg0, params=None, seed=None):
goal_r0=target_norm,
pump_params=pump_params)

# total time spent on BKZ thus far
T_BKZ = time.time() - T0_BKZ

# time for one tour of this blocksize
T_blocksizes[blocksize] = time.time() - T0_this_blocksize

if verbose:
slope = basis_quality(g6k.M)["/"]
fmt = "slope: %.5f, walltime: %.3f sec"
print( fmt % (slope, time.time() - T0) )
print(fmt % (slope, time.time() - T0))

g6k.lll(0, g6k.full_n)

if g6k.M.get_r(0, 0) <= target_norm:
break

# overdoing n_max would allocate too much memory, so we are careful
svp_Tmax = svp_bkz_time_factor * T_BKZ
expo = 0.292
# solving for maximal d s.t. 2^{expo * d} / param.threads <= svp_Tmax
# cannot figure out the additive 58, will leave for now
n_max = int(58+6 + (1./expo) * log(svp_Tmax * params.threads)/log(2.))

rr = [g6k.M.get_r(i, i) for i in range(d)]
for n_expected in range(2, d-2):
x = (target_norm/goal_margin) * n_expected/(1.*d)
if 4./3 * gaussian_heuristic(rr[d-n_expected:]) > x:
break

print( "Without otf, would expect solution at pump-%d. n_max=%d in the given time." % (n_expected, n_max) )# noqa
if n_expected >= n_max - 1:
svp_Tmax = svp_bkz_time_factor * T_BKZ

def attainable_SVP_dim(T_svp):
# solve max d s.t. 2^{expo * d} / param.threads <= svp_Tmax
return int(58+6 + (1./expo) *
log(T_svp * params.threads)/log(2.))

def expected_successful_SVP_dim(rr=None):
if rr is None:
rr = [g6k.M.get_r(i, i) for i in range(d)]
for n_expected in range(2, d):
x = (target_norm/goal_margin) * n_expected/(1.*d)
if 4./3 * gaussian_heuristic(rr[d-n_expected:]) > x:
break
return n_expected

# given current state of basis
current_n_max = attainable_SVP_dim(svp_Tmax)
current_n_expected = expected_successful_SVP_dim()

print("Without otf, would expect solution at pump-%d. n_max=%d in the given time." % (current_n_expected, current_n_max)) # noqa
if current_n_expected >= current_n_max - 1:
# stronger BKZ required
continue

# perhaps we still prefer stronger BKZ, e.g. to reduce memory
# currently look at the next three potential tours
ind = blocksizes.index(blocksize)
remaining_blocksizes = blocksizes[ind+1:ind+4]

# expected extra BKZ times, and future basis profiles
extra_T_BKZs = []
future_rrs = []

# data on blocksize, basis profile, individual blocksize times
previous_blocksize = blocksize
rr = list(g6k.M.r())
extra_T_BKZ = 0
temp_T_blocksizes = T_blocksizes

for rblock in remaining_blocksizes:
# simulate basis profile after next tour, update basis profile
bkz_params = fplll_bkz.Param(block_size=rblock, max_loops=1)
rr, _ = simulate(rr, bkz_params)
future_rrs += [rr]

# time for BKZ-rblock tour as a function of the previous tour
T_rblock_BKZ = temp_T_blocksizes[previous_blocksize] * 2**(expo * (rblock - previous_blocksize)) # noqa
temp_T_blocksizes[rblock] = T_rblock_BKZ

# total extra BKZ time with rblock tour
extra_T_BKZ += T_rblock_BKZ
extra_T_BKZs += [extra_T_BKZ]
previous_blocksize = rblock

more_BKZ = False

for (rr, extra_T_BKZ) in zip(future_rrs, extra_T_BKZs):
potential_n_expected = expected_successful_SVP_dim(rr=rr)
svp_dim_dec = potential_n_expected - current_n_expected
if svp_dim_dec >= 0:
continue
time_SVP_current = 2**(expo * (current_n_expected - 64))
time_SVP_current /= params.threads
if extra_T_BKZ < favour_memory_factor * (1-2**(expo*svp_dim_dec)) * time_SVP_current: # noqa
more_BKZ = True

if more_BKZ:
continue

n_max += 1
current_n_max += 1

# Larger SVP

llb = d - blocksize
while gaussian_heuristic([g6k.M.get_r(i, i) for i in range(llb, d)]) < target_norm * (d - llb)/(1.*d): # noqa
llb -= 1

f = d-llb-n_max
f = d-llb-current_n_max
if verbose:
print( "Starting svp pump_{%d, %d, %d}, n_max = %d, Tmax= %.2f sec" % (llb, d-llb, f, n_max, svp_Tmax) ) # noqa
print( "Starting svp pump_{%d, %d, %d}, n_max = %d, Tmax= %.2f sec" % (llb, d-llb, f, current_n_max, svp_Tmax) ) # noqa
pump(g6k, tracer, llb, d-llb, f, verbose=verbose,
goal_r0=target_norm * (d - llb)/(1.*d))

if verbose:
slope = basis_quality(g6k.M)["/"]
fmt = "\n slope: %.5f, walltime: %.3f sec"
print( fmt % (slope, time.time() - T0) )
print(fmt % (slope, time.time() - T0))
print

g6k.lll(0, g6k.full_n)
Expand All @@ -233,8 +294,8 @@ def lwe_kernel(arg0, params=None, seed=None):
break

if g6k.M.get_r(0, 0) <= target_norm:
print( "Finished! TT=%.2f sec" % (time.time() - T0) )
print( g6k.M.B[0] )
print("Finished! TT=%.2f sec" % (time.time() - T0))
print(g6k.M.B[0])
alpha_ = int(alpha*1000)
filename = 'lwechallenge/%03d-%03d-solution.txt' % (n, alpha_)
fn = open(filename, "w")
Expand All @@ -257,6 +318,7 @@ def lwe():
lwe__m=None,
lwe__goal_margin=1.5,
lwe__svp_bkz_time_factor=1,
lwe__favour_memory_factor=1,
bkz__blocksizes=None,
bkz__tours=1,
bkz__jump=1,
Expand Down