关于sagemath10.X运行Castryck-Decru-SageMath可能遇到的问题

环境:windows上的wsl2的arch,sagemath版本10.9
最近在学习超奇异椭圆曲线同源交换(SIDH),github上有封装好的攻击库,在拉取后运行遇到的一些问题总结

github:GiacomoPope/Castryck-Decru-SageMath: A SageMath implementation of the Castryck-Decru Key Recovery attack on SIDH
学习博客:Isogeny | 糖醋小鸡块的blog

拉取攻击库后,第一步就是要去试试baby_SIDH.sage能不能运行,如果不行,可能是下面的问题

如果懒得研究具体报错的话,可以试试直接将下面的脚本覆盖richelot_aux.py后再运行sage ./baby_SIDH.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
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
# SageMath imports
from sage.all import (
PolynomialRing,
EllipticCurve,
EllipticCurveIsogeny,
HyperellipticCurve,
Matrix,
vector,
ZZ,
set_verbose
)

set_verbose(-1)

def FromProdToJac(C, E, P_c, Q_c, P, Q, a):
Fp2 = C.base()
Rx = PolynomialRing(Fp2, name="x")
x = Rx.gens()[0]

P_c2 = 2**(a-1)*P_c
Q_c2 = 2**(a-1)*Q_c
P2 = 2**(a-1)*P
Q2 = 2**(a-1)*Q

a1, a2, a3 = P_c2[0], Q_c2[0], (P_c2 + Q_c2)[0]
b1, b2, b3 = P2[0], Q2[0], (P2 + Q2)[0]

# Compute coefficients
M = Matrix(Fp2, [
[a1*b1, a1, b1],
[a2*b2, a2, b2],
[a3*b3, a3, b3]])
R, S, T = M.inverse() * vector(Fp2, [1,1,1])
RD = R * M.determinant()
da = (a1 - a2)*(a2 - a3)*(a3 - a1)
db = (b1 - b2)*(b2 - b3)*(b3 - b1)

s1, t1 = - da / RD, db / RD
s2, t2 = -T/R, -S/R

a1_t = (a1 - s2) / s1
a2_t = (a2 - s2) / s1
a3_t = (a3 - s2) / s1
h = s1 * (x**2 - a1_t) * (x**2 - a2_t) * (x**2 - a3_t)

H = HyperellipticCurve(h)
J = H.jacobian()

def isogeny(pair):
Pc, P = pair
if Pc:
xPc, yPc = Pc.xy()
uPc = s1 * x**2 + s2 - xPc
vPc = Rx(yPc / s1)
# SageMath 10.x 兼容:确保 deg(v) < deg(u)
if vPc.degree() >= uPc.degree():
vPc = vPc % uPc
JPc = J([uPc, vPc])
# Same for E
# H->E: (xE = t1 x² + t2, yE = t1 y/x^3)
if P:
xP, yP = P.xy()
uP = (xP - t2) * x**2 - t1
vP = yP * x**3 / t1
# SageMath 10.x 兼容:确保 deg(v) < deg(u)
# 这里 vP 是 3 次,uP 是 2 次,必须取模!
if vP.degree() >= uP.degree():
vP = vP % uP
JP = J([uP, vP])
if Pc and P:
return JPc + JP
if Pc:
return JPc
if P:
return JP


imPcP = isogeny((P_c, P))
imQcQ = isogeny((Q_c, Q))

# Validate result, for debugging
# def projC(_x, _y):
# return (s1 * _x^2 + s2, s1 * _y)
# def projE(_x, _y):
# return (t1 / _x^2 + t2, t1 * _y / _x^3)
# Fp4 = Fp2.extension(2)
# E4 = E.change_ring(Fp4)
# C4 = C.change_ring(Fp4)
# divP = [(xr, imPcP[1](xr)) for xr, _ in imPcP[0].roots(Fp4)]
# assert 2*E4(P) == sum(E4(*projE(*pt)) for pt in divP)
# assert 2*C4(P_c) == sum(C4(*projC(*pt)) for pt in divP)
# divQ = [(xr, imQcQ[1](xr)) for xr, _ in imQcQ[0].roots(Fp4)]
# assert 2*E4(Q) == sum(E4(*projE(*pt)) for pt in divQ)
# assert 2*C4(Q_c) == sum(C4(*projC(*pt)) for pt in divQ)

