:NTT與Keccak硬件加速全記錄)
半年前有客戶問我公司手里那把用于長期數(shù)據(jù)加密的證書密鑰申請一次要用好幾年要不要現(xiàn)在就把算法換成后量子加密PQC我當(dāng)時沒敢拍胸脯。回來后我花了幾個星期把權(quán)威標(biāo)準(zhǔn)組織陸續(xù)公開的后量子密碼學(xué)標(biāo)準(zhǔn)化文件翻了個遍把候選算法的參考實現(xiàn)跑了個遍最后干脆拿了塊帶ARM硬核的FPGA開發(fā)板把其中最典型的一個密鑰封裝算法從數(shù)學(xué)公式一路寫到了RTL代碼完整復(fù)現(xiàn)了一遍。這篇文章就是這次調(diào)研與復(fù)現(xiàn)項目的全程記錄。文章覆蓋三塊內(nèi)容一是后量子密碼學(xué)目前的算法版圖和選型邏輯二是如何在FPGA上把以ML-KEM為代表的核心計算單元跑起來三是硬件實現(xiàn)過程中那些軟件工程師永遠(yuǎn)遇不到的坑。想入坑PQC、做密碼硬件加速、或者打算把設(shè)備密鑰體系升級到抗量子方案的讀者這篇文章可以直接當(dāng)參照系。1. 為什么是現(xiàn)在量子威脅倒計時與PQC調(diào)研起點1.1 Shor算法到底撕開了多大的口子要說PQC為什么突然從學(xué)術(shù)圈沖到工程圈繞不開Shor算法。1994年Shor提出用量子計算機(jī)在多項式時間內(nèi)分解大整數(shù)之后RSA這類基于大整數(shù)分解困難性的公鑰體系就在數(shù)學(xué)層面被判了死刑。橢圓曲線密碼也好不到哪去因為橢圓曲線離散對數(shù)問題同樣被Shor算法覆蓋。注意我說的是數(shù)學(xué)層面不是現(xiàn)實層面——目前沒人造出一臺能穩(wěn)定運行幾千個邏輯量子比特的機(jī)器所以在座的RSA-2048短期內(nèi)依然安全。但問題恰恰出在短期這個詞上。公鑰密碼體系有一個經(jīng)常被忽略的隱含假設(shè)秘密存續(xù)期。證書私鑰、長壽命密鑰、檔案加密密鑰很多數(shù)據(jù)需要保密十年、二十年甚至更長。如果今天有人把密文截獲等十多年后量子計算機(jī)成熟后再解密那今天部署的系統(tǒng)就等于白部署了。這就是業(yè)界常說的現(xiàn)在收割以后解密harvest now, decrypt later。對有長期保密需求的數(shù)據(jù)來說威脅不是未來的而是當(dāng)下的。調(diào)研時我反復(fù)跟團(tuán)隊強(qiáng)調(diào)別只盯著量子計算機(jī)哪年造出來要先看自己的數(shù)據(jù)哪年必須解密。1.2 標(biāo)準(zhǔn)化窗口已關(guān)閉工程遷移窗口才剛剛打開另一個更現(xiàn)實的推動力是標(biāo)準(zhǔn)化。權(quán)威標(biāo)準(zhǔn)組織在2024年正式發(fā)布了幾份后量子密碼標(biāo)準(zhǔn)FIPS 203ML-KEM密鑰封裝、FIPS 204ML-DSA數(shù)字簽名、FIPS 205SLH-DSA無狀態(tài)哈希簽名隨后又補(bǔ)了FN-DSA相關(guān)標(biāo)準(zhǔn)。標(biāo)準(zhǔn)的落地意味著算法設(shè)計競賽階段結(jié)束工程適配階段開始。操作系統(tǒng)、瀏覽器、加密庫已經(jīng)在逐步加入混合模式——也就是經(jīng)典算法和后量子算法同時使用、只有兩者一起被攻破才失效。這個趨勢非常明確越晚動手存量系統(tǒng)的遷移包袱就越重。所以我調(diào)研的第一個體會是PQC已經(jīng)不是要不要做的問題而是什么時候做、從哪開始做的問題。真正留給工程師的是遷移順序問題。先把哪些資產(chǎn)保護(hù)起來、哪些鏈路先換、怎么保證過渡期兼容這些問題比算法本身更影響落地節(jié)奏。1.3 調(diào)研范圍怎么收斂算法多但真正要復(fù)現(xiàn)的只有一個PQC候選算法從最早的幾十個收斂到現(xiàn)在的幾個標(biāo)準(zhǔn)背后的數(shù)學(xué)基礎(chǔ)分了好幾派格、編碼、多變量、哈希。最終進(jìn)入標(biāo)準(zhǔn)的主要是格基和哈希兩類。格基算法又分兩路做密鑰封裝的ML-KEM以及做簽名的ML-DSA和FN-DSA哈希簽名SLH-DSA走的是完全不同的保守路線。調(diào)研時不要平均用力。我把火力集中在ML-KEM上理由有三第一它是TLS混合模式里預(yù)期最先大規(guī)模部署的組件第二它的模塊格結(jié)構(gòu)在硬件上非常規(guī)整適合FPGA實現(xiàn)第三它涉及到的NTT數(shù)論變換和Keccak恰好是FPGA實現(xiàn)里最有含金量的兩個計算單元。把這兩個單元啃下來后面再看其他格基算法都會輕松很多。2. 算法版圖梳理密鑰封裝三兄弟與一套曲線救國組合2.1 ML-KEMKyber模塊格上的密鑰封裝ML-KEM的前身是Kyber屬于Module-LWE模塊格方案。看不懂?dāng)?shù)學(xué)沒關(guān)系先把骨架記住——它做的是一件事讓雙方在沒有預(yù)先共享秘密的情況下通過公鑰協(xié)商出一個只有雙方知道的會話密鑰。KEM是密鑰封裝機(jī)制的縮寫你可以把它理解成一次性密碼信封發(fā)送方拿公鑰封裝一個隨機(jī)生成的會話密鑰接收方用私鑰解封裝。它和傳統(tǒng)TLS里的協(xié)商不太一樣不需要來回多個握手輪邏輯更簡單。具體到參數(shù)多項式環(huán)是R_q Z_q[x]/(x^2561)這里q3329n256。每個多項式有256個系數(shù)每個系數(shù)是0到3328之間的整數(shù)。安全級別靠模塊維度k區(qū)分ML-KEM-512的k2、ML-KEM-768的k3、ML-KEM-1024的k4。密鑰生成過程可以簡單理解為生成一個隨機(jī)的小系數(shù)秘密向量s再從種子擴(kuò)展出一個公開矩陣A計算b A·s e其中e是精心設(shè)計的小噪聲。公鑰是(A, b)私鑰是s。攻擊者拿到(A, b)要恢復(fù)s等于在格上求解帶噪聲的線性方程組——這就是LWE問題的困難本質(zhì)。為什么帶噪聲的方程難解生活化類比你在一張表格里知道每一行是某些列值的線性組合但每個結(jié)果都被一個幾毛錢的小誤差污染了。小誤差單看無所謂但列數(shù)一多可能的組合數(shù)量爆炸每個組合都對應(yīng)一個幾乎相同的表格攻擊者無法分辨哪一個是真秘密。格密碼的安全性恰恰來自區(qū)分最接近的解的難度。2.2 簽名三件套ML-DSA、SLH-DSA、FN-DSA怎么選簽名方面ML-DSADilithium和ML-KEM同出一門都是模塊格性能均衡適合作為主力簽名。FN-DSAFalcon的優(yōu)勢是簽名尺寸小但實現(xiàn)復(fù)雜度高需要處理浮點數(shù)和NTRU格更適合對帶寬敏感的封閉系統(tǒng)。SLH-DSASPHINCS則徹底拋棄格安全性只依賴哈希函數(shù)的性質(zhì)屬于最保守的選擇代價是簽名和性能都不好看。工程上常用組合是正常流量用ML-DSA重要的長期資產(chǎn)用SLH-DSA做交叉?zhèn)浞?。簽名不同于密鑰封裝不是二選一的關(guān)系可以根據(jù)場景混用這也是PQC落地時比較靈活的一點。2.3 規(guī)格速查表與選擇邏輯算法標(biāo)準(zhǔn)化名稱數(shù)學(xué)基礎(chǔ)類型突出特點通常場景KyberML-KEM模塊格密鑰封裝密鑰密文小、性能好TLS、證書、加密會話DilithiumML-DSA模塊格數(shù)字簽名綜合性能均衡通用簽名、代碼簽名FalconFN-DSANTRU格數(shù)字簽名簽名尺寸最小帶寬受限場景SPHINCSSLH-DSA哈希函數(shù)數(shù)字簽名保守、無結(jié)構(gòu)長期資產(chǎn)、密鑰備份RSA/ECC—數(shù)論/橢圓曲線混合過渡已成熟與PQC混合過渡期這套表是調(diào)研后給團(tuán)隊做選型用的最終結(jié)論是如果只選一個算法開始工程適配選ML-KEM如果只選一個簽名選ML-DSASLH-DSA作為長期備份。這個結(jié)論直接指導(dǎo)了后面FPGA復(fù)現(xiàn)的選題——先啃ML-KEM這顆硬骨頭。3. FPGA復(fù)現(xiàn)選型為什么要用硬件把算法焊一遍3.1 三個說服自己的理由很多人一聽說調(diào)研PQC第一反應(yīng)是跑個Python腳本或者C參考實現(xiàn)就夠了為什么非得上FPGA我當(dāng)時的想法有三層。第一PQC真正的大規(guī)模應(yīng)用場景不一定是PC和服務(wù)器而是嵌入式設(shè)備、網(wǎng)關(guān)、工業(yè)控制器、車機(jī)這一類算力和內(nèi)存都緊張的地方。這些設(shè)備目前大量依賴專用密碼引擎算法遷移時必須在硬件上驗證不能只靠軟件模擬。第二軟件實現(xiàn)把算法關(guān)鍵性能藏在內(nèi)存帶寬和CPU指令流水線里FPGA實現(xiàn)能逼著你把存儲布局、乘法器數(shù)量、關(guān)鍵路徑一個個擺到桌面上來理解深度完全不一樣。第三FPGA能接入真實物理環(huán)境做功耗和側(cè)信道評估這是軟件庫給不了的。第三個理由在調(diào)研時顯得尤為重要。PQC算法雖然是新面孔但硬件實現(xiàn)中的側(cè)信道風(fēng)險一點沒少——功耗曲線、電磁輻射、指令時序都可能泄露私鑰信息。FPGA上可以做可控的電源噪聲注入和電磁探頭測量這套環(huán)境在普通軟件運行環(huán)境里根本搭不出來。3.2 硬件平臺與開發(fā)環(huán)境開發(fā)板選了一塊帶ARM硬核的中端FPGA板邏輯規(guī)模大概覆蓋幾萬LUT板載UART、若干開關(guān)和指示燈。ARM核負(fù)責(zé)接收上位機(jī)指令、通過UART把測試向量和種子灌進(jìn)來可編程邏輯部分只留一個簡潔的計算核。開發(fā)環(huán)境用的是主流FPGA廠商的綜合工具RTL語言用Verilog。為什么選這個組合因為調(diào)試DDR和復(fù)雜總線太費時間PQC核心計算單元只需要幾十KB存儲雙端口BRAM足夠ARM只是當(dāng)個高級串口控制器把我從繁瑣的PC通信中解放出來。這也是給沒接觸過FPGA的讀者一個建議不要迷信高端板卡先看算法需求再看接口需求最后看調(diào)試復(fù)雜度。ML-KEM這個量級的算法一塊中端板子完全夠用。3.3 實現(xiàn)范圍只做核心引擎不做完整產(chǎn)品項目沒有把完整的ML-KEM-768握手流程做成產(chǎn)品級引擎而是聚焦三件事第一跑通密鑰生成、封裝、解封裝三個主流程第二重點驗證模多項式乘法和Keccak這兩個熱點模塊第三對外開放狀態(tài)觀測接口方便調(diào)試和教學(xué)。這個邊界一開始就定好后面少走很多彎路。如果一開始就想做全流程旁路安全多通道并發(fā)大概率會因為調(diào)試復(fù)雜度失控而卡殼。先把邊界切小把核心模塊的質(zhì)量做扎實后面擴(kuò)展才有基礎(chǔ)。4. 系統(tǒng)頂層架構(gòu)從串口數(shù)據(jù)到三塊BRAM再到狀態(tài)機(jī)4.1 模塊劃分六個模塊各管一攤頂層框架按數(shù)據(jù)流劃分了六個模塊通信接口、頂層控制器、存儲管理器、NTT加速核、Keccak核、多項式運算單元。通信接口最簡單就是把ARM軟核發(fā)過來的數(shù)據(jù)按包格式拆開存成seed、公鑰、密文等測試輸入。頂層控制器是一個細(xì)粒度狀態(tài)機(jī)負(fù)責(zé)把密鑰生成、封裝、解封裝三大流程翻譯成一系列存儲讀寫和計算指令。存儲管理器不直接讓計算單元訪問物理RAM而是通過一組帶讀寫valid/ready信號的接口來管理這樣既方便仿真也方便后面換DDR或者加內(nèi)存隔離。Keccak核是PQC的配角但主角——ML-KEM的矩陣A生成、哈希函數(shù)、隨機(jī)數(shù)擴(kuò)展全都靠它。NTT加速核和多項式運算單元是真正的算力核心一次多項式乘法打包成正變換-點乘-逆變換三步。所有大數(shù)運算走到乘法單元后都統(tǒng)一經(jīng)過流水線蝶形算子。這個架構(gòu)幾乎沒有為特定算法做過度的定制后續(xù)換參數(shù)集或者改算法映射都比較容易。4.2 存儲布局12比特系數(shù)和雙端口帶寬存儲布局早期設(shè)計時吃過虧多講幾句。ML-KEM-768需要維護(hù)k×k矩陣k3的多項式也就是9個多項式每個多項式256個系數(shù)一個系數(shù)12比特。按單系數(shù)12比特算一個多項式需要384字節(jié)9個約3.5KB。板載36Kb約4.5KB的塊RAM單塊就能放下這些實際用了幾塊雙端口BRAM。為什么用雙端口因為蝶形運算每個周期需要同時讀兩個系數(shù)寫完結(jié)果還要讓另一個端口繼續(xù)輸出給下級處理單端口會卡死流水線。還有一個實用教訓(xùn)系數(shù)位寬不要圖省事按16比特存。12比特才是有效位寬按16比特存會白占三分之一帶寬。算帶寬時嚴(yán)格按12比特計BRAM的讀寫使能按字節(jié)拼接。前幾版為了省事統(tǒng)一按16比特結(jié)果內(nèi)存帶寬差了25%改回12比特后性能立竿見影。對FPGA來說BRAM帶寬往往比邏輯資源更緊缺位寬設(shè)計從一開始就要摳。4.3 主狀態(tài)機(jī)八個狀態(tài)走完一個流程主狀態(tài)機(jī)定義了八個狀態(tài)按順序走IDLE、LOAD_SEED、EXPAND_A、NTT_WS、POINTWISE、INTT_WT、PACK、DONE。每個狀態(tài)做的事很專一。比如EXPAND_A是從種子出發(fā)用Keccak生成矩陣A的系數(shù)NTT_WS是對秘密向量s做正變換POINTWISE則是在NTT域做逐項乘加INTT_WT做逆變換回到普通域。狀態(tài)之間用一組completion信號握手避免出現(xiàn)一拍沒對準(zhǔn)就全盤錯亂的情況。所有的中間系數(shù)都緩存在BRAM工作區(qū)不額外加通用寄存器堆——寄存器堆在這種密集數(shù)據(jù)流場景里是浪費。這里值得說明為什么用狀態(tài)機(jī)而不用微代碼或軟核狀態(tài)機(jī)每個跳轉(zhuǎn)都是確定性的時序容易收斂驗證簡單。對于ML-KEM這種流程固定的算法狀態(tài)機(jī)是最不容易出錯的實現(xiàn)方式。ARM軟核只負(fù)責(zé)通信和結(jié)果回傳計算流程不依賴它路徑很干凈。5. 核心計算單元解剖NTT蝶形、模約減與Keccak5.1 多項式乘法為什么是性能命門ML-KEM的安全性建立在格上工程性能卻幾乎全部押在多項式乘法上。一個多項式有256個系數(shù)直接做教科書卷積需要65536次乘加——看起來不算多但密鑰生成、封裝、解封裝要反復(fù)做很多次而且每次乘加之后還要做模3329約減。如果直接做光約減涉及的除法就要占掉大部分周期。NTT就是來解決這個問題的它把多項式轉(zhuǎn)到頻域兩個多項式乘法變成逐項點乘再做一次逆變換。復(fù)雜度從O(n2)降到O(n log n)對n256來說大概是8倍差距。更重要的是NTT之后所有系數(shù)固定在某個域內(nèi)模約減可以統(tǒng)一用一個乘法加一個移位替代除法硬件實現(xiàn)非常友好。Kyber的q3329專門選成了滿足q≡1 mod 256的素數(shù)這意味著x^2561在模q的多項式環(huán)上能完全分解可以直接做256點NTT而不需要額外補(bǔ)零。實際參考實現(xiàn)還把256點NTT拆成兩層用128個二次因子迭代完成硬件上表現(xiàn)為一組組小的蝶形級聯(lián)。這個性質(zhì)不是巧合是算法設(shè)計時為了讓硬件和軟件都能高效實現(xiàn)特意選的。復(fù)現(xiàn)PQC時多留意這類參數(shù)選擇會讓實現(xiàn)思路清晰很多。5.2 蝶形運算單元一乘兩加流水線要細(xì)摳NTT的核心算子是蝶形運算a a ζ·b (mod q) b a - ζ·b (mod q)其中ζ是預(yù)計算的旋轉(zhuǎn)因子。硬件上只需要一個乘法器加兩個加減法器就能實現(xiàn)一個蝶形。我最初的實現(xiàn)是每四個周期完成一組蝶形流水線分四段取系數(shù)、乘法、約減、加減。后來發(fā)現(xiàn)乘法器和約減器的組合邏輯深度是瓶頸改成六級流水后頻率從150MHz提到180MHz有效吞吐反而提升。代碼骨架大致如下module butterfly_unit #( parameter Q 3329 ) ( input wire clk, input wire valid_in, input wire [11:0] a_in, input wire [11:0] b_in, input wire [11:0] zeta_in, output reg valid_out, output reg [11:0] a_out, output reg [11:0] b_out ); // 流水線第一級乘法 reg [23:0] prod_reg; always (posedge clk) begin if (valid_in) prod_reg b_in * zeta_in; end // 第二級Barrett約減 wire [11:0] prod_mod barrett_reduce(prod_reg); // 第三級加減 always (posedge clk) begin if (valid_in) begin a_out (a_in prod_mod) % Q; b_out (a_in - prod_mod Q) % Q; end end // valid同步打拍 reg [2:0] valid_delay; always (posedge clk) valid_delay {valid_delay[1:0], valid_in}; assign valid_out valid_delay[2]; endmodule注意這個骨架為了表達(dá)清楚簡化了很多細(xì)節(jié)真實代碼里a_out/b_out必須與valid信號嚴(yán)格同步而且流水線級數(shù)變化時控制信號也要跟著變化。這就是第7章第一個坑的伏筆。5.3 Barrett約減用乘法器替代除法位寬是命門模約減在FPGA里的核心問題是如何用乘法器替代除法。我的方案是Barrett約減預(yù)計算常數(shù)c floor(2^16 / 3329)約減時先做一次乘法得到商的高位估計再做一次乘法和減法得到余數(shù)。誤差控制在很小的范圍內(nèi)只要輸入范圍不超過某個上限最后一步只需一次條件減法就能把結(jié)果拉回[0, 3328]。這個方案比Montgomery舒服的地方在于不需要把模數(shù)轉(zhuǎn)成特殊形式也不需要額外的轉(zhuǎn)換因子系數(shù)在每個階段都可以直接解釋為標(biāo)準(zhǔn)值。但位寬必須小心。Barrett約減要求乘法器輸出位寬足夠容納高位估計我最初用的普通乘法器截到高16位結(jié)果在邊界情況偶爾丟精度。后來改用完整位寬乘法器高位估計單獨接出來問題立刻消失。具體計算公式是t a - floor(a·c / 2^16)·q。這個floor操作在硬件里的等價實現(xiàn)是先做a×c乘法取結(jié)果的高16位作為商估計再乘以q做減法。因為c≈2^16/q商估計會有約1的誤差最后補(bǔ)一次條件減法。理解這個誤差邊界對位寬設(shè)計非常重要。5.4 Keccak核24輪輪函數(shù)單輪展開最劃算Keccak是SHA-3系列的核心置換一個f[1600]置換包括24輪每輪要執(zhí)行θ、ρ、π、χ、ι五個步驟處理1600比特的狀態(tài)。ML-KEM里Keccak承擔(dān)的角色很重種子擴(kuò)展為矩陣A、哈希、XOF都靠它。如果Keccak核太慢整個密鑰生成和封裝流程都會拖住。我選擇了單輪展開結(jié)構(gòu)把一輪的θ/ρ/π/χ/ι組合邏輯寫出來狀態(tài)寄存器在每個周期載入一輪結(jié)果重復(fù)24次完成一次置換。全展開24輪全部展開能省循環(huán)開銷但LUT消耗大好幾倍頻率也難上去不劃算。單輪展開配合狀態(tài)旁路Keccak核跑一次SHAKE256大約1.3μs對整體流程來說夠用。Keccak的旋轉(zhuǎn)偏移表和輪常數(shù)是固定查表這個坑我在第7章詳述。這里只提醒一句直接從規(guī)范附件復(fù)制不要憑記憶。6. 綜合結(jié)果與實測數(shù)據(jù)面積換吞吐的賬6.1 資源和時序報告綜合后的關(guān)鍵數(shù)字如下基于我這版只實現(xiàn)了單路徑核心的配置資源項數(shù)量說明LUT約12k主要是Keccak和NTT邏輯FF約8.7k流水線寄存器和狀態(tài)寄存器DSP6蝶形乘法器含Barrett相關(guān)BRAM6塊36Kb系數(shù)存儲與中間工作區(qū)最大頻率180MHz關(guān)鍵路徑在Keccak輪函數(shù)這是單套NTT核、單套Keccak核、主頻180MHz的結(jié)果。資源不算緊張但也不能算寬裕因為Keccak的24輪組合邏輯確實吃資源。如果把Keccak和NTT都做成雙實例并行面積大概翻1.6倍而不是2倍——控制邏輯可以共用這是并行化時的一個隱藏紅利。6.2 關(guān)鍵流程耗時拆解用板級測試跑完整ML-KEM-768三流程實測耗時如下從ARM收到命令到打印結(jié)果的時間UART開銷約2μs可忽略流程實測耗時密鑰生成KeyGen42μs封裝Encaps64μs解封裝Decaps85μs拆下來看密鑰生成里矩陣A的Keccak擴(kuò)展約占三分之一NTT相關(guān)計算占二分之一其余是打包和隨機(jī)數(shù)處理。封裝和解封裝因為要處理更多參數(shù)Keccak調(diào)用次數(shù)更多時間更長。6.3 與軟件實現(xiàn)的對照FPGA并不更慢對比軟件參考實現(xiàn)普通PC上優(yōu)化的C版本大致在幾十微秒到一百多微秒之間FPGA這組數(shù)據(jù)放到資源受限的嵌入式場景里是很有競爭力的。更重要的是這個性能是可預(yù)測的、確定性的沒有操作系統(tǒng)調(diào)度干擾。對工業(yè)控制、車載、網(wǎng)關(guān)這類實時性敏感的場合確定性比峰值性能更值錢。功耗方面我實測的核心邏輯功耗在幾百毫瓦量級遠(yuǎn)低于常見CPU跑軟件的功耗。雖然這次沒有做精細(xì)的DVFS調(diào)優(yōu)但已經(jīng)能看出FPGA在PQC場景下的功耗優(yōu)勢。對電池供電的物聯(lián)網(wǎng)設(shè)備來說這個優(yōu)勢往往比絕對性能更關(guān)鍵。7. 踩坑實錄流水線錯位、約減溢出與上板崩7.1 蝶形流水線的valid延遲必須同步第一個坑出現(xiàn)在NTT核聯(lián)調(diào)的時候。單獨測蝶形單元輸入輸出都正確單獨測NTT的地址生成訪存地址也對可一旦把兩級連起來輸出結(jié)果就完全對不上。查了兩天最后把波形拉出來逐周期看才發(fā)現(xiàn)問題蝶形單元里加了流水線寄存器a_out和b_out比a_in和b_in晚了幾拍才出來但地址生成模塊還按下一拍讀下一組系數(shù)在操作讀到的數(shù)據(jù)完全錯位。這是典型的valid延遲不同步問題數(shù)據(jù)通路的流水級數(shù)變了控制通路沒有跟著變。解決方式很樸素但通用給控制信號手動打同樣拍數(shù)的延遲保證valid和data在時間上嚴(yán)格對齊。更穩(wěn)的做法是讓地址生成模塊把數(shù)據(jù)請求做成請求-應(yīng)答式的握手而不是固定時序的發(fā)起-等待這樣流水線任何一級改變深度都不會影響正確性。經(jīng)過這次之后我在項目里所有數(shù)據(jù)通路都強(qiáng)制使用握手信號再沒犯這類錯。7.2 Barrett約減位寬溢出單測過不了邊界第二個坑是約減偶爾飄。表現(xiàn)為單測幾百組都沒問題跑長流程密鑰生成加封裝加解封裝會在隨機(jī)位置冒出個別系數(shù)大于3329。一開始以為是存取的隨機(jī)性問題后來把出問題的系數(shù)單獨拎出來復(fù)算發(fā)現(xiàn)每次都是同一個中間多項式里的同一類位置。對照Barrett約減公式細(xì)看才明白常量c約等于19.7做高位估計時如果乘法結(jié)果截斷太多部分輸入的商估計偏小最后的條件減法來不及修正。解決方法是把乘法器輸出完整保留擴(kuò)大乘法位寬而不是裁到高16位或者把條件減法擴(kuò)展為循環(huán)減法。我選了更直接的方案乘法器輸出完整保留高位估計單獨拉出來極大減小截斷誤差。改完之后連續(xù)跑了一整夜隨機(jī)測試向量零異常。這個坑給的經(jīng)驗是模約減這類模塊單測樣例再多也頂不上隨機(jī)長流程但反過來調(diào)試時又要先構(gòu)造特定邊界值來壓測把最壞情況暴露出來再跑長流程驗證。兩個方向都缺一不可。7.3 Keccak旋轉(zhuǎn)常量記錯半個比特中間狀態(tài)就全亂第三個坑有點丟人Keccak的旋轉(zhuǎn)偏移表我一開始按記憶寫結(jié)果反了。Keccak規(guī)范里旋轉(zhuǎn)量表的某一行我記錯了兩個數(shù)單輪置換測不出來因為很多狀態(tài)值對錯誤旋轉(zhuǎn)不敏感但在跑ML-KEM的向量比對時擴(kuò)展出來的矩陣A系數(shù)偶爾對不上。最后是用離線參考實現(xiàn)對中間狀態(tài)逐一比對定位到某一輪的輸出差異才把表改對。教訓(xùn)Keccak的輪常量和旋轉(zhuǎn)表是公認(rèn)的又長又容易抄錯務(wù)必直接從規(guī)范文檔復(fù)制不要手輸更不要憑記憶。硬件驗證里小錯不爆發(fā)、爆發(fā)難定位是最貴的能用離線腳本一個周期跑完的檢查不要留給板級調(diào)試。7.4 仿真全過一上板就隨機(jī)出錯第四個坑集中在通信和時鐘域。RTL仿真里UART、頂層控制器、Keccak全連起來能一次跑通但板子上一跑就隨機(jī)丟字節(jié)、偶發(fā)握手超時。排查過程先懷疑UART波特率微小偏差用邏輯分析儀抓了波形發(fā)現(xiàn)是ARM發(fā)出的讀請求在可編程邏輯端偶爾收不到——原因是異步時鐘域間的握手信號沒有同步。FPGA里兩個時鐘域的信號直接采大概率會采到亞穩(wěn)態(tài)。解決分兩步第一給所有跨時鐘信號加兩級同步器第二在時序約束里顯式聲明異步時鐘組把跨時鐘路徑設(shè)為false path不然工具亂報時序綜合結(jié)果也會偏。改完再上板跑連續(xù)十二小時沒有一次錯誤。仿真過不等于上板過這個道理做FPGA的都知道但每次踩到都還是疼??鐣r鐘域問題尤其隱蔽因為它在仿真里往往不出現(xiàn)只有真實硅片上才露出獠牙。8. 調(diào)研結(jié)論與后續(xù)擴(kuò)展8.1 PQC落地比想象的快但遷移順序很重要調(diào)研下來我最大的感受是PQC落地的真實阻力已經(jīng)不是算法成熟度而是存量系統(tǒng)的遷移成本?;旌夏J浇?jīng)典算法與PQC同時啟用是過渡期的必然選擇。TLS場景預(yù)計會先動然后是證書體系、代碼簽名、固件升級鏈路。對普通企業(yè)來說先把密鑰生命周期管理摸清楚把哪些資產(chǎn)需要長期保密梳理出來這個優(yōu)先級比糾結(jié)選Kyber還是Dilithium更現(xiàn)實。算法本身方面ML-KEM的優(yōu)勢非常突出它和現(xiàn)有密碼庫的接口兼容性也做得很好工程適配成本比想象中低。簽名方面ML-DSA是均衡之選SLH-DSA則適合作為長期資產(chǎn)的保守備份。這輪調(diào)研讓我對什么時候換、先換什么有了清晰答案。8.2 如果繼續(xù)做我會碰這幾個方向FPGA這個項目還可以往幾個方向走一是把單一NTT核改成多實例并行用面積換吞吐適合網(wǎng)關(guān)等高并發(fā)場景二是把核心計算單元加上側(cè)信道防護(hù)比如在NTT蝶形上做掩碼處理用FPGA驗證防護(hù)對性能的影響三是把經(jīng)典算法和PQC做成同一個硬核里的混合模式加速單元這樣設(shè)備升級時不用換芯片只換邏輯。我個人接下來最想做的是第三項因為它更接近真實產(chǎn)品的形態(tài)?;旌夏J降挠布铀倨饕坏┏墒煸O(shè)備廠商只需要更新比特流不需要改動電路板這對存量設(shè)備的平滑遷移價值很大。最后分享一點體會。PQC的數(shù)學(xué)門檻不高難的是把一個看起來只是換算法的工程問題拆成算法選型、硬件映射、驗證三條線并行推進(jìn)。FPGA復(fù)現(xiàn)的過程逼著我從數(shù)學(xué)定義一路走到電路時序這種全局視角是只看文檔學(xué)不來的。如果你也想做類似的事我建議從ML-KEM起步先跑通參考實現(xiàn)再動手寫NTT核不要一上來就追求完整產(chǎn)品。先讓核心計算單元穩(wěn)定跑起來再談優(yōu)化和擴(kuò)展這條路走下來最順。