Allow top-level vbmeta struct to be in 'boot' partition.
If there is no 'vbmeta' partition try to load the top-level vbmeta
struct from the end of 'boot' via a footer.
Two use-cases come to mind
- bring-up when the partition table doesn't yet mention vbmeta; and
- upgrades where it's not feasible to change the partition table
Bug: None
Test: New unit tests and all unit tests pass.
Change-Id: Id0c6c0f95ce157ffbeb0692d3c9547f49ab58640
diff --git a/avbtool b/avbtool
index 9850634..6ebbd8c 100755
--- a/avbtool
+++ b/avbtool
@@ -1902,7 +1902,7 @@
Arguments:
output: File to write the image to.
- chain_partitions: List of partitions to chain.
+ chain_partitions: List of partitions to chain or None.
algorithm_name: Name of algorithm to use.
key_path: Path to key to use or None.
public_key_metadata_path: Path to public key metadata or None.
@@ -1922,26 +1922,10 @@
"""
descriptors = []
-
- # Insert chained partition descriptors.
- if chain_partitions:
- for cp in chain_partitions:
- cp_tokens = cp.split(':')
- if len(cp_tokens) != 3:
- raise AvbError('Malformed chained partition "{}".'.format(cp))
- desc = AvbChainPartitionDescriptor()
- desc.partition_name = cp_tokens[0]
- desc.rollback_index_location = int(cp_tokens[1])
- if desc.rollback_index_location < 1:
- raise AvbError('Rollback index location must be 1 or larger.')
- file_path = cp_tokens[2]
- desc.public_key = open(file_path, 'rb').read()
- descriptors.append(desc)
-
vbmeta_blob = self._generate_vbmeta_blob(
algorithm_name, key_path, public_key_metadata_path, descriptors,
- rollback_index, flags, props, props_from_file, kernel_cmdlines,
- setup_rootfs_from_kernel,
+ chain_partitions, rollback_index, flags, props, props_from_file,
+ kernel_cmdlines, setup_rootfs_from_kernel,
include_descriptors_from_image, signing_helper, release_string,
append_to_release_string)
@@ -1951,6 +1935,7 @@
def _generate_vbmeta_blob(self, algorithm_name, key_path,
public_key_metadata_path, descriptors,
+ chain_partitions,
rollback_index, flags, props, props_from_file,
kernel_cmdlines,
setup_rootfs_from_kernel,
@@ -1971,6 +1956,7 @@
key_path: The path to the .pem file used to sign the blob.
public_key_metadata_path: Path to public key metadata or None.
descriptors: A list of descriptors to insert or None.
+ chain_partitions: List of partitions to chain or None.
rollback_index: The rollback index to use.
flags: Flags to use in the image.
props: Properties to insert (List of strings of the form 'key:value').
@@ -1998,11 +1984,28 @@
except KeyError:
raise AvbError('Unknown algorithm with name {}'.format(algorithm_name))
+ if not descriptors:
+ descriptors = []
+
+ # Insert chained partition descriptors, if any
+ if chain_partitions:
+ for cp in chain_partitions:
+ cp_tokens = cp.split(':')
+ if len(cp_tokens) != 3:
+ raise AvbError('Malformed chained partition "{}".'.format(cp))
+ desc = AvbChainPartitionDescriptor()
+ desc.partition_name = cp_tokens[0]
+ desc.rollback_index_location = int(cp_tokens[1])
+ if desc.rollback_index_location < 1:
+ raise AvbError('Rollback index location must be 1 or larger.')
+ file_path = cp_tokens[2]
+ desc.public_key = open(file_path, 'rb').read()
+ descriptors.append(desc)
+
# Descriptors.
encoded_descriptors = bytearray()
- if descriptors:
- for desc in descriptors:
- encoded_descriptors.extend(desc.encode())
+ for desc in descriptors:
+ encoded_descriptors.extend(desc.encode())
# Add properties.
if props:
@@ -2153,8 +2156,9 @@
write_rsa_key(output, key)
def add_hash_footer(self, image_filename, partition_size, partition_name,
- hash_algorithm, salt, algorithm_name, key_path,
- public_key_metadata_path, rollback_index, props,
+ hash_algorithm, salt, chain_partitions, algorithm_name,
+ key_path,
+ public_key_metadata_path, rollback_index, flags, props,
props_from_file, kernel_cmdlines,
setup_rootfs_from_kernel,
include_descriptors_from_image, signing_helper,
@@ -2168,10 +2172,12 @@
partition_name: Name of partition (without A/B suffix).
hash_algorithm: Hash algorithm to use.
salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
+ chain_partitions: List of partitions to chain.
algorithm_name: Name of algorithm to use.
key_path: Path to key to use or None.
public_key_metadata_path: Path to public key metadata or None.
rollback_index: Rollback index.
+ flags: Flags value to use in the image.
props: Properties to insert (List of strings of the form 'key:value').
props_from_file: Properties to insert (List of strings 'key:<path>').
kernel_cmdlines: Kernel cmdlines to insert (list of strings).
@@ -2250,14 +2256,11 @@
h_desc.salt = salt
h_desc.digest = digest
- # Flags are only allowed on top-level vbmeta struct.
- flags = 0
-
# Generate the VBMeta footer.
vbmeta_blob = self._generate_vbmeta_blob(
algorithm_name, key_path, public_key_metadata_path, [h_desc],
- rollback_index, flags, props, props_from_file, kernel_cmdlines,
- setup_rootfs_from_kernel,
+ chain_partitions, rollback_index, flags, props, props_from_file,
+ kernel_cmdlines, setup_rootfs_from_kernel,
include_descriptors_from_image, signing_helper, release_string,
append_to_release_string)
@@ -2310,8 +2313,9 @@
def add_hashtree_footer(self, image_filename, partition_size, partition_name,
generate_fec, fec_num_roots, hash_algorithm,
- block_size, salt, algorithm_name, key_path,
- public_key_metadata_path, rollback_index,
+ block_size, salt, chain_partitions, algorithm_name,
+ key_path,
+ public_key_metadata_path, rollback_index, flags,
props, props_from_file, kernel_cmdlines,
setup_rootfs_from_kernel,
include_descriptors_from_image,
@@ -2332,10 +2336,12 @@
hash_algorithm: Hash algorithm to use.
block_size: Block size to use.
salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
+ chain_partitions: List of partitions to chain.
algorithm_name: Name of algorithm to use.
key_path: Path to key to use or None.
public_key_metadata_path: Path to public key metadata or None.
rollback_index: Rollback index.
+ flags: Flags value to use in the image.
props: Properties to insert (List of strings of the form 'key:value').
props_from_file: Properties to insert (List of strings 'key:<path>').
kernel_cmdlines: Kernel cmdlines to insert (list of strings).
@@ -2478,15 +2484,12 @@
ht_desc.fec_offset = fec_offset
ht_desc.fec_size = len(fec_data)
- # Flags are only allowed on top-level vbmeta struct.
- flags = 0
-
# Generate the VBMeta footer and add padding as needed.
vbmeta_offset = tree_offset + len_hashtree_and_fec
vbmeta_blob = self._generate_vbmeta_blob(
algorithm_name, key_path, public_key_metadata_path, [ht_desc],
- rollback_index, flags, props, props_from_file, kernel_cmdlines,
- setup_rootfs_from_kernel,
+ chain_partitions, rollback_index, flags, props, props_from_file,
+ kernel_cmdlines, setup_rootfs_from_kernel,
include_descriptors_from_image, signing_helper, release_string,
append_to_release_string)
padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
@@ -2859,6 +2862,31 @@
metavar='IMAGE',
action='append',
type=argparse.FileType('rb'))
+ # These are only allowed from top-level vbmeta and boot-in-lieu-of-vbmeta.
+ sub_parser.add_argument('--chain_partition',
+ help='Allow signed integrity-data for partition',
+ metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
+ action='append')
+ sub_parser.add_argument('--flags',
+ help='VBMeta flags',
+ type=parse_number,
+ default=0)
+ sub_parser.add_argument('--set_hashtree_disabled_flag',
+ help='Set the HASHTREE_DISABLED flag',
+ action='store_true')
+
+ def _fixup_common_args(self, args):
+ """Common fixups needed by subcommands.
+
+ Arguments:
+ args: Arguments to modify.
+
+ Returns:
+ The modified arguments.
+ """
+ if args.set_hashtree_disabled_flag:
+ args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
+ return args
def run(self, argv):
"""Command-line processor.
@@ -2891,17 +2919,6 @@
type=argparse.FileType('wb'),
required=True)
self._add_common_args(sub_parser)
- sub_parser.add_argument('--chain_partition',
- help='Allow signed integrity-data for partition',
- metavar='PART_NAME:ROLLBACK_SLOT:KEY_PATH',
- action='append')
- sub_parser.add_argument('--flags',
- help='VBMeta flags',
- type=parse_number,
- default=0)
- sub_parser.add_argument('--set_hashtree_disabled_flag',
- help='Set the HASHTREE_DISABLED flag',
- action='store_true')
sub_parser.set_defaults(func=self.make_vbmeta_image)
sub_parser = subparsers.add_parser('add_hash_footer',
@@ -3104,8 +3121,7 @@
def make_vbmeta_image(self, args):
"""Implements the 'make_vbmeta_image' sub-command."""
- if args.set_hashtree_disabled_flag:
- args.flags |= AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED
+ args = self._fixup_common_args(args)
self.avb.make_vbmeta_image(args.output, args.chain_partition,
args.algorithm, args.key,
args.public_key_metadata, args.rollback_index,
@@ -3119,11 +3135,13 @@
def add_hash_footer(self, args):
"""Implements the 'add_hash_footer' sub-command."""
+ args = self._fixup_common_args(args)
self.avb.add_hash_footer(args.image.name, args.partition_size,
args.partition_name, args.hash_algorithm,
- args.salt, args.algorithm, args.key,
+ args.salt, args.chain_partition, args.algorithm,
+ args.key,
args.public_key_metadata, args.rollback_index,
- args.prop, args.prop_from_file,
+ args.flags, args.prop, args.prop_from_file,
args.kernel_cmdline,
args.setup_rootfs_from_kernel,
args.include_descriptors_from_image,
@@ -3135,14 +3153,15 @@
def add_hashtree_footer(self, args):
"""Implements the 'add_hashtree_footer' sub-command."""
+ args = self._fixup_common_args(args)
self.avb.add_hashtree_footer(args.image.name if args.image else None,
args.partition_size,
args.partition_name,
args.generate_fec, args.fec_num_roots,
args.hash_algorithm, args.block_size,
- args.salt, args.algorithm, args.key,
- args.public_key_metadata,
- args.rollback_index, args.prop,
+ args.salt, args.chain_partition, args.algorithm,
+ args.key, args.public_key_metadata,
+ args.rollback_index, args.flags, args.prop,
args.prop_from_file,
args.kernel_cmdline,
args.setup_rootfs_from_kernel,
diff --git a/libavb/avb_slot_verify.c b/libavb/avb_slot_verify.c
index 3423adc..f4b2929 100644
--- a/libavb/avb_slot_verify.c
+++ b/libavb/avb_slot_verify.c
@@ -237,14 +237,20 @@
const AvbDescriptor** descriptors = NULL;
size_t num_descriptors;
size_t n;
- int is_main_vbmeta;
+ bool is_main_vbmeta;
+ bool is_vbmeta_partition;
AvbVBMetaData* vbmeta_image_data = NULL;
ret = AVB_SLOT_VERIFY_RESULT_OK;
avb_assert(slot_data != NULL);
- is_main_vbmeta = (avb_strcmp(partition_name, "vbmeta") == 0);
+ /* Since we allow top-level vbmeta in 'boot', use
+ * rollback_index_location to determine whether we're the main
+ * vbmeta struct.
+ */
+ is_main_vbmeta = (rollback_index_location == 0);
+ is_vbmeta_partition = (avb_strcmp(partition_name, "vbmeta") == 0);
if (!avb_validate_utf8((const uint8_t*)partition_name, partition_name_len)) {
avb_error("Partition name is not valid UTF-8.\n");
@@ -273,7 +279,7 @@
* struct is in the beginning. Otherwise we have to locate it via a
* footer.
*/
- if (is_main_vbmeta) {
+ if (is_vbmeta_partition) {
vbmeta_offset = 0;
vbmeta_size = VBMETA_MAX_SIZE;
} else {
@@ -332,9 +338,32 @@
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
goto out;
} else if (io_ret != AVB_IO_RESULT_OK) {
- avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL);
- ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
- goto out;
+ /* If we're looking for 'vbmeta' but there is no such partition,
+ * go try to get it from the boot partition instead.
+ */
+ if (is_main_vbmeta && io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION &&
+ is_vbmeta_partition) {
+ avb_debugv(full_partition_name,
+ ": No such partition. Trying 'boot' instead.\n",
+ NULL);
+ ret = load_and_verify_vbmeta(ops,
+ requested_partitions,
+ ab_suffix,
+ allow_verification_error,
+ 0 /* toplevel_vbmeta_flags */,
+ 0 /* rollback_index_location */,
+ "boot",
+ avb_strlen("boot"),
+ NULL /* expected_public_key */,
+ 0 /* expected_public_key_length */,
+ slot_data,
+ out_algorithm_type);
+ goto out;
+ } else {
+ avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+ goto out;
+ }
}
avb_assert(vbmeta_num_read <= vbmeta_size);
@@ -561,6 +590,15 @@
goto out;
}
+ if (chain_desc.rollback_index_location == 0) {
+ avb_errorv(full_partition_name,
+ ": Chain partition has invalid "
+ "rollback_index_location field.\n",
+ NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
chain_partition_name = ((const uint8_t*)descriptors[n]) +
sizeof(AvbChainPartitionDescriptor);
chain_public_key = chain_partition_name + chain_desc.partition_name_len;
@@ -707,7 +745,8 @@
*/
static char* sub_cmdline(AvbOps* ops,
const char* cmdline,
- const char* ab_suffix) {
+ const char* ab_suffix,
+ bool using_boot_for_vbmeta) {
const char* part_name_str[NUM_GUIDS] = {"system", "boot", "vbmeta"};
const char* replace_str[NUM_GUIDS] = {"$(ANDROID_SYSTEM_PARTUUID)",
"$(ANDROID_BOOT_PARTUUID)",
@@ -715,6 +754,13 @@
char* ret = NULL;
AvbIOResult io_ret;
+ /* Special-case for when the top-level vbmeta struct is in the boot
+ * partition.
+ */
+ if (using_boot_for_vbmeta) {
+ part_name_str[2] = "boot";
+ }
+
/* Replace unique partition GUIDs */
for (size_t n = 0; n < NUM_GUIDS; n++) {
char part_name[PART_NAME_MAX_SIZE];
@@ -882,6 +928,7 @@
AvbSlotVerifyData* slot_data = NULL;
AvbAlgorithmType algorithm_type = AVB_ALGORITHM_TYPE_NONE;
AvbIOResult io_ret;
+ bool using_boot_for_vbmeta = false;
if (out_data != NULL) {
*out_data = NULL;
@@ -909,7 +956,7 @@
requested_partitions,
ab_suffix,
allow_verification_error,
- 0, /* toplevel_vbmeta_flags */
+ 0 /* toplevel_vbmeta_flags */,
0 /* rollback_index_location */,
"vbmeta",
avb_strlen("vbmeta"),
@@ -921,6 +968,12 @@
goto fail;
}
+ if (avb_strcmp(slot_data->vbmeta_images[0].partition_name, "vbmeta") != 0) {
+ avb_assert(avb_strcmp(slot_data->vbmeta_images[0].partition_name, "boot") ==
+ 0);
+ using_boot_for_vbmeta = true;
+ }
+
/* If things check out, mangle the kernel command-line as needed. */
if (result_should_continue(ret)) {
/* Fill in |ab_suffix| field. */
@@ -949,7 +1002,9 @@
/* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */
if (slot_data->cmdline != NULL) {
- char* new_cmdline = sub_cmdline(ops, slot_data->cmdline, ab_suffix);
+ char* new_cmdline;
+ new_cmdline = sub_cmdline(
+ ops, slot_data->cmdline, ab_suffix, using_boot_for_vbmeta);
if (new_cmdline == NULL) {
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
goto fail;
diff --git a/libavb/avb_slot_verify.h b/libavb/avb_slot_verify.h
index c50b847..08b11fc 100644
--- a/libavb/avb_slot_verify.h
+++ b/libavb/avb_slot_verify.h
@@ -108,8 +108,10 @@
* The VBMeta images that were checked are available in the
* |vbmeta_images| field. The field |num_vbmeta_images| contains the
* number of elements in this array. The first element -
- * vbmeta_images[0] - is guaranteed to be from the "vbmeta" partition
- * in the requested slot.
+ * vbmeta_images[0] - is guaranteed to be from the partition with the
+ * top-level vbmeta struct. This is usually the "vbmeta" partition in
+ * the requested slot but if there is no "vbmeta" partition it can
+ * also be the "boot" partition.
*
* The partitions loaded and verified from from the slot are
* accessible in the |loaded_partitions| array. The field
@@ -145,7 +147,8 @@
* androidboot.vbmeta.device: This is set to the value
* PARTUUID=$(ANDROID_VBMETA_PARTUUID) before substitution so it
* will end up pointing to the vbmeta partition for the verified
- * slot.
+ * slot. If there is no vbmeta partition it will point to the boot
+ * partition of the verified slot.
*
* androidboot.vbmeta.avb_version: This is set to the decimal value
* of AVB_VERSION_MAJOR followed by a dot followed by the decimal
diff --git a/test/avb_slot_verify_unittest.cc b/test/avb_slot_verify_unittest.cc
index c9398a2..1553173 100644
--- a/test/avb_slot_verify_unittest.cc
+++ b/test/avb_slot_verify_unittest.cc
@@ -1286,4 +1286,286 @@
CmdlineWithHashtreeVerification(true);
}
+// In the event that there's no vbmeta partition, we treat the vbmeta
+// struct from 'boot' as the top-level partition. Check that this
+// works.
+TEST_F(AvbSlotVerifyTest, NoVBMetaPartition) {
+ const size_t MiB = 1024 * 1024;
+ const size_t boot_size = 6 * MiB;
+ const size_t boot_part_size = 8 * MiB;
+ const size_t system_size = 16 * MiB;
+ const size_t system_part_size = 32 * MiB;
+ const size_t foobar_size = 8 * MiB;
+ const size_t foobar_part_size = 16 * MiB;
+ const size_t bazboo_size = 4 * MiB;
+ const size_t bazboo_part_size = 8 * MiB;
+ base::FilePath boot_path = GenerateImage("boot.img", boot_size);
+ base::FilePath system_path = GenerateImage("system.img", system_size);
+ base::FilePath foobar_path = GenerateImage("foobar.img", foobar_size);
+ base::FilePath bazboo_path = GenerateImage("bazboo.img", bazboo_size);
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer --salt d00df00d --image %s "
+ "--partition_size %d --partition_name system "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem "
+ "--internal_release_string \"\"",
+ system_path.value().c_str(),
+ (int)system_part_size);
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer --salt d00df00d --image %s "
+ "--partition_size %d --partition_name foobar "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem "
+ "--internal_release_string \"\"",
+ foobar_path.value().c_str(),
+ (int)foobar_part_size);
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer --salt d00df00d --image %s "
+ "--partition_size %d --partition_name bazboo "
+ "--algorithm SHA512_RSA4096 "
+ "--key test/data/testkey_rsa4096.pem "
+ "--internal_release_string \"\"",
+ bazboo_path.value().c_str(),
+ (int)bazboo_part_size);
+
+ base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
+ " --output %s",
+ pk_path.value().c_str());
+
+ // Explicitly pass "--flags 2147483648" (i.e. 1<<31) to check that
+ // boot.img is treated as top-level. Note the corresponding "Flags:"
+ // field below in the avbtool info_image output.
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer --salt d00df00d "
+ "--hash_algorithm sha256 --image %s "
+ "--partition_size %d --partition_name boot "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem "
+ "--internal_release_string \"\" "
+ "--include_descriptors_from_image %s "
+ "--include_descriptors_from_image %s "
+ "--setup_rootfs_from_kernel %s "
+ "--chain_partition bazboo:1:%s "
+ "--flags 2147483648",
+ boot_path.value().c_str(),
+ (int)boot_part_size,
+ system_path.value().c_str(),
+ foobar_path.value().c_str(),
+ system_path.value().c_str(),
+ pk_path.value().c_str());
+
+ ASSERT_EQ(
+ "Footer version: 1.0\n"
+ "Image size: 8388608 bytes\n"
+ "Original image size: 6291456 bytes\n"
+ "VBMeta offset: 6291456\n"
+ "VBMeta size: 3200 bytes\n"
+ "--\n"
+ "Minimum libavb version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 2624 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 2147483648\n"
+ "Release String: ''\n"
+ "Descriptors:\n"
+ " Hash descriptor:\n"
+ " Image Size: 6291456 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: boot\n"
+ " Salt: d00df00d\n"
+ " Digest: "
+ "4c109399b20e476bab15363bff55740add83e1c1e97e0b132f5c713ddd8c7868\n"
+ " Chain Partition descriptor:\n"
+ " Partition Name: bazboo\n"
+ " Rollback Index Location: 1\n"
+ " Public key (sha1): "
+ "2597c218aae470a130f61162feaae70afd97f011\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 1\n"
+ " Kernel Cmdline: 'dm=\"1 vroot none ro 1,0 32768 verity 1 "
+ "PARTUUID=$(ANDROID_SYSTEM_PARTUUID) PARTUUID=$(ANDROID_SYSTEM_PARTUUID) "
+ "4096 4096 4096 4096 sha1 c9ffc3bfae5000269a55a56621547fd1fcf819df "
+ "d00df00d 2 restart_on_corruption ignore_zero_blocks\" root=0xfd00'\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 2\n"
+ " Kernel Cmdline: "
+ "'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'\n"
+ " Hashtree descriptor:\n"
+ " Version of dm-verity: 1\n"
+ " Image Size: 16777216 bytes\n"
+ " Tree Offset: 16777216\n"
+ " Tree Size: 135168 bytes\n"
+ " Data Block Size: 4096 bytes\n"
+ " Hash Block Size: 4096 bytes\n"
+ " FEC num roots: 0\n"
+ " FEC offset: 0\n"
+ " FEC size: 0 bytes\n"
+ " Hash Algorithm: sha1\n"
+ " Partition Name: system\n"
+ " Salt: d00df00d\n"
+ " Root Digest: c9ffc3bfae5000269a55a56621547fd1fcf819df\n"
+ " Hashtree descriptor:\n"
+ " Version of dm-verity: 1\n"
+ " Image Size: 8388608 bytes\n"
+ " Tree Offset: 8388608\n"
+ " Tree Size: 69632 bytes\n"
+ " Data Block Size: 4096 bytes\n"
+ " Hash Block Size: 4096 bytes\n"
+ " FEC num roots: 0\n"
+ " FEC offset: 0\n"
+ " FEC size: 0 bytes\n"
+ " Hash Algorithm: sha1\n"
+ " Partition Name: foobar\n"
+ " Salt: d00df00d\n"
+ " Root Digest: d52d93c988d336a79abe1c05240ae9a79a9b7d61\n",
+ InfoImage(boot_path));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ // Now check that libavb will fall back to reading from 'boot'
+ // instead of 'vbmeta' when encountering
+ // AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION on trying to read from
+ // 'vbmeta'.
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"boot", NULL};
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ // Note 'boot' in the value androidboot.vbmeta.device since we've
+ // read from 'boot' and not 'vbmeta'.
+ EXPECT_EQ(
+ "dm=\"1 vroot none ro 1,0 32768 verity 1 "
+ "PARTUUID=1234-fake-guid-for:system PARTUUID=1234-fake-guid-for:system "
+ "4096 4096 4096 4096 sha1 c9ffc3bfae5000269a55a56621547fd1fcf819df "
+ "d00df00d 2 restart_on_corruption ignore_zero_blocks\" root=0xfd00 "
+ "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:boot "
+ "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.device_state=locked "
+ "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=5312 "
+ "androidboot.vbmeta.digest="
+ "403b4071873ca54187ca2c33e314edf3049ccb6cfadf518d7f6a509fce3d18b3",
+ std::string(slot_data->cmdline));
+ avb_slot_verify_data_free(slot_data);
+}
+
+// Check that non-zero flags in chained partition are caught in
+// avb_slot_verify().
+TEST_F(AvbSlotVerifyTest, ChainedPartitionEnforceFlagsZero) {
+ size_t boot_partition_size = 16 * 1024 * 1024;
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot_a.img", boot_image_size);
+ const char* requested_partitions[] = {"boot", NULL};
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer"
+ " --image %s"
+ " --kernel_cmdline 'cmdline2 in hash footer'"
+ " --rollback_index 12"
+ " --partition_name boot"
+ " --partition_size %zd"
+ " --algorithm SHA256_RSA4096"
+ " --key test/data/testkey_rsa4096.pem"
+ " --salt deadbeef"
+ " --flags 1"
+ " --internal_release_string \"\"",
+ boot_path.value().c_str(),
+ boot_partition_size);
+
+ base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
+ " --output %s",
+ pk_path.value().c_str());
+
+ GenerateVBMetaImage(
+ "vbmeta_a.img",
+ "SHA256_RSA2048",
+ 11,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf("--chain_partition boot:1:%s"
+ " --kernel_cmdline 'cmdline2 in vbmeta'"
+ " --internal_release_string \"\"",
+ pk_path.value().c_str()));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_EQ(nullptr, slot_data);
+}
+
+// Check that chain descriptors in chained partitions are caught in
+// avb_slot_verify().
+TEST_F(AvbSlotVerifyTest, ChainedPartitionEnforceNoChainPartitions) {
+ size_t boot_partition_size = 16 * 1024 * 1024;
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot_a.img", boot_image_size);
+ const char* requested_partitions[] = {"boot", NULL};
+
+ base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
+ " --output %s",
+ pk_path.value().c_str());
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer"
+ " --image %s"
+ " --kernel_cmdline 'cmdline2 in hash footer'"
+ " --rollback_index 12"
+ " --partition_name boot"
+ " --partition_size %zd"
+ " --algorithm SHA256_RSA4096"
+ " --key test/data/testkey_rsa4096.pem"
+ " --salt deadbeef"
+ " --chain_partition other:2:%s"
+ " --internal_release_string \"\"",
+ boot_path.value().c_str(),
+ boot_partition_size,
+ pk_path.value().c_str());
+
+ GenerateVBMetaImage(
+ "vbmeta_a.img",
+ "SHA256_RSA2048",
+ 11,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf("--chain_partition boot:1:%s"
+ " --kernel_cmdline 'cmdline2 in vbmeta'"
+ " --internal_release_string \"\"",
+ pk_path.value().c_str()));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_EQ(nullptr, slot_data);
+}
+
} // namespace avb
diff --git a/test/fake_avb_ops.cc b/test/fake_avb_ops.cc
index cdd0850..6e346e1 100644
--- a/test/fake_avb_ops.cc
+++ b/test/fake_avb_ops.cc
@@ -67,7 +67,11 @@
"Error opening file '%s': %s\n",
path.value().c_str(),
strerror(errno));
- return AVB_IO_RESULT_ERROR_IO;
+ if (errno == ENOENT) {
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ } else {
+ return AVB_IO_RESULT_ERROR_IO;
+ }
}
if (lseek(fd, offset, SEEK_SET) != offset) {
fprintf(stderr,
@@ -121,7 +125,11 @@
"Error opening file '%s': %s\n",
path.value().c_str(),
strerror(errno));
- return AVB_IO_RESULT_ERROR_IO;
+ if (errno == ENOENT) {
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ } else {
+ return AVB_IO_RESULT_ERROR_IO;
+ }
}
if (lseek(fd, offset, SEEK_SET) != offset) {
fprintf(stderr,