Snap for 6533464 from 23dbe6ec9b8734aea9812e1ecd3af47e88d6360a to sdk-release

Change-Id: I578fa5d62657b7040d0204ec4e99ffc5b602f0cf
diff --git a/Android.bp b/Android.bp
index aa4897a..a9bd385 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2017 The Android Open Source Project
+// Copyright (C) 2017-2020 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,7 +14,10 @@
 // limitations under the License.
 //
 
-subdirs = ["test", "tools"]
+subdirs = [
+    "test",
+    "tools",
+]
 
 cc_defaults {
     name: "avb_defaults",
@@ -85,6 +88,7 @@
         },
         py3: {
             enabled: true,
+            embedded_launcher: true,
         },
     },
 }
@@ -93,7 +97,6 @@
     name: "aftl_proto",
     srcs: [
         "proto/api.proto",
-        "proto/aftl.proto",
         "proto/crypto/sigpb/sigpb.proto",
         "proto/crypto/keyspb/keyspb.proto",
         "proto/trillian.proto",
@@ -243,7 +246,8 @@
     name: "libavb_aftl",
     defaults: [
         "avb_defaults",
-        "avb_sources"],
+        "avb_sources",
+    ],
     host_supported: true,
     recovery_available: true,
     header_libs: ["avb_headers"],
@@ -252,12 +256,38 @@
         "-fno-stack-protector",
     ],
     srcs: [
-      "libavb_aftl/avb_aftl_util.c",
-      "libavb_aftl/avb_aftl_validate.c",
-      "libavb_aftl/avb_aftl_verify.c",
+        "libavb_aftl/avb_aftl_util.c",
+        "libavb_aftl/avb_aftl_validate.c",
+        "libavb_aftl/avb_aftl_verify.c",
     ],
 }
 
+cc_fuzz {
+    name: "libavb_aftl_fuzzer",
+    defaults: ["avb_defaults"],
+    // The fuzzing entry point is declared and defined in the same file.
+    // Overwrite the behaviour introduced by avb_defaults.
+    cflags: [
+        "-Wno-missing-prototypes",
+    ],
+    srcs: [
+        "test/avb_aftl_fuzz.cc",
+    ],
+    static_libs: [
+        "libavb",
+        "libavb_aftl",
+    ],
+    host_supported: true,
+    corpus: ["test/corpus/*"],
+    fuzz_config: {
+        cc: [
+            "tweek@google.com",
+            "jpm@google.com",
+        ],
+        componentid: 685985,
+    },
+}
+
 cc_library_host_static {
     name: "libavb_atx_host",
     defaults: ["avb_defaults"],
@@ -301,7 +331,8 @@
     ],
     data: [
         "avbtool",
-        "test/avbtool_signing_helper_*.py",
+        "test/avbtool_signing_helper_test.py",
+        "test/avbtool_signing_helper_with_files_test.py",
         "test/data/*",
     ],
     test_config: "test/libavb_host_unittest.xml",
@@ -321,8 +352,8 @@
         "libcrypto",
     ],
     cflags: [
-            "-Wno-missing-prototypes",
-            "-DAVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED",
+        "-Wno-missing-prototypes",
+        "-DAVB_AB_I_UNDERSTAND_LIBAVB_AB_IS_DEPRECATED",
     ],
     srcs: [
         "test/avb_ab_flow_unittest.cc",
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+  license_type: NOTICE
+}
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 5789f69..0e19284 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,4 +1,5 @@
 [Builtin Hooks]
+bpfmt = true
 clang_format = true
 pylint = true
 
diff --git a/README.md b/README.md
index 8ae645e..4c15fb3 100644
--- a/README.md
+++ b/README.md
@@ -209,6 +209,11 @@
 Storing signed verification data on other images - for example
 `boot.img` and `system.img` - is also done with `avbtool`.
 
+The minimum requirement for running `avbtool` is to either have
+Python 3.5 installed or build the avbtool with the embedded launcher
+using `m avbtool` and then run it out of the build artifact directory:
+`out/soong/host/linux-x86/bin/avbtool`
+
 In addition to `avbtool`, a library - `libavb` - is provided. This
 library performs all verification on the device side e.g. it starts by
 loading the `vbmeta` partition, checks the signature, and then goes on
diff --git a/aftltool.py b/aftltool.py
index 0fe2d07..613e83b 100755
--- a/aftltool.py
+++ b/aftltool.py
@@ -24,10 +24,11 @@
 #
 """Command-line tool for AFTL support for Android Verified Boot images."""
 
+import abc
 import argparse
-import base64
+import enum
 import hashlib
-import json
+import io
 import multiprocessing
 import os
 import queue
@@ -46,9 +47,7 @@
 
 # pylint: disable=wrong-import-position,import-error
 import avbtool
-import aftl_pb2
 import api_pb2
-from crypto.sigpb import sigpb_pb2
 # pylint: enable=wrong-import-position,import-error
 
 
@@ -350,14 +349,16 @@
     log_url_size: Length of the string representing the transparency log URL.
     leaf_index: Leaf index in the transparency log representing this entry.
     log_root_descriptor_size: Size of the transparency log's SignedLogRoot.
-    fw_info_leaf_size: Size of the FirmwareInfo leaf passed to the log.
+    annotation_leaf_size: Size of the SignedVBMetaPrimaryAnnotationLeaf passed
+        to the log.
     log_root_sig_size: Size in bytes of the log_root_signature
     proof_hash_count: Number of hashes comprising the inclusion proof.
     inc_proof_size: The total size of the inclusion proof, in bytes.
     log_url: The URL for the transparency log that generated this inclusion
         proof.
     log_root_descriptor: The data comprising the signed tree head structure.
-    fw_info_leaf: The data comprising the FirmwareInfo leaf.
+    annotation_leaf: The data comprising the SignedVBMetaPrimaryAnnotationLeaf
+        leaf.
     log_root_signature: The data comprising the log root signature.
     proofs: The hashes comprising the inclusion proof.
 
@@ -370,9 +371,8 @@
                    'H'    # log root signature size
                    'B'    # number of hashes in the inclusion proof
                    'L')   # size of the inclusion proof in bytes
-  # These are used to capture the log_url, log_root_descriptor,
-  # fw_info leaf, log root signature, and the proofs elements for the
-  # encode function.
+  # This header is followed by the log_url, log_root_descriptor,
+  # annotation leaf, log root signature, and the proofs elements.
 
   def __init__(self, data=None):
     """Initializes a new ICP entry object.
@@ -391,7 +391,7 @@
       (self._log_url_size_expected,
        self.leaf_index,
        self._log_root_descriptor_size_expected,
-       self._fw_info_leaf_size_expected,
+       self._annotation_leaf_size_expected,
        self._log_root_sig_size_expected,
        self._proof_hash_count_expected,
        self._inc_proof_size_expected) = struct.unpack(self.FORMAT_STRING,
@@ -401,18 +401,20 @@
       expected_format_string = '{}s{}s{}s{}s{}s'.format(
           self._log_url_size_expected,
           self._log_root_descriptor_size_expected,
-          self._fw_info_leaf_size_expected,
+          self._annotation_leaf_size_expected,
           self._log_root_sig_size_expected,
           self._inc_proof_size_expected)
 
-      (log_url, log_root_descriptor_bytes, fw_info_leaf_bytes,
+      (log_url, log_root_descriptor_bytes, annotation_leaf_bytes,
        self.log_root_signature, proof_bytes) = struct.unpack(
            expected_format_string, data[self.SIZE:self.get_expected_size()])
 
       self.log_url = log_url.decode('ascii')
       self.log_root_descriptor = TrillianLogRootDescriptor(
           log_root_descriptor_bytes)
-      self.fw_info_leaf = FirmwareInfoLeaf(fw_info_leaf_bytes)
+
+      self.annotation_leaf = SignedVBMetaPrimaryAnnotationLeaf.parse(
+          annotation_leaf_bytes)
 
       self.proofs = []
       if self._proof_hash_count_expected > 0:
@@ -427,7 +429,7 @@
       self.leaf_index = 0
       self.log_url = ''
       self.log_root_descriptor = TrillianLogRootDescriptor()
-      self.fw_info_leaf = FirmwareInfoLeaf()
+      self.annotation_leaf = SignedVBMetaPrimaryAnnotationLeaf()
       self.log_root_signature = b''
       self.proofs = []
     if not self.is_valid():
@@ -448,11 +450,11 @@
     return self._log_root_descriptor_size_expected
 
   @property
-  def fw_info_leaf_size(self):
-    """Gets the size of the fw_info_leaf attribute."""
-    if hasattr(self, 'fw_info_leaf'):
-      return self.fw_info_leaf.get_expected_size()
-    return self._fw_info_leaf_size_expected
+  def annotation_leaf_size(self):
+    """Gets the size of the annotation_leaf attribute."""
+    if hasattr(self, 'annotation_leaf'):
+      return self.annotation_leaf.get_expected_size()
+    return self._annotation_leaf_size_expected
 
   @property
   def log_root_sig_size(self):
@@ -490,7 +492,7 @@
     if not transparency_log_pub_key:
       return False
 
-    leaf_hash = rfc6962_hash_leaf(self.fw_info_leaf.encode())
+    leaf_hash = rfc6962_hash_leaf(self.annotation_leaf.encode())
     calc_root = root_from_icp(self.leaf_index,
                               self.log_root_descriptor.tree_size,
                               self.proofs,
@@ -513,7 +515,7 @@
 
     Returns:
       True if the inclusion proof validates and the vbmeta hash of the given
-      VBMeta image matches the one in the fw_info_leaf; otherwise False.
+      VBMeta image matches the one in the annotation leaf; otherwise False.
     """
     if not vbmeta_image:
       return False
@@ -524,13 +526,13 @@
     # Validates the inclusion proof and then compare the calculated vbmeta_hash
     # against the one in the inclusion proof.
     return (self.verify_icp(transparency_log_pub_key)
-            and self.fw_info_leaf.vbmeta_hash == vbmeta_hash)
+            and self.annotation_leaf.annotation.vbmeta_hash == vbmeta_hash)
 
   def encode(self):
-    """Serializes the header |SIZE| and data to a bytearray().
+    """Serializes the header |SIZE| and data to bytes.
 
     Returns:
-      A bytearray() with the encoded header.
+      bytes with the encoded header.
 
     Raises:
       AftlError: If invalid entry structure.
@@ -543,7 +545,7 @@
         self.FORMAT_STRING,
         self.log_url_size,
         self.log_root_descriptor_size,
-        self.fw_info_leaf_size,
+        self.annotation_leaf_size,
         self.log_root_sig_size,
         self.inc_proof_size)
 
@@ -552,30 +554,31 @@
 
     return struct.pack(expected_format_string,
                        self.log_url_size, self.leaf_index,
-                       self.log_root_descriptor_size, self.fw_info_leaf_size,
+                       self.log_root_descriptor_size, self.annotation_leaf_size,
                        self.log_root_sig_size, self.proof_hash_count,
                        self.inc_proof_size, self.log_url.encode('ascii'),
                        self.log_root_descriptor.encode(),
-                       self.fw_info_leaf.encode(),
+                       self.annotation_leaf.encode(),
                        self.log_root_signature,
                        proof_bytes)
 
-  def translate_response(self, log_url, afi_response):
-    """Translates an AddFirmwareInfoResponse object to an AftlIcpEntry.
+  def translate_response(self, log_url, avbm_response):
+    """Translates an AddVBMetaResponse object to an AftlIcpEntry.
 
     Arguments:
       log_url: String representing the transparency log URL.
-      afi_response: The AddFirmwareResponse object to translate.
+      avbm_response: The AddVBMetaResponse object to translate.
     """
     self.log_url = log_url
 
-    # Deserializes from AddFirmwareInfoResponse.
-    self.leaf_index = afi_response.fw_info_proof.proof.leaf_index
-    self.log_root_descriptor = TrillianLogRootDescriptor(
-        afi_response.fw_info_proof.sth.log_root)
-    self.fw_info_leaf = FirmwareInfoLeaf(afi_response.fw_info_leaf)
-    self.log_root_signature = afi_response.fw_info_proof.sth.log_root_signature
-    self.proofs = afi_response.fw_info_proof.proof.hashes
+    # Deserializes from AddVBMetaResponse.
+    proof = avbm_response.annotation_proof
+    self.leaf_index = proof.proof.leaf_index
+    self.log_root_descriptor = TrillianLogRootDescriptor(proof.sth.log_root)
+    self.annotation_leaf = SignedVBMetaPrimaryAnnotationLeaf.parse(
+        avbm_response.annotation_leaf)
+    self.log_root_signature = proof.sth.log_root_signature
+    self.proofs = proof.proof.hashes
 
   def get_expected_size(self):
     """Gets the expected size of the full entry out of the header.
@@ -584,7 +587,7 @@
       The expected size of the AftlIcpEntry from the header.
     """
     return (self.SIZE + self.log_url_size + self.log_root_descriptor_size +
-            self.fw_info_leaf_size + self.log_root_sig_size +
+            self.annotation_leaf_size + self.log_root_sig_size +
             self.inc_proof_size)
 
   def is_valid(self):
@@ -604,9 +607,9 @@
       sys.stderr.write('ICP entry: invalid TrillianLogRootDescriptor.\n')
       return False
 
-    if (not self.fw_info_leaf or
-        not isinstance(self.fw_info_leaf, FirmwareInfoLeaf)):
-      sys.stderr.write('ICP entry: invalid FirmwareInfo.\n')
+    if (not self.annotation_leaf or
+        not isinstance(self.annotation_leaf, Leaf)):
+      sys.stderr.write('ICP entry: invalid Leaf.\n')
       return False
     return True
 
@@ -626,7 +629,7 @@
         o.write(' ' * 29)
       o.write('{}\n'.format(proof_hash.hex()))
     self.log_root_descriptor.print_desc(o)
-    self.fw_info_leaf.print_desc(o)
+    self.annotation_leaf.print_desc(o)
 
 
 class TrillianLogRootDescriptor(object):
@@ -793,139 +796,268 @@
       o.write(fmt.format(i, 'Metadata:', self.metadata.hex()))
 
 
-class FirmwareInfoLeaf(object):
-  """A class representing the FirmwareInfo leaf.
+def tls_decode_bytes(byte_size, stream):
+  """Decodes a variable-length vector.
 
-  AFTL returns the fw_info_leaf as a JSON blob and this class is able to
-  parse the blog and provide necessary attributes needed for validation.
+  In the TLS presentation language, a variable-length vector is a pair
+  (size, value). |size| describes the size of the |value| to read
+  in bytes. All values are encoded in big-endian.
+  See https://tools.ietf.org/html/rfc8446#section-3 for more details.
+
+  Arguments:
+      byte_size: A format character as described in the struct module
+          which describes the expected length of the size. For
+          instance, "B", "H", "L" or "Q".
+      stream: a BytesIO which contains the value to decode.
+
+  Returns:
+    A bytes containing the value decoded.
+
+  Raises:
+    AftlError: If |byte_size| is not a known format character, or if not
+    enough data is available to decode the size or the value.
+  """
+  byte_size_format = "!" + byte_size
+  try:
+    byte_size_length = struct.calcsize(byte_size_format)
+  except struct.error:
+    raise AftlError("Invalid byte_size character: {}. It must be a "
+                    "format supported by struct.".format(byte_size))
+  try:
+    value_size = struct.unpack(byte_size_format,
+                               stream.read(byte_size_length))[0]
+  except struct.error:
+    raise AftlError("Not enough data to read size: {}".format(byte_size))
+  value = stream.read(value_size)
+  if value_size != len(value):
+    raise AftlError("Not enough data to read value: "
+                    "{} != {}".format(value_size, len(value)))
+  return value
+
+def tls_encode_bytes(byte_size, value, stream):
+  """Encodes a variable-length vector.
+
+  In the TLS presentation language, a variable-length vector is a pair
+  (size, value). |size| describes the size of the |value| to read
+  in bytes. All values are encoded in big-endian.
+  See https://tools.ietf.org/html/rfc8446#section-3 for more details.
+
+  Arguments:
+      byte_size: A format character as described in the struct module
+          which describes the expected length of the size. For
+          instance, "B", "H", "L" or "Q".
+      value: the value to encode. The length of |value| must be
+          representable with |byte_size|.
+      stream: a BytesIO to which the value is encoded to.
+
+  Raises:
+    AftlError: If |byte_size| is not a known format character, or if
+    |value|'s length cannot be represent with |byte_size|.
+  """
+  byte_size_format = "!" + byte_size
+  try:
+    stream.write(struct.pack(byte_size_format, len(value)))
+  except struct.error:
+    # Whether byte_size is invalid or not large enough to represent value,
+    # struct returns an struct.error exception. Instead of matching on the
+    # exception message, capture both cases in a generic message.
+    raise AftlError("Invalid byte_size to store {} bytes".format(len(value)))
+  stream.write(value)
+
+class HashAlgorithm(enum.Enum):
+  SHA256 = 0
+
+class SignatureAlgorithm(enum.Enum):
+  RSA = 0
+  ECDSA = 1
+
+class Signature(object):
+  """Represents a signature of some data.
+
+  It is usually made using a manufacturer key and used to sign part of a leaf
+  that belongs to the transparency log. The encoding of this structure must
+  match the server expectation.
 
   Attributes:
-    vbmeta_hash: This is the SHA256 hash of vbmeta.
-    version_incremental: Subcomponent of the build fingerprint as defined at
-      https://source.android.com/compatibility/android-cdd#3_2_2_build_parameters.
-      For example, a Pixel device with the following build fingerprint
-      google/crosshatch/crosshatch:9/PQ3A.190605.003/5524043:user/release-keys,
-      would have 5524043 for the version incremental.
-    platform_key: Public key of the platform. This is the same key used to sign
-      the vbmeta.
-    manufacturer_key_hash:  SHA256 of the manufacturer public key (DER-encoded,
-      x509 subjectPublicKeyInfo format). The public key MUST already be in the
-      list of root keys known and trusted by the AFTL.
-    description: Free form description field. It can be used to annotate this
-      message with further context on the build (e.g., carrier specific build).
+    hash_algorithm: the HashAlgorithm used for the signature.
+    signature_algorithm: the SignatureAlgorithm used.
+    signature: the raw signature in bytes.
   """
+  FORMAT_STRING = ('!B'    # Hash algorithm
+                   'B'     # Signing algorithm
+                  )
+  # Followed by the raw signature, encoded as a TLS variable-length vector
+  # which size is represented using 2 bytes.
 
-  def __init__(self, data=None):
-    """Initializes a new FirmwareInfoLeaf descriptor."""
-    if data:
-      # We have to preserve the original fw_info_leaf bytes in order to preserve
-      # hash equivalence with what is stored in the Trillian log and matches up
-      # with the proofs.
-      self._fw_info_leaf_bytes = data
+  def __init__(self, hash_algorithm=HashAlgorithm.SHA256,
+               signature_algorithm=SignatureAlgorithm.RSA, signature=b''):
+    self.hash_algorithm = hash_algorithm
+    self.signature_algorithm = signature_algorithm
+    self.signature = signature
 
-      # Deserialize the JSON blob and keep only the FirmwareInfo parts.
-      try:
-        fw_info_leaf = json.loads(self._fw_info_leaf_bytes)
-        self._fw_info_leaf_dict = (
-            fw_info_leaf['Value']['FwInfo']['info']['info'])
-      except (ValueError, KeyError) as e:
-        raise AftlError('Invalid structure for FirmwareInfoLeaf: {}'.format(e))
-    else:
-      self._fw_info_leaf_bytes = b''
-      self._fw_info_leaf_dict = dict()
-
-    if not self.is_valid():
-      raise AftlError('Invalid structure for FirmwareInfoLeaf.')
-
-  @property
-  def vbmeta_hash(self):
-    """Gets the vbmeta_hash attribute."""
-    return self._lookup_base64_attribute('vbmeta_hash')
-
-  @property
-  def version_incremental(self):
-    """Gets the version_incremental attribute."""
-    return self._fw_info_leaf_dict.get('version_incremental')
-
-  @property
-  def platform_key(self):
-    """Gets the platform key attribute."""
-    return self._lookup_base64_attribute('platform_key')
-
-  @property
-  def manufacturer_key_hash(self):
-    """Gets the manufacturer_key_hash attribute."""
-    return self._lookup_base64_attribute('manufacturer_key_hash')
-
-  @property
-  def description(self):
-    """Gets the description attribute."""
-    return self._fw_info_leaf_dict.get('description')
-
-  def _lookup_base64_attribute(self, key):
-    """Looks up an attribute that is Base64 encoded and decodes it.
+  @classmethod
+  def parse(cls, stream):
+    """Parses a TLS-encoded structure and returns a new Signature.
 
     Arguments:
-      key: The name of the attribute to look up.
+      stream: a BytesIO to read the signature from.
 
     Returns:
-      The attribute value or None if not defined.
+      A new Signature object.
+
+    Raises:
+      AftlError: If the hash algorithm or signature algorithm value is
+        unknown; or if the decoding failed.
     """
-    result = self._fw_info_leaf_dict.get(key)
-    if result:
-      result = base64.b64decode(result)
-    return result
+    data_length = struct.calcsize(cls.FORMAT_STRING)
+    (hash_algorithm, signature_algorithm) = struct.unpack(
+        cls.FORMAT_STRING, stream.read(data_length))
+    try:
+      hash_algorithm = HashAlgorithm(hash_algorithm)
+    except ValueError:
+      raise AftlError('unknown hash algorithm: {}'.format(hash_algorithm))
+    try:
+      signature_algorithm = SignatureAlgorithm(signature_algorithm)
+    except ValueError:
+      raise AftlError('unknown signature algorithm: {}'.format(
+          signature_algorithm))
+    signature = tls_decode_bytes('H', stream)
+    return Signature(hash_algorithm, signature_algorithm, signature)
 
   def get_expected_size(self):
-    """Gets the expected size of the JSON-serialized FirmwareInfoLeaf.
+    """Returns the size of the encoded Signature."""
+    return struct.calcsize(self.FORMAT_STRING) + \
+        struct.calcsize('H') + len(self.signature)
+
+  def encode(self, stream):
+    """Encodes the Signature.
+
+    Arguments:
+      stream: a BytesIO to which the signature is written.
+    """
+    stream.write(struct.pack(self.FORMAT_STRING, self.hash_algorithm.value,
+                             self.signature_algorithm.value))
+    tls_encode_bytes('H', self.signature, stream)
+
+class VBMetaPrimaryAnnotation(object):
+  """An annotation that contains metadata about a VBMeta image.
+
+  Attributes:
+    vbmeta_hash: the SHA256 of the VBMeta it references.
+    version_incremental: the version incremental of the build, as string.
+    manufacturer_key_hash: the hash of the manufacturer key that will
+        sign this annotation.
+    description: a free-form field.
+  """
+
+  def __init__(self, vbmeta_hash=b'', version_incremental='',
+               manufacturer_key_hash=b'', description=''):
+    """Default constructor."""
+    self.vbmeta_hash = vbmeta_hash
+    self.version_incremental = version_incremental
+    self.manufacturer_key_hash = manufacturer_key_hash
+    self.description = description
+
+  @classmethod
+  def parse(cls, stream):
+    """Parses a VBMetaPrimaryAnnotation from data.
+
+    Arguments:
+      stream: an io.BytesIO to decode the annotation from.
 
     Returns:
-      The expected size of the FirmwareInfo leaf in byte or 0 if not initalized.
-    """
-    if not self._fw_info_leaf_bytes:
-      return 0
-    return len(self._fw_info_leaf_bytes)
+      A new VBMetaPrimaryAnnotation.
 
-  def encode(self):
-    """Serializes the FirmwareInfoLeaf.
+    Raises:
+      AftlError: If an error occured while parsing the annotation.
+    """
+    vbmeta_hash = tls_decode_bytes("B", stream)
+    version_incremental = tls_decode_bytes("B", stream)
+    try:
+      version_incremental = version_incremental.decode("ascii")
+    except UnicodeError:
+      raise AftlError('Failed to convert version incremental to an ASCII'
+                      'string')
+    manufacturer_key_hash = tls_decode_bytes("B", stream)
+    description = tls_decode_bytes("H", stream)
+    try:
+      description = description.decode("utf-8")
+    except UnicodeError:
+      raise AftlError('Failed to convert description to an UTF-8 string')
+    return cls(vbmeta_hash, version_incremental, manufacturer_key_hash,
+               description)
+
+  def sign(self, manufacturer_key_path, signing_helper=None,
+           signing_helper_with_files=None):
+    """Signs the annotation.
+
+    Arguments:
+      manufacturer_key_path: Path to key used to sign messages sent to the
+        transparency log servers.
+      signing_helper: Program which signs a hash and returns a signature.
+      signing_helper_with_files: Same as signing_helper but uses files instead.
 
     Returns:
-      A bytearray() with the JSON-serialized FirmwareInfoLeaf.
-    """
-    return self._fw_info_leaf_bytes
+      A new SignedVBMetaPrimaryAnnotation.
 
-  def is_valid(self):
-    """Ensures that values in the descritor are sane.
-
-    Returns:
-      True if the values are sane; otherwise False.
+    Raises:
+      AftlError: If an error occured while signing the annotation.
     """
-    # Checks that the decode fw_info_leaf at max contains values defined in the
-    # FirmwareInfo proto buf.
-    expected_fields = set(aftl_pb2.FirmwareInfo()
-                          .DESCRIPTOR.fields_by_name.keys())
-    actual_fields = set(self._fw_info_leaf_dict.keys())
-    if actual_fields.issubset(expected_fields):
-      return True
-    return False
+    # AFTL supports SHA256_RSA4096 for now, more will be available.
+    algorithm_name = 'SHA256_RSA4096'
+    encoded_leaf = io.BytesIO()
+    self.encode(encoded_leaf)
+    try:
+      rsa_key = avbtool.RSAPublicKey(manufacturer_key_path)
+      raw_signature = rsa_key.sign(algorithm_name, encoded_leaf.getvalue(),
+                                   signing_helper, signing_helper_with_files)
+    except avbtool.AvbError as e:
+      raise AftlError('Failed to sign VBMetaPrimaryAnnotation with '
+                      '--manufacturer_key: {}'.format(e))
+    signature = Signature(hash_algorithm=HashAlgorithm.SHA256,
+                          signature_algorithm=SignatureAlgorithm.RSA,
+                          signature=raw_signature)
+    return SignedVBMetaPrimaryAnnotation(signature=signature, annotation=self)
+
+  def encode(self, stream):
+    """Encodes the VBMetaPrimaryAnnotation.
+
+    Arguments:
+      stream: a BytesIO to which the signature is written.
+
+    Raises:
+      AftlError: If the encoding failed.
+    """
+    tls_encode_bytes("B", self.vbmeta_hash, stream)
+    try:
+      tls_encode_bytes("B", self.version_incremental.encode("ascii"), stream)
+    except UnicodeError:
+      raise AftlError('Unable to encode version incremental to ASCII')
+    tls_encode_bytes("B", self.manufacturer_key_hash, stream)
+    try:
+      tls_encode_bytes("H", self.description.encode("utf-8"), stream)
+    except UnicodeError:
+      raise AftlError('Unable to encode description to UTF-8')
+
+  def get_expected_size(self):
+    """Returns the size of the encoded annotation."""
+    b = io.BytesIO()
+    self.encode(b)
+    return len(b.getvalue())
 
   def print_desc(self, o):
-    """Print the FirmwareInfoLeaf.
+    """Print the VBMetaPrimaryAnnotation.
 
     Arguments:
       o: The object to write the output to.
     """
-    o.write('    Firmware Info Leaf:\n')
-    # The order of the fields is based on the definition in
-    # proto.aftl_pb2.FirmwareInfo.
-    i = ' ' * 6
+    o.write('      VBMeta Primary Annotation:\n')
+    i = ' ' * 8
     fmt = '{}{:23}{}\n'
     if self.vbmeta_hash:
       o.write(fmt.format(i, 'VBMeta hash:', self.vbmeta_hash.hex()))
     if self.version_incremental:
       o.write(fmt.format(i, 'Version incremental:', self.version_incremental))
-    if self.platform_key:
-      o.write(fmt.format(i, 'Platform key:', self.platform_key))
     if self.manufacturer_key_hash:
       o.write(fmt.format(i, 'Manufacturer key hash:',
                          self.manufacturer_key_hash.hex()))
@@ -933,6 +1065,244 @@
       o.write(fmt.format(i, 'Description:', self.description))
 
 
+class SignedVBMetaPrimaryAnnotation(object):
+  """A Signed VBMetaPrimaryAnnotation.
+
+  Attributes:
+    signature: a Signature.
+    annotation: a VBMetaPrimaryAnnotation.
+  """
+
+  def __init__(self, signature=None, annotation=None):
+    """Default constructor."""
+    if not signature:
+      signature = Signature()
+    self.signature = signature
+    if not annotation:
+      annotation = VBMetaPrimaryAnnotation()
+    self.annotation = annotation
+
+  @classmethod
+  def parse(cls, stream):
+    """Parses a signed annotation."""
+    signature = Signature.parse(stream)
+    annotation = VBMetaPrimaryAnnotation.parse(stream)
+    return cls(signature, annotation)
+
+  def get_expected_size(self):
+    """Returns the size of the encoded signed annotation."""
+    return self.signature.get_expected_size() + \
+             self.annotation.get_expected_size()
+
+  def encode(self, stream):
+    """Encodes the SignedVBMetaPrimaryAnnotation.
+
+    Arguments:
+      stream: a BytesIO to which the object is written.
+
+    Raises:
+      AftlError: If the encoding failed.
+    """
+    self.signature.encode(stream)
+    self.annotation.encode(stream)
+
+  def print_desc(self, o):
+    """Prints the annotation.
+
+    Arguments:
+      o: The object to write the output to.
+    """
+    self.annotation.print_desc(o)
+
+class Leaf(abc.ABC):
+  """An abstract class to represent the leaves in the transparency log."""
+  FORMAT_STRING = ('!B'   # Version
+                   'Q'    # Timestamp
+                   'B'    # LeafType
+                  )
+
+  class LeafType(enum.Enum):
+    VBMetaType = 0
+    SignedVBMetaPrimaryAnnotationType = 1
+
+  def __init__(self, version=1, timestamp=0, leaf_type=LeafType.VBMetaType):
+    """Build a new leaf."""
+    self.version = version
+    self.timestamp = timestamp
+    self.leaf_type = leaf_type
+
+  @classmethod
+  def _parse_header(cls, stream):
+    """Parses the header of a leaf.
+
+    This is called with the parse method of the subclasses.
+
+    Arguments:
+      stream: a BytesIO to read the header from.
+
+    Returns:
+      A tuple (version, timestamp, leaf_type).
+
+    Raises:
+      AftlError: If the header cannot be decoded; or if the leaf type is
+          unknown.
+    """
+    data_length = struct.calcsize(cls.FORMAT_STRING)
+    try:
+      (version, timestamp, leaf_type) = struct.unpack(
+          cls.FORMAT_STRING, stream.read(data_length))
+    except struct.error:
+      raise AftlError("Not enough data to parse leaf header")
+    try:
+      leaf_type = cls.LeafType(leaf_type)
+    except ValueError:
+      raise AftlError("Unknown leaf type: {}".format(leaf_type))
+    return version, timestamp, leaf_type
+
+  @classmethod
+  @abc.abstractmethod
+  def parse(cls, data):
+    """Parses a leaf and returned a new object.
+
+    This abstract method must be implemented by the subclass. It may use
+    _parse_header to parse the common fields.
+
+    Arguments:
+      data: a bytes-like object.
+
+    Returns:
+      An object of the type of the particular subclass.
+
+    Raises:
+      AftlError: If the leaf type is incorrect; or if the decoding failed.
+    """
+
+  @abc.abstractmethod
+  def encode(self):
+    """Encodes a leaf.
+
+    This abstract method must be implemented by the subclass. It may use
+    _encode_header to encode the common fields.
+
+    Returns:
+      A bytes with the encoded leaf.
+
+    Raises:
+      AftlError: If the encoding failed.
+    """
+
+  def _get_expected_header_size(self):
+    """Returns the size of the leaf header."""
+    return struct.calcsize(self.FORMAT_STRING)
+
+  def _encode_header(self, stream):
+    """Encodes the header of the leaf.
+
+    This method is called by the encode method in the subclass.
+
+    Arguments:
+      stream: a BytesIO to which the object is written.
+
+    Raises:
+      AftlError: If the encoding failed.
+    """
+    try:
+      stream.write(struct.pack(self.FORMAT_STRING, self.version, self.timestamp,
+                               self.leaf_type.value))
+    except struct.error:
+      raise AftlError('Unable to encode the leaf header')
+
+  def print_desc(self, o):
+    """Prints the leaf header.
+
+    Arguments:
+      o: The object to write the output to.
+    """
+    i = ' ' * 6
+    fmt = '{}{:23}{}\n'
+    o.write(fmt.format(i, 'Version:', self.version))
+    o.write(fmt.format(i, 'Timestamp:', self.timestamp))
+    o.write(fmt.format(i, 'Type:', self.leaf_type))
+
+
+class SignedVBMetaPrimaryAnnotationLeaf(Leaf):
+  """A Signed VBMetaPrimaryAnnotation leaf."""
+
+  def __init__(self, version=1, timestamp=0,
+               signed_vbmeta_primary_annotation=None):
+    """Builds a new Signed VBMeta Primary Annotation leaf."""
+    super(SignedVBMetaPrimaryAnnotationLeaf, self).__init__(
+        version=version, timestamp=timestamp,
+        leaf_type=self.LeafType.SignedVBMetaPrimaryAnnotationType)
+    if not signed_vbmeta_primary_annotation:
+      signed_vbmeta_primary_annotation = SignedVBMetaPrimaryAnnotation()
+    self.signed_vbmeta_primary_annotation = signed_vbmeta_primary_annotation
+
+  @property
+  def annotation(self):
+    """Returns the VBMetaPrimaryAnnotation contained in the leaf."""
+    return self.signed_vbmeta_primary_annotation.annotation
+
+  @property
+  def signature(self):
+    """Returns the Signature contained in the leaf."""
+    return self.signed_vbmeta_primary_annotation.signature
+
+  @classmethod
+  def parse(cls, data):
+    """Parses an encoded contained in data.
+
+    Arguments:
+      data: a bytes-like object.
+
+    Returns:
+      A SignedVBMetaPrimaryAnnotationLeaf.
+
+    Raises:
+      AftlError if the leaf type is incorrect; or if the decoding failed.
+    """
+    encoded_leaf = io.BytesIO(data)
+    version, timestamp, leaf_type = Leaf._parse_header(encoded_leaf)
+    if leaf_type != Leaf.LeafType.SignedVBMetaPrimaryAnnotationType:
+      raise AftlError("Incorrect leaf type")
+    signed_annotation = SignedVBMetaPrimaryAnnotation.parse(encoded_leaf)
+    return cls(version=version, timestamp=timestamp,
+               signed_vbmeta_primary_annotation=signed_annotation)
+
+  def get_expected_size(self):
+    """Returns the size of the leaf."""
+    size = self._get_expected_header_size()
+    if self.signed_vbmeta_primary_annotation:
+      size += self.signed_vbmeta_primary_annotation.get_expected_size()
+    return size
+
+  def encode(self):
+    """Encodes the leaf.
+
+    Returns:
+      bytes which contains the encoded leaf.
+
+    Raises:
+      AftlError: If the encoding failed.
+    """
+    stream = io.BytesIO()
+    self._encode_header(stream)
+    self.signed_vbmeta_primary_annotation.encode(stream)
+    return stream.getvalue()
+
+  def print_desc(self, o):
+    """Prints the leaf.
+
+    Arguments:
+      o: The object to write the output to.
+    """
+    i = ' ' * 4
+    fmt = '{}{:25}{}\n'
+    o.write(fmt.format(i, 'Leaf:', ''))
+    super(SignedVBMetaPrimaryAnnotationLeaf, self).print_desc(o)
+    self.signed_vbmeta_primary_annotation.print_desc(o)
+
+
 class AftlImage(object):
   """A class for the AFTL image, which contains the transparency log ICPs.
 
