Android Verified Boot 2.0 最新安卓P AVB詳解

什麼是AVB

驗證啓動是確保用戶設備運行軟件完整性的一整套流程。 它通常從設備固件的只讀部分啓動,使用加密方式驗證代碼是可靠且沒有任何已知的安全缺陷之後纔會執行。 AVB是驗證啓動的一種實現。

VBMeta結構體

AVB中使用的核心數據結構是VBMeta結構體。此數據結構包含許多描述符(和其他元數據),並且所有這些數據都以加密方式簽名。 描述符用於映像哈希值,映像哈希樹元數據和所謂的鏈接分區。 下面是一個簡單的例子:

image

其中vbmeta分區在哈希描述符中保存引導分區的哈希值。對於systemvender分區,哈希樹緊隨在各自的分區文件系統數據之後,而vbmeta分區只保存哈希樹描述符中哈希樹的根哈希(root hash),鹽(salt)和偏移量(offset)。因爲vbmeta分區中的VBMeta結構體是加密簽名的,所以引導加載程序可以檢查簽名並驗證它是否由key0的所有者製作(例如通過已嵌入的key0公鑰),從而信任分區中儲存的bootsystemvendor分區的哈希值。

鏈接分區描述符用於委派權限,它包含委派權限的分區的名稱以及該特定分區上已經簽名的可信公鑰。 例如,請考慮下面的設置:

image

在這個設置中,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),如下所示:

image

這些數字使用rollback_index[n]表示,任意映像發現並修復安全漏洞後,這個數字都會增加。 此外,設備會將最後看到的回滾索引存儲在防篡改存儲中:

image

這些被稱爲stored_rollback_index[n]

在使用回滾保護的設備上,只有當所有n都滿足rollback_index[n] >= stored_rollback_index[n]這個條件時纔可以加載映像,並且設備會隨着時間增加stored_rollback_index[n]。 具體如何完成此操作將在“更新存儲的回滾索引”部分中進行討論。

A/B 分區支持

AVB被設計成可以與A/B分區同時啓用,它要求存儲在描述符中的任何分區名稱中都不使用A/B後綴。這是一個有兩個插槽的例子:

image

注意不同分區的回滾索引是不一樣的——對於槽A,回滾索引是[42,101],對於槽B,它們是[43,103]

在1.1或更高版本中,avbtool的add_hash_footeradd_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_avbmeta_b)而且被設計的儘可能的小(用於帶外更新out-of-band update)。vbmeta映像使用密鑰簽名,映像中包含用於驗證boot.imgsystem.img和其他分區映像的驗證數據(例如加密摘要)。

vbmeta映像還可以包含對存儲驗證數據的其他分區的引用,以及指定驗證數據使用的公鑰。這種間接方式可以將驗證權進行委託,它允許第三方通過在vbmeta.img中包含它們的公鑰來控制給定分區上的內容。這樣只需通過更新vbmeta.img中的分區描述符,就可以輕鬆改變或撤銷驗證權限而不用修改其它分區。

將簽名的驗證數據存儲在其他映像上(例如boot.imgsystem.img)也是使用avbtool完成的。

除了avbtool之外,還提供了一個庫——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.avbavbctl
  • libavb_ab/
    • 用於引導加載程序和AVB示例的實驗性A/B實現。注意:此代碼是DEPRECATED,您必須定義AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED才能使用它。該代碼已經在2018年6月1日刪除。
  • boot_control/
    • Android boot_controlHAL的一個實現,用於使用實驗性libavb_abA/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的插槽驗證的源代碼。
  • README.md
    • README。
  • 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_majorrequired_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_footeravbtool 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
10330112

在使用make_vbmeta_imageadd_hash_footeradd_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_imageadd_hash_footeradd_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通信,而不是與STDINSTDOUT通信。在簽名助手將診斷結果輸出到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.imgsystem.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.imgboot.imgsystem.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.imgvbmeta.img映像中包含boot.imgsystem.img的哈希描述符,以及爲system.img設置dm-verity的內核命令行參數。 如果構建系統設置爲構建vendor.img/ product.img/ odm.img/ product_services.img中的一個或多個,則每個構建系統的哈希樹也將分別附加各自的映像中,並且他們的哈希樹描述符將相應地包含在vbmeta.img中。

