mpz(n) #初始化一个大整数 mpfr(x) # 初始化一个高精度浮点数x d = invert(e,n) # 求逆元,de = 1 mod n c = powmod(m,e,n) # 幂取模,结果是 c = m^e mod n is_prime(n) #素性检测 gcd(a,b) #欧几里得算法,最大公约数 gcdext(a,b) #扩展欧几里得算法 iroot(x,n) #x开n次根
# 多元 GCD(order='lex' 指定单项序) R.<x,y,z> = PolynomialRing(RationalField(), order='lex') f = 3*x^2*(x+y) g = 9*x*(y^2 - x^2) f.gcd(g)
结式恢复未知模数(两条关系多项式在模 p 下有公共根 ⟹ p 整除结式;多个结式取 gcd、factor()[-1][0] 取最大素因子消杂):
1 2 3 4 5 6 7
R.<x> = PolynomialRing(ZZ) f0, f1, f2 = [sum(rkm[j,i]*x^i for i inrange(11)) for j inrange(3)] p = gcd(f0.resultant(f1), f1.resultant(f2)).factor()[-1][0]
# 换环后求公共根 F = GF(p) a = gcd(f0.change_ring(F), f1.change_ring(F)).roots()[0][0]
for kd in convergents: k = kd.numerator() # 渐近分数的分子 → k d = kd.denominator() # 分母 → d if k == 0or (e * d - 1) % k != 0: continue phi = (e * d - 1) // k s = n - phi + 1# s = p + q delta = s^2 - 4 * n if delta < 0: continue sqrt_delta = isqrt(delta) if sqrt_delta^2 != delta: # 判别式是完全平方才分解成功 continue p = (s + sqrt_delta) // 2 q = (s - sqrt_delta) // 2
也可以不解判别式,直接用多项式环按韦达定理解 x² - (n-phi+1)x + n = 0:
1 2 3 4 5
x = PolynomialRing(RationalField(), "x").gen() f = x**2 - (n - phi + 1) * x + n roots = f.roots() iflen(roots) == 2: p, q = int(roots[0][0]), int(roots[1][0])
MatrixSpace(GF(p), n, n) # 矩阵空间,可对普通矩阵做换域 Mat_p(A)
构造矩阵的两种轻量手法(不想用 block_matrix 时):
1 2 3 4 5 6 7 8 9 10 11
# 手法一:建零矩阵后逐元素赋值 L = Matrix(ZZ, 8, 8) L[0,0] = n L[6,2] = c1r * X ...
# 手法二:分块切片赋值(高斯整数 2x2 块展开用) out = Matrix(ZZ, 2*len(mat), 2*len(mat[0])) for r inrange(len(mat)): for c inrange(len(mat[0])): out[2*r:2*r+2, 2*c:2*c+2] = block(mat[r][c]) # 2x2 子块直接赋值
解线性方程组 AX = B:
1 2 3 4
A = Matrix([[1,2,3],[3,2,1],[1,1,1]]) Y = vector([0,-4,-1]) X = A.solve_right(Y) # 反斜杠 \ 是 solve_right 的简写:X = A \ Y
Zmod(n) 上的首一低次多项式可以求模 n 下的小根。关键三步:建环 → 构造首一多项式 → 调 small_roots:
1 2 3 4
PR.<x> = PolynomialRing(Zmod(n)) f = p + x # 已知 p 高位,未知低位 res = f.small_roots(X=2^100, beta=0.4) # X: 根的上界; beta: p 相对 n 的位宽比例 p = int(res[0]) + p # 恢复完整 p
通用模板(含参数调优与爆破):
1 2 3 4 5 6 7
defpartial_prime_known_highbits(n, known_high, unknown_bits): f = known_high * 2^unknown_bits + x roots = f.small_roots(X=2^unknown_bits, beta=0.4, epsilon=0.01) for r in roots: p = int(known_high * 2^unknown_bits + r) if p and n % p == 0: return p
beta 的语义:根是"n 的 beta 比例大小因子"的根——已知 p 高位分解 n 时 beta=0.5(因子 ≈ √N),实际常用 0.4;beta=1 对应根以 n 本身为模(如明文)。理论上界 X < N^(β²/ε)。题目没给位宽时用 N.nbits() // 2 - bits_known 推 X。
e 很小(如 e=3)且明文有可枚举的已知结构时,直接对 (已知部分 + x)^e - c 求小根。注意:最高次系数可能不等于 1,需要先手动首一化——用环元素求逆:
1 2 3 4 5 6 7 8
P.<x> = PolynomialRing(Zmod(n)) f = (x * shift + h_val)^e - c
# 关键:首一化。最高次项系数模 n 求逆后乘上去 coeff = f.coefficients()[-1] f_monic = f * (coeff^-1) # sage 环元素可直接用 ^-1 求逆
X = 2^x_bits M_matrix = Matrix(ZZ, e + 1, e + 1) for i inrange(e): M_matrix[i, i] = X^i # 第 e 行放二项式展开系数:binomial(e, i) * M^(e-i) mod N,末位 X^e ... L = M_matrix.LLL() coeffs = [L[0, i] // (X^i) for i inrange(e+1)] # 除回缩放因子 P.<x> = PolynomialRing(ZZ) g = sum(coeffs[i] * x^i for i inrange(len(coeffs))) roots = g.roots() # 整数环上求根
defblock(z): a, b = z return Matrix(ZZ, 2, 2, [a, b, -b, a])
defexpand(mat): out = Matrix(ZZ, 2 * len(mat), 2 * len(mat[0])) for r inrange(len(mat)): for c inrange(len(mat[0])): out[2*r:2*r+2, 2*c:2*c+2] = block(mat[r][c]) return out
# 奇异曲线:判别式为 0 4*a^3 + 27*b^2 == 0 # 异常曲线(Smart's attack):#E == p E.order() == p # 超奇异曲线(MOV attack):Frobenius 迹 t = p + 1 - #E 满足 t % p == 0 t = p + 1 - E.order() t % p == 0
deffind_T(E_ext, G_ext, n): # 扩域上找 n 阶辅助点:随机 x → is_square 判 QR → sqrt 得 y for _ inrange(100): x = F_ext.random_element() rhs = x^3 + 1 if rhs.is_square(): y = rhs.sqrt() T = E_ext.point((x, y)) if n * T == E_ext(0): # n·T = O 判阶 pair = E_ext.weil_pairing(G_ext, T, n) if pair != 1: # 配对为 1 说明与 G 线性相关,跳过! return T
alpha = E_ext.weil_pairing(G_ext, T, n) beta = E_ext.weil_pairing(P_ext, T, n) k = beta.log(alpha) # 扩域乘法群 dlog # Tate 配对更快(四参数,多一个嵌入度) pair = E.tate_pairing(P, Q, n, k)
小阶子群上的 ECDLP(点不在标准曲线上时可对每个候选 c 建曲线):
1 2 3 4
curve = EllipticCurve(GF(P), [A, c]) base = curve(x, y) point = curve(rx, ry) k = int(discrete_log(point, base, ord=order, operation='+'))
aaa = Matrix(ZZ, output) L = aaa.LLL() # 最短向量就是目标行,必要时除以 GCD 归一化 g = GCD(L[0]) lis = list(map(abs, L[0]/g))
解线性方程组 s 满足 c_i·s = rhs_i 且 s 很小——把每条方程扩一行 [c_i | -rhs_i],目标 (s, 1) 就在右核里,QQ 求核 → 清分母 → LLL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
M = Matrix(ZZ, [list(row) + [-int(rhs)] for row, rhs inzip(eq_rows, eq_rhs)])
# QQ 上求右核 Kq = M.change_ring(QQ).right_kernel().basis_matrix()
# 逐行清分母转成整数格基 K_rows = [] for r in Kq.rows(): den = ZZ(1) for x in r: den = lcm(den, ZZ(x.denominator())) K_rows.append([ZZ(x * den) for x in r]) K = Matrix(ZZ, K_rows)
rkm2 = M.right_kernel_matrix().LLL() k = ZZ(rkm2.solve_left(dy[1:])[2]) # solve_left 求 v 在行基下的坐标(c·B = v) rkm2 *= k # 用坐标做尺度归一化
N = rkm2[::-1].stack(vector(a^i for i inrange(len(dy)-1))) # [::-1] 倒序切片 dx = vector(N.left_kernel_matrix()[0, 2::-1]).lift_centered() * rkm2
Kannan 嵌入(CVP → SVP):目标向量作为额外一列,右下角放缩放因子:
1 2 3 4 5 6 7 8
M_embed = Matrix(ZZ, m + n + 1, m + n + 1) for i inrange(m + n): for j inrange(m + n): M_embed[i, j] = M[i, j] M_embed[i, m + n] = target[i] for scale in [1, 10, 100, 1000]: # scale 多值尝试是实用调试技巧 M_embed[m + n, m + n] = scale L_embed = M_embed.LLL()
常见报错速查:NameError: name 'x' is not defined → 变量未声明;TypeError: unsupported operand → 类型/环不兼容,检查矩阵与域类型;ValueError: matrix must be square → 非方阵;ZeroDivisionError → 检查分母是否可逆。