全盤加密是使用加密密鑰對 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 閃存芯片對話。
加密算法是 128 高級加密標準 (AES),帶有密碼塊鏈接 (CBC) 和 ESSIV:SHA256。主密鑰通過調用 OpenSSL 庫使用 128 位 AES 加密。您必須為密鑰使用 128 位或更多位(256 位是可選的)。
注意: OEM 可以使用 128 位或更高版本來加密主密鑰。
在Android 5.0版本中,有四種加密狀態:
- 默認
- 別針
- 密碼
- 圖案
首次啟動時,設備會創建一個隨機生成的 128 位主密鑰,然後使用默認密碼和存儲的鹽對其進行哈希處理。默認密碼是:“default_password” 然而,生成的哈希值也是通過 TEE(例如 TrustZone)簽名的,它使用簽名的哈希值來加密主密鑰。
您可以在 Android 開源項目cryptfs.cpp文件中找到定義的默認密碼。
當用戶在設備上設置 PIN/密碼或密碼時,只有 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 及更早版本)。
- 使用
- 啟動加密設備:
- Starting an encrypted device with no password: Booting an encrypted device that has no set password(適用於運行Android 5.0及更高版本的設備)。
- Starting an encrypted device with a password:啟動一個設置了密碼的加密設備。
除了這些流之外,設備還可能無法加密/data
。下面詳細解釋每個流程。
使用 forceencrypt 加密新設備
這是 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
設置為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 中獲取的參數將/data
掛載到 tmpfs RAMDisk 上,該參數在ro.crypto.tmpfs_options
中init.rc
。 - 啟動框架
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
設置為vold
準備 tmpfs/data
以啟動加密系統並將屬性vold.decrypt
設置為:trigger_restart_min_framework
- 調出框架以顯示進度
trigger_restart_min_framework
導致init.rc
啟動main
服務類。當框架發現vold.encrypt_progress
設置為 0 時,它會顯示進度條 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
磁盤上啟動並且需要獲取用戶密碼。但是,首先,它需要確保磁盤已正確加密。它向
vold
發送命令cryptfs cryptocomplete
。如果加密成功完成,vold
返回 0,如果出現內部錯誤,則返回 -1,如果加密未成功完成,則返回 -2。vold
通過查看CRYPTO_ENCRYPTION_IN_PROGRESS
標誌的加密元數據來確定這一點。如果已設置,則加密過程被中斷,並且設備上沒有可用數據。如果vold
返回錯誤,UI 應向用戶顯示一條消息以重啟設備並將設備重置為出廠設置,並為用戶提供一個按鈕以執行此操作。 - 用密碼解密數據
cryptfs cryptocomplete
成功後,框架會顯示一個 UI,要求輸入磁盤密碼。 UI 通過向vold
發送命令cryptfs checkpw
來檢查密碼。如果密碼正確(這是通過將解密的/data
成功掛載到一個臨時位置,然後卸載它來確定的),vold
將解密的塊設備的名稱保存在屬性ro.crypto.fs_crypto_blkdev
中,並將狀態 0 返回到 UI .如果密碼不正確,它會向 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;一個零字節,32 個 IK1 字節,223 個零字節。
- 使用 HBK 對 IK1 進行符號填充以生成 256 字節的 IK2。
- 將 scrypt 應用於 IK2 和鹽(與步驟 2 相同的鹽)以生成 32 字節的 IK3。
- 使用 IK3 的前 16 個字節作為 KEK,最後 16 個字節作為 IV。
- 使用 AES_CBC、密鑰 KEK 和初始化向量 IV 加密 DEK。
更改密碼
當用戶選擇在設置中更改或刪除其密碼時,UI 會向vold
發送命令cryptfs changepw
,然後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 設置為 prep /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