Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
jack4818
GitHub Repository: jack4818/Castryck-Decru-SageMath
Path: blob/main/SIKEp610_variant.sage
323 views
1
"""
2
Demonstrate how to run the attack when c = small_prime*(u^2+v^2)
3
This is a mixture of Castryck-Decru and Maino-Martindale
4
5
The following variants are implemented:
6
Case 7: 2^301-3^188 == 7*(u*u+v*v) (3^4 guesses)
7
8
tau
9
Estart --> Eguess ---> EB
10
| u+iv | |
11
v | |
12
Estart |aux |
13
| phi7 | |
14
v v v
15
E7 ------> C -------> CB
16
tau7
17
18
(note that E(p^3) has (p^3+1) points, and a 7-torsion point)
19
20
Case 11: 2^305-19*3^189 = 11*(u*u+v*v) (3^3 guesses)
21
22
tau
23
Estart --> Eguess ---> EB ---> EB19
24
| u+iv | phi19 |
25
v | |
26
Estart |aux |
27
| phi11 | |
28
v v v
29
E11 -------> C ---------------> CB
30
tau11
31
32
(note that E(p^4) has (p^2-1)^2 points, and a 11-torsion point)
33
(note that E(p^9) has p^9+1 points, and a 19-torsion point)
34
35
"""
36
37
import time
38
import argparse
39
40
# Local imports
41
from helpers import possibly_parallel, supersingular_gens, fast_log3
42
import public_values_aux
43
from public_values_aux import *
44
from richelot_aux import Pushing3Chain, Does22ChainSplit
45
46
# Load Sage Files
47
load('speedup.sage')
48
49
set_verbose(-1)
50
51
argp = argparse.ArgumentParser()
52
argp.add_argument("--parallel", action="store_true")
53
argp.add_argument("--mode", type=int, choices=(7, 11), default=11,
54
help="mode 11 needs 27 guesses, mode 7 needs 81 guesses")
55
opts = argp.parse_args()
56
57
print("Instantiate E_start...")
58
a, b = 305, 192
59
p = 2^a*3^b - 1
60
public_values_aux.p = p
61
62
Fp2.<i> = GF(p^2, modulus=x^2+1)
63
R.<x> = Fp2[]
64
65
E_start = EllipticCurve(Fp2, [0,6,0,1,0])
66
E_start.set_order((p+1)^2, num_checks=0) # Speeds things up in Sage
67
68
# Generation of the endomorphism 2i
69
two_i = generate_distortion_map(E_start)
70
71
# Choose an outgoing degree 7 isogeny.
72
# We don't need to compute a torsion point in GF(p^6),
73
# we can factor the division polynomial and use Kohel formulas.
74
if opts.mode == 7:
75
print("Using 2^301-3^188 = 7*(u²+v²) (max guesses = 81)")
76
print("Precompute an isogeny of degree 7 on E_start")
77
P7 = E_start.division_polynomial(7)
78
ker7, _ = P7.factor()[0]
79
phi_left = E_start.isogeny(ker7)
80
print(phi_left)
81
82
u = 714020003029005719823753224880815399155339403
83
v = 28031663375683401880549715251102056676622848
84
assert 2^301-3^188 == 7*(u*u+v*v)
85
else:
86
print("Using 2^305-19*3^189 = 11*(u²+v²) (max guesses = 27)")
87
print("Precompute an isogeny of degree 11 on E_start")
88
# Sage is confused by degree 1 factors
89
#P11 = E_start.division_polynomial(11)
90
#ker11, _ = P11.factor()[0]
91
phi_left = E_start.isogenies_prime_degree(11)[0]
92
print(phi_left)
93
94
u = 1550193735342211609960431880234414865472178337
95
v = 965894233082293540389053428058397267855495894
96
assert 2^305-19*3^189 == 11*(u*u+v*v)
97
98
# Generate public torsion points, for SIKE implementations
99
# these are fixed but to save loading in constants we can
100
# just generate them on the fly
101
P2, Q2, P3, Q3 = generate_torsion_points(E_start, a, b)
102
check_torsion_points(E_start, a, b, P2, Q2, P3, Q3)
103
104
# Generate Bob's key pair
105
bob_private_key, EB, PB, QB = gen_bob_keypair(E_start, b, P2, Q2, P3, Q3)
106
solution = Integer(bob_private_key).digits(3, padto=b)
107
108
print(f"If all goes well then the following digits should be found: {solution}")
109
110
# Build the following diagram for each guess
111
#
112
# tau
113
# Estart --> Eguess ---> EB
114
# | u+iv | |
115
# v | |
116
# Estart |aux |
117
# | phi7 | |
118
# v v v
119
# E7 -------> C ------> CB
120
# tau7
121
#
122
# The isogeny Eguess->EB is secret, isogeny diamond is (Eguess, EB, C, CB)
123
# We don't compute aux, nor CB
124
125
if opts.parallel:
126
# Set number of cores for parallel computation
127
num_cores = os.cpu_count()
128
print(f"Performing the attack in parallel using {num_cores} cores")
129
else:
130
num_cores = 1
131
132
# Attack starts here
133
134
tim = time.time()
135
136
if opts.mode == 7:
137
phiB = EB.identity_morphism()
138
# SAGE identity morphisms are not functions??
139
phiB._call_ = lambda x: x
140
beta = 4 # 192-188
141
alp = 4 # 305-301
142
else:
143
print("Compute (once) an isogeny of degree 19 on E_B")
144
P19 = EB.division_polynomial(19)
145
ker19, _ = P19.factor()[0]
146
phiB = EB.isogeny(ker19)
147
print(phiB)
148
beta = 3 # 192-189
149
alp = 0 # 305-305
150
print(f"... done in {time.time()-tim:.3f} seconds")
151
152
@possibly_parallel(num_cores)
153
def CheckGuess(guess):
154
guess = Integer(guess)
155
first_digits = guess.digits(3, padto=beta)
156
print(f"Testing digits: {first_digits}")
157
158
guessker = 3^(b-beta) * (P3 + guess*Q3)
159
guessker_left = phi_left(u * guessker + (v//2) * two_i(guessker))
160
C, tau_C = Pushing3Chain(phi_left.codomain(), guessker_left, beta)
161
162
# Now compute the image of 2-torsion in C
163
def Estart_to_C(x):
164
x = u * x + (v//2) * two_i(x) # endomorphism
165
x = phi_left(x)
166
for c in tau_C:
167
x = c(x)
168
return x
169
170
P2_C = Estart_to_C(P2)
171
Q2_C = Estart_to_C(Q2)
172
173
# Replace EB by the codomain of phiB
174
split = Does22ChainSplit(C, phiB.codomain(),
175
2^alp*P2_C, 2^alp*Q2_C,
176
2^alp*phiB(PB), 2^alp*phiB(QB), a-alp)
177
178
if split:
179
Eguess, _ = Pushing3Chain(E_start, guessker, beta)
180
chain, (E1, E2) = split
181
if E1.j_invariant() == Eguess.j_invariant():
182
index = 1
183
CB = E2
184
else:
185
index = 0
186
CB = E1
187
def C_to_CB(x):
188
pt = (x, None)
189
for c in chain:
190
pt = c(pt)
191
return pt[index]
192
P3_C = Estart_to_C(P3)
193
Q3_C = Estart_to_C(Q3)
194
P3_CB = C_to_CB(P3_C)
195
Q3_CB = C_to_CB(Q3_C)
196
197
print("Computed image of 3-adic torsion in split factor C_B")
198
Z3 = Zmod(3^b)
199
G1_CB, G2_CB = supersingular_gens(CB)
200
G1_CB3 = ((p+1) / 3^b) * G1_CB
201
G2_CB3 = ((p+1) / 3^b) * G2_CB
202
w = G1_CB3.weil_pairing(G2_CB3, 3^b)
203
204
xP = fast_log3(P3_CB.weil_pairing(G1_CB3, 3^b), w)
205
xQ = fast_log3(Q3_CB.weil_pairing(G1_CB3, 3^b), w)
206
if xQ % 3 != 0:
207
sk = int(-Z3(xP) / Z3(xQ))
208
return sk
209
xP = fast_log3(P3_CB.weil_pairing(G2_CB3, 3^b), w)
210
xQ = fast_log3(Q3_CB.weil_pairing(G2_CB3, 3^b), w)
211
if xQ % 3 != 0:
212
sk = int(-Z3(xP) / Z3(xQ))
213
return sk
214
215
raise Exception("fail?!")
216
217
return None
218
219
for result in CheckGuess(list(range(3^beta))):
220
((guess,), _), sk = result
221
if sk is not None:
222
print("Glue-and-split! These are most likely the secret digits.")
223
bobskey = sk
224
break
225
226
# Sanity check
227
bobscurve, _ = Pushing3Chain(E_start, P3 + bobskey*Q3, b)
228
found = bobscurve.j_invariant() == EB.j_invariant()
229
230
if found:
231
print(f"Bob's secret key revealed as: {bobskey}")
232
print(f"In ternary, this is: {Integer(bobskey).digits(base=3, padto=b)}")
233
print(f"Altogether this took {time.time() - tim:.3f} seconds.")
234
else:
235
print("Something went wrong.")
236
print(f"Altogether this took {time.time() - tim:.3f} seconds.")
237
238