HARDWARE SECURITY

RTL Anti-Tampering Design|RTL 防竄改設計第 2 / 16 課

RTL Anti-Tampering Design 第二課:Fault model 怎麼寫,才知道測到了什麼?

從同一個取指放行電路,比較暫時的 Q 端擾動、持續的儲存狀態翻轉與上游判斷錯誤。用電路圖、RTL 與 42 個可重跑案例,把故障位置、時機、預算、可信邊界及觀察期限寫清楚。

16 分鐘

驗證工程師跑了一次失敗簽章的測試。他在結果暫存器附近翻轉一個 bit,沒有看到 CPU 取指,便記下「故障已被擋住」。設計工程師卻在波形上看到另一件事:擾動在取指之前就結束了。這次是防護生效,還是故障根本沒碰上請求?

若改動的是暫存器保存的狀態,錯誤可能一直留到請求到來。兩個測試都叫 bit flip,結果卻不同。差別不在名字,而在改了哪個值、何時改,以及它怎麼恢復。

本課把這些條件寫成可重跑的實驗契約。讀者不只要看懂注入器,也要能判斷一條沒有越權的 trace 究竟支持什麼結論。模型寫得清楚,別人才有辦法重跑並挑戰你的結果。

先讀過第一課:功能正確,為什麼仍不安全?會比較容易銜接。本課約 35~45 分鐘,延續同步暫存器與 valid/ready 介面。電路、注入器及 Python 練習都是原創的兩態教學模型;已執行的證據是 Python 枚舉與網站驗收,尚未執行 RTL 模擬、綜合、形式證明或矽上故障測試。

1. 都叫 bit flip,為什麼結果不同?

把整課想成學校門口的『作業檢查章』。老師規定,沒有『檢查完成』和『作業及格』兩項紀錄,就不能進教室拿今天的考卷。這份作業答錯了,公正老師的原判斷始終是不及格。第 0 節重新開始,第 2 節檢查完,第 4、6 節各到門口一次。以下『第 N 節』只對應 edge N 的離散記號,不是把實際一節課的時間當成一個時脈。

先問三句:改的是哪張紙、改了多久、門口那次有沒有看到?A 在第 3 節用手指遮住保存的『不及格』,只讓門口暫時看成及格;拿開後,紀錄仍不及格,第 4 節已看不到假象。B 在第 3 節真的把保存章改成及格,沒有改回;第 4、6 節都會放行,一次動手有兩次後果。C 在第 2 節老師交出結果、學校登記的那刻,把傳遞的判斷換成及格,錯答案就被保存;第 3 節才換,登記已關閉,沒有影響。D 不改成績紀錄,只在第 4 節把門口的放行判斷改成『准入』。D 另開了最終 grant 的故障邊界,不能混入前三種實驗。

故事裡老師的原判斷與保存的成績必須分清楚。『公正老師』表示這份作業原本不及格;C 改的是傳給暫存器的訊號,不是同時改老師的獨立答案。D 改的是最後許可,請求與接受機制仍照規則工作。

沿用第一課的虛構開機流程。checked_q 記錄檢查已完成,result_q 保存是否通過;放行條件是兩者都為 1。CPU 讀取程式指令稱為「取指」,本課只追蹤簡化介面真正接受取指的事件 accepted_commit,不把它當作完整 CPU 的退休或執行事件。

這次把時間固定下來:edge 0 是 reset,edge 2 完成驗證,edge 4 與 edge 6 各有一次有效且 ready 的取指請求。映像的簽章失敗,所以無故障時 result_q 一直是 0。

若只在 edge 3 暫時把「讀到的 Q」反相,edge 4 之前就取消擾動,下游看不到錯誤。如果在 edge 3 更新結束後改寫「保存的 Q」,而且後面沒有覆寫,錯誤便會留到 edge 4。差別出在持續時間與保存位置,不是 checker 忽然改變了答案。

先預測下游會讀到什麼,再看程式。你需要同時知道請求時刻與錯誤保留方式。只有「翻轉發生過」這個資訊,還不足以判斷接受點會不會看到錯誤。

上游判斷、結果暫存器、Q 端觀察與最終 grant 是四個不同位置;可信取指接受點位於放行邏輯之後。
圖 1:先把要保護的事件和故障位置畫出來。位置編號代表不同實驗,不表示一次同時注入四處;點圖可放大。

