AVB: support chain partition signing
Current build system will include AVB metadata from each partition and
store them into /vbmeta partiton when BOARD_AVB_ENABLE is set, which makes
each partition tightly-coupled.
Add the support for 'chain partition':
- The vbmeta of each partition is stored on the same partition itself.
- The public key used to verify each partition is stored in /vbmeta.
For example, the following build variables are required to enable chain
partition for system partition:
- BOARD_AVB_SYSTEM_KEY_PATH := path/to/system_private_key
- BOARD_AVB_SYSTEM_ALGORITHM := SHA512_RSA8192
- BOARD_AVB_SYSTEM_ROLLBACK_INDEX := 1
- BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION := 2
The corresponding settings will be added into META/misc_info.txt for
build_image.py and/or add_img_to_target_files.py:
- avb_system_key_path=path/to/system_private_key
- avb_system_algorithm=SHA512_RSA8192
- avb_system_add_hashtree_footer_args=--rollback_index 1
- avb_system_rollback_index_location=2
To enable chain partition for other partitions, just replace SYSTEM with
BOOT, VENDOR and/or DTBO in the build variables.
Also switch from `avbtool make_vbmeta_image --setup_rootfs_from_kernel system.img ...`
to `avbtool add_hashtree_footer --image system.img --setup_as_rootfs_from_kernel...`
when BOARD_BUILD_SYSTEM_ROOT_IMAGE is true. This works for both chained
and no-chained:
- chained: `avbtool add_hashtree_footer --setup_as_rootfs_from_kernel` will
add dm-verity kernel cmdline descriptor to system.img
- no-chained: `avbtool make_vbmeta_image --include_descriptors_from_image
system.img` will include the kernel cmdline descriptor from system.img into
vbmeta.img
Bug: 38399657
Test: `make` pass, flash images from $OUT and boot device without chain partitions
Test: `make` pass, flash images from $OUT and boot device with chain partitions
Test: `make dist` pass, flash images from TF.zip and boot device without chain partitions
Test: `make dist` pass, flash images from TF.zip and boot device with chain partitions
Test: follow the same steps in
https://android-review.googlesource.com/#/c/407572/
Change-Id: I344f79290743d7d47b5e7441b3a21df812a69099
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index 1d8090a..aab082b 100755
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -192,14 +192,14 @@
shutil.copy(dtbo_prebuilt_path, img.name)
# AVB-sign the image as needed.
- if OPTIONS.info_dict.get("board_avb_enable") == "true":
+ if OPTIONS.info_dict.get("avb_enable") == "true":
avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"]
part_size = OPTIONS.info_dict["dtbo_size"]
# The AVB hash footer will be replaced if already present.
cmd = [avbtool, "add_hash_footer", "--image", img.name,
"--partition_size", str(part_size), "--partition_name", "dtbo"]
- cmd.extend(shlex.split(OPTIONS.info_dict["avb_signing_args"]))
- args = OPTIONS.info_dict.get("board_avb_dtbo_add_hash_footer_args")
+ common.AppendAVBSigningArgs(cmd, "dtbo")
+ args = OPTIONS.info_dict.get("avb_dtbo_add_hash_footer_args")
if args and args.strip():
cmd.extend(shlex.split(args))
p = common.Run(cmd, stdout=subprocess.PIPE)
@@ -271,7 +271,7 @@
# by the avb tool.
is_verity_partition = "verity_block_device" in image_props
verity_supported = (image_props.get("verity") == "true" or
- image_props.get("board_avb_enable") == "true")
+ image_props.get("avb_enable") == "true")
is_avb_enable = image_props.get("avb_hashtree_enable") == "true"
if verity_supported and (is_verity_partition or is_avb_enable):
adjusted_blocks_value = image_props.get("partition_size")
@@ -334,25 +334,51 @@
img.Write()
+def AppendVBMetaArgsForPartition(cmd, partition, img_path, public_key_dir):
+ if not img_path:
+ return
+
+ # Check if chain partition is used.
+ key_path = OPTIONS.info_dict.get("avb_" + partition + "_key_path")
+ if key_path:
+ # extract public key in AVB format to be included in vbmeta.img
+ avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"]
+ public_key_path = os.path.join(public_key_dir, "%s.avbpubkey" % partition)
+ p = common.Run([avbtool, "extract_public_key", "--key", key_path,
+ "--output", public_key_path],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ p.communicate()
+ assert p.returncode == 0, \
+ "avbtool extract_public_key fail for partition: %r" % partition
+
+ rollback_index_location = OPTIONS.info_dict[
+ "avb_" + partition + "_rollback_index_location"]
+ cmd.extend(["--chain_partition", "%s:%s:%s" % (
+ partition, rollback_index_location, public_key_path)])
+ else:
+ cmd.extend(["--include_descriptors_from_image", img_path])
+
+
def AddVBMeta(output_zip, boot_img_path, system_img_path, vendor_img_path,
dtbo_img_path, prefix="IMAGES/"):
"""Create a VBMeta image and store it in output_zip."""
img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vbmeta.img")
avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"]
- cmd = [avbtool, "make_vbmeta_image",
- "--output", img.name,
- "--include_descriptors_from_image", boot_img_path,
- "--include_descriptors_from_image", system_img_path]
- if vendor_img_path is not None:
- cmd.extend(["--include_descriptors_from_image", vendor_img_path])
- if dtbo_img_path is not None:
- cmd.extend(["--include_descriptors_from_image", dtbo_img_path])
- if OPTIONS.info_dict.get("system_root_image") == "true":
- cmd.extend(["--setup_rootfs_from_kernel", system_img_path])
- cmd.extend(shlex.split(OPTIONS.info_dict["avb_signing_args"]))
- args = OPTIONS.info_dict.get("board_avb_make_vbmeta_image_args")
+ cmd = [avbtool, "make_vbmeta_image", "--output", img.name]
+ common.AppendAVBSigningArgs(cmd, "vbmeta")
+
+ public_key_dir = tempfile.mkdtemp(prefix="avbpubkey-")
+ OPTIONS.tempfiles.append(public_key_dir)
+
+ AppendVBMetaArgsForPartition(cmd, "boot", boot_img_path, public_key_dir)
+ AppendVBMetaArgsForPartition(cmd, "system", system_img_path, public_key_dir)
+ AppendVBMetaArgsForPartition(cmd, "vendor", vendor_img_path, public_key_dir)
+ AppendVBMetaArgsForPartition(cmd, "dtbo", dtbo_img_path, public_key_dir)
+
+ args = OPTIONS.info_dict.get("avb_vbmeta_args")
if args and args.strip():
cmd.extend(shlex.split(args))
+
p = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.communicate()
assert p.returncode == 0, "avbtool make_vbmeta_image failed"
@@ -553,7 +579,7 @@
banner("dtbo")
dtbo_img_path = AddDtbo(output_zip)
- if OPTIONS.info_dict.get("board_avb_enable") == "true":
+ if OPTIONS.info_dict.get("avb_enable") == "true":
banner("vbmeta")
boot_contents = boot_image.WriteToTemp()
AddVBMeta(output_zip, boot_contents.name, system_img_path,
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index de75a6b..6de9763 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -109,18 +109,19 @@
Returns:
The maximum image size or 0 if an error occurred.
"""
- cmdline = "%s add_%s_footer " % (avbtool, footer_type)
- cmdline += "--partition_size %d " % partition_size
- cmdline += "--calc_max_image_size "
- cmdline += additional_args
- (output, exit_code) = RunCommand(shlex.split(cmdline))
+ cmd =[avbtool, "add_%s_footer" % footer_type,
+ "--partition_size", partition_size, "--calc_max_image_size"]
+ cmd.extend(shlex.split(additional_args))
+
+ (output, exit_code) = RunCommand(cmd)
if exit_code != 0:
return 0
else:
return int(output)
def AVBAddFooter(image_path, avbtool, footer_type, partition_size,
- partition_name, signing_args, additional_args):
+ partition_name, key_path, algorithm,
+ additional_args):
"""Adds dm-verity hashtree and AVB metadata to an image.
Args:
@@ -129,19 +130,24 @@
footer_type: 'hash' or 'hashtree' for generating footer.
partition_size: The size of the partition in question.
partition_name: The name of the partition - will be embedded in metadata.
- signing_args: Arguments for signing the image.
+ key_path: Path to key to use or None.
+ algorithm: Name of algorithm to use or None.
additional_args: Additional arguments to pass to 'avbtool
add_hashtree_image'.
Returns:
True if the operation succeeded.
"""
- cmdline = "%s add_%s_footer " % (avbtool, footer_type)
- cmdline += "--partition_size %d " % partition_size
- cmdline += "--partition_name %s " % partition_name
- cmdline += "--image %s " % image_path
- cmdline += signing_args + " "
- cmdline += additional_args
- (_, exit_code) = RunCommand(shlex.split(cmdline))
+ cmd =[avbtool, "add_%s_footer" % footer_type,
+ "--partition_size", partition_size,
+ "--partition_name", partition_name,
+ "--image", image_path]
+
+ if key_path and algorithm:
+ cmd.extend(["--key", key_path, "--algorithm", algorithm])
+
+ cmd.extend(shlex.split(additional_args))
+
+ (_, exit_code) = RunCommand(cmd)
return exit_code == 0
def AdjustPartitionSizeForVerity(partition_size, fec_supported):
@@ -420,8 +426,8 @@
avb_footer_type = 'hashtree'
if avb_footer_type:
- avbtool = prop_dict.get("avb_avbtool")
- partition_size = int(prop_dict.get("partition_size"))
+ avbtool = prop_dict["avb_avbtool"]
+ partition_size = prop_dict["partition_size"]
# avb_add_hash_footer_args or avb_add_hashtree_footer_args.
additional_args = prop_dict["avb_add_" + avb_footer_type + "_footer_args"]
max_image_size = AVBCalcMaxImageSize(avbtool, avb_footer_type, partition_size,
@@ -429,7 +435,7 @@
if max_image_size == 0:
return False
prop_dict["partition_size"] = str(max_image_size)
- prop_dict["original_partition_size"] = str(partition_size)
+ prop_dict["original_partition_size"] = partition_size
if fs_type.startswith("ext"):
build_command = [prop_dict["ext_mkuserimg"]]
@@ -572,14 +578,16 @@
# Add AVB HASH or HASHTREE footer (metadata).
if avb_footer_type:
- avbtool = prop_dict.get("avb_avbtool")
- original_partition_size = int(prop_dict.get("original_partition_size"))
+ avbtool = prop_dict["avb_avbtool"]
+ original_partition_size = prop_dict["original_partition_size"]
partition_name = prop_dict["partition_name"]
- signing_args = prop_dict["avb_signing_args"]
+ # key_path and algorithm are only available when chain partition is used.
+ key_path = prop_dict.get("avb_key_path")
+ algorithm = prop_dict.get("avb_algorithm")
# avb_add_hash_footer_args or avb_add_hashtree_footer_args
additional_args = prop_dict["avb_add_" + avb_footer_type + "_footer_args"]
if not AVBAddFooter(out_file, avbtool, avb_footer_type, original_partition_size,
- partition_name, signing_args, additional_args):
+ partition_name, key_path, algorithm, additional_args):
return False
if run_fsck and prop_dict.get("skip_fsck") != "true":
@@ -624,8 +632,7 @@
"verity_key",
"verity_signer_cmd",
"verity_fec",
- "board_avb_enable",
- "avb_signing_args",
+ "avb_enable",
"avb_avbtool"
)
for p in common_props:
@@ -633,6 +640,11 @@
d["mount_point"] = mount_point
if mount_point == "system":
+ copy_prop("avb_system_hashtree_enable", "avb_hashtree_enable")
+ copy_prop("avb_system_add_hashtree_footer_args",
+ "avb_add_hashtree_footer_args")
+ copy_prop("avb_system_key_path", "avb_key_path")
+ copy_prop("avb_system_algorithm", "avb_algorithm")
copy_prop("fs_type", "fs_type")
# Copy the generic system fs type first, override with specific one if
# available.
@@ -650,13 +662,15 @@
copy_prop("system_squashfs_block_size", "squashfs_block_size")
copy_prop("system_squashfs_disable_4k_align", "squashfs_disable_4k_align")
copy_prop("system_base_fs_file", "base_fs_file")
- copy_prop("system_avb_hashtree_enable", "avb_hashtree_enable")
- copy_prop("system_avb_add_hashtree_footer_args",
- "avb_add_hashtree_footer_args")
copy_prop("system_extfs_inode_count", "extfs_inode_count")
elif mount_point == "system_other":
# We inherit the selinux policies of /system since we contain some of its files.
d["mount_point"] = "system"
+ copy_prop("avb_system_hashtree_enable", "avb_hashtree_enable")
+ copy_prop("avb_system_add_hashtree_footer_args",
+ "avb_add_hashtree_footer_args")
+ copy_prop("avb_system_key_path", "avb_key_path")
+ copy_prop("avb_system_algorithm", "avb_algorithm")
copy_prop("fs_type", "fs_type")
copy_prop("system_fs_type", "fs_type")
copy_prop("system_size", "partition_size")
@@ -667,9 +681,6 @@
copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt")
copy_prop("system_squashfs_block_size", "squashfs_block_size")
copy_prop("system_base_fs_file", "base_fs_file")
- copy_prop("system_avb_hashtree_enable", "avb_hashtree_enable")
- copy_prop("system_avb_add_hashtree_footer_args",
- "avb_add_hashtree_footer_args")
copy_prop("system_extfs_inode_count", "extfs_inode_count")
elif mount_point == "data":
# Copy the generic fs type first, override with specific one if available.
@@ -682,6 +693,11 @@
copy_prop("cache_fs_type", "fs_type")
copy_prop("cache_size", "partition_size")
elif mount_point == "vendor":
+ copy_prop("avb_vendor_hashtree_enable", "avb_hashtree_enable")
+ copy_prop("avb_vendor_add_hashtree_footer_args",
+ "avb_add_hashtree_footer_args")
+ copy_prop("avb_vendor_key_path", "avb_key_path")
+ copy_prop("avb_vendor_algorithm", "avb_algorithm")
copy_prop("vendor_fs_type", "fs_type")
copy_prop("vendor_size", "partition_size")
copy_prop("vendor_journal_size", "journal_size")
@@ -692,9 +708,6 @@
copy_prop("vendor_squashfs_block_size", "squashfs_block_size")
copy_prop("vendor_squashfs_disable_4k_align", "squashfs_disable_4k_align")
copy_prop("vendor_base_fs_file", "base_fs_file")
- copy_prop("vendor_avb_hashtree_enable", "avb_hashtree_enable")
- copy_prop("vendor_avb_add_hashtree_footer_args",
- "avb_add_hashtree_footer_args")
copy_prop("vendor_extfs_inode_count", "extfs_inode_count")
elif mount_point == "oem":
copy_prop("fs_type", "fs_type")
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 652fadf..2ad628b 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -345,6 +345,15 @@
print("%-25s = (%s) %s" % (k, type(v).__name__, v))
+def AppendAVBSigningArgs(cmd, partition):
+ """Append signing arguments for avbtool."""
+ # e.g., "--key path/to/signing_key --algorithm SHA256_RSA4096"
+ key_path = OPTIONS.info_dict.get("avb_" + partition + "_key_path")
+ algorithm = OPTIONS.info_dict.get("avb_" + partition + "_algorithm")
+ if key_path and algorithm:
+ cmd.extend(["--key", key_path, "--algorithm", algorithm])
+
+
def _BuildBootableImage(sourcedir, fs_config_file, info_dict=None,
has_ramdisk=False, two_step_image=False):
"""Build a bootable image from the specified sourcedir.
@@ -480,13 +489,13 @@
img_keyblock.close()
# AVB: if enabled, calculate and add hash to boot.img.
- if info_dict.get("board_avb_enable", None) == "true":
+ if info_dict.get("avb_enable") == "true":
avbtool = os.getenv('AVBTOOL') or info_dict["avb_avbtool"]
part_size = info_dict["boot_size"]
cmd = [avbtool, "add_hash_footer", "--image", img.name,
"--partition_size", str(part_size), "--partition_name", "boot"]
- cmd.extend(shlex.split(info_dict["avb_signing_args"]))
- args = info_dict.get("board_avb_boot_add_hash_footer_args")
+ AppendAVBSigningArgs(cmd, "boot")
+ args = info_dict.get("avb_boot_add_hash_footer_args")
if args and args.strip():
cmd.extend(shlex.split(args))
p = Run(cmd, stdout=subprocess.PIPE)
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 1c8fe65..1b0f68b 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -1168,7 +1168,7 @@
# into A/B OTA package.
target_zip = zipfile.ZipFile(target_file, "r")
if (OPTIONS.info_dict.get("verity") == "true" or
- OPTIONS.info_dict.get("board_avb_enable") == "true"):
+ OPTIONS.info_dict.get("avb_enable") == "true"):
care_map_path = "META/care_map.txt"
namelist = target_zip.namelist()
if care_map_path in namelist: