Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
jack4818
GitHub Repository: jack4818/Castryck-Decru-SageMath
Path: blob/main/castryck_decru_attack.sage
323 views
1
# Python imports
2
import time
3
from itertools import product
4
5
# Local Imports
6
from helpers import possibly_parallel
7
from richelot_aux import AuxiliaryIsogeny, Does22ChainSplit, Pushing3Chain
8
from uvtable import uvtable
9
10
# Load Sage Files
11
load('speedup.sage')
12
13
# ===================================
14
# ===== ATTACK ====================
15
# ===================================
16
17
def CastryckDecruAttack(E_start, P2, Q2, EB, PB, QB, two_i, num_cores=1):
18
tim = time.time()
19
20
skB = [] # TERNARY DIGITS IN EXPANSION OF BOB'S SECRET KEY
21
22
# gathering the alpha_i, u, v from table
23
expdata = [[0, 0, 0] for _ in range(b-3)]
24
for i in range(b%2, b-3, 2):
25
index = (b-i) // 2
26
row = uvtable[index-1]
27
if row[1] <= a:
28
expdata[i] = row[1:4]
29
30
# gather digits until beta_1
31
bet1 = 0
32
while not expdata[bet1][0]:
33
bet1 += 1
34
bet1 += 1
35
36
ai,u,v = expdata[bet1-1]
37
38
print(f"Determination of first {bet1} ternary digits. We are working with 2^{ai}-torsion.")
39
40
bi = b - bet1
41
alp = a - ai
42
43
@possibly_parallel(num_cores)
44
def CheckGuess(first_digits):
45
print(f"Testing digits: {first_digits}")
46
47
scalar = sum(3^k*d for k,d in enumerate(first_digits))
48
tauhatkernel = 3^bi * (P3 + scalar*Q3)
49
50
tauhatkernel_distort = u*tauhatkernel + v*two_i(tauhatkernel)
51
52
C, P_c, Q_c, _ = AuxiliaryIsogeny(bet1, u, v, E_start, P2, Q2, tauhatkernel, two_i)
53
54
return Does22ChainSplit(C, EB, 2^alp*P_c, 2^alp*Q_c, 2^alp*PB, 2^alp*QB, ai)
55
56
guesses = [ZZ(i).digits(3, padto=bet1) for i in range(3^bet1-1)]
57
58
for result in CheckGuess(guesses):
59
((first_digits,), _), is_split = result
60
if is_split is not None:
61
print("Glue-and-split! These are most likely the secret digits.")
62
skB += first_digits
63
break
64
65
else:
66
print("All other guesses failed, so first digits must be all 2!")
67
skB += [2]*bet1
68
69
print(skB)
70
71
# now compute longest prolongation of Bob's isogeny that may be needed
72
length = 1
73
max_length = 0
74
for i in range(bet1, b-3):
75
if expdata[i][0]:
76
max_length = max(length, max_length)
77
length = 0
78
else:
79
length += 1
80
81
while True:
82
K = 2^a*3^(b - max_length)*EB.random_point()
83
if K.order() == 3^max_length:
84
break
85
86
while True:
87
alternativeK = 2^a*3^(b - max_length)*EB.random_point()
88
if K.weil_pairing(alternativeK, 3^max_length)^(3^(max_length - 1)) != 1:
89
break
90
91
_, EBprolong = Pushing3Chain(EB, K, max_length)
92
93
# gather next digit and change prolongation if needed
94
i = bet1 + 1
95
bi = b - i
96
97
print(f"Determination of the {i}th ternary digit. We are working with 2^{ai}-torsion.")
98
prolong = 1
99
print("Prolonging with 1 steps.")
100
endPB = EBprolong[0](PB)
101
endQB = EBprolong[0](QB)
102
endEB = EBprolong[0].codomain()
103
# Speeds things up in Sage
104
endEB.set_order((p+1)^2, num_checks=0)
105
106
positives = []
107
108
@possibly_parallel(num_cores)
109
def CheckGuess(j):
110
print(f"Testing digit: {j}")
111
112
scalar = sum(3^k*d for k,d in enumerate(skB + [j]))
113
tauhatkernel = 3^bi * (P3 + scalar*Q3)
114
115
C, P_c, Q_c, _ = AuxiliaryIsogeny(i, u, v, E_start, P2, Q2, tauhatkernel, two_i)
116
117
return Does22ChainSplit(C, endEB, 2^alp*P_c, 2^alp*Q_c, 2^alp*endPB, 2^alp*endQB, ai)
118
119
for result in CheckGuess([0,1,2]):
120
((j,), _), is_split = result
121
if is_split:
122
print("Glue-and-split!")
123
positives.append(j)
124
# continue testing other digits unless we reached an
125
# ambiguity already:
126
# By Remark 4 of [Castryck-Decru], there a probability
127
# that K cancels the tail of Bob's isogeny, creating false
128
# positives, in this case, we have to switch to alternativeK.
129
if len(positives) > 1:
130
break
131
132
if len(positives) == 1:
133
print(f"Most likely good prolongation and the secret digit is: {positives[0]}")
134
skB.append(positives[0])
135
print(skB)
136
next_i = i + 1
137
else:
138
print("All glue-and-splits, must be bad prolongation: changing it and redoing this digit.")
139
_, EBprolong = Pushing3Chain(EB, alternativeK, max_length)
140
next_i = i
141
prolong = 0
142
143
144
# now gather all remaining digits, except for last three (we close that gap by trial and error)
145
146
for i in range(next_i, b-2):
147
bi = b - i
148
if expdata[i-1][0]:
149
ai,u,v = expdata[i-1]
150
alp = a - ai
151
prolong = 0
152
else:
153
prolong += 1
154
155
print(f"Determination of the {i}th ternary digit. We are working with 2^{ai}-torsion.")
156
print(f"Prolonging with {prolong} steps.")
157
endPB = PB
158
endQB = QB
159
endEB = EB
160
# for j in [1..prolong] do
161
for j in range(1,prolong+1):
162
endPB = EBprolong[j-1](endPB)
163
endQB = EBprolong[j-1](endQB)
164
if j == prolong:
165
endEB = EBprolong[j-1].codomain()
166
# Speeds things up in Sage
167
endEB.set_order((p+1)^2, num_checks=0)
168
169
@possibly_parallel(num_cores)
170
def CheckGuess(j):
171
print(f"Testing digit: {j}")
172
173
scalar = sum(3^k*d for k,d in enumerate(skB + [j]))
174
tauhatkernel = 3^bi * (P3 + scalar * Q3)
175
176
C, P_c, Q_c, _ = AuxiliaryIsogeny(i, u, v, E_start, P2, Q2, tauhatkernel, two_i)
177
178
return Does22ChainSplit(C, endEB, 2^alp*P_c, 2^alp*Q_c, 2^alp*endPB, 2^alp*endQB, ai)
179
180
for result in CheckGuess([0,1]):
181
((j,), _), is_split = result
182
if is_split:
183
print("Glue-and-split! This is most likely the secret digit.")
184
skB.append(j)
185
print(skB)
186
break
187
188
else:
189
print("All other guesses failed, so the digit must be 2")
190
skB.append(2)
191
print(skB)
192
193
key = sum([skB[i]*3^(i) for i in range(b-3)])
194
195
# bridge last safety gap
196
tim2 = time.time()
197
198
@possibly_parallel(num_cores)
199
def CheckGuess(i):
200
bobskey = key + i*3^(b-3)
201
bobscurve, _ = Pushing3Chain(E_start, P3 + bobskey*Q3, b)
202
return bobscurve.j_invariant() == EB.j_invariant()
203
204
print(f"Determination of last {3} ternary digits. We are brute-forcing this.")
205
206
for result in CheckGuess([0..3^3]):
207
((i,), _), found = result
208
if found:
209
bobskey = key + i*3^(b-3)
210
break
211
212
print(f"Bridging last gap took: {time.time() - tim2}")
213
214
if found:
215
print(f"Bob's secret key revealed as: {bobskey}")
216
print(f"In ternary, this is: {Integer(bobskey).digits(base=3)}")
217
print(f"Altogether this took {time.time() - tim} seconds.")
218
return bobskey
219
else:
220
print("Something went wrong.")
221
print(f"Altogether this took {time.time() - tim} seconds.")
222
return None
223
224