全盤加密

全盤加密是使用加密密鑰對 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命令: checkpwrestartenablecryptochangepwcryptocompleteverifypwsetfieldgetfieldmountdefaultencryptedgetpwtypegetpwclearpw

為了加密、解密或擦除/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 會將命令cryptfs changepw發送到vold ,然後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
,

全盤加密是使用加密密鑰對 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命令: checkpwrestartenablecryptochangepwcryptocompleteverifypwsetfieldgetfieldmountdefaultencryptedgetpwtypegetpwclearpw

為了加密、解密或擦除/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 或更早版本的設備時,就會發生這種情況。

此過程是用戶發起的,在代碼中稱為“就地加密”。當用戶選擇加密設備時,用戶界面會確保電池已充滿電並且交流適配器已插入,以便有足夠的電量來完成加密過程。

警告:如果設備在完成加密之前電量耗盡並關閉,文件數據將處於部分加密狀態。設備必須恢復出廠設置,所有數據都會丟失。

為了啟用就地加密, 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啟動其所有服務,並且系統已準備好使用。

Starting an encrypted device without default encryption

This is what happens when you boot up an encrypted device that has a set password. The device's password can be a pin, pattern, or password.

  1. Detect encrypted device with a password

    Detect that the Android device is encrypted because the flag ro.crypto.state = "encrypted"

    vold sets vold.decrypt to trigger_restart_min_framework because /data is encrypted with a password.

  2. Mount tmpfs

    init sets five properties to save the initial mount options given for /data with parameters passed from init.rc . vold uses these properties to set up the crypto mapping:

    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-digit hex number preceded by 0x)
  3. Start framework to prompt for password

    The framework starts up and sees that vold.decrypt is set to trigger_restart_min_framework . This tells the framework that it is booting on a tmpfs /data disk and it needs to get the user password.

    First, however, it needs to make sure that the disk was properly encrypted. It sends the command cryptfs cryptocomplete to vold . vold returns 0 if encryption was completed successfully, -1 on internal error, or -2 if encryption was not completed successfully. vold determines this by looking in the crypto metadata for the CRYPTO_ENCRYPTION_IN_PROGRESS flag. If it's set, the encryption process was interrupted, and there is no usable data on the device. If vold returns an error, the UI should display a message to the user to reboot and factory reset the device, and give the user a button to press to do so.

  4. Decrypt data with password

    Once cryptfs cryptocomplete is successful, the framework displays a UI asking for the disk password. The UI checks the password by sending the command cryptfs checkpw to vold . If the password is correct (which is determined by successfully mounting the decrypted /data at a temporary location, then unmounting it), vold saves the name of the decrypted block device in the property ro.crypto.fs_crypto_blkdev and returns status 0 to the UI. If the password is incorrect, it returns -1 to the UI.

  5. Stop framework

    The UI puts up a crypto boot graphic and then calls vold with the command cryptfs restart . vold sets the property vold.decrypt to trigger_reset_main , which causes init.rc to do class_reset main . This stops all services in the main class, which allows the tmpfs /data to be unmounted.

  6. Mount /data

    vold then mounts the decrypted real /data partition and prepares the new partition (which may never have been prepared if it was encrypted with the wipe option, which is not supported on first release). It sets the property vold.post_fs_data_done to 0 and then sets vold.decrypt to trigger_post_fs_data . This causes init.rc to run its post-fs-data commands. They will create any necessary directories or links and then set vold.post_fs_data_done to 1. Once vold sees the 1 in that property, it sets the property vold.decrypt to trigger_restart_framework . This causes init.rc to start services in class main again and also start services in class late_start for the first time since boot.

  7. Start full framework

    Now the framework boots all its services using the decrypted /data filesystem, and the system is ready for use.

Failure

A device that fails to decrypt might be awry for a few reasons. The device starts with the normal series of steps to boot:

  1. Detect encrypted device with a password
  2. Mount tmpfs
  3. Start framework to prompt for password

But after the framework opens, the device can encounter some errors:

  • Password matches but cannot decrypt data
  • User enters wrong password 30 times

If these errors are not resolved, prompt user to factory wipe :

