Android Verified Boot VBMeta 简介

一、VBMeta结构

AVB中使用的核心数据结构是VBMeta结构。该数据结构包含许多描述符和元数据,并且所有这些数据都会经过密钥签名。描述符用于映像哈希(image hash)、映像哈希树元数据(image hashtree metadata)和所谓的链接分区(chain partitions)。下图是一个简单的VBMeta结构:

其中vbmeta分区在哈希描述符中保存boot分区的哈希。对于system分区和vendor分区,哈希树取决于文件系统数据,而vbmeta分区在哈希树描述符中保存哈希树的根哈希(root hash),盐(salt)和偏移量(offset)。由于vbmeta分区中的VBMeta结构是经过加密签名的,因此bootloader可以检查签名并验证其是否由key0的owner进行签名,从而确认boot,system和vendor是否可信。

链式分区描述符用于委派权限,它包含委派权限的分区的名称,以及可信特定分区上的签名的公共密钥。其设置如下图:

在该设置中,xyz分区具有用于完整性检查的哈希树。哈希树之后是一个VBMeta结构,其中包含带有哈希树元数据(根哈希,盐,偏移量等)的哈希树描述符,并且此结构用key1签名。最后,在分区的末尾是一个页脚(footer),该页脚有VBMeta结构的偏移量。

该设置允许bootloader使用链分区描述符名称在分区末尾找到页脚,这反过来又有助于找到VBMeta结构并验证其是否由key1签名,因为key1_pub存储在链分区描述符中。正是由于有一个带有偏移量的页脚,所以可以更新xyz分区,而无需更改vbmeta分区。

VBMeta结构足够灵活,允许任何分区的哈希描述符和哈希树描述符都驻留在vbmeta分区,通过链分区描述符对分区进行完整性检查。

链式分区不需要使用页脚,该页脚允许将链式分区指向VBMeta结构位于开头的分区。这对于将整个组织拥有的分区的所有哈希和哈希树描述符存储在专用分区(例如vbmeta_google)中的用例很有用。在此示例中,系统的哈希树描述符位于vbmeta_google分区中,这意味着bootloader完全不需要访问system分区,这对于将system分区作为逻辑分区进行管理很有用。

二、VBMeta摘要

VBMeta摘要(digest)是所有VBMeta结构的摘要,包括根结构和所有VBMeta结构在链式分区中。可以在构建时使用avbtoolcalculate_vbmeta_digest计算摘要,也可以在运行时使用avb_slot_verify_data_calculate_vbmeta_digest()函数计算摘要。可在kernel cmdline上设置androidboot.vbmeta.digest。

该摘要可与libavb一起在加载的操作系统内的用户空间中使用,以验证加载的vbmeta结构的真实性。如果信任根和存储的回滚索引仅在bootloader中运行时可用,这将很有用。如果VBMeta摘要包含在硬件支持的证明数据(attestation data)中,则依赖方可以提取摘要并将其与已知良好操作系统的摘要列表进行比较,如果找到该摘要,则可以为运行应用程序的设备提供额外的保证。

对于Pixel 3及更高版本设备的出厂映像,位于tools/transparency中的pixel_factory_image_verify.py是用于下载,验证和计算VBMeta摘要的工具。

1
2
3
4
5
6
7
8
$ pixel_factory_image_verify.py https://dl.google.com/dl/android/aosp/image.zip
Fetching file from: https://dl.google.com/dl/android/aosp/image.zip
Successfully downloaded file.
Successfully unpacked factory image.
Successfully unpacked factory image partitions.
Successfully verified VBmeta.
Successfully calculated VBMeta Digest.
The VBMeta Digest for factory image is: 1f329b20a2dd69425e7a29566ca870dad51d2c579311992d41c9ba9ba05e170e

如果给定的参数不是URL,则将其视为本地文件:

1
$ pixel_factory_image_verify.py image.zip

三、A/B系统支持

为确保AVB可以与A/B一起使用,存储在描述符中的任何分区名称中都不使用_a/_b后缀。下图是有两个 插槽(slot)的示例:

上图中slot_a的回滚索引为[42,101],slot_b的回滚索引为[43,103]。

在1.1版或更高版本中,avbtool支持do_not_use_ab用于add_hash_footer和add_hashtree_footer操作。这样就支持Non-A/B了。对应于AVB_HASH [TREE] _DESCRIPTOR_FLAGS_DO_NOT_USE_AB标志。

四、回滚保护

AVB支持回滚保护(Rollback protection),用于防止已知的安全漏洞。每个VBMeta结构都有一个回滚索引,如下图所示:

这些数字称为rollback_index [n],并随着发现和修复安全漏洞而针对每个映像增加1。此外,该设备还将最后一次看到的回滚索引存储在防篡改存储(tamper-evident storage)中,这些被称为stored_rollback_index [n]。回滚保护是使设备拒绝映像,除非所有n的rollback_index [n]> = stored_rollback_index [n],并且使设备随时间增加stored_rollback_index [n]。 在更新存储的回滚索引部分中讨论了确切的操作方法。

为了使回滚保护起作用,引导程序将需要在将控制权转移到HLOS之前更新设备上的stored_rollback_indexes [n]阵列。如果不使用A / B,这很简单-只需在引导前将其更新为该插槽的AVB元数据中的内容即可。用伪代码看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 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, &current_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版本不起作用时才支持回滚,应仅从A / B元数据中标记为SUCCESSFUL的插槽中更新stored_rollback_index [n]。伪代码如下所示,其中is_slot_is_marked_as_successful()来自使用中的A / B堆栈:

1
2
3
4
5
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(现已弃用)中实现的。这需要在每次引导时都验证所有可引导插槽,这可能会影响引导时间。