@@ -1087,40 +1457,40 @@
     else:
       self.timeout = None
 
-  def add_firmware_info(self, request):
-    """Calls the AddFirmwareInfo RPC on the AFTL server.
+  def add_vbmeta(self, request):
+    """Calls the AddVBMeta RPC on the AFTL server.
 
     Arguments:
-      request: A AddFirmwareInfoRequest message.
+      request: An AddVBMetaRequest message.
 
     Returns:
-      An AddFirmwareInfoReponse message.
+      An AddVBMetaResponse message.
 
     Raises:
       AftlError: If grpc or the proto modules cannot be loaded, if there is an
         error communicating with the log.
     """
     raise NotImplementedError(
-        'AddFirmwareInfo() needs to be implemented by subclass.')
+        'add_vbmeta() needs to be implemented by subclass.')
 
 
 class AftlGrpcCommunication(AftlCommunication):
   """Class that implements GRPC communication to the AFTL server."""
 
-  def add_firmware_info(self, request):
-    """Calls the AddFirmwareInfo RPC on the AFTL server.
+  def add_vbmeta(self, request):
+    """Calls the AddVBMeta RPC on the AFTL server.
 
     Arguments:
-      request: A AddFirmwareInfoRequest message.
+      request: An AddVBMetaRequest message.
 
     Returns:
-      An AddFirmwareInfoReponse message.
+      An AddVBMetaResponse message.
 
     Raises:
       AftlError: If grpc or the proto modules cannot be loaded, if there is an
         error communicating with the log.
     """
-    # Import grpc now to avoid global dependencies as it otherwise breakes
+    # Import grpc now to avoid global dependencies as it otherwise breaks
     # running unittest with atest.
     try:
       import grpc  # pylint: disable=import-outside-toplevel
@@ -1145,8 +1515,8 @@
                      'with domain {}.\n'.format(
                          self.transparency_log_config.target))
     try:
-      response = stub.AddFirmwareInfo(request, timeout=self.timeout,
-                                      metadata=metadata)
+      response = stub.AddVBMeta(request, timeout=self.timeout,
+                                metadata=metadata)
     except grpc.RpcError as e:
       raise AftlError('Error: grpc failure ({})'.format(e))
     return response
@@ -1169,7 +1539,7 @@
     """
     # Reads and parses the vbmeta image.
     try:
-      image = avbtool.ImageHandler(image_filename)
+      image = avbtool.ImageHandler(image_filename, read_only=True)
     except (IOError, ValueError) as e:
       sys.stderr.write('The image does not contain a valid VBMeta structure: '
                        '{}.\n'.format(e))
@@ -1212,7 +1582,7 @@
       return None
 
     try:
-      image = avbtool.ImageHandler(image_filename)
+      image = avbtool.ImageHandler(image_filename, read_only=True)
     except ValueError as e:
       sys.stderr.write('The image does not contain a valid VBMeta structure: '
                        '{}.\n'.format(e))
@@ -1340,34 +1710,26 @@
     # Calculate the hash of the manufacturer key data.
     m_key_hash = hashlib.sha256(manufacturer_key_data).digest()
 
-    # Create an AddFirmwareInfoRequest protobuf for transmission to AFTL.
-    fw_info = aftl_pb2.FirmwareInfo(vbmeta_hash=vbmeta_hash,
-                                    version_incremental=version_inc,
-                                    manufacturer_key_hash=m_key_hash)
-    signed_fw_info = b''
-    # AFTL supports SHA256_RSA4096 for now, more will be available.
-    algorithm_name = 'SHA256_RSA4096'
-    try:
-      rsa_key = avbtool.RSAPublicKey(manufacturer_key_path)
-      signed_fw_info = rsa_key.sign(algorithm_name, fw_info.SerializeToString(),
-                                    signing_helper, signing_helper_with_files)
-    except avbtool.AvbError as e:
-      raise AftlError('Failed to sign FirmwareInfo with '
-                      '--manufacturer_key: {}'.format(e))
-    fw_info_sig = sigpb_pb2.DigitallySigned(
-        hash_algorithm='SHA256',
-        signature_algorithm='RSA',
-        signature=signed_fw_info)
+    # Build VBMetaPrimaryAnnotation with that data.
+    annotation = VBMetaPrimaryAnnotation(
+        vbmeta_hash=vbmeta_hash, version_incremental=version_inc,
+        manufacturer_key_hash=m_key_hash)
 
-    sfw_info = aftl_pb2.SignedFirmwareInfo(info=fw_info,
-                                           info_signature=fw_info_sig)
-    request = api_pb2.AddFirmwareInfoRequest(
-        vbmeta=vbmeta_image, fw_info=sfw_info)
+    # Sign annotation and add it to the request.
+    signed_annotation = annotation.sign(
+        manufacturer_key_path, signing_helper=signing_helper,
+        signing_helper_with_files=signing_helper_with_files)
 
-    # Submit signed FirmwareInfo to the server.
+    encoded_signed_annotation = io.BytesIO()
+    signed_annotation.encode(encoded_signed_annotation)
+    request = api_pb2.AddVBMetaRequest(
+        vbmeta=vbmeta_image,
+        signed_vbmeta_primary_annotation=encoded_signed_annotation.getvalue())
+
+    # Submit signed VBMeta annotation to the server.
     if not aftl_comms:
       aftl_comms = AftlGrpcCommunication(transparency_log_config, timeout)
-    response = aftl_comms.add_firmware_info(request)
+    response = aftl_comms.add_vbmeta(request)
 
     # Return an AftlIcpEntry representing this response.
     icp_entry = AftlIcpEntry()
@@ -1870,14 +2232,14 @@
     sub_parser.set_defaults(func=self.load_test_aftl)
 
     args = parser.parse_args(argv[1:])
-    try:
-      success = args.func(args)
-    except AttributeError:
+    if not 'func' in args:
       # This error gets raised when the command line tool is called without any
       # arguments. It mimics the original Python 2 behavior.
       parser.print_usage()
       print('aftltool: error: too few arguments')
       sys.exit(2)
+    try:
+      success = args.func(args)
     except AftlError as e:
       # Signals to calling tools that an unhandled exception occured.
       sys.stderr.write('Unhandled AftlError occured: {}\n'.format(e))
diff --git a/aftltool_test.py b/aftltool_test.py
index 006deb9..7c8c205 100755
--- a/aftltool_test.py
+++ b/aftltool_test.py
@@ -25,7 +25,6 @@
 """Unit tests for aftltool."""
 
 import argparse
-import base64
 import binascii
 import io
 import os
@@ -47,6 +46,107 @@
 # TODO(b/149307145): Remove workaround once the referenced bug is fixed.
 TEST_EXEC_PATH = os.path.dirname(os.path.realpath(__file__))
 
+class TlsDataTest(unittest.TestCase):
+
+  def test_decode(self):
+    data = io.BytesIO(b'\x01\x02')
+    value = aftltool.tls_decode_bytes('B', data)
+    self.assertEqual(value, b'\x02')
+    self.assertEqual(data.read(), b'')
+
+    data = io.BytesIO(b'\x00\x01\x03\xff')
+    value = aftltool.tls_decode_bytes('H', data)
+    self.assertEqual(value, b'\x03')
+    self.assertEqual(data.read(), b'\xff')
+
+    data = io.BytesIO(b'\x00\x00\x00\x02\x04\x05\xff\xff')
+    value = aftltool.tls_decode_bytes('L', data)
+    self.assertEqual(value, b'\x04\x05')
+    self.assertEqual(data.read(), b'\xff\xff')
+
+  def test_decode_invalid(self):
+    # Insufficient data for reading the size.
+    with self.assertRaises(aftltool.AftlError):
+      aftltool.tls_decode_bytes('B', io.BytesIO(b''))
+
+    # Invalid byte_size character.
+    with self.assertRaises(aftltool.AftlError):
+      aftltool.tls_decode_bytes('/o/', io.BytesIO(b'\x01\x02\xff'))
+
+    # Insufficient data for reading the value.
+    with self.assertRaises(aftltool.AftlError):
+      aftltool.tls_decode_bytes('B', io.BytesIO(b'\x01'))
+
+  def test_encode(self):
+    stream = io.BytesIO()
+    aftltool.tls_encode_bytes('B', b'\x01\x02\x03\x04', stream)
+    self.assertEqual(stream.getvalue(), b'\x04\x01\x02\x03\x04')
+
+    stream = io.BytesIO()
+    aftltool.tls_encode_bytes('H', b'\x01\x02\x03\x04', stream)
+    self.assertEqual(stream.getvalue(), b'\x00\x04\x01\x02\x03\x04')
+
+  def test_encode_invalid(self):
+    # Byte size is not large enough to encode the value.
+    stream = io.BytesIO()
+    with self.assertRaises(aftltool.AftlError):
+      aftltool.tls_encode_bytes('B', b'\x01'*256, stream)
+
+    # Invalid byte_size character.
+    stream = io.BytesIO()
+    with self.assertRaises(aftltool.AftlError):
+      aftltool.tls_encode_bytes('/o/', b'\x01\x02', stream)
+
+
+class VBMetaPrimaryAnnotationTest(unittest.TestCase):
+
+  def test_decode(self):
+    stream = io.BytesIO(b'\x00\x00\x00\x00\x00')
+    anno = aftltool.VBMetaPrimaryAnnotation.parse(stream)
+    self.assertEqual(anno.vbmeta_hash, b'')
+    self.assertEqual(anno.version_incremental, '')
+    self.assertEqual(anno.manufacturer_key_hash, b'')
+    self.assertEqual(anno.description, '')
+
+  def test_encode(self):
+    stream = io.BytesIO()
+    anno = aftltool.VBMetaPrimaryAnnotation()
+    anno.encode(stream)
+    self.assertEqual(stream.getvalue(), b'\x00\x00\x00\x00\x00')
+
+  def test_encode_invalid(self):
+    stream = io.BytesIO()
+    anno = aftltool.VBMetaPrimaryAnnotation()
+    # Version incremental should be ASCII only.
+    anno.version_incremental = '☃'
+    with self.assertRaises(aftltool.AftlError):
+      anno.encode(stream)
+
+
+class SignedVBMetaAnnotationLeafTest(unittest.TestCase):
+
+  def test_encode(self):
+    leaf = aftltool.SignedVBMetaPrimaryAnnotationLeaf()
+    self.assertEqual(leaf.encode(),
+                     b'\x01'   # Version
+                     b'\x00\x00\x00\x00\x00\x00\x00\x00'  # Timestamp
+                     b'\x01' + # Leaf Type
+                     b'\x00' * 4 + # Empty Signature
+                     b'\x00' * 5) # Empty Annotation
+
+  def test_encode_invalid_type(self):
+    # The version field must be a 1-byte integer.
+    leaf = aftltool.SignedVBMetaPrimaryAnnotationLeaf()
+    leaf.version = 'x'
+    with self.assertRaises(aftltool.AftlError):
+      leaf.encode()
+
+  def test_encode_invalid_size(self):
+    leaf = aftltool.SignedVBMetaPrimaryAnnotationLeaf()
+    leaf.version = 256
+    with self.assertRaises(aftltool.AftlError):
+      leaf.encode()
+
 
 class AftltoolTestCase(unittest.TestCase):
 
@@ -99,6 +199,28 @@
         b''                                  # metadata (empty)
     )
 
+    # Test Annotation #1
+    anno_1 = aftltool.VBMetaPrimaryAnnotation(vbmeta_hash=b'w'*32,
+                                              version_incremental='x'*5,
+                                              manufacturer_key_hash=b'y'*32,
+                                              description='z'*51)
+    signed_anno_1 = aftltool.SignedVBMetaPrimaryAnnotation(annotation=anno_1)
+
+    self.test_anno_1 = aftltool.SignedVBMetaPrimaryAnnotationLeaf(
+        signed_vbmeta_primary_annotation=signed_anno_1)
+    self.test_anno_1_bytes = (
+        b'\x01'                              # version
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'  # timestamp
+        b'\x01'                              # leaf_type
+        b'\x00'                              # hash_algorithm
+        b'\x00'                              # signature_algorithm
+        + b'\x00\x00'                        # signature
+        + b'\x20' + b'w' * 32                # vbmeta_hash
+        + b'\x05' + b'x' * 5                 # version_incremental
+        + b'\x20' + b'y' * 32                # manufacturer_key_hash
+        + b'\x00\x33' + b'z' * 51            # description
+    )
+
     # Fill each structure with an easily observable pattern for easy validation.
     self.test_proof_hashes_1 = []
     self.test_proof_hashes_1.append(b'b' * 32)
@@ -110,6 +232,7 @@
     self.test_entry_1 = aftltool.AftlIcpEntry()
     self.test_entry_1.log_url = self.test_tl_url_1
     self.test_entry_1.leaf_index = 1
+    self.test_entry_1.annotation_leaf = self.test_anno_1
     self.test_entry_1.log_root_descriptor = self.test_sth_1
     self.test_entry_1.proofs = self.test_proof_hashes_1
     self.test_entry_1.log_root_signature = b'g' * 512
@@ -118,12 +241,13 @@
         b'\x00\x00\x00\x1b'                  # Transparency log url size.
         b'\x00\x00\x00\x00\x00\x00\x00\x01'  # Leaf index.
         b'\x00\x00\x00\x3d'                  # Log root descriptor size.
-        b'\x00\x00\x00\x00'                  # Firmware info leaf size.
+        b'\x00\x00\x00\x8b'                  # Annotation leaf size.
         b'\x02\x00'                          # Log root signature size.
         b'\x04'                              # Number of hashes in ICP.
         b'\x00\x00\x00\x80'                  # Size of ICP in bytes.
         + self.test_tl_url_1.encode('ascii') # Transparency log url.
         + self.test_sth_1_bytes
+        + self.test_anno_1_bytes
         + b'g' * 512                         # Log root signature.
         + b'b' * 32                          # Hashes...
         + b'c' * 32
@@ -161,6 +285,7 @@
     self.test_entry_2 = aftltool.AftlIcpEntry()
     self.test_entry_2.log_url = self.test_tl_url_2
     self.test_entry_2.leaf_index = 2
+    self.test_entry_2.annotation_leaf = self.test_anno_1
     self.test_entry_2.log_root_descriptor = self.test_sth_2
     self.test_entry_2.log_root_signature = b'd' * 512
     self.test_entry_2.proofs = self.test_proof_hashes_2
@@ -169,12 +294,13 @@
         b'\x00\x00\x00\x1a'                   # Transparency log url size.
         b'\x00\x00\x00\x00\x00\x00\x00\x02'   # Leaf index.
         b'\x00\x00\x00\x3f'                   # Log root descriptor size.
-        b'\x00\x00\x00\x00'                   # Firmware info leaf size.
+        b'\x00\x00\x00\x8b'                   # Annotation leaf size.
         b'\x02\x00'                           # Log root signature size.
         b'\x02'                               # Number of hashes in ICP.
         b'\x00\x00\x00\x40'                   # Size of ICP in bytes.
         + self.test_tl_url_2.encode('ascii')  # Transparency log url.
         + self.test_sth_2_bytes               # Log root
+        + self.test_anno_1_bytes
         + b'd' * 512                          # Log root signature.
         + b'g' * 32                           # Hashes...
         + b'h' * 32)
@@ -188,61 +314,88 @@
         b'AFTL'                                         # Magic.
         + struct.pack('!L', avbtool.AVB_VERSION_MAJOR)  # Major version.
         + struct.pack('!L', avbtool.AVB_VERSION_MINOR)  # Minor version.
-        + b'\x00\x00\x05\xb9'                           # Image size.
+        + b'\x00\x00\x06\xcf'                           # Image size.
         b'\x00\x02'                                     # Number of ICP entries.
         + self.test_entry_1_bytes
         + self.test_entry_2_bytes)
 
-    # pylint: disable=no-member
-    self.test_afi_resp = api_pb2.AddFirmwareInfoResponse()
-    self.test_afi_resp.fw_info_proof.proof.leaf_index = 6263
+    self.test_avbm_resp = api_pb2.AddVBMetaResponse()
+    self.test_avbm_resp.annotation_proof.proof.leaf_index = 9127
     hashes = [
-        '3ad99869646980c0a51d637a9791f892d12e0bc83f6bac5d305a9e289e7f7e8b',
-        '2e5c664d2aee64f71cb4d292e787d0eae7ca9ed80d1e08abb41d26baca386c05',
-        'a671dd99f8d97e9155cc2f0a9dc776a112a5ec5b821ec71571bb258ac790717a',
-        '78046b839595e4e49ad4b0c73f92bf4803aacd4a3351181086509d057ef0d7a9',
-        'c0a7e013f03e7c69e9402070e113dadb345868cf144ccb174fabc384b5605abf',
-        'dc36e5dbe36abe9f4ad10f14170aa0148b6fe3fcaba9df43deaf4dede01b02e8',
-        'b063e7fb665370a361718208756c363dc5206e2e9af9b4d847d81289cdae30de',
-        'a69ea5ba88a221103636d3f4245c800570eb86ad9276121481521f97d0a04a81']
+        '61076ca285b4982669e67757f55682ddc43ab5c11ba671260f82a8efa8831f94',
+        '89c2fbcc58da25a65ce5e9b4fb22aaf208b20601f0bc023f73f05d35bc1f3bac',
+        '75d26b5f754b4bed332a3ce2a2bfea0334706a974b7e00ee663f0279fa8b446e',
+        'e1cd9c96feb893b5ef7771e424ac1c6c47509c2b98bc578d22ad07369c9641aa',
+        'e83e0e4dd352b1670a55f93f88781a73bb41efcadb9927399f59459dfa14bc40',
+        '8d5d25996117c88655d66f685baa3c94390867a040507b10587b17fbe92b496a',
+        '5de4c627e9ca712f207d6056f56f0d3286ed4a5381ed7f3cc1aa470217734138',
+        '19acfdb424d7fe28d1f850c76302f78f9a50146a5b9c65f9fdfbbc0173fd6993']
     for h in hashes:
-      self.test_afi_resp.fw_info_proof.proof.hashes.append(
+      self.test_avbm_resp.annotation_proof.proof.hashes.append(
           binascii.unhexlify(h))
-    self.test_afi_resp.fw_info_proof.sth.key_hint = binascii.unhexlify(
+    self.test_avbm_resp.annotation_proof.sth.key_hint = binascii.unhexlify(
         '5af859abce8fe1ea')
-    self.test_afi_resp.fw_info_proof.sth.log_root = binascii.unhexlify(
-        '000100000000000018782053b182b55dc1377197c938637f50093131daea4'
-        'd0696b1eae5b8a014bfde884a15edb28f1fc7954400000000000013a50000'
+    self.test_avbm_resp.annotation_proof.sth.log_root = binascii.unhexlify(
+        '0001'
+        '00000000000023a8'
+        '20'
+        '9a5f71340f8dc98bdc6320f976dda5f34db8554cb273ba5ab60f1697c519d6f6'
+        '1609ae15024774b1'
+        '0000000000001e5a'
+        '0000'
     )
-    self.test_afi_resp.fw_info_proof.sth.log_root_signature = (
+    self.test_avbm_resp.annotation_proof.sth.log_root_signature = (
         binascii.unhexlify(
-            'c264bc7986a1cf56364ca4dd04989f45515cb8764d05b4fb2b880172585ea404'
-            '2105f95a0e0471fb6e0f8c762b14b2e526fb78eaddcc61484917795a12f6ab3b'
-            '557b5571d492d07d7950595f9ad8647a606c7c633f4697c5eb59c272aeca0419'
-            '397c70a3b9b51537537c4ea6b49d356110e70a9286902f814cc6afbeafe612e4'
-            '9e180146140e902bdd9e9dae66b37b4943150a9571949027a648db88a4eea3ad'
-            'f930b4fa6a183e97b762ab0e55a3a26aa6b0fd44d30531e2541ecb94bf645e62'
-            '59e8e3151e7c3b51a09fe24557ce2fd2c0ecdada7ce99c390d2ef10e5d075801'
-            '7c10d49c55cdee930959cc35f0104e04f296591eeb5defbc9ebb237da7b204ca'
-            'a4608cb98d6bc3a01f18585a04441caf8ec7a35aa2d35f7483b92b14fd0f4a41'
-            '3a91133545579309adc593222ca5032a103b00d8fcaea911936dbec11349e4dd'
-            '419b091ea7d1130570d70e2589dd9445fd77fd7492507e1c87736847b9741cc6'
-            '236868af42558ff6e833e12010c8ede786e43ada40ff488f5f1870d1619887d7'
-            '66a24ad0a06a47cc14e2f7db07361be191172adf3155f49713807c7c265f5a84'
-            '040fc84246ccf7913e44721f0043cea05ee774e457e13206775eee992620c3f9'
-            'd2b2584f58aac19e4afe35f0a17df699c45729f94101083f9fc4302659a7e6e0'
-            'e7eb36f8d1ca0be2c9010160d329bd2d17bb707b010fdd63c30b667a0b886cf9'
+            '7c37903cc76e8689a6b31da9ad56c3daeb6194029510297cc7d147278390da33'
+            '09c4d9eb1f6be0cdcd1de5315b0b3b573cc9fcd8620d3fab956abbe3c597a572'
+            '46e5a5d277c4cc4b590872d0292fa64e1d3285626b1dedeb00b6aa0a7a0717c0'
+            '7d4c89b68fda9091be06180be1369675a7c4ce7f42cca133ef0daf8dcc5ba1ee'
+            '930cef6dcb71b0a7690446e19661c8e18c089a5d6f6fc9299a0592efb33a4db5'
+            '4c640027fa4f0ad0009f8bf75ec5fc17e0fa1091fabe74fe52738443745066ab'
+            '48f99b297809b863c01016abda17a2479fce91f9929c60bc2ce15e474204fc5a'
+            '8e79b2190aadb7c149671e8c76a4da506860f8d6020fb2eaabfee025cc267bad'
+            '3c8257186c8aaf1da9eefe50cae4b3e8deb66033ebc4bfcda2b317f9e7d2dd78'
+            'b47f2d86795815d82058ad4cba8fc7983a3bbf843e9b8c7ec7f1ae137be6848d'
+            '03c76eefdac40ce5e66cc23d9f3e79ad87acbe7ec0c0bb419a7d368ae1e73c85'
+            '742871f847bde69c871e8797638e0e270282fb058ef1cbcba52aded9dcc8249b'
+            '38fbed8424c33b8cfcde4f49797c64dda8d089d73b84062602fd41c66091543c'
+            'e13c18cfa7f8300530ad4b7adb8924bbb86d17bcc5f1d3d74c522a7dcc8c3c1f'
+            '28a999f2fe1bfe5520c66f93f7c90996dc7f52e62dd95ace9ceace90324c3040'
+            '669b7f5aeb5c5a53f217f1de46e32f80d0aaaf7d9cc9d0e8f8fd7026c612103a'
         )
     )
-    self.test_afi_resp.fw_info_leaf = (
-        b'{\"timestamp\":{\"seconds\":1580115370,\"nanos\":621454825},\"Va'
-        b'lue\":{\"FwInfo\":{\"info\":{\"info\":{\"vbmeta_hash\":\"ViNzEQS'
-        b'/oc/bJ13yl40fk/cvXw90bxHQbzCRxgHDIGc=\",\"version_incremental\":'
-        b'\"1\",\"manufacturer_key_hash\":\"yBCrUOdjvaAh4git5EgqWa5neegUao'
-        b'XeLlB67+N8ObY=\"}}}}}')
 
-    self.test_fw_info_leaf = aftltool.FirmwareInfoLeaf(
-        self.test_afi_resp.fw_info_leaf)
+    anno = aftltool.VBMetaPrimaryAnnotation(
+        vbmeta_hash=bytes.fromhex(
+            '5623731104bfa1cfdb275df2978d1f93f72f5f0f746f11d06f3091c601c32067'),
+        version_incremental='only_for_testing',
+        manufacturer_key_hash=bytes.fromhex(
+            '83ab3b109b73a1d32dce4153a2de57a1a0485052db8364f3180d98614749d7f7'))
+    raw_signature = bytes.fromhex(
+        '6a523021bc5b933bb58c38c8238be3a5fe1166002f5df8b77dee9dd22d353595'
+        'be7996656d3824ebf4e1411a05ee3652d64669d3d62b167d3290dbdf4f2741ba'
+        '4b6472e1bd71fc1860465fdcdca1ff08c4ab0420d7dcbf4ad144f64e211d8f92'
+        '081ba51192358e2478195e573d000282423b23e6dd945069907dcf11520ff11a'
+        '250e26643b820f8a5d80ccfe7d5d84f58e549cd05630f2254ade8edc88d9aa8a'
+        'ec2089f84643854e1f265a4f746598ce4cae529c4eaa637f6e35fa1d1da9254e'
+        'ec8dfede7a4313f7b151547dcdde98782ce6fb3149326ee5b8e750813d3fd37a'
+        '738fe92f6111bf0dff4091769e216b842980e05716f2e50268a7dcca430e175e'
+        '711f80e41a1a28f20635741ac11a56f97492d30db6d1955a827daf8e83faebe5'
+        'a96e18a13c558ae561a02c90982514c853db0296c2e791e68b77c30e6232a3b7'
+        'ed355441d4706277f33a01735f56cb8279336491731939691683f96f1c3e3183'
+        'a0b77510d6ff0199b7688902044829793106546fd6fd4a5294d63c31c91256ad'
+        'f7be6d053e77875698ad32ffaaeaac5d54b432e537f72549d2543072ae35578f'
+        '138d82afcadd668511ba276ce02b6f9c18ef3b6f2f6ae0d123e9f8cb930f21a9'
+        'c49a6d9e95de741c7860593a956735e1b77e9851ecb1f6572abf6e2c8ba15085'
+        'e37e0f7bab0a30d108b997ed5edd74cf7f89cf082590a6f0af7a3a1f68c0077a')
+    signature = aftltool.Signature(signature=raw_signature)
+    signed_anno = aftltool.SignedVBMetaPrimaryAnnotation(annotation=anno,
+                                                         signature=signature)
+    leaf = aftltool.SignedVBMetaPrimaryAnnotationLeaf(
+        timestamp=1587991742919072870,
+        signed_vbmeta_primary_annotation=signed_anno).encode()
+    self.test_avbm_resp.annotation_leaf = leaf
+
 
   def tearDown(self):
     """Tears down the test bed for the unit tests."""
@@ -394,17 +547,17 @@
     # Valid vbmeta image without footer with 1 ICP.
     tool = aftltool.Aftl()
     image_path = self.get_testdata_path(
-        'aftltool/aftl_output_vbmeta_with_1_icp.img')
+        'aftl_output_vbmeta_with_1_icp.img')
     vbmeta_image, _ = tool.get_vbmeta_image(image_path)
     desc = tool.get_aftl_image(image_path)
 
     # Valid image checked against correct log key.
     self.assertTrue(desc.verify_vbmeta_image(
-        vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_1.pub')]))
+        vbmeta_image, [self.get_testdata_path('aftl_pubkey_1.pem')]))
 
     # Valid image checked with a key from another log.
     self.assertFalse(desc.verify_vbmeta_image(
-        vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_2.pub')]))
+        vbmeta_image, [self.get_testdata_path('testkey_rsa4096_pub.pem')]))
 
     # Valid image checked with non existed key file path.
     self.assertFalse(desc.verify_vbmeta_image(
@@ -425,17 +578,17 @@
     # Valid vbmeta image without footer with 2 ICPs from same log.
     tool = aftltool.Aftl()
     image_path = self.get_testdata_path(
-        'aftltool/aftl_output_vbmeta_with_2_icp_same_log.img')
+        'aftl_output_vbmeta_with_2_icp_same_log.img')
     vbmeta_image, _ = tool.get_vbmeta_image(image_path)
     desc = tool.get_aftl_image(image_path)
 
     # Valid image checked against correct log key.
     self.assertTrue(desc.verify_vbmeta_image(
-        vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_1.pub')]))
+        vbmeta_image, [self.get_testdata_path('aftl_pubkey_1.pem')]))
 
     # Valid vbmeta image checked with key from another log.
     self.assertFalse(desc.verify_vbmeta_image(
-        vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_2.pub')]))
+        vbmeta_image, [self.get_testdata_path('testkey_rsa4096_pub.pem')]))
 
     # Valid image checked with non existed key file path.
     self.assertFalse(desc.verify_vbmeta_image(
@@ -448,56 +601,6 @@
     # Valid image but checked with empty list of keys.
     self.assertFalse(desc.verify_vbmeta_image(vbmeta_image, []))
 
-  def test_verify_vbmeta_image_with_2_icp_from_different_logs(self):
-    """Tests the verify_vbmeta_image method."""
-    # Valid vbmeta image without footer with 2 ICPs from different logs.
-    tool = aftltool.Aftl()
-    image_path = self.get_testdata_path(
-        'aftltool/aftl_output_vbmeta_with_2_icp_different_logs.img')
-    vbmeta_image, _ = tool.get_vbmeta_image(image_path)
-    desc = tool.get_aftl_image(image_path)
-
-    # Valid image checked against log keys from both logs.
-    self.assertTrue(desc.verify_vbmeta_image(
-        vbmeta_image, [
-            self.get_testdata_path('aftltool/aftl_pubkey_1.pub'),
-            self.get_testdata_path('aftltool/aftl_pubkey_2.pub')
-        ]))
-
-    # Valid image checked with one of the keys with an invalid file path.
-    self.assertFalse(desc.verify_vbmeta_image(
-        vbmeta_image, [
-            self.get_testdata_path('aftltool/aftl_pubkey_1.pub'),
-            self.get_testdata_path('non_existent_blabli')
-        ]))
-
-    # Valid image checked with one of the keys being a invalid key.
-    self.assertFalse(desc.verify_vbmeta_image(
-        vbmeta_image, [
-            self.get_testdata_path('aftltool/aftl_pubkey_1.pub'),
-            self.get_testdata_path('large_blob.bin')
-        ]))
-
-    # Valid image checked with one of the keys being None.
-    self.assertFalse(desc.verify_vbmeta_image(
-        vbmeta_image, [
-            self.get_testdata_path('aftltool/aftl_pubkey_1.pub'),
-            None
-        ]))
-
-    # Valid vbmeta image checked against only one of the log keys.
-    self.assertFalse(desc.verify_vbmeta_image(
-        vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_1.pub')]))
-    self.assertFalse(desc.verify_vbmeta_image(
-        vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_2.pub')]))
-
-    # Valid image checked with invalid key.
-    self.assertFalse(desc.verify_vbmeta_image(
-        vbmeta_image, [self.get_testdata_path('large_blob.bin')]))
-
-    # Valid image but checked with empty list of keys.
-    self.assertFalse(desc.verify_vbmeta_image(vbmeta_image, []))
-
   def test_encode(self):
     """Tests encode method."""
     desc_bytes = self.test_aftl_desc.encode()
@@ -649,7 +752,7 @@
     self.assertEqual(entry.log_url_size, 0)
     self.assertEqual(entry.leaf_index, 0)
     self.assertEqual(entry.log_root_descriptor_size, 29)
-    self.assertEqual(entry.fw_info_leaf_size, 0)
+    self.assertEqual(entry.annotation_leaf_size, 19)
     self.assertEqual(entry.log_root_sig_size, 0)
     self.assertEqual(entry.proof_hash_count, 0)
     self.assertEqual(entry.inc_proof_size, 0)
@@ -663,7 +766,7 @@
     entry = aftltool.AftlIcpEntry(self.test_entry_1_bytes)
     self.assertEqual(entry.log_url_size, len(self.test_tl_url_1))
     self.assertEqual(entry.leaf_index, 1)
-    self.assertEqual(entry.fw_info_leaf_size, 0)
+    self.assertEqual(entry.annotation_leaf_size, 139)
     self.assertEqual(entry.log_root_sig_size, 512)
     self.assertEqual(entry.proof_hash_count, len(self.test_proof_hashes_1))
     self.assertEqual(entry.inc_proof_size, 128)
@@ -680,11 +783,11 @@
     """Tests get_expected_size method."""
     # Default record.
     entry = aftltool.AftlIcpEntry()
-    self.assertEqual(entry.get_expected_size(), 56)
+    self.assertEqual(entry.get_expected_size(), 75)
     self.assertEqual(entry.get_expected_size(), len(entry.encode()))
 
     # Test record.
-    self.assertEqual(self.test_entry_1.get_expected_size(), 755)
+    self.assertEqual(self.test_entry_1.get_expected_size(), 894)
     self.assertEqual(self.test_entry_1.get_expected_size(),
                      len(self.test_entry_1.encode()))
 
@@ -717,15 +820,17 @@
   def test_translate_response(self):
     """Tests translate_response method."""
     entry = aftltool.AftlIcpEntry()
-    entry.translate_response('aftl-test.foo.bar:80', self.test_afi_resp)
+    entry.translate_response('aftl-test.foo.bar:80', self.test_avbm_resp)
     self.assertEqual(entry.log_url, 'aftl-test.foo.bar:80')
-    self.assertEqual(entry.leaf_index, 6263)
+    self.assertEqual(entry.leaf_index, 9127)
     self.assertEqual(entry.log_root_descriptor.encode(),
-                     self.test_afi_resp.fw_info_proof.sth.log_root)
-    self.assertEqual(entry.log_root_signature,
-                     self.test_afi_resp.fw_info_proof.sth.log_root_signature)
-    self.assertEqual(entry.proofs,
-                     self.test_afi_resp.fw_info_proof.proof.hashes)
+                     self.test_avbm_resp.annotation_proof.sth.log_root)
+    self.assertEqual(
+        entry.log_root_signature,
+        self.test_avbm_resp.annotation_proof.sth.log_root_signature)
+    self.assertEqual(
+        entry.proofs,
+        self.test_avbm_resp.annotation_proof.proof.hashes)
 
   def test_verify_icp(self):
     """Tests verify_icp method."""
@@ -735,24 +840,25 @@
 
       # Valid ICP.
       entry = aftltool.AftlIcpEntry()
-      entry.translate_response(self.test_tl_url_1, self.test_afi_resp)
+      entry.translate_response(self.test_tl_url_1, self.test_avbm_resp)
       self.assertTrue(entry.verify_icp(key_file.name))
 
-      # Invalid ICP where fw_info_leaf is not matching up with proofs.
+      # Invalid ICP where annotation_leaf is not matching up with proofs.
       # pylint: disable=protected-access
       entry = aftltool.AftlIcpEntry()
-      entry.translate_response(self.test_tl_url_1, self.test_afi_resp)
-      fw_info_leaf_bytes = entry.fw_info_leaf._fw_info_leaf_bytes.replace(
-          b'ViNzEQS', b'1234567')
-      entry.fw_info_leaf._fw_info_leaf_bytes = fw_info_leaf_bytes
-      self.assertFalse(entry.verify_icp(key_file.name))
+      entry.translate_response(self.test_tl_url_1, self.test_avbm_resp)
+      vbmeta_hash = entry.annotation_leaf.annotation.vbmeta_hash
+      vbmeta_hash = vbmeta_hash.replace(b"\x56\x23\x73\x11",
+                                        b"\x00\x00\x00\x00")
+      entry.annotation_leaf.annotation.vbmeta_hash = vbmeta_hash
+      self.assertFalse(entry.verify_icp(key_file))
 
   def test_verify_vbmeta_image(self):
     """Tests the verify_vbmeta_image method."""
     # Valid vbmeta image without footer with 1 ICP.
     tool = aftltool.Aftl()
     image_path = self.get_testdata_path(
-        'aftltool/aftl_output_vbmeta_with_1_icp.img')
+        'aftl_output_vbmeta_with_1_icp.img')
     vbmeta_image, _ = tool.get_vbmeta_image(image_path)
     desc = tool.get_aftl_image(image_path)
 
@@ -762,11 +868,11 @@
 
     # Valid vbmeta image checked with correct log key.
     self.assertTrue(entry.verify_vbmeta_image(
-        vbmeta_image, self.get_testdata_path('aftltool/aftl_pubkey_1.pub')))
+        vbmeta_image, self.get_testdata_path('aftl_pubkey_1.pem')))
 
     # Valid vbmeta image checked with public key of another log.
     self.assertFalse(entry.verify_vbmeta_image(
-        vbmeta_image, self.get_testdata_path('aftltool/aftl_pubkey_2.pub')))
+        vbmeta_image, self.get_testdata_path('testkey_rsa4096_pub.pem')))
 
     # Valid vbmeta image checked with invalid key.
     self.assertFalse(entry.verify_vbmeta_image(
@@ -780,7 +886,7 @@
     # Valid vbmeta image without footer with 1 ICP.
     tool = aftltool.Aftl()
     image_path = self.get_testdata_path(
-        'aftltool/aftl_output_vbmeta_with_1_icp.img')
+        'aftl_output_vbmeta_with_1_icp.img')
     vbmeta_image, _ = tool.get_vbmeta_image(image_path)
     desc = tool.get_aftl_image(image_path)
 
@@ -792,7 +898,7 @@
 
     # Invalid vbmeta image checked with correct log key.
     self.assertFalse(entry.verify_vbmeta_image(
-        vbmeta_image, self.get_testdata_path('aftltool/aftl_pubkey_1.pub')))
+        vbmeta_image, self.get_testdata_path('aftl_pubkey_1.pem')))
 
     # Invalid vbmeta image checked with invalid key.
     self.assertFalse(entry.verify_vbmeta_image(
@@ -803,7 +909,7 @@
 
     # None image checked with a key.
     self.assertFalse(entry.verify_vbmeta_image(
-        None, self.get_testdata_path('aftltool/aftl_pubkey_1.pub')))
+        None, self.get_testdata_path('aftl_pubkey_1.pem')))
 
   def test_print_desc(self):
     """Tests print_desc method."""
@@ -995,94 +1101,54 @@
     self.assertIn('Metadata:', desc)
 
 
-class FirmwareInfoLeafTest(AftltoolTestCase):
-  """Test suite for testing the FirmwareInfoLeaf."""
+class SignedVBMetaPrimaryAnnotationLeafTest(AftltoolTestCase):
+  """Test suite for testing the Leaf."""
 
   def test__init__(self):
     """Tests constructor and properties methods."""
     # Calls constructor without data.
-    leaf = aftltool.FirmwareInfoLeaf()
-    self.assertTrue(leaf.is_valid())
-    self.assertEqual(leaf.vbmeta_hash, None)
-    self.assertEqual(leaf.version_incremental, None)
-    self.assertEqual(leaf.platform_key, None)
-    self.assertEqual(leaf.manufacturer_key_hash, None)
-    self.assertEqual(leaf.description, None)
+    leaf = aftltool.SignedVBMetaPrimaryAnnotationLeaf()
+    self.assertEqual(leaf.version, 1)
+    self.assertEqual(leaf.timestamp, 0)
+    self.assertEqual(leaf.signature.signature, b'')
+    self.assertEqual(leaf.annotation.vbmeta_hash, b'')
+    self.assertEqual(leaf.annotation.description, '')
 
-    # Calls constructor with data.
-    leaf = aftltool.FirmwareInfoLeaf(self.test_afi_resp.fw_info_leaf)
-    self.assertTrue(leaf.is_valid())
-    self.assertEqual(
-        leaf.vbmeta_hash,
-        base64.b64decode('ViNzEQS/oc/bJ13yl40fk/cvXw90bxHQbzCRxgHDIGc='))
-    self.assertEqual(leaf.version_incremental, '1')
-    self.assertEqual(leaf.platform_key, None)
-    self.assertEqual(
-        leaf.manufacturer_key_hash,
-        base64.b64decode('yBCrUOdjvaAh4git5EgqWa5neegUaoXeLlB67+N8ObY='))
-    self.assertEqual(leaf.description, None)
+  def test_parse(self):
+    # Calls parse with valid data.
+    leaf = aftltool.SignedVBMetaPrimaryAnnotationLeaf.parse(
+        self.test_anno_1_bytes)
+    self.assertEqual(leaf.annotation.vbmeta_hash, b'w'*32)
+    self.assertEqual(leaf.annotation.version_incremental, 'x'*5)
+    self.assertEqual(leaf.annotation.manufacturer_key_hash, b'y'*32)
+    self.assertEqual(leaf.annotation.description, 'z'*51)
 
-    # Calls constructor with invalid JSON data.
+    # Calls parse with invalid data.
     with self.assertRaises(aftltool.AftlError):
-      leaf = aftltool.FirmwareInfoLeaf('Invalid JSON.')
+      leaf = aftltool.SignedVBMetaPrimaryAnnotationLeaf.parse(b'Invalid data')
 
   def test_get_expected_size(self):
     """Tests get_expected_size method."""
     # Calls constructor without data.
-    leaf = aftltool.FirmwareInfoLeaf()
-    self.assertEqual(leaf.get_expected_size(), 0)
+    leaf = aftltool.SignedVBMetaPrimaryAnnotationLeaf()
+    self.assertEqual(leaf.get_expected_size(), 19)
 
     # Calls constructor with data.
-    leaf = aftltool.FirmwareInfoLeaf(self.test_afi_resp.fw_info_leaf)
+    leaf = aftltool.SignedVBMetaPrimaryAnnotationLeaf.parse(
+        self.test_anno_1_bytes)
     self.assertEqual(leaf.get_expected_size(),
-                     len(self.test_afi_resp.fw_info_leaf))
+                     len(self.test_anno_1_bytes))
 
   def test_encode(self):
     """Tests encode method."""
-    # Calls constructor without data.
-    leaf = aftltool.FirmwareInfoLeaf()
-    self.assertEqual(leaf.encode(), b'')
-
     # Calls constructor with data.
-    self.assertEqual(self.test_fw_info_leaf.encode(),
-                     self.test_afi_resp.fw_info_leaf)
-
-  def test_is_valid(self):
-    """Tests is_valid method."""
-    # Calls constructor without data.
-    leaf = aftltool.FirmwareInfoLeaf()
-    self.assertTrue(leaf.is_valid())
-
-    # Calls constructor with data.
-    self.assertTrue(self.test_fw_info_leaf.is_valid())
-
-    # Incorrect name for Value key.
-    invalid_value_key_name = (
-        b'{\"timestamp\":{\"seconds\":1580115370,\"nanos\":621454825},\"In'
-        b'val\":{\"FwInfo\":{\"info\":{\"info\":{\"vbmeta_hash\":\"ViNzEQS'
-        b'/oc/bJ13yl40fk/cvXw90bxHQbzCRxgHDIGc=\",\"version_incremental\":'
-        b'\"1\",\"manufacturer_key_hash\":\"yBCrUOdjvaAh4git5EgqWa5neegUao'
-        b'XeLlB67+N8ObY=\"}}}}}')
-
-    with self.assertRaises(aftltool.AftlError):
-      aftltool.FirmwareInfoLeaf(invalid_value_key_name)
-
-    # Within Firmware Info having a field which does not exist in
-    # proto.aftl_pb2.FirmwareInfo.
-    invalid_fields = (
-        b'{\"timestamp\":{\"seconds\":1580115370,\"nanos\":621454825},\"Va'
-        b'lue\":{\"FwInfo\":{\"info\":{\"info\":{\"invalid_field\":\"ViNzEQS'
-        b'/oc/bJ13yl40fk/cvXw90bxHQbzCRxgHDIGc=\",\"version_incremental\":'
-        b'\"1\",\"manufacturer_key_hash\":\"yBCrUOdjvaAh4git5EgqWa5neegUao'
-        b'XeLlB67+N8ObY=\"}}}}}')
-
-    with self.assertRaises(aftltool.AftlError):
-      aftltool.FirmwareInfoLeaf(invalid_fields)
+    self.assertEqual(self.test_anno_1.encode(),
+                     self.test_anno_1_bytes)
 
   def test_print_desc(self):
     """Tests print_desc method."""
     buf = io.StringIO()
-    self.test_fw_info_leaf.print_desc(buf)
+    self.test_anno_1.print_desc(buf)
     desc = buf.getvalue()
 
     # Cursory check whether the printed description contains something useful.
@@ -1098,7 +1164,7 @@
 
     Arguments:
       transparency_log_config: An aftltool.TransparencyLogConfig instance.
-      canned_response: AddFirmwareInfoResponse to return or the Exception to
+      canned_response: AddVBMetaResponse to return or the Exception to
         raise.
     """
     super(AftlMockCommunication, self).__init__(transparency_log_config,
@@ -1106,7 +1172,7 @@
     self.request = None
     self.canned_response = canned_response
 
-  def add_firmware_info(self, request):
+  def add_vbmeta(self, request):
     """Records the request and returns the canned response."""
     self.request = request
 
@@ -1122,7 +1188,7 @@
     """Initializes the object.
 
     Arguments:
-      canned_response: AddFirmwareInfoResponse to return or the Exception to
+      canned_response: AddVBMetaResponse to return or the Exception to
         raise.
     """
     self.mock_canned_response = canned_response
@@ -1224,8 +1290,8 @@
   def set_up_environment(self):
     """Sets up the environment for unit testing without networking."""
     self.aftl_host = 'test.foo.bar:9000'
-    self.aftl_pubkey = self.get_testdata_path('aftltool/aftl_pubkey_1.pub')
-    self.vbmeta_image = self.get_testdata_path('aftltool/aftl_input_vbmeta.img')
+    self.aftl_pubkey = self.get_testdata_path('aftl_pubkey_1.pem')
+    self.vbmeta_image = self.get_testdata_path('aftl_input_vbmeta.img')
     self.manufacturer_key = self.get_testdata_path('testkey_rsa4096.pem')
 
   def get_aftl_implementation(self, canned_response):
@@ -1238,14 +1304,14 @@
 
     # Valid vbmeta image without footer and AftlImage.
     image, footer = tool.get_vbmeta_image(
-        self.get_testdata_path('aftltool/aftl_input_vbmeta.img'))
+        self.get_testdata_path('aftl_input_vbmeta.img'))
     self.assertIsNotNone(image)
     self.assertEqual(len(image), 4352)
     self.assertIsNone(footer)
 
     # Valid vbmeta image without footer but with AftlImage.
     image, footer = tool.get_vbmeta_image(
-        self.get_testdata_path('aftltool/aftl_output_vbmeta_with_1_icp.img'))
+        self.get_testdata_path('aftl_output_vbmeta_with_1_icp.img'))
     self.assertIsNotNone(image)
     self.assertEqual(len(image), 4352)
     self.assertIsNone(footer)
