def add_hashtree_footer(self, image_filename, partition_size, partition_name,
generate_fec, fec_num_roots, hash_algorithm,
block_size, salt, chain_partitions, algorithm_name,
key_path,
public_key_metadata_path, rollback_index, flags,
rollback_index_location,
props, props_from_file, kernel_cmdlines,
setup_rootfs_from_kernel,
setup_as_rootfs_from_kernel,
include_descriptors_from_image,
calc_max_image_size, signing_helper,
signing_helper_with_files,
release_string, append_to_release_string,
output_vbmeta_image, do_not_append_vbmeta_image,
print_required_libavb_version,
use_persistent_root_digest, do_not_use_ab,
no_hashtree, check_at_most_once):
required_libavb_version_minor = 0
if use_persistent_root_digest or do_not_use_ab or check_at_most_once:
required_libavb_version_minor = 1
if rollback_index_location > 0:
required_libavb_version_minor = 2
# If we're asked to calculate minimum required libavb version, we're done.
if print_required_libavb_version:
print('1.{}'.format(required_libavb_version_minor))
return
digest_size = len(create_avb_hashtree_hasher(hash_algorithm, b'')
.digest())
digest_padding = round_to_pow2(digest_size) - digest_size
# If |partition_size| is given (e.g. not 0), calculate the maximum image
# size such that an image this size + the hashtree + metadata (footer +
# vbmeta struct) fits in |partition_size|. We use very conservative figures
# for metadata.
if partition_size > 0:
max_tree_size = 0
max_fec_size = 0
if not no_hashtree:
(_, max_tree_size) = calc_hash_level_offsets(
partition_size, block_size, digest_size + digest_padding)
if generate_fec:
max_fec_size = calc_fec_data_size(partition_size, fec_num_roots)
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
else:
max_image_size = 0
# If we're asked to only calculate the maximum image size, we're done.
if calc_max_image_size:
print('{}'.format(max_image_size))
return
image = ImageHandler(image_filename)
if partition_size > 0:
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))
elif image.image_size % image.block_size != 0:
raise AvbError('File size of {} is not a multiple of the image '
'block size {}.'.format(image.image_size,
image.block_size))
# If there's already a footer, truncate the image to its original
# size. This way 'avbtool add_hashtree_footer' is idempotent
# (modulo salts).
if image.image_size >= AvbFooter.SIZE:
image.seek(image.image_size - AvbFooter.SIZE)
try:
footer = AvbFooter(image.read(AvbFooter.SIZE))
# Existing footer found. Just truncate.
original_image_size = footer.original_image_size
image.truncate(footer.original_image_size)
except (LookupError, struct.error):
original_image_size = image.image_size
else:
# Image size is too small to possibly contain a footer.
original_image_size = image.image_size
# If anything goes wrong from here-on, restore the image back to
# its original size.
try:
# Ensure image is multiple of block_size.
rounded_image_size = round_to_multiple(image.image_size, block_size)
if rounded_image_size > image.image_size:
# If we need to round up the image size, it means the length of the
# data to append is not a multiple of block size.
# Setting multiple_block_size to false, so append_raw() will not
# require it.
image.append_raw(b'\0' * (rounded_image_size - image.image_size),
multiple_block_size=False)
# If image size exceeds the maximum image size, fail.
if partition_size > 0:
if image.image_size > max_image_size:
raise AvbError('Image size of {} exceeds maximum image '
'size of {} in order to fit in a partition '
'size of {}.'.format(image.image_size, max_image_size,
partition_size))
if salt:
salt = binascii.unhexlify(salt)
elif salt is None and not use_persistent_root_digest:
# If salt is not explicitly specified, choose a hash that's the same
# size as the hash size. Don't populate a random salt if this
# descriptor is being created to use a persistent digest on device.
hash_size = digest_size
with open('/dev/urandom', 'rb') as f:
salt = f.read(hash_size)
else:
salt = b''
# Hashes are stored upside down so we need to calculate hash
# offsets in advance.
(hash_level_offsets, tree_size) = calc_hash_level_offsets(
image.image_size, block_size, digest_size + digest_padding)
# If the image isn't sparse, its size might not be a multiple of
# the block size. This will screw up padding later so just grow it.
if image.image_size % image.block_size != 0:
assert not image.is_sparse
padding_needed = image.block_size - (image.image_size%image.block_size)
image.truncate(image.image_size + padding_needed)
# Generate the tree and add padding as needed.
tree_offset = image.image_size
root_digest, hash_tree = generate_hash_tree(image, image.image_size,
block_size,
hash_algorithm, salt,
digest_padding,
hash_level_offsets,
tree_size)
# Generate HashtreeDescriptor with details about the tree we
# just generated.
if no_hashtree:
tree_size = 0
hash_tree = b''
ht_desc = AvbHashtreeDescriptor()
ht_desc.dm_verity_version = 1
ht_desc.image_size = image.image_size
ht_desc.tree_offset = tree_offset
ht_desc.tree_size = tree_size
ht_desc.data_block_size = block_size
ht_desc.hash_block_size = block_size
ht_desc.hash_algorithm = hash_algorithm
ht_desc.partition_name = partition_name
ht_desc.salt = salt
if do_not_use_ab:
ht_desc.flags |= AvbHashtreeDescriptor.FLAGS_DO_NOT_USE_AB
if not use_persistent_root_digest:
ht_desc.root_digest = root_digest
if check_at_most_once:
ht_desc.flags |= AvbHashtreeDescriptor.FLAGS_CHECK_AT_MOST_ONCE
# Write the hash tree
padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
len(hash_tree))
hash_tree_with_padding = hash_tree + b'\0' * padding_needed
#ifdef OPLUS_FEATURE_SECURITY_COMMON
#Zhiwei.Qu@BSP.Security.Basic, 2023/02/03, skip appending tree chunk if the size is zero.
if len(hash_tree_with_padding) > 0:
image.append_raw(hash_tree_with_padding)
#endif /*OPLUS_FEATURE_SECURITY_COMMON*/
len_hashtree_and_fec = len(hash_tree_with_padding)
# Generate FEC codes, if requested.
if generate_fec:
if no_hashtree:
fec_data = b''
else:
fec_data = generate_fec_data(image_filename, fec_num_roots)
padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
len(fec_data))
fec_data_with_padding = fec_data + b'\0' * padding_needed
fec_offset = image.image_size
image.append_raw(fec_data_with_padding)
len_hashtree_and_fec += len(fec_data_with_padding)
# Update the hashtree descriptor.
ht_desc.fec_num_roots = fec_num_roots
ht_desc.fec_offset = fec_offset
ht_desc.fec_size = len(fec_data)
ht_desc_to_setup = None
if setup_as_rootfs_from_kernel:
ht_desc_to_setup = ht_desc
# Generate the VBMeta footer and add padding as needed.
vbmeta_offset = tree_offset + len_hashtree_and_fec
vbmeta_blob = self._generate_vbmeta_blob(
algorithm_name, key_path, public_key_metadata_path, [ht_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)
padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
len(vbmeta_blob))
vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
# Write vbmeta blob, if requested.
if output_vbmeta_image:
output_vbmeta_image.write(vbmeta_blob)
# Append vbmeta blob and footer, unless requested not to.
if not do_not_append_vbmeta_image:
image.append_raw(vbmeta_blob_with_padding)
# Now insert a DONT_CARE chunk with enough bytes such that the
# final Footer block is at the end of partition_size..
if partition_size > 0:
image.append_dont_care(partition_size - image.image_size -
1 * image.block_size)
# Generate the Footer that tells where the VBMeta footer
# is. Also put enough padding in the front of the footer since
# we'll write out an entire block.
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)
image.append_raw(footer_blob_with_padding)
except Exception as e:
# Truncate back to original size, then re-raise.
image.truncate(original_image_size)
raise AvbError('Adding hashtree_footer failed: {}.'.format(e)) from e详细解析avbtool中的add_hashtree_footer方法