Android Verified Boot介紹與有關使用
背景
在搞安卓驅動調試的時候,由於不熟悉,導致系統沒有按照我預期啟動完畢;因此需要注意這一塊的東西。
簡介
Verified Boot 是 Android 4.4 開始引入的一個新特性,作用是在系統啟動時校驗 system 分區是否被篡改。好處在於可以檢測到 system “發生過” 改動,比如用戶使用 root 軟件強行植入 su 文件,但最后刪除了 su, 這種情況也能檢測出來。一旦檢驗不過,系統就不能正常啟動,並且有相關的圖文提示,請參看:https://source.android.com/security/verifiedboot/index.html
Android 6.0 的 CDD 文檔,章節 9.10. Verified Boot 對該特性有詳細的描述。可知,在出廠搭載 Android 6.0 系統的設備上,如果硬件達到要求,是必須激活這一特性的。請參看:https://static.googleusercontent.com/media/source.android.com/en//compatibility/android-cdd.pdf
啟用
在編譯系統中開啟
在編譯系統中開啟簽名 boot.img 和 recovery.img功能:
build/target/product/verity.mk文件中添加:
PRODUCT_SUPPORTS_VERITY := true
修改device/qcom/msm8996/msm8996.mk文件:
PRODUCT_SPPORTS_VERITY := true
在開啟 LK 驗證開啟
在編譯系統中開啟 Boot 和 Recovery 功能:
Bootable/bootloader/lk/AndroidBoot.mk
文件中:
ifeq($PRODUCT.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY), true)
VERIFIED_BOOT := VERIFIED_BOOT = 1
else
VERIFIED_BOOT := VERIFIED_BOOT = 0
endif
在device/OEM/project_name/project_name.mk
中添加:
PRODUCT_SUPPORTS_VERITY := truePRODUCT_SYSTEM_VERITY_PARTITION := /dev/block/bootdevice/by-name/system$(call inherit-product, build/target/product/verity.mk)PRODUCT_COPY_FILES += \frameworks/native/data/etc/android.software.verified_boot.xm:system/etc/permissions/android.software.verifie
在 device/OEM/project_name/fstab.qcom
中添加verify
標志:
/dev/block/bootdevice/by-name/system /system ext4 ro,barrier=1,discard wait,verify
簽名key
默認開發 key
包括公鑰和私鑰,它們位於:build/target/product/security/.
它們用來給 boot 和 recovery 簽名,以及驗證 system partition 的 metadata table.
它們的位置定義位於:build/target/product/verity.mk 文件中:
PRODUCT_VERITY_SIGNING_KEY := build/target/product/security/verity
Key 文件
作用說明:
build/target/product/security/verity.pk8
– privatekey used to sign boot.img and system.imgverity.x509.pem
– certificate include publickeyverity_key
– publickey used indm verity forsystem.img
有些老的版本只有兩個key文件:
verity_private_dev_key
– privatekey used to sign boot.img and system.imgverity_key
– publickey used indm verity forsystem image
我們可以看到在 build/core/Makefile
文件中:
$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES) $(BOOT_SIGNER)
$(call pretty,"Target boot image: $@")
$(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@
$(BOOT_SIGNER) /boot $@ $(PRODUCT_VERITY_SIGNING_KEY).pk8 $(PRODUCT_VERITY_SIGNING_KEY).x509.pem $@
$(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE))
生成 OEM 自己的公鑰密鑰對
在 Linux 系統中,確保所安裝的 openssl
版本足夠新,可以參看: /build/target/product/security_releasekey/README
文件。
openssl versionOpenSSL 1.0.2d 9 Jul 2015
development/tools/make_key mykey ‘/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com’
不用輸入密碼,然后 mykey.pk8 和 mykey.x509.pem 將會在當前目錄生成。
為DM-Verity 功能生成 verity key
生成 generate_verity_key
使用下面的命令來生成 verity key 的工具 generate_verity_key:
source build/envsetup.sh
choosecombo
make generate_verity_key (mmm system/extras/verity/)
將 *.x509.pem 轉換成 verity key
generate_verity_key 的代碼位於:system/extra/verity/generate_verity_key.c
generate_verity_key 的用法:generate_verity_key | -convert
out/host/linux-x86/bin/generate_verity_key -convert mykey.x509.pem verity_key
拷貝並重命名
拷貝mykey.pk8,mykey.x509.pem,verity_key.pub 至 build/target/product/security/
目錄,將其重命名: verity.pk8, verity.x509.pem,verity_key ,並替換默認的開發 key。
生成 keystore
在 LK 里面,有兩個 keystore:
- oem_keystore: 編譯到 LK 里面,它定義在 /bootloader/lk/platform/msm_shared/include/oem_keystore.h 中。
- user_keystore: 存儲在”keystore”分區里面。
LK 將使用 OEM keystore 來驗證 keystore 分區, 如果驗證通過, 將從里面讀取 user_keystore,然后用其驗證 boot.img 和 recovery.img。
Google 已經在后來的 release 里移除 user_keystore,不需要 keystore 分區。
使用system/extras/verity/keystore_signer, 它實際上是調用openssl 的一個腳本,所以也可以直接調用 openssl來實現。
keystore_signer <privatekey.pk8> <crefificate.x509.pem> <outfile> <publickey0.der>
# 相當於:
openssl rsa -in mykey.pk8 -inform DER -pubout -outform DER -out mypub.der
java -Xmx512M -jar out/host/linux-x86/framework/KeystoreSigner.jar mykey.pk8 mykey.x509.pem keystore.img mypub.der
如果在沒有 pk8 格式的老版本上,則可以使用下面的命令:
keystore_signer <PRIVATE_KEY> <KEYSTORE_IMG> <RSA_PUBLIC_KEY_DER>
java -Xmx512M -jar out/host/linux-x86/framework/KeystoreSigner.jar verity_private_dev_key keystore.img mypub.der
通過上述命令獲取 keystore.img之后,可以通過fast boot來刷。
當然,如果沒有keystore分區,這步可以忽略
Fastboot flash keystore keystore.img
Oem_keystore可以通過下面的方法使用相同的key:
通過下面的腳本將keystore.img生成oem_keystore.h文件
function generate_oem_keystore_h()
{
echo \#ifndef __OEM_KEYSTORE_H
echo \#define __OEM_KEYSTORE_H
xxd -i $1 | sed -e 's/unsigned char .* = {/const unsigned char OEM_KEYSTORE[] = {/g' -e 's/unsigned int .* =.*;//g'
echo \#endif
}
將該oem_keystore.h文件拷貝到:bootable/bootloader/lk/platform/msm_shared/include
在 LK 中的代碼調用流程
- boot_linux_from_mmc
- boot_verifier_init
- verify_signed_bootimg -> boot_verify_image -> verify_image_with_sig
手機安全狀態
開啟 Verified Boot 功能的設備有三種安全狀態:locked, verified 和 unlocked。任何狀態的轉換都需要使用 fastboot 命令,同時也會擦除 /data 分區(分區置0),當手機啟動的時候,新的狀態才會改變,同時會將一個新的文件系統掛載到 /data。除此之外,安全狀態的改變用戶通過物理按鍵(開機鍵,音量上下鍵等)的方式來確認,另外,如果是轉換到 unlocked 狀態,需要在開發者選項者將 OEM unlock 打開(這步需要屏幕鎖驗證),同時通過 fastboot oem unlock 打開。
locked state
- 用戶不能使用 fastboot 命令下載或者擦除任何分區。
- boot 和 recovery 分區將會被 keystore 驗證 (從 LK 里面的 OEM keystore開始)。
- 唯一能使用的 fastboot 命令只有 fastboot oem unlock,但如果要使用該命令,還必須在開發者選項中將 OEM unlock打開(默認關閉)。
verified state
- 用戶可以使用 fastboot 命令來下載或者擦除特定的分區(bootable/bootloader/lk/app/aboot/aboot.c中的 critical_flash_allowed_ptn 結構體)。
- 不允許登記 keystore或者篡改 persisten data block。
- 對於使用用戶或者第三方軟件來來,這是個比較合適的狀態,特別是開發者需要頻繁地燒寫軟件。
- 如果使用了沒有被OEM簽名的 keystore,那需要處理開機時的警告信息。
unlock state
- 所有的 fastboot 命令都可以使用。
- 用戶 keystore 可以被登記或者擦除。
- 在這種狀態下,boot和recovery不會被驗證。同時開機時會有警告,需要按音量上鍵才能繼續啟動。
影響
- 軟件集成和 OTA 升級,必須使用 block 方式做包和升級,相關工程師需要注意在適配。
- 售后,用戶自行刷三方固件以及 root 檢測等。
- 開機速度和 performance, Idol 4S 上實測,從校驗開始到 system 分區掛載完成耗時 50 ms 以內,對開機 速度的影響可以忽略,當前沒有任何證據表明會影響 performance。
- 工程師日常開發調試。
注意事項
- 該功能在 eng 軟件上是關閉的,刷 eng 的 boot.img 不會受影響。
- 混刷 boot.img 和 system.img 會啟動不了,使能后的 userdebug 或 user 版本的 boot.img 搭配的 eng 版本的 system.img 或者未使能前的 system.img 會開不起來,因為生成時用的 key 不同。
- 可以通過開機后查看 system 分區的掛載信息來查看是否開啟了 Verified Boot 功能:
adb shell mount | grep /system
開啟顯示
/dev/block/dm-0 /system ext4 ro,seclabel,relatime,discard,data=ordered 0 0
關閉顯示
/dev/block/bootdevice/by-name/system /system ext4 ro,seclabel,relatime,discard,data=ordered 0 0
通常會使用 userdebug 軟件(或者刷 userdebug 版本的 boot.img) 來調試,但使能后,system 是無法掛載成 rw 狀態的,不能 push
$ adb root
Restarting adbd as root
$ adb remount
Reremount succeeded
$ adb shell mount | grep /systtem
/dev/block/dm-0 /system ext4 ro,seclabel,relatime,discard,data=ordered 0 0
$ adb push out/target/product/project_name/system/xbin/su /system/xbin/su
failed to copy 'out/target/product/project_name/system/xbin/su' to '/system/xbin/su': Read-only file system
在 userdebug 軟件(或者刷 userdebug 版本的 boot.img)有兩種方法可以關閉:
1、用 adb 命令,重啟后生效:
$ adb disable-verity
$ adb enable-verity
注意,如果重新刷 了 system.img 就要重新操作,因為關閉標志是寫在 system.img 尾部的。當啟用boot verity功能,一旦你修改了手機里的 system 分區,比如 push 進去一個 apk,再打開的話,手機會起不來。
2、替換 eng.img ,也可以修改 fstab.qcom 文件,重新編譯 userdebug 的 boot.img:
具體操作是修改 device/OEM/project_name/fstab.qcom文件,去掉 verify 標志,例如:
/dev/block/bootdevice/by-name/system /system ext4 ro,barrier=1,discard wait,verify
/dev/block/bootdevice/by-name/system /system ext4 ro,barrier=1,discard wait
-
user 軟件沒有辦法,如果需要修改 system, 請刷 userdebug 或者 eng 的 boot.img。
-
舊版的 adb 可能不支持 disable-verity 和 enable-verity 命令,請更新到最新的 Android SDK,或者使用工程編譯出來的 adb (out/host/linux-x86/bin/adb)。