@@ -1268,12 +1334,12 @@
 
     # Valid vbmeta image without footer with AftlImage.
     desc = tool.get_aftl_image(
-        self.get_testdata_path('aftltool/aftl_output_vbmeta_with_1_icp.img'))
+        self.get_testdata_path('aftl_output_vbmeta_with_1_icp.img'))
     self.assertIsInstance(desc, aftltool.AftlImage)
 
     # Valid vbmeta image without footer and AftlImage.
     desc = tool.get_aftl_image(
-        self.get_testdata_path('aftltool/aftl_input_vbmeta.img'))
+        self.get_testdata_path('aftl_input_vbmeta.img'))
     self.assertIsNone(desc)
 
     # Invalid vbmeta image.
@@ -1284,30 +1350,36 @@
   def test_request_inclusion_proof(self):
     """Tests the request_inclusion_proof method."""
     # Always work with a mock independent if run as unit or integration tests.
-    aftl = AftlMock(self.test_afi_resp)
+    aftl = AftlMock(self.test_avbm_resp)
 
     icp = aftl.request_inclusion_proof(
         self.transparency_log_config, b'a' * 1024, '1',
         self.get_testdata_path('testkey_rsa4096.pem'), None, None, None)
     self.assertEqual(icp.leaf_index,
-                     self.test_afi_resp.fw_info_proof.proof.leaf_index)
+                     self.test_avbm_resp.annotation_proof.proof.leaf_index)
     self.assertEqual(icp.proof_hash_count,
-                     len(self.test_afi_resp.fw_info_proof.proof.hashes))
+                     len(self.test_avbm_resp.annotation_proof.proof.hashes))
     self.assertEqual(icp.log_url, self.aftl_host)
     self.assertEqual(
         icp.log_root_descriptor.root_hash, binascii.unhexlify(
-            '53b182b55dc1377197c938637f50093131daea4d0696b1eae5b8a014bfde884a'))
+            '9a5f71340f8dc98bdc6320f976dda5f34db8554cb273ba5ab60f1697c519d6f6'))
 
-    self.assertEqual(icp.fw_info_leaf.version_incremental, '1')
+    self.assertEqual(icp.annotation_leaf.annotation.version_incremental,
+                     'only_for_testing')
     # To calculate the hash of the a RSA key use the following command:
     # openssl rsa -in test/data/testkey_rsa4096.pem -pubout \
     #    -outform DER | sha256sum
-    self.assertEqual(icp.fw_info_leaf.manufacturer_key_hash, base64.b64decode(
-        'yBCrUOdjvaAh4git5EgqWa5neegUaoXeLlB67+N8ObY='))
+    self.assertEqual(
+        icp.annotation_leaf.annotation.manufacturer_key_hash,
+        bytes.fromhex(
+            "83ab3b109b73a1d32dce4153a2de57a1a0485052db8364f3180d98614749d7f7"))
 
-    self.assertEqual(icp.log_root_signature,
-                     self.test_afi_resp.fw_info_proof.sth.log_root_signature)
-    self.assertEqual(icp.proofs, self.test_afi_resp.fw_info_proof.proof.hashes)
+    self.assertEqual(
+        icp.log_root_signature,
+        self.test_avbm_resp.annotation_proof.sth.log_root_signature)
+    self.assertEqual(
+        icp.proofs,
+        self.test_avbm_resp.annotation_proof.proof.hashes)
 
   # pylint: disable=no-member
   def test_request_inclusion_proof_failure(self):
@@ -1323,7 +1395,7 @@
   def test_request_inclusion_proof_manuf_key_not_4096(self):
     """Tests request_inclusion_proof with manufacturing key not of size 4096."""
     # Always work with a mock independent if run as unit or integration tests.
-    aftl = AftlMock(self.test_afi_resp)
+    aftl = AftlMock(self.test_avbm_resp)
     with self.assertRaises(aftltool.AftlError) as e:
       aftl.request_inclusion_proof(
           self.transparency_log_config, b'a' * 1024, 'version_inc',
@@ -1332,7 +1404,7 @@
 
   def test_make_and_verify_icp_with_1_log(self):
     """Tests make_icp_from_vbmeta, verify_image_icp & info_image_icp."""
-    aftl = self.get_aftl_implementation(self.test_afi_resp)
+    aftl = self.get_aftl_implementation(self.test_avbm_resp)
 
     # Make a VBmeta image with ICP.
     with tempfile.NamedTemporaryFile('wb+') as output_file:
@@ -1357,7 +1429,7 @@
 
   def test_make_and_verify_icp_with_2_logs(self):
     """Tests make_icp_from_vbmeta, verify_image_icp & info_image_icp."""
-    aftl = self.get_aftl_implementation(self.test_afi_resp)
+    aftl = self.get_aftl_implementation(self.test_avbm_resp)
 
     # Reconfigures default parameters with two transparency logs.
     self.make_icp_default_params['transparency_log_configs'] = [
@@ -1388,10 +1460,10 @@
   def test_info_image_icp(self):
     """Tests info_image_icp with vbmeta image with 2 ICP."""
     # Always work with a mock independent if run as unit or integration tests.
-    aftl = AftlMock(self.test_afi_resp)
+    aftl = AftlMock(self.test_avbm_resp)
 
     image_path = self.get_testdata_path(
-        'aftltool/aftl_output_vbmeta_with_2_icp_different_logs.img')
+        'aftl_output_vbmeta_with_2_icp_same_log.img')
     self.info_icp_default_params['vbmeta_image_path'] = image_path
 
     # Verifies the generated image.
@@ -1401,7 +1473,7 @@
   def test_info_image_icp_fail(self):
     """Tests info_image_icp with invalid vbmeta image."""
     # Always work with a mock independent if run as unit or integration tests.
-    aftl = AftlMock(self.test_afi_resp)
+    aftl = AftlMock(self.test_avbm_resp)
 
     image_path = self.get_testdata_path('large_blob.bin')
     self.info_icp_default_params['vbmeta_image_path'] = image_path
@@ -1413,34 +1485,18 @@
   def test_verify_image_icp(self):
     """Tets verify_image_icp with 2 ICP with all matching log keys."""
     # Always work with a mock independent if run as unit or integration tests.
-    aftl = AftlMock(self.test_afi_resp)
+    aftl = AftlMock(self.test_avbm_resp)
 
     image_path = self.get_testdata_path(
-        'aftltool/aftl_output_vbmeta_with_2_icp_different_logs.img')
+        'aftl_output_vbmeta_with_2_icp_same_log.img')
     self.verify_icp_default_params['vbmeta_image_path'] = image_path
     self.verify_icp_default_params['transparency_log_pub_keys'] = [
-        self.get_testdata_path('aftltool/aftl_pubkey_1.pub'),
-        self.get_testdata_path('aftltool/aftl_pubkey_2.pub')
+        self.get_testdata_path('aftl_pubkey_1.pem'),
     ]
 
     result = aftl.verify_image_icp(**self.verify_icp_default_params)
     self.assertTrue(result)
 
-  def test_verify_image_icp_failure(self):
-    """Tests verify_image_icp with 2 ICP but only one matching log key."""
-    # Always work with a mock independent if run as unit or integration tests.
-    aftl = AftlMock(self.test_afi_resp)
-
-    image_path = self.get_testdata_path(
-        'aftltool/aftl_output_vbmeta_with_2_icp_different_logs.img')
-    self.verify_icp_default_params['vbmeta_image_path'] = image_path
-    self.verify_icp_default_params['transparency_log_pub_keys'] = [
-        self.get_testdata_path('aftltool/aftl_pubkey_1.pub')
-    ]
-
-    result = aftl.verify_image_icp(**self.verify_icp_default_params)
-    self.assertFalse(result)
-
   def test_make_icp_with_invalid_grpc_service(self):
     """Tests make_icp_from_vbmeta command with a host not supporting GRPC."""
     aftl = self.get_aftl_implementation(aftltool.AftlError('Comms error'))
@@ -1468,7 +1524,7 @@
 
   def test_load_test_single_process_single_submission(self):
     """Tests load_test_aftl command with 1 process which does 1 submission."""
