Make it possible to include public key metadata.

A new option --public_key_metadata can be used at image build time to
include a "public key metadata" blob in the vbmeta struct and this
data is passed to the validate_vbmeta_public_key() AvbOps operation
along with the public key.

The use-case for this option is a device where the root-of-trust
embedded in the device is different from the key used to sign AVB
metadata. Specifically, the public key metadata blob can be data
signed by the device root-of-trust and the data could assert the trust
chain between this root-of-trust and the AVB public key used to sign
the AVB metadata.

(This change breaks the on-disk image format but that's OK because
we're still pre-1.0 with respect to image format stability
guarantees.)

Bug: 32736356
Test: New unit tests and all unit tests pass.
Test: Tested in UEFI-based bootloader in qemu.

Change-Id: I7b9c3bf2f9326b5bb5659b2a431a59a5c9016aff
diff --git a/avbtool b/avbtool
index 599100b..3ace1e3 100755
--- a/avbtool
+++ b/avbtool
@@ -1464,7 +1464,7 @@
   SIZE = 256
 
   # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|.
-  RESERVED = 152
+  RESERVED = 136
 
   # Keep in sync with |AvbVBMetaImageHeader|.
   FORMAT_STRING = ('!4s2L'  # magic, 2 x version
@@ -1473,6 +1473,7 @@
                    '2Q'  # offset, size (hash)
                    '2Q'  # offset, size (signature)
                    '2Q'  # offset, size (public key)
+                   '2Q'  # offset, size (public key metadata)
                    '2Q'  # offset, size (descriptors)
                    'Q' +  # rollback_index
                    str(RESERVED) + 'x')  # padding for reserved bytes
@@ -1493,7 +1494,9 @@
        self.authentication_data_block_size, self.auxiliary_data_block_size,
        self.algorithm_type, self.hash_offset, self.hash_size,
        self.signature_offset, self.signature_size, self.public_key_offset,
-       self.public_key_size, self.descriptors_offset, self.descriptors_size,
+       self.public_key_size, self.public_key_metadata_offset,
+       self.public_key_metadata_size, self.descriptors_offset,
+       self.descriptors_size,
        self.rollback_index) = struct.unpack(self.FORMAT_STRING, data)
       # Nuke NUL-bytes at the end of the string.
       if self.magic != 'AVB0':
@@ -1511,6 +1514,8 @@
       self.signature_size = 0
       self.public_key_offset = 0
       self.public_key_size = 0
+      self.public_key_metadata_offset = 0
+      self.public_key_metadata_size = 0
       self.descriptors_offset = 0
       self.descriptors_size = 0
       self.rollback_index = 0
@@ -1526,8 +1531,9 @@
         self.header_version_minor, self.authentication_data_block_size,
         self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
         self.hash_size, self.signature_offset, self.signature_size,
-        self.public_key_offset, self.public_key_size, self.descriptors_offset,
-        self.descriptors_size, self.rollback_index))
+        self.public_key_offset, self.public_key_size,
+        self.public_key_metadata_offset, self.public_key_metadata_size,
+        self.descriptors_offset, self.descriptors_size, self.rollback_index))
 
   def encode(self):
     """Serializes the header (256) to a bytearray().
@@ -1541,7 +1547,8 @@
                        self.auxiliary_data_block_size, self.algorithm_type,
                        self.hash_offset, self.hash_size, self.signature_offset,
                        self.signature_size, self.public_key_offset,
-                       self.public_key_size, self.descriptors_offset,
+                       self.public_key_size, self.public_key_metadata_offset,
+                       self.public_key_metadata_size, self.descriptors_offset,
                        self.descriptors_size, self.rollback_index)
 
 
@@ -1777,8 +1784,8 @@
     return desc
 
   def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
-                        key_path, rollback_index, props, props_from_file,
-                        kernel_cmdlines,
+                        key_path, public_key_metadata_path, rollback_index,
+                        props, props_from_file, kernel_cmdlines,
                         generate_dm_verity_cmdline_from_hashtree,
                         include_descriptors_from_image):
     """Implements the 'make_vbmeta_image' command.
