[U-Boot] [PATCH v2 1/8] avb2.0: add Android Verified Boot 2.0 library

Igor Opaniuk igor.opaniuk at linaro.org
Sun Jun 3 18:56:36 UTC 2018


Add libavb lib (3rd party library from AOSP), that implements support of
AVB 2.0. This library is used for integrity checking of Android partitions
on eMMC.

libavb was added as it is and minimal changes were introduced to reduce
maintenance cost, because it will be deviated from AOSP upstream in the future.

Changes:
- license headers changed to conform SPDX-style
- avb_crc32.c dropped
- updates in avb_sysdeps_posix.c/avb_sysdeps.h

For additional details check [1] AVB 2.0 README.

[1] https://android.googlesource.com/platform/external/avb/+/master/README.md

Signed-off-by: Igor Opaniuk <igor.opaniuk at linaro.org>
---
 lib/libavb/avb_chain_partition_descriptor.c |   46 +
 lib/libavb/avb_chain_partition_descriptor.h |   54 ++
 lib/libavb/avb_cmdline.c                    |  422 +++++++++
 lib/libavb/avb_cmdline.h                    |   72 ++
 lib/libavb/avb_crypto.c                     |  354 +++++++
 lib/libavb/avb_crypto.h                     |  156 +++
 lib/libavb/avb_descriptor.c                 |  142 +++
 lib/libavb/avb_descriptor.h                 |  113 +++
 lib/libavb/avb_footer.c                     |   36 +
 lib/libavb/avb_footer.h                     |   68 ++
 lib/libavb/avb_hash_descriptor.c            |   44 +
 lib/libavb/avb_hash_descriptor.h            |   70 ++
 lib/libavb/avb_hashtree_descriptor.c        |   52 +
 lib/libavb/avb_hashtree_descriptor.h        |   80 ++
 lib/libavb/avb_kernel_cmdline_descriptor.c  |   40 +
 lib/libavb/avb_kernel_cmdline_descriptor.h  |   63 ++
 lib/libavb/avb_ops.h                        |  293 ++++++
 lib/libavb/avb_property_descriptor.c        |  167 ++++
 lib/libavb/avb_property_descriptor.h        |   89 ++
 lib/libavb/avb_rsa.c                        |  276 ++++++
 lib/libavb/avb_rsa.h                        |   55 ++
 lib/libavb/avb_sha.h                        |   72 ++
 lib/libavb/avb_sha256.c                     |  364 +++++++
 lib/libavb/avb_sha512.c                     |  362 +++++++
 lib/libavb/avb_slot_verify.c                | 1367 +++++++++++++++++++++++++++
 lib/libavb/avb_slot_verify.h                |  341 +++++++
 lib/libavb/avb_sysdeps.h                    |  101 ++
 lib/libavb/avb_sysdeps_posix.c              |   63 ++
 lib/libavb/avb_util.c                       |  412 ++++++++
 lib/libavb/avb_util.h                       |  269 ++++++
 lib/libavb/avb_vbmeta_image.c               |  290 ++++++
 lib/libavb/avb_vbmeta_image.h               |  276 ++++++
 lib/libavb/avb_version.c                    |   16 +
 lib/libavb/avb_version.h                    |   41 +
 lib/libavb/libavb.h                         |   32 +
 35 files changed, 6698 insertions(+)
 create mode 100644 lib/libavb/avb_chain_partition_descriptor.c
 create mode 100644 lib/libavb/avb_chain_partition_descriptor.h
 create mode 100644 lib/libavb/avb_cmdline.c
 create mode 100644 lib/libavb/avb_cmdline.h
 create mode 100644 lib/libavb/avb_crypto.c
 create mode 100644 lib/libavb/avb_crypto.h
 create mode 100644 lib/libavb/avb_descriptor.c
 create mode 100644 lib/libavb/avb_descriptor.h
 create mode 100644 lib/libavb/avb_footer.c
 create mode 100644 lib/libavb/avb_footer.h
 create mode 100644 lib/libavb/avb_hash_descriptor.c
 create mode 100644 lib/libavb/avb_hash_descriptor.h
 create mode 100644 lib/libavb/avb_hashtree_descriptor.c
 create mode 100644 lib/libavb/avb_hashtree_descriptor.h
 create mode 100644 lib/libavb/avb_kernel_cmdline_descriptor.c
 create mode 100644 lib/libavb/avb_kernel_cmdline_descriptor.h
 create mode 100644 lib/libavb/avb_ops.h
 create mode 100644 lib/libavb/avb_property_descriptor.c
 create mode 100644 lib/libavb/avb_property_descriptor.h
 create mode 100644 lib/libavb/avb_rsa.c
 create mode 100644 lib/libavb/avb_rsa.h
 create mode 100644 lib/libavb/avb_sha.h
 create mode 100644 lib/libavb/avb_sha256.c
 create mode 100644 lib/libavb/avb_sha512.c
 create mode 100644 lib/libavb/avb_slot_verify.c
 create mode 100644 lib/libavb/avb_slot_verify.h
 create mode 100644 lib/libavb/avb_sysdeps.h
 create mode 100644 lib/libavb/avb_sysdeps_posix.c
 create mode 100644 lib/libavb/avb_util.c
 create mode 100644 lib/libavb/avb_util.h
 create mode 100644 lib/libavb/avb_vbmeta_image.c
 create mode 100644 lib/libavb/avb_vbmeta_image.h
 create mode 100644 lib/libavb/avb_version.c
 create mode 100644 lib/libavb/avb_version.h
 create mode 100644 lib/libavb/libavb.h

