全磁碟加密

全磁碟加密是使用 加密金鑰裝置經過加密後,所有使用者建立的資料都會受到 自動加密後再將檔案提交至磁碟和所有讀取作業 就會自動解密資料,再將資料傳回呼叫程序。

全磁碟加密功能是在 Android 4.4 中推出,但 Android 5.0 推出了以下新功能:

  • 建立快速加密,只對已使用的資料分區加密的區塊加密 可避免初次啟動花費太多時間僅限 ext4 和 f2fs 檔案系統 請注意,目前也支援快速加密
  • 新增 forceencrypt fstab 旗標,以便在首次啟動時加密。
  • 新增對不含密碼的圖案和加密功能支援。
  • 新增採用「可信任」機制的加密金鑰儲存空間硬體支援儲存空間 執行環境 (TEE) 簽署能力 (例如 TrustZone 中)。 詳情請參閱儲存加密金鑰 詳細資料。

注意:先升級至 Android 5.0 的裝置 恢復原廠設定後,任何加密狀態都會還原為未加密狀態。在首次啟動時加密的新版 Android 5.0 裝置無法還原為未加密狀態。

Android 全磁碟加密功能的運作方式

Android 全磁碟加密是以 dm-crypt 為基礎,這是核心 可以在封鎖裝置層運作因此,加密功能可與 Embedded MultiMediaCard (eMMC) 和類似的隨身碟裝置搭配使用,這些裝置會將自己呈現給核心,做為區塊裝置。YAFFS 無法直接加密原始模型, NAND 快閃晶片。

加密演算法採用 128 Advanced Encryption Standard (AES), 加密區塊鏈結 (CBC) 和 ESSIV:SHA256。主金鑰會透過呼叫 OpenSSL 程式庫,以 128 位元 AES 加密。您必須使用 128 位元以上的 鍵 (256 為選用)。

注意:原始設備製造商 (OEM) 可使用 128 位元以上將主金鑰加密。

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

  • 預設值
  • PIN 碼
  • 密碼
  • pattern

裝置首次啟動時,會隨機產生 128 位元主金鑰,然後使用預設密碼和儲存的鹽值進行雜湊運算。預設密碼為:「default_password」 不過,結果雜湊會透過 TEE (例如 TrustZone) 簽署。 使用簽章的雜湊值加密主金鑰。

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

當使用者在裝置上設定 PIN 碼/密碼或密碼時,系統只會重新加密及儲存 128 位元金鑰。(例如,使用者變更 PIN 碼/密碼/解鎖圖案不會導致重新加密使用者資料)。請注意, 受管理的裝置 可能受到 PIN 碼、解鎖圖案或密碼的限制。

