實作 dm-verity

Android 4.4 以上版本支援驗證開機程序,可選擇是否啟用 device-mapper-verity (dm-verity) 核心功能 以及對區塊裝置的完整性檢查。dm-verity 有助於防止永久的 Rootkit 可能持有 Root 權限,進而入侵裝置這項功能可協助 Android 使用者確保裝置在啟動時,處於與上次使用時相同的狀態。

具有 root 權限的可能有害應用程式 (PHA) 可以隱藏在偵測程式中,或以其他方式遮蔽自己。根層級軟體可以 這通常比偵測工具更高的權限 「謊言」偵測程式

您可以使用 dm-verity 功能查看區塊裝置 (檔案系統的基礎儲存層),並判斷該裝置是否符合預期的設定。這項作業會使用加密編譯雜湊樹狀結構。每個區塊 (通常為 4k) 都有一個 SHA256 雜湊。

雜湊值儲存在網頁的樹狀結構中,因此只有頂層 「根」雜湊必須受到信任,才能驗證樹狀結構的其餘部分。Google Cloud 可以 修改任何區塊就等同於破壞加密編譯雜湊。 請參閱下圖,瞭解這個結構。

dm-verity-hash-table

圖 1:dm-verity 雜湊表

開機分割區包含公開金鑰,必須由裝置製造商在外部驗證。這組金鑰的用途是驗證簽章 並確認裝置的系統分區已受到保護 不變。

作業

dm-verity 防護機制位於核心中。因此,如果根目錄軟體在核心啟動前危害系統,就會保留該存取權。為降低這類風險,大多數製造商都會使用燒錄在裝置中的金鑰來驗證核心。裝置出廠後即無法變更車鑰。

製造商會使用該金鑰驗證第一層 Bootloader 的簽名,而 Bootloader 會依序驗證後續層級的簽名、應用程式 Bootloader,最後是核心。每家希望利用驗證開機程序的製造商,都應具備驗證核心完整性的方法。假設核心已完成驗證,核心可以查看區塊裝置,並在掛載時驗證。

驗證區塊裝置的方法之一,是直接對其內容進行雜湊運算,並與儲存的值進行比較。不過,嘗試驗證整個區塊裝置可能需要較長的時間,並耗用大量裝置電力。裝置 因此應用程式在使用前會大量消耗

相反地,dm-verity 會個別驗證區塊,且只在存取每個區塊時驗證。讀入記憶體時,系統會並行處理區塊的雜湊運算。雜湊為 完成樹狀結構的驗證後既然閱讀方塊是相當昂貴的 這項區塊層級驗證作業產生的延遲 也具有相對的名詞

如果驗證失敗,裝置會產生指出封鎖的 I/O 錯誤 無法讀取或無法讀取的內容系統會顯示檔案系統已損毀,這也是預期的結果。

應用程式可選擇在沒有結果資料的情況下繼續操作,例如 這些結果不一定適用於應用程式的主要功能。不過 如果應用程式無法在沒有資料的情況下繼續作業,就會失敗。

前向錯誤修正

Android 7.0 以上版本會使用前向錯誤修正 (FEC) 改善 dm-verity 的健全性。實作 Android 開放原始碼計畫時,我們會以 Reed-Solomon 錯誤修正程式碼,並套用 稱為「交錯」可減少空間負擔 可以復原的毀損區塊數量。如要進一步瞭解 FEC,請參閱「嚴格執行驗證開機程序並修正錯誤」。

實作

摘要

  1. 產生 ext4 系統映像檔。
  2. 為圖片產生雜湊樹
  3. 為該雜湊樹建立 dm-verity 表
  4. 簽署該 dm-verity 資料表,以產生資料表簽署。
  5. 將資料表簽章和 dm-verity 表格整合到 verity 中繼資料中。
  6. 連結系統映像檔、verity 中繼資料和雜湊樹狀結構。

請參閱 Chromium 專案 - 驗證開機程序 ,取得雜湊樹狀結構和 dm-verity 資料表的詳細說明。

產生雜湊樹狀結構

如前文所述,雜湊樹是 dm-verity 不可或缺的一環。cryptsetup 工具會為您產生雜湊樹狀圖。或者,您也可以在下方定義相容的值:

<your block device name> <your block device name> <block size> <block size> <image size in blocks> <image size in blocks + 8> <root hash> <salt>

為了形成雜湊,系統會將系統映像檔分割為第 0 層,分割成 4K 區塊,每個區塊 指派了 SHA256 雜湊第 1 層僅結合這些 SHA256 雜湊而產生 分割成 4K 區塊,產生的圖片會比較小同樣地,第 2 層會使用第 1 層的 SHA256 雜湊值。