2. 寫一張能交給驗證團隊的模型卡

把校規寫成可以交給另一位老師的模型卡。答錯作業不能拿考卷;從第 0 節到第 7 節,至多動手一次、選一個位置、改一個目標章。第 4、6 節共用這次機會。先信任作業題目與原始評分,也信任鐘、完成訊號、非目標欄位與接受機制;這些是分析前提,沒有說它們打不到。

學校故事的角色或紀錄硬體對照這次要分清楚什麼
固定而答錯的作業固定未授權映像作業不是故障目標
老師原判斷、交出的判斷正常 auth_ok_i、注入後 auth_seenC 改傳遞值;原始 reference 不跟著改
保存的成績欄/及格章result_qB 改保存值;A 只改下游看到的 result_seen
已完成登記的章checked_q完成不等於及格
第 2 節的完成提示verify_done_i提示使 write_en 成立,不是及格答案
門口放行判斷grant_clean/exec_grant_oA–C 先信任最後閘;D 另把最終許可列為 target
學生到門口、門口可接待fetch_valid_i/fetch_ready_i不是故障預算,也不是評分
拿到考卷的接受紀錄accepted_commit需請求、ready、grant 同時成立
另一份獨立評分與完成紀錄ref_auth_pass/ref_auth_complete是 testbench 判準,不從受改成績單抄答案

成績單是 DUT 的保存紀錄;獨立評分簿才是 reference。這個比喻沒有在晶片內加一位老師或新的防護電路。

模型卡是一份交接文件。下一位工程師應能照它設定注入器與 monitor,並知道什麼結果算越權。先從案例 B 開始:驗證失敗後,改寫一次保存的結果位元。不要先把所有位置混成一個「單 bit 攻擊」。

欄位案例 B 的明確設定
保護資產與失敗事件未授權映像不得取得任何 accepted_commit
初始狀態與工作負載edge 0 reset;本次映像固定且失敗;edge 2 檢查完成;edge 4、6 有請求
target/抽象層級一位元 result_q 的儲存狀態;RTL 狀態轉移模型
effect/恢復條件一次反相;改過的值保留到正常覆寫或 reset,不持續強制驅動
timing/順序在選定的 edge 2~7,先取樣接受事件,再正常更新,最後改寫新狀態
budget/單位每次 reset 到 edge 7 的開機嘗試至多 1 個事件、1 個位置、1 個目標 bit
可信與排除邊界映像與政策、獨立 reference、clock/reset、verify_done、checked_q、非目標邏輯、握手與接受端、注入器可信
觀察期限含 edge 0~7;兩次請求共用同一個嘗試與預算
結果與證據記錄每拍原始 Q、觀察值、grant、commit 與 reference;沒有 detector,不宣稱偵測成功

這不是從產品量測得到的攻擊能力。它是我們為了回答一個小問題,主動選出的分析邊界。若後續要測 clock、reset 或 checker,必須另建模型,不能把它們當成已由這張卡保護。

表中的可信項目也要交付給 reviewer。Reference 可信,才能判定 DUT 的結果是否錯誤;接受端可信,才能按約定記錄 commit。若攻擊者也能改這些位置,實驗要重新定義,而不是只多加一個 target 名稱。

SYNFI 的論文把故障位置、持續時間與發生時間列為模型的空間與時間條件,並用位置、故障效果和同時注入數量設定實驗。本課借用這種明確描述的方法,沒有執行 SYNFI。SYNFI 作者版,第 2.1、3.1 節

3. 一次、一個位置與一個 bit,分別怎麼數?

B 改一次及格章,第 4、6 節都拿到考卷,仍只花一次動手機會。若第 3 節改一次、第 5 節又改一次,就算兩次,即使碰的是同一章。A 用一次手指遮住兩個取樣緣,可以是一次持續兩緣的 pulse;B 的章留著則是改寫後的後效。事件數、持續時間和接受數各有自己的欄位,不能互相代算。實驗的下次 reset 才重開預算,沒有因此限制產品一生能試幾次。

「只能一次」至少要說明一次指什麼。一次事件可能影響一個位置,也可能影響同一個 register 的多個 bit;兩次事件可能打在同一個位置,卻相隔幾拍。這些限制不能互相代替。

