全盤加密是使用加密密鑰對 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、模式或密碼限制。
加密由init
和vold
管理。 init
調用vold
,vold 設置屬性以觸發 init 中的事件。系統的其他部分也會查看屬性來執行任務,例如報告狀態、詢問密碼或在發生致命錯誤時提示恢復出廠設置。為了調用vold
中的加密功能,系統使用命令行工具vdc
的cryptfs
命令: checkpw
、 restart
、 enablecrypto
、 changepw
、 cryptocomplete
、 verifypw
、 setfield
、 getfield
、 mountdefaultencrypted
、 getpwtype
、 getpw
和clearpw
。
為了加密、解密或擦除/data
,不得安裝/data
。但是,為了顯示任何用戶界面 (UI),框架必須啟動,並且框架需要/data
才能運行。為了解決這個難題,在/data
上安裝了一個臨時文件系統。這允許 Android 根據需要提示輸入密碼、顯示進度或建議擦除數據。它確實施加了限制,為了從臨時文件系統切換到真正的/data
文件系統,系統必須停止臨時文件系統上打開文件的每個進程,並在真正的/data
文件系統上重新啟動這些進程。為此,所有服務必須屬於三個組之一: core
、 main
和late_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 設備的正常首次啟動。
- 使用
forceencrypt
標誌檢測未加密的文件系統/data
未加密,但需要加密,因為forceencrypt
要求它。卸載/data
。 - 開始加密
/data
vold.decrypt = "trigger_encryption"
觸發init.rc
,這將導致vold
在沒有密碼的情況下加密/data
。 (未設置任何內容,因為這應該是新設備。) - 掛載tmpfs
vold
安裝 tmpfs/data
(使用ro.crypto.tmpfs_options
中的 tmpfs 選項)並將屬性vold.encrypt_progress
設置為 0。vold
準備 tmpfs/data
用於啟動加密系統,並將屬性vold.decrypt
設置為:trigger_restart_min_framework
- 提出框架以顯示進度
由於設備實際上沒有要加密的數據,因此通常不會實際出現進度條,因為加密發生得如此之快。有關進度 UI 的更多詳細信息,請參閱加密現有設備。
-
/data
加密後,取下框架vold
將vold.decrypt
設置為trigger_default_encryption
,從而啟動defaultcrypto
服務。 (這將啟動下面用於安裝默認加密用戶數據的流程。)trigger_default_encryption
檢查加密類型以查看/data
是否使用密碼加密。由於Android 5.0設備在首次啟動時已加密,因此不應設置密碼;因此我們解密並掛載/data
。 - 掛載
/data
然後
init
使用從ro.crypto.tmpfs_options
獲取的參數(在init.rc
中設置)將/data
掛載到 tmpfs RAMDisk 上。 - 啟動框架
vold
將vold.decrypt
設置為trigger_restart_framework
,這將繼續通常的啟動過程。
加密現有設備
當您加密已遷移到 L 的未加密 Android K 或更早版本的設備時,就會發生這種情況。
此過程是用戶發起的,在代碼中稱為“就地加密”。當用戶選擇加密設備時,UI 會確保電池已充滿電並且交流適配器已插入,以便有足夠的電量來完成加密過程。
警告:如果設備在完成加密之前電量耗盡並關閉,文件數據將處於部分加密狀態。設備必須恢復出廠設置,所有數據都會丟失。
為了啟用就地加密, vold
啟動一個循環來讀取真實塊設備的每個扇區,然後將其寫入加密塊設備。 vold
在讀取和寫入扇區之前檢查該扇區是否正在使用,這使得在幾乎沒有數據的新設備上加密速度更快。
設備狀態:設置ro.crypto.state = "unencrypted"
並執行on nonencrypted
init
觸發器以繼續啟動。
- 檢查密碼
UI 使用命令
cryptfs enablecrypto inplace
調用vold
,其中passwd
是用戶的鎖屏密碼。 - 拆掉框架
vold
檢查錯誤,如果無法加密則返回 -1,並在日誌中打印原因。如果它可以加密,則會將屬性vold.decrypt
設置為trigger_shutdown_framework
。這會導致init.rc
停止類late_start
和main
中的服務。 - 創建加密頁腳
- 創建麵包屑文件
- 重啟
- 檢測麵包屑文件
- 開始加密
/data
vold
然後設置加密映射,該映射創建一個虛擬加密塊設備,映射到真實塊設備,但在寫入時加密每個扇區,並在讀取時解密每個扇區。vold
然後創建並寫出加密元數據。 - 加密時掛載 tmpfs
vold
安裝 tmpfs/data
(使用ro.crypto.tmpfs_options
中的 tmpfs 選項)並將屬性vold.encrypt_progress
設置為 0。vold
準備 tmpfs/data
用於啟動加密系統,並將屬性vold.decrypt
設置為:trigger_restart_min_framework
- 提出框架以顯示進度
trigger_restart_min_framework
導致init.rc
啟動main
服務類。當框架發現vold.encrypt_progress
設置為 0 時,它會顯示進度條 UI,該 UI 每五秒查詢一次該屬性並更新進度條。每次加密分區的另一個百分比時,加密循環都會更新vold.encrypt_progress
。 - 當
/data
被加密時,更新加密頁腳當
/data
成功加密後,vold
會清除元數據中的ENCRYPTION_IN_PROGRESS
標誌。當設備成功解鎖後,密碼將用於加密主密鑰並更新加密頁腳。
如果由於某種原因重新啟動失敗,
vold
會將屬性vold.encrypt_progress
設置為error_reboot_failed
,並且 UI 應顯示一條消息,要求用戶按按鈕重新啟動。預計這種情況永遠不會發生。
使用默認加密啟動加密設備
這就是當您在沒有密碼的情況下啟動加密設備時會發生的情況。由於 Android 5.0 設備在首次啟動時進行加密,因此不應設置密碼,因此這是默認加密狀態。
- 檢測沒有密碼的加密
/data
檢測到 Android 設備已加密,因為無法掛載
/data
並且設置了encryptable
或forceencrypt
標誌之一。vold
將vold.decrypt
設置為trigger_default_encryption
,這將啟動defaultcrypto
服務。trigger_default_encryption
檢查加密類型以查看/data
是否使用密碼加密。 - 解密/數據
在塊設備上創建
dm-crypt
設備,以便該設備可供使用。 - 掛載/數據
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
類中的服務。 - 啟動框架
現在,框架使用解密的
/data
啟動其所有服務,並且系統已準備好使用。
啟動沒有默認加密的加密設備
當您啟動具有設置密碼的加密設備時,就會發生這種情況。設備的密碼可以是 PIN、圖案或密碼。
- 使用密碼檢測加密設備
檢測到 Android 設備已加密,因為標誌
ro.crypto.state = "encrypted"
vold
將vold.decrypt
設置為trigger_restart_min_framework
,因為/data
是用密碼加密的。 - 掛載tmpfs
init
設置五個屬性來保存為/data
指定的初始安裝選項以及從init.rc
傳遞的參數。vold
使用這些屬性來設置加密映射:-
ro.crypto.fs_type
-
ro.crypto.fs_real_blkdev
-
ro.crypto.fs_mnt_point
-
ro.crypto.fs_options
-
ro.crypto.fs_flags
(ASCII 8 位十六進制數字,前面帶有 0x)
-
- 啟動框架提示輸入密碼
框架啟動並看到
vold.decrypt
設置為trigger_restart_min_framework
。這告訴框架它正在 tmpfs/data
磁盤上啟動,並且需要獲取用戶密碼。然而,首先需要確保磁盤已正確加密。它將命令
cryptfs cryptocomplete
發送到vold
。如果加密成功完成,則vold
返回 0;如果發生內部錯誤,則返回 -1;如果加密未成功完成,則 vold 返回 -2。vold
通過在加密元數據中查找CRYPTO_ENCRYPTION_IN_PROGRESS
標誌來確定這一點。如果已設置,則加密過程將被中斷,並且設備上沒有可用的數據。如果vold
返回錯誤,UI 應向用戶顯示一條消息,要求用戶重新啟動設備並恢復出廠設置,並為用戶提供一個按鈕來執行此操作。 - 使用密碼解密數據
一旦
cryptfs cryptocomplete
成功,框架就會顯示一個 UI,要求輸入磁盤密碼。 UI 通過將命令cryptfs checkpw
發送到vold
來檢查密碼。如果密碼正確(通過在臨時位置成功掛載解密的/data
然後卸載它來確定),vold
會將解密的塊設備的名稱保存在屬性ro.crypto.fs_crypto_blkdev
中,並向 UI 返回狀態 0 。如果密碼不正確,則向 UI 返回 -1。 - 停止框架
UI 會顯示一個加密啟動圖形,然後使用命令
cryptfs restart
調用vold
。vold
將屬性vold.decrypt
設置為trigger_reset_main
,這會導致init.rc
執行class_reset main
。這將停止主類中的所有服務,從而允許卸載 tmpfs/data
。 - 掛載
/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
類中的服務。 - 啟動完整框架
現在,框架使用解密的
/data
文件系統啟動其所有服務,並且系統已準備好使用。
失敗
無法解密的設備可能由於多種原因而出現問題。設備啟動時會執行一系列正常的啟動步驟:
- 使用密碼檢測加密設備
- 掛載tmpfs
- 啟動框架提示輸入密碼
但框架打開後,設備可能會遇到一些錯誤:
- 密碼匹配但無法解密數據
- 用戶輸入錯誤密碼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 應用程序將所得簽名轉換為適當長度的密鑰。然後使用該密鑰來加密和解密主密鑰。要存儲此密鑰:
- 生成隨機 16 字節磁盤加密密鑰 (DEK) 和 16 字節鹽。
- 將 scrypt 應用於用戶密碼和鹽以生成 32 字節中間密鑰 1 (IK1)。
- 將 IK1 用零字節填充到硬件綁定私鑰 (HBK) 的大小。具體來說,我們填充為:00 || IK1 || 00..00; 1 個零字節、32 個 IK1 字節、223 個零字節。
- 用 HBK 對 IK1 進行符號填充,生成 256 字節的 IK2。
- 將 scrypt 應用於 IK2 和鹽(與步驟 2 相同的鹽)以生成 32 字節 IK3。
- 使用 IK3 的前 16 個字節作為 KEK,後 16 個字節作為 IV。
- 使用 AES_CBC、密鑰 KEK 和初始化向量 IV 加密 DEK。
更改密碼
當用戶選擇更改或刪除設置中的密碼時,UI 會將命令cryptfs changepw
發送到vold
,然後vold
使用新密碼重新加密磁盤主密鑰。
加密屬性
vold
和init
通過設置屬性來相互通信。以下是可用於加密的屬性的列表。
沃爾德屬性
財產 | 描述 |
---|---|
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.rc 或init.rc 設置。 |
初始化屬性
財產 | 描述 |
---|---|
ro.crypto.fs_crypto_blkdev | 通過vold 命令checkpw 設置,以便稍後通過vold 命令restart 使用。 |
ro.crypto.state unencrypted | 由init 設置表示該系統正在使用未加密的/data ro.crypto.state encrypted 運行。由init 設置表示該系統正在使用加密的/data 運行。 |
| 這五個屬性由init 在嘗試使用從init.rc 傳入的參數掛載/data 時設置。 vold 使用這些來設置加密映射。 |
ro.crypto.tmpfs_options | 由init.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、模式或密碼限制。
加密由init
和vold
管理。 init
調用vold
,vold 設置屬性以觸發 init 中的事件。系統的其他部分也會查看屬性來執行任務,例如報告狀態、詢問密碼或在發生致命錯誤時提示恢復出廠設置。為了調用vold
中的加密功能,系統使用命令行工具vdc
的cryptfs
命令: checkpw
、 restart
、 enablecrypto
、 changepw
、 cryptocomplete
、 verifypw
、 setfield
、 getfield
、 mountdefaultencrypted
、 getpwtype
、 getpw
和clearpw
。
為了加密、解密或擦除/data
,不得安裝/data
。但是,為了顯示任何用戶界面 (UI),框架必須啟動,並且框架需要/data
才能運行。為了解決這個難題,在/data
上安裝了一個臨時文件系統。這允許 Android 根據需要提示輸入密碼、顯示進度或建議擦除數據。它確實施加了限制,為了從臨時文件系統切換到真正的/data
文件系統,系統必須停止臨時文件系統上打開文件的每個進程,並在真正的/data
文件系統上重新啟動這些進程。為此,所有服務必須屬於三個組之一: core
、 main
和late_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 設備的正常首次啟動。
- 使用
forceencrypt
標誌檢測未加密的文件系統/data
未加密,但需要加密,因為forceencrypt
要求它。卸載/data
。 - 開始加密
/data
vold.decrypt = "trigger_encryption"
觸發init.rc
,這將導致vold
在沒有密碼的情況下加密/data
。 (未設置任何內容,因為這應該是新設備。) - 掛載tmpfs
vold
安裝 tmpfs/data
(使用ro.crypto.tmpfs_options
中的 tmpfs 選項)並將屬性vold.encrypt_progress
設置為 0。vold
準備 tmpfs/data
用於啟動加密系統,並將屬性vold.decrypt
設置為:trigger_restart_min_framework
- 提出框架以顯示進度
由於設備實際上沒有要加密的數據,因此通常不會實際出現進度條,因為加密發生得如此之快。有關進度 UI 的更多詳細信息,請參閱加密現有設備。
-
/data
加密後,取下框架vold
將vold.decrypt
設置為trigger_default_encryption
,從而啟動defaultcrypto
服務。 (這將啟動下面用於安裝默認加密用戶數據的流程。)trigger_default_encryption
檢查加密類型以查看/data
是否使用密碼加密。由於Android 5.0設備在首次啟動時已加密,因此不應設置密碼;因此我們解密並掛載/data
。 - 掛載
/data
然後
init
使用從ro.crypto.tmpfs_options
獲取的參數(在init.rc
中設置)將/data
掛載到 tmpfs RAMDisk 上。 - 啟動框架
vold
將vold.decrypt
設置為trigger_restart_framework
,這將繼續通常的啟動過程。
加密現有設備
當您加密已遷移到 L 的未加密 Android K 或更早版本的設備時,就會發生這種情況。
此過程是用戶發起的,在代碼中稱為“就地加密”。當用戶選擇加密設備時,用戶界面會確保電池已充滿電並且交流適配器已插入,以便有足夠的電量來完成加密過程。
警告:如果設備在完成加密之前電量耗盡並關閉,文件數據將處於部分加密狀態。設備必須恢復出廠設置,所有數據都會丟失。
為了啟用就地加密, vold
啟動一個循環來讀取真實塊設備的每個扇區,然後將其寫入加密塊設備。 vold
在讀取和寫入扇區之前檢查扇區是否正在使用,這使得在幾乎沒有數據的新設備上加密速度更快。
設備狀態:設置ro.crypto.state = "unencrypted"
並執行on nonencrypted
init
觸發器以繼續啟動。
- 檢查密碼
UI 使用命令
cryptfs enablecrypto inplace
調用vold
,其中passwd
是用戶的鎖屏密碼。 - 拆掉框架
vold
檢查錯誤,如果無法加密則返回 -1,並在日誌中打印原因。如果它可以加密,則會將屬性vold.decrypt
設置為trigger_shutdown_framework
。這會導致init.rc
停止類late_start
和main
中的服務。 - 創建加密頁腳
- 創建麵包屑文件
- 重啟
- 檢測麵包屑文件
- 開始加密
/data
vold
然後設置加密映射,該映射創建一個虛擬加密塊設備,映射到真實塊設備,但在寫入時加密每個扇區,並在讀取時解密每個扇區。vold
然後創建並寫出加密元數據。 - 加密時掛載 tmpfs
vold
安裝 tmpfs/data
(使用ro.crypto.tmpfs_options
中的 tmpfs 選項)並將屬性vold.encrypt_progress
設置為 0。vold
準備 tmpfs/data
用於啟動加密系統,並將屬性vold.decrypt
設置為:trigger_restart_min_framework
- 提出框架以顯示進度
trigger_restart_min_framework
導致init.rc
啟動main
服務類。當框架發現vold.encrypt_progress
設置為 0 時,它會顯示進度條 UI,該 UI 每五秒查詢一次該屬性並更新進度條。每次加密分區的另一個百分比時,加密循環都會更新vold.encrypt_progress
。 - 當
/data
被加密時,更新加密頁腳當
/data
成功加密後,vold
會清除元數據中的ENCRYPTION_IN_PROGRESS
標誌。當設備成功解鎖後,密碼將用於加密主密鑰並更新加密頁腳。
如果由於某種原因重新啟動失敗,
vold
會將屬性vold.encrypt_progress
設置為error_reboot_failed
,並且 UI 應顯示一條消息,要求用戶按按鈕重新啟動。預計這種情況永遠不會發生。
使用默認加密啟動加密設備
這就是當您在沒有密碼的情況下啟動加密設備時會發生的情況。由於 Android 5.0 設備在首次啟動時進行加密,因此不應設置密碼,因此這是默認加密狀態。
- 檢測沒有密碼的加密
/data
檢測到 Android 設備已加密,因為無法掛載
/data
並且設置了encryptable
或forceencrypt
標誌之一。vold
將vold.decrypt
設置為trigger_default_encryption
,這將啟動defaultcrypto
服務。trigger_default_encryption
檢查加密類型以查看/data
是否使用密碼加密。 - 解密/數據
在塊設備上創建
dm-crypt
設備,以便該設備可供使用。 - 掛載/數據
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
類中的服務。 - 啟動框架
現在,框架使用解密的
/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.
- Detect encrypted device with a password
Detect that the Android device is encrypted because the flag
ro.crypto.state = "encrypted"
vold
setsvold.decrypt
totrigger_restart_min_framework
because/data
is encrypted with a password. - Mount tmpfs
init
sets five properties to save the initial mount options given for/data
with parameters passed frominit.rc
.vold
uses these properties to set up the crypto mapping:-
ro.crypto.fs_type
-
ro.crypto.fs_real_blkdev
-
ro.crypto.fs_mnt_point
-
ro.crypto.fs_options
-
ro.crypto.fs_flags
(ASCII 8-digit hex number preceded by 0x)
-
- Start framework to prompt for password
The framework starts up and sees that
vold.decrypt
is set totrigger_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
tovold
.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 theCRYPTO_ENCRYPTION_IN_PROGRESS
flag. If it's set, the encryption process was interrupted, and there is no usable data on the device. Ifvold
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. - 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 commandcryptfs checkpw
tovold
. 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 propertyro.crypto.fs_crypto_blkdev
and returns status 0 to the UI. If the password is incorrect, it returns -1 to the UI. - Stop framework
The UI puts up a crypto boot graphic and then calls
vold
with the commandcryptfs restart
.vold
sets the propertyvold.decrypt
totrigger_reset_main
, which causesinit.rc
to doclass_reset main
. This stops all services in the main class, which allows the tmpfs/data
to be unmounted. - 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 propertyvold.post_fs_data_done
to 0 and then setsvold.decrypt
totrigger_post_fs_data
. This causesinit.rc
to run itspost-fs-data
commands. They will create any necessary directories or links and then setvold.post_fs_data_done
to 1. Oncevold
sees the 1 in that property, it sets the propertyvold.decrypt
totrigger_restart_framework
. This causesinit.rc
to start services in classmain
again and also start services in classlate_start
for the first time since boot. - 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:
- Detect encrypted device with a password
- Mount tmpfs
- 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:
- Generate random 16-byte disk encryption key (DEK) and 16-byte salt.
- Apply scrypt to the user password and the salt to produce 32-byte intermediate key 1 (IK1).
- 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.
- Sign padded IK1 with HBK to produce 256-byte IK2.
- Apply scrypt to IK2 and salt (same salt as step 2) to produce 32-byte IK3.
- Use the first 16 bytes of IK3 as KEK and the last 16 bytes as IV.
- 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 . |
| 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