在 Keymaster 1 中,所有 keymaster 密鑰都以加密方式綁定到設備Root of Trust或 Verified Boot 密鑰。在 Keymaster 2 和 3 中,所有密鑰也綁定到操作系統和系統映像的補丁級別。這確保了在舊版本系統或 TEE 軟件中發現漏洞的攻擊者無法將設備回滾到易受攻擊的版本並使用使用新版本創建的密鑰。此外,在升級到更新版本或補丁級別的設備上使用具有給定版本和補丁級別的密鑰時,該密鑰將在可以使用之前進行升級,並且先前版本的密鑰失效。這樣,隨著設備的升級,密鑰將與設備一起“棘輪”前進,但任何將設備恢復到先前版本的操作都會導緻密鑰無法使用。
為了支持 Treble 的模塊化結構並打破 system.img 與 boot.img 的綁定,Keymaster 4 將密鑰版本綁定模型更改為每個分區具有單獨的補丁級別。這允許每個分區獨立更新,同時仍提供回滾保護。
在 Android 9 中, boot
、 system
和vendor
分區都有自己的補丁級別。
- 具有 Android 驗證啟動 (AVB) 的設備可以將所有補丁級別和系統版本放在 vbmeta 中,因此引導加載程序可以將它們提供給 Keymaster。對於鍊式分區,分區的版本信息將在鍊式 vbmeta 中。通常,版本信息應該在包含給定分區的驗證數據(散列或散列樹)的
vbmeta struct
中。 - 在沒有 AVB 的設備上:
- 經過驗證的引導實現需要向引導加載程序提供版本元數據的哈希,以便引導加載程序可以向 Keymaster 提供哈希。
-
boot.img
可以繼續在頭文件中存儲補丁級別 system.img
可以繼續在只讀屬性中存儲補丁級別和操作系統版本vendor.img
將補丁級別存儲在只讀屬性ro.vendor.build.version.security_patch
中。- 引導加載程序可以向 keymaster 提供通過驗證引導驗證的所有數據的哈希值。
- 在 Android 9 中,使用以下標籤為以下分區提供版本信息:
-
VENDOR_PATCH_LEVEL
:vendor
分區 BOOT_PATCH_LEVEL
:boot
分區OS_PATCH_LEVEL
和OS_VERSION
:system
分區。 (OS_VERSION
已從boot.img
標頭中刪除。
-
- Keymaster 實現應獨立處理所有補丁級別。如果所有版本信息都與與密鑰關聯的值匹配,則密鑰可用,並且
IKeymaster::upgradeDevice()
會在需要時滾動到更高的補丁級別。
HAL 更改
為了支持版本綁定和版本證明,Android 7.1 添加了標籤Tag::OS_VERSION
和Tag::OS_PATCHLEVEL
以及方法configure
和upgradeKey
。版本標籤由 Keymaster 2+ 實現自動添加到所有新生成(或更新)的密鑰。此外,任何嘗試使用不具有與當前系統操作系統版本或補丁級別分別匹配的操作系統版本或補丁級別的密鑰的嘗試都會被ErrorCode::KEY_REQUIRES_UPGRADE
拒絕。
Tag::OS_VERSION
是一個UINT
值,將 Android 系統版本的主要、次要和次次要部分錶示為 MMmmss,其中 MM 是主要版本,mm 是次要版本,ss 是次次要版本。例如 6.1.2 將表示為 060102。
Tag::OS_PATCHLEVEL
是一個UINT
值,表示上次系統更新的年份和月份為 YYYYMM,其中 YYYY 是四位數的年份,MM 是兩位數的月份。例如,2016 年 3 月將表示為 201603。
升級密鑰
為了讓密鑰升級到新的操作系統版本和系統映像的補丁級別,Android 7.1 在 HAL 中添加了upgradeKey
方法:
鑰匙大師 3
upgradeKey(vec keyBlobToUpgrade, vec upgradeParams) generates(ErrorCode error, vec upgradedKeyBlob);
鑰匙大師 2
keymaster_error_t (*upgrade_key)(const struct keymaster2_device* dev, const keymaster_key_blob_t* key_to_upgrade, const keymaster_key_param_set_t* upgrade_params, keymaster_key_blob_t* upgraded_key);
-
dev
是設備結構 keyBlobToUpgrade
是需要升級的key-
upgradeParams
是升級密鑰所需的參數。這些將包括Tag::APPLICATION_ID
和Tag::APPLICATION_DATA
,如果它們是在生成期間提供的,則它們是解密密鑰 blob 所必需的。 -
upgradedKeyBlob
是輸出參數,用於返回新的密鑰 blob。
如果使用無法解析或無效的密鑰 blob 調用upgradeKey
,則返回ErrorCode::INVALID_KEY_BLOB
。如果使用補丁級別大於當前系統值的鍵調用它,則返回ErrorCode::INVALID_ARGUMENT
。如果使用 OS 版本大於當前系統值的鍵調用它,並且系統值非零,則返回ErrorCode::INVALID_ARGUMENT
。操作系統版本從非零升級到零是允許的。如果與安全世界通信發生錯誤,它會返回適當的錯誤值(例如ErrorCode::SECURE_HW_ACCESS_DENIED
、 ErrorCode::SECURE_HW_BUSY
)。否則,它返回ErrorCode::OK
並在upgradedKeyBlob
中返回一個新的密鑰 blob。
keyBlobToUpgrade
在upgradeKey
調用後仍然有效,理論上可以在設備降級後再次使用。在實踐中,keystore 通常在調用deleteKey
後不久對keyBlobToUpgrade
blob 調用upgradeKey
。如果keyBlobToUpgrade
有標籤Tag::ROLLBACK_RESISTANT
,那麼upgradedKeyBlob
也應該有它(並且應該是抗回滾的)。
安全配置
為了實現版本綁定,keymaster TA 需要一種安全接收當前操作系統版本和補丁級別(版本信息)的方法,並確保它接收到的信息與運行系統的信息高度匹配。
為了支持向 TA 安全傳送版本信息,已將OS_VERSION
字段添加到引導映像標頭。引導映像構建腳本會自動填充此字段。 OEM 和 keymaster TA 實施者需要共同修改設備引導加載程序,以從引導映像中提取版本信息,並在非安全系統引導之前將其傳遞給 TA。這確保了攻擊者不會干擾向 TA 提供版本信息。
還需要確保系統映像與啟動映像具有相同的版本信息。為此,已將 configure 方法添加到 keymaster HAL:
keymaster_error_t (*configure)(const struct keymaster2_device* dev, const keymaster_key_param_set_t* params);
params
參數包含Tag::OS_VERSION
和Tag::OS_PATCHLEVEL
。此方法由 keymaster2 客戶端在打開 HAL 後調用,但在調用任何其他方法之前。如果在配置之前調用了任何其他方法,TA 將返回ErrorCode::KEYMASTER_NOT_CONFIGURED
。
在設備啟動後第一次調用configure
時,它應該驗證提供的版本信息是否與引導加載程序提供的信息相匹配。如果版本信息不匹配,則configure
返回ErrorCode::INVALID_ARGUMENT
,所有其他 keymaster 方法繼續返回ErrorCode::KEYMASTER_NOT_CONFIGURED
。如果信息匹配,則configure
返回ErrorCode::OK
,其他 keymaster 方法開始正常運行。
對configure
的後續調用返回與第一次調用返回的值相同的值,並且不會更改 keymaster 的狀態。請注意,此過程將要求所有 OTA 更新系統和啟動映像;它們不能單獨更新以保持版本信息同步。
因為configure
將由要驗證其內容的系統調用,所以攻擊者有一個狹窄的機會窗口來破壞系統映像並強制它提供與啟動映像匹配的版本信息,但這不是實際的系統版本。啟動映像驗證、系統映像內容的 dm-verity 驗證以及在系統啟動的早期調用configure
的事實的組合應該使這個機會窗口難以利用。