研究#逆向工程

Claude 查明 NIST 椭圆曲线种子来源:1997–98 年的 lrand48 加 Unix 时间戳,3.6 万美元赏金捐给 ISRG

Anthropic 称研究模型查明 NIST 五条椭圆曲线种子来自 1997–98 年 lrand48 加时间戳,3.6 万美元赏金捐给 ISRG。

紫绿黑三色拼贴插画:左侧紫色色块压在网点纹纸上,右侧深色背景前悬着一枚带条纹的种子状物体

Anthropic 10 月 16 日发布研究文章,给一桩拖了二十多年的悬案结了案:NIST 五条素域椭圆曲线(P-192、P-224、P-256、P-384、P-521)的「种子」来源查清了,它们是 1997 至 1998 年间 C 库随机数生成器 lrand48 的输出,用当时的 Unix 时间戳初始化,与任何哈希算法无关。文章由 Anthropic 的 Frontier Red Team 署名发布,称完成这项追溯的是内部研究模型(页面里直接写作「Claude」),官方口径是「不到半小时」。以 P-256 为例:srand48(871696354)(1997-08-16 01:52:34 UTC)跑出的第 1842 个 20 字节候选,正是标准里那串 C49D3608 86E70493 6A6678E1 139D26B7 819F7E90。

要点

  • 悬赏与去向:2023 年 10 月 5 日,Filippo Valsorda 联合 11 位具名出资人挂出 12,288 美元(12 Ki$)的悬赏,承诺捐给 501(c)(3) 慈善机构则按三倍计;Anthropic 团队于 2026-10-15 01:07:51 UTC 认领,36,864 美元捐给运营 Let’s Encrypt 的 ISRG。
  • 官方定性:Anthropic 写明「这不是破解,不是新攻击,也不是 ECDSA 曲线的弱点」(原文 This is not a cryptographic break, new attack, or weakness in the ECDSA curves),并称结论让他们更有信心这些曲线当年没有被植入后门;用户无需做任何事。
  • 独立核验:悬赏组织者用 Go 重写了整套算法(放在 FiloSottile/mostly-harmless 仓库里)并复核通过,同时给出「即便任选 1997–98 年的时间戳、任选前 16 个素阶候选,也只有大约 30 位自由度」的论证,排除了 Bernstein–Lange 描述的 70 位自由度攻击路径。

背景

这段疑云从 1999 年就开始了。ANSI X9.62 与 2000 年的 FIPS 186-2 公布曲线时,种子由 NSA 提供、附在标准里却没有任何来历说明,Usenet 上几乎立刻有人发问——Steve Weis 2023 年那篇溯源文章记下了这条时间线。Koblitz 和 Menezes 的《A Riddle Wrapped in an Enigma》系统整理了这场争论,也给出了「即便 NSA 完全控制种子,也需要一类大到 25 年无人发现的弱曲线才可能利用」的反驳。

流传最广的解释来自 Jerry Solinas 本人。这位 NSA 长期雇员 2015 年说,种子是从一句幽默英文(「给 Bob 和 Jerry 加薪」之类)的哈希来的,代码由 Bob Reiter 执笔,细节两人都记不清了;他补了一句,当年的档案「多半躺在一块靠近约柜的硬盘上」。Solinas 2023 年去世,Anthropic 文章说,几位员工按「哈希了某句话」这条线索追过,全都落空。2023 年悬赏的动机写在 Filippo 的公告里:这些其实相当不错的曲线长期背着一层说不清的 FUD,而密码破解圈正好是找哈希原像的行家。

中文报道里,IT之家 10 月 17 日 06:53 的稿子把这桩悬案记作「困扰密码学界 26 年」,并单独解释了「种子」是什么:投入固定机器的原料,公开之后别人才能复算、检查参数确实按公布的步骤生成。这个口径与英文一手源一致,差别只在起算点。

事实部分

Anthropic 公布的结果是一张可以逐行核对的表(时间戳是 srand48 的输入,候选序号指该次运行里的第几个 20 字节块):

曲线srand48 时间戳(UTC)候选序号种子前 8 字节
P-1921997-05-18 14:45:341393045AE6F…
P-2241998-04-03 12:52:077026BD713447…
P-2561997-08-16 01:52:341842C49D3608…
P-3841997-08-30 14:50:082688A335926A…
P-5211997-08-08 01:12:041136D09E8800…

ANSI X9.62 附带的五条例子曲线也落在其中两次运行里:prime239v1 至 v3 来自 1997-05-15 那次,prime192v2、prime192v3 与 P-192 共用 1997-05-18 那次。

找到它的过程本身是一段推理。Anthropic 说,模型先做了一个逐位滑动的扫描器,覆盖 34 种「生成器 × 输出切片」组合;随后注意到一个高位规律:写成 5 个 32 位字之后,十条种子里有四条的最高位在全部 20 个字上都是 0。随机出现这种情形的概率是 1/5810,而「只返回 48 位状态里高 31 位」正是 lrand48 的签名。恢复状态的办法是先枚举 17 个隐藏位的 2¹⁷ 种取值;素域曲线保留的位更少,需要用 LLL 格基约减把状态解出来,模式是 lrand48() >> 23。再把状态倒推到 srand48 的初始化形态(低 16 位等于 0x330E),时间戳落点全在 1997 年 5 月到 1998 年 4 月之间。二进制曲线那几次初始化读出来是 1984 年 2 月,时间戳翻倍后对应 1998-04-17,官方推测当年的程序可能把时钟除以了二。1997 年 5 月生成的六条曲线(P-192 与五条 ANSI 例子曲线)的基点则来自 FreeLIP——Arjen Lenstra 的长整数运算包,早期 NTL 库带的是同一份代码——生成器种子分别是 20000、20420、20507、20509、20512、20517。