return h, imPcP[0], imPcP[1], imQcQ[0], imQcQ[1], isogeny

class RichelotCorr:
"""
The Richelot correspondance between hyperelliptic
curves y²=g1*g2*g3 and y²=h1*h2*h3=hnew(x)

It is defined by equations:
g1(x1) h1(x2) + g2(x1) h2(x2) = 0
and y1 y2 = g1(x1) h1(x2) (x1 - x2)

Given a divisor D in Mumford coordinates:
U(x) = x^2 + u1 x + u0 = 0
y = V(x) = v1 x + v0
Let xa and xb be the symbolic roots of U.
Let s, p by the sum and product (s=-u1, p=u0)

Then on x-coordinates, the image of D is defined by equation:
(g1(xa) h1(x) + g2(xa) h2(x))
* (g1(xb) h1(x) + g2(xb) h2(x))
which is a symmetric function of xa and xb.
This is a non-reduced polynomial of degree 4.

Write gred = g-U = g1*x + g0
then gred(xa) gred(xb) = g1^2*p + g1*g0*s + g0^2
and g1red(xa) g2red(xb) + g1red(xb) g2red(xa)
= 2 g11 g21 p + (g11*g20+g10*g21) s + 2 g10*g20

On y-coordinates, the image of D is defined by equations:
V(xa) y = Gred1(xa) h1(x) (xa - x)
OR V(xb) y = Gred1(xb) h1(x) (xb - x)
If we multiply:
* y^2 has coefficient V(xa)V(xb)
* y has coefficient h1(x) * (V(xa) Gred1(xb) (x-xb) + V(xb) Gred1(xa) (x-xa))
(x-degree 3)
* 1 has coefficient Gred1(xa) Gred1(xb) h1(x)^2 (x-xa)(x-xb)
= Gred1(xa) Gred1(xb) h1(x)^2 U(x)
(x-degree 4)
"""
def __init__(self, G1, G2, H1, H2, hnew):
assert G1[2].is_one() and G2[2].is_one()
self.G1 = G1
self.G2 = G2
self.H1 = H1
self.H11 = H1*H1
self.H12 = H1*H2
self.H22 = H2*H2
self.hnew = hnew
self.x = hnew.parent().gen()

def map(self, D):
"Computes (non-monic) Mumford coordinates for the image of D"
U, V = D[0], D[1]
if not U[2].is_one():
U = U / U[2]
V = V % U
# Sum and product of (xa, xb)
s, p = -U[1], U[0]
# Compute X coordinates (non reduced, degree 4)
g1red = self.G1 - U
g2red = self.G2 - U
assert g1red[2].is_zero() and g2red[2].is_zero()
g11, g10 = g1red[1], g1red[0]
g21, g20 = g2red[1], g2red[0]
# see above
Px = (g11*g11*p + g11*g10*s + g10*g10) * self.H11 \
+ (2*g11*g21*p + (g11*g20+g21*g10)*s + 2*g10*g20) * self.H12 \
+ (g21*g21*p + g21*g20*s + g20*g20) * self.H22