@@ -1788,6 +1795,7 @@
       chain_partitions: List of partitions to chain.
       algorithm_name: Name of algorithm to use.
       key_path: Path to key to use or None.
+      public_key_metadata_path: Path to public key metadata or None.
       rollback_index: The rollback index to use.
       props: Properties to insert (list of strings of the form 'key:value').
       props_from_file: Properties to insert (list of strings 'key:<path>').
@@ -1817,8 +1825,8 @@
         descriptors.append(desc)
 
     vbmeta_blob = self._generate_vbmeta_blob(
-        algorithm_name, key_path, descriptors, rollback_index, props,
-        props_from_file, kernel_cmdlines,
+        algorithm_name, key_path, public_key_metadata_path, descriptors,
+        rollback_index, props, props_from_file, kernel_cmdlines,
         generate_dm_verity_cmdline_from_hashtree,
         include_descriptors_from_image)
 
@@ -1826,7 +1834,8 @@
     output.seek(0)
     output.write(vbmeta_blob)
 
-  def _generate_vbmeta_blob(self, algorithm_name, key_path, descriptors,
+  def _generate_vbmeta_blob(self, algorithm_name, key_path,
+                            public_key_metadata_path, descriptors,
                             rollback_index, props, props_from_file,
                             kernel_cmdlines,
                             generate_dm_verity_cmdline_from_hashtree,
@@ -1844,6 +1853,7 @@
     Arguments:
       algorithm_name: The algorithm name as per the ALGORITHMS dict.
       key_path: The path to the .pem file used to sign the blob.
+      public_key_metadata_path: Path to public key metadata or None.
       descriptors: A list of descriptors to insert or None.
       rollback_index: The rollback index to use.
       props: Properties to insert (List of strings of the form 'key:value').
@@ -1918,6 +1928,12 @@
         for desc in image_descriptors:
           encoded_descriptors.extend(desc.encode())
 
+    # Load public key metadata blob, if requested.
+    pkmd_blob = []
+    if public_key_metadata_path:
+      with open(public_key_metadata_path) as f:
+        pkmd_blob = f.read()
+
     key = None
     encoded_key = bytearray()
     if alg.public_key_num_bytes > 0:
@@ -1932,14 +1948,16 @@
 
     h = AvbVBMetaHeader()
 
-    # For the Auxiliary data block, descriptors are stored at offset 0
-    # and the public key is immediately after that.
+    # For the Auxiliary data block, descriptors are stored at offset 0,
+    # followed by the public key, followed by the public key metadata blob.
     h.auxiliary_data_block_size = round_to_multiple(
-        len(encoded_descriptors) + len(encoded_key), 64)
+        len(encoded_descriptors) + len(encoded_key) + len(pkmd_blob), 64)
     h.descriptors_offset = 0
     h.descriptors_size = len(encoded_descriptors)
     h.public_key_offset = h.descriptors_size
     h.public_key_size = len(encoded_key)
+    h.public_key_metadata_offset = h.public_key_offset + h.public_key_size
+    h.public_key_metadata_size = len(pkmd_blob)
 
     # For the Authentication data block, the hash is first and then
     # the signature.
@@ -1962,6 +1980,7 @@
     aux_data_blob = bytearray()
     aux_data_blob.extend(encoded_descriptors)
     aux_data_blob.extend(encoded_key)
+    aux_data_blob.extend(pkmd_blob)
     padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
     aux_data_blob.extend('\0' * padding_bytes)
 
@@ -2013,7 +2032,8 @@
 
   def add_hash_footer(self, image_filename, partition_size, partition_name,
                       hash_algorithm, salt, algorithm_name, key_path,
-                      rollback_index, props, props_from_file, kernel_cmdlines,
+                      public_key_metadata_path, rollback_index, props,
+                      props_from_file, kernel_cmdlines,
                       generate_dm_verity_cmdline_from_hashtree,
                       include_descriptors_from_image):
     """Implementation of the add_hash_footer on unsparse images.
@@ -2026,6 +2046,7 @@
       salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
       algorithm_name: Name of algorithm to use.
       key_path: Path to key to use or None.
+      public_key_metadata_path: Path to public key metadata or None.
       rollback_index: Rollback index.
       props: Properties to insert (List of strings of the form 'key:value').
       props_from_file: Properties to insert (List of strings 'key:<path>').
@@ -2102,8 +2123,8 @@
 
       # Generate the VBMeta footer.
       vbmeta_blob = self._generate_vbmeta_blob(
-          algorithm_name, key_path, [h_desc], rollback_index, props,
-          props_from_file, kernel_cmdlines,
+          algorithm_name, key_path, public_key_metadata_path, [h_desc],
+          rollback_index, props, props_from_file, kernel_cmdlines,
           generate_dm_verity_cmdline_from_hashtree,
           include_descriptors_from_image)
 
@@ -2150,8 +2171,8 @@
   def add_hashtree_footer(self, image_filename, partition_size, partition_name,
                           generate_fec, fec_num_roots, hash_algorithm,
                           block_size, salt, algorithm_name, key_path,
-                          rollback_index, props, props_from_file,
-                          kernel_cmdlines,
+                          public_key_metadata_path, rollback_index, props,
+                          props_from_file, kernel_cmdlines,
                           generate_dm_verity_cmdline_from_hashtree,
                           include_descriptors_from_image,
                           calc_max_image_size):
@@ -2171,6 +2192,7 @@
       salt: Salt to use as a hexadecimal string or None to use /dev/urandom.
       algorithm_name: Name of algorithm to use.
       key_path: Path to key to use or None.
+      public_key_metadata_path: Path to public key metadata or None.
       rollback_index: Rollback index.
       props: Properties to insert (List of strings of the form 'key:value').
       props_from_file: Properties to insert (List of strings 'key:<path>').
@@ -2312,8 +2334,8 @@
       # Generate the VBMeta footer and add padding as needed.
       vbmeta_offset = tree_offset + len_hashtree_and_fec
       vbmeta_blob = self._generate_vbmeta_blob(
-          algorithm_name, key_path, [ht_desc], rollback_index, props,
-          props_from_file, kernel_cmdlines,
+          algorithm_name, key_path, public_key_metadata_path, [ht_desc],
+          rollback_index, props, props_from_file, kernel_cmdlines,
           generate_dm_verity_cmdline_from_hashtree,
           include_descriptors_from_image)
       padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
@@ -2522,6 +2544,10 @@
                             help='Path to RSA private key file',
                             metavar='KEY',
                             required=False)
+    sub_parser.add_argument('--public_key_metadata',
+                            help='Path to public key metadata file',
+                            metavar='KEY_METADATA',
+                            required=False)
     sub_parser.add_argument('--rollback_index',
                             help='Rollback Index',
                             type=parse_number,
@@ -2701,7 +2727,8 @@
   def make_vbmeta_image(self, args):
     """Implements the 'make_vbmeta_image' sub-command."""
     self.avb.make_vbmeta_image(args.output, args.chain_partition,
-                               args.algorithm, args.key, args.rollback_index,
+                               args.algorithm, args.key,
+                               args.public_key_metadata, args.rollback_index,
                                args.prop, args.prop_from_file,
                                args.kernel_cmdline,
                                args.generate_dm_verity_cmdline_from_hashtree,
@@ -2712,8 +2739,9 @@
     self.avb.add_hash_footer(args.image.name, args.partition_size,
                              args.partition_name, args.hash_algorithm,
                              args.salt, args.algorithm, args.key,
-                             args.rollback_index, args.prop,
-                             args.prop_from_file, args.kernel_cmdline,
+                             args.public_key_metadata, args.rollback_index,
+                             args.prop, args.prop_from_file,
+                             args.kernel_cmdline,
                              args.generate_dm_verity_cmdline_from_hashtree,
                              args.include_descriptors_from_image)
 
@@ -2725,6 +2753,7 @@
                                  args.generate_fec, args.fec_num_roots,
                                  args.hash_algorithm, args.block_size,
                                  args.salt, args.algorithm, args.key,
+                                 args.public_key_metadata,
                                  args.rollback_index, args.prop,
                                  args.prop_from_file,
                                  args.kernel_cmdline,