片上系統 (SoC) 中可信執行環境的可用性為 Android 設備提供了向 Android 操作系統、平台服務甚至第三方應用程序提供硬件支持的強大安全服務的機會。尋求 Android 特定擴展的開發人員應該訪問android.security.keystore 。
在 Android 6.0 之前,Android 已經有了一個簡單的、硬件支持的加密服務 API,由 Keymaster 硬件抽象層 (HAL) 0.2 和 0.3 版本提供。密鑰庫提供數字簽名和驗證操作,以及非對稱簽名密鑰對的生成和導入。這已經在許多設備上實現,但有許多安全目標無法僅通過簽名 API 輕鬆實現。 Android 6.0 中的 Keystore 擴展了 Keystore API 以提供更廣泛的功能。
在 Android 6.0 中,Keystore 添加了對稱加密原語、AES 和 HMAC,以及硬件支持密鑰的訪問控制系統。訪問控制在密鑰生成期間指定,並在密鑰的生命週期內強制執行。密鑰可以限制為僅在用戶經過身份驗證後才能使用,並且只能用於指定目的或使用指定的加密參數。有關更多信息,請參閱授權標籤和功能頁面。
除了擴展加密原語的範圍之外,Android 6.0 中的 Keystore 還添加了以下內容:
- 使用控制方案允許限制密鑰的使用,以減輕因濫用密鑰而造成安全損害的風險
- 一種訪問控制方案,可限制指定用戶、客戶端和定義時間範圍的密鑰
在 Android 7.0 中,Keymaster 2 添加了對密鑰證明和版本綁定的支持。密鑰證明提供公鑰證書,其中包含密鑰及其訪問控制的詳細描述,以使密鑰在安全硬件中的存在及其配置可遠程驗證。
版本綁定將密鑰綁定到操作系統和補丁級別版本。這可以確保發現舊版本系統或 TEE 軟件中存在漏洞的攻擊者無法將設備回滾到有漏洞的版本並使用使用新版本創建的密鑰。另外,當給定版本和補丁級別的密鑰在已升級到較新版本或補丁級別的設備上使用時,該密鑰將先升級後才能使用,並且之前版本的密鑰將失效。當設備升級時,密鑰會隨著設備一起“棘輪”前進,但設備恢復到以前的版本會導緻密鑰無法使用。
在 Android 8.0 中,Keymaster 3 從舊式 C 結構硬件抽象層 (HAL) 過渡到根據新硬件接口定義語言 (HIDL) 中的定義生成的 C++ HAL 接口。作為更改的一部分,許多參數類型發生了更改,儘管類型和方法與舊類型和 HAL 結構方法具有一一對應的關係。請參閱功能頁面了解更多詳細信息。
除了此接口修訂之外,Android 8.0還擴展了Keymaster 2的認證功能以支持ID認證。 ID 證明提供了一種有限且可選的機制,用於對硬件標識符進行強有力的證明,例如設備序列號、產品名稱和手機 ID (IMEI / MEID)。為了實現此添加,Android 8.0 更改了 ASN.1 證明架構以添加 ID 證明。 Keymaster 實現需要找到某種安全的方法來檢索相關數據項,並定義一種安全且永久禁用該功能的機制。
在 Android 9 中,更新包括:
- 更新至Keymaster 4
- 支持嵌入式安全元件
- 支持安全密鑰導入
- 支持3DES加密
- 更改版本綁定,以便 boot.img 和 system.img 單獨設置版本以允許獨立更新
詞彙表
以下是密鑰庫組件及其關係的快速概述。
AndroidKeystore是應用程序用來訪問密鑰庫功能的 Android 框架 API 和組件。它作為標準 Java 加密體系結構 API 的擴展來實現,並由在應用程序自己的進程空間中運行的 Java 代碼組成。 AndroidKeystore
通過將應用程序轉發到密鑰庫守護進程來滿足應用程序對密鑰庫行為的請求。
密鑰庫守護程序是一個 Android 系統守護程序,它通過Binder API提供對所有密鑰庫功能的訪問。它負責存儲“密鑰 blob”,其中包含實際的秘密密鑰材料,並經過加密,以便 Keystore 可以存儲它們,但不能使用或洩露它們。
keymasterd是一個 HIDL 服務器,提供對 Keymaster TA 的訪問。 (此名稱未標準化,僅用於概念目的。)
Keymaster TA (可信應用程序)是在安全環境中運行的軟件,通常在ARM SoC 上的TrustZone 中運行,它提供所有安全密鑰庫操作,可以訪問原始密鑰材料,驗證密鑰上的所有訪問控制條件, ETC。
LockSettingsService是 Android 系統組件,負責用戶身份驗證,包括密碼和指紋。它不是 Keystore 的一部分,但相關,因為許多 Keystore 密鑰操作需要用戶身份驗證。 LockSettingsService
與 Gatekeeper TA 和 Fingerprint TA 交互以獲取身份驗證令牌,並將其提供給密鑰庫守護程序,並最終由 Keymaster TA 應用程序使用。
Gatekeeper TA (可信應用程序)是在安全上下文中運行的另一個組件,它負責驗證用戶密碼並生成身份驗證令牌,用於向 Keymaster TA 證明在特定時間點對特定用戶進行了身份驗證。
指紋 TA (可信應用程序)是在安全上下文中運行的另一個組件,它負責對用戶指紋進行身份驗證並生成身份驗證令牌,用於向Keymaster TA 證明在特定時間點對特定用戶進行了身份驗證。
建築學
Android Keystore API 和底層 Keymaster HAL 提供了一組基本但足夠的加密原語,允許使用訪問控制、硬件支持的密鑰來實現協議。
Keymaster HAL 是 OEM 提供的動態可加載庫,Keystore 服務使用它來提供硬件支持的加密服務。為了確保安全,HAL 實現不會在用戶空間甚至內核空間中執行任何敏感操作。敏感操作被委託給通過某些內核接口訪問的安全處理器。最終的架構如下所示:

