元數據加密

Android 7.0 及更高版本支持基於文件的加密(FBE)。 FBE允許使用不同的密鑰對不同的文件進行加密,並且可以獨立解鎖。這些密鑰用於加密文件內容和文件名。使用 FBE 時,其他信息(例如目錄佈局、文件大小、權限和創建/修改時間)不會加密。總的來說,這些其他信息稱為文件系統元數據。

Android 9 引入了對元數據加密的支持。通過元數據加密,啟動時出現的單個密鑰可以加密 FBE 未加密的任何內容。該密鑰受 Keymaster 保護,而 Keymaster 又受驗證啟動保護。

只要啟用 FBE,元數據加密就始終在可採用的存儲上啟用。還可以在內部存儲上啟用元數據加密。使用 Android 11 或更高版本啟動的設備必須啟用內部存儲元數據加密。

內部存儲的實現

您可以通過設置metadata文件系統、更改初始化序列以及在設備的 fstab 文件中啟用元數據加密,在新設備的內部存儲上設置元數據加密。

先決條件

元數據加密只能在數據分區首次格式化時設置。因此,該功能僅適用於新設備;這不是 OTA 應該改變的事情。

元數據加密要求在內核中啟用dm-default-key模塊。在 Android 11 及更高版本中,Android 通用內核版本 4.14 及更高版本支持dm-default-key 。此版本的dm-default-key使用與硬件和供應商無關的加密框架,稱為blk-crypto

要啟用dm-default-key ,請使用:

CONFIG_BLK_INLINE_ENCRYPTION=y
CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y
CONFIG_DM_DEFAULT_KEY=y

dm-default-key在可用時使用內聯加密硬件(在數據傳入/傳出存儲設備時對數據進行加密/解密的硬件)。如果您使用內聯加密硬件,則還需要啟用內核加密 API 的回退:

CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y

當不使用內聯加密硬件時,您還應該按照FBE 文檔中的建議啟用任何可用的基於 CPU 的加速。

在 Android 10 及更低版本中,Android 通用內核不支持dm-default-key 。因此,由供應商來實施dm-default-key

設置元數據文件系統

由於在存在元數據加密密鑰之前無法讀取用戶數據分區中的任何內容,因此分區表必須留出一個稱為“元數據分區”的單獨分區,用於存儲保護該密鑰的密鑰主blob 。元數據分區應為 16MB。

fstab.hardware必須包含元數據文件系統的條目,該元數據文件系統位於安裝在/metadata的分區上,包括formattable標誌以確保它在啟動時格式化。 f2fs 文件系統不適用於較小的分區;我們建議使用 ext4。例如:

/dev/block/bootdevice/by-name/metadata              /metadata          ext4        noatime,nosuid,nodev,discard                          wait,check,formattable

要確保/metadata掛載點存在,請將以下行添加到BoardConfig-common.mk

BOARD_USES_METADATA_PARTITION := true

初始化順序的更改

當使用元數據加密時, vold必須在掛載/data之前運行。為了確保它足夠早啟動,請將以下節添加到init.hardware.rc

# We need vold early for metadata encryption
on early-fs
    start vold

在 init 嘗試掛載/data之前,Keymaster 必須正在運行並準備就緒。

init.hardware.rc應該已經包含一個mount_all指令,該指令將/data本身安裝在on late-fs節中。在此行之前,添加指令以執行wait_for_keymaster服務:

on late-fs
   … 
    # Wait for keymaster
    exec_start wait_for_keymaster

    # Mount RW partitions which need run fsck
    mount_all /vendor/etc/fstab.${ro.boot.hardware.platform} --late

開啟元數據加密

最後將keydirectory=/metadata/vold/metadata_encryption添加到userdatafstab條目的fs_mgr_flags列中。例如,完整的 fstab 行可能如下所示:

/dev/block/bootdevice/by-name/userdata              /data              f2fs        noatime,nosuid,nodev,discard,inlinecrypt latemount,wait,check,fileencryption=aes-256-xts:aes-256-cts:inlinecrypt_optimized,keydirectory=/metadata/vold/metadata_encryption,quota,formattable

默認情況下,內部存儲的元數據加密算法為 AES-256-XTS。這可以通過設置metadata_encryption選項來覆蓋,也在fs_mgr_flags列中:

  • 在缺乏 AES 加速的設備上,可以通過設置metadata_encryption=adiantum來啟用Adiantum 加密
  • 在支持硬件包裝密鑰的設備上,可以通過設置metadata_encryption=aes-256-xts:wrappedkey_v0 (或等效的metadata_encryption=:wrappedkey_v0 ,因為aes-256-xts是默認算法)來對元數據加密密鑰進行硬件包裝。

由於dm-default-key的內核接口在 Android 11 中發生了更改,因此您還需要確保在device.mk中為PRODUCT_SHIPPING_API_LEVEL設置了正確的值。例如,如果您的設備啟動時搭載 Android 11(API 級別 30),則device.mk應包含:

PRODUCT_SHIPPING_API_LEVEL := 30

您還可以設置以下系統屬性來強制使用新的dm-default-key API,無論發布的 API 級別如何:

