全磁碟加密

全磁碟加密是使用 加密金鑰裝置加密後,所有使用者建立的資料都會在寫入磁碟前自動加密,所有讀取作業都會在將資料傳回呼叫程序前自動解密資料。

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 碼
  • 密碼
  • 圖案

初次啟動時,裝置會建立隨機產生的 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),並將 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. 啟動架構

    vold 會將 vold.decrypt 設為 trigger_restart_framework,繼續執行一般啟動程序。

加密現有裝置

將未加密的 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. 掛接 /資料

    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 傳回錯誤,使用者介面應向使用者顯示訊息,要求他們重新啟動裝置並將裝置恢復原廠設定,並提供按鈕供使用者按下執行。

  4. 使用密碼解密資料

    cryptfs cryptocomplete 成功後,架構 會顯示一個 UI,要求提供磁碟密碼。UI 會藉由 傳送 cryptfs checkpw 指令至 vold。如果密碼正確 (系統會在暫時位置成功掛載解密的 /data,然後卸載),vold 會在屬性 ro.crypto.fs_crypto_blkdev 中儲存解密的區塊裝置名稱,並將狀態 0 傳回至 UI。如果密碼不正確,則會將 -1 傳回至 UI。

  5. 停止架構

    UI 會顯示加密啟動圖形,然後使用 cryptfs restart 指令呼叫 voldvold 會設定屬性 vold.decrypttrigger_reset_main,這會導致 需init.rc執行 class_reset main。這會停止所有服務 位於主要類別中,這可讓系統卸載 tmpfs /data

  6. Mount /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. 為使用者密碼和鹽,產生 32 位元組的中繼資訊 鍵 1 (IK1)。
  3. 為硬體繫結私密金鑰 (HBK) 大小提供零位元組的 Pad IK1。 具體來說,我們會以以下方式填充:00 || IK1 || 00..00;一個零位元組、32 個 IK1 位元組、223 個零位元組。
  4. 使用 HBK 簽署已填充的 IK1,以產生 256 位元組的 IK2。
  5. 將 scrypt 套用至 IK2 和鹽 (與步驟 2 相同的鹽),產生 32 位元組的 IK3。
  6. 請使用 IK3 的前 16 位元組做為 KEK,並將最後 16 位元組做為 IV。
  7. 使用 AES_CBC 加密 DEK,並使用金鑰 KEK 和初始化向量 IV 加密。

變更密碼

使用者在設定中選擇變更或移除密碼時,使用者介面會傳送 將 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 由 vold 設定,用於關閉要求磁碟密碼的 UI。
vold.decrypt trigger_post_fs_data 由 vold 設定,以便使用必要的目錄等準備 /data
vold.decrypt trigger_restart_framework 由 轉變為啟動實際架構和所有服務。
vold.decrypt trigger_shutdown_framework 由 vold 設定,用於關閉整個架構以啟動加密作業。
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 進度列 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 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 會在掛接 tmpfs /data 檔案系統時,使用 init.rc 設定的選項。

init 動作

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