验证启动是确保用户设备运行软件完整性的一整套流程。 它通常从设备固件的只读部分启动,使用加密方式验证代码是可靠且没有任何已知的安全缺陷之后才会执行。 AVB是验证启动的一种实现。
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]
。 具体如何完成此操作将在“更新存储的回滚索引”部分中进行讨论。
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结构体。 可以使用avbtool calculate_vbmeta_digest
在构建时计算此摘要,也可以在运行时使用avb_slot_verify_data_calculate_vbmeta_digest()
函数计算此摘要。在内核命令行中,它被设置为androidboot.vbmeta.digest
。有关详细信息,请参阅avb_slot_verify()
文档。
此摘要可以与加载的操作系统用户空间中的libavb
一起使用,以验证加载的vbmeta
结构体的真实性。如果信任根或存储的回滚索引仅在引导加载程序中运行时可用,则此选项非常有用。
此外,如果硬件级的可信数据中包含VBMeta摘要,则依赖方可以提取摘要并将其与已知完好操作系统的摘要列表进行比较,如果找到,则可以为运行应用程序的设备提供额外保证。
本节主要内容为AVB中的工具和库的相关信息。
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/
avb_rsa.[ch]
和avb_sha.[ch]
文件。平台预期提供的系统依赖关系在avb_sysdeps.h
中定义。如果平台提供标准C运行环境,则可以使用avb_sysdeps_posix.c
。libavb_atx/
libavb_user/
AvbOps
实现。用于boot_control.avb
和avbctl
。libavb_ab/
AVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED
才能使用它。该代码已经在2018年6月1日删除。boot_control/
boot_control
HAL的一个实现,用于使用实验性libavb_ab
A/B堆栈的引导加载程序。注意:此代码已弃用,已经在2018年6月1日删除。contrib/
contrib/linux/4.4
有Linux内核4.4的补丁,它们是由git format-patch
生成的。Android.bp
libavb
(用于设备上的静态库)、宿主端库(用于单元测试)和单元测试的构建说明。avbtool
test/
abvtool
, libavb
, libavb_ab
, 和libavb_atx
的测试单元。tools/avbctl/
examples/uefi/
libavb/
和libavb_ab/
的基于UEFI引导加载程序的源代码。examples/things/
README.md
docs/
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"
可以按如下方式生成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
10330112
在使用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
)或解锁(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
只需要较少的擦除。根据设备的外形和预期用途,删除任何数据前都应采取对应方式提示用户确认。
在本文档中,防篡改意味着可以检测HLOS
是否已经篡改了数据,例如可以检测出数据被重写。
已记录的回滚索引,用于验证的密钥,设备状态(LOCKED
或UNLOCKED
)以及已命名的持久值都应存放在防篡改存储中。如果检测到篡改,则相应的AvbOps
操作应该失败,例如 返回AVB_IO_RESULT_ERROR_IO
。验证密钥不能被篡改尤为重要,因为它们代表了信任根。
如果验签密钥可能发生改变,则它们必须只能由最终用户设置,例如,绝不能在最终用户之前在工厂或商店或任何中间点设置。此外,只有在设备处于UNLOCKED
状态时才能设置或清除密钥。
AVB1.1引入了对命名持久值的支持,这些值必须是防篡改的,并允许AVB存储任意键值对。集成商可以将对这些值的支持限制为一组固定的已知名称,最大值大小 和/或 最大值数量。
对分区使用持久性摘要意味着摘要(或在哈希树的情况下为根摘要)不存储在描述符中,而是存储在命名的持久值中。这允许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
手动提供随机盐。但是在写入持久摘要值之后,该盐就需要在设备的生命周期内保持不变。
为了使回滚保护工作,引导加载程序需要在将控制权转移到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的设备的建议引导流程如下:
注意事项:
LOCKED
状态下被拒绝的插槽可能在UNLOCKED
状态下不被拒绝(例如,当UNLOCKED
可以使用任何密钥并且允许回滚索引失败时),因此用于选择插槽的算法会根据设备处于何种状态而变化。rollback_index[n]
必须大于或等于设备上的stored_rollback_index[n]
中的值(对于所有的n),并且stored_rollback_index[n]
数组应该按照上一节中指定的方式进行更新。
UNLOCKED
,则不需要检查用于对OS进行签名的密钥,也不需要在设备上检查或更新回滚stored_rollback_index[n]
。因此,必须始终向用户显示关于未发生验证的警告。
根据设计,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上,引导加载程序必须在内核命令行上设置androidboot.verifiedbootstate
参数以标明引导状态。它应使用以下值:
LOCKED
且未使用可由用户设置的信任根。LOCKED
且使用了可由用户设置的信任根。UNLOCKED
。本节包含有关如何将AVB集成到特定设备的信息。这并不是一个详尽的清单。
在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.0支持未在更高版本中明确列出的所有功能。
原文地址:https://android.googlesource.com/platform/external/avb/+/master/