-    aftl = self.get_aftl_implementation(self.test_afi_resp)
+    aftl = self.get_aftl_implementation(self.test_avbm_resp)
 
     with tempfile.TemporaryDirectory() as tmp_dir:
       self.load_test_aftl_default_params[
@@ -1485,7 +1541,7 @@
 
   def test_load_test_multi_process_multi_submission(self):
     """Tests load_test_aftl command with 2 processes and 2 submissions each."""
-    aftl = self.get_aftl_implementation(self.test_afi_resp)
+    aftl = self.get_aftl_implementation(self.test_avbm_resp)
 
     self.load_test_aftl_default_params['process_count'] = 2
     self.load_test_aftl_default_params['submission_count'] = 2
diff --git a/avbtool.py b/avbtool.py
index 4b9c594..7dfbbc0 100755
--- a/avbtool.py
+++ b/avbtool.py
@@ -732,11 +732,12 @@
   NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
   NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
 
-  def __init__(self, image_filename):
+  def __init__(self, image_filename, read_only=False):
     """Initializes an image handler.
 
     Arguments:
       image_filename: The name of the file to operate on.
+      read_only: True if file is only opened for read-only operations.
 
     Raises:
       ValueError: If data in the file is invalid.
@@ -745,6 +746,7 @@
     self._num_total_blocks = 0
     self._num_total_chunks = 0
     self._file_pos = 0
+    self._read_only = read_only
     self._read_header()
 
   def _read_header(self):
@@ -759,7 +761,10 @@
     self.is_sparse = False
     self.block_size = 4096
     self._file_pos = 0
-    self._image = open(self.filename, 'r+b')
+    if self._read_only:
+      self._image = open(self.filename, 'rb')
+    else:
+      self._image = open(self.filename, 'r+b')
     self._image.seek(0, os.SEEK_END)
     self.image_size = self._image.tell()
 
@@ -885,9 +890,15 @@
 
     Arguments:
       num_bytes: Size in number of bytes of the DONT_CARE chunk.
+
+    Raises
+      OSError: If ImageHandler was initialized in read-only mode.
     """
     assert num_bytes % self.block_size == 0
 
+    if self._read_only:
+      raise OSError('ImageHandler is in read-only mode.')
+
     if not self.is_sparse:
       self._image.seek(0, os.SEEK_END)
       # This is more efficient that writing NUL bytes since it'll add
@@ -916,9 +927,15 @@
 
     Arguments:
       data: Data to append as bytes.
+
+    Raises
+      OSError: If ImageHandler was initialized in read-only mode.
     """
     assert len(data) % self.block_size == 0
 
+    if self._read_only:
+      raise OSError('ImageHandler is in read-only mode.')
+
     if not self.is_sparse:
       self._image.seek(0, os.SEEK_END)
       self._image.write(data)
@@ -947,11 +964,17 @@
     Arguments:
       fill_data: Fill data to append - must be four bytes.
       size: Number of chunk - must be a multiple of four and the block size.
+
+    Raises
+      OSError: If ImageHandler was initialized in read-only mode.
     """
     assert len(fill_data) == 4
     assert size % 4 == 0
     assert size % self.block_size == 0
 
+    if self._read_only:
+      raise OSError('ImageHandler is in read-only mode.')
+
     if not self.is_sparse:
       self._image.seek(0, os.SEEK_END)
       self._image.write(fill_data * (size//4))
@@ -1051,7 +1074,11 @@
 
     Raises:
       ValueError: If desired size isn't a multiple of the block size.
+      OSError: If ImageHandler was initialized in read-only mode.
     """
+    if self._read_only:
+      raise OSError('ImageHandler is in read-only mode.')
+
     if not self.is_sparse:
       self._image.truncate(size)
       self._read_header()
@@ -1467,7 +1494,7 @@
       image = image_containing_descriptor
     else:
       image_filename = os.path.join(image_dir, self.partition_name + image_ext)
-      image = ImageHandler(image_filename)
+      image = ImageHandler(image_filename, read_only=True)
     # Generate the hashtree and checks that it matches what's in the file.
     digest_size = len(hashlib.new(self.hash_algorithm).digest())
     digest_padding = round_to_pow2(digest_size) - digest_size
@@ -1635,7 +1662,7 @@
       image = image_containing_descriptor
     else:
       image_filename = os.path.join(image_dir, self.partition_name + image_ext)
-      image = ImageHandler(image_filename)
+      image = ImageHandler(image_filename, read_only=True)
     data = image.read(self.image_size)
     ha = hashlib.new(self.hash_algorithm)
     ha.update(self.salt)
@@ -2169,7 +2196,7 @@
     Raises:
       AvbError: If there's no footer in the image.
     """
-    image = ImageHandler(image_filename)
+    image = ImageHandler(image_filename, read_only=True)
     (footer, _, _, _) = self._parse_image(image)
     if not footer:
       raise AvbError('Given image does not have a footer.')
@@ -2365,7 +2392,7 @@
       image_filename: Image file to get information from (file object).
       output: Output file to write human-readable information to (file object).
     """
-    image = ImageHandler(image_filename)
+    image = ImageHandler(image_filename, read_only=True)
     o = output
     (footer, header, descriptors, image_size) = self._parse_image(image)
 
@@ -2460,7 +2487,7 @@
       print('Verifying image {} using embedded public key'.format(
           image_filename))
 
-    image = ImageHandler(image_filename)
+    image = ImageHandler(image_filename, read_only=True)
     (footer, header, descriptors, _) = self._parse_image(image)
     offset = 0
     if footer:
@@ -2553,7 +2580,7 @@
     Raises:
       AvbError: If getting the partition digests from the image fails.
     """
-    image = ImageHandler(image_filename)
+    image = ImageHandler(image_filename, read_only=True)
     (_, _, descriptors, _) = self._parse_image(image)
 
     for desc in descriptors:
@@ -2590,7 +2617,7 @@
     image_dir = os.path.dirname(image_filename)
     image_ext = os.path.splitext(image_filename)[1]
 
-    image = ImageHandler(image_filename)
+    image = ImageHandler(image_filename, read_only=True)
     (footer, header, descriptors, _) = self._parse_image(image)
     offset = 0
     if footer:
@@ -2607,7 +2634,7 @@
       if isinstance(desc, AvbChainPartitionDescriptor):
         ch_image_filename = os.path.join(image_dir,
                                          desc.partition_name + image_ext)
-        ch_image = ImageHandler(ch_image_filename)
+        ch_image = ImageHandler(ch_image_filename, read_only=True)
         (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
         ch_offset = 0
         ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
@@ -2630,7 +2657,7 @@
       output: Output file to write human-readable information to (file object).
     """
 
-    image = ImageHandler(image_filename)
+    image = ImageHandler(image_filename, read_only=True)
     _, _, descriptors, _ = self._parse_image(image)
 
     image_dir = os.path.dirname(image_filename)
@@ -2641,7 +2668,7 @@
       if isinstance(desc, AvbChainPartitionDescriptor):
         ch_image_filename = os.path.join(image_dir,
                                          desc.partition_name + image_ext)
-        ch_image = ImageHandler(ch_image_filename)
+        ch_image = ImageHandler(ch_image_filename, read_only=True)
         _, _, ch_descriptors, _ = self._parse_image(ch_image)
         for ch_desc in ch_descriptors:
           if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
@@ -2871,7 +2898,8 @@
       # Use the bump logic in AvbVBMetaHeader to calculate the max required
       # version of all included descriptors.
       for image in include_descriptors_from_image:
-        (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
+        (_, image_header, _, _) = self._parse_image(ImageHandler(
+            image.name, read_only=True))
         tmp_header.bump_required_libavb_version_minor(
             image_header.required_libavb_version_minor)
 
@@ -3047,7 +3075,7 @@
     if include_descriptors_from_image:
       descriptors_dict = dict()
       for image in include_descriptors_from_image:
-        image_handler = ImageHandler(image.name)
+        image_handler = ImageHandler(image.name, read_only=True)
         (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
             image_handler)
         # Bump the required libavb version to support all included descriptors.
diff --git a/libavb/avb_slot_verify.c b/libavb/avb_slot_verify.c
index f107cf0..fc4c564 100644
--- a/libavb/avb_slot_verify.c
+++ b/libavb/avb_slot_verify.c
@@ -105,11 +105,11 @@
     }
 
     if (*out_image_buf != NULL) {
+      *out_image_preloaded = true;
       if (part_num_read != image_size) {
         avb_errorv(part_name, ": Read incorrect number of bytes.\n", NULL);
         return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
       }
-      *out_image_preloaded = true;
     }
   }
 
diff --git a/libavb_aftl/avb_aftl_types.h b/libavb_aftl/avb_aftl_types.h
index 09610f3..028ffda 100644
--- a/libavb_aftl/avb_aftl_types.h
+++ b/libavb_aftl/avb_aftl_types.h
@@ -52,9 +52,8 @@
 #define AVB_AFTL_MAX_PROOF_SIZE 64 * AVB_AFTL_HASH_SIZE
 /* Max URL limit. */
 #define AVB_AFTL_MAX_URL_SIZE 2048ul
-/* Minimum valid size for a FirmwareInfo leaf. Derived from a minimal json
-   response that contains only the vbmeta_hash. */
-#define AVB_AFTL_MIN_FW_INFO_SIZE 103ul
+/* Minimum valid size for an Annotation leaf. */
+#define AVB_AFTL_MIN_ANNOTATION_SIZE 18ul
 /* Minimum valid size for a TrillianLogRootDescriptor. See the
    TrillianLogRootDescriptor struct for details. The values here cover:
    version: sizeof(uint16_t)
@@ -74,27 +73,27 @@
    log_url_size: sizeof(uint32_t)
    leaf_index: sizeof(uint64_t)
    log_root_descriptor_size: sizeof(uint32_t)
-   fw_info_leaf_size: sizeof(uint32_t)
+   annotation_leaf_size: sizeof(uint32_t)
    log_root_sig_size: sizeof(uint32_t)
    proof_hash_count: sizeof(uint8_t)
    inc_proof_size: sizeof(uint32_t)
    log_url: 4 (shortest practical URL)
    log_root_descriptor: AVB_AFTL_MIN_TLRD_SIZE
-   fw_info_leaf: AVB_AFTL_MIN_FW_INFO_SIZE
+   annotation_leaf: AVB_AFTL_MIN_ANNOTATION_SIZE
    log_root_signature: AVB_AFTL_SIGNATURE_SIZE
    proofs: AVB_AFTL_HASH_SIZE as there must be at least one hash. */
 #define AVB_AFTL_MIN_AFTL_ICP_ENTRY_SIZE                                       \
   (sizeof(uint32_t) + sizeof(uint64_t) + sizeof(uint32_t) + sizeof(uint32_t) + \
    sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t) + 4 +                 \
-   AVB_AFTL_MIN_TLRD_SIZE + AVB_AFTL_MIN_FW_INFO_SIZE +                        \
+   AVB_AFTL_MIN_TLRD_SIZE + AVB_AFTL_MIN_ANNOTATION_SIZE +                     \
    AVB_AFTL_SIGNATURE_SIZE + AVB_AFTL_HASH_SIZE)
 /* The maximum AftlIcpEntrySize is the max AftlImage size minus the size
    of the AftlImageHeader. */
 #define AVB_AFTL_MAX_AFTL_ICP_ENTRY_SIZE \
   (AVB_AFTL_MAX_AFTL_IMAGE_SIZE - sizeof(AftlImageHeader))
-/* The maximum FirmwareInfo is the max AftlImage size minus the
+/* The maximum Annotation size is the max AftlImage size minus the
    size of the smallest valid AftlIcpEntry. */
-#define AVB_AFTL_MAX_FW_INFO_SIZE \
+#define AVB_AFTL_MAX_ANNOTATION_SIZE \
   (AVB_AFTL_MAX_AFTL_IMAGE_SIZE - AVB_AFTL_MIN_AFTL_ICP_ENTRY_SIZE)
 /* The maximum metadata size in a TrillianLogRootDescriptor for AFTL is the
    max AftlImage size minus the smallest valid AftlIcpEntry size. */
@@ -105,15 +104,6 @@
 #define AVB_AFTL_MAX_TLRD_SIZE \
   (AVB_AFTL_MIN_TLRD_SIZE + AVB_AFTL_MAX_METADATA_SIZE)
 
-/* Data structure containing AFTL header information. */
-typedef struct AftlImageHeader {
-  uint32_t magic;
-  uint32_t required_icp_version_major;
-  uint32_t required_icp_version_minor;
-  uint32_t image_size; /* Total size of the AftlImage, including this header */
-  uint16_t icp_count;
-} AVB_ATTR_PACKED AftlImageHeader;
-
 /* Data structure containing a Trillian LogRootDescriptor, from
    https://github.com/google/trillian/blob/master/trillian.proto#L255
    The log_root_signature is calculated over this structure. */
@@ -128,15 +118,52 @@
   uint8_t* metadata;
 } TrillianLogRootDescriptor;
 
-/* Data structure containing the firmware image info stored in the
-   transparency log. This is defined in
-   https://android.googlesource.com/platform/external/avb/+/master/proto/aftl.proto
- */
-typedef struct FirmwareInfo {
-  uint32_t vbmeta_hash_size;
+typedef enum {
+  AVB_AFTL_HASH_SHA256,
+  _AVB_AFTL_HASH_ALGORITHM_NUM
+} HashAlgorithm;
+
+typedef enum {
+  AVB_AFTL_SIGNATURE_RSA,    // RSA with PKCS1v15
+  AVB_AFTL_SIGNATURE_ECDSA,  // ECDSA with P256 curve
+  _AVB_AFTL_SIGNATURE_ALGORITHM_NUM
+} SignatureAlgorithm;
+
+/* Data structure containing the signature within a leaf of the VBMeta
+ * annotation. This signature is made using the manufacturer key which is
+ * generally not available at boot time. Therefore, this structure is not
+ * verified by the bootloader. */
+typedef struct {
+  uint8_t hash_algorithm;
+  uint8_t signature_algorithm;
+  uint16_t signature_size;
+  uint8_t* signature;
+} Signature;
+
+/* Data structure containing the VBMeta annotation. */
+typedef struct {
+  uint8_t vbmeta_hash_size;
   uint8_t* vbmeta_hash;
-  uint8_t* json_data;
-} FirmwareInfo;
+  uint8_t version_incremental_size;
+  uint8_t* version_incremental;
+  uint8_t manufacturer_key_hash_size;
+  uint8_t* manufacturer_key_hash;
+  uint16_t description_size;
+  uint8_t* description;
+} VBMetaPrimaryAnnotation;
+
+#define AVB_AFTL_VBMETA_LEAF 0
+#define AVB_AFTL_SIGNED_VBMETA_PRIMARY_ANNOTATION_LEAF 1
+
+/* Data structure containing the leaf that is stored in the
+   transparency log. */
+typedef struct {
+  uint8_t version;
+  uint64_t timestamp;
+  uint8_t leaf_type;
+  Signature* signature;
+  VBMetaPrimaryAnnotation* annotation;
+} SignedVBMetaPrimaryAnnotationLeaf;
 
 /* Data structure containing AFTL inclusion proof data from a single
    transparency log. */
@@ -144,22 +171,33 @@
   uint32_t log_url_size;
   uint64_t leaf_index;
   uint32_t log_root_descriptor_size;
-  uint32_t fw_info_leaf_size;
+  uint32_t annotation_leaf_size;
   uint16_t log_root_sig_size;
   uint8_t proof_hash_count;
   uint32_t inc_proof_size;
   uint8_t* log_url;
   TrillianLogRootDescriptor log_root_descriptor;
-  FirmwareInfo fw_info_leaf;
+  uint8_t* log_root_descriptor_raw;
+  SignedVBMetaPrimaryAnnotationLeaf* annotation_leaf;
+  uint8_t* annotation_leaf_raw;
   uint8_t* log_root_signature;
-  uint8_t proofs[/*proof_hash_count*/][AVB_AFTL_HASH_SIZE];
-} AVB_ATTR_PACKED AftlIcpEntry;
+  uint8_t (*proofs)[AVB_AFTL_HASH_SIZE];
+} AftlIcpEntry;
+
+/* Data structure containing AFTL header information. */
+typedef struct AftlImageHeader {
+  uint32_t magic;
+  uint32_t required_icp_version_major;
+  uint32_t required_icp_version_minor;
+  uint32_t image_size; /* Total size of the AftlImage, including this header */
+  uint16_t icp_count;
+} AVB_ATTR_PACKED AftlImageHeader;
 
 /* Main data structure for an AFTL image. */
 typedef struct AftlImage {
   AftlImageHeader header;
   AftlIcpEntry** entries;
-} AVB_ATTR_PACKED AftlImage;
+} AftlImage;
 
 #ifdef __cplusplus
 }
diff --git a/libavb_aftl/avb_aftl_util.c b/libavb_aftl/avb_aftl_util.c
index e3747e1..f780e82 100644
--- a/libavb_aftl/avb_aftl_util.c
+++ b/libavb_aftl/avb_aftl_util.c
@@ -47,105 +47,6 @@
   return true;
 }
 
-/* Calculates a SHA256 hash of the TrillianLogRootDescriptor in icp_entry.
-
-   The hash is calculated over the entire TrillianLogRootDescriptor
-   structure. Some of the fields in this implementation are dynamically
-   allocated, and so the data needs to be reconstructed so that the hash
-   can be properly calculated. The TrillianLogRootDescriptor is defined
-   here: https://github.com/google/trillian/blob/master/trillian.proto#L255 */
-bool avb_aftl_hash_log_root_descriptor(AftlIcpEntry* icp_entry, uint8_t* hash) {
-  uint8_t* buffer;
-  uint8_t* lrd_offset; /* Byte offset into the descriptor. */
-  uint32_t tlrd_size;
-  uint16_t version;
-  uint64_t tree_size;
-  uint64_t timestamp;
-  uint64_t revision;
-  uint16_t metadata_size;
-  bool retval;
-
-  avb_assert(icp_entry != NULL && hash != NULL);
-
-  /* Size of the non-pointer elements of the TrillianLogRootDescriptor. */
-  tlrd_size = sizeof(uint16_t) * 2 + sizeof(uint64_t) * 3 + sizeof(uint8_t);
-  /* Ensure the log_root_descriptor size is correct. */
-  if (icp_entry->log_root_descriptor_size > AVB_AFTL_MAX_TLRD_SIZE) {
-    avb_error("Invalid log root descriptor size.\n");
-    return false;
-  }
-  if (icp_entry->log_root_descriptor_size !=
-      (tlrd_size + icp_entry->log_root_descriptor.root_hash_size +
-       icp_entry->log_root_descriptor.metadata_size)) {
-    avb_error("Log root descriptor size doesn't match fields.\n");
-    return false;
-  }
-  /* Check that the root_hash exists, and if not, it's size is sane. */
-  if (!icp_entry->log_root_descriptor.root_hash &&
-      (icp_entry->log_root_descriptor.root_hash_size != 0)) {
-    avb_error("Invalid tree root hash values.\n");
-    return false;
-  }
-
-  /* Check that the metadata exists, and if not, it's size is sane. */
-  if (!icp_entry->log_root_descriptor.metadata &&
-      (icp_entry->log_root_descriptor.metadata_size != 0)) {
-    avb_error("Invalid log root descriptor metadata values.\n");
-    return false;
-  }
-  buffer = (uint8_t*)avb_malloc(icp_entry->log_root_descriptor_size);
-  if (buffer == NULL) {
-    avb_error("Allocation failure in avb_aftl_hash_log_root_descriptor.\n");
-    return false;
-  }
-  lrd_offset = buffer;
-  /* Copy in the version, tree_size and root hash length. */
-  /* Ensure endianness is correct. */
-  version = avb_be16toh(icp_entry->log_root_descriptor.version);
-  avb_memcpy(lrd_offset, &version, sizeof(uint16_t));
-  lrd_offset += sizeof(uint16_t);
-  /* Ensure endianness is correct. */
-  tree_size = avb_be64toh(icp_entry->log_root_descriptor.tree_size);
-  avb_memcpy(lrd_offset, &tree_size, sizeof(uint64_t));
-  lrd_offset += sizeof(uint64_t);
-  avb_memcpy(lrd_offset,
-             &(icp_entry->log_root_descriptor.root_hash_size),
-             sizeof(uint8_t));
-  lrd_offset += sizeof(uint8_t);
-  /* Copy the root hash. */
-  if (icp_entry->log_root_descriptor.root_hash_size > 0) {
-    avb_memcpy(lrd_offset,
-               icp_entry->log_root_descriptor.root_hash,
-               icp_entry->log_root_descriptor.root_hash_size);
-  }
-  lrd_offset += icp_entry->log_root_descriptor.root_hash_size;
-  /* Copy in the timestamp, revision, and the metadata length. */
-  /* Ensure endianness is correct. */
-  timestamp = avb_be64toh(icp_entry->log_root_descriptor.timestamp);
-  avb_memcpy(lrd_offset, &timestamp, sizeof(uint64_t));
-  lrd_offset += sizeof(uint64_t);
-  /* Ensure endianness is correct. */
-  revision = avb_be64toh(icp_entry->log_root_descriptor.revision);
-  avb_memcpy(lrd_offset, &revision, sizeof(uint64_t));
-  lrd_offset += sizeof(uint64_t);
-  /* Ensure endianness is correct. */
-  metadata_size = avb_be16toh(icp_entry->log_root_descriptor.metadata_size);
-  avb_memcpy(lrd_offset, &metadata_size, sizeof(uint16_t));
-  lrd_offset += sizeof(uint16_t);
-
-  /* Copy the metadata if it exists. */
-  if (icp_entry->log_root_descriptor.metadata_size > 0) {
-    avb_memcpy(lrd_offset,
-               icp_entry->log_root_descriptor.metadata,
-               icp_entry->log_root_descriptor.metadata_size);
-  }
-  /* Hash the result & clean up. */
-
-  retval = avb_aftl_sha256(buffer, icp_entry->log_root_descriptor_size, hash);
-  avb_free(buffer);
-  return retval;
-}
-
 /* Computes a leaf hash as detailed by https://tools.ietf.org/html/rfc6962. */
 bool avb_aftl_rfc6962_hash_leaf(uint8_t* leaf,
                                 uint64_t leaf_size,
@@ -393,6 +294,61 @@
   return retval;
 }
 
+/* Defines helper functions read_u8, read_u16, read_u32 and read_u64. These
+ * functions can be used to read from a |data| stream a |value| of a specific
+ * size. The value endianness is converted from big-endian to host.  We ensure
+ * that the read do not overflow beyond |data_end|. If successful, |data| is
+ * brought forward by the size of the value read.
+ */
+#define _read_u(fct)                                   \
+  {                                                    \
+    size_t value_size = sizeof(*value);                \
+    if ((*data + value_size) < *data) return false;    \
+    if ((*data + value_size) > data_end) return false; \
+    avb_memcpy(value, *data, value_size);              \
+    *value = fct(*value);                              \
+    *data += value_size;                               \
+    return true;                                       \
+  }
+static bool read_u8(uint8_t* value, uint8_t** data, uint8_t* data_end) {
+  _read_u();
+}
+AVB_ATTR_WARN_UNUSED_RESULT
+static bool read_u16(uint16_t* value, uint8_t** data, uint8_t* data_end) {
+  _read_u(avb_be16toh);
+}
+AVB_ATTR_WARN_UNUSED_RESULT
+static bool read_u32(uint32_t* value, uint8_t** data, uint8_t* data_end) {
+  _read_u(avb_be32toh);
+}
+AVB_ATTR_WARN_UNUSED_RESULT
+static bool read_u64(uint64_t* value, uint8_t** data, uint8_t* data_end) {
+  _read_u(avb_be64toh);
+}
+AVB_ATTR_WARN_UNUSED_RESULT
+
+/* Allocates |value_size| bytes into |value| and copy |value_size| bytes from
+ * |data|.  Ensure that we don't overflow beyond |data_end|. It is the caller
+ * responsibility to avb_free |value|. Advances the |data| pointer pass the
+ * value that has been read. Returns false if an overflow would have occurred or
+ * if the allocation failed.
+ */
+static bool read_mem(uint8_t** value,
+                     size_t value_size,
+                     uint8_t** data,
+                     uint8_t* data_end) {
+  if (*data + value_size < *data || *data + value_size > data_end) {
+    return false;
+  }
+  *value = (uint8_t*)avb_calloc(value_size);
+  if (!value) {
+    return false;
+  }
+  avb_memcpy(*value, *data, value_size);
+  *data += value_size;
+  return true;
+}
+
 /* Allocates and populates a TrillianLogRootDescriptor element in an
    AftlIcpEntry from a binary blob.
    The blob is expected to be pointing to the beginning of a
@@ -403,220 +359,252 @@
 static bool parse_trillian_log_root_descriptor(AftlIcpEntry* icp_entry,
                                                uint8_t** aftl_blob,
                                                size_t aftl_blob_remaining) {
-  size_t parsed_size;
-
   avb_assert(icp_entry);
   avb_assert(aftl_blob);
-  avb_assert(aftl_blob_remaining >= AVB_AFTL_MIN_TLRD_SIZE);
-  /* Copy in the version field from the blob. */
-  avb_memcpy(&(icp_entry->log_root_descriptor.version),
-             *aftl_blob,
-             avb_aftl_member_size(TrillianLogRootDescriptor, version));
-  icp_entry->log_root_descriptor.version =
-      avb_be16toh(icp_entry->log_root_descriptor.version);
-  *aftl_blob += avb_aftl_member_size(TrillianLogRootDescriptor, version);
-  parsed_size = avb_aftl_member_size(TrillianLogRootDescriptor, version);
-  /* Copy in the tree size field from the blob. */
-  avb_memcpy(&(icp_entry->log_root_descriptor.tree_size),
-             *aftl_blob,
-             avb_aftl_member_size(TrillianLogRootDescriptor, tree_size));
-  icp_entry->log_root_descriptor.tree_size =
-      avb_be64toh(icp_entry->log_root_descriptor.tree_size);
-  *aftl_blob += avb_aftl_member_size(TrillianLogRootDescriptor, tree_size);
-  parsed_size += avb_aftl_member_size(TrillianLogRootDescriptor, tree_size);
-  /* Copy in the root hash size field from the blob. */
-  avb_memcpy(&(icp_entry->log_root_descriptor.root_hash_size),
-             *aftl_blob,
-             avb_aftl_member_size(TrillianLogRootDescriptor, root_hash_size));
-  if (icp_entry->log_root_descriptor.root_hash_size != AVB_AFTL_HASH_SIZE) {
-    avb_error("Invalid root hash size.\n");
-    free_aftl_icp_entry(icp_entry);
-    return false;
-  }
-  *aftl_blob += avb_aftl_member_size(TrillianLogRootDescriptor, root_hash_size);
-  parsed_size +=
-      avb_aftl_member_size(TrillianLogRootDescriptor, root_hash_size);
-  /* Copy in the root hash from the blob. */
-  icp_entry->log_root_descriptor.root_hash =
-      (uint8_t*)avb_calloc(icp_entry->log_root_descriptor.root_hash_size);
-  if (!icp_entry->log_root_descriptor.root_hash) {
-    avb_error("Failure to allocate root hash.\n");
-    free_aftl_icp_entry(icp_entry);
+  uint8_t* blob_end = *aftl_blob + aftl_blob_remaining;
+  if (*aftl_blob > blob_end) {
     return false;
   }
 
-  avb_memcpy(icp_entry->log_root_descriptor.root_hash,
-             *aftl_blob,
-             icp_entry->log_root_descriptor.root_hash_size);
-  *aftl_blob += icp_entry->log_root_descriptor.root_hash_size;
-  parsed_size += icp_entry->log_root_descriptor.root_hash_size;
+  /* Copy in the version field from the blob. */
+  if (!read_u16(
+          &(icp_entry->log_root_descriptor.version), aftl_blob, blob_end)) {
+    avb_error("Unable to parse version.\n");
+    return false;
+  }
+
+  /* Copy in the tree size field from the blob. */
+  if (!read_u64(
+          &(icp_entry->log_root_descriptor.tree_size), aftl_blob, blob_end)) {
+    avb_error("Unable to parse tree size.\n");
+    return false;
+  }
+
+  /* Copy in the root hash size field from the blob. */
+  if (!read_u8(&(icp_entry->log_root_descriptor.root_hash_size),
+               aftl_blob,
+               blob_end)) {
+    avb_error("Unable to parse root hash size.\n");
+    return false;
+  }
+  if (icp_entry->log_root_descriptor.root_hash_size != AVB_AFTL_HASH_SIZE) {
+    avb_error("Invalid root hash size.\n");
+    return false;
+  }
+
+  /* Copy in the root hash from the blob. */
+  if (!read_mem(&(icp_entry->log_root_descriptor.root_hash),
+                icp_entry->log_root_descriptor.root_hash_size,
+                aftl_blob,
+                blob_end)) {
+    avb_error("Unable to parse root hash.\n");
+    return false;
+  }
+
   /* Copy in the timestamp field from the blob. */
-  avb_memcpy(&(icp_entry->log_root_descriptor.timestamp),
-             *aftl_blob,
-             avb_aftl_member_size(TrillianLogRootDescriptor, timestamp));
-  icp_entry->log_root_descriptor.timestamp =
-      avb_be64toh(icp_entry->log_root_descriptor.timestamp);
-  *aftl_blob += avb_aftl_member_size(TrillianLogRootDescriptor, timestamp);
-  parsed_size += avb_aftl_member_size(TrillianLogRootDescriptor, timestamp);
+  if (!read_u64(
+          &(icp_entry->log_root_descriptor.timestamp), aftl_blob, blob_end)) {
+    avb_error("Unable to parse timestamp.\n");
+    return false;
+  }
+
   /* Copy in the revision field from the blob. */
-  avb_memcpy(&(icp_entry->log_root_descriptor.revision),
-             *aftl_blob,
-             avb_aftl_member_size(TrillianLogRootDescriptor, revision));
-  icp_entry->log_root_descriptor.revision =
-      avb_be64toh(icp_entry->log_root_descriptor.revision);
-  *aftl_blob += avb_aftl_member_size(TrillianLogRootDescriptor, revision);
-  parsed_size += avb_aftl_member_size(TrillianLogRootDescriptor, revision);
+  if (!read_u64(
+          &(icp_entry->log_root_descriptor.revision), aftl_blob, blob_end)) {
+    avb_error("Unable to parse revision.\n");
+    return false;
+  }
+
   /* Copy in the metadata size field from the blob. */
-  avb_memcpy(&(icp_entry->log_root_descriptor.metadata_size),
-             *aftl_blob,
-             avb_aftl_member_size(TrillianLogRootDescriptor, metadata_size));
-  icp_entry->log_root_descriptor.metadata_size =
-      avb_be16toh(icp_entry->log_root_descriptor.metadata_size);
-  *aftl_blob += avb_aftl_member_size(TrillianLogRootDescriptor, metadata_size);
-  parsed_size += avb_aftl_member_size(TrillianLogRootDescriptor, metadata_size);
+  if (!read_u16(&(icp_entry->log_root_descriptor.metadata_size),
+                aftl_blob,
+                blob_end)) {
+    avb_error("Unable to parse metadata size.\n");
+    return false;
+  }
+
   if (icp_entry->log_root_descriptor.metadata_size >
       AVB_AFTL_MAX_METADATA_SIZE) {
     avb_error("Invalid metadata size.\n");
-    free_aftl_icp_entry(icp_entry);
     return false;
   }
-  if (icp_entry->log_root_descriptor.metadata_size + parsed_size >
-      aftl_blob_remaining) {
-    avb_error("Invalid AftlImage.\n");
-    free_aftl_icp_entry(icp_entry);
-    return false;
-  }
+
   /* If it exists, copy in the metadata field from the blob. */
   if (icp_entry->log_root_descriptor.metadata_size > 0) {
-    icp_entry->log_root_descriptor.metadata =
-        (uint8_t*)avb_calloc(icp_entry->log_root_descriptor.metadata_size);
-    if (!icp_entry->log_root_descriptor.metadata) {
-      avb_error("Failure to allocate metadata.\n");
-      free_aftl_icp_entry(icp_entry);
+    if (!read_mem(&(icp_entry->log_root_descriptor.metadata),
+                  icp_entry->log_root_descriptor.metadata_size,
+                  aftl_blob,
+                  blob_end)) {
+      avb_error("Unable to parse metadata.\n");
       return false;
     }
-    avb_memcpy(icp_entry->log_root_descriptor.metadata,
-               *aftl_blob,
-               icp_entry->log_root_descriptor.metadata_size);
-    *aftl_blob += icp_entry->log_root_descriptor.metadata_size;
   } else {
     icp_entry->log_root_descriptor.metadata = NULL;
   }
   return true;
 }
 
-static void base64_decode(uint8_t* input,
-                          size_t input_size,
-                          uint8_t* output,
-                          size_t output_size) {
-  size_t i, j;
-  uint32_t tmp_val;
-  uint8_t decode_table[] = {
-      62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1,
-      -1, -1, -1, -1, -1, -1, 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
-      10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
-      -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
-      36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51};
-  avb_assert(input != NULL);
-  avb_assert(output != NULL);
-
-  for (i = 0, j = 0; i < input_size; i += 4, j += 3) {
-    tmp_val = decode_table[input[i] - '+'];
-    tmp_val = (tmp_val << 6) | decode_table[input[i + 1] - '+'];
-    tmp_val <<= 6;
-    if (input[i + 2] != '=') tmp_val |= decode_table[input[i + 2] - '+'];
-    tmp_val <<= 6;
-    if (input[i + 3] != '=') tmp_val |= decode_table[input[i + 3] - '+'];
-
-    output[j] = (tmp_val >> 16) & 0xff;
-    if (input[i + 2] != '=') output[j + 1] = (tmp_val >> 8) & 0xff;
-    if (input[i + 3] != '=') output[j + 2] = tmp_val & 0xff;
-  }
-}
-
-static bool find_and_decode_vbmeta_hash(uint8_t* vbmeta_hash,
-                                        size_t vbmeta_size,
-                                        uint8_t* json_data,
-                                        size_t json_data_size) {
-  const char vbmeta_id[] = "\"vbmeta_hash\":";
-  size_t vbmeta_id_size = sizeof(vbmeta_id) - 1;
-  size_t vbmeta_base64_size;
-  uint8_t* vbmeta_ptr;
-  uint8_t* vbmeta_base64;
-
-  avb_assert(vbmeta_hash != NULL);
-  avb_assert(vbmeta_size == AVB_AFTL_HASH_SIZE);
-  avb_assert(json_data != NULL);
-  avb_assert(json_data_size > vbmeta_size);
-
-  vbmeta_ptr = (uint8_t*)avb_strstr((const char*)json_data, vbmeta_id);
-  if (vbmeta_ptr == NULL) {
-    vbmeta_hash = NULL;
+/* Parses a Signature from |aftl_blob| into leaf->signature.
+ * Returns false if an error occurred during the parsing */
+static bool parse_signature(SignedVBMetaPrimaryAnnotationLeaf* leaf,
+                            uint8_t** aftl_blob,
+                            uint8_t* blob_end) {
+  Signature* signature = (Signature*)avb_calloc(sizeof(Signature));
+  if (!signature) {
+    avb_error("Failed to allocate signature.\n");
     return false;
   }
-  /* Jump past the vbmeta_hash identifier */
-  vbmeta_ptr += vbmeta_id_size;
-  if (vbmeta_ptr[0] == '"') {
-    vbmeta_base64_size = 1;
-    while (vbmeta_ptr[vbmeta_base64_size] != '"' &&
-           vbmeta_base64_size <= AVB_AFTL_HASH_SIZE * 4 / 3 + 1) {
-      vbmeta_base64_size++;
-    }
-    vbmeta_base64 = (uint8_t*)avb_calloc(vbmeta_base64_size + 1);
-    if (vbmeta_base64 == NULL) {
-      vbmeta_hash = NULL;
-      return false;
-    }
-    avb_memcpy(vbmeta_base64, vbmeta_ptr + 1, vbmeta_base64_size);
-    base64_decode(vbmeta_base64, vbmeta_base64_size, vbmeta_hash, vbmeta_size);
-    avb_free(vbmeta_base64);
-  } else {
-    vbmeta_hash = NULL;
+  leaf->signature = signature;
+
+  if (!read_u8(&(signature->hash_algorithm), aftl_blob, blob_end)) {
+    avb_error("Unable to parse the hash algorithm.\n");
+    return false;
+  }
+  if (signature->hash_algorithm >= _AVB_AFTL_HASH_ALGORITHM_NUM) {
+    avb_error("Unexpect hash algorithm in leaf signature.\n");
     return false;
   }
 
+  if (!read_u8(&(signature->signature_algorithm), aftl_blob, blob_end)) {
+    avb_error("Unable to parse the signature algorithm.\n");
+    return false;
+  }
+  if (signature->signature_algorithm >= _AVB_AFTL_SIGNATURE_ALGORITHM_NUM) {
+    avb_error("Unexpect signature algorithm in leaf signature.\n");
+    return false;
+  }
+
+  if (!read_u16(&(signature->signature_size), aftl_blob, blob_end)) {
+    avb_error("Unable to parse the signature size.\n");
+    return false;
+  }
+  if (!read_mem(&(signature->signature),
+                signature->signature_size,
+                aftl_blob,
+                blob_end)) {
+    avb_error("Unable to parse signature.\n");
+    return false;
+  }
   return true;
 }
 
-/* Allocates and populates a FirmwareInfo element in an
+/* Parses an VBMetaPrimaryAnnotation from |aftl_blob| into leaf->annotation.
+ * Returns false if an error occurred during the parsing */
+static bool parse_annotation(SignedVBMetaPrimaryAnnotationLeaf* leaf,
+                             uint8_t** aftl_blob,
+                             uint8_t* blob_end) {
+  VBMetaPrimaryAnnotation* annotation =
+      (VBMetaPrimaryAnnotation*)avb_calloc(sizeof(VBMetaPrimaryAnnotation));
+  if (!annotation) {
+    avb_error("Failed to allocate annotation.\n");
+    return false;
+  }
+  leaf->annotation = annotation;
+
+  if (!read_u8(&(annotation->vbmeta_hash_size), aftl_blob, blob_end)) {
+    avb_error("Unable to parse VBMeta hash size.\n");
+    return false;
+  }
+  if (annotation->vbmeta_hash_size != AVB_AFTL_HASH_SIZE) {
+    avb_error("Unexpected VBMeta hash size.\n");
+    return false;
+  }
+  if (!read_mem(&(annotation->vbmeta_hash),
+                annotation->vbmeta_hash_size,
+                aftl_blob,
+                blob_end)) {
+    avb_error("Unable to parse VBMeta hash.\n");
+    return false;
+  }
+
+  if (!read_u8(&(annotation->version_incremental_size), aftl_blob, blob_end)) {
+    avb_error("Unable to parse version incremental size.\n");
+    return false;
+  }
+  if (!read_mem(&(annotation->version_incremental),
+                annotation->version_incremental_size,
+                aftl_blob,
+                blob_end)) {
+    avb_error("Unable to parse version incremental.\n");
+    return false;
+  }
+
+  if (!read_u8(
+          &(annotation->manufacturer_key_hash_size), aftl_blob, blob_end)) {
+    avb_error("Unable to parse manufacturer key hash size.\n");
+    return false;
+  }
+  if (!read_mem(&(annotation->manufacturer_key_hash),
+                annotation->manufacturer_key_hash_size,
+                aftl_blob,
+                blob_end)) {
+    avb_error("Unable to parse manufacturer key hash.\n");
+    return false;
+  }
+
+  if (!read_u16(&(annotation->description_size), aftl_blob, blob_end)) {
+    avb_error("Unable to parse description size.\n");
+    return false;
+  }
+  if (!read_mem(&(annotation->description),
+                annotation->description_size,
+                aftl_blob,
+                blob_end)) {
+    avb_error("Unable to parse description.\n");
+    return false;
+  }
+  return true;
+}
+
+/* Allocates and populates a SignedVBMetaPrimaryAnnotationLeaf element in an
    AftlIcpEntry from a binary blob.
    The blob is expected to be pointing to the beginning of a
-   serialized FirmwareInfo element of an AftlIcpEntry.
-   The aftl_blob argument is updated to point to the area after the
-   FirmwareInfo leaf. */
-static bool parse_firmware_info(AftlIcpEntry* icp_entry, uint8_t** aftl_blob) {
-  /* Copy in the fw_info leaf bytes from the blob. */
-  /* Parse out and decode in the vbmeta_hash value from the fw_info
-     leaf bytes. */
-  icp_entry->fw_info_leaf.json_data =
-      (uint8_t*)avb_calloc(icp_entry->fw_info_leaf_size);
-  if (icp_entry->fw_info_leaf.json_data == NULL) {
-    avb_error("Failed to allocate for FirmwareInfo leaf.\n");
-    free_aftl_icp_entry(icp_entry);
-    return false;
-  }
-  avb_memcpy(icp_entry->fw_info_leaf.json_data,
-             *aftl_blob,
-             icp_entry->fw_info_leaf_size);
-  *aftl_blob += icp_entry->fw_info_leaf_size;
-
-  icp_entry->fw_info_leaf.vbmeta_hash_size = AVB_AFTL_HASH_SIZE;
-  icp_entry->fw_info_leaf.vbmeta_hash =
-      (uint8_t*)avb_calloc(icp_entry->fw_info_leaf.vbmeta_hash_size);
-  if (icp_entry->fw_info_leaf.vbmeta_hash == NULL) {
-    avb_error("Failed to allocate vbmeta hash.\n");
-    free_aftl_icp_entry(icp_entry);
-    return false;
-  }
-  if (!find_and_decode_vbmeta_hash(icp_entry->fw_info_leaf.vbmeta_hash,
-                                   icp_entry->fw_info_leaf.vbmeta_hash_size,
-                                   icp_entry->fw_info_leaf.json_data,
-                                   icp_entry->fw_info_leaf_size)) {
-    avb_error("Could not parse vbmeta_hash out of FirmwareInfo leaf.\n");
-    free_aftl_icp_entry(icp_entry);
+   serialized SignedVBMetaPrimaryAnnotationLeaf element of an AftlIcpEntry.
+   The aftl_blob argument is updated to point to the area after the leaf. */
+static bool parse_annotation_leaf(AftlIcpEntry* icp_entry,
+                                  uint8_t** aftl_blob) {
+  SignedVBMetaPrimaryAnnotationLeaf* leaf;
+  uint8_t* blob_end = *aftl_blob + icp_entry->annotation_leaf_size;
+  if (*aftl_blob > blob_end) {
     return false;
   }
 
+  leaf = (SignedVBMetaPrimaryAnnotationLeaf*)avb_calloc(
+      sizeof(SignedVBMetaPrimaryAnnotationLeaf));
+  if (!leaf) {
+    avb_error("Failed to allocate for annotation leaf.\n");
+    return false;
+  }
+  /* The leaf will be free'd within the free_aftl_icp_entry() */
+  icp_entry->annotation_leaf = leaf;
+  if (!read_u8(&(leaf->version), aftl_blob, blob_end)) {
+    avb_error("Unable to parse version.\n");
+    return false;
+  }
+  if (leaf->version != 1) {
+    avb_error("Unexpected leaf version.\n");
+    return false;
+  }
+  if (!read_u64(&(leaf->timestamp), aftl_blob, blob_end)) {
+    avb_error("Unable to parse timestamp.\n");
+    return false;
+  }
+  if (!read_u8(&(leaf->leaf_type), aftl_blob, blob_end)) {
+    avb_error("Unable to parse version.\n");
+    return false;
+  }
+  if (leaf->leaf_type != AVB_AFTL_SIGNED_VBMETA_PRIMARY_ANNOTATION_LEAF) {
+    avb_error("Unexpected leaf type.\n");
+    return false;
+  }
+  if (!parse_signature(leaf, aftl_blob, blob_end)) {
+    avb_error("Unable to parse signature.\n");
+    return false;
+  }
+  if (!parse_annotation(leaf, aftl_blob, blob_end)) {
+    avb_error("Unable to parse annotation.\n");
+    return false;
+  }
   return true;
 }
 
@@ -624,115 +612,100 @@
    The blob is expected to be pointing to the beginning of a
    serialized AftlIcpEntry structure. */
 AftlIcpEntry* parse_icp_entry(uint8_t** aftl_blob, size_t* remaining_size) {
-  AftlIcpEntry *icp_entry, *tmp_icp_entry;
-  uint32_t proof_size;
-  uint64_t parsed_size;
-
-  /* Make a temp AftlIcpEntry to get the inclusion proof size
-     for memory allocation purposes.*/
-  tmp_icp_entry = (AftlIcpEntry*)*aftl_blob;
-  proof_size = avb_be32toh(tmp_icp_entry->inc_proof_size);
-
-  /* Ensure the calculated size is sane. */
-  if (proof_size > AVB_AFTL_MAX_PROOF_SIZE) {
-    avb_error("Invalid inclusion proof size.\n");
+  AftlIcpEntry* icp_entry;
+  uint8_t* blob_start = *aftl_blob;
+  uint8_t* blob_end = *aftl_blob + *remaining_size;
+  if (*aftl_blob > blob_end) {
     return NULL;
   }
 
-  if (*remaining_size < proof_size + AVB_AFTL_MIN_AFTL_ICP_ENTRY_SIZE) {
+  if (*remaining_size < AVB_AFTL_MIN_AFTL_ICP_ENTRY_SIZE) {
     avb_error("Invalid AftlImage\n");
     return NULL;
   }
 
-  icp_entry = (AftlIcpEntry*)avb_calloc(proof_size + sizeof(AftlIcpEntry));
+  icp_entry = (AftlIcpEntry*)avb_calloc(sizeof(AftlIcpEntry));
   if (!icp_entry) {
     avb_error("Failure allocating AftlIcpEntry\n");
     return NULL;
   }
+
   /* Copy in the log server URL size field. */
-  avb_memcpy(&(icp_entry->log_url_size),
-             *aftl_blob,
-             avb_aftl_member_size(AftlIcpEntry, log_url_size));
-  icp_entry->log_url_size = avb_be32toh(icp_entry->log_url_size);
+  if (!read_u32(&(icp_entry->log_url_size), aftl_blob, blob_end)) {
+    avb_error("Unable to parse log url size.\n");
+    avb_free(icp_entry);
+    return NULL;
+  }
   if (icp_entry->log_url_size > AVB_AFTL_MAX_URL_SIZE) {
     avb_error("Invalid log URL size.\n");
     avb_free(icp_entry);
     return NULL;
   }
-  *aftl_blob += avb_aftl_member_size(AftlIcpEntry, log_url_size);
-  parsed_size = avb_aftl_member_size(AftlIcpEntry, log_url_size);
   /* Copy in the leaf index field. */
-  avb_memcpy(&(icp_entry->leaf_index),
-             *aftl_blob,
-             avb_aftl_member_size(AftlIcpEntry, leaf_index));
-  icp_entry->leaf_index = avb_be64toh(icp_entry->leaf_index);
-  *aftl_blob += avb_aftl_member_size(AftlIcpEntry, leaf_index);
-  parsed_size += avb_aftl_member_size(AftlIcpEntry, leaf_index);
+  if (!read_u64(&(icp_entry->leaf_index), aftl_blob, blob_end)) {
+    avb_error("Unable to parse leaf_index.\n");
+    avb_free(icp_entry);
+    return NULL;
+  }
   /* Copy in the TrillianLogRootDescriptor size field. */
-  avb_memcpy(&(icp_entry->log_root_descriptor_size),
-             *aftl_blob,
-             avb_aftl_member_size(AftlIcpEntry, log_root_descriptor_size));
-  icp_entry->log_root_descriptor_size =
-      avb_be32toh(icp_entry->log_root_descriptor_size);
+  if (!read_u32(&(icp_entry->log_root_descriptor_size), aftl_blob, blob_end)) {
+    avb_error("Unable to parse log root descriptor size.\n");
+    avb_free(icp_entry);
+    return NULL;
+  }
   if (icp_entry->log_root_descriptor_size < AVB_AFTL_MIN_TLRD_SIZE ||
       icp_entry->log_root_descriptor_size > AVB_AFTL_MAX_TLRD_SIZE) {
     avb_error("Invalid TrillianLogRootDescriptor size.\n");
     avb_free(icp_entry);
     return NULL;
   }
-  *aftl_blob += avb_aftl_member_size(AftlIcpEntry, log_root_descriptor_size);
-  parsed_size += avb_aftl_member_size(AftlIcpEntry, log_root_descriptor_size);
-  /* Copy in the FirmwareInfo leaf size field. */
-  avb_memcpy(&(icp_entry->fw_info_leaf_size),
-             *aftl_blob,
-             avb_aftl_member_size(AftlIcpEntry, fw_info_leaf_size));
-  icp_entry->fw_info_leaf_size = avb_be32toh(icp_entry->fw_info_leaf_size);
-  if (icp_entry->fw_info_leaf_size == 0 ||
-      icp_entry->fw_info_leaf_size > AVB_AFTL_MAX_FW_INFO_SIZE) {
-    avb_error("Invalid FirmwareInfo leaf size.\n");
+
+  /* Copy in the annotation leaf size field. */
+  if (!read_u32(&(icp_entry->annotation_leaf_size), aftl_blob, blob_end)) {
+    avb_error("Unable to parse annotation leaf size.\n");
     avb_free(icp_entry);
     return NULL;
   }
-  *aftl_blob += avb_aftl_member_size(AftlIcpEntry, fw_info_leaf_size);
-  parsed_size += avb_aftl_member_size(AftlIcpEntry, fw_info_leaf_size);
+  if (icp_entry->annotation_leaf_size == 0 ||
+      icp_entry->annotation_leaf_size > AVB_AFTL_MAX_ANNOTATION_SIZE) {
+    avb_error("Invalid annotation leaf size.\n");
+    avb_free(icp_entry);
+    return NULL;
+  }
+
   /* Copy the log root signature size field. */
-  avb_memcpy(&(icp_entry->log_root_sig_size),
-             *aftl_blob,
-             avb_aftl_member_size(AftlIcpEntry, log_root_sig_size));
-  icp_entry->log_root_sig_size = avb_be16toh(icp_entry->log_root_sig_size);
+  if (!read_u16(&(icp_entry->log_root_sig_size), aftl_blob, blob_end)) {
+    avb_error("Unable to parse log root signature size.\n");
+    avb_free(icp_entry);
+    return NULL;
+  }
   if (icp_entry->log_root_sig_size != AVB_AFTL_SIGNATURE_SIZE) {
     avb_error("Invalid log root signature size.\n");
     avb_free(icp_entry);
     return NULL;
   }
-  *aftl_blob += avb_aftl_member_size(AftlIcpEntry, log_root_sig_size);
-  parsed_size += avb_aftl_member_size(AftlIcpEntry, log_root_sig_size);
   /* Copy the inclusion proof hash count field. */
-  avb_memcpy(&(icp_entry->proof_hash_count),
-             *aftl_blob,
-             avb_aftl_member_size(AftlIcpEntry, proof_hash_count));
-  *aftl_blob += avb_aftl_member_size(AftlIcpEntry, proof_hash_count);
-  parsed_size += avb_aftl_member_size(AftlIcpEntry, proof_hash_count);
+  if (!read_u8(&(icp_entry->proof_hash_count), aftl_blob, blob_end)) {
+    avb_error("Unable to parse proof hash count.\n");
+    avb_free(icp_entry);
+    return NULL;
+  }
   /* Copy the inclusion proof size field. */
-  avb_memcpy(&(icp_entry->inc_proof_size),
-             *aftl_blob,
-             avb_aftl_member_size(AftlIcpEntry, inc_proof_size));
-  icp_entry->inc_proof_size = avb_be32toh(icp_entry->inc_proof_size);
-  if (icp_entry->inc_proof_size !=
-      icp_entry->proof_hash_count * AVB_AFTL_HASH_SIZE) {
+  if (!read_u32(&(icp_entry->inc_proof_size), aftl_blob, blob_end)) {
+    avb_error("Unable to parse inclusion proof size.\n");
+    avb_free(icp_entry);
+    return NULL;
+  }
+  if ((icp_entry->inc_proof_size !=
+       icp_entry->proof_hash_count * AVB_AFTL_HASH_SIZE) ||
+      (icp_entry->inc_proof_size > AVB_AFTL_MAX_PROOF_SIZE)) {
     avb_error("Invalid inclusion proof size.\n");
     avb_free(icp_entry);
     return NULL;
   }
-  *aftl_blob += avb_aftl_member_size(AftlIcpEntry, inc_proof_size);
-  parsed_size += avb_aftl_member_size(AftlIcpEntry, inc_proof_size);
   /* Copy in the log server URL from the blob. */
-  if (!avb_safe_add_to(&parsed_size, icp_entry->log_url_size)) {
-    avb_error("Invalid URL size.\n");
-    free_aftl_icp_entry(icp_entry);
-    return NULL;
-  }
-  if (parsed_size > *remaining_size) {
+  if (*aftl_blob + icp_entry->log_url_size < *aftl_blob ||
+      *aftl_blob + icp_entry->log_url_size > blob_end) {
     avb_error("Invalid AftlImage.\n");
     avb_free(icp_entry);
     return NULL;
@@ -747,49 +720,61 @@
   *aftl_blob += icp_entry->log_url_size;
 
   /* Populate the TrillianLogRootDescriptor elements. */
-  if (!avb_safe_add_to(&parsed_size, icp_entry->log_root_descriptor_size)) {
-    avb_error("Invalid TrillianLogRootDescriptor size.\n");
-    free_aftl_icp_entry(icp_entry);
-    return NULL;
-  }
-  if (parsed_size > *remaining_size) {
+  if (*aftl_blob + icp_entry->log_root_descriptor_size < *aftl_blob ||
+      *aftl_blob + icp_entry->log_root_descriptor_size > blob_end) {
     avb_error("Invalid AftlImage.\n");
     free_aftl_icp_entry(icp_entry);
     return NULL;
   }
+  icp_entry->log_root_descriptor_raw =
+      (uint8_t*)avb_calloc(icp_entry->log_root_descriptor_size);
+  if (!icp_entry->log_root_descriptor_raw) {
+    avb_error("Failure to allocate log root descriptor.\n");
+    free_aftl_icp_entry(icp_entry);
+    return NULL;
+  }
+  avb_memcpy(icp_entry->log_root_descriptor_raw,
+             *aftl_blob,
+             icp_entry->log_root_descriptor_size);
   if (!parse_trillian_log_root_descriptor(
           icp_entry, aftl_blob, icp_entry->log_root_descriptor_size)) {
-    return NULL;
-  }
-
-  /* Populate the FirmwareInfo elements. */
-  if (!avb_safe_add_to(&parsed_size, icp_entry->fw_info_leaf_size)) {
-    avb_error("Invalid FirmwareInfo leaf size.\n");
     free_aftl_icp_entry(icp_entry);
     return NULL;
   }
-  if (parsed_size > *remaining_size) {
+
+  /* Populate the annotation leaf. */
+  if (*aftl_blob + icp_entry->annotation_leaf_size < *aftl_blob ||
+      *aftl_blob + icp_entry->annotation_leaf_size > blob_end) {
     avb_error("Invalid AftlImage.\n");
     free_aftl_icp_entry(icp_entry);
     return NULL;
   }
-  if (!parse_firmware_info(icp_entry, aftl_blob)) return NULL;
+  icp_entry->annotation_leaf_raw =
+      (uint8_t*)avb_calloc(icp_entry->annotation_leaf_size);
+  if (!icp_entry->annotation_leaf_raw) {
+    avb_error("Failure to allocate annotation leaf.\n");
+    free_aftl_icp_entry(icp_entry);
+    return NULL;
+  }
+  avb_memcpy(icp_entry->annotation_leaf_raw,
+             *aftl_blob,
+             icp_entry->annotation_leaf_size);
+  if (!parse_annotation_leaf(icp_entry, aftl_blob)) {
+    free_aftl_icp_entry(icp_entry);
+    return NULL;
+  }
 
   /* Allocate and copy the log root signature from the blob. */
-  if (!avb_safe_add_to(&parsed_size, icp_entry->log_root_sig_size)) {
-    avb_error("Invalid log root signature size.\n");
-    free_aftl_icp_entry(icp_entry);
-    return NULL;
-  }
-  if (parsed_size > *remaining_size) {
+  if (*aftl_blob + icp_entry->log_root_sig_size < *aftl_blob ||
+      *aftl_blob + icp_entry->log_root_sig_size > blob_end) {
     avb_error("Invalid AftlImage.\n");
     free_aftl_icp_entry(icp_entry);
     return NULL;
   }
-
   icp_entry->log_root_signature =
       (uint8_t*)avb_calloc(icp_entry->log_root_sig_size);
   if (!icp_entry->log_root_signature) {
+    avb_error("Failure to allocate log root signature.\n");
     free_aftl_icp_entry(icp_entry);
     return NULL;
   }
@@ -797,22 +782,22 @@
       icp_entry->log_root_signature, *aftl_blob, icp_entry->log_root_sig_size);
   *aftl_blob += icp_entry->log_root_sig_size;
 
-  if (!avb_safe_add_to(&parsed_size, icp_entry->inc_proof_size)) {
-    avb_error("Invalid inclusion proof size.\n");
-    free_aftl_icp_entry(icp_entry);
-    return NULL;
-  }
-  if (parsed_size > *remaining_size) {
+  /* Finally, copy the proof hash data from the blob to the AftlImage. */
+  if (*aftl_blob + icp_entry->inc_proof_size < *aftl_blob ||
+      *aftl_blob + icp_entry->inc_proof_size > blob_end) {
     avb_error("Invalid AftlImage.\n");
     free_aftl_icp_entry(icp_entry);
     return NULL;
   }
-
-  /* Finally, copy the proof hash data from the blob to the AftlImage. */
+  icp_entry->proofs = avb_calloc(icp_entry->inc_proof_size);
+  if (!icp_entry->proofs) {
+    free_aftl_icp_entry(icp_entry);
+    return NULL;
+  }
   avb_memcpy(icp_entry->proofs, *aftl_blob, icp_entry->inc_proof_size);
   *aftl_blob += icp_entry->inc_proof_size;
-  *remaining_size -= parsed_size;
 
+  *remaining_size -= *aftl_blob - blob_start;
   return icp_entry;
 }
 
@@ -820,12 +805,16 @@
 AftlImage* parse_aftl_image(uint8_t* aftl_blob, size_t aftl_blob_size) {
   AftlImage* image;
   AftlImageHeader* image_header;
+  AftlIcpEntry* entry;
   size_t image_size;
   size_t i;
   size_t remaining_size;
 
   /* Ensure the blob is at least large enough for an AftlImageHeader */
-  avb_assert(aftl_blob_size >= sizeof(AftlImageHeader));
+  if (aftl_blob_size < sizeof(AftlImageHeader)) {
+    avb_error("Invalid image header.\n");
+    return NULL;
+  }
   image_header = (AftlImageHeader*)aftl_blob;
   /* Check for the magic value for an AftlImageHeader. */
   if (image_header->magic != AVB_AFTL_MAGIC) {
@@ -834,9 +823,11 @@
   }
   /* Extract the size out of the header. */
   image_size = avb_be32toh(image_header->image_size);
-  if (image_size > AVB_AFTL_MAX_AFTL_IMAGE_SIZE) return NULL;
-  avb_assert(image_size >= sizeof(AftlImageHeader) &&
-             image_size < AVB_AFTL_MAX_AFTL_IMAGE_SIZE);
+  if (image_size < sizeof(AftlImageHeader) ||
+      image_size > AVB_AFTL_MAX_AFTL_IMAGE_SIZE) {
+    avb_error("Invalid image size.\n");
+    return NULL;
+  }
   image = (AftlImage*)avb_calloc(sizeof(AftlImage));
   if (!image) {
     avb_error("Failed allocation for AftlImage.\n");
@@ -864,7 +855,12 @@
   aftl_blob += sizeof(AftlImageHeader);
   remaining_size = aftl_blob_size - sizeof(AftlImageHeader);
   for (i = 0; i < image->header.icp_count && remaining_size > 0; i++) {
-    image->entries[i] = parse_icp_entry(&aftl_blob, &remaining_size);
+    entry = parse_icp_entry(&aftl_blob, &remaining_size);
+    if (!entry) {
+      free_aftl_image(image);
+      return NULL;
+    }
+    image->entries[i] = entry;
   }
 
   return image;
@@ -877,16 +873,38 @@
     /* Free the log_url and log_root_signature elements if they exist. */
     if (icp_entry->log_url) avb_free(icp_entry->log_url);
     if (icp_entry->log_root_signature) avb_free(icp_entry->log_root_signature);
-    /* Free the FirmwareInfo elements if they exist. */
-    if (icp_entry->fw_info_leaf.json_data)
-      avb_free(icp_entry->fw_info_leaf.json_data);
-    if (icp_entry->fw_info_leaf.vbmeta_hash)
-      avb_free(icp_entry->fw_info_leaf.vbmeta_hash);
+    /* Free the annotation elements if they exist. */
+    if (icp_entry->annotation_leaf) {
+      if (icp_entry->annotation_leaf->signature) {
+        if (icp_entry->annotation_leaf->signature->signature) {
+          avb_free(icp_entry->annotation_leaf->signature->signature);
+        }
+        avb_free(icp_entry->annotation_leaf->signature);
+      }
+      if (icp_entry->annotation_leaf->annotation) {
+        if (icp_entry->annotation_leaf->annotation->vbmeta_hash)
+          avb_free(icp_entry->annotation_leaf->annotation->vbmeta_hash);
+        if (icp_entry->annotation_leaf->annotation->version_incremental)
+          avb_free(icp_entry->annotation_leaf->annotation->version_incremental);
+        if (icp_entry->annotation_leaf->annotation->manufacturer_key_hash)
+          avb_free(
+              icp_entry->annotation_leaf->annotation->manufacturer_key_hash);
+        if (icp_entry->annotation_leaf->annotation->description)
+          avb_free(icp_entry->annotation_leaf->annotation->description);
+        avb_free(icp_entry->annotation_leaf->annotation);
+      }
+      avb_free(icp_entry->annotation_leaf);
+    }
+    if (icp_entry->annotation_leaf_raw)
+      avb_free(icp_entry->annotation_leaf_raw);
     /* Free the TrillianLogRoot elements if they exist. */
     if (icp_entry->log_root_descriptor.metadata)
       avb_free(icp_entry->log_root_descriptor.metadata);
     if (icp_entry->log_root_descriptor.root_hash)
       avb_free(icp_entry->log_root_descriptor.root_hash);
+    if (icp_entry->log_root_descriptor_raw)
+      avb_free(icp_entry->log_root_descriptor_raw);
+    if (icp_entry->proofs) avb_free(icp_entry->proofs);
     /* Finally, free the AftlIcpEntry. */
     avb_free(icp_entry);
   }
diff --git a/libavb_aftl/avb_aftl_validate.c b/libavb_aftl/avb_aftl_validate.c
index 4121792..2d76b2f 100644
--- a/libavb_aftl/avb_aftl_validate.c
+++ b/libavb_aftl/avb_aftl_validate.c
@@ -42,14 +42,15 @@
 
   /* Only SHA256 hashes are currently supported. If the vbmeta hash
      size is not AVB_AFTL_HASH_SIZE, return false. */
-  if (icp_entry->fw_info_leaf.vbmeta_hash_size != AVB_AFTL_HASH_SIZE) {
+  if (icp_entry->annotation_leaf->annotation->vbmeta_hash_size !=
+      AVB_AFTL_HASH_SIZE) {
     avb_error("Invalid VBMeta hash size.\n");
     return false;
   }
 
   /* Return whether the calculated VBMeta hash matches the stored one. */
   return avb_safe_memcmp(vbmeta_hash,
-                         icp_entry->fw_info_leaf.vbmeta_hash,
+                         icp_entry->annotation_leaf->annotation->vbmeta_hash,
                          AVB_AFTL_HASH_SIZE) == 0;
 }
 
@@ -57,25 +58,14 @@
 bool avb_aftl_verify_icp_root_hash(AftlIcpEntry* icp_entry) {
   uint8_t leaf_hash[AVB_AFTL_HASH_SIZE];
   uint8_t result_hash[AVB_AFTL_HASH_SIZE];
-  uint8_t* buffer;
 
   avb_assert(icp_entry != NULL);
-  if (icp_entry->fw_info_leaf_size > AVB_AFTL_MAX_FW_INFO_SIZE) {
-    avb_error("Invalid FirmwareInfo leaf size\n");
-    return false;
-  }
-  buffer = (uint8_t*)avb_malloc(icp_entry->fw_info_leaf_size);
-  if (buffer == NULL) {
-    avb_error("Allocation failure in avb_aftl_verify_icp_root_hash\n");
-    return false;
-  }
   /* Calculate the RFC 6962 hash of the seed entry. */
-  if (!avb_aftl_rfc6962_hash_leaf(icp_entry->fw_info_leaf.json_data,
-                                  icp_entry->fw_info_leaf_size,
+  if (!avb_aftl_rfc6962_hash_leaf(icp_entry->annotation_leaf_raw,
+                                  icp_entry->annotation_leaf_size,
                                   leaf_hash)) {
     return false;
   }
-  avb_free(buffer);
   /* Calculate the Merkle tree's root hash. */
   if (!avb_aftl_root_from_icp(icp_entry->leaf_index,
                               icp_entry->log_root_descriptor.tree_size,
@@ -114,7 +104,9 @@
   log_root_hash_num_bytes = AVB_AFTL_HASH_SIZE;
 
   /* Calculate the SHA256 of the TrillianLogRootDescriptor. */
-  if (!avb_aftl_hash_log_root_descriptor(icp_entry, log_root_hash))
+  if (!avb_aftl_sha256(icp_entry->log_root_descriptor_raw,
+                       icp_entry->log_root_descriptor_size,
+                       log_root_hash))
     return false;
 
   /* algorithm_data is used to calculate the padding for signature verification.
diff --git a/proto/README.md b/proto/README.md
index eb3966c..ee64b01 100644
--- a/proto/README.md
+++ b/proto/README.md
@@ -2,27 +2,20 @@
 ---
 
 This directory contains the proto definitions required to communicate with an
-AFTL server. Two (api.proto and aftl.proto) contain the definitions of the
-protos needed to communicate with the AFTL Trillian personality. The remainder
-are dependencies. The original repos and purpose for each proto file are as
+AFTL server. The original repos and purpose for each proto file are as
 follows:
 
-* aftl.proto
-   <!-- TODO(danielaustin): Add detailed message descriptions. -->
-   Contains messages used by the AFTL frontend and the Trillian log.
 * api.proto
-   <!-- TODO(danielaustin): Add detailed message descriptions. -->
-   Contains the messages to communicate through the AFTL personality.
+   Contains the messages to communicate with the AFTL personality.
 * crypto/keyspb/keyspb.proto
    From https://github.com/google/trillian
    Dependency of trillian.proto
    Contains the PublicKey message definition used by Tree.
 * crypto/sigpb/sigpb.proto
    From https://github.com/google/trillian
-   Dependency of trillian.proto and aftl.proto
+   Dependency of trillian.proto
    For trillian.proto, contains the DigitallySigned message used by Tree and
-   SignedEntryTimestamp. For aftl.proto, contains the DigitallySigned message
-   used by SignedFirmwareInfo.
+   SignedEntryTimestamp.
 * trillian.proto
    From https://github.com/google/trillian
    Dependency of aftl.proto
diff --git a/proto/aftl.proto b/proto/aftl.proto
deleted file mode 100644
index 41f1148..0000000
--- a/proto/aftl.proto
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-syntax = "proto3";
-
-package aftl;
-option go_package = "proto";
-
-import "trillian.proto";
-import "crypto/sigpb/sigpb.proto";
-import "google/protobuf/timestamp.proto";
-
-// These messages are used both by the frontend API and the Trillian log.
-message FirmwareInfo {
-  // This is the SHA256 hash of vbmeta.
-  bytes vbmeta_hash = 1;
-
-  // Subcomponent of the build fingerprint as defined at
-  // https://source.android.com/compatibility/android-cdd#3_2_2_build_parameters.
-  // For example, a Pixel device with the following build fingerprint
-  // google/crosshatch/crosshatch:9/PQ3A.190605.003/5524043:user/release-keys,
-  // would have 5524043 for the version incremental.
-  string version_incremental = 2;
-
-  // Public key of the platform. This is the same key used to sign the vbmeta.
-  bytes platform_key = 3;
-
-  // SHA256 of the manufacturer public key (DER-encoded, x509
-  // subjectPublicKeyInfo format). The public key MUST already be in the list
-  // of root keys known and trusted by the AFTL.
-  // Internal: This field is required to be able to identify which manufacturer
-  // this request is coming from.
-  bytes manufacturer_key_hash = 4;
-
-  // Free form description field. It can be used to annotate this message with
-  // further context on the build (e.g., carrier specific build).
-  string description = 5;
-}
-
-message SignedFirmwareInfo {
-  FirmwareInfo info = 1;
-
-  // Signature of the info field, using manufacturer_pub_key.
-  // For the signature, info is first serialized to JSON. It is not
-  // expected to be able to reconstruct the info field from scratch.
-  // When verifying the inclusion proof associated with the info, it is
-  // expected that the leaf is provided.
-  sigpb.DigitallySigned info_signature = 2;
-}
-
-message FirmwareImageInfo {
-  // This is the SHA256 hash of vbmeta.
-  bytes vbmeta_hash = 1;
-
-  // SHA256 hash of the complete binary image. In case of Pixel, this would be
-  // the hash of the ZIP file that is offered for download at:
-  // https://developers.google.com/android/images
-  bytes hash = 2;
-
-  // Build fingerprint, e.g. in case of Pixel
-  // google/crosshatch/crosshatch:9/PQ3A.190605.003/5524043:user/release-keys
-  // See https://source.android.com/compatibility/android-cdd.html#3_2_2_build_parameters
-  // for the expected format of this field.
-  string build_fingerprint = 3;
-}
-
-message SignedFirmwareImageInfo {
-  FirmwareImageInfo image_info = 1;
-  sigpb.DigitallySigned image_info_signature = 2;
-}
-
-
-message InclusionProof {
-  trillian.Proof proof = 1;
-  trillian.SignedLogRoot sth = 2;
-}
-
-// Trillian-specific data types
-message Leaf {
-  int32 version = 1;
-
-  // Timestamp when the entry was added to the log.
-  google.protobuf.Timestamp timestamp = 2;
-
-  oneof value {
-    bytes vbmeta = 3;
-    FirmwareInfoAnnotation fw_info = 4;
-    FirmwareImageInfoAnnotation fw_image_info = 5;
-  }
-}
-
-message FirmwareInfoAnnotation {
-  SignedFirmwareInfo info = 1;
-}
-
-message FirmwareImageInfoAnnotation {
-  SignedFirmwareImageInfo info = 1;
-
-  // URL of the firmware image in the Cloud Storage bucket populated by AFTL.
-  string url = 2;
-}
diff --git a/proto/aftl_pb2.py b/proto/aftl_pb2.py
deleted file mode 100644
index ed811ae..0000000
--- a/proto/aftl_pb2.py
+++ /dev/null
@@ -1,469 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by the protocol buffer compiler.  DO NOT EDIT!
-# source: aftl.proto
-
-import sys
-_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
-from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
-from google.protobuf import symbol_database as _symbol_database
-# @@protoc_insertion_point(imports)
-
-_sym_db = _symbol_database.Default()
-
-
-import trillian_pb2 as trillian__pb2
-from crypto.sigpb import sigpb_pb2 as crypto_dot_sigpb_dot_sigpb__pb2
-from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2
-
-
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='aftl.proto',
-  package='aftl',
-  syntax='proto3',
-  serialized_options=_b('Z\005proto'),
-  serialized_pb=_b('\n\naftl.proto\x12\x04\x61\x66tl\x1a\x0etrillian.proto\x1a\x18\x63rypto/sigpb/sigpb.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x8a\x01\n\x0c\x46irmwareInfo\x12\x13\n\x0bvbmeta_hash\x18\x01 \x01(\x0c\x12\x1b\n\x13version_incremental\x18\x02 \x01(\t\x12\x14\n\x0cplatform_key\x18\x03 \x01(\x0c\x12\x1d\n\x15manufacturer_key_hash\x18\x04 \x01(\x0c\x12\x13\n\x0b\x64\x65scription\x18\x05 \x01(\t\"f\n\x12SignedFirmwareInfo\x12 \n\x04info\x18\x01 \x01(\x0b\x32\x12.aftl.FirmwareInfo\x12.\n\x0einfo_signature\x18\x02 \x01(\x0b\x32\x16.sigpb.DigitallySigned\"Q\n\x11\x46irmwareImageInfo\x12\x13\n\x0bvbmeta_hash\x18\x01 \x01(\x0c\x12\x0c\n\x04hash\x18\x02 \x01(\x0c\x12\x19\n\x11\x62uild_fingerprint\x18\x03 \x01(\t\"|\n\x17SignedFirmwareImageInfo\x12+\n\nimage_info\x18\x01 \x01(\x0b\x32\x17.aftl.FirmwareImageInfo\x12\x34\n\x14image_info_signature\x18\x02 \x01(\x0b\x32\x16.sigpb.DigitallySigned\"V\n\x0eInclusionProof\x12\x1e\n\x05proof\x18\x01 \x01(\x0b\x32\x0f.trillian.Proof\x12$\n\x03sth\x18\x02 \x01(\x0b\x32\x17.trillian.SignedLogRoot\"\xce\x01\n\x04Leaf\x12\x0f\n\x07version\x18\x01 \x01(\x05\x12-\n\ttimestamp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x10\n\x06vbmeta\x18\x03 \x01(\x0cH\x00\x12/\n\x07\x66w_info\x18\x04 \x01(\x0b\x32\x1c.aftl.FirmwareInfoAnnotationH\x00\x12:\n\rfw_image_info\x18\x05 \x01(\x0b\x32!.aftl.FirmwareImageInfoAnnotationH\x00\x42\x07\n\x05value\"@\n\x16\x46irmwareInfoAnnotation\x12&\n\x04info\x18\x01 \x01(\x0b\x32\x18.aftl.SignedFirmwareInfo\"W\n\x1b\x46irmwareImageInfoAnnotation\x12+\n\x04info\x18\x01 \x01(\x0b\x32\x1d.aftl.SignedFirmwareImageInfo\x12\x0b\n\x03url\x18\x02 \x01(\tB\x07Z\x05protob\x06proto3')
-  ,
-  dependencies=[trillian__pb2.DESCRIPTOR,crypto_dot_sigpb_dot_sigpb__pb2.DESCRIPTOR,google_dot_protobuf_dot_timestamp__pb2.DESCRIPTOR,])
-
-
-
-
-_FIRMWAREINFO = _descriptor.Descriptor(
-  name='FirmwareInfo',
-  full_name='aftl.FirmwareInfo',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='vbmeta_hash', full_name='aftl.FirmwareInfo.vbmeta_hash', index=0,
-      number=1, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b(""),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='version_incremental', full_name='aftl.FirmwareInfo.version_incremental', index=1,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='platform_key', full_name='aftl.FirmwareInfo.platform_key', index=2,
-      number=3, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b(""),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='manufacturer_key_hash', full_name='aftl.FirmwareInfo.manufacturer_key_hash', index=3,
-      number=4, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b(""),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='description', full_name='aftl.FirmwareInfo.description', index=4,
-      number=5, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=96,
-  serialized_end=234,
-)
-
-
-_SIGNEDFIRMWAREINFO = _descriptor.Descriptor(
-  name='SignedFirmwareInfo',
-  full_name='aftl.SignedFirmwareInfo',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='info', full_name='aftl.SignedFirmwareInfo.info', index=0,
-      number=1, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='info_signature', full_name='aftl.SignedFirmwareInfo.info_signature', index=1,
-      number=2, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=236,
-  serialized_end=338,
-)
-
-
-_FIRMWAREIMAGEINFO = _descriptor.Descriptor(
-  name='FirmwareImageInfo',
-  full_name='aftl.FirmwareImageInfo',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='vbmeta_hash', full_name='aftl.FirmwareImageInfo.vbmeta_hash', index=0,
-      number=1, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b(""),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='hash', full_name='aftl.FirmwareImageInfo.hash', index=1,
-      number=2, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b(""),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='build_fingerprint', full_name='aftl.FirmwareImageInfo.build_fingerprint', index=2,
-      number=3, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=340,
-  serialized_end=421,
-)
-
-
-_SIGNEDFIRMWAREIMAGEINFO = _descriptor.Descriptor(
-  name='SignedFirmwareImageInfo',
-  full_name='aftl.SignedFirmwareImageInfo',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='image_info', full_name='aftl.SignedFirmwareImageInfo.image_info', index=0,
-      number=1, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='image_info_signature', full_name='aftl.SignedFirmwareImageInfo.image_info_signature', index=1,
-      number=2, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=423,
-  serialized_end=547,
-)
-
-
-_INCLUSIONPROOF = _descriptor.Descriptor(
-  name='InclusionProof',
-  full_name='aftl.InclusionProof',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='proof', full_name='aftl.InclusionProof.proof', index=0,
-      number=1, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='sth', full_name='aftl.InclusionProof.sth', index=1,
-      number=2, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=549,
-  serialized_end=635,
-)
-
-
-_LEAF = _descriptor.Descriptor(
-  name='Leaf',
-  full_name='aftl.Leaf',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='version', full_name='aftl.Leaf.version', index=0,
-      number=1, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='timestamp', full_name='aftl.Leaf.timestamp', index=1,
-      number=2, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='vbmeta', full_name='aftl.Leaf.vbmeta', index=2,
-      number=3, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b(""),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='fw_info', full_name='aftl.Leaf.fw_info', index=3,
-      number=4, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='fw_image_info', full_name='aftl.Leaf.fw_image_info', index=4,
-      number=5, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-    _descriptor.OneofDescriptor(
-      name='value', full_name='aftl.Leaf.value',
-      index=0, containing_type=None, fields=[]),
-  ],
-  serialized_start=638,
-  serialized_end=844,
-)
-
-
-_FIRMWAREINFOANNOTATION = _descriptor.Descriptor(
-  name='FirmwareInfoAnnotation',
-  full_name='aftl.FirmwareInfoAnnotation',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='info', full_name='aftl.FirmwareInfoAnnotation.info', index=0,
-      number=1, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=846,
-  serialized_end=910,
-)
-
-
-_FIRMWAREIMAGEINFOANNOTATION = _descriptor.Descriptor(
-  name='FirmwareImageInfoAnnotation',
-  full_name='aftl.FirmwareImageInfoAnnotation',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='info', full_name='aftl.FirmwareImageInfoAnnotation.info', index=0,
-      number=1, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='url', full_name='aftl.FirmwareImageInfoAnnotation.url', index=1,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto3',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=912,
-  serialized_end=999,
-)
-
-_SIGNEDFIRMWAREINFO.fields_by_name['info'].message_type = _FIRMWAREINFO
-_SIGNEDFIRMWAREINFO.fields_by_name['info_signature'].message_type = crypto_dot_sigpb_dot_sigpb__pb2._DIGITALLYSIGNED
-_SIGNEDFIRMWAREIMAGEINFO.fields_by_name['image_info'].message_type = _FIRMWAREIMAGEINFO
-_SIGNEDFIRMWAREIMAGEINFO.fields_by_name['image_info_signature'].message_type = crypto_dot_sigpb_dot_sigpb__pb2._DIGITALLYSIGNED
-_INCLUSIONPROOF.fields_by_name['proof'].message_type = trillian__pb2._PROOF
-_INCLUSIONPROOF.fields_by_name['sth'].message_type = trillian__pb2._SIGNEDLOGROOT
-_LEAF.fields_by_name['timestamp'].message_type = google_dot_protobuf_dot_timestamp__pb2._TIMESTAMP
-_LEAF.fields_by_name['fw_info'].message_type = _FIRMWAREINFOANNOTATION
-_LEAF.fields_by_name['fw_image_info'].message_type = _FIRMWAREIMAGEINFOANNOTATION
-_LEAF.oneofs_by_name['value'].fields.append(
-  _LEAF.fields_by_name['vbmeta'])
-_LEAF.fields_by_name['vbmeta'].containing_oneof = _LEAF.oneofs_by_name['value']
-_LEAF.oneofs_by_name['value'].fields.append(
-  _LEAF.fields_by_name['fw_info'])
-_LEAF.fields_by_name['fw_info'].containing_oneof = _LEAF.oneofs_by_name['value']
-_LEAF.oneofs_by_name['value'].fields.append(
-  _LEAF.fields_by_name['fw_image_info'])
-_LEAF.fields_by_name['fw_image_info'].containing_oneof = _LEAF.oneofs_by_name['value']
-_FIRMWAREINFOANNOTATION.fields_by_name['info'].message_type = _SIGNEDFIRMWAREINFO
-_FIRMWAREIMAGEINFOANNOTATION.fields_by_name['info'].message_type = _SIGNEDFIRMWAREIMAGEINFO
-DESCRIPTOR.message_types_by_name['FirmwareInfo'] = _FIRMWAREINFO
-DESCRIPTOR.message_types_by_name['SignedFirmwareInfo'] = _SIGNEDFIRMWAREINFO
-DESCRIPTOR.message_types_by_name['FirmwareImageInfo'] = _FIRMWAREIMAGEINFO
-DESCRIPTOR.message_types_by_name['SignedFirmwareImageInfo'] = _SIGNEDFIRMWAREIMAGEINFO
-DESCRIPTOR.message_types_by_name['InclusionProof'] = _INCLUSIONPROOF
-DESCRIPTOR.message_types_by_name['Leaf'] = _LEAF
-DESCRIPTOR.message_types_by_name['FirmwareInfoAnnotation'] = _FIRMWAREINFOANNOTATION
-DESCRIPTOR.message_types_by_name['FirmwareImageInfoAnnotation'] = _FIRMWAREIMAGEINFOANNOTATION
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-FirmwareInfo = _reflection.GeneratedProtocolMessageType('FirmwareInfo', (_message.Message,), {
-  'DESCRIPTOR' : _FIRMWAREINFO,
-  '__module__' : 'aftl_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.FirmwareInfo)
-  })
-_sym_db.RegisterMessage(FirmwareInfo)
-
-SignedFirmwareInfo = _reflection.GeneratedProtocolMessageType('SignedFirmwareInfo', (_message.Message,), {
-  'DESCRIPTOR' : _SIGNEDFIRMWAREINFO,
-  '__module__' : 'aftl_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.SignedFirmwareInfo)
-  })
-_sym_db.RegisterMessage(SignedFirmwareInfo)
-
-FirmwareImageInfo = _reflection.GeneratedProtocolMessageType('FirmwareImageInfo', (_message.Message,), {
-  'DESCRIPTOR' : _FIRMWAREIMAGEINFO,
-  '__module__' : 'aftl_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.FirmwareImageInfo)
-  })
-_sym_db.RegisterMessage(FirmwareImageInfo)
-
-SignedFirmwareImageInfo = _reflection.GeneratedProtocolMessageType('SignedFirmwareImageInfo', (_message.Message,), {
-  'DESCRIPTOR' : _SIGNEDFIRMWAREIMAGEINFO,
-  '__module__' : 'aftl_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.SignedFirmwareImageInfo)
-  })
-_sym_db.RegisterMessage(SignedFirmwareImageInfo)
-
-InclusionProof = _reflection.GeneratedProtocolMessageType('InclusionProof', (_message.Message,), {
-  'DESCRIPTOR' : _INCLUSIONPROOF,
-  '__module__' : 'aftl_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.InclusionProof)
-  })
-_sym_db.RegisterMessage(InclusionProof)
-
-Leaf = _reflection.GeneratedProtocolMessageType('Leaf', (_message.Message,), {
-  'DESCRIPTOR' : _LEAF,
-  '__module__' : 'aftl_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.Leaf)
-  })
-_sym_db.RegisterMessage(Leaf)
-
-FirmwareInfoAnnotation = _reflection.GeneratedProtocolMessageType('FirmwareInfoAnnotation', (_message.Message,), {
-  'DESCRIPTOR' : _FIRMWAREINFOANNOTATION,
-  '__module__' : 'aftl_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.FirmwareInfoAnnotation)
-  })
-_sym_db.RegisterMessage(FirmwareInfoAnnotation)
-
-FirmwareImageInfoAnnotation = _reflection.GeneratedProtocolMessageType('FirmwareImageInfoAnnotation', (_message.Message,), {
-  'DESCRIPTOR' : _FIRMWAREIMAGEINFOANNOTATION,
-  '__module__' : 'aftl_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.FirmwareImageInfoAnnotation)
-  })
-_sym_db.RegisterMessage(FirmwareImageInfoAnnotation)
-
-
-DESCRIPTOR._options = None
-# @@protoc_insertion_point(module_scope)
diff --git a/proto/aftl_pb2_grpc.py b/proto/aftl_pb2_grpc.py
deleted file mode 100644
index a894352..0000000
--- a/proto/aftl_pb2_grpc.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
-import grpc
-
diff --git a/proto/api.proto b/proto/api.proto
index 4c66333..e22ae47 100644
--- a/proto/api.proto
+++ b/proto/api.proto
@@ -1,4 +1,4 @@
-// Copyright 2019 Google LLC
+// Copyright 2019-2020 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -17,27 +17,35 @@
 package aftl;
 option go_package = "proto";
 
-import "aftl.proto";
+import "trillian.proto";
 
-message AddFirmwareInfoRequest {
+message InclusionProof {
+  trillian.Proof proof = 1;
+  trillian.SignedLogRoot sth = 2;
+}
+
+message AddVBMetaRequest {
   // VBMeta structure as described in
   // https://android.googlesource.com/platform/external/avb/+/master/README.md.
   // In case of chained partitions, each VBMeta is added via a separate call.
   // The default size for gRPC payload is about 4MB. We expect vbmeta to be
-  // in the order of 1kB.
+  // in the order of 64kB.
   bytes vbmeta = 1;
 
-  SignedFirmwareInfo fw_info = 2;
+  // Serialized SignedVBMetaPrimaryAnnotation. This annotation contains the hash
+  // of the vbmeta structure. It is signed using the manufacturer key.
+  // See types/types.go.
+  bytes signed_vbmeta_primary_annotation = 2;
 }
 
-message AddFirmwareInfoResponse {
+message AddVBMetaResponse {
   // Inclusion proof and the leaf that was added to the log, which contains
-  // information on the firmware.
+  // the annotation on VBMeta.
   // It is required to have the complete leaf to validate the inclusion proof.
   // For on-device verification, only these first 2 fields are required to
   // validate the inclusion.
-  InclusionProof fw_info_proof = 1;
-  bytes          fw_info_leaf = 2;
+  InclusionProof annotation_proof = 1;
+  bytes          annotation_leaf = 2;
 
   // Inclusion proof and leaf that was added to the log, which contains the full
   // vbmeta partition.
@@ -47,12 +55,13 @@
   bytes          vbmeta_leaf = 4;
 }
 
-message AddFirmwareImageRequest {
+message AnnotateVBMetaWithBuildRequest {
+  // Serialized SignedVBMetaBuildAnnotation.  This annotation contains the hash
+  // of the full build image. See types/types.go.
+  bytes signed_vbmeta_build_annotation = 1;
 
-  SignedFirmwareImageInfo fw_image_info = 1;
-
-  // Bytes of the binary images. These are not signed as their final
-  // hash value is already signed in fw_image_info.hash
+  // Bytes of the binary images. The hash value of the concatenation of these
+  // chunk is contained in SignedVBMetaBuildAnnotation.
   // This is ignored if any of the requests origin_url is set.
   bytes image_chunk = 2;
 
@@ -61,14 +70,13 @@
   string origin_url = 3;
 }
 
-message AddFirmwareImageResponse {
-
+message AnnotateVBMetaWithBuildResponse {
   // Inclusion proof and leaf for the firmware image. The leaf contains the URL
   // where the image was stored.
   // It is not required for vendors to keep this information. However, this can
   // be used for their records to ensure the correctness of the log.
-  InclusionProof fw_image_info_proof = 1;
-  Leaf           fw_image_info_leaf = 2;
+  InclusionProof  annotation_proof = 1;
+  bytes           annotation_leaf = 2;
 }
 
 service AFTLog {
@@ -76,12 +84,11 @@
   // Insert a new VBMeta structure into the log.
   // This request will effectively create 2 log entries:
   //  - VBMeta itself
-  //  - Vendor annotations, including a reference to the VBMeta leaf.
-  rpc AddFirmwareInfo(AddFirmwareInfoRequest) returns (AddFirmwareInfoResponse) {}
+  //  - Vendor annotations, which includes a reference to the VBMeta.
+  rpc AddVBMeta(AddVBMetaRequest) returns (AddVBMetaResponse) {}
 
   // Upload (or copy) the complete firmware image.
-  rpc AddFirmwareImage(stream AddFirmwareImageRequest) returns (AddFirmwareImageResponse) {}
+  rpc AnnotateVBMetaWithBuild(stream AnnotateVBMetaWithBuildResponse) returns (AnnotateVBMetaWithBuildResponse) {}
 
-  // TODO GetProofByHash, GetSthConsistency, GetEntries, GetRootKeys
+  // TODO(tweek): GetProofByHash, GetSthConsistency, GetEntries, GetRootKeys
 }
-
diff --git a/proto/api_pb2.py b/proto/api_pb2.py
index f8f9a01..fbe3bab 100644
--- a/proto/api_pb2.py
+++ b/proto/api_pb2.py
@@ -1,4 +1,3 @@
-# pylint: skip-file
 # -*- coding: utf-8 -*-
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: api.proto
@@ -14,7 +13,7 @@
 _sym_db = _symbol_database.Default()
 
 
-import aftl_pb2 as aftl__pb2
+import trillian_pb2 as trillian__pb2
 
 
 DESCRIPTOR = _descriptor.FileDescriptor(
@@ -22,29 +21,29 @@
   package='aftl',
   syntax='proto3',
   serialized_options=_b('Z\005proto'),
-  serialized_pb=_b('\n\tapi.proto\x12\x04\x61\x66tl\x1a\naftl.proto\"S\n\x16\x41\x64\x64\x46irmwareInfoRequest\x12\x0e\n\x06vbmeta\x18\x01 \x01(\x0c\x12)\n\x07\x66w_info\x18\x02 \x01(\x0b\x32\x18.aftl.SignedFirmwareInfo\"\x9d\x01\n\x17\x41\x64\x64\x46irmwareInfoResponse\x12+\n\rfw_info_proof\x18\x01 \x01(\x0b\x32\x14.aftl.InclusionProof\x12\x14\n\x0c\x66w_info_leaf\x18\x02 \x01(\x0c\x12*\n\x0cvbmeta_proof\x18\x03 \x01(\x0b\x32\x14.aftl.InclusionProof\x12\x13\n\x0bvbmeta_leaf\x18\x04 \x01(\x0c\"x\n\x17\x41\x64\x64\x46irmwareImageRequest\x12\x34\n\rfw_image_info\x18\x01 \x01(\x0b\x32\x1d.aftl.SignedFirmwareImageInfo\x12\x13\n\x0bimage_chunk\x18\x02 \x01(\x0c\x12\x12\n\norigin_url\x18\x03 \x01(\t\"u\n\x18\x41\x64\x64\x46irmwareImageResponse\x12\x31\n\x13\x66w_image_info_proof\x18\x01 \x01(\x0b\x32\x14.aftl.InclusionProof\x12&\n\x12\x66w_image_info_leaf\x18\x02 \x01(\x0b\x32\n.aftl.Leaf2\xb1\x01\n\x06\x41\x46TLog\x12P\n\x0f\x41\x64\x64\x46irmwareInfo\x12\x1c.aftl.AddFirmwareInfoRequest\x1a\x1d.aftl.AddFirmwareInfoResponse\"\x00\x12U\n\x10\x41\x64\x64\x46irmwareImage\x12\x1d.aftl.AddFirmwareImageRequest\x1a\x1e.aftl.AddFirmwareImageResponse\"\x00(\x01\x42\x07Z\x05protob\x06proto3')
+  serialized_pb=_b('\n\tapi.proto\x12\x04\x61\x66tl\x1a\x0etrillian.proto\"V\n\x0eInclusionProof\x12\x1e\n\x05proof\x18\x01 \x01(\x0b\x32\x0f.trillian.Proof\x12$\n\x03sth\x18\x02 \x01(\x0b\x32\x17.trillian.SignedLogRoot\"L\n\x10\x41\x64\x64VBMetaRequest\x12\x0e\n\x06vbmeta\x18\x01 \x01(\x0c\x12(\n signed_vbmeta_primary_annotation\x18\x02 \x01(\x0c\"\x9d\x01\n\x11\x41\x64\x64VBMetaResponse\x12.\n\x10\x61nnotation_proof\x18\x01 \x01(\x0b\x32\x14.aftl.InclusionProof\x12\x17\n\x0f\x61nnotation_leaf\x18\x02 \x01(\x0c\x12*\n\x0cvbmeta_proof\x18\x03 \x01(\x0b\x32\x14.aftl.InclusionProof\x12\x13\n\x0bvbmeta_leaf\x18\x04 \x01(\x0c\"q\n\x1e\x41nnotateVBMetaWithBuildRequest\x12&\n\x1esigned_vbmeta_build_annotation\x18\x01 \x01(\x0c\x12\x13\n\x0bimage_chunk\x18\x02 \x01(\x0c\x12\x12\n\norigin_url\x18\x03 \x01(\t\"j\n\x1f\x41nnotateVBMetaWithBuildResponse\x12.\n\x10\x61nnotation_proof\x18\x01 \x01(\x0b\x32\x14.aftl.InclusionProof\x12\x17\n\x0f\x61nnotation_leaf\x18\x02 \x01(\x0c\x32\xb5\x01\n\x06\x41\x46TLog\x12>\n\tAddVBMeta\x12\x16.aftl.AddVBMetaRequest\x1a\x17.aftl.AddVBMetaResponse\"\x00\x12k\n\x17\x41nnotateVBMetaWithBuild\x12%.aftl.AnnotateVBMetaWithBuildResponse\x1a%.aftl.AnnotateVBMetaWithBuildResponse\"\x00(\x01\x42\x07Z\x05protob\x06proto3')
   ,
-  dependencies=[aftl__pb2.DESCRIPTOR,])
+  dependencies=[trillian__pb2.DESCRIPTOR,])
 
 
 
 
-_ADDFIRMWAREINFOREQUEST = _descriptor.Descriptor(
-  name='AddFirmwareInfoRequest',
-  full_name='aftl.AddFirmwareInfoRequest',
+_INCLUSIONPROOF = _descriptor.Descriptor(
+  name='InclusionProof',
+  full_name='aftl.InclusionProof',
   filename=None,
   file=DESCRIPTOR,
   containing_type=None,
   fields=[
     _descriptor.FieldDescriptor(
-      name='vbmeta', full_name='aftl.AddFirmwareInfoRequest.vbmeta', index=0,
-      number=1, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b(""),
+      name='proof', full_name='aftl.InclusionProof.proof', index=0,
+      number=1, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
-      name='fw_info', full_name='aftl.AddFirmwareInfoRequest.fw_info', index=1,
+      name='sth', full_name='aftl.InclusionProof.sth', index=1,
       number=2, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
@@ -62,41 +61,79 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=31,
-  serialized_end=114,
+  serialized_start=35,
+  serialized_end=121,
 )
 
 
-_ADDFIRMWAREINFORESPONSE = _descriptor.Descriptor(
-  name='AddFirmwareInfoResponse',
-  full_name='aftl.AddFirmwareInfoResponse',
+_ADDVBMETAREQUEST = _descriptor.Descriptor(
+  name='AddVBMetaRequest',
+  full_name='aftl.AddVBMetaRequest',
   filename=None,
   file=DESCRIPTOR,
   containing_type=None,
   fields=[
     _descriptor.FieldDescriptor(
-      name='fw_info_proof', full_name='aftl.AddFirmwareInfoResponse.fw_info_proof', index=0,
+      name='vbmeta', full_name='aftl.AddVBMetaRequest.vbmeta', index=0,
+      number=1, type=12, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b(""),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='signed_vbmeta_primary_annotation', full_name='aftl.AddVBMetaRequest.signed_vbmeta_primary_annotation', index=1,
+      number=2, type=12, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b(""),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=123,
+  serialized_end=199,
+)
+
+
+_ADDVBMETARESPONSE = _descriptor.Descriptor(
+  name='AddVBMetaResponse',
+  full_name='aftl.AddVBMetaResponse',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='annotation_proof', full_name='aftl.AddVBMetaResponse.annotation_proof', index=0,
       number=1, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
-      name='fw_info_leaf', full_name='aftl.AddFirmwareInfoResponse.fw_info_leaf', index=1,
+      name='annotation_leaf', full_name='aftl.AddVBMetaResponse.annotation_leaf', index=1,
       number=2, type=12, cpp_type=9, label=1,
       has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
-      name='vbmeta_proof', full_name='aftl.AddFirmwareInfoResponse.vbmeta_proof', index=2,
+      name='vbmeta_proof', full_name='aftl.AddVBMetaResponse.vbmeta_proof', index=2,
       number=3, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
-      name='vbmeta_leaf', full_name='aftl.AddFirmwareInfoResponse.vbmeta_leaf', index=3,
+      name='vbmeta_leaf', full_name='aftl.AddVBMetaResponse.vbmeta_leaf', index=3,
       number=4, type=12, cpp_type=9, label=1,
       has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
@@ -114,34 +151,34 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=117,
-  serialized_end=274,
+  serialized_start=202,
+  serialized_end=359,
 )
 
 
-_ADDFIRMWAREIMAGEREQUEST = _descriptor.Descriptor(
-  name='AddFirmwareImageRequest',
-  full_name='aftl.AddFirmwareImageRequest',
+_ANNOTATEVBMETAWITHBUILDREQUEST = _descriptor.Descriptor(
+  name='AnnotateVBMetaWithBuildRequest',
+  full_name='aftl.AnnotateVBMetaWithBuildRequest',
   filename=None,
   file=DESCRIPTOR,
   containing_type=None,
   fields=[
     _descriptor.FieldDescriptor(
-      name='fw_image_info', full_name='aftl.AddFirmwareImageRequest.fw_image_info', index=0,
-      number=1, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
+      name='signed_vbmeta_build_annotation', full_name='aftl.AnnotateVBMetaWithBuildRequest.signed_vbmeta_build_annotation', index=0,
+      number=1, type=12, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
-      name='image_chunk', full_name='aftl.AddFirmwareImageRequest.image_chunk', index=1,
+      name='image_chunk', full_name='aftl.AnnotateVBMetaWithBuildRequest.image_chunk', index=1,
       number=2, type=12, cpp_type=9, label=1,
       has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
-      name='origin_url', full_name='aftl.AddFirmwareImageRequest.origin_url', index=2,
+      name='origin_url', full_name='aftl.AnnotateVBMetaWithBuildRequest.origin_url', index=2,
       number=3, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=_b("").decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
@@ -159,29 +196,29 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=276,
-  serialized_end=396,
+  serialized_start=361,
+  serialized_end=474,
 )
 
 
-_ADDFIRMWAREIMAGERESPONSE = _descriptor.Descriptor(
-  name='AddFirmwareImageResponse',
-  full_name='aftl.AddFirmwareImageResponse',
+_ANNOTATEVBMETAWITHBUILDRESPONSE = _descriptor.Descriptor(
+  name='AnnotateVBMetaWithBuildResponse',
+  full_name='aftl.AnnotateVBMetaWithBuildResponse',
   filename=None,
   file=DESCRIPTOR,
   containing_type=None,
   fields=[
     _descriptor.FieldDescriptor(
-      name='fw_image_info_proof', full_name='aftl.AddFirmwareImageResponse.fw_image_info_proof', index=0,
+      name='annotation_proof', full_name='aftl.AnnotateVBMetaWithBuildResponse.annotation_proof', index=0,
       number=1, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
     _descriptor.FieldDescriptor(
-      name='fw_image_info_leaf', full_name='aftl.AddFirmwareImageResponse.fw_image_info_leaf', index=1,
-      number=2, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
+      name='annotation_leaf', full_name='aftl.AnnotateVBMetaWithBuildResponse.annotation_leaf', index=1,
+      number=2, type=12, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b(""),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
@@ -197,49 +234,56 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=398,
-  serialized_end=515,
+  serialized_start=476,
+  serialized_end=582,
 )
 
-_ADDFIRMWAREINFOREQUEST.fields_by_name['fw_info'].message_type = aftl__pb2._SIGNEDFIRMWAREINFO
-_ADDFIRMWAREINFORESPONSE.fields_by_name['fw_info_proof'].message_type = aftl__pb2._INCLUSIONPROOF
-_ADDFIRMWAREINFORESPONSE.fields_by_name['vbmeta_proof'].message_type = aftl__pb2._INCLUSIONPROOF
-_ADDFIRMWAREIMAGEREQUEST.fields_by_name['fw_image_info'].message_type = aftl__pb2._SIGNEDFIRMWAREIMAGEINFO
-_ADDFIRMWAREIMAGERESPONSE.fields_by_name['fw_image_info_proof'].message_type = aftl__pb2._INCLUSIONPROOF
-_ADDFIRMWAREIMAGERESPONSE.fields_by_name['fw_image_info_leaf'].message_type = aftl__pb2._LEAF
-DESCRIPTOR.message_types_by_name['AddFirmwareInfoRequest'] = _ADDFIRMWAREINFOREQUEST
-DESCRIPTOR.message_types_by_name['AddFirmwareInfoResponse'] = _ADDFIRMWAREINFORESPONSE
-DESCRIPTOR.message_types_by_name['AddFirmwareImageRequest'] = _ADDFIRMWAREIMAGEREQUEST
-DESCRIPTOR.message_types_by_name['AddFirmwareImageResponse'] = _ADDFIRMWAREIMAGERESPONSE
+_INCLUSIONPROOF.fields_by_name['proof'].message_type = trillian__pb2._PROOF
+_INCLUSIONPROOF.fields_by_name['sth'].message_type = trillian__pb2._SIGNEDLOGROOT
+_ADDVBMETARESPONSE.fields_by_name['annotation_proof'].message_type = _INCLUSIONPROOF
+_ADDVBMETARESPONSE.fields_by_name['vbmeta_proof'].message_type = _INCLUSIONPROOF
+_ANNOTATEVBMETAWITHBUILDRESPONSE.fields_by_name['annotation_proof'].message_type = _INCLUSIONPROOF
+DESCRIPTOR.message_types_by_name['InclusionProof'] = _INCLUSIONPROOF
+DESCRIPTOR.message_types_by_name['AddVBMetaRequest'] = _ADDVBMETAREQUEST
+DESCRIPTOR.message_types_by_name['AddVBMetaResponse'] = _ADDVBMETARESPONSE
+DESCRIPTOR.message_types_by_name['AnnotateVBMetaWithBuildRequest'] = _ANNOTATEVBMETAWITHBUILDREQUEST
+DESCRIPTOR.message_types_by_name['AnnotateVBMetaWithBuildResponse'] = _ANNOTATEVBMETAWITHBUILDRESPONSE
 _sym_db.RegisterFileDescriptor(DESCRIPTOR)
 
-AddFirmwareInfoRequest = _reflection.GeneratedProtocolMessageType('AddFirmwareInfoRequest', (_message.Message,), {
-  'DESCRIPTOR' : _ADDFIRMWAREINFOREQUEST,
+InclusionProof = _reflection.GeneratedProtocolMessageType('InclusionProof', (_message.Message,), {
+  'DESCRIPTOR' : _INCLUSIONPROOF,
   '__module__' : 'api_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.AddFirmwareInfoRequest)
+  # @@protoc_insertion_point(class_scope:aftl.InclusionProof)
   })
-_sym_db.RegisterMessage(AddFirmwareInfoRequest)
+_sym_db.RegisterMessage(InclusionProof)
 
-AddFirmwareInfoResponse = _reflection.GeneratedProtocolMessageType('AddFirmwareInfoResponse', (_message.Message,), {
-  'DESCRIPTOR' : _ADDFIRMWAREINFORESPONSE,
+AddVBMetaRequest = _reflection.GeneratedProtocolMessageType('AddVBMetaRequest', (_message.Message,), {
+  'DESCRIPTOR' : _ADDVBMETAREQUEST,
   '__module__' : 'api_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.AddFirmwareInfoResponse)
+  # @@protoc_insertion_point(class_scope:aftl.AddVBMetaRequest)
   })
-_sym_db.RegisterMessage(AddFirmwareInfoResponse)
+_sym_db.RegisterMessage(AddVBMetaRequest)
 
-AddFirmwareImageRequest = _reflection.GeneratedProtocolMessageType('AddFirmwareImageRequest', (_message.Message,), {
-  'DESCRIPTOR' : _ADDFIRMWAREIMAGEREQUEST,
+AddVBMetaResponse = _reflection.GeneratedProtocolMessageType('AddVBMetaResponse', (_message.Message,), {
+  'DESCRIPTOR' : _ADDVBMETARESPONSE,
   '__module__' : 'api_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.AddFirmwareImageRequest)
+  # @@protoc_insertion_point(class_scope:aftl.AddVBMetaResponse)
   })
-_sym_db.RegisterMessage(AddFirmwareImageRequest)
+_sym_db.RegisterMessage(AddVBMetaResponse)
 
-AddFirmwareImageResponse = _reflection.GeneratedProtocolMessageType('AddFirmwareImageResponse', (_message.Message,), {
-  'DESCRIPTOR' : _ADDFIRMWAREIMAGERESPONSE,
+AnnotateVBMetaWithBuildRequest = _reflection.GeneratedProtocolMessageType('AnnotateVBMetaWithBuildRequest', (_message.Message,), {
+  'DESCRIPTOR' : _ANNOTATEVBMETAWITHBUILDREQUEST,
   '__module__' : 'api_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.AddFirmwareImageResponse)
+  # @@protoc_insertion_point(class_scope:aftl.AnnotateVBMetaWithBuildRequest)
   })
-_sym_db.RegisterMessage(AddFirmwareImageResponse)
+_sym_db.RegisterMessage(AnnotateVBMetaWithBuildRequest)
+
+AnnotateVBMetaWithBuildResponse = _reflection.GeneratedProtocolMessageType('AnnotateVBMetaWithBuildResponse', (_message.Message,), {
+  'DESCRIPTOR' : _ANNOTATEVBMETAWITHBUILDRESPONSE,
+  '__module__' : 'api_pb2'
+  # @@protoc_insertion_point(class_scope:aftl.AnnotateVBMetaWithBuildResponse)
+  })
+_sym_db.RegisterMessage(AnnotateVBMetaWithBuildResponse)
 
 
 DESCRIPTOR._options = None
@@ -250,25 +294,25 @@
   file=DESCRIPTOR,
   index=0,
   serialized_options=None,
-  serialized_start=518,
-  serialized_end=695,
+  serialized_start=585,
+  serialized_end=766,
   methods=[
   _descriptor.MethodDescriptor(
-    name='AddFirmwareInfo',
-    full_name='aftl.AFTLog.AddFirmwareInfo',
+    name='AddVBMeta',
+    full_name='aftl.AFTLog.AddVBMeta',
     index=0,
     containing_service=None,
-    input_type=_ADDFIRMWAREINFOREQUEST,
-    output_type=_ADDFIRMWAREINFORESPONSE,
+    input_type=_ADDVBMETAREQUEST,
+    output_type=_ADDVBMETARESPONSE,
     serialized_options=None,
   ),
   _descriptor.MethodDescriptor(
-    name='AddFirmwareImage',
-    full_name='aftl.AFTLog.AddFirmwareImage',
+    name='AnnotateVBMetaWithBuild',
+    full_name='aftl.AFTLog.AnnotateVBMetaWithBuild',
     index=1,
     containing_service=None,
-    input_type=_ADDFIRMWAREIMAGEREQUEST,
-    output_type=_ADDFIRMWAREIMAGERESPONSE,
+    input_type=_ANNOTATEVBMETAWITHBUILDRESPONSE,
+    output_type=_ANNOTATEVBMETAWITHBUILDRESPONSE,
     serialized_options=None,
   ),
 ])
diff --git a/proto/api_pb2_grpc.py b/proto/api_pb2_grpc.py
index b1834c7..d487856 100644
--- a/proto/api_pb2_grpc.py
+++ b/proto/api_pb2_grpc.py
@@ -1,4 +1,3 @@
-# pylint: skip-file
 # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
 import grpc
 
@@ -15,15 +14,15 @@
     Args:
       channel: A grpc.Channel.
     """
-    self.AddFirmwareInfo = channel.unary_unary(
-        '/aftl.AFTLog/AddFirmwareInfo',
-        request_serializer=api__pb2.AddFirmwareInfoRequest.SerializeToString,
-        response_deserializer=api__pb2.AddFirmwareInfoResponse.FromString,
+    self.AddVBMeta = channel.unary_unary(
+        '/aftl.AFTLog/AddVBMeta',
+        request_serializer=api__pb2.AddVBMetaRequest.SerializeToString,
+        response_deserializer=api__pb2.AddVBMetaResponse.FromString,
         )
-    self.AddFirmwareImage = channel.stream_unary(
-        '/aftl.AFTLog/AddFirmwareImage',
-        request_serializer=api__pb2.AddFirmwareImageRequest.SerializeToString,
-        response_deserializer=api__pb2.AddFirmwareImageResponse.FromString,
+    self.AnnotateVBMetaWithBuild = channel.stream_unary(
+        '/aftl.AFTLog/AnnotateVBMetaWithBuild',
+        request_serializer=api__pb2.AnnotateVBMetaWithBuildResponse.SerializeToString,
+        response_deserializer=api__pb2.AnnotateVBMetaWithBuildResponse.FromString,
         )
 
 
@@ -31,17 +30,17 @@
   # missing associated documentation comment in .proto file
   pass
 
-  def AddFirmwareInfo(self, request, context):
+  def AddVBMeta(self, request, context):
     """Insert a new VBMeta structure into the log.
     This request will effectively create 2 log entries:
     - VBMeta itself
-    - Vendor annotations, including a reference to the VBMeta leaf.
+    - Vendor annotations, which includes a reference to the VBMeta.
     """
     context.set_code(grpc.StatusCode.UNIMPLEMENTED)
     context.set_details('Method not implemented!')
     raise NotImplementedError('Method not implemented!')
 
-  def AddFirmwareImage(self, request_iterator, context):
+  def AnnotateVBMetaWithBuild(self, request_iterator, context):
     """Upload (or copy) the complete firmware image.
     """
     context.set_code(grpc.StatusCode.UNIMPLEMENTED)
@@ -51,15 +50,15 @@
 
 def add_AFTLogServicer_to_server(servicer, server):
   rpc_method_handlers = {
-      'AddFirmwareInfo': grpc.unary_unary_rpc_method_handler(
-          servicer.AddFirmwareInfo,
-          request_deserializer=api__pb2.AddFirmwareInfoRequest.FromString,
-          response_serializer=api__pb2.AddFirmwareInfoResponse.SerializeToString,
+      'AddVBMeta': grpc.unary_unary_rpc_method_handler(
+          servicer.AddVBMeta,
+          request_deserializer=api__pb2.AddVBMetaRequest.FromString,
+          response_serializer=api__pb2.AddVBMetaResponse.SerializeToString,
       ),
-      'AddFirmwareImage': grpc.stream_unary_rpc_method_handler(
-          servicer.AddFirmwareImage,
-          request_deserializer=api__pb2.AddFirmwareImageRequest.FromString,
-          response_serializer=api__pb2.AddFirmwareImageResponse.SerializeToString,
+      'AnnotateVBMetaWithBuild': grpc.stream_unary_rpc_method_handler(
+          servicer.AnnotateVBMetaWithBuild,
+          request_deserializer=api__pb2.AnnotateVBMetaWithBuildResponse.FromString,
+          response_serializer=api__pb2.AnnotateVBMetaWithBuildResponse.SerializeToString,
       ),
   }
   generic_handler = grpc.method_handlers_generic_handler(
diff --git a/test/avb_aftl_fuzz.cc b/test/avb_aftl_fuzz.cc
new file mode 100644
index 0000000..89b59d8
--- /dev/null
+++ b/test/avb_aftl_fuzz.cc
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "libavb_aftl/avb_aftl_types.h"
+#include "libavb_aftl/avb_aftl_util.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) {
+  AftlImage* image = parse_aftl_image((uint8_t*)data, size);
+  free_aftl_image(image);
+  return 0;
+}
diff --git a/test/avb_aftl_util_unittest.cc b/test/avb_aftl_util_unittest.cc
index a813976..33a598c 100644
--- a/test/avb_aftl_util_unittest.cc
+++ b/test/avb_aftl_util_unittest.cc
@@ -29,8 +29,13 @@
 
 namespace {
 
-const char kAftlImagePath[] = "test/data/aftl_image.bin";
-const char kAftlImageMultiPath[] = "test/data/aftl_image_multi.bin";
+/* TODO(b/154115873): These VBMetas are manually generated. We need to implement
+ * a mock in aftltool that generates an inclusion proof and call that mock from
+ * the unit tests, similarly to what is done with GenerateVBMetaImage. */
+const char kAftlImagePath[] = "test/data/aftl_output_vbmeta_with_1_icp.img";
+const uint64_t kAftlImageOffset = 0x1100;
+const char kAftlImageMultiPath[] =
+    "test/data/aftl_output_vbmeta_with_2_icp_same_log.img";
 
 }  // namespace
 
@@ -41,31 +46,23 @@
   AvbAftlUtilTest() {}
   ~AvbAftlUtilTest() {}
   void SetUp() override {
-    uint8_t* aftl_blob;
-    int64_t aftl_image_size;
+    std::string content;
 
     BaseAvbToolTest::SetUp();
     /* Read in test data from the aftl_image binaries. */
-    base::GetFileSize(base::FilePath(kAftlImagePath), &aftl_image_size);
-    ASSERT_GT(aftl_image_size, 0);
-    aftl_blob = (uint8_t*)avb_malloc(aftl_image_size);
-    ASSERT_TRUE(aftl_blob != NULL);
-    base::ReadFile(
-        base::FilePath(kAftlImagePath), (char*)aftl_blob, aftl_image_size);
+    ASSERT_TRUE(
+        base::ReadFileToString(base::FilePath(kAftlImagePath), &content));
+    content = content.substr(kAftlImageOffset);
     /* Allocate and populate an AftlImage for testing. */
-    aftl_image_ = parse_aftl_image(aftl_blob, aftl_image_size);
-    avb_free(aftl_blob);
+    aftl_image_ = parse_aftl_image((uint8_t*)content.data(), content.size());
 
     /* Read in test data from the aftl_image file with multiple ICPs. */
-    base::GetFileSize(base::FilePath(kAftlImageMultiPath), &aftl_image_size);
-    ASSERT_GT(aftl_image_size, 0);
-    aftl_blob = (uint8_t*)avb_malloc(aftl_image_size);
-    ASSERT_TRUE(aftl_blob != NULL);
-    base::ReadFile(
-        base::FilePath(kAftlImageMultiPath), (char*)aftl_blob, aftl_image_size);
+    ASSERT_TRUE(
+        base::ReadFileToString(base::FilePath(kAftlImageMultiPath), &content));
+    content = content.substr(kAftlImageOffset);
     /* Allocate and populate an AftlImage for testing. */
-    aftl_image_multi_ = parse_aftl_image(aftl_blob, aftl_image_size);
-    avb_free(aftl_blob);
+    aftl_image_multi_ =
+        parse_aftl_image((uint8_t*)content.data(), content.size());
   }
 
   void TearDown() override {
@@ -77,7 +74,7 @@
   void TestAftlImageHeader(AftlImageHeader* aftl_header, uint16_t icp_count) {
     EXPECT_EQ(aftl_header->magic, 0x4c544641ul);
     EXPECT_EQ(aftl_header->required_icp_version_major, 1ul);
-    EXPECT_EQ(aftl_header->required_icp_version_minor, 1ul);
+    EXPECT_EQ(aftl_header->required_icp_version_minor, 2ul);
     EXPECT_EQ(aftl_header->icp_count, icp_count);
   }
 
@@ -86,7 +83,7 @@
     EXPECT_GT(icp_entry->log_url_size, 0ul);
     EXPECT_GT(icp_entry->leaf_index, 1ul);
     EXPECT_GT(icp_entry->log_root_descriptor_size, 0ul);
-    EXPECT_GT(icp_entry->fw_info_leaf_size, 0ul);
+    EXPECT_GT(icp_entry->annotation_leaf_size, 0ul);
     EXPECT_EQ(icp_entry->log_root_sig_size, AVB_AFTL_SIGNATURE_SIZE);
     EXPECT_GT(icp_entry->proof_hash_count, 0ul);
     EXPECT_LT(icp_entry->proof_hash_count, 64ul);
@@ -101,7 +98,8 @@
     EXPECT_GT(icp_entry->log_root_descriptor.revision, 0ull);
     EXPECT_EQ(icp_entry->log_root_descriptor.metadata_size, 0);
     /* Test the FirmwareInfo fields. */
-    EXPECT_EQ(icp_entry->fw_info_leaf.vbmeta_hash_size, AVB_AFTL_HASH_SIZE);
+    EXPECT_EQ(icp_entry->annotation_leaf->annotation->vbmeta_hash_size,
+              AVB_AFTL_HASH_SIZE);
     EXPECT_EQ(icp_entry->proof_hash_count * 32ul, icp_entry->inc_proof_size);
   }
 
diff --git a/test/avb_aftl_validate_unittest.cc b/test/avb_aftl_validate_unittest.cc
index 84a723d..2a78e6a 100644
--- a/test/avb_aftl_validate_unittest.cc
+++ b/test/avb_aftl_validate_unittest.cc
@@ -33,8 +33,15 @@
 
 namespace {
 
-const char kAftlKeyBytesPath[] = "test/data/aftl_key_bytes.bin";
-const char kAftlLogSigPath[] = "test/data/aftl_log_sig.bin";
+/* Public part of testkey_rsa4096.pem, in the AvbRsaPublicKey format. Generated
+ * using:
+ *   $ openssl rsa -in testkey_rsa4096.pem -pubout -out testkey_rsa4096_pub.pem
+ *   $ avbtool extract_public_key --key testkey_rsa4096_pub.pem --output \
+ *     testkey_rsa4096_pub.bin.
+ */
+const char kKeyBytesPath[] = "test/data/testkey_rsa4096_pub.bin";
+/* Example VBMeta. Its hash should match the value kVBMetaHash defined below. */
+const char kVBMetaPath[] = "test/data/aftl_input_vbmeta.img";
 
 } /* namespace */
 
@@ -46,44 +53,108 @@
   AvbAftlValidateTest() {}
   ~AvbAftlValidateTest() {}
   void SetUp() override {
-    uint8_t kAftlJsonData[] =
-        "{\"timestamp\":{\"seconds\":1581533076,\"nanos\":884246745},\"Value\":"
-        "{\"FwInfo\":{\"info\":{\"info\":{\"vbmeta_hash\":"
-        "\"mS461dkWuKtPENmqaVQpg/"
-        "xoHUPNsqRvnrh1uLUkKCQ=\",\"version_incremental\":\"1\",\"manufacturer_"
-        "key_hash\":\"JkjCeRzSiHsHxxiVVieHNEvd9bsehav59qmB4BRvYGs=\"},\"info_"
-        "signature\":{\"hash_algorithm\":4,\"signature_algorithm\":1,"
-        "\"signature\":\"YqMyK9rOly4dG+"
-        "QX3qXwkCedZK8w8iXHX90i0OXV4reCNS8xP51scQoh/"
-        "SINWjJQ3hDjIfveQ0SRtY748GeNfrajCDslRAce8f48M3B9Jf5RezbY/MA4ZE/"
-        "IfgTQp6sFLPp2xM+RoPd/GMHtEP0zc98+0/7hsDC7wZeGip7HoxGGiaWqpy+zkp/"
-        "NpD4aSEIz5gtvBisPI/blQbyPoH6cfNT9rJLvzfHIa6Cp/xpZoY7e2EUH/"
-        "XoG6cJGDC3ddPxuLISITQ6ddZkpyhTcA5+xSN8zJxjei1EQOk02Oo9Bqs4srIuO1o/"
-        "b91bTteykCK6ScCMt/rSsfxW6N9o/KvNSOr/"
-        "csXyIBkeHQZ952MaD8vGNX3NkE+FdOEXBr6AWdAwIuHsjVK1uSp+nR/"
-        "kQ2NuXnALXTsM1nB70rnUYdD0cC8OIHvJs9JvV4ATJ/"
-        "SQAoGIDdk1up7w6y7+QOtXC+Dd2Y6aul96xiqDRrdza0ZyEzOBPIssNq34dVR+k7+"
-        "jofkMsDD/"
-        "VT3Ngec17SeZUFfKj1Uv1z6bt6fusfv6Veb84ch0Yx5elLXNfnvvguF0z5qZp+"
-        "AjlkUEbhI5sRKrE9v1wV/IFiwYuHNMX3NBuKpx+8e7SXwZodXRBeocpSlA/"
-        "Qf8dtomxAALZrB30HSOzYavMs/4=\"}}}}}";
+    /* Generate an artificial inclusion proof with its own annotation. The
+     * annotation matches the kVBMetaPath file. It is signed using the
+     * testkey_rsa4096.pem key. */
+    /* We define the constants below as string literals (to be able to annotate
+     * the bytes). We keep their sizes in a separate variable as sizeof will
+     * include the final null byte that is automatically appended. */
+    const uint8_t kAnnotationLeafHeader[] =
+        "\x01"                              // Version
+        "\x00\x00\x00\x00\x00\x00\x00\x00"  // Timestamp
+        "\x01";                             // Leaf Type
+    const size_t kAnnotationLeafHeaderSize = sizeof(kAnnotationLeafHeader) - 1;
+    const uint8_t kSignature[] =
+        "\x00"   // Hash Type
+        "\x00"   // Signature Type
+        "\x00";  // Signature size
+    const size_t kSignatureSize = sizeof(kSignature) - 1;
+    const uint8_t kAnnotationHeader[] = "\x20";  // VBMeta hash size
+    const size_t kAnnotationHeaderSize = sizeof(kAnnotationHeader) - 1;
+    /* This is the SHA256 hash of the image at kVBMetaPath */
+    const uint8_t kVBMetaHash[] =
+        "\x34\x1c\x6c\xf2\x4b\xc1\xe6\x4a\xb1\x03\xa0\xee\xe1\x9d\xee\x9c"
+        "\x35\x34\xdb\x07\x17\x29\xb4\xad\xd0\xce\xa0\xbd\x52\x92\x54\xec";
+    const uint8_t kAnnotationFooter[] =
+        "\x03"      // Version incremental size
+        "123"       // Version incremental
+        "\x00"      // Manufacturer key hash size
+        "\x00\x05"  // Description size
+        "abcde";    // Description
+    const size_t kAnnotationFooterSize = sizeof(kAnnotationFooter) - 1;
+    const uint8_t kLogRootDescriptorHeader[] =
+        "\x00\x01"                          // Version
+        "\x00\x00\x00\x00\x00\x00\x00\x03"  // Tree size
+        "\x20";                             // Root hash size
+    const size_t kLogRootDescriptorHeaderSize =
+        sizeof(kLogRootDescriptorHeader) - 1;
+    const uint8_t kLogRootDescriptorRootHash[] =
+        "\x40\x79\x2f\xf1\xcb\xfc\xd1\x8a\x13\x70\x90\xaf\x6a\x16\x4d\xa9"
+        "\x36\x80\x99\xb3\xf9\x7f\x99\x13\x3e\x07\xff\xbc\x73\x42\xfc\xc7";
+    const uint8_t kLogRootDescriptorFooter[] =
+        "\x00\x00\x00\x00\x13\x36\x4b\xff"  // Timestamp
+        "\x00\x00\x00\x00\x00\x00\x00\x00"  // Revision
+        "\x00\x00";                         // Metadata size
+    const size_t kLogRootDescriptorFooterSize =
+        sizeof(kLogRootDescriptorFooter) - 1;
+    /* Signature of the log root descriptor.
+     *   $ openssl dgst -sha256 -sign testkey_rsa4096.pem \
+     *   -out kLogRootHashSignature log_root_descriptor_raw
+     * log_root_descriptor_raw is defined as the concatenation:
+     * kLogRootDescriptorHeader || kLogRootDescriptorRootHash ||
+     * kLogRootDescriptorFooter */
+    const uint8_t kLogRootHashSignature[] = {
+        0x55, 0x1d, 0xd3, 0x13, 0x3c, 0x41, 0xde, 0x67, 0x79, 0xf1, 0xc6, 0xad,
+        0x72, 0x10, 0xff, 0xfb, 0x6d, 0xac, 0xc1, 0x1c, 0x06, 0x2a, 0x3e, 0xa8,
+        0xd9, 0xf3, 0x8c, 0x9c, 0x67, 0xbe, 0x1e, 0x8e, 0xe1, 0x02, 0xf6, 0xdb,
+        0xd2, 0x5c, 0x31, 0x4b, 0x26, 0xad, 0x9a, 0xd1, 0xf5, 0x7d, 0xb9, 0x6b,
+        0x4b, 0xf1, 0x7a, 0x89, 0x9d, 0xf0, 0x17, 0xb4, 0xee, 0xb2, 0x08, 0x0d,
+        0xd8, 0x99, 0xac, 0x7b, 0x34, 0x1f, 0xd1, 0x9c, 0x2e, 0x0c, 0xd1, 0xb1,
+        0x42, 0x34, 0xf2, 0x65, 0xbb, 0x79, 0x7a, 0xac, 0x23, 0x37, 0xec, 0xfc,
+        0xff, 0xbf, 0x66, 0x51, 0xed, 0x3e, 0xa7, 0x45, 0x3a, 0xf9, 0x72, 0xaa,
+        0x01, 0x3c, 0xfd, 0x59, 0x01, 0x67, 0x67, 0xb4, 0x57, 0x23, 0xb6, 0x7e,
+        0x59, 0x82, 0xb3, 0x98, 0xa2, 0x57, 0xd4, 0x64, 0x83, 0xaa, 0x02, 0x17,
+        0x87, 0xfd, 0xa2, 0xe2, 0x3b, 0xa8, 0xf5, 0xc2, 0xfb, 0xce, 0x7f, 0x59,
+        0x72, 0x10, 0xc5, 0x11, 0x81, 0x80, 0x20, 0x4a, 0x3e, 0xf9, 0x85, 0x2e,
+        0x44, 0x94, 0x87, 0xec, 0xfa, 0x2e, 0x8f, 0x75, 0x00, 0x6f, 0x52, 0x1b,
+        0x4d, 0x5c, 0xfc, 0xe4, 0x1f, 0xe2, 0x94, 0xbc, 0x8c, 0xe8, 0x7f, 0x74,
+        0x14, 0x2f, 0x66, 0x8e, 0xfb, 0x11, 0x34, 0xde, 0x80, 0x21, 0x92, 0xc3,
+        0x52, 0xa7, 0xf7, 0x5e, 0x49, 0x53, 0x21, 0x7d, 0x8b, 0xa2, 0xcb, 0x84,
+        0x80, 0x64, 0x0d, 0xd7, 0xd0, 0x6d, 0x6f, 0x2a, 0x98, 0x57, 0x3b, 0x95,
+        0xa1, 0x63, 0x39, 0x00, 0x22, 0x9e, 0x5a, 0x75, 0x07, 0x10, 0x1f, 0x7e,
+        0xdb, 0x05, 0x5d, 0x3d, 0x76, 0x75, 0x3c, 0x1a, 0xd4, 0x1e, 0x8d, 0x6e,
+        0xce, 0x57, 0xd6, 0xce, 0x23, 0xc0, 0x23, 0x4c, 0xcb, 0x10, 0xec, 0x59,
+        0x22, 0x64, 0x57, 0x33, 0x1c, 0x3f, 0xa9, 0x43, 0x97, 0xc1, 0xc0, 0x93,
+        0x5a, 0x16, 0x80, 0x51, 0x56, 0x28, 0x98, 0x33, 0xee, 0x1a, 0xf8, 0x38,
+        0x7a, 0xaa, 0xdb, 0x43, 0x39, 0x90, 0x9e, 0x74, 0xb7, 0x9f, 0xfe, 0xa5,
+        0x84, 0x69, 0xf5, 0x77, 0x80, 0x92, 0xec, 0x06, 0x06, 0xe0, 0xd2, 0x98,
+        0x34, 0x66, 0x25, 0xc3, 0x7c, 0x89, 0x78, 0x3a, 0x0b, 0x48, 0x49, 0x37,
+        0x46, 0x07, 0xc4, 0xc8, 0x04, 0x72, 0x45, 0x60, 0x36, 0x98, 0x2d, 0x47,
+        0xfe, 0xba, 0x74, 0xb9, 0xb0, 0xe4, 0xf5, 0x45, 0xa0, 0xfb, 0x4a, 0x53,
+        0xe0, 0x16, 0x6a, 0x6b, 0x82, 0xcc, 0x33, 0x1c, 0x3c, 0x64, 0xe0, 0x90,
+        0x3c, 0x59, 0xfa, 0x04, 0x51, 0xe0, 0xe8, 0xaa, 0xe9, 0x92, 0x43, 0x04,
+        0x2a, 0x49, 0xd4, 0xdf, 0xac, 0x1d, 0x46, 0x44, 0xad, 0x65, 0x62, 0xaf,
+        0x44, 0x16, 0xb0, 0x05, 0x56, 0x2b, 0xa4, 0xad, 0x4c, 0x7e, 0xbd, 0x04,
+        0x95, 0xcb, 0xce, 0x0e, 0xf6, 0xd5, 0x4b, 0x3a, 0xc0, 0xde, 0x1e, 0xf8,
+        0xfa, 0xf5, 0x73, 0x4a, 0x6d, 0xc2, 0x4a, 0xe1, 0xaf, 0xae, 0xd8, 0x31,
+        0x23, 0x16, 0x5d, 0x15, 0x41, 0xe6, 0xbf, 0x4a, 0xe0, 0xf3, 0xdd, 0x74,
+        0x32, 0x96, 0x64, 0x4c, 0x16, 0x7d, 0xd3, 0xad, 0x21, 0x47, 0x2b, 0x17,
+        0xb9, 0xf3, 0x84, 0x38, 0x80, 0x60, 0xb6, 0xcb, 0x24, 0x45, 0x24, 0x90,
+        0x74, 0xe9, 0x50, 0xea, 0x2e, 0x1f, 0xc2, 0x74, 0x36, 0xa2, 0xf5, 0xd7,
+        0x24, 0xb3, 0xa1, 0x1f, 0xd3, 0x39, 0x61, 0x67, 0x37, 0xe4, 0x2a, 0x20,
+        0x67, 0x95, 0x53, 0x9d, 0xd4, 0xdb, 0x4f, 0xa6, 0xb8, 0x7f, 0x91, 0xb2,
+        0xc5, 0x6f, 0x71, 0x3c, 0x86, 0xc8, 0x36, 0x8d, 0xa4, 0x4d, 0x53, 0x6b,
+        0x3f, 0xe6, 0xce, 0xf1, 0x7a, 0xa2, 0x2e, 0x53, 0x80, 0x4c, 0x52, 0x9d,
+        0x3e, 0xd7, 0xec, 0x47, 0x4a, 0xfa, 0x84, 0xa5, 0x9a, 0x2f, 0x7b, 0xfc,
+        0xfc, 0xe8, 0xa4, 0x09, 0xfb, 0xb5, 0xb7, 0xf2};
     BaseAvbToolTest::SetUp();
 
     /* Read in test data from the key and log_sig binaries. */
-    base::GetFileSize(base::FilePath(kAftlKeyBytesPath), &key_size_);
-    if (key_size_ != AVB_AFTL_PUB_KEY_SIZE) return;
-    key_bytes_ = (uint8_t*)avb_malloc(key_size_);
-    if (!key_bytes_) return;
-    base::ReadFile(
-        base::FilePath(kAftlKeyBytesPath), (char*)key_bytes_, key_size_);
-    base::GetFileSize(base::FilePath(kAftlLogSigPath), &log_sig_size_);
-    if (log_sig_size_ != AVB_AFTL_SIGNATURE_SIZE) return;
-    log_sig_bytes_ = (uint8_t*)avb_malloc(log_sig_size_);
-    if (!log_sig_bytes_) return;
-    base::ReadFile(
-        base::FilePath(kAftlLogSigPath), (char*)log_sig_bytes_, log_sig_size_);
-    icp_entry_ =
-        (AftlIcpEntry*)avb_malloc(sizeof(AftlIcpEntry) + AVB_AFTL_HASH_SIZE);
+    ASSERT_TRUE(
+        base::ReadFileToString(base::FilePath(kKeyBytesPath), &key_bytes_));
+
+    /* Allocate and populate the inclusion proof */
+    icp_entry_ = (AftlIcpEntry*)avb_malloc(sizeof(AftlIcpEntry));
     if (!icp_entry_) return;
     icp_entry_->log_root_descriptor.version = 1;
     icp_entry_->log_root_descriptor.tree_size = 3;
@@ -92,108 +163,141 @@
     icp_entry_->log_root_descriptor.revision = 0;
     icp_entry_->log_root_descriptor.metadata_size = 0;
     icp_entry_->log_root_descriptor.metadata = NULL;
-    icp_entry_->log_root_descriptor_size =
-        icp_entry_->log_root_descriptor.root_hash_size +
-        icp_entry_->log_root_descriptor.metadata_size + 29;
-
-    icp_entry_->fw_info_leaf_size = sizeof(kAftlJsonData);
-    icp_entry_->fw_info_leaf.vbmeta_hash_size = AVB_AFTL_HASH_SIZE;
-    icp_entry_->fw_info_leaf.vbmeta_hash =
-        (uint8_t*)avb_malloc(AVB_AFTL_HASH_SIZE);
-    if (!icp_entry_->fw_info_leaf.vbmeta_hash) {
+    icp_entry_->log_root_descriptor_size = kLogRootDescriptorHeaderSize +
+                                           AVB_AFTL_HASH_SIZE +
+                                           kLogRootDescriptorFooterSize;
+    icp_entry_->log_root_descriptor_raw =
+        (uint8_t*)avb_malloc(icp_entry_->log_root_descriptor_size);
+    if (!icp_entry_->log_root_descriptor_raw) {
       return;
     }
-    memcpy(icp_entry_->fw_info_leaf.vbmeta_hash,
-           "\x65\xec\x58\x83\x43\x62\x8e\x81\x4d\xc7\x75\xa8\xcb\x77\x1f\x46"
-           "\x81\xcc\x79\x6f\xba\x32\xf0\x68\xc7\x17\xce\x2e\xe2\x14\x4d\x39",
+    memcpy(icp_entry_->log_root_descriptor_raw,
+           kLogRootDescriptorHeader,
+           kLogRootDescriptorHeaderSize);
+    memcpy(icp_entry_->log_root_descriptor_raw + kLogRootDescriptorHeaderSize,
+           kLogRootDescriptorRootHash,
            AVB_AFTL_HASH_SIZE);
-    icp_entry_->fw_info_leaf.json_data =
-        (uint8_t*)avb_calloc(icp_entry_->fw_info_leaf_size);
-    if (icp_entry_->fw_info_leaf.json_data == NULL) {
-      avb_free(icp_entry_->fw_info_leaf.vbmeta_hash);
-      return;
-    }
-    memcpy(icp_entry_->fw_info_leaf.json_data,
-           kAftlJsonData,
-           icp_entry_->fw_info_leaf_size);
-    icp_entry_->leaf_index = 2;
+    memcpy(icp_entry_->log_root_descriptor_raw + kLogRootDescriptorHeaderSize +
+               AVB_AFTL_HASH_SIZE,
+           kLogRootDescriptorFooter,
+           kLogRootDescriptorFooterSize);
+    icp_entry_->log_root_descriptor.root_hash =
+        (uint8_t*)avb_malloc(AVB_AFTL_HASH_SIZE);
+    if (!icp_entry_->log_root_descriptor.root_hash) return;
+    /* Copy the hash from within the raw version */
+    memcpy(icp_entry_->log_root_descriptor.root_hash,
+           kLogRootDescriptorRootHash,
+           AVB_AFTL_HASH_SIZE);
+    icp_entry_->log_root_sig_size = AVB_AFTL_SIGNATURE_SIZE;
+    icp_entry_->log_root_signature =
+        (uint8_t*)avb_malloc(AVB_AFTL_SIGNATURE_SIZE);
+    memcpy(icp_entry_->log_root_signature,
+           kLogRootHashSignature,
+           AVB_AFTL_SIGNATURE_SIZE);
 
+    /* Allocate the annotation leaf */
+    icp_entry_->annotation_leaf_size =
+        kAnnotationLeafHeaderSize + kSignatureSize + kAnnotationHeaderSize +
+        AVB_AFTL_HASH_SIZE + kAnnotationFooterSize;
+    icp_entry_->annotation_leaf =
+        (SignedVBMetaPrimaryAnnotationLeaf*)avb_calloc(
+            sizeof(SignedVBMetaPrimaryAnnotationLeaf));
+    if (!icp_entry_->annotation_leaf) return;
+    icp_entry_->annotation_leaf->version = 1;
+    icp_entry_->annotation_leaf->timestamp = 0;
+    icp_entry_->annotation_leaf->leaf_type =
+        AVB_AFTL_SIGNED_VBMETA_PRIMARY_ANNOTATION_LEAF;
+    icp_entry_->annotation_leaf->annotation =
+        (VBMetaPrimaryAnnotation*)avb_calloc(sizeof(VBMetaPrimaryAnnotation));
+    if (!icp_entry_->annotation_leaf->annotation) return;
+    icp_entry_->annotation_leaf->annotation->vbmeta_hash_size =
+        AVB_AFTL_HASH_SIZE;
+    icp_entry_->annotation_leaf->annotation->vbmeta_hash =
+        (uint8_t*)avb_calloc(AVB_AFTL_HASH_SIZE);
+    if (!icp_entry_->annotation_leaf->annotation->vbmeta_hash) return;
+    memcpy(icp_entry_->annotation_leaf->annotation->vbmeta_hash,
+           kVBMetaHash,
+           AVB_AFTL_HASH_SIZE);
+    icp_entry_->annotation_leaf_raw =
+        (uint8_t*)avb_calloc(icp_entry_->annotation_leaf_size);
+    if (!icp_entry_->annotation_leaf_raw) return;
+    memcpy(icp_entry_->annotation_leaf_raw,
+           kAnnotationLeafHeader,
+           kAnnotationLeafHeaderSize);
+    memcpy(icp_entry_->annotation_leaf_raw + kAnnotationLeafHeaderSize,
+           kSignature,
+           kSignatureSize);
+    memcpy(icp_entry_->annotation_leaf_raw + kAnnotationLeafHeaderSize +
+               kSignatureSize,
+           kAnnotationHeader,
+           kAnnotationHeaderSize);
+    memcpy(icp_entry_->annotation_leaf_raw + kAnnotationLeafHeaderSize +
+               kSignatureSize + kAnnotationHeaderSize,
+           kVBMetaHash,
+           AVB_AFTL_HASH_SIZE);
+    memcpy(icp_entry_->annotation_leaf_raw + kAnnotationLeafHeaderSize +
+               kSignatureSize + kAnnotationHeaderSize + AVB_AFTL_HASH_SIZE,
+           kAnnotationFooter,
+           kAnnotationFooterSize);
+
+    icp_entry_->leaf_index = 2;
+    icp_entry_->proofs =
+        (uint8_t(*)[AVB_AFTL_HASH_SIZE])avb_calloc(AVB_AFTL_HASH_SIZE);
     memcpy(icp_entry_->proofs[0],
            "\xfa\xc5\x42\x03\xe7\xcc\x69\x6c\xf0\xdf\xcb\x42\xc9\x2a\x1d\x9d"
            "\xba\xf7\x0a\xd9\xe6\x21\xf4\xbd\x8d\x98\x66\x2f\x00\xe3\xc1\x25",
            AVB_AFTL_HASH_SIZE);
     icp_entry_->proof_hash_count = 1;
-    icp_entry_->log_root_descriptor.root_hash =
-        (uint8_t*)avb_malloc(AVB_AFTL_HASH_SIZE);
-    if (!icp_entry_->log_root_descriptor.root_hash) return;
-    memcpy(icp_entry_->log_root_descriptor.root_hash,
-           "\x5a\xb3\x43\x21\x8f\x54\x4d\x05\x46\x34\x62\x86\x2f\xa8\xf8\x6e"
-           "\x3b\xa3\x19\x2d\xe9\x9c\xb2\xab\x8e\x09\xd8\x55\xc3\xde\x34\xd6",
-           AVB_AFTL_HASH_SIZE);
   }
 
   void TearDown() override {
-    if (icp_entry_ != NULL) {
-      if (icp_entry_->fw_info_leaf.json_data != NULL)
-        avb_free(icp_entry_->fw_info_leaf.json_data);
-      if (icp_entry_->fw_info_leaf.vbmeta_hash != NULL)
-        avb_free(icp_entry_->fw_info_leaf.vbmeta_hash);
-      if (icp_entry_->log_root_descriptor.root_hash != NULL)
+    if (icp_entry_) {
+      if (icp_entry_->annotation_leaf_raw)
+        avb_free(icp_entry_->annotation_leaf_raw);
+      if (icp_entry_->annotation_leaf) {
+        if (icp_entry_->annotation_leaf->annotation) {
+          if (icp_entry_->annotation_leaf->annotation->vbmeta_hash)
+            avb_free(icp_entry_->annotation_leaf->annotation->vbmeta_hash);
+          avb_free(icp_entry_->annotation_leaf->annotation);
+        }
+        avb_free(icp_entry_->annotation_leaf);
+      }
+      if (icp_entry_->log_root_descriptor.root_hash)
         avb_free(icp_entry_->log_root_descriptor.root_hash);
+      if (icp_entry_->log_root_descriptor_raw)
+        avb_free(icp_entry_->log_root_descriptor_raw);
+      if (icp_entry_->log_root_signature)
+        avb_free(icp_entry_->log_root_signature);
+      if (icp_entry_->proofs) avb_free(icp_entry_->proofs);
       avb_free(icp_entry_);
     }
-    avb_free(key_bytes_);
-    avb_free(log_sig_bytes_);
     BaseAvbToolTest::TearDown();
   }
 
  protected:
   AftlIcpEntry* icp_entry_;
-  uint8_t* key_bytes_;
-  uint8_t* log_sig_bytes_;
-  int64_t key_size_;
-  int64_t log_sig_size_;
+  std::string key_bytes_;
 };
 
-TEST_F(AvbAftlValidateTest, AvbAftlVerifySignature) {
-  icp_entry_->log_root_sig_size = AVB_AFTL_SIGNATURE_SIZE;
-  icp_entry_->log_root_signature =
-      (uint8_t*)avb_malloc(AVB_AFTL_SIGNATURE_SIZE);
-  memcpy(
-      icp_entry_->log_root_signature, log_sig_bytes_, AVB_AFTL_SIGNATURE_SIZE);
+TEST_F(AvbAftlValidateTest, VerifyEntrySignature) {
   EXPECT_EQ(true,
-            avb_aftl_verify_entry_signature(key_bytes_, key_size_, icp_entry_));
-  avb_free(icp_entry_->log_root_signature);
+            avb_aftl_verify_entry_signature(
+                (uint8_t*)key_bytes_.data(), key_bytes_.size(), icp_entry_));
 }
 
-TEST_F(AvbAftlValidateTest, AvbAftlHashLogRootDescriptor) {
-  uint8_t hash[AVB_AFTL_HASH_SIZE];
-
-  /* Initialize the icp_entry components used with the test. */
-
-  avb_aftl_hash_log_root_descriptor(icp_entry_, hash);
-  EXPECT_EQ("4f932f328f4b1c9b16500d6d09005c46abebf5c4dc761bbd1e8602378789edac",
-            mem_to_hexstring(hash, AVB_AFTL_HASH_SIZE));
-}
-
-TEST_F(AvbAftlValidateTest, AvbAftlVerifyIcpRootHash) {
-  /* Initialize the icp_entry components used with the test. */
+TEST_F(AvbAftlValidateTest, VerifyIcpRootHash) {
   EXPECT_EQ(true, avb_aftl_verify_icp_root_hash(icp_entry_));
 }
 
-// TODO(b/154115873): reenable this test
-TEST_F(AvbAftlValidateTest, DISABLED_AftlVerifyVbmetaHash) {
-  GenerateVBMetaImage("vbmeta.img",
-                      "SHA256_RSA4096",
-                      0,
-                      base::FilePath("test/data/testkey_rsa4096.pem"));
-
+TEST_F(AvbAftlValidateTest, VerifyVbmetaHash) {
+  std::string vbmeta;
+  ASSERT_TRUE(base::ReadFileToString(base::FilePath(kVBMetaPath), &vbmeta));
   EXPECT_EQ(true,
             avb_aftl_verify_vbmeta_hash(
-                vbmeta_image_.data(), vbmeta_image_.size(), icp_entry_));
+                (uint8_t*)vbmeta.data(), vbmeta.size(), icp_entry_));
 }
 
-TEST_F(AvbAftlValidateTest, AvbAftlRootFromIcp) {
+TEST_F(AvbAftlValidateTest, RootFromIcp) {
   /* Tests from trillian root_from_icp functionality:
      https://github.com/google/trillian/blob/master/merkle/log_verifier_test.go
   */
@@ -280,7 +384,7 @@
       << "Failed on test #4";
 }
 
-TEST_F(AvbAftlValidateTest, AvbAftlChainInner) {
+TEST_F(AvbAftlValidateTest, ChainInner) {
   uint8_t hash[AVB_AFTL_HASH_SIZE];
   uint8_t seed[AVB_AFTL_HASH_SIZE];
   uint8_t proof[4][AVB_AFTL_HASH_SIZE];
@@ -339,7 +443,7 @@
       << " and leaf_index 3";
 }
 
-TEST_F(AvbAftlValidateTest, AvbAftlChainBorderRight) {
+TEST_F(AvbAftlValidateTest, ChainBorderRight) {
   uint8_t hash[AVB_AFTL_HASH_SIZE];
   uint8_t seed[AVB_AFTL_HASH_SIZE];
   uint8_t proof[2][AVB_AFTL_HASH_SIZE];
@@ -369,7 +473,7 @@
          "\"7890abcdefghijklmnopqrstuvwxyz12\"]";
 }
 
-TEST_F(AvbAftlValidateTest, AvbAftlRFC6962HashChildren) {
+TEST_F(AvbAftlValidateTest, RFC6962HashChildren) {
   uint8_t hash[AVB_AFTL_HASH_SIZE];
 
   avb_aftl_rfc6962_hash_children((uint8_t*)"", 0, (uint8_t*)"", 0, hash);
@@ -394,7 +498,7 @@
       << "Failed on inputs \"abcd\" and \"efgh\"";
 }
 
-TEST_F(AvbAftlValidateTest, AvbAftlRFC6962HashLeaf) {
+TEST_F(AvbAftlValidateTest, RFC6962HashLeaf) {
   uint8_t hash[AVB_AFTL_HASH_SIZE];
   avb_aftl_rfc6962_hash_leaf((uint8_t*)"", 0, hash);
   EXPECT_EQ("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d",
@@ -406,7 +510,7 @@
       << "Failed on input \"abcdefg\"";
 }
 
-TEST_F(AvbAftlValidateTest, AvbAftlSha256) {
+TEST_F(AvbAftlValidateTest, Sha256) {
   /* Computed with:
    *
    * $ echo -n foobar |sha256sum
diff --git a/test/avb_aftl_verify_unittest.cc b/test/avb_aftl_verify_unittest.cc
index 9f64294..1e44a4a 100644
--- a/test/avb_aftl_verify_unittest.cc
+++ b/test/avb_aftl_verify_unittest.cc
@@ -36,12 +36,15 @@
 namespace {
 
 /* Log transparency key */
-const char kAftlTestKey[] = "test/data/aftl_log_key_bytes.bin";
-/* Regular VBMeta structure without AFTL-specific data */
-const char kVbmetaBin[] = "test/data/aftl_verify_vbmeta.bin";
-/* Full vbmeta partition which contains the VBMeta above followed by its
- * associated AftlDescriptor */
-const char kVbmetaWithAftlDescBin[] = "test/data/aftl_verify_full.img";
+const char kAftlTestKey[] = "test/data/aftl_pubkey_1.bin";
+/* Full VBMeta partition which contains an AftlImage */
+/* TODO(b/154115873): These VBMetas are manually generated. We need to implement
+ * a mock in aftltool that generates an inclusion proof and call that mock from
+ * the unit tests, similarly to what is done with GenerateVBMetaImage. */
+const char kVbmetaWithAftlDescBin[] =
+    "test/data/aftl_output_vbmeta_with_1_icp.img";
+/* Size of the VBMetaImage in the partition */
+const uint64_t kVbmetaSize = 0x1100;
 
 } /* namespace */
 
@@ -60,24 +63,11 @@
     asv_test_data_ = NULL;
 
     /* Read in the test data. */
-    base::GetFileSize(base::FilePath(kAftlTestKey), &key_size_);
-    key_bytes_ = (uint8_t*)avb_malloc(key_size_);
-    ASSERT_TRUE(key_bytes_ != NULL);
-    base::ReadFile(base::FilePath(kAftlTestKey), (char*)key_bytes_, key_size_);
-
-    base::GetFileSize(base::FilePath(kVbmetaBin), &vbmeta_blob_size_);
-    vbmeta_blob_ = (uint8_t*)avb_malloc(vbmeta_blob_size_);
-    ASSERT_TRUE(vbmeta_blob_ != NULL);
-    base::ReadFile(
-        base::FilePath(kVbmetaBin), (char*)vbmeta_blob_, vbmeta_blob_size_);
-
-    base::GetFileSize(base::FilePath(kVbmetaWithAftlDescBin),
-                      &vbmeta_full_blob_size_);
-    vbmeta_full_blob_ = (uint8_t*)avb_malloc(vbmeta_full_blob_size_);
-    ASSERT_TRUE(vbmeta_full_blob_ != NULL);
-    base::ReadFile(base::FilePath(kVbmetaWithAftlDescBin),
-                   (char*)vbmeta_full_blob_,
-                   vbmeta_full_blob_size_);
+    ASSERT_TRUE(base::ReadFileToString(base::FilePath(kAftlTestKey), &key_));
+    ASSERT_TRUE(base::ReadFileToString(base::FilePath(kVbmetaWithAftlDescBin),
+                                       &vbmeta_icp_));
+    /* Keep a truncated version of the image without the ICP */
+    vbmeta_ = vbmeta_icp_.substr(0, kVbmetaSize);
 
     /* Set up required parts of asv_test_data */
     asv_test_data_ = (AvbSlotVerifyData*)avb_calloc(sizeof(AvbSlotVerifyData));
@@ -87,20 +77,18 @@
     asv_test_data_->vbmeta_images =
         (AvbVBMetaData*)avb_calloc(sizeof(AvbVBMetaData));
     ASSERT_TRUE(asv_test_data_->vbmeta_images != NULL);
-    asv_test_data_->vbmeta_images[0].vbmeta_size = vbmeta_blob_size_;
+    asv_test_data_->vbmeta_images[0].vbmeta_size = vbmeta_.size();
     asv_test_data_->vbmeta_images[0].vbmeta_data =
-        (uint8_t*)avb_calloc(vbmeta_blob_size_);
+        (uint8_t*)avb_calloc(vbmeta_.size());
     ASSERT_TRUE(asv_test_data_->vbmeta_images[0].vbmeta_data != NULL);
     memcpy(asv_test_data_->vbmeta_images[0].vbmeta_data,
-           vbmeta_blob_,
-           vbmeta_blob_size_);
-    asv_test_data_->vbmeta_images[0].partition_name = (char*)"aftl_verify_full";
+           vbmeta_.data(),
+           vbmeta_.size());
+    asv_test_data_->vbmeta_images[0].partition_name =
+        (char*)"aftl_output_vbmeta_with_1_icp";
   }
 
   void TearDown() override {
-    if (key_bytes_ != NULL) avb_free(key_bytes_);
-    if (vbmeta_blob_ != NULL) avb_free(vbmeta_blob_);
-    if (vbmeta_full_blob_ != NULL) avb_free(vbmeta_full_blob_);
     if (asv_test_data_ != NULL) {
       if (asv_test_data_->vbmeta_images != NULL) {
         if (asv_test_data_->vbmeta_images[0].vbmeta_data != NULL) {
@@ -115,41 +103,37 @@
 
  protected:
   AvbSlotVerifyData* asv_test_data_;
-  uint8_t* key_bytes_;
-  int64_t key_size_;
-
-  uint8_t* vbmeta_blob_;
-  int64_t vbmeta_blob_size_;
-  uint8_t* vbmeta_full_blob_;
-  int64_t vbmeta_full_blob_size_;
+  std::string key_;
+  std::string vbmeta_;
+  std::string vbmeta_icp_;
 };
 
 TEST_F(AvbAftlVerifyTest, Basic) {
-  AftlSlotVerifyResult result =
-      aftl_slot_verify(ops_.avb_ops(), asv_test_data_, key_bytes_, key_size_);
+  AftlSlotVerifyResult result = aftl_slot_verify(
+      ops_.avb_ops(), asv_test_data_, (uint8_t*)key_.data(), key_.size());
   EXPECT_EQ(result, AFTL_SLOT_VERIFY_RESULT_OK);
 }
 
 TEST_F(AvbAftlVerifyTest, PartitionError) {
   asv_test_data_->vbmeta_images[0].partition_name = (char*)"do-no-exist";
-  AftlSlotVerifyResult result =
-      aftl_slot_verify(ops_.avb_ops(), asv_test_data_, key_bytes_, key_size_);
+  AftlSlotVerifyResult result = aftl_slot_verify(
+      ops_.avb_ops(), asv_test_data_, (uint8_t*)key_.data(), key_.size());
   EXPECT_EQ(result, AFTL_SLOT_VERIFY_RESULT_ERROR_IMAGE_NOT_FOUND);
 }
 
 TEST_F(AvbAftlVerifyTest, MismatchingVBMeta) {
   asv_test_data_->vbmeta_images[0].vbmeta_data[0] = 'X';
-  AftlSlotVerifyResult result =
-      aftl_slot_verify(ops_.avb_ops(), asv_test_data_, key_bytes_, key_size_);
+  AftlSlotVerifyResult result = aftl_slot_verify(
+      ops_.avb_ops(), asv_test_data_, (uint8_t*)key_.data(), key_.size());
   EXPECT_EQ(result, AFTL_SLOT_VERIFY_RESULT_ERROR_VBMETA_HASH_MISMATCH);
 }
 
 TEST_F(AvbAftlVerifyTest, InvalidKey) {
   // Corrupt the key in order to fail the verification: complement the last
   // byte, we keep the key header valid.
-  key_bytes_[key_size_ - 1] = ~key_bytes_[key_size_ - 1];
-  AftlSlotVerifyResult result =
-      aftl_slot_verify(ops_.avb_ops(), asv_test_data_, key_bytes_, key_size_);
+  key_[key_.size() - 1] = ~key_[key_.size() - 1];
+  AftlSlotVerifyResult result = aftl_slot_verify(
+      ops_.avb_ops(), asv_test_data_, (uint8_t*)key_.data(), key_.size());
   EXPECT_EQ(result, AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_PROOF_SIGNATURE);
 }
 
diff --git a/test/avb_slot_verify_unittest.cc b/test/avb_slot_verify_unittest.cc
index c516d24..680ea27 100644
--- a/test/avb_slot_verify_unittest.cc
+++ b/test/avb_slot_verify_unittest.cc
@@ -831,6 +831,64 @@
   avb_slot_verify_data_free(slot_data);
 }
 
+TEST_F(AvbSlotVerifyTest, SmallPreallocatedPreloadedPartitionFailGracefully) {
+  const size_t boot_partition_size = 16 * 1024 * 1024;
+  const size_t boot_image_size = 5 * 1024 * 1024;
+  // Generate vbmeta based on this boot image.
+  base::FilePath boot_path = GenerateImage("boot_a.img", boot_image_size);
+
+  // Preload smaller image than expected on the stack
+  // libavb should not attempt to free this buffer.
+  const size_t fake_preload_image_size = 1024;
+  uint8_t fake_preload_buf[fake_preload_image_size];
+
+  EXPECT_COMMAND(
+      0,
+      "./avbtool add_hash_footer"
+      " --image %s"
+      " --rollback_index 0"
+      " --partition_name boot"
+      " --partition_size %zd"
+      " --kernel_cmdline 'cmdline in hash footer $(ANDROID_SYSTEM_PARTUUID)'"
+      " --salt deadbeef"
+      " --internal_release_string \"\"",
+      boot_path.value().c_str(),
+      boot_partition_size);
+
+  GenerateVBMetaImage(
+      "vbmeta_a.img",
+      "SHA256_RSA2048",
+      4,
+      base::FilePath("test/data/testkey_rsa2048.pem"),
+      base::StringPrintf(
+          "--include_descriptors_from_image %s"
+          " --kernel_cmdline 'cmdline in vbmeta $(ANDROID_BOOT_PARTUUID)'"
+          " --internal_release_string \"\"",
+          boot_path.value().c_str()));
+
+  EXPECT_COMMAND(0,
+                 "./avbtool erase_footer"
+                 " --image %s",
+                 boot_path.value().c_str());
+
+  ops_.set_expected_public_key(
+      PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+  ops_.enable_get_preloaded_partition();
+  EXPECT_TRUE(ops_.preload_preallocated_partition(
+      "boot_a", fake_preload_buf, fake_preload_image_size));
+
+  AvbSlotVerifyData* slot_data = NULL;
+  const char* requested_partitions[] = {"boot", NULL};
+  EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_IO,
+            avb_slot_verify(ops_.avb_ops(),
+                            requested_partitions,
+                            "_a",
+                            AVB_SLOT_VERIFY_FLAGS_NONE,
+                            AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
+                            &slot_data));
+  EXPECT_EQ(nullptr, slot_data);
+}
+
 TEST_F(AvbSlotVerifyTest, HashDescriptorInVBMetaCorruptBoot) {
   size_t boot_partition_size = 16 * 1024 * 1024;
   base::FilePath boot_path = GenerateImage("boot_a.img", 5 * 1024 * 1024);
diff --git a/test/avbtool_signing_helper_test.py b/test/avbtool_signing_helper_test.py
index c9bb660..aa03fcc 100755
--- a/test/avbtool_signing_helper_test.py
+++ b/test/avbtool_signing_helper_test.py
@@ -1,7 +1,7 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 #
-# Copyright (C) 2016 The Android Open Source Project
+# Copyright (C) 2016-2020 The Android Open Source Project
 #
 # Permission is hereby granted, free of charge, to any person
 # obtaining a copy of this software and associated documentation
@@ -29,35 +29,38 @@
 # to catch mistakes where the standard C library is inadvertently
 # used.
 
-import subprocess
-import sys
 import errno
 import os
+import subprocess
+import sys
+
 
 def rsa_signer(argv):
   if len(argv) != 3:
-    sys.stderr.write("Wrong number of arguments: {} <alg> <pub key>\n".format(argv[0]))
+    sys.stderr.write('Wrong number of arguments: {} <alg> <pub key>\n'
+                     .format(argv[0]))
     return errno.EINVAL
 
-  data = sys.stdin.read()
-  if len(data) == 0:
-    sys.stderr.write("There is not input data\n")
+  data = sys.stdin.buffer.read()
+  if not data:
+    sys.stderr.write('There is not input data\n')
     return errno.EINVAL
 
   if os.environ.get('SIGNING_HELPER_GENERATE_WRONG_SIGNATURE'):
     # We're only called with this algorithm which signature size is 256.
     assert sys.argv[1] == 'SHA256_RSA2048'
-    sys.stdout.write('X'*256)
+    sys.stdout.buffer.write(b'X' * 256)
     return 0
 
-  if 'SIGNING_HELPER_TEST' not in os.environ or os.environ['SIGNING_HELPER_TEST'] == "":
-    sys.stderr.write("env SIGNING_HELPER_TEST is not set or empty\n")
+  if not os.getenv('SIGNING_HELPER_TEST'):
+    sys.stderr.write('env SIGNING_HELPER_TEST is not set or empty\n')
     return errno.EINVAL
 
   test_file_name = os.environ['SIGNING_HELPER_TEST']
   if os.path.isfile(test_file_name) and not os.access(test_file_name, os.W_OK):
-    sys.stderr.write("no permission to write into {} file\n".format(test_file_name))
-    return errno.EACCESS
+    sys.stderr.write('no permission to write into {} file\n'
+                     .format(test_file_name))
+    return errno.EACCES
 
   p = subprocess.Popen(
       ['openssl', 'rsautl', '-sign', '-inkey', argv[2], '-raw'],
@@ -68,10 +71,10 @@
   if retcode != 0:
     return retcode
 
-  with open(test_file_name, "w") as f:
-    f.write("DONE")
+  with open(test_file_name, 'w') as f:
+    f.write('DONE')
 
   return 0
 
 if __name__ == '__main__':
-    sys.exit(rsa_signer(sys.argv))
+  sys.exit(rsa_signer(sys.argv))
diff --git a/test/avbtool_signing_helper_with_files_test.py b/test/avbtool_signing_helper_with_files_test.py
index 9811225..2be3e97 100755
--- a/test/avbtool_signing_helper_with_files_test.py
+++ b/test/avbtool_signing_helper_with_files_test.py
@@ -1,7 +1,7 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 #
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright (C) 2017-2020 The Android Open Source Project
 #
 # Permission is hereby granted, free of charge, to any person
 # obtaining a copy of this software and associated documentation
@@ -24,37 +24,40 @@
 # SOFTWARE.
 #
 
-import subprocess
-import sys
 import errno
 import os
+import subprocess
+import sys
+
 
 def rsa_signer_with_files(argv):
   if len(argv) != 4:
-    sys.stderr.write("Wrong number of arguments: {} <alg> <pub key> <file>\n".format(argv[0]))
+    sys.stderr.write('Wrong number of arguments: {} <alg> <pub key> <file>\n'
+                     .format(argv[0]))
     return errno.EINVAL
 
-  signing_file = open(argv[3], mode='rw+')
+  signing_file = open(argv[3], mode='rb+')
   data = signing_file.read()
-  if len(data) == 0:
-    sys.stderr.write("There is no input data\n")
+  if not data:
+    sys.stderr.write('There is no input data\n')
     return errno.EINVAL
 
   if os.environ.get('SIGNING_HELPER_GENERATE_WRONG_SIGNATURE'):
     # We're only called with this algorithm which signature size is 256.
     assert argv[1] == 'SHA256_RSA2048'
     signing_file.seek(0)
-    signing_file.write('X'*256)
+    signing_file.write(b'X' * 256)
     return 0
 
-  if 'SIGNING_HELPER_TEST' not in os.environ or os.environ['SIGNING_HELPER_TEST'] == "":
-    sys.stderr.write("env SIGNING_HELPER_TEST is not set or empty\n")
+  if not os.getenv('SIGNING_HELPER_TEST'):
+    sys.stderr.write('env SIGNING_HELPER_TEST is not set or empty\n')
     return errno.EINVAL
 
   test_file_name = os.environ['SIGNING_HELPER_TEST']
   if os.path.isfile(test_file_name) and not os.access(test_file_name, os.W_OK):
-    sys.stderr.write("no permission to write into {} file\n".format(test_file_name))
-    return errno.EACCESS
+    sys.stderr.write('no permission to write into {} file\n'
+                     .format(test_file_name))
+    return errno.EACCES
 
   p = subprocess.Popen(
       ['openssl', 'rsautl', '-sign', '-inkey', argv[2], '-raw'],
@@ -68,8 +71,8 @@
   signing_file.seek(0)
   signing_file.write(pout)
 
-  with open(test_file_name, "w") as f:
-    f.write("DONE")
+  with open(test_file_name, 'w') as f:
+    f.write('DONE')
 
   return 0
 
diff --git a/test/corpus/icp.bin b/test/corpus/icp.bin
new file mode 100644
index 0000000..19125fe
--- /dev/null
+++ b/test/corpus/icp.bin
Binary files differ
diff --git a/test/data/aftl_image.bin b/test/data/aftl_image.bin
deleted file mode 100644
index 1e6e65a..0000000
--- a/test/data/aftl_image.bin
+++ /dev/null
Binary files differ
diff --git a/test/data/aftl_image_multi.bin b/test/data/aftl_image_multi.bin
deleted file mode 100644
index 9a16421..0000000
--- a/test/data/aftl_image_multi.bin
+++ /dev/null
Binary files differ
diff --git a/test/data/aftltool/aftl_input_vbmeta.img b/test/data/aftl_input_vbmeta.img
similarity index 100%
rename from test/data/aftltool/aftl_input_vbmeta.img
rename to test/data/aftl_input_vbmeta.img
Binary files differ
diff --git a/test/data/aftl_log_key_bytes.bin b/test/data/aftl_log_key_bytes.bin
deleted file mode 100644
index a4748d4..0000000
--- a/test/data/aftl_log_key_bytes.bin
+++ /dev/null
Binary files differ
diff --git a/test/data/aftl_log_sig.bin b/test/data/aftl_log_sig.bin
deleted file mode 100644
index 892040e..0000000
--- a/test/data/aftl_log_sig.bin
+++ /dev/null
Binary files differ
diff --git a/test/data/aftltool/aftl_output_vbmeta_with_1_icp.img b/test/data/aftl_output_vbmeta_with_1_icp.img
similarity index 67%
rename from test/data/aftltool/aftl_output_vbmeta_with_1_icp.img
rename to test/data/aftl_output_vbmeta_with_1_icp.img
index 25e0869..74b67bb 100644
--- a/test/data/aftltool/aftl_output_vbmeta_with_1_icp.img
+++ b/test/data/aftl_output_vbmeta_with_1_icp.img
Binary files differ
diff --git a/test/data/aftl_output_vbmeta_with_2_icp_same_log.img b/test/data/aftl_output_vbmeta_with_2_icp_same_log.img
new file mode 100644
index 0000000..2a6368e
--- /dev/null
+++ b/test/data/aftl_output_vbmeta_with_2_icp_same_log.img
Binary files differ
diff --git a/test/data/aftl_pubkey_1.bin b/test/data/aftl_pubkey_1.bin
new file mode 100644
index 0000000..5bad4be
--- /dev/null
+++ b/test/data/aftl_pubkey_1.bin
Binary files differ
diff --git a/test/data/aftltool/aftl_pubkey_1.pub b/test/data/aftl_pubkey_1.pem
similarity index 100%
rename from test/data/aftltool/aftl_pubkey_1.pub
rename to test/data/aftl_pubkey_1.pem
diff --git a/test/data/aftl_verify_full.img b/test/data/aftl_verify_full.img
deleted file mode 100644
index 6c2212e..0000000
--- a/test/data/aftl_verify_full.img
+++ /dev/null
Binary files differ
diff --git a/test/data/aftl_verify_vbmeta.bin b/test/data/aftl_verify_vbmeta.bin
deleted file mode 100644
index bde01e3..0000000
--- a/test/data/aftl_verify_vbmeta.bin
+++ /dev/null
Binary files differ
diff --git a/test/data/aftltool/aftl_output_vbmeta_with_2_icp_different_logs.img b/test/data/aftltool/aftl_output_vbmeta_with_2_icp_different_logs.img
deleted file mode 100644
index ad36580..0000000
--- a/test/data/aftltool/aftl_output_vbmeta_with_2_icp_different_logs.img
+++ /dev/null
Binary files differ
diff --git a/test/data/aftltool/aftl_output_vbmeta_with_2_icp_same_log.img b/test/data/aftltool/aftl_output_vbmeta_with_2_icp_same_log.img
deleted file mode 100644
index e53178c..0000000
--- a/test/data/aftltool/aftl_output_vbmeta_with_2_icp_same_log.img
+++ /dev/null
Binary files differ
diff --git a/test/data/aftltool/aftl_pubkey_2.pub b/test/data/aftltool/aftl_pubkey_2.pub
deleted file mode 100644
index 470fefa..0000000
--- a/test/data/aftltool/aftl_pubkey_2.pub
+++ /dev/null
@@ -1,14 +0,0 @@
------BEGIN PUBLIC KEY-----
-MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAylNLlTCf4FzlYDZIOWGf
-YQw3CusPo4bbcExfXP+0A5G94LXz02haTzlxHi3rIfiCqcUiJH1YrPWUFsD4dju+
-MKmb0lAPYSMjA5VMO3XINn5z0C+CUMq0/6rIw+n4iH/bfC3wqh0C/qqxDWM+pm5f
-duC0sk30jQ9+SMQqo/kxNY6Tzv3C1rLxDZ5lxsZ4+8IbAj7gtQFoibgwa9w2hbkZ
-dmXbyF/G8wjFPvQREC5VQik4RVPKnAB9r8ZDR9D3Myjfs/84KgMnpK7YVvsDqEcO
-4jB79t3AAVU/d6vl3hCGjTCXDZxaBzbB0PGJzDYX9cWn0tclu1WlpC7YY2YLZ8ai
-EWkMp7dXJxFGtQ7gn/RuULE+Li/H8jJkwQzVu2tb9geGDodXwNJYba4xs0CgLDjr
-L5LuiFsKhh+GBTAE/6JbQkgb97tI3ClmqhBNjvPki9sKMVL6hcUTzPHj4zxKTY8V
-yB+/1WEETcVyq1SDy0VrWPDgxXbvqkeMcMxr/8JfL6alsTCClPSlisLlhkXGrp+t
-FlkthdsD4M8BtccQHRuru3fAL8Kk5XOE7qeOcs0cDgzzmkZY1Pg4g4TpL3yiBvfi
-a3wHB2u7gF1XdLeMPNJa3v5pMwxHZzokQ8q01pC+gf/wNldY7oGWnTi5JHu/EyFV
-vWW/HX+Hd7cWppyAQFRqWwsCAwEAAQ==
------END PUBLIC KEY-----
diff --git a/test/data/aftl_key_bytes.bin b/test/data/testkey_rsa4096_pub.bin
similarity index 100%
rename from test/data/aftl_key_bytes.bin
rename to test/data/testkey_rsa4096_pub.bin
Binary files differ
diff --git a/test/data/testkey_rsa4096_pub.pem b/test/data/testkey_rsa4096_pub.pem
new file mode 100644
index 0000000..efd7144
--- /dev/null
+++ b/test/data/testkey_rsa4096_pub.pem
@@ -0,0 +1,14 @@
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2ASv49OEbH4NiT3CjNMS
+VeliyfEPXswWcqtEfCxlSpS1FisAuwbvEwdTTPlkuSh6G4SYiNhnpCP5p0vcSg/3
+OhiuVKgV/rCtrDXaO60nvK/o0y83NNZRK2xaJ9eWBq9ruIDK+jC0sYWzTaqqwxY0
+Grjnx/r5CXerl5PrRK7PILzwgBHbIwxHcblt1ntgR4cWVpO3wiqasEwBDDDYk4fw
+7W6LvjBb9qav3YB8RV6PkZNeRP64ggfuecq/MXNiWOPNxLzCER2hSr/+J32h9jWj
+XsrcVy8+8Mldhmr4r2an7c247aFfupuFGtUJrpROO8/LXMl5gPfMpkqoatjTMRH5
+9gJjKhot0RpmGxZBvb33TcBK5SdJX39Y4yct5clmDlI4Fjj7FutTP+b96aJeJVnY
+eUX/A0wmogBajsJRoRX5e/RcgZsYRzXYLQXprQ81dBWjjovMJ9p8XeT6BNMFC7o6
+sklFL0fHDUE/l4BNP8G1u3BfpzevSCISRS71D4eS4oQB+RIPFBUkzomZ7rnEF3Bw
+Feq+xmwfYrP0LRaH+1YeRauuMuReke1TZl697a3mEjkNg8noa2wtpe7EWmaujJfX
+DWxJx/XEkjGLCe4z2qk3tkkY+A5gRcgzke8gVxC+eC2DJtbKYfkv4L8FMFJaEhwA
+p13MfC7FlYujO/BDLl7dANsCAwEAAQ==
+-----END PUBLIC KEY-----
diff --git a/test/fake_avb_ops.cc b/test/fake_avb_ops.cc
index 6e8ab8c..0f4db5a 100644
--- a/test/fake_avb_ops.cc
+++ b/test/fake_avb_ops.cc
@@ -87,6 +87,18 @@
   return true;
 }
 
+bool FakeAvbOps::preload_preallocated_partition(const std::string& partition,
+                                                uint8_t* buffer,
+                                                size_t size) {
+  if (preallocated_preloaded_partitions_.count(partition) > 0) {
+    fprintf(stderr, "Partition '%s' already preloaded\n", partition.c_str());
+    return false;
+  }
+
+  preallocated_preloaded_partitions_[partition] = std::make_pair(buffer, size);
+  return true;
+}
+
 AvbIOResult FakeAvbOps::read_from_partition(const char* partition,
                                             int64_t offset,
                                             size_t num_bytes,
@@ -160,6 +172,15 @@
   if (hidden_partitions_.find(partition) != hidden_partitions_.end()) {
     return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
   }
+
+  std::map<std::string, std::pair<uint8_t*, size_t>>::iterator prealloc_it =
+      preallocated_preloaded_partitions_.find(std::string(partition));
+  if (prealloc_it != preallocated_preloaded_partitions_.end()) {
+    *out_pointer = prealloc_it->second.first;
+    *out_num_bytes_preloaded = std::min(prealloc_it->second.second, num_bytes);
+    return AVB_IO_RESULT_OK;
+  }
+
   std::map<std::string, uint8_t*>::iterator it =
       preloaded_partitions_.find(std::string(partition));
   if (it == preloaded_partitions_.end()) {
@@ -173,11 +194,8 @@
   if (result != AVB_IO_RESULT_OK) {
     return result;
   }
-  if (size != num_bytes) {
-    return AVB_IO_RESULT_ERROR_IO;
-  }
 
-  *out_num_bytes_preloaded = num_bytes;
+  *out_num_bytes_preloaded = std::min(static_cast<size_t>(size), num_bytes);
   *out_pointer = it->second;
   return AVB_IO_RESULT_OK;
 }
diff --git a/test/fake_avb_ops.h b/test/fake_avb_ops.h
index 2cc12d8..5dea5bd 100644
--- a/test/fake_avb_ops.h
+++ b/test/fake_avb_ops.h
@@ -211,6 +211,10 @@
   bool preload_partition(const std::string& partition,
                          const base::FilePath& path);
 
+  bool preload_preallocated_partition(const std::string& partition,
+                                      uint8_t* buffer,
+                                      size_t size);
+
   // Gets the partition names that were passed to the
   // read_from_partition() operation.
   std::set<std::string> get_partition_names_read_from();
@@ -315,6 +319,8 @@
 
   std::set<std::string> partition_names_read_from_;
   std::map<std::string, uint8_t*> preloaded_partitions_;
+  std::map<std::string, std::pair<uint8_t*, size_t>>
+      preallocated_preloaded_partitions_;
   std::set<std::string> hidden_partitions_;
 
   std::map<std::string, std::string> stored_values_;