計數單位應回答的問題本課怎麼算
事件數發生了幾次注入動作?每條 fault trace 只有 1 個事件
位置數幾個不同 target 被影響?每次只選一個 target
目標位元數一個 target 的哪些 bit 可被改?只有一個 bit;不推論多位元故障
有效期間一個事件影響多少取樣點?pulse 為 1 或 2 個 edge;state upset 的後效會保留
嘗試數/重置規則下一個預算何時開始?下一次 reset 後才開始新的開機嘗試

案例 B 在 edge 3 改寫一次狀態,edge 4 和 edge 6 都讀到錯誤,仍是一個事件留下的後效。相反地,edge 3、edge 5 各改一次,就是兩個事件。即使打在同一個 bit,也已超出本課預算。攻擊者能否反覆重開機,則是另一個需要寫清楚的產品假設;本課不限制裝置一生的嘗試總數。

因此,不要用「造成兩次錯誤接受」倒推「注入了兩次」。事件數描述攻擊動作,commit 數描述後果。一次保存錯誤可以影響多個請求;一個沒有碰到請求的事件,也可能完全沒有可見後果。

不同工具的計數規則也需要對照。FIRMER 將每拍可發生的事件數、可注入的拍數、效果類型,以及邏輯或記憶元件分開定義;它的持續故障表示法不能直接套成我們的「一次狀態改寫後保留」。搬模型時先對齊語意,再比較數字。FIRMER 論文,第 1、3 節

4. Q 端 pulse 與儲存狀態 upset,畫出來看

看成績單上的墨跡,才能區分 A 與 B。A 遮的是讀取路徑,對應 result_seen = result_q XOR fi_q_xor_i;墨跡沒有改。B 的手指只動一下,卻改了保存的章;之後保持路徑會讀回改過的 result_q。C 則在寫入開著時換掉老師交出的值,對應 auth_seen。章、遮擋和交出的紙都是三個不同 target,各跑一份實驗;不能同時動它們仍算單一位置。

案例 A 在讀取路徑放一個 XOR:result_seen = result_q ^ fi_q_xor_i。注入控制為 1 時,下游讀到反相值;控制回到 0,讀到的又是原本的 Q。XOR 沒有改寫暫存器。 只要觀察路徑沒有回授到 D,原始 result_q 不會因這個 pulse 改變。

result_q 經 XOR 與 fi_q_xor_i 形成 result_seen,再和 checked_q 進入 AND;XOR 沒有回到暫存器 D,因此不改寫原始 Q。
圖 2:案例 A 的觀察端 pulse。注入器是測試用途;線上沒有額外的故障偵測器。

案例 B 要讓保存的狀態改變。教學 RTL 用 next-state XOR 實作這個效果:先選正常要寫入的值,再依 fi_state_xor_i 反相,最後寫進 DFF。下一拍關掉注入控制,hold 路徑讀回已改壞的 Q,所以錯誤會留下來。這是在 RTL 中表達狀態轉移的注入方式,不是在宣稱粒子、雷射或 EM 實際打中了 D 端。

write_en 控制 mux 選 auth_seen 或 result_q 的保持回授,再經 fi_state_xor_i 的 XOR 寫入 DFF;一次脈衝造成新 Q 改變,後續 hold 保存它。
圖 3:案例 B 的持續狀態效果,以 D 端 next-state XOR 建模。低有效 reset 將結果清零;不是實體儲存單元受擾的電性模型。

還有案例 C:在 auth_ok_i 進暫存器之前反相。pulse 若涵蓋 edge 2 的完成取樣,錯誤答案就被保存;等 pulse 結束,暫存器裡仍然是錯的。若 pulse 只涵蓋 edge 3,正常寫入已經關閉,便不影響結果。這次 target 是上游判斷,與 A、B 各有自己的 trace,不能混成同一個位置。

force/release、backdoor deposit 與注入 mux 是實作注入的方法;它們在不同 simulator、net/variable 與正常更新流程中的效果要另行確認。不要只寫了 API 名稱,就把它視為所有 bit upset 的共同語意。OpenTitan 的安全防護驗證框架也以指定內部目標的注入來測試防護;使用時仍須核對模型和觀察點。官方驗證框架

5. 在同一拍,誰先看到哪個值?

