Snap for 8426163 from 66872e8dc9f4b0aa5fa1eca970430be003003a7b to mainline-tzdata2-release

Change-Id: I472505464f41f3fa0a2320881a0ce859bf2e687a
diff --git a/Android.bp b/Android.bp
index e61abe9..dfb1cc1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2017-2020 The Android Open Source Project
+// Copyright (C) 2017 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,41 +14,7 @@
 // limitations under the License.
 //
 
-package {
-    default_applicable_licenses: ["external_avb_license"],
-}
-
-// Added automatically by a large-scale-change that took the approach of
-// 'apply every license found to every target'. While this makes sure we respect
-// every license restriction, it may not be entirely correct.
-//
-// e.g. GPL in an MIT project might only apply to the contrib/ directory.
-//
-// Please consider splitting the single license below into multiple licenses,
-// taking care not to lose any license_kind information, and overriding the
-// default license using the 'licenses: [...]' property on targets as needed.
-//
-// For unused files, consider creating a 'fileGroup' with "//visibility:private"
-// to attach the license to, and including a comment whether the files may be
-// used in the current project.
-// See: http://go/android-license-faq
-license {
-    name: "external_avb_license",
-    visibility: [":__subpackages__"],
-    license_kinds: [
-        "SPDX-license-identifier-Apache-2.0",
-        "SPDX-license-identifier-BSD",
-        "SPDX-license-identifier-MIT",
-    ],
-    license_text: [
-        "LICENSE",
-    ],
-}
-
-subdirs = [
-    "test",
-    "tools",
-]
+subdirs = ["test", "tools"]
 
 cc_defaults {
     name: "avb_defaults",
@@ -99,6 +65,8 @@
         "libavb/avb_kernel_cmdline_descriptor.c",
         "libavb/avb_property_descriptor.c",
         "libavb/avb_rsa.c",
+        "libavb/avb_sha256.c",
+        "libavb/avb_sha512.c",
         "libavb/avb_slot_verify.c",
         "libavb/avb_util.c",
         "libavb/avb_vbmeta_image.c",
@@ -106,70 +74,42 @@
     ],
 }
 
-cc_defaults {
-    name: "avb_crypto_ops_impl_boringssl",
-    srcs: [
-        "libavb/boringssl/sha.c",
-    ],
-    local_include_dirs: [
-        "libavb/boringssl",
-    ],
-    shared_libs: [
-        "libcrypto",
-    ],
-}
-
-cc_defaults {
-    name: "avb_crypto_ops_impl_sha",
-    srcs: [
-        "libavb/sha/sha256_impl.c",
-        "libavb/sha/sha512_impl.c",
-    ],
-    local_include_dirs: [
-        "libavb/sha",
-    ],
-}
-
 python_binary_host {
     name: "avbtool",
-    srcs: ["avbtool.py"],
+    srcs: [":avbtool_py"],
     main: "avbtool.py",
     required: ["fec"],
     version: {
         py2: {
-            enabled: false,
-        },
-        py3: {
             enabled: true,
             embedded_launcher: true,
         },
+        py3: {
+            enabled: false,
+        },
     },
-    compile_multilib: "first",
+}
+
+genrule {
+  name: "aftltool_py",
+  srcs: ["aftltool",],
+  out: ["aftltool.py"],
+  cmd: "cp $(in) $(out)",
 }
 
 python_library_host {
     name: "aftl_proto",
     srcs: [
-        "proto/api.proto",
-        "proto/crypto/sigpb/sigpb.proto",
-        "proto/crypto/keyspb/keyspb.proto",
-        "proto/trillian.proto",
+        "proto/**/*.py",
     ],
-    proto: {
-        include_dirs: [
-            "external/protobuf/src",
-        ],
-        local_include_dirs: [
-            "proto",
-        ],
-        canonical_path_from_root: false,
-    },
     version: {
         py2: {
-            enabled: false,
+            enabled: true,
+            // This is needs to be false due to b/146057182#comment5.
+            embedded_launcher: false,
         },
         py3: {
-            enabled: true,
+            enabled: false,
         },
     },
 }
@@ -177,19 +117,21 @@
 python_binary_host {
     name: "aftltool",
     srcs: [
-        "aftltool.py",
-        "avbtool.py",
+        ":aftltool_py",
+        ":avbtool_py",
     ],
     libs: [
         "aftl_proto",
     ],
     main: "aftltool.py",
+    required: ["fec"],
     version: {
         py2: {
-            enabled: false,
+            enabled: true,
+            embedded_launcher: false,
         },
         py3: {
-            enabled: true,
+            enabled: false,
         },
     },
 }
@@ -198,9 +140,9 @@
     name: "aftltool_test",
     main: "aftltool_test.py",
     srcs: [
-        "aftltool.py",
+        ":aftltool_py",
+        ":avbtool_py",
         "aftltool_test.py",
-        "avbtool.py",
     ],
     libs: [
         "aftl_proto",
@@ -208,19 +150,26 @@
     data: [
         "test/data/**/*.*",
     ],
-    test_options: {
-        unit_test: true,
-    },
+    test_suites: ["general-tests"],
     version: {
         py2: {
-            enabled: false,
+            enabled: true,
+            // This is needs to be false due to b/146057182#comment5.
+            embedded_launcher: false,
         },
         py3: {
-            enabled: true,
+            enabled: false,
         },
     },
 }
 
+genrule {
+  name: "avbtool_py",
+  srcs: ["avbtool",],
+  out: ["avbtool.py"],
+  cmd: "cp $(in) $(out)",
+}
+
 // Build libavb - this is a static library that depends
 // on only libc and doesn't drag in any other dependencies.
 cc_library_static {
@@ -228,7 +177,6 @@
     defaults: [
         "avb_defaults",
         "avb_sources",
-        "avb_crypto_ops_impl_boringssl",
     ],
     host_supported: true,
     recovery_available: true,
@@ -254,7 +202,6 @@
     defaults: [
         "avb_defaults",
         "avb_sources",
-        "avb_crypto_ops_impl_boringssl",
     ],
     recovery_available: true,
     header_libs: [
@@ -282,10 +229,7 @@
         "libavb_user",
         "libfs_mgr",
     ],
-    shared_libs: [
-        "libbase",
-        "libcrypto",
-    ],
+    shared_libs: ["libbase"],
     srcs: ["tools/avbctl/avbctl.cc"],
 }
 
@@ -303,60 +247,26 @@
     srcs: ["libavb_ab/avb_ab_flow.c"],
 }
 
-cc_library_static {
-    name: "libavb_aftl",
+cc_library_host_static {
+    name: "libavb_aftl_host",
     defaults: [
         "avb_defaults",
-        "avb_sources",
-        "avb_crypto_ops_impl_boringssl",
-    ],
-    host_supported: true,
-    recovery_available: true,
+        "avb_sources"],
     header_libs: ["avb_headers"],
     export_header_lib_headers: ["avb_headers"],
     cflags: [
         "-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",
-    ],
-    shared_libs: ["libcrypto"],
-    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",
-        "avb_crypto_ops_impl_boringssl",
-    ],
+    defaults: ["avb_defaults"],
     header_libs: [
         "avb_headers",
     ],
@@ -379,10 +289,7 @@
 
 cc_library_host_static {
     name: "libavb_things_example",
-    defaults: [
-        "avb_defaults",
-        "avb_crypto_ops_impl_boringssl",
-    ],
+    defaults: ["avb_defaults"],
     header_libs: [
         "avb_headers",
     ],
@@ -390,22 +297,17 @@
     srcs: ["examples/things/avb_atx_slot_verify.c"],
 }
 
-cc_defaults {
-    name: "libavb_host_unittest_core",
+cc_test_host {
+    name: "libavb_host_unittest",
     defaults: ["avb_defaults"],
     required: [
         "simg2img",
         "img2simg",
         "avbtool",
     ],
-    test_options: {
-        unit_test: true,
-    },
-    compile_multilib: "first",
     data: [
         "avbtool",
-        "test/avbtool_signing_helper_test.py",
-        "test/avbtool_signing_helper_with_files_test.py",
+        "test/avbtool_signing_helper_*.py",
         "test/data/*",
     ],
     test_config: "test/libavb_host_unittest.xml",
@@ -413,7 +315,7 @@
     static_libs: [
         "libavb",
         "libavb_ab_host",
-        "libavb_aftl",
+        "libavb_aftl_host",
         "libavb_atx_host",
         "libavb_things_example",
         "libgmock_host",
@@ -425,8 +327,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",
@@ -435,7 +337,6 @@
         "test/avb_aftl_verify_unittest.cc",
         "test/avb_atx_validate_unittest.cc",
         "test/avb_atx_slot_verify_unittest.cc",
-        "test/avb_crypto_ops_unittest.cc",
         "test/avb_slot_verify_unittest.cc",
         "test/avb_unittest_util.cc",
         "test/avb_util_unittest.cc",
@@ -446,32 +347,6 @@
     ],
 }
 
-cc_test_host {
-    name: "libavb_host_unittest",
-    defaults: [
-        "avb_crypto_ops_impl_boringssl",
-        "libavb_host_unittest_core",
-    ],
-    data: [
-        ":img2simg",
-        ":simg2img",
-        ":fec",
-    ],
-}
-
-cc_test_host {
-    name: "libavb_host_unittest_sha",
-    defaults: [
-        "avb_crypto_ops_impl_sha",
-        "libavb_host_unittest_core",
-    ],
-    data: [
-        ":img2simg",
-        ":simg2img",
-        ":fec",
-    ],
-}
-
 cc_library_host_static {
     name: "libavb_host_user_code_test",
     defaults: ["avb_defaults"],
@@ -491,7 +366,6 @@
     ],
     shared_libs: [
         "libbase",
-        "libcrypto",
         "libcutils",
     ],
     cflags: [
diff --git a/LICENSE b/LICENSE
index 7d1e37b..d21621a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,4 @@
-Code in this repo uses one or more of the following licenses.
-Refer to each file to determine which licenses apply.
-
--------------------------------------------------------------------------
+Copyright 2016, 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
@@ -21,43 +18,3 @@
 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.
-
--------------------------------------------------------------------------
-
-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.
-
--------------------------------------------------------------------------
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the project nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
diff --git a/METADATA b/METADATA
deleted file mode 100644
index d97975c..0000000
--- a/METADATA
+++ /dev/null
@@ -1,3 +0,0 @@
-third_party {
-  license_type: NOTICE
-}
diff --git a/MODULE_LICENSE_BSD b/MODULE_LICENSE_BSD
deleted file mode 100644
index e69de29..0000000
--- a/MODULE_LICENSE_BSD
+++ /dev/null
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..d21621a
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,20 @@
+Copyright 2016, 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.
diff --git a/OWNERS b/OWNERS
index 114cf11..0883d94 100644
--- a/OWNERS
+++ b/OWNERS
@@ -2,5 +2,5 @@
 samitolvanen@google.com
 zeuthen@google.com
 dkrahn@google.com
+danielaustin@google.com
 jpm@google.com
-tweek@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 0e19284..5789f69 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,5 +1,4 @@
 [Builtin Hooks]
-bpfmt = true
 clang_format = true
 pylint = true
 
diff --git a/README.md b/README.md
index de2f014..b01acee 100644
--- a/README.md
+++ b/README.md
@@ -209,11 +209,6 @@
 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
@@ -257,6 +252,10 @@
       boot loaders using the experimental `libavb_ab` A/B stack.
       **NOTE**: This code is *DEPRECATED* and will be removed Jun 1
       2018.
+* `contrib/`
+    + Contains patches needed in other projects for interoperability with AVB.
+      For example, `contrib/linux/4.4` has the patches for Linux kernel 4.4,
+      which are generated by `git format-patch`.
 * `Android.bp`
     + Build instructions for building `libavb` (a static library for use
       on the device), host-side libraries (for unit tests), and unit
@@ -373,8 +372,7 @@
     $ avbtool make_vbmeta_image                                                    \
         [--output OUTPUT]                                                          \
         [--algorithm ALGORITHM] [--key /path/to/key_used_for_signing_or_pub_key]   \
-        [--public_key_metadata /path/to/pkmd.bin]                                  \
-        [--rollback_index NUMBER] [--rollback_index_location NUMBER]               \
+        [--public_key_metadata /path/to/pkmd.bin] [--rollback_index NUMBER]        \
         [--include_descriptors_from_image /path/to/image.bin]                      \
         [--setup_rootfs_from_kernel /path/to/image.bin]                            \
         [--chain_partition part_name:rollback_index_location:/path/to/key1.bin]    \
@@ -390,8 +388,7 @@
         --partition_name PARTNAME --partition_size SIZE                            \
         [--image IMAGE]                                                            \
         [--algorithm ALGORITHM] [--key /path/to/key_used_for_signing_or_pub_key]   \
-        [--public_key_metadata /path/to/pkmd.bin]                                  \
-        [--rollback_index NUMBER] [--rollback_index_location NUMBER]               \
+        [--public_key_metadata /path/to/pkmd.bin] [--rollback_index NUMBER]        \
         [--hash_algorithm HASH_ALG] [--salt HEX]                                   \
         [--include_descriptors_from_image /path/to/image.bin]                      \
         [--setup_rootfs_from_kernel /path/to/image.bin]                            \
@@ -404,8 +401,6 @@
         [--do_not_use_ab]                                                          \
         [--use_persistent_digest]
 
-Valid values for `HASH_ALG` above include `sha1` and `sha256`.
-
 An integrity footer containing the root digest and salt for a hashtree
 for a partition can be added to an existing image as follows. The
 hashtree is also appended to the image.
@@ -414,8 +409,7 @@
         --partition_name PARTNAME --partition_size SIZE                            \
         [--image IMAGE]                                                            \
         [--algorithm ALGORITHM] [--key /path/to/key_used_for_signing_or_pub_key]   \
-        [--public_key_metadata /path/to/pkmd.bin]                                  \
-        [--rollback_index NUMBER] [--rollback_index_location NUMBER]               \
+        [--public_key_metadata /path/to/pkmd.bin] [--rollback_index NUMBER]        \
         [--hash_algorithm HASH_ALG] [--salt HEX] [--block_size SIZE]               \
         [--include_descriptors_from_image /path/to/image.bin]                      \
         [--setup_rootfs_from_kernel /path/to/image.bin]                            \
@@ -431,8 +425,6 @@
         [--no_hashtree]                                                            \
         [--use_persistent_digest]
 
-Valid values for `HASH_ALG` above include `sha1`, `sha256`, and `blake2b-256`.
-
 The size of an image with integrity footers can be changed using the
 `resize_image` command:
 
@@ -529,10 +521,6 @@
         --vbmeta_image vbmeta.img
     $ fastboot flash boot boot-with-vbmeta-appended.img
 
-Information about an image can be obtained using the `info_image` command. The
-output of this command should not be relied on and the way information is
-structured may change.
-
 The `verify_image` command can be used to verify the contents of
 several image files at the same time. When invoked on an image the
 following checks are performed:
@@ -603,31 +591,6 @@
 structs have been loaded, the digest is calculated (using the hash algorithm
 given by the `--hash_algorithm` option) and printed out.
 
-To print hash and hashtree digests embedded in the verified metadata, use the
-`print_partition_digests` command like this:
-
-    $ avbtool print_partition_digests --image /path/to/vbmeta.img
-    system: ddaa513715fd2e22f3c1cea3c1a1f98ccb515fc6
-    boot: 5cba9a418e04b5f9e29ee6a250f6cdbe30c6cec867c59d388f141c3fedcb28c1
-    vendor: 06993a9e85e46e53d3892881bb75eff48ecadaa8
-
-For partitions with hash descriptors, this prints out the digest and for
-partitions with hashtree descriptors the root digest is printed out. Like the
-`calculate_vbmeta_digest` and `verify_image` commands, chain partitions are
-followed. To use JSON for the output, use the `--json` option.
-
-In case you would like to log all command lines for all avbtool invocations for
-debugging integrations with other tooling, you can configure the envirionment
-variable AVB_INVOCATION_LOGFILE with the name of the log file:
-
-    $ export AVB_INVOCATION_LOGFILE='/tmp/avb_invocation.log'
-    $ ./avbtool version
-    $ ./avbtool version
-    $ cat /tmp/avb_invocation.log
-    ./avbtool version
-    ./avbtool version
-
-
 ## Build System Integration
 
 In Android, AVB is enabled by the `BOARD_AVB_ENABLE` variable
@@ -667,21 +630,21 @@
 [chained partitions](#The-VBMeta-struct) in order to update a subset of
 partitions without changing the top-level `vbmeta` partition. For example,
 the following variables create `vbmeta_system.img` as a chained `vbmeta`
-image that contains the hash-tree descriptors for `system.img`, `system_ext.img`
-and `product.img`. `vbmeta_system.img` itself will be signed by the specified
+image that contains the hash-tree descriptors for `system.img` and
+`system_ext.img`. `vbmeta_system.img` itself will be signed by the specified
 key and algorithm.
 
-    BOARD_AVB_VBMETA_SYSTEM := system system_ext product
+    BOARD_AVB_VBMETA_SYSTEM := system system_ext
     BOARD_AVB_VBMETA_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
     BOARD_AVB_VBMETA_SYSTEM_ALGORITHM := SHA256_RSA2048
     BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION := 1
 
-Note that the hash-tree descriptors for `system.img`, `system_ext.img` and
-`product.img` will be included only in `vbmeta_system.img`, but not
-`vbmeta.img`. With the above setup, partitions `system.img`, `system_ext.img`,
-`product.img` and `vbmeta_system.img` can be updated independently - but as a
-group - of the rest of the partitions, *or* as part of the traditional updates
-that update all the partitions.
+Note that the hash-tree descriptors for `system.img` and `system_ext.img`
+will be included only in `vbmeta_system.img`, but not `vbmeta.img`. With
+the above setup, partitions `system.img`, `system_ext.img` and
+`vbmeta_system.img` can be updated independently - but as a group - of the
+rest of the partitions, *or* as part of the traditional updates that
+update all the partitions.
 
 Currently build system supports building chained `vbmeta` images of
 `vbmeta_system.img` (`BOARD_AVB_VBMETA_SYSTEM`) and `vbmeta_vendor.img`
@@ -1014,11 +977,6 @@
    be used for **ONLY** diagnostics and debugging. It cannot be used
    unless verification errors are allowed.
 
-* `AVB_HASHTREE_ERROR_MODE_PANIC` means that the OS will **panic** without
-  the current slot being invalidated. Be careful using this mode as it may
-  introduce boot panic if the same hashtree verification error is hit on
-  every boot. This mode is available since: 1.7.0 (kernel 5.9)
-
 The value passed in `hashtree_error_mode` is essentially just passed on through
 to the HLOS through the the `androidboot.veritymode`,
 `androidboot.veritymode.managed`, and `androidboot.vbmeta.invalidate_on_error`
@@ -1031,7 +989,6 @@
 | `AVB_HASHTREE_ERROR_MODE_EIO` | **eio** | (unset) | (unset) |
 | `AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO` | **eio** or **enforcing** | **yes** | (unset) |
 | `AVB_HASHTREE_ERROR_MODE_LOGGING` | **ignore_corruption** | (unset) | (unset) |
-| `AVB_HASHTREE_ERROR_MODE_PANIC` | **panicking** | (unset) | (unset) |
 
 The only exception to this table is that if the
 `AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED` flag is set in the top-level vbmeta,
@@ -1039,26 +996,6 @@
 `androidboot.veritymode.managed` and `androidboot.vbmeta.invalidate_on_error`
 are unset.
 
-The different values of `hashtree_error_mode` parameter in the `avb_slot_verify()`
-function can be categorized into three groups:
-
-* `AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE`, which needs `CONFIG_DM_VERITY_AVB`
-  in the kernel config for the kernel to invalidate the current slot and
-  restart. This is kept here for legacy Android Things devices and is not
-  recommended for other device form factors.
-
-* The bootloader handles the switch between `AVB_HASHTREE_ERROR_MODE_RESTART`
-  and `AVB_HASHTREE_ERROR_MODE_EIO`. This would need a persistent storage on the
-  device to store the vbmeta digest, so the bootloader can detect if a device
-  ever gets an update or not. Once the new OS is installed and if the device is
-  in **EIO** mode, the bootloader should switch back to **RESTART** mode.
-
-* `AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO`: `libavb` helps the
-  bootloader manage **EIO**/**RESTART** state transition. The bootloader needs
-  to implement the callbacks of `AvbOps->read_persistent_value()` and
-  `AvbOps->write_persistent_value()` for `libavb` to store the vbmeta digest to
-  detect whether a new OS is installed.
-
 ### Which mode should I use for my device?
 
 This depends entirely on the device, how the device is intended to be
@@ -1086,12 +1023,11 @@
 This section contains information about how AVB is integrated into specific
 devices. This is not an exhaustive list.
 
-### Pixel 2 and later
+### Pixel 2
 
-On the Pixel 2, Pixel 2 XL and later Pixel models, the boot loader supports a
-virtual partition with the name `avb_custom_key`. Flashing and erasing this
-partition only works in the UNLOCKED state. Setting the custom key is done like
-this:
+On the Pixel 2 and Pixel 2 XL the boot loader supports a virtual partition with
+the name `avb_custom_key`. Flashing and erasing this partition only works in the
+UNLOCKED state. Setting the custom key is done like this:
 
     avbtool extract_public_key --key key.pem --output pkmd.bin
     fastboot flash avb_custom_key pkmd.bin
@@ -1110,10 +1046,6 @@
 
 # Version History
 
-### Version 1.2
-
-Version 1.2 adds support for the `rollback_index_location` field of the main vbmeta header.
-
 ### Version 1.1
 
 Version 1.1 adds support for the following:
diff --git a/TEST_MAPPING b/TEST_MAPPING
index b97c31b..b5d800a 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,10 +1,12 @@
 {
-  "presubmit": [
+  "postsubmit": [
     {
-      "name": "libavb_host_unittest"
+      "name": "libavb_host_unittest",
+      "host": true
     },
     {
-      "name": "libavb_host_unittest_sha"
+      "name": "aftltool_test",
+      "host": true
     }
   ]
 }
diff --git a/aftltool b/aftltool
deleted file mode 120000
index 11a8ff9..0000000
--- a/aftltool
+++ /dev/null
@@ -1 +0,0 @@
-aftltool.py
\ No newline at end of file
diff --git a/aftltool.py b/aftltool
similarity index 60%
rename from aftltool.py
rename to aftltool
index 613e83b..2dc7330 100755
--- a/aftltool.py
+++ b/aftltool
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 
 # Copyright 2020, The Android Open Source Project
 #
@@ -24,31 +24,27 @@
 #
 """Command-line tool for AFTL support for Android Verified Boot images."""
 
-import abc
+from __future__ import division
+
 import argparse
-import enum
+import base64
+import binascii
 import hashlib
-import io
+import json
 import multiprocessing
 import os
-import queue
+import Queue  # pylint: disable=bad-python3-import
 import struct
 import subprocess
 import sys
 import tempfile
 import time
 
-# This is to work around temporarily with the issue that python3 does not permit
-# relative imports anymore going forward. This adds the proto directory relative
-# to the location of aftltool to the sys.path.
-# TODO(b/154068467): Implement proper importing of generated *_pb2 modules.
-EXEC_PATH = os.path.dirname(os.path.realpath(__file__))
-sys.path.append(os.path.join(EXEC_PATH, 'proto'))
 
-# pylint: disable=wrong-import-position,import-error
 import avbtool
-import api_pb2
-# pylint: enable=wrong-import-position,import-error
+from proto import aftl_pb2
+from proto import api_pb2
+from proto.crypto import sigpb
 
 
 class AftlError(Exception):
@@ -87,7 +83,7 @@
   retcode = p.wait()
   if retcode != 0:
     raise AftlError('Error decoding: {}'.format(perr))
-  return pout
+  return bytearray(pout)
 
 
 def check_signature(log_root, log_root_sig,
@@ -231,110 +227,119 @@
   return result
 
 
-class AftlImageHeader(object):
-  """A class for representing the AFTL image header.
+class AftlIcpHeader(object):
+  """A class for the transparency log inclusion proof header.
 
   Attributes:
-    magic: Magic for identifying the AftlImage.
+    magic: Magic for identifying the ICP header.
     required_icp_version_major: The major version of AVB that wrote the entry.
     required_icp_version_minor: The minor version of AVB that wrote the entry.
-    aftl_image_size: Total size of the AftlImage.
+    aftl_descriptor_size: Total size of the header's AftlDescriptor.
     icp_count: Number of inclusion proofs represented in this structure.
   """
 
   SIZE = 18  # The size of the structure, in bytes
-  MAGIC = b'AFTL'
-  FORMAT_STRING = ('!4s2L'  # magic, major & minor version.
-                   'L'      # AFTL image size.
-                   'H')     # number of inclusion proof entries.
+  MAGIC = 'AFTL'
+  FORMAT_STRING = ('!4s2L'  # magic, major & minor version
+                   'L'      # descriptor size
+                   'H')     # number of inclusion proof entries
 
   def __init__(self, data=None):
-    """Initializes a new AftlImageHeader object.
+    """Initializes a new transparency header object.
 
     Arguments:
       data: If not None, must be a bytearray of size |SIZE|.
 
     Raises:
-      AftlError: If invalid structure for AftlImageHeader.
+      AftlError: If invalid structure for AftlIcpHeader.
     """
     assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
 
     if data:
       (self.magic, self.required_icp_version_major,
-       self.required_icp_version_minor, self.aftl_image_size,
+       self.required_icp_version_minor, self.aftl_descriptor_size,
        self.icp_count) = struct.unpack(self.FORMAT_STRING, data)
     else:
       self.magic = self.MAGIC
       self.required_icp_version_major = avbtool.AVB_VERSION_MAJOR
       self.required_icp_version_minor = avbtool.AVB_VERSION_MINOR
-      self.aftl_image_size = self.SIZE
+      self.aftl_descriptor_size = self.SIZE
       self.icp_count = 0
     if not self.is_valid():
-      raise AftlError('Invalid structure for AftlImageHeader.')
+      raise AftlError('Invalid structure for AftlIcpHeader')
 
-  def encode(self):
-    """Serializes the AftlImageHeader |SIZE| to bytes.
+  def save(self, output):
+    """Serializes the transparency header |SIZE| to disk.
 
-    Returns:
-      The encoded AftlImageHeader as bytes.
+    Arguments:
+      output: The object to write the header to.
 
     Raises:
-      AftlError: If invalid structure for AftlImageHeader.
+      AftlError: If invalid structure for AftlIcpHeader.
+    """
+    output.write(self.encode())
+
+  def encode(self):
+    """Serializes the header |SIZE| to a bytearray().
+
+    Returns:
+      A bytearray() with the encoded header.
+
+    Raises:
+      AftlError: If invalid structure for AftlIcpHeader.
     """
     if not self.is_valid():
-      raise AftlError('Invalid structure for AftlImageHeader')
+      raise AftlError('Invalid structure for AftlIcpHeader')
     return struct.pack(self.FORMAT_STRING, self.magic,
                        self.required_icp_version_major,
                        self.required_icp_version_minor,
-                       self.aftl_image_size,
+                       self.aftl_descriptor_size,
                        self.icp_count)
 
   def is_valid(self):
-    """Ensures that values in the AftlImageHeader are sane.
+    """Ensures that values in an AftlIcpHeader structure are sane.
 
     Returns:
-      True if the values in the AftlImageHeader are sane, False otherwise.
+      True if the values in the AftlIcpHeader are sane, False otherwise.
     """
-    if self.magic != AftlImageHeader.MAGIC:
+    if self.magic != AftlIcpHeader.MAGIC:
       sys.stderr.write(
-          'AftlImageHeader: magic value mismatch: {}\n'
-          .format(repr(self.magic)))
+          'ICP Header: magic value mismatch: {}\n'.format(self.magic))
       return False
 
     if self.required_icp_version_major > avbtool.AVB_VERSION_MAJOR:
-      sys.stderr.write('AftlImageHeader: major version mismatch: {}\n'.format(
+      sys.stderr.write('ICP header: major version mismatch: {}\n'.format(
           self.required_icp_version_major))
       return False
 
     if self.required_icp_version_minor > avbtool.AVB_VERSION_MINOR:
-      sys.stderr.write('AftlImageHeader: minor version mismatch: {}\n'.format(
+      sys.stderr.write('ICP header: minor version mismatch: {}\n'.format(
           self.required_icp_version_minor))
       return False
 
-    if self.aftl_image_size < self.SIZE:
-      sys.stderr.write('AftlImageHeader: Invalid AFTL image size: {}\n'.format(
-          self.aftl_image_size))
+    if self.aftl_descriptor_size < self.SIZE:
+      sys.stderr.write('ICP Header: Invalid descriptor size: {}\n'.format(
+          self.aftl_descriptor_size))
       return False
 
     if self.icp_count < 0 or self.icp_count > 65535:
       sys.stderr.write(
-          'AftlImageHeader: ICP entry count out of range: {}\n'.format(
+          'ICP header: ICP entry count out of range: {}\n'.format(
               self.icp_count))
       return False
     return True
 
   def print_desc(self, o):
-    """Print the AftlImageHeader.
+    """Print the descriptor.
 
     Arguments:
       o: The object to write the output to.
     """
-    o.write('  AFTL image header:\n')
     i = ' ' * 4
     fmt = '{}{:25}{}\n'
     o.write(fmt.format(i, 'Major version:', self.required_icp_version_major))
     o.write(fmt.format(i, 'Minor version:', self.required_icp_version_minor))
-    o.write(fmt.format(i, 'Image size:', self.aftl_image_size))
+    o.write(fmt.format(i, 'Descriptor size:', self.aftl_descriptor_size))
     o.write(fmt.format(i, 'ICP entries count:', self.icp_count))
 
 
@@ -349,16 +354,14 @@
     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.
-    annotation_leaf_size: Size of the SignedVBMetaPrimaryAnnotationLeaf passed
-        to the log.
+    fw_info_leaf_size: Size of the FirmwareInfo leaf 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.
-    annotation_leaf: The data comprising the SignedVBMetaPrimaryAnnotationLeaf
-        leaf.
+    fw_info_leaf: The data comprising the FirmwareInfo leaf.
     log_root_signature: The data comprising the log root signature.
     proofs: The hashes comprising the inclusion proof.
 
@@ -371,8 +374,9 @@
                    'H'    # log root signature size
                    'B'    # number of hashes in the inclusion proof
                    'L')   # size of the inclusion proof in bytes
-  # This header is followed by the log_url, log_root_descriptor,
-  # annotation leaf, log root signature, and the proofs elements.
+  # These are used to capture the log_url, log_root_descriptor,
+  # fw_info leaf, log root signature, and the proofs elements for the
+  # encode & save functions.
 
   def __init__(self, data=None):
     """Initializes a new ICP entry object.
@@ -387,11 +391,11 @@
     assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
 
     if data:
-      # Deserialize the header from the data.
+      # Deserialize the header from the data descriptor.
       (self._log_url_size_expected,
        self.leaf_index,
        self._log_root_descriptor_size_expected,
-       self._annotation_leaf_size_expected,
+       self._fw_info_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,26 +405,22 @@
       expected_format_string = '{}s{}s{}s{}s{}s'.format(
           self._log_url_size_expected,
           self._log_root_descriptor_size_expected,
-          self._annotation_leaf_size_expected,
+          self._fw_info_leaf_size_expected,
           self._log_root_sig_size_expected,
           self._inc_proof_size_expected)
 
-      (log_url, log_root_descriptor_bytes, annotation_leaf_bytes,
+      (self.log_url, log_root_descriptor_bytes, fw_info_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.annotation_leaf = SignedVBMetaPrimaryAnnotationLeaf.parse(
-          annotation_leaf_bytes)
+      self.fw_info_leaf = FirmwareInfoLeaf(fw_info_leaf_bytes)
 
       self.proofs = []
       if self._proof_hash_count_expected > 0:
         proof_idx = 0
-        hash_size = (self._inc_proof_size_expected
-                     // self._proof_hash_count_expected)
+        hash_size = self._inc_proof_size_expected // self._proof_hash_count_expected
         for _ in range(self._proof_hash_count_expected):
           proof = proof_bytes[proof_idx:(proof_idx+hash_size)]
           self.proofs.append(proof)
@@ -429,8 +429,8 @@
       self.leaf_index = 0
       self.log_url = ''
       self.log_root_descriptor = TrillianLogRootDescriptor()
-      self.annotation_leaf = SignedVBMetaPrimaryAnnotationLeaf()
-      self.log_root_signature = b''
+      self.fw_info_leaf = FirmwareInfoLeaf()
+      self.log_root_signature = ''
       self.proofs = []
     if not self.is_valid():
       raise AftlError('Invalid structure for AftlIcpEntry')
@@ -450,11 +450,11 @@
     return self._log_root_descriptor_size_expected
 
   @property
-  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
+  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
 
   @property
   def log_root_sig_size(self):
@@ -492,7 +492,7 @@
     if not transparency_log_pub_key:
       return False
 
-    leaf_hash = rfc6962_hash_leaf(self.annotation_leaf.encode())
+    leaf_hash = rfc6962_hash_leaf(self.fw_info_leaf.encode())
     calc_root = root_from_icp(self.leaf_index,
                               self.log_root_descriptor.tree_size,
                               self.proofs,
@@ -505,34 +505,45 @@
       return True
     return False
 
-  def verify_vbmeta_image(self, vbmeta_image, transparency_log_pub_key):
-    """Verify the inclusion proof for the given VBMeta image.
+  def verify_vbmeta_image(self, vbmeta_descriptor, transparency_log_pub_key):
+    """Verify the inclusion proof for the given vbmeta_descriptor.
 
     Arguments:
-      vbmeta_image: A bytearray with the VBMeta image.
+      vbmeta_descriptor: A bytearray with the vbmeta descriptor.
       transparency_log_pub_key: File path to the PEM file containing the trusted
         transparency log public key.
 
     Returns:
       True if the inclusion proof validates and the vbmeta hash of the given
-      VBMeta image matches the one in the annotation leaf; otherwise False.
+      descriptor matches the one in the fw_info_leaf; otherwise False.
     """
-    if not vbmeta_image:
+    if not vbmeta_descriptor:
       return False
 
     # Calculate the hash of the vbmeta image.
-    vbmeta_hash = hashlib.sha256(vbmeta_image).digest()
+    vbmeta_hash = hashlib.sha256(vbmeta_descriptor).digest()
 
     # 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.annotation_leaf.annotation.vbmeta_hash == vbmeta_hash)
+            and self.fw_info_leaf.vbmeta_hash == vbmeta_hash)
+
+  def save(self, output):
+    """Serializes the transparency header |SIZE| and data to disk.
+
+    Arguments:
+      output: The object to write the header to.
+
+    Raises:
+      AftlError: If invalid entry structure.
+    """
+    output.write(self.encode())
 
   def encode(self):
-    """Serializes the header |SIZE| and data to bytes.
+    """Serializes the header |SIZE| and data to a bytearray().
 
     Returns:
-      bytes with the encoded header.
+      A bytearray() with the encoded header.
 
     Raises:
       AftlError: If invalid entry structure.
@@ -545,7 +556,7 @@
         self.FORMAT_STRING,
         self.log_url_size,
         self.log_root_descriptor_size,
-        self.annotation_leaf_size,
+        self.fw_info_leaf_size,
         self.log_root_sig_size,
         self.inc_proof_size)
 
@@ -554,31 +565,30 @@
 
     return struct.pack(expected_format_string,
                        self.log_url_size, self.leaf_index,
-                       self.log_root_descriptor_size, self.annotation_leaf_size,
+                       self.log_root_descriptor_size, self.fw_info_leaf_size,
                        self.log_root_sig_size, self.proof_hash_count,
-                       self.inc_proof_size, self.log_url.encode('ascii'),
+                       self.inc_proof_size, self.log_url,
                        self.log_root_descriptor.encode(),
-                       self.annotation_leaf.encode(),
-                       self.log_root_signature,
-                       proof_bytes)
+                       str(self.fw_info_leaf.encode()),
+                       str(self.log_root_signature),
+                       str(proof_bytes))
 
-  def translate_response(self, log_url, avbm_response):
-    """Translates an AddVBMetaResponse object to an AftlIcpEntry.
+  def translate_response(self, transparency_log, afi_response):
+    """Translates an AddFirmwareInfoResponse object to an AftlIcpEntry.
 
     Arguments:
-      log_url: String representing the transparency log URL.
-      avbm_response: The AddVBMetaResponse object to translate.
+      transparency_log: String representing the transparency log URL.
+      afi_response: The AddFirmwareResponse object to translate.
     """
-    self.log_url = log_url
+    self.log_url = transparency_log
 
-    # 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
+    # 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
 
   def get_expected_size(self):
     """Gets the expected size of the full entry out of the header.
@@ -587,7 +597,7 @@
       The expected size of the AftlIcpEntry from the header.
     """
     return (self.SIZE + self.log_url_size + self.log_root_descriptor_size +
-            self.annotation_leaf_size + self.log_root_sig_size +
+            self.fw_info_leaf_size + self.log_root_sig_size +
             self.inc_proof_size)
 
   def is_valid(self):
@@ -607,14 +617,14 @@
       sys.stderr.write('ICP entry: invalid TrillianLogRootDescriptor.\n')
       return False
 
-    if (not self.annotation_leaf or
-        not isinstance(self.annotation_leaf, Leaf)):
-      sys.stderr.write('ICP entry: invalid Leaf.\n')
+    if (not self.fw_info_leaf or
+        not isinstance(self.fw_info_leaf, FirmwareInfoLeaf)):
+      sys.stderr.write('ICP entry: invalid FirmwareInfo.\n')
       return False
     return True
 
   def print_desc(self, o):
-    """Print the ICP entry.
+    """Print the descriptor.
 
     Arguments:
       o: The object to write the output to.
@@ -627,9 +637,9 @@
     for i, proof_hash in enumerate(self.proofs):
       if i != 0:
         o.write(' ' * 29)
-      o.write('{}\n'.format(proof_hash.hex()))
+      o.write('{}\n'.format(binascii.hexlify(proof_hash)))
     self.log_root_descriptor.print_desc(o)
-    self.annotation_leaf.print_desc(o)
+    self.fw_info_leaf.print_desc(o)
 
 
 class TrillianLogRootDescriptor(object):
@@ -675,7 +685,7 @@
         self.root_hash = data[0:self.root_hash_size]
         data = data[self.root_hash_size:]
       else:
-        self.root_hash = b''
+        self.root_hash = bytearray()
 
       # Parses second part of the log_root descriptor.
       data_length = struct.calcsize(self.FORMAT_STRING_PART_2)
@@ -687,16 +697,16 @@
       if self.metadata_size > 0:
         self.metadata = data[0:self.metadata_size]
       else:
-        self.metadata = b''
+        self.metadata = bytearray()
     else:
       self.version = 1
       self.tree_size = 0
       self.root_hash_size = 0
-      self.root_hash = b''
+      self.root_hash = bytearray()
       self.timestamp = 0
       self.revision = 0
       self.metadata_size = 0
-      self.metadata = b''
+      self.metadata = bytearray()
 
     if not self.is_valid():
       raise AftlError('Invalid structure for TrillianLogRootDescriptor.')
@@ -730,8 +740,8 @@
 
     return struct.pack(expected_format_string,
                        self.version, self.tree_size, self.root_hash_size,
-                       self.root_hash, self.timestamp, self.revision,
-                       self.metadata_size, self.metadata)
+                       str(self.root_hash), self.timestamp, self.revision,
+                       self.metadata_size, str(self.metadata))
 
   def is_valid(self):
     """Ensures that values in the descritor are sane.
@@ -776,7 +786,7 @@
     return True
 
   def print_desc(self, o):
-    """Print the TrillianLogRootDescriptor.
+    """Print the descriptor.
 
     Arguments:
       o: The object to write the output to.
@@ -788,558 +798,191 @@
     o.write(fmt.format(i, 'Tree size:', self.tree_size))
     o.write(fmt.format(i, 'Root hash size:', self.root_hash_size))
     if self.root_hash_size > 0:
-      o.write(fmt.format(i, 'Root hash:', self.root_hash.hex()))
+      o.write(fmt.format(i, 'Root hash:', binascii.hexlify(self.root_hash)))
       o.write(fmt.format(i, 'Timestamp (ns):', self.timestamp))
     o.write(fmt.format(i, 'Revision:', self.revision))
     o.write(fmt.format(i, 'Metadata size:', self.metadata_size))
     if self.metadata_size > 0:
-      o.write(fmt.format(i, 'Metadata:', self.metadata.hex()))
+      o.write(fmt.format(i, 'Metadata:', binascii.hexlify(self.metadata)))
 
 
-def tls_decode_bytes(byte_size, stream):
-  """Decodes a variable-length vector.
+class FirmwareInfoLeaf(object):
+  """A class representing the FirmwareInfo leaf.
 
-  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.
+  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.
 
   Attributes:
-    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, hash_algorithm=HashAlgorithm.SHA256,
-               signature_algorithm=SignatureAlgorithm.RSA, signature=b''):
-    self.hash_algorithm = hash_algorithm
-    self.signature_algorithm = signature_algorithm
-    self.signature = signature
-
-  @classmethod
-  def parse(cls, stream):
-    """Parses a TLS-encoded structure and returns a new Signature.
-
-    Arguments:
-      stream: a BytesIO to read the signature from.
-
-    Returns:
-      A new Signature object.
-
-    Raises:
-      AftlError: If the hash algorithm or signature algorithm value is
-        unknown; or if the decoding failed.
-    """
-    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):
-    """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.
+    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).
   """
 
-  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
+  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
 
-  @classmethod
-  def parse(cls, stream):
-    """Parses a VBMetaPrimaryAnnotation from data.
+      # 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 = ''
+      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.
 
     Arguments:
-      stream: an io.BytesIO to decode the annotation from.
+      key: The name of the attribute to look up.
 
     Returns:
-      A new VBMetaPrimaryAnnotation.
-
-    Raises:
-      AftlError: If an error occured while parsing the annotation.
+      The attribute value or None if not defined.
     """
-    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 new SignedVBMetaPrimaryAnnotation.
-
-    Raises:
-      AftlError: If an error occured while signing the annotation.
-    """
-    # 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')
+    result = self._fw_info_leaf_dict.get(key)
+    if result:
+      result = base64.b64decode(result)
+    return result
 
   def get_expected_size(self):
-    """Returns the size of the encoded annotation."""
-    b = io.BytesIO()
-    self.encode(b)
-    return len(b.getvalue())
+    """Gets the expected size of the JSON-serialized FirmwareInfoLeaf.
+
+    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)
+
+  def encode(self):
+    """Serializes the FirmwareInfoLeaf.
+
+    Returns:
+      A bytearray() with the JSON-serialized FirmwareInfoLeaf.
+    """
+    return self._fw_info_leaf_bytes
+
+  def is_valid(self):
+    """Ensures that values in the descritor are sane.
+
+    Returns:
+      True if the values are sane; otherwise False.
+    """
+    # 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
 
   def print_desc(self, o):
-    """Print the VBMetaPrimaryAnnotation.
+    """Print the descriptor.
 
     Arguments:
       o: The object to write the output to.
     """
-    o.write('      VBMeta Primary Annotation:\n')
-    i = ' ' * 8
+    o.write('    Firmware Info Leaf:\n')
+    # The order of the fields is based on the definition in
+    # proto.aftl_pb2.FirmwareInfo.
+    i = ' ' * 6
     fmt = '{}{:23}{}\n'
     if self.vbmeta_hash:
-      o.write(fmt.format(i, 'VBMeta hash:', self.vbmeta_hash.hex()))
+      o.write(fmt.format(i, 'VBMeta hash:', binascii.hexlify(self.vbmeta_hash)))
     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()))
+                         binascii.hexlify(self.manufacturer_key_hash)))
     if self.description:
       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.
+class AftlDescriptor(object):
+  """A class for the transparency log inclusion proof descriptor.
 
   This encapsulates an AFTL ICP section with all information required to
   validate an inclusion proof.
 
   Attributes:
-    image_header: A header for the section.
+    icp_header: A header for the section.
     icp_entries: A list of AftlIcpEntry objects representing the inclusion
         proofs.
   """
 
   def __init__(self, data=None):
-    """Initializes a new AftlImage section.
+    """Initializes a new AftlDescriptor section.
 
     Arguments:
-      data: If not None, must be a bytearray representing an AftlImage.
+      data: If not None, must be a bytearray representing an AftlDescriptor.
 
     Raises:
-      AftlError: If the data does not represent a well-formed AftlImage.
+      AftlError: If the data does not represent a well-formed AftlDescriptor.
     """
     if data:
-      image_header_bytes = data[0:AftlImageHeader.SIZE]
-      self.image_header = AftlImageHeader(image_header_bytes)
-      if not self.image_header.is_valid():
-        raise AftlError('Invalid AftlImageHeader.')
-      icp_count = self.image_header.icp_count
+      icp_header_bytes = data[0:AftlIcpHeader.SIZE]
+      self.icp_header = AftlIcpHeader(icp_header_bytes)
+      if not self.icp_header.is_valid():
+        raise AftlError('Invalid ICP header.')
+      icp_count = self.icp_header.icp_count
 
       # Jump past the header for entry deserialization.
-      icp_index = AftlImageHeader.SIZE
+      icp_index = AftlIcpHeader.SIZE
       # Validate each entry.
       self.icp_entries = []
-      # add_icp_entry() updates entries and header, so set header count to
+      # Add_icp_entry updates entries and header, so set header count to
       # compensate.
-      self.image_header.icp_count = 0
+      self.icp_header.icp_count = 0
       for i in range(icp_count):
-        # Get the entry header from the AftlImage.
+        # Get the entry header from the AftlDescriptor.
         cur_icp_entry = AftlIcpEntry(data[icp_index:])
         cur_icp_entry_size = cur_icp_entry.get_expected_size()
         # Now validate the entry structure.
@@ -1348,20 +991,20 @@
         self.add_icp_entry(cur_icp_entry)
         icp_index += cur_icp_entry_size
     else:
-      self.image_header = AftlImageHeader()
+      self.icp_header = AftlIcpHeader()
       self.icp_entries = []
     if not self.is_valid():
-      raise AftlError('Invalid AftlImage.')
+      raise AftlError('Malformed AFTLDescriptor')
 
   def add_icp_entry(self, icp_entry):
-    """Adds a new AftlIcpEntry to the AftlImage, updating fields as needed.
+    """Adds a new AftlIcpEntry to the AftlDescriptor, updating fields as needed.
 
     Arguments:
       icp_entry: An AftlIcpEntry structure.
     """
     self.icp_entries.append(icp_entry)
-    self.image_header.icp_count += 1
-    self.image_header.aftl_image_size += icp_entry.get_expected_size()
+    self.icp_header.icp_count += 1
+    self.icp_header.aftl_descriptor_size += icp_entry.get_expected_size()
 
   def verify_vbmeta_image(self, vbmeta_image, transparency_log_pub_keys):
     """Verifies the contained inclusion proof given the public log key.
@@ -1390,36 +1033,47 @@
         icp_verified += 1
     return icp_verified == len(self.icp_entries)
 
-  def encode(self):
-    """Serialize the AftlImage to a bytearray().
+  def save(self, output):
+    """Serializes the AftlDescriptor to disk.
 
-    Returns:
-      A bytearray() with the encoded AFTL image.
+    Arguments:
+      output: The object to write the descriptor to.
 
     Raises:
-      AftlError: If invalid AFTL image structure.
+      AftlError: If invalid descriptor structure.
+    """
+    output.write(self.encode())
+
+  def encode(self):
+    """Serialize the AftlDescriptor to a bytearray().
+
+    Returns:
+      A bytearray() with the encoded header.
+
+    Raises:
+      AftlError: If invalid descriptor structure.
     """
     # The header and entries are guaranteed to be valid when encode is called.
     # Check the entire structure as a whole.
     if not self.is_valid():
-      raise AftlError('Invalid AftlImage structure.')
+      raise AftlError('Invalid AftlDescriptor structure.')
 
-    aftl_image = bytearray()
-    aftl_image.extend(self.image_header.encode())
+    icp_descriptor = bytearray()
+    icp_descriptor.extend(self.icp_header.encode())
     for icp_entry in self.icp_entries:
-      aftl_image.extend(icp_entry.encode())
-    return aftl_image
+      icp_descriptor.extend(icp_entry.encode())
+    return icp_descriptor
 
   def is_valid(self):
-    """Ensures that values in the AftlImage are sane.
+    """Ensures that values in the AftlDescriptor are sane.
 
     Returns:
-      True if the values in the AftlImage are sane, False otherwise.
+      True if the values in the AftlDescriptor are sane, False otherwise.
     """
-    if not self.image_header.is_valid():
+    if not self.icp_header.is_valid():
       return False
 
-    if self.image_header.icp_count != len(self.icp_entries):
+    if self.icp_header.icp_count != len(self.icp_entries):
       return False
 
     for icp_entry in self.icp_entries:
@@ -1428,13 +1082,14 @@
     return True
 
   def print_desc(self, o):
-    """Print the AFTL image.
+    """Print the descriptor.
 
     Arguments:
       o: The object to write the output to.
     """
-    o.write('Android Firmware Transparency Image:\n')
-    self.image_header.print_desc(o)
+    o.write('Android Firmware Transparency Descriptor:\n')
+    o.write('  Header:\n')
+    self.icp_header.print_desc(o)
     for i, icp_entry in enumerate(self.icp_entries):
       o.write('  Entry #{}:\n'.format(i + 1))
       icp_entry.print_desc(o)
@@ -1443,58 +1098,58 @@
 class AftlCommunication(object):
   """Class to abstract the communication layer with the transparency log."""
 
-  def __init__(self, transparency_log_config, timeout):
+  def __init__(self, transparency_log, timeout):
     """Initializes the object.
 
     Arguments:
-      transparency_log_config: A TransparencyLogConfig instance.
+      transparency_log: String containing the URL of a transparency log server.
       timeout: Duration in seconds before requests to the AFTL times out. A
         value of 0 or None means there will be no timeout.
     """
-    self.transparency_log_config = transparency_log_config
+    self.transparency_log = transparency_log
     if timeout:
       self.timeout = timeout
     else:
       self.timeout = None
 
-  def add_vbmeta(self, request):
-    """Calls the AddVBMeta RPC on the AFTL server.
+  def add_firmware_info(self, request):
+    """Calls the AddFirmwareInfo RPC on the AFTL server.
 
     Arguments:
-      request: An AddVBMetaRequest message.
+      request: A AddFirmwareInfoRequest message.
 
     Returns:
-      An AddVBMetaResponse message.
+      An AddFirmwareInfoReponse message.
 
     Raises:
       AftlError: If grpc or the proto modules cannot be loaded, if there is an
         error communicating with the log.
     """
     raise NotImplementedError(
-        'add_vbmeta() needs to be implemented by subclass.')
+        'AddFirmwareInfo() needs to be implemented by subclass.')
 
 
 class AftlGrpcCommunication(AftlCommunication):
   """Class that implements GRPC communication to the AFTL server."""
 
-  def add_vbmeta(self, request):
-    """Calls the AddVBMeta RPC on the AFTL server.
+  def add_firmware_info(self, request):
+    """Calls the AddFirmwareInfo RPC on the AFTL server.
 
     Arguments:
-      request: An AddVBMetaRequest message.
+      request: A AddFirmwareInfoRequest message.
 
     Returns:
-      An AddVBMetaResponse message.
+      An AddFirmwareInfoReponse 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 breaks
+    # Import grpc now to avoid global dependencies as it otherwise breakes
     # running unittest with atest.
     try:
-      import grpc  # pylint: disable=import-outside-toplevel
-      from proto import api_pb2_grpc # pylint: disable=import-outside-toplevel
+      import grpc
+      from proto import api_pb2_grpc
     except ImportError as e:
       err_str = 'grpc can be installed with python pip install grpcio.\n'
       raise AftlError('Failed to import module: ({}).\n{}'.format(e, err_str))
@@ -1502,21 +1157,15 @@
     # Set up the gRPC channel with the transparency log.
     sys.stdout.write('Preparing to request inclusion proof from {}. This could '
                      'take ~30 seconds for the process to complete.\n'.format(
-                         self.transparency_log_config.target))
-    channel = grpc.insecure_channel(self.transparency_log_config.target)
+                         self.transparency_log))
+    channel = grpc.insecure_channel(self.transparency_log)
     stub = api_pb2_grpc.AFTLogStub(channel)
 
-    metadata = []
-    if self.transparency_log_config.api_key:
-      metadata.append(('x-api-key', self.transparency_log_config.api_key))
-
     # Attempt to transmit to the transparency log.
     sys.stdout.write('ICP is about to be requested from transparency log '
-                     'with domain {}.\n'.format(
-                         self.transparency_log_config.target))
+                     'with domain {}.\n'.format(self.transparency_log))
     try:
-      response = stub.AddVBMeta(request, timeout=self.timeout,
-                                metadata=metadata)
+      response = stub.AddFirmwareInfo(request, timeout=self.timeout)
     except grpc.RpcError as e:
       raise AftlError('Error: grpc failure ({})'.format(e))
     return response
@@ -1539,7 +1188,7 @@
     """
     # Reads and parses the vbmeta image.
     try:
-      image = avbtool.ImageHandler(image_filename, read_only=True)
+      image = avbtool.ImageHandler(image_filename)
     except (IOError, ValueError) as e:
       sys.stderr.write('The image does not contain a valid VBMeta structure: '
                        '{}.\n'.format(e))
@@ -1567,14 +1216,14 @@
       return None, None
     return image.read(vbmeta_image_size), footer
 
-  def get_aftl_image(self, image_filename):
-    """Gets the AftlImage from image.
+  def get_aftl_descriptor(self, image_filename):
+    """Gets the AftlDescriptor from image.
 
     Arguments:
       image_filename: Image file to get information from.
 
     Returns:
-      An AftlImage or None if the file does not contain a AftlImage.
+      An AftlDescriptor or None if the file does not contain a AftlDescriptor.
     """
     # Reads the vbmeta image bytes.
     vbmeta_image, _ = self.get_vbmeta_image(image_filename)
@@ -1582,50 +1231,50 @@
       return None
 
     try:
-      image = avbtool.ImageHandler(image_filename, read_only=True)
+      image = avbtool.ImageHandler(image_filename)
     except ValueError as e:
       sys.stderr.write('The image does not contain a valid VBMeta structure: '
                        '{}.\n'.format(e))
       return None
 
-    # Seeks for the start of the AftlImage.
+    # Seeks for the start of the AftlDescriptor.
     try:
       image.seek(len(vbmeta_image))
     except RuntimeError as e:
-      sys.stderr.write('Given AftlImage image offset is invalid: {}.\n'
+      sys.stderr.write('Given AftlDescriptor image offset is invalid: {}.\n'
                        .format(e))
       return None
 
-    # Parses the header for the AftlImage size.
-    tmp_header_bytes = image.read(AftlImageHeader.SIZE)
-    if not tmp_header_bytes or len(tmp_header_bytes) != AftlImageHeader.SIZE:
-      sys.stderr.write('This image does not contain an AftlImage.\n')
+    # Parses the header for the AftlDescriptor size.
+    tmp_header_bytes = image.read(AftlIcpHeader.SIZE)
+    if not tmp_header_bytes or len(tmp_header_bytes) != AftlIcpHeader.SIZE:
+      sys.stderr.write('This image does not contain a AftlDescriptor.\n')
       return None
 
     try:
-      tmp_header = AftlImageHeader(tmp_header_bytes)
+      tmp_header = AftlIcpHeader(tmp_header_bytes)
     except AftlError as e:
-      sys.stderr.write('This image does not contain a valid AftlImage: '
+      sys.stderr.write('This image does not contain a valid AftlDescriptor: '
                        '{}.\n'.format(e))
       return None
 
-    # Resets to the beginning of the AftlImage.
+    # Resets to the beginning of the AftlDescriptor.
     try:
       image.seek(len(vbmeta_image))
     except RuntimeError as e:
-      sys.stderr.write('Given AftlImage image offset is invalid: {}.\n'
+      sys.stderr.write('Given AftlDescriptor image offset is invalid: {}.\n'
                        .format(e))
       return None
 
-    # Parses the full AftlImage.
-    aftl_image_bytes = image.read(tmp_header.aftl_image_size)
-    aftl_image = None
+    # Parses the full AftlDescriptor.
+    icp_bytes = image.read(tmp_header.aftl_descriptor_size)
+    aftl_descriptor = None
     try:
-      aftl_image = AftlImage(aftl_image_bytes)
+      aftl_descriptor = AftlDescriptor(icp_bytes)
     except AftlError as e:
-      sys.stderr.write('The image does not contain a valid AftlImage: '
+      sys.stderr.write('The image does not contain a valid AftlDescriptor: '
                        '{}.\n'.format(e))
-    return aftl_image
+    return aftl_descriptor
 
   def info_image_icp(self, vbmeta_image_path, output):
     """Implements the 'info_image_icp' command.
@@ -1635,13 +1284,13 @@
       output: Output file to write human-readable information to (file object).
 
     Returns:
-      True if the given image has an AftlImage and could successfully
+      True if the given image has an AftlDescriptor and could successfully
       be processed; otherwise False.
     """
-    aftl_image = self.get_aftl_image(vbmeta_image_path)
-    if not aftl_image:
+    aftl_descriptor = self.get_aftl_descriptor(vbmeta_image_path)
+    if not aftl_descriptor:
       return False
-    aftl_image.print_desc(output)
+    aftl_descriptor.print_desc(output)
     return True
 
   def verify_image_icp(self, vbmeta_image_path, transparency_log_pub_keys,
@@ -1659,26 +1308,27 @@
       False.
     """
     vbmeta_image, _ = self.get_vbmeta_image(vbmeta_image_path)
-    aftl_image = self.get_aftl_image(vbmeta_image_path)
-    if not aftl_image or not vbmeta_image:
+    aftl_descriptor = self.get_aftl_descriptor(vbmeta_image_path)
+    if not aftl_descriptor or not vbmeta_image:
       return False
-    verified = aftl_image.verify_vbmeta_image(vbmeta_image,
-                                              transparency_log_pub_keys)
+    verified = aftl_descriptor.verify_vbmeta_image(vbmeta_image,
+                                                   transparency_log_pub_keys)
     if not verified:
-      output.write('The inclusion proofs for the image do not validate.\n')
+      sys.stderr.write('The inclusion proofs for the image do not validate.\n')
       return False
-    output.write('The inclusion proofs for the image successfully validate.\n')
+    sys.stdout.write('The inclusion proofs for the image successfully '
+                     'validate.\n')
     return True
 
-  def request_inclusion_proof(self, transparency_log_config, vbmeta_image,
+  def request_inclusion_proof(self, transparency_log, vbmeta_descriptor,
                               version_inc, manufacturer_key_path,
                               signing_helper, signing_helper_with_files,
                               timeout, aftl_comms=None):
     """Packages and sends a request to the specified transparency log.
 
     Arguments:
-      transparency_log_config: A TransparencyLogConfig instance.
-      vbmeta_image: A bytearray with the VBMeta image.
+      transparency_log: String containing the URL of a transparency log server.
+      vbmeta_descriptor: A bytearray with the vbmeta descriptor.
       version_inc: Subcomponent of the build fingerprint.
       manufacturer_key_path: Path to key used to sign messages sent to the
         transparency log servers.
@@ -1698,7 +1348,7 @@
          cannot be decoded, or if the log submission cannot be signed.
     """
     # Calculate the hash of the vbmeta image.
-    vbmeta_hash = hashlib.sha256(vbmeta_image).digest()
+    vbmeta_hash = hashlib.sha256(vbmeta_descriptor).digest()
 
     # Extract the key data from the PEM file if of size 4096.
     manufacturer_key = avbtool.RSAPublicKey(manufacturer_key_path)
@@ -1710,50 +1360,73 @@
     # Calculate the hash of the manufacturer key data.
     m_key_hash = hashlib.sha256(manufacturer_key_data).digest()
 
-    # Build VBMetaPrimaryAnnotation with that data.
-    annotation = VBMetaPrimaryAnnotation(
-        vbmeta_hash=vbmeta_hash, version_incremental=version_inc,
-        manufacturer_key_hash=m_key_hash)
+    # 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 = bytearray()
+    # AFTL supports SHA256_RSA4096 for now, more will be available.
+    algorithm_name = 'SHA256_RSA4096'
+    sig_num_bytes = 0
+    alg_padding = ''
+    try:
+      alg = avbtool.ALGORITHMS[algorithm_name]
+      sig_num_bytes = alg.signature_num_bytes
+      alg_padding = alg.padding
+    except KeyError:
+      raise AftlError('Unknown algorithm with name {}'.format(algorithm_name))
 
-    # 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)
+    fw_info_hash = hashlib.sha256(fw_info.SerializeToString()).digest()
+    padding_and_hash = str(bytearray(alg_padding)) + fw_info_hash
+    try:
+      signed_fw_info = avbtool.raw_sign(signing_helper,
+                                        signing_helper_with_files,
+                                        algorithm_name,
+                                        sig_num_bytes,
+                                        manufacturer_key_path,
+                                        padding_and_hash)
+    except avbtool.AvbError as e:
+      raise AftlError('Failed to sign FirmwareInfo with '
+                      '--manufacturer_key: {}'.format(e))
+    fw_info_sig = sigpb.sigpb_pb2.DigitallySigned(
+        hash_algorithm='SHA256',
+        signature_algorithm='RSA',
+        signature=str(signed_fw_info))
 
-    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())
+    sfw_info = aftl_pb2.SignedFirmwareInfo(info=fw_info,
+                                           info_signature=fw_info_sig)
+    request = api_pb2.AddFirmwareInfoRequest(vbmeta=bytes(
+        str(vbmeta_descriptor)), fw_info=sfw_info)
 
-    # Submit signed VBMeta annotation to the server.
+    # Submit signed FirmwareInfo to the server.
     if not aftl_comms:
-      aftl_comms = AftlGrpcCommunication(transparency_log_config, timeout)
-    response = aftl_comms.add_vbmeta(request)
+      aftl_comms = AftlGrpcCommunication(transparency_log, timeout)
+    response = aftl_comms.add_firmware_info(request)
 
     # Return an AftlIcpEntry representing this response.
     icp_entry = AftlIcpEntry()
-    icp_entry.translate_response(transparency_log_config.target, response)
+    icp_entry.translate_response(transparency_log, response)
     return icp_entry
 
   def make_icp_from_vbmeta(self, vbmeta_image_path, output,
                            signing_helper, signing_helper_with_files,
-                           version_incremental, transparency_log_configs,
-                           manufacturer_key, padding_size, timeout):
+                           version_incremental, transparency_log_servers,
+                           transparency_log_pub_keys, manufacturer_key,
+                           padding_size, timeout):
     """Generates a vbmeta image with inclusion proof given a vbmeta image.
 
-    The AftlImage contains the information required to validate an inclusion
-    proof for a specific vbmeta image. It consists of a header (struct
-    AftlImageHeader) and zero or more entry structures (struct AftlIcpEntry)
-    that contain the vbmeta leaf hash, tree size, root hash, inclusion proof
-    hashes, and the signature for the root hash.
+    The descriptor (struct AftlDescriptor) contains the information required to
+    validate an inclusion proof for a specific vbmeta image. It consists
+    of a header (struct AftlIcpHeader) and zero or more entry structures
+    (struct AftlIcpEntry) that contain the vbmeta leaf hash, tree size,
+    root hash, inclusion proof hashes, and the signature for the root hash.
 
     The vbmeta image, its hash, the version_incremental part of the build
     fingerprint, and the hash of the manufacturer key are sent to the
     transparency log, with the message signed by the manufacturer key.
     An inclusion proof is calculated and returned. This inclusion proof is
-    then packaged in an AftlImage structure. The existing vbmeta data is
-    copied to a new file, appended with the AftlImage data, and written to
+    then packaged in a AftlDescriptor structure. The existing vbmeta data is
+    copied to a new file, appended with the AftlDescriptor data, and written to
     output. Validation of the inclusion proof does not require
     communication with the transparency log.
 
@@ -1764,8 +1437,13 @@
       signing_helper_with_files: Same as signing_helper but uses files instead.
       version_incremental: A string representing the subcomponent of the
         build fingerprint used to identify the vbmeta in the transparency log.
-      transparency_log_configs: List of TransparencyLogConfig used to request
-        the inclusion proofs.
+      transparency_log_servers: List of strings containing URLs of transparency
+        log servers where inclusion proofs are requested from.
+      transparency_log_pub_keys: List of paths to PEM files containing trusted
+        public keys that correspond with the transparency_logs. There must be
+        the same number of keys as log servers and they must be in the same
+        order, that is, transparency_log_pub_keys[n] corresponds to
+        transparency_log_servers[n].
       manufacturer_key: Path to PEM file containting the key file used to sign
         messages sent to the transparency log servers.
       padding_size: If not 0, pads output so size is a multiple of the number.
@@ -1776,45 +1454,54 @@
       True if the inclusion proofs could be fetched from the transparency log
       servers and could be successfully validated; otherwise False.
     """
+    # Validates command line parameters.
+    if len(transparency_log_servers) != len(transparency_log_pub_keys):
+      sys.stderr.write('Transparency log count and public key count mismatch: '
+                       '{} servers and {} public keys.\n'.format(
+                           len(transparency_log_servers),
+                           len(transparency_log_pub_keys)))
+      return False
+    transparency_logs = zip(transparency_log_servers, transparency_log_pub_keys)
+
     # Retrieves vbmeta structure from given partition image.
     vbmeta_image, footer = self.get_vbmeta_image(vbmeta_image_path)
 
     # Fetches inclusion proofs for vbmeta structure from all transparency logs.
-    aftl_image = AftlImage()
-    for log_config in transparency_log_configs:
+    aftl_descriptor = AftlDescriptor()
+    for host, pub_key in transparency_logs:
       try:
-        icp_entry = self.request_inclusion_proof(log_config, vbmeta_image,
+        icp_entry = self.request_inclusion_proof(host, vbmeta_image,
                                                  version_incremental,
                                                  manufacturer_key,
                                                  signing_helper,
                                                  signing_helper_with_files,
                                                  timeout)
-        if not icp_entry.verify_vbmeta_image(vbmeta_image, log_config.pub_key):
+        if not icp_entry.verify_vbmeta_image(vbmeta_image, pub_key):
           sys.stderr.write('The inclusion proof from {} could not be verified.'
-                           '\n'.format(log_config.target))
-        aftl_image.add_icp_entry(icp_entry)
+                           '\n'.format(host))
+        aftl_descriptor.add_icp_entry(icp_entry)
       except AftlError as e:
         # The inclusion proof request failed. Continue and see if others will.
         sys.stderr.write('Requesting inclusion proof failed: {}.\n'.format(e))
         continue
 
-    # Checks that the resulting AftlImage is sane.
-    if aftl_image.image_header.icp_count != len(transparency_log_configs):
+    # Checks that the resulting AftlDescriptor is sane.
+    if aftl_descriptor.icp_header.icp_count != len(transparency_log_pub_keys):
       sys.stderr.write('Valid inclusion proofs could only be retrieved from {} '
                        'out of {} transparency logs.\n'
-                       .format(aftl_image.image_header.icp_count,
-                               len(transparency_log_configs)))
+                       .format(aftl_descriptor.icp_header.icp_count,
+                               len(transparency_log_pub_keys)))
       return False
-    if not aftl_image.is_valid():
-      sys.stderr.write('Resulting AftlImage structure is malformed.\n')
+    if not aftl_descriptor.is_valid():
+      sys.stderr.write('Resulting AftlDescriptor structure is malformed.\n')
       return False
-    keys = [log.pub_key for log in transparency_log_configs]
-    if not aftl_image.verify_vbmeta_image(vbmeta_image, keys):
-      sys.stderr.write('Resulting AftlImage inclusion proofs do not '
+    if not aftl_descriptor.verify_vbmeta_image(vbmeta_image,
+                                               transparency_log_pub_keys):
+      sys.stderr.write('Resulting AftlDescriptor inclusion proofs do not '
                        'validate.\n')
       return False
 
-    # Writes original VBMeta image, followed by the AftlImage into the output.
+    # Write the original vbmeta descriptor, followed by the AftlDescriptor.
     if footer:  # Checks if it is a chained partition.
       # TODO(b/147217370): Determine the best way to handle chained partitions
       # like the system.img. Currently, we only put the main vbmeta.img in the
@@ -1823,39 +1510,42 @@
                        'implemented.\n')
       return False
 
+    # Writes vbmeta image with inclusion proof into a new vbmeta image.
     output.seek(0)
     output.write(vbmeta_image)
-    encoded_aftl_image = aftl_image.encode()
-    output.write(encoded_aftl_image)
+    encoded_aftl_descriptor = aftl_descriptor.encode()
+    output.write(encoded_aftl_descriptor)
 
     if padding_size > 0:
-      total_image_size = len(vbmeta_image) + len(encoded_aftl_image)
-      padded_size = avbtool.round_to_multiple(total_image_size, padding_size)
-      padding_needed = padded_size - total_image_size
+      descriptor_size = len(vbmeta_image) + len(encoded_aftl_descriptor)
+      padded_size = avbtool.round_to_multiple(descriptor_size, padding_size)
+      padding_needed = padded_size - descriptor_size
       output.write('\0' * padding_needed)
 
-    sys.stdout.write('VBMeta image with AFTL image successfully created.\n')
+    sys.stdout.write('VBMeta image with AFTL inclusion proofs successfully '
+                     'created.\n')
     return True
 
   def _load_test_process_function(self, vbmeta_image_path,
-                                  transparency_log_config,
-                                  manufacturer_key,
+                                  transparency_log_server,
+                                  transparency_log_pub_key, manufacturer_key,
                                   process_number, submission_count,
                                   preserve_icp_images, timeout, result_queue):
     """Function to be used by multiprocessing.Process.
 
     Arguments:
       vbmeta_image_path: Path to a vbmeta image file.
-      transparency_log_config: A TransparencyLogConfig instance used to request
-        an inclusion proof.
+      transparency_log_server: A string in host:port format of transparency log
+        servers where a inclusion proof is requested from.
+      transparency_log_pub_key: Path to PEM file containing trusted
+        public keys that corresponds with the transparency_log_server.
       manufacturer_key: Path to PEM file containting the key file used to sign
         messages sent to the transparency log servers.
       process_number: The number of the processes executing the function.
       submission_count: Number of total submissions to perform per
         process_count.
-      preserve_icp_images: Boolean to indicate if the generated vbmeta image
-        files with inclusion proofs should be preserved in the temporary
-        directory.
+      preserve_icp_images: Boolean to indicate if the generated vbmeta
+        image files with inclusion proofs should preserved.
       timeout: Duration in seconds before requests to the AFTL times out. A
         value of 0 or None means there will be no timeout.
       result_queue: Multiprocessing.Queue object for posting execution results.
@@ -1863,8 +1553,7 @@
     for count in range(0, submission_count):
       version_incremental = 'aftl_load_testing_{}_{}'.format(process_number,
                                                              count)
-      output_file = os.path.join(tempfile.gettempdir(),
-                                 '{}_icp.img'.format(version_incremental))
+      output_file = '{}_icp.img'.format(version_incremental)
       output = open(output_file, 'wb')
 
       # Instrumented section.
@@ -1875,7 +1564,8 @@
           signing_helper=None,
           signing_helper_with_files=None,
           version_incremental=version_incremental,
-          transparency_log_configs=[transparency_log_config],
+          transparency_log_servers=[transparency_log_server],
+          transparency_log_pub_keys=[transparency_log_pub_key],
           manufacturer_key=manufacturer_key,
           padding_size=0,
           timeout=timeout)
@@ -1890,8 +1580,8 @@
       result_queue.put((start_time, end_time, execution_time,
                         version_incremental, result))
 
-  def load_test_aftl(self, vbmeta_image_path, output, transparency_log_config,
-                     manufacturer_key,
+  def load_test_aftl(self, vbmeta_image_path, output, transparency_log_server,
+                     transparency_log_pub_key, manufacturer_key,
                      process_count, submission_count, stats_filename,
                      preserve_icp_images, timeout):
     """Performs multi-threaded load test on a given AFTL and prints stats.
@@ -1899,15 +1589,16 @@
     Arguments:
       vbmeta_image_path: Path to a vbmeta image file.
       output: File to write the report to.
-      transparency_log_config: A TransparencyLogConfig used to request an
-        inclusion proof.
+      transparency_log_server: A string in host:port format of transparency log
+        servers where a inclusion proof is requested from.
+      transparency_log_pub_key: Path to PEM file containing trusted
+        public keys that corresponds with the transparency_log_server.
       manufacturer_key: Path to PEM file containting the key file used to sign
         messages sent to the transparency log servers.
       process_count: Number of processes used for parallel testing.
       submission_count: Number of total submissions to perform per
         process_count.
       stats_filename: Path to the stats file to write the raw execution data to.
-        If None, it will be written to the temp directory.
       preserve_icp_images: Boolean to indicate if the generated vbmeta
         image files with inclusion proofs should preserved.
       timeout: Duration in seconds before requests to the AFTL times out. A
@@ -1922,13 +1613,10 @@
       return False
 
     if not stats_filename:
-      stats_filename = os.path.join(
-          tempfile.gettempdir(),
-          'load_test_p{}_s{}.csv'.format(process_count, submission_count))
-
-    stats_file = None
+      stats_filename = 'load_test_p{}_s{}.csv'.format(process_count,
+                                                      submission_count)
     try:
-      stats_file = open(stats_filename, 'wt')
+      stats_file = open(stats_filename, 'w')
       stats_file.write('start_time,end_time,execution_time,version_incremental,'
                        'result\n')
     except IOError as e:
@@ -1944,8 +1632,8 @@
     for i in range(0, process_count):
       p = multiprocessing.Process(
           target=self._load_test_process_function,
-          args=(vbmeta_image_path, transparency_log_config,
-                manufacturer_key, i, submission_count,
+          args=(vbmeta_image_path, transparency_log_server,
+                transparency_log_pub_key, manufacturer_key, i, submission_count,
                 preserve_icp_images, timeout, result_queue))
       p.start()
       processes.add(p)
@@ -1960,7 +1648,7 @@
                                                    version_incremental, result))
         execution_times.append(execution_time)
         results.append(result)
-      except queue.Empty:
+      except Queue.Empty:
         pass
 
       # Checks if processes are still alive; if not clean them up. join() would
@@ -1973,8 +1661,6 @@
     executions = sorted(execution_times)
     execution_count = len(execution_times)
     median = 0
-
-    # pylint: disable=old-division
     if execution_count % 2 == 0:
       median = (executions[execution_count // 2 - 1]
                 + executions[execution_count // 2]) / 2
@@ -2006,56 +1692,6 @@
     return True
 
 
-class TransparencyLogConfig(object):
-  """Class that gathers the fields representing a transparency log.
-
-  Attributes:
-    target: The hostname and port of the server in hostname:port format.
-    pub_key: A PEM file that contains the public key of the transparency
-      log server.
-    api_key: The API key to use to interact with the transparency log
-      server.
-  """
-
-  @staticmethod
-  def from_argument(arg):
-    """Build an object from a command line argument string.
-
-    Arguments:
-      arg: The transparency log as passed in the command line argument.
-        It must be in the format: host:port,key_file[,api_key].
-
-    Returns:
-      The TransparencyLogConfig instance.
-
-    Raises:
-      argparse.ArgumentTypeError: If the format of arg is invalid.
-    """
-    api_key = None
-    try:
-      target, pub_key, *rest = arg.split(",", maxsplit=2)
-    except ValueError:
-      raise argparse.ArgumentTypeError("incorrect format for transparency log "
-                                       "server, expected "
-                                       "host:port,publickey_file.")
-    if not target:
-      raise argparse.ArgumentTypeError("incorrect format for transparency log "
-                                       "server: host:port cannot be empty.")
-    if not pub_key:
-      raise argparse.ArgumentTypeError("incorrect format for transparency log "
-                                       "server: publickey_file cannot be "
-                                       "empty.")
-    if rest:
-      api_key = rest[0]
-    return TransparencyLogConfig(target, pub_key, api_key)
-
-  def __init__(self, target, pub_key, api_key=None):
-    """Initializes a new TransparencyLogConfig object."""
-    self.target = target
-    self.pub_key = pub_key
-    self.api_key = api_key
-
-
 class AftlTool(avbtool.AvbTool):
   """Object for aftltool command-line tool."""
 
@@ -2073,6 +1709,7 @@
                                           args.signing_helper_with_files,
                                           args.version_incremental,
                                           args.transparency_log_servers,
+                                          args.transparency_log_pub_keys,
                                           args.manufacturer_key,
                                           args.padding_size,
                                           args.timeout)
@@ -2092,6 +1729,7 @@
     return self.aftl.load_test_aftl(args.vbmeta_image_path,
                                     args.output,
                                     args.transparency_log_server,
+                                    args.transparency_log_pub_key,
                                     args.manufacturer_key,
                                     args.processes,
                                     args.submissions,
@@ -2128,9 +1766,21 @@
                             required=True)
     sub_parser.add_argument('--transparency_log_servers',
                             help='List of transparency log servers in '
-                            'host:port,publickey_file[,api_key] format. The '
-                            'publickey_file must be in the PEM format.',
-                            nargs='+', type=TransparencyLogConfig.from_argument)
+                            'host:port format. This must not be None and must '
+                            'be the same size as transparency_log_pub_keys. '
+                            'Also, transparency_log_servers[n] must correspond '
+                            'to transparency_log_pub_keys[n] for all values n.',
+                            nargs='*',
+                            required=True)
+    sub_parser.add_argument('--transparency_log_pub_keys',
+                            help='Paths to PEM files containing transparency '
+                            'log server key(s). This must not be None and must '
+                            'be the same size as transparency_log_servers. '
+                            'Also, transparency_log_pub_keys[n] must '
+                            'correspond to transparency_log_servers[n] for all '
+                            'values n.',
+                            nargs='*',
+                            required=True)
     sub_parser.add_argument('--padding_size',
                             metavar='NUMBER',
                             help='If non-zero, pads output with NUL bytes so '
@@ -2198,11 +1848,13 @@
                             'manufacturer key for use with the log.',
                             required=True)
     sub_parser.add_argument('--transparency_log_server',
-                            help='Transparency log server to test against in '
-                            'host:port,publickey_file[,api_key] format. The '
-                            'publickey_file must be in the PEM format.',
-                            required=True,
-                            type=TransparencyLogConfig.from_argument)
+                            help='Transparency log servers to test against in '
+                            'host:port format.',
+                            required=True)
+    sub_parser.add_argument('--transparency_log_pub_key',
+                            help='Paths to PEM file containing transparency '
+                            'log server key.',
+                            required=True)
     sub_parser.add_argument('--processes',
                             help='Number of parallel processes to use for '
                             'testing (default: 1).',
@@ -2232,23 +1884,16 @@
     sub_parser.set_defaults(func=self.load_test_aftl)
 
     args = parser.parse_args(argv[1:])
-    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)
+      if not success:
+        # Signals to calling tools that the command has failed.
+        sys.exit(1)
     except AftlError as e:
-      # Signals to calling tools that an unhandled exception occured.
+      # Signals to calling tools that an unhandled exeception occured.
       sys.stderr.write('Unhandled AftlError occured: {}\n'.format(e))
       sys.exit(2)
 
-    if not success:
-      # Signals to calling tools that the command has failed.
-      sys.exit(1)
-
 if __name__ == '__main__':
   tool = AftlTool()
   tool.run(sys.argv)
diff --git a/aftltool.py b/aftltool.py
new file mode 120000
index 0000000..39f589b
--- /dev/null
+++ b/aftltool.py
@@ -0,0 +1 @@
+aftltool
\ No newline at end of file
diff --git a/aftltool_integration_test.py b/aftltool_integration_test.py
index 814631b..e284874 100755
--- a/aftltool_integration_test.py
+++ b/aftltool_integration_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 
 # Copyright 2019, The Android Open Source Project
 #
@@ -30,8 +30,6 @@
 
   AFTL_HOST: host:port of the transparency log to test with.
   AFTL_PUBKEY: Transparency log public key in PEM format.
-  AFTL_APIKEY: If the transparency log requires an API key to submit data,
-      this should be the complete key.
   AFTL_VBMETA_IMAGE: VBMeta image that should be used for submission to AFTL.
   AFTL_MANUFACTURER_KEY: Manufacturer signing key used to sign submissions
       to the transparency log in PEM format.
@@ -56,7 +54,6 @@
     """Sets up the environment for integration testing with actual AFTL."""
     self.aftl_host = os.environ.get('AFTL_HOST')
     self.aftl_pubkey = os.environ.get('AFTL_PUBKEY')
-    self.aftl_apikey = os.environ.get('AFTL_APIKEY')
     self.vbmeta_image = os.environ.get('AFTL_VBMETA_IMAGE')
     self.manufacturer_key = os.environ.get('AFTL_MANUFACTURER_KEY')
 
diff --git a/aftltool_test.py b/aftltool_test.py
index 7c8c205..621d0f3 100755
--- a/aftltool_test.py
+++ b/aftltool_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 
 # Copyright 2019, The Android Open Source Project
 #
@@ -24,21 +24,21 @@
 #
 """Unit tests for aftltool."""
 
-import argparse
+# pylint: disable=unused-import
+from __future__ import print_function
+
+import base64
 import binascii
 import io
 import os
-import struct
 import sys
-import tempfile
 import unittest
 
 import aftltool
 import avbtool
-
-# pylint: disable=import-error
-import api_pb2
-# pylint: enable=import-error
+import proto.aftl_pb2
+import proto.api_pb2
+import proto.trillian_pb2
 
 
 # Workaround for b/149307145 in order to pick up the test data from the right
@@ -46,107 +46,6 @@
 # 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):
 
@@ -158,7 +57,7 @@
     # is that soong interprets any output on stderr as an error and marks the
     # unit test as failed although the test itself succeeded.
     self.stderr = sys.stderr
-    self.null = open(os.devnull, 'wt')
+    self.null = open(os.devnull, 'wb')
     sys.stderr = self.null
 
     # AFTL public key.
@@ -184,75 +83,51 @@
     self.test_sth_1 = aftltool.TrillianLogRootDescriptor()
     self.test_sth_1.tree_size = 2
     self.test_sth_1.root_hash_size = 32
-    self.test_sth_1.root_hash = b'f' * 32
+    self.test_sth_1.root_hash = bytearray('f' * 32)
     self.test_sth_1.timestamp = 0x1234567890ABCDEF
     self.test_sth_1.revision = 0xFEDCBA0987654321
 
-    self.test_sth_1_bytes = (
-        b'\x00\x01'                          # version
-        b'\x00\x00\x00\x00\x00\x00\x00\x02'  # tree_size
-        b'\x20'                              # root_hash_size
-        + b'f' * 32 +                        # root_hash
-        b'\x12\x34\x56\x78\x90\xAB\xCD\xEF'  # timestamp
-        b'\xFE\xDC\xBA\x09\x87\x65\x43\x21'  # revision
-        b'\x00\x00'                          # metadata_size
-        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
+    self.test_sth_1_bytes = bytearray(
+        '\x00\x01'                          # version
+        '\x00\x00\x00\x00\x00\x00\x00\x02'  # tree_size
+        '\x20'                              # root_hash_size
+        + 'f' * 32 +                        # root_hash
+        '\x12\x34\x56\x78\x90\xAB\xCD\xEF'  # timestamp
+        '\xFE\xDC\xBA\x09\x87\x65\x43\x21'  # revision
+        '\x00\x00'                          # metadata_size
+        ''                                  # metadata (empty)
     )
 
     # 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)
-    self.test_proof_hashes_1.append(b'c' * 32)
-    self.test_proof_hashes_1.append(b'd' * 32)
-    self.test_proof_hashes_1.append(b'e' * 32)
+    self.test_proof_hashes_1.append(bytearray('b' * 32))
+    self.test_proof_hashes_1.append(bytearray('c' * 32))
+    self.test_proof_hashes_1.append(bytearray('d' * 32))
+    self.test_proof_hashes_1.append(bytearray('e' * 32))
 
     # Valid test AftlIcpEntry #1.
     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
+    self.test_entry_1.log_root_signature = 'g' * 512  # bytearray('g' * 512)
 
-    self.test_entry_1_bytes = (
-        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\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_entry_1_bytes = bytearray(
+        '\x00\x00\x00\x1b'                  # Transparency log url size.
+        '\x00\x00\x00\x00\x00\x00\x00\x01'  # Leaf index.
+        '\x00\x00\x00\x3d'                  # Log root descriptor size.
+        '\x00\x00\x00\x00'                  # Firmware info leaf size.
+        '\x02\x00'                          # Log root signature size.
+        '\x04'                              # Number of hashes in ICP.
+        '\x00\x00\x00\x80'                  # Size of ICP in bytes.
+        + self.test_tl_url_1                # 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
-        + b'd' * 32
-        + b'e' * 32)
+        + 'g' * 512                         # Log root signature.
+        + 'b' * 32                          # Hashes...
+        + 'c' * 32
+        + 'd' * 32
+        + 'e' * 32)
 
     # Valid test AftlIcpEntry #2.
     self.test_tl_url_2 = 'aftl-test-server.google.ch'
@@ -260,148 +135,116 @@
     self.test_sth_2 = aftltool.TrillianLogRootDescriptor()
     self.test_sth_2.tree_size = 4
     self.test_sth_2.root_hash_size = 32
-    self.test_sth_2.root_hash = b'e' * 32
+    self.test_sth_2.root_hash = bytearray('e' * 32)
     self.test_sth_2.timestamp = 6
     self.test_sth_2.revision = 7
     self.test_sth_2.metadata_size = 2
-    self.test_sth_2.metadata = b'12'
+    self.test_sth_2.metadata = '12'
 
-    self.test_sth_2_bytes = (
-        b'\x00\x01'                          # version
-        b'\x00\x00\x00\x00\x00\x00\x00\x04'  # tree_size
-        b'\x20'                              # root_hash_size
-        + b'e' * 32 +                        # root_hash
-        b'\x00\x00\x00\x00\x00\x00\x00\x06'  # timestamp
-        b'\x00\x00\x00\x00\x00\x00\x00\x07'  # revision
-        b'\x00\x02'                          # metadata_size
-        b'12'                                # metadata
+    self.test_sth_2_bytes = bytearray(
+        '\x00\x01'                          # version
+        '\x00\x00\x00\x00\x00\x00\x00\x04'  # tree_size
+        '\x20'                              # root_hash_size
+        + 'e' * 32 +                        # root_hash
+        '\x00\x00\x00\x00\x00\x00\x00\x06'  # timestamp
+        '\x00\x00\x00\x00\x00\x00\x00\x07'  # revision
+        '\x00\x02'                          # metadata_size
+        '12'                                # metadata
     )
 
     # Fill each structure with an easily observable pattern for easy validation.
     self.test_proof_hashes_2 = []
-    self.test_proof_hashes_2.append(b'g' * 32)
-    self.test_proof_hashes_2.append(b'h' * 32)
+    self.test_proof_hashes_2.append(bytearray('g' * 32))
+    self.test_proof_hashes_2.append(bytearray('h' * 32))
 
     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.log_root_signature = bytearray('d' * 512)
     self.test_entry_2.proofs = self.test_proof_hashes_2
 
-    self.test_entry_2_bytes = (
-        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\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)
+    self.test_entry_2_bytes = bytearray(
+        '\x00\x00\x00\x1a'                  # Transparency log url size.
+        '\x00\x00\x00\x00\x00\x00\x00\x02'  # Leaf index.
+        '\x00\x00\x00\x3f'                  # Log root descriptor size.
+        '\x00\x00\x00\x00'                  # Firmware info leaf size.
+        '\x02\x00'                          # Log root signature size.
+        '\x02'                              # Number of hashes in ICP.
+        '\x00\x00\x00\x40'                  # Size of ICP in bytes.
+        + self.test_tl_url_2                # Transparency log url.
+        + self.test_sth_2_bytes             # Log root
+        + 'd' * 512                         # Log root signature.
+        + 'g' * 32                          # Hashes...
+        + 'h' * 32)
 
-    # Valid test AftlImage made out of AftlEntry #1 and #2.
-    self.test_aftl_desc = aftltool.AftlImage()
+    # Valid test AftlDescriptor made out of AftlEntry #1 and #2.
+    self.test_aftl_desc = aftltool.AftlDescriptor()
     self.test_aftl_desc.add_icp_entry(self.test_entry_1)
     self.test_aftl_desc.add_icp_entry(self.test_entry_2)
 
-    self.test_expected_aftl_image_bytes = (
-        b'AFTL'                                         # Magic.
-        + struct.pack('!L', avbtool.AVB_VERSION_MAJOR)  # Major version.
-        + struct.pack('!L', avbtool.AVB_VERSION_MINOR)  # Minor version.
-        + b'\x00\x00\x06\xcf'                           # Image size.
-        b'\x00\x02'                                     # Number of ICP entries.
+    self.test_expected_aftl_descriptor_bytes = bytearray(
+        'AFTL'                              # Magic.
+        '\x00\x00\x00\x01'                  # Major version.
+        '\x00\x00\x00\x01'                  # Minor version.
+        '\x00\x00\x05\xb9'                  # Descriptor size.
+        '\x00\x02'                          # Number of ICP entries.
         + self.test_entry_1_bytes
         + self.test_entry_2_bytes)
 
-    self.test_avbm_resp = api_pb2.AddVBMetaResponse()
-    self.test_avbm_resp.annotation_proof.proof.leaf_index = 9127
+    # pylint: disable=no-member
+    self.test_afi_resp = proto.api_pb2.AddFirmwareInfoResponse()
+    self.test_afi_resp.fw_info_proof.proof.leaf_index = 6263
     hashes = [
-        '61076ca285b4982669e67757f55682ddc43ab5c11ba671260f82a8efa8831f94',
-        '89c2fbcc58da25a65ce5e9b4fb22aaf208b20601f0bc023f73f05d35bc1f3bac',
-        '75d26b5f754b4bed332a3ce2a2bfea0334706a974b7e00ee663f0279fa8b446e',
-        'e1cd9c96feb893b5ef7771e424ac1c6c47509c2b98bc578d22ad07369c9641aa',
-        'e83e0e4dd352b1670a55f93f88781a73bb41efcadb9927399f59459dfa14bc40',
-        '8d5d25996117c88655d66f685baa3c94390867a040507b10587b17fbe92b496a',
-        '5de4c627e9ca712f207d6056f56f0d3286ed4a5381ed7f3cc1aa470217734138',
-        '19acfdb424d7fe28d1f850c76302f78f9a50146a5b9c65f9fdfbbc0173fd6993']
+        '3ad99869646980c0a51d637a9791f892d12e0bc83f6bac5d305a9e289e7f7e8b',
+        '2e5c664d2aee64f71cb4d292e787d0eae7ca9ed80d1e08abb41d26baca386c05',
+        'a671dd99f8d97e9155cc2f0a9dc776a112a5ec5b821ec71571bb258ac790717a',
+        '78046b839595e4e49ad4b0c73f92bf4803aacd4a3351181086509d057ef0d7a9',
+        'c0a7e013f03e7c69e9402070e113dadb345868cf144ccb174fabc384b5605abf',
+        'dc36e5dbe36abe9f4ad10f14170aa0148b6fe3fcaba9df43deaf4dede01b02e8',
+        'b063e7fb665370a361718208756c363dc5206e2e9af9b4d847d81289cdae30de',
+        'a69ea5ba88a221103636d3f4245c800570eb86ad9276121481521f97d0a04a81']
     for h in hashes:
-      self.test_avbm_resp.annotation_proof.proof.hashes.append(
+      self.test_afi_resp.fw_info_proof.proof.hashes.append(
           binascii.unhexlify(h))
-    self.test_avbm_resp.annotation_proof.sth.key_hint = binascii.unhexlify(
+    self.test_afi_resp.fw_info_proof.sth.key_hint = binascii.unhexlify(
         '5af859abce8fe1ea')
-    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 = binascii.unhexlify(
+        '000100000000000018782053b182b55dc1377197c938637f50093131daea4'
+        'd0696b1eae5b8a014bfde884a15edb28f1fc7954400000000000013a50000'
     )
-    self.test_avbm_resp.annotation_proof.sth.log_root_signature = (
-        binascii.unhexlify(
-            '7c37903cc76e8689a6b31da9ad56c3daeb6194029510297cc7d147278390da33'
-            '09c4d9eb1f6be0cdcd1de5315b0b3b573cc9fcd8620d3fab956abbe3c597a572'
-            '46e5a5d277c4cc4b590872d0292fa64e1d3285626b1dedeb00b6aa0a7a0717c0'
-            '7d4c89b68fda9091be06180be1369675a7c4ce7f42cca133ef0daf8dcc5ba1ee'
-            '930cef6dcb71b0a7690446e19661c8e18c089a5d6f6fc9299a0592efb33a4db5'
-            '4c640027fa4f0ad0009f8bf75ec5fc17e0fa1091fabe74fe52738443745066ab'
-            '48f99b297809b863c01016abda17a2479fce91f9929c60bc2ce15e474204fc5a'
-            '8e79b2190aadb7c149671e8c76a4da506860f8d6020fb2eaabfee025cc267bad'
-            '3c8257186c8aaf1da9eefe50cae4b3e8deb66033ebc4bfcda2b317f9e7d2dd78'
-            'b47f2d86795815d82058ad4cba8fc7983a3bbf843e9b8c7ec7f1ae137be6848d'
-            '03c76eefdac40ce5e66cc23d9f3e79ad87acbe7ec0c0bb419a7d368ae1e73c85'
-            '742871f847bde69c871e8797638e0e270282fb058ef1cbcba52aded9dcc8249b'
-            '38fbed8424c33b8cfcde4f49797c64dda8d089d73b84062602fd41c66091543c'
-            'e13c18cfa7f8300530ad4b7adb8924bbb86d17bcc5f1d3d74c522a7dcc8c3c1f'
-            '28a999f2fe1bfe5520c66f93f7c90996dc7f52e62dd95ace9ceace90324c3040'
-            '669b7f5aeb5c5a53f217f1de46e32f80d0aaaf7d9cc9d0e8f8fd7026c612103a'
-        )
+    self.test_afi_resp.fw_info_proof.sth.log_root_signature = binascii.unhexlify(
+        'c264bc7986a1cf56364ca4dd04989f45515cb8764d05b4fb2b880172585ea404'
+        '2105f95a0e0471fb6e0f8c762b14b2e526fb78eaddcc61484917795a12f6ab3b'
+        '557b5571d492d07d7950595f9ad8647a606c7c633f4697c5eb59c272aeca0419'
+        '397c70a3b9b51537537c4ea6b49d356110e70a9286902f814cc6afbeafe612e4'
+        '9e180146140e902bdd9e9dae66b37b4943150a9571949027a648db88a4eea3ad'
+        'f930b4fa6a183e97b762ab0e55a3a26aa6b0fd44d30531e2541ecb94bf645e62'
+        '59e8e3151e7c3b51a09fe24557ce2fd2c0ecdada7ce99c390d2ef10e5d075801'
+        '7c10d49c55cdee930959cc35f0104e04f296591eeb5defbc9ebb237da7b204ca'
+        'a4608cb98d6bc3a01f18585a04441caf8ec7a35aa2d35f7483b92b14fd0f4a41'
+        '3a91133545579309adc593222ca5032a103b00d8fcaea911936dbec11349e4dd'
+        '419b091ea7d1130570d70e2589dd9445fd77fd7492507e1c87736847b9741cc6'
+        '236868af42558ff6e833e12010c8ede786e43ada40ff488f5f1870d1619887d7'
+        '66a24ad0a06a47cc14e2f7db07361be191172adf3155f49713807c7c265f5a84'
+        '040fc84246ccf7913e44721f0043cea05ee774e457e13206775eee992620c3f9'
+        'd2b2584f58aac19e4afe35f0a17df699c45729f94101083f9fc4302659a7e6e0'
+        'e7eb36f8d1ca0be2c9010160d329bd2d17bb707b010fdd63c30b667a0b886cf9'
     )
+    self.test_afi_resp.fw_info_leaf = (
+        '{\"timestamp\":{\"seconds\":1580115370,\"nanos\":621454825},\"Va'
+        'lue\":{\"FwInfo\":{\"info\":{\"info\":{\"vbmeta_hash\":\"ViNzEQS'
+        '/oc/bJ13yl40fk/cvXw90bxHQbzCRxgHDIGc=\",\"version_incremental\":'
+        '\"1\",\"manufacturer_key_hash\":\"yBCrUOdjvaAh4git5EgqWa5neegUao'
+        'XeLlB67+N8ObY=\"}}}}}')
 
-    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
-
+    self.test_fw_info_leaf = aftltool.FirmwareInfoLeaf(
+        self.test_afi_resp.fw_info_leaf)
 
   def tearDown(self):
     """Tears down the test bed for the unit tests."""
     # Reconnects stderr back to the normal stderr; see setUp() for details.
     sys.stderr = self.stderr
-    self.null.close()
 
     super(AftltoolTestCase, self).tearDown()
 
@@ -506,21 +349,21 @@
       self.assertEqual(root_hash, roots[icp[1] -1])
 
 
-class AftlImageTest(AftltoolTestCase):
+class AftlDescriptorTest(AftltoolTestCase):
 
   def test__init__(self):
     """Tests the constructor."""
     # Calls constructor without data.
-    d = aftltool.AftlImage()
-    self.assertIsInstance(d.image_header, aftltool.AftlImageHeader)
-    self.assertEqual(d.image_header.icp_count, 0)
+    d = aftltool.AftlDescriptor()
+    self.assertIsInstance(d.icp_header, aftltool.AftlIcpHeader)
+    self.assertEqual(d.icp_header.icp_count, 0)
     self.assertEqual(d.icp_entries, [])
     self.assertTrue(d.is_valid())
 
     # Calls constructor with data.
-    d = aftltool.AftlImage(self.test_expected_aftl_image_bytes)
-    self.assertIsInstance(d.image_header, aftltool.AftlImageHeader)
-    self.assertEqual(d.image_header.icp_count, 2)
+    d = aftltool.AftlDescriptor(self.test_expected_aftl_descriptor_bytes)
+    self.assertIsInstance(d.icp_header, aftltool.AftlIcpHeader)
+    self.assertEqual(d.icp_header.icp_count, 2)
     self.assertEqual(len(d.icp_entries), 2)
     for entry in d.icp_entries:
       self.assertIsInstance(entry, aftltool.AftlIcpEntry)
@@ -528,17 +371,17 @@
 
   def test_add_icp_entry(self):
     """Tests the add_icp_entry method."""
-    d = aftltool.AftlImage()
+    d = aftltool.AftlDescriptor()
 
     # Adds 1st ICP.
     d.add_icp_entry(self.test_entry_1)
-    self.assertEqual(d.image_header.icp_count, 1)
+    self.assertEqual(d.icp_header.icp_count, 1)
     self.assertEqual(len(d.icp_entries), 1)
     self.assertTrue(d.is_valid())
 
     # Adds 2nd ICP.
     d.add_icp_entry(self.test_entry_2)
-    self.assertEqual(d.image_header.icp_count, 2)
+    self.assertEqual(d.icp_header.icp_count, 2)
     self.assertEqual(len(d.icp_entries), 2)
     self.assertTrue(d.is_valid())
 
@@ -547,17 +390,17 @@
     # Valid vbmeta image without footer with 1 ICP.
     tool = aftltool.Aftl()
     image_path = self.get_testdata_path(
-        'aftl_output_vbmeta_with_1_icp.img')
+        'aftltool/aftl_output_vbmeta_with_1_icp.img')
     vbmeta_image, _ = tool.get_vbmeta_image(image_path)
-    desc = tool.get_aftl_image(image_path)
+    desc = tool.get_aftl_descriptor(image_path)
 
     # Valid image checked against correct log key.
     self.assertTrue(desc.verify_vbmeta_image(
-        vbmeta_image, [self.get_testdata_path('aftl_pubkey_1.pem')]))
+        vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_1.pub')]))
 
     # Valid image checked with a key from another log.
     self.assertFalse(desc.verify_vbmeta_image(
-        vbmeta_image, [self.get_testdata_path('testkey_rsa4096_pub.pem')]))
+        vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_2.pub')]))
 
     # Valid image checked with non existed key file path.
     self.assertFalse(desc.verify_vbmeta_image(
@@ -578,17 +421,17 @@
     # Valid vbmeta image without footer with 2 ICPs from same log.
     tool = aftltool.Aftl()
     image_path = self.get_testdata_path(
-        'aftl_output_vbmeta_with_2_icp_same_log.img')
+        'aftltool/aftl_output_vbmeta_with_2_icp_same_log.img')
     vbmeta_image, _ = tool.get_vbmeta_image(image_path)
-    desc = tool.get_aftl_image(image_path)
+    desc = tool.get_aftl_descriptor(image_path)
 
     # Valid image checked against correct log key.
     self.assertTrue(desc.verify_vbmeta_image(
-        vbmeta_image, [self.get_testdata_path('aftl_pubkey_1.pem')]))
+        vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_1.pub')]))
 
     # Valid vbmeta image checked with key from another log.
     self.assertFalse(desc.verify_vbmeta_image(
-        vbmeta_image, [self.get_testdata_path('testkey_rsa4096_pub.pem')]))
+        vbmeta_image, [self.get_testdata_path('aftltool/aftl_pubkey_2.pub')]))
 
     # Valid image checked with non existed key file path.
     self.assertFalse(desc.verify_vbmeta_image(
@@ -601,29 +444,85 @@
     # 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_descriptor(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_save(self):
+    """Tests save method."""
+    buf = io.BytesIO()
+    self.test_aftl_desc.save(buf)
+    self.assertEqual(buf.getvalue(), self.test_expected_aftl_descriptor_bytes)
+
   def test_encode(self):
     """Tests encode method."""
     desc_bytes = self.test_aftl_desc.encode()
-    self.assertEqual(desc_bytes, self.test_expected_aftl_image_bytes)
+    self.assertEqual(desc_bytes, self.test_expected_aftl_descriptor_bytes)
 
   def test_is_valid(self):
     """Tests is_valid method."""
-    d = aftltool.AftlImage()
+    d = aftltool.AftlDescriptor()
     d.add_icp_entry(self.test_entry_1)
     d.add_icp_entry(self.test_entry_2)
 
     # Force invalid ICP header.
-    old_magic = d.image_header.magic
-    d.image_header.magic = b'YOLO'
+    old_magic = d.icp_header.magic
+    d.icp_header.magic = 'YOLO'
     self.assertFalse(d.is_valid())
-    d.image_header.magic = old_magic
+    d.icp_header.magic = old_magic
     self.assertTrue(d.is_valid())
 
     # Force count mismatch between header and actual entries.
-    old_icp_count = d.image_header.icp_count
-    d.image_header.icp_count = 1
+    old_icp_count = d.icp_header.icp_count
+    d.icp_header.icp_count = 1
     self.assertFalse(d.is_valid())
-    d.image_header.icp_count = old_icp_count
+    d.icp_header.icp_count = old_icp_count
     self.assertTrue(d.is_valid())
 
     # Force invalid ICP entry.
@@ -635,7 +534,7 @@
 
   def test_print_desc(self):
     """Tests print_desc method."""
-    buf = io.StringIO()
+    buf = io.BytesIO()
     self.test_aftl_desc.print_desc(buf)
     desc = buf.getvalue()
 
@@ -644,51 +543,52 @@
     self.assertIn('Log Root Descriptor:', desc)
 
 
-class AftlImageHeaderTest(AftltoolTestCase):
-  """Test suite for testing the AftlImageHeader descriptor."""
+class AftlIcpHeaderTest(AftltoolTestCase):
+  """Test suite for testing the AftlIcpHeader descriptor."""
 
   def setUp(self):
     """Sets up the test bed for the unit tests."""
-    super(AftlImageHeaderTest, self).setUp()
+    super(AftlIcpHeaderTest, self).setUp()
 
-    self.test_header_valid = aftltool.AftlImageHeader()
+    self.test_header_valid = aftltool.AftlIcpHeader()
     self.test_header_valid.icp_count = 1
 
-    self.test_header_invalid = aftltool.AftlImageHeader()
+    self.test_header_invalid = aftltool.AftlIcpHeader()
     self.test_header_invalid.icp_count = -34
 
-    self.test_header_bytes = (
-        b'AFTL'                                         # Magic.
-        + struct.pack('!L', avbtool.AVB_VERSION_MAJOR)  # Major version.
-        + struct.pack('!L', avbtool.AVB_VERSION_MINOR)  # Minor version.
-        + b'\x00\x00\x00\x12'                           # Image size.
-        b'\x00\x01')                                    # Number of ICP entries.
+    self.test_header_bytes = bytearray('\x41\x46\x54\x4c\x00\x00\x00\x01'
+                                       '\x00\x00\x00\x01\x00\x00\x00\x12'
+                                       '\x00\x01')
 
   def test__init__(self):
     """Tests constructor."""
 
     # Calls constructor without data.
-    header = aftltool.AftlImageHeader()
-    self.assertEqual(header.magic, b'AFTL')
+    header = aftltool.AftlIcpHeader()
+    self.assertEqual(header.magic, 'AFTL')
     self.assertEqual(header.required_icp_version_major,
                      avbtool.AVB_VERSION_MAJOR)
     self.assertEqual(header.required_icp_version_minor,
                      avbtool.AVB_VERSION_MINOR)
-    self.assertEqual(header.aftl_image_size, aftltool.AftlImageHeader.SIZE)
+    self.assertEqual(header.aftl_descriptor_size, aftltool.AftlIcpHeader.SIZE)
     self.assertEqual(header.icp_count, 0)
     self.assertTrue(header.is_valid())
 
     # Calls constructor with data.
-    header = aftltool.AftlImageHeader(self.test_header_bytes)
-    self.assertEqual(header.magic, b'AFTL')
-    self.assertEqual(header.required_icp_version_major,
-                     avbtool.AVB_VERSION_MAJOR)
-    self.assertEqual(header.required_icp_version_minor,
-                     avbtool.AVB_VERSION_MINOR)
-    self.assertEqual(header.aftl_image_size, aftltool.AftlImageHeader.SIZE)
+    header = aftltool.AftlIcpHeader(self.test_header_bytes)
+    self.assertEqual(header.magic, 'AFTL')
+    self.assertEqual(header.required_icp_version_major, 1)
+    self.assertEqual(header.required_icp_version_minor, 1)
+    self.assertEqual(header.aftl_descriptor_size, aftltool.AftlIcpHeader.SIZE)
     self.assertTrue(header.icp_count, 1)
     self.assertTrue(header.is_valid())
 
+  def test_save(self):
+    """Tests save method."""
+    buf = io.BytesIO()
+    self.test_header_valid.save(buf)
+    self.assertEqual(buf.getvalue(), self.test_header_bytes)
+
   def test_encode(self):
     """Tests encode method."""
     # Valid header.
@@ -702,12 +602,12 @@
   def test_is_valid(self):
     """Tests is_valid method."""
     # Valid default record.
-    header = aftltool.AftlImageHeader()
+    header = aftltool.AftlIcpHeader()
     self.assertTrue(header.is_valid())
 
     # Invalid magic.
-    header = aftltool.AftlImageHeader()
-    header.magic = b'YOLO'
+    header = aftltool.AftlIcpHeader()
+    header.magic = 'YOLO'
     self.assertFalse(header.is_valid())
 
     # Valid ICP count.
@@ -716,23 +616,23 @@
     # Invalid ICP count.
     self.assertFalse(self.test_header_invalid.is_valid())
 
-    header = aftltool.AftlImageHeader()
+    header = aftltool.AftlIcpHeader()
     header.icp_count = 10000000
     self.assertFalse(header.is_valid())
 
     # Invalid ICP major version.
-    header = aftltool.AftlImageHeader()
+    header = aftltool.AftlIcpHeader()
     header.required_icp_version_major = avbtool.AVB_VERSION_MAJOR + 1
     self.assertFalse(header.is_valid())
 
     # Invalid ICP minor version.
-    header = aftltool.AftlImageHeader()
+    header = aftltool.AftlIcpHeader()
     header.required_icp_version_minor = avbtool.AVB_VERSION_MINOR + 1
     self.assertFalse(header.is_valid())
 
   def test_print_desc(self):
     """Tests print_desc method."""
-    buf = io.StringIO()
+    buf = io.BytesIO()
     self.test_header_valid.print_desc(buf)
     desc = buf.getvalue()
 
@@ -752,7 +652,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.annotation_leaf_size, 19)
+    self.assertEqual(entry.fw_info_leaf_size, 0)
     self.assertEqual(entry.log_root_sig_size, 0)
     self.assertEqual(entry.proof_hash_count, 0)
     self.assertEqual(entry.inc_proof_size, 0)
@@ -766,7 +666,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.annotation_leaf_size, 139)
+    self.assertEqual(entry.fw_info_leaf_size, 0)
     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)
@@ -779,15 +679,21 @@
     entry_bytes = self.test_entry_1.encode()
     self.assertEqual(entry_bytes, self.test_entry_1_bytes)
 
+  def test_save(self):
+    """Tests save method."""
+    buf = io.BytesIO()
+    self.test_entry_1.save(buf)
+    self.assertEqual(buf.getvalue(), self.test_entry_1_bytes)
+
   def test_get_expected_size(self):
     """Tests get_expected_size method."""
     # Default record.
     entry = aftltool.AftlIcpEntry()
-    self.assertEqual(entry.get_expected_size(), 75)
+    self.assertEqual(entry.get_expected_size(), 56)
     self.assertEqual(entry.get_expected_size(), len(entry.encode()))
 
     # Test record.
-    self.assertEqual(self.test_entry_1.get_expected_size(), 894)
+    self.assertEqual(self.test_entry_1.get_expected_size(), 755)
     self.assertEqual(self.test_entry_1.get_expected_size(),
                      len(self.test_entry_1.encode()))
 
@@ -811,68 +717,66 @@
     entry.log_root_descriptor = None
     self.assertFalse(entry.is_valid())
 
-    entry.log_root_descriptor = b''
+    entry.log_root_descriptor = ''
     self.assertFalse(entry.is_valid())
 
-    entry.log_root_descriptor = b'blabli'
+    entry.log_root_descriptor = 'blabli'
     self.assertFalse(entry.is_valid())
 
   def test_translate_response(self):
     """Tests translate_response method."""
     entry = aftltool.AftlIcpEntry()
-    entry.translate_response('aftl-test.foo.bar:80', self.test_avbm_resp)
+    entry.translate_response('aftl-test.foo.bar:80', self.test_afi_resp)
     self.assertEqual(entry.log_url, 'aftl-test.foo.bar:80')
-    self.assertEqual(entry.leaf_index, 9127)
+    self.assertEqual(entry.leaf_index, 6263)
     self.assertEqual(entry.log_root_descriptor.encode(),
-                     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)
+                     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)
 
   def test_verify_icp(self):
     """Tests verify_icp method."""
-    with tempfile.NamedTemporaryFile('wt+') as key_file:
-      key_file.write(self.test_aftl_pub_key)
-      key_file.flush()
+    key_file = 'transparency_log_pub_key.pem'
+    with open(key_file, 'w') as f:
+      f.write(self.test_aftl_pub_key)
 
-      # Valid ICP.
-      entry = aftltool.AftlIcpEntry()
-      entry.translate_response(self.test_tl_url_1, self.test_avbm_resp)
-      self.assertTrue(entry.verify_icp(key_file.name))
+    # Valid ICP.
+    entry = aftltool.AftlIcpEntry()
+    entry.translate_response(self.test_tl_url_1, self.test_afi_resp)
+    self.assertTrue(entry.verify_icp(key_file))
 
-      # 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_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))
+    # Invalid ICP where fw_info_leaf is not matching up with proofs.
+    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(
+        'ViNzEQS', '1234567')
+    entry.fw_info_leaf._fw_info_leaf_bytes = fw_info_leaf_bytes
+    self.assertFalse(entry.verify_icp(key_file))
+
+    os.remove(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(
-        'aftl_output_vbmeta_with_1_icp.img')
+        'aftltool/aftl_output_vbmeta_with_1_icp.img')
     vbmeta_image, _ = tool.get_vbmeta_image(image_path)
-    desc = tool.get_aftl_image(image_path)
+    desc = tool.get_aftl_descriptor(image_path)
 
     # Checks that there is 1 ICP.
-    self.assertEqual(desc.image_header.icp_count, 1)
+    self.assertEqual(desc.icp_header.icp_count, 1)
     entry = desc.icp_entries[0]
 
     # Valid vbmeta image checked with correct log key.
     self.assertTrue(entry.verify_vbmeta_image(
-        vbmeta_image, self.get_testdata_path('aftl_pubkey_1.pem')))
+        vbmeta_image, self.get_testdata_path('aftltool/aftl_pubkey_1.pub')))
 
     # Valid vbmeta image checked with public key of another log.
     self.assertFalse(entry.verify_vbmeta_image(
-        vbmeta_image, self.get_testdata_path('testkey_rsa4096_pub.pem')))
+        vbmeta_image, self.get_testdata_path('aftltool/aftl_pubkey_2.pub')))
 
     # Valid vbmeta image checked with invalid key.
     self.assertFalse(entry.verify_vbmeta_image(
@@ -886,19 +790,19 @@
     # Valid vbmeta image without footer with 1 ICP.
     tool = aftltool.Aftl()
     image_path = self.get_testdata_path(
-        'aftl_output_vbmeta_with_1_icp.img')
+        'aftltool/aftl_output_vbmeta_with_1_icp.img')
     vbmeta_image, _ = tool.get_vbmeta_image(image_path)
-    desc = tool.get_aftl_image(image_path)
+    desc = tool.get_aftl_descriptor(image_path)
 
-    self.assertEqual(desc.image_header.icp_count, 1)
+    self.assertEqual(desc.icp_header.icp_count, 1)
     entry = desc.icp_entries[0]
 
     # Modify vbmeta image to become invalid
-    vbmeta_image = b'A' * len(vbmeta_image)
+    vbmeta_image = 'A' * len(vbmeta_image)
 
     # Invalid vbmeta image checked with correct log key.
     self.assertFalse(entry.verify_vbmeta_image(
-        vbmeta_image, self.get_testdata_path('aftl_pubkey_1.pem')))
+        vbmeta_image, self.get_testdata_path('aftltool/aftl_pubkey_1.pub')))
 
     # Invalid vbmeta image checked with invalid key.
     self.assertFalse(entry.verify_vbmeta_image(
@@ -909,11 +813,11 @@
 
     # None image checked with a key.
     self.assertFalse(entry.verify_vbmeta_image(
-        None, self.get_testdata_path('aftl_pubkey_1.pem')))
+        None, self.get_testdata_path('aftltool/aftl_pubkey_1.pub')))
 
   def test_print_desc(self):
     """Tests print_desc method."""
-    buf = io.StringIO()
+    buf = io.BytesIO()
     self.test_entry_1.print_desc(buf)
     desc = buf.getvalue()
 
@@ -954,11 +858,11 @@
     self.assertEqual(d.version, 1)
     self.assertEqual(d.tree_size, 0)
     self.assertEqual(d.root_hash_size, 0)
-    self.assertEqual(d.root_hash, b'')
+    self.assertEqual(d.root_hash, bytearray())
     self.assertEqual(d.timestamp, 0)
     self.assertEqual(d.revision, 0)
     self.assertEqual(d.metadata_size, 0)
-    self.assertEqual(d.metadata, b'')
+    self.assertEqual(d.metadata, bytearray())
 
     # Calls constructor with log_root w/o metadata
     d = aftltool.TrillianLogRootDescriptor(self.test_log_root_bytes_wo_metadata)
@@ -972,13 +876,13 @@
     self.assertEqual(d.timestamp, 1576762888554271289)
     self.assertEqual(d.revision, 740)
     self.assertEqual(d.metadata_size, 0)
-    self.assertEqual(d.metadata, b'')
+    self.assertEqual(d.metadata, bytearray())
 
     # Calls constructor with log_root with metadata
     d = aftltool.TrillianLogRootDescriptor(
         self.test_log_root_bytes_with_metadata)
     self.assertEqual(d.metadata_size, 2)
-    self.assertEqual(d.metadata, b'12')
+    self.assertEqual(d.metadata, bytearray('12'))
 
   def test_get_expected_size(self):
     """Tests get_expected_size method."""
@@ -1045,9 +949,9 @@
     # Invalid/valid root_hash_size / root_hash combination.
     d = aftltool.TrillianLogRootDescriptor()
     d.root_hash_size = 4
-    d.root_hash = b'123'
+    d.root_hash = '123'
     self.assertFalse(d.is_valid())
-    d.root_hash = b'1234'
+    d.root_hash = '1234'
     self.assertTrue(d.is_valid())
 
     # Invalid timestamp.
@@ -1070,15 +974,15 @@
     # Invalid/valid metadata_size / metadata combination.
     d = aftltool.TrillianLogRootDescriptor()
     d.metadata_size = 4
-    d.metadata = b'123'
+    d.metadata = '123'
     self.assertFalse(d.is_valid())
-    d.metadata = b'1234'
+    d.metadata = '1234'
     self.assertTrue(d.is_valid())
 
   def test_print_desc(self):
     """Tests print_desc method."""
     # Log root without metadata
-    buf = io.StringIO()
+    buf = io.BytesIO()
     d = aftltool.TrillianLogRootDescriptor(self.test_log_root_bytes_wo_metadata)
     d.print_desc(buf)
     desc = buf.getvalue()
@@ -1089,7 +993,7 @@
     self.assertNotIn('Metadata:', desc)
 
     # Log root with metadata
-    buf = io.StringIO()
+    buf = io.BytesIO()
     d = aftltool.TrillianLogRootDescriptor(
         self.test_log_root_bytes_with_metadata)
     d.print_desc(buf)
@@ -1101,54 +1005,94 @@
     self.assertIn('Metadata:', desc)
 
 
-class SignedVBMetaPrimaryAnnotationLeafTest(AftltoolTestCase):
-  """Test suite for testing the Leaf."""
+class FirmwareInfoLeafTest(AftltoolTestCase):
+  """Test suite for testing the FirmwareInfoLeaf."""
 
   def test__init__(self):
     """Tests constructor and properties methods."""
     # Calls constructor without data.
-    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, '')
+    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)
 
-  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 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)
 
-    # Calls parse with invalid data.
+    # Calls constructor with invalid JSON data.
     with self.assertRaises(aftltool.AftlError):
-      leaf = aftltool.SignedVBMetaPrimaryAnnotationLeaf.parse(b'Invalid data')
+      leaf = aftltool.FirmwareInfoLeaf('Invalid JSON.')
 
   def test_get_expected_size(self):
     """Tests get_expected_size method."""
     # Calls constructor without data.
-    leaf = aftltool.SignedVBMetaPrimaryAnnotationLeaf()
-    self.assertEqual(leaf.get_expected_size(), 19)
+    leaf = aftltool.FirmwareInfoLeaf()
+    self.assertEqual(leaf.get_expected_size(), 0)
 
     # Calls constructor with data.
-    leaf = aftltool.SignedVBMetaPrimaryAnnotationLeaf.parse(
-        self.test_anno_1_bytes)
+    leaf = aftltool.FirmwareInfoLeaf(self.test_afi_resp.fw_info_leaf)
     self.assertEqual(leaf.get_expected_size(),
-                     len(self.test_anno_1_bytes))
+                     len(self.test_afi_resp.fw_info_leaf))
 
   def test_encode(self):
     """Tests encode method."""
+    # Calls constructor without data.
+    leaf = aftltool.FirmwareInfoLeaf()
+    self.assertEqual(leaf.encode(), '')
+
     # Calls constructor with data.
-    self.assertEqual(self.test_anno_1.encode(),
-                     self.test_anno_1_bytes)
+    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 = (
+        '{\"timestamp\":{\"seconds\":1580115370,\"nanos\":621454825},\"In'
+        'val\":{\"FwInfo\":{\"info\":{\"info\":{\"vbmeta_hash\":\"ViNzEQS'
+        '/oc/bJ13yl40fk/cvXw90bxHQbzCRxgHDIGc=\",\"version_incremental\":'
+        '\"1\",\"manufacturer_key_hash\":\"yBCrUOdjvaAh4git5EgqWa5neegUao'
+        '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 = (
+        '{\"timestamp\":{\"seconds\":1580115370,\"nanos\":621454825},\"Va'
+        'lue\":{\"FwInfo\":{\"info\":{\"info\":{\"invalid_field\":\"ViNzEQS'
+        '/oc/bJ13yl40fk/cvXw90bxHQbzCRxgHDIGc=\",\"version_incremental\":'
+        '\"1\",\"manufacturer_key_hash\":\"yBCrUOdjvaAh4git5EgqWa5neegUao'
+        'XeLlB67+N8ObY=\"}}}}}')
+
+    with self.assertRaises(aftltool.AftlError):
+      aftltool.FirmwareInfoLeaf(invalid_fields)
 
   def test_print_desc(self):
     """Tests print_desc method."""
-    buf = io.StringIO()
-    self.test_anno_1.print_desc(buf)
+    buf = io.BytesIO()
+    self.test_fw_info_leaf.print_desc(buf)
     desc = buf.getvalue()
 
     # Cursory check whether the printed description contains something useful.
@@ -1159,20 +1103,19 @@
 class AftlMockCommunication(aftltool.AftlCommunication):
   """Testing Mock implementation of AftlCommunication."""
 
-  def __init__(self, transparency_log_config, canned_response):
+  def __init__(self, transparency_log, canned_response):
     """Initializes the object.
 
     Arguments:
-      transparency_log_config: An aftltool.TransparencyLogConfig instance.
-      canned_response: AddVBMetaResponse to return or the Exception to
+      transparency_log: String containing the URL of a transparency log server.
+      canned_response: AddFirmwareInfoResponse to return or the Exception to
         raise.
     """
-    super(AftlMockCommunication, self).__init__(transparency_log_config,
-                                                timeout=None)
+    super(AftlMockCommunication, self).__init__(transparency_log, timeout=None)
     self.request = None
     self.canned_response = canned_response
 
-  def add_vbmeta(self, request):
+  def add_firmware_info(self, request):
     """Records the request and returns the canned response."""
     self.request = request
 
@@ -1188,22 +1131,22 @@
     """Initializes the object.
 
     Arguments:
-      canned_response: AddVBMetaResponse to return or the Exception to
+      canned_response: AddFirmwareInfoResponse to return or the Exception to
         raise.
     """
     self.mock_canned_response = canned_response
 
-  def request_inclusion_proof(self, transparency_log_config, vbmeta_image,
+  def request_inclusion_proof(self, transparency_log, vbmeta_descriptor,
                               version_inc, manufacturer_key_path,
                               signing_helper, signing_helper_with_files,
                               timeout, aftl_comms=None):
     """Mocked request_inclusion_proof function."""
-    aftl_comms = AftlMockCommunication(transparency_log_config,
+    aftl_comms = AftlMockCommunication(transparency_log,
                                        self.mock_canned_response)
     return super(AftlMock, self).request_inclusion_proof(
-        transparency_log_config, vbmeta_image, version_inc,
-        manufacturer_key_path, signing_helper, signing_helper_with_files,
-        timeout, aftl_comms=aftl_comms)
+        transparency_log, vbmeta_descriptor, version_inc, manufacturer_key_path,
+        signing_helper, signing_helper_with_files, timeout,
+        aftl_comms=aftl_comms)
 
 
 class AftlTestCase(AftltoolTestCase):
@@ -1211,18 +1154,8 @@
   def setUp(self):
     """Sets up the test bed for the unit tests."""
     super(AftlTestCase, self).setUp()
-
-    # Sets up the member variables which are then configured by
-    # set_up_environment() in the subclasses.
-    self.aftl_host = None
-    self.aftl_pubkey = None
-    self.aftl_apikey = None
-    self.vbmeta_image = None
-    self.manufacturer_key = None
     self.set_up_environment()
-
-    self.transparency_log_config = aftltool.TransparencyLogConfig(
-        self.aftl_host, self.aftl_pubkey, self.aftl_apikey)
+    self.output_filename = 'vbmeta_icp.img'
 
     self.make_icp_default_params = {
         'vbmeta_image_path': self.vbmeta_image,
@@ -1230,27 +1163,29 @@
         'signing_helper': None,
         'signing_helper_with_files': None,
         'version_incremental': '1',
-        'transparency_log_configs': [self.transparency_log_config],
+        'transparency_log_servers': [self.aftl_host],
+        'transparency_log_pub_keys': [self.aftl_pubkey],
         'manufacturer_key': self.manufacturer_key,
         'padding_size': 0,
         'timeout': None
     }
 
     self.info_icp_default_params = {
-        'vbmeta_image_path': None,
-        'output': io.StringIO()
+        'vbmeta_image_path': self.output_filename,
+        'output': io.BytesIO()
     }
 
     self.verify_icp_default_params = {
-        'vbmeta_image_path': None,
+        'vbmeta_image_path': self.output_filename,
         'transparency_log_pub_keys': [self.aftl_pubkey],
-        'output': io.StringIO()
+        'output': io.BytesIO()
     }
 
     self.load_test_aftl_default_params = {
         'vbmeta_image_path': self.vbmeta_image,
-        'output': io.StringIO(),
-        'transparency_log_config': self.transparency_log_config,
+        'output': io.BytesIO(),
+        'transparency_log_server': self.aftl_host,
+        'transparency_log_pub_key': self.aftl_pubkey,
         'manufacturer_key': self.manufacturer_key,
         'process_count': 1,
         'submission_count': 1,
@@ -1259,6 +1194,24 @@
         'timeout': None
     }
 
+    self.load_test_stats_file_p1_s1 = 'load_test_p1_s1.csv'
+    self.load_test_stats_file_p2_p2 = 'load_test_p2_s2.csv'
+
+    self.files_to_cleanup = [
+        self.output_filename,
+        self.load_test_stats_file_p1_s1,
+        self.load_test_stats_file_p2_p2
+    ]
+
+  def tearDown(self):
+    """Tears down the test bed for the unit tests."""
+    for filename in self.files_to_cleanup:
+      try:
+        os.remove(filename)
+      except OSError:
+        pass
+    super(AftlTestCase, self).tearDown()
+
   def set_up_environment(self):
     """Sets up member variables for the particular test environment.
 
@@ -1268,18 +1221,11 @@
     raise NotImplementedError('set_up_environment() needs to be implemented '
                               'by subclass.')
 
-  def get_aftl_implementation(self, canned_response):
+  def get_aftl_implementation(self):
     """Gets the aftltool.Aftl implementation used for testing.
 
     This allows to have different Aftl implementations for unit tests and
     integration tests.
-
-    Arguments:
-      canned_response: Since we are using the actual implementation and not a
-      mock this gets ignored.
-
-    Raises:
-      NotImplementedError if subclass is not implementing the method.
     """
     raise NotImplementedError('get_aftl_implementation() needs to be'
                               'implemented by subclass.')
@@ -1287,11 +1233,16 @@
 
 class AftlTest(AftlTestCase):
 
+  def setUp(self):
+    """Sets up the test bed for the unit tests."""
+    super(AftlTest, self).setUp()
+    self.mock_aftl_host = 'test.foo.bar:9000'
+
   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('aftl_pubkey_1.pem')
-    self.vbmeta_image = self.get_testdata_path('aftl_input_vbmeta.img')
+    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.manufacturer_key = self.get_testdata_path('testkey_rsa4096.pem')
 
   def get_aftl_implementation(self, canned_response):
@@ -1302,16 +1253,16 @@
     """Tests the get_vbmeta_image method."""
     tool = aftltool.Aftl()
 
-    # Valid vbmeta image without footer and AftlImage.
+    # Valid vbmeta image without footer and AftlDescriptor.
     image, footer = tool.get_vbmeta_image(
-        self.get_testdata_path('aftl_input_vbmeta.img'))
+        self.get_testdata_path('aftltool/aftl_input_vbmeta.img'))
     self.assertIsNotNone(image)
     self.assertEqual(len(image), 4352)
     self.assertIsNone(footer)
 
-    # Valid vbmeta image without footer but with AftlImage.
+    # Valid vbmeta image without footer but with AftlDescriptor.
     image, footer = tool.get_vbmeta_image(
-        self.get_testdata_path('aftl_output_vbmeta_with_1_icp.img'))
+        self.get_testdata_path('aftltool/aftl_output_vbmeta_with_1_icp.img'))
     self.assertIsNotNone(image)
     self.assertEqual(len(image), 4352)
     self.assertIsNone(footer)
@@ -1328,58 +1279,52 @@
     self.assertIsNone(image)
     self.assertIsNone(footer)
 
-  def test_get_aftl_image(self):
-    """Tests the get_aftl_image method."""
+  def test_get_aftl_descriptor(self):
+    """Tests the get_aftl_descriptor method."""
     tool = aftltool.Aftl()
 
-    # Valid vbmeta image without footer with AftlImage.
-    desc = tool.get_aftl_image(
-        self.get_testdata_path('aftl_output_vbmeta_with_1_icp.img'))
-    self.assertIsInstance(desc, aftltool.AftlImage)
+    # Valid vbmeta image without footer with AftlDescriptor.
+    desc = tool.get_aftl_descriptor(
+        self.get_testdata_path('aftltool/aftl_output_vbmeta_with_1_icp.img'))
+    self.assertIsInstance(desc, aftltool.AftlDescriptor)
 
-    # Valid vbmeta image without footer and AftlImage.
-    desc = tool.get_aftl_image(
-        self.get_testdata_path('aftl_input_vbmeta.img'))
+    # Valid vbmeta image without footer and AftlDescriptor.
+    desc = tool.get_aftl_descriptor(
+        self.get_testdata_path('aftltool/aftl_input_vbmeta.img'))
     self.assertIsNone(desc)
 
     # Invalid vbmeta image.
-    desc = tool.get_aftl_image(self.get_testdata_path('large_blob.bin'))
+    desc = tool.get_aftl_descriptor(self.get_testdata_path('large_blob.bin'))
     self.assertIsNone(desc)
 
   # pylint: disable=no-member
   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_avbm_resp)
+    aftl = AftlMock(self.test_afi_resp)
 
     icp = aftl.request_inclusion_proof(
-        self.transparency_log_config, b'a' * 1024, '1',
+        self.mock_aftl_host, 'a' * 1024, '1',
         self.get_testdata_path('testkey_rsa4096.pem'), None, None, None)
     self.assertEqual(icp.leaf_index,
-                     self.test_avbm_resp.annotation_proof.proof.leaf_index)
+                     self.test_afi_resp.fw_info_proof.proof.leaf_index)
     self.assertEqual(icp.proof_hash_count,
-                     len(self.test_avbm_resp.annotation_proof.proof.hashes))
-    self.assertEqual(icp.log_url, self.aftl_host)
+                     len(self.test_afi_resp.fw_info_proof.proof.hashes))
+    self.assertEqual(icp.log_url, self.mock_aftl_host)
     self.assertEqual(
         icp.log_root_descriptor.root_hash, binascii.unhexlify(
-            '9a5f71340f8dc98bdc6320f976dda5f34db8554cb273ba5ab60f1697c519d6f6'))
+            '53b182b55dc1377197c938637f50093131daea4d0696b1eae5b8a014bfde884a'))
 
-    self.assertEqual(icp.annotation_leaf.annotation.version_incremental,
-                     'only_for_testing')
+    self.assertEqual(icp.fw_info_leaf.version_incremental, '1')
     # 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.annotation_leaf.annotation.manufacturer_key_hash,
-        bytes.fromhex(
-            "83ab3b109b73a1d32dce4153a2de57a1a0485052db8364f3180d98614749d7f7"))
+    self.assertEqual(icp.fw_info_leaf.manufacturer_key_hash, base64.b64decode(
+        'yBCrUOdjvaAh4git5EgqWa5neegUaoXeLlB67+N8ObY='))
 
-    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)
+    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)
 
   # pylint: disable=no-member
   def test_request_inclusion_proof_failure(self):
@@ -1389,81 +1334,77 @@
 
     with self.assertRaises(aftltool.AftlError):
       aftl.request_inclusion_proof(
-          self.transparency_log_config, b'a' * 1024, 'version_inc',
+          self.mock_aftl_host, 'a' * 1024, 'version_inc',
           self.get_testdata_path('testkey_rsa4096.pem'), None, None, None)
 
   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_avbm_resp)
+    aftl = AftlMock(self.test_afi_resp)
     with self.assertRaises(aftltool.AftlError) as e:
       aftl.request_inclusion_proof(
-          self.transparency_log_config, b'a' * 1024, 'version_inc',
+          self.mock_aftl_host, 'a' * 1024, 'version_inc',
           self.get_testdata_path('testkey_rsa2048.pem'), None, None, None)
     self.assertIn('not of size 4096: 2048', str(e.exception))
 
   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_avbm_resp)
+    aftl = self.get_aftl_implementation(self.test_afi_resp)
 
     # Make a VBmeta image with ICP.
-    with tempfile.NamedTemporaryFile('wb+') as output_file:
+    with open(self.output_filename, 'wb') as output_file:
       self.make_icp_default_params['output'] = output_file
       result = aftl.make_icp_from_vbmeta(**self.make_icp_default_params)
-      output_file.flush()
-      self.assertTrue(result)
+    self.assertTrue(result)
 
-      # Checks that there is 1 ICP.
-      aftl_image = aftl.get_aftl_image(output_file.name)
-      self.assertEqual(aftl_image.image_header.icp_count, 1)
+    # Checks that there is 1 ICP.
+    aftl_descriptor = aftl.get_aftl_descriptor(self.output_filename)
+    self.assertEqual(aftl_descriptor.icp_header.icp_count, 1)
 
-      # Verifies the generated image.
-      self.verify_icp_default_params['vbmeta_image_path'] = output_file.name
-      result = aftl.verify_image_icp(**self.verify_icp_default_params)
-      self.assertTrue(result)
+    # Verifies the generated image.
+    result = aftl.verify_image_icp(**self.verify_icp_default_params)
+    self.assertTrue(result)
 
-      # Prints the image details.
-      self.info_icp_default_params['vbmeta_image_path'] = output_file.name
-      result = aftl.info_image_icp(**self.info_icp_default_params)
-      self.assertTrue(result)
+    # Prints the image details.
+    result = aftl.info_image_icp(**self.info_icp_default_params)
+    self.assertTrue(result)
 
   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_avbm_resp)
+    aftl = self.get_aftl_implementation(self.test_afi_resp)
 
     # Reconfigures default parameters with two transparency logs.
-    self.make_icp_default_params['transparency_log_configs'] = [
-        self.transparency_log_config, self.transparency_log_config]
+    self.make_icp_default_params['transparency_log_servers'] = [
+        self.aftl_host, self.aftl_host]
+    self.make_icp_default_params['transparency_log_pub_keys'] = [
+        self.aftl_pubkey, self.aftl_pubkey]
 
     # Make a VBmeta image with ICP.
-    with tempfile.NamedTemporaryFile('wb+') as output_file:
+    with open(self.output_filename, 'wb') as output_file:
       self.make_icp_default_params['output'] = output_file
       result = aftl.make_icp_from_vbmeta(
           **self.make_icp_default_params)
-      output_file.flush()
       self.assertTrue(result)
 
-      # Checks that there are 2 ICPs.
-      aftl_image = aftl.get_aftl_image(output_file.name)
-      self.assertEqual(aftl_image.image_header.icp_count, 2)
+    # Checks that there are 2 ICPs.
+    aftl_descriptor = aftl.get_aftl_descriptor(self.output_filename)
+    self.assertEqual(aftl_descriptor.icp_header.icp_count, 2)
 
-      # Verifies the generated image.
-      self.verify_icp_default_params['vbmeta_image_path'] = output_file.name
-      result = aftl.verify_image_icp(**self.verify_icp_default_params)
-      self.assertTrue(result)
+    # Verifies the generated image.
+    result = aftl.verify_image_icp(**self.verify_icp_default_params)
+    self.assertTrue(result)
 
-      # Prints the image details.
-      self.info_icp_default_params['vbmeta_image_path'] = output_file.name
-      result = aftl.info_image_icp(**self.info_icp_default_params)
-      self.assertTrue(result)
+    # Prints the image details.
+    result = aftl.info_image_icp(**self.info_icp_default_params)
+    self.assertTrue(result)
 
   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_avbm_resp)
+    aftl = AftlMock(self.test_afi_resp)
 
     image_path = self.get_testdata_path(
-        'aftl_output_vbmeta_with_2_icp_same_log.img')
+        'aftltool/aftl_output_vbmeta_with_2_icp_different_logs.img')
     self.info_icp_default_params['vbmeta_image_path'] = image_path
 
     # Verifies the generated image.
@@ -1473,7 +1414,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_avbm_resp)
+    aftl = AftlMock(self.test_afi_resp)
 
     image_path = self.get_testdata_path('large_blob.bin')
     self.info_icp_default_params['vbmeta_image_path'] = image_path
@@ -1485,24 +1426,40 @@
   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_avbm_resp)
+    aftl = AftlMock(self.test_afi_resp)
 
     image_path = self.get_testdata_path(
-        'aftl_output_vbmeta_with_2_icp_same_log.img')
+        '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('aftl_pubkey_1.pem'),
+        self.get_testdata_path('aftltool/aftl_pubkey_1.pub'),
+        self.get_testdata_path('aftltool/aftl_pubkey_2.pub')
     ]
 
     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."""
+    """Tests make_icp_from_vbmeta command with a host that does not support GRPC."""
     aftl = self.get_aftl_implementation(aftltool.AftlError('Comms error'))
     self.make_icp_default_params[
-        'transparency_log_configs'][0].target = 'www.google.com:80'
-    with tempfile.NamedTemporaryFile('wb+') as output_file:
+        'transparency_log_servers'] = ['www.google.com:80']
+    with open(self.output_filename, 'wb') as output_file:
       self.make_icp_default_params['output'] = output_file
       result = aftl.make_icp_from_vbmeta(
           **self.make_icp_default_params)
@@ -1516,7 +1473,7 @@
     # time of the transparency log per load test results in b/139407814#2 where
     # it was 3.43 seconds.
     self.make_icp_default_params['timeout'] = 1
-    with tempfile.NamedTemporaryFile('wb+') as output_file:
+    with open(self.output_filename, 'wb') as output_file:
       self.make_icp_default_params['output'] = output_file
       result = aftl.make_icp_from_vbmeta(
           **self.make_icp_default_params)
@@ -1524,52 +1481,44 @@
 
   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_avbm_resp)
+    aftl = self.get_aftl_implementation(self.test_afi_resp)
 
-    with tempfile.TemporaryDirectory() as tmp_dir:
-      self.load_test_aftl_default_params[
-          'stats_filename'] = os.path.join(tmp_dir, 'load_test.csv')
-      result = aftl.load_test_aftl(**self.load_test_aftl_default_params)
-      self.assertTrue(result)
+    result = aftl.load_test_aftl(**self.load_test_aftl_default_params)
+    self.assertTrue(result)
 
-      output = self.load_test_aftl_default_params['output'].getvalue()
-      self.assertRegex(output, 'Succeeded:.+?1\n')
-      self.assertRegex(output, 'Failed:.+?0\n')
+    output = self.load_test_aftl_default_params['output'].getvalue()
+    self.assertRegexpMatches(output, 'Succeeded:.+?1\n')
+    self.assertRegexpMatches(output, 'Failed:.+?0\n')
 
-      self.assertTrue(os.path.exists(
-          self.load_test_aftl_default_params['stats_filename']))
+    self.assertTrue(os.path.exists(self.load_test_stats_file_p1_s1))
 
   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_avbm_resp)
+    aftl = self.get_aftl_implementation(self.test_afi_resp)
 
     self.load_test_aftl_default_params['process_count'] = 2
     self.load_test_aftl_default_params['submission_count'] = 2
-    with tempfile.TemporaryDirectory() as tmp_dir:
-      self.load_test_aftl_default_params[
-          'stats_filename'] = os.path.join(tmp_dir, 'load_test.csv')
-      result = aftl.load_test_aftl(**self.load_test_aftl_default_params)
-      self.assertTrue(result)
+    result = aftl.load_test_aftl(**self.load_test_aftl_default_params)
+    self.assertTrue(result)
 
-      output = self.load_test_aftl_default_params['output'].getvalue()
-      self.assertRegex(output, 'Succeeded:.+?4\n')
-      self.assertRegex(output, 'Failed:.+?0\n')
+    output = self.load_test_aftl_default_params['output'].getvalue()
+    self.assertRegexpMatches(output, 'Succeeded:.+?4\n')
+    self.assertRegexpMatches(output, 'Failed:.+?0\n')
 
-      self.assertTrue(os.path.exists(
-          self.load_test_aftl_default_params['stats_filename']))
+    self.assertTrue(os.path.exists(self.load_test_stats_file_p2_p2))
 
   def test_load_test_invalid_grpc_service(self):
     """Tests load_test_aftl command with a host that does not support GRPC."""
     aftl = self.get_aftl_implementation(aftltool.AftlError('Comms error'))
 
     self.load_test_aftl_default_params[
-        'transparency_log_config'].target = 'www.google.com:80'
+        'transparency_log_server'] = 'www.google.com:80'
     result = aftl.load_test_aftl(**self.load_test_aftl_default_params)
     self.assertFalse(result)
 
     output = self.load_test_aftl_default_params['output'].getvalue()
-    self.assertRegex(output, 'Succeeded:.+?0\n')
-    self.assertRegex(output, 'Failed:.+?1\n')
+    self.assertRegexpMatches(output, 'Succeeded:.+?0\n')
+    self.assertRegexpMatches(output, 'Failed:.+?1\n')
 
   def test_load_test_grpc_timeout(self):
     """Tests load_test_aftl command when running into timeout."""
@@ -1580,30 +1529,9 @@
     self.assertFalse(result)
 
     output = self.load_test_aftl_default_params['output'].getvalue()
-    self.assertRegex(output, 'Succeeded:.+?0\n')
-    self.assertRegex(output, 'Failed:.+?1\n')
+    self.assertRegexpMatches(output, 'Succeeded:.+?0\n')
+    self.assertRegexpMatches(output, 'Failed:.+?1\n')
 
 
-class TransparencyLogConfigTestCase(unittest.TestCase):
-
-  def test_from_argument(self):
-    log = aftltool.TransparencyLogConfig.from_argument(
-        "example.com:8080,mykey.pub")
-    self.assertEqual(log.target, "example.com:8080")
-    self.assertEqual(log.pub_key, "mykey.pub")
-
-    with self.assertRaises(argparse.ArgumentTypeError):
-      aftltool.TransparencyLogConfig.from_argument("example.com:8080,")
-
-    with self.assertRaises(argparse.ArgumentTypeError):
-      aftltool.TransparencyLogConfig.from_argument(",")
-
-  def test_from_argument_with_api_key(self):
-    log = aftltool.TransparencyLogConfig.from_argument(
-        "example.com:8080,mykey.pub,Aipl29gj3x9")
-    self.assertEqual(log.target, "example.com:8080")
-    self.assertEqual(log.pub_key, "mykey.pub")
-    self.assertEqual(log.api_key, "Aipl29gj3x9")
-
 if __name__ == '__main__':
   unittest.main(verbosity=2)
diff --git a/avbtool b/avbtool
deleted file mode 120000
index 5f6383e..0000000
--- a/avbtool
+++ /dev/null
@@ -1 +0,0 @@
-avbtool.py
\ No newline at end of file
diff --git a/avbtool.py b/avbtool
similarity index 82%
rename from avbtool.py
rename to avbtool
index 8647b29..c263b90 100755
--- a/avbtool.py
+++ b/avbtool
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 
 # Copyright 2016, The Android Open Source Project
 #
@@ -24,11 +24,12 @@
 #
 """Command-line tool for working with Android Verified Boot images."""
 
+from __future__ import print_function
+
 import argparse
 import binascii
 import bisect
 import hashlib
-import json
 import math
 import os
 import struct
@@ -39,7 +40,7 @@
 
 # Keep in sync with libavb/avb_version.h.
 AVB_VERSION_MAJOR = 1
-AVB_VERSION_MINOR = 2
+AVB_VERSION_MINOR = 1
 AVB_VERSION_SUB = 0
 
 # Keep in sync with libavb/avb_footer.h.
@@ -48,9 +49,6 @@
 
 AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
 
-# Configuration for enabling logging of calls to avbtool.
-AVB_INVOCATION_LOGFILE = os.environ.get('AVB_INVOCATION_LOGFILE')
-
 
 class AvbError(Exception):
   """Application-specific errors.
@@ -80,7 +78,7 @@
     hash_num_bytes: Number of bytes used to store the hash.
     signature_num_bytes: Number of bytes used to store the signature.
     public_key_num_bytes: Number of bytes used to store the public key.
-    padding: Padding used for signature as bytes, if any.
+    padding: Padding used for signature, if any.
   """
 
   def __init__(self, algorithm_type, hash_name, hash_num_bytes,
@@ -104,91 +102,91 @@
         hash_num_bytes=0,
         signature_num_bytes=0,
         public_key_num_bytes=0,
-        padding=b''),
+        padding=[]),
     'SHA256_RSA2048': Algorithm(
         algorithm_type=1,        # AVB_ALGORITHM_TYPE_SHA256_RSA2048
         hash_name='sha256',
         hash_num_bytes=32,
         signature_num_bytes=256,
         public_key_num_bytes=8 + 2*2048//8,
-        padding=bytes(bytearray([
+        padding=[
             # PKCS1-v1_5 padding
             0x00, 0x01] + [0xff]*202 + [0x00] + [
                 # ASN.1 header
                 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
                 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
                 0x00, 0x04, 0x20,
-            ]))),
+            ]),
     'SHA256_RSA4096': Algorithm(
         algorithm_type=2,        # AVB_ALGORITHM_TYPE_SHA256_RSA4096
         hash_name='sha256',
         hash_num_bytes=32,
         signature_num_bytes=512,
         public_key_num_bytes=8 + 2*4096//8,
-        padding=bytes(bytearray([
+        padding=[
             # PKCS1-v1_5 padding
             0x00, 0x01] + [0xff]*458 + [0x00] + [
                 # ASN.1 header
                 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
                 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
                 0x00, 0x04, 0x20,
-            ]))),
+            ]),
     'SHA256_RSA8192': Algorithm(
         algorithm_type=3,        # AVB_ALGORITHM_TYPE_SHA256_RSA8192
         hash_name='sha256',
         hash_num_bytes=32,
         signature_num_bytes=1024,
         public_key_num_bytes=8 + 2*8192//8,
-        padding=bytes(bytearray([
+        padding=[
             # PKCS1-v1_5 padding
             0x00, 0x01] + [0xff]*970 + [0x00] + [
                 # ASN.1 header
                 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
                 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
                 0x00, 0x04, 0x20,
-            ]))),
+            ]),
     'SHA512_RSA2048': Algorithm(
         algorithm_type=4,        # AVB_ALGORITHM_TYPE_SHA512_RSA2048
         hash_name='sha512',
         hash_num_bytes=64,
         signature_num_bytes=256,
         public_key_num_bytes=8 + 2*2048//8,
-        padding=bytes(bytearray([
+        padding=[
             # PKCS1-v1_5 padding
             0x00, 0x01] + [0xff]*170 + [0x00] + [
                 # ASN.1 header
                 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
                 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
                 0x00, 0x04, 0x40
-            ]))),
+            ]),
     'SHA512_RSA4096': Algorithm(
         algorithm_type=5,        # AVB_ALGORITHM_TYPE_SHA512_RSA4096
         hash_name='sha512',
         hash_num_bytes=64,
         signature_num_bytes=512,
         public_key_num_bytes=8 + 2*4096//8,
-        padding=bytes(bytearray([
+        padding=[
             # PKCS1-v1_5 padding
             0x00, 0x01] + [0xff]*426 + [0x00] + [
                 # ASN.1 header
                 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
                 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
                 0x00, 0x04, 0x40
-            ]))),
+            ]),
     'SHA512_RSA8192': Algorithm(
         algorithm_type=6,        # AVB_ALGORITHM_TYPE_SHA512_RSA8192
         hash_name='sha512',
         hash_num_bytes=64,
         signature_num_bytes=1024,
         public_key_num_bytes=8 + 2*8192//8,
-        padding=bytes(bytearray([
+        padding=[
             # PKCS1-v1_5 padding
             0x00, 0x01] + [0xff]*938 + [0x00] + [
                 # ASN.1 header
                 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
                 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
                 0x00, 0x04, 0x40
-            ]))),
+            ]),
 }
 
 
@@ -344,7 +342,7 @@
     num_bits: The key size.
   """
 
-  MODULUS_PREFIX = b'modulus='
+  MODULUS_PREFIX = 'modulus='
 
   def __init__(self, key_path):
     """Loads and parses an RSA key from either a private or public key file.
@@ -390,108 +388,40 @@
     # The exponent is assumed to always be 65537 and the number of
     # bits can be derived from the modulus by rounding up to the
     # nearest power of 2.
-    self.key_path = key_path
     self.modulus = int(modulus_hexstr, 16)
     self.num_bits = round_to_pow2(int(math.ceil(math.log(self.modulus, 2))))
     self.exponent = 65537
 
-  def encode(self):
-    """Encodes the public RSA key in |AvbRSAPublicKeyHeader| format.
 
-    This creates a |AvbRSAPublicKeyHeader| as well as the two large
-    numbers (|key_num_bits| bits long) following it.
+def encode_rsa_key(key_path):
+  """Encodes a public RSA key in |AvbRSAPublicKeyHeader| format.
 
-    Returns:
-      The |AvbRSAPublicKeyHeader| followed by two large numbers as bytes.
+  This creates a |AvbRSAPublicKeyHeader| as well as the two large
+  numbers (|key_num_bits| bits long) following it.
 
-    Raises:
-      AvbError: If given RSA key exponent is not 65537.
-    """
-    if self.exponent != 65537:
-      raise AvbError('Only RSA keys with exponent 65537 are supported.')
-    ret = bytearray()
-    # Calculate n0inv = -1/n[0] (mod 2^32)
-    b = 2 ** 32
-    n0inv = b - modinv(self.modulus, b)
-    # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
-    r = 2 ** self.modulus.bit_length()
-    rrmodn = r * r % self.modulus
-    ret.extend(struct.pack('!II', self.num_bits, n0inv))
-    ret.extend(encode_long(self.num_bits, self.modulus))
-    ret.extend(encode_long(self.num_bits, rrmodn))
-    return bytes(ret)
+  Arguments:
+    key_path: The path to a key file.
 
-  def sign(self, algorithm_name, data_to_sign, signing_helper=None,
-           signing_helper_with_files=None):
-    """Sign given data using |signing_helper| or openssl.
+  Returns:
+    A bytearray() with the |AvbRSAPublicKeyHeader|.
 
-    openssl is used if neither the parameters signing_helper nor
-    signing_helper_with_files are given.
-
-    Arguments:
-      algorithm_name: The algorithm name as per the ALGORITHMS dict.
-      data_to_sign: Data to sign as bytes or bytearray.
-      signing_helper: Program which signs a hash and returns the signature.
-      signing_helper_with_files: Same as signing_helper but uses files instead.
-
-    Returns:
-      The signature as bytes.
-
-    Raises:
-      AvbError: If an error occurred during signing.
-    """
-    # Checks requested algorithm for validity.
-    algorithm = ALGORITHMS.get(algorithm_name)
-    if not algorithm:
-      raise AvbError('Algorithm with name {} is not supported.'
-                     .format(algorithm_name))
-
-    if self.num_bits != (algorithm.signature_num_bytes * 8):
-      raise AvbError('Key size of key ({} bits) does not match key size '
-                     '({} bits) of given algorithm {}.'
-                     .format(self.num_bits, algorithm.signature_num_bytes * 8,
-                             algorithm_name))
-
-    # Hashes the data.
-    hasher = hashlib.new(algorithm.hash_name)
-    hasher.update(data_to_sign)
-    digest = hasher.digest()
-
-    # Calculates the signature.
-    padding_and_hash = algorithm.padding + digest
-    p = None
-    if signing_helper_with_files is not None:
-      with tempfile.NamedTemporaryFile() as signing_file:
-        signing_file.write(padding_and_hash)
-        signing_file.flush()
-        p = subprocess.Popen([signing_helper_with_files, algorithm_name,
-                              self.key_path, signing_file.name])
-        retcode = p.wait()
-        if retcode != 0:
-          raise AvbError('Error signing')
-        signing_file.seek(0)
-        signature = signing_file.read()
-    else:
-      if signing_helper is not None:
-        p = subprocess.Popen(
-            [signing_helper, algorithm_name, self.key_path],
-            stdin=subprocess.PIPE,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE)
-      else:
-        p = subprocess.Popen(
-            ['openssl', 'rsautl', '-sign', '-inkey', self.key_path, '-raw'],
-            stdin=subprocess.PIPE,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE)
-      (pout, perr) = p.communicate(padding_and_hash)
-      retcode = p.wait()
-      if retcode != 0:
-        raise AvbError('Error signing: {}'.format(perr))
-      signature = pout
-    if len(signature) != algorithm.signature_num_bytes:
-      raise AvbError('Error signing: Invalid length of signature')
-    return signature
+  Raises:
+    AvbError: If given RSA key exponent is not 65537.
+  """
+  key = RSAPublicKey(key_path)
+  if key.exponent != 65537:
+    raise AvbError('Only RSA keys with exponent 65537 are supported.')
+  ret = bytearray()
+  # Calculate n0inv = -1/n[0] (mod 2^32)
+  b = 2L**32  # pylint: disable=long-suffix
+  n0inv = b - modinv(key.modulus, b)
+  # Calculate rr = r^2 (mod N), where r = 2^(# of key bits)
+  r = 2L**key.modulus.bit_length()  # pylint: disable=long-suffix
+  rrmodn = r * r % key.modulus
+  ret.extend(struct.pack('!II', key.num_bits, n0inv))
+  ret.extend(encode_long(key.num_bits, key.modulus))
+  ret.extend(encode_long(key.num_bits, rrmodn))
+  return ret
 
 
 def lookup_algorithm_by_type(alg_type):
@@ -532,13 +462,66 @@
   raise AvbError('Unsupported algorithm type {}'.format(alg_type))
 
 
+def raw_sign(signing_helper, signing_helper_with_files,
+             algorithm_name, signature_num_bytes, key_path,
+             raw_data_to_sign):
+  """Computes a raw RSA signature using |signing_helper| or openssl.
+
+  Arguments:
+    signing_helper: Program which signs a hash and returns the signature.
+    signing_helper_with_files: Same as signing_helper but uses files instead.
+    algorithm_name: The algorithm name as per the ALGORITHMS dict.
+    signature_num_bytes: Number of bytes used to store the signature.
+    key_path: Path to the private key file. Must be PEM format.
+    raw_data_to_sign: Data to sign (bytearray or str expected).
+
+  Returns:
+    A bytearray containing the signature.
+
+  Raises:
+    Exception: If an error occurs.
+  """
+  p = None
+  if signing_helper_with_files is not None:
+    signing_file = tempfile.NamedTemporaryFile()
+    signing_file.write(str(raw_data_to_sign))
+    signing_file.flush()
+    p = subprocess.Popen([
+        signing_helper_with_files, algorithm_name, key_path, signing_file.name])
+    retcode = p.wait()
+    if retcode != 0:
+      raise AvbError('Error signing')
+    signing_file.seek(0)
+    signature = bytearray(signing_file.read())
+  else:
+    if signing_helper is not None:
+      p = subprocess.Popen(
+          [signing_helper, algorithm_name, key_path],
+          stdin=subprocess.PIPE,
+          stdout=subprocess.PIPE,
+          stderr=subprocess.PIPE)
+    else:
+      p = subprocess.Popen(
+          ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
+          stdin=subprocess.PIPE,
+          stdout=subprocess.PIPE,
+          stderr=subprocess.PIPE)
+    (pout, perr) = p.communicate(str(raw_data_to_sign))
+    retcode = p.wait()
+    if retcode != 0:
+      raise AvbError('Error signing: {}'.format(perr))
+    signature = bytearray(pout)
+  if len(signature) != signature_num_bytes:
+    raise AvbError('Error signing: Invalid length of signature')
+  return signature
+
+
 def verify_vbmeta_signature(vbmeta_header, vbmeta_blob):
   """Checks that signature in a vbmeta blob was made by the embedded public key.
 
   Arguments:
     vbmeta_header: A AvbVBMetaHeader.
-    vbmeta_blob: The whole vbmeta blob, including the header as bytes or
-        bytearray.
+    vbmeta_blob: The whole vbmeta blob, including the header.
 
   Returns:
     True if the signature is valid and corresponds to the embedded
@@ -581,7 +564,8 @@
   if computed_digest != digest_blob:
     return False
 
-  padding_and_digest = alg.padding + computed_digest
+  padding_and_digest = bytearray(alg.padding)
+  padding_and_digest.extend(computed_digest)
 
   (num_bits,) = struct.unpack('!I', pubkey_blob[0:4])
   modulus_blob = pubkey_blob[8:8 + num_bits//8]
@@ -610,47 +594,37 @@
               'parameter=NULL\n'
               '\n'
               '[rsapubkey]\n'
-              'n=INTEGER:{}\n'
-              'e=INTEGER:{}\n').format(hex(modulus).rstrip('L'),
-                                       hex(exponent).rstrip('L'))
+              'n=INTEGER:%s\n'
+              'e=INTEGER:%s\n' % (hex(modulus).rstrip('L'),
+                                  hex(exponent).rstrip('L')))
+  asn1_tmpfile = tempfile.NamedTemporaryFile()
+  asn1_tmpfile.write(asn1_str)
+  asn1_tmpfile.flush()
+  der_tmpfile = tempfile.NamedTemporaryFile()
+  p = subprocess.Popen(
+      ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
+       der_tmpfile.name, '-noout'])
+  retcode = p.wait()
+  if retcode != 0:
+    raise AvbError('Error generating DER file')
 
-  with tempfile.NamedTemporaryFile() as asn1_tmpfile:
-    asn1_tmpfile.write(asn1_str.encode('ascii'))
-    asn1_tmpfile.flush()
-
-    with tempfile.NamedTemporaryFile() as der_tmpfile:
-      p = subprocess.Popen(
-          ['openssl', 'asn1parse', '-genconf', asn1_tmpfile.name, '-out',
-           der_tmpfile.name, '-noout'])
-      retcode = p.wait()
-      if retcode != 0:
-        raise AvbError('Error generating DER file')
-
-      p = subprocess.Popen(
-          ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
-           '-keyform', 'DER', '-raw'],
-          stdin=subprocess.PIPE,
-          stdout=subprocess.PIPE,
-          stderr=subprocess.PIPE)
-      (pout, perr) = p.communicate(sig_blob)
-      retcode = p.wait()
-      if retcode != 0:
-        raise AvbError('Error verifying data: {}'.format(perr))
-      if pout != padding_and_digest:
-        sys.stderr.write('Signature not correct\n')
-        return False
+  p = subprocess.Popen(
+      ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', der_tmpfile.name,
+       '-keyform', 'DER', '-raw'],
+      stdin=subprocess.PIPE,
+      stdout=subprocess.PIPE,
+      stderr=subprocess.PIPE)
+  (pout, perr) = p.communicate(str(sig_blob))
+  retcode = p.wait()
+  if retcode != 0:
+    raise AvbError('Error verifying data: {}'.format(perr))
+  recovered_data = bytearray(pout)
+  if recovered_data != padding_and_digest:
+    sys.stderr.write('Signature not correct\n')
+    return False
   return True
 
 
-def create_avb_hashtree_hasher(algorithm, salt):
-  """Create the hasher for AVB hashtree based on the input algorithm."""
-
-  if algorithm.lower() == 'blake2b-256':
-    return hashlib.new('blake2b', salt, digest_size=32)
-
-  return hashlib.new(algorithm, salt)
-
-
 class ImageChunk(object):
   """Data structure used for representing chunks in Android sparse files.
 
@@ -679,10 +653,10 @@
       output_offset: Offset in de-sparsified file.
       output_size: Number of bytes in output.
       input_offset: Offset in sparse file if TYPE_RAW otherwise None.
-      fill_data: Blob as bytes with data to fill if TYPE_FILL otherwise None.
+      fill_data: Blob with data to fill if TYPE_FILL otherwise None.
 
     Raises:
-      ValueError: If given chunk parameters are invalid.
+      ValueError: If data is not well-formed.
     """
     self.chunk_type = chunk_type
     self.chunk_offset = chunk_offset
@@ -741,12 +715,11 @@
   NUM_CHUNKS_AND_BLOCKS_FORMAT = '<II'
   NUM_CHUNKS_AND_BLOCKS_OFFSET = 16
 
-  def __init__(self, image_filename, read_only=False):
+  def __init__(self, image_filename):
     """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.
@@ -755,7 +728,6 @@
     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):
@@ -770,10 +742,7 @@
     self.is_sparse = False
     self.block_size = 4096
     self._file_pos = 0
-    if self._read_only:
-      self._image = open(self.filename, 'rb')
-    else:
-      self._image = open(self.filename, 'r+b')
+    self._image = open(self.filename, 'r+b')
     self._image.seek(0, os.SEEK_END)
     self.image_size = self._image.tell()
 
@@ -899,15 +868,9 @@
 
     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
@@ -935,16 +898,10 @@
     The length of the given data must be a multiple of the block size.
 
     Arguments:
-      data: Data to append as bytes.
-
-    Raises
-      OSError: If ImageHandler was initialized in read-only mode.
+      data: Data to append.
     """
     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)
@@ -973,17 +930,11 @@
     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))
@@ -1013,7 +964,7 @@
       RuntimeError: If the given offset is negative.
     """
     if offset < 0:
-      raise RuntimeError('Seeking with negative offset: {}'.format(offset))
+      raise RuntimeError('Seeking with negative offset: %d' % offset)
     self._file_pos = offset
 
   def read(self, size):
@@ -1029,7 +980,8 @@
       size: Number of bytes to read.
 
     Returns:
-      The data as bytes.
+      The data.
+
     """
     if not self.is_sparse:
       self._image.seek(self._file_pos)
@@ -1056,7 +1008,7 @@
         data.extend(all_data[offset_mod:(offset_mod + chunk_pos_to_go)])
       else:
         assert chunk.chunk_type == ImageChunk.TYPE_DONT_CARE
-        data.extend(b'\0' * chunk_pos_to_go)
+        data.extend('\0' * chunk_pos_to_go)
 
       to_go -= chunk_pos_to_go
       self._file_pos += chunk_pos_to_go
@@ -1065,7 +1017,7 @@
       if chunk_idx >= len(self._chunks):
         break
 
-    return bytes(data)
+    return data
 
   def tell(self):
     """Returns the file cursor position for reading from unsparsified file.
@@ -1083,11 +1035,7 @@
 
     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()
@@ -1100,8 +1048,7 @@
     if size == self.image_size:
       # Trivial where there's nothing to do.
       return
-
-    if size < self.image_size:
+    elif size < self.image_size:
       chunk_idx = bisect.bisect_right(self._chunk_output_offsets, size) - 1
       chunk = self._chunks[chunk_idx]
       if chunk.output_offset != size:
@@ -1240,26 +1187,26 @@
   See the |AvbPropertyDescriptor| C struct for more information.
 
   Attributes:
-    key: The key as string.
-    value: The value as bytes.
+    key: The key.
+    value: The key.
   """
 
   TAG = 0
   SIZE = 32
   FORMAT_STRING = ('!QQ'  # tag, num_bytes_following (descriptor header)
-                   'Q'    # key size (bytes)
-                   'Q')   # value size (bytes)
+                   'Q'  # key size (bytes)
+                   'Q')  # value size (bytes)
 
   def __init__(self, data=None):
     """Initializes a new property descriptor.
 
     Arguments:
-      data: If not None, must be as bytes of size |SIZE|.
+      data: If not None, must be a bytearray of size |SIZE|.
 
     Raises:
       LookupError: If the given descriptor is malformed.
     """
-    super(AvbPropertyDescriptor, self).__init__(None)
+    AvbDescriptor.__init__(self, None)
     assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
 
     if data:
@@ -1270,15 +1217,12 @@
       if tag != self.TAG or num_bytes_following != expected_size:
         raise LookupError('Given data does not look like a property '
                           'descriptor.')
-      try:
-        self.key = data[self.SIZE:(self.SIZE + key_size)].decode('utf-8')
-      except UnicodeDecodeError as e:
-        raise LookupError('Key cannot be decoded as UTF-8: {}.'.format(e))
+      self.key = data[self.SIZE:(self.SIZE + key_size)]
       self.value = data[(self.SIZE + key_size + 1):(self.SIZE + key_size + 1 +
                                                     value_size)]
     else:
       self.key = ''
-      self.value = b''
+      self.value = ''
 
   def print_desc(self, o):
     """Print the descriptor.
@@ -1286,15 +1230,8 @@
     Arguments:
       o: The object to write the output to.
     """
-    # Go forward with python 3, bytes are represented with the 'b' prefix,
-    # e.g. b'foobar'. Thus, we trim off the 'b' to keep the print output
-    # the same between python 2 and python 3.
-    printable_value = repr(self.value)
-    if printable_value.startswith('b\''):
-      printable_value = printable_value[1:]
-
     if len(self.value) < 256:
-      o.write('    Prop: {} -> {}\n'.format(self.key, printable_value))
+      o.write('    Prop: {} -> {}\n'.format(self.key, repr(str(self.value))))
     else:
       o.write('    Prop: {} -> ({} bytes)\n'.format(self.key, len(self.value)))
 
@@ -1302,18 +1239,16 @@
     """Serializes the descriptor.
 
     Returns:
-      The descriptor data as bytes.
+      A bytearray() with the descriptor data.
     """
-    key_encoded = self.key.encode('utf-8')
-    num_bytes_following = (
-        self.SIZE + len(key_encoded) + len(self.value) + 2 - 16)
+    num_bytes_following = self.SIZE + len(self.key) + len(self.value) + 2 - 16
     nbf_with_padding = round_to_multiple(num_bytes_following, 8)
     padding_size = nbf_with_padding - num_bytes_following
     desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
-                       len(key_encoded), len(self.value))
-    ret = (desc + key_encoded + b'\0' + self.value + b'\0' +
-           padding_size * b'\0')
-    return ret
+                       len(self.key), len(self.value))
+    padding = struct.pack(str(padding_size) + 'x')
+    ret = desc + self.key + '\0' + self.value + '\0' + padding
+    return bytearray(ret)
 
   def verify(self, image_dir, image_ext, expected_chain_partitions_map,
              image_containing_descriptor, accept_zeroed_hashtree):
@@ -1345,15 +1280,15 @@
     image_size: Size of the image, after rounding up to |block_size|.
     tree_offset: Offset of the hash tree in the file.
     tree_size: Size of the tree.
-    data_block_size: Data block size.
-    hash_block_size: Hash block size.
+    data_block_size: Data block size
+    hash_block_size: Hash block size
     fec_num_roots: Number of roots used for FEC (0 if FEC is not used).
     fec_offset: Offset of FEC data (0 if FEC is not used).
     fec_size: Size of FEC data (0 if FEC is not used).
-    hash_algorithm: Hash algorithm used as string.
-    partition_name: Partition name as string.
-    salt: Salt used as bytes.
-    root_digest: Root digest as bytes.
+    hash_algorithm: Hash algorithm used.
+    partition_name: Partition name.
+    salt: Salt used.
+    root_digest: Root digest.
     flags: Descriptor flags (see avb_hashtree_descriptor.h).
   """
 
@@ -1361,19 +1296,19 @@
   RESERVED = 60
   SIZE = 120 + RESERVED
   FORMAT_STRING = ('!QQ'  # tag, num_bytes_following (descriptor header)
-                   'L'    # dm-verity version used
-                   'Q'    # image size (bytes)
-                   'Q'    # tree offset (bytes)
-                   'Q'    # tree size (bytes)
-                   'L'    # data block size (bytes)
-                   'L'    # hash block size (bytes)
-                   'L'    # FEC number of roots
-                   'Q'    # FEC offset (bytes)
-                   'Q'    # FEC size (bytes)
+                   'L'  # dm-verity version used
+                   'Q'  # image size (bytes)
+                   'Q'  # tree offset (bytes)
+                   'Q'  # tree size (bytes)
+                   'L'  # data block size (bytes)
+                   'L'  # hash block size (bytes)
+                   'L'  # FEC number of roots
+                   'Q'  # FEC offset (bytes)
+                   'Q'  # FEC size (bytes)
                    '32s'  # hash algorithm used
-                   'L'    # partition name (bytes)
-                   'L'    # salt length (bytes)
-                   'L'    # root digest length (bytes)
+                   'L'  # partition name (bytes)
+                   'L'  # salt length (bytes)
+                   'L'  # root digest length (bytes)
                    'L' +  # flags
                    str(RESERVED) + 's')  # reserved
 
@@ -1381,12 +1316,12 @@
     """Initializes a new hashtree descriptor.
 
     Arguments:
-      data: If not None, must be bytes of size |SIZE|.
+      data: If not None, must be a bytearray of size |SIZE|.
 
     Raises:
       LookupError: If the given descriptor is malformed.
     """
-    super(AvbHashtreeDescriptor, self).__init__(None)
+    AvbDescriptor.__init__(self, None)
     assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
 
     if data:
@@ -1402,21 +1337,17 @@
         raise LookupError('Given data does not look like a hashtree '
                           'descriptor.')
       # Nuke NUL-bytes at the end.
-      self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii')
+      self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
       o = 0
-      try:
-        self.partition_name = data[
-            (self.SIZE + o):(self.SIZE + o + partition_name_len)
-        ].decode('utf-8')
-      except UnicodeDecodeError as e:
-        raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
-                          .format(e))
+      self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
+                                                      partition_name_len)])
+      # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
+      self.partition_name.decode('utf-8')
       o += partition_name_len
       self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
       o += salt_len
       self.root_digest = data[(self.SIZE + o):(self.SIZE + o + root_digest_len)]
-
-      if root_digest_len != self._hashtree_digest_size():
+      if root_digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
         if root_digest_len != 0:
           raise LookupError('root_digest_len doesn\'t match hash algorithm')
 
@@ -1432,13 +1363,10 @@
       self.fec_size = 0
       self.hash_algorithm = ''
       self.partition_name = ''
-      self.salt = b''
-      self.root_digest = b''
+      self.salt = bytearray()
+      self.root_digest = bytearray()
       self.flags = 0
 
-  def _hashtree_digest_size(self):
-    return len(create_avb_hashtree_hasher(self.hash_algorithm, b'').digest())
-
   def print_desc(self, o):
     """Print the descriptor.
 
@@ -1459,32 +1387,33 @@
     o.write('      FEC size:              {} bytes\n'.format(self.fec_size))
     o.write('      Hash Algorithm:        {}\n'.format(self.hash_algorithm))
     o.write('      Partition Name:        {}\n'.format(self.partition_name))
-    o.write('      Salt:                  {}\n'.format(self.salt.hex()))
-    o.write('      Root Digest:           {}\n'.format(self.root_digest.hex()))
+    o.write('      Salt:                  {}\n'.format(str(self.salt).encode(
+        'hex')))
+    o.write('      Root Digest:           {}\n'.format(str(
+        self.root_digest).encode('hex')))
     o.write('      Flags:                 {}\n'.format(self.flags))
 
   def encode(self):
     """Serializes the descriptor.
 
     Returns:
-      The descriptor data as bytes.
+      A bytearray() with the descriptor data.
     """
-    hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
-    partition_name_encoded = self.partition_name.encode('utf-8')
-    num_bytes_following = (self.SIZE + len(partition_name_encoded)
-                           + len(self.salt) + len(self.root_digest) - 16)
+    encoded_name = self.partition_name.encode('utf-8')
+    num_bytes_following = (self.SIZE + len(encoded_name) + len(self.salt) +
+                           len(self.root_digest) - 16)
     nbf_with_padding = round_to_multiple(num_bytes_following, 8)
     padding_size = nbf_with_padding - num_bytes_following
     desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
                        self.dm_verity_version, self.image_size,
                        self.tree_offset, self.tree_size, self.data_block_size,
                        self.hash_block_size, self.fec_num_roots,
-                       self.fec_offset, self.fec_size, hash_algorithm_encoded,
-                       len(partition_name_encoded), len(self.salt),
-                       len(self.root_digest), self.flags, self.RESERVED * b'\0')
-    ret = (desc + partition_name_encoded + self.salt + self.root_digest +
-           padding_size * b'\0')
-    return ret
+                       self.fec_offset, self.fec_size, self.hash_algorithm,
+                       len(encoded_name), len(self.salt), len(self.root_digest),
+                       self.flags, self.RESERVED*'\0')
+    padding = struct.pack(str(padding_size) + 'x')
+    ret = desc + encoded_name + self.salt + self.root_digest + padding
+    return bytearray(ret)
 
   def verify(self, image_dir, image_ext, expected_chain_partitions_map,
              image_containing_descriptor, accept_zeroed_hashtree):
@@ -1507,9 +1436,9 @@
       image = image_containing_descriptor
     else:
       image_filename = os.path.join(image_dir, self.partition_name + image_ext)
-      image = ImageHandler(image_filename, read_only=True)
+      image = ImageHandler(image_filename)
     # Generate the hashtree and checks that it matches what's in the file.
-    digest_size = self._hashtree_digest_size()
+    digest_size = len(hashlib.new(name=self.hash_algorithm).digest())
     digest_padding = round_to_pow2(digest_size) - digest_size
     (hash_level_offsets, tree_size) = calc_hash_level_offsets(
         self.image_size, self.data_block_size, digest_size + digest_padding)
@@ -1527,7 +1456,7 @@
     # ... also check that the on-disk hashtree matches
     image.seek(self.tree_offset)
     hash_tree_ondisk = image.read(self.tree_size)
-    is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == b'ZeRoHaSH')
+    is_zeroed = (self.tree_size == 0) or (hash_tree_ondisk[0:8] == 'ZeRoHaSH')
     if is_zeroed and accept_zeroed_hashtree:
       print('{}: skipping verification since hashtree is zeroed and '
             '--accept_zeroed_hashtree was given'
@@ -1554,10 +1483,10 @@
 
   Attributes:
     image_size: Image size, in bytes.
-    hash_algorithm: Hash algorithm used as string.
-    partition_name: Partition name as string.
-    salt: Salt used as bytes.
-    digest: The hash value of salt and data combined as bytes.
+    hash_algorithm: Hash algorithm used.
+    partition_name: Partition name.
+    salt: Salt used.
+    digest: The hash value of salt and data combined.
     flags: The descriptor flags (see avb_hash_descriptor.h).
   """
 
@@ -1565,11 +1494,11 @@
   RESERVED = 60
   SIZE = 72 + RESERVED
   FORMAT_STRING = ('!QQ'  # tag, num_bytes_following (descriptor header)
-                   'Q'    # image size (bytes)
+                   'Q'  # image size (bytes)
                    '32s'  # hash algorithm used
-                   'L'    # partition name (bytes)
-                   'L'    # salt length (bytes)
-                   'L'    # digest length (bytes)
+                   'L'  # partition name (bytes)
+                   'L'  # salt length (bytes)
+                   'L'  # digest length (bytes)
                    'L' +  # flags
                    str(RESERVED) + 's')  # reserved
 
@@ -1577,12 +1506,12 @@
     """Initializes a new hash descriptor.
 
     Arguments:
-      data: If not None, must be bytes of size |SIZE|.
+      data: If not None, must be a bytearray of size |SIZE|.
 
     Raises:
       LookupError: If the given descriptor is malformed.
     """
-    super(AvbHashDescriptor, self).__init__(None)
+    AvbDescriptor.__init__(self, None)
     assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
 
     if data:
@@ -1593,22 +1522,19 @@
       expected_size = round_to_multiple(
           self.SIZE - 16 + partition_name_len + salt_len + digest_len, 8)
       if tag != self.TAG or num_bytes_following != expected_size:
-        raise LookupError('Given data does not look like a hash descriptor.')
+        raise LookupError('Given data does not look like a hash ' 'descriptor.')
       # Nuke NUL-bytes at the end.
-      self.hash_algorithm = self.hash_algorithm.rstrip(b'\0').decode('ascii')
+      self.hash_algorithm = self.hash_algorithm.split('\0', 1)[0]
       o = 0
-      try:
-        self.partition_name = data[
-            (self.SIZE + o):(self.SIZE + o + partition_name_len)
-        ].decode('utf-8')
-      except UnicodeDecodeError as e:
-        raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
-                          .format(e))
+      self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
+                                                      partition_name_len)])
+      # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
+      self.partition_name.decode('utf-8')
       o += partition_name_len
       self.salt = data[(self.SIZE + o):(self.SIZE + o + salt_len)]
       o += salt_len
       self.digest = data[(self.SIZE + o):(self.SIZE + o + digest_len)]
-      if digest_len != len(hashlib.new(self.hash_algorithm).digest()):
+      if digest_len != len(hashlib.new(name=self.hash_algorithm).digest()):
         if digest_len != 0:
           raise LookupError('digest_len doesn\'t match hash algorithm')
 
@@ -1616,8 +1542,8 @@
       self.image_size = 0
       self.hash_algorithm = ''
       self.partition_name = ''
-      self.salt = b''
-      self.digest = b''
+      self.salt = bytearray()
+      self.digest = bytearray()
       self.flags = 0
 
   def print_desc(self, o):
@@ -1630,29 +1556,30 @@
     o.write('      Image Size:            {} bytes\n'.format(self.image_size))
     o.write('      Hash Algorithm:        {}\n'.format(self.hash_algorithm))
     o.write('      Partition Name:        {}\n'.format(self.partition_name))
-    o.write('      Salt:                  {}\n'.format(self.salt.hex()))
-    o.write('      Digest:                {}\n'.format(self.digest.hex()))
+    o.write('      Salt:                  {}\n'.format(str(self.salt).encode(
+        'hex')))
+    o.write('      Digest:                {}\n'.format(str(self.digest).encode(
+        'hex')))
     o.write('      Flags:                 {}\n'.format(self.flags))
 
   def encode(self):
     """Serializes the descriptor.
 
     Returns:
-      The descriptor data as bytes.
+      A bytearray() with the descriptor data.
     """
-    hash_algorithm_encoded = self.hash_algorithm.encode('ascii')
-    partition_name_encoded = self.partition_name.encode('utf-8')
-    num_bytes_following = (self.SIZE + len(partition_name_encoded) +
-                           len(self.salt) + len(self.digest) - 16)
+    encoded_name = self.partition_name.encode('utf-8')
+    num_bytes_following = (
+        self.SIZE + len(encoded_name) + len(self.salt) + len(self.digest) - 16)
     nbf_with_padding = round_to_multiple(num_bytes_following, 8)
     padding_size = nbf_with_padding - num_bytes_following
     desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
-                       self.image_size, hash_algorithm_encoded,
-                       len(partition_name_encoded), len(self.salt),
-                       len(self.digest), self.flags, self.RESERVED * b'\0')
-    ret = (desc + partition_name_encoded + self.salt + self.digest +
-           padding_size * b'\0')
-    return ret
+                       self.image_size, self.hash_algorithm, len(encoded_name),
+                       len(self.salt), len(self.digest), self.flags,
+                       self.RESERVED*'\0')
+    padding = struct.pack(str(padding_size) + 'x')
+    ret = desc + encoded_name + self.salt + self.digest + padding
+    return bytearray(ret)
 
   def verify(self, image_dir, image_ext, expected_chain_partitions_map,
              image_containing_descriptor, accept_zeroed_hashtree):
@@ -1675,7 +1602,7 @@
       image = image_containing_descriptor
     else:
       image_filename = os.path.join(image_dir, self.partition_name + image_ext)
-      image = ImageHandler(image_filename, read_only=True)
+      image = ImageHandler(image_filename)
     data = image.read(self.image_size)
     ha = hashlib.new(self.hash_algorithm)
     ha.update(self.salt)
@@ -1699,14 +1626,14 @@
 
   Attributes:
     flags: Flags.
-    kernel_cmdline: The kernel command-line as string.
+    kernel_cmdline: The kernel command-line.
   """
 
   TAG = 3
   SIZE = 24
   FORMAT_STRING = ('!QQ'  # tag, num_bytes_following (descriptor header)
-                   'L'    # flags
-                   'L')   # cmdline length (bytes)
+                   'L'  # flags
+                   'L')  # cmdline length (bytes)
 
   FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0)
   FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
@@ -1715,12 +1642,12 @@
     """Initializes a new kernel cmdline descriptor.
 
     Arguments:
-      data: If not None, must be bytes of size |SIZE|.
+      data: If not None, must be a bytearray of size |SIZE|.
 
     Raises:
       LookupError: If the given descriptor is malformed.
     """
-    super(AvbKernelCmdlineDescriptor, self).__init__(None)
+    AvbDescriptor.__init__(self, None)
     assert struct.calcsize(self.FORMAT_STRING) == self.SIZE
 
     if data:
@@ -1732,12 +1659,10 @@
         raise LookupError('Given data does not look like a kernel cmdline '
                           'descriptor.')
       # Nuke NUL-bytes at the end.
-      try:
-        self.kernel_cmdline = data[
-            self.SIZE:(self.SIZE + kernel_cmdline_length)].decode('utf-8')
-      except UnicodeDecodeError as e:
-        raise LookupError('Kernel command-line cannot be decoded as UTF-8: {}.'
-                          .format(e))
+      self.kernel_cmdline = str(data[self.SIZE:(self.SIZE +
+                                                kernel_cmdline_length)])
+      # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
+      self.kernel_cmdline.decode('utf-8')
     else:
       self.flags = 0
       self.kernel_cmdline = ''
@@ -1750,22 +1675,24 @@
     """
     o.write('    Kernel Cmdline descriptor:\n')
     o.write('      Flags:                 {}\n'.format(self.flags))
-    o.write('      Kernel Cmdline:        \'{}\'\n'.format(self.kernel_cmdline))
+    o.write('      Kernel Cmdline:        {}\n'.format(repr(
+        self.kernel_cmdline)))
 
   def encode(self):
     """Serializes the descriptor.
 
     Returns:
-      The descriptor data as bytes.
+      A bytearray() with the descriptor data.
     """
-    kernel_cmd_encoded = self.kernel_cmdline.encode('utf-8')
-    num_bytes_following = (self.SIZE + len(kernel_cmd_encoded) - 16)
+    encoded_str = self.kernel_cmdline.encode('utf-8')
+    num_bytes_following = (self.SIZE + len(encoded_str) - 16)
     nbf_with_padding = round_to_multiple(num_bytes_following, 8)
     padding_size = nbf_with_padding - num_bytes_following
     desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
-                       self.flags, len(kernel_cmd_encoded))
-    ret = desc + kernel_cmd_encoded + padding_size * b'\0'
-    return ret
+                       self.flags, len(encoded_str))
+    padding = struct.pack(str(padding_size) + 'x')
+    ret = desc + encoded_str + padding
+    return bytearray(ret)
 
   def verify(self, image_dir, image_ext, expected_chain_partitions_map,
              image_containing_descriptor, accept_zeroed_hashtree):
@@ -1794,16 +1721,16 @@
 
   Attributes:
     rollback_index_location: The rollback index location to use.
-    partition_name: Partition name as string.
-    public_key: The public key as bytes.
+    partition_name: Partition name.
+    public_key: Bytes for the public key.
   """
 
   TAG = 4
   RESERVED = 64
   SIZE = 28 + RESERVED
   FORMAT_STRING = ('!QQ'  # tag, num_bytes_following (descriptor header)
-                   'L'    # rollback_index_location
-                   'L'    # partition_name_size (bytes)
+                   'L'  # rollback_index_location
+                   'L'  # partition_name_size (bytes)
                    'L' +  # public_key_size (bytes)
                    str(RESERVED) + 's')  # reserved
 
@@ -1829,20 +1756,17 @@
         raise LookupError('Given data does not look like a chain partition '
                           'descriptor.')
       o = 0
-      try:
-        self.partition_name = data[
-            (self.SIZE + o):(self.SIZE + o + partition_name_len)
-        ].decode('utf-8')
-      except UnicodeDecodeError as e:
-        raise LookupError('Partition name cannot be decoded as UTF-8: {}.'
-                          .format(e))
+      self.partition_name = str(data[(self.SIZE + o):(self.SIZE + o +
+                                                      partition_name_len)])
+      # Validate UTF-8 - decode() raises UnicodeDecodeError if not valid UTF-8.
+      self.partition_name.decode('utf-8')
       o += partition_name_len
       self.public_key = data[(self.SIZE + o):(self.SIZE + o + public_key_len)]
 
     else:
       self.rollback_index_location = 0
       self.partition_name = ''
-      self.public_key = b''
+      self.public_key = bytearray()
 
   def print_desc(self, o):
     """Print the descriptor.
@@ -1855,26 +1779,26 @@
     o.write('      Rollback Index Location: {}\n'.format(
         self.rollback_index_location))
     # Just show the SHA1 of the key, for size reasons.
-    pubkey_digest = hashlib.sha1(self.public_key).hexdigest()
-    o.write('      Public key (sha1):       {}\n'.format(pubkey_digest))
+    hexdig = hashlib.sha1(self.public_key).hexdigest()
+    o.write('      Public key (sha1):       {}\n'.format(hexdig))
 
   def encode(self):
     """Serializes the descriptor.
 
     Returns:
-      The descriptor data as bytes.
+      A bytearray() with the descriptor data.
     """
-    partition_name_encoded = self.partition_name.encode('utf-8')
+    encoded_name = self.partition_name.encode('utf-8')
     num_bytes_following = (
-        self.SIZE + len(partition_name_encoded) + len(self.public_key) - 16)
+        self.SIZE + len(encoded_name) + len(self.public_key) - 16)
     nbf_with_padding = round_to_multiple(num_bytes_following, 8)
     padding_size = nbf_with_padding - num_bytes_following
     desc = struct.pack(self.FORMAT_STRING, self.TAG, nbf_with_padding,
-                       self.rollback_index_location,
-                       len(partition_name_encoded), len(self.public_key),
-                       self.RESERVED * b'\0')
-    ret = desc + partition_name_encoded + self.public_key + padding_size * b'\0'
-    return ret
+                       self.rollback_index_location, len(encoded_name),
+                       len(self.public_key), self.RESERVED*'\0')
+    padding = struct.pack(str(padding_size) + 'x')
+    ret = desc + encoded_name + self.public_key + padding
+    return bytearray(ret)
 
   def verify(self, image_dir, image_ext, expected_chain_partitions_map,
              image_containing_descriptor, accept_zeroed_hashtree):
@@ -1930,7 +1854,7 @@
   """Parses a blob of data into descriptors.
 
   Arguments:
-    data: Encoded descriptors as bytes.
+    data: A bytearray() with encoded descriptors.
 
   Returns:
     A list of instances of objects derived from AvbDescriptor. For
@@ -1941,10 +1865,10 @@
   while o < len(data):
     tag, nb_following = struct.unpack('!2Q', data[o:o + 16])
     if tag < len(DESCRIPTOR_CLASSES):
-      clazz = DESCRIPTOR_CLASSES[tag]
+      c = DESCRIPTOR_CLASSES[tag]
     else:
-      clazz = AvbDescriptor
-    ret.append(clazz(data[o:o + 16 + nb_following]))
+      c = AvbDescriptor
+    ret.append(c(bytearray(data[o:o + 16 + nb_following])))
     o += 16 + nb_following
   return ret
 
@@ -1965,22 +1889,22 @@
     vbmeta_size: Size of the AvbVBMeta blob.
   """
 
-  MAGIC = b'AVBf'
+  MAGIC = 'AVBf'
   SIZE = 64
   RESERVED = 28
   FOOTER_VERSION_MAJOR = AVB_FOOTER_VERSION_MAJOR
   FOOTER_VERSION_MINOR = AVB_FOOTER_VERSION_MINOR
   FORMAT_STRING = ('!4s2L'  # magic, 2 x version.
-                   'Q'      # Original image size.
-                   'Q'      # Offset of VBMeta blob.
-                   'Q' +    # Size of VBMeta blob.
+                   'Q'  # Original image size.
+                   'Q'  # Offset of VBMeta blob.
+                   'Q' +  # Size of VBMeta blob.
                    str(RESERVED) + 'x')  # padding for reserved bytes
 
   def __init__(self, data=None):
     """Initializes a new footer object.
 
     Arguments:
-      data: If not None, must be bytes of size 4096.
+      data: If not None, must be a bytearray of size 4096.
 
     Raises:
       LookupError: If the given footer is malformed.
@@ -2003,10 +1927,10 @@
       self.vbmeta_size = 0
 
   def encode(self):
-    """Serializes the footer.
+    """Gets a string representing the binary encoding of the footer.
 
     Returns:
-      The footer as bytes.
+      A bytearray() with a binary representation of the footer.
     """
     return struct.pack(self.FORMAT_STRING, self.magic, self.version_major,
                        self.version_minor, self.original_image_size,
@@ -2048,33 +1972,31 @@
         older versions.
     flags: Flags from the AvbVBMetaImageFlags enumeration. This must be set to
         zero if the vbmeta image is not a top-level image.
-    rollback_index_location: The location of the rollback index defined in this
-        header. Only valid for the main vbmeta. For chained partitions, the
-        rollback index location must be specified in the
-        AvbChainPartitionDescriptor and this value must be set to 0.
     release_string: The release string from avbtool, e.g. "avbtool 1.0.0" or
         "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
         terminated. Applications must not make assumptions about how this
         string is formatted.
   """
-  MAGIC = b'AVB0'
+
   SIZE = 256
 
-  # Keep in sync with |reserved| field of |AvbVBMetaImageHeader|.
+  # Keep in sync with |reserved0| and |reserved| field of
+  # |AvbVBMetaImageHeader|.
+  RESERVED0 = 4
   RESERVED = 80
 
   # Keep in sync with |AvbVBMetaImageHeader|.
-  FORMAT_STRING = ('!4s2L'   # magic, 2 x version
-                   '2Q'      # 2 x block size
-                   'L'       # algorithm type
-                   '2Q'      # offset, size (hash)
-                   '2Q'      # offset, size (signature)
-                   '2Q'      # offset, size (public key)
-                   '2Q'      # offset, size (public key metadata)
-                   '2Q'      # offset, size (descriptors)
-                   'Q'       # rollback_index
-                   'L'       # flags
-                   'L'       # rollback_index_location
+  FORMAT_STRING = ('!4s2L'  # magic, 2 x version
+                   '2Q'  # 2 x block size
+                   'L'  # algorithm type
+                   '2Q'  # offset, size (hash)
+                   '2Q'  # offset, size (signature)
+                   '2Q'  # offset, size (public key)
+                   '2Q'  # offset, size (public key metadata)
+                   '2Q'  # offset, size (descriptors)
+                   'Q'  # rollback_index
+                   'L' +  # flags
+                   str(RESERVED0) + 'x' +  # padding for reserved bytes
                    '47sx' +  # NUL-terminated release string
                    str(RESERVED) + 'x')  # padding for reserved bytes
 
@@ -2100,14 +2022,12 @@
        self.descriptors_size,
        self.rollback_index,
        self.flags,
-       self.rollback_index_location,
-       release_string) = struct.unpack(self.FORMAT_STRING, data)
+       self.release_string) = struct.unpack(self.FORMAT_STRING, data)
       # Nuke NUL-bytes at the end of the string.
-      if self.magic != self.MAGIC:
+      if self.magic != 'AVB0':
         raise AvbError('Given image does not look like a vbmeta image.')
-      self.release_string = release_string.rstrip(b'\0').decode('utf-8')
     else:
-      self.magic = self.MAGIC
+      self.magic = 'AVB0'
       # Start by just requiring version 1.0. Code that adds features
       # in a future version can use bump_required_libavb_version_minor() to
       # bump the minor.
@@ -2128,7 +2048,6 @@
       self.descriptors_size = 0
       self.rollback_index = 0
       self.flags = 0
-      self.rollback_index_location = 0
       self.release_string = get_release_string()
 
   def bump_required_libavb_version_minor(self, minor):
@@ -2143,13 +2062,28 @@
     self.required_libavb_version_minor = (
         max(self.required_libavb_version_minor, minor))
 
+  def save(self, output):
+    """Serializes the header (256 bytes) to disk.
+
+    Arguments:
+      output: The object to write the output to.
+    """
+    output.write(struct.pack(
+        self.FORMAT_STRING, self.magic, self.required_libavb_version_major,
+        self.required_libavb_version_minor, self.authentication_data_block_size,
+        self.auxiliary_data_block_size, self.algorithm_type, self.hash_offset,
+        self.hash_size, self.signature_offset, self.signature_size,
+        self.public_key_offset, self.public_key_size,
+        self.public_key_metadata_offset, self.public_key_metadata_size,
+        self.descriptors_offset, self.descriptors_size, self.rollback_index,
+        self.flags, self.release_string))
+
   def encode(self):
-    """Serializes the header.
+    """Serializes the header (256) to a bytearray().
 
     Returns:
-      The header as bytes.
+      A bytearray() with the encoded header.
     """
-    release_string_encoded = self.release_string.encode('utf-8')
     return struct.pack(self.FORMAT_STRING, self.magic,
                        self.required_libavb_version_major,
                        self.required_libavb_version_minor,
@@ -2160,7 +2094,7 @@
                        self.public_key_size, self.public_key_metadata_offset,
                        self.public_key_metadata_size, self.descriptors_offset,
                        self.descriptors_size, self.rollback_index, self.flags,
-                       self.rollback_index_location, release_string_encoded)
+                       self.release_string)
 
 
 class Avb(object):
@@ -2168,7 +2102,7 @@
 
   # Keep in sync with avb_ab_flow.h.
   AB_FORMAT_NO_CRC = '!4sBB2xBBBxBBBx12x'
-  AB_MAGIC = b'\0AB0'
+  AB_MAGIC = '\0AB0'
   AB_MAJOR_VERSION = 1
   AB_MINOR_VERSION = 0
   AB_MISC_METADATA_OFFSET = 2048
@@ -2180,24 +2114,6 @@
   MAX_VBMETA_SIZE = 64 * 1024
   MAX_FOOTER_SIZE = 4096
 
-  def generate_test_image(self, output, image_size, start_byte):
-    """Generates a test image for testing avbtool with known content.
-
-    The content has following pattern: 0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
-
-    Arguments:
-      output: Write test image to this file.
-      image_size: The size of the requested file in bytes.
-      start_byte: The integer value of the start byte to use for pattern
-          generation.
-    """
-    pattern = bytearray([x & 0xFF for x in range(start_byte, start_byte + 256)])
-    buf = bytearray()
-    c = int(math.ceil(image_size / 256.0))
-    for _ in range(0, c):
-      buf.extend(pattern)
-    output.write(buf[0:image_size])
-
   def extract_vbmeta_image(self, output, image_filename, padding_size):
     """Implements the 'extract_vbmeta_image' command.
 
@@ -2209,8 +2125,10 @@
     Raises:
       AvbError: If there's no footer in the image.
     """
-    image = ImageHandler(image_filename, read_only=True)
+    image = ImageHandler(image_filename)
+
     (footer, _, _, _) = self._parse_image(image)
+
     if not footer:
       raise AvbError('Given image does not have a footer.')
 
@@ -2221,7 +2139,7 @@
     if padding_size > 0:
       padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
       padding_needed = padded_size - len(vbmeta_blob)
-      output.write(b'\0' * padding_needed)
+      output.write('\0' * padding_needed)
 
   def erase_footer(self, image_filename, keep_hashtree):
     """Implements the 'erase_footer' command.
@@ -2233,8 +2151,11 @@
     Raises:
       AvbError: If there's no footer in the image.
     """
+
     image = ImageHandler(image_filename)
+
     (footer, _, descriptors, _) = self._parse_image(image)
+
     if not footer:
       raise AvbError('Given image does not have a footer.')
 
@@ -2271,8 +2192,11 @@
     Raises:
       AvbError: If there's no footer in the image.
     """
+
     image = ImageHandler(image_filename)
+
     (footer, _, descriptors, _) = self._parse_image(image)
+
     if not footer:
       raise AvbError('Given image does not have a footer.')
 
@@ -2309,12 +2233,12 @@
     # Applications can use these markers to detect that the hashtree and/or
     # FEC needs to be recomputed.
     image.truncate(zero_ht_start_offset)
-    data_zeroed_firstblock = b'ZeRoHaSH' + b'\0' * (image.block_size - 8)
+    data_zeroed_firstblock = 'ZeRoHaSH' + '\0'*(image.block_size - 8)
     image.append_raw(data_zeroed_firstblock)
-    image.append_fill(b'\0\0\0\0', zero_ht_num_bytes - image.block_size)
+    image.append_fill('\0\0\0\0', zero_ht_num_bytes - image.block_size)
     if zero_fec_start_offset:
       image.append_raw(data_zeroed_firstblock)
-      image.append_fill(b'\0\0\0\0', zero_fec_num_bytes - image.block_size)
+      image.append_fill('\0\0\0\0', zero_fec_num_bytes - image.block_size)
     image.append_raw(data)
 
   def resize_image(self, image_filename, partition_size):
@@ -2329,38 +2253,42 @@
     """
 
     image = ImageHandler(image_filename)
+
     if partition_size % image.block_size != 0:
       raise AvbError('Partition size of {} is not a multiple of the image '
                      'block size {}.'.format(partition_size,
                                              image.block_size))
+
     (footer, _, _, _) = self._parse_image(image)
+
     if not footer:
       raise AvbError('Given image does not have a footer.')
 
     # The vbmeta blob is always at the end of the data so resizing an
     # image amounts to just moving the footer around.
+
     vbmeta_end_offset = footer.vbmeta_offset + footer.vbmeta_size
     if vbmeta_end_offset % image.block_size != 0:
       vbmeta_end_offset += image.block_size - (vbmeta_end_offset
                                                % image.block_size)
 
-    if partition_size < vbmeta_end_offset + 1 * image.block_size:
+    if partition_size < vbmeta_end_offset + 1*image.block_size:
       raise AvbError('Requested size of {} is too small for an image '
                      'of size {}.'
                      .format(partition_size,
-                             vbmeta_end_offset + 1 * image.block_size))
+                             vbmeta_end_offset + 1*image.block_size))
 
     # Cut at the end of the vbmeta blob and insert a DONT_CARE chunk
     # with enough bytes such that the final Footer block is at the end
     # of partition_size.
     image.truncate(vbmeta_end_offset)
     image.append_dont_care(partition_size - vbmeta_end_offset -
-                           1 * image.block_size)
+                           1*image.block_size)
 
     # Just reuse the same footer - only difference is that we're
     # writing it in a different place.
     footer_blob = footer.encode()
-    footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
+    footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
                                 footer_blob)
     image.append_raw(footer_blob_with_padding)
 
@@ -2382,10 +2310,10 @@
       raise AvbError('Malformed slot data "{}".'.format(slot_data))
     a_priority = int(tokens[0])
     a_tries_remaining = int(tokens[1])
-    a_success = int(tokens[2]) != 0
+    a_success = True if int(tokens[2]) != 0 else False
     b_priority = int(tokens[3])
     b_tries_remaining = int(tokens[4])
-    b_success = int(tokens[5]) != 0
+    b_success = True if int(tokens[5]) != 0 else False
 
     ab_data_no_crc = struct.pack(self.AB_FORMAT_NO_CRC,
                                  self.AB_MAGIC,
@@ -2398,16 +2326,18 @@
     misc_image.seek(self.AB_MISC_METADATA_OFFSET)
     misc_image.write(ab_data)
 
-  def info_image(self, image_filename, output, atx):
+  def info_image(self, image_filename, output):
     """Implements the 'info_image' command.
 
     Arguments:
       image_filename: Image file to get information from (file object).
       output: Output file to write human-readable information to (file object).
-      atx: If True, show information about Android Things eXtension (ATX).
     """
-    image = ImageHandler(image_filename, read_only=True)
+
+    image = ImageHandler(image_filename)
+
     o = output
+
     (footer, header, descriptors, image_size) = self._parse_image(image)
 
     # To show the SHA1 of the public key.
@@ -2444,9 +2374,8 @@
     o.write('Algorithm:                {}\n'.format(alg_name))
     o.write('Rollback Index:           {}\n'.format(header.rollback_index))
     o.write('Flags:                    {}\n'.format(header.flags))
-    o.write('Rollback Index Location:  {}\n'.format(
-        header.rollback_index_location))
-    o.write('Release String:           \'{}\'\n'.format(header.release_string))
+    o.write('Release String:           \'{}\'\n'.format(
+        header.release_string.rstrip('\0')))
 
     # Print descriptors.
     num_printed = 0
@@ -2457,31 +2386,6 @@
     if num_printed == 0:
       o.write('    (none)\n')
 
-    if atx and header.public_key_metadata_size:
-      o.write('Android Things eXtension (ATX):\n')
-      key_metadata_offset = (header.SIZE +
-                             header.authentication_data_block_size +
-                             header.public_key_metadata_offset)
-      key_metadata_blob = vbmeta_blob[key_metadata_offset: key_metadata_offset
-                                      + header.public_key_metadata_size]
-      version, pik, psk = struct.unpack('<I1620s1620s', key_metadata_blob)
-      o.write('    Metadata version:        {}\n'.format(version))
-
-      def print_atx_certificate(cert):
-        version, public_key, subject, usage, key_version, _signature = \
-            struct.unpack('<I1032s32s32sQ512s', cert)
-        o.write('      Version:               {}\n'.format(version))
-        o.write('      Public key (sha1):     {}\n'.format(
-            hashlib.sha1(public_key).hexdigest()))
-        o.write('      Subject:               {}\n'.format(subject.hex()))
-        o.write('      Usage:                 {}\n'.format(usage.hex()))
-        o.write('      Key version:           {}\n'.format(key_version))
-
-      o.write('    Product Intermediate Key:\n')
-      print_atx_certificate(pik)
-      o.write('    Product Signing Key:\n')
-      print_atx_certificate(psk)
-
   def verify_image(self, image_filename, key_path, expected_chain_partitions,
                    follow_chain_partitions, accept_zeroed_hashtree):
     """Implements the 'verify_image' command.
@@ -2509,8 +2413,7 @@
         partition_name = cp_tokens[0]
         rollback_index_location = int(cp_tokens[1])
         file_path = cp_tokens[2]
-        with open(file_path, 'rb') as f:
-          pk_blob = f.read()
+        pk_blob = open(file_path).read()
         expected_chain_partitions_map[partition_name] = (
             rollback_index_location, pk_blob)
 
@@ -2521,12 +2424,12 @@
     if key_path:
       print('Verifying image {} using key at {}'.format(image_filename,
                                                         key_path))
-      key_blob = RSAPublicKey(key_path).encode()
+      key_blob = encode_rsa_key(key_path)
     else:
       print('Verifying image {} using embedded public key'.format(
           image_filename))
 
-    image = ImageHandler(image_filename, read_only=True)
+    image = ImageHandler(image_filename)
     (footer, header, descriptors, _) = self._parse_image(image)
     offset = 0
     if footer:
@@ -2583,67 +2486,6 @@
         self.verify_image(chained_image_filename, key_path, None, False,
                           accept_zeroed_hashtree)
 
-  def print_partition_digests(self, image_filename, output, as_json):
-    """Implements the 'print_partition_digests' command.
-
-    Arguments:
-      image_filename: Image file to get information from (file object).
-      output: Output file to write human-readable information to (file object).
-      as_json: If True, print information as JSON
-
-    Raises:
-      AvbError: If getting the partition digests from the image fails.
-    """
-    image_dir = os.path.dirname(image_filename)
-    image_ext = os.path.splitext(image_filename)[1]
-    json_partitions = None
-    if as_json:
-      json_partitions = []
-    self._print_partition_digests(
-        image_filename, output, json_partitions, image_dir, image_ext)
-    if as_json:
-      output.write(json.dumps({'partitions': json_partitions}, indent=2))
-
-  def _print_partition_digests(self, image_filename, output, json_partitions,
-                               image_dir, image_ext):
-    """Helper for printing partitions.
-
-    Arguments:
-      image_filename: Image file to get information from (file object).
-      output: Output file to write human-readable information to (file object).
-      json_partitions: If not None, don't print to output, instead add partition
-          information to this list.
-      image_dir: The directory to use when looking for chained partition files.
-      image_ext: The extension to use for chained partition files.
-
-    Raises:
-      AvbError: If getting the partition digests from the image fails.
-    """
-    image = ImageHandler(image_filename, read_only=True)
-    (_, _, descriptors, _) = self._parse_image(image)
-
-    for desc in descriptors:
-      if isinstance(desc, AvbHashDescriptor):
-        digest = desc.digest.hex()
-        if json_partitions is not None:
-          json_partitions.append({'name': desc.partition_name,
-                                  'digest': digest})
-        else:
-          output.write('{}: {}\n'.format(desc.partition_name, digest))
-      elif isinstance(desc, AvbHashtreeDescriptor):
-        digest = desc.root_digest.hex()
-        if json_partitions is not None:
-          json_partitions.append({'name': desc.partition_name,
-                                  'digest': digest})
-        else:
-          output.write('{}: {}\n'.format(desc.partition_name, digest))
-      elif isinstance(desc, AvbChainPartitionDescriptor):
-        chained_image_filename = os.path.join(image_dir,
-                                              desc.partition_name + image_ext)
-        self._print_partition_digests(
-            chained_image_filename, output, json_partitions, image_dir,
-            image_ext)
-
   def calculate_vbmeta_digest(self, image_filename, hash_algorithm, output):
     """Implements the 'calculate_vbmeta_digest' command.
 
@@ -2656,7 +2498,7 @@
     image_dir = os.path.dirname(image_filename)
     image_ext = os.path.splitext(image_filename)[1]
 
-    image = ImageHandler(image_filename, read_only=True)
+    image = ImageHandler(image_filename)
     (footer, header, descriptors, _) = self._parse_image(image)
     offset = 0
     if footer:
@@ -2666,14 +2508,14 @@
     image.seek(offset)
     vbmeta_blob = image.read(size)
 
-    hasher = hashlib.new(hash_algorithm)
+    hasher = hashlib.new(name=hash_algorithm)
     hasher.update(vbmeta_blob)
 
     for desc in descriptors:
       if isinstance(desc, AvbChainPartitionDescriptor):
         ch_image_filename = os.path.join(image_dir,
                                          desc.partition_name + image_ext)
-        ch_image = ImageHandler(ch_image_filename, read_only=True)
+        ch_image = ImageHandler(ch_image_filename)
         (ch_footer, ch_header, _, _) = self._parse_image(ch_image)
         ch_offset = 0
         ch_size = (ch_header.SIZE + ch_header.authentication_data_block_size +
@@ -2685,7 +2527,7 @@
         hasher.update(ch_vbmeta_blob)
 
     digest = hasher.digest()
-    output.write('{}\n'.format(digest.hex()))
+    output.write('{}\n'.format(binascii.hexlify(digest)))
 
   def calculate_kernel_cmdline(self, image_filename, hashtree_disabled, output):
     """Implements the 'calculate_kernel_cmdline' command.
@@ -2696,7 +2538,7 @@
       output: Output file to write human-readable information to (file object).
     """
 
-    image = ImageHandler(image_filename, read_only=True)
+    image = ImageHandler(image_filename)
     _, _, descriptors, _ = self._parse_image(image)
 
     image_dir = os.path.dirname(image_filename)
@@ -2707,7 +2549,7 @@
       if isinstance(desc, AvbChainPartitionDescriptor):
         ch_image_filename = os.path.join(image_dir,
                                          desc.partition_name + image_ext)
-        ch_image = ImageHandler(ch_image_filename, read_only=True)
+        ch_image = ImageHandler(ch_image_filename)
         _, _, ch_descriptors, _ = self._parse_image(ch_image)
         for ch_desc in ch_descriptors:
           if isinstance(ch_desc, AvbKernelCmdlineDescriptor):
@@ -2818,19 +2660,20 @@
       when it is.
 
     """
+
     c = 'dm="1 vroot none ro 1,'
-    c += '0'                                                # start
-    c += ' {}'.format((ht.image_size // 512))               # size (# sectors)
-    c += ' verity {}'.format(ht.dm_verity_version)          # type and version
-    c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'             # data_dev
-    c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'             # hash_dev
-    c += ' {}'.format(ht.data_block_size)                   # data_block
-    c += ' {}'.format(ht.hash_block_size)                   # hash_block
+    c += '0'  # start
+    c += ' {}'.format((ht.image_size // 512))  # size (# sectors)
+    c += ' verity {}'.format(ht.dm_verity_version)  # type and version
+    c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'  # data_dev
+    c += ' PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'  # hash_dev
+    c += ' {}'.format(ht.data_block_size)  # data_block
+    c += ' {}'.format(ht.hash_block_size)  # hash_block
     c += ' {}'.format(ht.image_size // ht.data_block_size)  # #blocks
     c += ' {}'.format(ht.image_size // ht.data_block_size)  # hash_offset
-    c += ' {}'.format(ht.hash_algorithm)                    # hash_alg
-    c += ' {}'.format(ht.root_digest.hex())                 # root_digest
-    c += ' {}'.format(ht.salt.hex())                        # salt
+    c += ' {}'.format(ht.hash_algorithm)  # hash_alg
+    c += ' {}'.format(str(ht.root_digest).encode('hex'))  # root_digest
+    c += ' {}'.format(str(ht.salt).encode('hex'))  # salt
     if ht.fec_num_roots > 0:
       c += ' 10'  # number of optional args
       c += ' $(ANDROID_VERITY_MODE)'
@@ -2878,6 +2721,7 @@
       AvbError: If  |image| doesn't have a hashtree descriptor.
 
     """
+
     (_, _, descriptors, _) = self._parse_image(image)
 
     ht = None
@@ -2893,8 +2737,7 @@
 
   def make_vbmeta_image(self, output, chain_partitions, algorithm_name,
                         key_path, public_key_metadata_path, rollback_index,
-                        flags, rollback_index_location,
-                        props, props_from_file, kernel_cmdlines,
+                        flags, props, props_from_file, kernel_cmdlines,
                         setup_rootfs_from_kernel,
                         include_descriptors_from_image,
                         signing_helper,
@@ -2913,7 +2756,6 @@
       public_key_metadata_path: Path to public key metadata or None.
       rollback_index: The rollback index to use.
       flags: Flags value to use in the image.
-      rollback_index_location: Location of the main vbmeta rollback index.
       props: Properties to insert (list of strings of the form 'key:value').
       props_from_file: Properties to insert (list of strings 'key:<path>').
       kernel_cmdlines: Kernel cmdlines to insert (list of strings).
@@ -2929,21 +2771,21 @@
     Raises:
       AvbError: If a chained partition is malformed.
     """
-    # If we're asked to calculate minimum required libavb version, we're done.
-    tmp_header = AvbVBMetaHeader()
-    if rollback_index_location > 0:
-      tmp_header.bump_required_libavb_version_minor(2)
-    if include_descriptors_from_image:
-      # 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, read_only=True))
-        tmp_header.bump_required_libavb_version_minor(
-            image_header.required_libavb_version_minor)
 
+    # If we're asked to calculate minimum required libavb version, we're done.
     if print_required_libavb_version:
-      print('1.{}'.format(tmp_header.required_libavb_version_minor))
+      if include_descriptors_from_image:
+        # Use the bump logic in AvbVBMetaHeader to calculate the max required
+        # version of all included descriptors.
+        tmp_header = AvbVBMetaHeader()
+        for image in include_descriptors_from_image:
+          (_, image_header, _, _) = self._parse_image(ImageHandler(image.name))
+          tmp_header.bump_required_libavb_version_minor(
+              image_header.required_libavb_version_minor)
+        print('1.{}'.format(tmp_header.required_libavb_version_minor))
+      else:
+        # Descriptors aside, all vbmeta features are supported in 1.0.
+        print('1.0')
       return
 
     if not output:
@@ -2953,12 +2795,11 @@
     ht_desc_to_setup = None
     vbmeta_blob = self._generate_vbmeta_blob(
         algorithm_name, key_path, public_key_metadata_path, descriptors,
-        chain_partitions, rollback_index, flags, rollback_index_location,
-        props, props_from_file,
+        chain_partitions, rollback_index, flags, props, props_from_file,
         kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
         include_descriptors_from_image, signing_helper,
         signing_helper_with_files, release_string,
-        append_to_release_string, tmp_header.required_libavb_version_minor)
+        append_to_release_string, 0)
 
     # Write entire vbmeta blob (header, authentication, auxiliary).
     output.seek(0)
@@ -2967,13 +2808,12 @@
     if padding_size > 0:
       padded_size = round_to_multiple(len(vbmeta_blob), padding_size)
       padding_needed = padded_size - len(vbmeta_blob)
-      output.write(b'\0' * padding_needed)
+      output.write('\0' * padding_needed)
 
   def _generate_vbmeta_blob(self, algorithm_name, key_path,
                             public_key_metadata_path, descriptors,
                             chain_partitions,
-                            rollback_index, flags, rollback_index_location,
-                            props, props_from_file,
+                            rollback_index, flags, props, props_from_file,
                             kernel_cmdlines,
                             setup_rootfs_from_kernel,
                             ht_desc_to_setup,
@@ -2999,7 +2839,6 @@
       chain_partitions: List of partitions to chain or None.
       rollback_index: The rollback index to use.
       flags: Flags to use in the image.
-      rollback_index_location: Location of the main vbmeta rollback index.
       props: Properties to insert (List of strings of the form 'key:value').
       props_from_file: Properties to insert (List of strings 'key:<path>').
       kernel_cmdlines: Kernel cmdlines to insert (list of strings).
@@ -3016,12 +2855,13 @@
       required_libavb_version_minor: Use at least this required minor version.
 
     Returns:
-      The VBMeta blob as bytes.
+      A bytearray() with the VBMeta blob.
 
     Raises:
       Exception: If the |algorithm_name| is not found, if no key has
         been given and the given algorithm requires one, or the key is
         of the wrong size.
+
     """
     try:
       alg = ALGORITHMS[algorithm_name]
@@ -3036,27 +2876,26 @@
 
     # Insert chained partition descriptors, if any
     if chain_partitions:
-      used_locations = {rollback_index_location: True}
+      used_locations = {}
       for cp in chain_partitions:
         cp_tokens = cp.split(':')
         if len(cp_tokens) != 3:
           raise AvbError('Malformed chained partition "{}".'.format(cp))
         partition_name = cp_tokens[0]
-        chained_rollback_index_location = int(cp_tokens[1])
+        rollback_index_location = int(cp_tokens[1])
         file_path = cp_tokens[2]
         # Check that the same rollback location isn't being used by
         # multiple chained partitions.
-        if used_locations.get(chained_rollback_index_location):
+        if used_locations.get(rollback_index_location):
           raise AvbError('Rollback Index Location {} is already in use.'.format(
-              chained_rollback_index_location))
-        used_locations[chained_rollback_index_location] = True
+              rollback_index_location))
+        used_locations[rollback_index_location] = True
         desc = AvbChainPartitionDescriptor()
         desc.partition_name = partition_name
-        desc.rollback_index_location = chained_rollback_index_location
+        desc.rollback_index_location = rollback_index_location
         if desc.rollback_index_location < 1:
           raise AvbError('Rollback index location must be 1 or larger.')
-        with open(file_path, 'rb') as f:
-          desc.public_key = f.read()
+        desc.public_key = open(file_path, 'rb').read()
         descriptors.append(desc)
 
     # Descriptors.
@@ -3073,7 +2912,7 @@
         # pylint: disable=redefined-variable-type
         desc = AvbPropertyDescriptor()
         desc.key = prop[0:idx]
-        desc.value = prop[(idx + 1):].encode('utf-8')
+        desc.value = prop[(idx + 1):]
         encoded_descriptors.extend(desc.encode())
     if props_from_file:
       for prop in props_from_file:
@@ -3082,10 +2921,9 @@
           raise AvbError('Malformed property "{}".'.format(prop))
         desc = AvbPropertyDescriptor()
         desc.key = prop[0:idx]
+        desc.value = prop[(idx + 1):]
         file_path = prop[(idx + 1):]
-        with open(file_path, 'rb') as f:
-          # pylint: disable=attribute-defined-outside-init
-          desc.value = f.read()
+        desc.value = open(file_path, 'rb').read()
         encoded_descriptors.extend(desc.encode())
 
     # Add AvbKernelCmdline descriptor for dm-verity from an image, if requested.
@@ -3114,7 +2952,7 @@
     if include_descriptors_from_image:
       descriptors_dict = dict()
       for image in include_descriptors_from_image:
-        image_handler = ImageHandler(image.name, read_only=True)
+        image_handler = ImageHandler(image.name)
         (_, image_vbmeta_header, image_descriptors, _) = self._parse_image(
             image_handler)
         # Bump the required libavb version to support all included descriptors.
@@ -3135,28 +2973,29 @@
         encoded_descriptors.extend(descriptors_dict[key])
 
     # Load public key metadata blob, if requested.
-    pkmd_blob = b''
+    pkmd_blob = []
     if public_key_metadata_path:
-      with open(public_key_metadata_path, 'rb') as f:
+      with open(public_key_metadata_path) as f:
         pkmd_blob = f.read()
 
     key = None
-    encoded_key = b''
+    encoded_key = bytearray()
     if alg.public_key_num_bytes > 0:
       if not key_path:
         raise AvbError('Key is required for algorithm {}'.format(
             algorithm_name))
-      encoded_key = RSAPublicKey(key_path).encode()
+      encoded_key = encode_rsa_key(key_path)
       if len(encoded_key) != alg.public_key_num_bytes:
         raise AvbError('Key is wrong size for algorithm {}'.format(
             algorithm_name))
 
     # Override release string, if requested.
-    if isinstance(release_string, str):
+    # pylint: disable=unicode-builtin
+    if isinstance(release_string, (str, unicode)):
       h.release_string = release_string
 
     # Append to release string, if requested. Also insert a space before.
-    if isinstance(append_to_release_string, str):
+    if isinstance(append_to_release_string, (str, unicode)):
       h.release_string += ' ' + append_to_release_string
 
     # For the Auxiliary data block, descriptors are stored at offset 0,
@@ -3184,7 +3023,6 @@
 
     h.rollback_index = rollback_index
     h.flags = flags
-    h.rollback_index_location = rollback_index_location
 
     # Generate Header data block.
     header_data_blob = h.encode()
@@ -3195,31 +3033,33 @@
     aux_data_blob.extend(encoded_key)
     aux_data_blob.extend(pkmd_blob)
     padding_bytes = h.auxiliary_data_block_size - len(aux_data_blob)
-    aux_data_blob.extend(b'\0' * padding_bytes)
+    aux_data_blob.extend('\0' * padding_bytes)
 
     # Calculate the hash.
-    binary_hash = b''
-    binary_signature = b''
+    binary_hash = bytearray()
+    binary_signature = bytearray()
     if algorithm_name != 'NONE':
       ha = hashlib.new(alg.hash_name)
       ha.update(header_data_blob)
       ha.update(aux_data_blob)
-      binary_hash = ha.digest()
+      binary_hash.extend(ha.digest())
 
       # Calculate the signature.
-      rsa_key = RSAPublicKey(key_path)
-      data_to_sign = header_data_blob + bytes(aux_data_blob)
-      binary_signature = rsa_key.sign(algorithm_name, data_to_sign,
-                                      signing_helper, signing_helper_with_files)
+      padding_and_hash = str(bytearray(alg.padding)) + binary_hash
+      binary_signature.extend(raw_sign(signing_helper,
+                                       signing_helper_with_files,
+                                       algorithm_name,
+                                       alg.signature_num_bytes, key_path,
+                                       padding_and_hash))
 
     # Generate Authentication data block.
     auth_data_blob = bytearray()
     auth_data_blob.extend(binary_hash)
     auth_data_blob.extend(binary_signature)
     padding_bytes = h.authentication_data_block_size - len(auth_data_blob)
-    auth_data_blob.extend(b'\0' * padding_bytes)
+    auth_data_blob.extend('\0' * padding_bytes)
 
-    return header_data_blob + bytes(auth_data_blob) + bytes(aux_data_blob)
+    return header_data_blob + auth_data_blob + aux_data_blob
 
   def extract_public_key(self, key_path, output):
     """Implements the 'extract_public_key' command.
@@ -3227,11 +3067,8 @@
     Arguments:
       key_path: The path to a RSA private key file.
       output: The file to write to.
-
-    Raises:
-      AvbError: If the public key could not be extracted.
     """
-    output.write(RSAPublicKey(key_path).encode())
+    output.write(encode_rsa_key(key_path))
 
   def append_vbmeta_image(self, image_filename, vbmeta_image_filename,
                           partition_size):
@@ -3243,7 +3080,7 @@
       partition_size: Size of partition.
 
     Raises:
-      AvbError: If an argument is incorrect or if appending VBMeta image fialed.
+      AvbError: If an argument is incorrect.
     """
     image = ImageHandler(image_filename)
 
@@ -3287,7 +3124,7 @@
       vbmeta_offset = image.image_size
       padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
                         len(vbmeta_blob))
-      vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
+      vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
 
       # Append vbmeta blob and footer
       image.append_raw(vbmeta_blob_with_padding)
@@ -3296,7 +3133,7 @@
       # Now insert a DONT_CARE chunk with enough bytes such that the
       # final Footer block is at the end of partition_size..
       image.append_dont_care(partition_size - vbmeta_end_offset -
-                             1 * image.block_size)
+                             1*image.block_size)
 
       # Generate the Footer that tells where the VBMeta footer
       # is. Also put enough padding in the front of the footer since
@@ -3306,20 +3143,19 @@
       footer.vbmeta_offset = vbmeta_offset
       footer.vbmeta_size = len(vbmeta_blob)
       footer_blob = footer.encode()
-      footer_blob_with_padding = (b'\0' * (image.block_size - AvbFooter.SIZE) +
+      footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
                                   footer_blob)
       image.append_raw(footer_blob_with_padding)
 
-    except Exception as e:
-      # Truncate back to original size, then re-raise.
+    except:
+      # Truncate back to original size, then re-raise
       image.truncate(original_image_size)
-      raise AvbError('Appending VBMeta image failed: {}.'.format(e))
+      raise
 
   def add_hash_footer(self, image_filename, partition_size, partition_name,
                       hash_algorithm, salt, chain_partitions, algorithm_name,
                       key_path,
-                      public_key_metadata_path, rollback_index, flags,
-                      rollback_index_location, props,
+                      public_key_metadata_path, rollback_index, flags, props,
                       props_from_file, kernel_cmdlines,
                       setup_rootfs_from_kernel,
                       include_descriptors_from_image, calc_max_image_size,
@@ -3342,7 +3178,6 @@
       public_key_metadata_path: Path to public key metadata or None.
       rollback_index: Rollback index.
       flags: Flags value to use in the image.
-      rollback_index_location: Location of the main vbmeta rollback index.
       props: Properties to insert (List of strings of the form 'key:value').
       props_from_file: Properties to insert (List of strings 'key:<path>').
       kernel_cmdlines: Kernel cmdlines to insert (list of strings).
@@ -3364,13 +3199,12 @@
       do_not_use_ab: This partition does not use A/B.
 
     Raises:
-      AvbError: If an argument is incorrect of if adding of hash_footer failed.
+      AvbError: If an argument is incorrect.
     """
+
     required_libavb_version_minor = 0
     if use_persistent_digest or do_not_use_ab:
       required_libavb_version_minor = 1
-    if rollback_index_location > 0:
-      required_libavb_version_minor = 2
 
     # If we're asked to calculate minimum required libavb version, we're done.
     if print_required_libavb_version:
@@ -3425,7 +3259,7 @@
                        'size of {}.'.format(image.image_size, max_image_size,
                                             partition_size))
 
-      digest_size = len(hashlib.new(hash_algorithm).digest())
+      digest_size = len(hashlib.new(name=hash_algorithm).digest())
       if salt:
         salt = binascii.unhexlify(salt)
       elif salt is None and not use_persistent_digest:
@@ -3433,12 +3267,11 @@
         # size as the hash size. Don't populate a random salt if this
         # descriptor is being created to use a persistent digest on device.
         hash_size = digest_size
-        with open('/dev/urandom', 'rb') as f:
-          salt = f.read(hash_size)
+        salt = open('/dev/urandom').read(hash_size)
       else:
-        salt = b''
+        salt = ''
 
-      hasher = hashlib.new(hash_algorithm, salt)
+      hasher = hashlib.new(name=hash_algorithm, string=salt)
       # TODO(zeuthen): might want to read this in chunks to avoid
       # memory pressure, then again, this is only supposed to be used
       # on kernel/initramfs partitions. Possible optimization.
@@ -3461,8 +3294,7 @@
       ht_desc_to_setup = None
       vbmeta_blob = self._generate_vbmeta_blob(
           algorithm_name, key_path, public_key_metadata_path, [h_desc],
-          chain_partitions, rollback_index, flags, rollback_index_location,
-          props, props_from_file,
+          chain_partitions, rollback_index, flags, props, props_from_file,
           kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
           include_descriptors_from_image, signing_helper,
           signing_helper_with_files, release_string,
@@ -3490,7 +3322,7 @@
         padding_needed = (
             round_to_multiple(len(vbmeta_blob), image.block_size) -
             len(vbmeta_blob))
-        vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
+        vbmeta_blob_with_padding = vbmeta_blob + '\0' * padding_needed
 
         image.append_raw(vbmeta_blob_with_padding)
         vbmeta_end_offset = vbmeta_offset + len(vbmeta_blob_with_padding)
@@ -3498,7 +3330,7 @@
         # Now insert a DONT_CARE chunk with enough bytes such that the
         # final Footer block is at the end of partition_size..
         image.append_dont_care(partition_size - vbmeta_end_offset -
-                               1 * image.block_size)
+                               1*image.block_size)
 
         # Generate the Footer that tells where the VBMeta footer
         # is. Also put enough padding in the front of the footer since
@@ -3508,20 +3340,20 @@
         footer.vbmeta_offset = vbmeta_offset
         footer.vbmeta_size = len(vbmeta_blob)
         footer_blob = footer.encode()
-        footer_blob_with_padding = (
-            b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
+        footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
+                                    footer_blob)
         image.append_raw(footer_blob_with_padding)
-    except Exception as e:
-      # Truncate back to original size, then re-raise.
+
+    except:
+      # Truncate back to original size, then re-raise
       image.truncate(original_image_size)
-      raise AvbError('Adding hash_footer failed: {}.'.format(e))
+      raise
 
   def add_hashtree_footer(self, image_filename, partition_size, partition_name,
                           generate_fec, fec_num_roots, hash_algorithm,
                           block_size, salt, chain_partitions, algorithm_name,
                           key_path,
                           public_key_metadata_path, rollback_index, flags,
-                          rollback_index_location,
                           props, props_from_file, kernel_cmdlines,
                           setup_rootfs_from_kernel,
                           setup_as_rootfs_from_kernel,
@@ -3553,7 +3385,6 @@
       public_key_metadata_path: Path to public key metadata or None.
       rollback_index: Rollback index.
       flags: Flags value to use in the image.
-      rollback_index_location: Location of the main vbmeta rollback index.
       props: Properties to insert (List of strings of the form 'key:value').
       props_from_file: Properties to insert (List of strings 'key:<path>').
       kernel_cmdlines: Kernel cmdlines to insert (list of strings).
@@ -3578,22 +3409,19 @@
       no_hashtree: Do not append hashtree. Set size in descriptor as zero.
 
     Raises:
-      AvbError: If an argument is incorrect or adding the hashtree footer
-          failed.
+      AvbError: If an argument is incorrect.
     """
+
     required_libavb_version_minor = 0
     if use_persistent_root_digest or do_not_use_ab:
       required_libavb_version_minor = 1
-    if rollback_index_location > 0:
-      required_libavb_version_minor = 2
 
     # If we're asked to calculate minimum required libavb version, we're done.
     if print_required_libavb_version:
       print('1.{}'.format(required_libavb_version_minor))
       return
 
-    digest_size = len(create_avb_hashtree_hasher(hash_algorithm, b'')
-                      .digest())
+    digest_size = len(hashlib.new(name=hash_algorithm).digest())
     digest_padding = round_to_pow2(digest_size) - digest_size
 
     # If |partition_size| is given (e.g. not 0), calculate the maximum image
@@ -3671,10 +3499,9 @@
         # size as the hash size. Don't populate a random salt if this
         # descriptor is being created to use a persistent digest on device.
         hash_size = digest_size
-        with open('/dev/urandom', 'rb') as f:
-          salt = f.read(hash_size)
+        salt = open('/dev/urandom').read(hash_size)
       else:
-        salt = b''
+        salt = ''
 
       # Hashes are stored upside down so we need to calculate hash
       # offsets in advance.
@@ -3701,7 +3528,7 @@
       # just generated.
       if no_hashtree:
         tree_size = 0
-        hash_tree = b''
+        hash_tree = bytearray()
       ht_desc = AvbHashtreeDescriptor()
       ht_desc.dm_verity_version = 1
       ht_desc.image_size = image.image_size
@@ -3720,19 +3547,19 @@
       # Write the hash tree
       padding_needed = (round_to_multiple(len(hash_tree), image.block_size) -
                         len(hash_tree))
-      hash_tree_with_padding = hash_tree + b'\0' * padding_needed
+      hash_tree_with_padding = hash_tree + '\0'*padding_needed
       image.append_raw(hash_tree_with_padding)
       len_hashtree_and_fec = len(hash_tree_with_padding)
 
       # Generate FEC codes, if requested.
       if generate_fec:
         if no_hashtree:
-          fec_data = b''
+          fec_data = bytearray()
         else:
           fec_data = generate_fec_data(image_filename, fec_num_roots)
         padding_needed = (round_to_multiple(len(fec_data), image.block_size) -
                           len(fec_data))
-        fec_data_with_padding = fec_data + b'\0' * padding_needed
+        fec_data_with_padding = fec_data + '\0'*padding_needed
         fec_offset = image.image_size
         image.append_raw(fec_data_with_padding)
         len_hashtree_and_fec += len(fec_data_with_padding)
@@ -3749,15 +3576,14 @@
       vbmeta_offset = tree_offset + len_hashtree_and_fec
       vbmeta_blob = self._generate_vbmeta_blob(
           algorithm_name, key_path, public_key_metadata_path, [ht_desc],
-          chain_partitions, rollback_index, flags, rollback_index_location,
-          props, props_from_file,
+          chain_partitions, rollback_index, flags, props, props_from_file,
           kernel_cmdlines, setup_rootfs_from_kernel, ht_desc_to_setup,
           include_descriptors_from_image, signing_helper,
           signing_helper_with_files, release_string,
           append_to_release_string, required_libavb_version_minor)
       padding_needed = (round_to_multiple(len(vbmeta_blob), image.block_size) -
                         len(vbmeta_blob))
-      vbmeta_blob_with_padding = vbmeta_blob + b'\0' * padding_needed
+      vbmeta_blob_with_padding = vbmeta_blob + '\0'*padding_needed
 
       # Write vbmeta blob, if requested.
       if output_vbmeta_image:
@@ -3771,7 +3597,7 @@
         # final Footer block is at the end of partition_size..
         if partition_size > 0:
           image.append_dont_care(partition_size - image.image_size -
-                                 1 * image.block_size)
+                                 1*image.block_size)
 
         # Generate the Footer that tells where the VBMeta footer
         # is. Also put enough padding in the front of the footer since
@@ -3781,14 +3607,14 @@
         footer.vbmeta_offset = vbmeta_offset
         footer.vbmeta_size = len(vbmeta_blob)
         footer_blob = footer.encode()
-        footer_blob_with_padding = (
-            b'\0' * (image.block_size - AvbFooter.SIZE) + footer_blob)
+        footer_blob_with_padding = ('\0'*(image.block_size - AvbFooter.SIZE) +
+                                    footer_blob)
         image.append_raw(footer_blob_with_padding)
 
-    except Exception as e:
+    except:
       # Truncate back to original size, then re-raise.
       image.truncate(original_image_size)
-      raise AvbError('Adding hashtree_footer failed: {}.'.format(e))
+      raise
 
   def make_atx_certificate(self, output, authority_key_path, subject_key_path,
                            subject_key_version, subject,
@@ -3817,13 +3643,10 @@
       usage: If not empty, overrides the cert usage with a hash of this value.
       signing_helper: Program which signs a hash and returns the signature.
       signing_helper_with_files: Same as signing_helper but uses files instead.
-
-    Raises:
-      AvbError: If there an error during signing.
     """
     signed_data = bytearray()
     signed_data.extend(struct.pack('<I', 1))  # Format Version
-    signed_data.extend(RSAPublicKey(subject_key_path).encode())
+    signed_data.extend(encode_rsa_key(subject_key_path))
     hasher = hashlib.sha256()
     hasher.update(subject)
     signed_data.extend(hasher.digest())
@@ -3832,17 +3655,24 @@
       if is_intermediate_authority:
         usage += '.ca'
     hasher = hashlib.sha256()
-    hasher.update(usage.encode('ascii'))
+    hasher.update(usage)
     signed_data.extend(hasher.digest())
     if subject_key_version is None:
       subject_key_version = int(time.time())
     signed_data.extend(struct.pack('<Q', subject_key_version))
-    signature = b''
+    signature = bytearray()
     if authority_key_path:
-      rsa_key = RSAPublicKey(authority_key_path)
+      padding_and_hash = bytearray()
       algorithm_name = 'SHA512_RSA4096'
-      signature = rsa_key.sign(algorithm_name, signed_data, signing_helper,
-                               signing_helper_with_files)
+      alg = ALGORITHMS[algorithm_name]
+      hasher = hashlib.sha512()  # pylint: disable=redefined-variable-type
+      padding_and_hash.extend(alg.padding)
+      hasher.update(signed_data)
+      padding_and_hash.extend(hasher.digest())
+      signature.extend(raw_sign(signing_helper, signing_helper_with_files,
+                                algorithm_name,
+                                alg.signature_num_bytes, authority_key_path,
+                                padding_and_hash))
     output.write(signed_data)
     output.write(signature)
 
@@ -3867,7 +3697,7 @@
     if len(product_id) != EXPECTED_PRODUCT_ID_SIZE:
       raise AvbError('Invalid Product ID length.')
     output.write(struct.pack('<I', 1))  # Format Version
-    output.write(RSAPublicKey(root_authority_key_path).encode())
+    output.write(encode_rsa_key(root_authority_key_path))
     output.write(product_id)
 
   def make_atx_metadata(self, output, intermediate_key_certificate,
@@ -3928,7 +3758,7 @@
       signing_helper_with_files: Same as signing_helper but uses files instead.
 
     Raises:
-      AvbError: If an argument is incorrect or an error occurs during signing.
+      AvbError: If an argument is incorrect.
     """
     EXPECTED_CERTIFICATE_SIZE = 1620  # pylint: disable=invalid-name
     EXPECTED_CHALLENGE_SIZE = 16  # pylint: disable=invalid-name
@@ -3936,9 +3766,9 @@
       raise AvbError('Invalid intermediate key certificate length.')
     if len(unlock_key_certificate) != EXPECTED_CERTIFICATE_SIZE:
       raise AvbError('Invalid product key certificate length.')
-    challenge = b''
+    challenge = bytearray()
     if challenge_path:
-      with open(challenge_path, 'rb') as f:
+      with open(challenge_path, 'r') as f:
         challenge = f.read()
       if len(challenge) != EXPECTED_CHALLENGE_SIZE:
         raise AvbError('Invalid unlock challenge length.')
@@ -3946,10 +3776,18 @@
     output.write(intermediate_key_certificate)
     output.write(unlock_key_certificate)
     if challenge_path and unlock_key_path:
-      rsa_key = RSAPublicKey(unlock_key_path)
+      signature = bytearray()
+      padding_and_hash = bytearray()
       algorithm_name = 'SHA512_RSA4096'
-      signature = rsa_key.sign(algorithm_name, challenge, signing_helper,
-                               signing_helper_with_files)
+      alg = ALGORITHMS[algorithm_name]
+      hasher = hashlib.sha512()
+      padding_and_hash.extend(alg.padding)
+      hasher.update(challenge)
+      padding_and_hash.extend(hasher.digest())
+      signature.extend(raw_sign(signing_helper, signing_helper_with_files,
+                                algorithm_name,
+                                alg.signature_num_bytes, unlock_key_path,
+                                padding_and_hash))
       output.write(signature)
 
 
@@ -4008,6 +3846,7 @@
 
   Raises:
     ValueError: If output from the 'fec' tool is invalid.
+
   """
   p = subprocess.Popen(
       ['fec', '--print-fec-size', str(image_size), '--roots', str(num_roots)],
@@ -4028,21 +3867,17 @@
     num_roots: Number of roots.
 
   Returns:
-    The FEC data blob as bytes.
+    The FEC data blob.
 
   Raises:
-    ValueError: If calling the 'fec' tool failed or the output is invalid.
+    ValueError: If output from the 'fec' tool is invalid.
   """
-  with tempfile.NamedTemporaryFile() as fec_tmpfile:
-    try:
-      subprocess.check_call(
-          ['fec', '--encode', '--roots', str(num_roots), image_filename,
-           fec_tmpfile.name],
-          stderr=open(os.devnull, 'wb'))
-    except subprocess.CalledProcessError as e:
-      raise ValueError('Execution of \'fec\' tool failed: {}.'.format(e))
-    fec_data = fec_tmpfile.read()
-
+  fec_tmpfile = tempfile.NamedTemporaryFile()
+  subprocess.check_call(
+      ['fec', '--encode', '--roots', str(num_roots), image_filename,
+       fec_tmpfile.name],
+      stderr=open(os.devnull))
+  fec_data = fec_tmpfile.read()
   footer_size = struct.calcsize(FEC_FOOTER_FORMAT)
   footer_data = fec_data[-footer_size:]
   (magic, _, _, num_roots, fec_size, _, _) = struct.unpack(FEC_FOOTER_FORMAT,
@@ -4067,8 +3902,8 @@
     tree_size: The size of the tree, in number of bytes.
 
   Returns:
-    A tuple where the first element is the top-level hash as bytes and the
-    second element is the hash-tree as bytes.
+    A tuple where the first element is the top-level hash and the
+    second element is the hash-tree.
   """
   hash_ret = bytearray(tree_size)
   hash_src_offset = 0
@@ -4078,7 +3913,7 @@
     level_output_list = []
     remaining = hash_src_size
     while remaining > 0:
-      hasher = create_avb_hashtree_hasher(hash_alg_name, salt)
+      hasher = hashlib.new(name=hash_alg_name, string=salt)
       # Only read from the file for the first level - for subsequent
       # levels, access the array we're building.
       if level_num == 0:
@@ -4091,16 +3926,16 @@
 
       remaining -= len(data)
       if len(data) < block_size:
-        hasher.update(b'\0' * (block_size - len(data)))
+        hasher.update('\0' * (block_size - len(data)))
       level_output_list.append(hasher.digest())
       if digest_padding > 0:
-        level_output_list.append(b'\0' * digest_padding)
+        level_output_list.append('\0' * digest_padding)
 
-    level_output = b''.join(level_output_list)
+    level_output = ''.join(level_output_list)
 
     padding_needed = (round_to_multiple(
         len(level_output), block_size) - len(level_output))
-    level_output += b'\0' * padding_needed
+    level_output += '\0' * padding_needed
 
     # Copy level-output into resulting tree.
     offset = hash_level_offsets[level_num]
@@ -4110,9 +3945,9 @@
     hash_src_size = len(level_output)
     level_num += 1
 
-  hasher = create_avb_hashtree_hasher(hash_alg_name, salt)
+  hasher = hashlib.new(name=hash_alg_name, string=salt)
   hasher.update(level_output)
-  return hasher.digest(), bytes(hash_ret)
+  return hasher.digest(), hash_ret
 
 
 class AvbTool(object):
@@ -4154,10 +3989,6 @@
                             help='Rollback Index',
                             type=parse_number,
                             default=0)
-    sub_parser.add_argument('--rollback_index_location',
-                            help='Location of main vbmeta Rollback Index',
-                            type=parse_number,
-                            default=0)
     # This is used internally for unit tests. Do not include in --help output.
     sub_parser.add_argument('--internal_release_string',
                             help=argparse.SUPPRESS)
@@ -4248,24 +4079,6 @@
     parser = argparse.ArgumentParser()
     subparsers = parser.add_subparsers(title='subcommands')
 
-    sub_parser = subparsers.add_parser(
-        'generate_test_image',
-        help=('Generates a test image with a known pattern for testing: '
-              '0x00 0x01 0x02 ... 0xff 0x00 0x01 ...'))
-    sub_parser.add_argument('--image_size',
-                            help='Size of image to generate.',
-                            type=parse_number,
-                            required=True)
-    sub_parser.add_argument('--start_byte',
-                            help='Integer for the start byte of the pattern.',
-                            type=parse_number,
-                            default=0)
-    sub_parser.add_argument('--output',
-                            help='Output file name.',
-                            type=argparse.FileType('wb'),
-                            default=sys.stdout)
-    sub_parser.set_defaults(func=self.generate_test_image)
-
     sub_parser = subparsers.add_parser('version',
                                        help='Prints version of avbtool.')
     sub_parser.set_defaults(func=self.version)
@@ -4300,7 +4113,7 @@
                                        help='Add hashes and footer to image.')
     sub_parser.add_argument('--image',
                             help='Image to add hashes to',
-                            type=argparse.FileType('rb+'))
+                            type=argparse.FileType('rab+'))
     sub_parser.add_argument('--partition_size',
                             help='Partition size',
                             type=parse_number)
@@ -4333,7 +4146,7 @@
                                        help='Append vbmeta image to image.')
     sub_parser.add_argument('--image',
                             help='Image to append vbmeta blob to',
-                            type=argparse.FileType('rb+'))
+                            type=argparse.FileType('rab+'))
     sub_parser.add_argument('--partition_size',
                             help='Partition size',
                             type=parse_number,
@@ -4348,7 +4161,7 @@
         help='Add hashtree and footer to image.')
     sub_parser.add_argument('--image',
                             help='Image to add hashtree to',
-                            type=argparse.FileType('rb+'))
+                            type=argparse.FileType('rab+'))
     sub_parser.add_argument('--partition_size',
                             help='Partition size',
                             default=0,
@@ -4412,7 +4225,7 @@
                                        help='Erase footer from an image.')
     sub_parser.add_argument('--image',
                             help='Image with a footer',
-                            type=argparse.FileType('rb+'),
+                            type=argparse.FileType('rwb+'),
                             required=True)
     sub_parser.add_argument('--keep_hashtree',
                             help='Keep the hashtree and FEC in the image',
@@ -4423,7 +4236,7 @@
                                        help='Zero out hashtree and FEC data.')
     sub_parser.add_argument('--image',
                             help='Image with a footer',
-                            type=argparse.FileType('rb+'),
+                            type=argparse.FileType('rwb+'),
                             required=True)
     sub_parser.set_defaults(func=self.zero_hashtree)
 
@@ -4450,7 +4263,7 @@
                                        help='Resize image with a footer.')
     sub_parser.add_argument('--image',
                             help='Image with a footer',
-                            type=argparse.FileType('rb+'),
+                            type=argparse.FileType('rwb+'),
                             required=True)
     sub_parser.add_argument('--partition_size',
                             help='New partition size',
@@ -4468,10 +4281,6 @@
                             help='Write info to file',
                             type=argparse.FileType('wt'),
                             default=sys.stdout)
-    sub_parser.add_argument('--atx',
-                            help=('Show information about Android Things '
-                                  'eXtension (ATX).'),
-                            action='store_true')
     sub_parser.set_defaults(func=self.info_image)
 
     sub_parser = subparsers.add_parser(
@@ -4501,22 +4310,6 @@
     sub_parser.set_defaults(func=self.verify_image)
 
     sub_parser = subparsers.add_parser(
-        'print_partition_digests',
-        help='Prints partition digests.')
-    sub_parser.add_argument('--image',
-                            help='Image to print partition digests from',
-                            type=argparse.FileType('rb'),
-                            required=True)
-    sub_parser.add_argument('--output',
-                            help='Write info to file',
-                            type=argparse.FileType('wt'),
-                            default=sys.stdout)
-    sub_parser.add_argument('--json',
-                            help=('Print output as JSON'),
-                            action='store_true')
-    sub_parser.set_defaults(func=self.print_partition_digests)
-
-    sub_parser = subparsers.add_parser(
         'calculate_vbmeta_digest',
         help='Calculate vbmeta digest.')
     sub_parser.add_argument('--image',
@@ -4680,12 +4473,6 @@
     args = parser.parse_args(argv[1:])
     try:
       args.func(args)
-    except AttributeError:
-      # 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('avbtool: error: too few arguments')
-      sys.exit(2)
     except AvbError as e:
       sys.stderr.write('{}: {}\n'.format(argv[0], str(e)))
       sys.exit(1)
@@ -4694,10 +4481,6 @@
     """Implements the 'version' sub-command."""
     print(get_release_string())
 
-  def generate_test_image(self, args):
-    """Implements the 'generate_test_image' sub-command."""
-    self.avb.generate_test_image(args.output, args.image_size, args.start_byte)
-
   def extract_public_key(self, args):
     """Implements the 'extract_public_key' sub-command."""
     self.avb.extract_public_key(args.key, args.output)
@@ -4708,8 +4491,7 @@
     self.avb.make_vbmeta_image(args.output, args.chain_partition,
                                args.algorithm, args.key,
                                args.public_key_metadata, args.rollback_index,
-                               args.flags, args.rollback_index_location,
-                               args.prop, args.prop_from_file,
+                               args.flags, args.prop, args.prop_from_file,
                                args.kernel_cmdline,
                                args.setup_rootfs_from_kernel,
                                args.include_descriptors_from_image,
@@ -4734,8 +4516,7 @@
                              args.salt, args.chain_partition, args.algorithm,
                              args.key,
                              args.public_key_metadata, args.rollback_index,
-                             args.flags, args.rollback_index_location,
-                             args.prop, args.prop_from_file,
+                             args.flags, args.prop, args.prop_from_file,
                              args.kernel_cmdline,
                              args.setup_rootfs_from_kernel,
                              args.include_descriptors_from_image,
@@ -4767,8 +4548,7 @@
         args.hash_algorithm, args.block_size,
         args.salt, args.chain_partition, args.algorithm,
         args.key, args.public_key_metadata,
-        args.rollback_index, args.flags,
-        args.rollback_index_location, args.prop,
+        args.rollback_index, args.flags, args.prop,
         args.prop_from_file,
         args.kernel_cmdline,
         args.setup_rootfs_from_kernel,
@@ -4809,7 +4589,7 @@
 
   def info_image(self, args):
     """Implements the 'info_image' sub-command."""
-    self.avb.info_image(args.image.name, args.output, args.atx)
+    self.avb.info_image(args.image.name, args.output)
 
   def verify_image(self, args):
     """Implements the 'verify_image' sub-command."""
@@ -4818,10 +4598,6 @@
                           args.follow_chain_partitions,
                           args.accept_zeroed_hashtree)
 
-  def print_partition_digests(self, args):
-    """Implements the 'print_partition_digests' sub-command."""
-    self.avb.print_partition_digests(args.image.name, args.output, args.json)
-
   def calculate_vbmeta_digest(self, args):
     """Implements the 'calculate_vbmeta_digest' sub-command."""
     self.avb.calculate_vbmeta_digest(args.image.name, args.hash_algorithm,
@@ -4868,10 +4644,5 @@
 
 
 if __name__ == '__main__':
-  if AVB_INVOCATION_LOGFILE:
-    with open(AVB_INVOCATION_LOGFILE, 'a') as log:
-      log.write(' '.join(sys.argv))
-      log.write('\n')
-
   tool = AvbTool()
   tool.run(sys.argv)
diff --git a/avbtool.py b/avbtool.py
new file mode 120000
index 0000000..16657b9
--- /dev/null
+++ b/avbtool.py
@@ -0,0 +1 @@
+avbtool
\ No newline at end of file
diff --git a/contrib/linux/4.4/0001-ANDROID-Update-init-do_mounts_dm.c-to-the-latest-Chr.patch b/contrib/linux/4.4/0001-ANDROID-Update-init-do_mounts_dm.c-to-the-latest-Chr.patch
new file mode 100644
index 0000000..ff41996
--- /dev/null
+++ b/contrib/linux/4.4/0001-ANDROID-Update-init-do_mounts_dm.c-to-the-latest-Chr.patch
@@ -0,0 +1,760 @@
+From 6a6a7657c231e947233c43ae0522bbd4edf0139e Mon Sep 17 00:00:00 2001
+From: David Zeuthen <zeuthen@google.com>
+Date: Tue, 24 Jan 2017 13:02:35 -0500
+Subject: [PATCH 1/2] ANDROID: Update init/do_mounts_dm.c to the latest
+ ChromiumOS version.
+
+This is needed for AVB integration work.
+
+Bug: 31796270
+Test: Manually tested (other arch).
+Change-Id: I32fd37c1578c6414e3e6ff277d16ad94df7886b8
+Signed-off-by: David Zeuthen <zeuthen@google.com>
+---
+ include/linux/device-mapper.h |   7 +
+ init/do_mounts_dm.c           | 556 +++++++++++++++++++++++-------------------
+ 2 files changed, 307 insertions(+), 256 deletions(-)
+
+diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
+index b874d5b61ffc..f3f87db34429 100644
+--- a/include/linux/device-mapper.h
++++ b/include/linux/device-mapper.h
+@@ -415,6 +415,13 @@ union map_info *dm_get_rq_mapinfo(struct request *rq);
+ 
+ struct queue_limits *dm_get_queue_limits(struct mapped_device *md);
+ 
++void dm_lock_md_type(struct mapped_device *md);
++void dm_unlock_md_type(struct mapped_device *md);
++void dm_set_md_type(struct mapped_device *md, unsigned type);
++unsigned dm_get_md_type(struct mapped_device *md);
++int dm_setup_md_queue(struct mapped_device *md);
++unsigned dm_table_get_type(struct dm_table *t);
++
+ /*
+  * Geometry functions.
+  */
+diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c
+index ecda58df9a19..bce1c2fbb915 100644
+--- a/init/do_mounts_dm.c
++++ b/init/do_mounts_dm.c
+@@ -5,13 +5,17 @@
+  *
+  * This file is released under the GPL.
+  */
++#include <linux/async.h>
++#include <linux/ctype.h>
+ #include <linux/device-mapper.h>
+ #include <linux/fs.h>
+ #include <linux/string.h>
++#include <linux/delay.h>
+ 
+ #include "do_mounts.h"
+-#include "../drivers/md/dm.h"
+ 
++#define DM_MAX_DEVICES 256
++#define DM_MAX_TARGETS 256
+ #define DM_MAX_NAME 32
+ #define DM_MAX_UUID 129
+ #define DM_NO_UUID "none"
+@@ -19,14 +23,47 @@
+ #define DM_MSG_PREFIX "init"
+ 
+ /* Separators used for parsing the dm= argument. */
+-#define DM_FIELD_SEP ' '
+-#define DM_LINE_SEP ','
++#define DM_FIELD_SEP " "
++#define DM_LINE_SEP ","
++#define DM_ANY_SEP DM_FIELD_SEP DM_LINE_SEP
+ 
+ /*
+  * When the device-mapper and any targets are compiled into the kernel
+- * (not a module), one target may be created and used as the root device at
+- * boot time with the parameters given with the boot line dm=...
+- * The code for that is here.
++ * (not a module), one or more device-mappers may be created and used
++ * as the root device at boot time with the parameters given with the
++ * boot line dm=...
++ *
++ * Multiple device-mappers can be stacked specifing the number of
++ * devices. A device can have multiple targets if the the number of
++ * targets is specified.
++ *
++ * TODO(taysom:defect 32847)
++ * In the future, the <num> field will be mandatory.
++ *
++ * <device>        ::= [<num>] <device-mapper>+
++ * <device-mapper> ::= <head> "," <target>+
++ * <head>          ::= <name> <uuid> <mode> [<num>]
++ * <target>        ::= <start> <length> <type> <options> ","
++ * <mode>          ::= "ro" | "rw"
++ * <uuid>          ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | "none"
++ * <type>          ::= "verity" | "bootcache" | ...
++ *
++ * Example:
++ * 2 vboot none ro 1,
++ *     0 1768000 bootcache
++ *       device=aa55b119-2a47-8c45-946a-5ac57765011f+1
++ *       signature=76e9be054b15884a9fa85973e9cb274c93afadb6
++ *       cache_start=1768000 max_blocks=100000 size_limit=23 max_trace=20000,
++ *   vroot none ro 1,
++ *     0 1740800 verity payload=254:0 hashtree=254:0 hashstart=1740800 alg=sha1
++ *       root_hexdigest=76e9be054b15884a9fa85973e9cb274c93afadb6
++ *       salt=5b3549d54d6c7a3837b9b81ed72e49463a64c03680c47835bef94d768e5646fe
++ *
++ * Notes:
++ *  1. uuid is a label for the device and we set it to "none".
++ *  2. The <num> field will be optional initially and assumed to be 1.
++ *     Once all the scripts that set these fields have been set, it will
++ *     be made mandatory.
+  */
+ 
+ struct dm_setup_target {
+@@ -38,381 +75,388 @@ struct dm_setup_target {
+ 	struct dm_setup_target *next;
+ };
+ 
+-static struct {
++struct dm_device {
+ 	int minor;
+ 	int ro;
+ 	char name[DM_MAX_NAME];
+ 	char uuid[DM_MAX_UUID];
+-	char *targets;
++	unsigned long num_targets;
+ 	struct dm_setup_target *target;
+ 	int target_count;
++	struct dm_device *next;
++};
++
++struct dm_option {
++	char *start;
++	char *next;
++	size_t len;
++	char delim;
++};
++
++static struct {
++	unsigned long num_devices;
++	char *str;
+ } dm_setup_args __initdata;
+ 
+ static __initdata int dm_early_setup;
+ 
+-static size_t __init get_dm_option(char *str, char **next, char sep)
++static int __init get_dm_option(struct dm_option *opt, const char *accept)
+ {
+-	size_t len = 0;
+-	char *endp = NULL;
++	char *str = opt->next;
++	char *endp;
+ 
+ 	if (!str)
+ 		return 0;
+ 
+-	endp = strchr(str, sep);
++	str = skip_spaces(str);
++	opt->start = str;
++	endp = strpbrk(str, accept);
+ 	if (!endp) {  /* act like strchrnul */
+-		len = strlen(str);
+-		endp = str + len;
++		opt->len = strlen(str);
++		endp = str + opt->len;
+ 	} else {
+-		len = endp - str;
++		opt->len = endp - str;
+ 	}
+-
+-	if (endp == str)
+-		return 0;
+-
+-	if (!next)
+-		return len;
+-
++	opt->delim = *endp;
+ 	if (*endp == 0) {
+ 		/* Don't advance past the nul. */
+-		*next = endp;
++		opt->next = endp;
+ 	} else {
+-		*next = endp + 1;
++		opt->next = endp + 1;
+ 	}
+-	return len;
+-}
+-
+-static int __init dm_setup_args_init(void)
+-{
+-	dm_setup_args.minor = 0;
+-	dm_setup_args.ro = 0;
+-	dm_setup_args.target = NULL;
+-	dm_setup_args.target_count = 0;
+-	return 0;
++	return opt->len != 0;
+ }
+ 
+-static int __init dm_setup_cleanup(void)
++static int __init dm_setup_cleanup(struct dm_device *devices)
+ {
+-	struct dm_setup_target *target = dm_setup_args.target;
+-	struct dm_setup_target *old_target = NULL;
+-	while (target) {
+-		kfree(target->type);
+-		kfree(target->params);
+-		old_target = target;
+-		target = target->next;
+-		kfree(old_target);
+-		dm_setup_args.target_count--;
++	struct dm_device *dev = devices;
++
++	while (dev) {
++		struct dm_device *old_dev = dev;
++		struct dm_setup_target *target = dev->target;
++		while (target) {
++			struct dm_setup_target *old_target = target;
++			kfree(target->type);
++			kfree(target->params);
++			target = target->next;
++			kfree(old_target);
++			dev->target_count--;
++		}
++		BUG_ON(dev->target_count);
++		dev = dev->next;
++		kfree(old_dev);
+ 	}
+-	BUG_ON(dm_setup_args.target_count);
+ 	return 0;
+ }
+ 
+-static char * __init dm_setup_parse_device_args(char *str)
++static char * __init dm_parse_device(struct dm_device *dev, char *str)
+ {
+-	char *next = NULL;
+-	size_t len = 0;
++	struct dm_option opt;
++	size_t len;
+ 
+ 	/* Grab the logical name of the device to be exported to udev */
+-	len = get_dm_option(str, &next, DM_FIELD_SEP);
+-	if (!len) {
++	opt.next = str;
++	if (!get_dm_option(&opt, DM_FIELD_SEP)) {
+ 		DMERR("failed to parse device name");
+ 		goto parse_fail;
+ 	}
+-	len = min(len + 1, sizeof(dm_setup_args.name));
+-	strlcpy(dm_setup_args.name, str, len);  /* includes nul */
+-	str = skip_spaces(next);
++	len = min(opt.len + 1, sizeof(dev->name));
++	strlcpy(dev->name, opt.start, len);  /* includes nul */
+ 
+ 	/* Grab the UUID value or "none" */
+-	len = get_dm_option(str, &next, DM_FIELD_SEP);
+-	if (!len) {
++	if (!get_dm_option(&opt, DM_FIELD_SEP)) {
+ 		DMERR("failed to parse device uuid");
+ 		goto parse_fail;
+ 	}
+-	len = min(len + 1, sizeof(dm_setup_args.uuid));
+-	strlcpy(dm_setup_args.uuid, str, len);
+-	str = skip_spaces(next);
++	len = min(opt.len + 1, sizeof(dev->uuid));
++	strlcpy(dev->uuid, opt.start, len);
+ 
+ 	/* Determine if the table/device will be read only or read-write */
+-	if (!strncmp("ro,", str, 3)) {
+-		dm_setup_args.ro = 1;
+-	} else if (!strncmp("rw,", str, 3)) {
+-		dm_setup_args.ro = 0;
++	get_dm_option(&opt, DM_ANY_SEP);
++	if (!strncmp("ro", opt.start, opt.len)) {
++		dev->ro = 1;
++	} else if (!strncmp("rw", opt.start, opt.len)) {
++		dev->ro = 0;
+ 	} else {
+ 		DMERR("failed to parse table mode");
+ 		goto parse_fail;
+ 	}
+-	str = skip_spaces(str + 3);
+ 
+-	return str;
++	/* Optional number field */
++	/* XXX: The <num> field will be mandatory in the next round */
++	if (opt.delim == DM_FIELD_SEP[0]) {
++		if (!get_dm_option(&opt, DM_LINE_SEP))
++			return NULL;
++		dev->num_targets = simple_strtoul(opt.start, NULL, 10);
++	} else {
++		dev->num_targets = 1;
++	}
++	if (dev->num_targets > DM_MAX_TARGETS) {
++		DMERR("too many targets %lu > %d",
++			dev->num_targets, DM_MAX_TARGETS);
++	}
++	return opt.next;
+ 
+ parse_fail:
+ 	return NULL;
+ }
+ 
+-static void __init dm_substitute_devices(char *str, size_t str_len)
++static char * __init dm_parse_targets(struct dm_device *dev, char *str)
+ {
+-	char *candidate = str;
+-	char *candidate_end = str;
+-	char old_char;
+-	size_t len = 0;
+-	dev_t dev;
+-
+-	if (str_len < 3)
+-		return;
+-
+-	while (str && *str) {
+-		candidate = strchr(str, '/');
+-		if (!candidate)
+-			break;
+-
+-		/* Avoid embedded slashes */
+-		if (candidate != str && *(candidate - 1) != DM_FIELD_SEP) {
+-			str = strchr(candidate, DM_FIELD_SEP);
+-			continue;
+-		}
+-
+-		len = get_dm_option(candidate, &candidate_end, DM_FIELD_SEP);
+-		str = skip_spaces(candidate_end);
+-		if (len < 3 || len > 37)  /* name_to_dev_t max; maj:mix min */
+-			continue;
+-
+-		/* Temporarily terminate with a nul */
+-		if (*candidate_end)
+-			candidate_end--;
+-		old_char = *candidate_end;
+-		*candidate_end = '\0';
+-
+-		DMDEBUG("converting candidate device '%s' to dev_t", candidate);
+-		/* Use the boot-time specific device naming */
+-		dev = name_to_dev_t(candidate);
+-		*candidate_end = old_char;
+-
+-		DMDEBUG(" -> %u", dev);
+-		/* No suitable replacement found */
+-		if (!dev)
+-			continue;
+-
+-		/* Rewrite the /dev/path as a major:minor */
+-		len = snprintf(candidate, len, "%u:%u", MAJOR(dev), MINOR(dev));
+-		if (!len) {
+-			DMERR("error substituting device major/minor.");
+-			break;
+-		}
+-		candidate += len;
+-		/* Pad out with spaces (fixing our nul) */
+-		while (candidate < candidate_end)
+-			*(candidate++) = DM_FIELD_SEP;
+-	}
+-}
+-
+-static int __init dm_setup_parse_targets(char *str)
+-{
+-	char *next = NULL;
+-	size_t len = 0;
+-	struct dm_setup_target **target = NULL;
++	struct dm_option opt;
++	struct dm_setup_target **target = &dev->target;
++	unsigned long num_targets = dev->num_targets;
++	unsigned long i;
+ 
+ 	/* Targets are defined as per the table format but with a
+ 	 * comma as a newline separator. */
+-	target = &dm_setup_args.target;
+-	while (str && *str) {
++	opt.next = str;
++	for (i = 0; i < num_targets; i++) {
+ 		*target = kzalloc(sizeof(struct dm_setup_target), GFP_KERNEL);
+ 		if (!*target) {
+-			DMERR("failed to allocate memory for target %d",
+-			      dm_setup_args.target_count);
++			DMERR("failed to allocate memory for target %s<%ld>",
++				dev->name, i);
+ 			goto parse_fail;
+ 		}
+-		dm_setup_args.target_count++;
++		dev->target_count++;
+ 
+-		(*target)->begin = simple_strtoull(str, &next, 10);
+-		if (!next || *next != DM_FIELD_SEP) {
+-			DMERR("failed to parse starting sector for target %d",
+-			      dm_setup_args.target_count - 1);
++		if (!get_dm_option(&opt, DM_FIELD_SEP)) {
++			DMERR("failed to parse starting sector"
++				" for target %s<%ld>", dev->name, i);
+ 			goto parse_fail;
+ 		}
+-		str = skip_spaces(next + 1);
++		(*target)->begin = simple_strtoull(opt.start, NULL, 10);
+ 
+-		(*target)->length = simple_strtoull(str, &next, 10);
+-		if (!next || *next != DM_FIELD_SEP) {
+-			DMERR("failed to parse length for target %d",
+-			      dm_setup_args.target_count - 1);
++		if (!get_dm_option(&opt, DM_FIELD_SEP)) {
++			DMERR("failed to parse length for target %s<%ld>",
++				dev->name, i);
+ 			goto parse_fail;
+ 		}
+-		str = skip_spaces(next + 1);
+-
+-		len = get_dm_option(str, &next, DM_FIELD_SEP);
+-		if (!len ||
+-		    !((*target)->type = kstrndup(str, len, GFP_KERNEL))) {
+-			DMERR("failed to parse type for target %d",
+-			      dm_setup_args.target_count - 1);
++		(*target)->length = simple_strtoull(opt.start, NULL, 10);
++
++		if (get_dm_option(&opt, DM_FIELD_SEP))
++			(*target)->type = kstrndup(opt.start, opt.len,
++							GFP_KERNEL);
++		if (!((*target)->type)) {
++			DMERR("failed to parse type for target %s<%ld>",
++				dev->name, i);
+ 			goto parse_fail;
+ 		}
+-		str = skip_spaces(next);
+-
+-		len = get_dm_option(str, &next, DM_LINE_SEP);
+-		if (!len ||
+-		    !((*target)->params = kstrndup(str, len, GFP_KERNEL))) {
+-			DMERR("failed to parse params for target %d",
+-			      dm_setup_args.target_count - 1);
++		if (get_dm_option(&opt, DM_LINE_SEP))
++			(*target)->params = kstrndup(opt.start, opt.len,
++							GFP_KERNEL);
++		if (!((*target)->params)) {
++			DMERR("failed to parse params for target %s<%ld>",
++				dev->name, i);
+ 			goto parse_fail;
+ 		}
+-		str = skip_spaces(next);
+-
+-		/* Before moving on, walk through the copied target and
+-		 * attempt to replace all /dev/xxx with the major:minor number.
+-		 * It may not be possible to resolve them traditionally at
+-		 * boot-time. */
+-		dm_substitute_devices((*target)->params, len);
+-
+ 		target = &((*target)->next);
+ 	}
+-	DMDEBUG("parsed %d targets", dm_setup_args.target_count);
++	DMDEBUG("parsed %d targets", dev->target_count);
+ 
+-	return 0;
++	return opt.next;
+ 
+ parse_fail:
+-	return 1;
++	return NULL;
++}
++
++static struct dm_device * __init dm_parse_args(void)
++{
++	struct dm_device *devices = NULL;
++	struct dm_device **tail = &devices;
++	struct dm_device *dev;
++	char *str = dm_setup_args.str;
++	unsigned long num_devices = dm_setup_args.num_devices;
++	unsigned long i;
++
++	if (!str)
++		return NULL;
++	for (i = 0; i < num_devices; i++) {
++		dev = kzalloc(sizeof(*dev), GFP_KERNEL);
++		if (!dev) {
++			DMERR("failed to allocated memory for dev");
++			goto error;
++		}
++		*tail = dev;
++		tail = &dev->next;
++		/*
++		 * devices are given minor numbers 0 - n-1
++		 * in the order they are found in the arg
++		 * string.
++		 */
++		dev->minor = i;
++		str = dm_parse_device(dev, str);
++		if (!str)	/* NULL indicates error in parsing, bail */
++			goto error;
++
++		str = dm_parse_targets(dev, str);
++		if (!str)
++			goto error;
++	}
++	return devices;
++error:
++	dm_setup_cleanup(devices);
++	return NULL;
+ }
+ 
+ /*
+  * Parse the command-line parameters given our kernel, but do not
+  * actually try to invoke the DM device now; that is handled by
+- * dm_setup_drive after the low-level disk drivers have initialised.
+- * dm format is as follows:
+- *  dm="name uuid fmode,[table line 1],[table line 2],..."
+- * May be used with root=/dev/dm-0 as it always uses the first dm minor.
++ * dm_setup_drives after the low-level disk drivers have initialised.
++ * dm format is described at the top of the file.
++ *
++ * Because dm minor numbers are assigned in assending order starting with 0,
++ * You can assume the first device is /dev/dm-0, the next device is /dev/dm-1,
++ * and so forth.
+  */
+-
+ static int __init dm_setup(char *str)
+ {
+-	dm_setup_args_init();
++	struct dm_option opt;
++	unsigned long num_devices;
+ 
+-	str = dm_setup_parse_device_args(str);
+ 	if (!str) {
+ 		DMDEBUG("str is NULL");
+ 		goto parse_fail;
+ 	}
+-
+-	/* Target parsing is delayed until we have dynamic memory */
+-	dm_setup_args.targets = str;
+-
+-	printk(KERN_INFO "dm: will configure '%s' on dm-%d\n",
+-	       dm_setup_args.name, dm_setup_args.minor);
+-
++	opt.next = str;
++	if (!get_dm_option(&opt, DM_FIELD_SEP))
++		goto parse_fail;
++	if (isdigit(opt.start[0])) {	/* XXX: Optional number field */
++		num_devices = simple_strtoul(opt.start, NULL, 10);
++		str = opt.next;
++	} else {
++		num_devices = 1;
++		/* Don't advance str */
++	}
++	if (num_devices > DM_MAX_DEVICES) {
++		DMDEBUG("too many devices %lu > %d",
++			num_devices, DM_MAX_DEVICES);
++	}
++	dm_setup_args.str = str;
++	dm_setup_args.num_devices = num_devices;
++	DMINFO("will configure %lu devices", num_devices);
+ 	dm_early_setup = 1;
+ 	return 1;
+ 
+ parse_fail:
+-	printk(KERN_WARNING "dm: Invalid arguments supplied to dm=.\n");
++	DMWARN("Invalid arguments supplied to dm=.");
+ 	return 0;
+ }
+ 
+-
+-static void __init dm_setup_drive(void)
++static void __init dm_setup_drives(void)
+ {
+ 	struct mapped_device *md = NULL;
+ 	struct dm_table *table = NULL;
+ 	struct dm_setup_target *target;
+-	char *uuid = dm_setup_args.uuid;
++	struct dm_device *dev;
++	char *uuid;
+ 	fmode_t fmode = FMODE_READ;
++	struct dm_device *devices;
+ 
+-	/* Finish parsing the targets. */
+-	if (dm_setup_parse_targets(dm_setup_args.targets))
+-		goto parse_fail;
+-
+-	if (dm_create(dm_setup_args.minor, &md)) {
+-		DMDEBUG("failed to create the device");
+-		goto dm_create_fail;
+-	}
+-	DMDEBUG("created device '%s'", dm_device_name(md));
+-
+-	/* In addition to flagging the table below, the disk must be
+-	 * set explicitly ro/rw. */
+-	set_disk_ro(dm_disk(md), dm_setup_args.ro);
++	devices = dm_parse_args();
+ 
+-	if (!dm_setup_args.ro)
+-		fmode |= FMODE_WRITE;
+-	if (dm_table_create(&table, fmode, dm_setup_args.target_count, md)) {
+-		DMDEBUG("failed to create the table");
+-		goto dm_table_create_fail;
+-	}
++	for (dev = devices; dev; dev = dev->next) {
++		if (dm_create(dev->minor, &md)) {
++			DMDEBUG("failed to create the device");
++			goto dm_create_fail;
++		}
++		DMDEBUG("created device '%s'", dm_device_name(md));
++
++		/*
++		 * In addition to flagging the table below, the disk must be
++		 * set explicitly ro/rw.
++		 */
++		set_disk_ro(dm_disk(md), dev->ro);
++
++		if (!dev->ro)
++			fmode |= FMODE_WRITE;
++		if (dm_table_create(&table, fmode, dev->target_count, md)) {
++			DMDEBUG("failed to create the table");
++			goto dm_table_create_fail;
++		}
+ 
+-	dm_lock_md_type(md);
+-	target = dm_setup_args.target;
+-	while (target) {
+-		DMINFO("adding target '%llu %llu %s %s'",
+-		       (unsigned long long) target->begin,
+-		       (unsigned long long) target->length, target->type,
+-		       target->params);
+-		if (dm_table_add_target(table, target->type, target->begin,
+-					target->length, target->params)) {
+-			DMDEBUG("failed to add the target to the table");
+-			goto add_target_fail;
++		dm_lock_md_type(md);
++
++		for (target = dev->target; target; target = target->next) {
++			DMINFO("adding target '%llu %llu %s %s'",
++			       (unsigned long long) target->begin,
++			       (unsigned long long) target->length,
++			       target->type, target->params);
++			if (dm_table_add_target(table, target->type,
++						target->begin,
++						target->length,
++						target->params)) {
++				DMDEBUG("failed to add the target"
++					" to the table");
++				goto add_target_fail;
++			}
++		}
++		if (dm_table_complete(table)) {
++			DMDEBUG("failed to complete the table");
++			goto table_complete_fail;
+ 		}
+-		target = target->next;
+-	}
+ 
+-	if (dm_table_complete(table)) {
+-		DMDEBUG("failed to complete the table");
+-		goto table_complete_fail;
+-	}
++		/* Suspend the device so that we can bind it to the table. */
++		if (dm_suspend(md, 0)) {
++			DMDEBUG("failed to suspend the device pre-bind");
++			goto suspend_fail;
++		}
+ 
+-	if (dm_get_md_type(md) == DM_TYPE_NONE) {
++		/* Initial table load: acquire type of table. */
+ 		dm_set_md_type(md, dm_table_get_type(table));
++
++		/* Setup md->queue to reflect md's type. */
+ 		if (dm_setup_md_queue(md)) {
+ 			DMWARN("unable to set up device queue for new table.");
+ 			goto setup_md_queue_fail;
+ 		}
+-	} else if (dm_get_md_type(md) != dm_table_get_type(table)) {
+-		DMWARN("can't change device type after initial table load.");
+-		goto setup_md_queue_fail;
+-        }
+-
+-	/* Suspend the device so that we can bind it to the table. */
+-	if (dm_suspend(md, 0)) {
+-		DMDEBUG("failed to suspend the device pre-bind");
+-		goto suspend_fail;
+-	}
+ 
+-	/* Bind the table to the device. This is the only way to associate
+-	 * md->map with the table and set the disk capacity directly. */
+-	if (dm_swap_table(md, table)) {  /* should return NULL. */
+-		DMDEBUG("failed to bind the device to the table");
+-		goto table_bind_fail;
+-	}
++		/*
++		 * Bind the table to the device. This is the only way
++		 * to associate md->map with the table and set the disk
++		 * capacity directly.
++		 */
++		if (dm_swap_table(md, table)) {  /* should return NULL. */
++			DMDEBUG("failed to bind the device to the table");
++			goto table_bind_fail;
++		}
+ 
+-	/* Finally, resume and the device should be ready. */
+-	if (dm_resume(md)) {
+-		DMDEBUG("failed to resume the device");
+-		goto resume_fail;
+-	}
++		/* Finally, resume and the device should be ready. */
++		if (dm_resume(md)) {
++			DMDEBUG("failed to resume the device");
++			goto resume_fail;
++		}
+ 
+-	/* Export the dm device via the ioctl interface */
+-	if (!strcmp(DM_NO_UUID, dm_setup_args.uuid))
+-		uuid = NULL;
+-	if (dm_ioctl_export(md, dm_setup_args.name, uuid)) {
+-		DMDEBUG("failed to export device with given name and uuid");
+-		goto export_fail;
+-	}
+-	printk(KERN_INFO "dm: dm-%d is ready\n", dm_setup_args.minor);
++		/* Export the dm device via the ioctl interface */
++		if (!strcmp(DM_NO_UUID, dev->uuid))
++			uuid = NULL;
++		if (dm_ioctl_export(md, dev->name, uuid)) {
++			DMDEBUG("failed to export device with given"
++				" name and uuid");
++			goto export_fail;
++		}
+ 
+-	dm_unlock_md_type(md);
+-	dm_setup_cleanup();
++		dm_unlock_md_type(md);
++
++		DMINFO("dm-%d is ready", dev->minor);
++	}
++	dm_setup_cleanup(devices);
+ 	return;
+ 
+ export_fail:
+ resume_fail:
+ table_bind_fail:
+-suspend_fail:
+ setup_md_queue_fail:
++suspend_fail:
+ table_complete_fail:
+ add_target_fail:
+ 	dm_unlock_md_type(md);
+ dm_table_create_fail:
+ 	dm_put(md);
+ dm_create_fail:
+-	dm_setup_cleanup();
+-parse_fail:
+-	printk(KERN_WARNING "dm: starting dm-%d (%s) failed\n",
+-	       dm_setup_args.minor, dm_setup_args.name);
++	DMWARN("starting dm-%d (%s) failed",
++	       dev->minor, dev->name);
++	dm_setup_cleanup(devices);
+ }
+ 
+ __setup("dm=", dm_setup);
+@@ -421,6 +465,6 @@ void __init dm_run_setup(void)
+ {
+ 	if (!dm_early_setup)
+ 		return;
+-	printk(KERN_INFO "dm: attempting early device configuration.\n");
+-	dm_setup_drive();
++	DMINFO("attempting early device configuration.");
++	dm_setup_drives();
+ }
+-- 
+2.14.1.581.gf28d330327-goog
+
diff --git a/contrib/linux/4.4/0002-ANDROID-AVB-error-handler-to-invalidate-vbmeta-parti.patch b/contrib/linux/4.4/0002-ANDROID-AVB-error-handler-to-invalidate-vbmeta-parti.patch
new file mode 100644
index 0000000..0dad3a6
--- /dev/null
+++ b/contrib/linux/4.4/0002-ANDROID-AVB-error-handler-to-invalidate-vbmeta-parti.patch
@@ -0,0 +1,343 @@
+From fabdd30b4cf9ee341dabbbd51b2a1e3335d7d4cd Mon Sep 17 00:00:00 2001
+From: David Zeuthen <zeuthen@google.com>
+Date: Tue, 24 Jan 2017 13:17:01 -0500
+Subject: [PATCH 2/2] ANDROID: AVB error handler to invalidate vbmeta
+ partition.
+
+If androidboot.vbmeta.device is set and points to a device with vbmeta
+magic, this header will be overwritten upon an irrecoverable dm-verity
+error. The side-effect of this is that the slot will fail to verify on
+next reboot, effectively triggering the boot loader to fallback to
+another slot. This work both if the vbmeta struct is at the start of a
+partition or if there's an AVB footer at the end.
+
+This code is based on drivers/md/dm-verity-chromeos.c from ChromiumOS.
+
+Example:
+
+ [    0.000000] Kernel command line: rootfstype=ext4 init=/init console=ttyS0,115200 androidboot.console=ttyS0 androidboot.hardware=uefi_x86_64 enforcing=0 androidboot.selinux=permissive androidboot.debuggable=1 buildvariant=eng dm="1 vroot none ro 1,0 2080496 verity 1 PARTUUID=6779df46-78f6-4c69-bf53-59bb1fbf126b PARTUUID=6779df46-78f6-4c69-bf53-59bb1fbf126b 4096 4096 260062 260062 sha1 4f76354c86e430e27426d584a726f2fbffecae32 7e4085342d634065269631ac9a199e1a43f4632c 1 ignore_zero_blocks" root=0xfd00 androidboot.vbmeta.device=PARTUUID=b865935d-38fb-4c4e-b8b4-70dc67321552 androidboot.slot_suffix=_a androidboot.vbmeta.device_state=unlocked androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=3200 androidboot.vbmeta.digest=14fe41c2b3696c31b7ad5eae7877d7d188995e1ab122c604aaaf4785850b91f7 skip_initramfs
+ [...]
+ [    0.612802] device-mapper: verity-avb: AVB error handler initialized with vbmeta device: PARTUUID=b865935d-38fb-4c4e-b8b4-70dc67321552
+ [...]
+ [    1.213804] device-mapper: init: attempting early device configuration.
+ [    1.214752] device-mapper: init: adding target '0 2080496 verity 1 PARTUUID=6779df46-78f6-4c69-bf53-59bb1fbf126b PARTUUID=6779df46-78f6-4c69-bf53-59bb1fbf126b 4096 4096 260062 260062 sha1 4f76354c86e430e27426d584a726f2fbffecae32 7e4085342d634065269631ac9a199e1a43f4632c 1 ignore_zero_blocks'
+ [    1.217643] device-mapper: init: dm-0 is ready
+ [    1.226694] device-mapper: verity: 8:6: data block 0 is corrupted
+ [    1.227666] device-mapper: verity-avb: AVB error handler called for PARTUUID=b865935d-38fb-4c4e-b8b4-70dc67321552
+ [    1.234308] device-mapper: verity-avb: invalidate_vbmeta: found vbmeta partition
+ [    1.235848] device-mapper: verity-avb: invalidate_vbmeta: completed.
+ [...]
+
+Bug: 31622239
+Test: Manually tested (other arch).
+Change-Id: Idf6be32d6a3d28e15de9302aa26ad6a516d663aa
+Signed-off-by: David Zeuthen <zeuthen@google.com>
+---
+ drivers/md/Kconfig            |  11 ++
+ drivers/md/Makefile           |   4 +
+ drivers/md/dm-verity-avb.c    | 229 ++++++++++++++++++++++++++++++++++++++++++
+ drivers/md/dm-verity-target.c |   6 +-
+ drivers/md/dm-verity.h        |   1 +
+ 5 files changed, 250 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/md/dm-verity-avb.c
+
+diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
+index 9eb08a43cd27..6f2fde5d98e7 100644
+--- a/drivers/md/Kconfig
++++ b/drivers/md/Kconfig
+@@ -515,6 +515,17 @@ config DM_LOG_WRITES
+ 
+ 	  If unsure, say N.
+ 
++config DM_VERITY_AVB
++	tristate "Support AVB specific verity error behavior"
++	depends on DM_VERITY
++	---help---
++	  Enables Android Verified Boot platform-specific error
++	  behavior. In particular, it will modify the vbmeta partition
++	  specified on the kernel command-line when non-transient error
++	  occurs (followed by a panic).
++
++	  If unsure, say N.
++
+ config DM_ANDROID_VERITY
+ 	bool "Android verity target support"
+ 	depends on DM_VERITY=y
+diff --git a/drivers/md/Makefile b/drivers/md/Makefile
+index 32b5d0a90d60..c22cc74c9fa8 100644
+--- a/drivers/md/Makefile
++++ b/drivers/md/Makefile
+@@ -69,3 +69,7 @@ endif
+ ifeq ($(CONFIG_DM_VERITY_FEC),y)
+ dm-verity-objs			+= dm-verity-fec.o
+ endif
++
++ifeq ($(CONFIG_DM_VERITY_AVB),y)
++dm-verity-objs			+= dm-verity-avb.o
++endif
+diff --git a/drivers/md/dm-verity-avb.c b/drivers/md/dm-verity-avb.c
+new file mode 100644
+index 000000000000..727aacbb1480
+--- /dev/null
++++ b/drivers/md/dm-verity-avb.c
+@@ -0,0 +1,229 @@
++/*
++ * Copyright (C) 2017 Google.
++ *
++ * This file is released under the GPLv2.
++ *
++ * Based on drivers/md/dm-verity-chromeos.c
++ */
++
++#include <linux/device-mapper.h>
++#include <linux/module.h>
++#include <linux/mount.h>
++
++#define DM_MSG_PREFIX "verity-avb"
++
++/* Set via module parameters. */
++static char avb_vbmeta_device[64];
++static char avb_invalidate_on_error[4];
++
++static void invalidate_vbmeta_endio(struct bio *bio)
++{
++	if (bio->bi_error)
++		DMERR("invalidate_vbmeta_endio: error %d", bio->bi_error);
++	complete(bio->bi_private);
++}
++
++static int invalidate_vbmeta_submit(struct bio *bio,
++				    struct block_device *bdev,
++				    int rw, int access_last_sector,
++				    struct page *page)
++{
++	DECLARE_COMPLETION_ONSTACK(wait);
++
++	bio->bi_private = &wait;
++	bio->bi_end_io = invalidate_vbmeta_endio;
++	bio->bi_bdev = bdev;
++	bio->bi_rw = rw;
++
++	bio->bi_iter.bi_sector = 0;
++	if (access_last_sector) {
++		sector_t last_sector;
++
++		last_sector = (i_size_read(bdev->bd_inode)>>SECTOR_SHIFT) - 1;
++		bio->bi_iter.bi_sector = last_sector;
++	}
++	if (!bio_add_page(bio, page, PAGE_SIZE, 0)) {
++		DMERR("invalidate_vbmeta_submit: bio_add_page error");
++		return -EIO;
++	}
++
++	submit_bio(rw, bio);
++	/* Wait up to 2 seconds for completion or fail. */
++	if (!wait_for_completion_timeout(&wait, msecs_to_jiffies(2000)))
++		return -EIO;
++	return 0;
++}
++
++static int invalidate_vbmeta(dev_t vbmeta_devt)
++{
++	int ret = 0;
++	struct block_device *bdev;
++	struct bio *bio;
++	struct page *page;
++	fmode_t dev_mode;
++	/* Ensure we do synchronous unblocked I/O. We may also need
++	 * sync_bdev() on completion, but it really shouldn't.
++	 */
++	int rw = REQ_SYNC | REQ_SOFTBARRIER | REQ_NOIDLE;
++	int access_last_sector = 0;
++
++	DMINFO("invalidate_vbmeta: acting on device %d:%d",
++	       MAJOR(vbmeta_devt), MINOR(vbmeta_devt));
++
++	/* First we open the device for reading. */
++	dev_mode = FMODE_READ | FMODE_EXCL;
++	bdev = blkdev_get_by_dev(vbmeta_devt, dev_mode,
++				 invalidate_vbmeta);
++	if (IS_ERR(bdev)) {
++		DMERR("invalidate_kernel: could not open device for reading");
++		dev_mode = 0;
++		ret = -ENOENT;
++		goto failed_to_read;
++	}
++
++	bio = bio_alloc(GFP_NOIO, 1);
++	if (!bio) {
++		ret = -ENOMEM;
++		goto failed_bio_alloc;
++	}
++
++	page = alloc_page(GFP_NOIO);
++	if (!page) {
++		ret = -ENOMEM;
++		goto failed_to_alloc_page;
++	}
++
++	access_last_sector = 0;
++	ret = invalidate_vbmeta_submit(bio, bdev, rw, access_last_sector, page);
++	if (ret) {
++		DMERR("invalidate_vbmeta: error reading");
++		goto failed_to_submit_read;
++	}
++
++	/* We have a page. Let's make sure it looks right. */
++	if (memcmp("AVB0", page_address(page), 4) == 0) {
++		/* Stamp it. */
++		memcpy(page_address(page), "AVE0", 4);
++		DMINFO("invalidate_vbmeta: found vbmeta partition");
++	} else {
++		/* Could be this is on a AVB footer, check. Also, since the
++		 * AVB footer is in the last 64 bytes, adjust for the fact that
++		 * we're dealing with 512-byte sectors.
++		 */
++		size_t offset = (1<<SECTOR_SHIFT) - 64;
++
++		access_last_sector = 1;
++		ret = invalidate_vbmeta_submit(bio, bdev, rw,
++					       access_last_sector, page);
++		if (ret) {
++			DMERR("invalidate_vbmeta: error reading");
++			goto failed_to_submit_read;
++		}
++		if (memcmp("AVBf", page_address(page) + offset, 4) != 0) {
++			DMERR("invalidate_vbmeta on non-vbmeta partition");
++			ret = -EINVAL;
++			goto invalid_header;
++		}
++		/* Stamp it. */
++		memcpy(page_address(page) + offset, "AVE0", 4);
++		DMINFO("invalidate_vbmeta: found vbmeta footer partition");
++	}
++
++	/* Now rewrite the changed page - the block dev was being
++	 * changed on read. Let's reopen here.
++	 */
++	blkdev_put(bdev, dev_mode);
++	dev_mode = FMODE_WRITE | FMODE_EXCL;
++	bdev = blkdev_get_by_dev(vbmeta_devt, dev_mode,
++				 invalidate_vbmeta);
++	if (IS_ERR(bdev)) {
++		DMERR("invalidate_vbmeta: could not open device for writing");
++		dev_mode = 0;
++		ret = -ENOENT;
++		goto failed_to_write;
++	}
++
++	/* We re-use the same bio to do the write after the read. Need to reset
++	 * it to initialize bio->bi_remaining.
++	 */
++	bio_reset(bio);
++
++	rw |= REQ_WRITE;
++	ret = invalidate_vbmeta_submit(bio, bdev, rw, access_last_sector, page);
++	if (ret) {
++		DMERR("invalidate_vbmeta: error writing");
++		goto failed_to_submit_write;
++	}
++
++	DMERR("invalidate_vbmeta: completed.");
++	ret = 0;
++failed_to_submit_write:
++failed_to_write:
++invalid_header:
++	__free_page(page);
++failed_to_submit_read:
++	/* Technically, we'll leak a page with the pending bio, but
++	 * we're about to reboot anyway.
++	 */
++failed_to_alloc_page:
++	bio_put(bio);
++failed_bio_alloc:
++	if (dev_mode)
++		blkdev_put(bdev, dev_mode);
++failed_to_read:
++	return ret;
++}
++
++void dm_verity_avb_error_handler(void)
++{
++	dev_t dev;
++
++	DMINFO("AVB error handler called for %s", avb_vbmeta_device);
++
++	if (strcmp(avb_invalidate_on_error, "yes") != 0) {
++		DMINFO("Not configured to invalidate");
++		return;
++	}
++
++	if (avb_vbmeta_device[0] == '\0') {
++		DMERR("avb_vbmeta_device parameter not set");
++		goto fail_no_dev;
++	}
++
++	dev = name_to_dev_t(avb_vbmeta_device);
++	if (!dev) {
++		DMERR("No matching partition for device: %s",
++		      avb_vbmeta_device);
++		goto fail_no_dev;
++	}
++
++	invalidate_vbmeta(dev);
++
++fail_no_dev:
++	;
++}
++
++static int __init dm_verity_avb_init(void)
++{
++	DMINFO("AVB error handler initialized with vbmeta device: %s",
++	       avb_vbmeta_device);
++	return 0;
++}
++
++static void __exit dm_verity_avb_exit(void)
++{
++}
++
++module_init(dm_verity_avb_init);
++module_exit(dm_verity_avb_exit);
++
++MODULE_AUTHOR("David Zeuthen <zeuthen@google.com>");
++MODULE_DESCRIPTION("AVB-specific error handler for dm-verity");
++MODULE_LICENSE("GPL");
++
++/* Declare parameter with no module prefix */
++#undef MODULE_PARAM_PREFIX
++#define MODULE_PARAM_PREFIX	"androidboot.vbmeta."
++module_param_string(device, avb_vbmeta_device, sizeof(avb_vbmeta_device), 0);
++module_param_string(invalidate_on_error, avb_invalidate_on_error,
++		    sizeof(avb_invalidate_on_error), 0);
+diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
+index c7e97cf6e7fb..e34cf53bd068 100644
+--- a/drivers/md/dm-verity-target.c
++++ b/drivers/md/dm-verity-target.c
+@@ -233,8 +233,12 @@ out:
+ 	if (v->mode == DM_VERITY_MODE_LOGGING)
+ 		return 0;
+ 
+-	if (v->mode == DM_VERITY_MODE_RESTART)
++	if (v->mode == DM_VERITY_MODE_RESTART) {
++#ifdef CONFIG_DM_VERITY_AVB
++		dm_verity_avb_error_handler();
++#endif
+ 		kernel_restart("dm-verity device corrupted");
++	}
+ 
+ 	return 1;
+ }
+diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h
+index 75effca400a3..a90d1d416107 100644
+--- a/drivers/md/dm-verity.h
++++ b/drivers/md/dm-verity.h
+@@ -136,4 +136,5 @@ extern void verity_io_hints(struct dm_target *ti, struct queue_limits *limits);
+ extern void verity_dtr(struct dm_target *ti);
+ extern int verity_ctr(struct dm_target *ti, unsigned argc, char **argv);
+ extern int verity_map(struct dm_target *ti, struct bio *bio);
++extern void dm_verity_avb_error_handler(void);
+ #endif /* DM_VERITY_H */
+-- 
+2.14.1.581.gf28d330327-goog
+
diff --git a/docs/aftl-architecture.png b/docs/aftl-architecture.png
deleted file mode 100644
index 5526f92..0000000
--- a/docs/aftl-architecture.png
+++ /dev/null
Binary files differ
diff --git a/docs/aftl-boot-flow.png b/docs/aftl-boot-flow.png
deleted file mode 100644
index f724808..0000000
--- a/docs/aftl-boot-flow.png
+++ /dev/null
Binary files differ
diff --git a/docs/aftl-image-location.png b/docs/aftl-image-location.png
deleted file mode 100644
index 0e609d7..0000000
--- a/docs/aftl-image-location.png
+++ /dev/null
Binary files differ
diff --git a/docs/aftl-image-structure.png b/docs/aftl-image-structure.png
deleted file mode 100644
index 2487314..0000000
--- a/docs/aftl-image-structure.png
+++ /dev/null
Binary files differ
diff --git a/libavb/avb_cmdline.c b/libavb/avb_cmdline.c
index 6613020..f87e18c 100644
--- a/libavb/avb_cmdline.c
+++ b/libavb/avb_cmdline.c
@@ -166,6 +166,30 @@
   return 1;
 }
 
+#define AVB_MAX_DIGITS_UINT64 32
+
+/* Writes |value| to |digits| in base 10 followed by a NUL byte.
+ * Returns number of characters written excluding the NUL byte.
+ */
+static size_t uint64_to_base10(uint64_t value,
+                               char digits[AVB_MAX_DIGITS_UINT64]) {
+  char rev_digits[AVB_MAX_DIGITS_UINT64];
+  size_t n, num_digits;
+
+  for (num_digits = 0; num_digits < AVB_MAX_DIGITS_UINT64 - 1;) {
+    rev_digits[num_digits++] = avb_div_by_10(&value) + '0';
+    if (value == 0) {
+      break;
+    }
+  }
+
+  for (n = 0; n < num_digits; n++) {
+    digits[n] = rev_digits[num_digits - 1 - n];
+  }
+  digits[n] = '\0';
+  return n;
+}
+
 static int cmdline_append_version(AvbSlotVerifyData* slot_data,
                                   const char* key,
                                   uint64_t major_version,
@@ -175,8 +199,8 @@
   char combined[AVB_MAX_DIGITS_UINT64 * 2 + 1];
   size_t num_major_digits, num_minor_digits;
 
-  num_major_digits = avb_uint64_to_base10(major_version, major_digits);
-  num_minor_digits = avb_uint64_to_base10(minor_version, minor_digits);
+  num_major_digits = uint64_to_base10(major_version, major_digits);
+  num_minor_digits = uint64_to_base10(minor_version, minor_digits);
   avb_memcpy(combined, major_digits, num_major_digits);
   combined[num_major_digits] = '.';
   avb_memcpy(combined + num_major_digits + 1, minor_digits, num_minor_digits);
@@ -189,7 +213,7 @@
                                         const char* key,
                                         uint64_t value) {
   char digits[AVB_MAX_DIGITS_UINT64];
-  avb_uint64_to_base10(value, digits);
+  uint64_to_base10(value, digits);
   return cmdline_append_option(slot_data, key, digits);
 }
 
@@ -353,10 +377,6 @@
         // remapped by avb_manage_hashtree_error_mode().
         avb_assert_not_reached();
         break;
-      case AVB_HASHTREE_ERROR_MODE_PANIC:
-        verity_mode = "panicking";
-        dm_verity_mode = "panic_on_corruption";
-        break;
     }
     new_ret = avb_replace(
         slot_data->cmdline, "$(ANDROID_VERITY_MODE)", dm_verity_mode);
diff --git a/libavb/avb_crc32.c b/libavb/avb_crc32.c
index 6b70383..7d4cb09 100644
--- a/libavb/avb_crc32.c
+++ b/libavb/avb_crc32.c
@@ -1,66 +1,114 @@
+/*-
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ */
+
 /*
- * Copyright 2020 The Android Open Source Project
+ *  First, the polynomial itself and its table of feedback terms.  The
+ *  polynomial is
+ *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
  *
- * 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
+ *  Note that we take it "backwards" and put the highest-order term in
+ *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
+ *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
+ *  the MSB being 1
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *  Note that the usual hardware shift register implementation, which
+ *  is what we're using (we're merely optimizing it by doing eight-bit
+ *  chunks at a time) shifts bits into the lowest-order term.  In our
+ *  implementation, that means shifting towards the right.  Why do we
+ *  do it this way?  Because the calculated CRC must be transmitted in
+ *  order from highest-order term to lowest-order term.  UARTs transmit
+ *  characters in order from LSB to MSB.  By storing the CRC this way
+ *  we hand it to the UART in the order low-byte to high-byte; the UART
+ *  sends each low-bit to hight-bit; and the result is transmission bit
+ *  by bit from highest- to lowest-order term without requiring any bit
+ *  shuffling on our part.  Reception works similarly
  *
- * 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.
+ *  The feedback terms table consists of 256, 32-bit entries.  Notes
+ *
+ *      The table can be generated at runtime if desired; code to do so
+ *      is shown later.  It might not be obvious, but the feedback
+ *      terms simply represent the results of eight shift/xor opera
+ *      tions for all combinations of data and CRC register values
+ *
+ *      The values must be right-shifted by eight bits by the "updcrc
+ *      logic; the shift must be unsigned (bring in zeroes).  On some
+ *      hardware you could probably optimize the shift in assembler by
+ *      using byte-swap instructions
+ *      polynomial $edb88320
+ *
+ *
+ * CRC32 code derived from work by Gary S. Brown.
  */
 
-/* See https://en.wikipedia.org/wiki/Computation_of_cyclic_redundancy_checks
- * for info on the general algorithm. We use the following configuration:
- *   32-bit CRC
- *   Polynomial = 0x04C11DB7
- *   MSB-first
- *   Input and output complement
- *   Input and output bit reversal
- *
- * This implementation optimizes for size and readability. We only need this for
- * 28 bytes of A/B booting metadata, so efficiency is largely irrelevant whereas
- * a 1KiB lookup table can be a significant cost for bootloaders.
- */
-
+#include "avb_sysdeps.h"
 #include "avb_util.h"
 
-/* Lookup table for reversing 4 bits. */
-/* clang-format off */
-static uint8_t reverse_4bit_table[] = {
-  0x0, 0x8, 0x4, 0xC,
-  0x2, 0xA, 0x6, 0xE,
-  0x1, 0x9, 0x5, 0xD,
-  0x3, 0xB, 0x7, 0xF
-};
-/* clang-format on */
+/* Code taken from FreeBSD 8 */
 
-static uint8_t reverse_byte(uint8_t val) {
-  return (reverse_4bit_table[val & 0xF] << 4) | reverse_4bit_table[val >> 4];
-}
+static uint32_t iavb_crc32_tab[] = {
+    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+    0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+    0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+    0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+    0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+    0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+    0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+    0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+    0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+    0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+    0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+    0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+    0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+    0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+    0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+    0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+    0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+    0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+    0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+    0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+    0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+    0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
 
-static uint32_t reverse_uint32(uint32_t val) {
-  return (reverse_byte(val) << 24) | (reverse_byte(val >> 8) << 16) |
-         (reverse_byte(val >> 16) << 8) | reverse_byte(val >> 24);
+/*
+ * A function that calculates the CRC-32 based on the table above is
+ * given below for documentation purposes. An equivalent implementation
+ * of this function that's actually used in the kernel can be found
+ * in sys/libkern.h, where it can be inlined.
+ */
+
+static uint32_t iavb_crc32(uint32_t crc_in, const uint8_t* buf, int size) {
+  const uint8_t* p = buf;
+  uint32_t crc;
+
+  crc = crc_in ^ ~0U;
+  while (size--)
+    crc = iavb_crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+  return crc ^ ~0U;
 }
 
 uint32_t avb_crc32(const uint8_t* buf, size_t size) {
-  uint32_t crc = 0xFFFFFFFF;
-
-  for (size_t i = 0; i < size; ++i) {
-    crc = crc ^ ((uint32_t)reverse_byte(buf[i]) << 24);
-    for (int j = 0; j < 8; ++j) {
-      if (crc & 0x80000000) {
-        crc = (crc << 1) ^ 0x04C11DB7;
-      } else {
-        crc <<= 1;
-      }
-    }
-  }
-
-  return reverse_uint32(~crc);
+  return iavb_crc32(0, buf, size);
 }
diff --git a/libavb/avb_descriptor.c b/libavb/avb_descriptor.c
index 9a5a482..7030a40 100644
--- a/libavb/avb_descriptor.c
+++ b/libavb/avb_descriptor.c
@@ -48,8 +48,6 @@
   const uint8_t* desc_start;
   const uint8_t* desc_end;
   const uint8_t* p;
-  uint64_t desc_offset = 0;
-  uint64_t desc_size = 0;
 
   if (image_data == NULL) {
     avb_error("image_data is NULL\n.");
@@ -61,9 +59,6 @@
     goto out;
   }
 
-  /* The data is supposed to have been cryptographically verified at this point,
-   * This check is just adding defense in depth.
-   */
   if (image_size < sizeof(AvbVBMetaImageHeader)) {
     avb_error("Length is smaller than header.\n");
     goto out;
@@ -80,53 +75,27 @@
   header = (const AvbVBMetaImageHeader*)image_data;
   image_end = image_data + image_size;
 
-  /* Since the data is supposed to have been cryptographically verified at this
-   * point, being overflow-safe is just for defense.
-   * The following lines are overflow-safe version of:
-   * desc_offset = sizeof(AvbVBMetaImageHeader) +
-   *               avb_be64toh(header->authentication_data_block_size)) +
-   *               avb_be64toh(header->descriptors_offset)
-   */
-  if (!avb_safe_add(&desc_offset,
-                    sizeof(AvbVBMetaImageHeader),
-                    avb_be64toh(header->authentication_data_block_size))) {
-    avb_error("Invalid authentication data block size.\n");
-    goto out;
-  }
-  if (!avb_safe_add_to(&desc_offset, avb_be64toh(header->descriptors_offset))) {
-    avb_error("Invalid descriptors offset.\n");
-    goto out;
-  }
+  desc_start = image_data + sizeof(AvbVBMetaImageHeader) +
+               avb_be64toh(header->authentication_data_block_size) +
+               avb_be64toh(header->descriptors_offset);
 
-  if (desc_offset > (uint64_t)(image_end - image_data)) {
+  desc_end = desc_start + avb_be64toh(header->descriptors_size);
+
+  if (desc_start < image_data || desc_start > image_end ||
+      desc_end < image_data || desc_end > image_end || desc_end < desc_start) {
     avb_error("Descriptors not inside passed-in data.\n");
     goto out;
   }
 
-  desc_start = image_data + desc_offset;
-
-  desc_size = avb_be64toh(header->descriptors_size);
-  if (desc_size > (uint64_t)(image_end - desc_start)) {
-    avb_error("Descriptors not inside passed-in data.\n");
-    goto out;
-  }
-
-  desc_end = desc_start + desc_size;
-
   for (p = desc_start; p < desc_end;) {
-    uint64_t nb_following;
-    uint64_t nb_total = 0;
-    const AvbDescriptor* dh;
-
-    if (sizeof(AvbDescriptor) > (size_t)(desc_end - p)) {
+    if (p + sizeof(AvbDescriptor) > desc_end) {
       avb_error("Invalid descriptor length.\n");
       goto out;
     }
-
-    dh = (const AvbDescriptor*)p;
+    const AvbDescriptor* dh = (const AvbDescriptor*)p;
     avb_assert_aligned(dh);
-    nb_following = avb_be64toh(dh->num_bytes_following);
-
+    uint64_t nb_following = avb_be64toh(dh->num_bytes_following);
+    uint64_t nb_total = 0;
     if (!avb_safe_add(&nb_total, sizeof(AvbDescriptor), nb_following)) {
       avb_error("Invalid descriptor length.\n");
       goto out;
@@ -137,7 +106,7 @@
       goto out;
     }
 
-    if (nb_total > (uint64_t)(desc_end - p)) {
+    if (nb_total + p < desc_start || nb_total + p > desc_end) {
       avb_error("Invalid data in descriptors array.\n");
       goto out;
     }
@@ -146,7 +115,10 @@
       goto out;
     }
 
-    p += nb_total;
+    if (!avb_safe_add_to((uint64_t*)(&p), nb_total)) {
+      avb_error("Invalid descriptor length.\n");
+      goto out;
+    }
   }
 
   ret = true;
diff --git a/libavb/avb_property_descriptor.h b/libavb/avb_property_descriptor.h
index 85b0163..a2fef69 100644
--- a/libavb/avb_property_descriptor.h
+++ b/libavb/avb_property_descriptor.h
@@ -37,10 +37,10 @@
 
 /* A descriptor for properties (free-form key/value pairs).
  *
- * Following this struct are |key_num_bytes| bytes of key data encoded
- * as UTF-8, followed by a NUL byte, then |value_num_bytes| bytes of
- * value data, followed by a NUL byte and then enough padding to make
- * the combined size a multiple of 8.
+ * Following this struct are |key_num_bytes| bytes of key data,
+ * followed by a NUL byte, then |value_num_bytes| bytes of value data,
+ * followed by a NUL byte and then enough padding to make the combined
+ * size a multiple of 8.
  */
 typedef struct AvbPropertyDescriptor {
   AvbDescriptor parent_descriptor;
diff --git a/libavb/avb_sha.h b/libavb/avb_sha.h
index f4c42ea..82ac9a5 100644
--- a/libavb/avb_sha.h
+++ b/libavb/avb_sha.h
@@ -40,28 +40,28 @@
 #include "avb_crypto.h"
 #include "avb_sysdeps.h"
 
-/* The following defines must be set to something appropriate
- *
- *   AVB_SHA256_CONTEXT_SIZE - the size of AvbSHA256Ctx, excluding the buffer
- *   AVB_SHA512_CONTEXT_SIZE - the size of AvbSHA512Ctx, exclusing the buffer
- *
- * For example, if AvbSHA512Ctx is implemented using BoringSSL this would be
- * defined as sizeof(SHA256_CTX).
- *
- * We expect the implementation to provide a header file with the name
- * avb_crypto_ops_impl.h to do all this.
- */
-#include "avb_crypto_ops_impl.h"
+/* Block size in bytes of a SHA-256 digest. */
+#define AVB_SHA256_BLOCK_SIZE 64
+
+
+/* Block size in bytes of a SHA-512 digest. */
+#define AVB_SHA512_BLOCK_SIZE 128
 
 /* Data structure used for SHA-256. */
 typedef struct {
-  uint8_t reserved[AVB_SHA256_CONTEXT_SIZE];
+  uint32_t h[8];
+  uint64_t tot_len;
+  size_t len;
+  uint8_t block[2 * AVB_SHA256_BLOCK_SIZE];
   uint8_t buf[AVB_SHA256_DIGEST_SIZE]; /* Used for storing the final digest. */
 } AvbSHA256Ctx;
 
 /* Data structure used for SHA-512. */
 typedef struct {
-  uint8_t reserved[AVB_SHA512_CONTEXT_SIZE];
+  uint64_t h[8];
+  uint64_t tot_len;
+  size_t len;
+  uint8_t block[2 * AVB_SHA512_BLOCK_SIZE];
   uint8_t buf[AVB_SHA512_DIGEST_SIZE]; /* Used for storing the final digest. */
 } AvbSHA512Ctx;
 
diff --git a/libavb/sha/sha256_impl.c b/libavb/avb_sha256.c
similarity index 92%
rename from libavb/sha/sha256_impl.c
rename to libavb/avb_sha256.c
index 7e99358..8a1c3ae 100644
--- a/libavb/sha/sha256_impl.c
+++ b/libavb/avb_sha256.c
@@ -35,9 +35,7 @@
  * SUCH DAMAGE.
  */
 
-#include <libavb/avb_sha.h>
-
-#include "avb_crypto_ops_impl.h"
+#include "avb_sha.h"
 
 #define SHFR(x, n) (x >> n)
 #define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
@@ -114,8 +112,7 @@
     0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
 
 /* SHA-256 implementation */
-void avb_sha256_init(AvbSHA256Ctx* avb_ctx) {
-  AvbSHA256ImplCtx* ctx = (AvbSHA256ImplCtx*)avb_ctx->reserved;
+void avb_sha256_init(AvbSHA256Ctx* ctx) {
 #ifndef UNROLL_LOOPS
   int i;
   for (i = 0; i < 8; i++) {
@@ -136,7 +133,7 @@
   ctx->tot_len = 0;
 }
 
-static void SHA256_transform(AvbSHA256ImplCtx* ctx,
+static void SHA256_transform(AvbSHA256Ctx* ctx,
                              const uint8_t* message,
                              size_t block_nb) {
   uint32_t w[64];
@@ -335,8 +332,7 @@
   }
 }
 
-void avb_sha256_update(AvbSHA256Ctx* avb_ctx, const uint8_t* data, size_t len) {
-  AvbSHA256ImplCtx* ctx = (AvbSHA256ImplCtx*)avb_ctx->reserved;
+void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, size_t len) {
   size_t block_nb;
   size_t new_len, rem_len, tmp_len;
   const uint8_t* shifted_data;
@@ -367,8 +363,7 @@
   ctx->tot_len += (block_nb + 1) << 6;
 }
 
-uint8_t* avb_sha256_final(AvbSHA256Ctx* avb_ctx) {
-  AvbSHA256ImplCtx* ctx = (AvbSHA256ImplCtx*)avb_ctx->reserved;
+uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) {
   size_t block_nb;
   size_t pm_len;
   uint64_t len_b;
@@ -390,18 +385,18 @@
 
 #ifndef UNROLL_LOOPS
   for (i = 0; i < 8; i++) {
-    UNPACK32(ctx->h[i], &avb_ctx->buf[i << 2]);
+    UNPACK32(ctx->h[i], &ctx->buf[i << 2]);
   }
 #else
-  UNPACK32(ctx->h[0], &avb_ctx->buf[0]);
-  UNPACK32(ctx->h[1], &avb_ctx->buf[4]);
-  UNPACK32(ctx->h[2], &avb_ctx->buf[8]);
-  UNPACK32(ctx->h[3], &avb_ctx->buf[12]);
-  UNPACK32(ctx->h[4], &avb_ctx->buf[16]);
-  UNPACK32(ctx->h[5], &avb_ctx->buf[20]);
-  UNPACK32(ctx->h[6], &avb_ctx->buf[24]);
-  UNPACK32(ctx->h[7], &avb_ctx->buf[28]);
+  UNPACK32(ctx->h[0], &ctx->buf[0]);
+  UNPACK32(ctx->h[1], &ctx->buf[4]);
+  UNPACK32(ctx->h[2], &ctx->buf[8]);
+  UNPACK32(ctx->h[3], &ctx->buf[12]);
+  UNPACK32(ctx->h[4], &ctx->buf[16]);
+  UNPACK32(ctx->h[5], &ctx->buf[20]);
+  UNPACK32(ctx->h[6], &ctx->buf[24]);
+  UNPACK32(ctx->h[7], &ctx->buf[28]);
 #endif /* !UNROLL_LOOPS */
 
-  return avb_ctx->buf;
+  return ctx->buf;
 }
diff --git a/libavb/sha/sha512_impl.c b/libavb/avb_sha512.c
similarity index 92%
rename from libavb/sha/sha512_impl.c
rename to libavb/avb_sha512.c
index 1eb9ba4..6cdc494 100644
--- a/libavb/sha/sha512_impl.c
+++ b/libavb/avb_sha512.c
@@ -35,9 +35,7 @@
  * SUCH DAMAGE.
  */
 
-#include <libavb/avb_sha.h>
-
-#include "avb_crypto_ops_impl.h"
+#include "avb_sha.h"
 
 #define SHFR(x, n) (x >> n)
 #define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
@@ -133,8 +131,7 @@
 
 /* SHA-512 implementation */
 
-void avb_sha512_init(AvbSHA512Ctx* avb_ctx) {
-  AvbSHA512ImplCtx* ctx = (AvbSHA512ImplCtx*)avb_ctx->reserved;
+void avb_sha512_init(AvbSHA512Ctx* ctx) {
 #ifdef UNROLL_LOOPS_SHA512
   ctx->h[0] = sha512_h0[0];
   ctx->h[1] = sha512_h0[1];
@@ -155,7 +152,7 @@
   ctx->tot_len = 0;
 }
 
-static void SHA512_transform(AvbSHA512ImplCtx* ctx,
+static void SHA512_transform(AvbSHA512Ctx* ctx,
                              const uint8_t* message,
                              size_t block_nb) {
   uint64_t w[80];
@@ -321,8 +318,7 @@
   }
 }
 
-void avb_sha512_update(AvbSHA512Ctx* avb_ctx, const uint8_t* data, size_t len) {
-  AvbSHA512ImplCtx* ctx = (AvbSHA512ImplCtx*)avb_ctx->reserved;
+void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, size_t len) {
   size_t block_nb;
   size_t new_len, rem_len, tmp_len;
   const uint8_t* shifted_data;
@@ -353,8 +349,7 @@
   ctx->tot_len += (block_nb + 1) << 7;
 }
 
-uint8_t* avb_sha512_final(AvbSHA512Ctx* avb_ctx) {
-  AvbSHA512ImplCtx* ctx = (AvbSHA512ImplCtx*)avb_ctx->reserved;
+uint8_t* avb_sha512_final(AvbSHA512Ctx* ctx) {
   size_t block_nb;
   size_t pm_len;
   uint64_t len_b;
@@ -376,18 +371,18 @@
   SHA512_transform(ctx, ctx->block, block_nb);
 
 #ifdef UNROLL_LOOPS_SHA512
-  UNPACK64(ctx->h[0], &avb_ctx->buf[0]);
-  UNPACK64(ctx->h[1], &avb_ctx->buf[8]);
-  UNPACK64(ctx->h[2], &avb_ctx->buf[16]);
-  UNPACK64(ctx->h[3], &avb_ctx->buf[24]);
-  UNPACK64(ctx->h[4], &avb_ctx->buf[32]);
-  UNPACK64(ctx->h[5], &avb_ctx->buf[40]);
-  UNPACK64(ctx->h[6], &avb_ctx->buf[48]);
-  UNPACK64(ctx->h[7], &avb_ctx->buf[56]);
+  UNPACK64(ctx->h[0], &ctx->buf[0]);
+  UNPACK64(ctx->h[1], &ctx->buf[8]);
+  UNPACK64(ctx->h[2], &ctx->buf[16]);
+  UNPACK64(ctx->h[3], &ctx->buf[24]);
+  UNPACK64(ctx->h[4], &ctx->buf[32]);
+  UNPACK64(ctx->h[5], &ctx->buf[40]);
+  UNPACK64(ctx->h[6], &ctx->buf[48]);
+  UNPACK64(ctx->h[7], &ctx->buf[56]);
 #else
   for (i = 0; i < 8; i++)
-    UNPACK64(ctx->h[i], &avb_ctx->buf[i << 3]);
+    UNPACK64(ctx->h[i], &ctx->buf[i << 3]);
 #endif /* UNROLL_LOOPS_SHA512 */
 
-  return avb_ctx->buf;
+  return ctx->buf;
 }
diff --git a/libavb/avb_slot_verify.c b/libavb/avb_slot_verify.c
index 6a2745a..0d75210 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;
     }
   }
 
@@ -561,7 +561,7 @@
     AvbSlotVerifyFlags flags,
     bool allow_verification_error,
     AvbVBMetaImageFlags toplevel_vbmeta_flags,
-    uint32_t rollback_index_location,
+    int rollback_index_location,
     const char* partition_name,
     size_t partition_name_len,
     const uint8_t* expected_public_key,
@@ -791,9 +791,6 @@
   }
 
   uint32_t rollback_index_location_to_use = rollback_index_location;
-  if (is_main_vbmeta) {
-    rollback_index_location_to_use = vbmeta_header.rollback_index_location;
-  }
 
   /* Check if key used to make signature matches what is expected. */
   if (pk_data != NULL) {
@@ -1208,15 +1205,15 @@
     }
   }
 
-  if (rollback_index_location_to_use >=
-      AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) {
+  if (rollback_index_location < 0 ||
+      rollback_index_location >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) {
     avb_errorv(
         full_partition_name, ": Invalid rollback_index_location.\n", NULL);
     ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
     goto out;
   }
 
-  slot_data->rollback_indexes[rollback_index_location_to_use] =
+  slot_data->rollback_indexes[rollback_index_location] =
       vbmeta_header.rollback_index;
 
   if (out_algorithm_type != NULL) {
@@ -1335,7 +1332,7 @@
 
 static bool has_system_partition(AvbOps* ops, const char* ab_suffix) {
   char part_name[AVB_PART_NAME_MAX_SIZE];
-  const char* system_part_name = "system";
+  char* system_part_name = "system";
   char guid_buf[37];
   AvbIOResult io_ret;
 
diff --git a/libavb/avb_slot_verify.h b/libavb/avb_slot_verify.h
index 633e7da..a2c98f4 100644
--- a/libavb/avb_slot_verify.h
+++ b/libavb/avb_slot_verify.h
@@ -88,8 +88,7 @@
   AVB_HASHTREE_ERROR_MODE_RESTART,
   AVB_HASHTREE_ERROR_MODE_EIO,
   AVB_HASHTREE_ERROR_MODE_LOGGING,
-  AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO,
-  AVB_HASHTREE_ERROR_MODE_PANIC
+  AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO
 } AvbHashtreeErrorMode;
 
 /* Flags that influence how avb_slot_verify() works.
diff --git a/libavb/avb_util.c b/libavb/avb_util.c
index 83d526a..c0064cf 100644
--- a/libavb/avb_util.c
+++ b/libavb/avb_util.c
@@ -453,22 +453,3 @@
   hex_data[n * 2] = '\0';
   return hex_data;
 }
-
-size_t avb_uint64_to_base10(uint64_t value,
-                            char digits[AVB_MAX_DIGITS_UINT64]) {
-  char rev_digits[AVB_MAX_DIGITS_UINT64];
-  size_t n, num_digits;
-
-  for (num_digits = 0; num_digits < AVB_MAX_DIGITS_UINT64 - 1;) {
-    rev_digits[num_digits++] = avb_div_by_10(&value) + '0';
-    if (value == 0) {
-      break;
-    }
-  }
-
-  for (n = 0; n < num_digits; n++) {
-    digits[n] = rev_digits[num_digits - 1 - n];
-  }
-  digits[n] = '\0';
-  return n;
-}
diff --git a/libavb/avb_util.h b/libavb/avb_util.h
index b6b036e..3ca84f0 100644
--- a/libavb/avb_util.h
+++ b/libavb/avb_util.h
@@ -286,11 +286,6 @@
  */
 char* avb_bin2hex(const uint8_t* data, size_t data_len);
 
-/* Writes |value| to |digits| in base 10 followed by a NUL byte.
- * Returns number of characters written excluding the NUL byte.
- */
-#define AVB_MAX_DIGITS_UINT64 32
-size_t avb_uint64_to_base10(uint64_t value, char digits[AVB_MAX_DIGITS_UINT64]);
 #ifdef __cplusplus
 }
 #endif
diff --git a/libavb/avb_vbmeta_image.c b/libavb/avb_vbmeta_image.c
index 8a6d898..b1879f6 100644
--- a/libavb/avb_vbmeta_image.c
+++ b/libavb/avb_vbmeta_image.c
@@ -273,7 +273,6 @@
 
   dest->rollback_index = avb_be64toh(dest->rollback_index);
   dest->flags = avb_be32toh(dest->flags);
-  dest->rollback_index_location = avb_be32toh(dest->rollback_index_location);
 }
 
 const char* avb_vbmeta_verify_result_to_string(AvbVBMetaVerifyResult result) {
diff --git a/libavb/avb_vbmeta_image.h b/libavb/avb_vbmeta_image.h
index f9cbac4..d0c9f15 100644
--- a/libavb/avb_vbmeta_image.h
+++ b/libavb/avb_vbmeta_image.h
@@ -176,12 +176,10 @@
    */
   uint32_t flags;
 
-  /* 124: The location of the rollback index defined in this header.
-   * Only valid for the main vbmeta. For chained partitions, the rollback
-   * index location must be specified in the AvbChainPartitionDescriptor
-   * and this value must be set to 0.
+  /* 124: Reserved to ensure |release_string| start on a 16-byte
+   * boundary. Must be set to zeroes.
    */
-  uint32_t rollback_index_location;
+  uint8_t reserved0[4];
 
   /* 128: The release string from avbtool, e.g. "avbtool 1.0.0" or
    * "avbtool 1.0.0 xyz_board Git-234abde89". Is guaranteed to be NUL
diff --git a/libavb/avb_version.h b/libavb/avb_version.h
index e7b7f53..ce43136 100644
--- a/libavb/avb_version.h
+++ b/libavb/avb_version.h
@@ -37,7 +37,7 @@
 
 /* The version number of AVB - keep in sync with avbtool. */
 #define AVB_VERSION_MAJOR 1
-#define AVB_VERSION_MINOR 2
+#define AVB_VERSION_MINOR 1
 #define AVB_VERSION_SUB 0
 
 /* Returns a NUL-terminated string for the libavb version in use.  The
diff --git a/libavb/boringssl/avb_crypto_ops_impl.h b/libavb/boringssl/avb_crypto_ops_impl.h
deleted file mode 100644
index abfbcad..0000000
--- a/libavb/boringssl/avb_crypto_ops_impl.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2021 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.
- */
-
-#ifdef AVB_INSIDE_LIBAVB_H
-#error "You can't include avb_crypto_ops_impl.h in the public header libavb.h."
-#endif
-
-#ifndef AVB_COMPILATION
-#error "Never include this file, it may only be used from internal avb code."
-#endif
-
-#ifndef AVB_CRYPTO_OPS_IMPL_H_
-#define AVB_CRYPTO_OPS_IMPL_H_
-
-#include <openssl/is_boringssl.h>
-#include <openssl/sha.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define AVB_SHA256_CONTEXT_SIZE sizeof(SHA256_CTX)
-#define AVB_SHA512_CONTEXT_SIZE sizeof(SHA512_CTX)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* AVB_CRYPTO_OPS_IMPL_H_ */
diff --git a/libavb/boringssl/sha.c b/libavb/boringssl/sha.c
deleted file mode 100644
index 96973ec..0000000
--- a/libavb/boringssl/sha.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2021 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 <libavb/avb_sha.h>
-
-#include "avb_crypto_ops_impl.h"
-
-/* SHA-256 implementation */
-void avb_sha256_init(AvbSHA256Ctx* ctx) {
-  SHA256_CTX* realCtx = (SHA256_CTX*)ctx->reserved;
-  SHA256_Init(realCtx);
-}
-
-void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, size_t len) {
-  SHA256_CTX* realCtx = (SHA256_CTX*)ctx->reserved;
-  SHA256_Update(realCtx, data, len);
-}
-
-uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) {
-  SHA256_CTX* realCtx = (SHA256_CTX*)ctx->reserved;
-  SHA256_Final(ctx->buf, realCtx);
-  return ctx->buf;
-}
-
-/* SHA-512 implementation */
-void avb_sha512_init(AvbSHA512Ctx* ctx) {
-  SHA512_CTX* realCtx = (SHA512_CTX*)ctx->reserved;
-  SHA512_Init(realCtx);
-}
-
-void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, size_t len) {
-  SHA512_CTX* realCtx = (SHA512_CTX*)ctx->reserved;
-  SHA512_Update(realCtx, data, len);
-}
-
-uint8_t* avb_sha512_final(AvbSHA512Ctx* ctx) {
-  SHA512_CTX* realCtx = (SHA512_CTX*)ctx->reserved;
-  SHA512_Final(ctx->buf, realCtx);
-  return ctx->buf;
-}
diff --git a/libavb/sha/avb_crypto_ops_impl.h b/libavb/sha/avb_crypto_ops_impl.h
deleted file mode 100644
index 321e0fb..0000000
--- a/libavb/sha/avb_crypto_ops_impl.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2021 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.
- */
-
-#ifdef AVB_INSIDE_LIBAVB_H
-#error "You can't include avb_crypto_ops_impl.h in the public header libavb.h."
-#endif
-
-#ifndef AVB_COMPILATION
-#error "Never include this file, it may only be used from internal avb code."
-#endif
-
-#ifndef AVB_CRYPTO_OPS_IMPL_H_
-#define AVB_CRYPTO_OPS_IMPL_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <libavb/avb_sysdeps.h>
-
-/* Block size in bytes of a SHA-256 digest. */
-#define AVB_SHA256_BLOCK_SIZE 64
-
-/* Block size in bytes of a SHA-512 digest. */
-#define AVB_SHA512_BLOCK_SIZE 128
-
-/* Data structure used for SHA-256. */
-typedef struct {
-  uint32_t h[8];
-  uint64_t tot_len;
-  size_t len;
-  uint8_t block[2 * AVB_SHA256_BLOCK_SIZE];
-} AvbSHA256ImplCtx;
-
-/* Data structure used for SHA-512. */
-typedef struct {
-  uint64_t h[8];
-  uint64_t tot_len;
-  size_t len;
-  uint8_t block[2 * AVB_SHA512_BLOCK_SIZE];
-} AvbSHA512ImplCtx;
-
-#define AVB_SHA256_CONTEXT_SIZE sizeof(AvbSHA256ImplCtx)
-#define AVB_SHA512_CONTEXT_SIZE sizeof(AvbSHA512ImplCtx)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* AVB_CRYPTO_OPS_IMPL_H_ */
diff --git a/libavb_aftl/README.md b/libavb_aftl/README.md
deleted file mode 100644
index abf2636..0000000
--- a/libavb_aftl/README.md
+++ /dev/null
@@ -1,305 +0,0 @@
-# Android Firmware Transparency Log 1.0
----
-
-This repository contains tools and libraries for working with the Android
-Firmware Transparency Log components of Android Verified Boot. AFTL will be
-used to refer to these components.
-
-[TOC]
-
-# What is it?
-
-The Android Firmware Transparency Log (AFTL) is an implementation of binary
-transparency that leverages cryptographic proofs of inclusion of build images
-using a public append-only ledger. The device manufacturer stores these
-cryptographic proofs, called inclusion proofs, on-device to allow for offline
-validation of the build during system update and device boot. This ensures that
-only a publicly known build is running on the device. Furthermore, it allows
-device manufacturers and other interested parties to audit the information in
-the log to detect unexpected or malicious uses of the publisher's signing keys.
-
-
-## System overview
-
-An AFTL can be implemented using a
-[Trillian](https://github.com/google/trillian) instance which manages a Merkle
-tree with build metadata (particularly the [VBMeta
-struct](https://android.googlesource.com/platform/external/avb/+/refs/heads/master#The-VBMeta-struct))
-and keeps a repository of other data that were made transparent.
-
-![Android Firmware Transparency Log system overview](../docs/aftl-architecture.png)
-
-As part of the finalizing step in the build process the `aftltool` reaches out
-to the **AFTL personality** to submit a manufacturer-signed message (containing
-VBMeta struct and other metadata) to the AFTL. Then the AFTL personality
-submits that data to Trillian to incorporate it into the Merkle tree. After
-integration into the log, the AFTL personality returns the inclusion proof back
-to the `aftltool`, which in turn incorporates the inclusion proof with the
-VBMeta image.
-
-The AFTL uses two sets of keys for authentication and validation, the
-transparency log key and the manufacturer key.
-
-* **Transparency log key:** Used by Trillian to sign inclusion proofs. The
-  public key is embedded with the device for on-device for validation.
-* **Manufacturer key:** Used by OEMs or other build providers to sign
-  submissions sent to the log. This ensures that malicious entries posing as a
-  valid OEM entry cannot be provided to the log. For the log to authenticate
-  messages, the manufacturer key must be shared out-of-band with the AFTL prior
-  to submission.
-
-## The AftlImage struct
-
-The central data structure used for AFTL validation is the `AftlImage` struct.
-The structure is saved on the vbmeta partition, right after the
-`AvbVBMetaImage` struct, as illustrated below.
-
-![Overview of the AftlImage structure](../docs/aftl-image-location.png)
-
-This structure contains the `AftlImageHeader` header that describes the number
-of inclusion proofs (`AftlIcpEntry`) represented by this structure. Each
-inclusion proof has associated metadata, such as the transparency log URL. A
-high-level description of the structures is given below. See
-[aftltool](https://android.googlesource.com/platform/external/avb/+/refs/heads/master/aftltool.py)
-and
-[libavb_aftl](https://android.googlesource.com/platform/external/avb/+/refs/heads/master/libavb_aftl/)
-for more details.
-
-
-![Overview of AftlIcpHeader and AftlIcpEntry](../docs/aftl-image-structure.png)
-
-Each `AftlIcpEntry` structure contains the information required to validate an
-inclusion proof from a specific transparency log server for a given [VBMeta
-structure](https://android.googlesource.com/platform/external/avb/+/master#the-vbmeta-struct)
-given the corresponding transparency log public key. The inclusion proof
-validation process is described in the [inclusion proofs](#inclusion-proofs)
-section of this document.
-
-*Note*: A single `AftlImage` can have multiple inclusion proofs from different
-transparency logs. This allows the device manufacturer to not rely on a single
-transparency log, and ensures that the builds represented by the VBMeta
-structure are deemed transparent in multiple disparate jurisdictions.
-
-## Inclusion proofs
-
-An inclusion proof allows a user to prove that a specific VBMeta structure is
-included in a transparency log. An inclusion proof consists of three parts:
-
-*  A `SignedVBMetaPrimaryAnnotation` structure containing the hash of the
-VBMeta structure (and other build meta information) that is signed with the
-manufacturer key.
-*  A set of sibling node hashes (`Proof`) in a Merkle tree on the path from the
-leaf node in question, which represents the logged annotation, to the root
-node.
-*  A `TrillianLogRootDescriptor` structure containing the log's root hash,
-along with related metadata, which is signed by the transparency log’s private
-key.
-
-Validation of an inclusion proof can be performed with the following steps,
-which are implemented in both `aftltool` and `libavb_aftl`.
-
-1.  Calculate the hash of the VBMeta structure stored on the device.
-1.  Determine if the hash matches the hash stored in the
-`SignedVBMetaPrimaryAnnotation` structure inside the `AftlImage` on device. If
-it does, continue validation.
-1.  Given the set of hashes provided from the transparency log as part of the
-inclusion proof, attempt to recalculate the root hash. Details of the process
-can be found [here](https://www.certificate-transparency.org/log-proofs-work)
-in the Merkle Audit Proofs section.
-1.  Check the calculated root hash against the log's root hash from the
-inclusion proof. If it matches, continue validation.
-1.  Finally, verify the log root signature given the calculated root hash and
-the public key of the transparency log that is stored on device. If the
-signature is valid, the inclusion proof is valid.
-
-# Tools and libraries
-
-This section contains information about the tools and libraries added to AVB
-repository or modified to include AFTL support.
-
-## aftltool and libavb\_aftl
-
-The main purpose of
-[aftltool](https://android.googlesource.com/platform/external/avb/+/refs/heads/master/aftltool.py)
-is to add an inclusion proof to an existing `vbmeta.img` to be used
-for transparency checks at boot or system update time. This enhanced image is
-stored in the `vbmeta` partition or in the `vbmeta_a` and
-`vbmeta_b` slots when using A/B  and will still be of minimal size
-(for out-of-band updates). Creation, query, and verification tasks can be
-performed with `aftltool`.
-
-In addition to the `aftltool`, the
-[libavb](https://android.googlesource.com/platform/external/avb/+/master/libavb/)
-library comes with an extension called
-[libavb\_aftl](https://android.googlesource.com/platform/external/avb/+/master/libavb_aftl/).
-This component performs all verification on the device side related to AFTL and
-inclusion proofs. That is, it loads the `vbmeta` partition, checks
-the VBMeta structure signature, walks through each inclusion proof stored in
-the `AftlImage`, and validates them against a trusted transparency
-log key stored on the device.
-
-This library is intended to be used in both the boot loader and inside Android
-as part of the OTA client. The main entry point for verification is
-`aftl_slot_verify()`, which is intended to be called after `vbmeta`
-verification is done via `avb_slot_verify()`.
-
-## Files and directories
-
-* `libavb_aftl/`
-  + An implementation of AFTL inclusion proof validation. This
-    code is designed to be highly portable so it can be used in as many contexts
-    as possible. This code requires a C99-compliant C compiler. Only the content
-    declared in `libavb_aftl.h` is considered public. The other files are
-    considered internal to the implementation and may change without notice.
-* `libavb_aftl/README.md`
-  + This document.
-* `test/`
-  + Unit tests for `libavb_aftl.`
-* `test/data/`
-  + Test data for the `aftltool` and `libavb_aftl` unit tests.
-* `aftltool`
-  + A symlink to `aftltool.py`.
-* `aftltool.py`
-  + A tool written in Python for working with images related to AFTL.
-* `Android.bp`
-  + Build rules for `aftltool`,  `libavb_aftl` (a static library
-    for use on the device), host-side libraries (for unit tests), and unit
-    tests.
-* `aftltool_test.py`
-  + Source-code for `aftltool` related unit tests.
-* `aftltool_integration_test.py`
-  + Source-code for `aftltool` related integration tests against
-    a live transparency log.
-
-## Portability
-
-The `libavb_aftl` code is intended to be used in bootloaders in devices that
-will load Android or other operating systems. The suggested approach is to copy
-the appropriate header and C files mentioned in the previous section into the
-boot loader and integrate as appropriate. The library is intended to be highly
-portable, working on both little and big endian architectures, as well as
-32-bit and 64-bit variants of each. It is also intended to work in environments
-without the standard C library and runtime.
-
-As in libavb, if the `AVB_ENABLE_DEBUG` preprocessor symbol is set, the code
-will include useful debug information and run-time checks.
-
-## Versioning and compatibility
-
-The `libavb_aftl` library follows the [versioning of
-libavb](https://android.googlesource.com/platform/external/avb/+/master#Versioning-and-Compatibility).
-
-## Using aftltool
-
-The content for the vbmeta partition is assumed to have been generated
-previously using `avbtool`. Instructions can be found in the
-[README.md](https://android.googlesource.com/platform/external/avb/+/master/README.md)
-for libavb. After the VBMeta partition is generated, it can be extended with
-inclusion proofs from transparency logs in the following manner:
-
-
-```
-aftltool make_icp_from_vbmeta \
-      --vbmeta_image_path /path/to/image.bin \
-      --output OUTPUT \
-      [--signing_helper /path/to/external/signer]  \
-      [--signing_helper_with_files /path/to/external/signer_with_files] \
-      --version_incremental STR \
-      --transparency_log_servers host:port,/path/to/log_key.pub \
-      --manufacturer_key /path/to/priv_key \
-      [--padding_size NUM]
-```
-
-The
-[version\_incremental](https://developer.android.com/reference/android/os/Build.VERSION#INCREMENTAL)
-is a part of the build fingerprint which allows for tagging the transparency
-log entry for easier tracking.
-
-An example of how to use the `make_icp_from_vbmeta` command is as follows:
-
-```
-aftltool make_icp_from_vbmeta \
-      --vbmeta_image_path ./vbmeta.img \
-      --output ./vbmeta_icp.img \
-      --version_incremental 99999999 \
-      --transparency_log_servers \
-        log.aftl-android.com:9000,/aftl-log-rsa-pub.pem \
-      --manufacturer_key ./manufacturer-rsa.pem \
-      --algorithm SHA256_RSA4096 \
-      --padding 4096
-```
-
-The AFTL information can be viewed in a human readable format in the following
-manner:
-
-```
-aftltool info_image_icp \
-      --vbmeta_image_path /path/to/image.bin \
-      [--output OUTPUT]
-```
-
-An example using `info_image_icp` is as follows:
-
-```
-aftltool info_image_icp --vbmeta_image_path ./vbmeta.img
-```
-
-Verification of an AFTL enhanced vbmeta image can be performed with the
-following command:
-
-```
-aftltool verify_image_icp \
-      --vbmeta_image_path /path/to/image.bin \
-      --transparency_log_pub_keys [TRANSPARENCY_LOG_PUB_KEYS [TRANSPARENCY_LOG_PUB_KEYS ...]] 
-[--output OUTPUT]
-```
-
-An example using `verify_image_icp` is as follows:
-
-```
-aftltool verify_image_icp --vbmeta_image_path ./vbmeta.img --transparency_log_pub_keys ./log_pub_key.pem
-```
-
-More information on the options can be found using `aftltool --help`.
-
-# Build system integration
-
-AFTL modifications only will work if AVB is enabled in the build. In Android,
-AVB is enabled in an `Android.mk` file by the `BOARD_AVB_ENABLE` variable as
-described in the AVB
-[README.md](https://android.googlesource.com/platform/external/avb/#Build-System-Integration).
-
-When calling the
-[sign\_target\_files\_apks.py](https://android.googlesource.com/platform/build/+/master/tools/releasetools/sign_target_files_apks.py)
-script, the following parameters must be set:
-
-*  `--aftl_tool_path`, the location of aftltool.py
-*  `--aftl_server`, the address of the transparency log
-*  `--aftl_key_path`, which gives the path to the DER encoded transparency log public key
-*  `--aftl_manufacturer_key_path`, which gives the path to the DER encoded OEM
-  private key. Note: This key is different to the signing key used to sign VBMeta
-  structure
-
-Remember that the public part of the transparency log keys need to be available
-to the bootloader of the device to validate the inclusion proofs.
-
-# Device integration
-
-This section discusses recommendations and best practices for integrating
-`libavb` AFTL support with a device boot loader. It's important to emphasize
-that these are just recommendations. Most of these recommendations are the same
-as those for AVB.
-
-## Recommended bootflow
-
-The boot flow should ensure checking of the inclusion proofs independent of the
-unlock state of the device. It is recommended to present the user with a
-warning in case transparency checks fail.
-
-AFTL modifies this flow in the following manner: as soon as a valid OS has been
-found, search for an `AftlImage` for each VBMeta image and validate their
-inclusion proofs (this is done by the `aftl_slot_verify` function). The result
-of the verification can be appended to the kernel command line for further
-processing by the OS.
-
-![Recommended boot flow for a device using AFTL](../docs/aftl-boot-flow.png)
diff --git a/libavb_aftl/avb_aftl_types.h b/libavb_aftl/avb_aftl_types.h
index 028ffda..e36fc18 100644
--- a/libavb_aftl/avb_aftl_types.h
+++ b/libavb_aftl/avb_aftl_types.h
@@ -44,16 +44,17 @@
 #define AVB_AFTL_SIGNATURE_SIZE 512ul
 /* Raw key size used for signature validation. */
 #define AVB_AFTL_PUB_KEY_SIZE 1032ul
-/* Limit AftlImage size to 64KB. */
-#define AVB_AFTL_MAX_AFTL_IMAGE_SIZE 65536ul
+/* Limit AftlDescriptor size to 64KB. */
+#define AVB_AFTL_MAX_AFTL_DESCRIPTOR_SIZE 65536ul
 /* Limit version.incremental size to 256 characters. */
 #define AVB_AFTL_MAX_VERSION_INCREMENTAL_SIZE 256ul
 /* AFTL trees require at most 64 hashes to reconstruct the root */
 #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 an Annotation leaf. */
-#define AVB_AFTL_MIN_ANNOTATION_SIZE 18ul
+/* 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 a TrillianLogRootDescriptor. See the
    TrillianLogRootDescriptor struct for details. The values here cover:
    version: sizeof(uint16_t)
@@ -73,37 +74,46 @@
    log_url_size: sizeof(uint32_t)
    leaf_index: sizeof(uint64_t)
    log_root_descriptor_size: sizeof(uint32_t)
-   annotation_leaf_size: sizeof(uint32_t)
+   fw_info_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
-   annotation_leaf: AVB_AFTL_MIN_ANNOTATION_SIZE
+   fw_info_leaf: AVB_AFTL_MIN_FW_INFO_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_ANNOTATION_SIZE +                     \
+   AVB_AFTL_MIN_TLRD_SIZE + AVB_AFTL_MIN_FW_INFO_SIZE +                        \
    AVB_AFTL_SIGNATURE_SIZE + AVB_AFTL_HASH_SIZE)
-/* The maximum AftlIcpEntrySize is the max AftlImage size minus the size
-   of the AftlImageHeader. */
+/* The maximum AftlIcpEntrySize is the max AftlDescriptor size minus the size
+   of the AftlIcpHeader. */
 #define AVB_AFTL_MAX_AFTL_ICP_ENTRY_SIZE \
-  (AVB_AFTL_MAX_AFTL_IMAGE_SIZE - sizeof(AftlImageHeader))
-/* The maximum Annotation size is the max AftlImage size minus the
+  (AVB_AFTL_MAX_AFTL_DESCRIPTOR_SIZE - sizeof(AftlIcpHeader))
+/* The maximum FirmwareInfo is the max AftlDescriptor size minus the
    size of the smallest valid AftlIcpEntry. */
-#define AVB_AFTL_MAX_ANNOTATION_SIZE \
-  (AVB_AFTL_MAX_AFTL_IMAGE_SIZE - AVB_AFTL_MIN_AFTL_ICP_ENTRY_SIZE)
+#define AVB_AFTL_MAX_FW_INFO_SIZE \
+  (AVB_AFTL_MAX_AFTL_DESCRIPTOR_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. */
+   max AftlDescriptor size minus the smallest valid AftlIcpEntry size. */
 #define AVB_AFTL_MAX_METADATA_SIZE \
-  (AVB_AFTL_MAX_AFTL_IMAGE_SIZE - AVB_AFTL_MIN_AFTL_ICP_ENTRY_SIZE)
+  (AVB_AFTL_MAX_AFTL_DESCRIPTOR_SIZE - AVB_AFTL_MIN_AFTL_ICP_ENTRY_SIZE)
 /* The maximum TrillianLogRootDescriptor is the size of the smallest valid
 TrillianLogRootDescriptor + the largest possible metadata size. */
 #define AVB_AFTL_MAX_TLRD_SIZE \
   (AVB_AFTL_MIN_TLRD_SIZE + AVB_AFTL_MAX_METADATA_SIZE)
 
+/* Data structure containing AFTL header information. */
+typedef struct AftlIcpHeader {
+  uint32_t magic;
+  uint32_t required_icp_version_major;
+  uint32_t required_icp_version_minor;
+  uint32_t aftl_descriptor_size; /* Total size of the AftlDescriptor. */
+  uint16_t icp_count;
+} AVB_ATTR_PACKED AftlIcpHeader;
+
 /* 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. */
@@ -118,52 +128,15 @@
   uint8_t* metadata;
 } TrillianLogRootDescriptor;
 
-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;
+/* 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;
   uint8_t* vbmeta_hash;
-  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;
+  uint8_t* json_data;
+} FirmwareInfo;
 
 /* Data structure containing AFTL inclusion proof data from a single
    transparency log. */
@@ -171,33 +144,22 @@
   uint32_t log_url_size;
   uint64_t leaf_index;
   uint32_t log_root_descriptor_size;
-  uint32_t annotation_leaf_size;
+  uint32_t fw_info_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;
-  uint8_t* log_root_descriptor_raw;
-  SignedVBMetaPrimaryAnnotationLeaf* annotation_leaf;
-  uint8_t* annotation_leaf_raw;
+  FirmwareInfo fw_info_leaf;
   uint8_t* log_root_signature;
-  uint8_t (*proofs)[AVB_AFTL_HASH_SIZE];
-} AftlIcpEntry;
+  uint8_t proofs[/*proof_hash_count*/][AVB_AFTL_HASH_SIZE];
+} AVB_ATTR_PACKED 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;
+/* Main data structure for an AFTL descriptor. */
+typedef struct AftlDescriptor {
+  AftlIcpHeader header;
   AftlIcpEntry** entries;
-} AftlImage;
+} AVB_ATTR_PACKED AftlDescriptor;
 
 #ifdef __cplusplus
 }
diff --git a/libavb_aftl/avb_aftl_util.c b/libavb_aftl/avb_aftl_util.c
index f780e82..d1fa33c 100644
--- a/libavb_aftl/avb_aftl_util.c
+++ b/libavb_aftl/avb_aftl_util.c
@@ -47,6 +47,105 @@
   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,
@@ -294,61 +393,6 @@
   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
@@ -359,252 +403,220 @@
 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);
-  uint8_t* blob_end = *aftl_blob + aftl_blob_remaining;
-  if (*aftl_blob > blob_end) {
-    return false;
-  }
-
+  avb_assert(aftl_blob_remaining >= AVB_AFTL_MIN_TLRD_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;
-  }
-
+  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. */
-  if (!read_u64(
-          &(icp_entry->log_root_descriptor.tree_size), aftl_blob, blob_end)) {
-    avb_error("Unable to parse tree size.\n");
-    return false;
-  }
-
+  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. */
-  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;
-  }
+  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. */
-  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");
+  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);
     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 timestamp field from the blob. */
-  if (!read_u64(
-          &(icp_entry->log_root_descriptor.timestamp), aftl_blob, blob_end)) {
-    avb_error("Unable to parse timestamp.\n");
-    return false;
-  }
-
+  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);
   /* Copy in the revision field from the blob. */
-  if (!read_u64(
-          &(icp_entry->log_root_descriptor.revision), aftl_blob, blob_end)) {
-    avb_error("Unable to parse revision.\n");
-    return false;
-  }
-
+  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);
   /* Copy in the metadata size field from the blob. */
-  if (!read_u16(&(icp_entry->log_root_descriptor.metadata_size),
-                aftl_blob,
-                blob_end)) {
-    avb_error("Unable to parse metadata size.\n");
-    return false;
-  }
-
+  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 (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 AftlDescriptor.\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) {
-    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");
+    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);
       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;
 }
 
-/* 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;
-  }
-  leaf->signature = signature;
+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);
 
-  if (!read_u8(&(signature->hash_algorithm), aftl_blob, blob_end)) {
-    avb_error("Unable to parse the hash algorithm.\n");
+  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;
     return false;
   }
-  if (signature->hash_algorithm >= _AVB_AFTL_HASH_ALGORITHM_NUM) {
-    avb_error("Unexpect hash algorithm in leaf signature.\n");
+  /* 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;
     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;
 }
 
-/* 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
+/* Allocates and populates a FirmwareInfo element in an
    AftlIcpEntry from a binary blob.
    The blob is expected to be pointing to the beginning of a
-   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) {
+   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);
     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;
 }
 
@@ -612,101 +624,116 @@
    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;
-  uint8_t* blob_start = *aftl_blob;
-  uint8_t* blob_end = *aftl_blob + *remaining_size;
-  if (*aftl_blob > blob_end) {
+  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");
     return NULL;
   }
 
-  if (*remaining_size < AVB_AFTL_MIN_AFTL_ICP_ENTRY_SIZE) {
-    avb_error("Invalid AftlImage\n");
+  if (*remaining_size < proof_size + AVB_AFTL_MIN_AFTL_ICP_ENTRY_SIZE) {
+    avb_error("Invalid AftlDescriptor\n");
     return NULL;
   }
 
-  icp_entry = (AftlIcpEntry*)avb_calloc(sizeof(AftlIcpEntry));
+  icp_entry = (AftlIcpEntry*)avb_calloc(proof_size + sizeof(AftlIcpEntry));
   if (!icp_entry) {
     avb_error("Failure allocating AftlIcpEntry\n");
     return NULL;
   }
-
   /* Copy in the log server URL size field. */
-  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;
-  }
+  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 (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. */
-  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;
-  }
+  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);
   /* Copy in the TrillianLogRootDescriptor size field. */
-  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;
-  }
+  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 (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;
   }
-
-  /* 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");
+  *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");
     avb_free(icp_entry);
     return NULL;
   }
-  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;
-  }
-
+  *aftl_blob += avb_aftl_member_size(AftlIcpEntry, fw_info_leaf_size);
+  parsed_size += avb_aftl_member_size(AftlIcpEntry, fw_info_leaf_size);
   /* Copy the log root signature size field. */
-  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;
-  }
+  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 (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. */
-  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;
-  }
+  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);
   /* Copy the inclusion proof size field. */
-  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_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) {
     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 (*aftl_blob + icp_entry->log_url_size < *aftl_blob ||
-      *aftl_blob + icp_entry->log_url_size > blob_end) {
-    avb_error("Invalid AftlImage.\n");
+  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) {
+    avb_error("Invalid AftlDescriptor.\n");
     avb_free(icp_entry);
     return NULL;
   }
@@ -720,61 +747,49 @@
   *aftl_blob += icp_entry->log_url_size;
 
   /* Populate the TrillianLogRootDescriptor elements. */
-  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");
+  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;
   }
-  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");
+  if (parsed_size > *remaining_size) {
+    avb_error("Invalid AftlDescriptor.\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)) {
-    free_aftl_icp_entry(icp_entry);
     return NULL;
   }
 
-  /* 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");
+  /* 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;
   }
-  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");
+  if (parsed_size > *remaining_size) {
+    avb_error("Invalid AftlDescriptor.\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;
-  }
+  if (!parse_firmware_info(icp_entry, aftl_blob)) return NULL;
 
   /* Allocate and copy the log root signature from the blob. */
-  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");
+  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) {
+    avb_error("Invalid AftlDescriptor.\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;
   }
@@ -782,88 +797,81 @@
       icp_entry->log_root_signature, *aftl_blob, icp_entry->log_root_sig_size);
   *aftl_blob += icp_entry->log_root_sig_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");
+  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;
   }
-  icp_entry->proofs = avb_calloc(icp_entry->inc_proof_size);
-  if (!icp_entry->proofs) {
+  if (parsed_size > *remaining_size) {
+    avb_error("Invalid AftlDescriptor.\n");
     free_aftl_icp_entry(icp_entry);
     return NULL;
   }
+
+  /* Finally, copy the proof hash data from the blob to the AftlDescriptor. */
   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;
 }
 
-/* Allocate and parse an AftlImage object out of binary data. */
-AftlImage* parse_aftl_image(uint8_t* aftl_blob, size_t aftl_blob_size) {
-  AftlImage* image;
-  AftlImageHeader* image_header;
-  AftlIcpEntry* entry;
-  size_t image_size;
+/* Allocate and parse an AftlDescriptor object out of binary data. */
+AftlDescriptor* parse_aftl_descriptor(uint8_t* aftl_blob,
+                                      size_t aftl_blob_size) {
+  AftlDescriptor* aftl_descriptor;
+  AftlIcpHeader* icp_header;
+  size_t aftl_descriptor_size;
   size_t i;
   size_t remaining_size;
 
-  /* Ensure the blob is at least large enough for an 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) {
+  /* Ensure the blob is at least large enough for an AftlIcpHeader */
+  avb_assert(aftl_blob_size >= sizeof(AftlIcpHeader));
+  icp_header = (AftlIcpHeader*)aftl_blob;
+  /* Check for the magic value for an AftlIcpHeader. */
+  if (icp_header->magic != AVB_AFTL_MAGIC) {
     avb_error("Invalid magic number\n");
     return NULL;
   }
   /* Extract the size out of the header. */
-  image_size = avb_be32toh(image_header->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");
+  aftl_descriptor_size = avb_be32toh(icp_header->aftl_descriptor_size);
+  if (aftl_descriptor_size > AVB_AFTL_MAX_AFTL_DESCRIPTOR_SIZE) return NULL;
+  avb_assert(aftl_descriptor_size >= sizeof(AftlIcpHeader) &&
+             aftl_descriptor_size < AVB_AFTL_MAX_AFTL_DESCRIPTOR_SIZE);
+  aftl_descriptor = (AftlDescriptor*)avb_calloc(sizeof(AftlDescriptor));
+  if (!aftl_descriptor) {
+    avb_error("Failed allocation for AftlDescriptor.\n");
     return NULL;
   }
   /* Copy the header bytes directly from the aftl_blob. */
-  avb_memcpy(&(image->header), aftl_blob, sizeof(AftlImageHeader));
+  avb_memcpy(&(aftl_descriptor->header), aftl_blob, sizeof(AftlIcpHeader));
   /* Fix endianness. */
-  image->header.required_icp_version_major =
-      avb_be32toh(image->header.required_icp_version_major);
-  image->header.required_icp_version_minor =
-      avb_be32toh(image->header.required_icp_version_minor);
-  image->header.image_size = avb_be32toh(image->header.image_size);
-  image->header.icp_count = avb_be16toh(image->header.icp_count);
+  aftl_descriptor->header.required_icp_version_major =
+      avb_be32toh(aftl_descriptor->header.required_icp_version_major);
+  aftl_descriptor->header.required_icp_version_minor =
+      avb_be32toh(aftl_descriptor->header.required_icp_version_minor);
+  aftl_descriptor->header.aftl_descriptor_size =
+      avb_be32toh(aftl_descriptor->header.aftl_descriptor_size);
+  aftl_descriptor->header.icp_count =
+      avb_be16toh(aftl_descriptor->header.icp_count);
   /* Allocate memory for the entry array */
-  image->entries = (AftlIcpEntry**)avb_calloc(sizeof(AftlIcpEntry*) *
-                                              image->header.icp_count);
-  if (!image->entries) {
+  aftl_descriptor->entries = (AftlIcpEntry**)avb_calloc(
+      sizeof(AftlIcpEntry*) * aftl_descriptor->header.icp_count);
+  if (!aftl_descriptor->entries) {
     avb_error("Failed allocation for AftlIcpEntry array.\n");
-    avb_free(image);
+    avb_free(aftl_descriptor);
     return NULL;
   }
 
   /* Jump past the header and parse out each AftlIcpEntry. */
-  aftl_blob += sizeof(AftlImageHeader);
-  remaining_size = aftl_blob_size - sizeof(AftlImageHeader);
-  for (i = 0; i < image->header.icp_count && remaining_size > 0; i++) {
-    entry = parse_icp_entry(&aftl_blob, &remaining_size);
-    if (!entry) {
-      free_aftl_image(image);
-      return NULL;
-    }
-    image->entries[i] = entry;
+  aftl_blob += sizeof(AftlIcpHeader);
+  remaining_size = aftl_blob_size - sizeof(AftlIcpHeader);
+  for (i = 0; i < aftl_descriptor->header.icp_count && remaining_size > 0;
+       i++) {
+    aftl_descriptor->entries[i] = parse_icp_entry(&aftl_blob, &remaining_size);
   }
 
-  return image;
+  return aftl_descriptor;
 }
 
 /* Free an AftlIcpEntry and each allocated sub-element. */
@@ -873,60 +881,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 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 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 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);
   }
 }
 
-/* Free the AftlImage and each allocated sub-element. */
-void free_aftl_image(AftlImage* image) {
+/* Free the AftlDescriptor and each allocated sub-element. */
+void free_aftl_descriptor(AftlDescriptor* aftl_descriptor) {
   size_t i;
 
   /* Ensure the descriptor exists before attempting to free it. */
-  if (!image) {
+  if (!aftl_descriptor) {
     return;
   }
   /* Free the entry array. */
-  if (image->entries) {
+  if (aftl_descriptor->entries) {
     /* Walk through each entry, freeing each one. */
-    for (i = 0; i < image->header.icp_count; i++) {
-      if (image->entries[i]) {
-        free_aftl_icp_entry(image->entries[i]);
+    for (i = 0; i < aftl_descriptor->header.icp_count; i++) {
+      if (aftl_descriptor->entries[i]) {
+        free_aftl_icp_entry(aftl_descriptor->entries[i]);
       }
     }
-    avb_free(image->entries);
+    avb_free(aftl_descriptor->entries);
   }
-  avb_free(image);
+  avb_free(aftl_descriptor);
 }
diff --git a/libavb_aftl/avb_aftl_util.h b/libavb_aftl/avb_aftl_util.h
index db222c3..bbbc34a 100644
--- a/libavb_aftl/avb_aftl_util.h
+++ b/libavb_aftl/avb_aftl_util.h
@@ -99,20 +99,21 @@
     uint64_t leaf_hash_size, /* Size of the leaf hash. */
     uint8_t* root_hash);     /* The resulting tree root hash. */
 
-/* Allocates and populates an AftlImage from a binary blob. */
-AftlImage* parse_aftl_image(uint8_t* aftl_blob, size_t aftl_blob_size);
+/* Allocates and populates an AftlDescriptor from a binary blob. */
+AftlDescriptor* parse_aftl_descriptor(uint8_t* aftl_blob,
+                                      size_t aftl_blob_size);
 
 /* Allocates and populates an AftlIcpEntry and all sub-fields from
    a binary blob. It is assumed that the blob points to an AftlIcpEntry. */
 AftlIcpEntry* parse_icp_entry(uint8_t** aftl_blob, size_t* remaining_size);
 
 /* Frees an AftlIcpEntry and all sub-fields that were previously
-   allocated by a call to parse_icp_entry. */
+   allocated by a call to allocate_icp_entry. */
 void free_aftl_icp_entry(AftlIcpEntry* aftl_icp_entry);
 
-/* Frees an AftlImage and all sub-fields that were previously
-   allocated by a call to parse_aftl_image. */
-void free_aftl_image(AftlImage* image);
+/* Frees an AftlDescriptor and all sub-fields that were previously
+   allocated by a call to allocate_aftl_descriptor. */
+void free_aftl_descriptor(AftlDescriptor* aftl_descriptor);
 
 #ifdef __cplusplus
 }
diff --git a/libavb_aftl/avb_aftl_validate.c b/libavb_aftl/avb_aftl_validate.c
index 2d76b2f..4121792 100644
--- a/libavb_aftl/avb_aftl_validate.c
+++ b/libavb_aftl/avb_aftl_validate.c
@@ -42,15 +42,14 @@
 
   /* Only SHA256 hashes are currently supported. If the vbmeta hash
      size is not AVB_AFTL_HASH_SIZE, return false. */
-  if (icp_entry->annotation_leaf->annotation->vbmeta_hash_size !=
-      AVB_AFTL_HASH_SIZE) {
+  if (icp_entry->fw_info_leaf.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->annotation_leaf->annotation->vbmeta_hash,
+                         icp_entry->fw_info_leaf.vbmeta_hash,
                          AVB_AFTL_HASH_SIZE) == 0;
 }
 
@@ -58,14 +57,25 @@
 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->annotation_leaf_raw,
-                                  icp_entry->annotation_leaf_size,
+  if (!avb_aftl_rfc6962_hash_leaf(icp_entry->fw_info_leaf.json_data,
+                                  icp_entry->fw_info_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,
@@ -104,9 +114,7 @@
   log_root_hash_num_bytes = AVB_AFTL_HASH_SIZE;
 
   /* Calculate the SHA256 of the TrillianLogRootDescriptor. */
-  if (!avb_aftl_sha256(icp_entry->log_root_descriptor_raw,
-                       icp_entry->log_root_descriptor_size,
-                       log_root_hash))
+  if (!avb_aftl_hash_log_root_descriptor(icp_entry, log_root_hash))
     return false;
 
   /* algorithm_data is used to calculate the padding for signature verification.
diff --git a/libavb_aftl/avb_aftl_verify.c b/libavb_aftl/avb_aftl_verify.c
index b35e417..06911b0 100644
--- a/libavb_aftl/avb_aftl_verify.c
+++ b/libavb_aftl/avb_aftl_verify.c
@@ -33,155 +33,152 @@
 #include "libavb_aftl/avb_aftl_validate.h"
 
 /* Read the vbmeta partition, after the AvbVBMetaImageHeader structure, to find
- * the AftlImage.
+ * the AftlDescriptor.
  */
-static AftlSlotVerifyResult avb_aftl_find_aftl_image(AvbOps* ops,
-                                                     const char* part_name,
-                                                     size_t vbmeta_size,
-                                                     uint8_t* out_image_buf,
-                                                     size_t* out_image_size) {
+static AvbSlotVerifyResult avb_aftl_find_aftl_descriptor(
+    AvbOps* ops,
+    const char* part_name,
+    size_t vbmeta_size,
+    uint8_t* out_image_buf,
+    size_t* out_image_size) {
   AvbIOResult io_ret;
 
-  avb_assert(vbmeta_size <= AVB_AFTL_MAX_AFTL_IMAGE_SIZE);
-  io_ret = ops->read_from_partition(ops,
-                                    part_name,
-                                    vbmeta_size /* offset */,
-                                    AVB_AFTL_MAX_AFTL_IMAGE_SIZE - vbmeta_size,
-                                    out_image_buf,
-                                    out_image_size);
-  switch (io_ret) {
-    case AVB_IO_RESULT_OK:
-      break;
-    case AVB_IO_RESULT_ERROR_OOM:
-      return AFTL_SLOT_VERIFY_RESULT_ERROR_OOM;
-    case AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION:
-    case AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION:
-      return AFTL_SLOT_VERIFY_RESULT_ERROR_IMAGE_NOT_FOUND;
-    default:
-      avb_errorv(
-          part_name, ": Error loading AftlImage from partition.\n", NULL);
-      return AFTL_SLOT_VERIFY_RESULT_ERROR_IO;
+  avb_assert(vbmeta_size <= AVB_AFTL_MAX_AFTL_DESCRIPTOR_SIZE);
+  io_ret =
+      ops->read_from_partition(ops,
+                               part_name,
+                               vbmeta_size /* offset */,
+                               AVB_AFTL_MAX_AFTL_DESCRIPTOR_SIZE - vbmeta_size,
+                               out_image_buf,
+                               out_image_size);
+
+  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+    return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+  } else if (io_ret != AVB_IO_RESULT_OK) {
+    avb_errorv(
+        part_name, ": Error loading AftlDescriptor from partition.\n", NULL);
+    return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
   }
 
   if (*out_image_size < 4 || (out_image_buf[0] != 'A') ||
       (out_image_buf[1] != 'F') || (out_image_buf[2] != 'T') ||
       (out_image_buf[3] != 'L')) {
-    avb_errorv(part_name, ": Unexpected AftlImage magic.\n", NULL);
-    return AFTL_SLOT_VERIFY_RESULT_ERROR_IMAGE_NOT_FOUND;
+    avb_errorv(part_name, ": Unexpected AftlDescriptor magic.\n", NULL);
+    return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
   }
 
-  return AFTL_SLOT_VERIFY_RESULT_OK;
+  return AVB_SLOT_VERIFY_RESULT_OK;
 }
 
-/* Performs the three validation steps for an AFTL image:
-   1. Ensure the vbmeta image hash matches that in the image.
-   2. Ensure the root hash of the Merkle tree matches that in the image.
+/* Performs the three validation steps for an AFTL descriptor:
+   1. Ensure the vbmeta image hash matches that in the descriptor.
+   2. Ensure the root hash of the Merkle tree matches that in the descriptor.
    3. Verify the signature using the transparency log public key.
 */
-static AftlSlotVerifyResult avb_aftl_verify_image(uint8_t* cur_vbmeta_data,
-                                                  size_t cur_vbmeta_size,
-                                                  uint8_t* aftl_blob,
-                                                  size_t aftl_size,
-                                                  uint8_t* key_bytes,
-                                                  size_t key_num_bytes) {
+static AvbSlotVerifyResult avb_aftl_verify_descriptor(uint8_t* cur_vbmeta_data,
+                                                      size_t cur_vbmeta_size,
+                                                      uint8_t* aftl_blob,
+                                                      size_t aftl_size,
+                                                      uint8_t* key_bytes,
+                                                      size_t key_num_bytes) {
   size_t i;
-  AftlImage* image;
-  AftlSlotVerifyResult result = AFTL_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
+  AftlDescriptor* aftl_descriptor;
+  AvbSlotVerifyResult result = AVB_SLOT_VERIFY_RESULT_OK;
 
-  /* Attempt to parse the AftlImage pointed to by aftl_blob. */
-  image = parse_aftl_image(aftl_blob, aftl_size);
-  if (!image) {
-    return AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_IMAGE;
+  /* Attempt to parse the AftlDescriptor pointed to by aftl_blob. */
+  aftl_descriptor = parse_aftl_descriptor(aftl_blob, aftl_size);
+  if (!aftl_descriptor) {
+    return AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
   }
 
-  /* Now that a valid AftlImage has been parsed, attempt to verify
+  /* Now that a valid AftlDescriptor has been parsed, attempt to verify
      the inclusion proof(s) in three steps. */
-  for (i = 0; i < image->header.icp_count; i++) {
+  for (i = 0; i < aftl_descriptor->header.icp_count; i++) {
     /* 1. Ensure that the vbmeta hash stored in the AftlIcpEntry matches
        the one that represents the partition. */
     if (!avb_aftl_verify_vbmeta_hash(
-            cur_vbmeta_data, cur_vbmeta_size, image->entries[i])) {
+            cur_vbmeta_data, cur_vbmeta_size, aftl_descriptor->entries[i])) {
       avb_error("AFTL vbmeta hash verification failed.\n");
-      result = AFTL_SLOT_VERIFY_RESULT_ERROR_VBMETA_HASH_MISMATCH;
+      result = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
       break;
     }
     /* 2. Ensure that the root hash of the Merkle tree representing
        the transparency log entry matches the one stored in the
        AftlIcpEntry. */
-    if (!avb_aftl_verify_icp_root_hash(image->entries[i])) {
+    if (!avb_aftl_verify_icp_root_hash(aftl_descriptor->entries[i])) {
       avb_error("AFTL root hash verification failed.\n");
-      result = AFTL_SLOT_VERIFY_RESULT_ERROR_TREE_HASH_MISMATCH;
+      result = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
       break;
     }
     /* 3. Verify the signature using the transparency log public
        key stored on device. */
     if (!avb_aftl_verify_entry_signature(
-            key_bytes, key_num_bytes, image->entries[i])) {
+            key_bytes, key_num_bytes, aftl_descriptor->entries[i])) {
       avb_error("AFTL signature verification failed on entry.\n");
-      result = AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_PROOF_SIGNATURE;
+      result = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
       break;
     }
-    result = AFTL_SLOT_VERIFY_RESULT_OK;
   }
-  free_aftl_image(image);
+  free_aftl_descriptor(aftl_descriptor);
   return result;
 }
 
-AftlSlotVerifyResult aftl_slot_verify(AvbOps* ops,
-                                      AvbSlotVerifyData* slot_verify_data,
-                                      uint8_t* key_bytes,
-                                      size_t key_size) {
+AvbSlotVerifyResult aftl_slot_verify(AvbOps* ops,
+                                     AvbSlotVerifyData* asv_data,
+                                     uint8_t* key_bytes,
+                                     size_t key_size) {
   size_t i;
-  size_t aftl_image_size;
+  size_t aftl_descriptor_size;
   size_t vbmeta_size;
   uint8_t* current_aftl_blob;
   char part_name[AVB_PART_NAME_MAX_SIZE];
   char* pname;
-  AftlSlotVerifyResult ret = AFTL_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
+  AvbSlotVerifyResult ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
 
-  avb_assert(slot_verify_data != NULL);
+  avb_assert(asv_data != NULL);
   avb_assert(key_bytes != NULL);
   avb_assert(key_size == AVB_AFTL_PUB_KEY_SIZE);
-  if (slot_verify_data->vbmeta_images == NULL) {
-    return AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
+  if (asv_data->vbmeta_images == NULL) {
+    return AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
   }
 
-  current_aftl_blob = avb_malloc(AVB_AFTL_MAX_AFTL_IMAGE_SIZE);
+  current_aftl_blob = avb_malloc(AVB_AFTL_MAX_AFTL_DESCRIPTOR_SIZE);
   if (current_aftl_blob == NULL) {
-    return AFTL_SLOT_VERIFY_RESULT_ERROR_OOM;
+    return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
   }
 
   /* Walk through each vbmeta blob in the AvbSlotVerifyData struct. */
-  for (i = 0; i < slot_verify_data->num_vbmeta_images; i++) {
+  for (i = 0; i < asv_data->num_vbmeta_images; i++) {
     /* Rebuild partition name, appending the suffix */
-    pname = slot_verify_data->vbmeta_images[i].partition_name;
+    pname = asv_data->vbmeta_images[i].partition_name;
     if (!avb_str_concat(part_name,
                         sizeof part_name,
                         (const char*)pname,
                         avb_strlen(pname),
-                        slot_verify_data->ab_suffix,
-                        avb_strlen(slot_verify_data->ab_suffix))) {
+                        asv_data->ab_suffix,
+                        avb_strlen(asv_data->ab_suffix))) {
       avb_error("Partition name and suffix does not fit.\n");
-      ret = AFTL_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
       break;
     }
 
-    /* Use the partition info to find the AftlImage */
-    vbmeta_size = slot_verify_data->vbmeta_images[i].vbmeta_size;
-    ret = avb_aftl_find_aftl_image(
-        ops, part_name, vbmeta_size, current_aftl_blob, &aftl_image_size);
-    if (ret != AFTL_SLOT_VERIFY_RESULT_OK) {
-      avb_errorv(part_name, ": Unable to find the AftlImage.\n", NULL);
+    /* Use the partition info to find the AftlDescriptor */
+    vbmeta_size = asv_data->vbmeta_images[i].vbmeta_size;
+    ret = avb_aftl_find_aftl_descriptor(
+        ops, part_name, vbmeta_size, current_aftl_blob, &aftl_descriptor_size);
+    if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
+      avb_error("Unable to find the AftlDescriptor.\n");
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
       break;
     }
 
-    /* Validate the AFTL image in the vbmeta image. */
-    ret = avb_aftl_verify_image(slot_verify_data->vbmeta_images[i].vbmeta_data,
-                                vbmeta_size,
-                                current_aftl_blob,
-                                aftl_image_size,
-                                key_bytes,
-                                key_size);
+    /* Validate the AFTL descriptor in the vbmeta image. */
+    ret = avb_aftl_verify_descriptor(asv_data->vbmeta_images[i].vbmeta_data,
+                                     vbmeta_size,
+                                     current_aftl_blob,
+                                     aftl_descriptor_size,
+                                     key_bytes,
+                                     key_size);
     if (ret != AVB_SLOT_VERIFY_RESULT_OK) break;
   }
 
diff --git a/libavb_aftl/avb_aftl_verify.h b/libavb_aftl/avb_aftl_verify.h
index 7c6799f..fe7ef41 100644
--- a/libavb_aftl/avb_aftl_verify.h
+++ b/libavb_aftl/avb_aftl_verify.h
@@ -34,71 +34,24 @@
 extern "C" {
 #endif
 
-typedef enum {
-  // When the verification succeeded.
-  AFTL_SLOT_VERIFY_RESULT_OK,
+/* The entry point of AFTL validation. Takes an AvbSlotVerifyData
+   struct generated by a prior call to avb_slot_verify and the transparency
+   log's public key and validates the inclusion proof. It does this by
+   performing the following three validation steps for an AFTL
+   descriptor:
+   1. Ensure the vbmeta image hash matches that in the descriptor.
+   2. Ensure the root hash of the Merkle tree matches that in the descriptor.
+   3. Verify the signature using the transparency log public key.
+   It returns AVB_SLOT_VERIFY_RESULT_OK upon success,
+   AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA if at least one VBMeta did not
+   have an AftlDescriptor and AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION
+   otherwise.
+*/
 
-  // If at some point during the verification, a memory allocation failed. This
-  // could be the case when handling a large number of log keys or inclusion
-  // proofs.
-  AFTL_SLOT_VERIFY_RESULT_ERROR_OOM,
-
-  // If at some point during the verification, we were not able to access some
-  // devices. This can be the case when reading the AftlImage from the
-  // partition.
-  AFTL_SLOT_VERIFY_RESULT_ERROR_IO,
-
-  // The VBMeta hash in the inclusion proof is not matching the VBMeta image
-  // hash.
-  AFTL_SLOT_VERIFY_RESULT_ERROR_VBMETA_HASH_MISMATCH,
-
-  // The root hash of the reconstructed tree do not match the value contained in
-  // the inclusion proof.
-  AFTL_SLOT_VERIFY_RESULT_ERROR_TREE_HASH_MISMATCH,
-
-  // The inclusion proof signature cannot be verified by the given key.
-  AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_PROOF_SIGNATURE,
-
-  // A generic error occurred during the verification.
-  AFTL_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
-
-  // At least one of the VBMetas did not have an AftlImage attached.
-  AFTL_SLOT_VERIFY_RESULT_ERROR_IMAGE_NOT_FOUND,
-
-  // Some content of one of the AFTLImages was found corrupted.
-  AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_IMAGE,
-
-  // Returned if the caller passed invalid parameters, for example if the prior
-  // call to avb_slot_verify failed.
-  AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT
-
-} AftlSlotVerifyResult;
-
-/* The entry point of AFTL validation. It uses the AvbSlotVerifyData structure,
- * |slot_verify_data|, generated by a prior call to the avb_slot_verify
- * function, and a transparency log key to validate the inclusion proof(s)
- * attached to each VBMeta images.
- *
- * The caller is responsible for ensuring that the previous call to
- * avb_slot_verify succeeded. If |slot_verify_data| is incomplete or NULL,
- * AFTL_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT will be returned.
- *
- * The AftlImage structure is located after the VBMetaImage structure. Uses
- * |ops| to read the partition where the VBMeta was loaded from.
- *
- * For each inclusion proof found, the following three validation steps are
- * performed:
- *   1. Match the VBMeta image hash with the hash in the tree leaf.
- *   2. Match the root hash of the Merkle tree with the hash in the proof.
- *   3. Verify the signature of the proof using the transparency log public key.
- * See the definition of AftlSlotVerifyResult for all the possible return
- * values.
- */
-
-AftlSlotVerifyResult aftl_slot_verify(AvbOps* ops,
-                                      AvbSlotVerifyData* slot_verify_data,
-                                      uint8_t* key_bytes,
-                                      size_t key_size);
+AvbSlotVerifyResult aftl_slot_verify(AvbOps* ops,
+                                     AvbSlotVerifyData* asv_data,
+                                     uint8_t* key_bytes,
+                                     size_t key_size);
 #ifdef __cplusplus
 }
 #endif
diff --git a/proto/README.md b/proto/README.md
index ee64b01..f40db7a 100644
--- a/proto/README.md
+++ b/proto/README.md
@@ -2,21 +2,37 @@
 ---
 
 This directory contains the proto definitions required to communicate with an
-AFTL server. The original repos and purpose for each proto file are as
+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
 follows:
 
+* aftl.proto
+   <!-- TODO(danielaustin): Add detailed message descriptions. -->
+   Contains messages used by the AFTL frontend and the Trillian log.
 * api.proto
-   Contains the messages to communicate with the AFTL personality.
+   <!-- TODO(danielaustin): Add detailed message descriptions. -->
+   Contains the messages to communicate through 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
+   Dependency of trillian.proto and aftl.proto
    For trillian.proto, contains the DigitallySigned message used by Tree and
-   SignedEntryTimestamp.
+   SignedEntryTimestamp. For aftl.proto, contains the DigitallySigned message
+   used by SignedFirmwareInfo.
 * trillian.proto
    From https://github.com/google/trillian
    Dependency of aftl.proto
    For aftl.proto, contains message definitions for SignedLogRoot.
+* aftl_google/api/annotations.proto
+   From https://github.com/googleapis/googleapis
+   Used to get access to google.api.http options.
+* aftl_google/api/http.proto
+   From https://github.com/googleapis/googleapis
+   Dependency of aftl_google/api/annotations.proto
+   Contains the HttpRule message that extends MethodOptions.
+* aftl_google/rpc/status.proto
+   From https://github.com/googleapis/googleapis
diff --git a/proto/aftl.proto b/proto/aftl.proto
new file mode 100644
index 0000000..41f1148
--- /dev/null
+++ b/proto/aftl.proto
@@ -0,0 +1,112 @@
+// 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/MODULE_LICENSE_APACHE2 b/proto/aftl_google/__init__.py
similarity index 100%
rename from MODULE_LICENSE_APACHE2
rename to proto/aftl_google/__init__.py
diff --git a/MODULE_LICENSE_APACHE2 b/proto/aftl_google/api/__init__.py
similarity index 100%
copy from MODULE_LICENSE_APACHE2
copy to proto/aftl_google/api/__init__.py
diff --git a/proto/aftl_google/api/annotations.proto b/proto/aftl_google/api/annotations.proto
new file mode 100644
index 0000000..8e90cc8
--- /dev/null
+++ b/proto/aftl_google/api/annotations.proto
@@ -0,0 +1,31 @@
+// Copyright (c) 2015, Google Inc.
+//
+// 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 google.api;
+
+import "aftl_google/api/http.proto";
+import "google/protobuf/descriptor.proto";
+
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "AnnotationsProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+extend google.protobuf.MethodOptions {
+  // See `HttpRule`.
+  HttpRule http = 72295728;
+}
diff --git a/proto/aftl_google/api/annotations_pb2.py b/proto/aftl_google/api/annotations_pb2.py
new file mode 100644
index 0000000..2982db1
--- /dev/null
+++ b/proto/aftl_google/api/annotations_pb2.py
@@ -0,0 +1,47 @@
+# pylint: skip-file
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: aftl_google/api/annotations.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()
+
+
+from proto.aftl_google.api import http_pb2 as aftl__google_dot_api_dot_http__pb2
+from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='aftl_google/api/annotations.proto',
+  package='google.api',
+  syntax='proto3',
+  serialized_options=_b('\n\016com.google.apiB\020AnnotationsProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\242\002\004GAPI'),
+  serialized_pb=_b('\n!aftl_google/api/annotations.proto\x12\ngoogle.api\x1a\x1a\x61\x66tl_google/api/http.proto\x1a google/protobuf/descriptor.proto:E\n\x04http\x12\x1e.google.protobuf.MethodOptions\x18\xb0\xca\xbc\" \x01(\x0b\x32\x14.google.api.HttpRuleBn\n\x0e\x63om.google.apiB\x10\x41nnotationsProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x04GAPIb\x06proto3')
+  ,
+  dependencies=[aftl__google_dot_api_dot_http__pb2.DESCRIPTOR,google_dot_protobuf_dot_descriptor__pb2.DESCRIPTOR,])
+
+
+HTTP_FIELD_NUMBER = 72295728
+http = _descriptor.FieldDescriptor(
+  name='http', full_name='google.api.http', index=0,
+  number=72295728, 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=True, extension_scope=None,
+  serialized_options=None, file=DESCRIPTOR)
+
+DESCRIPTOR.extensions_by_name['http'] = http
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+http.message_type = aftl__google_dot_api_dot_http__pb2._HTTPRULE
+google_dot_protobuf_dot_descriptor__pb2.MethodOptions.RegisterExtension(http)
+
+DESCRIPTOR._options = None
+# @@protoc_insertion_point(module_scope)
diff --git a/proto/aftl_google/api/annotations_pb2_grpc.py b/proto/aftl_google/api/annotations_pb2_grpc.py
new file mode 100644
index 0000000..73636b2
--- /dev/null
+++ b/proto/aftl_google/api/annotations_pb2_grpc.py
@@ -0,0 +1,4 @@
+# pylint: skip-file
+# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+import grpc
+
diff --git a/proto/aftl_google/api/http.proto b/proto/aftl_google/api/http.proto
new file mode 100644
index 0000000..b2977f5
--- /dev/null
+++ b/proto/aftl_google/api/http.proto
@@ -0,0 +1,376 @@
+// 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 google.api;
+
+option cc_enable_arenas = true;
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "HttpProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+// Defines the HTTP configuration for an API service. It contains a list of
+// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
+// to one or more HTTP REST API methods.
+message Http {
+  // A list of HTTP configuration rules that apply to individual API methods.
+  //
+  // **NOTE:** All service configuration rules follow "last one wins" order.
+  repeated HttpRule rules = 1;
+
+  // When set to true, URL path parameters will be fully URI-decoded except in
+  // cases of single segment matches in reserved expansion, where "%2F" will be
+  // left encoded.
+  //
+  // The default behavior is to not decode RFC 6570 reserved characters in multi
+  // segment matches.
+  bool fully_decode_reserved_expansion = 2;
+}
+
+// # gRPC Transcoding
+//
+// gRPC Transcoding is a feature for mapping between a gRPC method and one or
+// more HTTP REST endpoints. It allows developers to build a single API service
+// that supports both gRPC APIs and REST APIs. Many systems, including [Google
+// APIs](https://github.com/googleapis/googleapis),
+// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC
+// Gateway](https://github.com/grpc-ecosystem/grpc-gateway),
+// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature
+// and use it for large scale production services.
+//
+// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies
+// how different portions of the gRPC request message are mapped to the URL
+// path, URL query parameters, and HTTP request body. It also controls how the
+// gRPC response message is mapped to the HTTP response body. `HttpRule` is
+// typically specified as an `google.api.http` annotation on the gRPC method.
+//
+// Each mapping specifies a URL path template and an HTTP method. The path
+// template may refer to one or more fields in the gRPC request message, as long
+// as each field is a non-repeated field with a primitive (non-message) type.
+// The path template controls how fields of the request message are mapped to
+// the URL path.
+//
+// Example:
+//
+//     service Messaging {
+//       rpc GetMessage(GetMessageRequest) returns (Message) {
+//         option (google.api.http) = {
+//             get: "/v1/{name=messages/*}"
+//         };
+//       }
+//     }
+//     message GetMessageRequest {
+//       string name = 1; // Mapped to URL path.
+//     }
+//     message Message {
+//       string text = 1; // The resource content.
+//     }
+//
+// This enables an HTTP REST to gRPC mapping as below:
+//
+// HTTP | gRPC
+// -----|-----
+// `GET /v1/messages/123456`  | `GetMessage(name: "messages/123456")`
+//
+// Any fields in the request message which are not bound by the path template
+// automatically become HTTP query parameters if there is no HTTP request body.
+// For example:
+//
+//     service Messaging {
+//       rpc GetMessage(GetMessageRequest) returns (Message) {
+//         option (google.api.http) = {
+//             get:"/v1/messages/{message_id}"
+//         };
+//       }
+//     }
+//     message GetMessageRequest {
+//       message SubMessage {
+//         string subfield = 1;
+//       }
+//       string message_id = 1; // Mapped to URL path.
+//       int64 revision = 2;    // Mapped to URL query parameter `revision`.
+//       SubMessage sub = 3;    // Mapped to URL query parameter `sub.subfield`.
+//     }
+//
+// This enables a HTTP JSON to RPC mapping as below:
+//
+// HTTP | gRPC
+// -----|-----
+// `GET /v1/messages/123456?revision=2&sub.subfield=foo` |
+// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield:
+// "foo"))`
+//
+// Note that fields which are mapped to URL query parameters must have a
+// primitive type or a repeated primitive type or a non-repeated message type.
+// In the case of a repeated type, the parameter can be repeated in the URL
+// as `...?param=A&param=B`. In the case of a message type, each field of the
+// message is mapped to a separate parameter, such as
+// `...?foo.a=A&foo.b=B&foo.c=C`.
+//
+// For HTTP methods that allow a request body, the `body` field
+// specifies the mapping. Consider a REST update method on the
+// message resource collection:
+//
+//     service Messaging {
+//       rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
+//         option (google.api.http) = {
+//           patch: "/v1/messages/{message_id}"
+//           body: "message"
+//         };
+//       }
+//     }
+//     message UpdateMessageRequest {
+//       string message_id = 1; // mapped to the URL
+//       Message message = 2;   // mapped to the body
+//     }
+//
+// The following HTTP JSON to RPC mapping is enabled, where the
+// representation of the JSON in the request body is determined by
+// protos JSON encoding:
+//
+// HTTP | gRPC
+// -----|-----
+// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
+// "123456" message { text: "Hi!" })`
+//
+// The special name `*` can be used in the body mapping to define that
+// every field not bound by the path template should be mapped to the
+// request body.  This enables the following alternative definition of
+// the update method:
+//
+//     service Messaging {
+//       rpc UpdateMessage(Message) returns (Message) {
+//         option (google.api.http) = {
+//           patch: "/v1/messages/{message_id}"
+//           body: "*"
+//         };
+//       }
+//     }
+//     message Message {
+//       string message_id = 1;
+//       string text = 2;
+//     }
+//
+//
+// The following HTTP JSON to RPC mapping is enabled:
+//
+// HTTP | gRPC
+// -----|-----
+// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id:
+// "123456" text: "Hi!")`
+//
+// Note that when using `*` in the body mapping, it is not possible to
+// have HTTP parameters, as all fields not bound by the path end in
+// the body. This makes this option more rarely used in practice when
+// defining REST APIs. The common usage of `*` is in custom methods
+// which don't use the URL at all for transferring data.
+//
+// It is possible to define multiple HTTP methods for one RPC by using
+// the `additional_bindings` option. Example:
+//
+//     service Messaging {
+//       rpc GetMessage(GetMessageRequest) returns (Message) {
+//         option (google.api.http) = {
+//           get: "/v1/messages/{message_id}"
+//           additional_bindings {
+//             get: "/v1/users/{user_id}/messages/{message_id}"
+//           }
+//         };
+//       }
+//     }
+//     message GetMessageRequest {
+//       string message_id = 1;
+//       string user_id = 2;
+//     }
+//
+// This enables the following two alternative HTTP JSON to RPC mappings:
+//
+// HTTP | gRPC
+// -----|-----
+// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")`
+// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id:
+// "123456")`
+//
+// ## Rules for HTTP mapping
+//
+// 1. Leaf request fields (recursive expansion nested messages in the request
+//    message) are classified into three categories:
+//    - Fields referred by the path template. They are passed via the URL path.
+//    - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP
+//      request body.
+//    - All other fields are passed via the URL query parameters, and the
+//      parameter name is the field path in the request message. A repeated
+//      field can be represented as multiple query parameters under the same
+//      name.
+//  2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL query parameter, all fields
+//     are passed via URL path and HTTP request body.
+//  3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all
+//     fields are passed via URL path and URL query parameters.
+//
+// ### Path template syntax
+//
+//     Template = "/" Segments [ Verb ] ;
+//     Segments = Segment { "/" Segment } ;
+//     Segment  = "*" | "**" | LITERAL | Variable ;
+//     Variable = "{" FieldPath [ "=" Segments ] "}" ;
+//     FieldPath = IDENT { "." IDENT } ;
+//     Verb     = ":" LITERAL ;
+//
+// The syntax `*` matches a single URL path segment. The syntax `**` matches
+// zero or more URL path segments, which must be the last part of the URL path
+// except the `Verb`.
+//
+// The syntax `Variable` matches part of the URL path as specified by its
+// template. A variable template must not contain other variables. If a variable
+// matches a single path segment, its template may be omitted, e.g. `{var}`
+// is equivalent to `{var=*}`.
+//
+// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL`
+// contains any reserved character, such characters should be percent-encoded
+// before the matching.
+//
+// If a variable contains exactly one path segment, such as `"{var}"` or
+// `"{var=*}"`, when such a variable is expanded into a URL path on the client
+// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The
+// server side does the reverse decoding. Such variables show up in the
+// [Discovery
+// Document](https://developers.google.com/discovery/v1/reference/apis) as
+// `{var}`.
+//
+// If a variable contains multiple path segments, such as `"{var=foo/*}"`
+// or `"{var=**}"`, when such a variable is expanded into a URL path on the
+// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded.
+// The server side does the reverse decoding, except "%2F" and "%2f" are left
+// unchanged. Such variables show up in the
+// [Discovery
+// Document](https://developers.google.com/discovery/v1/reference/apis) as
+// `{+var}`.
+//
+// ## Using gRPC API Service Configuration
+//
+// gRPC API Service Configuration (service config) is a configuration language
+// for configuring a gRPC service to become a user-facing product. The
+// service config is simply the YAML representation of the `google.api.Service`
+// proto message.
+//
+// As an alternative to annotating your proto file, you can configure gRPC
+// transcoding in your service config YAML files. You do this by specifying a
+// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same
+// effect as the proto annotation. This can be particularly useful if you
+// have a proto that is reused in multiple services. Note that any transcoding
+// specified in the service config will override any matching transcoding
+// configuration in the proto.
+//
+// Example:
+//
+//     http:
+//       rules:
+//         # Selects a gRPC method and applies HttpRule to it.
+//         - selector: example.v1.Messaging.GetMessage
+//           get: /v1/messages/{message_id}/{sub.subfield}
+//
+// ## Special notes
+//
+// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the
+// proto to JSON conversion must follow the [proto3
+// specification](https://developers.google.com/protocol-buffers/docs/proto3#json).
+//
+// While the single segment variable follows the semantics of
+// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String
+// Expansion, the multi segment variable **does not** follow RFC 6570 Section
+// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion
+// does not expand special characters like `?` and `#`, which would lead
+// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding
+// for multi segment variables.
+//
+// The path variables **must not** refer to any repeated or mapped field,
+// because client libraries are not capable of handling such variable expansion.
+//
+// The path variables **must not** capture the leading "/" character. The reason
+// is that the most common use case "{var}" does not capture the leading "/"
+// character. For consistency, all path variables must share the same behavior.
+//
+// Repeated message fields must not be mapped to URL query parameters, because
+// no client library can support such complicated mapping.
+//
+// If an API needs to use a JSON array for request or response body, it can map
+// the request or response body to a repeated field. However, some gRPC
+// Transcoding implementations may not support this feature.
+message HttpRule {
+  // Selects a method to which this rule applies.
+  //
+  // Refer to [selector][google.api.DocumentationRule.selector] for syntax details.
+  string selector = 1;
+
+  // Determines the URL pattern is matched by this rules. This pattern can be
+  // used with any of the {get|put|post|delete|patch} methods. A custom method
+  // can be defined using the 'custom' field.
+  oneof pattern {
+    // Maps to HTTP GET. Used for listing and getting information about
+    // resources.
+    string get = 2;
+
+    // Maps to HTTP PUT. Used for replacing a resource.
+    string put = 3;
+
+    // Maps to HTTP POST. Used for creating a resource or performing an action.
+    string post = 4;
+
+    // Maps to HTTP DELETE. Used for deleting a resource.
+    string delete = 5;
+
+    // Maps to HTTP PATCH. Used for updating a resource.
+    string patch = 6;
+
+    // The custom pattern is used for specifying an HTTP method that is not
+    // included in the `pattern` field, such as HEAD, or "*" to leave the
+    // HTTP method unspecified for this rule. The wild-card rule is useful
+    // for services that provide content to Web (HTML) clients.
+    CustomHttpPattern custom = 8;
+  }
+
+  // The name of the request field whose value is mapped to the HTTP request
+  // body, or `*` for mapping all request fields not captured by the path
+  // pattern to the HTTP body, or omitted for not having any HTTP request body.
+  //
+  // NOTE: the referred field must be present at the top-level of the request
+  // message type.
+  string body = 7;
+
+  // Optional. The name of the response field whose value is mapped to the HTTP
+  // response body. When omitted, the entire response message will be used
+  // as the HTTP response body.
+  //
+  // NOTE: The referred field must be present at the top-level of the response
+  // message type.
+  string response_body = 12;
+
+  // Additional HTTP bindings for the selector. Nested bindings must
+  // not contain an `additional_bindings` field themselves (that is,
+  // the nesting may only be one level deep).
+  repeated HttpRule additional_bindings = 11;
+}
+
+// A custom pattern is used for defining custom HTTP verb.
+message CustomHttpPattern {
+  // The name of this custom HTTP verb.
+  string kind = 1;
+
+  // The path matched by this custom verb.
+  string path = 2;
+}
diff --git a/proto/aftl_google/api/http_pb2.py b/proto/aftl_google/api/http_pb2.py
new file mode 100644
index 0000000..8cd65f3
--- /dev/null
+++ b/proto/aftl_google/api/http_pb2.py
@@ -0,0 +1,251 @@
+# pylint: skip-file
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: aftl_google/api/http.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()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='aftl_google/api/http.proto',
+  package='google.api',
+  syntax='proto3',
+  serialized_options=_b('\n\016com.google.apiB\tHttpProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\370\001\001\242\002\004GAPI'),
+  serialized_pb=_b('\n\x1a\x61\x66tl_google/api/http.proto\x12\ngoogle.api\"T\n\x04Http\x12#\n\x05rules\x18\x01 \x03(\x0b\x32\x14.google.api.HttpRule\x12\'\n\x1f\x66ully_decode_reserved_expansion\x18\x02 \x01(\x08\"\x81\x02\n\x08HttpRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12\r\n\x03get\x18\x02 \x01(\tH\x00\x12\r\n\x03put\x18\x03 \x01(\tH\x00\x12\x0e\n\x04post\x18\x04 \x01(\tH\x00\x12\x10\n\x06\x64\x65lete\x18\x05 \x01(\tH\x00\x12\x0f\n\x05patch\x18\x06 \x01(\tH\x00\x12/\n\x06\x63ustom\x18\x08 \x01(\x0b\x32\x1d.google.api.CustomHttpPatternH\x00\x12\x0c\n\x04\x62ody\x18\x07 \x01(\t\x12\x15\n\rresponse_body\x18\x0c \x01(\t\x12\x31\n\x13\x61\x64\x64itional_bindings\x18\x0b \x03(\x0b\x32\x14.google.api.HttpRuleB\t\n\x07pattern\"/\n\x11\x43ustomHttpPattern\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x0c\n\x04path\x18\x02 \x01(\tBj\n\x0e\x63om.google.apiB\tHttpProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xf8\x01\x01\xa2\x02\x04GAPIb\x06proto3')
+)
+
+
+
+
+_HTTP = _descriptor.Descriptor(
+  name='Http',
+  full_name='google.api.Http',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='rules', full_name='google.api.Http.rules', index=0,
+      number=1, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='fully_decode_reserved_expansion', full_name='google.api.Http.fully_decode_reserved_expansion', index=1,
+      number=2, type=8, cpp_type=7, label=1,
+      has_default_value=False, default_value=False,
+      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=42,
+  serialized_end=126,
+)
+
+
+_HTTPRULE = _descriptor.Descriptor(
+  name='HttpRule',
+  full_name='google.api.HttpRule',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='selector', full_name='google.api.HttpRule.selector', index=0,
+      number=1, 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='get', full_name='google.api.HttpRule.get', 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='put', full_name='google.api.HttpRule.put', 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),
+    _descriptor.FieldDescriptor(
+      name='post', full_name='google.api.HttpRule.post', index=3,
+      number=4, 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='delete', full_name='google.api.HttpRule.delete', 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),
+    _descriptor.FieldDescriptor(
+      name='patch', full_name='google.api.HttpRule.patch', index=5,
+      number=6, 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='custom', full_name='google.api.HttpRule.custom', index=6,
+      number=8, 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='body', full_name='google.api.HttpRule.body', index=7,
+      number=7, 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='response_body', full_name='google.api.HttpRule.response_body', index=8,
+      number=12, 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='additional_bindings', full_name='google.api.HttpRule.additional_bindings', index=9,
+      number=11, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      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='pattern', full_name='google.api.HttpRule.pattern',
+      index=0, containing_type=None, fields=[]),
+  ],
+  serialized_start=129,
+  serialized_end=386,
+)
+
+
+_CUSTOMHTTPPATTERN = _descriptor.Descriptor(
+  name='CustomHttpPattern',
+  full_name='google.api.CustomHttpPattern',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='kind', full_name='google.api.CustomHttpPattern.kind', index=0,
+      number=1, 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='path', full_name='google.api.CustomHttpPattern.path', 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=388,
+  serialized_end=435,
+)
+
+_HTTP.fields_by_name['rules'].message_type = _HTTPRULE
+_HTTPRULE.fields_by_name['custom'].message_type = _CUSTOMHTTPPATTERN
+_HTTPRULE.fields_by_name['additional_bindings'].message_type = _HTTPRULE
+_HTTPRULE.oneofs_by_name['pattern'].fields.append(
+  _HTTPRULE.fields_by_name['get'])
+_HTTPRULE.fields_by_name['get'].containing_oneof = _HTTPRULE.oneofs_by_name['pattern']
+_HTTPRULE.oneofs_by_name['pattern'].fields.append(
+  _HTTPRULE.fields_by_name['put'])
+_HTTPRULE.fields_by_name['put'].containing_oneof = _HTTPRULE.oneofs_by_name['pattern']
+_HTTPRULE.oneofs_by_name['pattern'].fields.append(
+  _HTTPRULE.fields_by_name['post'])
+_HTTPRULE.fields_by_name['post'].containing_oneof = _HTTPRULE.oneofs_by_name['pattern']
+_HTTPRULE.oneofs_by_name['pattern'].fields.append(
+  _HTTPRULE.fields_by_name['delete'])
+_HTTPRULE.fields_by_name['delete'].containing_oneof = _HTTPRULE.oneofs_by_name['pattern']
+_HTTPRULE.oneofs_by_name['pattern'].fields.append(
+  _HTTPRULE.fields_by_name['patch'])
+_HTTPRULE.fields_by_name['patch'].containing_oneof = _HTTPRULE.oneofs_by_name['pattern']
+_HTTPRULE.oneofs_by_name['pattern'].fields.append(
+  _HTTPRULE.fields_by_name['custom'])
+_HTTPRULE.fields_by_name['custom'].containing_oneof = _HTTPRULE.oneofs_by_name['pattern']
+DESCRIPTOR.message_types_by_name['Http'] = _HTTP
+DESCRIPTOR.message_types_by_name['HttpRule'] = _HTTPRULE
+DESCRIPTOR.message_types_by_name['CustomHttpPattern'] = _CUSTOMHTTPPATTERN
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+Http = _reflection.GeneratedProtocolMessageType('Http', (_message.Message,), {
+  'DESCRIPTOR' : _HTTP,
+  '__module__' : 'aftl_google.api.http_pb2'
+  # @@protoc_insertion_point(class_scope:google.api.Http)
+  })
+_sym_db.RegisterMessage(Http)
+
+HttpRule = _reflection.GeneratedProtocolMessageType('HttpRule', (_message.Message,), {
+  'DESCRIPTOR' : _HTTPRULE,
+  '__module__' : 'aftl_google.api.http_pb2'
+  # @@protoc_insertion_point(class_scope:google.api.HttpRule)
+  })
+_sym_db.RegisterMessage(HttpRule)
+
+CustomHttpPattern = _reflection.GeneratedProtocolMessageType('CustomHttpPattern', (_message.Message,), {
+  'DESCRIPTOR' : _CUSTOMHTTPPATTERN,
+  '__module__' : 'aftl_google.api.http_pb2'
+  # @@protoc_insertion_point(class_scope:google.api.CustomHttpPattern)
+  })
+_sym_db.RegisterMessage(CustomHttpPattern)
+
+
+DESCRIPTOR._options = None
+# @@protoc_insertion_point(module_scope)
diff --git a/proto/aftl_google/api/http_pb2_grpc.py b/proto/aftl_google/api/http_pb2_grpc.py
new file mode 100644
index 0000000..73636b2
--- /dev/null
+++ b/proto/aftl_google/api/http_pb2_grpc.py
@@ -0,0 +1,4 @@
+# pylint: skip-file
+# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+import grpc
+
diff --git a/MODULE_LICENSE_APACHE2 b/proto/aftl_google/rpc/__init__.py
similarity index 100%
copy from MODULE_LICENSE_APACHE2
copy to proto/aftl_google/rpc/__init__.py
diff --git a/proto/aftl_google/rpc/status.proto b/proto/aftl_google/rpc/status.proto
new file mode 100644
index 0000000..b0daa36
--- /dev/null
+++ b/proto/aftl_google/rpc/status.proto
@@ -0,0 +1,94 @@
+// Copyright 2017 Google Inc.
+//
+// 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 google.rpc;
+
+import "google/protobuf/any.proto";
+
+option go_package = "google.golang.org/genproto/googleapis/rpc/status;status";
+option java_multiple_files = true;
+option java_outer_classname = "StatusProto";
+option java_package = "com.google.rpc";
+option objc_class_prefix = "RPC";
+
+// The `Status` type defines a logical error model that is suitable for
+// different programming environments, including REST APIs and RPC APIs. It is
+// used by [gRPC](https://github.com/grpc). The error model is designed to be:
+//
+// - Simple to use and understand for most users
+// - Flexible enough to meet unexpected needs
+//
+// # Overview
+//
+// The `Status` message contains three pieces of data: error code, error
+// message, and error details. The error code should be an enum value of
+// [google.rpc.Code][google.rpc.Code], but it may accept additional error codes
+// if needed.  The error message should be a developer-facing English message
+// that helps developers *understand* and *resolve* the error. If a localized
+// user-facing error message is needed, put the localized message in the error
+// details or localize it in the client. The optional error details may contain
+// arbitrary information about the error. There is a predefined set of error
+// detail types in the package `google.rpc` that can be used for common error
+// conditions.
+//
+// # Language mapping
+//
+// The `Status` message is the logical representation of the error model, but it
+// is not necessarily the actual wire format. When the `Status` message is
+// exposed in different client libraries and different wire protocols, it can be
+// mapped differently. For example, it will likely be mapped to some exceptions
+// in Java, but more likely mapped to some error codes in C.
+//
+// # Other uses
+//
+// The error model and the `Status` message can be used in a variety of
+// environments, either with or without APIs, to provide a
+// consistent developer experience across different environments.
+//
+// Example uses of this error model include:
+//
+// - Partial errors. If a service needs to return partial errors to the client,
+//     it may embed the `Status` in the normal response to indicate the partial
+//     errors.
+//
+// - Workflow errors. A typical workflow has multiple steps. Each step may
+//     have a `Status` message for error reporting.
+//
+// - Batch operations. If a client uses batch request and batch response, the
+//     `Status` message should be used directly inside batch response, one for
+//     each error sub-response.
+//
+// - Asynchronous operations. If an API call embeds asynchronous operation
+//     results in its response, the status of those operations should be
+//     represented directly using the `Status` message.
+//
+// - Logging. If some API errors are stored in logs, the message `Status` could
+//     be used directly after any stripping needed for security/privacy reasons.
+message Status {
+  // The status code, which should be an enum value of
+  // [google.rpc.Code][google.rpc.Code].
+  int32 code = 1;
+
+  // A developer-facing error message, which should be in English. Any
+  // user-facing error message should be localized and sent in the
+  // [google.rpc.Status.details][google.rpc.Status.details] field, or localized
+  // by the client.
+  string message = 2;
+
+  // A list of messages that carry the error details.  There is a common set of
+  // message types for APIs to use.
+  repeated google.protobuf.Any details = 3;
+}
diff --git a/proto/aftl_google/rpc/status_pb2.py b/proto/aftl_google/rpc/status_pb2.py
new file mode 100644
index 0000000..28e7c88
--- /dev/null
+++ b/proto/aftl_google/rpc/status_pb2.py
@@ -0,0 +1,89 @@
+# pylint: skip-file
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: aftl_google/rpc/status.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()
+
+
+from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='aftl_google/rpc/status.proto',
+  package='google.rpc',
+  syntax='proto3',
+  serialized_options=_b('\n\016com.google.rpcB\013StatusProtoP\001Z7google.golang.org/genproto/googleapis/rpc/status;status\242\002\003RPC'),
+  serialized_pb=_b('\n\x1c\x61\x66tl_google/rpc/status.proto\x12\ngoogle.rpc\x1a\x19google/protobuf/any.proto\"N\n\x06Status\x12\x0c\n\x04\x63ode\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\x12%\n\x07\x64\x65tails\x18\x03 \x03(\x0b\x32\x14.google.protobuf.AnyB^\n\x0e\x63om.google.rpcB\x0bStatusProtoP\x01Z7google.golang.org/genproto/googleapis/rpc/status;status\xa2\x02\x03RPCb\x06proto3')
+  ,
+  dependencies=[google_dot_protobuf_dot_any__pb2.DESCRIPTOR,])
+
+
+
+
+_STATUS = _descriptor.Descriptor(
+  name='Status',
+  full_name='google.rpc.Status',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='code', full_name='google.rpc.Status.code', 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='message', full_name='google.rpc.Status.message', 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='details', full_name='google.rpc.Status.details', index=2,
+      number=3, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      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=71,
+  serialized_end=149,
+)
+
+_STATUS.fields_by_name['details'].message_type = google_dot_protobuf_dot_any__pb2._ANY
+DESCRIPTOR.message_types_by_name['Status'] = _STATUS
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+Status = _reflection.GeneratedProtocolMessageType('Status', (_message.Message,), {
+  'DESCRIPTOR' : _STATUS,
+  '__module__' : 'aftl_google.rpc.status_pb2'
+  # @@protoc_insertion_point(class_scope:google.rpc.Status)
+  })
+_sym_db.RegisterMessage(Status)
+
+
+DESCRIPTOR._options = None
+# @@protoc_insertion_point(module_scope)
diff --git a/proto/aftl_google/rpc/status_pb2_grpc.py b/proto/aftl_google/rpc/status_pb2_grpc.py
new file mode 100644
index 0000000..73636b2
--- /dev/null
+++ b/proto/aftl_google/rpc/status_pb2_grpc.py
@@ -0,0 +1,4 @@
+# pylint: skip-file
+# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+import grpc
+
diff --git a/proto/aftl_pb2.py b/proto/aftl_pb2.py
new file mode 100644
index 0000000..ed811ae
--- /dev/null
+++ b/proto/aftl_pb2.py
@@ -0,0 +1,469 @@
+# -*- 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
new file mode 100644
index 0000000..a894352
--- /dev/null
+++ b/proto/aftl_pb2_grpc.py
@@ -0,0 +1,3 @@
+# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+import grpc
+
diff --git a/proto/api.proto b/proto/api.proto
index e22ae47..4c66333 100644
--- a/proto/api.proto
+++ b/proto/api.proto
@@ -1,4 +1,4 @@
-// Copyright 2019-2020 Google LLC
+// 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.
@@ -17,35 +17,27 @@
 package aftl;
 option go_package = "proto";
 
-import "trillian.proto";
+import "aftl.proto";
 
-message InclusionProof {
-  trillian.Proof proof = 1;
-  trillian.SignedLogRoot sth = 2;
-}
-
-message AddVBMetaRequest {
+message AddFirmwareInfoRequest {
   // 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 64kB.
+  // in the order of 1kB.
   bytes vbmeta = 1;
 
-  // 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;
+  SignedFirmwareInfo fw_info = 2;
 }
 
-message AddVBMetaResponse {
+message AddFirmwareInfoResponse {
   // Inclusion proof and the leaf that was added to the log, which contains
-  // the annotation on VBMeta.
+  // information on the firmware.
   // 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 annotation_proof = 1;
-  bytes          annotation_leaf = 2;
+  InclusionProof fw_info_proof = 1;
+  bytes          fw_info_leaf = 2;
 
   // Inclusion proof and leaf that was added to the log, which contains the full
   // vbmeta partition.
@@ -55,13 +47,12 @@
   bytes          vbmeta_leaf = 4;
 }
 
-message AnnotateVBMetaWithBuildRequest {
-  // Serialized SignedVBMetaBuildAnnotation.  This annotation contains the hash
-  // of the full build image. See types/types.go.
-  bytes signed_vbmeta_build_annotation = 1;
+message AddFirmwareImageRequest {
 
-  // Bytes of the binary images. The hash value of the concatenation of these
-  // chunk is contained in SignedVBMetaBuildAnnotation.
+  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
   // This is ignored if any of the requests origin_url is set.
   bytes image_chunk = 2;
 
@@ -70,13 +61,14 @@
   string origin_url = 3;
 }
 
-message AnnotateVBMetaWithBuildResponse {
+message AddFirmwareImageResponse {
+
   // 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  annotation_proof = 1;
-  bytes           annotation_leaf = 2;
+  InclusionProof fw_image_info_proof = 1;
+  Leaf           fw_image_info_leaf = 2;
 }
 
 service AFTLog {
@@ -84,11 +76,12 @@
   // Insert a new VBMeta structure into the log.
   // This request will effectively create 2 log entries:
   //  - VBMeta itself
-  //  - Vendor annotations, which includes a reference to the VBMeta.
-  rpc AddVBMeta(AddVBMetaRequest) returns (AddVBMetaResponse) {}
+  //  - Vendor annotations, including a reference to the VBMeta leaf.
+  rpc AddFirmwareInfo(AddFirmwareInfoRequest) returns (AddFirmwareInfoResponse) {}
 
   // Upload (or copy) the complete firmware image.
-  rpc AnnotateVBMetaWithBuild(stream AnnotateVBMetaWithBuildResponse) returns (AnnotateVBMetaWithBuildResponse) {}
+  rpc AddFirmwareImage(stream AddFirmwareImageRequest) returns (AddFirmwareImageResponse) {}
 
-  // TODO(tweek): GetProofByHash, GetSthConsistency, GetEntries, GetRootKeys
+  // TODO GetProofByHash, GetSthConsistency, GetEntries, GetRootKeys
 }
+
diff --git a/proto/api_pb2.py b/proto/api_pb2.py
index fbe3bab..f8f9a01 100644
--- a/proto/api_pb2.py
+++ b/proto/api_pb2.py
@@ -1,3 +1,4 @@
+# pylint: skip-file
 # -*- coding: utf-8 -*-
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: api.proto
@@ -13,7 +14,7 @@
 _sym_db = _symbol_database.Default()
 
 
-import trillian_pb2 as trillian__pb2
+import aftl_pb2 as aftl__pb2
 
 
 DESCRIPTOR = _descriptor.FileDescriptor(
@@ -21,29 +22,29 @@
   package='aftl',
   syntax='proto3',
   serialized_options=_b('Z\005proto'),
-  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')
+  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')
   ,
-  dependencies=[trillian__pb2.DESCRIPTOR,])
+  dependencies=[aftl__pb2.DESCRIPTOR,])
 
 
 
 
-_INCLUSIONPROOF = _descriptor.Descriptor(
-  name='InclusionProof',
-  full_name='aftl.InclusionProof',
+_ADDFIRMWAREINFOREQUEST = _descriptor.Descriptor(
+  name='AddFirmwareInfoRequest',
+  full_name='aftl.AddFirmwareInfoRequest',
   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,
+      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(""),
       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,
+      name='fw_info', full_name='aftl.AddFirmwareInfoRequest.fw_info', 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,
@@ -61,79 +62,41 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=35,
-  serialized_end=121,
+  serialized_start=31,
+  serialized_end=114,
 )
 
 
-_ADDVBMETAREQUEST = _descriptor.Descriptor(
-  name='AddVBMetaRequest',
-  full_name='aftl.AddVBMetaRequest',
+_ADDFIRMWAREINFORESPONSE = _descriptor.Descriptor(
+  name='AddFirmwareInfoResponse',
+  full_name='aftl.AddFirmwareInfoResponse',
   filename=None,
   file=DESCRIPTOR,
   containing_type=None,
   fields=[
     _descriptor.FieldDescriptor(
-      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,
+      name='fw_info_proof', full_name='aftl.AddFirmwareInfoResponse.fw_info_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='annotation_leaf', full_name='aftl.AddVBMetaResponse.annotation_leaf', index=1,
+      name='fw_info_leaf', full_name='aftl.AddFirmwareInfoResponse.fw_info_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.AddVBMetaResponse.vbmeta_proof', index=2,
+      name='vbmeta_proof', full_name='aftl.AddFirmwareInfoResponse.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.AddVBMetaResponse.vbmeta_leaf', index=3,
+      name='vbmeta_leaf', full_name='aftl.AddFirmwareInfoResponse.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,
@@ -151,34 +114,34 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=202,
-  serialized_end=359,
+  serialized_start=117,
+  serialized_end=274,
 )
 
 
-_ANNOTATEVBMETAWITHBUILDREQUEST = _descriptor.Descriptor(
-  name='AnnotateVBMetaWithBuildRequest',
-  full_name='aftl.AnnotateVBMetaWithBuildRequest',
+_ADDFIRMWAREIMAGEREQUEST = _descriptor.Descriptor(
+  name='AddFirmwareImageRequest',
+  full_name='aftl.AddFirmwareImageRequest',
   filename=None,
   file=DESCRIPTOR,
   containing_type=None,
   fields=[
     _descriptor.FieldDescriptor(
-      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(""),
+      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,
       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.AnnotateVBMetaWithBuildRequest.image_chunk', index=1,
+      name='image_chunk', full_name='aftl.AddFirmwareImageRequest.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.AnnotateVBMetaWithBuildRequest.origin_url', index=2,
+      name='origin_url', full_name='aftl.AddFirmwareImageRequest.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,
@@ -196,29 +159,29 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=361,
-  serialized_end=474,
+  serialized_start=276,
+  serialized_end=396,
 )
 
 
-_ANNOTATEVBMETAWITHBUILDRESPONSE = _descriptor.Descriptor(
-  name='AnnotateVBMetaWithBuildResponse',
-  full_name='aftl.AnnotateVBMetaWithBuildResponse',
+_ADDFIRMWAREIMAGERESPONSE = _descriptor.Descriptor(
+  name='AddFirmwareImageResponse',
+  full_name='aftl.AddFirmwareImageResponse',
   filename=None,
   file=DESCRIPTOR,
   containing_type=None,
   fields=[
     _descriptor.FieldDescriptor(
-      name='annotation_proof', full_name='aftl.AnnotateVBMetaWithBuildResponse.annotation_proof', index=0,
+      name='fw_image_info_proof', full_name='aftl.AddFirmwareImageResponse.fw_image_info_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='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(""),
+      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,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
@@ -234,56 +197,49 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=476,
-  serialized_end=582,
+  serialized_start=398,
+  serialized_end=515,
 )
 
-_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
+_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
 _sym_db.RegisterFileDescriptor(DESCRIPTOR)
 
-InclusionProof = _reflection.GeneratedProtocolMessageType('InclusionProof', (_message.Message,), {
-  'DESCRIPTOR' : _INCLUSIONPROOF,
+AddFirmwareInfoRequest = _reflection.GeneratedProtocolMessageType('AddFirmwareInfoRequest', (_message.Message,), {
+  'DESCRIPTOR' : _ADDFIRMWAREINFOREQUEST,
   '__module__' : 'api_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.InclusionProof)
+  # @@protoc_insertion_point(class_scope:aftl.AddFirmwareInfoRequest)
   })
-_sym_db.RegisterMessage(InclusionProof)
+_sym_db.RegisterMessage(AddFirmwareInfoRequest)
 
-AddVBMetaRequest = _reflection.GeneratedProtocolMessageType('AddVBMetaRequest', (_message.Message,), {
-  'DESCRIPTOR' : _ADDVBMETAREQUEST,
+AddFirmwareInfoResponse = _reflection.GeneratedProtocolMessageType('AddFirmwareInfoResponse', (_message.Message,), {
+  'DESCRIPTOR' : _ADDFIRMWAREINFORESPONSE,
   '__module__' : 'api_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.AddVBMetaRequest)
+  # @@protoc_insertion_point(class_scope:aftl.AddFirmwareInfoResponse)
   })
-_sym_db.RegisterMessage(AddVBMetaRequest)
+_sym_db.RegisterMessage(AddFirmwareInfoResponse)
 
-AddVBMetaResponse = _reflection.GeneratedProtocolMessageType('AddVBMetaResponse', (_message.Message,), {
-  'DESCRIPTOR' : _ADDVBMETARESPONSE,
+AddFirmwareImageRequest = _reflection.GeneratedProtocolMessageType('AddFirmwareImageRequest', (_message.Message,), {
+  'DESCRIPTOR' : _ADDFIRMWAREIMAGEREQUEST,
   '__module__' : 'api_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.AddVBMetaResponse)
+  # @@protoc_insertion_point(class_scope:aftl.AddFirmwareImageRequest)
   })
-_sym_db.RegisterMessage(AddVBMetaResponse)
+_sym_db.RegisterMessage(AddFirmwareImageRequest)
 
-AnnotateVBMetaWithBuildRequest = _reflection.GeneratedProtocolMessageType('AnnotateVBMetaWithBuildRequest', (_message.Message,), {
-  'DESCRIPTOR' : _ANNOTATEVBMETAWITHBUILDREQUEST,
+AddFirmwareImageResponse = _reflection.GeneratedProtocolMessageType('AddFirmwareImageResponse', (_message.Message,), {
+  'DESCRIPTOR' : _ADDFIRMWAREIMAGERESPONSE,
   '__module__' : 'api_pb2'
-  # @@protoc_insertion_point(class_scope:aftl.AnnotateVBMetaWithBuildRequest)
+  # @@protoc_insertion_point(class_scope:aftl.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)
+_sym_db.RegisterMessage(AddFirmwareImageResponse)
 
 
 DESCRIPTOR._options = None
@@ -294,25 +250,25 @@
   file=DESCRIPTOR,
   index=0,
   serialized_options=None,
-  serialized_start=585,
-  serialized_end=766,
+  serialized_start=518,
+  serialized_end=695,
   methods=[
   _descriptor.MethodDescriptor(
-    name='AddVBMeta',
-    full_name='aftl.AFTLog.AddVBMeta',
+    name='AddFirmwareInfo',
+    full_name='aftl.AFTLog.AddFirmwareInfo',
     index=0,
     containing_service=None,
-    input_type=_ADDVBMETAREQUEST,
-    output_type=_ADDVBMETARESPONSE,
+    input_type=_ADDFIRMWAREINFOREQUEST,
+    output_type=_ADDFIRMWAREINFORESPONSE,
     serialized_options=None,
   ),
   _descriptor.MethodDescriptor(
-    name='AnnotateVBMetaWithBuild',
-    full_name='aftl.AFTLog.AnnotateVBMetaWithBuild',
+    name='AddFirmwareImage',
+    full_name='aftl.AFTLog.AddFirmwareImage',
     index=1,
     containing_service=None,
-    input_type=_ANNOTATEVBMETAWITHBUILDRESPONSE,
-    output_type=_ANNOTATEVBMETAWITHBUILDRESPONSE,
+    input_type=_ADDFIRMWAREIMAGEREQUEST,
+    output_type=_ADDFIRMWAREIMAGERESPONSE,
     serialized_options=None,
   ),
 ])
diff --git a/proto/api_pb2_grpc.py b/proto/api_pb2_grpc.py
index d487856..b1834c7 100644
--- a/proto/api_pb2_grpc.py
+++ b/proto/api_pb2_grpc.py
@@ -1,3 +1,4 @@
+# pylint: skip-file
 # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
 import grpc
 
@@ -14,15 +15,15 @@
     Args:
       channel: A grpc.Channel.
     """
-    self.AddVBMeta = channel.unary_unary(
-        '/aftl.AFTLog/AddVBMeta',
-        request_serializer=api__pb2.AddVBMetaRequest.SerializeToString,
-        response_deserializer=api__pb2.AddVBMetaResponse.FromString,
+    self.AddFirmwareInfo = channel.unary_unary(
+        '/aftl.AFTLog/AddFirmwareInfo',
+        request_serializer=api__pb2.AddFirmwareInfoRequest.SerializeToString,
+        response_deserializer=api__pb2.AddFirmwareInfoResponse.FromString,
         )
-    self.AnnotateVBMetaWithBuild = channel.stream_unary(
-        '/aftl.AFTLog/AnnotateVBMetaWithBuild',
-        request_serializer=api__pb2.AnnotateVBMetaWithBuildResponse.SerializeToString,
-        response_deserializer=api__pb2.AnnotateVBMetaWithBuildResponse.FromString,
+    self.AddFirmwareImage = channel.stream_unary(
+        '/aftl.AFTLog/AddFirmwareImage',
+        request_serializer=api__pb2.AddFirmwareImageRequest.SerializeToString,
+        response_deserializer=api__pb2.AddFirmwareImageResponse.FromString,
         )
 
 
@@ -30,17 +31,17 @@
   # missing associated documentation comment in .proto file
   pass
 
-  def AddVBMeta(self, request, context):
+  def AddFirmwareInfo(self, request, context):
     """Insert a new VBMeta structure into the log.
     This request will effectively create 2 log entries:
     - VBMeta itself
-    - Vendor annotations, which includes a reference to the VBMeta.
+    - Vendor annotations, including a reference to the VBMeta leaf.
     """
     context.set_code(grpc.StatusCode.UNIMPLEMENTED)
     context.set_details('Method not implemented!')
     raise NotImplementedError('Method not implemented!')
 
-  def AnnotateVBMetaWithBuild(self, request_iterator, context):
+  def AddFirmwareImage(self, request_iterator, context):
     """Upload (or copy) the complete firmware image.
     """
     context.set_code(grpc.StatusCode.UNIMPLEMENTED)
@@ -50,15 +51,15 @@
 
 def add_AFTLogServicer_to_server(servicer, server):
   rpc_method_handlers = {
-      'AddVBMeta': grpc.unary_unary_rpc_method_handler(
-          servicer.AddVBMeta,
-          request_deserializer=api__pb2.AddVBMetaRequest.FromString,
-          response_serializer=api__pb2.AddVBMetaResponse.SerializeToString,
+      'AddFirmwareInfo': grpc.unary_unary_rpc_method_handler(
+          servicer.AddFirmwareInfo,
+          request_deserializer=api__pb2.AddFirmwareInfoRequest.FromString,
+          response_serializer=api__pb2.AddFirmwareInfoResponse.SerializeToString,
       ),
-      'AnnotateVBMetaWithBuild': grpc.stream_unary_rpc_method_handler(
-          servicer.AnnotateVBMetaWithBuild,
-          request_deserializer=api__pb2.AnnotateVBMetaWithBuildResponse.FromString,
-          response_serializer=api__pb2.AnnotateVBMetaWithBuildResponse.SerializeToString,
+      'AddFirmwareImage': grpc.stream_unary_rpc_method_handler(
+          servicer.AddFirmwareImage,
+          request_deserializer=api__pb2.AddFirmwareImageRequest.FromString,
+          response_serializer=api__pb2.AddFirmwareImageResponse.SerializeToString,
       ),
   }
   generic_handler = grpc.method_handlers_generic_handler(
diff --git a/test/Android.bp b/test/Android.bp
index 38627fd..057cd1e 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -14,16 +14,6 @@
 // limitations under the License.
 //
 
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "external_avb_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   SPDX-license-identifier-MIT
-    default_applicable_licenses: ["external_avb_license"],
-}
-
 python_test_host {
     name: "at_auth_unlock_unittest",
     main: "at_auth_unlock_unittest.py",
@@ -50,13 +40,3 @@
     },
     test_config: "at_auth_unlock_unittest.xml",
 }
-
-filegroup {
-    name: "avb_testkey_rsa4096",
-    srcs: ["data/testkey_rsa4096.pem"],
-}
-
-filegroup {
-    name: "avb_testkey_rsa2048",
-    srcs: ["data/testkey_rsa2048.pem"],
-}
diff --git a/test/avb_aftl_fuzz.cc b/test/avb_aftl_fuzz.cc
deleted file mode 100644
index 89b59d8..0000000
--- a/test/avb_aftl_fuzz.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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 33a598c..0d3ba54 100644
--- a/test/avb_aftl_util_unittest.cc
+++ b/test/avb_aftl_util_unittest.cc
@@ -29,13 +29,8 @@
 
 namespace {
 
-/* 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";
+const char kAftlDescriptorPath[] = "test/data/aftl_descriptor.bin";
+const char kAftlDescriptorMultiPath[] = "test/data/aftl_descriptor_multi.bin";
 
 }  // namespace
 
@@ -46,35 +41,49 @@
   AvbAftlUtilTest() {}
   ~AvbAftlUtilTest() {}
   void SetUp() override {
-    std::string content;
+    uint8_t* aftl_blob;
+    int64_t aftl_descriptor_size;
 
     BaseAvbToolTest::SetUp();
-    /* Read in test data from the aftl_image binaries. */
-    ASSERT_TRUE(
-        base::ReadFileToString(base::FilePath(kAftlImagePath), &content));
-    content = content.substr(kAftlImageOffset);
-    /* Allocate and populate an AftlImage for testing. */
-    aftl_image_ = parse_aftl_image((uint8_t*)content.data(), content.size());
+    /* Read in test data from the aftl_descriptor binaries. */
+    base::GetFileSize(base::FilePath(kAftlDescriptorPath),
+                      &aftl_descriptor_size);
+    ASSERT_GT(aftl_descriptor_size, 0);
+    aftl_blob = (uint8_t*)avb_malloc(aftl_descriptor_size);
+    ASSERT_TRUE(aftl_blob != NULL);
+    base::ReadFile(base::FilePath(kAftlDescriptorPath),
+                   (char*)aftl_blob,
+                   aftl_descriptor_size);
+    /* Allocate and populate an AftlDescriptor for testing. */
+    aftl_descriptor_ = parse_aftl_descriptor(aftl_blob, aftl_descriptor_size);
+    avb_free(aftl_blob);
 
-    /* Read in test data from the aftl_image file with multiple ICPs. */
-    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((uint8_t*)content.data(), content.size());
+    /* Read in test data from the aftl_descriptor file with multiple ICPs. */
+    base::GetFileSize(base::FilePath(kAftlDescriptorMultiPath),
+                      &aftl_descriptor_size);
+    ASSERT_GT(aftl_descriptor_size, 0);
+    aftl_blob = (uint8_t*)avb_malloc(aftl_descriptor_size);
+    ASSERT_TRUE(aftl_blob != NULL);
+    base::ReadFile(base::FilePath(kAftlDescriptorMultiPath),
+                   (char*)aftl_blob,
+                   aftl_descriptor_size);
+    /* Allocate and populate an AftlDescriptor for testing. */
+    aftl_descriptor_multi_ =
+        parse_aftl_descriptor(aftl_blob, aftl_descriptor_size);
+    avb_free(aftl_blob);
   }
 
   void TearDown() override {
-    free_aftl_image(aftl_image_);
-    free_aftl_image(aftl_image_multi_);
+    free_aftl_descriptor(aftl_descriptor_);
+    free_aftl_descriptor(aftl_descriptor_multi_);
     BaseAvbToolTest::TearDown();
   }
 
-  void TestAftlImageHeader(AftlImageHeader* aftl_header, uint16_t icp_count) {
+  void TestAftlIcpHeader(AftlIcpHeader* 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, 2ul);
+    EXPECT_EQ(aftl_header->required_icp_version_minor, 1ul);
     EXPECT_EQ(aftl_header->icp_count, icp_count);
   }
 
@@ -83,7 +92,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->annotation_leaf_size, 0ul);
+    EXPECT_GT(icp_entry->fw_info_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);
@@ -98,44 +107,43 @@
     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->annotation_leaf->annotation->vbmeta_hash_size,
-              AVB_AFTL_HASH_SIZE);
+    EXPECT_EQ(icp_entry->fw_info_leaf.vbmeta_hash_size, AVB_AFTL_HASH_SIZE);
     EXPECT_EQ(icp_entry->proof_hash_count * 32ul, icp_entry->inc_proof_size);
   }
 
  protected:
-  AftlImage* aftl_image_;
-  AftlImage* aftl_image_multi_;
+  AftlDescriptor* aftl_descriptor_;
+  AftlDescriptor* aftl_descriptor_multi_;
 };
 
-TEST_F(AvbAftlUtilTest, AftlImageHeaderStructure) {
-  AftlImageHeader* header;
-  ASSERT_NE(aftl_image_, nullptr);
-  header = &(aftl_image_->header);
+TEST_F(AvbAftlUtilTest, AftlIcpHeaderStructure) {
+  AftlIcpHeader* header;
+  ASSERT_NE(aftl_descriptor_, nullptr);
+  header = &(aftl_descriptor_->header);
   ASSERT_NE(header, nullptr);
-  TestAftlImageHeader(header, 1);
+  TestAftlIcpHeader(header, 1);
 }
 
-TEST_F(AvbAftlUtilTest, AftlImageMultipleIcps) {
-  AftlImageHeader* header;
+TEST_F(AvbAftlUtilTest, AftlDescriptorMultipleIcps) {
+  AftlIcpHeader* header;
   size_t i;
 
-  ASSERT_NE(aftl_image_multi_, nullptr);
-  header = &(aftl_image_multi_->header);
+  ASSERT_NE(aftl_descriptor_multi_, nullptr);
+  header = &(aftl_descriptor_multi_->header);
   ASSERT_NE(header, nullptr);
-  TestAftlImageHeader(header, 2);
+  TestAftlIcpHeader(header, 2);
 
   for (i = 0; i < header->icp_count; i++) {
-    ASSERT_NE(aftl_image_multi_->entries[i], nullptr)
+    ASSERT_NE(aftl_descriptor_multi_->entries[i], nullptr)
         << " Failed at entry " << i;
-    TestAftlIcpEntry(aftl_image_multi_->entries[i]);
+    TestAftlIcpEntry(aftl_descriptor_multi_->entries[i]);
   }
 }
 
 TEST_F(AvbAftlUtilTest, AftlIcpEntryStructure) {
   AftlIcpEntry* icp_entry;
 
-  icp_entry = aftl_image_->entries[0];
+  icp_entry = aftl_descriptor_->entries[0];
   ASSERT_NE(icp_entry, nullptr);
   TestAftlIcpEntry(icp_entry);
 }
diff --git a/test/avb_aftl_validate_unittest.cc b/test/avb_aftl_validate_unittest.cc
index 2a78e6a..ce5ae71 100644
--- a/test/avb_aftl_validate_unittest.cc
+++ b/test/avb_aftl_validate_unittest.cc
@@ -33,15 +33,8 @@
 
 namespace {
 
-/* 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";
+const char kAftlKeyBytesPath[] = "test/data/aftl_key_bytes.bin";
+const char kAftlLogSigPath[] = "test/data/aftl_log_sig.bin";
 
 } /* namespace */
 
@@ -53,108 +46,44 @@
   AvbAftlValidateTest() {}
   ~AvbAftlValidateTest() {}
   void SetUp() override {
-    /* 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};
+    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=\"}}}}}";
     BaseAvbToolTest::SetUp();
 
     /* Read in test data from the key and log_sig binaries. */
-    ASSERT_TRUE(
-        base::ReadFileToString(base::FilePath(kKeyBytesPath), &key_bytes_));
-
-    /* Allocate and populate the inclusion proof */
-    icp_entry_ = (AftlIcpEntry*)avb_malloc(sizeof(AftlIcpEntry));
+    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);
     if (!icp_entry_) return;
     icp_entry_->log_root_descriptor.version = 1;
     icp_entry_->log_root_descriptor.tree_size = 3;
@@ -163,141 +92,107 @@
     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 = 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) {
+    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) {
       return;
     }
-    memcpy(icp_entry_->log_root_descriptor_raw,
-           kLogRootDescriptorHeader,
-           kLogRootDescriptorHeaderSize);
-    memcpy(icp_entry_->log_root_descriptor_raw + kLogRootDescriptorHeaderSize,
-           kLogRootDescriptorRootHash,
+    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",
            AVB_AFTL_HASH_SIZE);
-    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_->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;
-    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_) {
-      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)
+    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)
         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_;
-  std::string key_bytes_;
+  uint8_t* key_bytes_;
+  uint8_t* log_sig_bytes_;
+  int64_t key_size_;
+  int64_t log_sig_size_;
 };
 
-TEST_F(AvbAftlValidateTest, VerifyEntrySignature) {
+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);
   EXPECT_EQ(true,
-            avb_aftl_verify_entry_signature(
-                (uint8_t*)key_bytes_.data(), key_bytes_.size(), icp_entry_));
+            avb_aftl_verify_entry_signature(key_bytes_, key_size_, icp_entry_));
+  avb_free(icp_entry_->log_root_signature);
 }
 
-TEST_F(AvbAftlValidateTest, VerifyIcpRootHash) {
+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. */
   EXPECT_EQ(true, avb_aftl_verify_icp_root_hash(icp_entry_));
 }
 
-TEST_F(AvbAftlValidateTest, VerifyVbmetaHash) {
-  std::string vbmeta;
-  ASSERT_TRUE(base::ReadFileToString(base::FilePath(kVBMetaPath), &vbmeta));
+TEST_F(AvbAftlValidateTest, AftlVerifyVbmetaHash) {
+  GenerateVBMetaImage("vbmeta.img",
+                      "SHA256_RSA4096",
+                      0,
+                      base::FilePath("test/data/testkey_rsa4096.pem"));
+
   EXPECT_EQ(true,
             avb_aftl_verify_vbmeta_hash(
-                (uint8_t*)vbmeta.data(), vbmeta.size(), icp_entry_));
+                vbmeta_image_.data(), vbmeta_image_.size(), icp_entry_));
 }
 
-TEST_F(AvbAftlValidateTest, RootFromIcp) {
+TEST_F(AvbAftlValidateTest, AvbAftlRootFromIcp) {
   /* Tests from trillian root_from_icp functionality:
      https://github.com/google/trillian/blob/master/merkle/log_verifier_test.go
   */
@@ -384,7 +279,7 @@
       << "Failed on test #4";
 }
 
-TEST_F(AvbAftlValidateTest, ChainInner) {
+TEST_F(AvbAftlValidateTest, AvbAftlChainInner) {
   uint8_t hash[AVB_AFTL_HASH_SIZE];
   uint8_t seed[AVB_AFTL_HASH_SIZE];
   uint8_t proof[4][AVB_AFTL_HASH_SIZE];
@@ -443,7 +338,7 @@
       << " and leaf_index 3";
 }
 
-TEST_F(AvbAftlValidateTest, ChainBorderRight) {
+TEST_F(AvbAftlValidateTest, AvbAftlChainBorderRight) {
   uint8_t hash[AVB_AFTL_HASH_SIZE];
   uint8_t seed[AVB_AFTL_HASH_SIZE];
   uint8_t proof[2][AVB_AFTL_HASH_SIZE];
@@ -473,7 +368,7 @@
          "\"7890abcdefghijklmnopqrstuvwxyz12\"]";
 }
 
-TEST_F(AvbAftlValidateTest, RFC6962HashChildren) {
+TEST_F(AvbAftlValidateTest, AvbAftlRFC6962HashChildren) {
   uint8_t hash[AVB_AFTL_HASH_SIZE];
 
   avb_aftl_rfc6962_hash_children((uint8_t*)"", 0, (uint8_t*)"", 0, hash);
@@ -498,7 +393,7 @@
       << "Failed on inputs \"abcd\" and \"efgh\"";
 }
 
-TEST_F(AvbAftlValidateTest, RFC6962HashLeaf) {
+TEST_F(AvbAftlValidateTest, AvbAftlRFC6962HashLeaf) {
   uint8_t hash[AVB_AFTL_HASH_SIZE];
   avb_aftl_rfc6962_hash_leaf((uint8_t*)"", 0, hash);
   EXPECT_EQ("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d",
@@ -510,7 +405,7 @@
       << "Failed on input \"abcdefg\"";
 }
 
-TEST_F(AvbAftlValidateTest, Sha256) {
+TEST_F(AvbAftlValidateTest, AvbAftlSha256) {
   /* Computed with:
    *
    * $ echo -n foobar |sha256sum
diff --git a/test/avb_aftl_verify_unittest.cc b/test/avb_aftl_verify_unittest.cc
index 1e44a4a..8eedcd4 100644
--- a/test/avb_aftl_verify_unittest.cc
+++ b/test/avb_aftl_verify_unittest.cc
@@ -36,15 +36,12 @@
 namespace {
 
 /* Log transparency key */
-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;
+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";
 
 } /* namespace */
 
@@ -63,11 +60,24 @@
     asv_test_data_ = NULL;
 
     /* Read in the test data. */
-    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);
+    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_);
 
     /* Set up required parts of asv_test_data */
     asv_test_data_ = (AvbSlotVerifyData*)avb_calloc(sizeof(AvbSlotVerifyData));
@@ -77,18 +87,20 @@
     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_.size();
+    asv_test_data_->vbmeta_images[0].vbmeta_size = vbmeta_blob_size_;
     asv_test_data_->vbmeta_images[0].vbmeta_data =
-        (uint8_t*)avb_calloc(vbmeta_.size());
+        (uint8_t*)avb_calloc(vbmeta_blob_size_);
     ASSERT_TRUE(asv_test_data_->vbmeta_images[0].vbmeta_data != NULL);
     memcpy(asv_test_data_->vbmeta_images[0].vbmeta_data,
-           vbmeta_.data(),
-           vbmeta_.size());
-    asv_test_data_->vbmeta_images[0].partition_name =
-        (char*)"aftl_output_vbmeta_with_1_icp";
+           vbmeta_blob_,
+           vbmeta_blob_size_);
+    asv_test_data_->vbmeta_images[0].partition_name = (char*)"aftl_verify_full";
   }
 
   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) {
@@ -103,38 +115,33 @@
 
  protected:
   AvbSlotVerifyData* asv_test_data_;
-  std::string key_;
-  std::string vbmeta_;
-  std::string vbmeta_icp_;
+  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_;
 };
 
 TEST_F(AvbAftlVerifyTest, Basic) {
-  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);
+  AvbSlotVerifyResult result =
+      aftl_slot_verify(ops_.avb_ops(), asv_test_data_, key_bytes_, key_size_);
+  EXPECT_EQ(result, AVB_SLOT_VERIFY_RESULT_OK);
 }
 
-TEST_F(AvbAftlVerifyTest, PartitionError) {
+TEST_F(AvbAftlVerifyTest, MissingAFTLDescriptor) {
   asv_test_data_->vbmeta_images[0].partition_name = (char*)"do-no-exist";
-  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);
+  AvbSlotVerifyResult result =
+      aftl_slot_verify(ops_.avb_ops(), asv_test_data_, key_bytes_, key_size_);
+  EXPECT_EQ(result, AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA);
 }
 
-TEST_F(AvbAftlVerifyTest, MismatchingVBMeta) {
+TEST_F(AvbAftlVerifyTest, NonMatchingVBMeta) {
   asv_test_data_->vbmeta_images[0].vbmeta_data[0] = 'X';
-  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_[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);
+  AvbSlotVerifyResult result =
+      aftl_slot_verify(ops_.avb_ops(), asv_test_data_, key_bytes_, key_size_);
+  EXPECT_EQ(result, AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION);
 }
 
 } /* namespace avb */
diff --git a/test/avb_crypto_ops_unittest.cc b/test/avb_crypto_ops_unittest.cc
deleted file mode 100644
index 94141c7..0000000
--- a/test/avb_crypto_ops_unittest.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2021 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 <string.h>
-
-#include <gtest/gtest.h>
-
-#include <libavb/avb_sha.h>
-
-#include "avb_unittest_util.h"
-
-namespace avb {
-
-/* These smoke tests are intended to check that the cryptographic operations
- * conform to the AVB interface and not to check the correctness of the
- * cryptograhpy.
- */
-
-TEST(CryptoOpsTest, Sha256) {
-  AvbSHA256Ctx ctx;
-
-  /* Compare with
-   *
-   * $ echo -n foobar |sha256sum
-   * c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2 -
-   */
-  avb_sha256_init(&ctx);
-  avb_sha256_update(&ctx, (const uint8_t*)"foobar", 6);
-  EXPECT_EQ("c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2",
-            mem_to_hexstring(avb_sha256_final(&ctx), AVB_SHA256_DIGEST_SIZE));
-}
-
-// Disabled for now because it takes ~30 seconds to run.
-TEST(CryptoOpsTest, DISABLED_Sha256Large) {
-  AvbSHA256Ctx ctx;
-
-  /* Also check we this works with greater than 4GiB input. Compare with
-   *
-   * $ dd if=/dev/zero bs=1048576 count=4097 |sha256sum
-   * 829816e339ff597ec3ada4c30fc840d3f2298444169d242952a54bcf3fcd7747 -
-   */
-  const size_t kMebibyte = 1048576;
-  uint8_t* megabuf;
-  megabuf = new uint8_t[kMebibyte];
-  memset((char*)megabuf, '\0', kMebibyte);
-  avb_sha256_init(&ctx);
-  for (size_t n = 0; n < 4097; n++) {
-    avb_sha256_update(&ctx, megabuf, kMebibyte);
-  }
-  EXPECT_EQ("829816e339ff597ec3ada4c30fc840d3f2298444169d242952a54bcf3fcd7747",
-            mem_to_hexstring(avb_sha256_final(&ctx), AVB_SHA256_DIGEST_SIZE));
-  delete[] megabuf;
-}
-
-TEST(CryptoOpsTest, Sha512) {
-  AvbSHA512Ctx ctx;
-
-  /* Compare with
-   *
-   * $ echo -n foobar |sha512sum
-   * 0a50261ebd1a390fed2bf326f2673c145582a6342d523204973d0219337f81616a8069b012587cf5635f6925f1b56c360230c19b273500ee013e030601bf2425
-   * -
-   */
-  avb_sha512_init(&ctx);
-  avb_sha512_update(&ctx, (const uint8_t*)"foobar", 6);
-  EXPECT_EQ(
-      "0a50261ebd1a390fed2bf326f2673c145582a6342d523204973d0219337f81616a8069b0"
-      "12587cf5635f6925f1b56c360230c19b273500ee013e030601bf2425",
-      mem_to_hexstring(avb_sha512_final(&ctx), AVB_SHA512_DIGEST_SIZE));
-}
-
-// Disabled for now because it takes ~30 seconds to run.
-TEST(CryptoOpsTest, DISABLED_Sha512Large) {
-  AvbSHA512Ctx ctx;
-
-  /* Also check we this works with greater than 4GiB input. Compare with
-   *
-   * $ dd if=/dev/zero bs=1048576 count=4097 |sha512sum
-   * eac1685671cc2060315888746de072398116c0c83b7ee9463f0576e11bfdea9cdd5ddbf291fb3ffc4ee8a1b459c798d9fb9b50b7845e2871c4b1402470aaf4c0
-   * -
-   */
-  const size_t kMebibyte = 1048576;
-  uint8_t* megabuf;
-  megabuf = new uint8_t[kMebibyte];
-  memset((char*)megabuf, '\0', kMebibyte);
-  avb_sha512_init(&ctx);
-  for (size_t n = 0; n < 4097; n++) {
-    avb_sha512_update(&ctx, megabuf, kMebibyte);
-  }
-  EXPECT_EQ(
-      "eac1685671cc2060315888746de072398116c0c83b7ee9463f0576e11bfdea9cdd5ddbf2"
-      "91fb3ffc4ee8a1b459c798d9fb9b50b7845e2871c4b1402470aaf4c0",
-      mem_to_hexstring(avb_sha512_final(&ctx), AVB_SHA512_DIGEST_SIZE));
-  delete[] megabuf;
-}
-
-}  // namespace avb
diff --git a/test/avb_slot_verify_unittest.cc b/test/avb_slot_verify_unittest.cc
index 680ea27..6d4adac 100644
--- a/test/avb_slot_verify_unittest.cc
+++ b/test/avb_slot_verify_unittest.cc
@@ -75,7 +75,7 @@
   EXPECT_NE(nullptr, slot_data);
   EXPECT_EQ(
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1152 "
       "androidboot.vbmeta.digest="
@@ -116,7 +116,7 @@
   EXPECT_NE(nullptr, slot_data);
   EXPECT_EQ(
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha512 androidboot.vbmeta.size=1152 "
       "androidboot.vbmeta.digest="
@@ -164,7 +164,7 @@
   EXPECT_NE(nullptr, slot_data);
   EXPECT_EQ(
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=unlocked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1152 "
       "androidboot.vbmeta.digest="
@@ -410,89 +410,6 @@
   avb_slot_verify_data_free(slot_data);
 }
 
-TEST_F(AvbSlotVerifyTest, RollbackIndexLocationSpecified) {
-  GenerateVBMetaImage("vbmeta_a.img",
-                      "SHA256_RSA2048",
-                      42,
-                      base::FilePath("test/data/testkey_rsa2048.pem"),
-                      "--rollback_index_location 15");
-
-  ops_.set_expected_public_key(
-      PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
-
-  AvbSlotVerifyData* slot_data = NULL;
-  const char* requested_partitions[] = {"boot", NULL};
-
-  // First try with 42 as the stored rollback index - this should
-  // succeed since the image rollback index is 42 (as set above).
-  // The rollback index at location 0 should not be checked because
-  // the rollback index location is set to 15.
-  ops_.set_stored_rollback_indexes({{15, 42}});
-  EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
-            avb_slot_verify(ops_.avb_ops(),
-                            requested_partitions,
-                            "_a",
-                            AVB_SLOT_VERIFY_FLAGS_NONE,
-                            AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
-                            &slot_data));
-  EXPECT_NE(nullptr, slot_data);
-
-  for (size_t n = 0; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) {
-    uint64_t expected_rollback = 0;
-    if (n == 15) expected_rollback = 42;
-
-    EXPECT_EQ(expected_rollback, slot_data->rollback_indexes[n]);
-  }
-
-  avb_slot_verify_data_free(slot_data);
-
-  // Then try with 43 for the stored rollback index - this should fail
-  // because the image has rollback index 42 which is less than 43.
-  ops_.set_stored_rollback_indexes({{0, 41}, {15, 43}});
-  EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX,
-            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);
-  EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX,
-            avb_slot_verify(ops_.avb_ops(),
-                            requested_partitions,
-                            "_a",
-                            AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,
-                            AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
-                            &slot_data));
-  EXPECT_NE(nullptr, slot_data);
-  avb_slot_verify_data_free(slot_data);
-}
-
-TEST_F(AvbSlotVerifyTest, RollbackIndexLocationInvalid) {
-  uint32_t rollback_index_location = AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS;
-  GenerateVBMetaImage("vbmeta_a.img",
-                      "SHA256_RSA2048",
-                      42,
-                      base::FilePath("test/data/testkey_rsa2048.pem"),
-                      base::StringPrintf("--rollback_index_location %d",
-                                         rollback_index_location));
-
-  ops_.set_expected_public_key(
-      PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
-
-  AvbSlotVerifyData* slot_data = NULL;
-  const char* requested_partitions[] = {"boot", NULL};
-
-  EXPECT_NE(AVB_SLOT_VERIFY_RESULT_OK,
-            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, LoadEntirePartitionIfAllowingVerificationError) {
   const size_t boot_partition_size = 16 * 1024 * 1024;
   const size_t boot_image_size = 5 * 1024 * 1024;
@@ -667,7 +584,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           4\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Kernel Cmdline descriptor:\n"
@@ -738,7 +654,7 @@
       "cmdline in vbmeta 1234-fake-guid-for:boot_a cmdline in hash footer "
       "1234-fake-guid-for:system_a "
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1472 "
       "androidboot.vbmeta.digest="
@@ -831,64 +747,6 @@
   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);
@@ -1007,7 +865,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           11\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Chain Partition descriptor:\n"
@@ -1035,7 +892,6 @@
       "Algorithm:                SHA256_RSA4096\n"
       "Rollback Index:           12\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hash descriptor:\n"
@@ -1122,7 +978,7 @@
   EXPECT_EQ(
       "cmdline2 in hash footer cmdline2 in vbmeta "
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=4416 "
       "androidboot.vbmeta.digest="
@@ -1146,123 +1002,6 @@
             CalcVBMetaDigest("vbmeta.img", "sha256"));
 }
 
-TEST_F(AvbSlotVerifyTest, RollbackIndexLocationInChainedPartition) {
-  size_t boot_partition_size = 16 * 1024 * 1024;
-  const size_t boot_image_size = 5 * 1024 * 1024;
-  base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
-  const char* requested_partitions[] = {"boot", NULL};
-
-  EXPECT_COMMAND(0,
-                 "./avbtool add_hash_footer"
-                 " --image %s"
-                 " --kernel_cmdline 'cmdline2 in hash footer'"
-                 " --rollback_index 12"
-                 " --rollback_index_location 3"
-                 " --partition_name boot"
-                 " --partition_size %zd"
-                 " --algorithm SHA256_RSA4096"
-                 " --key test/data/testkey_rsa4096.pem"
-                 " --salt deadbeef"
-                 " --internal_release_string \"\"",
-                 boot_path.value().c_str(),
-                 boot_partition_size);
-
-  base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
-  EXPECT_COMMAND(
-      0,
-      "./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
-      " --output %s",
-      pk_path.value().c_str());
-
-  GenerateVBMetaImage(
-      "vbmeta.img",
-      "SHA256_RSA2048",
-      11,
-      base::FilePath("test/data/testkey_rsa2048.pem"),
-      base::StringPrintf("--chain_partition boot:2:%s"
-                         " --kernel_cmdline 'cmdline2 in vbmeta'"
-                         " --rollback_index_location 1"
-                         " --internal_release_string \"\"",
-                         pk_path.value().c_str()));
-
-  EXPECT_EQ(
-      "Minimum libavb version:   1.2\n"
-      "Header Block:             256 bytes\n"
-      "Authentication Block:     320 bytes\n"
-      "Auxiliary Block:          1728 bytes\n"
-      "Public key (sha1):        cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
-      "Algorithm:                SHA256_RSA2048\n"
-      "Rollback Index:           11\n"
-      "Flags:                    0\n"
-      "Rollback Index Location:  1\n"
-      "Release String:           ''\n"
-      "Descriptors:\n"
-      "    Chain Partition descriptor:\n"
-      "      Partition Name:          boot\n"
-      "      Rollback Index Location: 2\n"
-      "      Public key (sha1):       "
-      "2597c218aae470a130f61162feaae70afd97f011\n"
-      "    Kernel Cmdline descriptor:\n"
-      "      Flags:                 0\n"
-      "      Kernel Cmdline:        'cmdline2 in vbmeta'\n",
-      InfoImage(vbmeta_image_path_));
-
-  EXPECT_EQ(
-      "Footer version:           1.0\n"
-      "Image size:               16777216 bytes\n"
-      "Original image size:      5242880 bytes\n"
-      "VBMeta offset:            5242880\n"
-      "VBMeta size:              2112 bytes\n"
-      "--\n"
-      "Minimum libavb version:   1.2\n"
-      "Header Block:             256 bytes\n"
-      "Authentication Block:     576 bytes\n"
-      "Auxiliary Block:          1280 bytes\n"
-      "Public key (sha1):        2597c218aae470a130f61162feaae70afd97f011\n"
-      "Algorithm:                SHA256_RSA4096\n"
-      "Rollback Index:           12\n"
-      "Flags:                    0\n"
-      "Rollback Index Location:  3\n"
-      "Release String:           ''\n"
-      "Descriptors:\n"
-      "    Hash descriptor:\n"
-      "      Image Size:            5242880 bytes\n"
-      "      Hash Algorithm:        sha256\n"
-      "      Partition Name:        boot\n"
-      "      Salt:                  deadbeef\n"
-      "      Digest:                "
-      "184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\n"
-      "      Flags:                 0\n"
-      "    Kernel Cmdline descriptor:\n"
-      "      Flags:                 0\n"
-      "      Kernel Cmdline:        'cmdline2 in hash footer'\n",
-      InfoImage(boot_path));
-
-  ops_.set_expected_public_key(
-      PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
-
-  // Verify that the rollback index location in the chained partition is ignored
-  ops_.set_stored_rollback_indexes({{1, 11}, {2, 12}, {3, 20}});
-
-  AvbSlotVerifyData* slot_data = NULL;
-  EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
-            avb_slot_verify(ops_.avb_ops(),
-                            requested_partitions,
-                            "",
-                            AVB_SLOT_VERIFY_FLAGS_NONE,
-                            AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
-                            &slot_data));
-  EXPECT_NE(nullptr, slot_data);
-
-  EXPECT_EQ(0UL, slot_data->rollback_indexes[0]);
-  EXPECT_EQ(11UL, slot_data->rollback_indexes[1]);
-  EXPECT_EQ(12UL, slot_data->rollback_indexes[2]);
-  for (size_t n = 3; n < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; n++) {
-    EXPECT_EQ(0UL, slot_data->rollback_indexes[n]);
-  }
-  avb_slot_verify_data_free(slot_data);
-}
-
 TEST_F(AvbSlotVerifyTest, HashDescriptorInOtherVBMetaPartition) {
   size_t boot_partition_size = 16 * 1024 * 1024;
   const size_t boot_image_size = 5 * 1024 * 1024;
@@ -1323,7 +1062,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           11\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Chain Partition descriptor:\n"
@@ -1345,7 +1083,6 @@
       "Algorithm:                SHA256_RSA4096\n"
       "Rollback Index:           12\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hash descriptor:\n"
@@ -1433,7 +1170,7 @@
   EXPECT_EQ(
       "cmdline2 in hash footer cmdline2 in vbmeta "
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=4416 "
       "androidboot.vbmeta.digest="
@@ -1726,7 +1463,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           11\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Chain Partition descriptor:\n"
@@ -1779,7 +1515,7 @@
   EXPECT_EQ(
       "cmdline2 in hash footer cmdline2 in vbmeta "
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=4416 "
       "androidboot.vbmeta.digest="
@@ -1842,7 +1578,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           4\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hash descriptor:\n"
@@ -1957,7 +1692,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           4\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hash descriptor:\n"
@@ -2078,7 +1812,7 @@
   // Note the absence of 'androidboot.vbmeta.device'
   EXPECT_EQ(
       "this is=5 from foo=42 and=43 from bar "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 "
       "androidboot.vbmeta.size=3456 "
@@ -2141,7 +1875,7 @@
   EXPECT_NE(nullptr, slot_data);
   EXPECT_EQ(
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=2688 "
       "androidboot.vbmeta.digest="
@@ -2206,7 +1940,6 @@
           "Algorithm:                SHA256_RSA2048\n"
           "Rollback Index:           4\n"
           "Flags:                    %d\n"
-          "Rollback Index Location:  0\n"
           "Release String:           ''\n"
           "Descriptors:\n"
           "    Kernel Cmdline descriptor:\n"
@@ -2251,7 +1984,7 @@
         "restart_on_corruption ignore_zero_blocks\" root=/dev/dm-0 "
         "should_be_in_both=1 "
         "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-        "androidboot.vbmeta.avb_version=1.2 "
+        "androidboot.vbmeta.avb_version=1.1 "
         "androidboot.vbmeta.device_state=locked "
         "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1536 "
         "androidboot.vbmeta.digest="
@@ -2265,7 +1998,7 @@
     EXPECT_EQ(
         "root=PARTUUID=1234-fake-guid-for:system_a should_be_in_both=1 "
         "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-        "androidboot.vbmeta.avb_version=1.2 "
+        "androidboot.vbmeta.avb_version=1.1 "
         "androidboot.vbmeta.device_state=locked "
         "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1536 "
         "androidboot.vbmeta.digest="
@@ -2329,7 +2062,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hashtree descriptor:\n"
@@ -2391,7 +2123,6 @@
                          "Algorithm:                SHA256_RSA2048\n"
                          "Rollback Index:           4\n"
                          "Flags:                    %d\n"
-                         "Rollback Index Location:  0\n"
                          "Release String:           ''\n"
                          "Descriptors:\n"
                          "    Chain Partition descriptor:\n"
@@ -2432,7 +2163,7 @@
         "restart_on_corruption ignore_zero_blocks\" root=/dev/dm-0 "
         "should_be_in_both=1 "
         "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-        "androidboot.vbmeta.avb_version=1.2 "
+        "androidboot.vbmeta.avb_version=1.1 "
         "androidboot.vbmeta.device_state=locked "
         "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=3456 "
         "androidboot.vbmeta.digest="
@@ -2446,7 +2177,7 @@
     EXPECT_EQ(
         "root=PARTUUID=1234-fake-guid-for:system_a should_be_in_both=1 "
         "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-        "androidboot.vbmeta.avb_version=1.2 "
+        "androidboot.vbmeta.avb_version=1.1 "
         "androidboot.vbmeta.device_state=locked "
         "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=3456 "
         "androidboot.vbmeta.digest="
@@ -2529,7 +2260,6 @@
           "Algorithm:                SHA256_RSA2048\n"
           "Rollback Index:           4\n"
           "Flags:                    %d\n"
-          "Rollback Index Location:  0\n"
           "Release String:           ''\n"
           "Descriptors:\n"
           "    Kernel Cmdline descriptor:\n"
@@ -2755,7 +2485,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    2147483648\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hash descriptor:\n"
@@ -2838,7 +2567,7 @@
       "4096 4096 4096 4096 sha1 c9ffc3bfae5000269a55a56621547fd1fcf819df "
       "d00df00d 2 restart_on_corruption ignore_zero_blocks\" root=/dev/dm-0 "
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:boot "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=5312 "
       "androidboot.vbmeta.digest="
@@ -3009,7 +2738,7 @@
       "c9ffc3bfae5000269a55a56621547fd1fcf819df d00df00d 2 "
       "restart_on_corruption ignore_zero_blocks\" root=/dev/dm-0 "
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 "
       "androidboot.vbmeta.size=1664 "
@@ -3039,7 +2768,7 @@
       "c9ffc3bfae5000269a55a56621547fd1fcf819df d00df00d 2 "
       "restart_on_corruption ignore_zero_blocks\" root=/dev/dm-0 "
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 "
       "androidboot.vbmeta.size=1664 "
@@ -3068,7 +2797,7 @@
       "c9ffc3bfae5000269a55a56621547fd1fcf819df d00df00d 2 "
       "ignore_zero_blocks ignore_zero_blocks\" root=/dev/dm-0 "
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 "
       "androidboot.vbmeta.size=1664 "
@@ -3109,7 +2838,7 @@
       "c9ffc3bfae5000269a55a56621547fd1fcf819df d00df00d 2 "
       "ignore_corruption ignore_zero_blocks\" root=/dev/dm-0 "
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 "
       "androidboot.vbmeta.size=1664 "
@@ -3148,7 +2877,7 @@
     EXPECT_EQ(
         "root=PARTUUID=1234-fake-guid-for:system "
         "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta "
-        "androidboot.vbmeta.avb_version=1.2 "
+        "androidboot.vbmeta.avb_version=1.1 "
         "androidboot.vbmeta.device_state=locked "
         "androidboot.vbmeta.hash_alg=sha256 "
         "androidboot.vbmeta.size=1664 "
@@ -3198,7 +2927,6 @@
                                  "Algorithm:                SHA256_RSA2048\n"
                                  "Rollback Index:           0\n"
                                  "Flags:                    0\n"
-                                 "Rollback Index Location:  0\n"
                                  "Release String:           ''\n"
                                  "Descriptors:\n"
                                  "    Hash descriptor:\n"
@@ -3260,7 +2988,6 @@
                                  "Algorithm:                SHA256_RSA2048\n"
                                  "Rollback Index:           0\n"
                                  "Flags:                    0\n"
-                                 "Rollback Index Location:  0\n"
                                  "Release String:           ''\n"
                                  "Descriptors:\n"
                                  "    Kernel Cmdline descriptor:\n"
@@ -3340,7 +3067,7 @@
   Verify(true /* expect_success */);
   EXPECT_EQ(
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 "
       "androidboot.vbmeta.size=1280 "
@@ -3545,7 +3272,7 @@
       // Note: Here appear the bytes used in write_persistent_value above.
       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 "
       "androidboot.vbmeta.size=1408 "
@@ -3572,7 +3299,7 @@
       // Note: Here appear the bytes used in write_persistent_value above.
       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 "
       "androidboot.vbmeta.size=1408 "
@@ -3603,7 +3330,7 @@
       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 "
       "androidboot.vbmeta.size=1408 "
@@ -3911,7 +3638,7 @@
   EXPECT_NE(nullptr, slot_data);
   EXPECT_EQ(
       "androidboot.vbmeta.device=PARTUUID=1234-fake-guid-for:vbmeta_a "
-      "androidboot.vbmeta.avb_version=1.2 "
+      "androidboot.vbmeta.avb_version=1.1 "
       "androidboot.vbmeta.device_state=locked "
       "androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=1152 "
       "androidboot.vbmeta.digest="
diff --git a/test/avb_unittest_util.cc b/test/avb_unittest_util.cc
index 5202e6a..4c23a8f 100644
--- a/test/avb_unittest_util.cc
+++ b/test/avb_unittest_util.cc
@@ -126,18 +126,17 @@
 base::FilePath BaseAvbToolTest::GenerateImage(const std::string file_name,
                                               size_t image_size,
                                               uint8_t start_byte) {
+  std::vector<uint8_t> image;
+  image.resize(image_size);
+  for (size_t n = 0; n < image_size; n++) {
+    image[n] = uint8_t(n + start_byte);
+  }
   base::FilePath image_path = testdir_.Append(file_name);
-  EXPECT_COMMAND(0,
-                 "./avbtool generate_test_image "
-                 "--image_size %d "
-                 "--start_byte %d "
-                 "--output %s",
-                 image_size,
-                 start_byte,
-                 image_path.value().c_str());
-  base::File::Info stats;
-  EXPECT_TRUE(base::GetFileInfo(image_path, &stats));
-  EXPECT_EQ((size_t)stats.size, image_size);
+  EXPECT_EQ(image_size,
+            static_cast<const size_t>(
+                base::WriteFile(image_path,
+                                reinterpret_cast<const char*>(image.data()),
+                                image.size())));
   return image_path;
 }
 
diff --git a/test/avb_util_unittest.cc b/test/avb_util_unittest.cc
index 5670f85..defdfab 100644
--- a/test/avb_util_unittest.cc
+++ b/test/avb_util_unittest.cc
@@ -27,6 +27,7 @@
 
 #include <gtest/gtest.h>
 
+#include <libavb/avb_sha.h>
 #include <libavb/libavb.h>
 
 #include "avb_unittest_util.h"
@@ -542,4 +543,82 @@
   EXPECT_EQ("/", std::string(avb_basename("/")));
 }
 
+TEST_F(UtilTest, Sha256) {
+  AvbSHA256Ctx ctx;
+
+  /* Compare with
+   *
+   * $ echo -n foobar |sha256sum
+   * c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2 -
+   */
+  avb_sha256_init(&ctx);
+  avb_sha256_update(&ctx, (const uint8_t*)"foobar", 6);
+  EXPECT_EQ("c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2",
+            mem_to_hexstring(avb_sha256_final(&ctx), AVB_SHA256_DIGEST_SIZE));
+}
+
+// Disabled for now because it takes ~30 seconds to run.
+TEST_F(UtilTest, DISABLED_Sha256Large) {
+  AvbSHA256Ctx ctx;
+
+  /* Also check we this works with greater than 4GiB input. Compare with
+   *
+   * $ dd if=/dev/zero bs=1048576 count=4097 |sha256sum
+   * 829816e339ff597ec3ada4c30fc840d3f2298444169d242952a54bcf3fcd7747 -
+   */
+  const size_t kMebibyte = 1048576;
+  uint8_t* megabuf;
+  megabuf = new uint8_t[kMebibyte];
+  memset((char*)megabuf, '\0', kMebibyte);
+  avb_sha256_init(&ctx);
+  for (size_t n = 0; n < 4097; n++) {
+    avb_sha256_update(&ctx, megabuf, kMebibyte);
+  }
+  EXPECT_EQ("829816e339ff597ec3ada4c30fc840d3f2298444169d242952a54bcf3fcd7747",
+            mem_to_hexstring(avb_sha256_final(&ctx), AVB_SHA256_DIGEST_SIZE));
+  delete[] megabuf;
+}
+
+TEST_F(UtilTest, Sha512) {
+  AvbSHA512Ctx ctx;
+
+  /* Compare with
+   *
+   * $ echo -n foobar |sha512sum
+   * 0a50261ebd1a390fed2bf326f2673c145582a6342d523204973d0219337f81616a8069b012587cf5635f6925f1b56c360230c19b273500ee013e030601bf2425
+   * -
+   */
+  avb_sha512_init(&ctx);
+  avb_sha512_update(&ctx, (const uint8_t*)"foobar", 6);
+  EXPECT_EQ(
+      "0a50261ebd1a390fed2bf326f2673c145582a6342d523204973d0219337f81616a8069b0"
+      "12587cf5635f6925f1b56c360230c19b273500ee013e030601bf2425",
+      mem_to_hexstring(avb_sha512_final(&ctx), AVB_SHA512_DIGEST_SIZE));
+}
+
+// Disabled for now because it takes ~30 seconds to run.
+TEST_F(UtilTest, DISABLED_Sha512Large) {
+  AvbSHA512Ctx ctx;
+
+  /* Also check we this works with greater than 4GiB input. Compare with
+   *
+   * $ dd if=/dev/zero bs=1048576 count=4097 |sha512sum
+   * eac1685671cc2060315888746de072398116c0c83b7ee9463f0576e11bfdea9cdd5ddbf291fb3ffc4ee8a1b459c798d9fb9b50b7845e2871c4b1402470aaf4c0
+   * -
+   */
+  const size_t kMebibyte = 1048576;
+  uint8_t* megabuf;
+  megabuf = new uint8_t[kMebibyte];
+  memset((char*)megabuf, '\0', kMebibyte);
+  avb_sha512_init(&ctx);
+  for (size_t n = 0; n < 4097; n++) {
+    avb_sha512_update(&ctx, megabuf, kMebibyte);
+  }
+  EXPECT_EQ(
+      "eac1685671cc2060315888746de072398116c0c83b7ee9463f0576e11bfdea9cdd5ddbf2"
+      "91fb3ffc4ee8a1b459c798d9fb9b50b7845e2871c4b1402470aaf4c0",
+      mem_to_hexstring(avb_sha512_final(&ctx), AVB_SHA512_DIGEST_SIZE));
+  delete[] megabuf;
+}
+
 }  // namespace avb
diff --git a/test/avbtool_signing_helper_test.py b/test/avbtool_signing_helper_test.py
index aa03fcc..c9bb660 100755
--- a/test/avbtool_signing_helper_test.py
+++ b/test/avbtool_signing_helper_test.py
@@ -1,7 +1,7 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 
 #
-# Copyright (C) 2016-2020 The Android Open Source Project
+# Copyright (C) 2016 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,38 +29,35 @@
 # to catch mistakes where the standard C library is inadvertently
 # used.
 
-import errno
-import os
 import subprocess
 import sys
-
+import errno
+import os
 
 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.buffer.read()
-  if not data:
-    sys.stderr.write('There is not input data\n')
+  data = sys.stdin.read()
+  if len(data) == 0:
+    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.buffer.write(b'X' * 256)
+    sys.stdout.write('X'*256)
     return 0
 
-  if not os.getenv('SIGNING_HELPER_TEST'):
-    sys.stderr.write('env SIGNING_HELPER_TEST is not set or empty\n')
+  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")
     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.EACCES
+    sys.stderr.write("no permission to write into {} file\n".format(test_file_name))
+    return errno.EACCESS
 
   p = subprocess.Popen(
       ['openssl', 'rsautl', '-sign', '-inkey', argv[2], '-raw'],
@@ -71,10 +68,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 2be3e97..9811225 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 python3
+#!/usr/bin/env python
 
 #
-# Copyright (C) 2017-2020 The Android Open Source Project
+# Copyright (C) 2017 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,40 +24,37 @@
 # SOFTWARE.
 #
 
-import errno
-import os
 import subprocess
 import sys
-
+import errno
+import os
 
 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='rb+')
+  signing_file = open(argv[3], mode='rw+')
   data = signing_file.read()
-  if not data:
-    sys.stderr.write('There is no input data\n')
+  if len(data) == 0:
+    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(b'X' * 256)
+    signing_file.write('X'*256)
     return 0
 
-  if not os.getenv('SIGNING_HELPER_TEST'):
-    sys.stderr.write('env SIGNING_HELPER_TEST is not set or empty\n')
+  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")
     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.EACCES
+    sys.stderr.write("no permission to write into {} file\n".format(test_file_name))
+    return errno.EACCESS
 
   p = subprocess.Popen(
       ['openssl', 'rsautl', '-sign', '-inkey', argv[2], '-raw'],
@@ -71,8 +68,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/avbtool_unittest.cc b/test/avbtool_unittest.cc
index 7fb89c4..0d8e1eb 100644
--- a/test/avbtool_unittest.cc
+++ b/test/avbtool_unittest.cc
@@ -53,10 +53,6 @@
   }
 
   void AddHashFooterTest(bool sparse_image);
-  void CreateRootfsWithHashtreeFooter(bool sparse_image,
-                                      const std::string& hash_algorithm,
-                                      const std::string& root_digest,
-                                      base::FilePath* rootfs_path);
   void AddHashtreeFooterTest(bool sparse_image);
   void AddHashtreeFooterFECTest(bool sparse_image);
 
@@ -311,42 +307,6 @@
   EXPECT_EQ(rollback_index, h.rollback_index);
 }
 
-TEST_F(AvbToolTest, CheckRollbackIndexLocationOmitted) {
-  uint32_t expected_rollback_index_location = 0;
-
-  GenerateVBMetaImage("vbmeta.img",
-                      "SHA256_RSA2048",
-                      0,
-                      base::FilePath("test/data/testkey_rsa2048.pem"),
-                      "--internal_release_string \"\"");
-
-  AvbVBMetaImageHeader h;
-  avb_vbmeta_image_header_to_host_byte_order(
-      reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data()), &h);
-
-  EXPECT_EQ(expected_rollback_index_location, h.rollback_index_location);
-  EXPECT_EQ(1u, h.required_libavb_version_major);
-  EXPECT_EQ(0u, h.required_libavb_version_minor);
-}
-
-TEST_F(AvbToolTest, CheckRollbackIndexLocation) {
-  uint32_t rollback_index_location = 42;
-  GenerateVBMetaImage("vbmeta.img",
-                      "SHA256_RSA2048",
-                      0,
-                      base::FilePath("test/data/testkey_rsa2048.pem"),
-                      base::StringPrintf("--rollback_index_location %d",
-                                         rollback_index_location));
-
-  AvbVBMetaImageHeader h;
-  avb_vbmeta_image_header_to_host_byte_order(
-      reinterpret_cast<AvbVBMetaImageHeader*>(vbmeta_image_.data()), &h);
-
-  EXPECT_EQ(rollback_index_location, h.rollback_index_location);
-  EXPECT_EQ(1u, h.required_libavb_version_major);
-  EXPECT_EQ(2u, h.required_libavb_version_minor);
-}
-
 TEST_F(AvbToolTest, CheckPubkeyReturned) {
   GenerateVBMetaImage("vbmeta.img",
                       "SHA256_RSA2048",
@@ -400,7 +360,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Prop: foo -> 'brillo'\n"
@@ -443,7 +402,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hash descriptor:\n"
@@ -525,7 +483,6 @@
         "Algorithm:                SHA256_RSA2048\n"
         "Rollback Index:           0\n"
         "Flags:                    0\n"
-        "Rollback Index Location:  0\n"
         "Release String:           ''\n"
         "Descriptors:\n"
         "    Hash descriptor:\n"
@@ -748,7 +705,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hash descriptor:\n"
@@ -835,7 +791,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hash descriptor:\n"
@@ -879,7 +834,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hash descriptor:\n"
@@ -925,7 +879,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hash descriptor:\n"
@@ -938,11 +891,7 @@
       InfoImage(path));
 }
 
-void AvbToolTest::CreateRootfsWithHashtreeFooter(
-    bool sparse_image,
-    const std::string& hash_algorithm,
-    const std::string& root_digest,
-    base::FilePath* output_rootfs_path) {
+void AvbToolTest::AddHashtreeFooterTest(bool sparse_image) {
   const size_t rootfs_size = 1028 * 1024;
   const size_t partition_size = 1536 * 1024;
 
@@ -977,7 +926,6 @@
   for (int n = 0; n < 2; n++) {
     EXPECT_COMMAND(0,
                    "./avbtool add_hashtree_footer --salt d00df00d --image %s "
-                   "--hash_algorithm %s "
                    "--partition_size %d --partition_name foobar "
                    "--algorithm SHA256_RSA2048 "
                    "--key test/data/testkey_rsa2048.pem "
@@ -985,7 +933,6 @@
                    "--internal_release_string \"\" "
                    "--do_not_generate_fec",
                    rootfs_path.value().c_str(),
-                   hash_algorithm.c_str(),
                    (int)partition_size,
                    external_vbmeta_path.value().c_str());
 
@@ -1004,7 +951,6 @@
                                  "Algorithm:                SHA256_RSA2048\n"
                                  "Rollback Index:           0\n"
                                  "Flags:                    0\n"
-                                 "Rollback Index Location:  0\n"
                                  "Release String:           ''\n"
                                  "Descriptors:\n"
                                  "    Hashtree descriptor:\n"
@@ -1017,48 +963,43 @@
                                  "      FEC num roots:         0\n"
                                  "      FEC offset:            0\n"
                                  "      FEC size:              0 bytes\n"
-                                 "      Hash Algorithm:        %s\n"
+                                 "      Hash Algorithm:        sha1\n"
                                  "      Partition Name:        foobar\n"
                                  "      Salt:                  d00df00d\n"
                                  "      Root Digest:           "
-                                 "%s\n"
+                                 "e811611467dcd6e8dc4324e45f706c2bdd51db67\n"
                                  "      Flags:                 0\n",
-                                 sparse_image ? " (Sparse)" : "",
-                                 hash_algorithm.c_str(),
-                                 root_digest.c_str()),
+                                 sparse_image ? " (Sparse)" : ""),
               InfoImage(rootfs_path));
 
-    ASSERT_EQ(base::StringPrintf("Minimum libavb version:   1.0\n"
-                                 "Header Block:             256 bytes\n"
-                                 "Authentication Block:     320 bytes\n"
-                                 "Auxiliary Block:          768 bytes\n"
-                                 "Public key (sha1):        "
-                                 "cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
-                                 "Algorithm:                SHA256_RSA2048\n"
-                                 "Rollback Index:           0\n"
-                                 "Flags:                    0\n"
-                                 "Rollback Index Location:  0\n"
-                                 "Release String:           ''\n"
-                                 "Descriptors:\n"
-                                 "    Hashtree descriptor:\n"
-                                 "      Version of dm-verity:  1\n"
-                                 "      Image Size:            1052672 bytes\n"
-                                 "      Tree Offset:           1052672\n"
-                                 "      Tree Size:             16384 bytes\n"
-                                 "      Data Block Size:       4096 bytes\n"
-                                 "      Hash Block Size:       4096 bytes\n"
-                                 "      FEC num roots:         0\n"
-                                 "      FEC offset:            0\n"
-                                 "      FEC size:              0 bytes\n"
-                                 "      Hash Algorithm:        %s\n"
-                                 "      Partition Name:        foobar\n"
-                                 "      Salt:                  d00df00d\n"
-                                 "      Root Digest:           "
-                                 "%s\n"
-                                 "      Flags:                 0\n",
-                                 hash_algorithm.c_str(),
-                                 root_digest.c_str()),
-              InfoImage(external_vbmeta_path));
+    ASSERT_EQ(
+        "Minimum libavb version:   1.0\n"
+        "Header Block:             256 bytes\n"
+        "Authentication Block:     320 bytes\n"
+        "Auxiliary Block:          768 bytes\n"
+        "Public key (sha1):        cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+        "Algorithm:                SHA256_RSA2048\n"
+        "Rollback Index:           0\n"
+        "Flags:                    0\n"
+        "Release String:           ''\n"
+        "Descriptors:\n"
+        "    Hashtree descriptor:\n"
+        "      Version of dm-verity:  1\n"
+        "      Image Size:            1052672 bytes\n"
+        "      Tree Offset:           1052672\n"
+        "      Tree Size:             16384 bytes\n"
+        "      Data Block Size:       4096 bytes\n"
+        "      Hash Block Size:       4096 bytes\n"
+        "      FEC num roots:         0\n"
+        "      FEC offset:            0\n"
+        "      FEC size:              0 bytes\n"
+        "      Hash Algorithm:        sha1\n"
+        "      Partition Name:        foobar\n"
+        "      Salt:                  d00df00d\n"
+        "      Root Digest:           "
+        "e811611467dcd6e8dc4324e45f706c2bdd51db67\n"
+        "      Flags:                 0\n",
+        InfoImage(external_vbmeta_path));
 
     // Check that the extracted vbmeta matches the externally generally one.
     EXPECT_COMMAND(0,
@@ -1072,16 +1013,6 @@
                    extracted_vbmeta_path.value().c_str());
   }
 
-  *output_rootfs_path = rootfs_path;
-}
-
-void AvbToolTest::AddHashtreeFooterTest(bool sparse_image) {
-  base::FilePath rootfs_path;
-  CreateRootfsWithHashtreeFooter(sparse_image,
-                                 "sha1",
-                                 "e811611467dcd6e8dc4324e45f706c2bdd51db67",
-                                 &rootfs_path);
-
   /* Zero the hashtree on a copy of the image. */
   EXPECT_COMMAND(0,
                  "cp %s %s.zht",
@@ -1247,7 +1178,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Kernel Cmdline descriptor:\n"
@@ -1271,9 +1201,6 @@
   ASSERT_TRUE(base::GetFileSize(rootfs_path, &erased_footer_file_size));
   EXPECT_EQ(static_cast<size_t>(erased_footer_file_size), 1069056UL);
 
-  const size_t rootfs_size = 1028 * 1024;
-  const size_t partition_size = 1536 * 1024;
-  base::FilePath external_vbmeta_path = testdir_.Append("external_vbmeta.bin");
   // Check that --do_not_append_vbmeta_image works as intended.
   //
   // For this we need to reset the size of the image to the original
@@ -1308,15 +1235,6 @@
   AddHashtreeFooterTest(true);
 }
 
-TEST_F(AvbToolTest, AddHashtreeFooterSparseWithBlake2b256) {
-  base::FilePath rootfs_path;
-  CreateRootfsWithHashtreeFooter(
-      true,
-      "blake2b-256",
-      "9ed423dda921619181bf1889746fe2dd28ae1e673be8d802b4713122e3209513",
-      &rootfs_path);
-}
-
 void AvbToolTest::AddHashtreeFooterFECTest(bool sparse_image) {
   const size_t rootfs_size = 1028 * 1024;
   const size_t partition_size = 1536 * 1024;
@@ -1371,7 +1289,6 @@
                                  "Algorithm:                SHA256_RSA2048\n"
                                  "Rollback Index:           0\n"
                                  "Flags:                    0\n"
-                                 "Rollback Index Location:  0\n"
                                  "Release String:           ''\n"
                                  "Descriptors:\n"
                                  "    Hashtree descriptor:\n"
@@ -1544,7 +1461,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Kernel Cmdline descriptor:\n"
@@ -1700,7 +1616,6 @@
       "Algorithm:                SHA512_RSA4096\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hashtree descriptor:\n"
@@ -1752,7 +1667,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hashtree descriptor:\n"
@@ -1804,7 +1718,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hashtree descriptor:\n"
@@ -1858,7 +1771,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hashtree descriptor:\n"
@@ -1908,7 +1820,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hashtree descriptor:\n"
@@ -1973,7 +1884,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Kernel Cmdline descriptor:\n"
@@ -2099,7 +2009,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Hashtree descriptor:\n"
@@ -2153,7 +2062,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Chain Partition descriptor:\n"
@@ -2278,7 +2186,6 @@
       "Algorithm:                NONE\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Prop: name4 -> 'value4'\n"
@@ -2321,7 +2228,6 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
       "Release String:           ''\n"
       "Descriptors:\n"
       "    Chain Partition descriptor:\n"
@@ -2436,8 +2342,7 @@
       "Algorithm:                SHA256_RSA2048\n"
       "Rollback Index:           0\n"
       "Flags:                    0\n"
-      "Rollback Index Location:  0\n"
-      "Release String:           'avbtool 1.2.0 '\n"
+      "Release String:           'avbtool 1.1.0 '\n"
       "Descriptors:\n"
       "    Kernel Cmdline descriptor:\n"
       "      Flags:                 0\n"
@@ -3000,90 +2905,6 @@
                  vbmeta_google_path.value().c_str());
 }
 
-TEST_F(AvbToolTest, PrintPartitionDigests) {
-  base::FilePath pk4096_path = testdir_.Append("testkey_rsa4096.avbpubkey");
-  EXPECT_COMMAND(
-      0,
-      "./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
-      " --output %s",
-      pk4096_path.value().c_str());
-
-  const size_t boot_partition_size = 16 * 1024 * 1024;
-  const size_t boot_image_size = 5 * 1024 * 1024;
-  base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
-  EXPECT_COMMAND(0,
-                 "./avbtool add_hash_footer"
-                 " --image %s"
-                 " --rollback_index 0"
-                 " --partition_name boot"
-                 " --partition_size %zd"
-                 " --salt deadbeef"
-                 " --internal_release_string \"\"",
-                 boot_path.value().c_str(),
-                 boot_partition_size);
-
-  GenerateVBMetaImage("vbmeta.img",
-                      "SHA256_RSA2048",
-                      0,
-                      base::FilePath("test/data/testkey_rsa2048.pem"),
-                      base::StringPrintf("--chain_partition system:1:%s "
-                                         "--include_descriptors_from_image %s",
-                                         pk4096_path.value().c_str(),
-                                         boot_path.value().c_str()));
-
-  const size_t system_partition_size = 10 * 1024 * 1024;
-  const size_t system_image_size = 8 * 1024 * 1024;
-  base::FilePath system_path = GenerateImage("system.img", system_image_size);
-  EXPECT_COMMAND(0,
-                 "./avbtool add_hashtree_footer --salt d00df00d --image %s "
-                 "--partition_size %zd --partition_name system "
-                 "--algorithm SHA256_RSA4096 "
-                 "--key test/data/testkey_rsa4096.pem "
-                 "--internal_release_string \"\" ",
-                 system_path.value().c_str(),
-                 system_partition_size);
-
-  base::FilePath out_path = testdir_.Append("out.txt");
-  std::string out;
-
-  // Normal output
-  EXPECT_COMMAND(0,
-                 "./avbtool print_partition_digests --image %s --output %s",
-                 vbmeta_image_path_.value().c_str(),
-                 out_path.value().c_str());
-  ASSERT_TRUE(base::ReadFileToString(out_path, &out));
-  EXPECT_EQ(
-      "system: d52d93c988d336a79abe1c05240ae9a79a9b7d61\n"
-      "boot: "
-      "184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\n",
-      out);
-
-  // JSON output
-  EXPECT_COMMAND(
-      0,
-      "./avbtool print_partition_digests --image %s --json --output %s",
-      vbmeta_image_path_.value().c_str(),
-      out_path.value().c_str());
-  ASSERT_TRUE(base::ReadFileToString(out_path, &out));
-  // The trailing whitespace comes from python. If they fix that bug we need
-  // to update this test...
-  EXPECT_EQ(
-      "{\n"
-      "  \"partitions\": [\n"
-      "    {\n"
-      "      \"name\": \"system\",\n"
-      "      \"digest\": \"d52d93c988d336a79abe1c05240ae9a79a9b7d61\"\n"
-      "    },\n"
-      "    {\n"
-      "      \"name\": \"boot\",\n"
-      "      \"digest\": "
-      "\"184cb36243adb8b87d2d8c4802de32125fe294ec46753d732144ee65df68a23d\"\n"
-      "    }\n"
-      "  ]\n"
-      "}",
-      out);
-}
-
 class AvbToolTest_PrintRequiredVersion : public AvbToolTest {
  protected:
   const char* kOutputFile = "versions.txt";
@@ -3093,10 +2914,7 @@
     if (target_required_minor_version == 1) {
       // The --do_not_use_ab option will require 1.1.
       extra_args = "--do_not_use_ab";
-    } else if (target_required_minor_version == 2) {
-      extra_args = "--rollback_index_location 2";
     }
-
     const size_t boot_partition_size = 16 * 1024 * 1024;
     base::FilePath output_path = testdir_.Append(kOutputFile);
     EXPECT_COMMAND(0,
@@ -3119,8 +2937,6 @@
     if (target_required_minor_version == 1) {
       // The --do_not_use_ab option will require 1.1.
       extra_args = "--do_not_use_ab";
-    } else if (target_required_minor_version == 2) {
-      extra_args = "--rollback_index_location 2";
     }
     const size_t system_partition_size = 10 * 1024 * 1024;
     base::FilePath output_path = testdir_.Append(kOutputFile);
@@ -3154,10 +2970,7 @@
                      (int)boot_partition_size);
       extra_args = base::StringPrintf("--include_descriptors_from_image %s",
                                       image_path.value().c_str());
-    } else if (target_required_minor_version == 2) {
-      extra_args = "--rollback_index_location 2";
     }
-
     base::FilePath output_path = testdir_.Append(kOutputFile);
     EXPECT_COMMAND(0,
                    "./avbtool make_vbmeta_image "
@@ -3188,10 +3001,6 @@
   PrintWithAddHashFooter(1);
 }
 
-TEST_F(AvbToolTest_PrintRequiredVersion, HashFooter_1_2) {
-  PrintWithAddHashFooter(2);
-}
-
 TEST_F(AvbToolTest_PrintRequiredVersion, HashtreeFooter_1_0) {
   PrintWithAddHashtreeFooter(0);
 }
@@ -3200,10 +3009,6 @@
   PrintWithAddHashtreeFooter(1);
 }
 
-TEST_F(AvbToolTest_PrintRequiredVersion, HashtreeFooter_1_2) {
-  PrintWithAddHashtreeFooter(2);
-}
-
 TEST_F(AvbToolTest_PrintRequiredVersion, Vbmeta_1_0) {
   PrintWithMakeVbmetaImage(0);
 }
@@ -3212,10 +3017,6 @@
   PrintWithMakeVbmetaImage(1);
 }
 
-TEST_F(AvbToolTest_PrintRequiredVersion, Vbmeta_1_2) {
-  PrintWithMakeVbmetaImage(2);
-}
-
 TEST_F(AvbToolTest, MakeAtxPikCertificate) {
   base::FilePath subject_path = testdir_.Append("tmp_subject");
   ASSERT_TRUE(base::WriteFile(subject_path, "fake PIK subject", 16));
diff --git a/test/corpus/icp.bin b/test/corpus/icp.bin
deleted file mode 100644
index 19125fe..0000000
--- a/test/corpus/icp.bin
+++ /dev/null
Binary files differ
diff --git a/test/data/aftl_descriptor.bin b/test/data/aftl_descriptor.bin
new file mode 100644
index 0000000..1e6e65a
--- /dev/null
+++ b/test/data/aftl_descriptor.bin
Binary files differ
diff --git a/test/data/aftl_descriptor_multi.bin b/test/data/aftl_descriptor_multi.bin
new file mode 100644
index 0000000..9a16421
--- /dev/null
+++ b/test/data/aftl_descriptor_multi.bin
Binary files differ
diff --git a/test/data/testkey_rsa4096_pub.bin b/test/data/aftl_key_bytes.bin
similarity index 100%
rename from test/data/testkey_rsa4096_pub.bin
rename to test/data/aftl_key_bytes.bin
Binary files differ
diff --git a/test/data/aftl_log_key_bytes.bin b/test/data/aftl_log_key_bytes.bin
new file mode 100644
index 0000000..a4748d4
--- /dev/null
+++ b/test/data/aftl_log_key_bytes.bin
Binary files differ
diff --git a/test/data/aftl_log_sig.bin b/test/data/aftl_log_sig.bin
new file mode 100644
index 0000000..892040e
--- /dev/null
+++ b/test/data/aftl_log_sig.bin
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
deleted file mode 100644
index 2a6368e..0000000
--- a/test/data/aftl_output_vbmeta_with_2_icp_same_log.img
+++ /dev/null
Binary files differ
diff --git a/test/data/aftl_pubkey_1.bin b/test/data/aftl_pubkey_1.bin
deleted file mode 100644
index 5bad4be..0000000
--- a/test/data/aftl_pubkey_1.bin
+++ /dev/null
Binary files differ
diff --git a/test/data/aftl_verify_full.img b/test/data/aftl_verify_full.img
new file mode 100644
index 0000000..6c2212e
--- /dev/null
+++ b/test/data/aftl_verify_full.img
Binary files differ
diff --git a/test/data/aftl_verify_vbmeta.bin b/test/data/aftl_verify_vbmeta.bin
new file mode 100644
index 0000000..bde01e3
--- /dev/null
+++ b/test/data/aftl_verify_vbmeta.bin
Binary files differ
diff --git a/test/data/aftl_input_vbmeta.img b/test/data/aftltool/aftl_input_vbmeta.img
similarity index 100%
rename from test/data/aftl_input_vbmeta.img
rename to test/data/aftltool/aftl_input_vbmeta.img
Binary files differ
diff --git a/test/data/aftl_output_vbmeta_with_1_icp.img b/test/data/aftltool/aftl_output_vbmeta_with_1_icp.img
similarity index 67%
rename from test/data/aftl_output_vbmeta_with_1_icp.img
rename to test/data/aftltool/aftl_output_vbmeta_with_1_icp.img
index 74b67bb..25e0869 100644
--- a/test/data/aftl_output_vbmeta_with_1_icp.img
+++ b/test/data/aftltool/aftl_output_vbmeta_with_1_icp.img
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
new file mode 100644
index 0000000..ad36580
--- /dev/null
+++ b/test/data/aftltool/aftl_output_vbmeta_with_2_icp_different_logs.img
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
new file mode 100644
index 0000000..e53178c
--- /dev/null
+++ b/test/data/aftltool/aftl_output_vbmeta_with_2_icp_same_log.img
Binary files differ
diff --git a/test/data/aftl_pubkey_1.pem b/test/data/aftltool/aftl_pubkey_1.pub
similarity index 100%
rename from test/data/aftl_pubkey_1.pem
rename to test/data/aftltool/aftl_pubkey_1.pub
diff --git a/test/data/aftltool/aftl_pubkey_2.pub b/test/data/aftltool/aftl_pubkey_2.pub
new file mode 100644
index 0000000..470fefa
--- /dev/null
+++ b/test/data/aftltool/aftl_pubkey_2.pub
@@ -0,0 +1,14 @@
+-----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/testkey_rsa4096_pub.pem b/test/data/testkey_rsa4096_pub.pem
deleted file mode 100644
index efd7144..0000000
--- a/test/data/testkey_rsa4096_pub.pem
+++ /dev/null
@@ -1,14 +0,0 @@
------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 0f4db5a..6e8ab8c 100644
--- a/test/fake_avb_ops.cc
+++ b/test/fake_avb_ops.cc
@@ -87,18 +87,6 @@
   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,
@@ -172,15 +160,6 @@
   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()) {
@@ -194,8 +173,11 @@
   if (result != AVB_IO_RESULT_OK) {
     return result;
   }
+  if (size != num_bytes) {
+    return AVB_IO_RESULT_ERROR_IO;
+  }
 
-  *out_num_bytes_preloaded = std::min(static_cast<size_t>(size), num_bytes);
+  *out_num_bytes_preloaded = 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 5dea5bd..2cc12d8 100644
--- a/test/fake_avb_ops.h
+++ b/test/fake_avb_ops.h
@@ -211,10 +211,6 @@
   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();
@@ -319,8 +315,6 @@
 
   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_;
diff --git a/test/libavb_host_unittest.xml b/test/libavb_host_unittest.xml
index e34305f..826a758 100644
--- a/test/libavb_host_unittest.xml
+++ b/test/libavb_host_unittest.xml
@@ -17,6 +17,6 @@
     <option name="null-device" value="true" />
     <test class="com.android.tradefed.testtype.HostGTest" >
         <option name="module-name" value="libavb_host_unittest" />
-        <option name="native-test-timeout" value="7m"/>
+        <option name="native-test-timeout" value="3m"/>
     </test>
 </configuration>
diff --git a/tools/Android.bp b/tools/Android.bp
index cd705dc..a511fbb 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -14,16 +14,6 @@
 // limitations under the License.
 //
 
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "external_avb_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   SPDX-license-identifier-MIT
-    default_applicable_licenses: ["external_avb_license"],
-}
-
 python_library_host {
     name: "at_auth_unlock",
     srcs: [
diff --git a/tools/transparency/pixel_factory_image_verify.py b/tools/transparency/pixel_factory_image_verify.py
index 8e1069d..4479789 100755
--- a/tools/transparency/pixel_factory_image_verify.py
+++ b/tools/transparency/pixel_factory_image_verify.py
@@ -276,7 +276,7 @@
     """Extracts the build fingerprint from the system.img.
     Args:
       image_dir: The folder containing the unpacked factory image partitions,
-        which contains a vbmeta.img patition.
+	    which contains a vbmeta.img patition.
 
     Returns:
       The build fingerprint string, e.g.
@@ -285,7 +285,7 @@
     os.chdir(image_dir)
     args = ['grep',
             '-a',
-            'ro\..*build\.fingerprint=google/.*/release-keys',
+            'ro\.build\.fingerprint=google/.*/release-keys',
             'system.img']
 
     result, output = self._run_command(