加密功能由 initvold 管理。init 呼叫 vold,而且 vold 集會觸發要觸發的屬性 初始化事件。系統的其他部分也會查看屬性,執行相關工作,例如回報狀態、要求輸入密碼,或在發生致命錯誤時提示使用者重設為原廠設定。叫用 加密功能,系統將會使用指令列工具 (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。以下詳細說明每個流程。

使用強制加密功能加密新裝置

這是 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 接著使用以下程式碼,在 tmpfs RAMDisk 上掛接 /data: 系統從 ro.crypto.tmpfs_options 擷取的參數 位置:init.rc

  7. 開始使用架構

    voldvold.decrypt設為 trigger_restart_framework,會繼續正常啟動 上傳資料集之後,您可以運用 AutoML 自動完成部分資料準備工作

加密現有裝置

這是在您加密未加密的 Android K 或已遷移至 L 的舊版裝置時會發生的情況。

這項程序是由使用者啟動,在程式碼中稱為「原地加密」。使用者選擇加密裝置時,UI 會確保 電池已充飽電,且 AC 變壓器已接上電源,確保有足夠的電力 才能完成加密程序。

警告:如果裝置電力耗盡,並在完成加密前關機,檔案資料就會處於部分加密狀態。裝置必須恢復原廠設定,且所有資料都會遺失。

如要啟用內置加密,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,會每隔五秒就查詢該屬性,並更新進度列。 每次加密分區的百分比時,加密迴圈都會更新 vold.encrypt_progress

  10. /data 遭到加密時,請更新加密頁尾

    /data 成功加密後,vold 會清除中繼資料中的標記 ENCRYPTION_IN_PROGRESS

    裝置成功解鎖後,系統就會使用密碼加密主金鑰,並更新加密附註。

    如果因故重新啟動失敗,vold 會設定屬性 vold.encrypt_progresserror_reboot_failed和 UI 應顯示訊息,要求使用者按下按鈕 。請放心,這種錯誤不會發生。

啟動使用預設加密機制的加密裝置

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

  1. 偵測無密碼的已加密 /data

    偵測 Android 裝置已加密,因為無法掛載 /data,且已設定其中一個標記 encryptableforceencrypt

    voldvold.decrypt設為 trigger_default_encryption,從 「defaultcrypto」服務。trigger_default_encryption 會檢查加密類型,查看 /data 是否已使用密碼或不使用密碼加密。

  2. 解密 /data

    在區塊裝置上建立 dm-crypt 裝置,讓裝置可供使用。

  3. 掛載 /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 類別中啟動服務,第一個使用 所花費的時間。

  4. 啟動架構

    框架現在會使用已解密的 /data 啟動所有服務,系統也已準備就緒。

啟動未預設加密的加密裝置

這是在啟動已設定密碼的加密裝置時會發生的情況。裝置的密碼可以是 PIN 碼、解鎖圖案或密碼。

  1. 偵測使用密碼加密的裝置

    偵測 Android 裝置因標記而已加密 ro.crypto.state = "encrypted"

    vold 會將 vold.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 會顯示加密啟動圖形,然後利用以下內容呼叫 vold: 輸入 cryptfs restart 指令vold 會設定屬性 vold.decrypttrigger_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. 為硬體繫結私密金鑰 (HBK) 大小提供零位元組的 Pad IK1。 具體來說,我們會以以下方式填充:00 || IK1 || 00..00;一個零位元組、32 個 IK1 位元組、223 個零位元組。
  4. 使用 HBK 簽署已填充的 IK1,以產生 256 位元組的 IK2。
  5. 套用 IK2 和鹽 (與步驟 2 相同的鹽) 以產生 32 位元組 IK3。
  6. 使用 IK3 的前 16 個位元組做為 KEK,最後一個 16 個位元組做為 IV。
  7. 使用 AES_CBC 加密 DEK,並使用金鑰 KEK 和初始化向量 IV 加密。

變更密碼

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

加密屬性

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

Vold 屬性

資源 說明
vold.decrypt trigger_encryption 將磁碟加密,不使用 密碼。
vold.decrypt trigger_default_encryption 檢查雲端硬碟,確認檔案是否經過密碼加密。 如果是,請解密並掛載,否則請將 vold.decrypt 設為 trigger_restart_min_framework。
vold.decrypt trigger_reset_main 設為關閉要求提供磁碟密碼的 UI。
vold.decrypt trigger_post_fs_data 設定前提為 /data 準備必要的目錄等等。
vold.decrypt trigger_restart_framework 由 vold 設定,用於啟動實際架構和所有服務。
vold.decrypt trigger_shutdown_framework 設為關閉完整架構,啟動加密作業。
vold.decrypt trigger_restart_min_framework 由 vold 設定,視 ro.crypto.state 的值而定,可啟動加密進度列 UI 或密碼提示。
vold.encrypt_progress 當架構啟動時,如果已設定此屬性,就會進入進度列 UI 模式。
vold.encrypt_progress 0 to 100 進度列 UI 應顯示設定的百分比值。
vold.encrypt_progress error_partially_encrypted 進度列使用者介面應會顯示加密失敗的訊息, 為使用者提供 將裝置恢復原廠設定。
vold.encrypt_progress error_reboot_failed 進度列 UI 應顯示加密完成的訊息,並提供使用者重新啟動裝置的按鈕。這個錯誤 表示無需發生
vold.encrypt_progress error_not_encrypted 進度列 UI 應顯示訊息,說明發生錯誤,但沒有任何資料遭到加密或遺失,並提供使用者按鈕來重新啟動系統。
vold.encrypt_progress error_shutting_down 進度列使用者介面並未執行,因此無法確定回應者是誰 。而這個情況應該永遠不會發生。
vold.post_fs_data_done 0 在將 vold.decrypt 設為 trigger_post_fs_data 之前,由 vold 設定。
vold.post_fs_data_done 1 init.rcinit.rc 在完成 post-fs-data 工作後設定。

init 屬性

資源 說明
ro.crypto.fs_crypto_blkdev 透過 vold 指令 checkpw 設定以供日後使用 呼叫 vold 指令 restart
ro.crypto.state unencrypted init 設定,表示系統正在使用未加密的 /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_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