片段

Coppersmith

总结思路(逐步推理)和为什么 getFullP 里用多项式 P - N

为什么把多项式构造成 p(x) - n


一、从已知条件出发的代数关系(推导低位 p 的来源)

已知:RSA 参数 n=pqn=pq、公钥指数 e=3e=3、私钥 dd 满足

3d1(modφ(n)).3d \equiv 1 \pmod{\varphi(n)}.

所以存在整数 kk 使得

3d=1+kφ(n).3d = 1 + k\varphi(n).

并且 φ(n)=(p1)(q1)=npq+1\varphi(n)=(p-1)(q-1)=n-p-q+1

两边乘以 pp

3pd=p+k  pφ(n).3pd = p + k\;p\varphi(n).

φ(n)\varphi(n) 展开并化简(注意 pq=npq=n):

pφ(n)=p(npq+1)=pnp2pq+p=pnp2n+p.p\varphi(n)=p(n-p-q+1)=p n - p^2 - p q + p = p n - p^2 - n + p.

因此

3pd=p+k(pnp2n+p).3pd = p + k\big(p n - p^2 - n + p\big).

现在你知道 dd 的低 512 位(代码里的 low_d = d & ((1<<512)-1)),于是把上式对 25122^{512} 取模:

3p(low_d)p+k(pnp2n+p)(mod2512).3p\cdot (\text{low\_d}) \equiv p + k\big(p n - p^2 - n + p\big)\pmod{2^{512}}.

这就是 phase4 里用 solve_mod(..., 2^512) 去解的同余方程——解出的就是 pp 的低 512 位候选(maybe_p)。

(代码里循环 k=1k=133 是因为 3d=1+kφ(n)3d=1+k\varphi(n)kk 通常很小,试几个小的 kk 就可以覆盖实际情形。)


二、从低位 pp 恢复完整的 PP:为什么把多项式写成 p(x)np(x)-n

假设通过上一步我们得到了 pp 的低 512 位(记为 low_p)。把完整的素因子写成

P=low_p+x2512,P = \text{low\_p} + x\cdot 2^{512},

