diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2018-02-27 08:25:56 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2018-02-27 08:25:56 +0000 |
commit | 28dc1012dd287ff988da3f301903ea8968e1f199 (patch) | |
tree | b389467c4f5230592fda4016cdd0ee49ef19058b | |
parent | 2b4b57adfd60539cbab32daa7c4883f17484bb44 (diff) | |
parent | 791f6c3648dd9cea987396375592217b81898dd3 (diff) | |
download | avb-28dc1012dd287ff988da3f301903ea8968e1f199.tar.gz |
Snap for 4623514 from 791f6c3648dd9cea987396375592217b81898dd3 to pi-releaseandroid-wear-9.0.0_r9android-wear-9.0.0_r8android-wear-9.0.0_r7android-wear-9.0.0_r6android-wear-9.0.0_r5android-wear-9.0.0_r4android-wear-9.0.0_r34android-wear-9.0.0_r33android-wear-9.0.0_r32android-wear-9.0.0_r31android-wear-9.0.0_r30android-wear-9.0.0_r3android-wear-9.0.0_r29android-wear-9.0.0_r28android-wear-9.0.0_r27android-wear-9.0.0_r26android-wear-9.0.0_r25android-wear-9.0.0_r24android-wear-9.0.0_r23android-wear-9.0.0_r22android-wear-9.0.0_r21android-wear-9.0.0_r20android-wear-9.0.0_r2android-wear-9.0.0_r19android-wear-9.0.0_r18android-wear-9.0.0_r17android-wear-9.0.0_r16android-wear-9.0.0_r15android-wear-9.0.0_r14android-wear-9.0.0_r13android-wear-9.0.0_r12android-wear-9.0.0_r11android-wear-9.0.0_r10android-wear-9.0.0_r1android-vts-9.0_r9android-vts-9.0_r8android-vts-9.0_r7android-vts-9.0_r6android-vts-9.0_r5android-vts-9.0_r4android-vts-9.0_r19android-vts-9.0_r18android-vts-9.0_r17android-vts-9.0_r16android-vts-9.0_r15android-vts-9.0_r14android-vts-9.0_r13android-vts-9.0_r12android-vts-9.0_r11android-vts-9.0_r10android-security-9.0.0_r76android-security-9.0.0_r75android-security-9.0.0_r74android-security-9.0.0_r73android-security-9.0.0_r72android-security-9.0.0_r71android-security-9.0.0_r70android-security-9.0.0_r69android-security-9.0.0_r68android-security-9.0.0_r67android-security-9.0.0_r66android-security-9.0.0_r65android-security-9.0.0_r64android-security-9.0.0_r63android-security-9.0.0_r62android-cts-9.0_r9android-cts-9.0_r8android-cts-9.0_r7android-cts-9.0_r6android-cts-9.0_r5android-cts-9.0_r4android-cts-9.0_r3android-cts-9.0_r20android-cts-9.0_r2android-cts-9.0_r19android-cts-9.0_r18android-cts-9.0_r17android-cts-9.0_r16android-cts-9.0_r15android-cts-9.0_r14android-cts-9.0_r13android-cts-9.0_r12android-cts-9.0_r11android-cts-9.0_r10android-cts-9.0_r1android-9.0.0_r9android-9.0.0_r8android-9.0.0_r7android-9.0.0_r61android-9.0.0_r60android-9.0.0_r6android-9.0.0_r59android-9.0.0_r58android-9.0.0_r57android-9.0.0_r56android-9.0.0_r55android-9.0.0_r54android-9.0.0_r53android-9.0.0_r52android-9.0.0_r51android-9.0.0_r50android-9.0.0_r5android-9.0.0_r49android-9.0.0_r48android-9.0.0_r3android-9.0.0_r2android-9.0.0_r18android-9.0.0_r17android-9.0.0_r10android-9.0.0_r1security-pi-releasepie-vts-releasepie-security-releasepie-s2-releasepie-release-2pie-releasepie-r2-s2-releasepie-r2-s1-releasepie-r2-releasepie-platform-releasepie-gsipie-cuttlefish-testingpie-cts-release
Change-Id: If4d38d25a34413228062497bcd47fd86904ca5a3
-rw-r--r-- | README.md | 72 | ||||
-rwxr-xr-x | avbtool | 159 | ||||
-rw-r--r-- | libavb/avb_cmdline.c | 106 | ||||
-rw-r--r-- | libavb/avb_cmdline.h | 36 | ||||
-rw-r--r-- | libavb/avb_crypto.h | 3 | ||||
-rw-r--r-- | libavb/avb_hash_descriptor.c | 1 | ||||
-rw-r--r-- | libavb/avb_hash_descriptor.h | 17 | ||||
-rw-r--r-- | libavb/avb_hashtree_descriptor.c | 1 | ||||
-rw-r--r-- | libavb/avb_hashtree_descriptor.h | 17 | ||||
-rw-r--r-- | libavb/avb_ops.h | 64 | ||||
-rw-r--r-- | libavb/avb_slot_verify.c | 271 | ||||
-rw-r--r-- | libavb/avb_util.c | 27 | ||||
-rw-r--r-- | libavb/avb_util.h | 10 | ||||
-rw-r--r-- | libavb/avb_version.h | 2 | ||||
-rw-r--r-- | test/avb_atx_slot_verify_unittest.cc | 67 | ||||
-rw-r--r-- | test/avb_atx_validate_unittest.cc | 107 | ||||
-rw-r--r-- | test/avb_slot_verify_unittest.cc | 522 | ||||
-rw-r--r-- | test/avbtool_unittest.cc | 446 | ||||
-rw-r--r-- | test/fake_avb_ops.cc | 79 | ||||
-rw-r--r-- | test/fake_avb_ops.h | 141 |
20 files changed, 1802 insertions, 346 deletions
@@ -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. @@ -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_slot_verify_unittest.cc b/test/avb_atx_slot_verify_unittest.cc index 79c1cfd..8992c72 100644 --- a/test/avb_atx_slot_verify_unittest.cc +++ b/test/avb_atx_slot_verify_unittest.cc @@ -49,7 +49,7 @@ namespace avb { // relevant locations). class AvbAtxSlotVerifyExampleTest : public BaseAvbToolTest, - public FakeAvbOpsDelegate, + public FakeAvbOpsDelegateWithDefaults, public ::testing::WithParamInterface<uint64_t> { public: ~AvbAtxSlotVerifyExampleTest() override = default; @@ -63,33 +63,7 @@ class AvbAtxSlotVerifyExampleTest ops_.set_stored_is_device_unlocked(false); } - // FakeAvbOpsDelegate methods. All forward to FakeAvbOps default except for - // validate_vbmeta_public_key(). - AvbIOResult read_from_partition(const char* partition, - int64_t offset, - size_t num_bytes, - void* buffer, - size_t* out_num_read) override { - return ops_.read_from_partition( - partition, offset, num_bytes, buffer, out_num_read); - } - - AvbIOResult get_preloaded_partition( - const char* partition, - size_t num_bytes, - uint8_t** out_pointer, - size_t* out_num_bytes_preloaded) override { - return ops_.get_preloaded_partition( - partition, num_bytes, out_pointer, out_num_bytes_preloaded); - } - - AvbIOResult write_to_partition(const char* partition, - int64_t offset, - size_t num_bytes, - const void* buffer) override { - return ops_.write_to_partition(partition, offset, num_bytes, buffer); - } - + // FakeAvbOpsDelegate overrides. AvbIOResult validate_vbmeta_public_key(AvbOps* ops, const uint8_t* public_key_data, size_t public_key_length, @@ -106,13 +80,6 @@ class AvbAtxSlotVerifyExampleTest out_key_is_trusted); } - AvbIOResult read_rollback_index(AvbOps* ops, - size_t rollback_index_slot, - uint64_t* out_rollback_index) override { - return ops_.read_rollback_index( - ops, rollback_index_slot, out_rollback_index); - } - AvbIOResult write_rollback_index(AvbOps* ops, size_t rollback_index_slot, uint64_t rollback_index) override { @@ -120,35 +87,6 @@ class AvbAtxSlotVerifyExampleTest return ops_.write_rollback_index(ops, rollback_index_slot, rollback_index); } - AvbIOResult read_is_device_unlocked(AvbOps* ops, - bool* out_is_device_unlocked) override { - return ops_.read_is_device_unlocked(ops, out_is_device_unlocked); - } - - AvbIOResult get_unique_guid_for_partition(AvbOps* ops, - const char* partition, - char* guid_buf, - size_t guid_buf_size) override { - return ops_.get_unique_guid_for_partition( - ops, partition, guid_buf, guid_buf_size); - } - - AvbIOResult get_size_of_partition(AvbOps* ops, - const char* partition, - uint64_t* out_size) override { - return ops_.get_size_of_partition(ops, partition, out_size); - } - - AvbIOResult read_permanent_attributes( - AvbAtxPermanentAttributes* attributes) override { - return ops_.read_permanent_attributes(attributes); - } - - AvbIOResult read_permanent_attributes_hash( - uint8_t hash[AVB_SHA256_DIGEST_SIZE]) override { - return ops_.read_permanent_attributes_hash(hash); - } - void set_key_version(size_t rollback_index_location, uint64_t key_version) override { num_key_version_calls_++; @@ -229,7 +167,6 @@ class AvbAtxSlotVerifyExampleTest } protected: - FakeAvbOps ops_; AvbAtxPermanentAttributes attributes_; int num_atx_calls_ = 0; int num_key_version_calls_ = 0; diff --git a/test/avb_atx_validate_unittest.cc b/test/avb_atx_validate_unittest.cc index 254048a..c32ecf8 100644 --- a/test/avb_atx_validate_unittest.cc +++ b/test/avb_atx_validate_unittest.cc @@ -82,7 +82,8 @@ class ScopedRSA { namespace avb { -class AvbAtxValidateTest : public ::testing::Test, public FakeAvbOpsDelegate { +class AvbAtxValidateTest : public ::testing::Test, + public FakeAvbOpsDelegateWithDefaults { public: ~AvbAtxValidateTest() override {} @@ -105,8 +106,11 @@ class AvbAtxValidateTest : public ::testing::Test, public FakeAvbOpsDelegate { } AvbIOResult 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) override { + // Expect method not used. return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION; } @@ -169,6 +173,21 @@ class AvbAtxValidateTest : public ::testing::Test, public FakeAvbOpsDelegate { 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_) { @@ -185,11 +204,6 @@ class AvbAtxValidateTest : public ::testing::Test, public FakeAvbOpsDelegate { return ops_.read_permanent_attributes_hash(hash); } - void set_key_version(size_t rollback_index_location, - uint64_t key_version) override { - ops_.set_key_version(rollback_index_location, key_version); - } - protected: virtual AvbIOResult Validate(bool* is_trusted) { return avb_atx_validate_vbmeta_public_key( @@ -222,7 +236,6 @@ class AvbAtxValidateTest : public ::testing::Test, public FakeAvbOpsDelegate { metadata_.product_signing_key_certificate.signature)); } - FakeAvbOps ops_; AvbAtxPermanentAttributes attributes_; AvbAtxPublicKeyMetadata metadata_; bool fail_read_permanent_attributes_{false}; @@ -551,7 +564,8 @@ TEST_F(AvbAtxValidateTest, PSKMismatch) { } // A fixture for testing avb_slot_verify() with ATX. -class AvbAtxSlotVerifyTest : public BaseAvbToolTest, public FakeAvbOpsDelegate { +class AvbAtxSlotVerifyTest : public BaseAvbToolTest, + public FakeAvbOpsDelegateWithDefaults { public: ~AvbAtxSlotVerifyTest() override = default; @@ -570,32 +584,7 @@ class AvbAtxSlotVerifyTest : public BaseAvbToolTest, public FakeAvbOpsDelegate { ops_.set_stored_is_device_unlocked(false); } - // FakeAvbOpsDelegate methods. All forward to FakeAvbOps default except for - // validate_vbmeta_public_key(). - AvbIOResult read_from_partition(const char* partition, - int64_t offset, - size_t num_bytes, - void* buffer, - size_t* out_num_read) override { - return ops_.read_from_partition( - partition, offset, num_bytes, buffer, out_num_read); - } - - AvbIOResult get_preloaded_partition( - const char* partition, size_t num_bytes, uint8_t** out_pointer, - size_t* out_num_bytes_preloaded) override { - *out_num_bytes_preloaded = 0; - *out_pointer = NULL; - return AVB_IO_RESULT_OK; - } - - AvbIOResult write_to_partition(const char* partition, - int64_t offset, - size_t num_bytes, - const void* buffer) override { - return ops_.write_to_partition(partition, offset, num_bytes, buffer); - } - + // FakeAvbOpsDelegate override. AvbIOResult validate_vbmeta_public_key(AvbOps* ops, const uint8_t* public_key_data, size_t public_key_length, @@ -612,55 +601,7 @@ class AvbAtxSlotVerifyTest : public BaseAvbToolTest, public FakeAvbOpsDelegate { out_key_is_trusted); } - AvbIOResult read_rollback_index(AvbOps* ops, - size_t rollback_index_slot, - uint64_t* out_rollback_index) override { - return ops_.read_rollback_index( - ops, rollback_index_slot, out_rollback_index); - } - - AvbIOResult write_rollback_index(AvbOps* ops, - size_t rollback_index_slot, - uint64_t rollback_index) override { - return ops_.write_rollback_index(ops, rollback_index_slot, rollback_index); - } - - AvbIOResult read_is_device_unlocked(AvbOps* ops, - bool* out_is_device_unlocked) override { - return ops_.read_is_device_unlocked(ops, out_is_device_unlocked); - } - - AvbIOResult get_unique_guid_for_partition(AvbOps* ops, - const char* partition, - char* guid_buf, - size_t guid_buf_size) override { - return ops_.get_unique_guid_for_partition( - ops, partition, guid_buf, guid_buf_size); - } - - AvbIOResult get_size_of_partition(AvbOps* ops, - const char* partition, - uint64_t* out_size) override { - return ops_.get_size_of_partition(ops, partition, out_size); - } - - AvbIOResult read_permanent_attributes( - AvbAtxPermanentAttributes* attributes) override { - return ops_.read_permanent_attributes(attributes); - } - - AvbIOResult read_permanent_attributes_hash( - uint8_t hash[AVB_SHA256_DIGEST_SIZE]) override { - return ops_.read_permanent_attributes_hash(hash); - } - - void set_key_version(size_t rollback_index_location, - uint64_t key_version) override { - return ops_.set_key_version(rollback_index_location, key_version); - } - protected: - FakeAvbOps ops_; AvbAtxPermanentAttributes attributes_; int num_atx_calls_ = 0; 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 a2bb473..769f3cc 100644 --- a/test/fake_avb_ops.h +++ b/test/fake_avb_ops.h @@ -36,7 +36,8 @@ namespace avb { // A delegate interface for ops callbacks. This allows tests to override default -// fake implementations. +// fake implementations. For convenience, test fixtures can inherit +// FakeAvbOpsDelegateWithDefaults and only override as needed. class FakeAvbOpsDelegate { public: virtual ~FakeAvbOpsDelegate() {} @@ -47,7 +48,9 @@ class FakeAvbOpsDelegate { size_t* out_num_read) = 0; virtual AvbIOResult 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) = 0; virtual AvbIOResult write_to_partition(const char* partition, @@ -83,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; @@ -182,9 +194,10 @@ class FakeAvbOps : public FakeAvbOpsDelegate { void* buffer, size_t* out_num_read) override; - AvbIOResult get_preloaded_partition( - const char* partition, size_t num_bytes, uint8_t** out_pointer, - size_t* out_num_bytes_preloaded) override; + AvbIOResult get_preloaded_partition(const char* partition, + size_t num_bytes, + uint8_t** out_pointer, + size_t* out_num_bytes_preloaded) override; AvbIOResult write_to_partition(const char* partition, int64_t offset, @@ -218,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; @@ -249,6 +271,115 @@ 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. +class FakeAvbOpsDelegateWithDefaults : public FakeAvbOpsDelegate { + public: + AvbIOResult read_from_partition(const char* partition, + int64_t offset, + size_t num_bytes, + void* buffer, + size_t* out_num_read) override { + return ops_.read_from_partition( + partition, offset, num_bytes, buffer, out_num_read); + } + + AvbIOResult get_preloaded_partition( + const char* partition, + size_t num_bytes, + uint8_t** out_pointer, + size_t* out_num_bytes_preloaded) override { + return ops_.get_preloaded_partition( + partition, num_bytes, out_pointer, out_num_bytes_preloaded); + } + + AvbIOResult write_to_partition(const char* partition, + int64_t offset, + size_t num_bytes, + const void* buffer) override { + return ops_.write_to_partition(partition, offset, num_bytes, buffer); + } + + AvbIOResult validate_vbmeta_public_key(AvbOps* ops, + const uint8_t* public_key_data, + size_t public_key_length, + const uint8_t* public_key_metadata, + size_t public_key_metadata_length, + bool* out_key_is_trusted) override { + return ops_.validate_vbmeta_public_key(ops, + public_key_data, + public_key_length, + public_key_metadata, + public_key_metadata_length, + out_key_is_trusted); + } + + AvbIOResult read_rollback_index(AvbOps* ops, + size_t rollback_index_slot, + uint64_t* out_rollback_index) override { + return ops_.read_rollback_index( + ops, rollback_index_slot, out_rollback_index); + } + + AvbIOResult write_rollback_index(AvbOps* ops, + size_t rollback_index_slot, + uint64_t rollback_index) override { + return ops_.write_rollback_index(ops, rollback_index_slot, rollback_index); + } + + AvbIOResult read_is_device_unlocked(AvbOps* ops, + bool* out_is_device_unlocked) override { + return ops_.read_is_device_unlocked(ops, out_is_device_unlocked); + } + + AvbIOResult get_unique_guid_for_partition(AvbOps* ops, + const char* partition, + char* guid_buf, + size_t guid_buf_size) override { + return ops_.get_unique_guid_for_partition( + ops, partition, guid_buf, guid_buf_size); + } + + AvbIOResult get_size_of_partition(AvbOps* ops, + const char* partition, + uint64_t* out_size) override { + 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); + } + + AvbIOResult read_permanent_attributes_hash( + uint8_t hash[AVB_SHA256_DIGEST_SIZE]) override { + return ops_.read_permanent_attributes_hash(hash); + } + + void set_key_version(size_t rollback_index_location, + uint64_t key_version) override { + ops_.set_key_version(rollback_index_location, key_version); + } + + protected: + FakeAvbOps ops_; }; } // namespace avb |