-
Notifications
You must be signed in to change notification settings - Fork 70
/
Copy pathold_backdoorgen.sage
393 lines (340 loc) · 13.3 KB
/
old_backdoorgen.sage
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
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
#################################################################
# DH Backdoor generator
#################################################################
# This tool generates Diffie-Hellman parameters
# that would allow for a backdoor.
#
# several method are availables
#################################################################
import sys, pdb
#################################################################
# Helpers
#################################################################
# colors
class cc:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
END = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
# Produce a generator for a group modulo a prime
def produce_generator(modulus, order, subgroups, g=2):
# we want g s.t. g^{order/subgroup_order} != 1 for all subgroups
is_generator = False
while not is_generator:
is_generator = True
for subgroup in subgroups:
if int(power_mod(g, order//subgroup, modulus)) == 1:
g = randint(2, modulus - 1)
is_generator = False
break
return g
# Produce good enough for a group modulo a prime
def produce_good_enough_generator(modulus, order, subgroups, g=2):
# we want g s.t. g^{order/subgroup_order} != 1 for all subgroups
is_generator = False
while not is_generator:
is_generator = True
for subgroup in subgroups:
if int(power_mod(g, subgroup, modulus)) == 1:
g = randint(2, modulus - 1)
is_generator = False
break
return g
# returns the order of g
def order_of_g(g, order, subgroups, modulus):
for subgroup in subgroups:
if power_mod(g, order//subgroup, modulus) == 1:
reduced_subgroups = list(subgroups)
reduced_subgroups.remove(subgroup)
return order_of_g(g, order//subgroup, reduced_subgroups, modulus)
return order, subgroups
# returns false if order of g is not a multiplication of some subgroups
def not_so_bad(g, order, subgroups, modulus):
for subgroup in subgroups:
if power_mod(g, subgroup, modulus) == 1:
return False
return True
# Produce a generator of a target subgroup for any kind of group
def produce_bad_generator(modulus, order_group, subgroups, target, g=2):
while int(power_mod(g, target, modulus)) != 1 and not_so_bad(g, target, subgroups, modulus):
g = randint(2, modulus - 1)
g = power_mod(g, order_group//target, modulus)
print "# Found a generator"
order_g, subgroups_g = order_of_g(g, target, subgroups, modulus)
print "generator_order:", order_g
print "inbits:", len(bin(order_g)) - 2
print "generator_subgr:", subgroups_g
return g, order_g, subgroups_g
#################################################################
# Methods
#################################################################
def method1(modulus_size, factors_size):
"""
# Description
* This method creates a prime modulus p
* p-1 factors are small enough for DLOG
* p-1 factors are big enough to avoid factorization
# How to use the backdoor
* You should be able to do a DLOG modulo a factors_size prime
# NOBUS?
* Nobody should be able to find a factor of size factors_size
"""
print "this is not a good idea"
return True
def method2(modulus_size, number_of_factors, smooth_size):
"""
# Description
* This method creates a modulus n = p_1 * ... * p_{number_of_factors}
with each p_i - 1 smooth, that is, each p_i - 1 = q_1 * ... * q_{something}
* `something` is calculated according to `smooth_size`:
each q_i is of size ~ `smooth_size`
# How to use the backdoor
* To use this backdoor you need to keep track of each q_i
* Pohlig-Hellman will have to do the DLOG modulo every q_i
* To verify that the `smooth_size` is low enough: try to compute a DLOG on a q_i
# NOBUS?
* Since each p_i-1 are smooth, i's highly possible that
Pollard's p-1 factorization algorithm could factor the modulus
"""
# checks
assert(number_of_factors > 1)
assert(smooth_size > 0)
# generation of the X primes p_i s.t. p_i - 1 smooth
p_i = []
subgroups_list = []
for i in range(number_of_factors):
prime_size = modulus_size // number_of_factors
number_small_primes = prime_size // smooth_size
# let's compute a p_i
prime_test = 0
while not is_prime(prime_test):
primes_list = [2] # number is even
prime_test = 2
for j in range(number_small_primes):
prime = random_prime(1<<(smooth_size + 1), lbound=1<<(smooth_size-3))
primes_list.append(prime)
prime_test *= prime
# prime_test - 1 = p_i - 1 = \prod primes_list = \prod q_i
prime_test += 1
# we found a p_i
p_i.append(prime_test)
subgroups_list += primes_list
# compute the modulus
modulus = 1
for p in p_i:
modulus *= p
# verify the order of the group
order_group = 1
for p in p_i:
order_group *= p - 1
if power_mod(2, order_group, modulus) != 1:
print "Method 1 crashed"
sys.exit(1)
# find a generator
g = produce_good_enough_generator(modulus, order_group, subgroups_list)
# print
print "modulus =", modulus
print "bitlength =", len(bin(modulus)) - 2
print "factors =", p_i
print "generator =", g
print "subgroups =", subgroups_list
print "# be sure to test if you can do a DLOG modulo", subgroups_list[1]
#
return modulus, subgroups_list, p_i, g
def method3(modulus_size, number_of_factors, smooth_size, B2_size):
"""
# Description
* This is the same method as method 2 above, except:
one q_i (we'll call it q_B2) of each p_i-1 is big.
* This makes the p_i-1 "partially" smooth
# How to use the backdoor
* To use this backdoor you need to keep track of each q_i
* Pohlig-Hellman will have to do the DLOG modulo every q_i
* To verify that the `B2_size` is low enough: try to compute a DLOG on a q_B2
# NOBUS?
* Since both p-1 and q-1 have a large factor,
* Pollard's p-1 would need a B2 bound too large to work efficiently.
* ECM could still work if the large factor is not large enough
"""
# B2 should be > smooth_size
assert(B2_size > smooth_size)
# generation of the X primes p_i s.t. p_i - 1 smooth
p_i = []
subgroups_list = []
for i in range(number_of_factors):
prime_size = modulus_size // number_of_factors
number_small_primes = (prime_size // smooth_size) - (B2_size // smooth_size)
# let's compute a p_i
prime_test = 0
while not is_prime(prime_test):
primes_list = [2] # number is even
prime_test = 2
for j in range(number_small_primes):
prime = random_prime(1<<(smooth_size+1), lbound=1<<(smooth_size-3))
primes_list.append(prime)
prime_test *= prime
# now the time for q_B2
prime = random_prime(1<<(B2_size+1), lbound=1<<(B2_size-3))
primes_list.append(prime)
prime_test *= prime
# prime_test - 1 = p_i - 1 = \prod primes_list = \prod q_i
prime_test += 1
# we found a p_i
p_i.append(prime_test)
subgroups_list += primes_list
# compute the modulus
modulus = 1
for p in p_i:
modulus *= p
# verify the order of the group
order_group = 1
for p in p_i:
order_group *= p - 1
if power_mod(2, order_group, modulus) != 1:
print "Method 1 crashed"
sys.exit(1)
# find a generator
# we should make the generator target all the smooth factors except the large one
g = produce_good_enough_generator(modulus, order_group, subgroups_list)
# print
print "modulus =", modulus
print "bitlength =", len(bin(modulus)) - 2
print "factors =", p_i
print "subgroups =", subgroups_list
print "generator =", g
print "# be sure to test if you can do a DLOG modulo", subgroups_list[-1]
#
return modulus, subgroups_list, p_i, g
def method4(modulus_size=1024, factors_size=256):
"""
# Description
* n = \prod p_i with each p_i the same large size and
p_i - 1 = 2q_i with q_i prime (so p_i - 1 are not smooth)
# How to use the backdoor
* To use this backdoor you need to keep track of each p_i
* Pohlig-Hellman will have to do the DLOG modulo each p_i - 1
* This is a large modulus, for a 1024 bits dh modulus the dlogs will
have to be done modulus 256 bits prime
# NOBUS?
* Since none of the p_i - 1 are smooth, Pollard's p-1 would not yield anything
* But 256bits factors are "easy" to find
* You also have "not easy" DLOG to do
"""
number_of_factors = modulus_size // factors_size
return method2(modulus_size, number_of_factors, factors_size)
def method5(modulus_size, subgroups_order, large_factor_size):
"""
# Description
* n = pq and p-1 has large factors except for a small one that will
be our generator's subgroup
"""
# generation of the 2 primes p and q s.t. p-1 has one small factor
prime_size = modulus_size // 2
# q should be have few factors (not smooth) if we pick it randomly
q = random_prime(1<<(prime_size+1), lbound=1<<(prime_size-3))
# p
number_small_subgroups = (prime_size - large_factor_size) // subgroups_order
p = 0
while not is_prime(p):
subgroups_list = [2]
generator_subgroup = 2
# generate the `number_small_subgroups` small subgroups
for j in range(number_small_subgroups):
prime = random_prime(1<<(subgroups_order+1), lbound=1<<(subgroups_order-3))
subgroups_list.append(prime)
generator_subgroup *= prime
# the large rest
large_subgroup = random_prime(1<<(large_factor_size+1), lbound=1<<(large_factor_size-3))
# p
p = generator_subgroup * large_subgroup + 1
# compute the modulus
modulus = p * q
# verify the order of the group
order_group = (p-1) * (q-1)
if power_mod(2, order_group, modulus) != 1:
print "Method 1 crashed"
sys.exit(1)
# find a generator of the small subgroup
# This is not gonna work, we need to know the order of the generator
g = produce_bad_generator(modulus, order_group, subgroups_list, generator_subgroup)
# print
print "modulus =", modulus
print "bitlength =", len(bin(modulus)) - 2
print "factors =", p, " * ", q
print "subgroups =", subgroups_list
print "generator =", g
print "# be sure to test if you can do a DLOG modulo", subgroups_list[-1]
#
return modulus, subgroups_list, generator_subgroup, g
def method6():
return True
#################################################################
# Main menu
#################################################################
menu = ["modulus p is prime, p-1 have 'small' factors",
"modulus = pq with p-1 and q-1 smooth",
"same as above but partially smooth",
"modulus = p_1*p_2*p_3*p_4 with no smooth p_i-1",
"modulus = pq with p-1 partially smooth, g generates the smooth part"
]
def main():
# display menu if not provided with an option
if len(sys.argv) < 2:
sys.stderr.write("\x1b[2J\x1b[H")
print cc.HEADER + "# Choose a method from:" + cc.END
for index, item in enumerate(menu, 1):
print "%d. %s" % (index, menu[index - 1])
print "(you can also pass that choice as an argument)"
# prompt
choice = int(raw_input(cc.OKGREEN + "# Enter a digit:\n" + cc.END))
sys.stderr.write("\x1b[2J\x1b[H")
else:
choice = int(sys.argv[1])
# run method
if choice == 1:
print "# using method 1.", menu[0]
"""
compute a prime modulus p where p-1 has small factors
small enough to do the dlog, but large enough to avoid factorization
(this is not really possible)
"""
method1(1024, 256)
if choice == 2:
print "# using method 2.", menu[1]
"""
We use it to compute n = pq with p-1 and q-1 32bits-smooth
dlog in 32 bits is relatively easy (?)
factoring n is easy with Pollard's p - 1
"""
method2(1024, 2, 32)
if choice == 3:
print "# using method 3.", menu[2]
"""
As above, we generate n = pq with p-1 and q-1 32bits-smooth
except! for one factor that is ~64bits
dlog should be possible in 64 bits
factoring n with Pollard's p-1 will have to catch that 64bits with B2
"""
method3(1024, 2, 32, 64)
if choice == 4:
print "# using method 4.", menu[3]
"""
We compute n = p1*p2*p3*p4 with each p_i 256bits and
p_i - 1 = 2q_i with q_i prime (so p_i - 1 are not smooth)
This is not a very good way, because easily factorable
"""
method4(1024, 256)
if choice == 5:
print "# using method 5.", menu[4]
"""
We compute n = pq with p and q prime s.t.
p-1 has a few 32bits factors and a large 250bits factor
We use a generator of the entire small subgroups
"""
method5(1024, 32, 250)
if __name__ == "__main__":
main()