背景
avb签名涉及到安卓系统启动的安全部分,作为系统开发或者系统架构师,需要理解avb的签名逻辑。
本篇意图为承接上篇的流程安卓构建自定义镜像
介绍avb之前,先了解一下什么是数字签名。因为avb签名就是典型的数字签名
什么是数字签名
数字签名(Digital Signature)也叫作数字指纹(Digital Fingerprint),它是消息摘要算法和非对称加密算法的结合体,能够验证数据的完整性,并且认证数据的来源。
数字签名算法的模型分为两个主要阶段:
1、签名: 先计算数据的 [摘要],再使用私钥对 [摘要] 进行加密生成 [签名],将 [数据 + 签名] 一并发送给接收方;
2、验证: 先使用相同的摘要算法计算接收数据的 [摘要],再使用预先得到的公钥解密 [签名],对比 [解密的签名] 和 [计算的摘要] 是否一致。若一致,则说明数据没有被篡改。
Image的签名验证示意图
avb是什么
avb(android verified boot)是启动时验证,目前已经来到AVB2.0。
为什么使用avb
一句话概括,为了在安卓系统启动的时候验证各个分区的安全性和完整性
如何使用avb
avb除了集成在build system中对image进行签名外,还提供了好用的工具avbtool。在目录external/avb/avbtool
查看image签名信息:avbtool info_image --image xxxx.img
关于如何解读工具查到的签名信息,待补充哦。TODO
avb签名流程
签名流程开始于build_image.py中verity_image_builder.Build(out_file)。这里我们关注AVB2.0,verity_image_builder代表VerifiedBootVersion2VerityImageBuilder类
签名流程概述
- 调用avbtool签名
- 计算摘要digest的大小
- 计算max_image_size,作为后续动态调整image大小的阈值
- 检查partition_size是否是Image的block_size的倍数
- 调整image size,以确保image size是block_size倍数。
- 创建salt
- 创建root_digest、hash_tree
- 创建AvbHashtreeDescriptor
- 将hash_tree加入目标image
- 创建VBMeta_blob数据
- VBMeta _blob加入到目标image
- 创建AvbFooter,加入到目标image
签名流程详述
- 调用avbtool
调用avbtool的add_hash_footer()或add_hashtree_footer(),这里跟踪add_hashtree_footer()
# VerifiedBootVersion2VerityImageBuilder类
def Build(self, out_file):
add_footer = ("add_hash_footer" if self.footer_type == self.AVB_HASH_FOOTER else "add_hashtree_footer")
cmd = [self.avbtool, add_footer, "--partition_size", str(self.partition_size),"--partition_name",self.partition_name,"--image", out_file]
if self.key_path and self.algorithm:
cmd.extend(["--key", self.key_path, "--algorithm", self.algorithm])
if self.salt:
cmd.extend(["--salt", self.salt])
cmd.extend(shlex.split(self.signing_args))
proc = common.Run(cmd)
# 最终执行cmd命令(举例):['out/host/linux-x86/bin/avbtool', 'add_hashtree_footer', '--partition_size', 'xxx', '--partition_name', 'xxx', '--image', 'xxx.img', '--key', 'xxx.pem', '--algorithm', 'SHA256_RSA4096', '--salt','xxx]
- 计算摘要digest的大小
def add_hashtree_footer(self, ........):
digest_size = len(create_avb_hashtree_hasher(hash_algorithm, b'').digest())
- 计算max_image_size,作为后续动态调整image大小的阈值
总的来说,Image的最大阈值,不能超过partition大小减去metadata大小。metadata大小由fec大小、hashtree大小、vbmeta大小、footer大小组成。前面计算的digest_size用于计算hashtree大小了。
MAX_VBMETA_SIZE = 64 * 1024
MAX_FOOTER_SIZE = 4096
..............
if not no_hashtree:
(_, max_tree_size) = calc_hash_level_offsets(partition_size, block_size, digest_size + digest_padding)
..............
max_metadata_size = (max_fec_size + max_tree_size + self.MAX_VBMETA_SIZE + self.MAX_FOOTER_SIZE)
max_image_size = partition_size - max_metadata_size
- 检查partition_size是否是Image的block_size的倍数
if partition_size % image.block_size != 0:
raise AvbError('Partition size of {} is not a multiple of the image ''block size {}.'.format(partition_size,image.block_size))
- 调整image size,以确保image size是block_size倍数。
# Ensure image is multiple of block_size.
rounded_image_size = round_to_multiple(image.image_size, block_size)
- 创建salt
如果salt参数传了,将二进制转换成十六进制。否则使用/dev/urandom设备生成伪随机数,作为salt
if salt:
salt = binascii.unhexlify(salt)
elif salt is None and not use_persistent_root_digest:
hash_size = digest_size
with open('/dev/urandom', 'rb') as f:
salt = f.read(hash_size)
..................
- 创建root_digest、hash_tree
这里是重点,注意创建入参用到了image相关、hash算法、盐。说白了就是使用hash算法、盐等计算摘要、hash_tree。经典的数字签名流程之一。
root_digest, hash_tree = generate_hash_tree(image, image.image_size,block_size,hash_algorithm, salt,digest_padding,hash_level_offsets,tree_size)
- 创建AvbHashtreeDescriptor
描述符包含dm_verity_version、hash_algorithm、salt、root_digest等关键信息。这些信息avbtool info_image命令可以查看
ht_desc = AvbHashtreeDescriptor()
ht_desc.dm_verity_version = 1
...............
ht_desc.hash_algorithm = hash_algorithm
ht_desc.partition_name = partition_name
ht_desc.salt = salt
...............
if not use_persistent_root_digest:
ht_desc.root_digest = root_digest
- 将hash_tree加入目标image
hash_tree_with_padding = hash_tree + b'\0' * padding_needed
image.append_raw(hash_tree_with_padding)
- 创建VBMeta_blob数据
创建会用到算法名称、私钥、[AvbHashtreeDescriptor]数组等关键参数
vbmeta_blob = self._generate_vbmeta_blob(algorithm_name, key_path, public_key_metadata_path, [h_desc],
chain_partitions, rollback_index, flags, rollback_index_location,props, props_from_file,
kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,include_descriptors_from_image, signing_helper,signing_helper_with_files, release_string,append_to_release_string, required_libavb_version_minor)
vbmeta_blob由header_data_blob、auth_data_blob、aux_data_blob 3个部分组成
header_data_blob,如其名存放头信息,比如descriptors偏移量、大小。公钥的偏移量、大小等等。
auth_data_blob,存放摘要、签名
aux_data_blob ,存放描述符、公钥
def _generate_vbmeta_blob(...):
h = AvbVBMetaHeader()
.........
# 添加chained partition descriptors
if chain_partitions:
for cp in chain_partitions:
.............
desc = AvbChainPartitionDescriptor()
desc.partition_name = partition_name
.............
with open(file_path, 'rb') as f:
desc.public_key = f.read()
descriptors.append(desc)
# 将之前的入参[AvbHashtreeDescriptor]和ChainPartitionDescriptor加密存放起来。这信息后期可通过avbtool查看的
encoded_descriptors = bytearray()
for desc in descriptors:
encoded_descriptors.extend(desc.encode())
# 添加prop
if props:
for prop in props:
.................
desc = AvbPropertyDescriptor()
.....................
encoded_descriptors.extend(desc.encode())
..................
if alg.public_key_num_bytes > 0:
.................
# 重点,公钥是从私钥那里取得的
encoded_key = RSAPublicKey(key_path).encode()
.................
h.descriptors_size = len(encoded_descriptors)
h.public_key_offset = h.descriptors_size
h.public_key_size = len(encoded_key)
......................
header_data_blob = h.encode()
# aux_data_blob存放vbmeta相关的描述符、公钥
aux_data_blob = bytearray()
aux_data_blob.extend(encoded_descriptors)
aux_data_blob.extend(encoded_key)
aux_data_blob.extend(pkmd_blob)
.......................
# auth_data_blob存放认证信息,摘要、签名
binary_hash = b''
binary_signature = b''
if algorithm_name != 'NONE':
ha = hashlib.new(alg.hash_name)
................
binary_hash = ha.digest()
................
binary_signature = rsa_key.sign(algorithm_name, data_to_sign,signing_helper, signing_helper_with_files)
auth_data_blob = bytearray()
auth_data_blob.extend(binary_hash)
auth_data_blob.extend(binary_signature)
return header_data_blob + bytes(auth_data_blob) + bytes(aux_data_blob)
- VBMeta _blob加入到目标image
vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
# 添加vbmeta信息
if not do_not_append_vbmeta_image:
image.append_raw(vbmeta_blob_with_padding)
- 创建AvbFooter,加入到目标image
....................
footer = AvbFooter()
footer.original_image_size = original_image_size
footer.vbmeta_offset = vbmeta_offset
footer.vbmeta_size = len(vbmeta_blob)
footer_blob = footer.encode()
footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
# 添加footer
image.append_raw(footer_blob_with_padding)