關鍵字:AES,SAES, HUK, DHUK, 安全存儲
引言
近年來,嵌入式設備信息安全被越來越多地提及,從智能穿戴產品、智能工業設備到物聯網產品都對設備信息安全提出了要求,比如基礎的安全啟動,安全升級,調試端口保護等,一些應用場景還會涉及安全存儲的需求。一些相關的信息安全技術標準中也會提及對設備安全存儲能力的要求,例如針對物聯網設備的 EN303645 ,針對工業自動控制系統中的設備安全的 IEC62443-4-2 。安全存儲的目標包括實現對敏感數據或用戶數據的保護,對機密數據例如密鑰的保護等等。
為了更好地滿足安全存儲需要,方便客戶更容易地實現安全存儲的目標,STM32 的新系列產品中增加了相關硬件,來更好地支持安全存儲功能的實現,其中最典型的硬件特性就是安全AES 模塊(Secure AES)和硬件唯一密鑰模塊(Hardware Unique Key),分別簡稱 SAES 和 HUK。本文將首先對 SAES 的主要特性做簡要介紹,然后介紹幾個有關 SAES 和 HUK 實現安全存儲的典型用例,最后討論幾個 SAES 和 HUK 使用中的常見問題和解決方法。
1 SAES 主要特性
STM32 的幾個較新的系列中都增加了 SAES 和 HUK 硬件,在目前已經發布的產品中支持 SAES 和 HUK 的主要包括:STM32U5系列,STM32WBA 系列,STM32H7S系列,STM32H5 系列,STM32N6系列等(這里指的都是其中帶有硬件 crypto 的型號)。
SAES 是獨立于普通 AES 外設的另一個安全 AES 加解密引擎,也就是說在這些系列中除了原有的 AES 外設之外,新增加了另一個 SAES 硬件模塊。SAES之所以叫做 Secure AES,它和普通 AES 相比主要有以下幾點不同:
- SAES 的加解密單元是安全核,采用更安全的設計,在硬件算法實現上就能夠抵御側信道攻擊及錯誤注入攻擊。當然,由于加入了防護,它的運算效率沒有普通 AES 硬件高,同樣的算法需要更多的時鐘周期來完成。
- SAES 增加了密鑰來源的選擇,除了傳統的通過軟件寫入密鑰寄存器,還支持硬件密鑰,比如 HUK,BHK,AHK。硬件密鑰在發生入侵事件時將被自動清除或者鎖定(比如數值保持全 0 或者無法使用)
軟件可以選擇硬件密鑰中的一個或者其中兩個的組合作為密鑰,對數據進行加解密,例如需要加密某個數據的時候,可以使用 DHUK,或者 BHK,或者 DHUK⊕BHK 作為密鑰來源,支持 AHK 的系列中也可以選擇 AHK 或者 DHUK⊕AHK。
1.1SAES 支持的幾種硬件密鑰及其特點
HUK:Hardware Unique Key
- 出廠時芯片自帶的硬件唯一密鑰,長度為 256bit,我們把它稱為 RHUK,即 Root?HUK, RHUK 通過私有總線連接到 SAES。CPU、DMA、調試端口等都無法獲取密鑰內容,但是 SAES 可以使用 HUK 進行加解密操作。
- SAES 的加解密并不會直接使用 RHUK,SAES 內部帶有一個密鑰派生單元,從RHUK 生成派生密鑰 DHUK(Derived HUK),實際加解密操作用到的是 DHUK。DHUK 的生成依賴其他一些系統配置和狀態,比如密鑰使用模式,當前 TrustZone的安全/非安全上下文等,因此系統中可以使用多個不同的 DHUK。
- 在部分系列中(例如 STM32H5,STM32H7S,STM32N6),DHUK 的密鑰派生過程還增加了時域隔離保護(基于 HDPL 隱藏保護級別)和防重放攻擊保護(基于EPOCH 單向計數器)。
? ? ? ? ? 時域隔離保護:當不同階段的軟件在執行時,可以設置 HDP 保護為不同的級別,例如第一級 bootloader 代碼運行在 HDPL1,第二級 bootloader 運行在HDPL2,App 運行在 HDPL3。HDPL 處于不同級別時,DHUK 將派生出不同的密鑰。這意味著各個不同階段的軟件用 DHUK 加密數據時將使用不同的密鑰。
? ? ? ? ? 防重放攻擊保護:系統中有 EPOCH 單向計數器,如果芯片的 Product State從 Closed 狀態經過 Debug Authentication 流程回到 Open 狀態,EPOCH 單向計數器會自動增加。由于 EPOCH 的值也會影響 DHUK 的派生,這樣只要發生了產品的狀態回退,那么回退之前存儲的所有使用 DHUK 參與加密的數據都無法再被解密,因為 DHUK 在更新的 EPOCH 下會產生不同的密鑰值。
BHK: Boot Hardware Key
- BHK 是由軟件寫入后備域寄存器 0-7 的密鑰,通常由軟件在啟動階段寫入。
- BHK 寫入后可以被鎖定,鎖定后的 BHK 只能通過私有總線導入 SAES,而無法被軟件讀取。
AHK:Application Hardware Key
- STM32H7S 系列帶有 AHK 功能,AHK 數據來自 OBK 存儲區對應每個 HDP level 的前 8 個 index 的數據,OBK Index0-7 的數據只能寫,不能讀。
- 應用程序可以將需要操作的密鑰寫入 OBK index0-7,作為 AHK,并配置 SAES 選擇AHK 作為密鑰來源
上述這些硬件密鑰以及鎖定后的 BHK 只能被 SAES 使用,而不能被軟件(包括 CPU,DMA,調試端口等)訪問。DHUK 相當于每顆芯片都預置了不可知的密鑰,但是應用可以使用這些派生密鑰對數進行加解密,BHK 可以看作是 bootloader 才知道的密鑰,而 AHK 則是用戶自己寫入的已知的硬件密鑰,但是所有軟件運行過程中都無法看到這個密鑰的內容。
1.2 SAES 及硬件密鑰主要特性
圖 1 圖 2 圖 3 圖 4 分別給出了 STM32U5/WBA,STM32H5,STM32H7S 和 STM32N6 的 SAES 單元與硬件密鑰的示意圖。
圖1. STM32U5/WBA SAES 與 HUK/BHK
圖2. STM32H5 SAES 與 HUK/BHK
圖3. STM32H7S SAES 與 HUK/BHK/AHK
圖4. STM32N6 SAES 與 HUK/BHK
Error! Reference source not found.總結了幾個系列產品中 SAES 和硬件密鑰的主要特性及差異
表1. 幾個系列產品中 SAES 和硬件密鑰的主要特性及差異
1.2.1 幾種硬件密鑰及其使用場景
從前文可以看到 SAES 可以選擇幾種不同的硬件密鑰來源,接下來介紹幾種硬件密鑰的特點,有什么不同,以及通常什么應用場景下可以考慮選擇哪種硬件密鑰或者硬件密鑰的組合。
1.2.2 DHUK vs. AHK vs. BHK
表 2 總結了三種硬件密鑰的異同以及主要使用場景。
表2. 幾個系列產品中 SAES 和硬件密鑰的主要特性及差異
1.2.3 DHUK⊕AHK, DHUK⊕BHK
在表 2 描述的場景 1 的基礎上,有時客戶會希望加密密鑰不是完全來自于不可知的硬件密鑰,而是能有自己可控的部分數據的參與。
對于這樣的需求,在支持 AHK 的系列上就可以考慮使用 DHUK⊕AHK 作為密鑰來源,因為 AHK 是用戶自己定義和寫入的。AHK 只需要一次性燒錄,不需要每次執行軟件的時候寫。不支持 AHK 的系列上則可以選擇 DHUK⊕BHK。
DHUK⊕BHK 能夠支持的另一個應用場景是,用戶不僅希望硬件密鑰有自己可控的部分數據參與,而且可控部分的數據可能隨著軟件的更新發生變化,比如某些時候如果需要讓以前加密存儲的數據失效,那么就可以在某個軟件升級之后,軟件寫入新的值到 BHK,此后用 DHUK⊕BHK 加密數據時,就會采用新的密鑰。
1.2.4 關于 SAES 密鑰使用模式
1.2.4.1 Normal 模式
Normal 模式用于直接對數據進行加解密,例如選擇密鑰來源為 DHUK 時,輸入明文數據到 DIN 寄存器,DOUT 寄存器則可以得到加密后的密文數據。
1.2.4.2 Key Wrap 模式
Key Wrap 模式可以用于安全存儲和使用 AES 密鑰。例如,如果希望存儲一個 AES 密鑰 Kclear,可以先用 Key Wrap 模式對明文密鑰數據加密從而得到密文密鑰,然后只需要存儲這個密文密鑰,而無需保留明文密鑰在任何 Memory 中。后續可以直接導入存儲的密文密鑰進而恢復明文密鑰,之后 SAES 硬件就可以用 Kclear進行數據的加解密操作,如圖 5所示。
圖5. SAES Key Wrap 模式
Key Wrap 模式下的加密操作用于加密某個 AES 密鑰,例如對于需要加密的明文密鑰Kclear,可以選擇 DHUK 作為密鑰來源,將明文密鑰 Kclear作為數據輸入給 DIN 寄存器,DOUT 寄存器將得到加密后的密文密鑰 Kenc。這個密文密鑰 Kenc可以存儲在 NVM 中,方便后續的使用。
Key Wrap 模式解密操作用于導入之前 Wrap 的密文密鑰 Kenc,也稱為 Unwrap 操作。Unwrap 操作依舊選擇相同的密鑰(例如硬件 DHUK)作為密鑰來源,Kenc作為數據輸入給到 DIN,此時解密后的原始明文密鑰 Kclear并不會出現在 DOUT,硬件會自動將其直接導入 SAES 密鑰寄存器。Unwrap 操作完成后,已經導入的 AES 密鑰 Kclear可以用于后續的數據加解密。
1.2.4.3 Key Share 模式
Key Share 模式同樣可以用于實現 AES 密鑰的安全存儲與使用。類似地,在密鑰導入階段對原始明文密鑰進行加密,此時加密操作選擇 Key Share 模式,得到密文密鑰數據,然后進行存儲;在使用階段首先對密文密鑰進行解密,同樣采用 Key Share 模式,這里不同于 Key Wrap 模式的是,Key Share 模式解密后的明文密鑰不會被導入 SAES 的密鑰寄存器,而是通過另一條私有總線自動導入 AES 模塊,后續的數據加解密操作可以由 AES模塊完成。Key Share 模式即能夠在 AES 密鑰存儲和使用過程中很好地保護密鑰的機密性,同時又可以使用 AES 模塊更高的加解密性能。
過程如圖 6 所示,如果希望存儲一個 AES 密鑰 Kclear,可以先用 SAES 的 Key Share 模式對 Kclear加密,得到密文密鑰用于存儲,后續需要使用密鑰的時候,先用 Key Share模式對密文密鑰解密并導入 AES 模塊,然后 AES 模塊就可以使用 Kclear進行數據的加解密。無論是存儲數據還是加解密操作過程中,始終不會出現能夠被軟件獲取到密鑰數據明文的機會。
圖6. SAES Key Share 模式
?
2 SAES 與 HUK 實現安全存儲舉例
2.1 數據安全存儲
用例 1:數據安全存儲
目標和期望達到的效果:
- 加密存儲機密數據,本機可以解密使用該數據。
- 加密密鑰一機一密,本機密文數據不能在另一臺設備上解密,有防克隆效果。
實現方案:
- 設備中的關鍵數據加密存儲
- 采用 SAES Normal 模式對數據加密和解密,選擇 DHUK 或 DHUK⊕BHK 作為加密密鑰。
由于 DHUK 每顆芯片都不一樣,本機加密的數據只能在本機解密使用,同樣的密文數據即使復制到另外一臺設備上也無法解密,達到數據安全存儲的期望目標,如圖 7 所示。
圖7. 數據安全存儲示例
2.2 AES 密鑰安全存儲與使用
用例 2:AES 密鑰的安全存儲與使用
目標和期望達到的效果:
- 用戶需要存儲自己的 AES 密鑰,例如預置的共享密鑰,用于數據通信中的加解密。
- 本機應用可以使用存儲的 AES 密鑰進行加解密操作。
- 密鑰在存儲和使用過程中始終保持密文形態,且使用過程中全程不暴露原始 AES 密鑰明文,即軟件、DMA、調試端口都無法獲取 AES 密鑰明文內容。
- 加密存儲使用的密鑰為一機一密,存儲的密文密鑰不能在另一 MCU 上解密。
實現方案 1:
- 密鑰生成導入階段:使用 SAES Key Wrap(密鑰封裝模式)加密原始密鑰,并存儲封裝后的密文密鑰數據,采用 DHUK 或 DHUK⊕BHK 作為密鑰進行封裝操作,設備上只存儲密文密鑰,導入后明文密鑰可以銷毀。
- 密鑰使用階段:首先使用 SAES Key Wrap(密鑰封裝模式)進行解封裝,也就是對密文密鑰進行解密,密鑰后的明文密鑰只出現在 SAES 內部,且會自動導入 SAES密鑰寄存器,此后,SAES 可以使用該密鑰進行數據加解密。
實現方案 2:
- 密鑰生成導入階段:使用 SAES Key Share(密鑰共享模式)加密原始密鑰,并存儲加密后的密文密鑰數據,采用 DHUK 或 DHUK⊕BHK 作為密鑰進行封裝操作,設備上只存儲密文密鑰,導入后明文密鑰可以銷毀。
- 密鑰使用階段:首先使用 SAES Key Share(密鑰共享模式)對密文密鑰進行解密,解密后的明文密鑰由 SAES 經過私有總線直接導入 AES 單元,此后,AES 模塊可以使用該密鑰進行數據加解密。
由于用戶的 AES 密鑰只以密文形式存儲,且 DHUK 每顆芯片都不一樣,本機存儲的加密后的密鑰數據只能在本機解密使用,從而達到 AES 密鑰安全存儲的目的。使用過程中,AES 密鑰明文只出現在 SAES 或者 AES 模塊內部,不會被軟件獲取,因而也達到了 AES密鑰安全使用的效果。方案 1 的優點在于 SAES 本身有側信道攻擊防御能力,如果對密鑰的保護有更高要求的時候可以采用方案 1;方案 2 中數據的加解密使用 AES 而非 SAES 單元,因此不具備側信道攻擊防御,但是也因此算法的執行效率更高,如果對加解密效率有更多要求的時候可以采用方案 2。
圖8. AES 密鑰安全存儲與使用示例
2.3 非對稱密鑰安全存儲
用例 3:非對稱密鑰的安全存儲與使用
目標和期望達到的效果:
- 以密文形式存儲設備私鑰,加密采用一機一密
- 存儲的密文密鑰不能在另一 MCU 上解密,防克隆
- 保護非對稱密鑰的訪問和使用,只允許關鍵可信代碼使用密鑰進行簽名驗簽等操作
實現方案:
- 通過 SAES+HUK 對密鑰進行加密(私鑰加密存儲,公鑰部分增加完整性校驗數據,可以選擇加密或不加密),使用 SAES 普通模式對密鑰數據加密和解密,密鑰來源選擇 DHUK 或 DHUK⊕BHK,實現一機一密安全存儲,防克隆
- 支持 TrustZone 架構的系列上,可以結合 TrustZone 安全隔離機制,密鑰存儲于Flash 安全區,存儲的密鑰只能由運行于安全區的可信代碼操作,運算結果輸出給非安全區的應用程序使用。
以圖 9 所示為例,假設 MCU 采用 STM32U5/WBA/H5/N6 系列,支持 TrustZone架構。非對稱密鑰數據首先經過 SAES 加密,密鑰可以選擇 DHUK 或者 DHUK⊕BHK,加密后的密文密鑰存儲于 Flash 的安全區(或者在 STM32H5 上可以存儲在 OBK 區,OBK區數據只能被安全代碼訪問)。需要使用密鑰的時候,非安全側代碼調用安全側提供的API,進行簽名驗簽等操作,安全側代碼用 DHUK(或者 DHUK⊕BHK)對存儲的密文密鑰數據進行解密,解密后的明文密鑰臨時存放在安全側的 SRAM 中,然后使用密鑰做簽名/驗簽運算,運算結束后清除 SRAM 中的臨時明文密鑰數據,并將運算結果(比如簽名數據或者驗簽結果)通過 API 接口返回給非安全側的應用程序。整個過程中 Flash 中存儲的只有密文密鑰,且密文密鑰數據只能被本機解密,如果復制到其他設備上無法得到正確的解密結果,密鑰使用過程中密鑰明文只出現在 TrustZone 安全側,只有安全側的可信代碼才能使用密鑰數據進行運算,非安全側代碼、DMA 都無法訪問到安全側的任何數據,如果開啟了調試端口保護功能,比如 STM32U5/WBA 上 RDP 級別設置到 0.5 或以上級別、STM32H5 Product State 設置到 TZ Closed 或者 Closed 級別,或者 STM32N6 處于默認的 BSEC Closed 狀態且軟件沒有主動開啟調試功能,則調試端口也無法訪問到密鑰數據,無論是明文還是密文密鑰。
圖9. 非對稱密鑰安全存儲與使用示例
3 SAES/AES + HUK 使用常見問題
本小節將列舉一些使用 SAES/AES+HUK 中的常見問題和解決方法。
3.1 SAES 初始化失敗
3.1.1 問題現象
客戶在 STM32U5 上使用 TF-M,但是沒有使用 MCUBoot 的安全啟動部分,Bootloader 采用自己的代碼。TF-M 編譯選項中開啟了 Crypto 硬件加速,也就是使能了 TFM_HW_ACCEL_ENABLE 編譯選項,從串口日志上看到 tfm_crypto_init 初始化失敗,單步調試發現:1)單步調試時發現 tfm_crypto_init 執行時跳轉不正確;2)單步調試到HAL_CRYP_Init 程序時,發現其初始化失敗,位置如下:
3.1.2 問題分析
STM32U5 的 CubeFW 包中提供的 TF-M Example 包含兩個部分,一個是基于 MCUBoot 的安全啟動部分,一個是 TF-M 和 NS 應用,默認是從 MCUBoot 啟動,再跳轉 TF-M 工程。采用默認的啟動流程時,問題不會出現,而單獨執行 TF-M 工程時能夠看到 CRYP 初始化失敗的問題。
進一步研究發現時鐘初始化的代碼只在 MCUBoot 工程中執行一次,跳轉 TF-M 工程后,TF-M 中不會再做時鐘初始化。時鐘初始化的代碼中有兩個重要的部分會影響到 CRYP/SEAS,即 HSI48 和 SHSI 時鐘的使能。
原因是 STM32U5 的 SAES 算法內核由一個單獨的 SHSI 驅動,這個時鐘不使能,SAES 無法執行正常的算法運算,同時 SAES 的側信道攻擊防御實現需要用到 RNG 單元產生的隨機數,使用 SAES 必須要同時使能 RNG 單元,RNG 默認依賴 HSI48 時鐘源,因此使用 SAES 至少需要把 HSI48 和 SHSI 都使能。
3.1.3 解決方法與結論
如果在 STM32U5 上單獨使用 TF-M,或者使用客戶自己的 bootloader 配合 TF-M,需要在 Platform 初始化代碼中至少使能 HSI48 和 SHSI 兩個時鐘。如果用戶的bootloader 工程沒有做特別的時鐘初始化,則一個簡單的方法是把 MCUBoot 工程的Platform 初始化代碼中的 clock 初始化函數復制添加到 TF-M 工程中(比如SetSysClock(void)函數),確保 HSI48 和 SHSI 都使能。
這里另外提示一下,在目前已經發布的幾款帶有 SAES 和 HUK 的 STM32 系列產品中,這個 SHSI 沒有使能問題通常只會在使用 STM32U5 系列的時候遇到,因為其他的幾個系列,例如 STM32H5,STM32WBA,STM32H7S,STM32N6 系列中,這些系列上SAES 的運算核不再由單獨的 SHSI 驅動,內核和總線時鐘使用相同的時鐘源,只要 RCC中使能了 IP 的時鐘就可以使用 SAES 進行加解密運算了。
3.2 STM32U5 從 stop 模式喚醒后 SAES 加解密結果異常
3.2.1 問題現象
客戶 App 在使用 SAES 進行 GMAC 運算時,得到的結果全為 0。之前在做工廠版本測試時,并沒有出現這個問題,后續使用用戶版本測試時,發現這個問題。
3.2.2 問題分析
客戶測試發現:
1)剛剛上電開機的時候是可以產生正確的 GMAC tag 的
2)用戶版本 App 會經常進入低功耗模式,再喚醒,一旦進入低功耗模式,喚醒后再執行 SAES GMAC 操作就會出現運算結果全 0 的現象。
通過調查得知 Factory 版本總是在 run 模式,不會進低功耗,而 User 版 App 是開機幾秒鐘就會進入 stop 模式,喚醒后也會每隔一段時間進入 stop 模式。
根據現象猜測是因為進入 STOP 模式再喚醒之后有些時鐘沒有恢復。因為 STM32U5?SAES 除了普通的總線時鐘,還需要單獨的 SHSI 來驅動,因此嘗試檢查 RCC->CR 寄存器的 SHSIRDY 和 SHSION bit:
通過在線調試,在問題現象產生時讀取 RCC->CR 寄存器 (0x56020C00)
- Stop 模式下讀到都是 0 (正常)
- 喚醒后讀到的結果是 33030535
- 剛剛上電正常情況下讀到結果是 3303C535
由此可以判斷進入 stop 模式之后喚醒的時候 SHSI 沒有使能,這意味著從 stop 模式喚醒后 SAES 的內核驅動時鐘沒有使能,因此雖然軟件可以操縱 SAES 寄存器,但是SAES 無法進行實際運算,導致運算結果輸出都為 0。
3.2.3 解決方法和結論
從 stop 模式喚醒后,除了恢復相關外設時鐘,在使能 SAES 時鐘之前,增加 HSI48 和SHSI 時鐘的恢復使能。
STM32U5 的 SAES 的正常工作,除了外設時鐘本身以外,還需要 SHSI 來驅動其算法內核,另外 SAES 的側信道攻擊防御實現需要用到 RNG 單元產生的隨機數,因此還需要使能 RNG 依賴的 HSI48 時鐘,HSI48 和 SHSI 除了需要在正常的上電復位初始化中使能,如果系統會進入低功耗模式(例如 stop1, stop2),那么在喚醒之后也需要在相關code 中添加恢復這兩個時鐘的使能代碼。如果在使用 SAES 之前沒有使能 RNG 模塊,也要增加 RNG 模塊相關的使能代碼。
3.3 使用硬件密鑰進行多次 ECB/CBC 運算后出現解密結果異常
3.3.1 問題現象
客戶用 SAES+DHUK Normal 模式加密了一段數據,密文數據存放在 Flash,軟件代碼中有一個函數負責對該段密文數據進行解密,上電復位后,這個函數可能會被調用多次。
程序初始化過程中 MX_SAES_AES_Init() 函數會對 CRYP 進行配置并調用 HAL driver的 CRYP 初始化函數。數據解密函數實現中直接調用 HAL_CRYP_Decrypt()函數對數據進行解密。類似如下代碼:
測試中發現,第一次調用解密 API 函數時,可以得到正確的結果,解密后的數據與原始明文相符,而再次調用相同的 API 函數,得到的結果卻發生了變化,不再是期望的明文數據,而是其他數據。
類似的現象發生在 SAES Key Share 模式。操作過程大致如下:
? HAL_CRYP_Init() 初始化 Instance 為 SAES,其中和 HUK 相關的配置包括
- 密鑰來源 KeySelect 選擇硬件密鑰 CRYP_KEYSEL_HW
- 密鑰模式 KeyMode 選擇為 Key share 模式 CRYP_KEYMODE_SHARED
? 初始化完成后進行密鑰解密和導入操作
- 密文密鑰通過 HAL_CRYPEx_DecryptSharedKey() 函數解密并導入 AES 模塊。
? 接下來調用 HAL_CRYP_Init() 重新配置初始化 CRYP 模塊
- Instance 選擇 AES
- 密鑰模式 KeyMode 選擇為 Key share 模式 CRYP_KEYMODE_SHARED
? 其后使用 AES Instance 做數據加解密
- 調用 HAL_CRYP_Decrypt() 對一段事先用已知密鑰加密過的數據進行解密
- 第一次調用可以得到預期的解密后的明文數據
- 再次調用解密函數,無法得到正確結果
3.3.2 問題分析
這兩個問題有相似性,都是使用硬件密鑰時,ECB/CBC 模式解密操作只有第一次能得到正確解密結果,不做其他額外操作,直接再次調用解密函數將無法得到正確的解密結果。
進一步調查發現,如果在解密函數前每次都做一遍 CRYP 反初始化和再次初始化,即調用 HAL_CRYP_DeInit()和 HAL_CRYP_Init()函數重新初始化 SAES/AES 之后,解密操作總是能夠得到正常結果。
閱讀芯片的參考手冊,可以看到 AES/SAES 模塊對于幾個加解密運算模式的描述
其中 Mode 1 是加密模式,Mode3 是解密模式,對于 ECB 和 CBC Chaining 模式,解密操作之前需要經過一個 Mode 2,也就是密鑰準備操作,這里會有一個 key?derivation 運算,用于從原始的加密 Key 得到解密 Key,Mode 2 操作之后,解密密鑰準備完成,解密密鑰數據會自動保存在密鑰寄存器當中,之后才能使用 Mode 3 進行數據的解密運算。
在使用軟件密鑰的時候,HAL_CRYP_Decrypt()函數會完成密鑰寫入寄存器,設置Mode 2 準備解密密鑰,再設置 Mode 3 進行數據解密的一系列操作。當使用硬件密鑰的時候,HAL_CRYP_Decrypt()函數并不會進行密鑰寫入寄存器的動作,因為密鑰來自硬件,通常會假設調用 HAL_CRYP_Decrypt()函數時,硬件密鑰已經導入密鑰寄存器,只需要做第二步 Mode 2 的解密密鑰準備和第三步 Mode 3 的數據解密就可以了。因此,這種使用場景下,第一次調用 HAL_CRYP_Decrypt()函數可以得到正確的解密結果。
在第一次完成解密過程之后,密鑰寄存器里面已經是經過 Mode 2 運算的解密密鑰了,也就是 KeyDec,而非原始的加密密鑰 KeyEnc,這時候再次調用 HAL_CRYP_Decrypt()函數做數據解密,依舊需要先經過密鑰準備過程,這會導致密鑰準備結束后,密鑰寄存器的內容變成由 KeyDec 派生得到的密鑰(KeyDec)Dec,而不是從原本的 KeyEnc派生得到的解密密鑰 KeyDec,這會導致后續的解密實際上用了錯誤的密鑰,如圖 10 所示。
圖10. 解密過程中密鑰寄存器數據的變化
3.3.3 解決方法和結論
使用 SAES 硬件密鑰做 ECB/CBC 解密操作,或者使用 AES 選擇經過 SAES 用 Key?share 模式導入的密鑰進行 ECB/CBC 解密操作時,在調用 HAL_CRYP_Decrypt()函數之前,需要確保密鑰寄存器里面已經加載了原始的加密密鑰。也就是說在兩個連續的 HAL_CRYP_Decrypt() 函數調用之間,需要重新加載密鑰到密鑰寄存器。由于硬件導入的密鑰并不是通過軟件寫入,因此需要讓 CRYP 再次加載原始的硬件密鑰。為了達到這個目的,有兩種解決方法。
方法一:在兩次 HAL_CRYP_Decrypt()函數調用之間,進行 CRYP 反初始化和初始化,也就是調用 HAL_CRYP_DeInit()和 HAL_CRYP_Init()函數重新初始化 SAES 或者 AES(重新初始化 SAES 還是 AES 取決于解密數據使用 SAES 還是 AES 模塊)。
方法二:在兩次 HAL_CRYP_Decrypt() 函數調用之間,調用 HAL_CRYP_GetConfig() 和 HAL_CRYP_SetConfig(),重新寫入 CRYP 的配置參數,示例代碼如下:
提示:如果使用 Key wrap 模式,首先對密文密鑰進行 unwrap,然后再用 unwrap 后的明文密鑰進行 ECB/CBC 解密操作,也會出現類似的連續兩次解密后輸出數據異常問題,產生問題的原因是類似的,都和 decryption 所需要的密鑰準備操作有關,但是 key wrap 模式時,類似的問題并不能夠通過在兩次解密之間重新初始化 CRYP 或者重新配置 CRYP 的方法解決。使用 unwrap 的 Key 作為 ECB/CBC模式解密密鑰時,需要每次都先做unwrap,然后再做數據解密,也就是每次數據解密總是需要先調用HAL_CRYPEx_UnwrapKey()來獲得解密密鑰,然后再調用HAL_CRYP_Decrypt()做數據解密。
3.4 使用 SAES+HUK 加密數據時在不同芯片上得到的密文數據都一樣
3.4.1 問題現象
客戶嘗試使用 DHUK 作為密鑰來源,用 SAES 對一段數據進行加解密,因為 DHUK 是從硬件唯一密鑰 HUK 派生而來,在不同芯片上應該產生不同的 DHUK,那么對相同的數據進行加解密應該得到不同的結果,然而用幾個不同的芯片測試發現加解密的結果都相同,不符合唯一密鑰的預期。
3.4.2 問題分析
STM32 的硬件設計上對 HUK 進行了保護,在調試完全開放的狀態下(即 Open 狀態)無法使用真正的 HUK,此時雖然也可以測試 SAES+DHUK 的數據加解密,但是在不同芯片上的 HUK 表現為一樣的值,因此對同一段數據加解密的結果也相同。真正的 HUK只有在非 Open 狀態下才可以使用。
這里說的 Open 狀態和非 Open 狀態,在不同的系列上可能指的是不同的芯片生命周期狀態配置,比如在 Option Byte 支持 RDP 的系列上,Open 狀態即為出廠默認的 RDP?level 為 0 的狀態,也就是調試端口沒有任何保護的狀態,非 Open 狀態可以是 RDP?level1 或者 level2,如果芯片支持 TrustZone,RDP level0.5 關閉安全側調試的狀態也是非 Open 狀態。在 Option Byte 支持 Product State 的系列上,Open 狀態即為出廠默認的 Product State=OPEN 的狀態,其他狀態,例如 Provisioning, Provisioned, Closed,?TZ Closed 等均為非 Open 狀態,詳細的 Product State 狀態說明請參考各個系列的參考手冊。
表 3 列舉了本文提及的幾個系列中芯片生命周期狀態配置以及對 HUK 的影響。
表3. 幾個系列中 HUK 與芯片生命周期狀態的關系
3.4.3 解決方法和結論
當芯片處在 Open 狀態,真實 HUK 無法使用時,其表現為不同芯片的 DHUK 值相同。因此,如果想要測試 HUK 的硬件密鑰唯一性,則需要根據不同系列的特性,將 RDP或者 Product State 設置到非 Open 狀態后,再進行測試。
4 總結
前文中提到的幾類常見問題的總結見 Error! Reference source not found.。
表4. SAES+HUK 使用中的常見問題和解決方法
5 小結
本文首先簡要回顧了幾個產品系列當中 SAES 和 HUK 的主要特性以及異同,然后通過幾個用例介紹了基于 SAES 和 HUK 在不同應用場景中如何實現數據和密鑰的安全存儲與使用,最后列舉了幾個 SAES 和 HUK 使用中的常見問題以及解決方法。本文提及的主要是目前已經量產上市的幾個支持 SAES 和 HUK 的系列,包括STM32U5,STM32H5,STM32WBA,STM32H7S,STM32N6,此外后續的一些新產品也會帶有 SAES 和 HUK 特性,對相關的新產品來說,本文的內容也會有一定的參考價值。希望這篇文章能夠對于希望在 STM32MCU 上使用 SAES 和 HUK 的開發者有所幫助。
?