diff --git a/core/Makefile b/core/Makefile index a283380281..c45fc153a7 100644 --- a/core/Makefile +++ b/core/Makefile @@ -901,9 +901,11 @@ INTERNAL_BOOTIMAGE_ARGS := \ INTERNAL_INIT_BOOT_IMAGE_ARGS := +INTERNAL_BOOT_HAS_RAMDISK := ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) ifneq ($(BUILDING_INIT_BOOT_IMAGE),true) INTERNAL_BOOTIMAGE_ARGS += --ramdisk $(INSTALLED_RAMDISK_TARGET) + INTERNAL_BOOT_HAS_RAMDISK := true else INTERNAL_INIT_BOOT_IMAGE_ARGS += --ramdisk $(INSTALLED_RAMDISK_TARGET) endif @@ -952,21 +954,48 @@ INTERNAL_MKBOOTIMG_VERSION_ARGS := \ --os_version $(PLATFORM_VERSION_LAST_STABLE) \ --os_patch_level $(PLATFORM_SECURITY_PATCH) -ifdef BOARD_GKI_SIGNING_KEY_PATH -ifndef BOARD_GKI_SIGNING_ALGORITHM -$(error BOARD_GKI_SIGNING_ALGORITHM should be defined with BOARD_GKI_SIGNING_KEY_PATH) -endif -INTERNAL_MKBOOTIMG_GKI_SINGING_ARGS := \ - --gki_signing_key $(BOARD_GKI_SIGNING_KEY_PATH) \ - --gki_signing_algorithm $(BOARD_GKI_SIGNING_ALGORITHM) \ - --gki_signing_avbtool_path $(AVBTOOL) -endif +# $(1): image target to certify +# $(2): out certificate target +# $(3): image name +# $(4): additional AVB arguments +define generate_generic_boot_image_certificate + rm -rf "$(2)" + mkdir -p "$(dir $(2))" + $(GENERATE_GKI_CERTIFICATE) $(INTERNAL_GKI_CERTIFICATE_ARGS) \ + --additional_avb_args "$(4)" \ + --name "$(3)" --output "$(2)" "$(1)" +endef -# Using double quote to pass BOARD_GKI_SIGNING_SIGNATURE_ARGS as a single string -# to MKBOOTIMG, although it may contain multiple args. -ifdef BOARD_GKI_SIGNING_SIGNATURE_ARGS -INTERNAL_MKBOOTIMG_GKI_SINGING_ARGS += \ - --gki_signing_signature_args "$(BOARD_GKI_SIGNING_SIGNATURE_ARGS)" +INTERNAL_GKI_CERTIFICATE_ARGS := +INTERNAL_GKI_CERTIFICATE_DEPS := +INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE := +ifdef BOARD_GKI_SIGNING_KEY_PATH + ifndef BOARD_GKI_SIGNING_ALGORITHM + $(error BOARD_GKI_SIGNING_ALGORITHM should be defined with BOARD_GKI_SIGNING_KEY_PATH) + endif + + INTERNAL_GKI_CERTIFICATE_ARGS := \ + --key "$(BOARD_GKI_SIGNING_KEY_PATH)" \ + --algorithm "$(BOARD_GKI_SIGNING_ALGORITHM)" \ + --avbtool "$(AVBTOOL)" + + # Quote and pass BOARD_GKI_SIGNING_SIGNATURE_ARGS as a single string argument. + ifdef BOARD_GKI_SIGNING_SIGNATURE_ARGS + INTERNAL_GKI_CERTIFICATE_ARGS += --additional_avb_args "$(BOARD_GKI_SIGNING_SIGNATURE_ARGS)" + endif + + INTERNAL_GKI_CERTIFICATE_DEPS := \ + $(GENERATE_GKI_CERTIFICATE) \ + $(BOARD_GKI_SIGNING_KEY_PATH) \ + $(AVBTOOL) + + ifdef INSTALLED_RAMDISK_TARGET + INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE := \ + $(call intermediates-dir-for,PACKAGING,generic_ramdisk)/boot_signature + + $(INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE): $(INSTALLED_RAMDISK_TARGET) $(INTERNAL_GKI_CERTIFICATE_DEPS) + $(call generate_generic_boot_image_certificate,$(INSTALLED_RAMDISK_TARGET),$@,generic_ramdisk,$(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS)) + endif endif # Define these only if we are building boot @@ -983,8 +1012,15 @@ ifeq (true,$(BOARD_AVB_ENABLE)) # $1: boot image target define build_boot_board_avb_enabled - $(MKBOOTIMG) --kernel $(call bootimage-to-kernel,$(1)) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) \ - $(INTERNAL_MKBOOTIMG_GKI_SINGING_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1) + $(eval kernel := $(call bootimage-to-kernel,$(1))) + $(if $(BOARD_GKI_SIGNING_KEY_PATH), \ + $(eval kernel_signature := $(call intermediates-dir-for,PACKAGING,generic_kernel)/$(notdir $(kernel)).boot_signature) \ + $(call generate_generic_boot_image_certificate,$(kernel),$(kernel_signature),generic_kernel,$(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)) $(newline) \ + $(if $(INTERNAL_BOOT_HAS_RAMDISK), \ + cat $(INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE) >> $(kernel_signature) $(newline))) + $(MKBOOTIMG) --kernel $(kernel) $(INTERNAL_BOOTIMAGE_ARGS) \ + $(if $(BOARD_GKI_SIGNING_KEY_PATH),--boot_signature "$(kernel_signature)",$(INTERNAL_MKBOOTIMG_VERSION_ARGS)) \ + $(BOARD_MKBOOTIMG_ARGS) --output $(1) $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(call get-bootimage-partition-size,$(1),boot))) $(AVBTOOL) add_hash_footer \ --image $(1) \ @@ -993,12 +1029,15 @@ define build_boot_board_avb_enabled $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS) endef -$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(AVBTOOL) $(INTERNAL_BOOTIMAGE_FILES) $(BOARD_AVB_BOOT_KEY_PATH) $(BOARD_GKI_SIGNING_KEY_PATH) +ifdef INTERNAL_BOOT_HAS_RAMDISK +$(INSTALLED_BOOTIMAGE_TARGET): $(INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE) +endif +$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(AVBTOOL) $(INTERNAL_BOOTIMAGE_FILES) $(BOARD_AVB_BOOT_KEY_PATH) $(INTERNAL_GKI_CERTIFICATE_DEPS) $(call pretty,"Target boot image: $@") $(call build_boot_board_avb_enabled,$@) .PHONY: bootimage-nodeps -bootimage-nodeps: $(MKBOOTIMG) $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH) $(BOARD_GKI_SIGNING_KEY_PATH) +bootimage-nodeps: $(MKBOOTIMG) $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH) $(INTERNAL_GKI_CERTIFICATE_DEPS) @echo "make $@: ignoring dependencies" $(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),$(call build_boot_board_avb_enabled,$(b))) @@ -1097,9 +1136,12 @@ ifdef BOARD_KERNEL_PAGESIZE endif ifeq ($(BOARD_AVB_ENABLE),true) +$(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE) $(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_INIT_BOOT_KEY_PATH) $(call pretty,"Target init_boot image: $@") - $(MKBOOTIMG) $(INTERNAL_INIT_BOOT_IMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_INIT_ARGS) --output $@ + $(MKBOOTIMG) $(INTERNAL_INIT_BOOT_IMAGE_ARGS) \ + $(if $(BOARD_GKI_SIGNING_KEY_PATH),--boot_signature "$(INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE)",$(INTERNAL_MKBOOTIMG_VERSION_ARGS)) \ + $(BOARD_MKBOOTIMG_INIT_ARGS) --output "$@" $(call assert-max-image-size,$@,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)) $(AVBTOOL) add_hash_footer \ --image $@ \ @@ -3774,6 +3816,13 @@ BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS += \ --prop com.android.build.pvmfw.security_patch:$(PVMFW_SECURITY_PATCH) endif +# For upgrading devices without a init_boot partition, the init_boot footer args +# should fallback to boot partition footer. +ifndef INSTALLED_INIT_BOOT_IMAGE_TARGET +BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS += \ + $(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS) +endif + BOOT_FOOTER_ARGS := BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS INIT_BOOT_FOOTER_ARGS := BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS VENDOR_BOOT_FOOTER_ARGS := BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS @@ -4485,6 +4534,7 @@ INTERNAL_OTATOOLS_MODULES := \ fec \ fsck.f2fs \ fs_config \ + generate_gki_certificate \ generate_verity_key \ host_init_verifier \ img2simg \ diff --git a/core/config.mk b/core/config.mk index 3c7c5ced98..6a50738ced 100644 --- a/core/config.mk +++ b/core/config.mk @@ -593,6 +593,7 @@ VTSC := $(HOST_OUT_EXECUTABLES)/vtsc$(HOST_EXECUTABLE_SUFFIX) MKBOOTFS := $(HOST_OUT_EXECUTABLES)/mkbootfs$(HOST_EXECUTABLE_SUFFIX) MINIGZIP := $(HOST_OUT_EXECUTABLES)/minigzip$(HOST_EXECUTABLE_SUFFIX) LZ4 := $(HOST_OUT_EXECUTABLES)/lz4$(HOST_EXECUTABLE_SUFFIX) +GENERATE_GKI_CERTIFICATE := $(HOST_OUT_EXECUTABLES)/generate_gki_certificate$(HOST_EXECUTABLE_SUFFIX) ifeq (,$(strip $(BOARD_CUSTOM_MKBOOTIMG))) MKBOOTIMG := $(HOST_OUT_EXECUTABLES)/mkbootimg$(HOST_EXECUTABLE_SUFFIX) else diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp index f3123b2a12..7b2c290ce5 100644 --- a/tools/releasetools/Android.bp +++ b/tools/releasetools/Android.bp @@ -162,6 +162,7 @@ python_defaults { required: [ "brillo_update_payload", "checkvintf", + "generate_gki_certificate", "minigzip", "lz4", "toybox", @@ -236,6 +237,7 @@ python_library_host { "boot_signer", "brotli", "bsdiff", + "generate_gki_certificate", "imgdiff", "minigzip", "lz4", @@ -301,6 +303,7 @@ python_defaults { "brotli", "bsdiff", "deapexer", + "generate_gki_certificate", "imgdiff", "minigzip", "lz4", diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py index 30dcf5b1de..e5c68bc0d1 100644 --- a/tools/releasetools/common.py +++ b/tools/releasetools/common.py @@ -1396,34 +1396,52 @@ def GetAvbChainedPartitionArg(partition, info_dict, key=None): return "{}:{}:{}".format(partition, rollback_index_location, pubkey_path) -def AppendGkiSigningArgs(cmd): - """Append GKI signing arguments for mkbootimg.""" - # e.g., --gki_signing_key path/to/signing_key - # --gki_signing_algorithm SHA256_RSA4096" +def _HasGkiCertificationArgs(): + return ("gki_signing_key_path" in OPTIONS.info_dict and + "gki_signing_algorithm" in OPTIONS.info_dict) + +def _GenerateGkiCertificate(image, image_name, partition_name): key_path = OPTIONS.info_dict.get("gki_signing_key_path") - # It's fine that a non-GKI boot.img has no gki_signing_key_path. - if not key_path: - return + algorithm = OPTIONS.info_dict.get("gki_signing_algorithm") if not os.path.exists(key_path) and OPTIONS.search_path: new_key_path = os.path.join(OPTIONS.search_path, key_path) if os.path.exists(new_key_path): key_path = new_key_path - # Checks key_path exists, before appending --gki_signing_* args. + # Checks key_path exists, before processing --gki_signing_* args. if not os.path.exists(key_path): raise ExternalError( 'gki_signing_key_path: "{}" not found'.format(key_path)) - algorithm = OPTIONS.info_dict.get("gki_signing_algorithm") - if key_path and algorithm: - cmd.extend(["--gki_signing_key", key_path, - "--gki_signing_algorithm", algorithm]) + output_certificate = tempfile.NamedTemporaryFile() + cmd = [ + "generate_gki_certificate", + "--name", image_name, + "--algorithm", algorithm, + "--key", key_path, + "--output", output_certificate.name, + image, + ] - signature_args = OPTIONS.info_dict.get("gki_signing_signature_args") - if signature_args: - cmd.extend(["--gki_signing_signature_args", signature_args]) + signature_args = OPTIONS.info_dict.get("gki_signing_signature_args", "") + signature_args = signature_args.strip() + if signature_args: + cmd.extend(["--additional_avb_args", signature_args]) + + args = OPTIONS.info_dict.get( + "avb_" + partition_name + "_add_hash_footer_args", "") + args = args.strip() + if args: + cmd.extend(["--additional_avb_args", args]) + + RunAndCheckOutput(cmd) + + output_certificate.seek(os.SEEK_SET, 0) + data = output_certificate.read() + output_certificate.close() + return data def BuildVBMeta(image_path, partitions, name, needed_partitions): @@ -1549,6 +1567,8 @@ def _BuildBootableImage(image_name, sourcedir, fs_config_file, info_dict=None, if kernel and not os.access(os.path.join(sourcedir, kernel), os.F_OK): return None + kernel_path = os.path.join(sourcedir, kernel) if kernel else None + if has_ramdisk and not os.access(os.path.join(sourcedir, "RAMDISK"), os.F_OK): return None @@ -1563,8 +1583,8 @@ def _BuildBootableImage(image_name, sourcedir, fs_config_file, info_dict=None, mkbootimg = os.getenv('MKBOOTIMG') or "mkbootimg" cmd = [mkbootimg] - if kernel: - cmd += ["--kernel", os.path.join(sourcedir, kernel)] + if kernel_path is not None: + cmd.extend(["--kernel", kernel_path]) fn = os.path.join(sourcedir, "second") if os.access(fn, os.F_OK): @@ -1604,15 +1624,31 @@ def _BuildBootableImage(image_name, sourcedir, fs_config_file, info_dict=None, if args and args.strip(): cmd.extend(shlex.split(args)) - args = info_dict.get("mkbootimg_version_args") - if args and args.strip(): - cmd.extend(shlex.split(args)) + boot_signature = None + if _HasGkiCertificationArgs(): + # Certify GKI images. + boot_signature_bytes = b'' + if kernel_path is not None: + boot_signature_bytes += _GenerateGkiCertificate( + kernel_path, "generic_kernel", "boot") + if has_ramdisk: + boot_signature_bytes += _GenerateGkiCertificate( + ramdisk_img.name, "generic_ramdisk", "init_boot") + + if len(boot_signature_bytes) > 0: + boot_signature = tempfile.NamedTemporaryFile() + boot_signature.write(boot_signature_bytes) + boot_signature.flush() + cmd.extend(["--boot_signature", boot_signature.name]) + else: + # Certified GKI boot/init_boot image mustn't set 'mkbootimg_version_args'. + args = info_dict.get("mkbootimg_version_args") + if args and args.strip(): + cmd.extend(shlex.split(args)) if has_ramdisk: cmd.extend(["--ramdisk", ramdisk_img.name]) - AppendGkiSigningArgs(cmd) - img_unsigned = None if info_dict.get("vboot"): img_unsigned = tempfile.NamedTemporaryFile() @@ -1690,6 +1726,9 @@ def _BuildBootableImage(image_name, sourcedir, fs_config_file, info_dict=None, ramdisk_img.close() img.close() + if boot_signature is not None: + boot_signature.close() + return data diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py index e42d41791c..7dd365fc39 100644 --- a/tools/releasetools/test_common.py +++ b/tools/releasetools/test_common.py @@ -1631,66 +1631,7 @@ class CommonUtilsTest(test_utils.ReleaseToolsTestCase): self.assertEqual('3', chained_partition_args[1]) self.assertTrue(os.path.exists(chained_partition_args[2])) - @test_utils.SkipIfExternalToolsUnavailable() - def test_AppendGkiSigningArgs_NoSigningKeyPath(self): - # A non-GKI boot.img has no gki_signing_key_path. - common.OPTIONS.info_dict = { - # 'gki_signing_key_path': pubkey, - 'gki_signing_algorithm': 'SHA256_RSA4096', - 'gki_signing_signature_args': '--prop foo:bar', - } - - # Tests no --gki_signing_* args are appended if there is no - # gki_signing_key_path. - cmd = ['mkbootimg', '--header_version', '4'] - expected_cmd = ['mkbootimg', '--header_version', '4'] - common.AppendGkiSigningArgs(cmd) - self.assertEqual(cmd, expected_cmd) - - def test_AppendGkiSigningArgs_NoSigningAlgorithm(self): - pubkey = os.path.join(self.testdata_dir, 'testkey_gki.pem') - with open(pubkey, 'wb') as f: - f.write(b'\x00' * 100) - self.assertTrue(os.path.exists(pubkey)) - - # Tests no --gki_signing_* args are appended if there is no - # gki_signing_algorithm. - common.OPTIONS.info_dict = { - 'gki_signing_key_path': pubkey, - # 'gki_signing_algorithm': 'SHA256_RSA4096', - 'gki_signing_signature_args': '--prop foo:bar', - } - - cmd = ['mkbootimg', '--header_version', '4'] - expected_cmd = ['mkbootimg', '--header_version', '4'] - common.AppendGkiSigningArgs(cmd) - self.assertEqual(cmd, expected_cmd) - - @test_utils.SkipIfExternalToolsUnavailable() - def test_AppendGkiSigningArgs(self): - pubkey = os.path.join(self.testdata_dir, 'testkey_gki.pem') - with open(pubkey, 'wb') as f: - f.write(b'\x00' * 100) - self.assertTrue(os.path.exists(pubkey)) - - common.OPTIONS.info_dict = { - 'gki_signing_key_path': pubkey, - 'gki_signing_algorithm': 'SHA256_RSA4096', - 'gki_signing_signature_args': '--prop foo:bar', - } - cmd = ['mkbootimg', '--header_version', '4'] - common.AppendGkiSigningArgs(cmd) - - expected_cmd = [ - 'mkbootimg', '--header_version', '4', - '--gki_signing_key', pubkey, - '--gki_signing_algorithm', 'SHA256_RSA4096', - '--gki_signing_signature_args', '--prop foo:bar' - ] - self.assertEqual(cmd, expected_cmd) - - @test_utils.SkipIfExternalToolsUnavailable() - def test_AppendGkiSigningArgs_KeyPathNotFound(self): + def test_GenerateGkiCertificate_KeyPathNotFound(self): pubkey = os.path.join(self.testdata_dir, 'no_testkey_gki.pem') self.assertFalse(os.path.exists(pubkey)) @@ -1699,41 +1640,11 @@ class CommonUtilsTest(test_utils.ReleaseToolsTestCase): 'gki_signing_algorithm': 'SHA256_RSA4096', 'gki_signing_signature_args': '--prop foo:bar', } - cmd = ['mkbootimg', '--header_version', '4'] - self.assertRaises(common.ExternalError, common.AppendGkiSigningArgs, cmd) + test_file = tempfile.NamedTemporaryFile() + self.assertRaises(common.ExternalError, common._GenerateGkiCertificate, + test_file.name, 'generic_kernel', 'boot') - @test_utils.SkipIfExternalToolsUnavailable() - def test_AppendGkiSigningArgs_SearchKeyPath(self): - pubkey = 'testkey_gki.pem' - self.assertFalse(os.path.exists(pubkey)) - - # Tests it should replace the pubkey with an existed key under - # OPTIONS.search_path, i.e., os.path.join(OPTIONS.search_path, pubkey). - search_path_dir = common.MakeTempDir() - search_pubkey = os.path.join(search_path_dir, pubkey) - with open(search_pubkey, 'wb') as f: - f.write(b'\x00' * 100) - self.assertTrue(os.path.exists(search_pubkey)) - - common.OPTIONS.search_path = search_path_dir - common.OPTIONS.info_dict = { - 'gki_signing_key_path': pubkey, - 'gki_signing_algorithm': 'SHA256_RSA4096', - 'gki_signing_signature_args': '--prop foo:bar', - } - cmd = ['mkbootimg', '--header_version', '4'] - common.AppendGkiSigningArgs(cmd) - - expected_cmd = [ - 'mkbootimg', '--header_version', '4', - '--gki_signing_key', search_pubkey, - '--gki_signing_algorithm', 'SHA256_RSA4096', - '--gki_signing_signature_args', '--prop foo:bar' - ] - self.assertEqual(cmd, expected_cmd) - - @test_utils.SkipIfExternalToolsUnavailable() - def test_AppendGkiSigningArgs_SearchKeyPathNotFound(self): + def test_GenerateGkiCertificate_SearchKeyPathNotFound(self): pubkey = 'no_testkey_gki.pem' self.assertFalse(os.path.exists(pubkey)) @@ -1749,9 +1660,9 @@ class CommonUtilsTest(test_utils.ReleaseToolsTestCase): 'gki_signing_algorithm': 'SHA256_RSA4096', 'gki_signing_signature_args': '--prop foo:bar', } - cmd = ['mkbootimg', '--header_version', '4'] - self.assertRaises(common.ExternalError, common.AppendGkiSigningArgs, cmd) - + test_file = tempfile.NamedTemporaryFile() + self.assertRaises(common.ExternalError, common._GenerateGkiCertificate, + test_file.name, 'generic_kernel', 'boot') class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase): """Checks the format of install-recovery.sh.