全盤加密

全碟加密是使用加密金鑰對 Android 裝置上的所有使用者資料進行編碼的過程。一旦裝置被加密,所有使用者建立的資料在提交到磁碟之前都會自動加密,並且所有讀取都會在將資料傳回呼叫程序之前自動解密資料。

Android 4.4 中引入了全碟加密,但 Android 5.0 引入了以下新功能:

  • 建立快速加密,僅加密資料分割區上使用的區塊,以避免首次啟動花費很長時間。目前只有 ext4 和 f2fs 檔案系統支援快速加密。
  • 新增了forceencrypt fstab 標誌以在首次啟動時進行加密。
  • 新增了對模式和無密碼加密的支援。
  • 使用可信任執行環境 (TEE) 簽章功能(例如在 TrustZone 中)新增了硬體支援的加密金鑰儲存。有關更多詳細信息,請參閱儲存加密金鑰

注意:升級到 Android 5.0 然後加密的裝置可能會透過恢復原廠設定恢復到未加密狀態。首次啟動時加密的新 Android 5.0 裝置無法回到未加密狀態。

Android 全碟加密的工作原理

Android 全碟加密基於dm-crypt ,這是一個工作在區塊裝置層的核心功能。因此,加密可與嵌入式多媒體卡( eMMC) 和類似的快閃記憶體裝置一起使用,這些裝置將自身作為區塊裝置呈現給核心。 YAFFS 無法進行加密,它直接與原始 NAND 快閃記憶體晶片對話。

加密演算法是帶有密碼區塊連結 (CBC) 和 ESSIV:SHA256 的 128 高級加密標準 (AES)。主密鑰透過呼叫 OpenSSL 庫使用 128 位元 AES 進行加密。您必須使用 128 位元或更多位元作為密鑰(256 位元是可選的)。

注意: OEM 可以使用 128 位元或更高位元來加密主金鑰。

在Android 5.0版本中,有四種加密狀態:

  • 預設
  • 別針
  • 密碼
  • 圖案

首次啟動時,裝置會建立一個隨機產生的 128 位元主金鑰,然後使用預設密碼和儲存的鹽進行雜湊處理。預設密碼為:「default_password」 但是,產生的雜湊值也會透過 TEE(例如 TrustZone)進行簽名,TEE 使用簽署的雜湊值來加密主金鑰。

您可以在 Android 開源專案cryptfs.cpp檔案中找到定義的預設密碼。

當使用者在裝置上設定 PIN/pass 或密碼時,只有 128 位元金鑰會被重新加密並儲存。 (即,使用者 PIN/密碼/模式變更不會導致使用者資料重新加密。)請注意,託管裝置可能受到 PIN、模式或密碼限制。

加密由initvold管理。 init呼叫vold ,vold 設定屬性以觸發 init 中的事件。系統的其他部分也會查看屬性來執行任務,例如報告狀態、詢問密碼或在發生致命錯誤時提示恢復出廠設定。為了呼叫vold中的加密功能,系統使用命令列工具vdccryptfs指令: checkpwrestartenablecryptochangepwcryptocompleteverifypwsetfieldgetfieldmountdefaultencryptedgetpwtypegetpw clearpw

為了加密、解密或擦除/data ,不得安裝/data 。但是,為了顯示任何使用者介面 (UI),框架必須啟動,並且框架需要/data才能運作。為了解決這個難題,在/data上安裝了一個臨時檔案系統。這允許 Android 根據需要提示輸入密碼、顯示進度或建議擦除資料。它確實施加了限制,為了從臨時檔案系統切換到真正的/data檔案系統,系統必須停止臨時檔案系統上開啟檔案的每個進程,並在真正的/data檔案系統上重新啟動這些進程。為此,所有服務必須屬於三個群組之一: coremainlate_start

  • core :啟動後切勿關閉。
  • main :輸入磁碟密碼後關機並重新啟動。
  • late_start :直到/data被解密並掛載後才開始。