圖 1.訪問 Keymaster
在 Android 設備中,Keymaster HAL 的“客戶端”由多個層組成(例如應用程序、框架、Keystore 守護程序),但就本文檔而言可以忽略這些層。這意味著所描述的 Keymaster HAL API 是低級的,由平台內部組件使用,並且不暴露給應用程序開發人員。 Android 開發者網站上描述了更高級別的 API。
Keymaster HAL 的目的不是實現安全敏感算法,而只是將請求編組和解組到安全世界。有線格式是實現定義的。
與以前版本的兼容性
Keymaster 1 HAL 與之前發布的 HAL 完全不兼容,例如 Keymaster 0.2 和 0.3。為了促進運行 Android 5.0 及更早版本(使用舊版 Keymaster HAL 啟動)的設備上的互操作性,Keystore 提供了一個適配器,通過調用現有硬件庫來實現 Keymaster 1 HAL。結果無法提供 Keymaster 1 HAL 中的全部功能。特別是,它僅支持 RSA 和 ECDSA 算法,並且所有密鑰授權強制執行均由適配器在非安全世界中執行。
Keymaster 2 通過刪除get_supported_*
方法並允許finish()
方法接受輸入,進一步簡化了 HAL 接口。在輸入一次性可用的情況下,這減少了到 TEE 的往返次數,並簡化了 AEAD 解密的實現。
在 Android 8.0 中,Keymaster 3 從舊式 C 結構 HAL 過渡到根據新硬件接口定義語言 (HIDL) 中的定義生成的 C++ HAL 接口。通過子類化生成的IKeymasterDevice
類並實現純虛方法來創建新型 HAL 實現。作為更改的一部分,許多參數類型已更改,但類型和方法與舊類型和 HAL 結構方法具有一一對應的關係。
HIDL 概述
硬件接口定義語言 (HIDL) 提供了一種獨立於實現語言的機制來指定硬件接口。 HIDL 工具當前支持生成 C++ 和 Java 接口。預計大多數可信執行環境 (TEE) 實現者會發現 C++ 工具更方便,因此本文檔僅討論 C++ 表示形式。
HIDL 接口由一組方法組成,表示為:
methodName(INPUT ARGUMENTS) generates (RESULT ARGUMENTS);
有多種預定義類型,HAL 可以定義新的枚舉和結構類型。有關 HIDL 的更多詳細信息,請參閱參考部分。
Keymaster 3 IKeymasterDevice.hal
的示例方法是:
generateKey(vec<KeyParameter> keyParams) generates(ErrorCode error, vec<uint8_t> keyBlob, KeyCharacteristics keyCharacteristics);
這相當於 keymaster2 HAL 中的以下內容:
keymaster_error_t (*generate_key)( const struct keymaster2_device* dev, const keymaster_key_param_set_t* params, keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics);
在 HIDL 版本中,刪除了dev
參數,因為它是隱式的。 params
參數不再是包含引用key_parameter_t
對像數組的指針的結構體,而是包含KeyParameter
對象的vec
(向量)。返回值列在“ generates
”子句中,包括密鑰 blob 的uint8_t
值向量。
HIDL 編譯器生成的 C++ 虛方法為:
Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams, generateKey_cb _hidl_cb) override;
其中generateKey_cb
是一個函數指針,定義為:
std::function<void(ErrorCode error, const hidl_vec<uint8_t>& keyBlob, const KeyCharacteristics& keyCharacteristics)>
也就是說, generateKey_cb
是一個函數,它採用generate 子句中列出的返回值。 HAL實現類重寫這個generateKey
方法並調用generateKey_cb
函數指針將操作結果返回給調用者。請注意,函數指針調用是同步的。調用者調用generateKey
, generateKey
調用提供的函數指針,該函數指針執行完成,將控制權返回給generateKey
實現,然後該實現返回給調用者。
有關詳細示例,請參閱hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp
中的默認實現。默認實現為具有舊式 keymaster0、keymaster1 或 keymaster2 HALS 的設備提供向後兼容性。
訪問控制
Keystore訪問控制最基本的規則是每個應用程序都有自己的命名空間。但每條規則都有一個例外。密鑰庫有一些硬編碼映射,允許某些系統組件訪問某些其他命名空間。這是一種非常生硬的工具,因為它讓一個組件完全控制另一個名稱空間。然後還有供應商組件作為密鑰庫客戶端的問題。我們目前無法為供應商組件(例如 WPA 請求者)建立命名空間。
為了適應供應商組件並在沒有硬編碼異常的情況下通用化訪問控制,Keystore 2.0 引入了域和 SELinux 命名空間。
密鑰庫域
通過密鑰庫域,我們可以將命名空間與 UID 解耦。訪問密鑰庫中密鑰的客戶端必須指定他們想要訪問的域、命名空間和別名。根據這個元組和調用者的身份,我們可以確定調用者想要訪問哪個密鑰以及它是否具有適當的權限。
我們引入了五個域參數來控制如何訪問密鑰。它們控制密鑰描述符的命名空間參數的語義以及如何執行訪問控制。
-
DOMAIN_APP
:應用程序域涵蓋舊行為。 Java Keystore SPI 默認使用此域。使用此域時,命名空間參數將被忽略,而使用調用者的 UID。對此域的訪問由 SELinux 策略中keystore_key
類的密鑰庫標籤控制。 -
DOMAIN_SELINUX
:該域表示該命名空間在 SELinux 策略中具有標籤。查找命名空間參數並將其轉換為目標上下文,並對keystore_key
類的調用 SELinux 上下文執行權限檢查。當為給定操作建立了權限後,完整元組將用於鍵查找。 -
DOMAIN_GRANT
:授權域表示命名空間參數是授權標識符。別名參數被忽略。 SELinux 檢查是在創建授權時執行的。進一步的訪問控制僅檢查調用者 UID 是否與所請求授權的受讓人 UID 匹配。 -
DOMAIN_KEY_ID
:該域表示命名空間參數是唯一的鍵id。密鑰本身可能是使用DOMAIN_APP
或DOMAIN_SELINUX
創建的。從密鑰數據庫加載domain
和namespace
空間後執行權限檢查,就像通過域、命名空間和別名元組加載 blob 一樣。密鑰 ID 域的基本原理是連續性。當通過別名訪問密鑰時,後續調用可能會對不同的密鑰進行操作,因為可能已經生成或導入了新密鑰並綁定到該別名。然而,密鑰 ID 永遠不會改變。因此,當使用別名從 Keystore 數據庫加載一次密鑰後,通過密鑰 id 使用密鑰時,只要密鑰 id 仍然存在,就可以確定它是同一個密鑰。此功能不會向應用程序開發人員公開。相反,它在 Android Keystore SPI 中使用,即使以不安全的方式同時使用,也能提供更一致的體驗。 -
DOMAIN_BLOB
:blob 域表示調用者自己管理 blob。這用於在掛載數據分區之前需要訪問密鑰庫的客戶端。密鑰 blob 包含在密鑰描述符的blob
字段中。
使用 SELinux 域,我們可以讓供應商組件訪問非常特定的密鑰庫命名空間,這些命名空間可以由系統組件(例如設置對話框)共享。
keystore_key 的 SELinux 策略
命名空間標籤是使用keystore2_key_context
文件配置的。
這些文件中的每一行都將數字名稱空間 ID 映射到 SELinux 標籤。例如,
# wifi_key is a keystore2_key namespace intended to be used by wpa supplicant and # Settings to share keystore keys. 102 u:object_r:wifi_key:s0
以這種方式設置新的密鑰命名空間後,我們可以通過添加適當的策略來授予對其的訪問權限。例如,為了允許wpa_supplicant
獲取並使用新命名空間中的密鑰,我們將以下行添加到hal_wifi_supplicant.te
:
allow hal_wifi_supplicant wifi_key:keystore2_key { get, use };
設置新的命名空間後,AndroidKeyStore幾乎可以像平常一樣使用。唯一的區別是必須指定命名空間 ID。為了從 Keystore 加載和導入密鑰,命名空間 id 是使用AndroidKeyStoreLoadStoreParameter
指定的。例如,
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter; import java.security.KeyStore; KeyStore keystore = KeyStore.getInstance("AndroidKeyStore"); keystore.load(new AndroidKeyStoreLoadStoreParameter(102));
要在給定命名空間中生成密鑰,必須使用KeyGenParameterSpec.Builder#setNamespace():
import android.security.keystore.KeyGenParameterSpec; KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder(); specBuilder.setNamespace(102);
以下上下文文件可用於配置 Keystore 2.0 SELinux 命名空間。每個分區都有不同的保留範圍(10,000 個命名空間 ID)以避免衝突。
分割 | 範圍 | 配置文件 |
---|---|---|
系統 | 0 ... 9,999 | /system/etc/selinux/keystore2_key_contexts, /plat_keystore2_key_contexts |
擴展系統 | 10,000 ... 19,999 | /system_ext/etc/selinux/system_ext_keystore2_key_contexts, /system_ext_keystore2_key_contexts |
產品 | 20,000 ... 29,999 | /product/etc/selinux/product_keystore2_key_contexts, /product_keystore2_key_contexts |
小販 | 30,000 ... 39,999 | /vendor/etc/selinux/vendor_keystore2_key_contexts, /vendor_keystore2_key_contexts |
客戶端通過請求 SELinux 域和所需的虛擬命名空間(在本例中為"wifi_key"
通過其數字 id 來請求密鑰。
在此之上,定義了以下命名空間。如果它們替換特殊規則,則下表指示它們用來對應的UID。
命名空間ID | SE政策標籤 | 用戶識別碼 | 描述 |
---|---|---|---|
0 | 蘇鍵 | 不適用 | 超級用戶密鑰。僅用於在 userdebug 和 eng 版本上進行測試。與用戶構建無關。 |
1 | 外殼鍵 | 不適用 | 可用於 shell 的命名空間。主要用於測試,但也可以從命令行用於用戶構建。 |
100 | 沃爾德密鑰 | 不適用 | 供 vold 使用。 |
101 | odsing_key | 不適用 | 由設備上簽名守護程序使用。 |
102 | wifi_key | AID_WIFI(1010) | 由 Android 的 Wifi 系統系統(包括 wpa_supplicant)使用。 |
120 | 重新啟動鍵時恢復 | 輔助系統(1000) | 由 Android 系統服務器用來支持重啟時恢復。 |
訪問向量
SELinux 類keystore_key
已經老化了很多,並且某些權限(例如verify
或sign
已經失去了意義。這是 Keystore 2.0 將強制執行的一組新權限keystore2_key
。
允許 | 意義 |
---|---|
delete | 從密鑰庫中刪除密鑰時檢查。 |
get_info | 當請求密鑰的元數據時檢查。 |
grant | 調用者需要此權限才能在目標上下文中創建對密鑰的授權。 |
manage_blob | 調用者可以在給定的 SELinux 命名空間上使用DOMAIN_BLOB ,從而自行管理 blob。這對於 vold 特別有用。 |
rebind | 此權限控制別名是否可以反彈到新密鑰。這是插入所必需的,並且意味著先前綁定的鍵將被刪除。它基本上是一個插入權限,但它更好地捕獲了密鑰庫的語義。 |
req_forced_op | 具有此權限的客戶端可以創建不可修剪的操作,並且操作創建永遠不會失敗,除非所有操作槽都被不可修剪的操作佔用。 |
update | 需要更新密鑰的子組件。 |
use | 在創建使用密鑰材料的 Keymint 操作(例如用於簽名、加密/解密)時選中。 |
use_dev_id | 生成設備標識信息(例如設備 ID 證明)時需要。 |
此外,我們在 SELinux 安全類keystore2
中分離出一組非特定於密鑰的密鑰庫權限:
允許 | 意義 |
---|---|
add_auth | 身份驗證提供程序(例如 Gatekeeper 或 BiometricsManager)需要添加身份驗證令牌。 |
clear_ns | 此權限以前稱為clear_uid,允許命名空間的非所有者刪除該命名空間中的所有密鑰。 |
list | 系統需要通過各種屬性(例如所有權或身份驗證邊界)枚舉密鑰。枚舉自己的命名空間的調用者不需要此權限。 get_info 權限涵蓋了這一點。 |
lock | 此權限允許鎖定密鑰庫,即逐出主密鑰,從而使身份驗證綁定密鑰變得不可用且無法創建。 |
reset | 此權限允許將密鑰庫重置為出廠默認值,刪除對 Android 操作系統功能不重要的所有密鑰。 |
unlock | 嘗試解鎖身份驗證綁定密鑰的主密鑰需要此權限。 |
片上系統 (SoC) 中可信執行環境的可用性為 Android 設備提供了向 Android 操作系統、平台服務甚至第三方應用程序提供硬件支持的強大安全服務的機會。尋求 Android 特定擴展的開發人員應該訪問android.security.keystore 。
在 Android 6.0 之前,Android 已經有了一個簡單的、硬件支持的加密服務 API,由 Keymaster 硬件抽象層 (HAL) 0.2 和 0.3 版本提供。密鑰庫提供數字簽名和驗證操作,以及非對稱簽名密鑰對的生成和導入。這已經在許多設備上實現,但有許多安全目標無法僅通過簽名 API 輕鬆實現。 Android 6.0 中的 Keystore 擴展了 Keystore API 以提供更廣泛的功能。
在 Android 6.0 中,Keystore 添加了對稱加密原語、AES 和 HMAC,以及硬件支持密鑰的訪問控制系統。訪問控制在密鑰生成期間指定,並在密鑰的生命週期內強制執行。密鑰可以限制為僅在用戶經過身份驗證後才能使用,並且只能用於指定目的或使用指定的加密參數。有關更多信息,請參閱授權標籤和功能頁面。
除了擴展加密原語的範圍之外,Android 6.0 中的 Keystore 還添加了以下內容:
- 使用控制方案允許限制密鑰的使用,以減輕因濫用密鑰而造成安全損害的風險
- 一種訪問控制方案,可限制指定用戶、客戶端和定義時間範圍的密鑰
在 Android 7.0 中,Keymaster 2 添加了對密鑰證明和版本綁定的支持。密鑰證明提供公鑰證書,其中包含密鑰及其訪問控制的詳細描述,以使密鑰在安全硬件中的存在及其配置可遠程驗證。
版本綁定將密鑰綁定到操作系統和補丁級別版本。這可以確保發現舊版本系統或 TEE 軟件中存在漏洞的攻擊者無法將設備回滾到有漏洞的版本並使用使用新版本創建的密鑰。另外,當給定版本和補丁級別的密鑰在已升級到較新版本或補丁級別的設備上使用時,該密鑰將先升級後才能使用,並且之前版本的密鑰將失效。當設備升級時,密鑰會隨著設備一起“棘輪”前進,但設備恢復到以前的版本會導緻密鑰無法使用。
在 Android 8.0 中,Keymaster 3 從舊式 C 結構硬件抽象層 (HAL) 過渡到根據新硬件接口定義語言 (HIDL) 中的定義生成的 C++ HAL 接口。作為更改的一部分,許多參數類型發生了更改,儘管類型和方法與舊類型和 HAL 結構方法具有一一對應的關係。請參閱功能頁面了解更多詳細信息。
除了此接口修訂之外,Android 8.0還擴展了Keymaster 2的認證功能以支持ID認證。 ID 證明提供了一種有限且可選的機制,用於對硬件標識符進行強有力的證明,例如設備序列號、產品名稱和手機 ID (IMEI / MEID)。為了實現此添加,Android 8.0 更改了 ASN.1 證明架構以添加 ID 證明。 Keymaster 實現需要找到某種安全的方法來檢索相關數據項,並定義一種安全且永久禁用該功能的機制。
在 Android 9 中,更新包括:
- 更新至Keymaster 4
- 支持嵌入式安全元件
- 支持安全密鑰導入
- 支持3DES加密
- 更改版本綁定,以便 boot.img 和 system.img 分別設置版本以允許獨立更新
詞彙表
以下是密鑰庫組件及其關係的快速概述。
AndroidKeystore是應用程序用來訪問密鑰庫功能的 Android 框架 API 和組件。它作為標準 Java 加密體系結構 API 的擴展來實現,並由在應用程序自己的進程空間中運行的 Java 代碼組成。 AndroidKeystore
通過將應用程序轉發到密鑰庫守護進程來滿足應用程序對密鑰庫行為的請求。
密鑰庫守護程序是一個 Android 系統守護程序,它通過Binder API提供對所有密鑰庫功能的訪問。它負責存儲“密鑰 blob”,其中包含實際的秘密密鑰材料,並經過加密,以便 Keystore 可以存儲它們,但不能使用或洩露它們。
keymasterd是一個 HIDL 服務器,提供對 Keymaster TA 的訪問。 (此名稱未標準化,僅用於概念目的。)
Keymaster TA (可信應用程序)是在安全環境中運行的軟件,通常在ARM SoC 上的TrustZone 中運行,它提供所有安全密鑰庫操作,可以訪問原始密鑰材料,驗證密鑰上的所有訪問控制條件, ETC。
LockSettingsService是 Android 系統組件,負責用戶身份驗證,包括密碼和指紋。它不是 Keystore 的一部分,但相關,因為許多 Keystore 密鑰操作需要用戶身份驗證。 LockSettingsService
與 Gatekeeper TA 和 Fingerprint TA 交互以獲取身份驗證令牌,並將其提供給密鑰庫守護程序,並最終由 Keymaster TA 應用程序使用。
Gatekeeper TA (可信應用程序)是在安全上下文中運行的另一個組件,它負責驗證用戶密碼並生成身份驗證令牌,用於向 Keymaster TA 證明在特定時間點對特定用戶進行了身份驗證。
指紋 TA (可信應用程序)是在安全上下文中運行的另一個組件,它負責對用戶指紋進行身份驗證並生成身份驗證令牌,用於向Keymaster TA 證明在特定時間點對特定用戶進行了身份驗證。
建築學
Android Keystore API 和底層 Keymaster HAL 提供了一組基本但足夠的加密原語,允許使用訪問控制、硬件支持的密鑰來實現協議。
Keymaster HAL 是 OEM 提供的動態可加載庫,Keystore 服務使用它來提供硬件支持的加密服務。為了確保安全,HAL 實現不會在用戶空間甚至內核空間中執行任何敏感操作。敏感操作被委託給通過某些內核接口訪問的安全處理器。最終的架構如下所示:

圖 1.訪問 Keymaster
在 Android 設備中,Keymaster HAL 的“客戶端”由多個層組成(例如應用程序、框架、Keystore 守護程序),但就本文檔而言可以忽略這些層。這意味著所描述的 Keymaster HAL API 是低級的,由平台內部組件使用,並且不暴露給應用程序開發人員。 Android 開發者網站上描述了更高級別的 API。
Keymaster HAL 的目的不是實現安全敏感算法,而只是將請求編組和解組到安全世界。有線格式是實現定義的。
與以前版本的兼容性
Keymaster 1 HAL 與之前發布的 HAL 完全不兼容,例如 Keymaster 0.2 和 0.3。為了促進運行 Android 5.0 及更早版本(使用舊版 Keymaster HAL 啟動)的設備上的互操作性,Keystore 提供了一個適配器,通過調用現有硬件庫來實現 Keymaster 1 HAL。結果無法提供 Keymaster 1 HAL 中的全部功能。特別是,它僅支持 RSA 和 ECDSA 算法,並且所有密鑰授權強制執行均由適配器在非安全世界中執行。
Keymaster 2 通過刪除get_supported_*
方法並允許finish()
方法接受輸入,進一步簡化了 HAL 接口。在輸入一次性可用的情況下,這減少了到 TEE 的往返次數,並簡化了 AEAD 解密的實現。
在 Android 8.0 中,Keymaster 3 從舊式 C 結構 HAL 過渡到根據新硬件接口定義語言 (HIDL) 中的定義生成的 C++ HAL 接口。通過子類化生成的IKeymasterDevice
類並實現純虛方法來創建新型 HAL 實現。作為更改的一部分,許多參數類型已更改,但類型和方法與舊類型和 HAL 結構方法具有一一對應的關係。
HIDL 概述
硬件接口定義語言 (HIDL) 提供了一種獨立於實現語言的機制來指定硬件接口。 HIDL 工具當前支持生成 C++ 和 Java 接口。預計大多數可信執行環境 (TEE) 實現者會發現 C++ 工具更方便,因此本文檔僅討論 C++ 表示形式。
HIDL 接口由一組方法組成,表示為:
methodName(INPUT ARGUMENTS) generates (RESULT ARGUMENTS);
有多種預定義類型,HAL 可以定義新的枚舉和結構類型。有關 HIDL 的更多詳細信息,請參閱參考部分。
Keymaster 3 IKeymasterDevice.hal
的示例方法是:
generateKey(vec<KeyParameter> keyParams) generates(ErrorCode error, vec<uint8_t> keyBlob, KeyCharacteristics keyCharacteristics);
這相當於 keymaster2 HAL 中的以下內容:
keymaster_error_t (*generate_key)( const struct keymaster2_device* dev, const keymaster_key_param_set_t* params, keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics);
在 HIDL 版本中,刪除了dev
參數,因為它是隱式的。 params
參數不再是包含引用key_parameter_t
對像數組的指針的結構體,而是包含KeyParameter
對象的vec
(向量)。返回值列在“ generates
”子句中,包括密鑰 blob 的uint8_t
值向量。
HIDL 編譯器生成的 C++ 虛方法為:
Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams, generateKey_cb _hidl_cb) override;
其中generateKey_cb
是一個函數指針,定義為:
std::function<void(ErrorCode error, const hidl_vec<uint8_t>& keyBlob, const KeyCharacteristics& keyCharacteristics)>
也就是說, generateKey_cb
是一個函數,它採用generate 子句中列出的返回值。 HAL實現類重寫這個generateKey
方法並調用generateKey_cb
函數指針將操作結果返回給調用者。請注意,函數指針調用是同步的。調用者調用generateKey
, generateKey
調用提供的函數指針,該函數指針執行完成,將控制權返回給generateKey
實現,然後該實現返回給調用者。
有關詳細示例,請參閱hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp
中的默認實現。默認實現為具有舊式 keymaster0、keymaster1 或 keymaster2 HALS 的設備提供向後兼容性。
訪問控制
Keystore訪問控制最基本的規則是每個應用程序都有自己的命名空間。但每條規則都有一個例外。密鑰庫有一些硬編碼映射,允許某些系統組件訪問某些其他命名空間。這是一種非常生硬的工具,因為它讓一個組件完全控制另一個名稱空間。然後還有供應商組件作為密鑰庫客戶端的問題。我們目前無法為供應商組件(例如 WPA 請求者)建立命名空間。
為了適應供應商組件並在沒有硬編碼異常的情況下通用化訪問控制,Keystore 2.0 引入了域和 SELinux 命名空間。
密鑰庫域
通過密鑰庫域,我們可以將命名空間與 UID 解耦。訪問密鑰庫中密鑰的客戶端必須指定他們想要訪問的域、命名空間和別名。根據這個元組和調用者的身份,我們可以確定調用者想要訪問哪個密鑰以及它是否具有適當的權限。
我們引入了五個域參數來控制如何訪問密鑰。它們控制密鑰描述符的命名空間參數的語義以及如何執行訪問控制。
-
DOMAIN_APP
:應用程序域涵蓋舊行為。 Java Keystore SPI 默認使用此域。使用此域時,命名空間參數將被忽略,而使用調用者的 UID。對此域的訪問由 SELinux 策略中keystore_key
類的密鑰庫標籤控制。 -
DOMAIN_SELINUX
:該域表示該命名空間在 SELinux 策略中具有標籤。查找命名空間參數並將其轉換為目標上下文,並對keystore_key
類的調用 SELinux 上下文執行權限檢查。當為給定操作建立了權限後,完整元組將用於鍵查找。 -
DOMAIN_GRANT
: The grant domain indicates that the namespace parameter is a grant identifier. The alias parameter is ignored. SELinux checks are performed when the grant is created. Further access control only checks if the caller UID matches the grantees UID of the requested grant. -
DOMAIN_KEY_ID
: This domain indicates that the namespace parameter is a unique key id. The key itself may have been created withDOMAIN_APP
orDOMAIN_SELINUX
. The permission check is performed after thedomain
and thenamespace
have been loaded from the key database in the same way as if the blob was loaded by the domain, namespace, and alias tuple. The rationale for the key id domain is continuity. When accessing a key by alias, subsequent calls may operate on different keys, because a new key may have been generated or imported and bound to this alias. The key id, however, never changes. So when using a key by key id after it has been loaded from the Keystore database using the alias once, one can be certain that it is the same key as long as the key id still exists. This functionality is not exposed to app developers. Instead, it is used within the Android Keystore SPI to provide a more consistent experience even when used concurrently in an unsafe way. -
DOMAIN_BLOB
: The blob domain indicates that the caller manages the blob by itself. This is used for clients that need to access the Keystore before the data partition is mounted. The key blob is included in theblob
field of the key descriptor.
Using the SELinux domain, we can give vendor components access to very specific Keystore namespaces which can be shared by system components such as the settings dialog.
SELinux policy for keystore_key
Namespace labels are configured using the keystore2_key_context
file.
Each line in these files maps a numeric namespace id to an SELinux label. For example,
# wifi_key is a keystore2_key namespace intended to be used by wpa supplicant and # Settings to share keystore keys. 102 u:object_r:wifi_key:s0
After having set up a new key namespace in this way, we can give access to it by adding an appropriate policy. For example, to allow wpa_supplicant
to get and use keys in the new namespace we would add the following line to hal_wifi_supplicant.te
:
allow hal_wifi_supplicant wifi_key:keystore2_key { get, use };
After setting up the new namespace, AndroidKeyStore can be used almost as usual. The only difference is that the namespace ID must be specified. For loading and importing keys from and into Keystore, the namespace id is specified using the AndroidKeyStoreLoadStoreParameter
. For example,
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter; import java.security.KeyStore; KeyStore keystore = KeyStore.getInstance("AndroidKeyStore"); keystore.load(new AndroidKeyStoreLoadStoreParameter(102));
To generate a key in a given namespace, the namespace id must be given using KeyGenParameterSpec.Builder#setNamespace():
import android.security.keystore.KeyGenParameterSpec; KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder(); specBuilder.setNamespace(102);
The following context files may be used to configure Keystore 2.0 SELinux namespaces. Each partition has a different reserved range of 10,000 namespace ids to avoid collisions.
Partition | Range | Config files |
---|---|---|
System | 0 ... 9,999 | /system/etc/selinux/keystore2_key_contexts, /plat_keystore2_key_contexts |
Extended System | 10,000 ... 19,999 | /system_ext/etc/selinux/system_ext_keystore2_key_contexts, /system_ext_keystore2_key_contexts |
Product | 20,000 ... 29,999 | /product/etc/selinux/product_keystore2_key_contexts, /product_keystore2_key_contexts |
Vendor | 30,000 ... 39,999 | /vendor/etc/selinux/vendor_keystore2_key_contexts, /vendor_keystore2_key_contexts |
The client requests the key by requesting the SELinux domain and the desired virtual namespace, in this case "wifi_key"
, by its numeric id.
Above that, the following namespaces have been defined. If they replace special rules, the following table indicates the UID they used to correspond to.
Namespace ID | SEPolicy Label | UID | Description |
---|---|---|---|
0 | su_key | N/A | Super user key. Only used for testing on userdebug and eng builds. Not relevant on user builds. |
1 | shell_key | N/A | Namespace available to shell. Mostly used for testing, but can be used on user builds as well from the command line. |
100 | vold_key | N/A | Intended for use by vold. |
101 | odsing_key | N/A | Used by the on-device signing daemon. |
102 | wifi_key | AID_WIFI(1010) | Used by Android's Wifi sybsystem including wpa_supplicant. |
120 | resume_on_reboot_key | AID_SYSTEM(1000) | Used by Android's system server to support resume on reboot. |
Access Vectors
The SELinux class keystore_key
has aged quite a bit and some of the permissions, such as verify
or sign
have lost their meaning. Here is the new set of permissions, keystore2_key
, that Keystore 2.0 will enforce.
Permission | Meaning |
---|---|
delete | Checked when removing keys from Keystore. |
get_info | Checked when a key's metadata is requested. |
grant | The caller needs this permission to create a grant to the key in the target context. |
manage_blob | The caller may use DOMAIN_BLOB on the given SELinux namespace, thereby managing blobs by itself. This is specifically useful for vold. |
rebind | This permission controls if an alias may be rebound to a new key. This is required for insertion and implies that the previously bound key will be deleted. It is basically an insert permission, but it captures the semantic of keystore better. |
req_forced_op | Clients with this permission may create unpruneable operations, and operation creation never fails unless all operation slots are taken by unpruneable operations. |
update | Required to update the subcomponent of a key. |
use | Checked when creating a Keymint operation that uses the key material, eg, for signing, en/decryption. |
use_dev_id | Required when generating device identifying information, such as device id attestation. |
Additionally, we split out a set of non key specific keystore permissions in the SELinux security class keystore2
:
Permission | Meaning |
---|---|
add_auth | Required by authentication provider such as Gatekeeper or BiometricsManager for adding auth tokens. |
clear_ns | Formerly clear_uid, this permission allows a non owner of a namespace to delete all keys in that namespace. |
list | Required by the system for enumerating keys by various properties, such as ownership or auth boundedness. This permission is not required by callers enumerating their own namespaces. This is covered by the get_info permission. |
lock | This permission allows to lock Keystore, that is, evict the master key, such that auth bound keys become unusable and uncreatable. |
reset | This permission allows to reset Keystore to factory default, deleting all keys that are not vital to the functioning of the Android OS. |
unlock | This permission is required to attempt to unlock the master key for auth bound keys. |