默認使用SHA256_RSA4096算法和external/avb/test/data目錄中的測試密鑰。算法和密鑰分別由BOARD_AVB_ALGORITHMBOARD_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_pkAVB_pkmd都傳遞給validate_vbmeta_public_key()操作。

可以將設備配置爲創建額外的vbmeta分區作爲鏈接分區,以便在不更改頂級vbmeta分區的情況下更新子分區。例如,下面的變量創建vbmeta_mainline.img,作爲一個鏈接vbmeta映像,它包含system.imgproduct_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.imgproduct_services.img的哈希樹描述符將僅包含在vbmeta_mainline.img中,但不包含在vbmeta.img中。通過上面的設置,分區system.imgproduct_services.imgvbmeta_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狀態。

在斷言用戶的物理存在之後,設備必須僅允許狀態轉換(例如從LOCKEDUNLOCKEDUNLOCKEDLOCKED)。如果設備具有顯示器和按鈕,則通常通過顯示對話框並要求用戶使用物理按鈕來完成確認或取消操作。

LOCKED轉換到UNLOCKED狀態時,必須清除包括userdata分區和任何NVRAM(譯者注: Non-Volatile Random Access Memory非易失性隨機訪問存儲器)在內的所有用戶數據。此外,stored_rollback_index[n]所在地址也將被擦除(所有元素必須設置爲零)。當從UNLOCKED轉換爲LOCKED時,也會發生類似的操作(擦除userdata,NVRAM空間和stored_rollback_index[n]位置)。如果要求設備使用全盤加密,則UNLOCKEDLOCKED只需要較少的擦除。根據設備的外形和預期用途,刪除任何數據前都應採取對應方式提示用戶確認。

防篡改存儲(Tamper-evident Storage)

在本文檔中,防篡改意味着可以檢測HLOS是否已經篡改了數據,例如可以檢測出數據被重寫。

已記錄的回滾索引,用於驗證的密鑰,設備狀態(LOCKEDUNLOCKED)以及已命名的持久值都應存放在防篡改存儲中。如果檢測到篡改,則相應的AvbOps操作應該失敗,例如 返回AVB_IO_RESULT_ERROR_IO。驗證密鑰不能被篡改尤爲重要,因爲它們代表了信任根。

如果驗籤密鑰可能發生改變,則它們必須只能由最終用戶設置,例如,絕不能在最終用戶之前在工廠或商店或任何中間點設置。此外,只有在設備處於UNLOCKED狀態時才能設置或清除密鑰。

命名持久值(Named Persistent Values)

AVB1.1引入了對命名持久值的支持,這些值必須是防篡改的,並允許AVB存儲任意鍵值對。集成商可以將對這些值的支持限制爲一組固定的已知名稱,最大值大小 和/或 最大值數量。

持久摘要(Persistent Digests)

對分區使用持久性摘要意味着摘要(或在哈希樹的情況下爲根摘要)不存儲在描述符中,而是存儲在命名的持久值中。這允許AVB驗證可能因設備而異的配置數據。當設備處於LOCKED狀態時,一定不能修改持久摘要,除非摘要不存時進行初始化。

要指定描述符應使用持久性摘要,請對add_hash_footeradd_hashtree_footer avbtool操作使用--use_persistent_digest選項。然後,在驗證描述符期間,AVB將在命名的持久值avb.persistent_digest.$(partition_name)中查找摘要,而不是在描述符本身中查找摘要。

對於使用持久摘要的哈希樹描述符,可以使用$(AVB_FOO_ROOT_DIGEST)形式的令牌將摘要值替換爲內核命令行描述符,其中“FOO”是大寫分區名,在本例中是名爲“FOO”的分區。令牌將被十六進制形式的摘要所替代。

默認情況下,當--use_persistent_digest選項與add_hash_footeradd_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的設備的建議引導流程如下:

image

注意事項:

  • 設備應搜索所有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表示根據狀態使用RESTARTEIO模式。該模式實現了一個狀態機,默認情況下使用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.veritymodeandroidboot.veritymode.managedandroidboot.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需要設置爲disabledandroidboot.veritymode.managedandroidboot.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支持未在更高版本中明確列出的所有功能。

原文地址:https://android.googlesource.com/platform/external/avb/+/master/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章