若要觸發這些操作,請將vold.decrypt屬性設為各種字串。要終止並重新啟動服務, init命令是:

  • class_reset :停止服務,但允許使用 class_start 重新啟動它。
  • class_start :重新啟動服務。
  • class_stop :停止服務並新增SVC_DISABLED標誌。停止的服務不會回應class_start

流量

加密設備有四種流程。設備僅加密一次,然後遵循正常的啟動流程。

  • 加密以前未加密的裝置:
    • 使用forceencrypt加密新裝置:首次啟動時強制加密(從Android L開始)。
    • 加密現有裝置:使用者啟動的加密(Android K 及更早版本)。
  • 啟動加密設備:
    • 啟動沒有密碼的加密裝置:啟動沒有設定密碼的加密裝置(與運行 Android 5.0 及更高版本的裝置相關)。
    • 使用密碼啟動加密設備:啟動具有設定密碼的加密設備。

除了這些串流之外,裝置還可能無法加密/data 。下面詳細解釋每個流程。

使用forcecrypt加密新設備

這是 Android 5.0 裝置的正常首次啟動。

  1. 使用forceencrypt標誌偵測未加密的檔案系統

    /data未加密,但需要加密,因為forceencrypt要求它。卸載/data

  2. 開始加密/data

    vold.decrypt = "trigger_encryption"觸發init.rc ,這將導致vold在沒有密碼的情況下加密/data 。 (未設定任何內容,因為這應該是新設備。)

  3. 掛載tmpfs

    vold安裝 tmpfs /data (使用ro.crypto.tmpfs_options中的 tmpfs 選項)並將屬性vold.encrypt_progress設為 0. vold準備 tmpfs /data用於啟動加密系統,並將屬性vold.decrypt設為: trigger_restart_min_framework

  4. 提出框架以顯示進度

    由於設備實際上沒有要加密的數據,因此通常不會實際出現進度條,因為加密發生得如此之快。有關進度 UI 的更多詳細信息,請參閱加密現有設備

  5. /data加密後,取下框架

    voldvold.decrypt設定為trigger_default_encryption ,從而啟動defaultcrypto服務。 (這將啟動下面用於安裝預設加密使用者資料的流程。) trigger_default_encryption檢查加密類型以查看/data是否使用密碼加密。由於Android 5.0裝置在首次啟動時已加密,因此不應設定密碼;因此我們解密並掛載/data

  6. 掛載/data

    然後init使用從ro.crypto.tmpfs_options取得的參數(在init.rc中設定)將/data掛載到 tmpfs RAMDisk 上。

  7. 啟動框架

    voldvold.decrypt設定為trigger_restart_framework ,這將繼續通常的啟動過程。

加密現有設備

當您加密已移轉到 L 的未加密 Android K 或更早版本的裝置時,就會發生這種情況。

此過程是用戶發起的,在程式碼中稱為「就地加密」。當使用者選擇加密裝置時,UI 會確保電池已充滿電且交流電源供應器已插入,以便有足夠的電量來完成加密過程。

警告:如果裝置在完成加密之前電量耗盡並關閉,檔案資料將處於部分加密狀態。設備必須恢復出廠設置,所有資料都會遺失。

為了啟用就地加密, vold啟動一個循環來讀取真實區塊裝置的每個磁區,然後將其寫入加密區塊裝置。 vold在讀取和寫入磁區之前檢查該磁區是否正在使用,這使得在幾乎沒有資料的新裝置上加密速度更快。

