和大多數磁碟和檔案加密軟體一樣,Android 的儲存空間加密功能通常會依賴系統記憶體中的原始加密金鑰,才能執行加密作業。即使是透過專用硬體而非軟體執行加密作業,軟體通常仍需要管理原始加密金鑰。
傳統上,這不會視為問題,因為在離線攻擊期間不會出現金鑰,而離線攻擊是儲存加密機制用來防範的主要攻擊類型。不過,我們希望能提供更強大的防護,對抗其他類型的攻擊,例如冷啟動攻擊和線上攻擊,攻擊者可能會在未完全危害裝置的情況下,洩漏系統記憶體。
為解決這個問題,Android 11 推出了硬體包裝金鑰支援功能,前提是硬體支援。硬體包裝金鑰是儲存金鑰,只有專用硬體會以原始形式知道這些金鑰;軟體只會看到並處理這些金鑰的包裝 (加密) 形式。這項硬體必須能夠產生及匯入儲存空間金鑰、以暫時和長期形式包裝儲存空間金鑰、衍生子金鑰、直接將一個子金鑰程式設計至內嵌加密引擎,以及將個別子金鑰傳回至軟體。
注意:「內嵌加密引擎」(或「內嵌加密硬體」) 是指在資料傳輸至/從儲存裝置的過程中,用於加密/解密資料的硬體。通常是 UFS 或 eMMC 主機控制器,會實作相應 JEDEC 規格定義的加密編譯擴充功能。
設計
本節將說明硬體包裝金鑰功能的設計,包括需要哪些硬體支援。本討論重點是檔案型加密 (FBE),但解決方案也適用於中繼資料加密。
避免在系統記憶體中使用原始加密金鑰的其中一種方法,就是將這些金鑰只保留在內嵌加密編譯引擎的金鑰槽中。不過,這項做法會遇到一些問題:
- 加密金鑰的數量可能會超過金鑰專用的記憶體位址數量。
- 如果儲存控制器 (通常為 UFS 或 eMMC) 重設,內嵌加密引擎通常會遺失其金鑰插槽的內容。重設儲存空間控制器是標準錯誤復原程序,會在發生特定類型的儲存空間錯誤時執行,而這類錯誤隨時都可能發生。因此,在使用內嵌加密編譯時,作業系統必須隨時準備好重新編寫金鑰槽,無須使用者介入。
- 內嵌加密引擎只能用於加密/解密磁碟上的完整資料區塊。不過,在 FBE 的情況下,軟體仍需能夠執行其他加密編譯作業,例如檔案名稱加密和衍生金鑰 ID。軟體仍需要存取原始 FBE 金鑰,才能執行這項其他作業。
為避免這些問題,儲存金鑰會改為硬體包裝金鑰,只能由專用硬體解開並使用。這樣一來,系統就能支援無限數量的鍵。此外,金鑰階層經過修改,然後部分移至該硬體。如果工作無法使用內嵌加密編譯引擎,系統就會將子金鑰傳回軟體。
金鑰階層
您可以使用 KDF (金鑰衍生函式) (例如 HKDF) 從其他鍵衍生金鑰,進而產生金鑰階層。
下圖顯示,當「不」使用硬體包裝金鑰時,FBE 的典型金鑰階層:
FBE 類別金鑰是 Android 傳遞至 Linux 核心的原始加密金鑰,用於解鎖特定的加密目錄組合,例如特定 Android 使用者的憑證加密儲存空間。(在核心中,這個金鑰稱為 fscrypt 主金鑰)。核心會從這個鍵衍生出下列子鍵:
- 金鑰 ID。這不是用於加密,而是用來識別用於保護特定檔案或目錄的金鑰。
- 檔案內容加密金鑰
- 檔案名稱加密金鑰
相反地,下圖顯示使用硬體包裝金鑰時,FBE 的金鑰階層:
與前述案例相比,金鑰階層增加了額外的層級,且檔案內容加密金鑰已移動位置。根節點仍代表 Android 傳遞至 Linux 的金鑰,用於解鎖一組已加密的目錄。不過,由於金鑰是暫時包裝的格式,因此必須傳遞至專屬硬體才能使用。此硬體必須實作兩個介面,以便取得臨時包裝的金鑰:
- 一個介面,用於擷取
inline_encryption_key
,並直接將其編程至內嵌加密引擎的鍵槽。這樣一來,即使軟體無法存取原始金鑰,也能加密/解密檔案內容。在 Android 通用核心中,此介面會對應至blk_crypto_ll_ops::keyslot_program
作業,而儲存空間驅動程式必須實作此作業。 - 一個介面,用於衍生並傳回
sw_secret
(「軟體密鑰」-- 在某些地方也稱為「原始密鑰」),這是 Linux 用來衍生檔案內容加密以外所有項目的子金鑰。在 Android 通用核心中,此介面會對應至blk_crypto_ll_ops::derive_sw_secret
作業,而儲存空間驅動程式必須實作此作業。
如要從原始儲存金鑰衍生 inline_encryption_key
和 sw_secret
,硬體必須使用經過加密編譯的強 KDF。這個 KDF 必須遵循密碼編譯最佳做法,安全強度必須至少為 256 位元,也就是足以支援日後使用的任何演算法。另外,在衍生各類型子鍵時,也必須使用獨特的標籤、內容和應用程式專屬資訊字串,以確保產生的子鍵經過加密隔離,也就是說,如果知道其中一個子鍵,就不會洩漏其他子鍵。由於原始儲存空間金鑰已是均一隨機金鑰,因此不需要進行鍵延展作業。
從技術層面來說,任何符合安全性規定的 KDF 都可以使用。不過,為了測試,您必須在測試程式碼中重新實作相同的 KDF。目前,我們已審查並實作一個 KDF,您可以在 vts_kernel_encryption_test
的原始碼中找到該 KDF。硬體建議使用這個 KDF,該 KDF 會使用 NIST SP 800-108「KDF in Counter Mode」,並以 AES-256-CMAC 做為 PRF。請注意,為了確保相容性,演算法的所有部分都必須相同,包括 KDF 上下文的選擇,以及每個子鍵的標籤。
金鑰包裝
為了達成硬體包裝金鑰的安全性目標,我們定義了兩種金鑰包裝方式:
- 暫時性包裝:硬體會使用隨機產生且不會直接在硬體外部公開的金鑰,加密原始金鑰。
- 長期包裝:硬體會使用硬體內建的不重複永久金鑰加密原始金鑰,不會直接在硬體外公開。
所有傳遞至 Linux 核心的金鑰,都是為瞭解鎖儲存空間而暫時包裝的。這可確保即使攻擊者從系統記憶體中擷取已使用的金鑰,該金鑰仍無法在裝置關機後或重新啟動後使用。
同時,Android 仍需能夠在磁碟上儲存金鑰的加密版本,以便在第一時間解鎖金鑰。原始鍵可用於此用途。不過,建議您不要讓原始金鑰出現在系統記憶體中,這樣一來,即使在啟動期間擷取,也無法將原始金鑰擷取到裝置外使用。因此,我們定義了長期包裝的概念。
為了支援以這兩種不同方式包裝的金鑰,硬體必須實作下列介面:
- 產生及匯入儲存空間金鑰的介面,以長期包裝形式傳回。這些介面會透過 KeyMint 間接存取,且對應至
TAG_STORAGE_KEY
KeyMint 標記。vold
會使用「產生」功能,為 Android 產生可供使用的新儲存空間金鑰,而vts_kernel_encryption_test
會使用「匯入」功能匯入測試金鑰。 - 這個介面可將長期包裝的儲存空間金鑰轉換為暫時包裝的儲存空間金鑰。這會對應至
convertStorageKeyToEphemeral
KeyMint 方法。vold
和vts_kernel_encryption_test
都會使用這個方法解鎖儲存空間。
金鑰包裝演算法是實作細節,但應使用強大的 AEAD,例如 AES-256-GCM 搭配隨機 IV。
需要變更軟體
AOSP 已提供基本架構,可支援硬體包裝金鑰。包括支援使用者空間元件 (例如 vold
),以及 blk-crypto、fscrypt 和 dm-default-key 中的 Linux kernel 支援。
不過,您需要進行一些針對實作方面的變更。
KeyMint 異動
必須修改裝置的 KeyMint 實作項目,以便支援 TAG_STORAGE_KEY
並實作 convertStorageKeyToEphemeral
方法。
在 Keymaster 中,系統使用的是 exportKey
,而不是 convertStorageKeyToEphemeral
。
Linux 核心變更
您必須修改裝置內嵌加密引擎的 Linux 核心驅動程式,才能支援硬體包裝的金鑰。
針對 android14
以上版本的核心,請在 blk_crypto_profile::key_types_supported
中設定 BLK_CRYPTO_KEY_TYPE_HW_WRAPPED
,讓 blk_crypto_ll_ops::keyslot_program
和 blk_crypto_ll_ops::keyslot_evict
支援程式設計/淘汰硬體包裝的鍵,並實作 blk_crypto_ll_ops::derive_sw_secret
。
針對 android12
和 android13
核心,請在 blk_keyslot_manager::features
中設定 BLK_CRYPTO_FEATURE_WRAPPED_KEYS
,讓 blk_ksm_ll_ops::keyslot_program
和 blk_ksm_ll_ops::keyslot_evict
支援編寫/撤銷硬體包裝的金鑰,並實作 blk_ksm_ll_ops::derive_raw_secret
。
針對 android11
核心,請在 keyslot_manager::features
中設定 BLK_CRYPTO_FEATURE_WRAPPED_KEYS
,讓 keyslot_mgmt_ll_ops::keyslot_program
和 keyslot_mgmt_ll_ops::keyslot_evict
支援程式設計/淘汰硬體包裝的鍵,並實作 keyslot_mgmt_ll_ops::derive_raw_secret
。
測試
雖然使用硬體包裝金鑰進行加密的測試比使用標準金鑰更難,但仍可透過匯入測試金鑰,並重新實作硬體所執行的金鑰衍生作業來進行測試。這項功能已在 vts_kernel_encryption_test
中實作。如要執行這項測試,請執行:
atest -v vts_kernel_encryption_test
請閱讀測試記錄,並確認硬體包裝金鑰測試案例 (例如 FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy
和 DmDefaultKeyTest.TestHwWrappedKey
) 並未因未偵測到硬體包裝金鑰的支援而略過,因為在這種情況下,測試結果仍會是「通過」。
啟用按鍵
裝置的硬體包裝金鑰支援功能正常運作後,您可以對裝置的 fstab
檔案進行下列變更,讓 Android 將其用於 FBE 和中繼資料加密:
- FBE:將
wrappedkey_v0
標記新增至fileencryption
參數。例如使用fileencryption=::inlinecrypt_optimized+wrappedkey_v0
。詳情請參閱 FBE 說明文件。 - 中繼資料加密:將
wrappedkey_v0
標記新增至metadata_encryption
參數。例如使用metadata_encryption=:wrappedkey_v0
。詳情請參閱結構描述資料加密說明文件。