與大多數磁盤和文件加密軟件一樣,Android 的存儲加密傳統上依賴於系統內存中存在的原始加密密鑰,以便可以執行加密。即使當加密是由專用硬件而不是軟件執行時,軟件通常仍然需要管理原始加密密鑰。
傳統上這並不被視為問題,因為在離線攻擊期間密鑰不會出現,而離線攻擊是存儲加密旨在防範的主要攻擊類型。然而,人們希望提供針對其他類型攻擊的增強保護,例如冷啟動攻擊和在線攻擊,其中攻擊者可能能夠在不完全損害設備的情況下泄漏系統內存。
為了解決這個問題,Android 11引入了對硬件封裝按鍵的支持,其中硬件支持是存在的。硬件封裝密鑰是僅以原始形式為專用硬件所知的存儲密鑰;軟件只能看到並使用這些封裝(加密)形式的密鑰。該硬件必須能夠生成和導入存儲密鑰、以臨時和長期形式包裝存儲密鑰、派生子密鑰、將一個子密鑰直接編程到內聯加密引擎中以及將單獨的子密鑰返回給軟件。
注意:內聯加密引擎(或內聯加密硬件)是指在數據傳入/傳出存儲設備時對數據進行加密/解密的硬件。通常這是一個 UFS 或 eMMC 主機控制器,用於實現相應 JEDEC 規範定義的加密擴展。
設計
本節介紹硬件封裝按鍵功能的設計,包括它需要哪些硬件支持。本討論重點討論基於文件的加密(FBE),但該解決方案也適用於元數據加密。
避免在系統內存中需要原始加密密鑰的一種方法是將它們僅保存在內聯加密引擎的密鑰槽中。然而,這種方法遇到了一些問題:
- 加密密鑰的數量可能超過密鑰槽的數量。
- 內聯加密引擎只能用於加密/解密磁盤上的完整數據塊。然而,就 FBE 而言,軟件仍然需要能夠執行其他加密工作,例如文件名加密和派生密鑰標識符。軟件仍然需要訪問原始 FBE 密鑰才能完成其他工作。
為了避免這些問題,存儲密鑰被製成硬件包裝密鑰,只能由專用硬件解包和使用。這允許支持無限數量的密鑰。此外,密鑰層次結構經過修改並部分移至該硬件,這允許將子密鑰返回給軟件以執行無法使用內聯加密引擎的任務。
關鍵層次結構
可以使用KDF(密鑰派生函數) (例如HKDF)從其他密鑰派生密鑰,從而形成密鑰層次結構。
下圖描述了不使用硬件封裝密鑰時 FBE 的典型密鑰層次結構:
FBE 類密鑰是 Android 傳遞給 Linux 內核以解鎖一組特定加密目錄的原始加密密鑰,例如特定 Android 用戶的憑證加密存儲。 (在內核中,該密鑰稱為fscrypt 主密鑰。)內核從該密鑰派生出以下子密鑰:
- 密鑰標識符。這不用於加密,而是用於標識保護特定文件或目錄的密鑰的值。
- 文件內容加密密鑰
- 文件名加密密鑰
相反,下圖描述了使用硬件包裝密鑰時 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,它使用NIST SP 800-108“計數器模式下的 KDF”,並以AES-256-CMAC作為 PRF。請注意,為了兼容,算法的所有部分都必須相同,包括 KDF 上下文的選擇和每個子密鑰的標籤。
密鑰包裝
為了滿足硬件包裝密鑰的安全目標,定義了兩種類型的密鑰包裝:
- 臨時包裝:硬件使用每次啟動時隨機生成的密鑰對原始密鑰進行加密,並且不會直接暴露在硬件之外。
- 長期包裝:硬件使用硬件內置的唯一、持久密鑰對原始密鑰進行加密,該密鑰不會直接暴露在硬件外部。
所有傳遞給 Linux 內核以解鎖存儲的密鑰都被臨時包裝。這確保瞭如果攻擊者能夠從系統內存中提取正在使用的密鑰,則該密鑰不僅在設備外無法使用,而且在重新啟動後在設備上也無法使用。
與此同時,Android 仍然需要能夠在磁盤上存儲密鑰的加密版本,以便能夠首先解鎖它們。原始密鑰可以用於此目的。然而,我們希望原始密鑰根本不存在於系統內存中,這樣即使在啟動時提取它們,它們也永遠不會被提取出來在設備外使用。為此,定義了長期包裹的概念。
為了支持管理以這兩種不同方式包裝的密鑰,硬件必須實現以下接口:
- 生成和導入存儲密鑰的接口,以長期包裝形式返回它們。這些接口通過KeyMint間接訪問,它們對應於KeyMint標籤
TAG_STORAGE_KEY
。vold
使用“生成”功能來生成供 Android 使用的新存儲密鑰,而vts_kernel_encryption_test
使用“導入”功能來導入測試密鑰。 - 將長期包裝存儲密鑰轉換為臨時包裝存儲密鑰的接口。這對應於
convertStorageKeyToEphemeral
KeyMint 方法。vold
和vts_kernel_encryption_test
都使用此方法來解鎖存儲。
密鑰包裝算法是一個實現細節,但它應該使用強大的 AEAD,例如帶有隨機 IV 的 AES-256-GCM。
需要更改軟件
AOSP 已經有了支持硬件封裝密鑰的基本框架。這包括對vold
等用戶空間組件的支持,以及blk-crypto 、 fscrypt和dm-default-key中的 Linux 內核支持。
但是,需要進行一些特定於實現的更改。
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
。有關更多詳細信息,請參閱元數據加密文檔。