怎么复现

复现这段历史的成本,现在是几十行代码。Anthropic 在文章里附了 Python(页面注明以 CC0 放弃版权),Filippo 的 Go 版放在 FiloSottile/mostly-harmless 仓库的 seeds/seeds.go。我把前者的核心逻辑在这台机器上重跑了一遍,取 P-256 的时间戳:

x = (871696354 << 16) | 0x330E     # srand48(1997-08-16 01:52:34 UTC)
for i in range(20 * 1842):         # 每个候选 20 字节
    x = (0x5DEECE66D * x + 0xB) % 2**48
    if i >= 20 * 1841:
        print('%02X' % (x >> 40), end='')

输出 C49D360886E704936A6678E1139D26B7819F7E90,与 FIPS 186 里 P-256 的标准种子逐字节一致。换掉时间戳和候选序号,其余四条可以自己验;Go 那份还顺带算了曲线参数与阶,基点那一节只有 Anthropic 页面的 Python 代码,没有对应的 Go 版。

各方说法

Filippo 的复核文章给了这次发现最锋利的推论。他说,即便假设 NSA 当年可以任选时间戳、并从每条流的素阶候选里任选前 16 个(这已经宽松地覆盖了那些无法解释的跳过),自由度的上限也只有大约 30 位——不足以定向命中一类弱曲线,也不足以埋一个只有自己知道的 NOBUS 后门。被这套论证直接否掉的,是 Bernstein 和 Lange 2017 年向 Koblitz、Menezes 描述的那条路径:用 70 位自由度瞄准一类规模 2⁶¹ 的假想弱素阶曲线。该转述见于论文后版,Filippo 引用了它,我未读论文原文。他还补了一句个人结论:他会转而推荐 NIST 曲线,而不是 Curve25519/Ed25519/X25519——「赶在它们被后量子签名取代之前」;本站写过 Cloudflare 的公共 CA 计划,后量子证书正排在 2027 年一季度。

Anthropic 自己留了保留意见。他们观察到有可用的候选被跳过:P-256 同一轮里的第 876 个候选也能生成规格相同的素阶曲线,P-224 那次跳过了三个,两条 ANSI 例子曲线的运行又跳过了七个,判断标准至今不明。官方自评是「因为被跳过的候选数量很少,我们不认为选中弱曲线是可信的」;但他们也承认,原则上仍然存在「搜时间戳挑种子」的可能,只是那样做的前提是打算把时间戳公开当作佐证。

Hacker News 主帖(抓取于 2026-10-16 23:59 UTC,594 分、248 条评论)上最集中的反方意见来自用户 fidotron:这套结果确认了生成方式,却说明不了挑选过程,被丢弃的候选永远看不见。用户 adastra22 说得更重,认为这恰恰就是「被做了手脚」该有的样子:只要随机一千个或一百万个密钥里有一个能被某种未公开的攻击利用,换时间戳、换迭代次数就能把它试出来,事后还有一套「纯属巧合」的说辞。Filippo 本人在帖内做了回应,重复了 30 位自由度的论证。另一条相当热闹的子线与此无关但值得一记:用户 slowin 说 Anthropic 能拿模型做这类密码学工作,普通人做同样的工作会被拦下,护城河在变宽。反驳也具体:dadrian 说「这与本任务完全无关,你不会被拦」,Retr0id 说他自己用 Claude 做过这个题目,没被拦(虽然没做出来);与此同时 jcomp、petesergeant、landr0id 都举出了自己因包含「C&C」「Vigenère」「反向工程」等字样被降级的例子。

编辑判断

对写代码和做架构评审的人来说,这条新闻的用途很窄,但很硬:如果合规或设计文档里还写着「NIST 曲线种子来历不明」,现在可以换成一段任何人都能跑的十行代码。P-256 与 P-384 支撑着大量 X.509 证书,而这次结论不影响任何部署动作——Anthropic 明确说没有事情需要做。

诚实的一课在证明的边界上。结论证明的是生成方式,挑选过程(尤其是被丢弃的候选)依然不可见;它能让人放心,靠的是「弱曲线类必须大到能被 2³⁰ 次随机尝试撞上」这个反证。Filippo 自己的说法是,那样的弱曲线类一旦存在,等于宣告整个椭圆曲线密码学被攻破。这确实是个很强的假设,但它终究是假设。同样要写清的是没解完的部分:B-163 的种子至今对不上任何尝试,P-224、P-256、P-384、P-521 与二进制曲线的基点生成方式也仍未知。

最后,这还是一则关于「模型做研究」的样本。完成它的是内部模型,官方口径是不到半小时,而 HN 上围绕普通用户能不能得到同样的帮助吵了一整条子线。两边的说法我们都不站:这次任务的产物是可复现的代码,谁都可以自己验,这一点比「模型说它对」有力得多。这不是 Anthropic 第一次用「模型发现了什么」做发布,本站写过 950 个 Claude 智能体发现类 CRISPR 系统,那次学界的第一反应是要先补实验。