原文地址:https://android.googlesource.com/platform/external/avb/+/master/
譯文地址:https://blog.csdn.net/shangyexin/article/details/86649504
背景
在燒錄系統鏡像到設備的時候,發現燒寫以后設備無限重啟。請教了同事以后,發現需要輸入下列命令:
fastboot flash --disable-verification vbmeta vbmeta.img
好奇就了解了一下,於是轉載了此文。
什么是AVB
驗證啟動是確保用戶設備運行軟件完整性的一整套流程。 它通常從設備固件的只讀部分啟動,使用加密方式驗證代碼是可靠且沒有任何已知的安全缺陷之后才會執行。 AVB是驗證啟動的一種實現。
VBMeta結構體
AVB中使用的核心數據結構是VBMeta
結構體。此數據結構包含許多描述符(和其他元數據),並且所有這些數據都以加密方式簽名。 描述符用於映像哈希值,映像哈希樹元數據和所謂的鏈接分區。 下面是一個簡單的例子:
其中vbmeta
分區在哈希描述符中保存引導分區的哈希值。對於system
和vender
分區,哈希樹緊隨在各自的分區文件系統數據之后,而vbmeta分區只保存哈希樹描述符中哈希樹的根哈希(root hash),鹽(salt)和偏移量(offset)。因為vbmeta
分區中的VBMeta
結構體是加密簽名的,所以引導加載程序可以檢查簽名並驗證它是否由key0
的所有者制作(例如通過已嵌入的key0公鑰),從而信任分區中儲存的boot
, system
,vendor
分區的哈希值。
鏈接分區描述符用於委派權限,它包含委派權限的分區的名稱以及該特定分區上已經簽名的可信公鑰。 例如,請考慮下面的設置:
在這個設置中,xyz
分區包含用於完整性校驗的哈希樹。在哈希樹之后是一個VBMeta
結構體,它包含帶有哈希樹元數據的哈希樹描述符(根哈希,鹽和偏移量),這個結構使用key1
簽名。最后,在分區的末尾是一個有VBMeta
結構體偏移量的頁腳。
這個設置允許引導加載程序使用鏈接分區描述符在分區末尾找到頁腳(使用鏈接分區描述符中的名稱),這有助於定位VBMeta
結構體並驗證它是否由key1
簽名(使用存儲在鏈接分區描述符中的key1_pub
)。至關重要的是,因為有一個帶有偏移量的頁腳,所以可以更新xyz
分區,而不需要vbmeta分區進行任何更改(譯者注:使用key1
公鑰驗證更新后的xyz
分區簽名即可)。
VBMeta
結構體非常靈活,任何分區的哈希描述符和哈希樹描述符都可以儲存在vbmeta
分區或者其它用於完整性校驗的分區(通過使用鏈接分區描述符)以及其它任意分區。這樣主要是考慮到大范圍的組織化可信關系。
在將鏈接分區指向VBMeta
結構體位於開頭的分區(就像vbmeta
分區一樣)時,該分區可以不使用頁腳。這對於整個組織擁有的分區的哈希和哈希樹描述符都存儲在專用分區(例如vbmeta_google
)中的用例非常有用。在此示例中,系統的哈希樹描述符位於vbmeta_google
分區中,這意味着引導加載程序根本不需要訪問system
分區,這對於將system
分區作為邏輯分區進行管理的實例是非常有用的(例如LVM或類似技術)。
回滾保護
AVB還包括回滾保護,用於防范已知的安全漏洞。每個VBMeta
結構體都有一個回滾索引(rollback index),如下所示:
這些數字使用rollback_index[n]
表示,任意映像發現並修復安全漏洞后,這個數字都會增加。 此外,設備會將最后看到的回滾索引存儲在防篡改存儲中:
這些被稱為stored_rollback_index[n]
。
在使用回滾保護的設備上,只有當所有n都滿足rollback_index[n] >= stored_rollback_index[n]
這個條件時才可以加載映像,並且設備會隨着時間增加stored_rollback_index[n]
。 具體如何完成此操作將在“更新存儲的回滾索引”部分中進行討論。
A/B 分區支持
AVB被設計成可以與A/B分區同時啟用,它要求存儲在描述符中的任何分區名稱中都不使用A/B后綴。這是一個有兩個插槽的例子:
注意不同分區的回滾索引是不一樣的——對於槽A,回滾索引是[42,101]
,對於槽B,它們是[43,103]
。
在1.1或更高版本中,avbtool的add_hash_footer
和add_hashtree_footer
操作增加了可選參數--do_not_use_ab
。這樣那些沒有A/B分區且也不應該有前綴的分區就可以使用AVB了。這對應於AVB_HASH[TREE]_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
標志。
VBMeta摘要
VBMeta摘要是所有VBMeta結構體的摘要,包括根結構體(例如在vbmeta
分區中)和鏈接分區中的所有VBMeta結構體。 可以使用avbtool calculate_vbmeta_digest
在構建時計算此摘要,也可以在運行時使用avb_slot_verify_data_calculate_vbmeta_digest()
函數計算此摘要。在內核命令行中,它被設置為androidboot.vbmeta.digest
。有關詳細信息,請參閱avb_slot_verify()
文檔。
此摘要可以與加載的操作系統用戶空間中的libavb
一起使用,以驗證加載的vbmeta
結構體的真實性。如果信任根或存儲的回滾索引僅在引導加載程序中運行時可用,則此選項非常有用。
此外,如果硬件級的可信數據中包含VBMeta摘要,則依賴方可以提取摘要並將其與已知完好操作系統的摘要列表進行比較,如果找到,則可以為運行應用程序的設備提供額外保證。
工具和庫
本節主要內容為AVB中的工具和庫的相關信息。
avbtool和libavb
avbtool
主要用來生成vbmeta.img
,它是驗證啟動的頂級對象。這個映像將被燒錄到vbmeta
分區(如果使用A/B分區,則為vbmeta_a
或vbmeta_b
)而且被設計的盡可能的小(用於帶外更新out-of-band update)。vbmeta
映像使用密鑰簽名,映像中包含用於驗證boot.img
,system.img
和其他分區映像的驗證數據(例如加密摘要)。
vbmeta
映像還可以包含對存儲驗證數據的其他分區的引用,以及指定驗證數據使用的公鑰。這種間接方式可以將驗證權進行委托,它允許第三方通過在vbmeta.img
中包含它們的公鑰來控制給定分區上的內容。這樣只需通過更新vbmeta.img中的分區描述符,就可以輕松改變或撤銷驗證權限而不用修改其它分區。
將簽名的驗證數據存儲在其他映像上(例如boot.img
和system.img
)也是使用avbtool
完成的。
除了avbtoo
l之外,還提供了一個庫——libavb
。該庫在設備端執行所有驗證,例如它首先加載vbmeta
分區,檢查簽名,然后繼續加載啟動分區以進行驗證。此庫旨在用於引導加載程序和Android內部。它有一個簡單的系統依賴抽象(參見avb_sysdeps.h
)以及引導加載程序或操作系統應該實現的操作(參見avb_ops.h
)。驗證的主要入口點是avb_slot_verify()
。
Adroid Things(譯者注:一套谷歌推出的面向物聯網平台的操作系統)對vbmeta
公鑰有特定的要求和驗證邏輯。libavb_atx
中提供了一個擴展,該擴展是libavb
公鑰驗證操作的實現。(請參閱avb_ops.h
中的avb_validate_vbmeta_public_key()
)。
文件和目錄
libavb/
: 映像驗證的實現。這段代碼可移植行強,它可以在盡可能多的上下文中使用,但編譯器需要支持C99標准。庫中的部分代碼是算法的內部實現,應避免在外部使用。例如avb_rsa.[ch]
和avb_sha.[ch]
文件。平台預期提供的系統依賴關系在avb_sysdeps.h
中定義。如果平台提供標准C運行環境,則可以使用avb_sysdeps_posix.c
。
libavb_atx/
: 用於驗證公鑰元數據的Android Things擴展。
libavb_user/
: 包含適用於Android用戶空間的AvbOps
實現。用於boot_control.avb
和avbctl
。
libavb_ab/
: 用於引導加載程序和AVB示例的實驗性A/B實現。注意:此代碼是DEPRECATED,您必須定義AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
才能使用它。該代碼已經在2018年6月1日刪除。
boot_control/
: Android boot_control
HAL的一個實現,用於使用實驗性libavb_ab
A/B堆棧的引導加載程序。注意:此代碼已棄用,已經在2018年6月1日刪除。
contrib/
: 包含其他項目中與AVB交互的補丁。例如,contrib/linux/4.4
有Linux內核4.4的補丁,它們是由git format-patch
生成的。
Android.bp
: 構建libavb
(用於設備上的靜態庫)、宿主端庫(用於單元測試)和單元測試的構建說明。
avbtool
: 用Python編寫的工具,用於處理與驗證啟動相關的映像。
test/
: abvtool
, libavb
, libavb_ab
, 和libavb_atx
的測試單元。
tools/avbctl/
: 包含可用於在Android中運行時控制AVB的工具的源代碼。
examples/uefi/
: 包含使用libavb/
和libavb_ab/
的基於UEFI引導加載程序的源代碼。
examples/things/
: 包含適用於Android Things的插槽驗證的源代碼。
docs/
: 說明文檔。
可移植性(Portability)
libavb
代碼在加載Android或其他操作系統的設備引導加載程序中使用。建議的方法是將上一節中提到的相應頭文件和C文件復制到引導加載程序中,並根據需要進行集成。
libavb/
代碼庫會隨着時間的推移不斷更新優化,集成應盡可能無創。目的是保持庫的API穩定,但必要時也會進行修改。至於可移植性,該庫在設計時就以高度可移植為目標,適用於小端和大端架構以及32位和64位。它還可以在沒有標准C庫和運行環境的非標准環境中工作。
如果設置了AVB_ENABLE_DEBUG
預處理器符號,則代碼將包含有用的調試信息和運行檢查。生產構建中不應該使用該符號。只應在編譯庫時設置預處理程序符號AVB_COMPILATION
。代碼必須編譯成一個單獨的庫。
使用已編譯的libavb
庫的應用程序只能包含libavb/libavb.h
文件(將包括所有公共接口),並且必須沒有avb_compile
預處理器符號集。這是為了確保將來可能更改的內部代碼(例如avb_sha.[ch]
和avb_rsa.[ch]
)對應用程序代碼不可見。
版本控制和兼容性
AVB使用具有三個字段的版本號——主要版本,次要版本和子版本。 這是一個示例版本號:
1.4.3
^ ^ ^
| | |
the major version ---+ | |
the minor version -----+ |
the sub version -------+
只有在兼容性被破壞時,主要版本號才會受到影響,例如結構體字段已被刪除或更改。只有在引入新功能時才會觸發次要版本號,例如添加了新算法或描述符。當修復錯誤或進行其他不影響兼容性的更改時,子版本號都會更改。
AvbVBMetaImageHeader
結構(在avb_vbmeta_image.h
中定義)包含驗證相關結構體所需的主副本版本號libavb
。它存儲在required_libavb_version_major
和required_libavb_version_minor
字段中。此外,此結構體包含一個文本字段,其中包含用於創建結構體的avbtool版本,例如“avbtool 1.4.3”或“avbtool 1.4.3 some_board Git-4589fbec”。
請注意,AvbVBMetaImageHeader
結構體可以包含如下信息:
required_libavb_version_major = 1
required_libavb_version_minor = 0
avbtool_release_string = "avbtool 1.4.3"
avbtool的使用
可以按如下方式生成vbmeta分區的內容:
$ avbtool make_vbmeta_image \
[--output OUTPUT] \
[--algorithm ALGORITHM] [--key /path/to/key_used_for_signing_or_pub_key] \
[--public_key_metadata /path/to/pkmd.bin] [--rollback_index NUMBER] \
[--include_descriptors_from_image /path/to/image.bin] \
[--setup_rootfs_from_kernel /path/to/image.bin] \
[--chain_partition part_name:rollback_index_location:/path/to/key1.bin] \
[--signing_helper /path/to/external/signer] \
[--signing_helper_with_files /path/to/external/signer_with_files] \
[--print_required_libavb_version] \
[--append_to_release_string STR]
包含整個分區哈希的完整性頁腳可以添加到現有映像,如下所示:
$ avbtool add_hash_footer \
--partition_name PARTNAME --partition_size SIZE \
[--image IMAGE] \
[--algorithm ALGORITHM] [--key /path/to/key_used_for_signing_or_pub_key] \
[--public_key_metadata /path/to/pkmd.bin] [--rollback_index NUMBER] \
[--hash_algorithm HASH_ALG] [--salt HEX] \
[--include_descriptors_from_image /path/to/image.bin] \
[--setup_rootfs_from_kernel /path/to/image.bin] \
[--output_vbmeta_image OUTPUT_IMAGE] [--do_not_append_vbmeta_image] \
[--signing_helper /path/to/external/signer] \
[--signing_helper_with_files /path/to/external/signer_with_files] \
[--print_required_libavb_version] \
[--append_to_release_string STR] \
[--calc_max_image_size] \
[--do_not_use_ab] \
[--use_persistent_digest]
可以將用於校驗分區完整性的頁腳添加到現有映像中,該頁腳中包含根摘要和分區哈希樹的鹽。如下所示,哈希樹也被添加到映像中。
$ avbtool add_hashtree_footer \
--partition_name PARTNAME --partition_size SIZE \
[--image IMAGE] \
[--algorithm ALGORITHM] [--key /path/to/key_used_for_signing_or_pub_key] \
[--public_key_metadata /path/to/pkmd.bin] [--rollback_index NUMBER] \
[--hash_algorithm HASH_ALG] [--salt HEX] [--block_size SIZE] \
[--include_descriptors_from_image /path/to/image.bin] \
[--setup_rootfs_from_kernel /path/to/image.bin] \
[--setup_as_rootfs_from_kernel] \
[--output_vbmeta_image OUTPUT_IMAGE] [--do_not_append_vbmeta_image] \
[--do_not_generate_fec] [--fec_num_roots FEC_NUM_ROOTS] \
[--signing_helper /path/to/external/signer] \
[--signing_helper_with_files /path/to/external/signer_with_files] \
[--print_required_libavb_version] \
[--append_to_release_string STR] \
[--calc_max_image_size] \
[--do_not_use_ab] \
[--use_persistent_digest]
可以使用resize_image
命令更改具有完整性頁腳的映像的大小:
$ avbtool resize_image \
--image IMAGE \
--partition_size SIZE
可以從映像中刪除映像上的完整性頁腳。作為可選項,可以保留哈希樹。
$ avbtool erase_footer --image IMAGE [--keep_hashtree]
對於哈希和哈希樹映像,vbmeta
結構體也可以通過--output_vbmeta_image
選項寫入外部文件,還可以指定不將vbmeta
結構體和頁腳添加到正在操作的映像中。
在使用avbtool add_hash_footer
或avbtool add_hashtree_footer
命令之后,要計算適合已經指定大小的分區映像的最大值,請使用--calc_max_image_size
選項:
$ avbtool add_hash_footer --partition_size $((10*1024*1024)) \
--calc_max_image_size
10416128
$ avbtool add_hashtree_footer --partition_size $((10*1024*1024)) \
--calc_max_image_size
在使用make_vbmeta_image
、add_hash_footer
和add_hashtree_footer
命令時,要計算需要放在vbmeta
結構中的libavb版本
,可以使用--print_required_libavb_version
選項:
$ avbtool make_vbmeta_image \
--algorithm SHA256_RSA2048 --key /path/to/key.pem \
--include_descriptors_from_image /path/to/boot.img \
--include_descriptors_from_image /path/to/system.img \
--print_required_libavb_version
1.0
可以在make_vbmeta_image
、add_hash_footer
和add_hashtree_footer
命令中使用--signing_helper
選項來指定用於簽名哈希的外部程序。要簽名的數據通過STDIN
輸入,簽名的數據通過STDOUT
返回。如果--signing_helper
出現在命令行中,--key
選項只需要包含公鑰。簽名助手的參數是算法和公鑰。如果簽名助手結束時返回的不是0,則意味着失敗。
這是一個示例調用:
/path/to/my_signing_program SHA256_RSA2048 /path/to/publickey.pem
--signing_helper_with_files
類似於--signing_helper
,只是使用一個臨時文件與helper通信,而不是與STDIN
和STDOUT
通信。在簽名助手將診斷結果輸出到STDOUT
而不是STDERR
的情況下,這非常有用。
下面是一個示例調用:
/path/to/my_signing_program_with_files SHA256_RSA2048 \
/path/to/publickey.pem /tmp/path/to/communication_file
其中最后一個位置參數是包含要簽名數據的文件。助手應該在這個文件中寫入簽名。
可以使用ppend_vbmeta_image
命令將整個vbmeta
二進制文件追加到另一個映像的末尾。這對於不使用任何vbmeta分區的情況非常有用,例如:
$ cp boot.img boot-with-vbmeta-appended.img
$ avbtool append_vbmeta_image \
--image boot-with-vbmeta-appended.img \
--partition_size SIZE_OF_BOOT_PARTITION \
--vbmeta_image vbmeta.img
$ fastboot flash boot boot-with-vbmeta-appended.img
verify_image
命令可用於同時驗證多個映像文件的內容。在對象映像調用時,執行以下檢查:
-
如果映像具有
VBMeta
結構體,則根據嵌入的公鑰檢查簽名。如果映像看起來不像vbmeta.img
,則查找頁腳,如果頁腳存在就使用頁腳。 -
如果傳遞了
--key
選項,則需要一個.pem
文件,並檢查VBMeta
結構體中嵌入的公鑰是否和給定的密鑰相匹配。 -
VBMeta結構體中的所有描述符都按以下方式檢查:
- 對於哈希描述符,加載對應分區名稱的映像文件,並且根據描述符中的摘要檢查映像摘要是否一致。
- 對於哈希樹描述符,加載分區名對應的映像文件,計算哈希樹,並將其根摘要與描述符中的映像文件進行比較。
-
對於鏈接分區描述符,將其內容與需要通過
--expected_chain_partition
選項傳入的內容進行比較。 此選項的格式類似於--chain_partition
選項的格式。如果鏈接分區描述符沒有--expected_chain_partition
描述符,則檢查失敗。
這是一個設置示例,其中boot.img
和system.img
的摘要存儲在使用my_key.pem
簽名的vbmeta.img
中。它還檢查分區foobar
的鏈接分區是否使用回滾索引8,以及AVB格式的公鑰是否與文件foobar_vendor_key.avbpubkey
的公鑰匹配:
$ avbtool verify_image \
--image /path/to/vbmeta.img \
--key my_key.pem \
--expect_chained_partition foobar:8:foobar_vendor_key.avbpubkey
Verifying image /path/to/vbmeta.img using key at my_key.pem
vbmeta: Successfully verified SHA256_RSA4096 vbmeta struct in /path_to/vbmeta.img
boot: Successfully verified sha256 hash of /path/to/boot.img for image of 10543104 bytes
system: Successfully verified sha1 hashtree of /path/to/system.img for image of 1065213952 bytes
foobar: Successfully verified chain partition descriptor matches expected data
在此示例中,verify_image
命令驗證目錄/path/to
中的文件vbmeta.img
,boot.img
和system.img
。給定映像的目錄和文件擴展名(例如/path/to/vbmeta.img
)與描述符中的分區名稱一起使用,以計算保存哈希和哈希樹映像的文件名。
verify_image
命令還可用於檢查自定義簽名助手是否按預期工作。
calculate_vbmeta_digest
命令可用於同時計算多個映像文件的vbmeta
摘要。 結果是十六進制字符串,將打印在STDOUT
或提供的路徑上(使用--output
選項)。
$ avbtool calculate_vbmeta_digest \
--hash_algorithm sha256 \
--image /path/to/vbmeta.img
a20fdd01a6638c55065fe08497186acde350d6797d59a55d70ffbcf41e95c2f5
在此示例中,calculate_vbmeta_digest
命令加載vbmeta.img
文件。如果此映像具有一個或多個鏈接分區描述符,則使用與verify_image
命令相同的邏輯來加載這些文件(例如,它假定與給定映像具有相同的目錄和文件擴展名)。一旦加載了所有vbmeta
結構體,就會計算摘要(使用--hash_algorithm
選項指定使用的哈希算法)並打印出來。
編譯集成
在Android中,AVB通過BOARD_AVB_ENABLE
變量開啟。
BOARD_AVB_ENABLE := true
設置該選項后,編譯安卓時就會為system.img
附加哈希樹並創建vbmeta.img
,vbmeta.img
映像中包含boot.img
和system.img
的哈希描述符,以及為system.img
設置dm-verity
的內核命令行參數。 如果構建系統設置為構建vendor.img
/ product.img
/ odm.img
/ product_services.img
中的一個或多個,則每個構建系統的哈希樹也將分別附加各自的映像中,並且他們的哈希樹描述符將相應地包含在vbmeta.img
中。
默認使用SHA256_RSA4096
算法和external/avb/test/data
目錄中的測試密鑰。算法和密鑰分別由BOARD_AVB_ALGORITHM
和BOARD_AVB_KEY_PATH
變量指定,比如下面的示例就指定使用4096-bit RSA key and SHA-512
算法:
BOARD_AVB_ALGORITHM := SHA512_RSA4096
BOARD_AVB_KEY_PATH := /path/to/rsa_key_4096bits.pem
需要注意的是,公鑰應該對引導加載程序可用,這樣才能用來校驗對應的分區。使用avbtool extract_public_key
以預期格式提取密鑰(以下為AVB_pk
)。如果設備使用的是與AVB_pk
不同的信任根,那么--public_key_metadata
選項可用於嵌入一個二進制文件(以下為AVB_pkmd
),該文件可用於派生AVB_pk。在驗證插槽(slot)時,AVB_pk
和AVB_pkmd
都傳遞給validate_vbmeta_public_key()
操作。
可以將設備配置為創建額外的vbmeta
分區作為鏈接分區,以便在不更改頂級vbmeta
分區的情況下更新子分區。例如,下面的變量創建vbmeta_mainline.img
,作為一個鏈接vbmeta
映像,它包含system.img
、product_services.img
的哈希樹描述符。vbmeta_mainline.img
本身將由指定的密鑰和算法簽名。
BOARD_AVB_VBMETA_MAINLINE := system product_services
BOARD_AVB_VBMETA_MAINLINE_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
BOARD_AVB_VBMETA_MAINLINE_ALGORITHM := SHA256_RSA2048
BOARD_AVB_VBMETA_MAINLINE_ROLLBACK_INDEX_LOCATION := 1
請注意,system.img
和product_services.img
的哈希樹描述符將僅包含在vbmeta_mainline.img
中,但不包含在vbmeta.img
中。通過上面的設置,分區system.img
,product_services.img
和vbmeta_mainline.img
可以獨立更新。
目前,構建系統支持構建vbmeta_mainline.img
(BOARD_AVB_VBMETA_MAINLINE
)和vbmeta_vendor.img
(BOARD_AVB_VBMETA_VENDOR
)這樣的鏈接vbmeta
映像。
為防止回滾攻擊,應定期增加回滾索引。可以使用BOARD_AVB_ROLLBACK_INDEX
變量設置回滾索引:
BOARD_AVB_ROLLBACK_INDEX := 5
如果未設置,則回滾索引默認為0。
變量BOARD_AVB_MAKE_VBMETA_IMAGE_ARGS
可用於指定傳遞給avbtool make_vbmeta_image
的其他選項。這里使用的典型選項包括--prop
, -- prop_from_file
,--chain_partition
,--public_key_metadata
和--signing_helper
。
使用avbtool add_hash_footer
制作boot.img
時,變量BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS
可用於指定其他選項。這里使用的典型選項包括--hash_algorithm
和--salt
。
使用avbtool add_hash_footer
制作system.img
時,變量BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS
可用於指定其他選項。這里使用的典型選項包括--hash_algorithm
,--salt
,--block_size
和--do_not_generate_fec
。
使用avbtool add_hash_footer
制作vendor.img
時,變量BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS
可用於指定其他選項。這里使用的典型選項包括括--hash_algorithm
,--salt
,--block_size
和--do_not_generate_fec
。
使用avbtool add_hash_footer
制作dtbo.img
時,變量BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS
可用於指定其他選項。這里使用的典型選項包括--hash_algorithm
和--salt
。
舊版Android中構建Verified Boot的系統變量(例如PRODUCT_SUPPORTS_VERITY_FEC
)未在AVB中使用。
點擊此處找到A/B相關的構建系統變量。
設備集成
本節討論將libavb
與設備引導加載程序集成的建議和最佳實踐。重要的是要強調這些只是建議,因此用詞必須謹慎。
此外,本章還使用術語HLOS來指代高級操作系統(High Level Operating System)。這顯然包括Android(包括但不限於手機形式),但也可能是其他操作系統。
系統依賴
libavb
庫的編寫方式使得它可以移植到任何使用C99編譯器的系統。它不需要標准的C庫,但是引導加載程序必須實現libavb
所需的一組簡單的系統原語,如avb_malloc()
、avb_free()
和avb_print()
。
除了系統原語之外,libavb
還通過AvbOps
結構體作為接口與引導加載程序交互。這包括從分區讀取和寫入數據,讀取和寫入回滾索引,檢查是否應接受用於簽名的公鑰,等等。
鎖定和解鎖模式(Locked and Unlocked mode)
無論安卓設備處於鎖定(LOCKED
)或解鎖(UNLOCKED
)狀態,都支持AVB。
在AVB的上下文中,LOCKED
狀態意味着驗證錯誤是致命的,而在UNLOCKED
狀態則不是。 如果設備是UNLOCKED
,則在avb_slot_verify()
的flags參數中傳遞AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
標志,包括下面錯誤在內的驗證錯誤將為非致命錯誤:
AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED
AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION
AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX
如果設備處於LOCKED狀態,請不要在avb_slot_verify()
的flags參數中傳遞AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
標志,並且僅將AVB_SLOT_VERIFY_RESULT_OK
視為非致命錯誤。
在Android上,可以通過fastboot接口來改變設備狀態。例如使用fastboot flashing lock
轉換到LOCKED
狀態,使用fastboot flashing unlock
轉換到UNLOCKED
狀態。
在斷言用戶的物理存在之后,設備必須僅允許狀態轉換(例如從LOCKED
到UNLOCKED
或UNLOCKED
到LOCKED
)。如果設備具有顯示器和按鈕,則通常通過顯示對話框並要求用戶使用物理按鈕來完成確認或取消操作。
從LOCKED
轉換到UNLOCKED
狀態時,必須清除包括userdata
分區和任何NVRAM(譯者注: Non-Volatile Random Access Memory
非易失性隨機訪問存儲器)在內的所有用戶數據。此外,stored_rollback_index[n]
所在地址也將被擦除(所有元素必須設置為零)。當從UNLOCKED
轉換為LOCKED
時,也會發生類似的操作(擦除userdata
,NVRAM空間和stored_rollback_index[n]
位置)。如果要求設備使用全盤加密,則UNLOCKED
到LOCKED
只需要較少的擦除。根據設備的外形和預期用途,刪除任何數據前都應采取對應方式提示用戶確認。
防篡改存儲(Tamper-evident Storage)
在本文檔中,防篡改意味着可以檢測HLOS
是否已經篡改了數據,例如可以檢測出數據被重寫。
已記錄的回滾索引,用於驗證的密鑰,設備狀態(LOCKED
或UNLOCKED
)以及已命名的持久值都應存放在防篡改存儲中。如果檢測到篡改,則相應的AvbOps
操作應該失敗,例如 返回AVB_IO_RESULT_ERROR_IO
。驗證密鑰不能被篡改尤為重要,因為它們代表了信任根。
如果驗簽密鑰可能發生改變,則它們必須只能由最終用戶設置,例如,絕不能在最終用戶之前在工廠或商店或任何中間點設置。此外,只有在設備處於UNLOCKED
狀態時才能設置或清除密鑰。
命名持久值(Named Persistent Values)
AVB1.1引入了對命名持久值的支持,這些值必須是防篡改的,並允許AVB存儲任意鍵值對。集成商可以將對這些值的支持限制為一組固定的已知名稱,最大值大小 和/或 最大值數量。
持久摘要(Persistent Digests)
對分區使用持久性摘要意味着摘要(或在哈希樹的情況下為根摘要)不存儲在描述符中,而是存儲在命名的持久值中。這允許AVB驗證可能因設備而異的配置數據。當設備處於LOCKED
狀態時,一定不能修改持久摘要,除非摘要不存時進行初始化。
要指定描述符應使用持久性摘要,請對add_hash_footer
或add_hashtree_footer avbtool
操作使用--use_persistent_digest
選項。然后,在驗證描述符期間,AVB將在命名的持久值avb.persistent_digest.$(partition_name)
中查找摘要,而不是在描述符本身中查找摘要。
對於使用持久摘要的哈希樹描述符,可以使用$(AVB_FOO_ROOT_DIGEST)
形式的令牌將摘要值替換為內核命令行描述符,其中“FOO”是大寫分區名,在本例中是名為“FOO”的分區。令牌將被十六進制形式的摘要所替代。
默認情況下,當--use_persistent_digest
選項與add_hash_footer
或add_hashtree_footer
一起使用時,avbtool將生成一個沒有鹽(salt)的描述符,而不是生成默認與摘要長度相等的隨機鹽。這是因為摘要值存儲在不隨時間改變的永久存儲器中。另一種選擇是使用--salt
手動提供隨機鹽。但是在寫入持久摘要值之后,該鹽就需要在設備的生命周期內保持不變。
更新已存儲回滾索引(Updating Stored Rollback Indexes)
為了使回滾保護工作,引導加載程序需要在將控制權轉移到HLOS之前更新設備上的stored_rollback_indexes[n]
數組。如果不使用A/B
,這很簡單——只需在引導之前將其更新到槽的AVB元數據中。在偽代碼中是這樣的:
// The |slot_data| parameter should be the AvbSlotVerifyData returned
// by avb_slot_verify() for the slot we're about to boot.
//
bool update_stored_rollback_indexes_for_slot(AvbOps* ops,
AvbSlotVerifyData* slot_data) {
for (int n = 0; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) {
uint64_t rollback_index = slot_data->rollback_indexes[n];
if (rollback_index > 0) {
AvbIOResult io_ret;
uint64_t current_stored_rollback_index;
io_ret = ops->read_rollback_index(ops, n, ¤t_stored_rollback_index);
if (io_ret != AVB_IO_RESULT_OK) {
return false;
}
if (rollback_index > current_stored_rollback_index) {
io_ret = ops->write_rollback_index(ops, n, rollback_index);
if (io_ret != AVB_IO_RESULT_OK) {
return false;
}
}
}
}
return true;
}
但是,如果使用A/B的話則必須更加小心,當更新不起作用時,仍然允許設備回退到舊插槽。
對於像Android這樣的HLOS,如果發現更新的OS版本不起作用,則僅支持回滾,stored_rollback_index[n]
應僅從A/B元數據中標記為SUCCESSFUL
的插槽更新。偽代碼如下,其中slot_is_marked_as_successful()
來自正在使用的A/B堆棧:
if (is_slot_is_marked_as_successful(slot->ab_suffix)) {
if (!update_stored_rollback_indexes_for_slot(ops, slot)) {
// TODO: handle error.
}
}
對於可以回滾到先前版本的HLOS,stored_rollback_index[n]
應設置為所有可以正常啟動的插槽允許的最大值。此方法在AVB的實驗性(現在已被棄用)A/B堆棧libavb_ab
中實現,請參閱avb_ab_flow()
實現。請注意,這需要在每次啟動時驗證所有可啟動插槽,這可能會影響啟動時間。
推薦引導流程
使用AVB的設備的建議引導流程如下:
注意事項:
-
設備應搜索所有A/B插槽,直到找到有效的操作系統進行引導。在
LOCKED
狀態下被拒絕的插槽可能在UNLOCKED
狀態下不被拒絕(例如,當UNLOCKED
可以使用任何密鑰並且允許回滾索引失敗時),因此用於選擇插槽的算法會根據設備處於何種狀態而變化。 -
如果找不到有效的操作系統(即沒有可引導的A/B插槽),設備無法引導,必須進入修復模式。這取決於設備。如果設備有一個屏幕,它必須將這個狀態傳遞給用戶。
-
如果設備被鎖定,則只接受由已經固化的驗證密鑰簽名的操作系統(見上一節)。此外,存儲在已驗證的映像中的
rollback_index[n]
必須大於或等於設備上的stored_rollback_index[n]
中的值(對於所有的n),並且stored_rollback_index[n]
數組應該按照上一節中指定的方式進行更新。- 如果用於驗證的密鑰是由最終用戶設置的,並且設備有一個屏幕,那么它必須顯示一個帶有密鑰指紋的警告,以表明設備正在啟動一個定制操作系統。在引導過程繼續之前,警告必須顯示至少10秒。如果設備沒有屏幕,則必須使用其他方式來傳遞設備正在引導自定義操作系統(燈條、LED等)。
-
如果設備是
UNLOCKED
,則不需要檢查用於對OS進行簽名的密鑰,也不需要在設備上檢查或更新回滾stored_rollback_index[n]
。因此,必須始終向用戶顯示關於未發生驗證的警告。- 它取決於設備的外形和預期用途,取決於設備是如何實現的。如果設備具有屏幕和按鈕(例如手機),則警告將在引導過程繼續之前顯示至少10秒。如果設備沒有屏幕,則必須使用其他方式來傳達設備已解鎖(燈條,LED等)。
處理dm-verity錯誤
根據設計,HLOS檢測哈希樹驗證錯誤,而不是引導加載程序。AVB提供了一種方法,用於指定如何通過avb_slot_verify()
函數中的hashtree_error_mode
參數處理錯誤。可能的值包括:
-
AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE
表示HLOS將當前插槽標記為無效並將重新啟動。在具有A/B的設備上,這將導致嘗試引導另一個插槽(如果它被標記為可引導),或者它可能導致進入無法引導OS的模式(例如,某種形式的修復模式)。在Linux中,這需要使用CONFIG_DM_VERITY_AVB
構建的內核。 -
AVB_HASHTREE_ERROR_MODE_RESTART
表示操作系統將在當前槽無效的情況下重新啟動。無條件地使用此模式時要小心,因為如果每次引導都遇到相同的哈希樹驗證錯誤,那么可能會進入死循環。 -
AVB_HASHTREE_ERROR_MODE_EIO
表示將向應用程序返回EIO錯誤。 -
AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO
表示根據狀態使用RESTART
或EIO
模式。該模式實現了一個狀態機,默認情況下使用RESTART
,當AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION
傳遞給avb_slot_verify()
時,模式轉換為EIO
。當檢測到新的操作系統時,設備將轉換回RESTART
模式。- 要實現這個持久存儲是必需的——具體來說,這意味着在
AvbOps
中傳遞的操作將需要實現read_persistent_value()
和write_persistent_value()
操作。使用的持久值名為avb.managed_verity_mode
並占用32字節的存儲空間。
- 要實現這個持久存儲是必需的——具體來說,這意味着在
-
AVB_HASHTREE_ERROR_MODE_LOGGING
表示將記錄錯誤,並且可能會將損壞的數據返回給應用程序。此模式應僅用於診斷和調試。除非允許校驗錯誤,否則不能使用它。
hashtree_error_mode
中傳遞的值基本上只是通過androidboot.veritymode
,androidboot.veritymode.managed
和androidboot.vbmeta.invalidate_on_error
內核命令行參數以下列方式傳遞給HLOS:
value | androidboot.veritymode | androidboot.veritymode.managed | androidboot.vbmeta.invalidate_on_error |
---|---|---|---|
AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE |
enforcing | (unset) | yes |
AVB_HASHTREE_ERROR_MODE_RESTART |
enforcing | (unset) | (unset) |
AVB_HASHTREE_ERROR_MODE_EIO |
eio | (unset) | (unset) |
AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO |
eio or enforcing | yes | (unset) |
AVB_HASHTREE_ERROR_MODE_LOGGING |
ignore_corruption | (unset) | (unset) |
此表的唯一例外是,如果在頂級vbmeta
中設置了AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
標志,則androidboot.veritymode
需要設置為disabled
,androidboot.veritymode.managed
和androidboot.vbmeta.invalidate_on_error
不需要設置。
應該為我的設備使用哪種模式?
這完全取決於設備,設備的使用方式以及所需的用戶體驗。
對於Android設備,應使用AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO
模式。 另請參閱source.android.com上的Boot Flow章節,了解引導加載程序應實現的UX和UI類型。
如果設備沒有屏幕或者HLOS同時支持多個可引導插槽,則使用AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE
可能更有意義。
Android特定集成
在Android上,引導加載程序必須在內核命令行上設置androidboot.verifiedbootstate
參數以標明引導狀態。它應使用以下值:
- green: 如果設備已
LOCKED
且未使用可由用戶設置的信任根。 - yellow: 如果設備已
LOCKED
且使用了可由用戶設置的信任根。 - orange: 如果設備已
UNLOCKED
。
特定設備注意事項
本節包含有關如何將AVB集成到特定設備的信息。這並不是一個詳盡的清單。
Pixel 2
在Pixel 2和Pixel 2 XL上,引導加載程序支持名為avb_custom_key
的虛擬分區。僅在UNLOCKED
狀態下可以擦除並重寫該分區。設置自定義密鑰的方式如下:
avbtool extract_public_key --key key.pem --output pkmd.bin
fastboot flash avb_custom_key pkmd.bin
擦除密鑰是通過擦除虛擬分區來完成的:
fastboot erase avb_custom_key
設置自定義密鑰並且設備處於LOCKED
狀態時,它將啟動使用內置密鑰和自定義密鑰簽名的映像。所有其他安全功能(包括回滾保護)都有效,唯一的區別是使用的信任根不同。
啟動使用自定義密鑰簽名的映像時,引導過程中屏幕將顯示為黃色以提醒用戶正在使用自定義密鑰。
歷史版本
1.1版本
1.1版增加了對以下內容的支持:
- 將32位標志元素添加到哈希和哈希樹描述符中。
- 支持不使用A/B的分區。
- 防篡改的持久值。
- 哈希或哈希樹描述符的持久摘要。
1.0版本
1.0支持未在更高版本中明確列出的所有功能。