每次鐘響,門口先用已穩定的紀錄決定這次是否交考卷,接著才完成登記與改章。B 在第 3 節更新後改章,能影響第 4 節;若第 4 節更新後才改,也不能倒改第 4 節已紀錄的交付。這對應先取樣 commit、再正常更新、最後 state upset 的順序。人在一節課內可以邊看邊改,硬體教學模型則固定緣前與緣後,沒有模擬這些同時動作或真實閘延遲。

我們把每個 rising edge 分成三個閱讀步驟。edge 之前先穩定輸入與 pulse 控制;edge 上取樣原本的 Q、grant 和 commit;之後才完成暫存器更新。案例 B 的狀態 XOR 影響更新後的 Q,因此不會回頭改變同一個 edge 已經取樣的 commit。

每個 edge 先穩定 pulse,再取樣舊狀態的 commit,最後更新新 Q;edge 2 完成驗證,edge 4與6接受請求,edge 3 的儲存狀態翻轉會留到後續接受點。
圖 4:本課的離散取樣契約。圖中先後是模型規則,不是量測出的閘延遲,也不涵蓋亞穩態或拍內 glitch。

edge 2 取樣之前,可信 reference 的 ref_auth_complete=0;更新之後它才變成 1。verify_done_i 本身保持可信,案例 C 只改 auth_ok_i。reference 的 pass 依固定映像與獨立政策判斷,失敗映像始終是 0,沒有從 faulted result_q 複製答案。

把接受事件往後移一拍,或把 state upset 改成 edge 前寫入,都可能改變反例。若 testbench 在 posedge 同時用 blocking assignment 改控制訊號,DUT 與 monitor 可能讀到不同順序的值。練習中的注入控制應在接受 edge 前穩定,狀態觀察則分清更新前後;這份 Python 模型沒有模擬 event-region race。

讀波形時,分別記下 edge 前的 Q 與更新後的 Q。Edge 3 後才改壞的值,會影響 edge 4 的接受,不會改寫 edge 3 已記錄的事件。這個順序也是 Python 與 RTL harness 必須對齊的契約。

用同一個 edge 3 比較三種效果

固定在第 3 節動手,A 拿開手指便恢復不及格,B 的改章留下來,C 則遇到不再收件的登記窗口。所以第 4 節只有 B 越權。把 C 提早到第 2 節,登記還開著,假及格也會留下來。這是同一份答錯作業與同一個到門口排程,只有效果或時機變了;A、C 未放行沒有警報器介入,不能寫成已偵測或已阻擋。

「緣前」是取樣前已穩定的值;「緣後」是正常更新與注入結束後的保存值。映像失敗,edge 2 後 checked=1、result=0。請求固定在 edge 4、6,所以下列比較沒有偷偷改變工作負載。

edge 3 的實驗edge 3 讀到/保存什麼edge 4 的 grant
A:只在 edge 3 開 Q pulseseen=1,但保存 Q=0;pulse 隨後取消checked AND seen = 1 AND 0 = 0
B:edge 3 更新後翻保存 QQ 從 0 變 1;hold 保存這個 11 AND 1 = 1
C:只在 edge 3 開來源 pulsewrite_en=0,沒有再捕捉來源1 AND 0 = 0

因此 B 在 edge 4、6 都能越權,卻只有一次注入。A、C 沒有越權,是這個時程沒有保存或使用錯誤,不是 detector 擋住它。把 C 移到 edge 2 的完成取樣,write_en 變成 1,錯誤來源也會留在 Q。先預測這一列,再重跑 Python trace。

6. 用 RTL 對照三種效果,再擴大一個邊界

讀 RTL 時,把 write_en 想成成績登記窗口,fi_source_xor_i 是交接時換值,fi_state_xor_i 是寫入後改章,fi_q_xor_i 是門口看值時的遮擋。D 的 fi_grant_xor_i 則直接改最終許可:成績仍不及格,第 4 節門口卻放行。D 已改變『最後閘可信』這條前提,仍未攻擊 valid、ready 或接受機制。

稽核員在真正拿到考卷時,核對獨立的完成紀錄與原始成績,對應 accepted_commit 後面的 ref_auth_complete AND ref_auth_pass。寫了這條規則,還須由 harness 限制一次機會;SVA 不會自動替人收走第二次改章的工具。

完整的教學注入 wrapper可下載。以下片段共用同一模組;所有 fi_* 為 0 時,它等價於第一課的脆弱放行電路。這些控制只供實驗,不應留下在正式產品的 RTL。