# Compute Y coordinates (non reduced, degree 3)
assert V[2].is_zero()
v1, v0 = V[1], V[0]
# coefficient of y^2 is V(xa)V(xb)
Py2 = v1*v1*p + v1*v0*s + v0*v0
# coefficient of y is h1(x) * (V(xa) Gred1(xb) (x-xb) + V(xb) Gred1(xa) (x-xa))
# so we need to symmetrize:
# V(xa) Gred1(xb) (x-xb)
# = (v1*xa+v0)*(g11*xb+g10)*(x-xb)
# = (v1*g11*p + v1*g10*xa + v0*g11*xb + v0*g10)*x
# - xb*(v1*g11*p + v1*g10*xa + v0*g11*xb + v0*g10)
# Symmetrizing xb^2 gives u1^2-2*u0
Py1 = (2*v1*g11*p + v1*g10*s + v0*g11*s + 2*v0*g10)*self.x \
- (v1*g11*s*p + 2*v1*g10*p + v0*g11*(s*s-2*p) + v0*g10*s)
Py1 *= self.H1
# coefficient of 1 is Gred1(xa) Gred1(xb) h1(x)^2 U(x)
Py0 = self.H11 * U * (g11*g11*p + g11*g10*s + g10*g10)

# Now reduce the divisor, and compute Cantor reduction.
# Py2 * y^2 + Py1 * y + Py0 = 0
# y = - (Py2 * hnew + Py0) / Py1
_, Py1inv, _ = Py1.xgcd(Px)
Py = (- Py1inv * (Py2 * self.hnew + Py0)) % Px
assert Px.degree() == 4
assert Py.degree() <= 3

