aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarren Krahn <dkrahn@google.com>2018-02-27 02:00:33 +0000
committerandroid-build-merger <android-build-merger@google.com>2018-02-27 02:00:33 +0000
commit791f6c3648dd9cea987396375592217b81898dd3 (patch)
treeb389467c4f5230592fda4016cdd0ee49ef19058b
parent89775acb3b5414fec2e5473da205fc48026d1ad9 (diff)
parentaa350af3d09e54968cdc63badbe94733ccf393ab (diff)
downloadavb-pie-dr1-dev.tar.gz
am: aa350af3d0 Change-Id: I54494537b88dff7550bf22733772a0f45edcd8da
-rw-r--r--README.md72
-rwxr-xr-xavbtool159
-rw-r--r--libavb/avb_cmdline.c106
-rw-r--r--libavb/avb_cmdline.h36
-rw-r--r--libavb/avb_crypto.h3
-rw-r--r--libavb/avb_hash_descriptor.c1
-rw-r--r--libavb/avb_hash_descriptor.h17
-rw-r--r--libavb/avb_hashtree_descriptor.c1
-rw-r--r--libavb/avb_hashtree_descriptor.h17
-rw-r--r--libavb/avb_ops.h64
-rw-r--r--libavb/avb_slot_verify.c271
-rw-r--r--libavb/avb_util.c27
-rw-r--r--libavb/avb_util.h10
-rw-r--r--libavb/avb_version.h2
-rw-r--r--test/avb_atx_validate_unittest.cc15
-rw-r--r--test/avb_slot_verify_unittest.cc522
-rw-r--r--test/avbtool_unittest.cc446
-rw-r--r--test/fake_avb_ops.cc79
-rw-r--r--test/fake_avb_ops.h34
19 files changed, 1689 insertions, 193 deletions
diff --git a/README.md b/README.md
index 0ad0a0b..bbcf96c 100644
--- a/README.md
+++ b/README.md
@@ -22,11 +22,14 @@ Verified Boot 2.0. Usually AVB is used to refer to this codebase.
+ [System Dependencies](#System-Dependencies)
+ [Locked and Unlocked mode](#Locked-and-Unlocked-mode)
+ [Tamper-evident Storage](#Tamper_evident-Storage)
+ + [Named Persistent Values](#Named-Persistent-Values)
+ + [Persistent Digests](#Persistent-Digests)
+ [Updating Stored Rollback Indexes](#Updating-Stored-Rollback-Indexes)
+ [Recommended Bootflow](#Recommended-Bootflow)
+ [Handling dm-verity Errors](#Handling-dm_verity-Errors)
+ [Android Specific Integration](#Android-Specific-Integration)
- + [Device Specific Notes](Device-Specific-Notes)
+ + [Device Specific Notes](#Device-Specific-Notes)
+* [Version History](#Version-History)
# What is it?
@@ -121,6 +124,12 @@ descriptors. Here's an example with two slots:
Note how the rollback indexes differ between slots - for slot A the
rollback indexes are `[42, 101]` and for slot B they are `[43, 103]`.
+In version 1.1 or later, avbtool supports `--do_not_use_ab` for
+`add_hash_footer` and `add_hashtree_footer` operations. This makes it
+possible to work with a partition that does not use A/B and should
+never have the prefix. This corresponds to the
+`AVB_HASH[TREE]_DESCRIPTOR_FLAGS_DO_NOT_USE_AB` flags.
+
# Tools and Libraries
This section contains information about the tools and libraries
@@ -335,7 +344,9 @@ added to an existing image as follows:
[--signing_helper_with_files /path/to/external/signer_with_files] \
[--print_required_libavb_version] \
[--append_to_release_string STR] \
- [--calc_max_image_size]
+ [--calc_max_image_size] \
+ [--do_not_use_ab] \
+ [--use_persistent_digest]
An integrity footer containing the root digest and salt for a hashtree
for a partition can be added to an existing image as follows. The
@@ -356,7 +367,9 @@ hashtree is also appended to the image.
[--signing_helper_with_files /path/to/external/signer_with_files] \
[--print_required_libavb_version] \
[--append_to_release_string STR] \
- [--calc_max_image_size]
+ [--calc_max_image_size] \
+ [--do_not_use_ab] \
+ [--use_persistent_digest]
The size of an image with integrity footers can be changed using the
`resize_image` command:
@@ -522,7 +535,7 @@ e.g. derive `AVB_pk`. Both `AVB_pk` and `AVB_pkmd` are passed to the
`validate_vbmeta_public_key()` operation when verifying a slot.
Some devices may support the end-user configuring the root of trust to use, see
-the [Device Specific Notes](Device-Specific-Notes) section for details.
+the [Device Specific Notes](#Device-Specific-Notes) section for details.
To prevent rollback attacks, the rollback index should be increased on
a regular basis. The rollback index can be set with the
@@ -639,9 +652,9 @@ if the HLOS has tampered with the data, e.g. if it has been
overwritten.
Tamper-evident storage must be used for stored rollback indexes, keys
-used for verification, and device state (whether the device is LOCKED
-or UNLOCKED). If tampering has been detected the corresponding
-`AvbOps` operation should fail by e.g. returning
+used for verification, device state (whether the device is LOCKED or
+UNLOCKED), and named persistent values. If tampering has been detected
+the corresponding `AvbOps` operation should fail by e.g. returning
`AVB_IO_RESULT_ERROR_IO`. It is especially important that verification
keys cannot be tampered with since they represent the root-of-trust.
@@ -651,6 +664,36 @@ intermediate point before the end user. Additionally, it must only be
possible to set or clear a key while the device is in the UNLOCKED
state.
+## Named Persistent Values
+
+AVB 1.1 introduces support for named persistent values which must be
+tamper evident and allows AVB to store arbitrary key-value pairs.
+Integrators may limit support for these values to a set of fixed
+well-known names, a maximum value size, and / or a maximum number of
+values.
+
+## Persistent Digests
+
+Using a persistent digest for a partition means the digest (or root
+digest in the case of a hashtree) is not stored in the descriptor but
+is stored in a named persistent value. This allows configuration data
+which may differ from device to device to be verified by AVB. It must
+not be possible to modify the persistent digest when the device is in
+the LOCKED state.
+
+To specify that a descriptor should use a persistent digest, use the
+`--use_persistent_digest` option for the `add_hash_footer` or
+`add_hashtree_footer` avbtool operations. Then, during verification of
+the descriptor, AVB will look for the digest in the named persistent
+value `avb.persistent_digest.$(partition_name)` instead of in the
+descriptor itself.
+
+For hashtree descriptors using a persistent digest, the digest value
+will be available for substitution into kernel command line descriptors
+using a token of the form `$(AVB_FOO_ROOT_DIGEST)` where 'FOO' is the
+uppercase partition name, in this case for the partition named 'foo'.
+The token will be replaced by the digest in hexadecimal form.
+
## Updating Stored Rollback Indexes
In order for Rollback Protection to work the bootloader will need to
@@ -860,3 +903,18 @@ security features (including rollback-protection) are in effect, e.g. the
When booting an image signed with a custom key, a yellow screen will be shown as
part of the boot process to remind the user that the custom key is in use.
+
+# Version History
+
+### Version 1.1
+
+Version 1.1 adds support for the following:
+
+* A 32-bit `flags` element is added to hash and hashtree descriptors.
+* Support for partitions which don't use [A/B](#A_B-Support).
+* Tamper-evident [named persistent values](#Named-Persistent-Values).
+* [Persistent digests](#Persistent-Digests) for hash or hashtree descriptors.
+
+### Version 1.0
+
+All features not explicitly listed under a later version are supported by 1.0.
diff --git a/avbtool b/avbtool
index b742466..14914ce 100755
--- a/avbtool
+++ b/avbtool
@@ -38,9 +38,13 @@ import time
# Keep in sync with libavb/avb_version.h.
AVB_VERSION_MAJOR = 1
-AVB_VERSION_MINOR = 0
+AVB_VERSION_MINOR = 1
AVB_VERSION_SUB = 0
+# Keep in sync with libavb/avb_footer.h.
+AVB_FOOTER_VERSION_MAJOR = 1
+AVB_FOOTER_VERSION_MINOR = 0
+
AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
@@ -1195,11 +1199,12 @@ class AvbHashtreeDescriptor(AvbDescriptor):
partition_name: Partition name.
salt: Salt used.
root_digest: Root digest.
+ flags: Descriptor flags (see avb_hashtree_descriptor.h).
"""
TAG = 1
- RESERVED = 64
- SIZE = 116 + RESERVED
+ RESERVED = 60
+ SIZE = 120 + RESERVED
FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
'L' # dm-verity version used
'Q' # image size (bytes)
@@ -1213,7 +1218,8 @@ class AvbHashtreeDescriptor(AvbDescriptor):
'32s' # hash algorithm used
'L' # partition name (bytes)
'L' # salt length (bytes)
- 'L' + # root digest length (bytes)
+ 'L' # root digest length (bytes)
+ 'L' + # flags
str(RESERVED) + 's') # reserved
def __init__(self, data=None):
@@ -1233,8 +1239,8 @@ class AvbHashtreeDescriptor(AvbDescriptor):
self.tree_offset, self.tree_size, self.data_block_size,
self.hash_block_size, self.fec_num_roots, self.fec_offset, self.fec_size,
self.hash_algorithm, partition_name_len, salt_len,
- root_digest_len, _) = struct.unpack(self.FORMAT_STRING,
- data[0:self.SIZE])
+ root_digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
+ data[0:self.SIZE])
expected_size = round_to_multiple(
self.SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8)
if tag != self.TAG or num_bytes_following != expected_size:
@@ -1252,7 +1258,8 @@ class AvbHashtreeDescriptor(AvbDescriptor):
o += salt_len
self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
- raise LookupError('root_digest_len doesn\'t match hash algorithm')
+ if root_digest_len != 0:
+ raise LookupError('root_digest_len doesn\'t match hash algorithm')
else:
self.dm_verity_version = 0
@@ -1268,6 +1275,7 @@ class AvbHashtreeDescriptor(AvbDescriptor):
self.partition_name = ''
self.salt = bytearray()
self.root_digest = bytearray()
+ self.flags = 0
def print_desc(self, o):
"""Print the descriptor.
@@ -1293,6 +1301,7 @@ class AvbHashtreeDescriptor(AvbDescriptor):
'hex')))
o.write(' Root Digest: {}\n'.format(str(
self.root_digest).encode('hex')))
+ o.write(' Flags: {}\n'.format(self.flags))
def encode(self):
"""Serializes the descriptor.
@@ -1311,7 +1320,7 @@ class AvbHashtreeDescriptor(AvbDescriptor):
self.hash_block_size, self.fec_num_roots,
self.fec_offset, self.fec_size, self.hash_algorithm,
len(encoded_name), len(self.salt), len(self.root_digest),
- self.RESERVED*'\0')
+ self.flags, self.RESERVED*'\0')
padding = struct.pack(str(padding_size) + 'x')
ret = desc + encoded_name + self.salt + self.root_digest + padding
return bytearray(ret)
@@ -1341,8 +1350,8 @@ class AvbHashtreeDescriptor(AvbDescriptor):
digest_padding,
hash_level_offsets,
tree_size)
- # The root digest must match...
- if root_digest != self.root_digest:
+ # The root digest must match unless it is not embedded in the descriptor.
+ if len(self.root_digest) != 0 and root_digest != self.root_digest:
sys.stderr.write('hashtree of {} does not match descriptor\n'.
format(image_filename))
return False
@@ -1374,17 +1383,19 @@ class AvbHashDescriptor(AvbDescriptor):
partition_name: Partition name.
salt: Salt used.
digest: The hash value of salt and data combined.
+ flags: The descriptor flags (see avb_hash_descriptor.h).
"""
TAG = 2
- RESERVED = 64
- SIZE = 68 + RESERVED
+ RESERVED = 60
+ SIZE = 72 + RESERVED
FORMAT_STRING = ('!QQ' # tag, num_bytes_following (descriptor header)
'Q' # image size (bytes)
'32s' # hash algorithm used
'L' # partition name (bytes)
'L' # salt length (bytes)
- 'L' + # digest length (bytes)
+ 'L' # digest length (bytes)
+ 'L' + # flags
str(RESERVED) + 's') # reserved
def __init__(self, data=None):
@@ -1402,7 +1413,8 @@ class AvbHashDescriptor(AvbDescriptor):
if data:
(tag, num_bytes_following, self.image_size, self.hash_algorithm,
partition_name_len, salt_len,
- digest_len, _) = struct.unpack(self.FORMAT_STRING, data[0:self.SIZE])
+ digest_len, self.flags, _) = struct.unpack(self.FORMAT_STRING,
+ data[0:self.SIZE])
expected_size = round_to_multiple(
self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
if tag != self.TAG or num_bytes_following != expected_size:
@@ -1419,7 +1431,8 @@ class AvbHashDescriptor(AvbDescriptor):
o += salt_len
self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
- raise LookupError('digest_len doesn\'t match hash algorithm')
+ if digest_len != 0:
+ raise LookupError('digest_len doesn\'t match hash algorithm')
else:
self.image_size = 0
@@ -1427,6 +1440,7 @@ class AvbHashDescriptor(AvbDescriptor):
self.partition_name = ''
self.salt = bytearray()
self.digest = bytearray()
+ self.flags = 0
def print_desc(self, o):
"""Print the descriptor.
@@ -1442,6 +1456,7 @@ class AvbHashDescriptor(AvbDescriptor):
'hex')))
o.write(' Digest: {}\n'.format(str(self.digest).encode(
'hex')))
+ o.write(' Flags: {}\n'.format(self.flags))
def encode(self):
"""Serializes the descriptor.
@@ -1456,7 +1471,8 @@ class AvbHashDescriptor(AvbDescriptor):
padding_size = nbf_with_padding - num_bytes_following
desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
self.image_size, self.hash_algorithm, len(encoded_name),
- len(self.salt), len(self.digest), self.RESERVED*'\0')
+ len(self.salt), len(self.digest), self.flags,
+ self.RESERVED*'\0')
padding = struct.pack(str(padding_size) + 'x')
ret = desc + encoded_name + self.salt + self.digest + padding
return bytearray(ret)
@@ -1480,7 +1496,8 @@ class AvbHashDescriptor(AvbDescriptor):
ha.update(self.salt)
ha.update(data)
digest = ha.digest()
- if digest != self.digest:
+ # The digest must match unless there is no digest in the descriptor.
+ if len(self.digest) != 0 and digest != self.digest:
sys.stderr.write('{} digest of {} does not match digest in descriptor\n'.
format(self.hash_algorithm, image_filename))
return False
@@ -1754,8 +1771,8 @@ class AvbFooter(object):
MAGIC = 'AVBf'
SIZE = 64
RESERVED = 28
- FOOTER_VERSION_MAJOR = 1
- FOOTER_VERSION_MINOR = 0
+ FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
+ FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
FORMAT_STRING = ('!4s2L' # magic, 2 x version.
'Q' # Original image size.
'Q' # Offset of VBMeta blob.
@@ -1889,7 +1906,7 @@ class AvbVBMetaHeader(object):
minor: The minor version of libavb that has support for the feature.
"""
self.required_libavb_version_minor = (
- min(self.required_libavb_version_minor, minor))
+ max(self.required_libavb_version_minor, minor))
def save(self, output):
"""Serializes the header (256 bytes) to disk.
@@ -2384,10 +2401,19 @@ class Avb(object):
"""
# If we're asked to calculate minimum required libavb version, we're done.
- #
- # NOTE: When we get to 1.1 and later this will get more complicated.
if print_required_libavb_version:
- print '1.0'
+ if include_descriptors_from_image:
+ # Use the bump logic in AvbVBMetaHeader to calculate the max required
+ # version of all included descriptors.
+ tmp_header = AvbVBMetaHeader()
+ for image in include_descriptors_from_image:
+ (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
+ tmp_header.bump_required_libavb_version_minor(
+ image_header.required_libavb_version_minor)
+ print '1.{}'.format(tmp_header.required_libavb_version_minor)
+ else:
+ # Descriptors aside, all vbmeta features are supported in 1.0.
+ print '1.0'
return
if not output:
@@ -2401,7 +2427,7 @@ class Avb(object):
kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
include_descriptors_from_image, signing_helper,
signing_helper_with_files, release_string,
- append_to_release_string)
+ append_to_release_string, 0)
# Write entire vbmeta blob (header, authentication, auxiliary).
output.seek(0)
@@ -2421,7 +2447,8 @@ class Avb(object):
ht_desc_to_setup,
include_descriptors_from_image, signing_helper,
signing_helper_with_files,
- release_string, append_to_release_string):
+ release_string, append_to_release_string,
+ required_libavb_version_minor):
"""Generates a VBMeta blob.
This blob contains the header (struct AvbVBMetaHeader), the
@@ -2453,6 +2480,7 @@ class Avb(object):
signing_helper_with_files: Same as signing_helper but uses files instead.
release_string: None or avbtool release string.
append_to_release_string: None or string to append.
+ required_libavb_version_minor: Use at least this required minor version.
Returns:
A bytearray() with the VBMeta blob.
@@ -2471,6 +2499,9 @@ class Avb(object):
if not descriptors:
descriptors = []
+ h = AvbVBMetaHeader()
+ h.bump_required_libavb_version_minor(required_libavb_version_minor)
+
# Insert chained partition descriptors, if any
if chain_partitions:
used_locations = {}
@@ -2548,7 +2579,11 @@ class Avb(object):
if include_descriptors_from_image:
for image in include_descriptors_from_image:
image_handler = ImageHandler(image.name)
- (_, _, image_descriptors, _) = self._parse_image(image_handler)
+ (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
+ image_handler)
+ # Bump the required libavb version to support all included descriptors.
+ h.bump_required_libavb_version_minor(
+ image_vbmeta_header.required_libavb_version_minor)
for desc in image_descriptors:
encoded_descriptors.extend(desc.encode())
@@ -2569,8 +2604,6 @@ class Avb(object):
raise AvbError('Key is wrong size for algorithm {}'.format(
algorithm_name))
- h = AvbVBMetaHeader()
-
# Override release string, if requested.
if isinstance(release_string, (str, unicode)):
h.release_string = release_string
@@ -2743,7 +2776,8 @@ class Avb(object):
signing_helper, signing_helper_with_files,
release_string, append_to_release_string,
output_vbmeta_image, do_not_append_vbmeta_image,
- print_required_libavb_version):
+ print_required_libavb_version, use_persistent_digest,
+ do_not_use_ab):
"""Implementation of the add_hash_footer on unsparse images.
Arguments:
@@ -2775,16 +2809,20 @@ class Avb(object):
output_vbmeta_image: If not None, also write vbmeta struct to this file.
do_not_append_vbmeta_image: If True, don't append vbmeta struct.
print_required_libavb_version: True to only print required libavb version.
+ use_persistent_digest: Use a persistent digest on device.
+ do_not_use_ab: This partition does not use A/B.
Raises:
AvbError: If an argument is incorrect.
"""
+ required_libavb_version_minor = 0
+ if use_persistent_digest or do_not_use_ab:
+ required_libavb_version_minor = 1
+
# If we're asked to calculate minimum required libavb version, we're done.
- #
- # NOTE: When we get to 1.1 and later this will get more complicated.
if print_required_libavb_version:
- print '1.0'
+ print '1.{}'.format(required_libavb_version_minor)
return
# First, calculate the maximum image size such that an image
@@ -2860,7 +2898,11 @@ class Avb(object):
h_desc.hash_algorithm = hash_algorithm
h_desc.partition_name = partition_name
h_desc.salt = salt
- h_desc.digest = digest
+ h_desc.flags = 0
+ if do_not_use_ab:
+ h_desc.flags |= 1 # AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
+ if not use_persistent_digest:
+ h_desc.digest = digest
# Generate the VBMeta footer.
ht_desc_to_setup = None
@@ -2870,7 +2912,7 @@ class Avb(object):
kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
include_descriptors_from_image, signing_helper,
signing_helper_with_files, release_string,
- append_to_release_string)
+ append_to_release_string, required_libavb_version_minor)
# Write vbmeta blob, if requested.
if output_vbmeta_image:
@@ -2934,7 +2976,8 @@ class Avb(object):
signing_helper_with_files,
release_string, append_to_release_string,
output_vbmeta_image, do_not_append_vbmeta_image,
- print_required_libavb_version):
+ print_required_libavb_version,
+ use_persistent_root_digest, do_not_use_ab):
"""Implements the 'add_hashtree_footer' command.
See https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for
@@ -2974,16 +3017,20 @@ class Avb(object):
output_vbmeta_image: If not None, also write vbmeta struct to this file.
do_not_append_vbmeta_image: If True, don't append vbmeta struct.
print_required_libavb_version: True to only print required libavb version.
+ use_persistent_root_digest: Use a persistent root digest on device.
+ do_not_use_ab: The partition does not use A/B.
Raises:
AvbError: If an argument is incorrect.
"""
+ required_libavb_version_minor = 0
+ if use_persistent_root_digest or do_not_use_ab:
+ required_libavb_version_minor = 1
+
# If we're asked to calculate minimum required libavb version, we're done.
- #
- # NOTE: When we get to 1.1 and later this will get more complicated.
if print_required_libavb_version:
- print '1.0'
+ print '1.{}'.format(required_libavb_version_minor)
return
digest_size = len(hashlib.new(name=hash_algorithm).digest())
@@ -3090,7 +3137,10 @@ class Avb(object):
ht_desc.hash_algorithm = hash_algorithm
ht_desc.partition_name = partition_name
ht_desc.salt = salt
- ht_desc.root_digest = root_digest
+ if do_not_use_ab:
+ ht_desc.flags |= 1 # AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB
+ if not use_persistent_root_digest:
+ ht_desc.root_digest = root_digest
# Write the hash tree
padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
@@ -3125,7 +3175,7 @@ class Avb(object):
kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
include_descriptors_from_image, signing_helper,
signing_helper_with_files, release_string,
- append_to_release_string)
+ append_to_release_string, required_libavb_version_minor)
padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
len(vbmeta_blob))
vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
@@ -3517,6 +3567,25 @@ class AvbTool(object):
help='Set the HASHTREE_DISABLED flag',
action='store_true')
+ def _add_common_footer_args(self, sub_parser):
+ """Adds arguments used by add_*_footer sub-commands.
+
+ Arguments:
+ sub_parser: The parser to add arguments to.
+ """
+ sub_parser.add_argument('--use_persistent_digest',
+ help='Use a persistent digest on device instead of '
+ 'storing the digest in the descriptor. This '
+ 'cannot be used with A/B so must be combined '
+ 'with --do_not_use_ab when an A/B suffix is '
+ 'expected at runtime.',
+ action='store_true')
+ sub_parser.add_argument('--do_not_use_ab',
+ help='The partition does not use A/B even when an '
+ 'A/B suffix is present. This must not be used '
+ 'for vbmeta or chained partitions.',
+ action='store_true')
+
def _fixup_common_args(self, args):
"""Common fixups needed by subcommands.
@@ -3598,6 +3667,7 @@ class AvbTool(object):
'to the image'),
action='store_true')
self._add_common_args(sub_parser)
+ self._add_common_footer_args(sub_parser)
sub_parser.set_defaults(func=self.add_hash_footer)
sub_parser = subparsers.add_parser('append_vbmeta_image',
@@ -3670,6 +3740,7 @@ class AvbTool(object):
action='store_true',
help='Adds kernel cmdline for setting up rootfs')
self._add_common_args(sub_parser)
+ self._add_common_footer_args(sub_parser)
sub_parser.set_defaults(func=self.add_hashtree_footer)
sub_parser = subparsers.add_parser('erase_footer',
@@ -3869,7 +3940,9 @@ class AvbTool(object):
args.append_to_release_string,
args.output_vbmeta_image,
args.do_not_append_vbmeta_image,
- args.print_required_libavb_version)
+ args.print_required_libavb_version,
+ args.use_persistent_digest,
+ args.do_not_use_ab)
def add_hashtree_footer(self, args):
"""Implements the 'add_hashtree_footer' sub-command."""
@@ -3900,7 +3973,9 @@ class AvbTool(object):
args.append_to_release_string,
args.output_vbmeta_image,
args.do_not_append_vbmeta_image,
- args.print_required_libavb_version)
+ args.print_required_libavb_version,
+ args.use_persistent_digest,
+ args.do_not_use_ab)
def erase_footer(self, args):
"""Implements the 'erase_footer' sub-command."""
diff --git a/libavb/avb_cmdline.c b/libavb/avb_cmdline.c
index 3df7a30..426f909 100644
--- a/libavb/avb_cmdline.c
+++ b/libavb/avb_cmdline.c
@@ -33,14 +33,18 @@
* values. Returns NULL on OOM, otherwise the cmdline with values
* replaced.
*/
-char* avb_sub_cmdline(AvbOps* ops, const char* cmdline, const char* ab_suffix,
- bool using_boot_for_vbmeta) {
+char* avb_sub_cmdline(AvbOps* ops,
+ const char* cmdline,
+ const char* ab_suffix,
+ bool using_boot_for_vbmeta,
+ const AvbCmdlineSubstList* additional_substitutions) {
const char* part_name_str[NUM_GUIDS] = {"system", "boot", "vbmeta"};
const char* replace_str[NUM_GUIDS] = {"$(ANDROID_SYSTEM_PARTUUID)",
"$(ANDROID_BOOT_PARTUUID)",
"$(ANDROID_VBMETA_PARTUUID)"};
char* ret = NULL;
AvbIOResult io_ret;
+ size_t n;
/* Special-case for when the top-level vbmeta struct is in the boot
* partition.
@@ -50,7 +54,7 @@ char* avb_sub_cmdline(AvbOps* ops, const char* cmdline, const char* ab_suffix,
}
/* Replace unique partition GUIDs */
- for (size_t n = 0; n < NUM_GUIDS; n++) {
+ for (n = 0; n < NUM_GUIDS; n++) {
char part_name[AVB_PART_NAME_MAX_SIZE];
char guid_buf[37];
@@ -67,7 +71,7 @@ char* avb_sub_cmdline(AvbOps* ops, const char* cmdline, const char* ab_suffix,
io_ret = ops->get_unique_guid_for_partition(
ops, part_name, guid_buf, sizeof guid_buf);
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
- return NULL;
+ goto fail;
} else if (io_ret != AVB_IO_RESULT_OK) {
avb_error("Error getting unique GUID for partition.\n");
goto fail;
@@ -85,6 +89,22 @@ char* avb_sub_cmdline(AvbOps* ops, const char* cmdline, const char* ab_suffix,
}
}
+ avb_assert(ret != NULL);
+
+ /* Replace any additional substitutions. */
+ if (additional_substitutions != NULL) {
+ for (n = 0; n < additional_substitutions->size; ++n) {
+ char* new_ret = avb_replace(ret,
+ additional_substitutions->tokens[n],
+ additional_substitutions->values[n]);
+ avb_free(ret);
+ ret = new_ret;
+ if (ret == NULL) {
+ goto fail;
+ }
+ }
+ }
+
return ret;
fail:
@@ -185,22 +205,11 @@ static int cmdline_append_hex(AvbSlotVerifyData* slot_data,
const char* key,
const uint8_t* data,
size_t data_len) {
- char hex_digits[17] = "0123456789abcdef";
- char* hex_data;
int ret;
- size_t n;
-
- hex_data = avb_malloc(data_len * 2 + 1);
+ char* hex_data = avb_bin2hex(data, data_len);
if (hex_data == NULL) {
return 0;
}
-
- for (n = 0; n < data_len; n++) {
- hex_data[n * 2] = hex_digits[data[n] >> 4];
- hex_data[n * 2 + 1] = hex_digits[data[n] & 0x0f];
- }
- hex_data[n * 2] = '\0';
-
ret = cmdline_append_option(slot_data, key, hex_data);
avb_free(hex_data);
return ret;
@@ -368,3 +377,68 @@ out:
return ret;
}
+AvbCmdlineSubstList* avb_new_cmdline_subst_list() {
+ return (AvbCmdlineSubstList*)avb_calloc(sizeof(AvbCmdlineSubstList));
+}
+
+void avb_free_cmdline_subst_list(AvbCmdlineSubstList* cmdline_subst) {
+ size_t i;
+ for (i = 0; i < cmdline_subst->size; ++i) {
+ avb_free(cmdline_subst->tokens[i]);
+ avb_free(cmdline_subst->values[i]);
+ }
+ cmdline_subst->size = 0;
+ avb_free(cmdline_subst);
+}
+
+AvbSlotVerifyResult avb_add_root_digest_substitution(
+ const char* part_name,
+ const uint8_t* digest,
+ size_t digest_size,
+ AvbCmdlineSubstList* out_cmdline_subst) {
+ const char* kDigestSubPrefix = "$(AVB_";
+ const char* kDigestSubSuffix = "_ROOT_DIGEST)";
+ size_t part_name_len = avb_strlen(part_name);
+ size_t list_index = out_cmdline_subst->size;
+
+ avb_assert(part_name_len < AVB_PART_NAME_MAX_SIZE);
+ avb_assert(digest_size <= AVB_SHA512_DIGEST_SIZE);
+ if (part_name_len >= AVB_PART_NAME_MAX_SIZE ||
+ digest_size > AVB_SHA512_DIGEST_SIZE) {
+ return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ }
+
+ if (out_cmdline_subst->size >= AVB_MAX_NUM_CMDLINE_SUBST) {
+ /* The list is full. Currently dynamic growth of this list is not supported.
+ */
+ return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ }
+
+ /* Construct the token to replace in the command line based on the partition
+ * name. For partition 'foo', this will be '$(AVB_FOO_ROOT_DIGEST)'.
+ */
+ out_cmdline_subst->tokens[list_index] =
+ avb_strdupv(kDigestSubPrefix, part_name, kDigestSubSuffix, NULL);
+ if (out_cmdline_subst->tokens[list_index] == NULL) {
+ goto fail;
+ }
+ avb_uppercase(out_cmdline_subst->tokens[list_index]);
+
+ /* The digest value is hex encoded when inserted in the command line. */
+ out_cmdline_subst->values[list_index] = avb_bin2hex(digest, digest_size);
+ if (out_cmdline_subst->values[list_index] == NULL) {
+ goto fail;
+ }
+
+ out_cmdline_subst->size++;
+ return AVB_SLOT_VERIFY_RESULT_OK;
+
+fail:
+ if (out_cmdline_subst->tokens[list_index]) {
+ avb_free(out_cmdline_subst->tokens[list_index]);
+ }
+ if (out_cmdline_subst->values[list_index]) {
+ avb_free(out_cmdline_subst->values[list_index]);
+ }
+ return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+}
diff --git a/libavb/avb_cmdline.h b/libavb/avb_cmdline.h
index 648e202..996535d 100644
--- a/libavb/avb_cmdline.h
+++ b/libavb/avb_cmdline.h
@@ -41,12 +41,24 @@
*/
#define AVB_PART_NAME_MAX_SIZE 32
+#define AVB_MAX_NUM_CMDLINE_SUBST 10
+
+/* Holds information about command-line substitutions. */
+typedef struct AvbCmdlineSubstList {
+ size_t size;
+ char* tokens[AVB_MAX_NUM_CMDLINE_SUBST];
+ char* values[AVB_MAX_NUM_CMDLINE_SUBST];
+} AvbCmdlineSubstList;
+
/* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with
* values. Returns NULL on OOM, otherwise the cmdline with values
* replaced.
*/
-char* avb_sub_cmdline(AvbOps* ops, const char* cmdline, const char* ab_suffix,
- bool using_boot_for_vbmeta);
+char* avb_sub_cmdline(AvbOps* ops,
+ const char* cmdline,
+ const char* ab_suffix,
+ bool using_boot_for_vbmeta,
+ const AvbCmdlineSubstList* additional_substitutions);
AvbSlotVerifyResult avb_append_options(
AvbOps* ops,
@@ -55,4 +67,24 @@ AvbSlotVerifyResult avb_append_options(
AvbAlgorithmType algorithm_type,
AvbHashtreeErrorMode hashtree_error_mode);
+/* Allocates and initializes a new command line substitution list. Free with
+ * |avb_free_cmdline_subst_list|.
+ */
+AvbCmdlineSubstList* avb_new_cmdline_subst_list(void);
+
+/* Use this instead of |avb_free| to deallocate a AvbCmdlineSubstList. */
+void avb_free_cmdline_subst_list(AvbCmdlineSubstList* cmdline_subst);
+
+/* Adds a hashtree root digest to be substituted in $(AVB_*_ROOT_DIGEST)
+ * variables. The partition name differentiates the variable. For example, if
+ * |part_name| is "foo" then $(AVB_FOO_ROOT_DIGEST) will be substituted with the
+ * hex encoding of the digest. The substitution will be added to
+ * |out_cmdline_subst|. Returns AVB_SLOT_VERIFY_RESULT_OK on success.
+ */
+AvbSlotVerifyResult avb_add_root_digest_substitution(
+ const char* part_name,
+ const uint8_t* digest,
+ size_t digest_size,
+ AvbCmdlineSubstList* out_cmdline_subst);
+
#endif
diff --git a/libavb/avb_crypto.h b/libavb/avb_crypto.h
index 7e8d7e2..0903baa 100644
--- a/libavb/avb_crypto.h
+++ b/libavb/avb_crypto.h
@@ -44,6 +44,9 @@ extern "C" {
/* Size of a RSA-8192 signature. */
#define AVB_RSA8192_NUM_BYTES 1024
+/* Size in bytes of a SHA-1 digest. */
+#define AVB_SHA1_DIGEST_SIZE 20
+
/* Size in bytes of a SHA-256 digest. */
#define AVB_SHA256_DIGEST_SIZE 32
diff --git a/libavb/avb_hash_descriptor.c b/libavb/avb_hash_descriptor.c
index 2e427de..3a6b8c8 100644
--- a/libavb/avb_hash_descriptor.c
+++ b/libavb/avb_hash_descriptor.c
@@ -44,6 +44,7 @@ bool avb_hash_descriptor_validate_and_byteswap(const AvbHashDescriptor* src,
dest->partition_name_len = avb_be32toh(dest->partition_name_len);
dest->salt_len = avb_be32toh(dest->salt_len);
dest->digest_len = avb_be32toh(dest->digest_len);
+ dest->flags = avb_be32toh(dest->flags);
/* Check that partition_name, salt, and digest are fully contained. */
expected_size = sizeof(AvbHashDescriptor) - sizeof(AvbDescriptor);
diff --git a/libavb/avb_hash_descriptor.h b/libavb/avb_hash_descriptor.h
index 2668118..9ee8997 100644
--- a/libavb/avb_hash_descriptor.h
+++ b/libavb/avb_hash_descriptor.h
@@ -35,6 +35,16 @@
extern "C" {
#endif
+/* Flags for hash descriptors.
+ *
+ * AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB: Do not apply the default A/B
+ * partition logic to this partition. This is intentionally a negative boolean
+ * because A/B should be both the default and most used in practice.
+ */
+typedef enum {
+ AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB = (1 << 0),
+} AvbHashDescriptorFlags;
+
/* A descriptor containing information about hash for an image.
*
* This descriptor is typically used for boot partitions to verify the
@@ -46,6 +56,10 @@ extern "C" {
*
* The |reserved| field is for future expansion and must be set to NUL
* bytes.
+ *
+ * Changes in v1.1:
+ * - flags field is added which supports AVB_HASH_DESCRIPTOR_FLAGS_USE_AB
+ * - digest_len may be zero, which indicates the use of a persistent digest
*/
typedef struct AvbHashDescriptor {
AvbDescriptor parent_descriptor;
@@ -54,7 +68,8 @@ typedef struct AvbHashDescriptor {
uint32_t partition_name_len;
uint32_t salt_len;
uint32_t digest_len;
- uint8_t reserved[64];
+ uint32_t flags;
+ uint8_t reserved[60];
} AVB_ATTR_PACKED AvbHashDescriptor;
/* Copies |src| to |dest| and validates, byte-swapping fields in the
diff --git a/libavb/avb_hashtree_descriptor.c b/libavb/avb_hashtree_descriptor.c
index b961e47..0822458 100644
--- a/libavb/avb_hashtree_descriptor.c
+++ b/libavb/avb_hashtree_descriptor.c
@@ -52,6 +52,7 @@ bool avb_hashtree_descriptor_validate_and_byteswap(
dest->partition_name_len = avb_be32toh(dest->partition_name_len);
dest->salt_len = avb_be32toh(dest->salt_len);
dest->root_digest_len = avb_be32toh(dest->root_digest_len);
+ dest->flags = avb_be32toh(dest->flags);
/* Check that partition_name, salt, and root_digest are fully contained. */
expected_size = sizeof(AvbHashtreeDescriptor) - sizeof(AvbDescriptor);
diff --git a/libavb/avb_hashtree_descriptor.h b/libavb/avb_hashtree_descriptor.h
index a5aafbf..d0f7e2c 100644
--- a/libavb/avb_hashtree_descriptor.h
+++ b/libavb/avb_hashtree_descriptor.h
@@ -35,6 +35,16 @@
extern "C" {
#endif
+/* Flags for hashtree descriptors.
+ *
+ * AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB: Do not apply the default A/B
+ * partition logic to this partition. This is intentionally a negative boolean
+ * because A/B should be both the default and most used in practice.
+ */
+typedef enum {
+ AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB = (1 << 0),
+} AvbHashtreeDescriptorFlags;
+
/* A descriptor containing information about a dm-verity hashtree.
*
* Hash-trees are used to verify large partitions typically containing
@@ -48,6 +58,10 @@ extern "C" {
*
* The |reserved| field is for future expansion and must be set to NUL
* bytes.
+ *
+ * Changes in v1.1:
+ * - flags field is added which supports AVB_HASHTREE_DESCRIPTOR_FLAGS_USE_AB
+ * - digest_len may be zero, which indicates the use of a persistent digest
*/
typedef struct AvbHashtreeDescriptor {
AvbDescriptor parent_descriptor;
@@ -64,7 +78,8 @@ typedef struct AvbHashtreeDescriptor {
uint32_t partition_name_len;
uint32_t salt_len;
uint32_t root_digest_len;
- uint8_t reserved[64];
+ uint32_t flags;
+ uint8_t reserved[60];
} AVB_ATTR_PACKED AvbHashtreeDescriptor;
/* Copies |src| to |dest| and validates, byte-swapping fields in the
diff --git a/libavb/avb_ops.h b/libavb/avb_ops.h
index bfc21fd..77f7ec3 100644
--- a/libavb/avb_ops.h
+++ b/libavb/avb_ops.h
@@ -35,6 +35,9 @@
extern "C" {
#endif
+/* Well-known names of named persistent values. */
+#define AVB_NPV_PERSISTENT_DIGEST_PREFIX "avb.persistent_digest."
+
/* Return codes used for I/O operations.
*
* AVB_IO_RESULT_OK is returned if the requested operation was
@@ -51,13 +54,25 @@ extern "C" {
* AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION is returned if the
* range of bytes requested to be read or written is outside the range
* of the partition.
+ *
+ * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE is returned if a named persistent value
+ * does not exist.
+ *
+ * AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE is returned if a named persistent
+ * value size is not supported or does not match the expected size.
+ *
+ * AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE is returned if a buffer is too small
+ * for the requested operation.
*/
typedef enum {
AVB_IO_RESULT_OK,
AVB_IO_RESULT_ERROR_OOM,
AVB_IO_RESULT_ERROR_IO,
AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION,
- AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION
+ AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION,
+ AVB_IO_RESULT_ERROR_NO_SUCH_VALUE,
+ AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE,
+ AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE,
} AvbIOResult;
struct AvbOps;
@@ -240,6 +255,53 @@ struct AvbOps {
AvbIOResult (*get_size_of_partition)(AvbOps* ops,
const char* partition,
uint64_t* out_size_num_bytes);
+
+ /* Reads a persistent value corresponding to the given |name|. The value is
+ * returned in |out_buffer| which must point to |buffer_size| bytes. On
+ * success |out_num_bytes_read| contains the number of bytes read into
+ * |out_buffer|. If AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE is returned,
+ * |out_num_bytes_read| contains the number of bytes that would have been read
+ * which can be used to allocate a buffer.
+ *
+ * The |buffer_size| may be zero and the |out_buffer| may be NULL, but if
+ * |out_buffer| is NULL then |buffer_size| *must* be zero.
+ *
+ * Returns AVB_IO_RESULT_OK on success, otherwise an error code.
+ *
+ * If the value does not exist, is not supported, or is not populated, returns
+ * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. If |buffer_size| is smaller than the
+ * size of the stored value, returns AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE.
+ *
+ * This operation is currently only used to support persistent digests. If a
+ * device does not use persistent digests this function pointer can be set to
+ * NULL.
+ */
+ AvbIOResult (*read_persistent_value)(AvbOps* ops,
+ const char* name,
+ size_t buffer_size,
+ uint8_t* out_buffer,
+ size_t* out_num_bytes_read);
+
+ /* Writes a persistent value corresponding to the given |name|. The value is
+ * supplied in |value| which must point to |value_size| bytes. Any existing
+ * value with the same name is overwritten. If |value_size| is zero, future
+ * calls to |read_persistent_value| will return
+ * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE.
+ *
+ * Returns AVB_IO_RESULT_OK on success, otherwise an error code.
+ *
+ * If the value |name| is not supported, returns
+ * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. If the |value_size| is not supported,
+ * returns AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE.
+ *
+ * This operation is currently only used to support persistent digests. If a
+ * device does not use persistent digests this function pointer can be set to
+ * NULL.
+ */
+ AvbIOResult (*write_persistent_value)(AvbOps* ops,
+ const char* name,
+ size_t value_size,
+ const uint8_t* value);
};
#ifdef __cplusplus
diff --git a/libavb/avb_slot_verify.c b/libavb/avb_slot_verify.c
index f8c941c..3e6b04c 100644
--- a/libavb/avb_slot_verify.c
+++ b/libavb/avb_slot_verify.c
@@ -27,6 +27,7 @@
#include "avb_cmdline.h"
#include "avb_footer.h"
#include "avb_hash_descriptor.h"
+#include "avb_hashtree_descriptor.h"
#include "avb_kernel_cmdline_descriptor.h"
#include "avb_sha.h"
#include "avb_util.h"
@@ -65,10 +66,11 @@ static inline bool result_should_continue(AvbSlotVerifyResult result) {
return false;
}
-static AvbSlotVerifyResult load_full_partition(
- AvbOps* ops, const char* part_name,
- uint64_t image_size, uint8_t** out_image_buf,
- bool* out_image_preloaded) {
+static AvbSlotVerifyResult load_full_partition(AvbOps* ops,
+ const char* part_name,
+ uint64_t image_size,
+ uint8_t** out_image_buf,
+ bool* out_image_preloaded) {
size_t part_num_read;
AvbIOResult io_ret;
@@ -110,9 +112,12 @@ static AvbSlotVerifyResult load_full_partition(
return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
}
- io_ret = ops->read_from_partition(
- ops, part_name, 0 /* offset */, image_size, *out_image_buf,
- &part_num_read);
+ io_ret = ops->read_from_partition(ops,
+ part_name,
+ 0 /* offset */,
+ image_size,
+ *out_image_buf,
+ &part_num_read);
if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
} else if (io_ret != AVB_IO_RESULT_OK) {
@@ -128,6 +133,47 @@ static AvbSlotVerifyResult load_full_partition(
return AVB_SLOT_VERIFY_RESULT_OK;
}
+static AvbSlotVerifyResult read_persistent_digest(AvbOps* ops,
+ const char* part_name,
+ size_t expected_digest_size,
+ uint8_t* out_digest) {
+ char* persistent_value_name = NULL;
+ AvbIOResult io_ret = AVB_IO_RESULT_OK;
+ size_t stored_digest_size = 0;
+
+ if (ops->read_persistent_value == NULL) {
+ avb_errorv(part_name, ": Persistent values are not implemented.\n", NULL);
+ return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ }
+ persistent_value_name =
+ avb_strdupv(AVB_NPV_PERSISTENT_DIGEST_PREFIX, part_name, NULL);
+ if (persistent_value_name == NULL) {
+ return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ }
+ io_ret = ops->read_persistent_value(ops,
+ persistent_value_name,
+ expected_digest_size,
+ out_digest,
+ &stored_digest_size);
+ avb_free(persistent_value_name);
+ if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+ return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ } else if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE) {
+ avb_errorv(part_name, ": Persistent digest does not exist.\n", NULL);
+ return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ } else if (io_ret == AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE ||
+ io_ret == AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE ||
+ expected_digest_size != stored_digest_size) {
+ avb_errorv(
+ part_name, ": Persistent digest is not of expected size.\n", NULL);
+ return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ } else if (io_ret != AVB_IO_RESULT_OK) {
+ avb_errorv(part_name, ": Error reading persistent digest.\n", NULL);
+ return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+ }
+ return AVB_SLOT_VERIFY_RESULT_OK;
+}
+
static AvbSlotVerifyResult load_and_verify_hash_partition(
AvbOps* ops,
const char* const* requested_partitions,
@@ -148,6 +194,9 @@ static AvbSlotVerifyResult load_and_verify_hash_partition(
size_t digest_len;
const char* found;
uint64_t image_size;
+ size_t expected_digest_len = 0;
+ uint8_t expected_digest_buf[AVB_SHA512_DIGEST_SIZE];
+ const uint8_t* expected_digest = NULL;
if (!avb_hash_descriptor_validate_and_byteswap(
(const AvbHashDescriptor*)descriptor, &hash_desc)) {
@@ -177,15 +226,35 @@ static AvbSlotVerifyResult load_and_verify_hash_partition(
goto out;
}
- if (!avb_str_concat(part_name,
- sizeof part_name,
- (const char*)desc_partition_name,
- hash_desc.partition_name_len,
- ab_suffix,
- avb_strlen(ab_suffix))) {
- avb_error("Partition name and suffix does not fit.\n");
+ if ((hash_desc.flags & AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB) != 0) {
+ /* No ab_suffix, just copy the partition name as is. */
+ if (hash_desc.partition_name_len >= AVB_PART_NAME_MAX_SIZE) {
+ avb_error("Partition name does not fit.\n");
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+ avb_memcpy(part_name, desc_partition_name, hash_desc.partition_name_len);
+ part_name[hash_desc.partition_name_len] = '\0';
+ } else if (hash_desc.digest_len == 0 && avb_strlen(ab_suffix) != 0) {
+ /* No ab_suffix allowed for partitions without a digest in the descriptor
+ * because these partitions hold data unique to this device and are not
+ * updated using an A/B scheme.
+ */
+ avb_error("Cannot use A/B with a persistent digest.\n");
ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
goto out;
+ } else {
+ /* Add ab_suffix to the partition name. */
+ if (!avb_str_concat(part_name,
+ sizeof part_name,
+ (const char*)desc_partition_name,
+ hash_desc.partition_name_len,
+ ab_suffix,
+ avb_strlen(ab_suffix))) {
+ avb_error("Partition name and suffix does not fit.\n");
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
}
/* If we're allowing verification errors then hash_desc.image_size
@@ -244,14 +313,31 @@ static AvbSlotVerifyResult load_and_verify_hash_partition(
goto out;
}
- if (digest_len != hash_desc.digest_len) {
+ if (hash_desc.digest_len == 0) {
+ // Expect a match to a persistent digest.
+ avb_debugv(part_name, ": No digest, using persistent digest.\n", NULL);
+ expected_digest_len = digest_len;
+ expected_digest = expected_digest_buf;
+ avb_assert(expected_digest_len <= sizeof(expected_digest_buf));
+ ret =
+ read_persistent_digest(ops, part_name, digest_len, expected_digest_buf);
+ if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
+ goto out;
+ }
+ } else {
+ // Expect a match to the digest in the descriptor.
+ expected_digest_len = hash_desc.digest_len;
+ expected_digest = desc_digest;
+ }
+
+ if (digest_len != expected_digest_len) {
avb_errorv(
part_name, ": Digest in descriptor not of expected size.\n", NULL);
ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
goto out;
}
- if (avb_safe_memcmp(digest, desc_digest, digest_len) != 0) {
+ if (avb_safe_memcmp(digest, expected_digest, digest_len) != 0) {
avb_errorv(part_name,
": Hash of data does not match digest in descriptor.\n",
NULL);
@@ -352,7 +438,7 @@ static AvbSlotVerifyResult load_requested_partitions(
goto out;
}
loaded_partition->data_size = image_size;
- loaded_partition->data = image_buf; /* Transferring the owner. */
+ loaded_partition->data = image_buf; /* Transferring the owner. */
loaded_partition->preloaded = image_preloaded;
image_buf = NULL;
image_preloaded = false;
@@ -382,7 +468,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
const uint8_t* expected_public_key,
size_t expected_public_key_length,
AvbSlotVerifyData* slot_data,
- AvbAlgorithmType* out_algorithm_type) {
+ AvbAlgorithmType* out_algorithm_type,
+ AvbCmdlineSubstList* out_additional_cmdline_subst) {
char full_partition_name[AVB_PART_NAME_MAX_SIZE];
AvbSlotVerifyResult ret;
AvbIOResult io_ret;
@@ -518,7 +605,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
NULL /* expected_public_key */,
0 /* expected_public_key_length */,
slot_data,
- out_algorithm_type);
+ out_algorithm_type,
+ out_additional_cmdline_subst);
goto out;
} else {
avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL);
@@ -714,7 +802,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
* checks that it matches what's in the hash descriptor.
*
* - hashtree descriptor: Do nothing since verification happens
- * on-the-fly from within the OS.
+ * on-the-fly from within the OS. (Unless the descriptor uses a
+ * persistent digest, in which case we need to find it).
*
* - chained partition descriptor: Load the footer, load the vbmeta
* image, verify vbmeta image (includes rollback checks, hash
@@ -785,18 +874,20 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
sizeof(AvbChainPartitionDescriptor);
chain_public_key = chain_partition_name + chain_desc.partition_name_len;
- sub_ret = load_and_verify_vbmeta(ops,
- requested_partitions,
- ab_suffix,
- allow_verification_error,
- toplevel_vbmeta_flags,
- chain_desc.rollback_index_location,
- (const char*)chain_partition_name,
- chain_desc.partition_name_len,
- chain_public_key,
- chain_desc.public_key_len,
- slot_data,
- NULL /* out_algorithm_type */);
+ sub_ret =
+ load_and_verify_vbmeta(ops,
+ requested_partitions,
+ ab_suffix,
+ allow_verification_error,
+ toplevel_vbmeta_flags,
+ chain_desc.rollback_index_location,
+ (const char*)chain_partition_name,
+ chain_desc.partition_name_len,
+ chain_public_key,
+ chain_desc.public_key_len,
+ slot_data,
+ NULL, /* out_algorithm_type */
+ NULL /* out_additional_cmdline_subst */);
if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
ret = sub_ret;
if (!result_should_continue(ret)) {
@@ -882,9 +973,90 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
}
} break;
- /* Explicit fall-through */
+ case AVB_DESCRIPTOR_TAG_HASHTREE: {
+ AvbHashtreeDescriptor hashtree_desc;
+
+ if (!avb_hashtree_descriptor_validate_and_byteswap(
+ (AvbHashtreeDescriptor*)descriptors[n], &hashtree_desc)) {
+ avb_errorv(
+ full_partition_name, ": Hashtree descriptor is invalid.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ /* We only need to continue when there is no digest in the descriptor.
+ * This is because the only processing here is to find the digest and
+ * make it available on the kernel command line.
+ */
+ if (hashtree_desc.root_digest_len == 0) {
+ char part_name[AVB_PART_NAME_MAX_SIZE];
+ size_t digest_len = 0;
+ uint8_t digest_buf[AVB_SHA512_DIGEST_SIZE];
+ const uint8_t* desc_partition_name =
+ ((const uint8_t*)descriptors[n]) + sizeof(AvbHashtreeDescriptor);
+
+ if (!avb_validate_utf8(desc_partition_name,
+ hashtree_desc.partition_name_len)) {
+ avb_error("Partition name is not valid UTF-8.\n");
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ /* No ab_suffix for partitions without a digest in the descriptor
+ * because these partitions hold data unique to this device and are
+ * not updated using an A/B scheme.
+ */
+ if ((hashtree_desc.flags &
+ AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB) == 0 &&
+ avb_strlen(ab_suffix) != 0) {
+ avb_error("Cannot use A/B with a persistent root digest.\n");
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+ if (hashtree_desc.partition_name_len >= AVB_PART_NAME_MAX_SIZE) {
+ avb_error("Partition name does not fit.\n");
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+ avb_memcpy(
+ part_name, desc_partition_name, hashtree_desc.partition_name_len);
+ part_name[hashtree_desc.partition_name_len] = '\0';
+
+ /* Determine the expected digest size from the hash algorithm. */
+ if (avb_strcmp((const char*)hashtree_desc.hash_algorithm, "sha1") ==
+ 0) {
+ digest_len = AVB_SHA1_DIGEST_SIZE;
+ } else if (avb_strcmp((const char*)hashtree_desc.hash_algorithm,
+ "sha256") == 0) {
+ digest_len = AVB_SHA256_DIGEST_SIZE;
+ } else if (avb_strcmp((const char*)hashtree_desc.hash_algorithm,
+ "sha512") == 0) {
+ digest_len = AVB_SHA512_DIGEST_SIZE;
+ } else {
+ avb_errorv(part_name, ": Unsupported hash algorithm.\n", NULL);
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ goto out;
+ }
+
+ ret = read_persistent_digest(ops, part_name, digest_len, digest_buf);
+ if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
+ goto out;
+ }
+
+ if (out_additional_cmdline_subst) {
+ ret =
+ avb_add_root_digest_substitution(part_name,
+ digest_buf,
+ digest_len,
+ out_additional_cmdline_subst);
+ if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
+ goto out;
+ }
+ }
+ }
+ } break;
+
case AVB_DESCRIPTOR_TAG_PROPERTY:
- case AVB_DESCRIPTOR_TAG_HASHTREE:
/* Do nothing. */
break;
}
@@ -932,6 +1104,7 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
AvbVBMetaImageHeader toplevel_vbmeta;
bool allow_verification_error =
(flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR);
+ AvbCmdlineSubstList* additional_cmdline_subst = NULL;
/* Fail early if we're missing the AvbOps needed for slot verification.
*
@@ -976,6 +1149,12 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
goto fail;
}
+ additional_cmdline_subst = avb_new_cmdline_subst_list();
+ if (additional_cmdline_subst == NULL) {
+ ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ goto fail;
+ }
+
ret = load_and_verify_vbmeta(ops,
requested_partitions,
ab_suffix,
@@ -987,7 +1166,8 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
NULL /* expected_public_key */,
0 /* expected_public_key_length */,
slot_data,
- &algorithm_type);
+ &algorithm_type,
+ additional_cmdline_subst);
if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) {
goto fail;
}
@@ -1032,9 +1212,11 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
/* Add options - any failure in avb_append_options() is either an
* I/O or OOM error.
*/
- AvbSlotVerifyResult sub_ret = avb_append_options(
- ops, slot_data, &toplevel_vbmeta, algorithm_type,
- hashtree_error_mode);
+ AvbSlotVerifyResult sub_ret = avb_append_options(ops,
+ slot_data,
+ &toplevel_vbmeta,
+ algorithm_type,
+ hashtree_error_mode);
if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
ret = sub_ret;
goto fail;
@@ -1044,8 +1226,11 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
/* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */
if (slot_data->cmdline != NULL) {
char* new_cmdline;
- new_cmdline = avb_sub_cmdline(
- ops, slot_data->cmdline, ab_suffix, using_boot_for_vbmeta);
+ new_cmdline = avb_sub_cmdline(ops,
+ slot_data->cmdline,
+ ab_suffix,
+ using_boot_for_vbmeta,
+ additional_cmdline_subst);
if (new_cmdline != slot_data->cmdline) {
if (new_cmdline == NULL) {
ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
@@ -1063,6 +1248,9 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
}
}
+ avb_free_cmdline_subst_list(additional_cmdline_subst);
+ additional_cmdline_subst = NULL;
+
if (!allow_verification_error) {
avb_assert(ret == AVB_SLOT_VERIFY_RESULT_OK);
}
@@ -1073,6 +1261,9 @@ fail:
if (slot_data != NULL) {
avb_slot_verify_data_free(slot_data);
}
+ if (additional_cmdline_subst != NULL) {
+ avb_free_cmdline_subst_list(additional_cmdline_subst);
+ }
return ret;
}
diff --git a/libavb/avb_util.c b/libavb/avb_util.c
index fafde01..c04c79a 100644
--- a/libavb/avb_util.c
+++ b/libavb/avb_util.c
@@ -401,3 +401,30 @@ const char* avb_basename(const char* str) {
}
return str;
}
+
+void avb_uppercase(char* str) {
+ size_t i;
+ for (i = 0; str[i] != '\0'; ++i) {
+ if (str[i] <= 0x7A && str[i] >= 0x61) {
+ str[i] -= 0x20;
+ }
+ }
+}
+
+char* avb_bin2hex(const uint8_t* data, size_t data_len) {
+ const char hex_digits[17] = "0123456789abcdef";
+ char* hex_data;
+ size_t n;
+
+ hex_data = avb_malloc(data_len * 2 + 1);
+ if (hex_data == NULL) {
+ return NULL;
+ }
+
+ for (n = 0; n < data_len; n++) {
+ hex_data[n * 2] = hex_digits[data[n] >> 4];
+ hex_data[n * 2 + 1] = hex_digits[data[n] & 0x0f];
+ }
+ hex_data[n * 2] = '\0';
+ return hex_data;
+}
diff --git a/libavb/avb_util.h b/libavb/avb_util.h
index 07c3258..be1b3c9 100644
--- a/libavb/avb_util.h
+++ b/libavb/avb_util.h
@@ -270,6 +270,16 @@ uint32_t avb_crc32(const uint8_t* buf, size_t buf_size);
*/
const char* avb_basename(const char* str);
+/* Converts any ascii lowercase characters in |str| to uppercase in-place.
+ * |str| must be NUL-terminated and valid UTF-8.
+ */
+void avb_uppercase(char* str);
+
+/* Converts |data_len| bytes of |data| to hex and returns the result. Returns
+ * NULL on OOM. Caller must free the returned string with avb_free.
+ */
+char* avb_bin2hex(const uint8_t* data, size_t data_len);
+
#ifdef __cplusplus
}
#endif
diff --git a/libavb/avb_version.h b/libavb/avb_version.h
index 9d92970..ce43136 100644
--- a/libavb/avb_version.h
+++ b/libavb/avb_version.h
@@ -37,7 +37,7 @@ extern "C" {
/* The version number of AVB - keep in sync with avbtool. */
#define AVB_VERSION_MAJOR 1
-#define AVB_VERSION_MINOR 0
+#define AVB_VERSION_MINOR 1
#define AVB_VERSION_SUB 0
/* Returns a NUL-terminated string for the libavb version in use. The
diff --git a/test/avb_atx_validate_unittest.cc b/test/avb_atx_validate_unittest.cc
index 0299b51..c32ecf8 100644
--- a/test/avb_atx_validate_unittest.cc
+++ b/test/avb_atx_validate_unittest.cc
@@ -173,6 +173,21 @@ class AvbAtxValidateTest : public ::testing::Test,
return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
}
+ AvbIOResult read_persistent_value(const char* name,
+ size_t buffer_size,
+ uint8_t* out_buffer,
+ size_t* out_num_bytes_read) override {
+ // Expect method not used.
+ return AVB_IO_RESULT_ERROR_NO_SUCH_VALUE;
+ }
+
+ AvbIOResult write_persistent_value(const char* name,
+ size_t value_size,
+ const uint8_t* value) override {
+ // Expect method not used.
+ return AVB_IO_RESULT_ERROR_NO_SUCH_VALUE;
+ }
+
AvbIOResult read_permanent_attributes(
AvbAtxPermanentAttributes* attributes) override {
if (fail_read_permanent_attributes_) {
diff --git a/test/avb_slot_verify_unittest.cc b/test/avb_slot_verify_unittest.cc
index 86d756e..9584439 100644
--- a/test/avb_slot_verify_unittest.cc
+++ b/test/avb_slot_verify_unittest.cc
@@ -33,12 +33,14 @@
namespace avb {
-class AvbSlotVerifyTest : public BaseAvbToolTest {
+class AvbSlotVerifyTest : public BaseAvbToolTest,
+ public FakeAvbOpsDelegateWithDefaults {
public:
AvbSlotVerifyTest() {}
virtual void SetUp() override {
BaseAvbToolTest::SetUp();
+ ops_.set_delegate(this);
ops_.set_partition_dir(testdir_);
ops_.set_stored_rollback_indexes({{0, 0}, {1, 0}, {2, 0}, {3, 0}});
ops_.set_stored_is_device_unlocked(false);
@@ -47,8 +49,6 @@ class AvbSlotVerifyTest : public BaseAvbToolTest {
void CmdlineWithHashtreeVerification(bool hashtree_verification_on);
void CmdlineWithChainedHashtreeVerification(bool hashtree_verification_on);
void VerificationDisabled(bool use_avbctl, bool preload);
-
- FakeAvbOps ops_;
};
TEST_F(AvbSlotVerifyTest, Basic) {
@@ -73,7 +73,7 @@ TEST_F(AvbSlotVerifyTest, Basic) {
EXPECT_NE(nullptr, slot_data);
EXPECT_EQ(
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1152 "
"androidboot.vbmeta.digest="
@@ -106,7 +106,7 @@ TEST_F(AvbSlotVerifyTest, BasicSha512) {
EXPECT_NE(nullptr, slot_data);
EXPECT_EQ(
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha512 androidboot.vbmeta.size=1152 "
"androidboot.vbmeta.digest="
@@ -142,7 +142,7 @@ TEST_F(AvbSlotVerifyTest, BasicUnlocked) {
EXPECT_NE(nullptr, slot_data);
EXPECT_EQ(
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=unlocked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1152 "
"androidboot.vbmeta.digest="
@@ -506,6 +506,7 @@ TEST_F(AvbSlotVerifyTest, HashDescriptorInVBMeta) {
" Salt: deadbeef\n"
" Digest: "
"184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\n"
+ " Flags: 0\n"
" Kernel Cmdline descriptor:\n"
" Flags: 0\n"
" Kernel Cmdline: 'cmdline in hash footer "
@@ -562,7 +563,7 @@ TEST_F(AvbSlotVerifyTest, HashDescriptorInVBMeta) {
"cmdline in vbmeta 1234-fake-guid-for:boot_a cmdline in hash footer "
"1234-fake-guid-for:system_a "
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1472 "
"androidboot.vbmeta.digest="
@@ -807,6 +808,7 @@ TEST_F(AvbSlotVerifyTest, HashDescriptorInChainedPartition) {
" Salt: deadbeef\n"
" Digest: "
"184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\n"
+ " Flags: 0\n"
" Kernel Cmdline descriptor:\n"
" Flags: 0\n"
" Kernel Cmdline: 'cmdline2 in hash footer'\n",
@@ -883,7 +885,7 @@ TEST_F(AvbSlotVerifyTest, HashDescriptorInChainedPartition) {
EXPECT_EQ(
"cmdline2 in hash footer cmdline2 in vbmeta "
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=4416 "
"androidboot.vbmeta.digest="
@@ -1219,7 +1221,7 @@ TEST_F(AvbSlotVerifyTest, ChainedPartitionNoSlots) {
EXPECT_EQ(
"cmdline2 in hash footer cmdline2 in vbmeta "
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=4416 "
"androidboot.vbmeta.digest="
@@ -1290,13 +1292,15 @@ TEST_F(AvbSlotVerifyTest, PartitionsOtherThanBoot) {
" Salt: deadbeef\n"
" Digest: "
"184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\n"
+ " Flags: 0\n"
" Hash descriptor:\n"
" Image Size: 10485760 bytes\n"
" Hash Algorithm: sha256\n"
" Partition Name: bar\n"
" Salt: deadbeef\n"
" Digest: "
- "baea4bbd261d0edf4d1fe5e6e5a36976c291eeba66b6a46fa81dba691327a727\n",
+ "baea4bbd261d0edf4d1fe5e6e5a36976c291eeba66b6a46fa81dba691327a727\n"
+ " Flags: 0\n",
InfoImage(vbmeta_image_path_));
ops_.set_expected_public_key(
@@ -1401,13 +1405,15 @@ TEST_F(AvbSlotVerifyTest, OnlyLoadWhatHasBeenRequested) {
" Salt: deadbeef\n"
" Digest: "
"184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\n"
+ " Flags: 0\n"
" Hash descriptor:\n"
" Image Size: 10485760 bytes\n"
" Hash Algorithm: sha256\n"
" Partition Name: bar\n"
" Salt: deadbeef\n"
" Digest: "
- "baea4bbd261d0edf4d1fe5e6e5a36976c291eeba66b6a46fa81dba691327a727\n",
+ "baea4bbd261d0edf4d1fe5e6e5a36976c291eeba66b6a46fa81dba691327a727\n"
+ " Flags: 0\n",
InfoImage(vbmeta_image_path_));
ops_.set_expected_public_key(
@@ -1464,7 +1470,7 @@ TEST_F(AvbSlotVerifyTest, PublicKeyMetadata) {
EXPECT_NE(nullptr, slot_data);
EXPECT_EQ(
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=2688 "
"androidboot.vbmeta.digest="
@@ -1572,7 +1578,7 @@ void AvbSlotVerifyTest::CmdlineWithHashtreeVerification(
"restart_on_corruption ignore_zero_blocks\" root=/dev/dm-0 "
"should_be_in_both=1 "
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1536 "
"androidboot.vbmeta.digest="
@@ -1586,7 +1592,7 @@ void AvbSlotVerifyTest::CmdlineWithHashtreeVerification(
EXPECT_EQ(
"root=PARTUUID=1234-fake-guid-for:system_a should_be_in_both=1 "
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1536 "
"androidboot.vbmeta.digest="
@@ -1665,6 +1671,7 @@ void AvbSlotVerifyTest::CmdlineWithChainedHashtreeVerification(
" Partition Name: foobar\n"
" Salt: d00df00d\n"
" Root Digest: e811611467dcd6e8dc4324e45f706c2bdd51db67\n"
+ " Flags: 0\n"
" Kernel Cmdline descriptor:\n"
" Flags: 1\n"
" Kernel Cmdline: 'dm=\"1 vroot none ro 1,0 2056 verity 1 "
@@ -1747,7 +1754,7 @@ void AvbSlotVerifyTest::CmdlineWithChainedHashtreeVerification(
"restart_on_corruption ignore_zero_blocks\" root=/dev/dm-0 "
"should_be_in_both=1 "
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=3456 "
"androidboot.vbmeta.digest="
@@ -1761,7 +1768,7 @@ void AvbSlotVerifyTest::CmdlineWithChainedHashtreeVerification(
EXPECT_EQ(
"root=PARTUUID=1234-fake-guid-for:system_a should_be_in_both=1 "
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=3456 "
"androidboot.vbmeta.digest="
@@ -1780,8 +1787,8 @@ TEST_F(AvbSlotVerifyTest, CmdlineWithChainedHashtreeVerificationOn) {
CmdlineWithChainedHashtreeVerification(true);
}
-void AvbSlotVerifyTest::VerificationDisabled(
- bool use_avbctl, bool preload_boot) {
+void AvbSlotVerifyTest::VerificationDisabled(bool use_avbctl,
+ bool preload_boot) {
const size_t boot_part_size = 32 * 1024 * 1024;
const size_t dtbo_part_size = 4 * 1024 * 1024;
const size_t rootfs_size = 1028 * 1024;
@@ -2046,6 +2053,7 @@ TEST_F(AvbSlotVerifyTest, NoVBMetaPartition) {
" Salt: d00df00d\n"
" Digest: "
"4c109399b20e476bab15363bff55740add83e1c1e97e0b132f5c713ddd8c7868\n"
+ " Flags: 0\n"
" Chain Partition descriptor:\n"
" Partition Name: bazboo\n"
" Rollback Index Location: 1\n"
@@ -2075,6 +2083,7 @@ TEST_F(AvbSlotVerifyTest, NoVBMetaPartition) {
" Partition Name: system\n"
" Salt: d00df00d\n"
" Root Digest: c9ffc3bfae5000269a55a56621547fd1fcf819df\n"
+ " Flags: 0\n"
" Hashtree descriptor:\n"
" Version of dm-verity: 1\n"
" Image Size: 8388608 bytes\n"
@@ -2088,7 +2097,8 @@ TEST_F(AvbSlotVerifyTest, NoVBMetaPartition) {
" Hash Algorithm: sha1\n"
" Partition Name: foobar\n"
" Salt: d00df00d\n"
- " Root Digest: d52d93c988d336a79abe1c05240ae9a79a9b7d61\n",
+ " Root Digest: d52d93c988d336a79abe1c05240ae9a79a9b7d61\n"
+ " Flags: 0\n",
InfoImage(boot_path));
ops_.set_expected_public_key(
@@ -2116,7 +2126,7 @@ TEST_F(AvbSlotVerifyTest, NoVBMetaPartition) {
"4096 4096 4096 4096 sha1 c9ffc3bfae5000269a55a56621547fd1fcf819df "
"d00df00d 2 restart_on_corruption ignore_zero_blocks\" root=/dev/dm-0 "
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:boot "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=5312 "
"androidboot.vbmeta.digest="
@@ -2287,7 +2297,7 @@ TEST_F(AvbSlotVerifyTest, HashtreeErrorModes) {
"c9ffc3bfae5000269a55a56621547fd1fcf819df d00df00d 2 "
"restart_on_corruption ignore_zero_blocks\" root=/dev/dm-0 "
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 "
"androidboot.vbmeta.size=1664 "
@@ -2317,7 +2327,7 @@ TEST_F(AvbSlotVerifyTest, HashtreeErrorModes) {
"c9ffc3bfae5000269a55a56621547fd1fcf819df d00df00d 2 "
"restart_on_corruption ignore_zero_blocks\" root=/dev/dm-0 "
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 "
"androidboot.vbmeta.size=1664 "
@@ -2346,7 +2356,7 @@ TEST_F(AvbSlotVerifyTest, HashtreeErrorModes) {
"c9ffc3bfae5000269a55a56621547fd1fcf819df d00df00d 2 "
"ignore_zero_blocks ignore_zero_blocks\" root=/dev/dm-0 "
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 "
"androidboot.vbmeta.size=1664 "
@@ -2387,7 +2397,7 @@ TEST_F(AvbSlotVerifyTest, HashtreeErrorModes) {
"c9ffc3bfae5000269a55a56621547fd1fcf819df d00df00d 2 "
"ignore_corruption ignore_zero_blocks\" root=/dev/dm-0 "
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 "
"androidboot.vbmeta.size=1664 "
@@ -2426,7 +2436,7 @@ TEST_F(AvbSlotVerifyTest, HashtreeErrorModes) {
EXPECT_EQ(
"root=PARTUUID=1234-fake-guid-for:system "
"androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
- "androidboot.vbmeta.avb_version=1.0 "
+ "androidboot.vbmeta.avb_version=1.1 "
"androidboot.vbmeta.device_state=locked "
"androidboot.vbmeta.hash_alg=sha256 "
"androidboot.vbmeta.size=1664 "
@@ -2438,4 +2448,466 @@ TEST_F(AvbSlotVerifyTest, HashtreeErrorModes) {
}
}
+class AvbSlotVerifyTestWithPersistentDigest : public AvbSlotVerifyTest {
+ protected:
+ void SetupWithHashDescriptor(bool do_not_use_ab = true) {
+ const size_t factory_partition_size = 16 * 1024 * 1024;
+ const size_t factory_image_size = 5 * 1024 * 1024;
+ base::FilePath factory_path =
+ GenerateImage("factory.img", factory_image_size);
+
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer"
+ " --image %s"
+ " --rollback_index 0"
+ " --partition_name factory"
+ " --partition_size %zd"
+ " --salt deadbeef"
+ " --internal_release_string \"\""
+ " --use_persistent_digest %s",
+ factory_path.value().c_str(),
+ factory_partition_size,
+ do_not_use_ab ? "--do_not_use_ab" : "");
+
+ GenerateVBMetaImage(
+ "vbmeta_a.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf("--internal_release_string \"\" "
+ "--include_descriptors_from_image %s ",
+ factory_path.value().c_str()));
+
+ EXPECT_EQ(base::StringPrintf("Minimum libavb version: 1.1\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 704 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: ''\n"
+ "Descriptors:\n"
+ " Hash descriptor:\n"
+ " Image Size: 5242880 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: factory\n"
+ " Salt: deadbeef\n"
+ " Digest: \n"
+ " Flags: %d\n",
+ do_not_use_ab ? 1 : 0),
+ InfoImage(vbmeta_image_path_));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+ }
+
+ void SetupWithHashtreeDescriptor(bool do_not_use_ab = true) {
+ const size_t factory_partition_size = 16 * 1024 * 1024;
+ const size_t factory_image_size = 5 * 1024 * 1024;
+ base::FilePath factory_path =
+ GenerateImage("factory.img", factory_image_size);
+
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool add_hashtree_footer"
+ " --image %s"
+ " --rollback_index 0"
+ " --partition_name factory"
+ " --partition_size %zd"
+ " --salt deadbeef"
+ " --hash_algorithm %s"
+ " --internal_release_string \"\""
+ " --kernel_cmdline "
+ "'androidboot.vbmeta.root_digest.factory=$(AVB_FACTORY_ROOT_DIGEST)'"
+ " --use_persistent_digest %s",
+ factory_path.value().c_str(),
+ factory_partition_size,
+ verity_hash_algorithm_.c_str(),
+ do_not_use_ab ? "--do_not_use_ab" : "");
+
+ GenerateVBMetaImage(
+ "vbmeta_a.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ base::StringPrintf("--internal_release_string \"\" "
+ "--include_descriptors_from_image %s ",
+ factory_path.value().c_str()));
+
+ int expected_tree_size =
+ (verity_hash_algorithm_ == "sha512") ? 86016 : 45056;
+ int expected_fec_offset =
+ (verity_hash_algorithm_ == "sha512") ? 5328896 : 5287936;
+ EXPECT_EQ(base::StringPrintf("Minimum libavb version: 1.1\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 832 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: ''\n"
+ "Descriptors:\n"
+ " Hashtree descriptor:\n"
+ " Version of dm-verity: 1\n"
+ " Image Size: 5242880 bytes\n"
+ " Tree Offset: 5242880\n"
+ " Tree Size: %d bytes\n"
+ " Data Block Size: 4096 bytes\n"
+ " Hash Block Size: 4096 bytes\n"
+ " FEC num roots: 2\n"
+ " FEC offset: %d\n"
+ " FEC size: 49152 bytes\n"
+ " Hash Algorithm: %s\n"
+ " Partition Name: factory\n"
+ " Salt: deadbeef\n"
+ " Root Digest: \n"
+ " Flags: %d\n"
+ " Kernel Cmdline descriptor:\n"
+ " Flags: 0\n"
+ " Kernel Cmdline: "
+ "'androidboot.vbmeta.root_digest.factory=$("
+ "AVB_FACTORY_ROOT_DIGEST)'\n",
+ expected_tree_size,
+ expected_fec_offset,
+ verity_hash_algorithm_.c_str(),
+ do_not_use_ab ? 1 : 0),
+ InfoImage(vbmeta_image_path_));
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+ }
+
+ void Verify(bool expect_success) {
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"factory", NULL};
+ AvbSlotVerifyResult result =
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ AVB_SLOT_VERIFY_FLAGS_NONE,
+ AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
+ &slot_data);
+ if (expect_success) {
+ ASSERT_EQ(AVB_SLOT_VERIFY_RESULT_OK, result);
+ ASSERT_NE(nullptr, slot_data);
+ last_cmdline_ = slot_data->cmdline;
+ avb_slot_verify_data_free(slot_data);
+ } else {
+ EXPECT_NE(AVB_SLOT_VERIFY_RESULT_OK, result);
+ EXPECT_EQ(nullptr, slot_data);
+ if (expected_error_code_ != AVB_SLOT_VERIFY_RESULT_OK) {
+ EXPECT_EQ(expected_error_code_, result);
+ }
+ }
+ }
+
+ std::string last_cmdline_;
+ std::string verity_hash_algorithm_{"sha1"};
+ AvbSlotVerifyResult expected_error_code_{AVB_SLOT_VERIFY_RESULT_OK};
+
+ public:
+ // Persistent digests always use AVB_NPV_PERSISTENT_DIGEST_PREFIX followed by
+ // the partition name.
+ const char* kPersistentValueName = "avb.persistent_digest.factory";
+ // The digest for the hash descriptor which matches the factory contents.
+ const uint8_t kDigest[AVB_SHA256_DIGEST_SIZE] = {
+ 0x18, 0x4c, 0xb3, 0x62, 0x43, 0xad, 0xb8, 0xb8, 0x7d, 0x2d, 0x8c,
+ 0x48, 0x02, 0xde, 0x32, 0x12, 0x5f, 0xe2, 0x94, 0xec, 0x46, 0x75,
+ 0x3d, 0x73, 0x21, 0x44, 0xee, 0x65, 0xdf, 0x68, 0xa2, 0x3d};
+};
+
+TEST_F(AvbSlotVerifyTestWithPersistentDigest, Basic) {
+ SetupWithHashDescriptor();
+ // Store the expected image digest as a persistent value.
+ ops_.write_persistent_value(
+ kPersistentValueName, AVB_SHA256_DIGEST_SIZE, kDigest);
+ Verify(true /* expect_success */);
+ EXPECT_EQ(
+ "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
+ "androidboot.vbmeta.avb_version=1.1 "
+ "androidboot.vbmeta.device_state=locked "
+ "androidboot.vbmeta.hash_alg=sha256 "
+ "androidboot.vbmeta.size=1280 "
+ "androidboot.vbmeta.digest="
+ "604268b04d4a971d2d727c79a70b2ea7f6a0e42ccbdead1983acbf015061ce6b "
+ "androidboot.vbmeta.invalidate_on_error=yes "
+ "androidboot.veritymode=enforcing",
+ last_cmdline_);
+}
+
+TEST_F(AvbSlotVerifyTestWithPersistentDigest, Basic_WithAB) {
+ SetupWithHashDescriptor(false /* do_not_use_ab */);
+ // Store the expected image digest as a persistent value.
+ ops_.write_persistent_value(
+ kPersistentValueName, AVB_SHA256_DIGEST_SIZE, kDigest);
+ Verify(false /* expect_success */);
+}
+
+class AvbSlotVerifyTestWithPersistentDigest_InvalidDigestLength
+ : public AvbSlotVerifyTestWithPersistentDigest,
+ public ::testing::WithParamInterface<size_t> {};
+
+TEST_P(AvbSlotVerifyTestWithPersistentDigest_InvalidDigestLength, Param) {
+ SetupWithHashDescriptor();
+ // Store a digest value with the given length.
+ ops_.write_persistent_value(kPersistentValueName, GetParam(), kDigest);
+ Verify(false /* expect_success */);
+}
+
+// Test a bunch of invalid digest length values.
+INSTANTIATE_TEST_CASE_P(
+ P,
+ AvbSlotVerifyTestWithPersistentDigest_InvalidDigestLength,
+ ::testing::Values(AVB_SHA256_DIGEST_SIZE + 1,
+ AVB_SHA256_DIGEST_SIZE - 1,
+ 0,
+ AVB_SHA512_DIGEST_SIZE));
+
+class AvbSlotVerifyTestWithPersistentDigest_InvalidPersistentValueName
+ : public AvbSlotVerifyTestWithPersistentDigest,
+ public ::testing::WithParamInterface<const char*> {};
+
+TEST_P(AvbSlotVerifyTestWithPersistentDigest_InvalidPersistentValueName,
+ Param) {
+ SetupWithHashDescriptor();
+ ops_.write_persistent_value(GetParam(), AVB_SHA256_DIGEST_SIZE, kDigest);
+ Verify(false /* expect_success */);
+}
+
+// Test a bunch of invalid persistent value names.
+INSTANTIATE_TEST_CASE_P(
+ P,
+ AvbSlotVerifyTestWithPersistentDigest_InvalidPersistentValueName,
+ ::testing::Values(
+ "",
+ "AVBPD_factory0",
+ "AVBPD_factor",
+ "loooooooooooooooooooooooooooooooooooooooooooooongvalue"));
+
+class AvbSlotVerifyTestWithPersistentDigest_ReadDigestFailure
+ : public AvbSlotVerifyTestWithPersistentDigest,
+ public ::testing::WithParamInterface<AvbIOResult> {
+ // FakeAvbOpsDelegate override.
+ AvbIOResult read_persistent_value(const char* name,
+ size_t buffer_size,
+ uint8_t* out_buffer,
+ size_t* out_num_bytes_read) override {
+ return GetParam();
+ }
+};
+
+TEST_P(AvbSlotVerifyTestWithPersistentDigest_ReadDigestFailure, Param) {
+ SetupWithHashDescriptor();
+ ops_.write_persistent_value(
+ kPersistentValueName, AVB_SHA256_DIGEST_SIZE, kDigest);
+ switch (GetParam()) {
+ case AVB_IO_RESULT_ERROR_OOM:
+ expected_error_code_ = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ break;
+ // Fall through.
+ case AVB_IO_RESULT_ERROR_NO_SUCH_VALUE:
+ case AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE:
+ case AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE:
+ expected_error_code_ = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ break;
+ default:
+ break;
+ }
+ Verify(false /* expect_success */);
+}
+
+// Test a bunch of error codes.
+INSTANTIATE_TEST_CASE_P(
+ P,
+ AvbSlotVerifyTestWithPersistentDigest_ReadDigestFailure,
+ ::testing::Values(AVB_IO_RESULT_ERROR_OOM,
+ AVB_IO_RESULT_ERROR_IO,
+ AVB_IO_RESULT_ERROR_NO_SUCH_VALUE,
+ AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE,
+ AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE));
+
+TEST_F(AvbSlotVerifyTestWithPersistentDigest, Basic_Hashtree_Sha1) {
+ verity_hash_algorithm_ = "sha1";
+ SetupWithHashtreeDescriptor();
+ // Store an arbitrary image digest.
+ uint8_t fake_digest[]{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
+ ops_.write_persistent_value(
+ kPersistentValueName, AVB_SHA1_DIGEST_SIZE, fake_digest);
+ Verify(true /* expect_success */);
+ EXPECT_EQ(
+ "androidboot.vbmeta.root_digest.factory="
+ // Note: Here appear the bytes used in write_persistent_value above.
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "
+ "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
+ "androidboot.vbmeta.avb_version=1.1 "
+ "androidboot.vbmeta.device_state=locked "
+ "androidboot.vbmeta.hash_alg=sha256 "
+ "androidboot.vbmeta.size=1408 "
+ "androidboot.vbmeta.digest="
+ "bdeff592f85f34a6ae40919e311273a10027f3877daa9c8c1be8e685947abb3d "
+ "androidboot.vbmeta.invalidate_on_error=yes "
+ "androidboot.veritymode=enforcing",
+ last_cmdline_);
+}
+
+TEST_F(AvbSlotVerifyTestWithPersistentDigest, Basic_Hashtree_Sha256) {
+ verity_hash_algorithm_ = "sha256";
+ SetupWithHashtreeDescriptor();
+ // Store an arbitrary image digest.
+ uint8_t fake_digest[]{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
+ ops_.write_persistent_value(
+ kPersistentValueName, AVB_SHA256_DIGEST_SIZE, fake_digest);
+ Verify(true /* expect_success */);
+ EXPECT_EQ(
+ "androidboot.vbmeta.root_digest.factory="
+ // Note: Here appear the bytes used in write_persistent_value above.
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "
+ "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
+ "androidboot.vbmeta.avb_version=1.1 "
+ "androidboot.vbmeta.device_state=locked "
+ "androidboot.vbmeta.hash_alg=sha256 "
+ "androidboot.vbmeta.size=1408 "
+ "androidboot.vbmeta.digest="
+ "03d287a0a126ed3fce48d6d8907612559e1485d29e201ede5838d65c5cc4bec2 "
+ "androidboot.vbmeta.invalidate_on_error=yes "
+ "androidboot.veritymode=enforcing",
+ last_cmdline_);
+}
+
+TEST_F(AvbSlotVerifyTestWithPersistentDigest, Basic_Hashtree_Sha512) {
+ verity_hash_algorithm_ = "sha512";
+ SetupWithHashtreeDescriptor();
+ // Store an arbitrary image digest.
+ uint8_t fake_digest[]{
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
+ ops_.write_persistent_value(
+ kPersistentValueName, AVB_SHA512_DIGEST_SIZE, fake_digest);
+ Verify(true /* expect_success */);
+ EXPECT_EQ(
+ "androidboot.vbmeta.root_digest.factory="
+ // Note: Here appear the bytes used in write_persistent_value above.
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "
+ "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
+ "androidboot.vbmeta.avb_version=1.1 "
+ "androidboot.vbmeta.device_state=locked "
+ "androidboot.vbmeta.hash_alg=sha256 "
+ "androidboot.vbmeta.size=1408 "
+ "androidboot.vbmeta.digest="
+ "931b10c7c8e7ab270437a4481b7d8d5c9757a3df190b7df3b6f93bf0289b9911 "
+ "androidboot.vbmeta.invalidate_on_error=yes "
+ "androidboot.veritymode=enforcing",
+ last_cmdline_);
+}
+
+TEST_F(AvbSlotVerifyTestWithPersistentDigest, Basic_Hashtree_WithAB) {
+ verity_hash_algorithm_ = "sha1";
+ SetupWithHashtreeDescriptor(false /* do_not_use_ab */);
+ // Store an arbitrary image digest.
+ uint8_t fake_digest[]{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
+ ops_.write_persistent_value(
+ kPersistentValueName, AVB_SHA1_DIGEST_SIZE, fake_digest);
+ Verify(false /* expect_success */);
+}
+
+class AvbSlotVerifyTestWithPersistentDigest_Hashtree_InvalidDigestLength
+ : public AvbSlotVerifyTestWithPersistentDigest,
+ public ::testing::WithParamInterface<size_t> {};
+
+TEST_P(AvbSlotVerifyTestWithPersistentDigest_Hashtree_InvalidDigestLength,
+ Param) {
+ SetupWithHashtreeDescriptor();
+ // Store a digest value with the given length.
+ ops_.write_persistent_value(kPersistentValueName, GetParam(), kDigest);
+ Verify(false /* expect_success */);
+}
+
+// Test a bunch of invalid digest length values.
+INSTANTIATE_TEST_CASE_P(
+ P,
+ AvbSlotVerifyTestWithPersistentDigest_Hashtree_InvalidDigestLength,
+ ::testing::Values(AVB_SHA1_DIGEST_SIZE + 1,
+ AVB_SHA1_DIGEST_SIZE - 1,
+ 0,
+ AVB_SHA256_DIGEST_SIZE,
+ AVB_SHA512_DIGEST_SIZE));
+
+class AvbSlotVerifyTestWithPersistentDigest_Hashtree_InvalidPersistentValueName
+ : public AvbSlotVerifyTestWithPersistentDigest,
+ public ::testing::WithParamInterface<const char*> {};
+
+TEST_P(
+ AvbSlotVerifyTestWithPersistentDigest_Hashtree_InvalidPersistentValueName,
+ Param) {
+ SetupWithHashtreeDescriptor();
+ ops_.write_persistent_value(GetParam(), AVB_SHA256_DIGEST_SIZE, kDigest);
+ Verify(false /* expect_success */);
+}
+
+// Test a bunch of invalid persistent value names.
+INSTANTIATE_TEST_CASE_P(
+ P,
+ AvbSlotVerifyTestWithPersistentDigest_Hashtree_InvalidPersistentValueName,
+ ::testing::Values(
+ "",
+ "avb.persistent_digest.factory0",
+ "avb.persistent_digest.factor",
+ "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
+ "oooooooooooooooooooooooooooooooooooooooooooooooooooongvalue"));
+
+class AvbSlotVerifyTestWithPersistentDigest_Hashtree_ReadDigestFailure
+ : public AvbSlotVerifyTestWithPersistentDigest,
+ public ::testing::WithParamInterface<AvbIOResult> {
+ // FakeAvbOpsDelegate override.
+ AvbIOResult read_persistent_value(const char* name,
+ size_t buffer_size,
+ uint8_t* out_buffer,
+ size_t* out_num_bytes_read) override {
+ return GetParam();
+ }
+};
+
+TEST_P(AvbSlotVerifyTestWithPersistentDigest_Hashtree_ReadDigestFailure,
+ Param) {
+ SetupWithHashtreeDescriptor();
+ ops_.write_persistent_value(
+ kPersistentValueName, AVB_SHA256_DIGEST_SIZE, kDigest);
+ switch (GetParam()) {
+ case AVB_IO_RESULT_ERROR_OOM:
+ expected_error_code_ = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+ break;
+ // Fall through.
+ case AVB_IO_RESULT_ERROR_NO_SUCH_VALUE:
+ case AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE:
+ case AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE:
+ expected_error_code_ = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+ break;
+ default:
+ break;
+ }
+ Verify(false /* expect_success */);
+}
+
+// Test a bunch of error codes.
+INSTANTIATE_TEST_CASE_P(
+ P,
+ AvbSlotVerifyTestWithPersistentDigest_Hashtree_ReadDigestFailure,
+ ::testing::Values(AVB_IO_RESULT_ERROR_OOM,
+ AVB_IO_RESULT_ERROR_IO,
+ AVB_IO_RESULT_ERROR_NO_SUCH_VALUE,
+ AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE,
+ AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE));
+
} // namespace avb
diff --git a/test/avbtool_unittest.cc b/test/avbtool_unittest.cc
index a2561bd..fe7d3ba 100644
--- a/test/avbtool_unittest.cc
+++ b/test/avbtool_unittest.cc
@@ -409,7 +409,8 @@ static std::string AddHashFooterGetExpectedVBMetaInfo(
" Salt: d00df00d\n"
" Digest: "
"9a58cc996d405e08a1e00f96dbfe9104fedf41cb83b1f"
- "5e4ed357fbcf58d88d9\n",
+ "5e4ed357fbcf58d88d9\n"
+ " Flags: 0\n",
partition_size,
sparse_image ? " (Sparse)" : "");
}
@@ -486,7 +487,8 @@ void AvbToolTest::AddHashFooterTest(bool sparse_image) {
" Salt: d00df00d\n"
" Digest: "
"9a58cc996d405e08a1e00f96dbfe9104fedf41cb83b1f"
- "5e4ed357fbcf58d88d9\n",
+ "5e4ed357fbcf58d88d9\n"
+ " Flags: 0\n",
InfoImage(ext_vbmeta_path));
}
@@ -690,7 +692,8 @@ TEST_F(AvbToolTest, AddHashFooterSparseWithHoleAtTheEnd) {
" Image Size: 10354688 bytes\n"
" Hash Algorithm: sha256\n"
" Partition Name: foobar\n"
- " Salt: d00df00d\n",
+ " Salt: d00df00d\n"
+ " Flags: 0\n",
info);
EXPECT_COMMAND(0,
@@ -738,6 +741,134 @@ TEST_F(AvbToolTest, AddHashFooterCalcMaxImageSize) {
partition_size);
}
+TEST_F(AvbToolTest, AddHashFooterWithPersistentDigest) {
+ size_t partition_size = 1024 * 1024;
+ base::FilePath path = GenerateImage("digest_location", 1024);
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer --salt d00df00d "
+ "--hash_algorithm sha256 --image %s "
+ "--partition_size %d --partition_name foobar "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem "
+ "--internal_release_string \"\" "
+ "--use_persistent_digest",
+ path.value().c_str(),
+ (int)partition_size);
+ // There are two important bits specific to these flags:
+ // Minimum libavb version = 1.1
+ // Hash descriptor -> Digest = (empty)
+ ASSERT_EQ(
+ "Footer version: 1.0\n"
+ "Image size: 1048576 bytes\n"
+ "Original image size: 1024 bytes\n"
+ "VBMeta offset: 4096\n"
+ "VBMeta size: 1280 bytes\n"
+ "--\n"
+ "Minimum libavb version: 1.1\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 704 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: ''\n"
+ "Descriptors:\n"
+ " Hash descriptor:\n"
+ " Image Size: 1024 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: foobar\n"
+ " Salt: d00df00d\n"
+ " Digest: \n"
+ " Flags: 0\n",
+ InfoImage(path));
+}
+
+TEST_F(AvbToolTest, AddHashFooterWithNoAB) {
+ size_t partition_size = 1024 * 1024;
+ base::FilePath path = GenerateImage("digest_location", 1024);
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer --salt d00df00d "
+ "--hash_algorithm sha256 --image %s "
+ "--partition_size %d --partition_name foobar "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem "
+ "--internal_release_string \"\" "
+ "--do_not_use_ab",
+ path.value().c_str(),
+ (int)partition_size);
+ // There are two important bits specific to these flags:
+ // Minimum libavb version = 1.1
+ // Hash descriptor -> Flags = 1
+ ASSERT_EQ(
+ "Footer version: 1.0\n"
+ "Image size: 1048576 bytes\n"
+ "Original image size: 1024 bytes\n"
+ "VBMeta offset: 4096\n"
+ "VBMeta size: 1280 bytes\n"
+ "--\n"
+ "Minimum libavb version: 1.1\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 704 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: ''\n"
+ "Descriptors:\n"
+ " Hash descriptor:\n"
+ " Image Size: 1024 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: foobar\n"
+ " Salt: d00df00d\n"
+ " Digest: "
+ "91386fea3e251ad0c2cb6859e4f4772f37fdb69f17d46636ddc9e7fbfd3bf3d0\n"
+ " Flags: 1\n",
+ InfoImage(path));
+}
+
+TEST_F(AvbToolTest, AddHashFooterWithPersistentDigestAndNoAB) {
+ size_t partition_size = 1024 * 1024;
+ base::FilePath path = GenerateImage("digest_location", 1024);
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer --salt d00df00d "
+ "--hash_algorithm sha256 --image %s "
+ "--partition_size %d --partition_name foobar "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem "
+ "--internal_release_string \"\" "
+ "--use_persistent_digest --do_not_use_ab",
+ path.value().c_str(),
+ (int)partition_size);
+ // There are three important bits specific to these flags:
+ // Minimum libavb version = 1.1
+ // Hash descriptor -> Digest = (empty)
+ // Hash descriptor -> Flags = 1
+ ASSERT_EQ(
+ "Footer version: 1.0\n"
+ "Image size: 1048576 bytes\n"
+ "Original image size: 1024 bytes\n"
+ "VBMeta offset: 4096\n"
+ "VBMeta size: 1280 bytes\n"
+ "--\n"
+ "Minimum libavb version: 1.1\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 704 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: ''\n"
+ "Descriptors:\n"
+ " Hash descriptor:\n"
+ " Image Size: 1024 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: foobar\n"
+ " Salt: d00df00d\n"
+ " Digest: \n"
+ " Flags: 1\n",
+ InfoImage(path));
+}
+
void AvbToolTest::AddHashtreeFooterTest(bool sparse_image) {
const size_t rootfs_size = 1028 * 1024;
const size_t partition_size = 1536 * 1024;
@@ -810,7 +941,8 @@ void AvbToolTest::AddHashtreeFooterTest(bool sparse_image) {
" Partition Name: foobar\n"
" Salt: d00df00d\n"
" Root Digest: "
- "e811611467dcd6e8dc4324e45f706c2bdd51db67\n",
+ "e811611467dcd6e8dc4324e45f706c2bdd51db67\n"
+ " Flags: 0\n",
sparse_image ? " (Sparse)" : ""),
InfoImage(rootfs_path));
@@ -838,7 +970,8 @@ void AvbToolTest::AddHashtreeFooterTest(bool sparse_image) {
" Partition Name: foobar\n"
" Salt: d00df00d\n"
" Root Digest: "
- "e811611467dcd6e8dc4324e45f706c2bdd51db67\n",
+ "e811611467dcd6e8dc4324e45f706c2bdd51db67\n"
+ " Flags: 0\n",
InfoImage(ext_vbmeta_path));
}
@@ -1080,7 +1213,8 @@ void AvbToolTest::AddHashtreeFooterFECTest(bool sparse_image) {
" Partition Name: foobar\n"
" Salt: d00df00d\n"
" Root Digest: "
- "e811611467dcd6e8dc4324e45f706c2bdd51db67\n",
+ "e811611467dcd6e8dc4324e45f706c2bdd51db67\n"
+ " Flags: 0\n",
sparse_image ? " (Sparse)" : ""),
InfoImage(rootfs_path));
}
@@ -1289,6 +1423,158 @@ TEST_F(AvbToolTest, AddHashtreeFooterCalcMaxImageSizeWithFEC) {
partition_size);
}
+TEST_F(AvbToolTest, AddHashtreeFooterWithPersistentDigest) {
+ size_t partition_size = 10 * 1024 * 1024;
+ base::FilePath path = GenerateImage("digest_location", partition_size / 2);
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer --salt d00df00d "
+ "--hash_algorithm sha256 --image %s "
+ "--partition_size %d --partition_name foobar "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem "
+ "--internal_release_string \"\" "
+ "--use_persistent_digest",
+ path.value().c_str(),
+ (int)partition_size);
+ // There are two important bits here specific to --use_persistent_digest:
+ // Minimum libavb version = 1.1
+ // Hashtree descriptor -> Root Digest = (empty)
+ ASSERT_EQ(
+ "Footer version: 1.0\n"
+ "Image size: 10485760 bytes\n"
+ "Original image size: 5242880 bytes\n"
+ "VBMeta offset: 5337088\n"
+ "VBMeta size: 1344 bytes\n"
+ "--\n"
+ "Minimum libavb version: 1.1\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 768 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: ''\n"
+ "Descriptors:\n"
+ " Hashtree descriptor:\n"
+ " Version of dm-verity: 1\n"
+ " Image Size: 5242880 bytes\n"
+ " Tree Offset: 5242880\n"
+ " Tree Size: 45056 bytes\n"
+ " Data Block Size: 4096 bytes\n"
+ " Hash Block Size: 4096 bytes\n"
+ " FEC num roots: 2\n"
+ " FEC offset: 5287936\n"
+ " FEC size: 49152 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: foobar\n"
+ " Salt: d00df00d\n"
+ " Root Digest: \n"
+ " Flags: 0\n",
+ InfoImage(path));
+}
+
+TEST_F(AvbToolTest, AddHashtreeFooterWithNoAB) {
+ size_t partition_size = 10 * 1024 * 1024;
+ base::FilePath path = GenerateImage("digest_location", partition_size / 2);
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer --salt d00df00d "
+ "--hash_algorithm sha256 --image %s "
+ "--partition_size %d --partition_name foobar "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem "
+ "--internal_release_string \"\" "
+ "--do_not_use_ab",
+ path.value().c_str(),
+ (int)partition_size);
+ // There are two important bits here we're expecting with --do_not_use_ab:
+ // Minimum libavb version = 1.1
+ // Hashtree descriptor -> Flags = 1
+ ASSERT_EQ(
+ "Footer version: 1.0\n"
+ "Image size: 10485760 bytes\n"
+ "Original image size: 5242880 bytes\n"
+ "VBMeta offset: 5337088\n"
+ "VBMeta size: 1344 bytes\n"
+ "--\n"
+ "Minimum libavb version: 1.1\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 768 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: ''\n"
+ "Descriptors:\n"
+ " Hashtree descriptor:\n"
+ " Version of dm-verity: 1\n"
+ " Image Size: 5242880 bytes\n"
+ " Tree Offset: 5242880\n"
+ " Tree Size: 45056 bytes\n"
+ " Data Block Size: 4096 bytes\n"
+ " Hash Block Size: 4096 bytes\n"
+ " FEC num roots: 2\n"
+ " FEC offset: 5287936\n"
+ " FEC size: 49152 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: foobar\n"
+ " Salt: d00df00d\n"
+ " Root Digest: "
+ "d0e31526f5a3f8e3f59acf726bd31ae7861ee78f9baa9195356bf479c6f9119d\n"
+ " Flags: 1\n",
+ InfoImage(path));
+}
+
+TEST_F(AvbToolTest, AddHashtreeFooterWithPersistentDigestAndNoAB) {
+ size_t partition_size = 10 * 1024 * 1024;
+ base::FilePath path = GenerateImage("digest_location", partition_size / 2);
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer --salt d00df00d "
+ "--hash_algorithm sha256 --image %s "
+ "--partition_size %d --partition_name foobar "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem "
+ "--internal_release_string \"\" "
+ "--use_persistent_digest --do_not_use_ab",
+ path.value().c_str(),
+ (int)partition_size);
+ // There are three important bits specific to these flags:
+ // Minimum libavb version = 1.1
+ // Hashtree descriptor -> Root Digest = (empty)
+ // Hashtree descriptor -> Flags = 1
+ ASSERT_EQ(
+ "Footer version: 1.0\n"
+ "Image size: 10485760 bytes\n"
+ "Original image size: 5242880 bytes\n"
+ "VBMeta offset: 5337088\n"
+ "VBMeta size: 1344 bytes\n"
+ "--\n"
+ "Minimum libavb version: 1.1\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 768 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: ''\n"
+ "Descriptors:\n"
+ " Hashtree descriptor:\n"
+ " Version of dm-verity: 1\n"
+ " Image Size: 5242880 bytes\n"
+ " Tree Offset: 5242880\n"
+ " Tree Size: 45056 bytes\n"
+ " Data Block Size: 4096 bytes\n"
+ " Hash Block Size: 4096 bytes\n"
+ " FEC num roots: 2\n"
+ " FEC offset: 5287936\n"
+ " FEC size: 49152 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: foobar\n"
+ " Salt: d00df00d\n"
+ " Root Digest: \n"
+ " Flags: 1\n",
+ InfoImage(path));
+}
+
TEST_F(AvbToolTest, KernelCmdlineDescriptor) {
base::FilePath vbmeta_path =
testdir_.Append("vbmeta_kernel_cmdline_desc.bin");
@@ -1374,9 +1660,11 @@ TEST_F(AvbToolTest, AddHashFooterSmallImageWithExternalVbmeta) {
base::FilePath ext_vbmeta_path = testdir_.Append("ext_vbmeta.bin");
base::FilePath image_path = testdir_.Append("kernel.bin");
- EXPECT_EQ(image_size, static_cast<const size_t>(
- base::WriteFile(image_path, reinterpret_cast<const char*>(image.data()),
- image.size())));
+ EXPECT_EQ(image_size,
+ static_cast<const size_t>(
+ base::WriteFile(image_path,
+ reinterpret_cast<const char*>(image.data()),
+ image.size())));
EXPECT_COMMAND(0,
"./avbtool add_hash_footer --salt d00df00d "
"--hash_algorithm sha256 --image %s "
@@ -1591,7 +1879,7 @@ TEST_F(AvbToolTest, AppendVBMetaImage) {
"Algorithm: SHA256_RSA2048\n"
"Rollback Index: 0\n"
"Flags: 0\n"
- "Release String: 'avbtool 1.0.0 '\n"
+ "Release String: 'avbtool 1.1.0 '\n"
"Descriptors:\n"
" Kernel Cmdline descriptor:\n"
" Flags: 0\n"
@@ -1946,42 +2234,116 @@ TEST_F(AvbToolTest, VerifyImageChainPartition) {
pk8192_path.value().c_str());
}
-TEST_F(AvbToolTest, PrintRequiredLibavbVersion) {
- base::FilePath output_path = testdir_.Append("versions.txt");
+class AvbToolTest_PrintRequiredVersion : public AvbToolTest {
+ protected:
+ const char* kOutputFile = "versions.txt";
- const size_t boot_partition_size = 16 * 1024 * 1024;
- EXPECT_COMMAND(0,
- "./avbtool add_hash_footer"
- " --rollback_index 0"
- " --partition_name boot"
- " --partition_size %zd"
- " --salt deadbeef"
- " --internal_release_string \"\""
- " --print_required_libavb_version >> %s",
- boot_partition_size,
- output_path.value().c_str());
+ void PrintWithAddHashFooter(int target_required_minor_version) {
+ std::string extra_args;
+ if (target_required_minor_version == 1) {
+ // The --do_not_use_ab option will require 1.1.
+ extra_args = "--do_not_use_ab";
+ }
+ const size_t boot_partition_size = 16 * 1024 * 1024;
+ base::FilePath output_path = testdir_.Append(kOutputFile);
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer"
+ " --rollback_index 0"
+ " --partition_name boot"
+ " --partition_size %zd"
+ " --salt deadbeef"
+ " --internal_release_string \"\""
+ " %s"
+ " --print_required_libavb_version > %s",
+ boot_partition_size,
+ extra_args.c_str(),
+ output_path.value().c_str());
+ CheckVersion(target_required_minor_version);
+ }
- const size_t system_partition_size = 10 * 1024 * 1024;
- EXPECT_COMMAND(0,
- "./avbtool add_hashtree_footer --salt d00df00d "
- "--partition_size %zd --partition_name system "
- "--internal_release_string \"\""
- " --print_required_libavb_version >> %s",
- system_partition_size,
- output_path.value().c_str());
+ void PrintWithAddHashtreeFooter(int target_required_minor_version) {
+ std::string extra_args;
+ if (target_required_minor_version == 1) {
+ // The --do_not_use_ab option will require 1.1.
+ extra_args = "--do_not_use_ab";
+ }
+ const size_t system_partition_size = 10 * 1024 * 1024;
+ base::FilePath output_path = testdir_.Append(kOutputFile);
+ EXPECT_COMMAND(0,
+ "./avbtool add_hashtree_footer --salt d00df00d "
+ "--partition_size %zd --partition_name system "
+ "--internal_release_string \"\""
+ " %s"
+ " --print_required_libavb_version > %s",
+ system_partition_size,
+ extra_args.c_str(),
+ output_path.value().c_str());
+ CheckVersion(target_required_minor_version);
+ }
- EXPECT_COMMAND(0,
- "./avbtool make_vbmeta_image "
- "--algorithm SHA256_RSA2048 "
- "--key test/data/testkey_rsa2048.pem "
- "--internal_release_string \"\""
- " --print_required_libavb_version >> %s",
- output_path.value().c_str());
+ void PrintWithMakeVbmetaImage(int target_required_minor_version) {
+ std::string extra_args;
+ if (target_required_minor_version == 1) {
+ // An included descriptor that requires 1.1 will require 1.1 for vbmeta.
+ const size_t boot_partition_size = 16 * 1024 * 1024;
+ base::FilePath image_path = GenerateImage("test_print_version", 1024);
+ EXPECT_COMMAND(0,
+ "./avbtool add_hash_footer --salt d00df00d "
+ "--hash_algorithm sha256 --image %s "
+ "--partition_size %d --partition_name foobar "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem "
+ "--internal_release_string \"\" "
+ "--do_not_use_ab",
+ image_path.value().c_str(),
+ (int)boot_partition_size);
+ extra_args = base::StringPrintf("--include_descriptors_from_image %s",
+ image_path.value().c_str());
+ }
+ base::FilePath output_path = testdir_.Append(kOutputFile);
+ EXPECT_COMMAND(0,
+ "./avbtool make_vbmeta_image "
+ "--algorithm SHA256_RSA2048 "
+ "--key test/data/testkey_rsa2048.pem "
+ "--internal_release_string \"\""
+ " %s"
+ " --print_required_libavb_version > %s",
+ extra_args.c_str(),
+ output_path.value().c_str());
+ CheckVersion(target_required_minor_version);
+ }
+
+ void CheckVersion(int expected_required_minor_version) {
+ base::FilePath output_path = testdir_.Append(kOutputFile);
+ std::string output;
+ ASSERT_TRUE(base::ReadFileToString(output_path, &output));
+ EXPECT_EQ(output,
+ base::StringPrintf("1.%d\n", expected_required_minor_version));
+ }
+};
+
+TEST_F(AvbToolTest_PrintRequiredVersion, HashFooter_1_0) {
+ PrintWithAddHashFooter(0);
+}
+
+TEST_F(AvbToolTest_PrintRequiredVersion, HashFooter_1_1) {
+ PrintWithAddHashFooter(1);
+}
+
+TEST_F(AvbToolTest_PrintRequiredVersion, HashtreeFooter_1_0) {
+ PrintWithAddHashtreeFooter(0);
+}
+
+TEST_F(AvbToolTest_PrintRequiredVersion, HashtreeFooter_1_1) {
+ PrintWithAddHashtreeFooter(1);
+}
+
+TEST_F(AvbToolTest_PrintRequiredVersion, Vbmeta_1_0) {
+ PrintWithMakeVbmetaImage(0);
+}
- // Check that "1.0\n" was printed for all three invocations.
- std::string versions;
- ASSERT_TRUE(base::ReadFileToString(output_path, &versions));
- EXPECT_EQ(versions, std::string("1.0\n1.0\n1.0\n"));
+TEST_F(AvbToolTest_PrintRequiredVersion, Vbmeta_1_1) {
+ PrintWithMakeVbmetaImage(1);
}
TEST_F(AvbToolTest, MakeAtxPikCertificate) {
diff --git a/test/fake_avb_ops.cc b/test/fake_avb_ops.cc
index 9d3963a..6ee128a 100644
--- a/test/fake_avb_ops.cc
+++ b/test/fake_avb_ops.cc
@@ -56,9 +56,7 @@ bool FakeAvbOps::preload_partition(const std::string& partition,
int64_t file_size;
if (!base::GetFileSize(path, &file_size)) {
- fprintf(stderr,
- "Error getting size of file '%s'\n",
- path.value().c_str());
+ fprintf(stderr, "Error getting size of file '%s'\n", path.value().c_str());
return false;
}
@@ -150,7 +148,9 @@ AvbIOResult FakeAvbOps::read_from_partition(const char* partition,
}
AvbIOResult FakeAvbOps::get_preloaded_partition(
- const char* partition, size_t num_bytes, uint8_t** out_pointer,
+ const char* partition,
+ size_t num_bytes,
+ uint8_t** out_pointer,
size_t* out_num_bytes_preloaded) {
std::map<std::string, uint8_t*>::iterator it =
preloaded_partitions_.find(std::string(partition));
@@ -161,8 +161,7 @@ AvbIOResult FakeAvbOps::get_preloaded_partition(
}
uint64_t size;
- AvbIOResult result = get_size_of_partition(
- avb_ops(), partition, &size);
+ AvbIOResult result = get_size_of_partition(avb_ops(), partition, &size);
if (result != AVB_IO_RESULT_OK) {
return result;
}
@@ -311,6 +310,33 @@ AvbIOResult FakeAvbOps::get_size_of_partition(AvbOps* ops,
return AVB_IO_RESULT_OK;
}
+AvbIOResult FakeAvbOps::read_persistent_value(const char* name,
+ size_t buffer_size,
+ uint8_t* out_buffer,
+ size_t* out_num_bytes_read) {
+ if (out_buffer == NULL && buffer_size > 0) {
+ return AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE;
+ }
+ if (stored_values_.count(name) == 0) {
+ return AVB_IO_RESULT_ERROR_NO_SUCH_VALUE;
+ }
+ if (stored_values_[name].size() > buffer_size) {
+ *out_num_bytes_read = stored_values_[name].size();
+ return AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE;
+ }
+ memcpy(out_buffer, stored_values_[name].data(), stored_values_[name].size());
+ *out_num_bytes_read = stored_values_[name].size();
+ return AVB_IO_RESULT_OK;
+}
+
+AvbIOResult FakeAvbOps::write_persistent_value(const char* name,
+ size_t value_size,
+ const uint8_t* value) {
+ stored_values_[name] =
+ std::string(reinterpret_cast<const char*>(value), value_size);
+ return AVB_IO_RESULT_OK;
+}
+
AvbIOResult FakeAvbOps::read_permanent_attributes(
AvbAtxPermanentAttributes* attributes) {
*attributes = permanent_attributes_;
@@ -347,15 +373,16 @@ static AvbIOResult my_ops_read_from_partition(AvbOps* ops,
->read_from_partition(partition, offset, num_bytes, buffer, out_num_read);
}
-static AvbIOResult my_ops_get_preloaded_partition(AvbOps* ops,
- const char* partition,
- size_t num_bytes,
- uint8_t** out_pointer,
- size_t* out_num_bytes_preloaded) {
+static AvbIOResult my_ops_get_preloaded_partition(
+ AvbOps* ops,
+ const char* partition,
+ size_t num_bytes,
+ uint8_t** out_pointer,
+ size_t* out_num_bytes_preloaded) {
return FakeAvbOps::GetInstanceFromAvbOps(ops)
->delegate()
- ->get_preloaded_partition(partition, num_bytes, out_pointer,
- out_num_bytes_preloaded);
+ ->get_preloaded_partition(
+ partition, num_bytes, out_pointer, out_num_bytes_preloaded);
}
static AvbIOResult my_ops_write_to_partition(AvbOps* ops,
@@ -424,6 +451,26 @@ static AvbIOResult my_ops_get_size_of_partition(AvbOps* ops,
->get_size_of_partition(ops, partition, out_size);
}
+static AvbIOResult my_ops_read_persistent_value(AvbOps* ops,
+ const char* name,
+ size_t buffer_size,
+ uint8_t* out_buffer,
+ size_t* out_num_bytes_read) {
+ return FakeAvbOps::GetInstanceFromAvbOps(ops)
+ ->delegate()
+ ->read_persistent_value(
+ name, buffer_size, out_buffer, out_num_bytes_read);
+}
+
+static AvbIOResult my_ops_write_persistent_value(AvbOps* ops,
+ const char* name,
+ size_t value_size,
+ const uint8_t* value) {
+ return FakeAvbOps::GetInstanceFromAvbOps(ops)
+ ->delegate()
+ ->write_persistent_value(name, value_size, value);
+}
+
static AvbIOResult my_ops_read_permanent_attributes(
AvbAtxOps* atx_ops, AvbAtxPermanentAttributes* attributes) {
return FakeAvbOps::GetInstanceFromAvbOps(atx_ops->ops)
@@ -459,6 +506,8 @@ FakeAvbOps::FakeAvbOps() {
avb_ops_.read_is_device_unlocked = my_ops_read_is_device_unlocked;
avb_ops_.get_unique_guid_for_partition = my_ops_get_unique_guid_for_partition;
avb_ops_.get_size_of_partition = my_ops_get_size_of_partition;
+ avb_ops_.read_persistent_value = my_ops_read_persistent_value;
+ avb_ops_.write_persistent_value = my_ops_write_persistent_value;
// Just use the built-in A/B metadata read/write routines.
avb_ab_ops_.ops = &avb_ops_;
@@ -476,8 +525,8 @@ FakeAvbOps::FakeAvbOps() {
FakeAvbOps::~FakeAvbOps() {
std::map<std::string, uint8_t*>::iterator it;
- for (it = preloaded_partitions_.begin();
- it != preloaded_partitions_.end(); it++) {
+ for (it = preloaded_partitions_.begin(); it != preloaded_partitions_.end();
+ it++) {
free(it->second);
}
}
diff --git a/test/fake_avb_ops.h b/test/fake_avb_ops.h
index 93c7ee9..769f3cc 100644
--- a/test/fake_avb_ops.h
+++ b/test/fake_avb_ops.h
@@ -86,6 +86,15 @@ class FakeAvbOpsDelegate {
const char* partition,
uint64_t* out_size) = 0;
+ virtual AvbIOResult read_persistent_value(const char* name,
+ size_t buffer_size,
+ uint8_t* out_buffer,
+ size_t* out_num_bytes_read) = 0;
+
+ virtual AvbIOResult write_persistent_value(const char* name,
+ size_t value_size,
+ const uint8_t* value) = 0;
+
virtual AvbIOResult read_permanent_attributes(
AvbAtxPermanentAttributes* attributes) = 0;
@@ -222,6 +231,15 @@ class FakeAvbOps : public FakeAvbOpsDelegate {
const char* partition,
uint64_t* out_size) override;
+ AvbIOResult read_persistent_value(const char* name,
+ size_t buffer_size,
+ uint8_t* out_buffer,
+ size_t* out_num_bytes_read) override;
+
+ AvbIOResult write_persistent_value(const char* name,
+ size_t value_size,
+ const uint8_t* value) override;
+
AvbIOResult read_permanent_attributes(
AvbAtxPermanentAttributes* attributes) override;
@@ -253,6 +271,8 @@ class FakeAvbOps : public FakeAvbOpsDelegate {
std::set<std::string> partition_names_read_from_;
std::map<std::string, uint8_t*> preloaded_partitions_;
+
+ std::map<std::string, std::string> stored_values_;
};
// A delegate implementation that calls FakeAvbOps by default.
@@ -329,6 +349,20 @@ class FakeAvbOpsDelegateWithDefaults : public FakeAvbOpsDelegate {
return ops_.get_size_of_partition(ops, partition, out_size);
}
+ AvbIOResult read_persistent_value(const char* name,
+ size_t buffer_size,
+ uint8_t* out_buffer,
+ size_t* out_num_bytes_read) override {
+ return ops_.read_persistent_value(
+ name, buffer_size, out_buffer, out_num_bytes_read);
+ }
+
+ AvbIOResult write_persistent_value(const char* name,
+ size_t value_size,
+ const uint8_t* value) override {
+ return ops_.write_persistent_value(name, value_size, value);
+ }
+
AvbIOResult read_permanent_attributes(
AvbAtxPermanentAttributes* attributes) override {
return ops_.read_permanent_attributes(attributes);