assign write_en = verify_done_i && !checked_q;
assign auth_seen = auth_ok_i ^ fi_source_xor_i;
assign result_d = (write_en ? auth_seen : result_q) ^ fi_state_xor_i;

always_ff @(posedge clk_i or negedge rst_ni) begin
  if (!rst_ni) begin
    checked_q <= 1'b0;
    result_q  <= 1'b0;
  end else begin
    if (write_en) checked_q <= 1'b1;
    result_q <= result_d;
  end
end

fi_source_xor_i 改的是要被取樣的判斷;fi_state_xor_i 改的是這次寫入後的狀態。模型只允許一次、單一目標;連續兩拍把 state XOR 接成 1 會翻兩次,不符合這裡的單次 upset。

assign result_seen = result_q ^ fi_q_xor_i;
assign grant_clean = checked_q && result_seen;
assign exec_grant_o = grant_clean ^ fi_grant_xor_i;
assign accepted_commit = fetch_valid_i && fetch_ready_i && exec_grant_o;

前三個案例排除了最終 grant 的故障。案例 D 另開這個邊界:fi_grant_xor_i 反相 grant,取指的 valid、ready 與真正接受機制仍可信。如果失敗映像在 edge 4 得到 grant,接受端就可能照契約接收它。不能拿 A、B、C 的結果宣稱已保護 D。

checked_q 與 result_seen 產生 grant_clean,再由單獨的 grant XOR 形成 exec_grant_o;可信接受端及獨立 monitor 觀察 commit,比對映像授權。
圖 5:案例 D 新增 grant 為 target。reference 是可信驗證環境,沒有被畫成晶片內新增的保護電路。

安全性要求仍然寫在真正的接受點:

assert property (@(posedge clk_i) disable iff (!rst_ni)
  accepted_commit |-> ref_auth_complete && ref_auth_pass);

