全盤加密

全盤加密是使用加密密鑰對 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)進行簽名,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 。下面詳細解釋每個流程。

使用 forceencrypt 加密新設備

這是 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設置為啟動defaultcrypto服務的trigger_default_encryption 。 (這將啟動下面安裝默認加密用戶數據的流程。) trigger_default_encryption檢查加密類型以查看/data是否使用密碼進行加密。由於 Android 5.0 設備在首次啟動時已加密,因此不應設置密碼;因此我們解密並掛載/data

  6. 掛載/data

    然後init使用從ro.crypto.tmpfs_options中設置的init.rc獲取的參數將/data掛載到 tmpfs RAMDisk 上。

  7. 啟動框架

    vold設置為trigger_restart_framework ,這將繼續通常的引導過程。

加密現有設備

當您對已遷移到 L 的未加密 Android K 或更早版本的設備進行加密時,就會發生這種情況。

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

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

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

設備狀態:設置ro.crypto.state = "unencrypted"並執行on nonencrypted init trigger 以繼續引導。

  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,每五秒查詢一次該屬性並更新一個進度條。每次加密另一個百分比的分區時,加密循環都會更新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啟動它的所有服務,並且系統可以使用了。

啟動沒有默認加密的加密設備

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

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

    檢測到安卓設備是加密的,因為標誌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,或者加密未成功完成返回 -2。 vold通過查看CRYPTO_ENCRYPTION_IN_PROGRESS標誌的加密元數據來確定這一點。如果已設置,則加密過程被中斷,並且設備上沒有可用數據。如果vold返回錯誤,UI 應該向用戶顯示一條消息以重新啟動設備並將設備恢復出廠設置,並為用戶提供一個按鈕來執行此操作。

  4. 使用密碼解密數據

    一旦cryptfs cryptocomplete成功,框架會顯示一個 UI 詢問磁盤密碼。 UI 通過將命令cryptfs checkpw發送到vold來檢查密碼。如果密碼正確(通過成功將解密的/data掛載到臨時位置,然後卸載它來確定), vold將解密的塊設備的名稱保存在屬性ro.crypto.fs_crypto_blkdev中,並將狀態 0 返回到 UI .如果密碼不正確,它會向 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。然後,用戶界面應顯示一條消息,說明加密失敗,並為用戶提供一個按鈕以將設備恢復出廠設置。

存儲加密密鑰

加密的密鑰存儲在加密元數據中。硬件支持是通過使用可信執行環境 (TEE) 的簽名功能來實現的。以前,我們使用通過對用戶密碼和存儲的鹽應用 scrypt 生成的密鑰來加密主密鑰。為了使密鑰能夠抵禦開箱攻擊,我們通過使用存儲的 TEE 密鑰對結果密鑰進行簽名來擴展該算法。然後通過 scrypt 的另一種應用程序將生成的簽名轉換為適當長度的密鑰。然後使用此密鑰對主密鑰進行加密和解密。要存儲此密鑰:

  1. 生成隨機的 16 字節磁盤加密密鑰 (DEK) 和 16 字節鹽。
  2. 對用戶密碼和 salt 應用 scrypt 以生成 32 字節的中間密鑰 1 (IK1)。
  3. 用零字節填充 IK1 到硬件綁定私鑰 (HBK) 的大小。具體來說,我們填充為:00 || IK1 || 00..00; 1 個零字節,32 個 IK1 字節,223 個零字節。
  4. 用 HBK 對 IK1 進行符號填充以生成 256 字節的 IK2。
  5. 將 scrypt 應用於 IK2 和 salt(與步驟 2 相同的 salt)以生成 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_blkdevvold命令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