Dx = ((self.hnew - Py ** 2) // Px)
Dy = (-Py) % Dx
return (Dx, Dy)

def jacobian_double(h, u, v):
"""
Computes the double of a jacobian point (u,v)
given by Mumford coordinates: except that u is not required
to be monic, to avoid redundant reduction during repeated doubling.

See SAGE cantor_composition() and cantor_reduction
"""
assert u.degree() == 2
# Replace u by u^2
# Compute h3 the inverse of 2*v modulo u
# Replace v by (v + h3 * (h - v^2)) % u
q, r = u.quo_rem(2*v)
if r[0] == 0: # gcd(u, v) = v, very improbable
a = q**2
b = (v + (h - v**2) // v) % a
return a, b
else: # gcd(u, v) = 1
h3 = 1 / (-r[0]) * q
a = u*u
b = (v + h3 * (h - v**2)) % a
# Cantor reduction
Dx = (h - b**2) // a
Dy = (-b) % Dx
return Dx, Dy

def jacobian_iter_double(h, u, v, n):
for _ in range(n):
u, v = jacobian_double(h, u, v)
return u.monic(), v

def FromJacToJac(h, D11, D12, D21, D22, a, powers=None):
# power is an optional list of precomputed tuples
# (l, 2^l D1, 2^l D2) where l < a are increasing
R,x = h.parent().objgen()
Fp2 = R.base()

D1 = (D11, D12)
D2 = (D21, D22)

next_powers = None
if not powers:
# Precompute some powers of D1, D2 to save computations later.
# We are going to perform O(a^1.5) squarings instead of O(a^2)
if a >= 16:
gap = ZZ(a).isqrt()
doubles = [(0, D1, D2)]
_D1, _D2 = D1, D2
for i in range(a-1):
_D1 = jacobian_double(h, _D1[0], _D1[1])
_D2 = jacobian_double(h, _D2[0], _D2[1])
doubles.append((i+1, _D1, _D2))
_, (G1, _), (G2, _) = doubles[a-1]
G1, G2 = G1.monic(), G2.monic()
next_powers = [doubles[a-2*gap], doubles[a-gap]]
else:
G1, _ = jacobian_iter_double(h, D1[0], D1[1], a-1)
G2, _ = jacobian_iter_double(h, D2[0], D2[1], a-1)
else:
(l, _D1, _D2) = powers[-1]
if a >= 16:
next_powers = powers if l < a-1 else powers[:-1]
G1, _ = jacobian_iter_double(h, _D1[0], _D1[1], a-1-l)
G2, _ = jacobian_iter_double(h, _D2[0], _D2[1], a-1-l)

# assert 2**a*D1 == 0
# assert 2**a*D2 == 0
G3, r3 = h.quo_rem(G1 * G2)
assert r3 == 0

delta = Matrix(G.padded_list(3) for G in (G1,G2,G3))
# H1 = 1/det (G2[1]*G3[0] - G2[0]*G3[1])
# +2x (G2[2]*G3[0] - G3[2]*G2[0])
# +x^2(G2[1]*G3[2] - G3[1]*G2[2])
# The coefficients correspond to the inverse matrix of delta.
delta = delta.inverse()
H1 = -delta[0][0]*x**2 + 2*delta[1][0]*x - delta[2][0]
H2 = -delta[0][1]*x**2 + 2*delta[1][1]*x - delta[2][1]
H3 = -delta[0][2]*x**2 + 2*delta[1][2]*x - delta[2][2]

hnew = H1*H2*H3

# Now compute image points: Richelot isogeny is defined by the degree 2
R = RichelotCorr(G1, G2, H1, H2, hnew)

imD1 = R.map(D1)
imD2 = R.map(D2)
if next_powers:
next_powers = [(l, R.map(_D1), R.map(_D2))
for l, _D1, _D2 in next_powers]
return hnew, imD1[0], imD1[1], imD2[0], imD2[1], R.map, next_powers

def FromJacToProd(G1, G2, G3):
"""
Construct the "split" isogeny from Jac(y^2 = G1*G2*G3)
to a product of elliptic curves.

This computation is the same as Benjamin Smith
see 8.3 in http://iml.univ-mrs.fr/~kohel/phd/thesis_smith.pdf
"""
h = G1*G2*G3
R = h.parent()
Fp2 = R.base()
x = R.gen()

M = Matrix(G.padded_list(3) for G in (G1,G2,G3))
# Find homography
u, v, w = M.right_kernel().gen()
d = u/2
(ad, _), (b, _) = (x**2 - v*x + w*d/2).roots()
a = ad/d

# Apply transform G(x) -> G((a*x+b)/(x+d))*(x+d)^2
# The coefficients of x^2 are M * (1, a, a^2)
# The coefficients of 1 are M * (d^2, b*d, b^2)
H11, H21, H31 = M * vector([1, a, a*a])
H10, H20, H30 = M * vector([d*d, b*d, b*b])
assert G1((a*x+b)/(x+d))*(x+d)**2 == H11*x**2+H10

h2 = (H11*x**2+H10)*(H21*x**2+H20)*(H31*x**2+H30)
H2 = HyperellipticCurve(h2)

p1 = (H11*x+H10)*(H21*x+H20)*(H31*x+H30)
p2 = (H11+H10*x)*(H21+H20*x)*(H31+H30*x)
# We will need to map to actual elliptic curve
p1norm = (x + H10*H21*H31)*(x + H20*H11*H31)*(x + H30*H11*H21)
p2norm = (x + H11*H20*H30)*(x + H21*H10*H30)*(x + H31*H10*H20)
E1 = EllipticCurve([0, p1norm[2], 0, p1norm[1], p1norm[0]])
E2 = EllipticCurve([0, p2norm[2], 0, p2norm[1], p2norm[0]])

def morphE1(x, y):
# from y^2=p1 to y^2=p1norm
return (H11*H21*H31*x, H11*H21*H31*y)
def morphE2(x, y):
# from y^2=p1 to y^2=p2norm
return (H10*H20*H30*x, H10*H20*H30*y)
# The morphisms are:
# inverse homography:
# H->H2: x, y => ((b-dx) / (x-a), y/(x-a)^3)
# then H2->E1:(x,y) => (x^2,y)
# or H2->E2:(x,y) => (1/x^2,y/x^3)

def isogeny(D):
HyperellipticCurve(h).jacobian()(D)
# To map a divisor, perform the change of coordinates
# on Mumford coordinates
U, V = D[0], D[1]
# apply homography
# y = v1 x + v0 =>
U_ = U[0] * (x+d)**2 + U[1]*(a*x+b)*(x+d) + U[2]*(a*x+b)**2
V_ = V[0] * (x+d)**3 + V[1]*(a*x+b)*(x+d)**2
V_ = V_ % U_
v1, v0 = V_[1], V_[0]
# prepare symmetric functions
s = - U_[1] / U_[2]
p = U_[0] / U_[2]
# compute Mumford coordinates on E1
# Points x1, x2 map to x1^2, x2^2
U1 = x**2 - (s*s - 2*p)*x + p**2
# y = v1 x + v0 becomes (y - v0)^2 = v1^2 x^2
# so 2v0 y-v0^2 = p1 - v1^2 xH^2 = p1 - v1^2 xE1
V1 = (p1 - v1**2 * x + v0**2) / (2*v0)
# Reduce Mumford coordinates to get a E1 point
V1 = V1 % U1
U1red = (p1 - V1**2) // U1
xP1 = -U1red[0] / U1red[1]
yP1 = V1(xP1)
assert yP1**2 == p1(xP1)
# Same for E2
# Points x1, x2 map to 1/x1^2, 1/x2^2
U2 = x**2 - (s*s-2*p)/p**2*x + 1/p**2
# yE = y1/x1^3, xE = 1/x1^2
# means yE = y1 x1 xE^2
# (yE - y1 x1 xE^2)(yE - y2 x2 xE^2) = 0
# p2 - yE (x1 y1 + x2 y2) xE^2 + (x1 y1 x2 y2 xE^4) = 0
V21 = x**2 * (v1 * (s*s-2*p) + v0*s)
V20 = p2 + x**4 * (p*(v1**2*p + v1*v0*s + v0**2))
# V21 * y = V20
_, V21inv, _ = V21.xgcd(U2)
V2 = (V21inv * V20) % U2
# assert V2**2 % U2 == p2 % U2
# Reduce coordinates
U2red = (p2 - V2**2) // U2
xP2 = -U2red[0] / U2red[1]
yP2 = V2(xP2)
# assert yP2**2 == p2(xP2)

return E1(morphE1(xP1, yP1)), E2(morphE2(xP2, yP2))

return isogeny, (E1, E2)

def Does22ChainSplit(C, E, P_c, Q_c, P, Q, a):
"""
Returns None if the chain does not split
or a tuple (chain of isogenies, codomain (E1, E2))
"""
chain = []
# gluing step
h, D11, D12, D21, D22, f = FromProdToJac(C, E, P_c, Q_c, P, Q, a)
chain.append(f)
next_powers = None
# print(f"order 2^{a-1} on hyp curve ...")
for i in range(1,a-2+1):
h, D11, D12, D21, D22, f, next_powers = FromJacToJac(
h, D11, D12, D21, D22, a-i, powers=next_powers)
chain.append(f)

# now we are left with a quadratic splitting: is it singular?
G1 = D11
G2 = D21
G3, r3 = h.quo_rem(G1 * G2)
assert r3 == 0

delta = Matrix(G.padded_list(3) for G in (G1,G2,G3))
if delta.determinant():
# Determinant is non-zero, no splitting
return None

# Splitting found!
# Finish chain
f, codomain = FromJacToProd(G1, G2, G3)
chain.append(f)
return chain, codomain


def Pushing3Chain(E, P, i):
"""
Compute chain of isogenies quotienting
out a point P of order 3^i

https://trac.sagemath.org/ticket/34239
"""
def rec(Q, k):
assert k
if k == 1: # base case
# assert Q and not 3*Q
return [EllipticCurveIsogeny(Q.curve(), Q, degree=3, check=False)]

k1 = int(k * .8 + .5)
k1 = max(1, min(k-1, k1)) # clamp to [1, k-1]

Q1 = 3**k1 * Q
L = rec(Q1, k-k1)

Q2 = Q
for psi in L:
Q2 = psi(Q2)
R = rec(Q2, k1)

return L + R

chain = rec(P, i)
return chain[-1].codomain(), chain

def AuxiliaryIsogeny(i, u, v, E_start, P2, Q2, tauhatkernel, two_i):
"""
Compute the distored kernel using precomputed u,v and the
automorphism two_i.

This is used to construct the curve C from E_start and we
compute the image of the points P_c and Q_c
"""
tauhatkernel_distort = u*tauhatkernel + v*two_i(tauhatkernel)

C, tau_tilde = Pushing3Chain(E_start, tauhatkernel_distort, i)
def chain(P):
Pc = u*P + v*two_i(P)
for taut in tau_tilde:
Pc = taut(Pc)
return Pc
return C, chain(P2), chain(Q2), chain

问题一

遇到这样的报错:
File "/usr/lib/python3.14/site-packages/sage/schemes/hyperelliptic_curves/jacobian_homset_generic.py", line 499, in _cantor_composition_generic assert v1.degree() < u1.degree() and v2.degree() < u2.degree(), ( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AssertionError: The degree of bi must be smaller than ai

这是遇到攻击库与10.X版本的兼容性问题,具体是在Jacobian点加法时触发了SageMath新版本更严格的断言:AssertionError: The degree of bi must be smaller than ai

解决:
找到richelot_aux.py的FromProdToJac里的isogeny(差不多RichelotCorr第49行):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
c, P = pair
if Pc:
xPc, yPc = Pc.xy()
JPc = J([s1 * x**2 + s2 - xPc, Rx(yPc / s1)])
# Same for E
# H->E: (xE = t1 x² + t2, yE = t1 y/x^3)
if P:
xP, yP = P.xy()
JP = J([(xP - t2) * x**2 - t1, yP * x**3 / t1])
if Pc and P:
return JPc + JP
if Pc:
return JPc
if P:
return JP

改为:

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
Pc, P = pair
if Pc:
xPc, yPc = Pc.xy()
uPc = s1 * x**2 + s2 - xPc
vPc = Rx(yPc / s1)
# SageMath 10.x 兼容:确保 deg(v) < deg(u)
if vPc.degree() >= uPc.degree():
vPc = vPc % uPc
JPc = J([uPc, vPc])
# Same for E
# H->E: (xE = t1 x² + t2, yE = t1 y/x^3)
if P:
xP, yP = P.xy()
uP = (xP - t2) * x**2 - t1
vP = yP * x**3 / t1
# SageMath 10.x 兼容:确保 deg(v) < deg(u)
# 这里 vP 是 3 次,uP 是 2 次,取模将vP“降次”
if vP.degree() >= uP.degree():
vP = vP % uP
JP = J([uP, vP])
if Pc and P:
return JPc + JP
if Pc:
return JPc
if P:
return JP

问题二

报错:

1
"richelot_aux.py", line 148, in map U, V = D ^^^^ ValueError: too many values to unpack (expected 2)

这是因为:
在sagemath10.x中,当 D 是一个 Jacobian 点对象(而非简单的二元 tuple)时,对它的解包(unpack)会得到 超过 2 个元素(Sage 10.x 的 Jacobian 点内部表示有变化,迭代时可能多出额外的成分)。因此 U, V = D 报 too many values to unpack。

解决:
找到richelot_aux.py的RichelotCorr里的map函数(差不多第139行)和FromJacToProd里的isogeny函数(差不多第334行)

将

1
2
3
4
5
6
7
8
9
def map(self, D):
"Computes (non-monic) Mumford coordinates for the image of D"
U, V = D
和
def isogeny(D):
HyperellipticCurve(h).jacobian()(D)
# To map a divisor, perform the change of coordinates
# on Mumford coordinates
U, V = D

改为

1
2
3
4
5
6
7
8
9
def map(self, D):
"Computes (non-monic) Mumford coordinates for the image of D"
U, V = D[0], D[1]
和
def isogeny(D):
HyperellipticCurve(h).jacobian()(D)
# To map a divisor, perform the change of coordinates
# on Mumford coordinates
U, V = D[0], D[1]

注:作者遇到的就是这俩个问题,如果有什么其他问题和出错欢迎来交流


关于sagemath10.X运行Castryck-Decru-SageMath可能遇到的问题
https://baymax-fools.github.io/2026/05/13/crypto/关于sagemath10-X运行Castryck-Decru-SageMath可能遇到的问题/
Author
Baymax
Posted on
May 13, 2026
Updated on
May 13, 2026
Licensed under