其中 xx 是未知的高位整数量(待求)。令多项式(关于未知整数 xx

p(x):=x2512+low_p.p(x) := x\cdot 2^{512} + \text{low\_p}.

关键事实:真正的 x0x_0(使得 p(x0)=Pp(x_0)=P)满足

p(x0)n=Pn=P(q1).p(x_0) - n = P - n = -P(q-1).

因此 p(x0)np(x_0)-nPP 整除,换句话说

p(x0)n0(modP).p(x_0)-n \equiv 0 \pmod{P}.

也就是说, x0x_0 是多项式 f(x)=p(x)nf(x)=p(x)-n 在模 PP 下的根(根是模 PP 的)。

Coppersmith 小根法(及其在 Sage/NTL 中的实现)可以 在已知合数 nn 的条件下 找到使得 f(x)0(modp)f(x)\equiv 0\pmod{p} 的“”整数根 xx(这里 ppnn 的一个大素因子),前提是 xx 的绝对大小小于某个界(我们在代码中用 X=2^128 指定)。在 Sage 中通常把多项式放在 Zmod(n) 上,调用 .small_roots(),算法会寻找可以使得 f(x)f(x)nn 的某个素因子上为 0 的小根 —— 恰好是我们需要的 x0x_0

因此把多项式构造成

f(x)=p(x)nf(x) = p(x) - n

的理由是:

  1. 对真实的 x0x_0f(x0)=Pnf(x_0)=P-n PP 整除:这给出了“模 PP 的零点”这一性质,正是 Coppersmith 能利用的结构。
  2. 我们知道 nnlow_p,所以 f(x)f(x) 的系数是已知的整数(可直接构造)。
  3. 在实现上把多项式放在 PolynomialRing(Zmod(n)) 上并 .monic().small_roots(X, beta),可以在 n 上构造格并搜索在某个因子(即 PP)模下的小根

(补充:理论上用 g(x)=p(x)g(x)=p(x) 也是在模 PP 下为 0 的,但常见的做法是用 p(x)np(x)-n 来把“与 nn 的整除关系”显式编码进多项式,这样在构造 lattice 时更自然,并且与已有实现习惯一致。)


三、参数选择与验证

  • p = x*2^512 + low_p:这是把已知低 512 位固定下来,未知高位写作 xx
  • X = 2^128:这是对 xx 的上界估计。为什么 128?因为如果 PP 的总体位长是 512+128=640512+128=640(例如),那么高位的范围就是 <2128<2^{128}。你需要把 X 设为可以覆盖真实 xx 的值但又不能太大,否则 Coppersmith 的小根条件不会成立。
  • beta = 0.4:这是小根算法的一个参数(表示目标模因子 PP 相对于 nn 的规模比),需要根据 nn 的长度与因子大小做合理设定。实务中会调整 beta,X 以保证算法能工作。
  • .monic():把多项式首项标准化为 1,便于用格方法做 LLL 等数值运算。
  • 找到候选根 r 之后,getFullP 返回 p(r),再验证 P * (n//P) == n,这一步一定要做以排除错误根。

四、整体流程回顾

  1. 3d ≡ 1 (mod φ(n))、已知 low_d 通过模 25122^{512} 的方程求出 pp 的低 512 位候选 maybe_pphase4 的前半部分)。

  2. 对每个 low_p 候选,调用 getFullP(low_p, n)

    • 构造 p(x)=x2512+low_pp(x)=x\cdot2^{512}+{\rm low\_p}
    • 构造 f(x)=p(x)nf(x)=p(x)-n(放在 Zn[x]\mathbb Z_n[x])并用 Coppersmith(.small_roots)找小根 xx
    • 若找到根,计算 P=p(x)P=p(x),再 P*Q==n 验证通过则成功恢复 P,QP,Q
  3. 恢复 d=inv_mod(3, (P1)(Q1))d=\text{inv\_mod}(3,\ (P-1)(Q-1)),然后用 m=cdmodnm = c^d \bmod n 解密得到消息。


五、补充说明(直观理解)

  • 把多项式写成 p(x)-n 的直观原因:我们利用的是“PPnn 的整除关系”。真实的 x0x_0 使得 p(x0)p(x_0) 恰好等于 PP,而 PPnn 的因子,所以 p(x0)np(x_0)-n 在模 PP 下为 0——这就是 Coppersmith 能够抓住的模式。把已知的 nn 显式放进多项式,可以把“根模素因子”这一性质编码到求解问题里,从而用小根算法恢复未知高位。

关于整除的详解

1. 已知关系

我们已经知道:

  • n=PQn = P \cdot Q,其中 P,QP,Q 是素数。

  • p(x)p(x) 的定义是:

    p(x)=x2512+low_pp(x) = x\cdot 2^{512} + \text{low\_p}

  • 真实的 x0x_0 使得 p(x0)=Pp(x_0)=P


2. 把 p(x0)np(x_0)-n 展开

代入 p(x0)=Pp(x_0)=P

p(x0)n=Pnp(x_0)-n = P - n

再把 n=PQn = P Q 代入:

p(x0)n=PPQ=P(1Q)=P(Q1)p(x_0)-n = P - P Q = P(1 - Q) = -P(Q-1)


3. 得出整除结论

右边是 P(Q1)-P(Q-1),显然含有因子 PP,所以:

p(x0)n0(modP)p(x_0)-n \equiv 0 \pmod{P}

换句话说,对模 PP 来说,x0x_0 是多项式 p(x)np(x)-n 的一个根


4. 为什么重要

我们并不知道 PP,但是我们知道 nn。把多项式放在模 nn 的多项式环里,去找一个小的整数根 xx,Coppersmith 方法就能找到这个 x0x_0,因为它必须满足:

  • 在模 PPp(x0)n=0p(x_0)-n=0,而且
  • x0x_0 是一个很小的整数

一旦找到了 x0x_0,就能恢复 P=p(x0)P = p(x_0)

Related Issues not found

Please contact @n-WN to initialize the comment