設備狀態:設定ro.crypto.state = "unencrypted"並執行on nonencrypted init觸發器以繼續啟動。

  1. 檢查密碼

    UI 使用指令cryptfs enablecrypto inplace呼叫vold ,其中passwd是使用者的鎖定畫面密碼。

  2. 拆掉框架

    vold檢查錯誤,如果無法加密則傳回 -1,並在日誌中列印原因。如果它可以加密,則會將屬性vold.decrypt設定為trigger_shutdown_framework 。這會導致init.rc停止類別late_startmain中的服務。

  3. 建立加密頁腳
  4. 建立麵包屑文件
  5. 重啟
  6. 偵測麵包屑文件
  7. 開始加密/data

    然後, vold設定加密映射,該映射建立一個虛擬加密區塊設備,映射到真實區塊設備,但在寫入時加密每個磁區,並在讀取時解密每個磁區。 vold接著建立並寫出加密元資料。

  8. 加密時掛載 tmpfs

    vold安裝 tmpfs /data (使用ro.crypto.tmpfs_options中的 tmpfs 選項)並將屬性vold.encrypt_progress設為 0. vold準備 tmpfs /data用於啟動加密系統,並將屬性vold.decrypt設為: trigger_restart_min_framework

  9. 提出框架以顯示進度

    trigger_restart_min_framework導致init.rc啟動main服務類別。當框架發現vold.encrypt_progress設定為 0 時,它會顯示進度條 UI,該 UI 每五秒查詢一次該屬性並更新進度條。每次加密分割區的另一個百分比時,加密循環都會更新vold.encrypt_progress

  10. /data被加密時,更新加密頁腳

    /data成功加密後, vold會清除元資料中的ENCRYPTION_IN_PROGRESS標誌。

    當裝置成功解鎖後,密碼將用於加密主密鑰並更新加密頁腳。

    如果因為某些原因重新啟動失敗, vold會將屬性vold.encrypt_progress設定為error_reboot_failed ,並且 UI 應顯示一則訊息,要求使用者按按鈕重新啟動。預計這種情況永遠不會發生。

使用預設加密啟動加密設備

這就是當您在沒有密碼的情況下啟動加密裝置時會發生的情況。由於 Android 5.0 裝置在首次啟動時進行加密,因此不應設定密碼,因此這是預設加密狀態。

  1. 檢測沒有密碼的加密/data

    偵測到 Android 裝置已加密,因為無法掛載/data並且設定了encryptableforceencrypt標誌之一。

    voldvold.decrypt設為trigger_default_encryption ,這會啟動defaultcrypto服務。 trigger_default_encryption檢查加密類型以查看/data是否使用密碼加密。

  2. 解密/數據

    在區塊設備上建立dm-crypt設備,以便該設備可供使用。

  3. 掛載/數據

    vold接著掛載解密後的真實/data分割區,然後準備新分割區。它將屬性vold.post_fs_data_done設為 0,然後將vold.decrypt設為trigger_post_fs_data 。這會導致init.rc運行其post-fs-data命令。他們將創建任何必要的目錄或鏈接,然後將vold.post_fs_data_done設為 1。

    一旦vold在該屬性中看到 1,它就會將屬性vold.decrypt設為: trigger_restart_framework.這會導致init.rc再次啟動main類別中的服務,並且自啟動以來第一次啟動late_start類別中的服務。

  4. 啟動框架

    現在,框架使用解密的/data啟動其所有服務,並且系統已準備好使用。

啟動沒有預設加密的加密設備

