From c819b29f46fe60aec668cf110246818f55163719 Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Fri, 2 Jun 2023 16:41:19 -0700 Subject: [PATCH] Always set a avb salt for hermetic build When building images via `m` , build_image.py is invoked directly without going through add_img_to_target_files. To ensure images built in either way are identical, move uuid/salt computation to build_image.py, so that the same uuid/salt will be used. Bug: 281960439 Test: m installclean && m && m target-files-dir , maks sure images in $OUT and $OUT/obj/PACKING/target_files_intermediates are identical Change-Id: Icdab29df84f5a0ec7c080f99f9fdbdc3c9b10b90 --- tools/releasetools/add_img_to_target_files.py | 29 ++++-------- tools/releasetools/build_image.py | 44 ++++++++++++++++++- tools/releasetools/common.py | 13 ------ tools/releasetools/verity_utils.py | 13 ++++-- 4 files changed, 61 insertions(+), 38 deletions(-) diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py index 8d660f88b9..465d222a83 100644 --- a/tools/releasetools/add_img_to_target_files.py +++ b/tools/releasetools/add_img_to_target_files.py @@ -68,6 +68,7 @@ import sparse_img from concurrent.futures import ThreadPoolExecutor from apex_utils import GetApexInfoFromTargetFiles from common import ZipDelete, PARTITIONS_WITH_CARE_MAP, ExternalError, RunAndCheckOutput, IsSparseImage, MakeTempFile, ZipWrite +from build_image import FIXED_FILE_TIMESTAMP if sys.hexversion < 0x02070000: print("Python 2.7 or newer is required.", file=sys.stderr) @@ -81,12 +82,6 @@ OPTIONS.rebuild_recovery = False OPTIONS.replace_updated_files_list = [] OPTIONS.is_signing = False -# Use a fixed timestamp (01/01/2009 00:00:00 UTC) for files when packaging -# images. (b/24377993, b/80600931) -FIXED_FILE_TIMESTAMP = int(( - datetime.datetime(2009, 1, 1, 0, 0, 0, 0, None) - - datetime.datetime.utcfromtimestamp(0)).total_seconds()) - def ParseAvbFooter(img_path) -> avbtool.AvbFooter: with open(img_path, 'rb') as fp: @@ -594,15 +589,6 @@ def CreateImage(input_dir, info_dict, what, output_file, block_list=None): if block_list: image_props["block_list"] = block_list.name - # Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and - # build fingerprint). Also use the legacy build id, because the vbmeta digest - # isn't available at this point. - build_info = common.BuildInfo(info_dict, use_legacy_id=True) - uuid_seed = what + "-" + build_info.GetPartitionFingerprint(what) - image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed)) - hash_seed = "hash_seed-" + uuid_seed - image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed)) - build_image.BuildImage( os.path.join(input_dir, what.upper()), image_props, output_file.name) @@ -1144,14 +1130,18 @@ def AddImagesToTargetFiles(filename): item for item in vbmeta_partitions if item not in vbmeta_vendor.split()] vbmeta_partitions.append("vbmeta_vendor") - custom_avb_partitions = OPTIONS.info_dict.get("avb_custom_vbmeta_images_partition_list", "").strip().split() + custom_avb_partitions = OPTIONS.info_dict.get( + "avb_custom_vbmeta_images_partition_list", "").strip().split() if custom_avb_partitions: for avb_part in custom_avb_partitions: partition_name = "vbmeta_" + avb_part - included_partitions = OPTIONS.info_dict.get("avb_vbmeta_{}".format(avb_part), "").strip().split() - assert included_partitions, "Custom vbmeta partition {0} missing avb_vbmeta_{0} prop".format(avb_part) + included_partitions = OPTIONS.info_dict.get( + "avb_vbmeta_{}".format(avb_part), "").strip().split() + assert included_partitions, "Custom vbmeta partition {0} missing avb_vbmeta_{0} prop".format( + avb_part) banner(partition_name) - logger.info("VBMeta partition {} needs {}".format(partition_name, included_partitions)) + logger.info("VBMeta partition {} needs {}".format( + partition_name, included_partitions)) partitions[partition_name] = AddVBMeta( output_zip, partitions, partition_name, included_partitions) vbmeta_partitions = [ @@ -1159,7 +1149,6 @@ def AddImagesToTargetFiles(filename): if item not in included_partitions] vbmeta_partitions.append(partition_name) - if OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true": banner("vbmeta") AddVBMeta(output_zip, partitions, "vbmeta", vbmeta_partitions) diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py index 90641360e6..11bd78428c 100755 --- a/tools/releasetools/build_image.py +++ b/tools/releasetools/build_image.py @@ -23,24 +23,34 @@ Usage: build_image input_directory properties_file output_image \\ """ from __future__ import print_function +import datetime import glob import logging import os import os.path import re +import shlex import shutil import sys +import uuid import common import verity_utils + logger = logging.getLogger(__name__) OPTIONS = common.OPTIONS BLOCK_SIZE = common.BLOCK_SIZE BYTES_IN_MB = 1024 * 1024 +# Use a fixed timestamp (01/01/2009 00:00:00 UTC) for files when packaging +# images. (b/24377993, b/80600931) +FIXED_FILE_TIMESTAMP = int(( + datetime.datetime(2009, 1, 1, 0, 0, 0, 0, None) - + datetime.datetime.utcfromtimestamp(0)).total_seconds()) + class BuildImageError(Exception): """An Exception raised during image building.""" @@ -487,6 +497,20 @@ def RunErofsFsck(out_file): raise +def SetUUIDIfNotExist(image_props): + + # Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and + # build fingerprint). Also use the legacy build id, because the vbmeta digest + # isn't available at this point. + what = image_props["mount_point"] + fingerprint = image_props.get("fingerprint", "") + uuid_seed = what + "-" + fingerprint + logger.info("Using fingerprint %s for partition %s", fingerprint, what) + image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed)) + hash_seed = "hash_seed-" + uuid_seed + image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed)) + + def BuildImage(in_dir, prop_dict, out_file, target_out=None): """Builds an image for the files under in_dir and writes it to out_file. @@ -504,6 +528,7 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): BuildImageError: On build image failures. """ in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict) + SetUUIDIfNotExist(prop_dict) build_command = [] fs_type = prop_dict.get("fs_type", "") @@ -635,6 +660,19 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): verity_image_builder.Build(out_file) +def TryParseFingerprint(glob_dict: dict): + for (key, val) in glob_dict.items(): + if not key.endswith("_add_hashtree_footer_args") and not key.endswith("_add_hash_footer_args"): + continue + for arg in shlex.split(val): + m = re.match(r"^com\.android\.build\.\w+\.fingerprint:", arg) + if m is None: + continue + fingerprint = arg[len(m.group()):] + glob_dict["fingerprint"] = fingerprint + return + + def ImagePropFromGlobalDict(glob_dict, mount_point): """Build an image property dictionary from the global dictionary. @@ -643,7 +681,9 @@ def ImagePropFromGlobalDict(glob_dict, mount_point): mount_point: such as "system", "data" etc. """ d = {} + TryParseFingerprint(glob_dict) + d["timestamp"] = FIXED_FILE_TIMESTAMP if "build.prop" in glob_dict: timestamp = glob_dict["build.prop"].GetProp("ro.build.date.utc") if timestamp: @@ -680,6 +720,7 @@ def ImagePropFromGlobalDict(glob_dict, mount_point): "avb_enable", "avb_avbtool", "use_dynamic_partition_size", + "fingerprint", ) for p in common_props: copy_prop(p, p) @@ -870,10 +911,9 @@ def BuildVBMeta(in_dir, glob_dict, output_path): if item not in vbmeta_vendor.split()] vbmeta_partitions.append("vbmeta_vendor") - partitions = {part: os.path.join(in_dir, part + ".img") for part in vbmeta_partitions} - partitions = {part:path for (part, path) in partitions.items() if os.path.exists(path)} + partitions = {part: path for (part, path) in partitions.items() if os.path.exists(path)} common.BuildVBMeta(output_path, partitions, name, vbmeta_partitions) diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py index 7adc9fac99..1293e90f93 100644 --- a/tools/releasetools/common.py +++ b/tools/releasetools/common.py @@ -928,20 +928,7 @@ def LoadInfoDict(input_file, repacking=False): input_file, partition, ramdisk_format=ramdisk_format) d["build.prop"] = d["system.build.prop"] - # Set up the salt (based on fingerprint) that will be used when adding AVB - # hash / hashtree footers. if d.get("avb_enable") == "true": - build_info = BuildInfo(d, use_legacy_id=True) - for partition in PARTITIONS_WITH_BUILD_PROP: - fingerprint = build_info.GetPartitionFingerprint(partition) - if fingerprint: - d["avb_{}_salt".format(partition)] = sha256( - fingerprint.encode()).hexdigest() - - # Set up the salt for partitions without build.prop - if build_info.fingerprint: - d["avb_salt"] = sha256(build_info.fingerprint.encode()).hexdigest() - # Set the vbmeta digest if exists try: d["vbmeta_digest"] = read_helper("META/vbmeta_digest.txt").rstrip() diff --git a/tools/releasetools/verity_utils.py b/tools/releasetools/verity_utils.py index dddb7f4448..7caeed48af 100644 --- a/tools/releasetools/verity_utils.py +++ b/tools/releasetools/verity_utils.py @@ -31,6 +31,7 @@ import sys import common import sparse_img from rangelib import RangeSet +from hashlib import sha256 logger = logging.getLogger(__name__) @@ -42,6 +43,7 @@ FIXED_SALT = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7" MAX_VBMETA_SIZE = 64 * 1024 MAX_FOOTER_SIZE = 4096 + class BuildVerityImageError(Exception): """An Exception raised during verity image building.""" @@ -64,6 +66,11 @@ def CreateVerityImageBuilder(prop_dict): # partition_size could be None at this point, if using dynamic partitions. if partition_size: partition_size = int(partition_size) + # Set up the salt (based on fingerprint) that will be used when adding AVB + # hash / hashtree footers. + salt = prop_dict.get("avb_salt") + if salt is None: + salt = sha256(prop_dict.get("fingerprint", "").encode()).hexdigest() # Verified Boot 2.0 if (prop_dict.get("avb_hash_enable") == "true" or @@ -81,7 +88,7 @@ def CreateVerityImageBuilder(prop_dict): prop_dict["avb_avbtool"], key_path, algorithm, - prop_dict.get("avb_salt"), + salt, prop_dict["avb_add_hash_footer_args"]) # Image uses hashtree footer. @@ -92,7 +99,7 @@ def CreateVerityImageBuilder(prop_dict): prop_dict["avb_avbtool"], key_path, algorithm, - prop_dict.get("avb_salt"), + salt, prop_dict["avb_add_hashtree_footer_args"]) return None @@ -279,7 +286,7 @@ class VerifiedBootVersion2VerityImageBuilder(VerityImageBuilder): def CreateCustomImageBuilder(info_dict, partition_name, partition_size, - key_path, algorithm, signing_args): + key_path, algorithm, signing_args): builder = None if info_dict.get("avb_enable") == "true": builder = VerifiedBootVersion2VerityImageBuilder(