2026 年 10 月 15 日 UTC 01:07:51,一条悬赏认领记录出现在 Filippo Valsorda 三年前发起的 NIST 椭圆曲线种子悬赏页面上。认领人是一支 Anthropic 团队,他们没有按传闻中的思路去找那句「英文俏皮话」,而是让 Claude 从零推断这些种子是怎么生成的。答案揭开时,密码学界攒了近三十年的猜测集体作废:这些种子只是 1997 到 1998 年间某台工作站上的 C 语言随机数发生器 drand48 的输出,初始种子是当时的 Unix 时间戳。Anthropic 在 10 月 16 日发布了完整研究记录,标题下那句导语写得很平静:It's just timestamps。

一个没有解释的参数
ECDSA 是目前使用最广的椭圆曲线签名标准,HTTPS 证书、SSH 密钥、代码签名大量依赖它。2000 年,NIST 在 FIPS 186-2 数字签名标准中收录了一组推荐曲线,其中素域曲线 P-192、P-224、P-256、P-384、P-521 直到今天仍在生产环境中承担大部分椭圆曲线密码任务。
按 ANSI X9.62 的生成流程,每条曲线要从一个任意选取的种子(seed)出发,把种子做 SHA-1 哈希后派生曲线参数 b。FIPS 标准公布了每条曲线的种子值,例如 P-256 的种子是 C49D3608 86E70493 6A6678E1 139D26B7 819F7E90,但只写到「由 NSA 提供」,没有说明它从何而来。
密码学界的顾虑由来已久:如果种子可以被刻意挑选,NSA 就有机会预先测试大量种子、从中选出对攻击者有利的弱曲线,再假装它是随机的。Koblitz 与 Menezes 在《A Riddle Wrapped in an Enigma》里系统梳理过这场争议。2015 年,Jerry Solinas(种子的经手人、NSA 资深数学家)留下了一段后来被反复引用的回忆:大意是种子来自对某句幽默短语的哈希,「也许是一句 counter 而非多次哈希,细节我记不清了,写代码的是 Bob Reiter,他也记不清了。就算有存档,多半也在某个装着约柜的硬盘旁边。」2023 年 Solinas 去世,这条口头线索成了绝路。Filippo Valsorda 随后发起 12,288 美元悬赏(捐给慈善机构则三倍),征集任何能复现这些种子的人。三年里无人认领——因为所有人都拿着「哈希一句英文」的假设在挖,而这个假设是错的。
Claude 的破解路径
Anthropic 团队事先没有把 Solinas 的传闻喂给模型。2026 年 10 月,一位员工向内部研究模型抛出问题:能否从种子值本身推断出生成方式。半小时内,Claude 找到了全部五条 NIST 素域曲线种子以及五条 ANSI X9.62 示例曲线种子的来源。整条推理链值得逐段拆开看。
第一步:猜方向。 Claude 判断,1990 年代的生成程序大概率用的是当时 C 标准库自带的伪随机数发生器(PRNG)。这类老式 PRNG(线性同余生成器一族)的输出自带数学结构,可以从中反推出内部状态。
第二步:滑窗扫描。 Claude 写了一个扫描器,沿每条公开种子逐位滑动,把相邻数据块当作候选的连续输出,测试它们是否符合 1990 年代某款生成器的状态转移。一共覆盖 34 种「生成器 × 输出切片」组合。扫到这里,一个异常浮出水面:部分种子的最高位全是零。写成 10 个 32 位字来看,十条种子中有四条的全部 20 个字最高位为零,随机出现这种排列的概率是 1/5810。这是 lrand48 的签名特征——它只返回 48 位内部状态的高 31 位,最高位永远是 0。
第三步:恢复内部状态。 二进制曲线种子保留了完整的 lrand48 输出字,Claude 取第一个字、穷举 2^17 个隐藏低位组合、验证后续四个字是否吻合,四条种子全部恢复成功。素域曲线更难:每条种子只保留了每个输出字的低若干位(lrand48() >> 23),恢复一个被截断的线性同余生成器的完整状态,是一道教科书级的格问题,用 LLL 格基规约算法可解。Claude 对每个可能的比特窗口都跑了一遍求解器,十条素域种子(NIST 五条 + ANSI X9.62 五条)全部命中同一模式。
第四步:倒推初始时间戳。 线性同余生成器可以向后步进。Claude 把每条种子的内部状态一路回退到 srand48 初始化的形态(低 16 位等于 0x330E),发现十条素域种子对应的初始化值全部落在 1997 年 5 月到 1998 年 4 月之间的 Unix 时间戳上。这个形态本身每 65,536 步就会出现一次,单独看不构成证据;但十个相互独立的种子全部落在同一年窗口内的整秒时间戳上,就不是巧合能解释的了。