當您啟動具有設定密碼的加密裝置時,就會發生這種情況。設備的密碼可以是 PIN、圖案或密碼。

  1. 使用密碼檢測加密設備

    偵測到 Android 裝置已加密,因為標誌ro.crypto.state = "encrypted"

    voldvold.decrypt設定為trigger_restart_min_framework ,因為/data是用密碼加密的。

  2. 掛載tmpfs

    init設定五個屬性來儲存為/data指定的初始安裝選項以及從init.rc傳遞的參數。 vold使用這些屬性來設定加密映射:

    1. ro.crypto.fs_type
    2. ro.crypto.fs_real_blkdev
    3. ro.crypto.fs_mnt_point
    4. ro.crypto.fs_options
    5. ro.crypto.fs_flags (ASCII 8 位十六進位數字,前面帶有 0x)
  3. 啟動框架提示輸入密碼

    框架啟動並看到vold.decrypt設定為trigger_restart_min_framework 。這告訴框架它正在 tmpfs /data磁碟上啟動,並且需要取得使用者密碼。

    然而,首先需要確保磁碟已正確加密。它將命令cryptfs cryptocomplete傳送到vold 。如果加密成功完成,則vold傳回 0;如果發生內部錯誤,則傳回 -1;如果加密未成功完成,則 vold 傳回 -2。 vold透過在加密元資料中尋找CRYPTO_ENCRYPTION_IN_PROGRESS標誌來確定這一點。如果已設置,則加密過程將中斷,並且設備上沒有可用的資料。如果vold回傳錯誤,UI 應向使用者顯示一則訊息,要求使用者重新啟動裝置並恢復出廠設置,並為使用者提供一個按鈕來執行此操作。

  4. 使用密碼解密資料

    一旦cryptfs cryptocomplete成功,框架就會顯示一個 UI,要求輸入磁碟密碼。 UI 透過將指令cryptfs checkpw傳送到vold來檢查密碼。如果密碼正確(透過在臨時位置成功掛載解密的/data然後卸載它來確定), vold會將解密的區塊裝置的名稱保存在屬性ro.crypto.fs_crypto_blkdev中,並向 UI 傳回狀態 0 。如果密碼不正確,則向 UI 傳回 -1。

  5. 停止框架

    UI 會顯示一個加密啟動圖形,然後使用指令cryptfs restart呼叫voldvold將屬性vold.decrypt設為trigger_reset_main ,這會導致init.rc執行class_reset main 。這將停止主類別中的所有服務,從而允許卸載 tmpfs /data

  6. 掛載/data

    然後, vold掛載解密的真實/data分割區並準備新分割區(如果使用擦除選項加密,則可能永遠不會準備好該分割區,而第一個版本不支援該選項)。它將屬性vold.post_fs_data_done設為 0,然後將vold.decrypt設為trigger_post_fs_data 。這會導致init.rc運行其post-fs-data命令。他們將創建任何必要的目錄或鏈接,然後將vold.post_fs_data_done設為 1。一旦vold在該屬性中看到 1,它將屬性vold.decrypt設為trigger_restart_framework 。這會導致init.rc再次啟動main類別中的服務,並且自啟動以來首次啟動late_start類別中的服務。

  7. 啟動完整框架

    現在,框架使用解密的/data檔案系統啟動其所有服務,並且系統已準備好使用。

失敗

無法解密的裝置可能由於多種原因而出現問題。設備啟動時會執行一系列正常的啟動步驟:

  1. 使用密碼檢測加密設備
  2. 掛載tmpfs
  3. 啟動框架提示輸入密碼

但框架開啟後,設備可能會遇到一些錯誤:

  • 密碼匹配但無法解密數據
  • 使用者輸入錯誤密碼30次

如果這些錯誤未解決,請提示使用者進行工廠擦除

如果vold在加密過程中偵測到錯誤,尚未銷毀任何資料且框架已啟動, vold將屬性vold.encrypt_progress設為error_not_encrypted 。 UI 會提示使用者重新啟動並提醒他們加密過程從未啟動。如果錯誤發生在框架被拆除之後,但在進度條 UI 啟動之前, vold將重新啟動系統。如果重新啟動失敗,則將vold.encrypt_progress設為error_shutting_down並返回-1;但不會有任何東西來捕捉錯誤。預計不會發生這種情況。

如果vold在加密過程中偵測到錯誤,則會將vold.encrypt_progress設為error_partially_encrypted並傳回 -1。然後,UI 應顯示一則訊息,說明加密失敗,並為使用者提供一個按鈕以將裝置重設為原廠設定。

儲存加密金鑰

加密的密鑰儲存在加密元資料中。硬體支援是透過使用可信任執行環境 (TEE) 簽章功能來實現的。之前,我們使用透過對使用者密碼和儲存的鹽套用 scrypt 產生的金鑰來加密主金鑰。為了使金鑰能夠抵禦離機攻擊,我們透過使用儲存的 TEE 金鑰對結果金鑰進行簽章來擴展此演算法。然後,透過另一次 scrypt 應用程式將所得簽章轉換為適當長度的金鑰。然後使用該密鑰來加密和解密主密鑰。若要儲存此密鑰:

  1. 產生隨機 16 位元組磁碟加密金鑰 (DEK) 和 16 位元組鹽。
  2. 將 scrypt 應用於使用者密碼和鹽以產生 32 位元組中間金鑰 1 (IK1)。
  3. 將 IK1 以零位元組填入硬體綁定私鑰 (HBK) 的大小。具體來說,我們填充為:00 || IK1 || 00..00; 1 個零位元組、32 個 IK1 位元組、223 個零位元組。
  4. 以 HBK 對 IK1 進行符號填充,產生 256 位元組的 IK2。
  5. 將 scrypt 應用於 IK2 和鹽(與步驟 2 相同的鹽)以產生 32 位元組 IK3。
  6. 使用 IK3 的前 16 個位元組作為 KEK,後 16 個位元組作為 IV。
  7. 使用 AES_CBC、金鑰 KEK 和初始化向量 IV 加密 DEK。