diff --git a/lib/libavb/avb_chain_partition_descriptor.c b/lib/libavb/avb_chain_partition_descriptor.c
new file mode 100644
index 0000000..e6e8f71
--- /dev/null
+++ b/lib/libavb/avb_chain_partition_descriptor.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#include "avb_chain_partition_descriptor.h"
+#include "avb_util.h"
+
+bool avb_chain_partition_descriptor_validate_and_byteswap(
+    const AvbChainPartitionDescriptor* src, AvbChainPartitionDescriptor* dest) {
+  uint64_t expected_size;
+
+  avb_memcpy(dest, src, sizeof(AvbChainPartitionDescriptor));
+
+  if (!avb_descriptor_validate_and_byteswap((const AvbDescriptor*)src,
+                                            (AvbDescriptor*)dest))
+    return false;
+
+  if (dest->parent_descriptor.tag != AVB_DESCRIPTOR_TAG_CHAIN_PARTITION) {
+    avb_error("Invalid tag for chain partition descriptor.\n");
+    return false;
+  }
+
+  dest->rollback_index_location = avb_be32toh(dest->rollback_index_location);
+  dest->partition_name_len = avb_be32toh(dest->partition_name_len);
+  dest->public_key_len = avb_be32toh(dest->public_key_len);
+
+  if (dest->rollback_index_location < 1) {
+    avb_error("Invalid rollback index location value.\n");
+    return false;
+  }
+
+  /* Check that partition_name and public_key are fully contained. */
+  expected_size = sizeof(AvbChainPartitionDescriptor) - sizeof(AvbDescriptor);
+  if (!avb_safe_add_to(&expected_size, dest->partition_name_len) ||
+      !avb_safe_add_to(&expected_size, dest->public_key_len)) {
+    avb_error("Overflow while adding up sizes.\n");
+    return false;
+  }
+  if (expected_size > dest->parent_descriptor.num_bytes_following) {
+    avb_error("Descriptor payload size overflow.\n");
+    return false;
+  }
+  return true;
+}
diff --git a/lib/libavb/avb_chain_partition_descriptor.h b/lib/libavb/avb_chain_partition_descriptor.h
new file mode 100644
index 0000000..a841828
--- /dev/null
+++ b/lib/libavb/avb_chain_partition_descriptor.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_CHAIN_PARTITION_DESCRIPTOR_H_
+#define AVB_CHAIN_PARTITION_DESCRIPTOR_H_
+
+#include "avb_descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* A descriptor containing a pointer to signed integrity data stored
+ * on another partition. The descriptor contains the partition name in
+ * question (without the A/B suffix), the public key used to sign the
+ * integrity data, and rollback index location to use for rollback
+ * protection.
+ *
+ * Following this struct are |partition_name_len| bytes of the
+ * partition name (UTF-8 encoded) and |public_key_len| bytes of the
+ * public key.
+ *
+ * The |reserved| field is for future expansion and must be set to NUL
+ * bytes.
+ */
+typedef struct AvbChainPartitionDescriptor {
+  AvbDescriptor parent_descriptor;
+  uint32_t rollback_index_location;
+  uint32_t partition_name_len;
+  uint32_t public_key_len;
+  uint8_t reserved[64];
+} AVB_ATTR_PACKED AvbChainPartitionDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_chain_partition_descriptor_validate_and_byteswap(
+    const AvbChainPartitionDescriptor* src,
+    AvbChainPartitionDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_CHAIN_PARTITION_DESCRIPTOR_H_ */
diff --git a/lib/libavb/avb_cmdline.c b/lib/libavb/avb_cmdline.c
new file mode 100644
index 0000000..1481b5c
--- /dev/null
+++ b/lib/libavb/avb_cmdline.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#include "avb_cmdline.h"
+#include "avb_sha.h"
+#include "avb_util.h"
+#include "avb_version.h"
+
+#define NUM_GUIDS 3
+
+/* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with
+ * values. Returns NULL on OOM, otherwise the cmdline with values
+ * replaced.
+ */
+char* avb_sub_cmdline(AvbOps* ops,
+                      const char* cmdline,
+                      const char* ab_suffix,
+                      bool using_boot_for_vbmeta,
+                      const AvbCmdlineSubstList* additional_substitutions) {
+  const char* part_name_str[NUM_GUIDS] = {"system", "boot", "vbmeta"};
+  const char* replace_str[NUM_GUIDS] = {"$(ANDROID_SYSTEM_PARTUUID)",
+                                        "$(ANDROID_BOOT_PARTUUID)",
+                                        "$(ANDROID_VBMETA_PARTUUID)"};
+  char* ret = NULL;
+  AvbIOResult io_ret;
+  size_t n;
+
+  /* Special-case for when the top-level vbmeta struct is in the boot
+   * partition.
+   */
+  if (using_boot_for_vbmeta) {
+    part_name_str[2] = "boot";
+  }
+
+  /* Replace unique partition GUIDs */
+  for (n = 0; n < NUM_GUIDS; n++) {
+    char part_name[AVB_PART_NAME_MAX_SIZE];
+    char guid_buf[37];
+
+    if (!avb_str_concat(part_name,
+                        sizeof part_name,
+                        part_name_str[n],
+                        avb_strlen(part_name_str[n]),
+                        ab_suffix,
+                        avb_strlen(ab_suffix))) {
+      avb_error("Partition name and suffix does not fit.\n");
+      goto fail;
+    }
+
+    io_ret = ops->get_unique_guid_for_partition(
+        ops, part_name, guid_buf, sizeof guid_buf);
+    if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+      goto fail;
+    } else if (io_ret != AVB_IO_RESULT_OK) {
+      avb_error("Error getting unique GUID for partition.\n");
+      goto fail;
+    }
+
+    if (ret == NULL) {
+      ret = avb_replace(cmdline, replace_str[n], guid_buf);
+    } else {
+      char* new_ret = avb_replace(ret, replace_str[n], guid_buf);
+      avb_free(ret);
+      ret = new_ret;
+    }
+    if (ret == NULL) {
+      goto fail;
+    }
+  }
+
+  avb_assert(ret != NULL);
+
+  /* Replace any additional substitutions. */
+  if (additional_substitutions != NULL) {
+    for (n = 0; n < additional_substitutions->size; ++n) {
+      char* new_ret = avb_replace(ret,
+                                  additional_substitutions->tokens[n],
+                                  additional_substitutions->values[n]);
+      avb_free(ret);
+      ret = new_ret;
+      if (ret == NULL) {
+        goto fail;
+      }
+    }
+  }
+
+  return ret;
+
+fail:
+  if (ret != NULL) {
+    avb_free(ret);
+  }
+  return NULL;
+}
+
+static int cmdline_append_option(AvbSlotVerifyData* slot_data,
+                                 const char* key,
+                                 const char* value) {
+  size_t offset, key_len, value_len;
+  char* new_cmdline;
+
+  key_len = avb_strlen(key);
+  value_len = avb_strlen(value);
+
+  offset = 0;
+  if (slot_data->cmdline != NULL) {
+    offset = avb_strlen(slot_data->cmdline);
+    if (offset > 0) {
+      offset += 1;
+    }
+  }
+
+  new_cmdline = avb_calloc(offset + key_len + value_len + 2);
+  if (new_cmdline == NULL) {
+    return 0;
+  }
+  if (offset > 0) {
+    avb_memcpy(new_cmdline, slot_data->cmdline, offset - 1);
+    new_cmdline[offset - 1] = ' ';
+  }
+  avb_memcpy(new_cmdline + offset, key, key_len);
+  new_cmdline[offset + key_len] = '=';
+  avb_memcpy(new_cmdline + offset + key_len + 1, value, value_len);
+  if (slot_data->cmdline != NULL) {
+    avb_free(slot_data->cmdline);
+  }
+  slot_data->cmdline = new_cmdline;
+
+  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,
+                                  uint64_t minor_version) {
+  char major_digits[AVB_MAX_DIGITS_UINT64];
+  char minor_digits[AVB_MAX_DIGITS_UINT64];
+  char combined[AVB_MAX_DIGITS_UINT64 * 2 + 1];
+  size_t num_major_digits, num_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);
+  combined[num_major_digits + 1 + num_minor_digits] = '\0';
+
+  return cmdline_append_option(slot_data, key, combined);
+}
+
+static int cmdline_append_uint64_base10(AvbSlotVerifyData* slot_data,
+                                        const char* key,
+                                        uint64_t value) {
+  char digits[AVB_MAX_DIGITS_UINT64];
+  uint64_to_base10(value, digits);
+  return cmdline_append_option(slot_data, key, digits);
+}
+
+static int cmdline_append_hex(AvbSlotVerifyData* slot_data,
+                              const char* key,
+                              const uint8_t* data,
+                              size_t data_len) {
+  int ret;
+  char* hex_data = avb_bin2hex(data, data_len);
+  if (hex_data == NULL) {
+    return 0;
+  }
+  ret = cmdline_append_option(slot_data, key, hex_data);
+  avb_free(hex_data);
+  return ret;
+}
+
+AvbSlotVerifyResult avb_append_options(
+    AvbOps* ops,
+    AvbSlotVerifyData* slot_data,
+    AvbVBMetaImageHeader* toplevel_vbmeta,
+    AvbAlgorithmType algorithm_type,
+    AvbHashtreeErrorMode hashtree_error_mode) {
+  AvbSlotVerifyResult ret;
+  const char* verity_mode;
+  bool is_device_unlocked;
+  AvbIOResult io_ret;
+
+  /* Add androidboot.vbmeta.device option. */
+  if (!cmdline_append_option(slot_data,
+                             "androidboot.vbmeta.device",
+                             "PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto out;
+  }
+
+  /* Add androidboot.vbmeta.avb_version option. */
+  if (!cmdline_append_version(slot_data,
+                              "androidboot.vbmeta.avb_version",
+                              AVB_VERSION_MAJOR,
+                              AVB_VERSION_MINOR)) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto out;
+  }
+
+  /* Set androidboot.avb.device_state to "locked" or "unlocked". */
+  io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked);
+  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto out;
+  } else if (io_ret != AVB_IO_RESULT_OK) {
+    avb_error("Error getting device state.\n");
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+    goto out;
+  }
+  if (!cmdline_append_option(slot_data,
+                             "androidboot.vbmeta.device_state",
+                             is_device_unlocked ? "unlocked" : "locked")) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto out;
+  }
+
+  /* Set androidboot.vbmeta.{hash_alg, size, digest} - use same hash
+   * function as is used to sign vbmeta.
+   */
+  switch (algorithm_type) {
+    /* Explicit fallthrough. */
+    case AVB_ALGORITHM_TYPE_NONE:
+    case AVB_ALGORITHM_TYPE_SHA256_RSA2048:
+    case AVB_ALGORITHM_TYPE_SHA256_RSA4096:
+    case AVB_ALGORITHM_TYPE_SHA256_RSA8192: {
+      size_t n, total_size = 0;
+      uint8_t vbmeta_digest[AVB_SHA256_DIGEST_SIZE];
+      avb_slot_verify_data_calculate_vbmeta_digest(
+          slot_data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest);
+      for (n = 0; n < slot_data->num_vbmeta_images; n++) {
+        total_size += slot_data->vbmeta_images[n].vbmeta_size;
+      }
+      if (!cmdline_append_option(
+              slot_data, "androidboot.vbmeta.hash_alg", "sha256") ||
+          !cmdline_append_uint64_base10(
+              slot_data, "androidboot.vbmeta.size", total_size) ||
+          !cmdline_append_hex(slot_data,
+                              "androidboot.vbmeta.digest",
+                              vbmeta_digest,
+                              AVB_SHA256_DIGEST_SIZE)) {
+        ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+        goto out;
+      }
+    } break;
+    /* Explicit fallthrough. */
+    case AVB_ALGORITHM_TYPE_SHA512_RSA2048:
+    case AVB_ALGORITHM_TYPE_SHA512_RSA4096:
+    case AVB_ALGORITHM_TYPE_SHA512_RSA8192: {
+      size_t n, total_size = 0;
+      uint8_t vbmeta_digest[AVB_SHA512_DIGEST_SIZE];
+      avb_slot_verify_data_calculate_vbmeta_digest(
+          slot_data, AVB_DIGEST_TYPE_SHA512, vbmeta_digest);
+      for (n = 0; n < slot_data->num_vbmeta_images; n++) {
+        total_size += slot_data->vbmeta_images[n].vbmeta_size;
+      }
+      if (!cmdline_append_option(
+              slot_data, "androidboot.vbmeta.hash_alg", "sha512") ||
+          !cmdline_append_uint64_base10(
+              slot_data, "androidboot.vbmeta.size", total_size) ||
+          !cmdline_append_hex(slot_data,
+                              "androidboot.vbmeta.digest",
+                              vbmeta_digest,
+                              AVB_SHA512_DIGEST_SIZE)) {
+        ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+        goto out;
+      }
+    } break;
+    case _AVB_ALGORITHM_NUM_TYPES:
+      avb_assert_not_reached();
+      break;
+  }
+
+  /* Set androidboot.veritymode and androidboot.vbmeta.invalidate_on_error */
+  if (toplevel_vbmeta->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) {
+    verity_mode = "disabled";
+  } else {
+    const char* dm_verity_mode;
+    char* new_ret;
+
+    switch (hashtree_error_mode) {
+      case AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE:
+        if (!cmdline_append_option(
+                slot_data, "androidboot.vbmeta.invalidate_on_error", "yes")) {
+          ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+          goto out;
+        }
+        verity_mode = "enforcing";
+        dm_verity_mode = "restart_on_corruption";
+        break;
+      case AVB_HASHTREE_ERROR_MODE_RESTART:
+        verity_mode = "enforcing";
+        dm_verity_mode = "restart_on_corruption";
+        break;
+      case AVB_HASHTREE_ERROR_MODE_EIO:
+        verity_mode = "eio";
+        /* For now there's no option to specify the EIO mode. So
+         * just use 'ignore_zero_blocks' since that's already set
+         * and dm-verity-target.c supports specifying this multiple
+         * times.
+         */
+        dm_verity_mode = "ignore_zero_blocks";
+        break;
+      case AVB_HASHTREE_ERROR_MODE_LOGGING:
+        verity_mode = "logging";
+        dm_verity_mode = "ignore_corruption";
+        break;
+    }
+    new_ret = avb_replace(
+        slot_data->cmdline, "$(ANDROID_VERITY_MODE)", dm_verity_mode);
+    avb_free(slot_data->cmdline);
+    slot_data->cmdline = new_ret;
+    if (slot_data->cmdline == NULL) {
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+      goto out;
+    }
+  }
+  if (!cmdline_append_option(
+          slot_data, "androidboot.veritymode", verity_mode)) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto out;
+  }
+
+  ret = AVB_SLOT_VERIFY_RESULT_OK;
+
+out:
+
+  return ret;
+}
+
+AvbCmdlineSubstList* avb_new_cmdline_subst_list() {
+  return (AvbCmdlineSubstList*)avb_calloc(sizeof(AvbCmdlineSubstList));
+}
+
+void avb_free_cmdline_subst_list(AvbCmdlineSubstList* cmdline_subst) {
+  size_t i;
+  for (i = 0; i < cmdline_subst->size; ++i) {
+    avb_free(cmdline_subst->tokens[i]);
+    avb_free(cmdline_subst->values[i]);
+  }
+  cmdline_subst->size = 0;
+  avb_free(cmdline_subst);
+}
+
+AvbSlotVerifyResult avb_add_root_digest_substitution(
+    const char* part_name,
+    const uint8_t* digest,
+    size_t digest_size,
+    AvbCmdlineSubstList* out_cmdline_subst) {
+  const char* kDigestSubPrefix = "$(AVB_";
+  const char* kDigestSubSuffix = "_ROOT_DIGEST)";
+  size_t part_name_len = avb_strlen(part_name);
+  size_t list_index = out_cmdline_subst->size;
+
+  avb_assert(part_name_len < AVB_PART_NAME_MAX_SIZE);
+  avb_assert(digest_size <= AVB_SHA512_DIGEST_SIZE);
+  if (part_name_len >= AVB_PART_NAME_MAX_SIZE ||
+      digest_size > AVB_SHA512_DIGEST_SIZE) {
+    return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+  }
+
+  if (out_cmdline_subst->size >= AVB_MAX_NUM_CMDLINE_SUBST) {
+    /* The list is full. Currently dynamic growth of this list is not supported.
+     */
+    return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+  }
+
+  /* Construct the token to replace in the command line based on the partition
+   * name. For partition 'foo', this will be '$(AVB_FOO_ROOT_DIGEST)'.
+   */
+  out_cmdline_subst->tokens[list_index] =
+      avb_strdupv(kDigestSubPrefix, part_name, kDigestSubSuffix, NULL);
+  if (out_cmdline_subst->tokens[list_index] == NULL) {
+    goto fail;
+  }
+  avb_uppercase(out_cmdline_subst->tokens[list_index]);
+
+  /* The digest value is hex encoded when inserted in the command line. */
+  out_cmdline_subst->values[list_index] = avb_bin2hex(digest, digest_size);
+  if (out_cmdline_subst->values[list_index] == NULL) {
+    goto fail;
+  }
+
+  out_cmdline_subst->size++;
+  return AVB_SLOT_VERIFY_RESULT_OK;
+
+fail:
+  if (out_cmdline_subst->tokens[list_index]) {
+    avb_free(out_cmdline_subst->tokens[list_index]);
+  }
+  if (out_cmdline_subst->values[list_index]) {
+    avb_free(out_cmdline_subst->values[list_index]);
+  }
+  return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+}
diff --git a/lib/libavb/avb_cmdline.h b/lib/libavb/avb_cmdline.h
new file mode 100644
index 0000000..e94f0d8
--- /dev/null
+++ b/lib/libavb/avb_cmdline.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#ifdef AVB_INSIDE_LIBAVB_H
+#error "You can't include avb_sha.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_CMDLINE_H_
+#define AVB_CMDLINE_H_
+
+#include "avb_ops.h"
+#include "avb_slot_verify.h"
+
+/* Maximum allow length (in bytes) of a partition name, including
+ * ab_suffix.
+ */
+#define AVB_PART_NAME_MAX_SIZE 32
+
+#define AVB_MAX_NUM_CMDLINE_SUBST 10
+
+/* Holds information about command-line substitutions. */
+typedef struct AvbCmdlineSubstList {
+  size_t size;
+  char* tokens[AVB_MAX_NUM_CMDLINE_SUBST];
+  char* values[AVB_MAX_NUM_CMDLINE_SUBST];
+} AvbCmdlineSubstList;
+
+/* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with
+ * values. Returns NULL on OOM, otherwise the cmdline with values
+ * replaced.
+ */
+char* avb_sub_cmdline(AvbOps* ops,
+                      const char* cmdline,
+                      const char* ab_suffix,
+                      bool using_boot_for_vbmeta,
+                      const AvbCmdlineSubstList* additional_substitutions);
+
+AvbSlotVerifyResult avb_append_options(
+    AvbOps* ops,
+    AvbSlotVerifyData* slot_data,
+    AvbVBMetaImageHeader* toplevel_vbmeta,
+    AvbAlgorithmType algorithm_type,
+    AvbHashtreeErrorMode hashtree_error_mode);
+
+/* Allocates and initializes a new command line substitution list. Free with
+ * |avb_free_cmdline_subst_list|.
+ */
+AvbCmdlineSubstList* avb_new_cmdline_subst_list(void);
+
+/* Use this instead of |avb_free| to deallocate a AvbCmdlineSubstList. */
+void avb_free_cmdline_subst_list(AvbCmdlineSubstList* cmdline_subst);
+
+/* Adds a hashtree root digest to be substituted in $(AVB_*_ROOT_DIGEST)
+ * variables. The partition name differentiates the variable. For example, if
+ * |part_name| is "foo" then $(AVB_FOO_ROOT_DIGEST) will be substituted with the
+ * hex encoding of the digest. The substitution will be added to
+ * |out_cmdline_subst|. Returns AVB_SLOT_VERIFY_RESULT_OK on success.
+ */
+AvbSlotVerifyResult avb_add_root_digest_substitution(
+    const char* part_name,
+    const uint8_t* digest,
+    size_t digest_size,
+    AvbCmdlineSubstList* out_cmdline_subst);
+
+#endif
diff --git a/lib/libavb/avb_crypto.c b/lib/libavb/avb_crypto.c
new file mode 100644
index 0000000..af9d770
--- /dev/null
+++ b/lib/libavb/avb_crypto.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#include "avb_crypto.h"
+#include "avb_rsa.h"
+#include "avb_sha.h"
+#include "avb_util.h"
+
+/* NOTE: The PKC1-v1.5 padding is a blob of binary DER of ASN.1 and is
+ * obtained from section 5.2.2 of RFC 4880.
+ */
+
+static const uint8_t
+    padding_RSA2048_SHA256[AVB_RSA2048_NUM_BYTES - AVB_SHA256_DIGEST_SIZE] = {
+        0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0x00, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+        0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
+
+static const uint8_t
+    padding_RSA4096_SHA256[AVB_RSA4096_NUM_BYTES - AVB_SHA256_DIGEST_SIZE] = {
+        0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60,
+        0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
+
+static const uint8_t
+    padding_RSA8192_SHA256[AVB_RSA8192_NUM_BYTES - AVB_SHA256_DIGEST_SIZE] = {
+        0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0x00, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+        0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
+
+static const uint8_t
+    padding_RSA2048_SHA512[AVB_RSA2048_NUM_BYTES - AVB_SHA512_DIGEST_SIZE] = {
+        0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60,
+        0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40};
+
+static const uint8_t
+    padding_RSA4096_SHA512[AVB_RSA4096_NUM_BYTES - AVB_SHA512_DIGEST_SIZE] = {
+        0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x51, 0x30,
+        0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
+        0x05, 0x00, 0x04, 0x40};
+
+static const uint8_t
+    padding_RSA8192_SHA512[AVB_RSA8192_NUM_BYTES - AVB_SHA512_DIGEST_SIZE] = {
+        0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60,
+        0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40};
+
+static AvbAlgorithmData algorithm_data[_AVB_ALGORITHM_NUM_TYPES] = {
+    /* AVB_ALGORITHM_TYPE_NONE */
+    {.padding = NULL, .padding_len = 0, .hash_len = 0},
+    /* AVB_ALGORITHM_TYPE_SHA256_RSA2048 */
+    {.padding = padding_RSA2048_SHA256,
+     .padding_len = sizeof(padding_RSA2048_SHA256),
+     .hash_len = AVB_SHA256_DIGEST_SIZE},
+    /* AVB_ALGORITHM_TYPE_SHA256_RSA4096 */
+    {.padding = padding_RSA4096_SHA256,
+     .padding_len = sizeof(padding_RSA4096_SHA256),
+     .hash_len = AVB_SHA256_DIGEST_SIZE},
+    /* AVB_ALGORITHM_TYPE_SHA256_RSA8192 */
+    {.padding = padding_RSA8192_SHA256,
+     .padding_len = sizeof(padding_RSA8192_SHA256),
+     .hash_len = AVB_SHA256_DIGEST_SIZE},
+    /* AVB_ALGORITHM_TYPE_SHA512_RSA2048 */
+    {.padding = padding_RSA2048_SHA512,
+     .padding_len = sizeof(padding_RSA2048_SHA512),
+     .hash_len = AVB_SHA512_DIGEST_SIZE},
+    /* AVB_ALGORITHM_TYPE_SHA512_RSA4096 */
+    {.padding = padding_RSA4096_SHA512,
+     .padding_len = sizeof(padding_RSA4096_SHA512),
+     .hash_len = AVB_SHA512_DIGEST_SIZE},
+    /* AVB_ALGORITHM_TYPE_SHA512_RSA8192 */
+    {.padding = padding_RSA8192_SHA512,
+     .padding_len = sizeof(padding_RSA8192_SHA512),
+     .hash_len = AVB_SHA512_DIGEST_SIZE},
+};
+
+const AvbAlgorithmData* avb_get_algorithm_data(AvbAlgorithmType algorithm) {
+  if ((size_t)algorithm < _AVB_ALGORITHM_NUM_TYPES) {
+    return &algorithm_data[algorithm];
+  }
+  return NULL;
+}
+
+bool avb_rsa_public_key_header_validate_and_byteswap(
+    const AvbRSAPublicKeyHeader* src, AvbRSAPublicKeyHeader* dest) {
+  avb_memcpy(dest, src, sizeof(AvbRSAPublicKeyHeader));
+
+  dest->key_num_bits = avb_be32toh(dest->key_num_bits);
+  dest->n0inv = avb_be32toh(dest->n0inv);
+
+  return true;
+}
diff --git a/lib/libavb/avb_crypto.h b/lib/libavb/avb_crypto.h
new file mode 100644
index 0000000..cede08f
--- /dev/null
+++ b/lib/libavb/avb_crypto.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_CRYPTO_H_
+#define AVB_CRYPTO_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Size of a RSA-2048 signature. */
+#define AVB_RSA2048_NUM_BYTES 256
+
+/* Size of a RSA-4096 signature. */
+#define AVB_RSA4096_NUM_BYTES 512
+
+/* Size of a RSA-8192 signature. */
+#define AVB_RSA8192_NUM_BYTES 1024
+
+/* Size in bytes of a SHA-1 digest. */
+#define AVB_SHA1_DIGEST_SIZE 20
+
+/* Size in bytes of a SHA-256 digest. */
+#define AVB_SHA256_DIGEST_SIZE 32
+
+/* Size in bytes of a SHA-512 digest. */
+#define AVB_SHA512_DIGEST_SIZE 64
+
+/* Possible digest types supported by libavb routines. */
+typedef enum {
+  AVB_DIGEST_TYPE_SHA256,
+  AVB_DIGEST_TYPE_SHA512,
+} AvbDigestType;
+
+/* Algorithms that can be used in the vbmeta image for
+ * verification. An algorithm consists of a hash type and a signature
+ * type.
+ *
+ * The data used to calculate the hash is the three blocks mentioned
+ * in the documentation for |AvbVBMetaImageHeader| except for the data
+ * in the "Authentication data" block.
+ *
+ * For signatures with RSA keys, PKCS v1.5 padding is used. The public
+ * key data is stored in the auxiliary data block, see
+ * |AvbRSAPublicKeyHeader| for the serialization format.
+ *
+ * Each algorithm type is described below:
+ *
+ * AVB_ALGORITHM_TYPE_NONE: There is no hash, no signature of the
+ * data, and no public key. The data cannot be verified. The fields
+ * |hash_size|, |signature_size|, and |public_key_size| must be zero.
+ *
+ * AVB_ALGORITHM_TYPE_SHA256_RSA2048: The hash function used is
+ * SHA-256, resulting in 32 bytes of hash digest data. This hash is
+ * signed with a 2048-bit RSA key. The field |hash_size| must be 32,
+ * |signature_size| must be 256, and the public key data must have
+ * |key_num_bits| set to 2048.
+ *
+ * AVB_ALGORITHM_TYPE_SHA256_RSA4096: Like above, but only with
+ * a 4096-bit RSA key and |signature_size| set to 512.
+ *
+ * AVB_ALGORITHM_TYPE_SHA256_RSA8192: Like above, but only with
+ * a 8192-bit RSA key and |signature_size| set to 1024.
+ *
+ * AVB_ALGORITHM_TYPE_SHA512_RSA2048: The hash function used is
+ * SHA-512, resulting in 64 bytes of hash digest data. This hash is
+ * signed with a 2048-bit RSA key. The field |hash_size| must be 64,
+ * |signature_size| must be 256, and the public key data must have
+ * |key_num_bits| set to 2048.
+ *
+ * AVB_ALGORITHM_TYPE_SHA512_RSA4096: Like above, but only with
+ * a 4096-bit RSA key and |signature_size| set to 512.
+ *
+ * AVB_ALGORITHM_TYPE_SHA512_RSA8192: Like above, but only with
+ * a 8192-bit RSA key and |signature_size| set to 1024.
+ */
+typedef enum {
+  AVB_ALGORITHM_TYPE_NONE,
+  AVB_ALGORITHM_TYPE_SHA256_RSA2048,
+  AVB_ALGORITHM_TYPE_SHA256_RSA4096,
+  AVB_ALGORITHM_TYPE_SHA256_RSA8192,
+  AVB_ALGORITHM_TYPE_SHA512_RSA2048,
+  AVB_ALGORITHM_TYPE_SHA512_RSA4096,
+  AVB_ALGORITHM_TYPE_SHA512_RSA8192,
+  _AVB_ALGORITHM_NUM_TYPES
+} AvbAlgorithmType;
+
+/* Holds algorithm-specific data. The |padding| is needed by avb_rsa_verify. */
+typedef struct {
+  const uint8_t* padding;
+  size_t padding_len;
+  size_t hash_len;
+} AvbAlgorithmData;
+
+/* Provides algorithm-specific data for a given |algorithm|. Returns NULL if
+ * |algorithm| is invalid.
+ */
+const AvbAlgorithmData* avb_get_algorithm_data(AvbAlgorithmType algorithm)
+    AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* The header for a serialized RSA public key.
+ *
+ * The size of the key is given by |key_num_bits|, for example 2048
+ * for a RSA-2048 key. By definition, a RSA public key is the pair (n,
+ * e) where |n| is the modulus (which can be represented in
+ * |key_num_bits| bits) and |e| is the public exponent. The exponent
+ * is not stored since it's assumed to always be 65537.
+ *
+ * To optimize verification, the key block includes two precomputed
+ * values, |n0inv| (fits in 32 bits) and |rr| and can always be
+ * represented in |key_num_bits|.
+
+ * The value |n0inv| is the value -1/n[0] (mod 2^32). The value |rr|
+ * is (2^key_num_bits)^2 (mod n).
+ *
+ * Following this header is |key_num_bits| bits of |n|, then
+ * |key_num_bits| bits of |rr|. Both values are stored with most
+ * significant bit first. Each serialized number takes up
+ * |key_num_bits|/8 bytes.
+ *
+ * All fields in this struct are stored in network byte order when
+ * serialized.  To generate a copy with fields swapped to native byte
+ * order, use the function avb_rsa_public_key_header_validate_and_byteswap().
+ *
+ * The avb_rsa_verify() function expects a key in this serialized
+ * format.
+ *
+ * The 'avbtool extract_public_key' command can be used to generate a
+ * serialized RSA public key.
+ */
+typedef struct AvbRSAPublicKeyHeader {
+  uint32_t key_num_bits;
+  uint32_t n0inv;
+} AVB_ATTR_PACKED AvbRSAPublicKeyHeader;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ */
+bool avb_rsa_public_key_header_validate_and_byteswap(
+    const AvbRSAPublicKeyHeader* src,
+    AvbRSAPublicKeyHeader* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_CRYPTO_H_ */
diff --git a/lib/libavb/avb_descriptor.c b/lib/libavb/avb_descriptor.c
new file mode 100644
index 0000000..6422293
--- /dev/null
+++ b/lib/libavb/avb_descriptor.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#include "avb_descriptor.h"
+#include "avb_util.h"
+#include "avb_vbmeta_image.h"
+
+bool avb_descriptor_validate_and_byteswap(const AvbDescriptor* src,
+                                          AvbDescriptor* dest) {
+  dest->tag = avb_be64toh(src->tag);
+  dest->num_bytes_following = avb_be64toh(src->num_bytes_following);
+
+  if ((dest->num_bytes_following & 0x07) != 0) {
+    avb_error("Descriptor size is not divisible by 8.\n");
+    return false;
+  }
+  return true;
+}
+
+bool avb_descriptor_foreach(const uint8_t* image_data,
+                            size_t image_size,
+                            AvbDescriptorForeachFunc foreach_func,
+                            void* user_data) {
+  const AvbVBMetaImageHeader* header = NULL;
+  bool ret = false;
+  const uint8_t* image_end;
+  const uint8_t* desc_start;
+  const uint8_t* desc_end;
+  const uint8_t* p;
+
+  if (image_data == NULL) {
+    avb_error("image_data is NULL\n.");
+    goto out;
+  }
+
+  if (foreach_func == NULL) {
+    avb_error("foreach_func is NULL\n.");
+    goto out;
+  }
+
+  if (image_size < sizeof(AvbVBMetaImageHeader)) {
+    avb_error("Length is smaller than header.\n");
+    goto out;
+  }
+
+  /* Ensure magic is correct. */
+  if (avb_memcmp(image_data, AVB_MAGIC, AVB_MAGIC_LEN) != 0) {
+    avb_error("Magic is incorrect.\n");
+    goto out;
+  }
+
+  /* Careful, not byteswapped - also ensure it's aligned properly. */
+  avb_assert_aligned(image_data);
+  header = (const AvbVBMetaImageHeader*)image_data;
+  image_end = image_data + image_size;
+
+  desc_start = image_data + sizeof(AvbVBMetaImageHeader) +
+               avb_be64toh(header->authentication_data_block_size) +
+               avb_be64toh(header->descriptors_offset);
+
+  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;
+  }
+
+  for (p = desc_start; p < desc_end;) {
+    const AvbDescriptor* dh = (const AvbDescriptor*)p;
+    avb_assert_aligned(dh);
+    uint64_t nb_following = avb_be64toh(dh->num_bytes_following);
+    uint64_t nb_total = sizeof(AvbDescriptor) + nb_following;
+
+    if ((nb_total & 7) != 0) {
+      avb_error("Invalid descriptor length.\n");
+      goto out;
+    }
+
+    if (nb_total + p < desc_start || nb_total + p > desc_end) {
+      avb_error("Invalid data in descriptors array.\n");
+      goto out;
+    }
+
+    if (foreach_func(dh, user_data) == 0) {
+      goto out;
+    }
+
+    p += nb_total;
+  }
+
+  ret = true;
+
+out:
+  return ret;
+}
+
+static bool count_descriptors(const AvbDescriptor* descriptor,
+                              void* user_data) {
+  size_t* num_descriptors = user_data;
+  *num_descriptors += 1;
+  return true;
+}
+
+typedef struct {
+  size_t descriptor_number;
+  const AvbDescriptor** descriptors;
+} SetDescriptorData;
+
+static bool set_descriptors(const AvbDescriptor* descriptor, void* user_data) {
+  SetDescriptorData* data = user_data;
+  data->descriptors[data->descriptor_number++] = descriptor;
+  return true;
+}
+
+const AvbDescriptor** avb_descriptor_get_all(const uint8_t* image_data,
+                                             size_t image_size,
+                                             size_t* out_num_descriptors) {
+  size_t num_descriptors = 0;
+  SetDescriptorData data;
+
+  avb_descriptor_foreach(
+      image_data, image_size, count_descriptors, &num_descriptors);
+
+  data.descriptor_number = 0;
+  data.descriptors =
+      avb_calloc(sizeof(const AvbDescriptor*) * (num_descriptors + 1));
+  if (data.descriptors == NULL) {
+    return NULL;
+  }
+  avb_descriptor_foreach(image_data, image_size, set_descriptors, &data);
+  avb_assert(data.descriptor_number == num_descriptors);
+
+  if (out_num_descriptors != NULL) {
+    *out_num_descriptors = num_descriptors;
+  }
+
+  return data.descriptors;
+}
diff --git a/lib/libavb/avb_descriptor.h b/lib/libavb/avb_descriptor.h
new file mode 100644
index 0000000..13a3efd
--- /dev/null
+++ b/lib/libavb/avb_descriptor.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_DESCRIPTOR_H_
+#define AVB_DESCRIPTOR_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Well-known descriptor tags.
+ *
+ * AVB_DESCRIPTOR_TAG_PROPERTY: see |AvbPropertyDescriptor| struct.
+ * AVB_DESCRIPTOR_TAG_HASHTREE: see |AvbHashtreeDescriptor| struct.
+ * AVB_DESCRIPTOR_TAG_HASH: see |AvbHashDescriptor| struct.
+ * AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE: see |AvbKernelCmdlineDescriptor| struct.
+ * AVB_DESCRIPTOR_TAG_CHAIN_PARTITION: see |AvbChainPartitionDescriptor| struct.
+ */
+typedef enum {
+  AVB_DESCRIPTOR_TAG_PROPERTY,
+  AVB_DESCRIPTOR_TAG_HASHTREE,
+  AVB_DESCRIPTOR_TAG_HASH,
+  AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE,
+  AVB_DESCRIPTOR_TAG_CHAIN_PARTITION,
+} AvbDescriptorTag;
+
+/* The header for a serialized descriptor.
+ *
+ * A descriptor always have two fields, a |tag| (denoting its type,
+ * see the |AvbDescriptorTag| enumeration) and the size of the bytes
+ * following, |num_bytes_following|.
+ *
+ * For padding, |num_bytes_following| is always a multiple of 8.
+ */
+typedef struct AvbDescriptor {
+  uint64_t tag;
+  uint64_t num_bytes_following;
+} AVB_ATTR_PACKED AvbDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_descriptor_validate_and_byteswap(
+    const AvbDescriptor* src, AvbDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Signature for callback function used in avb_descriptor_foreach().
+ * The passed in descriptor is given by |descriptor| and the
+ * |user_data| passed to avb_descriptor_foreach() function is in
+ * |user_data|. Return true to continue iterating, false to stop
+ * iterating.
+ *
+ * Note that |descriptor| points into the image passed to
+ * avb_descriptor_foreach() - all fields need to be byteswapped!
+ */
+typedef bool AvbDescriptorForeachFunc(const AvbDescriptor* descriptor,
+                                      void* user_data);
+
+/* Convenience function to iterate over all descriptors in an vbmeta
+ * image.
+ *
+ * The function given by |foreach_func| will be called for each
+ * descriptor. The given function should return true to continue
+ * iterating, false to stop.
+ *
+ * The |user_data| parameter will be passed to |foreach_func|.
+ *
+ * Returns false if the iteration was short-circuited, that is if
+ * an invocation of |foreach_func| returned false.
+ *
+ * Before using this function, you MUST verify |image_data| with
+ * avb_vbmeta_image_verify() and reject it unless it's signed by a known
+ * good public key. Additionally, |image_data| must be word-aligned.
+ */
+bool avb_descriptor_foreach(const uint8_t* image_data,
+                            size_t image_size,
+                            AvbDescriptorForeachFunc foreach_func,
+                            void* user_data);
+
+/* Gets all descriptors in a vbmeta image.
+ *
+ * The return value is a NULL-pointer terminated array of
+ * AvbDescriptor pointers. Free with avb_free() when you are done with
+ * it. If |out_num_descriptors| is non-NULL, the number of descriptors
+ * will be returned there.
+ *
+ * Note that each AvbDescriptor pointer in the array points into
+ * |image_data| - all fields need to be byteswapped!
+ *
+ * Before using this function, you MUST verify |image_data| with
+ * avb_vbmeta_image_verify() and reject it unless it's signed by a known
+ * good public key. Additionally, |image_data| must be word-aligned.
+ */
+const AvbDescriptor** avb_descriptor_get_all(const uint8_t* image_data,
+                                             size_t image_size,
+                                             size_t* out_num_descriptors)
+    AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_DESCRIPTOR_H_ */
diff --git a/lib/libavb/avb_footer.c b/lib/libavb/avb_footer.c
new file mode 100644
index 0000000..f8b6873
--- /dev/null
+++ b/lib/libavb/avb_footer.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#include "avb_footer.h"
+#include "avb_util.h"
+
+bool avb_footer_validate_and_byteswap(const AvbFooter* src, AvbFooter* dest) {
+  avb_memcpy(dest, src, sizeof(AvbFooter));
+
+  dest->version_major = avb_be32toh(dest->version_major);
+  dest->version_minor = avb_be32toh(dest->version_minor);
+
+  dest->original_image_size = avb_be64toh(dest->original_image_size);
+  dest->vbmeta_offset = avb_be64toh(dest->vbmeta_offset);
+  dest->vbmeta_size = avb_be64toh(dest->vbmeta_size);
+
+  /* Check that magic is correct. */
+  if (avb_safe_memcmp(dest->magic, AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN) !=
+      0) {
+    avb_error("Footer magic is incorrect.\n");
+    return false;
+  }
+
+  /* Ensure we don't attempt to access any fields if the footer major
+   * version is not supported.
+   */
+  if (dest->version_major > AVB_FOOTER_VERSION_MAJOR) {
+    avb_error("No support for footer version.\n");
+    return false;
+  }
+
+  return true;
+}
diff --git a/lib/libavb/avb_footer.h b/lib/libavb/avb_footer.h
new file mode 100644
index 0000000..975136a
--- /dev/null
+++ b/lib/libavb/avb_footer.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_FOOTER_H_
+#define AVB_FOOTER_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Magic for the footer. */
+#define AVB_FOOTER_MAGIC "AVBf"
+#define AVB_FOOTER_MAGIC_LEN 4
+
+/* Size of the footer. */
+#define AVB_FOOTER_SIZE 64
+
+/* The current footer version used - keep in sync with avbtool. */
+#define AVB_FOOTER_VERSION_MAJOR 1
+#define AVB_FOOTER_VERSION_MINOR 0
+
+/* The struct used as a footer used on partitions, used to find the
+ * AvbVBMetaImageHeader struct. This struct is always stored at the
+ * end of a partition.
+ */
+typedef struct AvbFooter {
+  /*   0: Four bytes equal to "AVBf" (AVB_FOOTER_MAGIC). */
+  uint8_t magic[AVB_FOOTER_MAGIC_LEN];
+  /*   4: The major version of the footer struct. */
+  uint32_t version_major;
+  /*   8: The minor version of the footer struct. */
+  uint32_t version_minor;
+
+  /*  12: The original size of the image on the partition. */
+  uint64_t original_image_size;
+
+  /*  20: The offset of the |AvbVBMetaImageHeader| struct. */
+  uint64_t vbmeta_offset;
+
+  /*  28: The size of the vbmeta block (header + auth + aux blocks). */
+  uint64_t vbmeta_size;
+
+  /*  36: Padding to ensure struct is size AVB_FOOTER_SIZE bytes. This
+   * must be set to zeroes.
+   */
+  uint8_t reserved[28];
+} AVB_ATTR_PACKED AvbFooter;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ */
+bool avb_footer_validate_and_byteswap(const AvbFooter* src, AvbFooter* dest)
+    AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_FOOTER_H_ */
diff --git a/lib/libavb/avb_hash_descriptor.c b/lib/libavb/avb_hash_descriptor.c
new file mode 100644
index 0000000..6dfaf62
--- /dev/null
+++ b/lib/libavb/avb_hash_descriptor.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#include "avb_hash_descriptor.h"
+#include "avb_util.h"
+
+bool avb_hash_descriptor_validate_and_byteswap(const AvbHashDescriptor* src,
+                                               AvbHashDescriptor* dest) {
+  uint64_t expected_size;
+
+  avb_memcpy(dest, src, sizeof(AvbHashDescriptor));
+
+  if (!avb_descriptor_validate_and_byteswap((const AvbDescriptor*)src,
+                                            (AvbDescriptor*)dest))
+    return false;
+
+  if (dest->parent_descriptor.tag != AVB_DESCRIPTOR_TAG_HASH) {
+    avb_error("Invalid tag for hash descriptor.\n");
+    return false;
+  }
+
+  dest->image_size = avb_be64toh(dest->image_size);
+  dest->partition_name_len = avb_be32toh(dest->partition_name_len);
+  dest->salt_len = avb_be32toh(dest->salt_len);
+  dest->digest_len = avb_be32toh(dest->digest_len);
+  dest->flags = avb_be32toh(dest->flags);
+
+  /* Check that partition_name, salt, and digest are fully contained. */
+  expected_size = sizeof(AvbHashDescriptor) - sizeof(AvbDescriptor);
+  if (!avb_safe_add_to(&expected_size, dest->partition_name_len) ||
+      !avb_safe_add_to(&expected_size, dest->salt_len) ||
+      !avb_safe_add_to(&expected_size, dest->digest_len)) {
+    avb_error("Overflow while adding up sizes.\n");
+    return false;
+  }
+  if (expected_size > dest->parent_descriptor.num_bytes_following) {
+    avb_error("Descriptor payload size overflow.\n");
+    return false;
+  }
+  return true;
+}
diff --git a/lib/libavb/avb_hash_descriptor.h b/lib/libavb/avb_hash_descriptor.h
new file mode 100644
index 0000000..3d43520
--- /dev/null
+++ b/lib/libavb/avb_hash_descriptor.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_HASH_DESCRIPTOR_H_
+#define AVB_HASH_DESCRIPTOR_H_
+
+#include "avb_descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Flags for hash descriptors.
+ *
+ * AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB: Do not apply the default A/B
+ *   partition logic to this partition. This is intentionally a negative boolean
+ *   because A/B should be both the default and most used in practice.
+ */
+typedef enum {
+  AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB = (1 << 0),
+} AvbHashDescriptorFlags;
+
+/* A descriptor containing information about hash for an image.
+ *
+ * This descriptor is typically used for boot partitions to verify the
+ * entire kernel+initramfs image before executing it.
+ *
+ * Following this struct are |partition_name_len| bytes of the
+ * partition name (UTF-8 encoded), |salt_len| bytes of salt, and then
+ * |digest_len| bytes of the digest.
+ *
+ * The |reserved| field is for future expansion and must be set to NUL
+ * bytes.
+ *
+ * Changes in v1.1:
+ *   - flags field is added which supports AVB_HASH_DESCRIPTOR_FLAGS_USE_AB
+ *   - digest_len may be zero, which indicates the use of a persistent digest
+ */
+typedef struct AvbHashDescriptor {
+  AvbDescriptor parent_descriptor;
+  uint64_t image_size;
+  uint8_t hash_algorithm[32];
+  uint32_t partition_name_len;
+  uint32_t salt_len;
+  uint32_t digest_len;
+  uint32_t flags;
+  uint8_t reserved[60];
+} AVB_ATTR_PACKED AvbHashDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_hash_descriptor_validate_and_byteswap(const AvbHashDescriptor* src,
+                                               AvbHashDescriptor* dest)
+    AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_HASH_DESCRIPTOR_H_ */
diff --git a/lib/libavb/avb_hashtree_descriptor.c b/lib/libavb/avb_hashtree_descriptor.c
new file mode 100644
index 0000000..da90277
--- /dev/null
+++ b/lib/libavb/avb_hashtree_descriptor.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#include "avb_hashtree_descriptor.h"
+#include "avb_util.h"
+
+bool avb_hashtree_descriptor_validate_and_byteswap(
+    const AvbHashtreeDescriptor* src, AvbHashtreeDescriptor* dest) {
+  uint64_t expected_size;
+
+  avb_memcpy(dest, src, sizeof(AvbHashtreeDescriptor));
+
+  if (!avb_descriptor_validate_and_byteswap((const AvbDescriptor*)src,
+                                            (AvbDescriptor*)dest))
+    return false;
+
+  if (dest->parent_descriptor.tag != AVB_DESCRIPTOR_TAG_HASHTREE) {
+    avb_error("Invalid tag for hashtree descriptor.\n");
+    return false;
+  }
+
+  dest->dm_verity_version = avb_be32toh(dest->dm_verity_version);
+  dest->image_size = avb_be64toh(dest->image_size);
+  dest->tree_offset = avb_be64toh(dest->tree_offset);
+  dest->tree_size = avb_be64toh(dest->tree_size);
+  dest->data_block_size = avb_be32toh(dest->data_block_size);
+  dest->hash_block_size = avb_be32toh(dest->hash_block_size);
+  dest->fec_num_roots = avb_be32toh(dest->fec_num_roots);
+  dest->fec_offset = avb_be64toh(dest->fec_offset);
+  dest->fec_size = avb_be64toh(dest->fec_size);
+  dest->partition_name_len = avb_be32toh(dest->partition_name_len);
+  dest->salt_len = avb_be32toh(dest->salt_len);
+  dest->root_digest_len = avb_be32toh(dest->root_digest_len);
+  dest->flags = avb_be32toh(dest->flags);
+
+  /* Check that partition_name, salt, and root_digest are fully contained. */
+  expected_size = sizeof(AvbHashtreeDescriptor) - sizeof(AvbDescriptor);
+  if (!avb_safe_add_to(&expected_size, dest->partition_name_len) ||
+      !avb_safe_add_to(&expected_size, dest->salt_len) ||
+      !avb_safe_add_to(&expected_size, dest->root_digest_len)) {
+    avb_error("Overflow while adding up sizes.\n");
+    return false;
+  }
+  if (expected_size > dest->parent_descriptor.num_bytes_following) {
+    avb_error("Descriptor payload size overflow.\n");
+    return false;
+  }
+  return true;
+}
diff --git a/lib/libavb/avb_hashtree_descriptor.h b/lib/libavb/avb_hashtree_descriptor.h
new file mode 100644
index 0000000..3d3e647
--- /dev/null
+++ b/lib/libavb/avb_hashtree_descriptor.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_HASHTREE_DESCRIPTOR_H_
+#define AVB_HASHTREE_DESCRIPTOR_H_
+
+#include "avb_descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Flags for hashtree descriptors.
+ *
+ * AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB: Do not apply the default A/B
+ *   partition logic to this partition. This is intentionally a negative boolean
+ *   because A/B should be both the default and most used in practice.
+ */
+typedef enum {
+  AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB = (1 << 0),
+} AvbHashtreeDescriptorFlags;
+
+/* A descriptor containing information about a dm-verity hashtree.
+ *
+ * Hash-trees are used to verify large partitions typically containing
+ * file systems. See
+ * https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity for more
+ * information about dm-verity.
+ *
+ * Following this struct are |partition_name_len| bytes of the
+ * partition name (UTF-8 encoded), |salt_len| bytes of salt, and then
+ * |root_digest_len| bytes of the root digest.
+ *
+ * The |reserved| field is for future expansion and must be set to NUL
+ * bytes.
+ *
+ * Changes in v1.1:
+ *   - flags field is added which supports AVB_HASHTREE_DESCRIPTOR_FLAGS_USE_AB
+ *   - digest_len may be zero, which indicates the use of a persistent digest
+ */
+typedef struct AvbHashtreeDescriptor {
+  AvbDescriptor parent_descriptor;
+  uint32_t dm_verity_version;
+  uint64_t image_size;
+  uint64_t tree_offset;
+  uint64_t tree_size;
+  uint32_t data_block_size;
+  uint32_t hash_block_size;
+  uint32_t fec_num_roots;
+  uint64_t fec_offset;
+  uint64_t fec_size;
+  uint8_t hash_algorithm[32];
+  uint32_t partition_name_len;
+  uint32_t salt_len;
+  uint32_t root_digest_len;
+  uint32_t flags;
+  uint8_t reserved[60];
+} AVB_ATTR_PACKED AvbHashtreeDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_hashtree_descriptor_validate_and_byteswap(
+    const AvbHashtreeDescriptor* src,
+    AvbHashtreeDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_HASHTREE_DESCRIPTOR_H_ */
diff --git a/lib/libavb/avb_kernel_cmdline_descriptor.c b/lib/libavb/avb_kernel_cmdline_descriptor.c
new file mode 100644
index 0000000..5457dde
--- /dev/null
+++ b/lib/libavb/avb_kernel_cmdline_descriptor.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#include "avb_kernel_cmdline_descriptor.h"
+#include "avb_util.h"
+
+bool avb_kernel_cmdline_descriptor_validate_and_byteswap(
+    const AvbKernelCmdlineDescriptor* src, AvbKernelCmdlineDescriptor* dest) {
+  uint64_t expected_size;
+
+  avb_memcpy(dest, src, sizeof(AvbKernelCmdlineDescriptor));
+
+  if (!avb_descriptor_validate_and_byteswap((const AvbDescriptor*)src,
+                                            (AvbDescriptor*)dest))
+    return false;
+
+  if (dest->parent_descriptor.tag != AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE) {
+    avb_error("Invalid tag for kernel cmdline descriptor.\n");
+    return false;
+  }
+
+  dest->flags = avb_be32toh(dest->flags);
+  dest->kernel_cmdline_length = avb_be32toh(dest->kernel_cmdline_length);
+
+  /* Check that kernel_cmdline is fully contained. */
+  expected_size = sizeof(AvbKernelCmdlineDescriptor) - sizeof(AvbDescriptor);
+  if (!avb_safe_add_to(&expected_size, dest->kernel_cmdline_length)) {
+    avb_error("Overflow while adding up sizes.\n");
+    return false;
+  }
+  if (expected_size > dest->parent_descriptor.num_bytes_following) {
+    avb_error("Descriptor payload size overflow.\n");
+    return false;
+  }
+
+  return true;
+}
diff --git a/lib/libavb/avb_kernel_cmdline_descriptor.h b/lib/libavb/avb_kernel_cmdline_descriptor.h
new file mode 100644
index 0000000..655918d
--- /dev/null
+++ b/lib/libavb/avb_kernel_cmdline_descriptor.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_KERNEL_CMDLINE_DESCRIPTOR_H_
+#define AVB_KERNEL_CMDLINE_DESCRIPTOR_H_
+
+#include "avb_descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Flags for kernel command-line descriptors.
+ *
+ * AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED: The
+ * cmdline will only be applied if hashtree verification is not
+ * disabled (cf. AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED).
+ *
+ * AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_DISABLED: The cmdline
+ * will only be applied if hashtree verification is disabled
+ * (cf. AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED).
+ */
+typedef enum {
+  AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED = (1 << 0),
+  AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_DISABLED = (1 << 1)
+} AvbKernelCmdlineFlags;
+
+/* A descriptor containing information to be appended to the kernel
+ * command-line.
+ *
+ * The |flags| field contains flags from the AvbKernelCmdlineFlags
+ * enumeration.
+ *
+ * Following this struct are |kernel_cmdline_len| bytes with the
+ * kernel command-line (UTF-8 encoded).
+ */
+typedef struct AvbKernelCmdlineDescriptor {
+  AvbDescriptor parent_descriptor;
+  uint32_t flags;
+  uint32_t kernel_cmdline_length;
+} AVB_ATTR_PACKED AvbKernelCmdlineDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_kernel_cmdline_descriptor_validate_and_byteswap(
+    const AvbKernelCmdlineDescriptor* src,
+    AvbKernelCmdlineDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_KERNEL_CMDLINE_DESCRIPTOR_H_ */
diff --git a/lib/libavb/avb_ops.h b/lib/libavb/avb_ops.h
new file mode 100644
index 0000000..3613434
--- /dev/null
+++ b/lib/libavb/avb_ops.h
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_OPS_H_
+#define AVB_OPS_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Well-known names of named persistent values. */
+#define AVB_NPV_PERSISTENT_DIGEST_PREFIX "avb.persistent_digest."
+
+/* Return codes used for I/O operations.
+ *
+ * AVB_IO_RESULT_OK is returned if the requested operation was
+ * successful.
+ *
+ * AVB_IO_RESULT_ERROR_IO is returned if the underlying hardware (disk
+ * or other subsystem) encountered an I/O error.
+ *
+ * AVB_IO_RESULT_ERROR_OOM is returned if unable to allocate memory.
+ *
+ * AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION is returned if the requested
+ * partition does not exist.
+ *
+ * AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION is returned if the
+ * range of bytes requested to be read or written is outside the range
+ * of the partition.
+ *
+ * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE is returned if a named persistent value
+ * does not exist.
+ *
+ * AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE is returned if a named persistent
+ * value size is not supported or does not match the expected size.
+ *
+ * AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE is returned if a buffer is too small
+ * for the requested operation.
+ */
+typedef enum {
+  AVB_IO_RESULT_OK,
+  AVB_IO_RESULT_ERROR_OOM,
+  AVB_IO_RESULT_ERROR_IO,
+  AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION,
+  AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION,
+  AVB_IO_RESULT_ERROR_NO_SUCH_VALUE,
+  AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE,
+  AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE,
+} AvbIOResult;
+
+struct AvbOps;
+typedef struct AvbOps AvbOps;
+
+/* Forward-declaration of operations in libavb_ab. */
+struct AvbABOps;
+
+/* Forward-declaration of operations in libavb_atx. */
+struct AvbAtxOps;
+
+/* High-level operations/functions/methods that are platform
+ * dependent.
+ *
+ * Operations may be added in the future so when implementing it
+ * always make sure to zero out sizeof(AvbOps) bytes of the struct to
+ * ensure that unimplemented operations are set to NULL.
+ */
+struct AvbOps {
+  /* This pointer can be used by the application/bootloader using
+   * libavb and is typically used in each operation to get a pointer
+   * to platform-specific resources. It cannot be used by libraries.
+   */
+  void* user_data;
+
+  /* If libavb_ab is used, this should point to the
+   * AvbABOps. Otherwise it must be set to NULL.
+   */
+  struct AvbABOps* ab_ops;
+
+  /* If libavb_atx is used, this should point to the
+   * AvbAtxOps. Otherwise it must be set to NULL.
+   */
+  struct AvbAtxOps* atx_ops;
+
+  /* Reads |num_bytes| from offset |offset| from partition with name
+   * |partition| (NUL-terminated UTF-8 string). If |offset| is
+   * negative, its absolute value should be interpreted as the number
+   * of bytes from the end of the partition.
+   *
+   * This function returns AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION if
+   * there is no partition with the given name,
+   * AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION if the requested
+   * |offset| is outside the partition, and AVB_IO_RESULT_ERROR_IO if
+   * there was an I/O error from the underlying I/O subsystem.  If the
+   * operation succeeds as requested AVB_IO_RESULT_OK is returned and
+   * the data is available in |buffer|.
+   *
+   * The only time partial I/O may occur is if reading beyond the end
+   * of the partition. In this case the value returned in
+   * |out_num_read| may be smaller than |num_bytes|.
+   */
+  AvbIOResult (*read_from_partition)(AvbOps* ops,
+                                     const char* partition,
+                                     int64_t offset,
+                                     size_t num_bytes,
+                                     void* buffer,
+                                     size_t* out_num_read);
+
+  /* Gets the starting pointer of a partition that is pre-loaded in memory, and
+   * save it to |out_pointer|. The preloaded partition is expected to be
+   * |num_bytes|, where the actual preloaded byte count is returned in
+   * |out_num_bytes_preloaded|. |out_num_bytes_preloaded| must be no larger than
+   * |num_bytes|.
+   *
+   * This provides an alternative way to access a partition that is preloaded
+   * into memory without a full memory copy. When this function pointer is not
+   * set (has value NULL), or when the |out_pointer| is set to NULL as a result,
+   * |read_from_partition| will be used as the fallback. This function is mainly
+   * used for accessing the entire partition content to calculate its hash.
+   *
+   * Preloaded partition data must outlive the lifespan of the
+   * |AvbSlotVerifyData| structure that |avb_slot_verify| outputs.
+   */
+  AvbIOResult (*get_preloaded_partition)(AvbOps* ops,
+                                         const char* partition,
+                                         size_t num_bytes,
+                                         uint8_t** out_pointer,
+                                         size_t* out_num_bytes_preloaded);
+
+  /* Writes |num_bytes| from |bffer| at offset |offset| to partition
+   * with name |partition| (NUL-terminated UTF-8 string). If |offset|
+   * is negative, its absolute value should be interpreted as the
+   * number of bytes from the end of the partition.
+   *
+   * This function returns AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION if
+   * there is no partition with the given name,
+   * AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION if the requested
+   * byterange goes outside the partition, and AVB_IO_RESULT_ERROR_IO
+   * if there was an I/O error from the underlying I/O subsystem.  If
+   * the operation succeeds as requested AVB_IO_RESULT_OK is
+   * returned.
+   *
+   * This function never does any partial I/O, it either transfers all
+   * of the requested bytes or returns an error.
+   */
+  AvbIOResult (*write_to_partition)(AvbOps* ops,
+                                    const char* partition,
+                                    int64_t offset,
+                                    size_t num_bytes,
+                                    const void* buffer);
+
+  /* Checks if the given public key used to sign the 'vbmeta'
+   * partition is trusted. Boot loaders typically compare this with
+   * embedded key material generated with 'avbtool
+   * extract_public_key'.
+   *
+   * The public key is in the array pointed to by |public_key_data|
+   * and is of |public_key_length| bytes.
+   *
+   * If there is no public key metadata (set with the avbtool option
+   * --public_key_metadata) then |public_key_metadata| will be set to
+   * NULL. Otherwise this field points to the data which is
+   * |public_key_metadata_length| bytes long.
+   *
+   * If AVB_IO_RESULT_OK is returned then |out_is_trusted| is set -
+   * true if trusted or false if untrusted.
+   */
+  AvbIOResult (*validate_vbmeta_public_key)(AvbOps* ops,
+                                            const uint8_t* public_key_data,
+                                            size_t public_key_length,
+                                            const uint8_t* public_key_metadata,
+                                            size_t public_key_metadata_length,
+                                            bool* out_is_trusted);
+
+  /* Gets the rollback index corresponding to the location given by
+   * |rollback_index_location|. The value is returned in
+   * |out_rollback_index|. Returns AVB_IO_RESULT_OK if the rollback
+   * index was retrieved, otherwise an error code.
+   *
+   * A device may have a limited amount of rollback index locations (say,
+   * one or four) so may error out if |rollback_index_location| exceeds
+   * this number.
+   */
+  AvbIOResult (*read_rollback_index)(AvbOps* ops,
+                                     size_t rollback_index_location,
+                                     uint64_t* out_rollback_index);
+
+  /* Sets the rollback index corresponding to the location given by
+   * |rollback_index_location| to |rollback_index|. Returns
+   * AVB_IO_RESULT_OK if the rollback index was set, otherwise an
+   * error code.
+   *
+   * A device may have a limited amount of rollback index locations (say,
+   * one or four) so may error out if |rollback_index_location| exceeds
+   * this number.
+   */
+  AvbIOResult (*write_rollback_index)(AvbOps* ops,
+                                      size_t rollback_index_location,
+                                      uint64_t rollback_index);
+
+  /* Gets whether the device is unlocked. The value is returned in
+   * |out_is_unlocked| (true if unlocked, false otherwise). Returns
+   * AVB_IO_RESULT_OK if the state was retrieved, otherwise an error
+   * code.
+   */
+  AvbIOResult (*read_is_device_unlocked)(AvbOps* ops, bool* out_is_unlocked);
+
+  /* Gets the unique partition GUID for a partition with name in
+   * |partition| (NUL-terminated UTF-8 string). The GUID is copied as
+   * a string into |guid_buf| of size |guid_buf_size| and will be NUL
+   * terminated. The string must be lower-case and properly
+   * hyphenated. For example:
+   *
+   *  527c1c6d-6361-4593-8842-3c78fcd39219
+   *
+   * Returns AVB_IO_RESULT_OK on success, otherwise an error code.
+   */
+  AvbIOResult (*get_unique_guid_for_partition)(AvbOps* ops,
+                                               const char* partition,
+                                               char* guid_buf,
+                                               size_t guid_buf_size);
+
+  /* Gets the size of a partition with the name in |partition|
+   * (NUL-terminated UTF-8 string). Returns the value in
+   * |out_size_num_bytes|.
+   *
+   * Returns AVB_IO_RESULT_OK on success, otherwise an error code.
+   */
+  AvbIOResult (*get_size_of_partition)(AvbOps* ops,
+                                       const char* partition,
+                                       uint64_t* out_size_num_bytes);
+
+  /* Reads a persistent value corresponding to the given |name|. The value is
+   * returned in |out_buffer| which must point to |buffer_size| bytes. On
+   * success |out_num_bytes_read| contains the number of bytes read into
+   * |out_buffer|. If AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE is returned,
+   * |out_num_bytes_read| contains the number of bytes that would have been read
+   * which can be used to allocate a buffer.
+   *
+   * The |buffer_size| may be zero and the |out_buffer| may be NULL, but if
+   * |out_buffer| is NULL then |buffer_size| *must* be zero.
+   *
+   * Returns AVB_IO_RESULT_OK on success, otherwise an error code.
+   *
+   * If the value does not exist, is not supported, or is not populated, returns
+   * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. If |buffer_size| is smaller than the
+   * size of the stored value, returns AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE.
+   *
+   * This operation is currently only used to support persistent digests. If a
+   * device does not use persistent digests this function pointer can be set to
+   * NULL.
+   */
+  AvbIOResult (*read_persistent_value)(AvbOps* ops,
+                                       const char* name,
+                                       size_t buffer_size,
+                                       uint8_t* out_buffer,
+                                       size_t* out_num_bytes_read);
+
+  /* Writes a persistent value corresponding to the given |name|. The value is
+   * supplied in |value| which must point to |value_size| bytes. Any existing
+   * value with the same name is overwritten. If |value_size| is zero, future
+   * calls to |read_persistent_value| will return
+   * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE.
+   *
+   * Returns AVB_IO_RESULT_OK on success, otherwise an error code.
+   *
+   * If the value |name| is not supported, returns
+   * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. If the |value_size| is not supported,
+   * returns AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE.
+   *
+   * This operation is currently only used to support persistent digests. If a
+   * device does not use persistent digests this function pointer can be set to
+   * NULL.
+   */
+  AvbIOResult (*write_persistent_value)(AvbOps* ops,
+                                        const char* name,
+                                        size_t value_size,
+                                        const uint8_t* value);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_OPS_H_ */
diff --git a/lib/libavb/avb_property_descriptor.c b/lib/libavb/avb_property_descriptor.c
new file mode 100644
index 0000000..2627cd0
--- /dev/null
+++ b/lib/libavb/avb_property_descriptor.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#include "avb_property_descriptor.h"
+#include "avb_util.h"
+
+bool avb_property_descriptor_validate_and_byteswap(
+    const AvbPropertyDescriptor* src, AvbPropertyDescriptor* dest) {
+  uint64_t expected_size;
+
+  avb_memcpy(dest, src, sizeof(AvbPropertyDescriptor));
+
+  if (!avb_descriptor_validate_and_byteswap((const AvbDescriptor*)src,
+                                            (AvbDescriptor*)dest))
+    return false;
+
+  if (dest->parent_descriptor.tag != AVB_DESCRIPTOR_TAG_PROPERTY) {
+    avb_error("Invalid tag for property descriptor.\n");
+    return false;
+  }
+
+  dest->key_num_bytes = avb_be64toh(dest->key_num_bytes);
+  dest->value_num_bytes = avb_be64toh(dest->value_num_bytes);
+
+  /* Check that key and value are fully contained. */
+  expected_size = sizeof(AvbPropertyDescriptor) - sizeof(AvbDescriptor) + 2;
+  if (!avb_safe_add_to(&expected_size, dest->key_num_bytes) ||
+      !avb_safe_add_to(&expected_size, dest->value_num_bytes)) {
+    avb_error("Overflow while adding up sizes.\n");
+    return false;
+  }
+  if (expected_size > dest->parent_descriptor.num_bytes_following) {
+    avb_error("Descriptor payload size overflow.\n");
+    return false;
+  }
+
+  return true;
+}
+
+typedef struct {
+  const char* key;
+  size_t key_size;
+  const char* ret_value;
+  size_t ret_value_size;
+} PropertyIteratorData;
+
+static bool property_lookup_desc_foreach(const AvbDescriptor* header,
+                                         void* user_data) {
+  PropertyIteratorData* data = (PropertyIteratorData*)user_data;
+  AvbPropertyDescriptor prop_desc;
+  const uint8_t* p;
+  bool ret = true;
+
+  if (header->tag != AVB_DESCRIPTOR_TAG_PROPERTY) {
+    goto out;
+  }
+
+  if (!avb_property_descriptor_validate_and_byteswap(
+          (const AvbPropertyDescriptor*)header, &prop_desc)) {
+    goto out;
+  }
+
+  p = (const uint8_t*)header;
+  if (p[sizeof(AvbPropertyDescriptor) + prop_desc.key_num_bytes] != 0) {
+    avb_error("No terminating NUL byte in key.\n");
+    goto out;
+  }
+
+  if (data->key_size == prop_desc.key_num_bytes) {
+    if (avb_memcmp(p + sizeof(AvbPropertyDescriptor),
+                   data->key,
+                   data->key_size) == 0) {
+      data->ret_value = (const char*)(p + sizeof(AvbPropertyDescriptor) +
+                                      prop_desc.key_num_bytes + 1);
+      data->ret_value_size = prop_desc.value_num_bytes;
+      /* Stop iterating. */
+      ret = false;
+      goto out;
+    }
+  }
+
+out:
+  return ret;
+}
+
+const char* avb_property_lookup(const uint8_t* image_data,
+                                size_t image_size,
+                                const char* key,
+                                size_t key_size,
+                                size_t* out_value_size) {
+  PropertyIteratorData data;
+
+  if (key_size == 0) {
+    key_size = avb_strlen(key);
+  }
+
+  data.key = key;
+  data.key_size = key_size;
+
+  if (avb_descriptor_foreach(
+          image_data, image_size, property_lookup_desc_foreach, &data) == 0) {
+    if (out_value_size != NULL) {
+      *out_value_size = data.ret_value_size;
+    }
+    return data.ret_value;
+  }
+
+  if (out_value_size != NULL) {
+    *out_value_size = 0;
+  }
+  return NULL;
+}
+
+bool avb_property_lookup_uint64(const uint8_t* image_data,
+                                size_t image_size,
+                                const char* key,
+                                size_t key_size,
+                                uint64_t* out_value) {
+  const char* value;
+  bool ret = false;
+  uint64_t parsed_val;
+  int base;
+  int n;
+
+  value = avb_property_lookup(image_data, image_size, key, key_size, NULL);
+  if (value == NULL) {
+    goto out;
+  }
+
+  base = 10;
+  if (avb_memcmp(value, "0x", 2) == 0) {
+    base = 16;
+    value += 2;
+  }
+
+  parsed_val = 0;
+  for (n = 0; value[n] != '\0'; n++) {
+    int c = value[n];
+    int digit;
+
+    parsed_val *= base;
+
+    if (c >= '0' && c <= '9') {
+      digit = c - '0';
+    } else if (base == 16 && c >= 'a' && c <= 'f') {
+      digit = c - 'a' + 10;
+    } else if (base == 16 && c >= 'A' && c <= 'F') {
+      digit = c - 'A' + 10;
+    } else {
+      avb_error("Invalid digit.\n");
+      goto out;
+    }
+
+    parsed_val += digit;
+  }
+
+  ret = true;
+  if (out_value != NULL) {
+    *out_value = parsed_val;
+  }
+
+out:
+  return ret;
+}
diff --git a/lib/libavb/avb_property_descriptor.h b/lib/libavb/avb_property_descriptor.h
new file mode 100644
index 0000000..2a70896
--- /dev/null
+++ b/lib/libavb/avb_property_descriptor.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_PROPERTY_DESCRIPTOR_H_
+#define AVB_PROPERTY_DESCRIPTOR_H_
+
+#include "avb_descriptor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* A descriptor for properties (free-form key/value pairs).
+ *
+ * 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;
+  uint64_t key_num_bytes;
+  uint64_t value_num_bytes;
+} AVB_ATTR_PACKED AvbPropertyDescriptor;
+
+/* Copies |src| to |dest| and validates, byte-swapping fields in the
+ * process if needed. Returns true if valid, false if invalid.
+ *
+ * Data following the struct is not validated nor copied.
+ */
+bool avb_property_descriptor_validate_and_byteswap(
+    const AvbPropertyDescriptor* src,
+    AvbPropertyDescriptor* dest) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Convenience function for looking up the value for a property with
+ * name |key| in a vbmeta image. If |key_size| is 0, |key| must be
+ * NUL-terminated.
+ *
+ * The |image_data| parameter must be a pointer to a vbmeta image of
+ * size |image_size|.
+ *
+ * This function returns a pointer to the value inside the passed-in
+ * image or NULL if not found. Note that the value is always
+ * guaranteed to be followed by a NUL byte.
+ *
+ * If the value was found and |out_value_size| is not NULL, the size
+ * of the value is returned there.
+ *
+ * This function is O(n) in number of descriptors so if you need to
+ * look up a lot of values, you may want to build a more efficient
+ * lookup-table by manually walking all descriptors using
+ * avb_descriptor_foreach().
+ *
+ * Before using this function, you MUST verify |image_data| with
+ * avb_vbmeta_image_verify() and reject it unless it's signed by a
+ * known good public key.
+ */
+const char* avb_property_lookup(const uint8_t* image_data,
+                                size_t image_size,
+                                const char* key,
+                                size_t key_size,
+                                size_t* out_value_size)
+    AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Like avb_property_lookup() but parses the intial portions of the
+ * value as an unsigned 64-bit integer. Both decimal and hexadecimal
+ * representations (e.g. "0x2a") are supported. Returns false on
+ * failure and true on success. On success, the parsed value is
+ * returned in |out_value|.
+ */
+bool avb_property_lookup_uint64(const uint8_t* image_data,
+                                size_t image_size,
+                                const char* key,
+                                size_t key_size,
+                                uint64_t* out_value)
+    AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_PROPERTY_DESCRIPTOR_H_ */
diff --git a/lib/libavb/avb_rsa.c b/lib/libavb/avb_rsa.c
new file mode 100644
index 0000000..269f7d0
--- /dev/null
+++ b/lib/libavb/avb_rsa.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	(MIT or BSD-3-Clause)
+ */
+
+/* Implementation of RSA signature verification which uses a pre-processed
+ * key for computation. The code extends libmincrypt RSA verification code to
+ * support multiple RSA key lengths and hash digest algorithms.
+ */
+
+#include "avb_rsa.h"
+#include "avb_sha.h"
+#include "avb_util.h"
+#include "avb_vbmeta_image.h"
+
+typedef struct IAvbKey {
+  unsigned int len; /* Length of n[] in number of uint32_t */
+  uint32_t n0inv;   /* -1 / n[0] mod 2^32 */
+  uint32_t* n;      /* modulus as array (host-byte order) */
+  uint32_t* rr;     /* R^2 as array (host-byte order) */
+} IAvbKey;
+
+static IAvbKey* iavb_parse_key_data(const uint8_t* data, size_t length) {
+  AvbRSAPublicKeyHeader h;
+  IAvbKey* key = NULL;
+  size_t expected_length;
+  unsigned int i;
+  const uint8_t* n;
+  const uint8_t* rr;
+
+  if (!avb_rsa_public_key_header_validate_and_byteswap(
+          (const AvbRSAPublicKeyHeader*)data, &h)) {
+    avb_error("Invalid key.\n");
+    goto fail;
+  }
+
+  if (!(h.key_num_bits == 2048 || h.key_num_bits == 4096 ||
+        h.key_num_bits == 8192)) {
+    avb_error("Unexpected key length.\n");
+    goto fail;
+  }
+
+  expected_length = sizeof(AvbRSAPublicKeyHeader) + 2 * h.key_num_bits / 8;
+  if (length != expected_length) {
+    avb_error("Key does not match expected length.\n");
+    goto fail;
+  }
+
+  n = data + sizeof(AvbRSAPublicKeyHeader);
+  rr = data + sizeof(AvbRSAPublicKeyHeader) + h.key_num_bits / 8;
+
+  /* Store n and rr following the key header so we only have to do one
+   * allocation.
+   */
+  key = (IAvbKey*)(avb_malloc(sizeof(IAvbKey) + 2 * h.key_num_bits / 8));
+  if (key == NULL) {
+    goto fail;
+  }
+
+  key->len = h.key_num_bits / 32;
+  key->n0inv = h.n0inv;
+  key->n = (uint32_t*)(key + 1); /* Skip ahead sizeof(IAvbKey) bytes. */
+  key->rr = key->n + key->len;
+
+  /* Crypto-code below (modpowF4() and friends) expects the key in
+   * little-endian format (rather than the format we're storing the
+   * key in), so convert it.
+   */
+  for (i = 0; i < key->len; i++) {
+    key->n[i] = avb_be32toh(((uint32_t*)n)[key->len - i - 1]);
+    key->rr[i] = avb_be32toh(((uint32_t*)rr)[key->len - i - 1]);
+  }
+  return key;
+
+fail:
+  if (key != NULL) {
+    avb_free(key);
+  }
+  return NULL;
+}
+
+static void iavb_free_parsed_key(IAvbKey* key) {
+  avb_free(key);
+}
+
+/* a[] -= mod */
+static void subM(const IAvbKey* key, uint32_t* a) {
+  int64_t A = 0;
+  uint32_t i;
+  for (i = 0; i < key->len; ++i) {
+    A += (uint64_t)a[i] - key->n[i];
+    a[i] = (uint32_t)A;
+    A >>= 32;
+  }
+}
+
+/* return a[] >= mod */
+static int geM(const IAvbKey* key, uint32_t* a) {
+  uint32_t i;
+  for (i = key->len; i;) {
+    --i;
+    if (a[i] < key->n[i]) {
+      return 0;
+    }
+    if (a[i] > key->n[i]) {
+      return 1;
+    }
+  }
+  return 1; /* equal */
+}
+
+/* montgomery c[] += a * b[] / R % mod */
+static void montMulAdd(const IAvbKey* key,
+                       uint32_t* c,
+                       const uint32_t a,
+                       const uint32_t* b) {
+  uint64_t A = (uint64_t)a * b[0] + c[0];
+  uint32_t d0 = (uint32_t)A * key->n0inv;
+  uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A;
+  uint32_t i;
+
+  for (i = 1; i < key->len; ++i) {
+    A = (A >> 32) + (uint64_t)a * b[i] + c[i];
+    B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A;
+    c[i - 1] = (uint32_t)B;
+  }
+
+  A = (A >> 32) + (B >> 32);
+
+  c[i - 1] = (uint32_t)A;
+
+  if (A >> 32) {
+    subM(key, c);
+  }
+}
+
+/* montgomery c[] = a[] * b[] / R % mod */
+static void montMul(const IAvbKey* key, uint32_t* c, uint32_t* a, uint32_t* b) {
+  uint32_t i;
+  for (i = 0; i < key->len; ++i) {
+    c[i] = 0;
+  }
+  for (i = 0; i < key->len; ++i) {
+    montMulAdd(key, c, a[i], b);
+  }
+}
+
+/* In-place public exponentiation. (65537}
+ * Input and output big-endian byte array in inout.
+ */
+static void modpowF4(const IAvbKey* key, uint8_t* inout) {
+  uint32_t* a = (uint32_t*)avb_malloc(key->len * sizeof(uint32_t));
+  uint32_t* aR = (uint32_t*)avb_malloc(key->len * sizeof(uint32_t));
+  uint32_t* aaR = (uint32_t*)avb_malloc(key->len * sizeof(uint32_t));
+  if (a == NULL || aR == NULL || aaR == NULL) {
+    goto out;
+  }
+
+  uint32_t* aaa = aaR; /* Re-use location. */
+  int i;
+
+  /* Convert from big endian byte array to little endian word array. */
+  for (i = 0; i < (int)key->len; ++i) {
+    uint32_t tmp = (inout[((key->len - 1 - i) * 4) + 0] << 24) |
+                   (inout[((key->len - 1 - i) * 4) + 1] << 16) |
+                   (inout[((key->len - 1 - i) * 4) + 2] << 8) |
+                   (inout[((key->len - 1 - i) * 4) + 3] << 0);
+    a[i] = tmp;
+  }
+
+  montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M   */
+  for (i = 0; i < 16; i += 2) {
+    montMul(key, aaR, aR, aR);  /* aaR = aR * aR / R mod M */
+    montMul(key, aR, aaR, aaR); /* aR = aaR * aaR / R mod M */
+  }
+  montMul(key, aaa, aR, a); /* aaa = aR * a / R mod M */
+
+  /* Make sure aaa < mod; aaa is at most 1x mod too large. */
+  if (geM(key, aaa)) {
+    subM(key, aaa);
+  }
+
+  /* Convert to bigendian byte array */
+  for (i = (int)key->len - 1; i >= 0; --i) {
+    uint32_t tmp = aaa[i];
+    *inout++ = (uint8_t)(tmp >> 24);
+    *inout++ = (uint8_t)(tmp >> 16);
+    *inout++ = (uint8_t)(tmp >> 8);
+    *inout++ = (uint8_t)(tmp >> 0);
+  }
+
+out:
+  if (a != NULL) {
+    avb_free(a);
+  }
+  if (aR != NULL) {
+    avb_free(aR);
+  }
+  if (aaR != NULL) {
+    avb_free(aaR);
+  }
+}
+
+/* Verify a RSA PKCS1.5 signature against an expected hash.
+ * Returns false on failure, true on success.
+ */
+bool avb_rsa_verify(const uint8_t* key,
+                    size_t key_num_bytes,
+                    const uint8_t* sig,
+                    size_t sig_num_bytes,
+                    const uint8_t* hash,
+                    size_t hash_num_bytes,
+                    const uint8_t* padding,
+                    size_t padding_num_bytes) {
+  uint8_t* buf = NULL;
+  IAvbKey* parsed_key = NULL;
+  bool success = false;
+
+  if (key == NULL || sig == NULL || hash == NULL || padding == NULL) {
+    avb_error("Invalid input.\n");
+    goto out;
+  }
+
+  parsed_key = iavb_parse_key_data(key, key_num_bytes);
+  if (parsed_key == NULL) {
+    avb_error("Error parsing key.\n");
+    goto out;
+  }
+
+  if (sig_num_bytes != (parsed_key->len * sizeof(uint32_t))) {
+    avb_error("Signature length does not match key length.\n");
+    goto out;
+  }
+
+  if (padding_num_bytes != sig_num_bytes - hash_num_bytes) {
+    avb_error("Padding length does not match hash and signature lengths.\n");
+    goto out;
+  }
+
+  buf = (uint8_t*)avb_malloc(sig_num_bytes);
+  if (buf == NULL) {
+    avb_error("Error allocating memory.\n");
+    goto out;
+  }
+  avb_memcpy(buf, sig, sig_num_bytes);
+
+  modpowF4(parsed_key, buf);
+
+  /* Check padding bytes.
+   *
+   * Even though there are probably no timing issues here, we use
+   * avb_safe_memcmp() just to be on the safe side.
+   */
+  if (avb_safe_memcmp(buf, padding, padding_num_bytes)) {
+    avb_error("Padding check failed.\n");
+    goto out;
+  }
+
+  /* Check hash. */
+  if (avb_safe_memcmp(buf + padding_num_bytes, hash, hash_num_bytes)) {
+    avb_error("Hash check failed.\n");
+    goto out;
+  }
+
+  success = true;
+
+out:
+  if (parsed_key != NULL) {
+    iavb_free_parsed_key(parsed_key);
+  }
+  if (buf != NULL) {
+    avb_free(buf);
+  }
+  return success;
+}
diff --git a/lib/libavb/avb_rsa.h b/lib/libavb/avb_rsa.h
new file mode 100644
index 0000000..f5c6a9c
--- /dev/null
+++ b/lib/libavb/avb_rsa.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	(MIT or BSD-3-Clause)
+ */
+
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifdef AVB_INSIDE_LIBAVB_H
+#error "You can't include avb_rsa.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_RSA_H_
+#define AVB_RSA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "avb_crypto.h"
+#include "avb_sysdeps.h"
+
+/* Using the key given by |key|, verify a RSA signature |sig| of
+ * length |sig_num_bytes| against an expected |hash| of length
+ * |hash_num_bytes|. The padding to expect must be passed in using
+ * |padding| of length |padding_num_bytes|.
+ *
+ * The data in |key| must match the format defined in
+ * |AvbRSAPublicKeyHeader|, including the two large numbers
+ * following. The |key_num_bytes| must be the size of the entire
+ * serialized key.
+ *
+ * Returns false if verification fails, true otherwise.
+ */
+bool avb_rsa_verify(const uint8_t* key,
+                    size_t key_num_bytes,
+                    const uint8_t* sig,
+                    size_t sig_num_bytes,
+                    const uint8_t* hash,
+                    size_t hash_num_bytes,
+                    const uint8_t* padding,
+                    size_t padding_num_bytes) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_RSA_H_ */
diff --git a/lib/libavb/avb_sha.h b/lib/libavb/avb_sha.h
new file mode 100644
index 0000000..f1d5f5a
--- /dev/null
+++ b/lib/libavb/avb_sha.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#ifdef AVB_INSIDE_LIBAVB_H
+#error "You can't include avb_sha.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_SHA_H_
+#define AVB_SHA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "avb_crypto.h"
+#include "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];
+  uint32_t tot_len;
+  uint32_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 {
+  uint64_t h[8];
+  uint32_t tot_len;
+  uint32_t len;
+  uint8_t block[2 * AVB_SHA512_BLOCK_SIZE];
+  uint8_t buf[AVB_SHA512_DIGEST_SIZE]; /* Used for storing the final digest. */
+} AvbSHA512Ctx;
+
+/* Initializes the SHA-256 context. */
+void avb_sha256_init(AvbSHA256Ctx* ctx);
+
+/* Updates the SHA-256 context with |len| bytes from |data|. */
+void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, uint32_t len);
+
+/* Returns the SHA-256 digest. */
+uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Initializes the SHA-512 context. */
+void avb_sha512_init(AvbSHA512Ctx* ctx);
+
+/* Updates the SHA-512 context with |len| bytes from |data|. */
+void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, uint32_t len);
+
+/* Returns the SHA-512 digest. */
+uint8_t* avb_sha512_final(AvbSHA512Ctx* ctx) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_SHA_H_ */
diff --git a/lib/libavb/avb_sha256.c b/lib/libavb/avb_sha256.c
new file mode 100644
index 0000000..b81c242
--- /dev/null
+++ b/lib/libavb/avb_sha256.c
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2005, 2007 Olivier Gay <olivier.gay at a3.epfl.ch>
+ * All rights reserved.
+ *
+ * FIPS 180-2 SHA-224/256/384/512 implementation
+ * Last update: 02/02/2007
+ * Issue date:  04/30/2005
+ *
+ * SPDX-License-Identifier:	BSD-3-Clause
+ */
+
+#include "avb_sha.h"
+
+#define SHFR(x, n) (x >> n)
+#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
+#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n)))
+#define CH(x, y, z) ((x & y) ^ (~x & z))
+#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+
+#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3))
+#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10))
+
+#define UNPACK32(x, str)                 \
+  {                                      \
+    *((str) + 3) = (uint8_t)((x));       \
+    *((str) + 2) = (uint8_t)((x) >> 8);  \
+    *((str) + 1) = (uint8_t)((x) >> 16); \
+    *((str) + 0) = (uint8_t)((x) >> 24); \
+  }
+
+#define PACK32(str, x)                                                    \
+  {                                                                       \
+    *(x) = ((uint32_t) * ((str) + 3)) | ((uint32_t) * ((str) + 2) << 8) | \
+           ((uint32_t) * ((str) + 1) << 16) |                             \
+           ((uint32_t) * ((str) + 0) << 24);                              \
+  }
+
+/* Macros used for loops unrolling */
+
+#define SHA256_SCR(i) \
+  { w[i] = SHA256_F4(w[i - 2]) + w[i - 7] + SHA256_F3(w[i - 15]) + w[i - 16]; }
+
+#define SHA256_EXP(a, b, c, d, e, f, g, h, j)                               \
+  {                                                                         \
+    t1 = wv[h] + SHA256_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) + sha256_k[j] + \
+         w[j];                                                              \
+    t2 = SHA256_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]);                       \
+    wv[d] += t1;                                                            \
+    wv[h] = t1 + t2;                                                        \
+  }
+
+static const uint32_t sha256_h0[8] = {0x6a09e667,
+                                      0xbb67ae85,
+                                      0x3c6ef372,
+                                      0xa54ff53a,
+                                      0x510e527f,
+                                      0x9b05688c,
+                                      0x1f83d9ab,
+                                      0x5be0cd19};
+
+static const uint32_t sha256_k[64] = {
+    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
+    0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+    0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
+    0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
+    0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+    0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
+    0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
+    0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+    0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
+
+/* SHA-256 implementation */
+void avb_sha256_init(AvbSHA256Ctx* ctx) {
+#ifndef UNROLL_LOOPS
+  int i;
+  for (i = 0; i < 8; i++) {
+    ctx->h[i] = sha256_h0[i];
+  }
+#else
+  ctx->h[0] = sha256_h0[0];
+  ctx->h[1] = sha256_h0[1];
+  ctx->h[2] = sha256_h0[2];
+  ctx->h[3] = sha256_h0[3];
+  ctx->h[4] = sha256_h0[4];
+  ctx->h[5] = sha256_h0[5];
+  ctx->h[6] = sha256_h0[6];
+  ctx->h[7] = sha256_h0[7];
+#endif /* !UNROLL_LOOPS */
+
+  ctx->len = 0;
+  ctx->tot_len = 0;
+}
+
+static void SHA256_transform(AvbSHA256Ctx* ctx,
+                             const uint8_t* message,
+                             unsigned int block_nb) {
+  uint32_t w[64];
+  uint32_t wv[8];
+  uint32_t t1, t2;
+  const unsigned char* sub_block;
+  int i;
+
+#ifndef UNROLL_LOOPS
+  int j;
+#endif
+
+  for (i = 0; i < (int)block_nb; i++) {
+    sub_block = message + (i << 6);
+
+#ifndef UNROLL_LOOPS
+    for (j = 0; j < 16; j++) {
+      PACK32(&sub_block[j << 2], &w[j]);
+    }
+
+    for (j = 16; j < 64; j++) {
+      SHA256_SCR(j);
+    }
+
+    for (j = 0; j < 8; j++) {
+      wv[j] = ctx->h[j];
+    }
+
+    for (j = 0; j < 64; j++) {
+      t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha256_k[j] +
+           w[j];
+      t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]);
+      wv[7] = wv[6];
+      wv[6] = wv[5];
+      wv[5] = wv[4];
+      wv[4] = wv[3] + t1;
+      wv[3] = wv[2];
+      wv[2] = wv[1];
+      wv[1] = wv[0];
+      wv[0] = t1 + t2;
+    }
+
+    for (j = 0; j < 8; j++) {
+      ctx->h[j] += wv[j];
+    }
+#else
+    PACK32(&sub_block[0], &w[0]);
+    PACK32(&sub_block[4], &w[1]);
+    PACK32(&sub_block[8], &w[2]);
+    PACK32(&sub_block[12], &w[3]);
+    PACK32(&sub_block[16], &w[4]);
+    PACK32(&sub_block[20], &w[5]);
+    PACK32(&sub_block[24], &w[6]);
+    PACK32(&sub_block[28], &w[7]);
+    PACK32(&sub_block[32], &w[8]);
+    PACK32(&sub_block[36], &w[9]);
+    PACK32(&sub_block[40], &w[10]);
+    PACK32(&sub_block[44], &w[11]);
+    PACK32(&sub_block[48], &w[12]);
+    PACK32(&sub_block[52], &w[13]);
+    PACK32(&sub_block[56], &w[14]);
+    PACK32(&sub_block[60], &w[15]);
+
+    SHA256_SCR(16);
+    SHA256_SCR(17);
+    SHA256_SCR(18);
+    SHA256_SCR(19);
+    SHA256_SCR(20);
+    SHA256_SCR(21);
+    SHA256_SCR(22);
+    SHA256_SCR(23);
+    SHA256_SCR(24);
+    SHA256_SCR(25);
+    SHA256_SCR(26);
+    SHA256_SCR(27);
+    SHA256_SCR(28);
+    SHA256_SCR(29);
+    SHA256_SCR(30);
+    SHA256_SCR(31);
+    SHA256_SCR(32);
+    SHA256_SCR(33);
+    SHA256_SCR(34);
+    SHA256_SCR(35);
+    SHA256_SCR(36);
+    SHA256_SCR(37);
+    SHA256_SCR(38);
+    SHA256_SCR(39);
+    SHA256_SCR(40);
+    SHA256_SCR(41);
+    SHA256_SCR(42);
+    SHA256_SCR(43);
+    SHA256_SCR(44);
+    SHA256_SCR(45);
+    SHA256_SCR(46);
+    SHA256_SCR(47);
+    SHA256_SCR(48);
+    SHA256_SCR(49);
+    SHA256_SCR(50);
+    SHA256_SCR(51);
+    SHA256_SCR(52);
+    SHA256_SCR(53);
+    SHA256_SCR(54);
+    SHA256_SCR(55);
+    SHA256_SCR(56);
+    SHA256_SCR(57);
+    SHA256_SCR(58);
+    SHA256_SCR(59);
+    SHA256_SCR(60);
+    SHA256_SCR(61);
+    SHA256_SCR(62);
+    SHA256_SCR(63);
+
+    wv[0] = ctx->h[0];
+    wv[1] = ctx->h[1];
+    wv[2] = ctx->h[2];
+    wv[3] = ctx->h[3];
+    wv[4] = ctx->h[4];
+    wv[5] = ctx->h[5];
+    wv[6] = ctx->h[6];
+    wv[7] = ctx->h[7];
+
+    SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 0);
+    SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 1);
+    SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 2);
+    SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 3);
+    SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 4);
+    SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 5);
+    SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 6);
+    SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 7);
+    SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 8);
+    SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 9);
+    SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 10);
+    SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 11);
+    SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 12);
+    SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 13);
+    SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 14);
+    SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 15);
+    SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 16);
+    SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 17);
+    SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 18);
+    SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 19);
+    SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 20);
+    SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 21);
+    SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 22);
+    SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 23);
+    SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 24);
+    SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 25);
+    SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 26);
+    SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 27);
+    SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 28);
+    SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 29);
+    SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 30);
+    SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 31);
+    SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 32);
+    SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 33);
+    SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 34);
+    SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 35);
+    SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 36);
+    SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 37);
+    SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 38);
+    SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 39);
+    SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 40);
+    SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 41);
+    SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 42);
+    SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 43);
+    SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 44);
+    SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 45);
+    SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 46);
+    SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 47);
+    SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 48);
+    SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 49);
+    SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 50);
+    SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 51);
+    SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 52);
+    SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 53);
+    SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 54);
+    SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 55);
+    SHA256_EXP(0, 1, 2, 3, 4, 5, 6, 7, 56);
+    SHA256_EXP(7, 0, 1, 2, 3, 4, 5, 6, 57);
+    SHA256_EXP(6, 7, 0, 1, 2, 3, 4, 5, 58);
+    SHA256_EXP(5, 6, 7, 0, 1, 2, 3, 4, 59);
+    SHA256_EXP(4, 5, 6, 7, 0, 1, 2, 3, 60);
+    SHA256_EXP(3, 4, 5, 6, 7, 0, 1, 2, 61);
+    SHA256_EXP(2, 3, 4, 5, 6, 7, 0, 1, 62);
+    SHA256_EXP(1, 2, 3, 4, 5, 6, 7, 0, 63);
+
+    ctx->h[0] += wv[0];
+    ctx->h[1] += wv[1];
+    ctx->h[2] += wv[2];
+    ctx->h[3] += wv[3];
+    ctx->h[4] += wv[4];
+    ctx->h[5] += wv[5];
+    ctx->h[6] += wv[6];
+    ctx->h[7] += wv[7];
+#endif /* !UNROLL_LOOPS */
+  }
+}
+
+void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, uint32_t len) {
+  unsigned int block_nb;
+  unsigned int new_len, rem_len, tmp_len;
+  const uint8_t* shifted_data;
+
+  tmp_len = AVB_SHA256_BLOCK_SIZE - ctx->len;
+  rem_len = len < tmp_len ? len : tmp_len;
+
+  avb_memcpy(&ctx->block[ctx->len], data, rem_len);
+
+  if (ctx->len + len < AVB_SHA256_BLOCK_SIZE) {
+    ctx->len += len;
+    return;
+  }
+
+  new_len = len - rem_len;
+  block_nb = new_len / AVB_SHA256_BLOCK_SIZE;
+
+  shifted_data = data + rem_len;
+
+  SHA256_transform(ctx, ctx->block, 1);
+  SHA256_transform(ctx, shifted_data, block_nb);
+
+  rem_len = new_len % AVB_SHA256_BLOCK_SIZE;
+
+  avb_memcpy(ctx->block, &shifted_data[block_nb << 6], rem_len);
+
+  ctx->len = rem_len;
+  ctx->tot_len += (block_nb + 1) << 6;
+}
+
+uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) {
+  unsigned int block_nb;
+  unsigned int pm_len;
+  unsigned int len_b;
+#ifndef UNROLL_LOOPS
+  int i;
+#endif
+
+  block_nb =
+      (1 + ((AVB_SHA256_BLOCK_SIZE - 9) < (ctx->len % AVB_SHA256_BLOCK_SIZE)));
+
+  len_b = (ctx->tot_len + ctx->len) << 3;
+  pm_len = block_nb << 6;
+
+  avb_memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
+  ctx->block[ctx->len] = 0x80;
+  UNPACK32(len_b, ctx->block + pm_len - 4);
+
+  SHA256_transform(ctx, ctx->block, block_nb);
+
+#ifndef UNROLL_LOOPS
+  for (i = 0; i < 8; i++) {
+    UNPACK32(ctx->h[i], &ctx->buf[i << 2]);
+  }
+#else
+  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 ctx->buf;
+}
diff --git a/lib/libavb/avb_sha512.c b/lib/libavb/avb_sha512.c
new file mode 100644
index 0000000..128986d
--- /dev/null
+++ b/lib/libavb/avb_sha512.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2005, 2007 Olivier Gay <olivier.gay at a3.epfl.ch>
+ * All rights reserved.
+ *
+ * FIPS 180-2 SHA-224/256/384/512 implementation
+ * Last update: 02/02/2007
+ * Issue date:  04/30/2005
+ *
+ * SPDX-License-Identifier:	BSD-3-Clause
+ */
+
+#include "avb_sha.h"
+
+#define SHFR(x, n) (x >> n)
+#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
+#define ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n)))
+#define CH(x, y, z) ((x & y) ^ (~x & z))
+#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+
+#define SHA512_F1(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39))
+#define SHA512_F2(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41))
+#define SHA512_F3(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHFR(x, 7))
+#define SHA512_F4(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ SHFR(x, 6))
+
+#define UNPACK32(x, str)                 \
+  {                                      \
+    *((str) + 3) = (uint8_t)((x));       \
+    *((str) + 2) = (uint8_t)((x) >> 8);  \
+    *((str) + 1) = (uint8_t)((x) >> 16); \
+    *((str) + 0) = (uint8_t)((x) >> 24); \
+  }
+
+#define UNPACK64(x, str)                         \
+  {                                              \
+    *((str) + 7) = (uint8_t)x;                   \
+    *((str) + 6) = (uint8_t)((uint64_t)x >> 8);  \
+    *((str) + 5) = (uint8_t)((uint64_t)x >> 16); \
+    *((str) + 4) = (uint8_t)((uint64_t)x >> 24); \
+    *((str) + 3) = (uint8_t)((uint64_t)x >> 32); \
+    *((str) + 2) = (uint8_t)((uint64_t)x >> 40); \
+    *((str) + 1) = (uint8_t)((uint64_t)x >> 48); \
+    *((str) + 0) = (uint8_t)((uint64_t)x >> 56); \
+  }
+
+#define PACK64(str, x)                                                        \
+  {                                                                           \
+    *(x) =                                                                    \
+        ((uint64_t) * ((str) + 7)) | ((uint64_t) * ((str) + 6) << 8) |        \
+        ((uint64_t) * ((str) + 5) << 16) | ((uint64_t) * ((str) + 4) << 24) | \
+        ((uint64_t) * ((str) + 3) << 32) | ((uint64_t) * ((str) + 2) << 40) | \
+        ((uint64_t) * ((str) + 1) << 48) | ((uint64_t) * ((str) + 0) << 56);  \
+  }
+
+/* Macros used for loops unrolling */
+
+#define SHA512_SCR(i) \
+  { w[i] = SHA512_F4(w[i - 2]) + w[i - 7] + SHA512_F3(w[i - 15]) + w[i - 16]; }
+
+#define SHA512_EXP(a, b, c, d, e, f, g, h, j)                               \
+  {                                                                         \
+    t1 = wv[h] + SHA512_F2(wv[e]) + CH(wv[e], wv[f], wv[g]) + sha512_k[j] + \
+         w[j];                                                              \
+    t2 = SHA512_F1(wv[a]) + MAJ(wv[a], wv[b], wv[c]);                       \
+    wv[d] += t1;                                                            \
+    wv[h] = t1 + t2;                                                        \
+  }
+
+static const uint64_t sha512_h0[8] = {0x6a09e667f3bcc908ULL,
+                                      0xbb67ae8584caa73bULL,
+                                      0x3c6ef372fe94f82bULL,
+                                      0xa54ff53a5f1d36f1ULL,
+                                      0x510e527fade682d1ULL,
+                                      0x9b05688c2b3e6c1fULL,
+                                      0x1f83d9abfb41bd6bULL,
+                                      0x5be0cd19137e2179ULL};
+
+static const uint64_t sha512_k[80] = {
+    0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL,
+    0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
+    0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL,
+    0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
+    0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL,
+    0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
+    0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL,
+    0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
+    0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL,
+    0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
+    0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL,
+    0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
+    0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL,
+    0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
+    0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL,
+    0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
+    0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL,
+    0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
+    0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL,
+    0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
+    0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL,
+    0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
+    0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL,
+    0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
+    0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL,
+    0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
+    0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL};
+
+/* SHA-512 implementation */
+
+void avb_sha512_init(AvbSHA512Ctx* ctx) {
+#ifdef UNROLL_LOOPS_SHA512
+  ctx->h[0] = sha512_h0[0];
+  ctx->h[1] = sha512_h0[1];
+  ctx->h[2] = sha512_h0[2];
+  ctx->h[3] = sha512_h0[3];
+  ctx->h[4] = sha512_h0[4];
+  ctx->h[5] = sha512_h0[5];
+  ctx->h[6] = sha512_h0[6];
+  ctx->h[7] = sha512_h0[7];
+#else
+  int i;
+
+  for (i = 0; i < 8; i++)
+    ctx->h[i] = sha512_h0[i];
+#endif /* UNROLL_LOOPS_SHA512 */
+
+  ctx->len = 0;
+  ctx->tot_len = 0;
+}
+
+static void SHA512_transform(AvbSHA512Ctx* ctx,
+                             const uint8_t* message,
+                             unsigned int block_nb) {
+  uint64_t w[80];
+  uint64_t wv[8];
+  uint64_t t1, t2;
+  const uint8_t* sub_block;
+  int i, j;
+
+  for (i = 0; i < (int)block_nb; i++) {
+    sub_block = message + (i << 7);
+
+#ifdef UNROLL_LOOPS_SHA512
+    PACK64(&sub_block[0], &w[0]);
+    PACK64(&sub_block[8], &w[1]);
+    PACK64(&sub_block[16], &w[2]);
+    PACK64(&sub_block[24], &w[3]);
+    PACK64(&sub_block[32], &w[4]);
+    PACK64(&sub_block[40], &w[5]);
+    PACK64(&sub_block[48], &w[6]);
+    PACK64(&sub_block[56], &w[7]);
+    PACK64(&sub_block[64], &w[8]);
+    PACK64(&sub_block[72], &w[9]);
+    PACK64(&sub_block[80], &w[10]);
+    PACK64(&sub_block[88], &w[11]);
+    PACK64(&sub_block[96], &w[12]);
+    PACK64(&sub_block[104], &w[13]);
+    PACK64(&sub_block[112], &w[14]);
+    PACK64(&sub_block[120], &w[15]);
+
+    SHA512_SCR(16);
+    SHA512_SCR(17);
+    SHA512_SCR(18);
+    SHA512_SCR(19);
+    SHA512_SCR(20);
+    SHA512_SCR(21);
+    SHA512_SCR(22);
+    SHA512_SCR(23);
+    SHA512_SCR(24);
+    SHA512_SCR(25);
+    SHA512_SCR(26);
+    SHA512_SCR(27);
+    SHA512_SCR(28);
+    SHA512_SCR(29);
+    SHA512_SCR(30);
+    SHA512_SCR(31);
+    SHA512_SCR(32);
+    SHA512_SCR(33);
+    SHA512_SCR(34);
+    SHA512_SCR(35);
+    SHA512_SCR(36);
+    SHA512_SCR(37);
+    SHA512_SCR(38);
+    SHA512_SCR(39);
+    SHA512_SCR(40);
+    SHA512_SCR(41);
+    SHA512_SCR(42);
+    SHA512_SCR(43);
+    SHA512_SCR(44);
+    SHA512_SCR(45);
+    SHA512_SCR(46);
+    SHA512_SCR(47);
+    SHA512_SCR(48);
+    SHA512_SCR(49);
+    SHA512_SCR(50);
+    SHA512_SCR(51);
+    SHA512_SCR(52);
+    SHA512_SCR(53);
+    SHA512_SCR(54);
+    SHA512_SCR(55);
+    SHA512_SCR(56);
+    SHA512_SCR(57);
+    SHA512_SCR(58);
+    SHA512_SCR(59);
+    SHA512_SCR(60);
+    SHA512_SCR(61);
+    SHA512_SCR(62);
+    SHA512_SCR(63);
+    SHA512_SCR(64);
+    SHA512_SCR(65);
+    SHA512_SCR(66);
+    SHA512_SCR(67);
+    SHA512_SCR(68);
+    SHA512_SCR(69);
+    SHA512_SCR(70);
+    SHA512_SCR(71);
+    SHA512_SCR(72);
+    SHA512_SCR(73);
+    SHA512_SCR(74);
+    SHA512_SCR(75);
+    SHA512_SCR(76);
+    SHA512_SCR(77);
+    SHA512_SCR(78);
+    SHA512_SCR(79);
+
+    wv[0] = ctx->h[0];
+    wv[1] = ctx->h[1];
+    wv[2] = ctx->h[2];
+    wv[3] = ctx->h[3];
+    wv[4] = ctx->h[4];
+    wv[5] = ctx->h[5];
+    wv[6] = ctx->h[6];
+    wv[7] = ctx->h[7];
+
+    j = 0;
+
+    do {
+      SHA512_EXP(0, 1, 2, 3, 4, 5, 6, 7, j);
+      j++;
+      SHA512_EXP(7, 0, 1, 2, 3, 4, 5, 6, j);
+      j++;
+      SHA512_EXP(6, 7, 0, 1, 2, 3, 4, 5, j);
+      j++;
+      SHA512_EXP(5, 6, 7, 0, 1, 2, 3, 4, j);
+      j++;
+      SHA512_EXP(4, 5, 6, 7, 0, 1, 2, 3, j);
+      j++;
+      SHA512_EXP(3, 4, 5, 6, 7, 0, 1, 2, j);
+      j++;
+      SHA512_EXP(2, 3, 4, 5, 6, 7, 0, 1, j);
+      j++;
+      SHA512_EXP(1, 2, 3, 4, 5, 6, 7, 0, j);
+      j++;
+    } while (j < 80);
+
+    ctx->h[0] += wv[0];
+    ctx->h[1] += wv[1];
+    ctx->h[2] += wv[2];
+    ctx->h[3] += wv[3];
+    ctx->h[4] += wv[4];
+    ctx->h[5] += wv[5];
+    ctx->h[6] += wv[6];
+    ctx->h[7] += wv[7];
+#else
+    for (j = 0; j < 16; j++) {
+      PACK64(&sub_block[j << 3], &w[j]);
+    }
+
+    for (j = 16; j < 80; j++) {
+      SHA512_SCR(j);
+    }
+
+    for (j = 0; j < 8; j++) {
+      wv[j] = ctx->h[j];
+    }
+
+    for (j = 0; j < 80; j++) {
+      t1 = wv[7] + SHA512_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha512_k[j] +
+           w[j];
+      t2 = SHA512_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]);
+      wv[7] = wv[6];
+      wv[6] = wv[5];
+      wv[5] = wv[4];
+      wv[4] = wv[3] + t1;
+      wv[3] = wv[2];
+      wv[2] = wv[1];
+      wv[1] = wv[0];
+      wv[0] = t1 + t2;
+    }
+
+    for (j = 0; j < 8; j++)
+      ctx->h[j] += wv[j];
+#endif /* UNROLL_LOOPS_SHA512 */
+  }
+}
+
+void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, uint32_t len) {
+  unsigned int block_nb;
+  unsigned int new_len, rem_len, tmp_len;
+  const uint8_t* shifted_data;
+
+  tmp_len = AVB_SHA512_BLOCK_SIZE - ctx->len;
+  rem_len = len < tmp_len ? len : tmp_len;
+
+  avb_memcpy(&ctx->block[ctx->len], data, rem_len);
+
+  if (ctx->len + len < AVB_SHA512_BLOCK_SIZE) {
+    ctx->len += len;
+    return;
+  }
+
+  new_len = len - rem_len;
+  block_nb = new_len / AVB_SHA512_BLOCK_SIZE;
+
+  shifted_data = data + rem_len;
+
+  SHA512_transform(ctx, ctx->block, 1);
+  SHA512_transform(ctx, shifted_data, block_nb);
+
+  rem_len = new_len % AVB_SHA512_BLOCK_SIZE;
+
+  avb_memcpy(ctx->block, &shifted_data[block_nb << 7], rem_len);
+
+  ctx->len = rem_len;
+  ctx->tot_len += (block_nb + 1) << 7;
+}
+
+uint8_t* avb_sha512_final(AvbSHA512Ctx* ctx) {
+  unsigned int block_nb;
+  unsigned int pm_len;
+  unsigned int len_b;
+
+#ifndef UNROLL_LOOPS_SHA512
+  int i;
+#endif
+
+  block_nb =
+      1 + ((AVB_SHA512_BLOCK_SIZE - 17) < (ctx->len % AVB_SHA512_BLOCK_SIZE));
+
+  len_b = (ctx->tot_len + ctx->len) << 3;
+  pm_len = block_nb << 7;
+
+  avb_memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
+  ctx->block[ctx->len] = 0x80;
+  UNPACK32(len_b, ctx->block + pm_len - 4);
+
+  SHA512_transform(ctx, ctx->block, block_nb);
+
+#ifdef UNROLL_LOOPS_SHA512
+  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], &ctx->buf[i << 3]);
+#endif /* UNROLL_LOOPS_SHA512 */
+
+  return ctx->buf;
+}
diff --git a/lib/libavb/avb_slot_verify.c b/lib/libavb/avb_slot_verify.c
new file mode 100644
index 0000000..66ac6f0
--- /dev/null
+++ b/lib/libavb/avb_slot_verify.c
@@ -0,0 +1,1367 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#include "avb_slot_verify.h"
+#include "avb_chain_partition_descriptor.h"
+#include "avb_cmdline.h"
+#include "avb_footer.h"
+#include "avb_hash_descriptor.h"
+#include "avb_hashtree_descriptor.h"
+#include "avb_kernel_cmdline_descriptor.h"
+#include "avb_sha.h"
+#include "avb_util.h"
+#include "avb_vbmeta_image.h"
+#include "avb_version.h"
+
+/* Maximum number of partitions that can be loaded with avb_slot_verify(). */
+#define MAX_NUMBER_OF_LOADED_PARTITIONS 32
+
+/* Maximum number of vbmeta images that can be loaded with avb_slot_verify(). */
+#define MAX_NUMBER_OF_VBMETA_IMAGES 32
+
+/* Maximum size of a vbmeta image - 64 KiB. */
+#define VBMETA_MAX_SIZE (64 * 1024)
+
+/* Helper function to see if we should continue with verification in
+ * allow_verification_error=true mode if something goes wrong. See the
+ * comments for the avb_slot_verify() function for more information.
+ */
+static inline bool result_should_continue(AvbSlotVerifyResult result) {
+  switch (result) {
+    case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
+    case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
+    case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
+    case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
+    case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT:
+      return false;
+
+    case AVB_SLOT_VERIFY_RESULT_OK:
+    case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
+    case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
+    case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
+      return true;
+  }
+
+  return false;
+}
+
+static AvbSlotVerifyResult load_full_partition(AvbOps* ops,
+                                               const char* part_name,
+                                               uint64_t image_size,
+                                               uint8_t** out_image_buf,
+                                               bool* out_image_preloaded) {
+  size_t part_num_read;
+  AvbIOResult io_ret;
+
+  /* Make sure that we do not overwrite existing data. */
+  avb_assert(*out_image_buf == NULL);
+  avb_assert(!*out_image_preloaded);
+
+  /* We are going to implicitly cast image_size from uint64_t to size_t in the
+   * following code, so we need to make sure that the cast is safe. */
+  if (image_size != (size_t)(image_size)) {
+    avb_errorv(part_name, ": Partition size too large to load.\n", NULL);
+    return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+  }
+
+  /* Try use a preloaded one. */
+  if (ops->get_preloaded_partition != NULL) {
+    io_ret = ops->get_preloaded_partition(
+        ops, part_name, image_size, out_image_buf, &part_num_read);
+    if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+      return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    } else if (io_ret != AVB_IO_RESULT_OK) {
+      avb_errorv(part_name, ": Error loading data from partition.\n", NULL);
+      return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+    }
+
+    if (*out_image_buf != NULL) {
+      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;
+    }
+  }
+
+  /* Allocate and copy the partition. */
+  if (!*out_image_preloaded) {
+    *out_image_buf = avb_malloc(image_size);
+    if (*out_image_buf == NULL) {
+      return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    }
+
+    io_ret = ops->read_from_partition(ops,
+                                      part_name,
+                                      0 /* offset */,
+                                      image_size,
+                                      *out_image_buf,
+                                      &part_num_read);
+    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 data from partition.\n", NULL);
+      return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+    }
+    if (part_num_read != image_size) {
+      avb_errorv(part_name, ": Read incorrect number of bytes.\n", NULL);
+      return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+    }
+  }
+
+  return AVB_SLOT_VERIFY_RESULT_OK;
+}
+
+static AvbSlotVerifyResult read_persistent_digest(AvbOps* ops,
+                                                  const char* part_name,
+                                                  size_t expected_digest_size,
+                                                  uint8_t* out_digest) {
+  char* persistent_value_name = NULL;
+  AvbIOResult io_ret = AVB_IO_RESULT_OK;
+  size_t stored_digest_size = 0;
+
+  if (ops->read_persistent_value == NULL) {
+    avb_errorv(part_name, ": Persistent values are not implemented.\n", NULL);
+    return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+  }
+  persistent_value_name =
+      avb_strdupv(AVB_NPV_PERSISTENT_DIGEST_PREFIX, part_name, NULL);
+  if (persistent_value_name == NULL) {
+    return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+  }
+  io_ret = ops->read_persistent_value(ops,
+                                      persistent_value_name,
+                                      expected_digest_size,
+                                      out_digest,
+                                      &stored_digest_size);
+  avb_free(persistent_value_name);
+  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+    return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+  } else if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE) {
+    avb_errorv(part_name, ": Persistent digest does not exist.\n", NULL);
+    return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+  } else if (io_ret == AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE ||
+             io_ret == AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE ||
+             expected_digest_size != stored_digest_size) {
+    avb_errorv(
+        part_name, ": Persistent digest is not of expected size.\n", NULL);
+    return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+  } else if (io_ret != AVB_IO_RESULT_OK) {
+    avb_errorv(part_name, ": Error reading persistent digest.\n", NULL);
+    return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+  }
+  return AVB_SLOT_VERIFY_RESULT_OK;
+}
+
+static AvbSlotVerifyResult load_and_verify_hash_partition(
+    AvbOps* ops,
+    const char* const* requested_partitions,
+    const char* ab_suffix,
+    bool allow_verification_error,
+    const AvbDescriptor* descriptor,
+    AvbSlotVerifyData* slot_data) {
+  AvbHashDescriptor hash_desc;
+  const uint8_t* desc_partition_name = NULL;
+  const uint8_t* desc_salt;
+  const uint8_t* desc_digest;
+  char part_name[AVB_PART_NAME_MAX_SIZE];
+  AvbSlotVerifyResult ret;
+  AvbIOResult io_ret;
+  uint8_t* image_buf = NULL;
+  bool image_preloaded = false;
+  uint8_t* digest;
+  size_t digest_len;
+  const char* found;
+  uint64_t image_size;
+  size_t expected_digest_len = 0;
+  uint8_t expected_digest_buf[AVB_SHA512_DIGEST_SIZE];
+  const uint8_t* expected_digest = NULL;
+
+  if (!avb_hash_descriptor_validate_and_byteswap(
+          (const AvbHashDescriptor*)descriptor, &hash_desc)) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+    goto out;
+  }
+
+  desc_partition_name =
+      ((const uint8_t*)descriptor) + sizeof(AvbHashDescriptor);
+  desc_salt = desc_partition_name + hash_desc.partition_name_len;
+  desc_digest = desc_salt + hash_desc.salt_len;
+
+  if (!avb_validate_utf8(desc_partition_name, hash_desc.partition_name_len)) {
+    avb_error("Partition name is not valid UTF-8.\n");
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+    goto out;
+  }
+
+  /* Don't bother loading or validating unless the partition was
+   * requested in the first place.
+   */
+  found = avb_strv_find_str(requested_partitions,
+                            (const char*)desc_partition_name,
+                            hash_desc.partition_name_len);
+  if (found == NULL) {
+    ret = AVB_SLOT_VERIFY_RESULT_OK;
+    goto out;
+  }
+
+  if ((hash_desc.flags & AVB_HASH_DESCRIPTOR_FLAGS_DO_NOT_USE_AB) != 0) {
+    /* No ab_suffix, just copy the partition name as is. */
+    if (hash_desc.partition_name_len >= AVB_PART_NAME_MAX_SIZE) {
+      avb_error("Partition name does not fit.\n");
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+      goto out;
+    }
+    avb_memcpy(part_name, desc_partition_name, hash_desc.partition_name_len);
+    part_name[hash_desc.partition_name_len] = '\0';
+  } else if (hash_desc.digest_len == 0 && avb_strlen(ab_suffix) != 0) {
+    /* No ab_suffix allowed for partitions without a digest in the descriptor
+     * because these partitions hold data unique to this device and are not
+     * updated using an A/B scheme.
+     */
+    avb_error("Cannot use A/B with a persistent digest.\n");
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+    goto out;
+  } else {
+    /* Add ab_suffix to the partition name. */
+    if (!avb_str_concat(part_name,
+                        sizeof part_name,
+                        (const char*)desc_partition_name,
+                        hash_desc.partition_name_len,
+                        ab_suffix,
+                        avb_strlen(ab_suffix))) {
+      avb_error("Partition name and suffix does not fit.\n");
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+      goto out;
+    }
+  }
+
+  /* If we're allowing verification errors then hash_desc.image_size
+   * may no longer match what's in the partition... so in this case
+   * just load the entire partition.
+   *
+   * For example, this can happen if a developer does 'fastboot flash
+   * boot /path/to/new/and/bigger/boot.img'. We want this to work
+   * since it's such a common workflow.
+   */
+  image_size = hash_desc.image_size;
+  if (allow_verification_error) {
+    if (ops->get_size_of_partition == NULL) {
+      avb_errorv(part_name,
+                 ": The get_size_of_partition() operation is "
+                 "not implemented so we may not load the entire partition. "
+                 "Please implement.",
+                 NULL);
+    } else {
+      io_ret = ops->get_size_of_partition(ops, part_name, &image_size);
+      if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+        ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+        goto out;
+      } else if (io_ret != AVB_IO_RESULT_OK) {
+        avb_errorv(part_name, ": Error determining partition size.\n", NULL);
+        ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+        goto out;
+      }
+      avb_debugv(part_name, ": Loading entire partition.\n", NULL);
+    }
+  }
+
+  ret = load_full_partition(
+      ops, part_name, image_size, &image_buf, &image_preloaded);
+  if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
+    goto out;
+  }
+
+  if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha256") == 0) {
+    AvbSHA256Ctx sha256_ctx;
+    avb_sha256_init(&sha256_ctx);
+    avb_sha256_update(&sha256_ctx, desc_salt, hash_desc.salt_len);
+    avb_sha256_update(&sha256_ctx, image_buf, hash_desc.image_size);
+    digest = avb_sha256_final(&sha256_ctx);
+    digest_len = AVB_SHA256_DIGEST_SIZE;
+  } else if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha512") == 0) {
+    AvbSHA512Ctx sha512_ctx;
+    avb_sha512_init(&sha512_ctx);
+    avb_sha512_update(&sha512_ctx, desc_salt, hash_desc.salt_len);
+    avb_sha512_update(&sha512_ctx, image_buf, hash_desc.image_size);
+    digest = avb_sha512_final(&sha512_ctx);
+    digest_len = AVB_SHA512_DIGEST_SIZE;
+  } else {
+    avb_errorv(part_name, ": Unsupported hash algorithm.\n", NULL);
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+    goto out;
+  }
+
+  if (hash_desc.digest_len == 0) {
+    // Expect a match to a persistent digest.
+    avb_debugv(part_name, ": No digest, using persistent digest.\n", NULL);
+    expected_digest_len = digest_len;
+    expected_digest = expected_digest_buf;
+    avb_assert(expected_digest_len <= sizeof(expected_digest_buf));
+    ret =
+        read_persistent_digest(ops, part_name, digest_len, expected_digest_buf);
+    if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
+      goto out;
+    }
+  } else {
+    // Expect a match to the digest in the descriptor.
+    expected_digest_len = hash_desc.digest_len;
+    expected_digest = desc_digest;
+  }
+
+  if (digest_len != expected_digest_len) {
+    avb_errorv(
+        part_name, ": Digest in descriptor not of expected size.\n", NULL);
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+    goto out;
+  }
+
+  if (avb_safe_memcmp(digest, expected_digest, digest_len) != 0) {
+    avb_errorv(part_name,
+               ": Hash of data does not match digest in descriptor.\n",
+               NULL);
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
+    goto out;
+  }
+
+  ret = AVB_SLOT_VERIFY_RESULT_OK;
+
+out:
+
+  /* If it worked and something was loaded, copy to slot_data. */
+  if ((ret == AVB_SLOT_VERIFY_RESULT_OK || result_should_continue(ret)) &&
+      image_buf != NULL) {
+    AvbPartitionData* loaded_partition;
+    if (slot_data->num_loaded_partitions == MAX_NUMBER_OF_LOADED_PARTITIONS) {
+      avb_errorv(part_name, ": Too many loaded partitions.\n", NULL);
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+      goto fail;
+    }
+    loaded_partition =
+        &slot_data->loaded_partitions[slot_data->num_loaded_partitions++];
+    loaded_partition->partition_name = avb_strdup(found);
+    loaded_partition->data_size = image_size;
+    loaded_partition->data = image_buf;
+    loaded_partition->preloaded = image_preloaded;
+    image_buf = NULL;
+  }
+
+fail:
+  if (image_buf != NULL && !image_preloaded) {
+    avb_free(image_buf);
+  }
+  return ret;
+}
+
+static AvbSlotVerifyResult load_requested_partitions(
+    AvbOps* ops,
+    const char* const* requested_partitions,
+    const char* ab_suffix,
+    AvbSlotVerifyData* slot_data) {
+  AvbSlotVerifyResult ret;
+  uint8_t* image_buf = NULL;
+  bool image_preloaded = false;
+  size_t n;
+
+  if (ops->get_size_of_partition == NULL) {
+    avb_error("get_size_of_partition() not implemented.\n");
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
+    goto out;
+  }
+
+  for (n = 0; requested_partitions[n] != NULL; n++) {
+    char part_name[AVB_PART_NAME_MAX_SIZE];
+    AvbIOResult io_ret;
+    uint64_t image_size;
+    AvbPartitionData* loaded_partition;
+
+    if (!avb_str_concat(part_name,
+                        sizeof part_name,
+                        requested_partitions[n],
+                        avb_strlen(requested_partitions[n]),
+                        ab_suffix,
+                        avb_strlen(ab_suffix))) {
+      avb_error("Partition name and suffix does not fit.\n");
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+      goto out;
+    }
+
+    io_ret = ops->get_size_of_partition(ops, part_name, &image_size);
+    if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+      goto out;
+    } else if (io_ret != AVB_IO_RESULT_OK) {
+      avb_errorv(part_name, ": Error determining partition size.\n", NULL);
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+      goto out;
+    }
+    avb_debugv(part_name, ": Loading entire partition.\n", NULL);
+
+    ret = load_full_partition(
+        ops, part_name, image_size, &image_buf, &image_preloaded);
+    if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
+      goto out;
+    }
+
+    /* Move to slot_data. */
+    if (slot_data->num_loaded_partitions == MAX_NUMBER_OF_LOADED_PARTITIONS) {
+      avb_errorv(part_name, ": Too many loaded partitions.\n", NULL);
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+      goto out;
+    }
+    loaded_partition =
+        &slot_data->loaded_partitions[slot_data->num_loaded_partitions++];
+    loaded_partition->partition_name = avb_strdup(requested_partitions[n]);
+    if (loaded_partition->partition_name == NULL) {
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+      goto out;
+    }
+    loaded_partition->data_size = image_size;
+    loaded_partition->data = image_buf; /* Transferring the owner. */
+    loaded_partition->preloaded = image_preloaded;
+    image_buf = NULL;
+    image_preloaded = false;
+  }
+
+  ret = AVB_SLOT_VERIFY_RESULT_OK;
+
+out:
+  /* Free the current buffer if any. */
+  if (image_buf != NULL && !image_preloaded) {
+    avb_free(image_buf);
+  }
+  /* Buffers that are already saved in slot_data will be handled by the caller
+   * even on failure. */
+  return ret;
+}
+
+static AvbSlotVerifyResult load_and_verify_vbmeta(
+    AvbOps* ops,
+    const char* const* requested_partitions,
+    const char* ab_suffix,
+    bool allow_verification_error,
+    AvbVBMetaImageFlags toplevel_vbmeta_flags,
+    int rollback_index_location,
+    const char* partition_name,
+    size_t partition_name_len,
+    const uint8_t* expected_public_key,
+    size_t expected_public_key_length,
+    AvbSlotVerifyData* slot_data,
+    AvbAlgorithmType* out_algorithm_type,
+    AvbCmdlineSubstList* out_additional_cmdline_subst) {
+  char full_partition_name[AVB_PART_NAME_MAX_SIZE];
+  AvbSlotVerifyResult ret;
+  AvbIOResult io_ret;
+  size_t vbmeta_offset;
+  size_t vbmeta_size;
+  uint8_t* vbmeta_buf = NULL;
+  size_t vbmeta_num_read;
+  AvbVBMetaVerifyResult vbmeta_ret;
+  const uint8_t* pk_data;
+  size_t pk_len;
+  AvbVBMetaImageHeader vbmeta_header;
+  uint64_t stored_rollback_index;
+  const AvbDescriptor** descriptors = NULL;
+  size_t num_descriptors;
+  size_t n;
+  bool is_main_vbmeta;
+  bool is_vbmeta_partition;
+  AvbVBMetaData* vbmeta_image_data = NULL;
+
+  ret = AVB_SLOT_VERIFY_RESULT_OK;
+
+  avb_assert(slot_data != NULL);
+
+  /* Since we allow top-level vbmeta in 'boot', use
+   * rollback_index_location to determine whether we're the main
+   * vbmeta struct.
+   */
+  is_main_vbmeta = (rollback_index_location == 0);
+  is_vbmeta_partition = (avb_strcmp(partition_name, "vbmeta") == 0);
+
+  if (!avb_validate_utf8((const uint8_t*)partition_name, partition_name_len)) {
+    avb_error("Partition name is not valid UTF-8.\n");
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+    goto out;
+  }
+
+  /* Construct full partition name. */
+  if (!avb_str_concat(full_partition_name,
+                      sizeof full_partition_name,
+                      partition_name,
+                      partition_name_len,
+                      ab_suffix,
+                      avb_strlen(ab_suffix))) {
+    avb_error("Partition name and suffix does not fit.\n");
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+    goto out;
+  }
+
+  avb_debugv("Loading vbmeta struct from partition '",
+             full_partition_name,
+             "'.\n",
+             NULL);
+
+  /* If we're loading from the main vbmeta partition, the vbmeta
+   * struct is in the beginning. Otherwise we have to locate it via a
+   * footer.
+   */
+  if (is_vbmeta_partition) {
+    vbmeta_offset = 0;
+    vbmeta_size = VBMETA_MAX_SIZE;
+  } else {
+    uint8_t footer_buf[AVB_FOOTER_SIZE];
+    size_t footer_num_read;
+    AvbFooter footer;
+
+    io_ret = ops->read_from_partition(ops,
+                                      full_partition_name,
+                                      -AVB_FOOTER_SIZE,
+                                      AVB_FOOTER_SIZE,
+                                      footer_buf,
+                                      &footer_num_read);
+    if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+      goto out;
+    } else if (io_ret != AVB_IO_RESULT_OK) {
+      avb_errorv(full_partition_name, ": Error loading footer.\n", NULL);
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+      goto out;
+    }
+    avb_assert(footer_num_read == AVB_FOOTER_SIZE);
+
+    if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf,
+                                          &footer)) {
+      avb_errorv(full_partition_name, ": Error validating footer.\n", NULL);
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+      goto out;
+    }
+
+    /* Basic footer sanity check since the data is untrusted. */
+    if (footer.vbmeta_size > VBMETA_MAX_SIZE) {
+      avb_errorv(
+          full_partition_name, ": Invalid vbmeta size in footer.\n", NULL);
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+      goto out;
+    }
+
+    vbmeta_offset = footer.vbmeta_offset;
+    vbmeta_size = footer.vbmeta_size;
+  }
+
+  vbmeta_buf = avb_malloc(vbmeta_size);
+  if (vbmeta_buf == NULL) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto out;
+  }
+
+  io_ret = ops->read_from_partition(ops,
+                                    full_partition_name,
+                                    vbmeta_offset,
+                                    vbmeta_size,
+                                    vbmeta_buf,
+                                    &vbmeta_num_read);
+  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto out;
+  } else if (io_ret != AVB_IO_RESULT_OK) {
+    /* If we're looking for 'vbmeta' but there is no such partition,
+     * go try to get it from the boot partition instead.
+     */
+    if (is_main_vbmeta && io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION &&
+        is_vbmeta_partition) {
+      avb_debugv(full_partition_name,
+                 ": No such partition. Trying 'boot' instead.\n",
+                 NULL);
+      ret = load_and_verify_vbmeta(ops,
+                                   requested_partitions,
+                                   ab_suffix,
+                                   allow_verification_error,
+                                   0 /* toplevel_vbmeta_flags */,
+                                   0 /* rollback_index_location */,
+                                   "boot",
+                                   avb_strlen("boot"),
+                                   NULL /* expected_public_key */,
+                                   0 /* expected_public_key_length */,
+                                   slot_data,
+                                   out_algorithm_type,
+                                   out_additional_cmdline_subst);
+      goto out;
+    } else {
+      avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL);
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+      goto out;
+    }
+  }
+  avb_assert(vbmeta_num_read <= vbmeta_size);
+
+  /* Check if the image is properly signed and get the public key used
+   * to sign the image.
+   */
+  vbmeta_ret =
+      avb_vbmeta_image_verify(vbmeta_buf, vbmeta_num_read, &pk_data, &pk_len);
+  switch (vbmeta_ret) {
+    case AVB_VBMETA_VERIFY_RESULT_OK:
+      avb_assert(pk_data != NULL && pk_len > 0);
+      break;
+
+    case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
+    case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
+    case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
+      avb_errorv(full_partition_name,
+                 ": Error verifying vbmeta image: ",
+                 avb_vbmeta_verify_result_to_string(vbmeta_ret),
+                 "\n",
+                 NULL);
+      if (!allow_verification_error) {
+        goto out;
+      }
+      break;
+
+    case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
+      /* No way to continue this case. */
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+      avb_errorv(full_partition_name,
+                 ": Error verifying vbmeta image: invalid vbmeta header\n",
+                 NULL);
+      goto out;
+
+    case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:
+      /* No way to continue this case. */
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION;
+      avb_errorv(full_partition_name,
+                 ": Error verifying vbmeta image: unsupported AVB version\n",
+                 NULL);
+      goto out;
+  }
+
+  /* Byteswap the header. */
+  avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_buf,
+                                             &vbmeta_header);
+
+  /* If we're the toplevel, assign flags so they'll be passed down. */
+  if (is_main_vbmeta) {
+    toplevel_vbmeta_flags = (AvbVBMetaImageFlags)vbmeta_header.flags;
+  } else {
+    if (vbmeta_header.flags != 0) {
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+      avb_errorv(full_partition_name,
+                 ": chained vbmeta image has non-zero flags\n",
+                 NULL);
+      goto out;
+    }
+  }
+
+  /* Check if key used to make signature matches what is expected. */
+  if (pk_data != NULL) {
+    if (expected_public_key != NULL) {
+      avb_assert(!is_main_vbmeta);
+      if (expected_public_key_length != pk_len ||
+          avb_safe_memcmp(expected_public_key, pk_data, pk_len) != 0) {
+        avb_errorv(full_partition_name,
+                   ": Public key used to sign data does not match key in chain "
+                   "partition descriptor.\n",
+                   NULL);
+        ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED;
+        if (!allow_verification_error) {
+          goto out;
+        }
+      }
+    } else {
+      bool key_is_trusted = false;
+      const uint8_t* pk_metadata = NULL;
+      size_t pk_metadata_len = 0;
+
+      if (vbmeta_header.public_key_metadata_size > 0) {
+        pk_metadata = vbmeta_buf + sizeof(AvbVBMetaImageHeader) +
+                      vbmeta_header.authentication_data_block_size +
+                      vbmeta_header.public_key_metadata_offset;
+        pk_metadata_len = vbmeta_header.public_key_metadata_size;
+      }
+
+      avb_assert(is_main_vbmeta);
+      io_ret = ops->validate_vbmeta_public_key(
+          ops, pk_data, pk_len, pk_metadata, pk_metadata_len, &key_is_trusted);
+      if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+        ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+        goto out;
+      } else if (io_ret != AVB_IO_RESULT_OK) {
+        avb_errorv(full_partition_name,
+                   ": Error while checking public key used to sign data.\n",
+                   NULL);
+        ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+        goto out;
+      }
+      if (!key_is_trusted) {
+        avb_errorv(full_partition_name,
+                   ": Public key used to sign data rejected.\n",
+                   NULL);
+        ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED;
+        if (!allow_verification_error) {
+          goto out;
+        }
+      }
+    }
+  }
+
+  /* Check rollback index. */
+  io_ret = ops->read_rollback_index(
+      ops, rollback_index_location, &stored_rollback_index);
+  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto out;
+  } else if (io_ret != AVB_IO_RESULT_OK) {
+    avb_errorv(full_partition_name,
+               ": Error getting rollback index for location.\n",
+               NULL);
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+    goto out;
+  }
+  if (vbmeta_header.rollback_index < stored_rollback_index) {
+    avb_errorv(
+        full_partition_name,
+        ": Image rollback index is less than the stored rollback index.\n",
+        NULL);
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX;
+    if (!allow_verification_error) {
+      goto out;
+    }
+  }
+
+  /* Copy vbmeta to vbmeta_images before recursing. */
+  if (is_main_vbmeta) {
+    avb_assert(slot_data->num_vbmeta_images == 0);
+  } else {
+    avb_assert(slot_data->num_vbmeta_images > 0);
+  }
+  if (slot_data->num_vbmeta_images == MAX_NUMBER_OF_VBMETA_IMAGES) {
+    avb_errorv(full_partition_name, ": Too many vbmeta images.\n", NULL);
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto out;
+  }
+  vbmeta_image_data = &slot_data->vbmeta_images[slot_data->num_vbmeta_images++];
+  vbmeta_image_data->partition_name = avb_strdup(partition_name);
+  vbmeta_image_data->vbmeta_data = vbmeta_buf;
+  /* Note that |vbmeta_buf| is actually |vbmeta_num_read| bytes long
+   * and this includes data past the end of the image. Pass the
+   * actual size of the vbmeta image. Also, no need to use
+   * avb_safe_add() since the header has already been verified.
+   */
+  vbmeta_image_data->vbmeta_size =
+      sizeof(AvbVBMetaImageHeader) +
+      vbmeta_header.authentication_data_block_size +
+      vbmeta_header.auxiliary_data_block_size;
+  vbmeta_image_data->verify_result = vbmeta_ret;
+
+  /* If verification has been disabled by setting a bit in the image,
+   * we're done... except that we need to load the entirety of the
+   * requested partitions.
+   */
+  if (vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {
+    AvbSlotVerifyResult sub_ret;
+    avb_debugv(
+        full_partition_name, ": VERIFICATION_DISABLED bit is set.\n", NULL);
+    /* If load_requested_partitions() fail it is always a fatal
+     * failure (e.g. ERROR_INVALID_ARGUMENT, ERROR_OOM, etc.) rather
+     * than recoverable (e.g. one where result_should_continue()
+     * returns true) and we want to convey that error.
+     */
+    sub_ret = load_requested_partitions(
+        ops, requested_partitions, ab_suffix, slot_data);
+    if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
+      ret = sub_ret;
+    }
+    goto out;
+  }
+
+  /* Now go through all descriptors and take the appropriate action:
+   *
+   * - hash descriptor: Load data from partition, calculate hash, and
+   *   checks that it matches what's in the hash descriptor.
+   *
+   * - hashtree descriptor: Do nothing since verification happens
+   *   on-the-fly from within the OS. (Unless the descriptor uses a
+   *   persistent digest, in which case we need to find it).
+   *
+   * - chained partition descriptor: Load the footer, load the vbmeta
+   *   image, verify vbmeta image (includes rollback checks, hash
+   *   checks, bail on chained partitions).
+   */
+  descriptors =
+      avb_descriptor_get_all(vbmeta_buf, vbmeta_num_read, &num_descriptors);
+  for (n = 0; n < num_descriptors; n++) {
+    AvbDescriptor desc;
+
+    if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {
+      avb_errorv(full_partition_name, ": Descriptor is invalid.\n", NULL);
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+      goto out;
+    }
+
+    switch (desc.tag) {
+      case AVB_DESCRIPTOR_TAG_HASH: {
+        AvbSlotVerifyResult sub_ret;
+        sub_ret = load_and_verify_hash_partition(ops,
+                                                 requested_partitions,
+                                                 ab_suffix,
+                                                 allow_verification_error,
+                                                 descriptors[n],
+                                                 slot_data);
+        if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
+          ret = sub_ret;
+          if (!allow_verification_error || !result_should_continue(ret)) {
+            goto out;
+          }
+        }
+      } break;
+
+      case AVB_DESCRIPTOR_TAG_CHAIN_PARTITION: {
+        AvbSlotVerifyResult sub_ret;
+        AvbChainPartitionDescriptor chain_desc;
+        const uint8_t* chain_partition_name;
+        const uint8_t* chain_public_key;
+
+        /* Only allow CHAIN_PARTITION descriptors in the main vbmeta image. */
+        if (!is_main_vbmeta) {
+          avb_errorv(full_partition_name,
+                     ": Encountered chain descriptor not in main image.\n",
+                     NULL);
+          ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+          goto out;
+        }
+
+        if (!avb_chain_partition_descriptor_validate_and_byteswap(
+                (AvbChainPartitionDescriptor*)descriptors[n], &chain_desc)) {
+          avb_errorv(full_partition_name,
+                     ": Chain partition descriptor is invalid.\n",
+                     NULL);
+          ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+          goto out;
+        }
+
+        if (chain_desc.rollback_index_location == 0) {
+          avb_errorv(full_partition_name,
+                     ": Chain partition has invalid "
+                     "rollback_index_location field.\n",
+                     NULL);
+          ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+          goto out;
+        }
+
+        chain_partition_name = ((const uint8_t*)descriptors[n]) +
+                               sizeof(AvbChainPartitionDescriptor);
+        chain_public_key = chain_partition_name + chain_desc.partition_name_len;
+
+        sub_ret =
+            load_and_verify_vbmeta(ops,
+                                   requested_partitions,
+                                   ab_suffix,
+                                   allow_verification_error,
+                                   toplevel_vbmeta_flags,
+                                   chain_desc.rollback_index_location,
+                                   (const char*)chain_partition_name,
+                                   chain_desc.partition_name_len,
+                                   chain_public_key,
+                                   chain_desc.public_key_len,
+                                   slot_data,
+                                   NULL, /* out_algorithm_type */
+                                   NULL /* out_additional_cmdline_subst */);
+        if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
+          ret = sub_ret;
+          if (!result_should_continue(ret)) {
+            goto out;
+          }
+        }
+      } break;
+
+      case AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE: {
+        const uint8_t* kernel_cmdline;
+        AvbKernelCmdlineDescriptor kernel_cmdline_desc;
+        bool apply_cmdline;
+
+        if (!avb_kernel_cmdline_descriptor_validate_and_byteswap(
+                (AvbKernelCmdlineDescriptor*)descriptors[n],
+                &kernel_cmdline_desc)) {
+          avb_errorv(full_partition_name,
+                     ": Kernel cmdline descriptor is invalid.\n",
+                     NULL);
+          ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+          goto out;
+        }
+
+        kernel_cmdline = ((const uint8_t*)descriptors[n]) +
+                         sizeof(AvbKernelCmdlineDescriptor);
+
+        if (!avb_validate_utf8(kernel_cmdline,
+                               kernel_cmdline_desc.kernel_cmdline_length)) {
+          avb_errorv(full_partition_name,
+                     ": Kernel cmdline is not valid UTF-8.\n",
+                     NULL);
+          ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+          goto out;
+        }
+
+        /* Compare the flags for top-level VBMeta struct with flags in
+         * the command-line descriptor so command-line snippets only
+         * intended for a certain mode (dm-verity enabled/disabled)
+         * are skipped if applicable.
+         */
+        apply_cmdline = true;
+        if (toplevel_vbmeta_flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED) {
+          if (kernel_cmdline_desc.flags &
+              AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_NOT_DISABLED) {
+            apply_cmdline = false;
+          }
+        } else {
+          if (kernel_cmdline_desc.flags &
+              AVB_KERNEL_CMDLINE_FLAGS_USE_ONLY_IF_HASHTREE_DISABLED) {
+            apply_cmdline = false;
+          }
+        }
+
+        if (apply_cmdline) {
+          if (slot_data->cmdline == NULL) {
+            slot_data->cmdline =
+                avb_calloc(kernel_cmdline_desc.kernel_cmdline_length + 1);
+            if (slot_data->cmdline == NULL) {
+              ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+              goto out;
+            }
+            avb_memcpy(slot_data->cmdline,
+                       kernel_cmdline,
+                       kernel_cmdline_desc.kernel_cmdline_length);
+          } else {
+            /* new cmdline is: <existing_cmdline> + ' ' + <newcmdline> + '\0' */
+            size_t orig_size = avb_strlen(slot_data->cmdline);
+            size_t new_size =
+                orig_size + 1 + kernel_cmdline_desc.kernel_cmdline_length + 1;
+            char* new_cmdline = avb_calloc(new_size);
+            if (new_cmdline == NULL) {
+              ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+              goto out;
+            }
+            avb_memcpy(new_cmdline, slot_data->cmdline, orig_size);
+            new_cmdline[orig_size] = ' ';
+            avb_memcpy(new_cmdline + orig_size + 1,
+                       kernel_cmdline,
+                       kernel_cmdline_desc.kernel_cmdline_length);
+            avb_free(slot_data->cmdline);
+            slot_data->cmdline = new_cmdline;
+          }
+        }
+      } break;
+
+      case AVB_DESCRIPTOR_TAG_HASHTREE: {
+        AvbHashtreeDescriptor hashtree_desc;
+
+        if (!avb_hashtree_descriptor_validate_and_byteswap(
+                (AvbHashtreeDescriptor*)descriptors[n], &hashtree_desc)) {
+          avb_errorv(
+              full_partition_name, ": Hashtree descriptor is invalid.\n", NULL);
+          ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+          goto out;
+        }
+
+        /* We only need to continue when there is no digest in the descriptor.
+         * This is because the only processing here is to find the digest and
+         * make it available on the kernel command line.
+         */
+        if (hashtree_desc.root_digest_len == 0) {
+          char part_name[AVB_PART_NAME_MAX_SIZE];
+          size_t digest_len = 0;
+          uint8_t digest_buf[AVB_SHA512_DIGEST_SIZE];
+          const uint8_t* desc_partition_name =
+              ((const uint8_t*)descriptors[n]) + sizeof(AvbHashtreeDescriptor);
+
+          if (!avb_validate_utf8(desc_partition_name,
+                                 hashtree_desc.partition_name_len)) {
+            avb_error("Partition name is not valid UTF-8.\n");
+            ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+            goto out;
+          }
+
+          /* No ab_suffix for partitions without a digest in the descriptor
+           * because these partitions hold data unique to this device and are
+           * not updated using an A/B scheme.
+           */
+          if ((hashtree_desc.flags &
+               AVB_HASHTREE_DESCRIPTOR_FLAGS_DO_NOT_USE_AB) == 0 &&
+              avb_strlen(ab_suffix) != 0) {
+            avb_error("Cannot use A/B with a persistent root digest.\n");
+            ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+            goto out;
+          }
+          if (hashtree_desc.partition_name_len >= AVB_PART_NAME_MAX_SIZE) {
+            avb_error("Partition name does not fit.\n");
+            ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+            goto out;
+          }
+          avb_memcpy(
+              part_name, desc_partition_name, hashtree_desc.partition_name_len);
+          part_name[hashtree_desc.partition_name_len] = '\0';
+
+          /* Determine the expected digest size from the hash algorithm. */
+          if (avb_strcmp((const char*)hashtree_desc.hash_algorithm, "sha1") ==
+              0) {
+            digest_len = AVB_SHA1_DIGEST_SIZE;
+          } else if (avb_strcmp((const char*)hashtree_desc.hash_algorithm,
+                                "sha256") == 0) {
+            digest_len = AVB_SHA256_DIGEST_SIZE;
+          } else if (avb_strcmp((const char*)hashtree_desc.hash_algorithm,
+                                "sha512") == 0) {
+            digest_len = AVB_SHA512_DIGEST_SIZE;
+          } else {
+            avb_errorv(part_name, ": Unsupported hash algorithm.\n", NULL);
+            ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
+            goto out;
+          }
+
+          ret = read_persistent_digest(ops, part_name, digest_len, digest_buf);
+          if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
+            goto out;
+          }
+
+          if (out_additional_cmdline_subst) {
+            ret =
+                avb_add_root_digest_substitution(part_name,
+                                                 digest_buf,
+                                                 digest_len,
+                                                 out_additional_cmdline_subst);
+            if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
+              goto out;
+            }
+          }
+        }
+      } break;
+
+      case AVB_DESCRIPTOR_TAG_PROPERTY:
+        /* Do nothing. */
+        break;
+    }
+  }
+
+  if (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] =
+      vbmeta_header.rollback_index;
+
+  if (out_algorithm_type != NULL) {
+    *out_algorithm_type = (AvbAlgorithmType)vbmeta_header.algorithm_type;
+  }
+
+out:
+  /* If |vbmeta_image_data| isn't NULL it means that it adopted
+   * |vbmeta_buf| so in that case don't free it here.
+   */
+  if (vbmeta_image_data == NULL) {
+    if (vbmeta_buf != NULL) {
+      avb_free(vbmeta_buf);
+    }
+  }
+  if (descriptors != NULL) {
+    avb_free(descriptors);
+  }
+  return ret;
+}
+
+AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
+                                    const char* const* requested_partitions,
+                                    const char* ab_suffix,
+                                    AvbSlotVerifyFlags flags,
+                                    AvbHashtreeErrorMode hashtree_error_mode,
+                                    AvbSlotVerifyData** out_data) {
+  AvbSlotVerifyResult ret;
+  AvbSlotVerifyData* slot_data = NULL;
+  AvbAlgorithmType algorithm_type = AVB_ALGORITHM_TYPE_NONE;
+  bool using_boot_for_vbmeta = false;
+  AvbVBMetaImageHeader toplevel_vbmeta;
+  bool allow_verification_error =
+      (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR);
+  AvbCmdlineSubstList* additional_cmdline_subst = NULL;
+
+  /* Fail early if we're missing the AvbOps needed for slot verification.
+   *
+   * For now, handle get_size_of_partition() not being implemented. In
+   * a later release we may change that.
+   */
+  avb_assert(ops->read_is_device_unlocked != NULL);
+  avb_assert(ops->read_from_partition != NULL);
+  avb_assert(ops->validate_vbmeta_public_key != NULL);
+  avb_assert(ops->read_rollback_index != NULL);
+  avb_assert(ops->get_unique_guid_for_partition != NULL);
+
+  if (out_data != NULL) {
+    *out_data = NULL;
+  }
+
+  /* Allowing dm-verity errors defeats the purpose of verified boot so
+   * only allow this if set up to allow verification errors
+   * (e.g. typically only UNLOCKED mode).
+   */
+  if (hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_LOGGING &&
+      !allow_verification_error) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
+    goto fail;
+  }
+
+  slot_data = avb_calloc(sizeof(AvbSlotVerifyData));
+  if (slot_data == NULL) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto fail;
+  }
+  slot_data->vbmeta_images =
+      avb_calloc(sizeof(AvbVBMetaData) * MAX_NUMBER_OF_VBMETA_IMAGES);
+  if (slot_data->vbmeta_images == NULL) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto fail;
+  }
+  slot_data->loaded_partitions =
+      avb_calloc(sizeof(AvbPartitionData) * MAX_NUMBER_OF_LOADED_PARTITIONS);
+  if (slot_data->loaded_partitions == NULL) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto fail;
+  }
+
+  additional_cmdline_subst = avb_new_cmdline_subst_list();
+  if (additional_cmdline_subst == NULL) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto fail;
+  }
+
+  ret = load_and_verify_vbmeta(ops,
+                               requested_partitions,
+                               ab_suffix,
+                               allow_verification_error,
+                               0 /* toplevel_vbmeta_flags */,
+                               0 /* rollback_index_location */,
+                               "vbmeta",
+                               avb_strlen("vbmeta"),
+                               NULL /* expected_public_key */,
+                               0 /* expected_public_key_length */,
+                               slot_data,
+                               &algorithm_type,
+                               additional_cmdline_subst);
+  if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) {
+    goto fail;
+  }
+
+  /* If things check out, mangle the kernel command-line as needed. */
+  if (result_should_continue(ret)) {
+    if (avb_strcmp(slot_data->vbmeta_images[0].partition_name, "vbmeta") != 0) {
+      avb_assert(
+          avb_strcmp(slot_data->vbmeta_images[0].partition_name, "boot") == 0);
+      using_boot_for_vbmeta = true;
+    }
+
+    /* Byteswap top-level vbmeta header since we'll need it below. */
+    avb_vbmeta_image_header_to_host_byte_order(
+        (const AvbVBMetaImageHeader*)slot_data->vbmeta_images[0].vbmeta_data,
+        &toplevel_vbmeta);
+
+    /* Fill in |ab_suffix| field. */
+    slot_data->ab_suffix = avb_strdup(ab_suffix);
+    if (slot_data->ab_suffix == NULL) {
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+      goto fail;
+    }
+
+    /* If verification is disabled, we are done ... we specifically
+     * don't want to add any androidboot.* options since verification
+     * is disabled.
+     */
+    if (toplevel_vbmeta.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {
+      /* Since verification is disabled we didn't process any
+       * descriptors and thus there's no cmdline... so set root= such
+       * that the system partition is mounted.
+       */
+      avb_assert(slot_data->cmdline == NULL);
+      slot_data->cmdline =
+          avb_strdup("root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)");
+      if (slot_data->cmdline == NULL) {
+        ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+        goto fail;
+      }
+    } else {
+      /* Add options - any failure in avb_append_options() is either an
+       * I/O or OOM error.
+       */
+      AvbSlotVerifyResult sub_ret = avb_append_options(ops,
+                                                       slot_data,
+                                                       &toplevel_vbmeta,
+                                                       algorithm_type,
+                                                       hashtree_error_mode);
+      if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
+        ret = sub_ret;
+        goto fail;
+      }
+    }
+
+    /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */
+    if (slot_data->cmdline != NULL) {
+      char* new_cmdline;
+      new_cmdline = avb_sub_cmdline(ops,
+                                    slot_data->cmdline,
+                                    ab_suffix,
+                                    using_boot_for_vbmeta,
+                                    additional_cmdline_subst);
+      if (new_cmdline != slot_data->cmdline) {
+        if (new_cmdline == NULL) {
+          ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+          goto fail;
+        }
+        avb_free(slot_data->cmdline);
+        slot_data->cmdline = new_cmdline;
+      }
+    }
+
+    if (out_data != NULL) {
+      *out_data = slot_data;
+    } else {
+      avb_slot_verify_data_free(slot_data);
+    }
+  }
+
+  avb_free_cmdline_subst_list(additional_cmdline_subst);
+  additional_cmdline_subst = NULL;
+
+  if (!allow_verification_error) {
+    avb_assert(ret == AVB_SLOT_VERIFY_RESULT_OK);
+  }
+
+  return ret;
+
+fail:
+  if (slot_data != NULL) {
+    avb_slot_verify_data_free(slot_data);
+  }
+  if (additional_cmdline_subst != NULL) {
+    avb_free_cmdline_subst_list(additional_cmdline_subst);
+  }
+  return ret;
+}
+
+void avb_slot_verify_data_free(AvbSlotVerifyData* data) {
+  if (data->ab_suffix != NULL) {
+    avb_free(data->ab_suffix);
+  }
+  if (data->cmdline != NULL) {
+    avb_free(data->cmdline);
+  }
+  if (data->vbmeta_images != NULL) {
+    size_t n;
+    for (n = 0; n < data->num_vbmeta_images; n++) {
+      AvbVBMetaData* vbmeta_image = &data->vbmeta_images[n];
+      if (vbmeta_image->partition_name != NULL) {
+        avb_free(vbmeta_image->partition_name);
+      }
+      if (vbmeta_image->vbmeta_data != NULL) {
+        avb_free(vbmeta_image->vbmeta_data);
+      }
+    }
+    avb_free(data->vbmeta_images);
+  }
+  if (data->loaded_partitions != NULL) {
+    size_t n;
+    for (n = 0; n < data->num_loaded_partitions; n++) {
+      AvbPartitionData* loaded_partition = &data->loaded_partitions[n];
+      if (loaded_partition->partition_name != NULL) {
+        avb_free(loaded_partition->partition_name);
+      }
+      if (loaded_partition->data != NULL && !loaded_partition->preloaded) {
+        avb_free(loaded_partition->data);
+      }
+    }
+    avb_free(data->loaded_partitions);
+  }
+  avb_free(data);
+}
+
+const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result) {
+  const char* ret = NULL;
+
+  switch (result) {
+    case AVB_SLOT_VERIFY_RESULT_OK:
+      ret = "OK";
+      break;
+    case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
+      ret = "ERROR_OOM";
+      break;
+    case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
+      ret = "ERROR_IO";
+      break;
+    case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
+      ret = "ERROR_VERIFICATION";
+      break;
+    case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
+      ret = "ERROR_ROLLBACK_INDEX";
+      break;
+    case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
+      ret = "ERROR_PUBLIC_KEY_REJECTED";
+      break;
+    case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
+      ret = "ERROR_INVALID_METADATA";
+      break;
+    case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
+      ret = "ERROR_UNSUPPORTED_VERSION";
+      break;
+    case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT:
+      ret = "ERROR_INVALID_ARGUMENT";
+      break;
+      /* Do not add a 'default:' case here because of -Wswitch. */
+  }
+
+  if (ret == NULL) {
+    avb_error("Unknown AvbSlotVerifyResult value.\n");
+    ret = "(unknown)";
+  }
+
+  return ret;
+}
+
+void avb_slot_verify_data_calculate_vbmeta_digest(AvbSlotVerifyData* data,
+                                                  AvbDigestType digest_type,
+                                                  uint8_t* out_digest) {
+  bool ret = false;
+  size_t n;
+
+  switch (digest_type) {
+    case AVB_DIGEST_TYPE_SHA256: {
+      AvbSHA256Ctx ctx;
+      avb_sha256_init(&ctx);
+      for (n = 0; n < data->num_vbmeta_images; n++) {
+        avb_sha256_update(&ctx,
+                          data->vbmeta_images[n].vbmeta_data,
+                          data->vbmeta_images[n].vbmeta_size);
+      }
+      avb_memcpy(out_digest, avb_sha256_final(&ctx), AVB_SHA256_DIGEST_SIZE);
+      ret = true;
+    } break;
+
+    case AVB_DIGEST_TYPE_SHA512: {
+      AvbSHA512Ctx ctx;
+      avb_sha512_init(&ctx);
+      for (n = 0; n < data->num_vbmeta_images; n++) {
+        avb_sha512_update(&ctx,
+                          data->vbmeta_images[n].vbmeta_data,
+                          data->vbmeta_images[n].vbmeta_size);
+      }
+      avb_memcpy(out_digest, avb_sha512_final(&ctx), AVB_SHA512_DIGEST_SIZE);
+      ret = true;
+    } break;
+
+      /* Do not add a 'default:' case here because of -Wswitch. */
+  }
+
+  if (!ret) {
+    avb_fatal("Unknown digest type");
+  }
+}
diff --git a/lib/libavb/avb_slot_verify.h b/lib/libavb/avb_slot_verify.h
new file mode 100644
index 0000000..d603060
--- /dev/null
+++ b/lib/libavb/avb_slot_verify.h
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_SLOT_VERIFY_H_
+#define AVB_SLOT_VERIFY_H_
+
+#include "avb_ops.h"
+#include "avb_vbmeta_image.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Return codes used in avb_slot_verify(), see that function for
+ * documentation for each field.
+ *
+ * Use avb_slot_verify_result_to_string() to get a textual
+ * representation usable for error/debug output.
+ */
+typedef enum {
+  AVB_SLOT_VERIFY_RESULT_OK,
+  AVB_SLOT_VERIFY_RESULT_ERROR_OOM,
+  AVB_SLOT_VERIFY_RESULT_ERROR_IO,
+  AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION,
+  AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX,
+  AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED,
+  AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA,
+  AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION,
+  AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT
+} AvbSlotVerifyResult;
+
+/* Various error handling modes for when verification fails using a
+ * hashtree at runtime inside the HLOS.
+ *
+ * AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE means that the OS
+ * will invalidate the current slot and restart.
+ *
+ * AVB_HASHTREE_ERROR_MODE_RESTART means that the OS will restart.
+ *
+ * AVB_HASHTREE_ERROR_MODE_EIO means that an EIO error will be
+ * returned to applications.
+ *
+ * AVB_HASHTREE_ERROR_MODE_LOGGING means that errors will be logged
+ * and corrupt data may be returned to applications. This mode should
+ * be used ONLY for diagnostics and debugging. It cannot be used
+ * unless AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is also
+ * used.
+ */
+typedef enum {
+  AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
+  AVB_HASHTREE_ERROR_MODE_RESTART,
+  AVB_HASHTREE_ERROR_MODE_EIO,
+  AVB_HASHTREE_ERROR_MODE_LOGGING
+} AvbHashtreeErrorMode;
+
+/* Flags that influence how avb_slot_verify() works.
+ *
+ * If AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is NOT set then
+ * avb_slot_verify() will bail out as soon as an error is encountered
+ * and |out_data| is set only if AVB_SLOT_VERIFY_RESULT_OK is
+ * returned.
+ *
+ * Otherwise if AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is set
+ * avb_slot_verify() will continue verification efforts and |out_data|
+ * is also set if AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED,
+ * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION, or
+ * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX is returned. It is
+ * undefined which error is returned if more than one distinct error
+ * is encountered. It is guaranteed that AVB_SLOT_VERIFY_RESULT_OK is
+ * returned if, and only if, there are no errors. This mode is needed
+ * to boot valid but unverified slots when the device is unlocked.
+ *
+ * Also, if AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is set the
+ * contents loaded from |requested_partition| will be the contents of
+ * the entire partition instead of just the size specified in the hash
+ * descriptor.
+ */
+typedef enum {
+  AVB_SLOT_VERIFY_FLAGS_NONE = 0,
+  AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR = (1 << 0)
+} AvbSlotVerifyFlags;
+
+/* Get a textual representation of |result|. */
+const char* avb_slot_verify_result_to_string(AvbSlotVerifyResult result);
+
+/* Maximum number of rollback index locations supported. */
+#define AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS 32
+
+/* AvbPartitionData contains data loaded from partitions when using
+ * avb_slot_verify(). The |partition_name| field contains the name of
+ * the partition (without A/B suffix), |data| points to the loaded
+ * data which is |data_size| bytes long. If |preloaded| is set to true,
+ * this structure dose not own |data|. The caller of |avb_slot_verify|
+ * needs to make sure that the preloaded data outlives this
+ * |AvbPartitionData| structure.
+ *
+ * Note that this is strictly less than the partition size - it's only
+ * the image stored there, not the entire partition nor any of the
+ * metadata.
+ */
+typedef struct {
+  char* partition_name;
+  uint8_t* data;
+  size_t data_size;
+  bool preloaded;
+} AvbPartitionData;
+
+/* AvbVBMetaData contains a vbmeta struct loaded from a partition when
+ * using avb_slot_verify(). The |partition_name| field contains the
+ * name of the partition (without A/B suffix), |vbmeta_data| points to
+ * the loaded data which is |vbmeta_size| bytes long.
+ *
+ * The |verify_result| field contains the result of
+ * avb_vbmeta_image_verify() on the data. This is guaranteed to be
+ * AVB_VBMETA_VERIFY_RESULT_OK for all vbmeta images if
+ * avb_slot_verify() returns AVB_SLOT_VERIFY_RESULT_OK.
+ *
+ * You can use avb_descriptor_get_all(), avb_descriptor_foreach(), and
+ * avb_vbmeta_image_header_to_host_byte_order() with this data.
+ */
+typedef struct {
+  char* partition_name;
+  uint8_t* vbmeta_data;
+  size_t vbmeta_size;
+  AvbVBMetaVerifyResult verify_result;
+} AvbVBMetaData;
+
+/* AvbSlotVerifyData contains data needed to boot a particular slot
+ * and is returned by avb_slot_verify() if partitions in a slot are
+ * successfully verified.
+ *
+ * All data pointed to by this struct - including data in each item in
+ * the |partitions| array - will be freed when the
+ * avb_slot_verify_data_free() function is called.
+ *
+ * The |ab_suffix| field is the copy of the of |ab_suffix| field
+ * passed to avb_slot_verify(). It is the A/B suffix of the slot. This
+ * value includes the leading underscore - typical values are "" (if
+ * no slots are in use), "_a" (for the first slot), and "_b" (for the
+ * second slot).
+ *
+ * The VBMeta images that were checked are available in the
+ * |vbmeta_images| field. The field |num_vbmeta_images| contains the
+ * number of elements in this array. The first element -
+ * vbmeta_images[0] - is guaranteed to be from the partition with the
+ * top-level vbmeta struct. This is usually the "vbmeta" partition in
+ * the requested slot but if there is no "vbmeta" partition it can
+ * also be the "boot" partition.
+ *
+ * The partitions loaded and verified from from the slot are
+ * accessible in the |loaded_partitions| array. The field
+ * |num_loaded_partitions| contains the number of elements in this
+ * array. The order of partitions in this array may not necessarily be
+ * the same order as in the passed-in |requested_partitions| array.
+ *
+ * Rollback indexes for the verified slot are stored in the
+ * |rollback_indexes| field. Note that avb_slot_verify() will NEVER
+ * modify stored_rollback_index[n] locations e.g. it will never use
+ * the write_rollback_index() AvbOps operation. Instead it is the job
+ * of the caller of avb_slot_verify() to do this based on e.g. A/B
+ * policy and other factors. See libavb_ab/avb_ab_flow.c for an
+ * example of how to do this.
+ *
+ * The |cmdline| field is a NUL-terminated string in UTF-8 resulting
+ * from concatenating all |AvbKernelCmdlineDescriptor| and then
+ * performing proper substitution of the variables
+ * $(ANDROID_SYSTEM_PARTUUID), $(ANDROID_BOOT_PARTUUID), and
+ * $(ANDROID_VBMETA_PARTUUID) using the
+ * get_unique_guid_for_partition() operation in |AvbOps|. Additionally
+ * $(ANDROID_VERITY_MODE) will be replaced with the proper dm-verity
+ * option depending on the value of |hashtree_error_mode|.
+ *
+ * Additionally, the |cmdline| field will have the following kernel
+ * command-line options set (unless verification is disabled, see
+ * below):
+ *
+ *   androidboot.veritymode: This is set to 'disabled' if the
+ *   AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED flag is set in top-level
+ *   vbmeta struct. Otherwise it is set to 'enforcing' if the
+ *   passed-in hashtree error mode is AVB_HASHTREE_ERROR_MODE_RESTART
+ *   or AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, 'eio' if it's
+ *   set to AVB_HASHTREE_ERROR_MODE_EIO, and 'logging' if it's set to
+ *   AVB_HASHTREE_ERROR_MODE_LOGGING.
+ *
+ *   androidboot.vbmeta.invalidate_on_error: This is set to 'yes' only
+ *   if hashtree validation isn't disabled and the passed-in hashtree
+ *   error mode is AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE.
+ *
+ *   androidboot.vbmeta.device_state: set to "locked" or "unlocked"
+ *   depending on the result of the result of AvbOps's
+ *   read_is_unlocked() function.
+ *
+ *   androidboot.vbmeta.{hash_alg, size, digest}: Will be set to
+ *   the digest of all images in |vbmeta_images|.
+ *
+ *   androidboot.vbmeta.device: This is set to the value
+ *   PARTUUID=$(ANDROID_VBMETA_PARTUUID) before substitution so it
+ *   will end up pointing to the vbmeta partition for the verified
+ *   slot. If there is no vbmeta partition it will point to the boot
+ *   partition of the verified slot.
+ *
+ *   androidboot.vbmeta.avb_version: This is set to the decimal value
+ *   of AVB_VERSION_MAJOR followed by a dot followed by the decimal
+ *   value of AVB_VERSION_MINOR, for example "1.0" or "1.4". This
+ *   version number represents the vbmeta file format version
+ *   supported by libavb copy used in the boot loader. This is not
+ *   necessarily the same version number of the on-disk metadata for
+ *   the slot that was verified.
+ *
+ * Note that androidboot.slot_suffix is not set in the |cmdline| field
+ * in |AvbSlotVerifyData| - you will have to set this yourself.
+ *
+ * If the |AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED| flag is set
+ * in the top-level vbmeta struct then only the top-level vbmeta
+ * struct is verified and descriptors will not processed. The return
+ * value will be set accordingly (if this flag is set via 'avbctl
+ * disable-verification' then the return value will be
+ * |AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION|) and
+ * |AvbSlotVerifyData| is returned. Additionally all partitions in the
+ * |requested_partitions| are loaded and the |cmdline| field is set to
+ * "root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)" and the GUID for the
+ * appropriate system partition is substituted in. Note that none of
+ * the androidboot.* options mentioned above will be set.
+ *
+ * This struct may grow in the future without it being considered an
+ * ABI break.
+ */
+typedef struct {
+  char* ab_suffix;
+  AvbVBMetaData* vbmeta_images;
+  size_t num_vbmeta_images;
+  AvbPartitionData* loaded_partitions;
+  size_t num_loaded_partitions;
+  char* cmdline;
+  uint64_t rollback_indexes[AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS];
+} AvbSlotVerifyData;
+
+/* Calculates a digest of all vbmeta images in |data| using
+ * the digest indicated by |digest_type|. Stores the result
+ * in |out_digest| which must be large enough to hold a digest
+ * of the requested type.
+ */
+void avb_slot_verify_data_calculate_vbmeta_digest(AvbSlotVerifyData* data,
+                                                  AvbDigestType digest_type,
+                                                  uint8_t* out_digest);
+
+/* Frees a |AvbSlotVerifyData| including all data it points to. */
+void avb_slot_verify_data_free(AvbSlotVerifyData* data);
+
+/* Performs a full verification of the slot identified by |ab_suffix|
+ * and load and verify the contents of the partitions whose name is in
+ * the NULL-terminated string array |requested_partitions| (each
+ * partition must use hash verification). If not using A/B, pass an
+ * empty string (e.g. "", not NULL) for |ab_suffix|. This parameter
+ * must include the leading underscore, for example "_a" should be
+ * used to refer to the first slot.
+ *
+ * Typically the |requested_partitions| array only contains a single
+ * item for the boot partition, 'boot'.
+ *
+ * Verification includes loading and verifying data from the 'vbmeta',
+ * the requested hash partitions, and possibly other partitions (with
+ * |ab_suffix| appended), inspecting rollback indexes, and checking if
+ * the public key used to sign the data is acceptable. The functions
+ * in |ops| will be used to do this.
+ *
+ * If |out_data| is not NULL, it will be set to a newly allocated
+ * |AvbSlotVerifyData| struct containing all the data needed to
+ * actually boot the slot. This data structure should be freed with
+ * avb_slot_verify_data_free() when you are done with it. See below
+ * for when this is returned.
+ *
+ * The |flags| parameter is used to influence the semantics of
+ * avb_slot_verify() - for example the
+ * AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR flag can be used to
+ * ignore verification errors which is something needed in the
+ * UNLOCKED state. See the AvbSlotVerifyFlags enumeration for details.
+ *
+ * The |hashtree_error_mode| parameter should be set to the desired
+ * error handling mode when hashtree validation fails inside the
+ * HLOS. This value isn't used by libavb per se - it is forwarded to
+ * the HLOS through the androidboot.veritymode and
+ * androidboot.vbmeta.invalidate_on_error cmdline parameters. See the
+ * AvbHashtreeErrorMode enumeration for details.
+ *
+ * Also note that |out_data| is never set if
+ * AVB_SLOT_VERIFY_RESULT_ERROR_OOM, AVB_SLOT_VERIFY_RESULT_ERROR_IO,
+ * or AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA is returned.
+ *
+ * AVB_SLOT_VERIFY_RESULT_OK is returned if everything is verified
+ * correctly and all public keys are accepted.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED is returned if
+ * everything is verified correctly out but one or more public keys
+ * are not accepted. This includes the case where integrity data is
+ * not signed.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_OOM is returned if unable to
+ * allocate memory.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_IO is returned if an I/O error
+ * occurred while trying to load data or get a rollback index.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION is returned if the data
+ * did not verify, e.g. the digest didn't match or signature checks
+ * failed.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX is returned if a
+ * rollback index was less than its stored value.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA is returned if some
+ * of the metadata is invalid or inconsistent.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION is returned if
+ * some of the metadata requires a newer version of libavb than what
+ * is in use.
+ *
+ * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT is returned if the
+ * caller passed invalid parameters, for example trying to use
+ * AVB_HASHTREE_ERROR_MODE_LOGGING without
+ * AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR.
+ */
+AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
+                                    const char* const* requested_partitions,
+                                    const char* ab_suffix,
+                                    AvbSlotVerifyFlags flags,
+                                    AvbHashtreeErrorMode hashtree_error_mode,
+                                    AvbSlotVerifyData** out_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_SLOT_VERIFY_H_ */
diff --git a/lib/libavb/avb_sysdeps.h b/lib/libavb/avb_sysdeps.h
new file mode 100644
index 0000000..345c2b2
--- /dev/null
+++ b/lib/libavb/avb_sysdeps.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_SYSDEPS_H_
+#define AVB_SYSDEPS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Change these includes to match your platform to bring in the
+ * equivalent types available in a normal C runtime. At least things
+ * like uint8_t, uint64_t, and bool (with |false|, |true| keywords)
+ * must be present.
+ */
+#include <common.h>
+
+/* If you don't have gcc or clang, these attribute macros may need to
+ * be adjusted.
+ */
+#define AVB_ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#define AVB_ATTR_PACKED __attribute__((packed))
+#define AVB_ATTR_NO_RETURN __attribute__((noreturn))
+#define AVB_ATTR_SENTINEL __attribute__((__sentinel__))
+
+/* Size in bytes used for alignment. */
+#ifdef __LP64__
+#define AVB_ALIGNMENT_SIZE 8
+#else
+#define AVB_ALIGNMENT_SIZE 4
+#endif
+
+/* Compare |n| bytes in |src1| and |src2|.
+ *
+ * Returns an integer less than, equal to, or greater than zero if the
+ * first |n| bytes of |src1| is found, respectively, to be less than,
+ * to match, or be greater than the first |n| bytes of |src2|. */
+int avb_memcmp(const void* src1,
+               const void* src2,
+               size_t n) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Compare two strings.
+ *
+ * Return an integer less than, equal to, or greater than zero if |s1|
+ * is found, respectively, to be less than, to match, or be greater
+ * than |s2|.
+ */
+int avb_strcmp(const char* s1, const char* s2);
+
+/* Copy |n| bytes from |src| to |dest|. */
+void* avb_memcpy(void* dest, const void* src, size_t n);
+
+/* Set |n| bytes starting at |s| to |c|.  Returns |dest|. */
+void* avb_memset(void* dest, const int c, size_t n);
+
+/* Prints out a message. The string passed must be a NUL-terminated
+ * UTF-8 string.
+ */
+void avb_print(const char* message);
+
+/* Prints out a vector of strings. Each argument must point to a
+ * NUL-terminated UTF-8 string and NULL should be the last argument.
+ */
+void avb_printv(const char* message, ...) AVB_ATTR_SENTINEL;
+
+/* Aborts the program or reboots the device. */
+void avb_abort(void) AVB_ATTR_NO_RETURN;
+
+/* Allocates |size| bytes. Returns NULL if no memory is available,
+ * otherwise a pointer to the allocated memory.
+ *
+ * The memory is not initialized.
+ *
+ * The pointer returned is guaranteed to be word-aligned.
+ *
+ * The memory should be freed with avb_free() when you are done with it.
+ */
+void* avb_malloc_(size_t size) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Frees memory previously allocated with avb_malloc(). */
+void avb_free(void* ptr);
+
+/* Returns the lenght of |str|, excluding the terminating NUL-byte. */
+size_t avb_strlen(const char* str) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Divide the |dividend| by 10 and saves back to the pointer. Return the
+ * remainder. */
+uint32_t avb_div_by_10(uint64_t* dividend);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_SYSDEPS_H_ */
diff --git a/lib/libavb/avb_sysdeps_posix.c b/lib/libavb/avb_sysdeps_posix.c
new file mode 100644
index 0000000..0f20847
--- /dev/null
+++ b/lib/libavb/avb_sysdeps_posix.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "avb_sysdeps.h"
+
+int avb_memcmp(const void* src1, const void* src2, size_t n) {
+  return memcmp(src1, src2, n);
+}
+
+void* avb_memcpy(void* dest, const void* src, size_t n) {
+  return memcpy(dest, src, n);
+}
+
+void* avb_memset(void* dest, const int c, size_t n) {
+  return memset(dest, c, n);
+}
+
+int avb_strcmp(const char* s1, const char* s2) {
+  return strcmp(s1, s2);
+}
+
+size_t avb_strlen(const char* str) {
+  return strlen(str);
+}
+
+uint32_t avb_div_by_10(uint64_t* dividend) {
+  uint32_t rem = (uint32_t)(*dividend % 10);
+  *dividend /= 10;
+  return rem;
+}
+
+void avb_abort(void) {
+  hang();
+}
+
+void avb_print(const char* message) {
+  printf("%s", message);
+}
+
+void avb_printv(const char* message, ...) {
+  va_list ap;
+  const char* m;
+
+  va_start(ap, message);
+  for (m = message; m != NULL; m = va_arg(ap, const char*)) {
+    printf("%s", m);
+  }
+  va_end(ap);
+}
+
+void* avb_malloc_(size_t size) {
+  return malloc(size);
+}
+
+void avb_free(void* ptr) {
+  free(ptr);
+}
diff --git a/lib/libavb/avb_util.c b/lib/libavb/avb_util.c
new file mode 100644
index 0000000..169f83d
--- /dev/null
+++ b/lib/libavb/avb_util.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#include "avb_util.h"
+
+#include <stdarg.h>
+
+uint32_t avb_be32toh(uint32_t in) {
+  uint8_t* d = (uint8_t*)∈
+  uint32_t ret;
+  ret = ((uint32_t)d[0]) << 24;
+  ret |= ((uint32_t)d[1]) << 16;
+  ret |= ((uint32_t)d[2]) << 8;
+  ret |= ((uint32_t)d[3]);
+  return ret;
+}
+
+uint64_t avb_be64toh(uint64_t in) {
+  uint8_t* d = (uint8_t*)∈
+  uint64_t ret;
+  ret = ((uint64_t)d[0]) << 56;
+  ret |= ((uint64_t)d[1]) << 48;
+  ret |= ((uint64_t)d[2]) << 40;
+  ret |= ((uint64_t)d[3]) << 32;
+  ret |= ((uint64_t)d[4]) << 24;
+  ret |= ((uint64_t)d[5]) << 16;
+  ret |= ((uint64_t)d[6]) << 8;
+  ret |= ((uint64_t)d[7]);
+  return ret;
+}
+
+/* Converts a 32-bit unsigned integer from host to big-endian byte order. */
+uint32_t avb_htobe32(uint32_t in) {
+  union {
+    uint32_t word;
+    uint8_t bytes[4];
+  } ret;
+  ret.bytes[0] = (in >> 24) & 0xff;
+  ret.bytes[1] = (in >> 16) & 0xff;
+  ret.bytes[2] = (in >> 8) & 0xff;
+  ret.bytes[3] = in & 0xff;
+  return ret.word;
+}
+
+/* Converts a 64-bit unsigned integer from host to big-endian byte order. */
+uint64_t avb_htobe64(uint64_t in) {
+  union {
+    uint64_t word;
+    uint8_t bytes[8];
+  } ret;
+  ret.bytes[0] = (in >> 56) & 0xff;
+  ret.bytes[1] = (in >> 48) & 0xff;
+  ret.bytes[2] = (in >> 40) & 0xff;
+  ret.bytes[3] = (in >> 32) & 0xff;
+  ret.bytes[4] = (in >> 24) & 0xff;
+  ret.bytes[5] = (in >> 16) & 0xff;
+  ret.bytes[6] = (in >> 8) & 0xff;
+  ret.bytes[7] = in & 0xff;
+  return ret.word;
+}
+
+int avb_safe_memcmp(const void* s1, const void* s2, size_t n) {
+  const unsigned char* us1 = s1;
+  const unsigned char* us2 = s2;
+  int result = 0;
+
+  if (0 == n) {
+    return 0;
+  }
+
+  /*
+   * Code snippet without data-dependent branch due to Nate Lawson
+   * (nate at root.org) of Root Labs.
+   */
+  while (n--) {
+    result |= *us1++ ^ *us2++;
+  }
+
+  return result != 0;
+}
+
+bool avb_safe_add_to(uint64_t* value, uint64_t value_to_add) {
+  uint64_t original_value;
+
+  avb_assert(value != NULL);
+
+  original_value = *value;
+
+  *value += value_to_add;
+  if (*value < original_value) {
+    avb_error("Overflow when adding values.\n");
+    return false;
+  }
+
+  return true;
+}
+
+bool avb_safe_add(uint64_t* out_result, uint64_t a, uint64_t b) {
+  uint64_t dummy;
+  if (out_result == NULL) {
+    out_result = &dummy;
+  }
+  *out_result = a;
+  return avb_safe_add_to(out_result, b);
+}
+
+bool avb_validate_utf8(const uint8_t* data, size_t num_bytes) {
+  size_t n;
+  unsigned int num_cc;
+
+  for (n = 0, num_cc = 0; n < num_bytes; n++) {
+    uint8_t c = data[n];
+
+    if (num_cc > 0) {
+      if ((c & (0x80 | 0x40)) == 0x80) {
+        /* 10xx xxxx */
+      } else {
+        goto fail;
+      }
+      num_cc--;
+    } else {
+      if (c < 0x80) {
+        num_cc = 0;
+      } else if ((c & (0x80 | 0x40 | 0x20)) == (0x80 | 0x40)) {
+        /* 110x xxxx */
+        num_cc = 1;
+      } else if ((c & (0x80 | 0x40 | 0x20 | 0x10)) == (0x80 | 0x40 | 0x20)) {
+        /* 1110 xxxx */
+        num_cc = 2;
+      } else if ((c & (0x80 | 0x40 | 0x20 | 0x10 | 0x08)) ==
+                 (0x80 | 0x40 | 0x20 | 0x10)) {
+        /* 1111 0xxx */
+        num_cc = 3;
+      } else {
+        goto fail;
+      }
+    }
+  }
+
+  if (num_cc != 0) {
+    goto fail;
+  }
+
+  return true;
+
+fail:
+  return false;
+}
+
+bool avb_str_concat(char* buf,
+                    size_t buf_size,
+                    const char* str1,
+                    size_t str1_len,
+                    const char* str2,
+                    size_t str2_len) {
+  uint64_t combined_len;
+
+  if (!avb_safe_add(&combined_len, str1_len, str2_len)) {
+    avb_error("Overflow when adding string sizes.\n");
+    return false;
+  }
+
+  if (combined_len > buf_size - 1) {
+    avb_error("Insufficient buffer space.\n");
+    return false;
+  }
+
+  avb_memcpy(buf, str1, str1_len);
+  avb_memcpy(buf + str1_len, str2, str2_len);
+  buf[combined_len] = '\0';
+
+  return true;
+}
+
+void* avb_malloc(size_t size) {
+  void* ret = avb_malloc_(size);
+  if (ret == NULL) {
+    avb_error("Failed to allocate memory.\n");
+    return NULL;
+  }
+  return ret;
+}
+
+void* avb_calloc(size_t size) {
+  void* ret = avb_malloc(size);
+  if (ret == NULL) {
+    return NULL;
+  }
+
+  avb_memset(ret, '\0', size);
+  return ret;
+}
+
+char* avb_strdup(const char* str) {
+  size_t len = avb_strlen(str);
+  char* ret = avb_malloc(len + 1);
+  if (ret == NULL) {
+    return NULL;
+  }
+
+  avb_memcpy(ret, str, len);
+  ret[len] = '\0';
+
+  return ret;
+}
+
+const char* avb_strstr(const char* haystack, const char* needle) {
+  size_t n, m;
+
+  /* Look through |haystack| and check if the first character of
+   * |needle| matches. If so, check the rest of |needle|.
+   */
+  for (n = 0; haystack[n] != '\0'; n++) {
+    if (haystack[n] != needle[0]) {
+      continue;
+    }
+
+    for (m = 1;; m++) {
+      if (needle[m] == '\0') {
+        return haystack + n;
+      }
+
+      if (haystack[n + m] != needle[m]) {
+        break;
+      }
+    }
+  }
+
+  return NULL;
+}
+
+const char* avb_strv_find_str(const char* const* strings,
+                              const char* str,
+                              size_t str_size) {
+  size_t n;
+  for (n = 0; strings[n] != NULL; n++) {
+    if (avb_strlen(strings[n]) == str_size &&
+        avb_memcmp(strings[n], str, str_size) == 0) {
+      return strings[n];
+    }
+  }
+  return NULL;
+}
+
+char* avb_replace(const char* str, const char* search, const char* replace) {
+  char* ret = NULL;
+  size_t ret_len = 0;
+  size_t search_len, replace_len;
+  const char* str_after_last_replace;
+
+  search_len = avb_strlen(search);
+  replace_len = avb_strlen(replace);
+
+  str_after_last_replace = str;
+  while (*str != '\0') {
+    const char* s;
+    size_t num_before;
+    size_t num_new;
+
+    s = avb_strstr(str, search);
+    if (s == NULL) {
+      break;
+    }
+
+    num_before = s - str;
+
+    if (ret == NULL) {
+      num_new = num_before + replace_len + 1;
+      ret = avb_malloc(num_new);
+      if (ret == NULL) {
+        goto out;
+      }
+      avb_memcpy(ret, str, num_before);
+      avb_memcpy(ret + num_before, replace, replace_len);
+      ret[num_new - 1] = '\0';
+      ret_len = num_new - 1;
+    } else {
+      char* new_str;
+      num_new = ret_len + num_before + replace_len + 1;
+      new_str = avb_malloc(num_new);
+      if (new_str == NULL) {
+        goto out;
+      }
+      avb_memcpy(new_str, ret, ret_len);
+      avb_memcpy(new_str + ret_len, str, num_before);
+      avb_memcpy(new_str + ret_len + num_before, replace, replace_len);
+      new_str[num_new - 1] = '\0';
+      avb_free(ret);
+      ret = new_str;
+      ret_len = num_new - 1;
+    }
+
+    str = s + search_len;
+    str_after_last_replace = str;
+  }
+
+  if (ret == NULL) {
+    ret = avb_strdup(str_after_last_replace);
+    if (ret == NULL) {
+      goto out;
+    }
+  } else {
+    size_t num_remaining = avb_strlen(str_after_last_replace);
+    size_t num_new = ret_len + num_remaining + 1;
+    char* new_str = avb_malloc(num_new);
+    if (new_str == NULL) {
+      goto out;
+    }
+    avb_memcpy(new_str, ret, ret_len);
+    avb_memcpy(new_str + ret_len, str_after_last_replace, num_remaining);
+    new_str[num_new - 1] = '\0';
+    avb_free(ret);
+    ret = new_str;
+    ret_len = num_new - 1;
+  }
+
+out:
+  return ret;
+}
+
+/* We only support a limited amount of strings in avb_strdupv(). */
+#define AVB_STRDUPV_MAX_NUM_STRINGS 32
+
+char* avb_strdupv(const char* str, ...) {
+  va_list ap;
+  const char* strings[AVB_STRDUPV_MAX_NUM_STRINGS];
+  size_t lengths[AVB_STRDUPV_MAX_NUM_STRINGS];
+  size_t num_strings, n;
+  uint64_t total_length;
+  char *ret = NULL, *dest;
+
+  num_strings = 0;
+  total_length = 0;
+  va_start(ap, str);
+  do {
+    size_t str_len = avb_strlen(str);
+    strings[num_strings] = str;
+    lengths[num_strings] = str_len;
+    if (!avb_safe_add_to(&total_length, str_len)) {
+      avb_fatal("Overflow while determining total length.\n");
+      break;
+    }
+    num_strings++;
+    if (num_strings == AVB_STRDUPV_MAX_NUM_STRINGS) {
+      avb_fatal("Too many strings passed.\n");
+      break;
+    }
+    str = va_arg(ap, const char*);
+  } while (str != NULL);
+  va_end(ap);
+
+  ret = avb_malloc(total_length + 1);
+  if (ret == NULL) {
+    goto out;
+  }
+
+  dest = ret;
+  for (n = 0; n < num_strings; n++) {
+    avb_memcpy(dest, strings[n], lengths[n]);
+    dest += lengths[n];
+  }
+  *dest = '\0';
+  avb_assert(dest == ret + total_length);
+
+out:
+  return ret;
+}
+
+const char* avb_basename(const char* str) {
+  int64_t n;
+  size_t len;
+
+  len = avb_strlen(str);
+  if (len >= 2) {
+    for (n = len - 2; n >= 0; n--) {
+      if (str[n] == '/') {
+        return str + n + 1;
+      }
+    }
+  }
+  return str;
+}
+
+void avb_uppercase(char* str) {
+  size_t i;
+  for (i = 0; str[i] != '\0'; ++i) {
+    if (str[i] <= 0x7A && str[i] >= 0x61) {
+      str[i] -= 0x20;
+    }
+  }
+}
+
+char* avb_bin2hex(const uint8_t* data, size_t data_len) {
+  const char hex_digits[17] = "0123456789abcdef";
+  char* hex_data;
+  size_t n;
+
+  hex_data = avb_malloc(data_len * 2 + 1);
+  if (hex_data == NULL) {
+    return NULL;
+  }
+
+  for (n = 0; n < data_len; n++) {
+    hex_data[n * 2] = hex_digits[data[n] >> 4];
+    hex_data[n * 2 + 1] = hex_digits[data[n] & 0x0f];
+  }
+  hex_data[n * 2] = '\0';
+  return hex_data;
+}
diff --git a/lib/libavb/avb_util.h b/lib/libavb/avb_util.h
new file mode 100644
index 0000000..e566d77
--- /dev/null
+++ b/lib/libavb/avb_util.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_UTIL_H_
+#define AVB_UTIL_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define AVB_STRINGIFY(x) #x
+#define AVB_TO_STRING(x) AVB_STRINGIFY(x)
+
+#ifdef AVB_ENABLE_DEBUG
+/* Aborts the program if |expr| is false.
+ *
+ * This has no effect unless AVB_ENABLE_DEBUG is defined.
+ */
+#define avb_assert(expr)                     \
+  do {                                       \
+    if (!(expr)) {                           \
+      avb_fatal("assert fail: " #expr "\n"); \
+    }                                        \
+  } while (0)
+#else
+#define avb_assert(expr)
+#endif
+
+/* Aborts the program if reached.
+ *
+ * This has no effect unless AVB_ENABLE_DEBUG is defined.
+ */
+#ifdef AVB_ENABLE_DEBUG
+#define avb_assert_not_reached()         \
+  do {                                   \
+    avb_fatal("assert_not_reached()\n"); \
+  } while (0)
+#else
+#define avb_assert_not_reached()
+#endif
+
+/* Aborts the program if |addr| is not word-aligned.
+ *
+ * This has no effect unless AVB_ENABLE_DEBUG is defined.
+ */
+#define avb_assert_aligned(addr) \
+  avb_assert((((uintptr_t)addr) & (AVB_ALIGNMENT_SIZE - 1)) == 0)
+
+#ifdef AVB_ENABLE_DEBUG
+/* Print functions, used for diagnostics.
+ *
+ * These have no effect unless AVB_ENABLE_DEBUG is defined.
+ */
+#define avb_debug(message)              \
+  do {                                  \
+    avb_printv(avb_basename(__FILE__),  \
+               ":",                     \
+               AVB_TO_STRING(__LINE__), \
+               ": DEBUG: ",             \
+               message,                 \
+               NULL);                   \
+  } while (0)
+#define avb_debugv(message, ...)        \
+  do {                                  \
+    avb_printv(avb_basename(__FILE__),  \
+               ":",                     \
+               AVB_TO_STRING(__LINE__), \
+               ": DEBUG: ",             \
+               message,                 \
+               ##__VA_ARGS__);          \
+  } while (0)
+#else
+#define avb_debug(message)
+#define avb_debugv(message, ...)
+#endif
+
+/* Prints out a message. This is typically used if a runtime-error
+ * occurs.
+ */
+#define avb_error(message)              \
+  do {                                  \
+    avb_printv(avb_basename(__FILE__),  \
+               ":",                     \
+               AVB_TO_STRING(__LINE__), \
+               ": ERROR: ",             \
+               message,                 \
+               NULL);                   \
+  } while (0)
+#define avb_errorv(message, ...)        \
+  do {                                  \
+    avb_printv(avb_basename(__FILE__),  \
+               ":",                     \
+               AVB_TO_STRING(__LINE__), \
+               ": ERROR: ",             \
+               message,                 \
+               ##__VA_ARGS__);          \
+  } while (0)
+
+/* Prints out a message and calls avb_abort().
+ */
+#define avb_fatal(message)              \
+  do {                                  \
+    avb_printv(avb_basename(__FILE__),  \
+               ":",                     \
+               AVB_TO_STRING(__LINE__), \
+               ": FATAL: ",             \
+               message,                 \
+               NULL);                   \
+    avb_abort();                        \
+  } while (0)
+#define avb_fatalv(message, ...)        \
+  do {                                  \
+    avb_printv(avb_basename(__FILE__),  \
+               ":",                     \
+               AVB_TO_STRING(__LINE__), \
+               ": FATAL: ",             \
+               message,                 \
+               ##__VA_ARGS__);          \
+    avb_abort();                        \
+  } while (0)
+
+/* Converts a 32-bit unsigned integer from big-endian to host byte order. */
+uint32_t avb_be32toh(uint32_t in) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Converts a 64-bit unsigned integer from big-endian to host byte order. */
+uint64_t avb_be64toh(uint64_t in) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Converts a 32-bit unsigned integer from host to big-endian byte order. */
+uint32_t avb_htobe32(uint32_t in) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Converts a 64-bit unsigned integer from host to big-endian byte order. */
+uint64_t avb_htobe64(uint64_t in) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Compare |n| bytes starting at |s1| with |s2| and return 0 if they
+ * match, 1 if they don't.  Returns 0 if |n|==0, since no bytes
+ * mismatched.
+ *
+ * Time taken to perform the comparison is only dependent on |n| and
+ * not on the relationship of the match between |s1| and |s2|.
+ *
+ * Note that unlike avb_memcmp(), this only indicates inequality, not
+ * whether |s1| is less than or greater than |s2|.
+ */
+int avb_safe_memcmp(const void* s1,
+                    const void* s2,
+                    size_t n) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Adds |value_to_add| to |value| with overflow protection.
+ *
+ * Returns false if the addition overflows, true otherwise. In either
+ * case, |value| is always modified.
+ */
+bool avb_safe_add_to(uint64_t* value,
+                     uint64_t value_to_add) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Adds |a| and |b| with overflow protection, returning the value in
+ * |out_result|.
+ *
+ * It's permissible to pass NULL for |out_result| if you just want to
+ * check that the addition would not overflow.
+ *
+ * Returns false if the addition overflows, true otherwise.
+ */
+bool avb_safe_add(uint64_t* out_result,
+                  uint64_t a,
+                  uint64_t b) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Checks if |num_bytes| data at |data| is a valid UTF-8
+ * string. Returns true if valid UTF-8, false otherwise.
+ */
+bool avb_validate_utf8(const uint8_t* data,
+                       size_t num_bytes) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Concatenates |str1| (of |str1_len| bytes) and |str2| (of |str2_len|
+ * bytes) and puts the result in |buf| which holds |buf_size|
+ * bytes. The result is also guaranteed to be NUL terminated. Fail if
+ * there is not enough room in |buf| for the resulting string plus
+ * terminating NUL byte.
+ *
+ * Returns true if the operation succeeds, false otherwise.
+ */
+bool avb_str_concat(char* buf,
+                    size_t buf_size,
+                    const char* str1,
+                    size_t str1_len,
+                    const char* str2,
+                    size_t str2_len);
+
+/* Like avb_malloc_() but prints a error using avb_error() if memory
+ * allocation fails.
+ */
+void* avb_malloc(size_t size) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Like avb_malloc() but sets the memory with zeroes. */
+void* avb_calloc(size_t size) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Duplicates a NUL-terminated string. Returns NULL on OOM. */
+char* avb_strdup(const char* str) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Duplicates a NULL-terminated array of NUL-terminated strings by
+ * concatenating them. The returned string will be
+ * NUL-terminated. Returns NULL on OOM.
+ */
+char* avb_strdupv(const char* str,
+                  ...) AVB_ATTR_WARN_UNUSED_RESULT AVB_ATTR_SENTINEL;
+
+/* Finds the first occurrence of |needle| in the string |haystack|
+ * where both strings are NUL-terminated strings. The terminating NUL
+ * bytes are not compared.
+ *
+ * Returns NULL if not found, otherwise points into |haystack| for the
+ * first occurrence of |needle|.
+ */
+const char* avb_strstr(const char* haystack,
+                       const char* needle) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Finds the first occurrence of |str| in the NULL-terminated string
+ * array |strings|. Each element in |strings| must be
+ * NUL-terminated. The string given by |str| need not be
+ * NUL-terminated but its size must be given in |str_size|.
+ *
+ * Returns NULL if not found, otherwise points into |strings| for the
+ * first occurrence of |str|.
+ */
+const char* avb_strv_find_str(const char* const* strings,
+                              const char* str,
+                              size_t str_size);
+
+/* Replaces all occurrences of |search| with |replace| in |str|.
+ *
+ * Returns a newly allocated string or NULL if out of memory.
+ */
+char* avb_replace(const char* str,
+                  const char* search,
+                  const char* replace) AVB_ATTR_WARN_UNUSED_RESULT;
+
+/* Calculates the CRC-32 for data in |buf| of size |buf_size|. */
+uint32_t avb_crc32(const uint8_t* buf, size_t buf_size);
+
+/* Returns the basename of |str|. This is defined as the last path
+ * component, assuming the normal POSIX separator '/'. If there are no
+ * separators, returns |str|.
+ */
+const char* avb_basename(const char* str);
+
+/* Converts any ascii lowercase characters in |str| to uppercase in-place.
+ * |str| must be NUL-terminated and valid UTF-8.
+ */
+void avb_uppercase(char* str);
+
+/* Converts |data_len| bytes of |data| to hex and returns the result. Returns
+ * NULL on OOM. Caller must free the returned string with avb_free.
+ */
+char* avb_bin2hex(const uint8_t* data, size_t data_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_UTIL_H_ */
diff --git a/lib/libavb/avb_vbmeta_image.c b/lib/libavb/avb_vbmeta_image.c
new file mode 100644
index 0000000..2acca7d
--- /dev/null
+++ b/lib/libavb/avb_vbmeta_image.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#include "avb_vbmeta_image.h"
+#include "avb_crypto.h"
+#include "avb_rsa.h"
+#include "avb_sha.h"
+#include "avb_util.h"
+#include "avb_version.h"
+
+AvbVBMetaVerifyResult avb_vbmeta_image_verify(
+    const uint8_t* data,
+    size_t length,
+    const uint8_t** out_public_key_data,
+    size_t* out_public_key_length) {
+  AvbVBMetaVerifyResult ret;
+  AvbVBMetaImageHeader h;
+  uint8_t* computed_hash;
+  const AvbAlgorithmData* algorithm;
+  AvbSHA256Ctx sha256_ctx;
+  AvbSHA512Ctx sha512_ctx;
+  const uint8_t* header_block;
+  const uint8_t* authentication_block;
+  const uint8_t* auxiliary_block;
+  int verification_result;
+
+  ret = AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER;
+
+  if (out_public_key_data != NULL) {
+    *out_public_key_data = NULL;
+  }
+  if (out_public_key_length != NULL) {
+    *out_public_key_length = 0;
+  }
+
+  /* Ensure magic is correct. */
+  if (avb_safe_memcmp(data, AVB_MAGIC, AVB_MAGIC_LEN) != 0) {
+    avb_error("Magic is incorrect.\n");
+    goto out;
+  }
+
+  /* Before we byteswap, ensure length is long enough. */
+  if (length < sizeof(AvbVBMetaImageHeader)) {
+    avb_error("Length is smaller than header.\n");
+    goto out;
+  }
+  avb_vbmeta_image_header_to_host_byte_order((const AvbVBMetaImageHeader*)data,
+                                             &h);
+
+  /* Ensure we don't attempt to access any fields if we do not meet
+   * the specified minimum version of libavb.
+   */
+  if ((h.required_libavb_version_major != AVB_VERSION_MAJOR) ||
+      (h.required_libavb_version_minor > AVB_VERSION_MINOR)) {
+    avb_error("Mismatch between image version and libavb version.\n");
+    ret = AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION;
+    goto out;
+  }
+
+  /* Ensure |release_string| ends with a NUL byte. */
+  if (h.release_string[AVB_RELEASE_STRING_SIZE - 1] != '\0') {
+    avb_error("Release string does not end with a NUL byte.\n");
+    goto out;
+  }
+
+  /* Ensure inner block sizes are multiple of 64. */
+  if ((h.authentication_data_block_size & 0x3f) != 0 ||
+      (h.auxiliary_data_block_size & 0x3f) != 0) {
+    avb_error("Block size is not a multiple of 64.\n");
+    goto out;
+  }
+
+  /* Ensure block sizes all add up to at most |length|. */
+  uint64_t block_total = sizeof(AvbVBMetaImageHeader);
+  if (!avb_safe_add_to(&block_total, h.authentication_data_block_size) ||
+      !avb_safe_add_to(&block_total, h.auxiliary_data_block_size)) {
+    avb_error("Overflow while computing size of boot image.\n");
+    goto out;
+  }
+  if (block_total > length) {
+    avb_error("Block sizes add up to more than given length.\n");
+    goto out;
+  }
+
+  uintptr_t data_ptr = (uintptr_t)data;
+  /* Ensure passed in memory doesn't wrap. */
+  if (!avb_safe_add(NULL, (uint64_t)data_ptr, length)) {
+    avb_error("Boot image location and length mismatch.\n");
+    goto out;
+  }
+
+  /* Ensure hash and signature are entirely in the Authentication data block. */
+  uint64_t hash_end;
+  if (!avb_safe_add(&hash_end, h.hash_offset, h.hash_size) ||
+      hash_end > h.authentication_data_block_size) {
+    avb_error("Hash is not entirely in its block.\n");
+    goto out;
+  }
+  uint64_t signature_end;
+  if (!avb_safe_add(&signature_end, h.signature_offset, h.signature_size) ||
+      signature_end > h.authentication_data_block_size) {
+    avb_error("Signature is not entirely in its block.\n");
+    goto out;
+  }
+
+  /* Ensure public key is entirely in the Auxiliary data block. */
+  uint64_t pubkey_end;
+  if (!avb_safe_add(&pubkey_end, h.public_key_offset, h.public_key_size) ||
+      pubkey_end > h.auxiliary_data_block_size) {
+    avb_error("Public key is not entirely in its block.\n");
+    goto out;
+  }
+
+  /* Ensure public key metadata (if set) is entirely in the Auxiliary
+   * data block. */
+  if (h.public_key_metadata_size > 0) {
+    uint64_t pubkey_md_end;
+    if (!avb_safe_add(&pubkey_md_end,
+                      h.public_key_metadata_offset,
+                      h.public_key_metadata_size) ||
+        pubkey_md_end > h.auxiliary_data_block_size) {
+      avb_error("Public key metadata is not entirely in its block.\n");
+      goto out;
+    }
+  }
+
+  /* Bail early if there's no hash or signature. */
+  if (h.algorithm_type == AVB_ALGORITHM_TYPE_NONE) {
+    ret = AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED;
+    goto out;
+  }
+
+  /* Ensure algorithm field is supported. */
+  algorithm = avb_get_algorithm_data(h.algorithm_type);
+  if (!algorithm) {
+    avb_error("Invalid or unknown algorithm.\n");
+    goto out;
+  }
+
+  /* Bail if the embedded hash size doesn't match the chosen algorithm. */
+  if (h.hash_size != algorithm->hash_len) {
+    avb_error("Embedded hash has wrong size.\n");
+    goto out;
+  }
+
+  /* No overflow checks needed from here-on after since all block
+   * sizes and offsets have been verified above.
+   */
+
+  header_block = data;
+  authentication_block = header_block + sizeof(AvbVBMetaImageHeader);
+  auxiliary_block = authentication_block + h.authentication_data_block_size;
+
+  switch (h.algorithm_type) {
+    /* Explicit fall-through: */
+    case AVB_ALGORITHM_TYPE_SHA256_RSA2048:
+    case AVB_ALGORITHM_TYPE_SHA256_RSA4096:
+    case AVB_ALGORITHM_TYPE_SHA256_RSA8192:
+      avb_sha256_init(&sha256_ctx);
+      avb_sha256_update(
+          &sha256_ctx, header_block, sizeof(AvbVBMetaImageHeader));
+      avb_sha256_update(
+          &sha256_ctx, auxiliary_block, h.auxiliary_data_block_size);
+      computed_hash = avb_sha256_final(&sha256_ctx);
+      break;
+    /* Explicit fall-through: */
+    case AVB_ALGORITHM_TYPE_SHA512_RSA2048:
+    case AVB_ALGORITHM_TYPE_SHA512_RSA4096:
+    case AVB_ALGORITHM_TYPE_SHA512_RSA8192:
+      avb_sha512_init(&sha512_ctx);
+      avb_sha512_update(
+          &sha512_ctx, header_block, sizeof(AvbVBMetaImageHeader));
+      avb_sha512_update(
+          &sha512_ctx, auxiliary_block, h.auxiliary_data_block_size);
+      computed_hash = avb_sha512_final(&sha512_ctx);
+      break;
+    default:
+      avb_error("Unknown algorithm.\n");
+      goto out;
+  }
+
+  if (avb_safe_memcmp(authentication_block + h.hash_offset,
+                      computed_hash,
+                      h.hash_size) != 0) {
+    avb_error("Hash does not match!\n");
+    ret = AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH;
+    goto out;
+  }
+
+  verification_result =
+      avb_rsa_verify(auxiliary_block + h.public_key_offset,
+                     h.public_key_size,
+                     authentication_block + h.signature_offset,
+                     h.signature_size,
+                     authentication_block + h.hash_offset,
+                     h.hash_size,
+                     algorithm->padding,
+                     algorithm->padding_len);
+
+  if (verification_result == 0) {
+    ret = AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH;
+    goto out;
+  }
+
+  if (h.public_key_size > 0) {
+    if (out_public_key_data != NULL) {
+      *out_public_key_data = auxiliary_block + h.public_key_offset;
+    }
+    if (out_public_key_length != NULL) {
+      *out_public_key_length = h.public_key_size;
+    }
+  }
+
+  ret = AVB_VBMETA_VERIFY_RESULT_OK;
+
+out:
+  return ret;
+}
+
+void avb_vbmeta_image_header_to_host_byte_order(const AvbVBMetaImageHeader* src,
+                                                AvbVBMetaImageHeader* dest) {
+  avb_memcpy(dest, src, sizeof(AvbVBMetaImageHeader));
+
+  dest->required_libavb_version_major =
+      avb_be32toh(dest->required_libavb_version_major);
+  dest->required_libavb_version_minor =
+      avb_be32toh(dest->required_libavb_version_minor);
+
+  dest->authentication_data_block_size =
+      avb_be64toh(dest->authentication_data_block_size);
+  dest->auxiliary_data_block_size =
+      avb_be64toh(dest->auxiliary_data_block_size);
+
+  dest->algorithm_type = avb_be32toh(dest->algorithm_type);
+
+  dest->hash_offset = avb_be64toh(dest->hash_offset);
+  dest->hash_size = avb_be64toh(dest->hash_size);
+
+  dest->signature_offset = avb_be64toh(dest->signature_offset);
+  dest->signature_size = avb_be64toh(dest->signature_size);
+
+  dest->public_key_offset = avb_be64toh(dest->public_key_offset);
+  dest->public_key_size = avb_be64toh(dest->public_key_size);
+
+  dest->public_key_metadata_offset =
+      avb_be64toh(dest->public_key_metadata_offset);
+  dest->public_key_metadata_size = avb_be64toh(dest->public_key_metadata_size);
+
+  dest->descriptors_offset = avb_be64toh(dest->descriptors_offset);
+  dest->descriptors_size = avb_be64toh(dest->descriptors_size);
+
+  dest->rollback_index = avb_be64toh(dest->rollback_index);
+  dest->flags = avb_be32toh(dest->flags);
+}
+
+const char* avb_vbmeta_verify_result_to_string(AvbVBMetaVerifyResult result) {
+  const char* ret = NULL;
+
+  switch (result) {
+    case AVB_VBMETA_VERIFY_RESULT_OK:
+      ret = "OK";
+      break;
+    case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
+      ret = "OK_NOT_SIGNED";
+      break;
+    case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
+      ret = "INVALID_VBMETA_HEADER";
+      break;
+    case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:
+      ret = "UNSUPPORTED_VERSION";
+      break;
+    case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
+      ret = "HASH_MISMATCH";
+      break;
+    case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
+      ret = "SIGNATURE_MISMATCH";
+      break;
+      /* Do not add a 'default:' case here because of -Wswitch. */
+  }
+
+  if (ret == NULL) {
+    avb_error("Unknown AvbVBMetaVerifyResult value.\n");
+    ret = "(unknown)";
+  }
+
+  return ret;
+}
diff --git a/lib/libavb/avb_vbmeta_image.h b/lib/libavb/avb_vbmeta_image.h
new file mode 100644
index 0000000..85a3e1b
--- /dev/null
+++ b/lib/libavb/avb_vbmeta_image.h
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_VBMETA_IMAGE_H_
+#define AVB_VBMETA_IMAGE_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "avb_crypto.h"
+#include "avb_descriptor.h"
+
+/* Size of the vbmeta image header. */
+#define AVB_VBMETA_IMAGE_HEADER_SIZE 256
+
+/* Magic for the vbmeta image header. */
+#define AVB_MAGIC "AVB0"
+#define AVB_MAGIC_LEN 4
+
+/* Maximum size of the release string including the terminating NUL byte. */
+#define AVB_RELEASE_STRING_SIZE 48
+
+/* Flags for the vbmeta image.
+ *
+ * AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED: If this flag is set,
+ * hashtree image verification will be disabled.
+ *
+ * AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED: If this flag is set,
+ * verification will be disabled and descriptors will not be parsed.
+ */
+typedef enum {
+  AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = (1 << 0),
+  AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED = (1 << 1)
+} AvbVBMetaImageFlags;
+
+/* Binary format for header of the vbmeta image.
+ *
+ * The vbmeta image consists of three blocks:
+ *
+ *  +-----------------------------------------+
+ *  | Header data - fixed size                |
+ *  +-----------------------------------------+
+ *  | Authentication data - variable size     |
+ *  +-----------------------------------------+
+ *  | Auxiliary data - variable size          |
+ *  +-----------------------------------------+
+ *
+ * The "Header data" block is described by this struct and is always
+ * |AVB_VBMETA_IMAGE_HEADER_SIZE| bytes long.
+ *
+ * The "Authentication data" block is |authentication_data_block_size|
+ * bytes long and contains the hash and signature used to authenticate
+ * the vbmeta image. The type of the hash and signature is defined by
+ * the |algorithm_type| field.
+ *
+ * The "Auxiliary data" is |auxiliary_data_block_size| bytes long and
+ * contains the auxiliary data including the public key used to make
+ * the signature and descriptors.
+ *
+ * The public key is at offset |public_key_offset| with size
+ * |public_key_size| in this block. The size of the public key data is
+ * defined by the |algorithm_type| field. The format of the public key
+ * data is described in the |AvbRSAPublicKeyHeader| struct.
+ *
+ * The descriptors starts at |descriptors_offset| from the beginning
+ * of the "Auxiliary Data" block and take up |descriptors_size|
+ * bytes. Each descriptor is stored as a |AvbDescriptor| with tag and
+ * number of bytes following. The number of descriptors can be
+ * determined by walking this data until |descriptors_size| is
+ * exhausted.
+ *
+ * The size of each of the "Authentication data" and "Auxiliary data"
+ * blocks must be divisible by 64. This is to ensure proper alignment.
+ *
+ * Descriptors are free-form blocks stored in a part of the vbmeta
+ * image subject to the same integrity checks as the rest of the
+ * image. See the documentation for |AvbDescriptor| for well-known
+ * descriptors. See avb_descriptor_foreach() for a convenience
+ * function to iterate over descriptors.
+ *
+ * This struct is versioned, see the |required_libavb_version_major|
+ * and |required_libavb_version_minor| fields. This represents the
+ * minimum version of libavb required to verify the header and depends
+ * on the features (e.g. algorithms, descriptors) used. Note that this
+ * may be 1.0 even if generated by an avbtool from 1.4 but where no
+ * features introduced after 1.0 has been used. See the "Versioning
+ * and compatibility" section in the README.md file for more details.
+ *
+ * All fields are stored in network byte order when serialized. To
+ * generate a copy with fields swapped to native byte order, use the
+ * function avb_vbmeta_image_header_to_host_byte_order().
+ *
+ * Before reading and/or using any of this data, you MUST verify it
+ * using avb_vbmeta_image_verify() and reject it unless it's signed by
+ * a known good public key.
+ */
+typedef struct AvbVBMetaImageHeader {
+  /*   0: Four bytes equal to "AVB0" (AVB_MAGIC). */
+  uint8_t magic[AVB_MAGIC_LEN];
+
+  /*   4: The major version of libavb required for this header. */
+  uint32_t required_libavb_version_major;
+  /*   8: The minor version of libavb required for this header. */
+  uint32_t required_libavb_version_minor;
+
+  /*  12: The size of the signature block. */
+  uint64_t authentication_data_block_size;
+  /*  20: The size of the auxiliary data block. */
+  uint64_t auxiliary_data_block_size;
+
+  /*  28: The verification algorithm used, see |AvbAlgorithmType| enum. */
+  uint32_t algorithm_type;
+
+  /*  32: Offset into the "Authentication data" block of hash data. */
+  uint64_t hash_offset;
+  /*  40: Length of the hash data. */
+  uint64_t hash_size;
+
+  /*  48: Offset into the "Authentication data" block of signature data. */
+  uint64_t signature_offset;
+  /*  56: Length of the signature data. */
+  uint64_t signature_size;
+
+  /*  64: Offset into the "Auxiliary data" block of public key data. */
+  uint64_t public_key_offset;
+  /*  72: Length of the public key data. */
+  uint64_t public_key_size;
+
+  /*  80: Offset into the "Auxiliary data" block of public key metadata. */
+  uint64_t public_key_metadata_offset;
+  /*  88: Length of the public key metadata. Must be set to zero if there
+   *  is no public key metadata.
+   */
+  uint64_t public_key_metadata_size;
+
+  /*  96: Offset into the "Auxiliary data" block of descriptor data. */
+  uint64_t descriptors_offset;
+  /* 104: Length of descriptor data. */
+  uint64_t descriptors_size;
+
+  /* 112: The rollback index which can be used to prevent rollback to
+   *  older versions.
+   */
+  uint64_t rollback_index;
+
+  /* 120: Flags from the AvbVBMetaImageFlags enumeration. This must be
+   * set to zero if the vbmeta image is not a top-level image.
+   */
+  uint32_t flags;
+
+  /* 124: Reserved to ensure |release_string| start on a 16-byte
+   * boundary. Must be set to zeroes.
+   */
+  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
+   * terminated. Applications must not make assumptions about how this
+   * string is formatted.
+   */
+  uint8_t release_string[AVB_RELEASE_STRING_SIZE];
+
+  /* 176: Padding to ensure struct is size AVB_VBMETA_IMAGE_HEADER_SIZE
+   * bytes. This must be set to zeroes.
+   */
+  uint8_t reserved[80];
+} AVB_ATTR_PACKED AvbVBMetaImageHeader;
+
+/* Copies |src| to |dest|, byte-swapping fields in the process.
+ *
+ * Make sure you've verified |src| using avb_vbmeta_image_verify()
+ * before accessing the data and/or using this function.
+ */
+void avb_vbmeta_image_header_to_host_byte_order(const AvbVBMetaImageHeader* src,
+                                                AvbVBMetaImageHeader* dest);
+
+/* Return codes used in avb_vbmeta_image_verify().
+ *
+ * AVB_VBMETA_VERIFY_RESULT_OK is returned if the vbmeta image header
+ * is valid, the hash is correct and the signature is correct. Keep in
+ * mind that you still need to check that you know the public key used
+ * to sign the image, see avb_vbmeta_image_verify() for details.
+ *
+ * AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED is returned if the vbmeta
+ * image header is valid but there is no signature or hash.
+ *
+ * AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER is returned if the
+ * header of the vbmeta image is invalid, for example, invalid magic
+ * or inconsistent data.
+ *
+ * AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION is returned if a) the
+ * vbmeta image requires a minimum version of libavb which exceeds the
+ * version of libavb used; or b) the vbmeta image major version
+ * differs from the major version of libavb in use.
+ *
+ * AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH is returned if the hash
+ * stored in the "Authentication data" block does not match the
+ * calculated hash.
+ *
+ * AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH is returned if the
+ * signature stored in the "Authentication data" block is invalid or
+ * doesn't match the public key stored in the vbmeta image.
+ */
+typedef enum {
+  AVB_VBMETA_VERIFY_RESULT_OK,
+  AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED,
+  AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER,
+  AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION,
+  AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH,
+  AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH,
+} AvbVBMetaVerifyResult;
+
+/* Get a textual representation of |result|. */
+const char* avb_vbmeta_verify_result_to_string(AvbVBMetaVerifyResult result);
+
+/* Checks that vbmeta image at |data| of size |length| is a valid
+ * vbmeta image. The complete contents of the vbmeta image must be
+ * passed in. It's fine if |length| is bigger than the actual image,
+ * typically callers of this function will load the entire contents of
+ * the 'vbmeta_a' or 'vbmeta_b' partition and pass in its length (for
+ * example, 1 MiB).
+ *
+ * See the |AvbVBMetaImageHeader| struct for information about the
+ * three blocks (header, authentication, auxiliary) that make up a
+ * vbmeta image.
+ *
+ * If the function returns |AVB_VBMETA_VERIFY_RESULT_OK| and
+ * |out_public_key_data| is non-NULL, it will be set to point inside
+ * |data| for where the serialized public key data is stored and
+ * |out_public_key_length|, if non-NULL, will be set to the length of
+ * the public key data. If there is no public key in the metadata then
+ * |out_public_key_data| is set to NULL.
+ *
+ * See the |AvbVBMetaVerifyResult| enum for possible return values.
+ *
+ * VERY IMPORTANT:
+ *
+ *   1. Even if |AVB_VBMETA_VERIFY_RESULT_OK| is returned, you still
+ *      need to check that the public key embedded in the image
+ *      matches a known key! You can use 'avbtool extract_public_key'
+ *      to extract the key (at build time, then store it along your
+ *      code) and compare it to what is returned in
+ *      |out_public_key_data|.
+ *
+ *   2. You need to check the |rollback_index| field against a stored
+ *      value in NVRAM and reject the vbmeta image if the value in
+ *      NVRAM is bigger than |rollback_index|. You must also update
+ *      the value stored in NVRAM to the smallest value of
+ *      |rollback_index| field from boot images in all bootable and
+ *      authentic slots marked as GOOD.
+ *
+ * This is a low-level function to only verify the vbmeta data - you
+ * are likely looking for avb_slot_verify() instead for verifying
+ * integrity data for a whole set of partitions.
+ */
+AvbVBMetaVerifyResult avb_vbmeta_image_verify(
+    const uint8_t* data,
+    size_t length,
+    const uint8_t** out_public_key_data,
+    size_t* out_public_key_length) AVB_ATTR_WARN_UNUSED_RESULT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_VBMETA_IMAGE_H_ */
diff --git a/lib/libavb/avb_version.c b/lib/libavb/avb_version.c
new file mode 100644
index 0000000..92a2edf
--- /dev/null
+++ b/lib/libavb/avb_version.c
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#include "avb_version.h"
+
+#define AVB_QUOTE(str) #str
+#define AVB_EXPAND_AND_QUOTE(str) AVB_QUOTE(str)
+
+/* Keep in sync with get_release_string() in avbtool. */
+const char* avb_version_string(void) {
+  return AVB_EXPAND_AND_QUOTE(AVB_VERSION_MAJOR) "." AVB_EXPAND_AND_QUOTE(
+      AVB_VERSION_MINOR) "." AVB_EXPAND_AND_QUOTE(AVB_VERSION_SUB);
+}
diff --git a/lib/libavb/avb_version.h b/lib/libavb/avb_version.h
new file mode 100644
index 0000000..a36da62
--- /dev/null
+++ b/lib/libavb/avb_version.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_H) && !defined(AVB_COMPILATION)
+#error "Never include this file directly, include libavb.h instead."
+#endif
+
+#ifndef AVB_VERSION_H_
+#define AVB_VERSION_H_
+
+#include "avb_sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The version number of AVB - keep in sync with avbtool. */
+#define AVB_VERSION_MAJOR 1
+#define AVB_VERSION_MINOR 1
+#define AVB_VERSION_SUB 0
+
+/* Returns a NUL-terminated string for the libavb version in use.  The
+ * returned string usually looks like "%d.%d.%d". Applications must
+ * not make assumptions about the content of this string.
+ *
+ * Boot loaders should display this string in debug/diagnostics output
+ * to aid with debugging.
+ *
+ * This is similar to the string put in the |release_string| string
+ * field in the VBMeta struct by avbtool.
+ */
+const char* avb_version_string(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_VERSION_H_ */
diff --git a/lib/libavb/libavb.h b/lib/libavb/libavb.h
new file mode 100644
index 0000000..dab17ce
--- /dev/null
+++ b/lib/libavb/libavb.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier:	MIT
+ */
+
+#ifndef LIBAVB_H_
+#define LIBAVB_H_
+
+/* The AVB_INSIDE_LIBAVB_H preprocessor symbol is used to enforce
+ * library users to include only this file. All public interfaces, and
+ * only public interfaces, must be included here.
+ */
+
+#define AVB_INSIDE_LIBAVB_H
+#include "avb_chain_partition_descriptor.h"
+#include "avb_crypto.h"
+#include "avb_descriptor.h"
+#include "avb_footer.h"
+#include "avb_hash_descriptor.h"
+#include "avb_hashtree_descriptor.h"
+#include "avb_kernel_cmdline_descriptor.h"
+#include "avb_ops.h"
+#include "avb_property_descriptor.h"
+#include "avb_slot_verify.h"
+#include "avb_sysdeps.h"
+#include "avb_util.h"
+#include "avb_vbmeta_image.h"
+#include "avb_version.h"
+#undef AVB_INSIDE_LIBAVB_H
+
+#endif /* LIBAVB_H_ */
-- 
2.7.4



More information about the U-Boot mailing list