直到上層的 SHA256 雜湊可放入單一區塊為止。取得該區塊的 SHA256 雜湊後,您就會取得樹狀結構的根雜湊。

雜湊樹狀結構的大小 (以及對應的磁碟空間用量) 會隨著 驗證分區的大小實務上,雜湊樹狀結構的大小通常為 通常小於 30 MB

如果圖層中的區塊無法由上一個圖層的雜湊值自然填滿,您應以零填充,以達到預期的 4k。這樣一來,您就能知道雜湊樹並未遭到移除,而是以空白資料填入。

如要產生雜湊樹狀結構,請將第 2 層雜湊連結至第 1 層,將第 3 層雜湊連結至第 2 層,依此類推。將所有內容寫入磁碟。請注意,這不會參照根雜湊的 0 層。

回顧一下,建構雜湊樹狀結構的一般演算法如下:

  1. 選擇隨機鹽 (十六進位編碼)。
  2. 將系統映像檔解密為 4k 區塊。
  3. 取得每個區塊的 (加鹽) SHA256 雜湊。
  4. 將這些雜湊連接起來,形成一個層級
  5. 用 0s 到 4K 區塊邊界的版面。
  6. 將層級連結至雜湊樹狀圖。
  7. 重複步驟 2 到 6,以前一個層級做為下一個步驟的來源,直到 只有一個雜湊值

完成後,系統會產生單一雜湊,也就是根雜湊。這和你的鹽分 用於建構 dm-verity 製圖表。

建構 dm-verity 對應資料表

建立 dm-verity 對應表,用於識別核心的區塊裝置 (或目標) 和雜湊樹位置 (相同的值)。這個 對應用於產生及啟動 fstab。表格也會列出 區塊大小和 hash_start,雜湊樹狀結構的開始位置 (具體來說,是從圖片開頭加上區塊編號)。

請參閱密碼設定以瞭解 錯誤目標對應資料表欄位的詳細說明。

簽署 dm-verity 資料表

簽署 dm-verity 資料表,產生資料表簽名。驗證分割區時,系統會先驗證資料表簽章。這取決於 保存在固定位置的開機映像檔製造商的建構系統通常會納入金鑰,以便在裝置上自動納入固定位置。

如要使用此簽名和金鑰組合驗證分區,請按照下列步驟操作:

  1. 將 libmincrypt 相容格式的 RSA-2048 金鑰加進 /boot 個分區 /verity_key。找出用來驗證雜湊樹狀結構的金鑰位置。
  2. 在相關項目的 fstab 中,將 verify 新增至 fs_mgr 標記。

將資料表簽章封裝至中繼資料

將資料表簽名和 dm-verity 表格合併至 verity 中繼資料。整個 中繼資料區塊不同,因此有可能會擴充,例如加入 或更改部分順序

做為例行性檢查,系統會在每組資料表中繼資料中,產生相關聯的魔法數字 以便識別資料表由於長度已包含在 ext4 系統映像檔標頭中,因此您可以透過這種方式搜尋中繼資料,而無須知道資料本身的內容。

這務必確認您並未選擇驗證未經驗證的分區。如果是這種情況,缺少這個魔法數字就會導致驗證程序停止。這個數字與 0xb001b001

十六進位的位元組值如下:

  • 第一個位元組 = b0
  • 位元組 = 01
  • 第三個位元組 = b0
  • 第四位元組 = 01

下圖顯示 verity 中繼資料的細目:

<magic number>|<version>|<signature>|<table length>|<table>|<padding>
\-------------------------------------------------------------------/
\----------------------------------------------------------/   |
                            |                                  |
                            |                                 32K
                       block content

下表說明這些中繼資料欄位。

表 1. Verity 中繼資料欄位

欄位 目的 大小
魔幻數字 用 fs_mgr 做為性質檢查 4 個位元組 0xb001b001
version 以便將中繼資料區塊 4 個位元組 目前為 0
簽名 PKCS1.5 填充形式的資料表簽章 256 個位元組
表格長度 dm-verity 資料表的長度 (以位元組為單位) 4 個位元組
桌子 先前所述的 dm-verity 表 資料表長度位元組數
padding 這個結構體會以 0 填補至 32k 長度 0

最佳化 dm-verity

如要讓 dm-verity 發揮最佳效能,請務必:

  • 在核心中,為 ARMv7 和 SHA-2 開啟 NEON SHA-2 適用於 ARMv8 的擴充功能
  • 嘗試使用不同的預讀和 prefetch_cluster 設定,找出最適合裝置的最佳設定。