更改密碼

當使用者選擇在設定中變更或刪除其密碼時,UI 會向vold傳送指令cryptfs changepw ,然後vold使用新密碼重新加密磁碟主金鑰。

加密屬性

voldinit透過設定屬性來相互通訊。以下是可用於加密的屬性的清單。

沃爾德屬性

財產描述
vold.decrypt trigger_encryption無需密碼即可加密磁碟機。
vold.decrypt trigger_default_encryption檢查磁碟機是否已無密碼加密。如果是,則解密並掛載它,否則將vold.decrypt設為trigger_restart_min_framework。
vold.decrypt trigger_reset_main由 vold 設定以關閉詢問磁碟密碼的 UI。
vold.decrypt trigger_post_fs_data由 vold 設定為準備/data以及必要的目錄等。
vold.decrypt trigger_restart_framework透過vold設定來啟動真正的框架和所有服務。
vold.decrypt trigger_shutdown_framework由 vold 設定以關閉整個框架以開始加密。
vold.decrypt trigger_restart_min_framework由 vold 設定以啟動加密進度條 UI 或提示輸入密碼,取決於ro.crypto.state的值。
vold.encrypt_progress當框架啟動時,如果設定該屬性,則進入進度條UI模式。
vold.encrypt_progress 0 to 100進度條 UI 應顯示設定的百分比值。
vold.encrypt_progress error_partially_encrypted進度條 UI 應顯示加密失敗的訊息,並提供使用者將裝置重設為原廠設定的選項。
vold.encrypt_progress error_reboot_failed進度條 UI 應顯示一則訊息,說明加密已完成,並提供使用者重新啟動裝置的按鈕。預計不會發生此錯誤。
vold.encrypt_progress error_not_encrypted進度條 UI 應顯示一則訊息,說明發生錯誤、沒有資料加密或遺失,並提供使用者重新啟動系統的按鈕。
vold.encrypt_progress error_shutting_down進度條 UI 未運行,因此尚不清楚誰將回應此錯誤。無論如何,這種事永遠不該發生。
vold.post_fs_data_done 0在將vold.decrypt設定為trigger_post_fs_data之前由vold設定。
vold.post_fs_data_done 1在完成任務post-fs-data後由init.rcinit.rc設定。

初始化屬性

財產描述
ro.crypto.fs_crypto_blkdev透過vold指令checkpw設置,以便稍後透過vold指令restart使用。
ro.crypto.state unencryptedinit設定表示系統正在使用未加密的/data ro.crypto.state encrypted運作。由init設定表示該系統正在使用加密的/data運作。

ro.crypto.fs_type
ro.crypto.fs_real_blkdev
ro.crypto.fs_mnt_point
ro.crypto.fs_options
ro.crypto.fs_flags

這五個屬性由init在嘗試使用從init.rc傳入的參數掛載/data時設定。 vold使用這些來設定加密映射。
ro.crypto.tmpfs_optionsinit.rc設置,並設定 init 在掛載 tmpfs /data檔案系統時應使用的選項。

初始化動作

on post-fs-data
on nonencrypted
on property:vold.decrypt=trigger_reset_main
on property:vold.decrypt=trigger_post_fs_data
on property:vold.decrypt=trigger_restart_min_framework
on property:vold.decrypt=trigger_restart_framework
on property:vold.decrypt=trigger_shutdown_framework
on property:vold.decrypt=trigger_encryption
on property:vold.decrypt=trigger_default_encryption