From c99819311c0124dc6f6a0f0f6db74cae7cf570fb Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 16 Sep 2019 12:10:43 -0700 Subject: [PATCH] Also install verity_key to ramdisk for non-system-as-root target. The commit in d14b895665f9fb122f93edb16655fd3a49510032 (https://android-review.googlesource.com/c/platform/build/+/728287) changed partition layout, to always build the root dir into system.img, even for devices not using system-as-root (i.e. the ones with separate boot ramdisk). With the new layout, there will be two root dirs for non-system-as-root targets during the boot. If such a device uses Verified Boot 1.0, /verity_key needs to be available in both roots, to establish the chain of trust. - bootloader uses the baked-in key to verify boot.img; it then loads the ramdisk from the verified boot.img - First stage init uses /verity_key (in ramdisk) to verify and mount system.img at /system, then chroot's to it - Second stage init uses /verity_key (in system.img) to verify and mount other partitions This CL adds rules to additionally install verity_key into ramdisk for such targets. Bug: 139770257 Test: Set up a target to use non-system-as-root (BOARD_BUILD_SYSTEM_ROOT_IMAGE != true). `m dist`. Test: Check that both ROOT/verity_key and BOOT/RAMDISK/verity_key exist in the built target_files.zip. Test: Run validate_target_files to validate the above target_files.zip. $ validate_target_files \ --verity_key_mincrypt /path/to/verity_key \ target_files.zip Test: Run sign_target_files_apks to sign the above target. Re-run validate_target_files on the signed target_files.zip. Test: python -m unittest test_validate_target_files Change-Id: Ibe7e771c8c376429add85851ac86055564765d3c --- target/product/security/Android.mk | 22 +++++- tools/releasetools/sign_target_files_apks.py | 13 ++-- .../test_validate_target_files.py | 70 ++++++++++++++++++- tools/releasetools/validate_target_files.py | 23 ++++-- 4 files changed, 113 insertions(+), 15 deletions(-) diff --git a/target/product/security/Android.mk b/target/product/security/Android.mk index a0b2d6dc65..3631cfdb27 100644 --- a/target/product/security/Android.mk +++ b/target/product/security/Android.mk @@ -1,7 +1,7 @@ LOCAL_PATH:= $(call my-dir) ####################################### -# verity_key +# verity_key (installed to /, i.e. part of system.img) include $(CLEAR_VARS) LOCAL_MODULE := verity_key @@ -9,8 +9,28 @@ LOCAL_SRC_FILES := $(LOCAL_MODULE) LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) +# For devices using a separate ramdisk, we need a copy there to establish the chain of trust. +ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) +LOCAL_REQUIRED_MODULES := verity_key_ramdisk +endif + include $(BUILD_PREBUILT) +####################################### +# verity_key (installed to ramdisk) +# +# Enabling the target when using system-as-root would cause build failure, as TARGET_RAMDISK_OUT +# points to the same location as TARGET_ROOT_OUT. +ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) + include $(CLEAR_VARS) + LOCAL_MODULE := verity_key_ramdisk + LOCAL_MODULE_CLASS := ETC + LOCAL_SRC_FILES := verity_key + LOCAL_MODULE_STEM := verity_key + LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT) + include $(BUILD_PREBUILT) +endif + ####################################### # adb key, if configured via PRODUCT_ADB_KEYS ifdef PRODUCT_ADB_KEYS diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py index 3119afa65a..1f41431b78 100755 --- a/tools/releasetools/sign_target_files_apks.py +++ b/tools/releasetools/sign_target_files_apks.py @@ -602,11 +602,16 @@ def ProcessTargetFiles(input_tf_zip, output_tf_zip, misc_info, ReplaceVerityPrivateKey(misc_info, OPTIONS.replace_verity_private_key[1]) if OPTIONS.replace_verity_public_key: - dest = "ROOT/verity_key" if system_root_image else "BOOT/RAMDISK/verity_key" - # We are replacing the one in boot image only, since the one under - # recovery won't ever be needed. + # Replace the one in root dir in system.img. ReplaceVerityPublicKey( - output_tf_zip, dest, OPTIONS.replace_verity_public_key[1]) + output_tf_zip, 'ROOT/verity_key', OPTIONS.replace_verity_public_key[1]) + + if not system_root_image: + # Additionally replace the copy in ramdisk if not using system-as-root. + ReplaceVerityPublicKey( + output_tf_zip, + 'BOOT/RAMDISK/verity_key', + OPTIONS.replace_verity_public_key[1]) # Replace the keyid string in BOOT/cmdline. if OPTIONS.replace_verity_keyid: diff --git a/tools/releasetools/test_validate_target_files.py b/tools/releasetools/test_validate_target_files.py index 0f0d773c6a..9c816eb416 100644 --- a/tools/releasetools/test_validate_target_files.py +++ b/tools/releasetools/test_validate_target_files.py @@ -143,20 +143,51 @@ class ValidateTargetFilesTest(test_utils.ReleaseToolsTestCase): verity_image_builder.Build(output_file) @test_utils.SkipIfExternalToolsUnavailable() - def test_ValidateVerifiedBootImages_systemImage(self): + def test_ValidateVerifiedBootImages_systemRootImage(self): input_tmp = common.MakeTempDir() os.mkdir(os.path.join(input_tmp, 'IMAGES')) system_image = os.path.join(input_tmp, 'IMAGES', 'system.img') self._generate_system_image(system_image) # Pack the verity key. - verity_key_mincrypt = os.path.join( - input_tmp, 'BOOT', 'RAMDISK', 'verity_key') + verity_key_mincrypt = os.path.join(input_tmp, 'ROOT', 'verity_key') os.makedirs(os.path.dirname(verity_key_mincrypt)) shutil.copyfile( os.path.join(self.testdata_dir, 'testkey_mincrypt'), verity_key_mincrypt) + info_dict = { + 'system_root_image' : 'true', + 'verity' : 'true', + } + options = { + 'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'), + 'verity_key_mincrypt' : verity_key_mincrypt, + } + ValidateVerifiedBootImages(input_tmp, info_dict, options) + + @test_utils.SkipIfExternalToolsUnavailable() + def test_ValidateVerifiedBootImages_nonSystemRootImage(self): + input_tmp = common.MakeTempDir() + os.mkdir(os.path.join(input_tmp, 'IMAGES')) + system_image = os.path.join(input_tmp, 'IMAGES', 'system.img') + self._generate_system_image(system_image) + + # Pack the verity key into the root dir in system.img. + verity_key_mincrypt = os.path.join(input_tmp, 'ROOT', 'verity_key') + os.makedirs(os.path.dirname(verity_key_mincrypt)) + shutil.copyfile( + os.path.join(self.testdata_dir, 'testkey_mincrypt'), + verity_key_mincrypt) + + # And a copy in ramdisk. + verity_key_ramdisk = os.path.join( + input_tmp, 'BOOT', 'RAMDISK', 'verity_key') + os.makedirs(os.path.dirname(verity_key_ramdisk)) + shutil.copyfile( + os.path.join(self.testdata_dir, 'testkey_mincrypt'), + verity_key_ramdisk) + info_dict = { 'verity' : 'true', } @@ -166,6 +197,39 @@ class ValidateTargetFilesTest(test_utils.ReleaseToolsTestCase): } ValidateVerifiedBootImages(input_tmp, info_dict, options) + @test_utils.SkipIfExternalToolsUnavailable() + def test_ValidateVerifiedBootImages_nonSystemRootImage_mismatchingKeys(self): + input_tmp = common.MakeTempDir() + os.mkdir(os.path.join(input_tmp, 'IMAGES')) + system_image = os.path.join(input_tmp, 'IMAGES', 'system.img') + self._generate_system_image(system_image) + + # Pack the verity key into the root dir in system.img. + verity_key_mincrypt = os.path.join(input_tmp, 'ROOT', 'verity_key') + os.makedirs(os.path.dirname(verity_key_mincrypt)) + shutil.copyfile( + os.path.join(self.testdata_dir, 'testkey_mincrypt'), + verity_key_mincrypt) + + # And an invalid copy in ramdisk. + verity_key_ramdisk = os.path.join( + input_tmp, 'BOOT', 'RAMDISK', 'verity_key') + os.makedirs(os.path.dirname(verity_key_ramdisk)) + shutil.copyfile( + os.path.join(self.testdata_dir, 'verity_mincrypt'), + verity_key_ramdisk) + + info_dict = { + 'verity' : 'true', + } + options = { + 'verity_key' : os.path.join(self.testdata_dir, 'testkey.x509.pem'), + 'verity_key_mincrypt' : verity_key_mincrypt, + } + self.assertRaises( + AssertionError, ValidateVerifiedBootImages, input_tmp, info_dict, + options) + @test_utils.SkipIfExternalToolsUnavailable() def test_ValidateFileConsistency_incompleteRange(self): input_tmp = common.MakeTempDir() diff --git a/tools/releasetools/validate_target_files.py b/tools/releasetools/validate_target_files.py index d189499dc3..c299a488ae 100755 --- a/tools/releasetools/validate_target_files.py +++ b/tools/releasetools/validate_target_files.py @@ -276,15 +276,12 @@ def ValidateVerifiedBootImages(input_tmp, info_dict, options): # Verify verity signed system images in Verified Boot 1.0. Note that not using # 'elif' here, since 'boot_signer' and 'verity' are not bundled in VB 1.0. if info_dict.get('verity') == 'true': - # First verify that the verity key that's built into the root image (as - # /verity_key) matches the one given via command line, if any. - if info_dict.get("system_root_image") == "true": - verity_key_mincrypt = os.path.join(input_tmp, 'ROOT', 'verity_key') - else: - verity_key_mincrypt = os.path.join( - input_tmp, 'BOOT', 'RAMDISK', 'verity_key') + # First verify that the verity key is built into the root image (regardless + # of system-as-root). + verity_key_mincrypt = os.path.join(input_tmp, 'ROOT', 'verity_key') assert os.path.exists(verity_key_mincrypt), 'Missing verity_key' + # Verify /verity_key matches the one given via command line, if any. if options['verity_key_mincrypt'] is None: logging.warn( 'Skipped checking the content of /verity_key, as the key file not ' @@ -295,6 +292,18 @@ def ValidateVerifiedBootImages(input_tmp, info_dict, options): "Mismatching mincrypt verity key files" logging.info('Verified the content of /verity_key') + # For devices with a separate ramdisk (i.e. non-system-as-root), there must + # be a copy in ramdisk. + if info_dict.get("system_root_image") != "true": + verity_key_ramdisk = os.path.join( + input_tmp, 'BOOT', 'RAMDISK', 'verity_key') + assert os.path.exists(verity_key_ramdisk), 'Missing verity_key in ramdisk' + + assert filecmp.cmp( + verity_key_mincrypt, verity_key_ramdisk, shallow=False), \ + 'Mismatching verity_key files in root and ramdisk' + logging.info('Verified the content of /verity_key in ramdisk') + # Then verify the verity signed system/vendor/product images, against the # verity pubkey in mincrypt format. for image in ('system.img', 'vendor.img', 'product.img'):