-
Notifications
You must be signed in to change notification settings - Fork 0
/
estimates.py
249 lines (200 loc) · 8.53 KB
/
estimates.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# -*- coding: utf-8 -*-
"""
Security cross-estimation for lattice based schemes proposed to NIST against the primal and dual attacks.
This script assumes that the LWE estimator commit 9302d42 is present in a subdirectory `estimator`.
NOTATION:
LWE
n lwe secret dimension
k mlwe rank
q lwe modulo
sd lwe secret standard deviation (if normal form)
m number of lwe samples
AUTHOR:
Rachel Player - 2017, 2018
Fernando Virdia - 2017, 2018
Thomas Wunderer - 2018
"""
from sage.all import *
import estimator as est
from schemes import LWE_SCHEMES, NTRU_SCHEMES
from cost_asymptotics import BKZ_COST_ASYMPTOTICS
from html import generate_json
from inspect import getsource
try:
from config import NCPUS
except ImportError:
NCPUS = 2
try:
from config import SOBJPATH
except ImportError:
SOBJPATH = "all_the_schemes.sobj"
def flatten(l):
""" Flattens a list of lists into a list containing the elements of each
sublist.
:params l: list of lists
:return: flattened list
"""
return [item for sublist in l for item in sublist]
@parallel(ncpus=NCPUS)
def para_cost_scheme(scheme):
""" Utility function for running LWE costing in parallel.
:param scheme: list containing an LWE scheme object
"""
return cost_scheme(scheme[0])
def cost_scheme(scheme, debug=False):
""" Costs LWE scheme by calling the [APS15] estimator.
Costing is done against primal and dual attacks, and considers the distribution
of the secret vector to apply scaling and dropping of columns.
:params scheme: LWE scheme object
:params debug: Boolean value, if set to True, catched exceptions are re-raised.
:returns estimates: list of estimated costs for the primal and dual attack
"""
sname = scheme["name"]
params = scheme["params"]
# verbose output
print "Costing lwe scheme: %s"%(sname)
# loop parametrisations
estimates = []
for param in params:
# cast everything to python type so we can json.dump it
n = param["n"]
sd = param["sd"]
q = param["q"]
secret_distribution = param["secret_distribution"]
alpha = sqrt(2*pi) * sd / RR(q)
key = "%s-%04d-%.2f-%d"%(sname,n,sd,q)
primal_estimate = {
"attack": "primal",
"key": key,
"scheme": {
"name": sname,
"primitive": scheme["primitive"],
"assumption": scheme["assumption"],
},
"param": param,
"cost": {},
}
dual_estimate = {
"attack": "dual",
"key": key,
"scheme": {
"name": sname,
"primitive": scheme["primitive"],
"assumption": scheme["assumption"],
},
"param": param,
"cost": {},
}
is_ntru = "NTRU" in scheme["assumption"]
samples = [n] if is_ntru else [n, 2*n]
try:
# loop cost models
for cost_model in BKZ_COST_ASYMPTOTICS:
cname = cost_model["name"]
reduction_cost_model = cost_model["reduction_cost_model"]
success_probability = cost_model["success_probability"]
primal_results = {"n": {}, "2n": {}}
if not is_ntru:
dual_results = {"n": {}, "2n": {}}
for m in samples:
dropped = False
# Estimate standard attacks. The estimator will apply any possible scaling
cost_primal = est.primal_usvp(n, alpha, q, secret_distribution=secret_distribution,
m=m, success_probability=success_probability,
reduction_cost_model=reduction_cost_model)
if not is_ntru:
cost_dual = est.dual_scale(n, alpha, q, secret_distribution=secret_distribution,
m=m, success_probability=success_probability,
reduction_cost_model=reduction_cost_model)
if est.SDis.is_bounded_uniform(secret_distribution):
a, b = est.SDis.bounds(secret_distribution)
if -a == b:
# Try guessing secret entries via drop_and_solve
primald = est.partial(est.drop_and_solve, est.primal_usvp, postprocess=False, decision=False)
cost_primal_dropped = primald(n, alpha, q, secret_distribution=secret_distribution,
m=m, success_probability=success_probability,
reduction_cost_model=reduction_cost_model, rotations=is_ntru)
if not is_ntru:
duald = est.partial(est.drop_and_solve, est.dual_scale, postprocess=True)
cost_dual_dropped = duald(n, alpha, q, secret_distribution=secret_distribution,
m=m, success_probability=success_probability,
reduction_cost_model=reduction_cost_model)
# Sometimes drop_and_solve results in a more costly attack
if cost_primal_dropped["rop"] < cost_primal["rop"]:
cost_primal = cost_primal_dropped
dropped = True
if not is_ntru:
if cost_dual_dropped["rop"] < cost_dual["rop"]:
cost_dual = cost_dual_dropped
dropped = True
# save results
primal_results["n" if m == n else "2n"] = {
"name": cname,
"dim": int(cost_primal["d"]),
"beta": int(cost_primal["beta"]),
"rop": int(ceil(log(cost_primal["rop"], 2))),
"drop": dropped,
}
if not is_ntru:
dual_results["n" if m == n else "2n"] = {
"name": cname,
"dim": int(cost_dual["d"]),
"beta": int(cost_dual["beta"]),
"rop": int(ceil(log(cost_dual["rop"], 2))),
"drop": dropped,
}
primal_estimate["cost"][cname] = primal_results
if not is_ntru:
dual_estimate["cost"][cname] = dual_results
except Exception, e:
primal_estimate["error"] = str(e)
if not is_ntru:
dual_estimate["error"] = str(e)
if debug:
raise
estimates += [primal_estimate]
if not is_ntru:
estimates += [dual_estimate]
return estimates
def main():
""" Main function costing LWE and NTRU schemes.
Runs each scheme costing in parallel.
Results are saved as a Sage object and as an HTML table.
:return estimates_list: list containing scheme costs
"""
lwe_estimates = list(para_cost_scheme([[s] for s in LWE_SCHEMES + NTRU_SCHEMES]))
estimates_list = flatten([x[1] for x in lwe_estimates])
# save estimates as sage object
save(estimates_list, SOBJPATH)
try:
print "Generating html"
generate_json(estimates_list)
except Exception, e:
print "Error generating html:", e
print "Done."
return estimates_list
def main_load():
""" Main function costing LWE and NTRU schemes, loading from a Sage object.
:return estimates_list: list containing scheme costs
"""
estimates_list = load(SOBJPATH)
try:
print "Generating html"
generate_json(estimates_list)
except Exception, e:
print "Error generating html:", e
print "Done."
#return estimates_list
def debug_call():
""" Debug call to a single costing.
It avoids the automatic exception handling done by @parallel.
"""
cost_scheme(LWE_SCHEMES[0], debug=True)
""" Run main is executed as a script.
Don't if attached/loaded/imported into sage/python.
"""
import __main__
if __name__ == "__main__" and hasattr(__main__, '__file__'):
#main_load()
main()
# debug_call()