PRODUCT_PROPERTY_OVERRIDES += \
    ro.crypto.dm_default_key.options_format.version=2

驗證

要驗證元數據加密是否已啟用並且正常工作,請運行下面描述的測試。另請注意下面描述的常見問題

測試

首先運行以下命令來驗證內部存儲上是否啟用了元數據加密:

adb root
adb shell dmctl table userdata

輸出應類似於:

Targets in the device-mapper table for userdata:
0-4194304: default-key, aes-xts-plain64 - 0 252:2 0 3 allow_discards sector_size:4096 iv_large_sectors

如果您通過在設備的fstab中設置metadata_encryption選項來覆蓋默認加密設置,則輸出將與上面略有不同。例如,如果您啟用Adiantum 加密,則第三個字段將為xchacha12,aes-adiantum-plain64而不是aes-xts-plain64

接下來,運行vts_kernel_encryption_test來驗證元數據加密和FBE的正確性:

atest vts_kernel_encryption_test

或者:

vts-tradefed run vts -m vts_kernel_encryption_test

常見問題

在調用mount_all期間(掛載元數據加密的/data分區), init會執行 vdc 工具。 vdc 工具通過binder連接到vold以設置元數據加密設備並掛載分區。在此調用期間, init被阻止,讀取或設置init屬性的嘗試將被阻止,直到mount_all完成。如果在此階段, vold工作的任何部分在讀取或設置屬性時直接或間接被阻止,則會導致死鎖。重要的是要確保vold可以完成讀取密鑰、與 Keymaster 交互以及掛載數據目錄的工作,而無需進一步與init交互。

如果在mount_all運行時 Keymaster 未完全啟動,則在從init讀取某些屬性之前,它不會響應vold ,從而導致所描述的死鎖。按照規定將exec_start wait_for_keymaster放置在相關的mount_all調用之上可確保 Keymaster 提前完全運行,從而避免這種死鎖。

可採用存儲上的配置

自 Android 9 起,每當啟用 FBE 時,即使未在內部存儲上啟用元數據加密,也會在可採用存儲上啟用某種形式的元數據加密。

在 AOSP 中,可採用存儲上有兩種元數據加密實現:一種基於dm-crypt已棄用的實現,以及一種基於dm-default-key較新的實現。為了確保為您的設備選擇正確的實現,請確保您已在device.mk中為PRODUCT_SHIPPING_API_LEVEL設置正確的值。例如,如果您的設備啟動時搭載 Android 11(API 級別 30),則device.mk應包含:

PRODUCT_SHIPPING_API_LEVEL := 30

您還可以設置以下系統屬性來強制使用新的捲元數據加密方法(以及新的默認 FBE 策略版本),無論發布的 API 級別如何:

PRODUCT_PROPERTY_OVERRIDES += \
    ro.crypto.volume.metadata.method=dm-default-key \
    ro.crypto.dm_default_key.options_format.version=2 \
    ro.crypto.volume.options=::v2

目前的方法

在搭載 Android 11 或更高版本的設備上,可採用存儲上的元數據加密使用dm-default-key內核模塊,就像內部存儲上一樣。請參閱上面的先決條件以了解要啟用的內核配置選項。請注意,在設備內部存儲上工作的內聯加密硬件可能在可採用存儲上不可用,因此可能需要CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y

默認情況下, dm-default-key卷元數據加密方法使用具有 4096 字節加密扇區的 AES-256-XTS 加密算法。可以通過設置ro.crypto.volume.metadata.encryption系統屬性來覆蓋該算法。此屬性的值與上述的metadata_encryption fstab 選項具有相同的語法。例如,在缺乏 AES 加速的設備上,可以通過設置ro.crypto.volume.metadata.encryption=adiantum來啟用Adiantum 加密

舊方法

在搭載 Android 10 或更低版本的設備上,可採用存儲上的元數據加密使用dm-crypt內核模塊而不是dm-default-key

CONFIG_DM_CRYPT=y

dm-default-key方法不同, dm-crypt方法會導致文件內容加密兩次:一次使用 FBE 密鑰,一次使用元數據加密密鑰。這種雙重加密會降低性能,並且不需要實現元數據加密的安全目標,因為 Android 確保 FBE 密鑰至少與元數據加密密鑰一樣難以洩露。供應商可以進行內核自定義以避免雙重加密,特別是通過實現allow_encrypt_override選項,當系統屬性ro.crypto.allow_encrypt_override設置為true時,Android 將傳遞給dm-crypt 。 Android 通用內核不支持這些自定義。

默認情況下, dm-crypt卷元數據加密方法使用具有 ESSIV 和 512 字節加密扇區的 AES-128-CBC 加密算法。這可以通過設置以下系統屬性(也用於 FDE)來覆蓋:

  • ro.crypto.fde_algorithm選擇元數據加密算法。選擇是aes-128-cbcadiantum 。僅當設備缺乏 AES 加速時才可以使用Adiantum
  • ro.crypto.fde_sector_size選擇加密扇區大小。選項包括 512、1024、2048 和 4096。對於 Adiantum 加密,請使用 4096。