Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
jvdsn
GitHub Repository: jvdsn/crypto-attacks
Path: blob/master/shared/ecc.py
2587 views
1
import logging
2
from random import choice
3
from random import randrange
4
5
from sage.all import EllipticCurve
6
from sage.all import GF
7
from sage.all import cyclotomic_polynomial
8
from sage.all import factor
9
from sage.all import is_prime
10
from sage.all import kronecker
11
from sage.all import next_prime
12
from sage.all import pari
13
14
from shared import is_square
15
from shared.complex_multiplication import solve_cm
16
17
18
def get_embedding_degree(q, n, max_k):
19
"""
20
Returns the embedding degree k of an elliptic curve.
21
Note: strictly speaking this function computes the Tate-embedding degree of a curve.
22
In almost all cases, the Tate-embedding degree is the same as the Weil-embedding degree (also just called the "embedding degree").
23
More information: Maas M., "Pairing-Based Cryptography" (Section 5.2)
24
:param q: the order of the curve base ring
25
:param n: the order of the base point
26
:param max_k: the maximum value of embedding degree to try
27
:return: the embedding degree k, or None if it was not found
28
"""
29
for k in range(1, max_k + 1):
30
if q ** k % n == 1:
31
return k
32
33
return None
34
35
36
def generate_anomalous_q(q, D=None, c=None):
37
"""
38
Generates random anomalous elliptic curves for a specific modulus.
39
More information: Leprevost F. et al., "Generating Anomalous Elliptic Curves"
40
:param q: the prime finite field modulus
41
:param D: the (negative) CM discriminant to use (default: randomly chosen from [-11, -19, -43, -67, -163])
42
:param c: the parameter c to use in the CM method (default: random value)
43
:return: a generator generating random anomalous elliptic curves
44
"""
45
# Idea:
46
# 4q = t^2 - Dv^2
47
# Dv^2 = t^2 - 4q
48
# -> if D divides 1 - 4q and the result is square, it is a good D value
49
Ds = [-11, -19, -43, -67, -163] if D is None else [D]
50
Ds = [D for D in Ds if (1 - 4 * q) % D == 0 and is_square((1 - 4 * q) // D)]
51
assert len(Ds) > 0, "Invalid value for q and default values of D."
52
D = choice(Ds)
53
logging.info(f"Found appropriate D value = {D}")
54
for E in solve_cm(D, q, c):
55
if E.trace_of_frobenius() == 1:
56
yield E
57
else:
58
E = E.quadratic_twist()
59
yield E
60
61
62
def generate_anomalous(q_bit_length, D=None, c=None):
63
"""
64
Generates random anomalous elliptic curves for a specific modulus bit length.
65
More information: Leprevost F. et al., "Generating Anomalous Elliptic Curves"
66
:param q_bit_length: the bit length of the modulus, used to generate a random q
67
:param D: the (negative) CM discriminant to use (default: randomly chosen from [-11, -19, -43, -67, -163])
68
:param c: the parameter c to use in the CM method (default: random value)
69
:return: a generator generating random anomalous elliptic curves
70
"""
71
Ds = [-11, -19, -43, -67, -163] if D is None else [D]
72
while True:
73
# Idea:
74
# 4q = t^2 - Dv^2
75
# 4q = 1 - D(2m + 1)^2
76
# 4q = 1 - D(4m^2 + 4m + 1)
77
# q = -Dm^2 - Dm - (D + 1) / 4
78
D = choice(Ds)
79
m_bit_length = (q_bit_length - D.bit_length()) // 2 + 1
80
m = randrange(2 ** (m_bit_length - 1), 2 ** m_bit_length)
81
q = -D * m * (m + 1) + (-D + 1) // 4
82
if q.bit_length() == q_bit_length and is_prime(q):
83
yield from generate_anomalous_q(q, D, c)
84
85
86
def generate_with_trace_q(t, q, D=None, c=None):
87
"""
88
Generates random elliptic curves for a specific trace of Frobenius and modulus.
89
Note: this method may take a very long time if D is not provided.
90
:param t: the trace of Frobenius
91
:param q: the prime finite field modulus
92
:param D: the (negative) CM discriminant to use (default: computed using t and q)
93
:param c: the parameter c to use in the CM method (default: random value)
94
:return: a generator generating random elliptic curves
95
"""
96
assert t ** 2 < 4 * q, f"Trace {t} is outside Hasse's interval for GF({q})"
97
98
# Idea:
99
# 4q = t^2 - Dv^2
100
# Dv^2 = t^2 - 4q
101
# -> D can be immediately computed from t and q
102
if D is None:
103
D = t ** 2 - 4 * q
104
# We don't make D square-free because that removes solutions.
105
logging.info(f"Found appropriate D value = {D}")
106
else:
107
assert (t ** 2 - 4 * q) % D == 0 and is_square((t ** 2 - 4 * q) // D), "Invalid values for t, q, and D."
108
109
for E in solve_cm(D, q, c):
110
if E.trace_of_frobenius() == t:
111
yield E
112
else:
113
E = E.quadratic_twist()
114
yield E
115
116
117
def generate_with_trace(t, q_bit_length, D=None, c=None):
118
"""
119
Generates random elliptic curves for a specific trace of Frobenius and modulus bit length.
120
:param t: the trace of Frobenius
121
:param q_bit_length: the bit length of the modulus, used to generate a random q
122
:param D: the (negative) CM discriminant to use (default: computed using t)
123
:param c: the parameter c to use in the CM method (default: random value)
124
:return: a generator generating random elliptic curves
125
"""
126
if D is None:
127
D = 11
128
while D % 4 != 3 or t % D == 0:
129
D = next_prime(D)
130
D = int(-D)
131
logging.info(f"Found appropriate D value = {D}")
132
else:
133
assert (-D) % 4 == 3 and t % (-D) != 0 and is_prime(-D), "Invalid values for t and D."
134
135
v_bit_length = (q_bit_length + 2 - D.bit_length()) // 2 + 1
136
assert v_bit_length > 0, "Invalid values for t and q bit length."
137
138
while True:
139
# Idea:
140
# 4q = t^2 - Dv^2
141
# -> we simply try random values for v until a suitable q is found
142
v = randrange(2 ** (v_bit_length - 1), 2 ** v_bit_length)
143
q4 = t ** 2 - D * v ** 2
144
if q4.bit_length() - 2 == q_bit_length and q4 % 4 == 0 and is_prime(q4 // 4):
145
q = q4 // 4
146
yield from generate_with_trace_q(t, q, D, c)
147
148
149
def generate_with_order_q(m, q, D=None, c=None):
150
"""
151
Generates random elliptic curves for a specific order and modulus.
152
Note: this method may take a very long time if D is not provided.
153
:param m: the order
154
:param q: the prime finite field modulus
155
:param D: the (negative) CM discriminant to use (default: computed using m and q)
156
:param c: the parameter c to use in the CM method (default: random value)
157
:return: a generator generating random elliptic curves
158
"""
159
yield from generate_with_trace_q(q + 1 - m, q, D, c)
160
161
162
def generate_with_order(m, D=None, c=None):
163
"""
164
Generates random elliptic curves for a specific order.
165
The modulus bit length will always be approximately equal to the order bit length.
166
Based on: Broeker R., Stevenhagen P., "Constructing Elliptic Curves of Prime Order"
167
:param m: the order
168
:param D: the (negative) CM discriminant to use (default: computed using m)
169
:param c: the parameter c to use in the CM method (default: random value)
170
:return: a generator generating random elliptic curves
171
"""
172
factor_4m = factor(4 * m)
173
174
def get_q(D):
175
# We can't use qfbcornacchia here, because it does not return all (or any) solutions...
176
for t in set(map(lambda sol: int(sol[0]), pari.qfbsolve(pari.Qfb(1, 0, -D), factor_4m, 1))):
177
if is_prime(m + 1 - t):
178
return m + 1 - t
179
if is_prime(m + 1 + t):
180
return m + 1 + t
181
182
q = None
183
if D is None:
184
for D in range(7, 4 * m):
185
if not (D % 4 == 0 or D % 4 == 3):
186
continue
187
188
q = get_q(-D)
189
if q is not None:
190
break
191
192
assert q is not None, "Unable to find appropriate D value for m."
193
D = int(-D)
194
logging.info(f"Found appropriate D value = {D}")
195
else:
196
q = get_q(D)
197
assert q is not None, "Invalid values for m and D."
198
199
yield from generate_with_trace_q(q + 1 - m, q, D, c)
200
201
202
def generate_supersingular(q, c=None):
203
"""
204
Generates random supersingular elliptic curves.
205
More information: Broeker R., "Constructing Supersingular Elliptic Curves"
206
:param q: a prime power q
207
:param c: the parameter c to use in the CM method (default: random value)
208
:return: a generator generating random supersingular elliptic curves
209
"""
210
gf = GF(q)
211
p = gf.characteristic()
212
if p == 2:
213
# E with j-invariant 0 are singular (Silverman, Arithmetic of Elliptic Curves, Appendix A).
214
while True:
215
a3 = gf.random_element()
216
a4 = gf.random_element()
217
a6 = gf.random_element()
218
if a3 > 0:
219
yield EllipticCurve(gf, [0, 0, a3, a4, a6])
220
if p == 3:
221
# E with j-invariant 0 are singular (Silverman, Arithmetic of Elliptic Curves, Appendix A).
222
while True:
223
a = gf.random_element()
224
b = gf.random_element()
225
if a > 0:
226
yield EllipticCurve(gf, [a, b])
227
if p % 3 == 2:
228
# E with j-invariant 0 are singular.
229
while True:
230
b = gf.random_element()
231
if b > 0:
232
yield EllipticCurve(gf, [0, b])
233
if p % 4 == 3:
234
# E with j-invariant 1728 are singular.
235
while True:
236
a = gf.random_element()
237
if a > 0:
238
yield EllipticCurve(gf, [a, 0])
239
D = 3
240
while D % 4 != 3 or kronecker(-D, p) != -1:
241
D = next_prime(D)
242
243
yield from solve_cm(-D, q, c)
244
245
246
def generate_mnt(k, h_min=1, h_max=4, D_min=7, D_max=10000, c=None):
247
"""
248
Generates random MNT curves.
249
More information: Scott M., Barreto P. S. L. M., "Generating more MNT elliptic curves"
250
:param k: the embedding degree (3, 4, or 6)
251
:param h_min: the minimum cofactor to try (inclusive, default: 1)
252
:param h_max: the maximum cofactor to try (inclusive, default: 4)
253
:param D_min: the minimum D value to try (inclusive, default: 7)
254
:param D_max: the maximum D value to try (inclusive, default: 10000)
255
:param c: the parameter c to use in the CM method (default: random value)
256
:return: a generator generating random MNT curves with embedding degree k
257
"""
258
assert k in {3, 4, 6}
259
260
phi = cyclotomic_polynomial(k)
261
l = -2 * (k // 2) + 4
262
for h in range(h_min, h_max + 1):
263
for d in range(1, 4 * h):
264
if k == 4 and not (d % 4 == 1 or d % 4 == 2):
265
continue
266
if (k == 3 or k == 6) and not (d % 6 == 1 or d % 6 == 3):
267
continue
268
269
a = l * h + d
270
b = 4 * h - d
271
f = a ** 2 - b ** 2
272
factor_f = 0 if f == 0 else factor(f)
273
for D in range(D_min, D_max + 1):
274
if not (D % 4 == 0 or D % 4 == 3):
275
continue
276
277
g = d * b * D
278
if is_square(g):
279
continue
280
281
ys = set(map(lambda sol: int(sol[0]), pari.qfbsolve(pari.Qfb(1, 0, -g), factor_f, 1)))
282
for y in ys:
283
if (y - a) % b != 0:
284
continue
285
x = (y - a) // b
286
if phi(x) % d != 0:
287
continue
288
r = int(phi(x) // d)
289
n = h * r
290
q = n + x
291
# Unfortunately, this sanity check is needed in some cases.
292
if all((q ** i - 1) % r == 0 for i in range(1, k)):
293
continue
294
if is_prime(r) and is_prime(q):
295
logging.info(f"Found appropriate D value = {-D}")
296
yield from generate_with_order_q(n, q, -D, c)
297
298
299
def generate_mnt_k2(q_bit_length, D=None, c=None):
300
"""
301
Generates random MNT curves with embedding degree 2.
302
More information: Scott M., Barreto P. S. L. M., "Generating more MNT elliptic curves" (Section 5)
303
:param q_bit_length: the bit length of the modulus, used to generate a random q
304
:param D: the (negative) CM discriminant to use (default: -7)
305
:param c: the parameter c to use in the CM method (default: random value)
306
:return: a generator generating random MNT curves with embedding degree k
307
"""
308
if D is None:
309
D = -7
310
logging.info(f"Found appropriate D value = {D}")
311
else:
312
assert D < 0 and (D % 4 == 0 or D % 4 == 1), "Invalid value for D."
313
314
x_bit_length = (q_bit_length + 2) // 2
315
z_bit_length = (x_bit_length - 1 - D.bit_length()) // 2 + 1
316
assert z_bit_length > 0, "Invalid values for D and q bit length."
317
while True:
318
z = randrange(2 ** (z_bit_length - 1), 2 ** z_bit_length)
319
x = 2 * (-D) * z ** 2 + 1
320
q = (x ** 2 + 4 * x - 1) // 4
321
r = (x + 1) // 2
322
if q.bit_length() == q_bit_length and is_prime(r) and is_prime(q):
323
yield from generate_with_trace_q(x + 1, q, D, c)
324
325