def test_GetAvbChainedPartitionArg_withPrivateKey(self): key = os.path.join(self.testdata_dir, 'testkey.key') info_dict = { 'avb_avbtool': 'avbtool', 'avb_product_key_path': key, 'avb_product_rollback_index_location': 2, } args = common.GetAvbChainedPartitionArg('product', info_dict).split(':') self.assertEqual(3, len(args)) self.assertEqual('product', args[0]) self.assertEqual('2', args[1]) self.assertTrue(os.path.exists(args[2]))
def test_GetAvbChainedPartitionArg(self): pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem') info_dict = { 'avb_avbtool': 'avbtool', 'avb_system_key_path': pubkey, 'avb_system_rollback_index_location': 2, } args = common.GetAvbChainedPartitionArg('system', info_dict).split(':') self.assertEqual(3, len(args)) self.assertEqual('system', args[0]) self.assertEqual('2', args[1]) self.assertTrue(os.path.exists(args[2]))
def AppendVBMetaArgsForPartition(cmd, partition, image): """Appends the VBMeta arguments for partition. It sets up the VBMeta argument by including the partition descriptor from the given 'image', or by configuring the partition as a chained partition. Args: cmd: A list of command args that will be used to generate the vbmeta image. The argument for the partition will be appended to the list. partition: The name of the partition (e.g. "system"). image: The path to the partition image. """ # Check if chain partition is used. key_path = OPTIONS.info_dict.get("avb_" + partition + "_key_path") if key_path: chained_partition_arg = common.GetAvbChainedPartitionArg( partition, OPTIONS.info_dict) cmd.extend(["--chain_partition", chained_partition_arg]) else: cmd.extend(["--include_descriptors_from_image", image])
def ValidateVerifiedBootImages(input_tmp, info_dict, options): """Validates the Verified Boot related images. For Verified Boot 1.0, it verifies the signatures of the bootable images (boot/recovery etc), as well as the dm-verity metadata in system images (system/vendor/product). For Verified Boot 2.0, it calls avbtool to verify vbmeta.img, which in turn verifies all the descriptors listed in vbmeta. Args: input_tmp: The top-level directory of unpacked target-files.zip. info_dict: The loaded info dict. options: A dict that contains the user-supplied public keys to be used for image verification. In particular, 'verity_key' is used to verify the bootable images in VB 1.0, and the vbmeta image in VB 2.0, where applicable. 'verity_key_mincrypt' will be used to verify the system images in VB 1.0. Raises: AssertionError: On any verification failure. """ # Verified boot 1.0 (images signed with boot_signer and verity_signer). if info_dict.get('boot_signer') == 'true': logging.info('Verifying Verified Boot images...') # Verify the boot/recovery images (signed with boot_signer), against the # given X.509 encoded pubkey (or falling back to the one in the info_dict if # none given). verity_key = options['verity_key'] if verity_key is None: verity_key = info_dict['verity_key'] + '.x509.pem' for image in ('boot.img', 'recovery.img', 'recovery-two-step.img'): if image == 'recovery-two-step.img': image_path = os.path.join(input_tmp, 'OTA', image) else: image_path = os.path.join(input_tmp, 'IMAGES', image) if not os.path.exists(image_path): continue cmd = [ 'boot_signer', '-verify', image_path, '-certificate', verity_key ] proc = common.Run(cmd) stdoutdata, _ = proc.communicate() assert proc.returncode == 0, \ 'Failed to verify {} with boot_signer:\n{}'.format(image, stdoutdata) logging.info('Verified %s with boot_signer (key: %s):\n%s', image, verity_key, stdoutdata.rstrip()) # 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 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 ' 'provided. Use --verity_key_mincrypt to specify.') else: expected_key = options['verity_key_mincrypt'] assert filecmp.cmp(expected_key, verity_key_mincrypt, shallow=False), \ "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'): image_path = os.path.join(input_tmp, 'IMAGES', image) # We are not checking if the image is actually enabled via info_dict (e.g. # 'system_verity_block_device=...'). Because it's most likely a bug that # skips signing some of the images in signed target-files.zip, while # having the top-level verity flag enabled. if not os.path.exists(image_path): continue cmd = [ 'verity_verifier', image_path, '-mincrypt', verity_key_mincrypt ] proc = common.Run(cmd) stdoutdata, _ = proc.communicate() assert proc.returncode == 0, \ 'Failed to verify {} with verity_verifier (key: {}):\n{}'.format( image, verity_key_mincrypt, stdoutdata) logging.info('Verified %s with verity_verifier (key: %s):\n%s', image, verity_key_mincrypt, stdoutdata.rstrip()) # Handle the case of Verified Boot 2.0 (AVB). if info_dict.get("avb_enable") == "true": logging.info('Verifying Verified Boot 2.0 (AVB) images...') key = options['verity_key'] if key is None: key = info_dict['avb_vbmeta_key_path'] # avbtool verifies all the images that have descriptors listed in vbmeta. image = os.path.join(input_tmp, 'IMAGES', 'vbmeta.img') cmd = [ info_dict['avb_avbtool'], 'verify_image', '--image', image, '--key', key ] # Append the args for chained partitions if any. for partition in common.AVB_PARTITIONS + common.AVB_VBMETA_PARTITIONS: key_name = 'avb_' + partition + '_key_path' if info_dict.get(key_name) is not None: # Use the key file from command line if specified; otherwise fall back # to the one in info dict. key_file = options.get(key_name, info_dict[key_name]) chained_partition_arg = common.GetAvbChainedPartitionArg( partition, info_dict, key_file) cmd.extend( ["--expected_chain_partition", chained_partition_arg]) proc = common.Run(cmd) stdoutdata, _ = proc.communicate() assert proc.returncode == 0, \ 'Failed to verify {} with avbtool (key: {}):\n{}'.format( image, key, stdoutdata) logging.info('Verified %s with avbtool (key: %s):\n%s', image, key, stdoutdata.rstrip())
def ValidateVerifiedBootImages(input_tmp, info_dict, options): """Validates the Verified Boot related images. For Verified Boot 1.0, it verifies the signatures of the bootable images (boot/recovery etc), as well as the dm-verity metadata in system images (system/vendor/product). For Verified Boot 2.0, it calls avbtool to verify vbmeta.img, which in turn verifies all the descriptors listed in vbmeta. Args: input_tmp: The top-level directory of unpacked target-files.zip. info_dict: The loaded info dict. options: A dict that contains the user-supplied public keys to be used for image verification. In particular, 'verity_key' is used to verify the bootable images in VB 1.0, and the vbmeta image in VB 2.0, where applicable. 'verity_key_mincrypt' will be used to verify the system images in VB 1.0. Raises: AssertionError: On any verification failure. """ # See bug 159299583 # After commit 5277d1015, some images (e.g. acpio.img and tos.img) are no # longer copied from RADIO to the IMAGES folder. But avbtool assumes that # images are in IMAGES folder. So we symlink them. symlinkIfNotExists(os.path.join(input_tmp, "RADIO"), os.path.join(input_tmp, "IMAGES")) # Verified boot 1.0 (images signed with boot_signer and verity_signer). if info_dict.get('boot_signer') == 'true': logging.info('Verifying Verified Boot images...') # Verify the boot/recovery images (signed with boot_signer), against the # given X.509 encoded pubkey (or falling back to the one in the info_dict if # none given). verity_key = options['verity_key'] if verity_key is None: verity_key = info_dict['verity_key'] + '.x509.pem' for image in ('boot.img', 'recovery.img', 'recovery-two-step.img'): if image == 'recovery-two-step.img': image_path = os.path.join(input_tmp, 'OTA', image) else: image_path = os.path.join(input_tmp, 'IMAGES', image) if not os.path.exists(image_path): continue cmd = [ 'boot_signer', '-verify', image_path, '-certificate', verity_key ] proc = common.Run(cmd) stdoutdata, _ = proc.communicate() assert proc.returncode == 0, \ 'Failed to verify {} with boot_signer:\n{}'.format(image, stdoutdata) logging.info('Verified %s with boot_signer (key: %s):\n%s', image, verity_key, stdoutdata.rstrip()) # 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 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 ' 'provided. Use --verity_key_mincrypt to specify.') else: expected_key = options['verity_key_mincrypt'] assert filecmp.cmp(expected_key, verity_key_mincrypt, shallow=False), \ "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'): image_path = os.path.join(input_tmp, 'IMAGES', image) # We are not checking if the image is actually enabled via info_dict (e.g. # 'system_verity_block_device=...'). Because it's most likely a bug that # skips signing some of the images in signed target-files.zip, while # having the top-level verity flag enabled. if not os.path.exists(image_path): continue cmd = [ 'verity_verifier', image_path, '-mincrypt', verity_key_mincrypt ] proc = common.Run(cmd) stdoutdata, _ = proc.communicate() assert proc.returncode == 0, \ 'Failed to verify {} with verity_verifier (key: {}):\n{}'.format( image, verity_key_mincrypt, stdoutdata) logging.info('Verified %s with verity_verifier (key: %s):\n%s', image, verity_key_mincrypt, stdoutdata.rstrip()) # Handle the case of Verified Boot 2.0 (AVB). if info_dict.get("avb_enable") == "true": logging.info('Verifying Verified Boot 2.0 (AVB) images...') key = options['verity_key'] if key is None: key = info_dict['avb_vbmeta_key_path'] ValidatePartitionFingerprints(input_tmp, info_dict) # avbtool verifies all the images that have descriptors listed in vbmeta. # Using `--follow_chain_partitions` so it would additionally verify chained # vbmeta partitions (e.g. vbmeta_system). image = os.path.join(input_tmp, 'IMAGES', 'vbmeta.img') cmd = [ info_dict['avb_avbtool'], 'verify_image', '--image', image, '--follow_chain_partitions' ] # Custom images. custom_partitions = info_dict.get("avb_custom_images_partition_list", "").strip().split() # Append the args for chained partitions if any. for partition in (common.AVB_PARTITIONS + common.AVB_VBMETA_PARTITIONS + tuple(custom_partitions)): key_name = 'avb_' + partition + '_key_path' if info_dict.get(key_name) is not None: if info_dict.get( 'ab_update') != 'true' and partition == 'recovery': continue # Use the key file from command line if specified; otherwise fall back # to the one in info dict. key_file = options.get(key_name, info_dict[key_name]) chained_partition_arg = common.GetAvbChainedPartitionArg( partition, info_dict, key_file) cmd.extend( ['--expected_chain_partition', chained_partition_arg]) # Handle the boot image with a non-default name, e.g. boot-5.4.img boot_images = info_dict.get("boot_images") if boot_images: # we used the 1st boot image to generate the vbmeta. Rename the filename # to boot.img so that avbtool can find it correctly. first_image_name = boot_images.split()[0] first_image_path = os.path.join(input_tmp, 'IMAGES', first_image_name) assert os.path.isfile(first_image_path) renamed_boot_image_path = os.path.join(input_tmp, 'IMAGES', 'boot.img') os.rename(first_image_path, renamed_boot_image_path) proc = common.Run(cmd) stdoutdata, _ = proc.communicate() assert proc.returncode == 0, \ 'Failed to verify {} with avbtool (key: {}):\n{}'.format( image, key, stdoutdata) logging.info('Verified %s with avbtool (key: %s):\n%s', image, key, stdoutdata.rstrip()) # avbtool verifies recovery image for non-A/B devices. if (info_dict.get('ab_update') != 'true' and info_dict.get('no_recovery') != 'true'): image = os.path.join(input_tmp, 'IMAGES', 'recovery.img') key = info_dict['avb_recovery_key_path'] cmd = [ info_dict['avb_avbtool'], 'verify_image', '--image', image, '--key', key ] proc = common.Run(cmd) stdoutdata, _ = proc.communicate() assert proc.returncode == 0, \ 'Failed to verify {} with avbtool (key: {}):\n{}'.format( image, key, stdoutdata) logging.info('Verified %s with avbtool (key: %s):\n%s', image, key, stdoutdata.rstrip())