diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 05:03:44 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 05:03:44 +0000 |
commit | 6b1084e0d98f484dab74d57179048eaa2a9f3b24 (patch) | |
tree | 0921ab3b257bd2e1724da2ea06cad119a50ea1d2 | |
parent | 6e33a4d151d2e2688d063ca593bee8d1e5eb5f85 (diff) | |
parent | 22dec6ce02ae63c40381b6b10bb3e0014387b7ee (diff) | |
download | mkbootimg-aml_med_341513600.tar.gz |
Snap for 10453563 from 22dec6ce02ae63c40381b6b10bb3e0014387b7ee to mainline-media-releaseaml_med_341711000aml_med_341619000aml_med_341513600aml_med_341312300aml_med_341312020aml_med_341111000aml_med_341011000aml_med_340922010android14-mainline-media-release
Change-Id: Iadba1ab764d60b459703ed8e1ed9bcb4c43c3833
-rw-r--r-- | Android.bp | 4 | ||||
-rw-r--r-- | BUILD.bazel | 1 | ||||
-rw-r--r-- | OWNERS | 3 | ||||
-rw-r--r-- | gki/Android.bp | 56 | ||||
-rw-r--r-- | gki/Android.mk | 36 | ||||
-rw-r--r-- | gki/README.md | 74 | ||||
-rwxr-xr-x | gki/certify_bootimg.py | 110 | ||||
-rw-r--r-- | gki/certify_bootimg_test.py | 539 | ||||
-rwxr-xr-x | gki/retrofit_gki.sh | 231 | ||||
-rwxr-xr-x | gki/retrofit_gki_test.sh | 144 | ||||
-rwxr-xr-x | unpack_bootimg.py | 6 |
11 files changed, 542 insertions, 662 deletions
@@ -43,11 +43,7 @@ cc_library { python_defaults { name: "mkbootimg_defaults", version: { - py2: { - enabled: false, - }, py3: { - enabled: true, embedded_launcher: true, }, }, diff --git a/BUILD.bazel b/BUILD.bazel index e80a82a..6fc323b 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -14,4 +14,5 @@ exports_files([ "mkbootimg.py", + "gki/testdata/testkey_rsa4096.pem", ]) @@ -1,2 +1 @@ -smuckle@google.com -yochiang@google.com
\ No newline at end of file +yochiang@google.com diff --git a/gki/Android.bp b/gki/Android.bp index c62e7d8..41a1c3e 100644 --- a/gki/Android.bp +++ b/gki/Android.bp @@ -45,59 +45,3 @@ python_binary_host { "avbtool", ], } - -sh_binary_host { - name: "retrofit_gki", - src: "retrofit_gki.sh", - required: [ - "avbtool", - "mkbootimg", - "unpack_bootimg", - ], -} - -sh_test_host { - name: "retrofit_gki_test", - src: "retrofit_gki_test.sh", - data: [ - "retrofit_gki.sh", - ], - data_bins: [ - "avbtool", - "mkbootimg", - "unpack_bootimg", - ], - test_suites: [ - "general-tests", - ], -} - -genrule { - name: "gki_retrofitting_tools", - tools: [ - "soong_zip", - "retrofit_gki", - "avbtool", - "mkbootimg", - "unpack_bootimg", - ], - srcs: [ - "README.md", - ], - cmd: "STAGE_DIR=$(genDir)/gki_retrofitting_tools && " + - "rm -rf $${STAGE_DIR} && mkdir -p $${STAGE_DIR} && " + - "cp $(location retrofit_gki) $${STAGE_DIR} && " + - "cp $(location avbtool) $${STAGE_DIR} && " + - "cp $(location mkbootimg) $${STAGE_DIR} && " + - "cp $(location unpack_bootimg) $${STAGE_DIR} && " + - "cp $(in) $${STAGE_DIR} && " + - "$(location soong_zip) -o $(out) -C $(genDir) -D $${STAGE_DIR}", - out: [ - "gki_retrofitting_tools.zip", - ], - dist: { - targets: [ - "gki_retrofitting_tools", - ], - }, -} diff --git a/gki/Android.mk b/gki/Android.mk deleted file mode 100644 index c0af5ef..0000000 --- a/gki/Android.mk +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright (C) 2022 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -_gsi_gki_product_names := \ - aosp_arm \ - aosp_arm64 \ - aosp_x86 \ - aosp_x86_64 \ - gsi_arm \ - gsi_arm64 \ - gsi_x86 \ - gsi_x86_64 \ - gki_arm64 \ - gki_x86_64 \ - -# Add gki_retrofitting_tools to `m dist` of GSI/GKI for easy pickup. -ifneq (,$(filter $(_gsi_gki_product_names),$(TARGET_PRODUCT))) - -droidcore-unbundled: gki_retrofitting_tools - -endif - -_gsi_gki_product_names := diff --git a/gki/README.md b/gki/README.md deleted file mode 100644 index 628e52a..0000000 --- a/gki/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# GKI boot image retrofitting tools for upgrading devices - -Starting from Android T the GKI boot images consist of the generic `boot.img` -and `init_boot.img`. The `boot.img` contains the generic kernel, and -`init_boot.img` contains the generic ramdisk. -For upgrading devices whose `vendor_boot` partition is non-existent, this tool -(or spec) can be used to retrofit a set of Android T GKI `boot`, `init_boot` and -OEM `vendor_boot` partition images back into a single boot image containing the -GKI kernel plus generic and vendor ramdisks. - -## Retrofitting the boot images - -1. Download the certified GKI `boot.img`. -2. Go to the build artifacts page of `aosp_arm64` on `aosp-master` branch on - https://ci.android.com/ and download `gki_retrofitting_tools.zip`. -3. Unzip and make sure the tool is in `${PATH}`. - - ```bash - unzip gki_retrofitting_tools.zip - export PATH="$(pwd)/gki_retrofitting_tools:${PATH}" - # See tool usage: - retrofit_gki --help - ``` - -4. Create the retrofitted image. The `--version` argument lets you choose the - boot image header version of the retrofitted boot image. Only version 2 is - supported at the moment. - - ```bash - retrofit_gki --boot boot.img --init_boot init_boot.img \ - --vendor_boot vendor_boot.img --version 2 -o boot.retrofitted.img - ``` - -## Spec of the retrofitted images - -* The SOURCE `boot.img` must be officially certified Android T (or later) GKI. -* The DEST retrofitted boot image must not set the security patch level in its - header. This is because the SOURCE images might have different SPL value, thus - making the boot header SPL of the retrofitted image ill-defined. The SPL value - must be defined by the chained vbmeta image of the `boot` partition. -* The `boot signature` of the DEST image is the `boot signature` of the DEST - `boot.img`. -* The DEST retrofitted boot image must pass the `vts_gki_compliance_test` - testcase. - -### Retrofit to boot image V2 - -* The `kernel` of the DEST image must be from the SOURCE `boot.img`. -* The `ramdisk` of the DEST image must be from the SOURCE `vendor_boot.img` and - `init_boot.img`. The DEST `ramdisk` is the ramdisk concatenation of the vendor - ramdisk and generic ramdisk. -* The `recovery dtbo / acpio` must be empty. -* The `dtb` of the DEST image must be from the SOURCE `vendor_boot.img`. -* The `boot_signature` section must be appended to the end of the boot image, - and its size is zero-padded to 16KiB. - -``` - +---------------------+ - | boot header | 1 page - +---------------------+ - | kernel | n pages - +---------------------+ - | * vendor ramdisk | - | +generic ramdisk | m pages - +---------------------+ - | second stage | o pages - +---------------------+ - | recovery dtbo/acpio | 0 byte - +---------------------+ - | dtb | q pages - +---------------------+ - | * boot signature | 16384 (16K) bytes - +---------------------+ -``` diff --git a/gki/certify_bootimg.py b/gki/certify_bootimg.py index 9a7b058..68a042e 100755 --- a/gki/certify_bootimg.py +++ b/gki/certify_bootimg.py @@ -132,7 +132,7 @@ def get_avb_image_size(image): return 0 -def add_avb_footer(image, partition_size): +def add_avb_footer(image, partition_size, extra_footer_args): """Appends a AVB hash footer to the image.""" avbtool_cmd = ['avbtool', 'add_hash_footer', '--image', image, @@ -143,6 +143,7 @@ def add_avb_footer(image, partition_size): else: avbtool_cmd.extend(['--dynamic_partition_size']) + avbtool_cmd.extend(extra_footer_args) subprocess.check_call(avbtool_cmd) @@ -160,6 +161,40 @@ def load_dict_from_file(path): return d +def load_gki_info_file(gki_info_file, extra_args, extra_footer_args): + """Loads extra arguments from the gki info file. + + Args: + gki_info_file: path to a gki-info.txt. + extra_args: the extra arguments forwarded to avbtool when creating + the gki certificate. + extra_footer_args: the extra arguments forwarded to avbtool when + creating the avb footer. + + """ + info_dict = load_dict_from_file(gki_info_file) + if 'certify_bootimg_extra_args' in info_dict: + extra_args.extend( + shlex.split(info_dict['certify_bootimg_extra_args'])) + if 'certify_bootimg_extra_footer_args' in info_dict: + extra_footer_args.extend( + shlex.split(info_dict['certify_bootimg_extra_footer_args'])) + + +def get_archive_name_and_format_for_shutil(path): + """Returns archive name and format to shutil.make_archive() for the |path|. + + e.g., returns ('/path/to/boot-img', 'gztar') if |path| is + '/path/to/boot-img.tar.gz'. + """ + for format_name, format_extensions, _ in shutil.get_unpack_formats(): + for extension in format_extensions: + if path.endswith(extension): + return path[:-len(extension)], format_name + + raise ValueError(f"Unsupported archive format: '{path}'") + + def parse_cmdline(): """Parse command-line options.""" parser = ArgumentParser(add_help=True) @@ -169,30 +204,50 @@ def parse_cmdline(): input_group.add_argument( '--boot_img', help='path to the boot image to certify') input_group.add_argument( - '--boot_img_zip', help='path to the boot-img-*.zip archive to certify') + '--boot_img_archive', help='path to the boot images archive to certify') parser.add_argument('--algorithm', required=True, help='signing algorithm for the certificate') parser.add_argument('--key', required=True, help='path to the RSA private key') + parser.add_argument('--gki_info', + help='path to a gki-info.txt to append additional' + 'properties into the boot signature') parser.add_argument('-o', '--output', required=True, help='output file name') # Optional args. parser.add_argument('--extra_args', default=[], action='append', help='extra arguments to be forwarded to avbtool') + parser.add_argument('--extra_footer_args', default=[], action='append', + help='extra arguments for adding the avb footer') args = parser.parse_args() + if args.gki_info and args.boot_img_archive: + parser.error('--gki_info cannot be used with --boot_image_archive. ' + 'The gki_info file should be included in the archive.') + extra_args = [] for a in args.extra_args: extra_args.extend(shlex.split(a)) args.extra_args = extra_args + extra_footer_args = [] + for a in args.extra_footer_args: + extra_footer_args.extend(shlex.split(a)) + args.extra_footer_args = extra_footer_args + + if args.gki_info: + load_gki_info_file(args.gki_info, + args.extra_args, + args.extra_footer_args) + return args -def certify_bootimg(boot_img, output_img, algorithm, key, extra_args): +def certify_bootimg(boot_img, output_img, algorithm, key, extra_args, + extra_footer_args): """Certify a GKI boot image by generating and appending a boot_signature.""" with tempfile.TemporaryDirectory() as temp_dir: boot_tmp = os.path.join(temp_dir, 'boot.tmp') @@ -202,44 +257,53 @@ def certify_bootimg(boot_img, output_img, algorithm, key, extra_args): add_certificate(boot_tmp, algorithm, key, extra_args) avb_partition_size = get_avb_image_size(boot_img) - add_avb_footer(boot_tmp, avb_partition_size) + add_avb_footer(boot_tmp, avb_partition_size, extra_footer_args) # We're done, copy the temp image to the final output. shutil.copy2(boot_tmp, output_img) -def certify_bootimg_zip(boot_img_zip, output_zip, algorithm, key, extra_args): - """Similar to certify_bootimg(), but for a zip archive of boot images.""" - with tempfile.TemporaryDirectory() as unzip_dir: - shutil.unpack_archive(boot_img_zip, unzip_dir) +def certify_bootimg_archive(boot_img_archive, output_archive, + algorithm, key, extra_args, extra_footer_args): + """Similar to certify_bootimg(), but for an archive of boot images.""" + with tempfile.TemporaryDirectory() as unpack_dir: + shutil.unpack_archive(boot_img_archive, unpack_dir) - gki_info_file = os.path.join(unzip_dir, 'gki-info.txt') + gki_info_file = os.path.join(unpack_dir, 'gki-info.txt') if os.path.exists(gki_info_file): - info_dict = load_dict_from_file(gki_info_file) - if 'certify_bootimg_extra_args' in info_dict: - extra_args.extend( - shlex.split(info_dict['certify_bootimg_extra_args'])) + load_gki_info_file(gki_info_file, extra_args, extra_footer_args) - for boot_img in glob.glob(os.path.join(unzip_dir, 'boot-*.img')): + for boot_img in glob.glob(os.path.join(unpack_dir, 'boot*.img')): print(f'Certifying {os.path.basename(boot_img)} ...') certify_bootimg(boot_img=boot_img, output_img=boot_img, - algorithm=algorithm, key=key, extra_args=extra_args) - - print(f'Making certified archive: {output_zip}') - archive_base_name = os.path.splitext(output_zip)[0] - shutil.make_archive(archive_base_name, 'zip', unzip_dir) + algorithm=algorithm, key=key, extra_args=extra_args, + extra_footer_args=extra_footer_args) + + print(f'Making certified archive: {output_archive}') + archive_file_name, archive_format = ( + get_archive_name_and_format_for_shutil(output_archive)) + built_archive = shutil.make_archive(archive_file_name, + archive_format, + unpack_dir) + # shutil.make_archive() builds *.tar.gz when then |archive_format| is + # 'gztar'. However, the end user might specify |output_archive| with + # *.tgz. Renaming *.tar.gz to *.tgz for this case. + if built_archive != os.path.realpath(output_archive): + print(f'Renaming {built_archive} -> {output_archive} ...') + os.rename(built_archive, output_archive) def main(): """Parse arguments and certify the boot image.""" args = parse_cmdline() - if args.boot_img_zip: - certify_bootimg_zip(args.boot_img_zip, args.output, args.algorithm, - args.key, args.extra_args) + if args.boot_img_archive: + certify_bootimg_archive(args.boot_img_archive, args.output, + args.algorithm, args.key, args.extra_args, + args.extra_footer_args) else: certify_bootimg(args.boot_img, args.output, args.algorithm, - args.key, args.extra_args) + args.key, args.extra_args, args.extra_footer_args) if __name__ == '__main__': diff --git a/gki/certify_bootimg_test.py b/gki/certify_bootimg_test.py index 8c7c4d3..ec5f505 100644 --- a/gki/certify_bootimg_test.py +++ b/gki/certify_bootimg_test.py @@ -68,20 +68,27 @@ def generate_test_boot_image(boot_img, kernel_size=4096, seed='kernel', subprocess.check_call(avbtool_cmd) -def generate_test_boot_image_archive(output_zip, boot_img_info, gki_info=None): - """Generates a zip archive of test boot images. +def generate_test_boot_image_archive(archive_file_name, archive_format, + boot_img_info, gki_info=None): + """Generates an archive of test boot images. It also adds a file gki-info.txt, which contains additional settings for for `certify_bootimg --extra_args`. Args: - output_zip: the output zip archive, e.g., /path/to/boot-img.zip. + archive_file_name: the name of the archive file to create, including the + path, minus any format-specific extension. + archive_format: the |format| parameter for shutil.make_archive(). + e.g., 'zip', 'tar', or 'gztar', etc. boot_img_info: a list of (boot_image_name, kernel_size, partition_size) tuples. e.g., - [('boot-1.0.img', 4096, 4 * 1024), - ('boot-2.0.img', 8192, 8 * 1024)]. + [('boot.img', 4096, 4 * 1024), + ('boot-lz4.img', 8192, 8 * 1024)]. gki_info: the file content to be written into 'gki-info.txt' in the - |output_zip|. + created archive. + + Returns: + The full path of the created archive. e.g., /path/to/boot-img.tar.gz. """ with tempfile.TemporaryDirectory() as temp_out_dir: for name, kernel_size, partition_size in boot_img_info: @@ -96,8 +103,9 @@ def generate_test_boot_image_archive(output_zip, boot_img_info, gki_info=None): with open(gki_info_path, 'w', encoding='utf-8') as f: f.write(gki_info) - archive_base_name = os.path.splitext(output_zip)[0] - shutil.make_archive(archive_base_name, 'zip', temp_out_dir) + return shutil.make_archive(archive_file_name, + archive_format, + temp_out_dir) def has_avb_footer(image): @@ -175,21 +183,21 @@ def extract_boot_signatures(boot_img, output_dir): boot_signature_bytes = boot_signature_bytes[next_signature_size:] -def extract_boot_archive_with_signatures(boot_img_zip, output_dir): +def extract_boot_archive_with_signatures(boot_img_archive, output_dir): """Extracts boot images and signatures of a boot images archive. - Suppose there are two boot images in |boot_img_zip|: boot-1.0.img - and boot-2.0.img. This function then extracts each boot-*.img and + Suppose there are two boot images in |boot_img_archive|: boot.img + and boot-lz4.img. This function then extracts each boot*.img and their signatures as: - - |output_dir|/boot-1.0.img - - |output_dir|/boot-2.0.img - - |output_dir|/boot-1.0/boot_signature1 - - |output_dir|/boot-1.0/boot_signature2 - - |output_dir|/boot-2.0/boot_signature1 - - |output_dir|/boot-2.0/boot_signature2 + - |output_dir|/boot.img + - |output_dir|/boot-lz4.img + - |output_dir|/boot/boot_signature1 + - |output_dir|/boot/boot_signature2 + - |output_dir|/boot-lz4/boot_signature1 + - |output_dir|/boot-lz4/boot_signature2 """ - shutil.unpack_archive(boot_img_zip, output_dir) - for boot_img in glob.glob(os.path.join(output_dir, 'boot-*.img')): + shutil.unpack_archive(boot_img_archive, output_dir) + for boot_img in glob.glob(os.path.join(output_dir, 'boot*.img')): img_name = os.path.splitext(os.path.basename(boot_img))[0] signature_output_dir = os.path.join(output_dir, img_name) os.mkdir(signature_output_dir, 0o777) @@ -211,6 +219,197 @@ class CertifyBootimgTest(unittest.TestCase): # C0103: invalid-name for maxDiff. self.maxDiff = None # pylint: disable=C0103 + # For AVB footers, we don't sign it so the Authentication block + # is zero bytes and the Algorithm is NONE. The footer will be + # replaced by device-specific settings when being incorporated into + # a device codebase. The footer here is just to pass some GKI + # pre-release test. + self._EXPECTED_AVB_FOOTER_BOOT_CERTIFIED = ( # pylint: disable=C0103 + 'Footer version: 1.0\n' + 'Image size: 131072 bytes\n' + 'Original image size: 24576 bytes\n' + 'VBMeta offset: 24576\n' + 'VBMeta size: 576 bytes\n' + '--\n' + 'Minimum libavb version: 1.0\n' + 'Header Block: 256 bytes\n' + 'Authentication Block: 0 bytes\n' + 'Auxiliary Block: 320 bytes\n' + 'Algorithm: NONE\n' + 'Rollback Index: 0\n' + 'Flags: 0\n' + 'Rollback Index Location: 0\n' + "Release String: 'avbtool 1.2.0'\n" + 'Descriptors:\n' + ' Hash descriptor:\n' + ' Image Size: 24576 bytes\n' + ' Hash Algorithm: sha256\n' + ' Partition Name: boot\n' + ' Salt: a11ba11b\n' + ' Digest: ' + 'c9b4ad78fae6f72f7eff939dee6078ed' + '8a75132e53f6c11ba1ec0f4b57f9eab0\n' + ' Flags: 0\n' + " Prop: avb -> 'nice'\n" + " Prop: avb_space -> 'nice to meet you'\n" + ) + + self._EXPECTED_AVB_FOOTER_BOOT_CERTIFIED_2 = ( # pylint: disable=C0103 + 'Footer version: 1.0\n' + 'Image size: 131072 bytes\n' + 'Original image size: 24576 bytes\n' + 'VBMeta offset: 24576\n' + 'VBMeta size: 576 bytes\n' + '--\n' + 'Minimum libavb version: 1.0\n' + 'Header Block: 256 bytes\n' + 'Authentication Block: 0 bytes\n' + 'Auxiliary Block: 320 bytes\n' + 'Algorithm: NONE\n' + 'Rollback Index: 0\n' + 'Flags: 0\n' + 'Rollback Index Location: 0\n' + "Release String: 'avbtool 1.2.0'\n" + 'Descriptors:\n' + ' Hash descriptor:\n' + ' Image Size: 24576 bytes\n' + ' Hash Algorithm: sha256\n' + ' Partition Name: boot\n' + ' Salt: a11ba11b\n' + ' Digest: ' + 'ae2538e78b2a30b1112cede30d858a5f' + '6f8dc2a1b109dd4a7bb28124b77d2ab0\n' + ' Flags: 0\n' + " Prop: avb -> 'nice'\n" + " Prop: avb_space -> 'nice to meet you'\n" + ) + + self._EXPECTED_AVB_FOOTER_WITH_GKI_INFO = ( # pylint: disable=C0103 + 'Footer version: 1.0\n' + 'Image size: 131072 bytes\n' + 'Original image size: 24576 bytes\n' + 'VBMeta offset: 24576\n' + 'VBMeta size: 704 bytes\n' + '--\n' + 'Minimum libavb version: 1.0\n' + 'Header Block: 256 bytes\n' + 'Authentication Block: 0 bytes\n' + 'Auxiliary Block: 448 bytes\n' + 'Algorithm: NONE\n' + 'Rollback Index: 0\n' + 'Flags: 0\n' + 'Rollback Index Location: 0\n' + "Release String: 'avbtool 1.2.0'\n" + 'Descriptors:\n' + ' Hash descriptor:\n' + ' Image Size: 24576 bytes\n' + ' Hash Algorithm: sha256\n' + ' Partition Name: boot\n' + ' Salt: a11ba11b\n' + ' Digest: ' + '363d4f246a4a5e1bba8ba8b86f5eb0cf' + '9817e4e51663ba26edccf71c3861090a\n' + ' Flags: 0\n' + " Prop: avb -> 'nice'\n" + " Prop: avb_space -> 'nice to meet you'\n" + " Prop: com.android.build.boot.os_version -> '13'\n" + " Prop: com.android.build.boot.security_patch -> '2022-05-05'\n" + ) + + self._EXPECTED_AVB_FOOTER_BOOT = ( # pylint: disable=C0103 + 'Footer version: 1.0\n' + 'Image size: 131072 bytes\n' + 'Original image size: 28672 bytes\n' + 'VBMeta offset: 28672\n' + 'VBMeta size: 704 bytes\n' + '--\n' + 'Minimum libavb version: 1.0\n' + 'Header Block: 256 bytes\n' + 'Authentication Block: 0 bytes\n' + 'Auxiliary Block: 448 bytes\n' + 'Algorithm: NONE\n' + 'Rollback Index: 0\n' + 'Flags: 0\n' + 'Rollback Index Location: 0\n' + "Release String: 'avbtool 1.2.0'\n" + 'Descriptors:\n' + ' Hash descriptor:\n' + ' Image Size: 28672 bytes\n' + ' Hash Algorithm: sha256\n' + ' Partition Name: boot\n' + ' Salt: a11ba11b\n' + ' Digest: ' + 'b93084707ba2367120e19547f17f1073' + '4c7ad8e56008ec2159d5f01b950335ad\n' + ' Flags: 0\n' + " Prop: avb -> 'nice'\n" + " Prop: avb_space -> 'nice to meet you'\n" + " Prop: com.android.build.boot.os_version -> '13'\n" + " Prop: com.android.build.boot.security_patch -> '2022-05-05'\n" + ) + + self._EXPECTED_AVB_FOOTER_BOOT_LZ4 = ( # pylint: disable=C0103 + 'Footer version: 1.0\n' + 'Image size: 262144 bytes\n' + 'Original image size: 36864 bytes\n' + 'VBMeta offset: 36864\n' + 'VBMeta size: 704 bytes\n' + '--\n' + 'Minimum libavb version: 1.0\n' + 'Header Block: 256 bytes\n' + 'Authentication Block: 0 bytes\n' + 'Auxiliary Block: 448 bytes\n' + 'Algorithm: NONE\n' + 'Rollback Index: 0\n' + 'Flags: 0\n' + 'Rollback Index Location: 0\n' + "Release String: 'avbtool 1.2.0'\n" + 'Descriptors:\n' + ' Hash descriptor:\n' + ' Image Size: 36864 bytes\n' + ' Hash Algorithm: sha256\n' + ' Partition Name: boot\n' + ' Salt: a11ba11b\n' + ' Digest: ' + '6b3f583f1bc5fbc284102e0185d02c6b' + '294f675c95b9337e89ea1e6b743af2ab\n' + ' Flags: 0\n' + " Prop: avb -> 'nice'\n" + " Prop: avb_space -> 'nice to meet you'\n" + " Prop: com.android.build.boot.os_version -> '13'\n" + " Prop: com.android.build.boot.security_patch -> '2022-05-05'\n" + ) + + self._EXPECTED_AVB_FOOTER_BOOT_GZ = ( # pylint: disable=C0103 + 'Footer version: 1.0\n' + 'Image size: 131072 bytes\n' + 'Original image size: 28672 bytes\n' + 'VBMeta offset: 28672\n' + 'VBMeta size: 576 bytes\n' + '--\n' + 'Minimum libavb version: 1.0\n' + 'Header Block: 256 bytes\n' + 'Authentication Block: 0 bytes\n' + 'Auxiliary Block: 320 bytes\n' + 'Algorithm: NONE\n' + 'Rollback Index: 0\n' + 'Flags: 0\n' + 'Rollback Index Location: 0\n' + "Release String: 'avbtool 1.2.0'\n" + 'Descriptors:\n' + ' Hash descriptor:\n' + ' Image Size: 28672 bytes\n' + ' Hash Algorithm: sha256\n' + ' Partition Name: boot\n' + ' Salt: a11ba11b\n' + ' Digest: ' + 'd2098d507e039afc6b4d7ec3de129a8d' + 'd0e0cf889c9181ebee65ce2fb25de3f5\n' + ' Flags: 0\n' + " Prop: avb -> 'nice'\n" + " Prop: avb_space -> 'nice to meet you'\n" + ) + self._EXPECTED_BOOT_SIGNATURE_RSA2048 = ( # pylint: disable=C0103 'Minimum libavb version: 1.0\n' 'Header Block: 256 bytes\n' @@ -315,7 +514,69 @@ class CertifyBootimgTest(unittest.TestCase): " Prop: space -> 'nice to meet you'\n" ) - self._EXPECTED_BOOT_1_0_SIGNATURE1_RSA4096 = ( # pylint: disable=C0103 + self._EXPECTED_BOOT_SIGNATURE_WITH_GKI_INFO = ( # pylint: disable=C0103 + 'Minimum libavb version: 1.0\n' + 'Header Block: 256 bytes\n' + 'Authentication Block: 576 bytes\n' + 'Auxiliary Block: 1600 bytes\n' + 'Public key (sha1): ' + '2597c218aae470a130f61162feaae70afd97f011\n' + 'Algorithm: SHA256_RSA4096\n' # RSA4096 + 'Rollback Index: 0\n' + 'Flags: 0\n' + 'Rollback Index Location: 0\n' + "Release String: 'avbtool 1.2.0'\n" + 'Descriptors:\n' + ' Hash descriptor:\n' + ' Image Size: 8192 bytes\n' + ' Hash Algorithm: sha256\n' + ' Partition Name: boot\n' # boot + ' Salt: d00df00d\n' + ' Digest: ' + 'faf1da72a4fba97ddab0b8f7a410db86' + '8fb72392a66d1440ff8bff490c73c771\n' + ' Flags: 0\n' + " Prop: gki -> 'nice'\n" + " Prop: space -> 'nice to meet you'\n" + " Prop: KERNEL_RELEASE -> '5.10.42-android13-0-00544-" + "ged21d463f856'\n" + " Prop: BRANCH -> 'android13-5.10-2022-05'\n" + " Prop: BUILD_NUMBER -> 'ab8295296'\n" + " Prop: GKI_INFO -> 'added here'\n" + ) + + self._EXPECTED_KERNEL_SIGNATURE_WITH_GKI_INFO = (# pylint: disable=C0103 + 'Minimum libavb version: 1.0\n' + 'Header Block: 256 bytes\n' + 'Authentication Block: 576 bytes\n' + 'Auxiliary Block: 1600 bytes\n' + 'Public key (sha1): ' + '2597c218aae470a130f61162feaae70afd97f011\n' + 'Algorithm: SHA256_RSA4096\n' # RSA4096 + 'Rollback Index: 0\n' + 'Flags: 0\n' + 'Rollback Index Location: 0\n' + "Release String: 'avbtool 1.2.0'\n" + 'Descriptors:\n' + ' Hash descriptor:\n' + ' Image Size: 4096 bytes\n' + ' Hash Algorithm: sha256\n' + ' Partition Name: generic_kernel\n' # generic_kernel + ' Salt: d00df00d\n' + ' Digest: ' + '762c877f3af0d50a4a4fbc1385d5c7ce' + '52a1288db74b33b72217d93db6f2909f\n' + ' Flags: 0\n' + " Prop: gki -> 'nice'\n" + " Prop: space -> 'nice to meet you'\n" + " Prop: KERNEL_RELEASE -> '5.10.42-android13-0-00544-" + "ged21d463f856'\n" + " Prop: BRANCH -> 'android13-5.10-2022-05'\n" + " Prop: BUILD_NUMBER -> 'ab8295296'\n" + " Prop: GKI_INFO -> 'added here'\n" + ) + + self._EXPECTED_BOOT_SIGNATURE1_RSA4096 = ( # pylint: disable=C0103 'Minimum libavb version: 1.0\n' 'Header Block: 256 bytes\n' 'Authentication Block: 576 bytes\n' @@ -334,8 +595,8 @@ class CertifyBootimgTest(unittest.TestCase): ' Partition Name: boot\n' # boot ' Salt: d00df00d\n' ' Digest: ' - '88465e463bffb9f7dfc0c1f46d01bcf3' - '15f7693e19bd188a0ca1feca2ed7b9df\n' + '30208b4d0a6d16db47fc13c9527bfe81' + 'a168d3b3940325d1ca8d3439792bfe18\n' ' Flags: 0\n' " Prop: gki -> 'nice'\n" " Prop: space -> 'nice to meet you'\n" @@ -346,7 +607,7 @@ class CertifyBootimgTest(unittest.TestCase): " Prop: SPACE -> 'nice to meet you'\n" ) - self._EXPECTED_BOOT_1_0_SIGNATURE2_RSA4096 = ( # pylint: disable=C0103 + self._EXPECTED_BOOT_SIGNATURE2_RSA4096 = ( # pylint: disable=C0103 'Minimum libavb version: 1.0\n' 'Header Block: 256 bytes\n' 'Authentication Block: 576 bytes\n' @@ -365,8 +626,8 @@ class CertifyBootimgTest(unittest.TestCase): ' Partition Name: generic_kernel\n' # generic_kernel ' Salt: d00df00d\n' ' Digest: ' - '14ac8d0d233e57a317acd05cd458f2bb' - 'cc78725ef9f66c1b38e90697fb09d943\n' + 'd4c8847e7d9900a98f77e1f0b5272854' + '7bf9c1e428fea500d419275f72ec5bd6\n' ' Flags: 0\n' " Prop: gki -> 'nice'\n" " Prop: space -> 'nice to meet you'\n" @@ -377,7 +638,7 @@ class CertifyBootimgTest(unittest.TestCase): " Prop: SPACE -> 'nice to meet you'\n" ) - self._EXPECTED_BOOT_2_0_SIGNATURE1_RSA4096 = ( # pylint: disable=C0103 + self._EXPECTED_BOOT_LZ4_SIGNATURE1_RSA4096 = ( # pylint: disable=C0103 'Minimum libavb version: 1.0\n' 'Header Block: 256 bytes\n' 'Authentication Block: 576 bytes\n' @@ -396,8 +657,8 @@ class CertifyBootimgTest(unittest.TestCase): ' Partition Name: boot\n' # boot ' Salt: d00df00d\n' ' Digest: ' - '3e6a9854a9d2350a7071083bc3f37376' - '37573fd87b1c72b146cb4870ac6af36f\n' + '9d3a0670a9fd3de66e940117ef97700f' + 'ed5fd1c6fb90798fd3873af45fc91cb4\n' ' Flags: 0\n' " Prop: gki -> 'nice'\n" " Prop: space -> 'nice to meet you'\n" @@ -408,7 +669,7 @@ class CertifyBootimgTest(unittest.TestCase): " Prop: SPACE -> 'nice to meet you'\n" ) - self._EXPECTED_BOOT_2_0_SIGNATURE2_RSA4096 = ( # pylint: disable=C0103 + self._EXPECTED_BOOT_LZ4_SIGNATURE2_RSA4096 = ( # pylint: disable=C0103 'Minimum libavb version: 1.0\n' 'Header Block: 256 bytes\n' 'Authentication Block: 576 bytes\n' @@ -427,8 +688,8 @@ class CertifyBootimgTest(unittest.TestCase): ' Partition Name: generic_kernel\n' # generic_kernel ' Salt: d00df00d\n' ' Digest: ' - '92fb8443cd284b67a4cbf5ce00348b50' - '1c657e0aedf4e2181c92ad7fc8b5224f\n' + '7d109e3dccca9e30e04249162d07e58c' + '62fdf269804b35857b956fba339b2679\n' ' Flags: 0\n' " Prop: gki -> 'nice'\n" " Prop: space -> 'nice to meet you'\n" @@ -439,7 +700,7 @@ class CertifyBootimgTest(unittest.TestCase): " Prop: SPACE -> 'nice to meet you'\n" ) - self._EXPECTED_BOOT_3_0_SIGNATURE1_RSA4096 = ( # pylint: disable=C0103 + self._EXPECTED_BOOT_GZ_SIGNATURE1_RSA4096 = ( # pylint: disable=C0103 'Minimum libavb version: 1.0\n' 'Header Block: 256 bytes\n' 'Authentication Block: 576 bytes\n' @@ -458,14 +719,14 @@ class CertifyBootimgTest(unittest.TestCase): ' Partition Name: boot\n' # boot ' Salt: d00df00d\n' ' Digest: ' - '9b9cd845a367d7fc9b61d6ac02b0e7c9' - 'dc3d3b219abf60dd6e19359f0353c917\n' + '6fcddc6167ae3c2037b424d35c3ef107' + 'f586510dbb2d652d7c08b88e6ea52fc6\n' ' Flags: 0\n' " Prop: gki -> 'nice'\n" " Prop: space -> 'nice to meet you'\n" ) - self._EXPECTED_BOOT_3_0_SIGNATURE2_RSA4096 = ( # pylint: disable=C0103 + self._EXPECTED_BOOT_GZ_SIGNATURE2_RSA4096 = ( # pylint: disable=C0103 'Minimum libavb version: 1.0\n' 'Header Block: 256 bytes\n' 'Authentication Block: 576 bytes\n' @@ -484,8 +745,8 @@ class CertifyBootimgTest(unittest.TestCase): ' Partition Name: generic_kernel\n' # generic_kernel ' Salt: d00df00d\n' ' Digest: ' - '0cd7d331ed9b32dcd92f00e2cac75595' - '52199170afe788a8fcf1954f9ea072d0\n' + '7a6a43eb4048b783346fb6d039103647' + '6c4313146da521467af282dff1838d0e\n' ' Flags: 0\n' " Prop: gki -> 'nice'\n" " Prop: space -> 'nice to meet you'\n" @@ -577,6 +838,8 @@ class CertifyBootimgTest(unittest.TestCase): '--key', './testdata/testkey_rsa2048.pem', '--extra_args', '--prop gki:nice ' '--prop space:"nice to meet you"', + '--extra_footer_args', '--salt a11ba11b --prop avb:nice ' + '--prop avb_space:"nice to meet you"', '--output', boot_certified_img, ] subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir) @@ -585,7 +848,13 @@ class CertifyBootimgTest(unittest.TestCase): self.assertTrue(has_avb_footer(boot_certified_img)) self.assertEqual(os.path.getsize(boot_img), os.path.getsize(boot_certified_img)) + # Checks the content in the AVB footer. + self._test_boot_signatures( + temp_out_dir, + {'boot-certified.img': + self._EXPECTED_AVB_FOOTER_BOOT_CERTIFIED}) + # Checks the content in the GKI certificate. extract_boot_signatures(boot_certified_img, temp_out_dir) self._test_boot_signatures( temp_out_dir, @@ -602,6 +871,8 @@ class CertifyBootimgTest(unittest.TestCase): '--key', './testdata/testkey_rsa4096.pem', '--extra_args', '--prop gki:nice ' '--prop space:"nice to meet you"', + '--extra_footer_args', '--salt a11ba11b --prop avb:nice ' + '--prop avb_space:"nice to meet you"', '--output', boot_certified2_img, ] subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir) @@ -610,13 +881,77 @@ class CertifyBootimgTest(unittest.TestCase): self.assertTrue(has_avb_footer(boot_certified2_img)) self.assertEqual(os.path.getsize(boot_certified_img), os.path.getsize(boot_certified2_img)) + # Checks the content in the AVB footer. + self._test_boot_signatures( + temp_out_dir, + {'boot-certified2.img': + self._EXPECTED_AVB_FOOTER_BOOT_CERTIFIED_2}) + # Checks the content in the GKI certificate. extract_boot_signatures(boot_certified2_img, temp_out_dir) self._test_boot_signatures( temp_out_dir, {'boot_signature1': self._EXPECTED_BOOT_SIGNATURE_RSA4096, 'boot_signature2': self._EXPECTED_KERNEL_SIGNATURE_RSA4096}) + def test_certify_bootimg_with_gki_info(self): + """Tests certify_bootimg with --gki_info.""" + with tempfile.TemporaryDirectory() as temp_out_dir: + boot_img = os.path.join(temp_out_dir, 'boot.img') + generate_test_boot_image(boot_img=boot_img, + avb_partition_size=128 * 1024) + self.assertTrue(has_avb_footer(boot_img)) + + gki_info = ('certify_bootimg_extra_args=' + '--prop KERNEL_RELEASE:5.10.42' + '-android13-0-00544-ged21d463f856 ' + '--prop BRANCH:android13-5.10-2022-05 ' + '--prop BUILD_NUMBER:ab8295296 ' + '--prop GKI_INFO:"added here"\n' + 'certify_bootimg_extra_footer_args=' + '--prop com.android.build.boot.os_version:13 ' + '--prop com.android.build.boot.security_patch:' + '2022-05-05\n') + gki_info_path = os.path.join(temp_out_dir, 'gki-info.txt') + with open(gki_info_path, 'w', encoding='utf-8') as f: + f.write(gki_info) + + # Certifies the boot image with --gki_info. + boot_certified_img = os.path.join(temp_out_dir, + 'boot-certified.img') + certify_bootimg_cmds = [ + 'certify_bootimg', + '--boot_img', boot_img, + '--algorithm', 'SHA256_RSA4096', + '--key', './testdata/testkey_rsa4096.pem', + '--extra_args', '--prop gki:nice ' + '--prop space:"nice to meet you"', + '--extra_footer_args', '--salt a11ba11b --prop avb:nice ' + '--prop avb_space:"nice to meet you"', + '--gki_info', gki_info_path, + '--output', boot_certified_img, + ] + subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir) + + # Checks an AVB footer exists and the image size remains. + self.assertTrue(has_avb_footer(boot_certified_img)) + self.assertEqual(os.path.getsize(boot_img), + os.path.getsize(boot_certified_img)) + + # Checks the content in the AVB footer. + self._test_boot_signatures( + temp_out_dir, + {'boot-certified.img': self._EXPECTED_AVB_FOOTER_WITH_GKI_INFO}) + + # Checks the content in the GKI certificate. + extract_boot_signatures(boot_certified_img, temp_out_dir) + self._test_boot_signatures( + temp_out_dir, + {'boot_signature1': + self._EXPECTED_BOOT_SIGNATURE_WITH_GKI_INFO, + 'boot_signature2': + self._EXPECTED_KERNEL_SIGNATURE_WITH_GKI_INFO}) + def test_certify_bootimg_exceed_size(self): """Tests the boot signature size exceeded max size of the signature.""" with tempfile.TemporaryDirectory() as temp_out_dir: @@ -647,121 +982,147 @@ class CertifyBootimgTest(unittest.TestCase): err.stderr) def test_certify_bootimg_archive(self): - """Tests certify_bootimg for a boot-img.zip.""" + """Tests certify_bootimg for a boot images archive..""" with tempfile.TemporaryDirectory() as temp_out_dir: - boot_img_zip = os.path.join(temp_out_dir, 'boot-img.zip') + boot_img_archive_name = os.path.join(temp_out_dir, 'boot-img') gki_info = ('certify_bootimg_extra_args=' '--prop KERNEL_RELEASE:5.10.42' '-android13-0-00544-ged21d463f856 ' '--prop BRANCH:android13-5.10-2022-05 ' '--prop BUILD_NUMBER:ab8295296 ' - '--prop SPACE:"nice to meet you"\n') - generate_test_boot_image_archive( - boot_img_zip, + '--prop SPACE:"nice to meet you"\n' + 'certify_bootimg_extra_footer_args=' + '--prop com.android.build.boot.os_version:13 ' + '--prop com.android.build.boot.security_patch:' + '2022-05-05\n') + boot_img_archive_path = generate_test_boot_image_archive( + boot_img_archive_name, + 'gztar', # A list of (boot_img_name, kernel_size, partition_size). - [('boot-1.0.img', 8 * 1024, 128 * 1024), - ('boot-2.0.img', 16 * 1024, 256 * 1024)], + [('boot.img', 8 * 1024, 128 * 1024), + ('boot-lz4.img', 16 * 1024, 256 * 1024)], gki_info) # Certify the boot image archive, with a RSA4096 key. - boot_certified_img_zip = os.path.join(temp_out_dir, - 'boot-certified-img.zip') + boot_certified_img_archive = os.path.join( + temp_out_dir, 'boot-certified-img.tar.gz') certify_bootimg_cmds = [ 'certify_bootimg', - '--boot_img_zip', boot_img_zip, + '--boot_img_archive', boot_img_archive_path, '--algorithm', 'SHA256_RSA4096', '--key', './testdata/testkey_rsa4096.pem', '--extra_args', '--prop gki:nice ' '--prop space:"nice to meet you"', - '--output', boot_certified_img_zip, + '--extra_footer_args', '--salt a11ba11b --prop avb:nice ' + '--prop avb_space:"nice to meet you"', + '--output', boot_certified_img_archive, ] subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir) - extract_boot_archive_with_signatures(boot_certified_img_zip, + extract_boot_archive_with_signatures(boot_certified_img_archive, temp_out_dir) # Checks an AVB footer exists and the image size remains. - boot_1_img = os.path.join(temp_out_dir, 'boot-1.0.img') - self.assertTrue(has_avb_footer(boot_1_img)) - self.assertEqual(os.path.getsize(boot_1_img), 128 * 1024) + boot_img = os.path.join(temp_out_dir, 'boot.img') + self.assertTrue(has_avb_footer(boot_img)) + self.assertEqual(os.path.getsize(boot_img), 128 * 1024) - boot_2_img = os.path.join(temp_out_dir, 'boot-2.0.img') - self.assertTrue(has_avb_footer(boot_2_img)) - self.assertEqual(os.path.getsize(boot_2_img), 256 * 1024) + boot_lz4_img = os.path.join(temp_out_dir, 'boot-lz4.img') + self.assertTrue(has_avb_footer(boot_lz4_img)) + self.assertEqual(os.path.getsize(boot_lz4_img), 256 * 1024) + # Checks the content in the AVB footer. self._test_boot_signatures( temp_out_dir, - {'boot-1.0/boot_signature1': - self._EXPECTED_BOOT_1_0_SIGNATURE1_RSA4096, - 'boot-1.0/boot_signature2': - self._EXPECTED_BOOT_1_0_SIGNATURE2_RSA4096, - 'boot-2.0/boot_signature1': - self._EXPECTED_BOOT_2_0_SIGNATURE1_RSA4096, - 'boot-2.0/boot_signature2': - self._EXPECTED_BOOT_2_0_SIGNATURE2_RSA4096}) + {'boot.img': self._EXPECTED_AVB_FOOTER_BOOT, + 'boot-lz4.img': self._EXPECTED_AVB_FOOTER_BOOT_LZ4}) + + # Checks the content in the GKI certificate. + self._test_boot_signatures( + temp_out_dir, + {'boot/boot_signature1': + self._EXPECTED_BOOT_SIGNATURE1_RSA4096, + 'boot/boot_signature2': + self._EXPECTED_BOOT_SIGNATURE2_RSA4096, + 'boot-lz4/boot_signature1': + self._EXPECTED_BOOT_LZ4_SIGNATURE1_RSA4096, + 'boot-lz4/boot_signature2': + self._EXPECTED_BOOT_LZ4_SIGNATURE2_RSA4096}) def test_certify_bootimg_archive_without_gki_info(self): - """Tests certify_bootimg for a boot-img.zip.""" + """Tests certify_bootimg for a boot images archive.""" with tempfile.TemporaryDirectory() as temp_out_dir: - boot_img_zip = os.path.join(temp_out_dir, 'boot-img.zip') + boot_img_archive_name = os.path.join(temp_out_dir, 'boot-img') - # Checks ceritfy_bootimg works for a boot-img.zip without a - # gki-info.txt. - generate_test_boot_image_archive( - boot_img_zip, + # Checks ceritfy_bootimg works for a boot images archive without a + # gki-info.txt. Using *.zip -> *.tar. + boot_img_archive_path = generate_test_boot_image_archive( + boot_img_archive_name, + 'zip', # A list of (boot_img_name, kernel_size, partition_size). - [('boot-3.0.img', 8 * 1024, 128 * 1024)], + [('boot-gz.img', 8 * 1024, 128 * 1024)], gki_info=None) # Certify the boot image archive, with a RSA4096 key. - boot_certified_img_zip = os.path.join(temp_out_dir, - 'boot-certified-img.zip') + boot_certified_img_archive = os.path.join( + temp_out_dir, 'boot-certified-img.tar') certify_bootimg_cmds = [ 'certify_bootimg', - '--boot_img_zip', boot_img_zip, + '--boot_img_archive', boot_img_archive_path, '--algorithm', 'SHA256_RSA4096', '--key', './testdata/testkey_rsa4096.pem', '--extra_args', '--prop gki:nice ' '--prop space:"nice to meet you"', - '--output', boot_certified_img_zip, + '--extra_footer_args', '--salt a11ba11b --prop avb:nice ' + '--prop avb_space:"nice to meet you"', + '--output', boot_certified_img_archive, ] subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir) - # Checks ceritfy_bootimg works for a boot-img.zip with a special - # gki-info.txt. - generate_test_boot_image_archive( - boot_img_zip, + # Checks ceritfy_bootimg works for a boot images archive with a + # special gki-info.txt. Using *.tar -> *.tgz. + boot_img_archive_path = generate_test_boot_image_archive( + boot_img_archive_name, + 'tar', # A list of (boot_img_name, kernel_size, partition_size). - [('boot-3.0.img', 8 * 1024, 128 * 1024)], + [('boot-gz.img', 8 * 1024, 128 * 1024)], gki_info='a=b\n' 'c=d\n') # Certify the boot image archive, with a RSA4096 key. - boot_certified_img_zip = os.path.join(temp_out_dir, - 'boot-certified-img.zip') + boot_certified_img_archive2 = os.path.join( + temp_out_dir, 'boot-certified-img.tgz') certify_bootimg_cmds = [ 'certify_bootimg', - '--boot_img_zip', boot_img_zip, + '--boot_img_archive', boot_img_archive_path, '--algorithm', 'SHA256_RSA4096', '--key', './testdata/testkey_rsa4096.pem', '--extra_args', '--prop gki:nice ' '--prop space:"nice to meet you"', - '--output', boot_certified_img_zip, + '--extra_footer_args', '--salt a11ba11b --prop avb:nice ' + '--prop avb_space:"nice to meet you"', + '--output', boot_certified_img_archive2, ] subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir) - extract_boot_archive_with_signatures(boot_certified_img_zip, + extract_boot_archive_with_signatures(boot_certified_img_archive2, temp_out_dir) # Checks an AVB footer exists and the image size remains. - boot_3_img = os.path.join(temp_out_dir, 'boot-3.0.img') + boot_3_img = os.path.join(temp_out_dir, 'boot-gz.img') self.assertTrue(has_avb_footer(boot_3_img)) self.assertEqual(os.path.getsize(boot_3_img), 128 * 1024) + # Checks the content in the AVB footer. + self._test_boot_signatures( + temp_out_dir, + {'boot-gz.img': self._EXPECTED_AVB_FOOTER_BOOT_GZ}) + + # Checks the content in the GKI certificate. self._test_boot_signatures( temp_out_dir, - {'boot-3.0/boot_signature1': - self._EXPECTED_BOOT_3_0_SIGNATURE1_RSA4096, - 'boot-3.0/boot_signature2': - self._EXPECTED_BOOT_3_0_SIGNATURE2_RSA4096}) + {'boot-gz/boot_signature1': + self._EXPECTED_BOOT_GZ_SIGNATURE1_RSA4096, + 'boot-gz/boot_signature2': + self._EXPECTED_BOOT_GZ_SIGNATURE2_RSA4096}) # I don't know how, but we need both the logger configuration and verbosity diff --git a/gki/retrofit_gki.sh b/gki/retrofit_gki.sh deleted file mode 100755 index 01af7fa..0000000 --- a/gki/retrofit_gki.sh +++ /dev/null @@ -1,231 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2022 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# -# Retrofits GKI boot images for upgrading devices. -# - -set -eo errtrace - -usage() { - cat <<EOF -Usage: - $0 --boot BOOT --init_boot INIT_BOOT --version {3,4} -o OUTPUT - $0 --boot BOOT --init_boot INIT_BOOT --vendor_boot VENDOR_BOOT --version 2 -o OUTPUT - -Options: - --boot FILE - Path to the generic boot image. - --init_boot FILE - Path to the generic init_boot image. - --vendor_boot FILE - Path to the vendor boot image. - --version {2,3,4} - Boot image header version to retrofit to. - -o, --output FILE - Path to the output boot image. - -v, --verbose - Show debug messages. - -h, --help, --usage - Show this help message. -EOF -} - -die() { - echo >&2 "ERROR:" "${@}" - exit 1 -} - -file_size() { - stat -c '%s' "$1" -} - -get_arg() { - local arg="$1" - shift - while [[ "$#" -gt 0 ]]; do - if [[ "$1" == "${arg}" ]]; then - shift - echo "$1" - return - fi - shift - done -} - -TEMP_DIR="$(mktemp -d --tmpdir retrofit_gki.XXXXXXXX)" -readonly TEMP_DIR - -exit_handler() { - readonly EXIT_CODE="$?" - rm -rf "${TEMP_DIR}" ||: - exit "${EXIT_CODE}" -} - -trap exit_handler EXIT -trap 'die "line ${LINENO}, ${FUNCNAME:-<main>}(): \"${BASH_COMMAND}\" returned \"$?\"" ' ERR - -while [[ "$1" =~ ^- ]]; do - case "$1" in - --boot ) - shift - BOOT_IMAGE="$1" - ;; - --init_boot ) - shift - INIT_BOOT_IMAGE="$1" - ;; - --vendor_boot ) - shift - VENDOR_BOOT_IMAGE="$1" - ;; - --version ) - shift - OUTPUT_BOOT_IMAGE_VERSION="$1" - ;; - -o | --output ) - shift - OUTPUT_BOOT_IMAGE="$1" - ;; - -v | --verbose ) - VERBOSE=true - ;; - -- ) - shift - break - ;; - -h | --help | --usage ) - usage - exit 0 - ;; - * ) - echo >&2 "Unexpected flag: '$1'" - usage >&2 - exit 1 - ;; - esac - shift -done - -declare -ir OUTPUT_BOOT_IMAGE_VERSION -readonly BOOT_IMAGE -readonly INIT_BOOT_IMAGE -readonly VENDOR_BOOT_IMAGE -readonly OUTPUT_BOOT_IMAGE -readonly VERBOSE - -# Make sure the input arguments make sense. -[[ -f "${BOOT_IMAGE}" ]] || - die "argument '--boot': not a regular file: '${BOOT_IMAGE}'" -[[ -f "${INIT_BOOT_IMAGE}" ]] || - die "argument '--init_boot': not a regular file: '${INIT_BOOT_IMAGE}'" -if [[ "${OUTPUT_BOOT_IMAGE_VERSION}" -lt 2 ]] || [[ "${OUTPUT_BOOT_IMAGE_VERSION}" -gt 4 ]]; then - die "argument '--version': valid choices are {2, 3, 4}" -elif [[ "${OUTPUT_BOOT_IMAGE_VERSION}" -eq 2 ]]; then - [[ -f "${VENDOR_BOOT_IMAGE}" ]] || - die "argument '--vendor_boot': not a regular file: '${VENDOR_BOOT_IMAGE}'" -fi -[[ -z "${OUTPUT_BOOT_IMAGE}" ]] && - die "argument '--output': cannot be empty" - -readonly BOOT_IMAGE_WITHOUT_AVB_FOOTER="${TEMP_DIR}/boot.img.without_avb_footer" -readonly BOOT_DIR="${TEMP_DIR}/boot" -readonly INIT_BOOT_DIR="${TEMP_DIR}/init_boot" -readonly VENDOR_BOOT_DIR="${TEMP_DIR}/vendor_boot" -readonly VENDOR_BOOT_MKBOOTIMG_ARGS="${TEMP_DIR}/vendor_boot.mkbootimg_args" -readonly OUTPUT_RAMDISK="${TEMP_DIR}/out.ramdisk" -readonly OUTPUT_BOOT_SIGNATURE="${TEMP_DIR}/out.boot_signature" - -readonly AVBTOOL="${AVBTOOL:-avbtool}" -readonly MKBOOTIMG="${MKBOOTIMG:-mkbootimg}" -readonly UNPACK_BOOTIMG="${UNPACK_BOOTIMG:-unpack_bootimg}" - -# Fixed boot signature size for easy discovery in VTS. -readonly BOOT_SIGNATURE_SIZE=$(( 16 << 10 )) - - -# -# Preparations are done. Now begin the actual work. -# - -# Copy the boot image because `avbtool erase_footer` edits the file in-place. -cp "${BOOT_IMAGE}" "${BOOT_IMAGE_WITHOUT_AVB_FOOTER}" -( [[ -n "${VERBOSE}" ]] && set -x - "${AVBTOOL}" erase_footer --image "${BOOT_IMAGE_WITHOUT_AVB_FOOTER}" 2>/dev/null ||: - tail -c "${BOOT_SIGNATURE_SIZE}" "${BOOT_IMAGE_WITHOUT_AVB_FOOTER}" > "${OUTPUT_BOOT_SIGNATURE}" - "${UNPACK_BOOTIMG}" --boot_img "${BOOT_IMAGE}" --out "${BOOT_DIR}" >/dev/null - "${UNPACK_BOOTIMG}" --boot_img "${INIT_BOOT_IMAGE}" --out "${INIT_BOOT_DIR}" >/dev/null -) -if [[ "$(file_size "${OUTPUT_BOOT_SIGNATURE}")" -ne "${BOOT_SIGNATURE_SIZE}" ]]; then - die "boot signature size must be equal to ${BOOT_SIGNATURE_SIZE}" -fi - -declare -a mkbootimg_args=() - -if [[ "${OUTPUT_BOOT_IMAGE_VERSION}" -eq 4 ]]; then - mkbootimg_args+=( \ - --header_version 4 \ - --kernel "${BOOT_DIR}/kernel" \ - --ramdisk "${INIT_BOOT_DIR}/ramdisk" \ - ) -elif [[ "${OUTPUT_BOOT_IMAGE_VERSION}" -eq 3 ]]; then - mkbootimg_args+=( \ - --header_version 3 \ - --kernel "${BOOT_DIR}/kernel" \ - --ramdisk "${INIT_BOOT_DIR}/ramdisk" \ - ) -elif [[ "${OUTPUT_BOOT_IMAGE_VERSION}" -eq 2 ]]; then - ( [[ -n "${VERBOSE}" ]] && set -x - "${UNPACK_BOOTIMG}" --boot_img "${VENDOR_BOOT_IMAGE}" --out "${VENDOR_BOOT_DIR}" \ - --format=mkbootimg -0 > "${VENDOR_BOOT_MKBOOTIMG_ARGS}" - cat "${VENDOR_BOOT_DIR}/vendor_ramdisk" "${INIT_BOOT_DIR}/ramdisk" > "${OUTPUT_RAMDISK}" - ) - - declare -a vendor_boot_args=() - while IFS= read -r -d '' ARG; do - vendor_boot_args+=("${ARG}") - done < "${VENDOR_BOOT_MKBOOTIMG_ARGS}" - - pagesize="$(get_arg --pagesize "${vendor_boot_args[@]}")" - kernel_offset="$(get_arg --kernel_offset "${vendor_boot_args[@]}")" - ramdisk_offset="$(get_arg --ramdisk_offset "${vendor_boot_args[@]}")" - tags_offset="$(get_arg --tags_offset "${vendor_boot_args[@]}")" - dtb_offset="$(get_arg --dtb_offset "${vendor_boot_args[@]}")" - kernel_cmdline="$(get_arg --vendor_cmdline "${vendor_boot_args[@]}")" - - mkbootimg_args+=( \ - --header_version 2 \ - --base 0 \ - --kernel_offset "${kernel_offset}" \ - --ramdisk_offset "${ramdisk_offset}" \ - --second_offset 0 \ - --tags_offset "${tags_offset}" \ - --dtb_offset "${dtb_offset}" \ - --cmdline "${kernel_cmdline}" \ - --pagesize "${pagesize}" \ - --kernel "${BOOT_DIR}/kernel" \ - --ramdisk "${OUTPUT_RAMDISK}" \ - ) - if [[ -f "${VENDOR_BOOT_DIR}/dtb" ]]; then - mkbootimg_args+=(--dtb "${VENDOR_BOOT_DIR}/dtb") - fi -fi - -( [[ -n "${VERBOSE}" ]] && set -x - "${MKBOOTIMG}" "${mkbootimg_args[@]}" --output "${OUTPUT_BOOT_IMAGE}" - cat "${OUTPUT_BOOT_SIGNATURE}" >> "${OUTPUT_BOOT_IMAGE}" -) diff --git a/gki/retrofit_gki_test.sh b/gki/retrofit_gki_test.sh deleted file mode 100755 index b3cb0a5..0000000 --- a/gki/retrofit_gki_test.sh +++ /dev/null @@ -1,144 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2022 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -set -eo errtrace - -die() { - echo >&2 "ERROR:" "${@}" - exit 1 -} - -trap 'die "line ${LINENO}, ${FUNCNAME:-<main>}(): \"${BASH_COMMAND}\" returned \"$?\"" ' ERR - -# Figure out where we are and where to look for test executables. -cd "$(dirname "${BASH_SOURCE[0]}")" -TEST_DIR="$(pwd)" -readonly TEST_DIR -readonly TEMP_DIR="${TEST_DIR}/stage.retrofit_gki_test" - -export PATH="${TEST_DIR}:${PATH}" -rm -rf "${TEMP_DIR}" -mkdir -p "${TEMP_DIR}" - -# Generate some test files. -readonly TEST_DTB="${TEMP_DIR}/dtb" -readonly TEST_KERNEL="${TEMP_DIR}/kernel" -readonly TEST_RAMDISK="${TEMP_DIR}/ramdisk" -readonly TEST_VENDOR_RAMDISK="${TEMP_DIR}/vendor_ramdisk" -readonly TEST_BOOT_SIGNATURE="${TEMP_DIR}/boot.boot_signature" -readonly TEST_V2_RETROFITTED_RAMDISK="${TEMP_DIR}/retrofitted.ramdisk" -readonly TEST_BOOT_IMAGE="${TEMP_DIR}/boot.img" -readonly TEST_INIT_BOOT_IMAGE="${TEMP_DIR}/init_boot.img" -readonly TEST_VENDOR_BOOT_IMAGE="${TEMP_DIR}/vendor_boot.img" - -( # Run these in subshell because dd is noisy. - dd if=/dev/urandom of="${TEST_DTB}" bs=1024 count=10 - dd if=/dev/urandom of="${TEST_KERNEL}" bs=1024 count=10 - dd if=/dev/urandom of="${TEST_RAMDISK}" bs=1024 count=10 - dd if=/dev/urandom of="${TEST_VENDOR_RAMDISK}" bs=1024 count=10 - dd if=/dev/urandom of="${TEST_BOOT_SIGNATURE}" bs=1024 count=16 -) 2> /dev/null - -cat "${TEST_VENDOR_RAMDISK}" "${TEST_RAMDISK}" > "${TEST_V2_RETROFITTED_RAMDISK}" - -mkbootimg \ - --header_version 4 \ - --kernel "${TEST_KERNEL}" \ - --output "${TEST_BOOT_IMAGE}" -cat "${TEST_BOOT_SIGNATURE}" >> "${TEST_BOOT_IMAGE}" -avbtool add_hash_footer --image "${TEST_BOOT_IMAGE}" --partition_name boot --partition_size $((20 << 20)) - -mkbootimg \ - --header_version 4 \ - --ramdisk "${TEST_RAMDISK}" \ - --output "${TEST_INIT_BOOT_IMAGE}" -mkbootimg \ - --header_version 4 \ - --pagesize 4096 \ - --dtb "${TEST_DTB}" \ - --vendor_ramdisk "${TEST_VENDOR_RAMDISK}" \ - --vendor_boot "${TEST_VENDOR_BOOT_IMAGE}" - -readonly RETROFITTED_IMAGE="${TEMP_DIR}/retrofitted_boot.img" -readonly RETROFITTED_IMAGE_DIR="${TEMP_DIR}/retrofitted_boot.img.unpack" -readonly BOOT_SIGNATURE_SIZE=$(( 16 << 10 )) - - -# -# Begin test. -# -echo >&2 "TEST: retrofit to boot v4" - -retrofit_gki.sh \ - --boot "${TEST_BOOT_IMAGE}" \ - --init_boot "${TEST_INIT_BOOT_IMAGE}" \ - --version 4 \ - --output "${RETROFITTED_IMAGE}" - -rm -rf "${RETROFITTED_IMAGE_DIR}" -unpack_bootimg --boot_img "${RETROFITTED_IMAGE}" --out "${RETROFITTED_IMAGE_DIR}" > /dev/null -tail -c "${BOOT_SIGNATURE_SIZE}" "${RETROFITTED_IMAGE}" > "${RETROFITTED_IMAGE_DIR}/boot_signature" - -cmp -s "${TEST_KERNEL}" "${RETROFITTED_IMAGE_DIR}/kernel" || - die "unexpected diff: kernel" -cmp -s "${TEST_RAMDISK}" "${RETROFITTED_IMAGE_DIR}/ramdisk" || - die "unexpected diff: ramdisk" -cmp -s "${TEST_BOOT_SIGNATURE}" "${RETROFITTED_IMAGE_DIR}/boot_signature" || - die "unexpected diff: boot signature" - - -echo >&2 "TEST: retrofit to boot v3" - -retrofit_gki.sh \ - --boot "${TEST_BOOT_IMAGE}" \ - --init_boot "${TEST_INIT_BOOT_IMAGE}" \ - --version 3 \ - --output "${RETROFITTED_IMAGE}" - -rm -rf "${RETROFITTED_IMAGE_DIR}" -unpack_bootimg --boot_img "${RETROFITTED_IMAGE}" --out "${RETROFITTED_IMAGE_DIR}" > /dev/null -tail -c "${BOOT_SIGNATURE_SIZE}" "${RETROFITTED_IMAGE}" > "${RETROFITTED_IMAGE_DIR}/boot_signature" - -cmp -s "${TEST_KERNEL}" "${RETROFITTED_IMAGE_DIR}/kernel" || - die "unexpected diff: kernel" -cmp -s "${TEST_RAMDISK}" "${RETROFITTED_IMAGE_DIR}/ramdisk" || - die "unexpected diff: ramdisk" -cmp -s "${TEST_BOOT_SIGNATURE}" "${RETROFITTED_IMAGE_DIR}/boot_signature" || - die "unexpected diff: boot signature" - - -echo >&2 "TEST: retrofit to boot v2" - -retrofit_gki.sh \ - --boot "${TEST_BOOT_IMAGE}" \ - --init_boot "${TEST_INIT_BOOT_IMAGE}" \ - --vendor_boot "${TEST_VENDOR_BOOT_IMAGE}" \ - --version 2 \ - --output "${RETROFITTED_IMAGE}" - -rm -rf "${RETROFITTED_IMAGE_DIR}" -unpack_bootimg --boot_img "${RETROFITTED_IMAGE}" --out "${RETROFITTED_IMAGE_DIR}" > /dev/null -tail -c "${BOOT_SIGNATURE_SIZE}" "${RETROFITTED_IMAGE}" > "${RETROFITTED_IMAGE_DIR}/boot_signature" - -cmp -s "${TEST_DTB}" "${RETROFITTED_IMAGE_DIR}/dtb" || - die "unexpected diff: dtb" -cmp -s "${TEST_KERNEL}" "${RETROFITTED_IMAGE_DIR}/kernel" || - die "unexpected diff: kernel" -cmp -s "${TEST_V2_RETROFITTED_RAMDISK}" "${RETROFITTED_IMAGE_DIR}/ramdisk" || - die "unexpected diff: ramdisk" -cmp -s "${TEST_BOOT_SIGNATURE}" "${RETROFITTED_IMAGE_DIR}/boot_signature" || - die "unexpected diff: boot signature" diff --git a/unpack_bootimg.py b/unpack_bootimg.py index 462190f..a3f1a50 100755 --- a/unpack_bootimg.py +++ b/unpack_bootimg.py @@ -404,9 +404,6 @@ def unpack_vendor_boot_image(boot_img, output_dir): ramdisk_offset_base = page_size * num_boot_header_pages image_info_list = [] - image_info_list.append( - (ramdisk_offset_base, info.vendor_ramdisk_size, 'vendor_ramdisk')) - if info.header_version > 3: info.vendor_ramdisk_table_size = unpack('I', boot_img.read(4))[0] vendor_ramdisk_table_entry_num = unpack('I', boot_img.read(4))[0] @@ -449,6 +446,9 @@ def unpack_vendor_boot_image(boot_img, output_dir): + num_vendor_ramdisk_table_pages) image_info_list.append((bootconfig_offset, info.vendor_bootconfig_size, 'bootconfig')) + else: + image_info_list.append( + (ramdisk_offset_base, info.vendor_ramdisk_size, 'vendor_ramdisk')) dtb_offset = page_size * (num_boot_header_pages + num_boot_ramdisk_pages ) # header + vendor_ramdisk |