版本繫結

在 Keymaster 1 中,所有 Keymaster 金鑰都會以加密編譯方式繫結至裝置的 Root of Trust 或 Verified Boot 金鑰。在 Keymaster 2 和 3 中,所有鍵都會繫結至作業系統和系統映像檔的修補程式等級。這可確保攻擊者在舊版系統或 TEE 軟體中發現弱點時,無法將裝置回溯至有安全漏洞的版本,並使用新版建立的金鑰。此外,如果在已升級至新版本或修補程式等級的裝置上使用特定版本和修補程式等級的金鑰,金鑰會先升級再使用,而舊版金鑰會失效。因此,當裝置升級時,這些金鑰會隨著裝置「逐漸」升級,但如果裝置回復至先前的版本,這些金鑰就會無法使用。

為了支援 Treble 的模組結構,並將 system.img 繫結至 boot.img,Keymaster 4 已變更主要版本繫結模型,讓每個分區都有不同的修補程式層級。這樣一來,每個分區都能獨立更新,同時仍提供回溯保護功能。

在 Android 9 中,bootsystemvendor 分割區各自有專屬的修補程式等級。

  • 搭載 Android 驗證啟動程序 (AVB) 的裝置可將所有修補程式級別和系統版本放入 vbmeta,以便系統啟動載入程式將這些資訊提供給 Keymaster。對於鏈結式分區,分區的版本資訊位於鏈結式 vbmeta 中。一般來說,版本資訊應位於 vbmeta struct 中,其中包含特定分區的驗證資料 (雜湊或雜湊樹)。
  • 在未支援 AVB 的裝置上:
    • 驗證啟動程序實作項目需要向系統啟動載入程式提供版本中繼資料的雜湊值,以便系統啟動載入程式將雜湊值提供給 Keymaster。
    • boot.img 可繼續在標頭中儲存修補程式等級
    • system.img 可繼續在唯讀屬性中儲存修補程式層級和 OS 版本
    • vendor.img 會將修補程式級別儲存在唯讀屬性 ro.vendor.build.version.security_patch 中。
    • 引導程式可將經過驗證開機程序驗證的所有資料雜湊值提供給 Keymaster。
  • 在 Android 9 中,請使用下列標記為下列分區提供版本資訊:
    • VENDOR_PATCH_LEVELvendor 區段
    • BOOT_PATCH_LEVELboot 區段
    • OS_PATCH_LEVELOS_VERSIONsystem 分區。(OS_VERSION 已從 boot.img 標頭中移除。
  • Keymaster 實作應獨立處理所有修補程式級別。如果所有版本資訊都與鍵相關聯的值相符,鍵就會可用,且 IKeymaster::upgradeDevice() 會視需要升級至更高修補程式級別。

HAL 變更

為支援版本繫結和版本認證,Android 7.1 新增了標記 Tag::OS_VERSIONTag::OS_PATCHLEVEL,以及方法 configureupgradeKey。版本標記會由 Keymaster 2+ 實作項目自動新增至所有新產生 (或更新) 的金鑰。此外,如果嘗試使用不符合目前系統 OS 版本或修補程式等級的金鑰,系統會以 ErrorCode::KEY_REQUIRES_UPGRADE 拒絕。

Tag::OS_VERSIONUINT 值,代表 Android 系統版本的主要、次要和次次要部分,分別為 MMmmss,其中 MM 為主要版本、mm 為次要版本,而 ss 為次次要版本。舉例來說,6.1.2 會以 060102 表示。

Tag::OS_PATCHLEVELUINT 值,代表系統上次更新的年份和月份,格式為 YYYYMM,其中 YYYY 是四位數年份,MM 是兩位數月份。舉例來說,2016 年 3 月會以 201603 表示。

UpgradeKey

為讓金鑰升級至新的 OS 版本和系統映像檔的修補程式級別,Android 7.1 將 upgradeKey 方法新增至 HAL:

Keymaster 3

    upgradeKey(vec keyBlobToUpgrade, vec upgradeParams)
        generates(ErrorCode error, vec upgradedKeyBlob);

Keymaster 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 是需要升級的鍵
  • upgradeParams 是升級金鑰所需的參數。這些值包括 Tag::APPLICATION_IDTag::APPLICATION_DATA,如果在產生期間提供這些值,則必須用來解密金鑰 blob。
  • upgradedKeyBlob 是輸出參數,用於傳回新的金鑰 blob。

如果 upgradeKey 以無法剖析或其他無效的鍵 blob 呼叫,則會傳回 ErrorCode::INVALID_KEY_BLOB。如果使用修補程式級別大於目前系統值的鍵呼叫此方法,則會傳回 ErrorCode::INVALID_ARGUMENT。如果使用 OS 版本大於目前系統值的鍵呼叫此方法,且系統值非零,則會傳回 ErrorCode::INVALID_ARGUMENT。作業系統版本可從非零升級至零。如果與安全世界通訊時發生錯誤,則會傳回適當的錯誤值 (例如 ErrorCode::SECURE_HW_ACCESS_DENIEDErrorCode::SECURE_HW_BUSY)。否則,會傳回 ErrorCode::OK,並在 upgradedKeyBlob 中傳回新的金鑰 blob。

keyBlobToUpgradeupgradeKey 呼叫後仍有效,理論上如果裝置降級,可以再次使用。實際上,KeyStore 通常會在呼叫 upgradeKey 後不久,對 keyBlobToUpgrade blob 呼叫 deleteKey。如果 keyBlobToUpgrade 有標記 Tag::ROLLBACK_RESISTANTupgradedKeyBlob 也應有該標記 (且應可防回溯)。

安全設定

為了實作版本繫結,Keymaster TA 需要一種方法,可安全地接收目前的 OS 版本和修補程式等級 (版本資訊),並確保接收的資訊與執行中的系統資訊完全相符。

為確保版本資訊可安全地傳送至 TA,我們已在啟動映像檔標頭中新增 OS_VERSION 欄位。開機映像檔建構指令碼會自動填入這個欄位。OEM 和 Keymaster TA 實作者必須共同修改裝置 Bootloader,從啟動映像檔中擷取版本資訊,並在啟動非安全系統前將其傳遞至 TA。這可確保攻擊者無法干擾版本資訊的配置作業。

您也必須確保系統映像檔的版本資訊與開機映像檔相同。為此,我們已將設定方法新增至 Keymaster HAL:

keymaster_error_t (*configure)(const struct keymaster2_device* dev,
  const keymaster_key_param_set_t* params);

params 引數包含 Tag::OS_VERSIONTag::OS_PATCHLEVEL。這個方法會在開啟 HAL 後,但在呼叫任何其他方法之前,由 keymaster2 用戶端呼叫。如果在設定之前呼叫任何其他方法,TA 會傳回 ErrorCode::KEYMASTER_NOT_CONFIGURED

裝置啟動後,系統首次呼叫 configure 時,應驗證所提供的版本資訊是否與 Bootloader 提供的資訊相符。如果版本資訊不相符,configure 會傳回 ErrorCode::INVALID_ARGUMENT,而所有其他 Keymaster 方法會繼續傳回 ErrorCode::KEYMASTER_NOT_CONFIGURED。如果資訊相符,configure 會傳回 ErrorCode::OK,其他 Keymaster 方法也會開始正常運作。

後續對 configure 的呼叫會傳回與第一次呼叫相同的值,且不會變更 Keymaster 的狀態。

由於 configure 是由系統呼叫,而系統會驗證其內容,因此攻擊者有短暫的機會可以入侵系統映像檔,並強制系統提供與啟動映像檔相符的版本資訊,但這並非系統的實際版本。結合開機映像檔驗證、系統映像檔內容的 dm-verity 驗證,以及 configure 在系統啟動初期就會呼叫的事實,應該可以讓這個時機窗口難以遭到濫用。