cover property (@(posedge clk_i) disable iff (!rst_ni)
  ref_auth_complete && ref_auth_pass ##[1:8] accepted_commit);

這是待放入 testbench 或 formal harness 的性質,尚未編譯或證明。|-> 檢查同一取樣緣;disable iff 排除了 reset 有效期間。cover 只要求存在一條合法接受路徑,不保證每次合法映像都會成功;每次開機的可用性還需要自己的進度、timeout 與復原要求。注入預算也要由 harness 真正限制,光放 assertion 不會自動得到「一次故障」模型。

7. 跑 42 個案例,先看 trace 再讀 PASS

42 次演練每次都重新交同一份答錯作業。報告先寫哪節拿到考卷,再查那次改的是哪張紀錄。第 3 節改章會留下兩次交付;第 6 節更新後才改章,原行程第 7 節前沒有再到門口,便沒有交付。這對應兩條不同起始緣的 B trace。後者可能仍保存錯誤,不能把『沒再去』寫成『警衛擋住』;18/42 是這份枚舉的結果,沒有量測真人搞鬼成功率。

下載 Python 練習,用 Python 3 執行;加上輸出資料夾可以保存每拍 trace:

python lesson02_fault_model.py --output-dir lesson02-results

這裡枚舉 6 個起始 edge(2~7),A、C、D 各測 1/2 個 edge 的 pulse,B 各測一次狀態翻轉,共 42 個 fault case。每次都重新 reset,使用同一份失敗映像與固定請求排程。另有兩個不注入的控制案例:合法映像在 edge 4、6 被接受;失敗映像兩次都不被接受。

例子觀察到的未授權接受 edge應怎麼讀
A:Q pulse 只涵蓋 edge 3無edge 0~7 未觀察到越權;沒有 detector,不能說偵測成功
B:edge 3 更新後翻儲存狀態4、6一次事件留下的錯誤,影響兩個接受機會
C:上游 pulse 涵蓋 edge 24、6完成取樣把錯誤判斷保存下來
C:上游 pulse 只涵蓋 edge 3無edge 0~7 未觀察到越權;錯過寫入時機,不代表防護完成
D:grant pulse 涵蓋 edge 44上游結果仍是失敗,最終放行卻被改掉
B:edge 6 更新後翻儲存狀態無edge 0~7 的排程沒有下一次請求;沒有 detector,期限外仍未知

實際執行得到 18 個案例出現未授權接受,24 個在指定窗口與請求排程內沒有。PASS 表示程式重現預先寫出的反例與控制結果,包含故意脆弱設計的失敗;它不是 IP 通過安全驗證。18/42 也不是實體攻擊成功率:這份枚舉沒有量測機率、位置可達性或擾動強度。

這張表沒有「已偵測」欄,因為本課未加入 detector。對沒有 commit 的案例,只能回報觀察結果,再查原因。Pulse 錯過請求、來源錯過寫入、窗口內沒有後續請求,都與防護主動阻擋不同。

最後一列尤其值得重跑。把請求排程改成 edge 4、6、7,同一次 edge 6 之後的狀態翻轉就會在 edge 7 越權。原程式已有這個對照檢查。先前沒有觀察到 commit,是排程與窗口內的結果,不能寫成「fault 被阻擋」。

8. 把實驗結果接回設計與產品

要改善這套校規,先問改章、換交接值與改門口許可各需守住哪段。把成績單印得更複雜,只處理保存欄的某些改動;不能替老師到登記窗口的交接或最後門口背書。這對應儲存完整性、來源綁定與 consumer 路徑。生活故事讓責任分段,沒有證明增加章的數量就能處理所有實體故障。

本課沒有加入防護電路,所以第一個改進是把模型寫對,讓失敗可以重現。接著才依 target 選措施:保存狀態出錯,要分析儲存完整性;上游判斷被保存,要檢查產生端與授權綁定;grant 被改寫,則要把 consumer 與最終交付路徑納入防護。第三課會先深入 register 的完整性,後面再處理多位元控制與共同失效。

一次 campaign 的紀錄,至少要留下設計版本、模型版本、target 清單、注入參數、初始狀態、工作負載、觀察期限和反例 trace。另記錄注入是否真的發生,以及要求的性質有沒有被評估。工具 timeout、注入失敗、assertion 被關掉或沒有請求到來,都不能歸成安全通過。

實務上應把三件事分開:注入器有沒有生效、敏感請求有沒有到達、性質有沒有被檢查。少任何一件,都要在報告留下未解項目。這比只收集最後一行 PASS 更能幫團隊找出驗證缺口。

若產品有 prefetch、DMA、debug 或金鑰讀取,把各自的敏感接受事件列出來。若要加入 clock/reset 攻擊或多拍、重複注入,重新描述其效果與可信邊界,並重跑基準。為了讓某個性質通過而把 reference 改成 faulted 值,只會把錯誤藏起來。

邏輯模型也有量測落差。SYNFI 說明其邏輯分析沒有納入拍內 transient、閘傳播延遲及實體 layout;這份更小的 Python 練習同樣不能回答那些問題。要把模型用到產品,還得確認綜合後 target 的對應、實體擾動能否產生該效果,以及 detection/blocking 是否早於交付。SYNFI 第 6 節

9. 論文怎麼讀,才不會搬錯模型?

讀別人的故障模型,就像收到另一間學校的演練紀錄。若它准許整節課一直遮住章,我們卻只准一個取樣緣,兩邊『一次』的有效期間已不同。若它測的是最後門口,結果也不能搬到保存成績單。對照論文時要把 target、duration 與接受期限寫在同一張卡上;這只是閱讀方法,沒有把本課變成 SYNFI、VerFI 或 FIRMER 的實驗。

SYNFI 適合拿來練習讀實驗設定:選了哪段電路、輸入與預期輸出是什麼、允許哪種 gate 替換與同時注入數量?其 netlist 分析有自己的前處理與時間假設,不是本課逐拍模型的另一個名字。

VerFI(IEEE HOST 2020) 說明使用者如何限制 adversary model,再針對 test vector 模擬故障;它記錄未偵測案例的位置、效果與時脈。閱讀時要分清測試向量辨識出的錯誤、晶片內防護是否發 alert,以及受保護操作有沒有先被交付。本課的 Python 未呼叫 VerFI。作者論文,第 II、III 節

FIRMER 把允許的事件、拍數、效果與元件類型放進明確參數。可以用它練習問「工具中的一個事件,和我的模型卡中的一次注入,是否相同?」本課沒有重現其 benchmark 或 SAT 證明。論文

OpenTitan 的硬體設計指南 提醒設計者先找敏感操作,並分析內部節點與最終放行訊號受改寫的後果。這可以轉成模型卡中的保護事件與 target 清單;它並未替本課的 gate 提供安全證明。官方指南

以上來源於 2026-10-03 核對。Grok 完成獨立引文研究;Claude 的網路搜尋未完成,改以 Codex 查證的來源與模型做唯讀意見,並在教材完成後審查實作。來源取捨、程式與部署由 Codex 負責。不同工具的宣稱只在各自的條件下成立,不能合併成整顆晶片的保證。

  1. Pascal Nasahl 等,SYNFI: Pre-Silicon Fault Analysis of an Open-Source Secure Element,TCHES 2022(4)。作者版。作業:把它的同時故障數、位置與效果對照你的模型卡。
  2. Victor Arribas、Felix Wegener、Amir Moradi、Svetla Nikova,Cryptographic Fault Diagnosis using VerFI,IEEE HOST 2020,229–240。作者論文/機構書目。作業:找出報告記錄哪些未偵測案例欄位,並補上你要觀察的接受事件。
  3. Huiyu Tan 等,SAT-based Formal Verification of Fault Injection Countermeasures for Cryptographic Circuits,TCHES 2024(4)。CHES 網站論文版本。作業:對照每拍事件數、可注入拍數與元件類型;不直接沿用本課的計數法。
  4. lowRISC,Secure Hardware Design Guidelines 與 Security Countermeasure Verification Framework。設計/驗證。線上文件會更新;專案實驗應固定版本。

10. 換一個請求時間,再寫一次契約

最後只改行程:第 7 節再到門口一次。原本第 6 節更新後改好的及格章還在,第 7 節就拿到考卷。預算仍只用一次,變的是新的接受機會。這對應 extraFetch=true;延長觀察會揭開後效,沒有重新注入。若再准許兩次動手,則是另外一張模型卡,不能連同新行程一起改後仍稱只改一個條件。

把 edge 7 加進取指排程,重新預測 42 個案例。不要先改預期答案,先寫下哪些 target 的後效還會存在,以及哪個接受點會看到它。再把事件數從 1 改成 2,說明同位置重複翻轉和不同位置同時受影響,為什麼需要不同的枚舉方式。

完成這一課,應該能交出一張模型卡和一條反例 trace,讓同事不用猜你的注入語意就能重跑。下一課是「Register 的完整性保護」,會用這套契約比較 parity、互補編碼與 ECC。發布狀態與完整順序放在課程大綱。

MY ACADEMY · LESSON FILM

教學影片

影片依序說明本課的資料路徑。看完一段,可以回到下面的互動練習,改變輸入或故障條件。動畫呈現教學模型;它沒有替代 RTL 模擬。

下載 MP4 · 字幕 VTT

旁白使用合成聲音。互動教學與動畫均有模型邊界;請以本課的來源與驗證範圍解讀結果。

MY ACADEMY · RTL LAB

操作故障模型:這一拍到底改了什麼?

比較 edge 3 的 Q pulse 與 state upset。前者只改讀到的值,後者在更新後改保存狀態。接著試 edge 6 的 state upset,再加上 edge 7 請求。觀察窗口與排程會改變結論。

每拍先施加 pulse,再取樣 commit,最後更新暫存器。state upset 在正常更新後發生。保存結果在 edge 2 寫入;請求預設在 edge 4、6。

證據範圍:既有 Python 練習的有限雙態教學模型,觀察 edge 0~7。獨立 reference、clock/reset、取樣端維持可信。沒有執行 RTL/形式/晶片驗證。

result Qconsumer grantaccepted commit

當拍取樣前

當拍更新後

逐拍紀錄(只顯示已走過的拍)
edgeraw Qseen Qsourcegrantcommit越權

沒有越權 commit,只代表本次窗口內沒看到。這個模型沒有 detector,不能把錯過請求說成已偵測或已阻擋。

下載原始 Python 練習,獨立重跑

Wrap-up|把這一課帶回設計審查

威脅模型與成立條件

這一課保護的是固定失敗映像不能得到一次取指接受。每次 reset 到 edge 7 是一個完整嘗試,edge 4、6 的請求共用至多一次事件、單一位置與一個目標 bit 的預算。A 只改 Q 的觀察值,B 改更新後的儲存狀態,C 改完成取樣前的 auth_ok,D 另把最終 grant 納入;它們分別建立 trace。

映像與政策、獨立 reference、clock/reset、verify_done、checked_q、非目標邏輯及接受機制都排除故障。這是為了限定實驗而採用的可信假設,並不表示產品裡這些位置不會被攻擊。Pulse 控制在 edge 前穩定;接受事件讀舊 Q,B 的效果則留在更新後的新 Q。

失效原因

同樣叫 bit flip,可能只是讀取線短暫反相,也可能把保存的值改掉。前者關掉注入器就消失;後者即使停止注入,也可能透過 hold 一直留到下一次接受點。上游錯誤若碰到完成取樣,還會被保存成正式結果;最終 grant 被改寫時,上游算對也無法替接受端擋住它。

另一種失誤出在解讀報告:pulse 錯過請求、期限內沒有下一次請求,或性質沒有被評估,都可能讓 trace 看不到越權。若把這些結果寫成「已偵測並阻擋」,就把測試條件造成的空白當成了防護。

防護方法

先用模型卡限定 target、effect、時機、恢復條件、計數單位、可信邊界與觀察期限,讓反例可重現,再針對實際失效位置選防護。儲存完整性、產生端授權綁定與 consumer 放行檢查各自處理不同問題,不能互相代替。

本課的 XOR 與 mux 是測試注入器,不是防護電路;沒有新增 detector。正式設計必須移除這些 fi 控制,並把防護的接受期限、可用性與復原政策另行驗證。第三課才開始比較 register 的保護措施。

驗證方式與待做檢查

已執行兩個無故障控制與 42 個 Python fault case:18 個出現未授權接受,24 個在 edge 0~7 與固定排程內未觀察到。已檢查 Q pulse 不改 raw Q、一次 state upset 保留、上游取樣時機、reference 不受擾,以及增加 edge 7 請求後出現的新反例。PASS 代表預期行為被重現,沒有把越權案例當成安全通過。

RTL wrapper 與 SVA 是供後續 harness 使用的教材,尚未執行 RTL 模擬、綜合或形式證明。後續要限制注入預算、檢查注入是否生效、區分更新前後取樣,並分開報告 detection、及時 blocking 與 unauthorized commit。網站圖解驗收與硬體驗證是不同證據。

防護界線與未驗證項目

這是兩態、離散 edge 的有限模型,沒有 X、亞穩態、拍內 glitch、閘延遲、實體位置或擾動機率。18/42 不是矽上攻擊成功率;沒有在窗口內看到越權,也不是無限時間、其他工作負載或其他 target 的保證。

Clock/reset、共同失效、多位元與重複注入、DMA/debug 及資訊洩漏未被這次實驗覆蓋。把模型轉到 netlist 與矽上時,還要核對 target 對應與實體效果。反覆重開機的總次數沒有被本課的 per-attempt 預算限制。

換個情境再想一次

把請求排程改成 edge 4、6、7,先預測哪種故障在 edge 7 還有後效,再重跑。接著允許同一次開機出現兩個事件:請在模型卡中分開寫同位置重複翻轉與不同位置同時注入,說明要新增哪些 trace 與預算檢查。

以上整理對照本課的教學案例、參考資料與實驗範圍;未列為已完成的驗證,都是後續工作。

學習指南

RTL Anti-Tampering Design|RTL 防竄改設計

0 / 16

查看課程大綱 → · 進度只計入已發布課程

先備知識

  • 第一課;同步暫存器與 valid/ready 介面

我學會了什麼

  • 寫出可重跑的故障模型卡
  • 區分暫時觀察擾動與持續儲存錯誤
  • 定義取樣順序與事件預算單位
  • 解讀有限的未觀察結果,不誤稱偵測成功

本課術語

查看術語字典 →

延伸閱讀

課後小測驗

1. Q 端 XOR pulse 改變了什麼?
2. 一次 upset 影響 edge 4、6 的請求,中間沒有 reset。幾個事件?
3. state upset 在 edge 6 更新後作用,能改剛才取樣的 commit 嗎?
4. edge 0~7 沒有越權,且沒有 detector。能回報什麼?
5. 原模型排除最終 grant 故障。要加入它,需要什麼?

讀到這裡,辛苦了。

把概念帶走,比把術語背走更重要。

#RTL#Fault Model#Fault Injection#Secure Boot#Verification#SYNFI#VerFI#FIRMER