2026 年 10 月 15 日 UTC 凌晨 01 時 07 分 51 秒,有人領走了一筆懸了三年的賞金:NIST 橢圓曲線的種子,並不是某句英文的雜湊,而是 POSIX 的 drand48 亂數產生器,從 1997 到 1998 年間的某個時間戳吐出來的輸出。
NIST 曲線種子之謎收場
這套曲線的參數號稱可以從種子重算驗證,種子的來歷卻是個懸案:本該是一句英文的雜湊,那句英文已經遺失,當事人事後回憶的說法又證實是錯的。這個缺口留了二十多年,Filippo Valsorda 在 2023 年為它開出賞金,2026 年 10 月答案出現,和長年流傳的說法完全不同。
一串沒有人說得出來歷的 160 位元
NIST 的質數橢圓曲線 P-192、P-224、P-256、P-384、P-521,在 2000 年隨 FIPS 186-2 公開。三年前的賞金公告引述的說法是,這些曲線依 ANSI X9.62 產生,屬於「verifiably at random」。曲線的參數由一個種子經過公開的程序算出來,任何人都能照程序重算,確認參數出自這個種子。這個設計的用意,是讓設計者沒辦法為了私下的目的去調整曲線;這是我對機制的理解。
可驗證的只到種子為止。種子本身從哪裡來,程序沒有管。賞金公告列出了五個種子的十六進位值,每個都是 SHA-1 的輸出,P-256 的是 C49D360886E704936A6678E1139D26B7819F7E90。照 ANSI X9.62 的做法,這些種子本來該是對某個任意輸入做 SHA-1 的結果,那個輸入就是賞金要找的 pre-seed,一句英文。公告說的歷史是:種子由 NSA 提供,Jerry Solinas 在 1997 年生成,而原本的那些句子已經遺失。
於是出現一個結構上的缺口。曲線標榜可以驗證,驗證鏈的最上游卻是一個沒有人能出示的輸入。Anthropic 的文章補了另一段:2015 年 Koblitz 與 Menezes 發表〈A Riddle Wrapped in an Enigma〉時,Solinas 回憶的說法是,種子是一句幽默口令的雜湊。這句話後來證實是錯的。賞金公告與 Filippo 的文章都連到那篇論文,標題本身就說明,這個疑問早有人認真提過,也有人試著向當事人問出答案。
時間在這裡成了壓力。種子產生於 1997 到 1998 年,Solinas 在 2023 年過世,其他同事沒有回應詢問,這是 Anthropic 文章的交代。從 1997 年算到 2023 年,隔了約 26 年;能直接說出那句英文來歷的人,在賞金公告的同一年離開了。文章沒有給出過世的月份,所以上面的時間軸只把他標在年份上,與 10 月 5 日的公告誰先誰後並不清楚。
疑慮的形狀決定了後面的結果「解開」了什麼。疑慮是:不知道種子的來歷,就沒辦法排除有人在幾乎無限的可能種子裡挑了一個,讓曲線帶著只有挑選者知道的性質。文章的說法比較克制:Filippo 把這個謎團當作待解的歷史問題,沒有宣稱這些曲線有已知的弱點。
三年前的賞金:五個種子,一個條件
賞金公告的日期是 2023 年 10 月 5 日。Filippo 在文章裡把它稱為「the full bounty I announced three years ago」。公告頁面列的金額是 12,288 美元,寫成 12 Ki$,並註明是從 8,192 美元提高上來的。出資者除了 Filippo 本人,還有十一位具名的人,包括 Matt Green、Ryan Sleevi、Soatok 等;Filippo 個人保證全額兌付。
規則簡單。任務是找出五條質數曲線種子對應的英文 pre-seed。一半,也就是 6,144 美元,給第一位交出至少一個 pre-seed 的人;另一半給第一位交出全部五個的人。如果得主把獎金捐給美國的 501(c)(3) 慈善機構,金額會變成三倍,也就是 36,864 美元。這個數字等於 12,288 乘以三。公告還寫了一句對解法完全不設限的話:發現的方法不重要。
這個分成兩半的設計,讓只找出部分答案的人也能拿到一半獎金;這是我從條款結構做的讀法,公告沒有說明用意。條款要的是英文 pre-seed,答案卻是沒有 pre-seed。Filippo 的文章仍寫「claimed the full bounty」,全額付出,兩份材料沒有說明這個落差怎麼處理。
此後到 2026 年 10 月,賞金懸了三年。文章沒有交代這段時間有哪些人試過、用什麼方法試過,所以我也不寫。可以確定的只有兩端:2023 年 10 月 5 日公告,2026 年 10 月 15 日 UTC 01 時 07 分 51 秒有人領走。這個時間戳是文章給的,精確到秒。
不帶提示,不到半小時
解法來自 Anthropic。Filippo 的文章用了「solved the riddle」連到 Anthropic 的研究頁面,並說領獎的是 Anthropic 的團隊。他們選擇把獎金捐給 ISRG,也就是營運 Let's Encrypt 的非營利組織,Filippo 的文字是「tripling its value to $36,864」,三倍之後是 36,864 美元,他也寫成 36 Ki$。
Anthropic 的文章交代了過程。2026 年 10 月,一位員工請內部的研究模型在不給提示的情況下,推導這些種子是怎麼產生的。這裡的「提示」指的就是 Solinas 那個「幽默口令的雜湊」的說法。按他們的說法,模型在不到半小時內找到了出處。這是 Anthropic 自己的敘述,沒有第二個來源可以交叉比對,下面的細節也都出自這篇文章。
推理的起點是一個猜測:種子可能來自 1990 年代 C 函式庫附帶的弱亂數產生器,因為這類產生器的輸出有辦法被辨認出來。於是模型逐位元掃描每個種子,找連續輸出像是這類產生器的跡象,涵蓋 34 種「產生器與切片方式」的組合。突破口是一個統計上的異常:把種子寫成五個 32 位元字,十個種子裡有四個,其 20 個字的最高位元全是 0。他們給的機率是約 1/5,810,推導這裡不重現。
這個樣態對上了 lrand48。lrand48 只回傳 48 位元內部狀態的高 31 位元,所以輸出的最高位元永遠是 0。48 位元的狀態只露出 31 位元,對這四個種子,隱藏的位元就剩 17 個(48 減 31),窮舉 2 的 17 次方,也就是 131,072 種可能,再檢查後續的字是否吻合,就能還原內部狀態。對其餘的質數曲線種子,每次輸出保留的位元更少,他們說它們取的是 lrand48 輸出右移 23 位的結果。這時窮舉不夠用,模型改用 LLL 格基約化還原狀態,由於不知道狀態的哪些位元會出現在種子裡,求解器對每個視窗都跑了一遍。最後把還原的狀態倒推回 srand48 會產生的樣子,其中低 16 位元是 0x330E。
到這一步,種子的來源有了具體的時間。五條質數曲線的 srand48 輸入都是時間戳,全部落在 1997 年 5 月到 1998 年 4 月之間。最早的是 P-192,1997 年 5 月 18 日;最晚的是 P-224,1998 年 4 月 3 日,兩者相隔不到 11 個月;中間依序是 P-521(8 月 8 日)、P-256(8 月 16 日)、P-384(8 月 30 日)。下面的長條圖畫的是該文章列出的括號數字,也就是每條曲線採用的候選編號。
依日期排序,採用的候選編號也依序變大:139、1,136、1,842、2,688、7,026,最晚的 P-224 約是最早的 P-192 的 50 倍(7,026 除以 139 約 50.5)。文章沒有說這個順序有沒有因果關係,這裡只列出觀察。
驗證的部分,Anthropic 講得很明確。每個還原出的 48 位元狀態,都能重新產生對應種子的全部 160 位元,作者認為這不可能是巧合。他們公開了重現程式碼,輸出和已公布的種子一致。Filippo 的文章則寫道,他自己用 Go 重新實作了這個演算法,目的是驗證這個說法,程式碼放在 GitHub 的 mostly-harmless 倉庫(seeds.go)。他的文章建立在 Anthropic 團隊的成果上,這個實作因此不算獨立的第二方佐證。
文章對這個結果的整體說法是:「we now know they were instead generated with the POSIX-standard drand48 random number generator」。這句話裡的「instead」承接的就是長年的口耳相傳,種子是某句話的雜湊。結論是它們不是。
時間戳怎麼變成曲線
知道來源之後,剩下的是機制。解題的方向是從種子倒推到時間戳,種子的產生方向則相反。根據兩篇文章的描述,正向的流程是這樣:拿一個 Unix 時間戳去給 drand48 當起點,吐出 20 位元組的候選種子,再把候選種子送進 ANSI X9.62 的流程;只有能產出有效曲線的候選才會被留下。有些時間戳不只產出一條曲線,因為程序在第一個被接受的種子之後可以繼續往下走。下面的圖把這條路徑畫出來,注意「否」的那條回頭箭頭:它說明為什麼每條曲線都附著一個候選編號。
兩條「未文件化條件」是這個流程裡最有意思的部分。Filippo 的原話是:「There are two additional undocumented criteria beyond the ANSI X9.62 ones, one fairly clear and one that still escapes us」。按 Filippo 的描述,比較清楚的一條是:曲線的階 n 的位元長度超過基礎域 p 的位元長度時,種子會被拒絕;另一條至今沒有人弄懂。換句話說,重算出已公布種子的程序,比標準裡寫的多了兩道篩選,而其中一道,作者群還說不出它在篩什麼。
不明白的那道篩選,直接造成上圖的那個奇怪現象:P-256 採用了候選 1,842,但候選 876 也能得到有效曲線。如果有人要質疑「為什麼偏偏是這個」,876 就是他手邊最具體的證據。Anthropic 的文章直接承認不知道原因。這個細節的分量在於:已公布的種子並不是時間戳之後第一個能用的候選,所以選取的規則至少有一部分還不在已知的條件裡。這是我從 876 與 1,842 並存所做的讀法,文章只陳述了兩個編號。
二元域曲線(B 曲線)走了不同的路。Anthropic 的文章說,B 曲線的時間戳直接讀起來是 1984 年 2 月;把數字加倍,得到的是 1998 年 4 月 17 日,比 P-224 的時間戳晚兩個星期。作者因此提出一個假說:產生種子的程式可能把時鐘值減半了。他們明確標示這是假說。依他們的整理,B-233、B-283、B-409、B-571 都是在 1998 年 4 月 17 日前後產生的。Filippo 的文章補了一筆,他們也找出了這些 B 曲線種子的產生方式,但這些曲線很少被使用。B-163 的種子則和所有試過的方法都對不上,仍是未解。
還有基點的問題。曲線除了種子,還有一個基點。該文章寫道,P-192,以及五條 ANSI X9.62 範例曲線(prime192v2、prime192v3、prime239v1 到 v3),它們的基點來自 FreeLIP 這個函式庫的亂數產生器,產生器種子分別是 20000、20420、20507、20509、20512、20517,程式碼能重現六組已公布的 Gx。其他曲線,包括 P-224、P-256、P-384、P-521 與 NIST 的二元曲線,基點怎麼來的,仍未解釋。解開的只有種子,曲線的每一個參數並未全部解釋。
結案之後:能證明的,與不能證明的
Filippo 對安全面的說法是:「even before this discovery there were no serious concerns among practitioners」,也就是從業者之間早就沒有嚴重的疑慮。他算的自由度是這樣:假設 NSA 可以任選 1997 到 1998 年間的任何時間戳,以及前 16 個質數階候選中的任何一個(他說這個假設已經寬鬆地涵蓋了無法解釋的拒絕),結果是「they would have had barely 30 bits of freedom」。對照之下,Bernstein 與 Lange 在 2017 年向 Koblitz 與 Menezes 描述的技術,是「a technique that could have exploited 70 bits of freedom in selection of the seed」。這個 30 位元是在寬鬆假設下的上限,我的讀法是「they」指挑選種子的那一方。
為什麼位元數重要,這是一般性的推理,不屬於任何來源。想讓曲線帶有某種稀有的特殊性質,挑選者要靠嘗試很多個種子,直到撞上一個。可嘗試的種子數是 2 的 n 次方,n 就是自由度的位元數。30 位元代表大約十億種選擇,70 位元代表大約 1.2 乘以 10 的 21 次方種。兩者相差 2 的 40 次方倍。下面的滑桿讓你自己拖到不同的位元數,看選擇空間怎麼爆炸。
拖曳滑桿,看 n 位元自由度對應多少種可嘗試的種子 · 0 到 80 位元
30 位元 = 2^30 = 1,073,741,824 種組合。70 位元 = 2^70 = 1,180,591,620,717,411,303,424 種,是 30 位元的 2^40 = 1,099,511,627,776 倍。
預設停在 30 位元,約十億種選擇。這個量級的直觀含義是:十億種嘗試對現代電腦不算大,但要在十億個種子裡找到某種「十億分之一」等級稀有的性質,已經是碰運氣的邊緣,更別說比這更稀有的性質。拖到 70 位元,空間就從十億個擴大到十的二十一次方個,這是 Bernstein 與 Lange 那種技術假設的規模。我的推論是:若自由度只有約 30 位元,那個需要 70 位元的技術就用不上。
Anthropic 的文章給了同方向、語氣更保守的說法。作者表示,這不是密碼學上的破解,也不是發現了弱點;他們寫的是「This is not a cryptographic break, new attack, or weakness in the ECDSA curves」,並把種子描述為以簡單、可重現的程序產生。他們也寫了「In fact, this makes us more confident the curves were chosen without any backdoors or intentional weaknesses」,並說這些發現讓後門的說法「much less plausible」。這是 Anthropic 自己的判斷,強度低於證明,下表照這個強度標示。
| 說法 | 現在的狀態 | 依據(誰說的) |
|---|---|---|
| 種子是某句英文的雜湊 | 被推翻 | Filippo:它們是用 POSIX 的 drand48 產生的;Anthropic:Solinas 回憶的說法錯了 |
| 種子的產生程序可以被重現 | 已重現 | Anthropic 公開的重現程式碼輸出與已公布種子一致;Filippo 用 Go 重新實作以驗證,但他的文章建立在 Anthropic 的成果上,不算獨立佐證 |
| 曲線藏有後門 | 未被證明不存在,但可能性下降 | Anthropic:不是 break 或 weakness,程序簡單可重現(Anthropic:much less plausible);Filippo:實際自由度約 30 位元 |
| 曲線在密碼學上有弱點 | 兩份來源都未報告弱點 | Anthropic:不是 break 或 weakness;Filippo:此前沒有嚴重疑慮 |
| 所有生成細節都已解釋 | 沒有 | Filippo:一條未文件化條件仍不明;Anthropic:多數基點、B-163 種子、被略過的候選都未解 |
| 為什麼選這幾個時間戳 | 兩份來源都未交代 | 我讀到的材料沒有討論;不推測 |
表裡第一列推翻的是 Solinas 的回憶,與 NIST 或 NSA 的任何正式聲明無關;賞金公告只說原句已經遺失。第三列的「可能性下降」複述 Anthropic 的措辭,強度不能再往上調。以上只依據 Filippo 的文章、他連到的賞金公告與 Anthropic 的研究頁面;他文中連到的 Steve Weis 文章、2015 年的論文與 Go 程式碼沒有打開。
把這些收攏,有三句話可以直接說。種子是 drand48 加上 1997 到 1998 年間的時間戳,Anthropic 的重現程式碼對得上已公布的種子;若有人想在種子上動手腳,Filippo 在寬鬆假設下算出的自由度約 30 位元,遠小於 Bernstein 與 Lange 的技術所需的 70 位元;仍待解釋的,包括那條沒人讀懂的篩選條件、像候選 876 這樣有效卻沒採用的種子、其餘曲線的基點、B-163 的種子,以及為什麼選了這些時間戳。
If asked:有人問 P-256 或 NIST 曲線能不能繼續用,我的讀法是照上面三句回答,不要多說也不要少說:不要說這證明整條曲線沒有後門,也不要說已經沒有任何未解之處。Filippo 在文章裡寫道,這「nicely completes the 『rehabilitation arc』 of NIST elliptic curve cryptography」,並說他現在會個人推薦 NIST 曲線勝過 Curve25519、Ed25519、X25519,這是他在 2026 年 10 月的個人判斷。兩份材料都沒有建議因此遷移,這是我讀這兩份材料的結果,不是他們的明文結論。