If vold detects an error during the encryption process, and if no data has been destroyed yet and the framework is up, vold sets the property vold.encrypt_progress to error_not_encrypted . The UI prompts the user to reboot and alerts them the encryption process never started. If the error occurs after the framework has been torn down, but before the progress bar UI is up, vold will reboot the system. If the reboot fails, it sets vold.encrypt_progress to error_shutting_down and returns -1; but there will not be anything to catch the error. This is not expected to happen.

If vold detects an error during the encryption process, it sets vold.encrypt_progress to error_partially_encrypted and returns -1. The UI should then display a message saying the encryption failed and provide a button for the user to factory reset the device.

Storing the encrypted key

The encrypted key is stored in the crypto metadata. Hardware backing is implemented by using Trusted Execution Environment's (TEE) signing capability. Previously, we encrypted the master key with a key generated by applying scrypt to the user's password and the stored salt. In order to make the key resilient against off-box attacks, we extend this algorithm by signing the resultant key with a stored TEE key. The resultant signature is then turned into an appropriate length key by one more application of scrypt. This key is then used to encrypt and decrypt the master key. To store this key:

  1. Generate random 16-byte disk encryption key (DEK) and 16-byte salt.
  2. Apply scrypt to the user password and the salt to produce 32-byte intermediate key 1 (IK1).
  3. Pad IK1 with zero bytes to the size of the hardware-bound private key (HBK). Specifically, we pad as: 00 || IK1 || 00..00; one zero byte, 32 IK1 bytes, 223 zero bytes.
  4. Sign padded IK1 with HBK to produce 256-byte IK2.
  5. Apply scrypt to IK2 and salt (same salt as step 2) to produce 32-byte IK3.
  6. Use the first 16 bytes of IK3 as KEK and the last 16 bytes as IV.
  7. Encrypt DEK with AES_CBC, with key KEK, and initialization vector IV.

Changing the password

When a user elects to change or remove their password in settings, the UI sends the command cryptfs changepw to vold , and vold re-encrypts the disk master key with the new password.

Encryption properties

vold and init communicate with each other by setting properties. Here is a list of available properties for encryption.

Vold properties

Property Description
vold.decrypt trigger_encryption Encrypt the drive with no password.
vold.decrypt trigger_default_encryption Check the drive to see if it is encrypted with no password. If it is, decrypt and mount it, else set vold.decrypt to trigger_restart_min_framework.
vold.decrypt trigger_reset_main Set by vold to shutdown the UI asking for the disk password.
vold.decrypt trigger_post_fs_data Set by vold to prep /data with necessary directories, et al.
vold.decrypt trigger_restart_framework Set by vold to start the real framework and all services.
vold.decrypt trigger_shutdown_framework Set by vold to shutdown the full framework to start encryption.
vold.decrypt trigger_restart_min_framework Set by vold to start the progress bar UI for encryption or prompt for password, depending on the value of ro.crypto.state .
vold.encrypt_progress When the framework starts up, if this property is set, enter the progress bar UI mode.
vold.encrypt_progress 0 to 100 The progress bar UI should display the percentage value set.
vold.encrypt_progress error_partially_encrypted The progress bar UI should display a message that the encryption failed, and give the user an option to factory reset the device.
vold.encrypt_progress error_reboot_failed The progress bar UI should display a message saying encryption completed, and give the user a button to reboot the device. This error is not expected to happen.
vold.encrypt_progress error_not_encrypted The progress bar UI should display a message saying an error occurred, no data was encrypted or lost, and give the user a button to reboot the system.
vold.encrypt_progress error_shutting_down The progress bar UI is not running, so it is unclear who will respond to this error. And it should never happen anyway.
vold.post_fs_data_done 0 Set by vold just before setting vold.decrypt to trigger_post_fs_data .
vold.post_fs_data_done 1 Set by init.rc or init.rc just after finishing the task post-fs-data .

init properties

Property Description
ro.crypto.fs_crypto_blkdev Set by the vold command checkpw for later use by the vold command restart .
ro.crypto.state unencrypted Set by init to say this system is running with an unencrypted /data ro.crypto.state encrypted . Set by init to say this system is running with an encrypted /data .

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

These five properties are set by init when it tries to mount /data with parameters passed in from init.rc . vold uses these to setup the crypto mapping.
ro.crypto.tmpfs_options Set by init.rc with the options init should use when mounting the tmpfs /data filesystem.

Init actions

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