Anthropic 公布了 CC0 公开域授权的完整复现代码。核心逻辑只有几行:把时间戳左移 16 位拼上 0x330E 构造初始状态,按 x = (0x5DEECE66D * x + 0xB) mod 2^48 步进,每 20 字节作为一个候选种子,第 n 个候选就是目标值。P-256 需要迭代 1842 次才产出标准公布的那个种子;P-224 跑了 7026 次。下表是官方代码在本地跑出的逐条复现结果(节选),每条的时间戳、候选序号、种子值与 FIPS 标准公布值完全一致:
| 曲线 | srand48 时间戳 (UTC) | 候选序号 | 复现种子值 |
|---|---|---|---|
| P-192 | 1997-05-18 14:45:34 | 139 | 3045AE6FC8422F64ED579528D38120EAE12196D5 |
| P-256 | 1997-08-16 01:52:34 | 1842 | C49D360886E704936A6678E1139D26B7819F7E90 |
| P-384 | 1997-08-30 14:50:08 | 2688 | A335926AA319A27A1D00896A6773A4827ACDAC73 |
| P-521 | 1997-08-08 01:12:04 | 1136 | D09E8800291CB85396CC6717393284AAA0DA64BA |
| P-224 | 1998-04-03 12:52:07 | 7026 | BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5 |

五条二进制域曲线(B-233/283/409/571)也一并解开:初始化时间戳折半后统一落在 1998 年 4 月 17 日 15 点多的几分钟内,程序当时可能把时钟除以了二。
基点也被认出来了两条
每条曲线除了种子,还有一个固定基点 G,所有公钥都是它的倍数。种子不决定基点,所以基点从哪来是第二个悬案。Claude 顺带解出了六条 1997 年 5 月生成的曲线(P-192 与五条 ANSI 示例曲线)的基点来源:Arjen Lenstra 的长整数运算包 FreeLIP 里的 zrandom 随机数发生器,配方是「小整数种子播种、丢弃七个输出、逐 26 位 limb 生成候选 x 坐标、循环直到落在曲线上」。六个基点的生成器种子是 20000、20420、20507、20509、20512、20517,复现代码对六条曲线全部验证通过,x 坐标与公布值逐一相符。P-224、P-256、P-384、P-521 的基点来源仍未找到解释。在这次研究之前,没有任何一条 NIST 基点的来历被解释过。
这对安全性意味着什么
这次发现没有在任何曲线里找到弱点,方向恰好相反:它显著削弱了「NSA 蓄意挑弱曲线」假说的可行性。
Filippo Valsorda 在认领帖里给出了定量分析。2017 年,Daniel Bernstein 与 Tanja Lange 曾向 Koblitz 和 Menezes 描述过一种理论攻击:如果 NSA 能自由挑选种子(约 70 位自由度),可以瞄准一类假设存在的 2^61 条弱素域曲线做筛选植入。这是「曲线后门」猜想最有分量的理论版本,也是多年阴谋论的燃料。而这次的发现把这个前提直接拆掉了:种子生成程序用的是时钟初始化的 drand48,NSA 在 1997-98 年能操纵的自由度,最多是「挑哪个时间戳 + 挑前 16 个合格候选中的哪一个」,满打满算约 30 位。30 位自由度不足以瞄准一个假设规模只有 2^61 的弱曲线集合,也不足以植入任何 NOBUS(仅我们自己知道)式后门。
「用一个可以事后公开的弱随机源生成种子」与「精心挑选种子植入后门」是两种相反的操作。Anthropic 团队同样强调:多数研究者本来就信任这些曲线,这项发现只是把「信任」的理由从「没有证据表明有问题」升级成了「有正面证据表明生成过程可复现且无操纵空间」。用 Filippo 的话说,这补上了 NIST 椭圆曲线密码「康复弧」的最后一块:确定性签名与 hedged 签名方案、形式化验证的域运算实现、2015 年 Renes/Costello/Batina 的完备加法公式,再加上这次的种子溯源,他个人现在推荐 NIST 曲线的程度已经高于 Curve25519/Ed25519——理由还包括 P-256 的硬件支持无处不在。时机则颇有戏剧性:这份背书到得恰逢这些曲线即将让位于后量子签名算法之际。
没有闭合的口子
研究也留下了几个诚实的未解项。Filippo 用 Go 独立复算了整套流程,发现合格种子数大约是公布数的两倍:有些同样满足 ANSI X9.62 全部条件(如 P-256 同批的第 876 号候选)没有被采用,已知的文档条件解释不了这些落选。可能的解释包括生成器输出被校验程序复用消耗、或者 1997 年的代码本身有 bug——这些当年需要尖端算力的计算,今天用现成软件几分钟就能跑完。落选数量少这一事实本身就构成证据:如果存在批量制造弱曲线的能力,弱曲线就不会如此稀少难寻。此外,第五条二进制种子 B-163 不符合任何已试模式,仍是悬案;四条主要素域曲线的基点生成方式也没有答案。
对普通开发者,这次事件不需要任何行动:没有曲线被废、没有库需要升级、没有配置需要修改。真正值得带走的是方法论层面的东西——一个悬置 28 年、线索只剩一句模糊回忆的问题,被「先推断工具链再推断参数」的思路在半小时内解开。Claude 当时的内部思考被 Anthropic 原文公开:在确认 160 位种子能由一个 32 位时间戳加小序号完整复现时,它先怀疑自己在测试环境里,再用「十个独立种子的 160 位全部吻合」排除了巧合。对密码学而言,1997 年那个下午敲下 srand48(time(NULL)) 的程序员大概不会想到,这行代码的阴影会笼罩一个标准四分之一个世纪,而照亮它的会是一个语言模型。
参考来源:Anthropic 官方研究记录(2026-10-16,Frontier Red Team,John Wood、Henry de Valence、Nicholas Carlini 等);Filippo Valsorda 悬赏认领帖(2026-10-16,含 Go 复算与自由度分析);悬赏最初公告 words.filippo.io/seeds-bounty/(2023-10)。