Make it possible to disable verification.

Introduce a new AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED flag
which can be set in the top-level vbmeta struct. Also add {get,
enable, disable}-verification sub-commands to avbctl to read/write
this flag.

This flag acts much in the same way as the HASTREE_DISABLED flag
insofar that it's usually unset on images so setting it on a slot
means that the slot will only work in UNLOCKED mode (because the
signature on the top-level vbmeta struct will no longer verify).

If this flag is set, then

 - only the top-level vbmeta struct is read; and

 - descriptors are not processed; and

 - all requested partitions are loaded without verification; and

 - the |cmdline| field in the returned AvbSlotVerifyData object
   will be set to "root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)"
   and the GUID substitutions are performed. No androidboot.*
   options are set in the returned command-line.

In effect this should allow booting the OS in the slot without any
modifications to the bootloader as if verified boot is not in use at
all. This is useful in some use-cases for example if using a
system.img image without any verification metadata at all.

Test: New unit tests + all unit tests pass.
Test: Manually tested on UEIF-based boot loader.
Bug: 62523303
Merged-In: Ib85539433c39691e407009dc8d66d94dc3ec5b09
Change-Id: Ib85539433c39691e407009dc8d66d94dc3ec5b09
(cherry picked from commit 0922bf8970fd2a61b9053a6fca81d8165cc0af67)
diff --git a/Android.bp b/Android.bp
index 3e14003..48480d1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -114,6 +114,7 @@
         "libavb_ab/avb_ab_flow.c",
         "libavb_user/avb_ops_user.c",
         "libavb_user/avb_user_verity.c",
+        "libavb_user/avb_user_verification.c",
     ],
 }
 
diff --git a/libavb/avb_slot_verify.c b/libavb/avb_slot_verify.c
index 7ba4075..f7b74a6 100644
--- a/libavb/avb_slot_verify.c
+++ b/libavb/avb_slot_verify.c
@@ -244,6 +244,99 @@
   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;
+  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[PART_NAME_MAX_SIZE];
+    AvbIOResult io_ret;
+    uint64_t image_size;
+    size_t part_num_read;
+    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);
+
+    image_buf = avb_malloc(image_size);
+    if (image_buf == NULL) {
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+      goto out;
+    }
+
+    io_ret = ops->read_from_partition(
+        ops, part_name, 0 /* offset */, image_size, image_buf, &part_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(part_name, ": Error loading data from partition.\n", NULL);
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+      goto out;
+    }
+    if (part_num_read != image_size) {
+      avb_errorv(part_name, ": Read fewer than requested bytes.\n", NULL);
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+      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;
+    image_buf = NULL;
+  }
+
+  ret = AVB_SLOT_VERIFY_RESULT_OK;
+
+out:
+  if (image_buf != NULL) {
+    avb_free(image_buf);
+  }
+  return ret;
+}
+
 static AvbSlotVerifyResult load_and_verify_vbmeta(
     AvbOps* ops,
     const char* const* requested_partitions,
@@ -561,6 +654,27 @@
       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
@@ -954,6 +1068,168 @@
   return ret;
 }
 
+static AvbSlotVerifyResult 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: {
+      AvbSHA256Ctx ctx;
+      size_t n, total_size = 0;
+      avb_sha256_init(&ctx);
+      for (n = 0; n < slot_data->num_vbmeta_images; n++) {
+        avb_sha256_update(&ctx,
+                          slot_data->vbmeta_images[n].vbmeta_data,
+                          slot_data->vbmeta_images[n].vbmeta_size);
+        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",
+                              avb_sha256_final(&ctx),
+                              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: {
+      AvbSHA512Ctx ctx;
+      size_t n, total_size = 0;
+      avb_sha512_init(&ctx);
+      for (n = 0; n < slot_data->num_vbmeta_images; n++) {
+        avb_sha512_update(&ctx,
+                          slot_data->vbmeta_images[n].vbmeta_data,
+                          slot_data->vbmeta_images[n].vbmeta_size);
+        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",
+                              avb_sha512_final(&ctx),
+                              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;
+}
+
 AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
                                     const char* const* requested_partitions,
                                     const char* ab_suffix,
@@ -963,10 +1239,8 @@
   AvbSlotVerifyResult ret;
   AvbSlotVerifyData* slot_data = NULL;
   AvbAlgorithmType algorithm_type = AVB_ALGORITHM_TYPE_NONE;
-  AvbIOResult io_ret;
   bool using_boot_for_vbmeta = false;
   AvbVBMetaImageHeader toplevel_vbmeta;
-  const char* verity_mode;
   bool allow_verification_error =
       (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR);
 
@@ -1050,21 +1324,35 @@
       goto fail;
     }
 
-    /* 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 fail;
-    }
-
-    /* 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 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 append_options() is either an
+       * I/O or OOM error.
+       */
+      AvbSlotVerifyResult sub_ret = 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. */
@@ -1080,133 +1368,6 @@
       slot_data->cmdline = new_cmdline;
     }
 
-    /* Set androidboot.avb.device_state to "locked" or "unlocked". */
-    bool is_device_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 fail;
-    } else if (io_ret != AVB_IO_RESULT_OK) {
-      avb_error("Error getting device state.\n");
-      ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
-      goto fail;
-    }
-    if (!cmdline_append_option(slot_data,
-                               "androidboot.vbmeta.device_state",
-                               is_device_unlocked ? "unlocked" : "locked")) {
-      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
-      goto fail;
-    }
-
-    /* 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: {
-        AvbSHA256Ctx ctx;
-        size_t n, total_size = 0;
-        avb_sha256_init(&ctx);
-        for (n = 0; n < slot_data->num_vbmeta_images; n++) {
-          avb_sha256_update(&ctx,
-                            slot_data->vbmeta_images[n].vbmeta_data,
-                            slot_data->vbmeta_images[n].vbmeta_size);
-          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",
-                                avb_sha256_final(&ctx),
-                                AVB_SHA256_DIGEST_SIZE)) {
-          ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
-          goto fail;
-        }
-      } break;
-      /* Explicit fallthrough. */
-      case AVB_ALGORITHM_TYPE_SHA512_RSA2048:
-      case AVB_ALGORITHM_TYPE_SHA512_RSA4096:
-      case AVB_ALGORITHM_TYPE_SHA512_RSA8192: {
-        AvbSHA512Ctx ctx;
-        size_t n, total_size = 0;
-        avb_sha512_init(&ctx);
-        for (n = 0; n < slot_data->num_vbmeta_images; n++) {
-          avb_sha512_update(&ctx,
-                            slot_data->vbmeta_images[n].vbmeta_data,
-                            slot_data->vbmeta_images[n].vbmeta_size);
-          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",
-                                avb_sha512_final(&ctx),
-                                AVB_SHA512_DIGEST_SIZE)) {
-          ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
-          goto fail;
-        }
-      } 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 fail;
-          }
-          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) {
-        goto fail;
-      }
-    }
-    if (!cmdline_append_option(
-            slot_data, "androidboot.veritymode", verity_mode)) {
-      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
-      goto fail;
-    }
-
     if (out_data != NULL) {
       *out_data = slot_data;
     } else {
diff --git a/libavb/avb_slot_verify.h b/libavb/avb_slot_verify.h
index 8794b98..d8de8fb 100644
--- a/libavb/avb_slot_verify.h
+++ b/libavb/avb_slot_verify.h
@@ -192,7 +192,8 @@
  * option depending on the value of |hashtree_error_mode|.
  *
  * Additionally, the |cmdline| field will have the following kernel
- * command-line options set:
+ * 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
@@ -227,14 +228,20 @@
  *   necessarily the same version number of the on-disk metadata for
  *   the slot that was verified.
  *
- * Note that neither androidboot.slot_suffix nor androidboot.slot are
- * set in the |cmdline| field in |AvbSlotVerifyData| - you will have
- * to pass these yourself.
+ * Note that androidboot.slot_suffix is not set in the |cmdline| field
+ * in |AvbSlotVerifyData| - you will have to set this yourself.
  *
- * Also note that androidboot.veritymode is set by libavb and since
- * AVB only supports 'enforcing' and 'disabled' values, the boot
- * loader is relieved of managing any state related to dm-verity or
- * setting this cmdline parameter.
+ * 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.
diff --git a/libavb/avb_vbmeta_image.h b/libavb/avb_vbmeta_image.h
index 90c3a54..d0c9f15 100644
--- a/libavb/avb_vbmeta_image.h
+++ b/libavb/avb_vbmeta_image.h
@@ -52,9 +52,13 @@
  *
  * 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_HASHTREE_DISABLED = (1 << 0),
+  AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED = (1 << 1)
 } AvbVBMetaImageFlags;
 
 /* Binary format for header of the vbmeta image.
diff --git a/libavb_ab/avb_ab_flow.c b/libavb_ab/avb_ab_flow.c
index e0661fa..bf6eab1 100644
--- a/libavb_ab/avb_ab_flow.c
+++ b/libavb_ab/avb_ab_flow.c
@@ -515,6 +515,7 @@
     case AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS:
       ret = "ERROR_NO_BOOTABLE_SLOTS";
       break;
+
     case AVB_AB_FLOW_RESULT_ERROR_INVALID_ARGUMENT:
       ret = "ERROR_INVALID_ARGUMENT";
       break;
diff --git a/libavb_user/avb_user_verification.c b/libavb_user/avb_user_verification.c
new file mode 100644
index 0000000..f572128
--- /dev/null
+++ b/libavb_user/avb_user_verification.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "avb_user_verification.h"
+
+/* Maximum allow length (in bytes) of a partition name, including
+ * ab_suffix.
+ */
+#define AVB_PART_NAME_MAX_SIZE 32
+
+/* Loads the toplevel AvbVBMetaImageHeader from the slot denoted by
+ * |ab_suffix| into |vbmeta_image|. No validation, verification, or
+ * byteswapping is performed.
+ *
+ * If successful, |true| is returned and the partition it was loaded
+ * from is returned in |out_partition_name| and the offset on said
+ * partition is returned in |out_vbmeta_offset|.
+ */
+static bool load_top_level_vbmeta_header(
+    AvbOps* ops,
+    const char* ab_suffix,
+    uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE],
+    char out_partition_name[AVB_PART_NAME_MAX_SIZE],
+    uint64_t* out_vbmeta_offset) {
+  uint64_t vbmeta_offset = 0;
+  size_t num_read;
+  bool ret = false;
+  AvbIOResult io_res;
+
+  /* Construct full partition name. */
+  if (!avb_str_concat(out_partition_name,
+                      AVB_PART_NAME_MAX_SIZE,
+                      "vbmeta",
+                      6,
+                      ab_suffix,
+                      avb_strlen(ab_suffix))) {
+    avb_error("Partition name and suffix does not fit.\n");
+    goto out;
+  }
+
+  /* Only read the header, not the entire struct. */
+  io_res = ops->read_from_partition(ops,
+                                    out_partition_name,
+                                    vbmeta_offset,
+                                    AVB_VBMETA_IMAGE_HEADER_SIZE,
+                                    vbmeta_image,
+                                    &num_read);
+  if (io_res == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION) {
+    AvbFooter footer;
+
+    /* Try looking for the vbmeta struct in 'boot' via the footer. */
+    if (!avb_str_concat(out_partition_name,
+                        AVB_PART_NAME_MAX_SIZE,
+                        "boot",
+                        4,
+                        ab_suffix,
+                        avb_strlen(ab_suffix))) {
+      avb_error("Partition name and suffix does not fit.\n");
+      goto out;
+    }
+    io_res = ops->read_from_partition(ops,
+                                      out_partition_name,
+                                      -AVB_FOOTER_SIZE,
+                                      AVB_FOOTER_SIZE,
+                                      &footer,
+                                      &num_read);
+    if (io_res != AVB_IO_RESULT_OK) {
+      avb_errorv("Error loading footer from partition '",
+                 out_partition_name,
+                 "'\n",
+                 NULL);
+      goto out;
+    }
+
+    if (avb_memcmp(footer.magic, AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN) != 0) {
+      avb_errorv("Data from '",
+                 out_partition_name,
+                 "' does not look like a vbmeta footer.\n",
+                 NULL);
+      goto out;
+    }
+
+    vbmeta_offset = avb_be64toh(footer.vbmeta_offset);
+    io_res = ops->read_from_partition(ops,
+                                      out_partition_name,
+                                      vbmeta_offset,
+                                      AVB_VBMETA_IMAGE_HEADER_SIZE,
+                                      vbmeta_image,
+                                      &num_read);
+  }
+
+  if (io_res != AVB_IO_RESULT_OK) {
+    avb_errorv(
+        "Error loading from partition '", out_partition_name, "'\n", NULL);
+    goto out;
+  }
+
+  if (out_vbmeta_offset != NULL) {
+    *out_vbmeta_offset = vbmeta_offset;
+  }
+
+  ret = true;
+
+out:
+  return ret;
+}
+
+bool avb_user_verification_get(AvbOps* ops,
+                               const char* ab_suffix,
+                               bool* out_verification_enabled) {
+  uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE]; /* 256 bytes. */
+  char partition_name[AVB_PART_NAME_MAX_SIZE];        /* 32 bytes. */
+  AvbVBMetaImageHeader* header;
+  uint32_t flags;
+  bool ret = false;
+
+  if (!load_top_level_vbmeta_header(
+          ops, ab_suffix, vbmeta_image, partition_name, NULL)) {
+    goto out;
+  }
+
+  if (avb_memcmp(vbmeta_image, AVB_MAGIC, AVB_MAGIC_LEN) != 0) {
+    avb_errorv("Data from '",
+               partition_name,
+               "' does not look like a vbmeta header.\n",
+               NULL);
+    goto out;
+  }
+
+  /* Set/clear the VERIFICATION_DISABLED bit, as requested. */
+  header = (AvbVBMetaImageHeader*)vbmeta_image;
+  flags = avb_be32toh(header->flags);
+
+  if (out_verification_enabled != NULL) {
+    *out_verification_enabled =
+        !(flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+  }
+
+  ret = true;
+
+out:
+  return ret;
+}
+
+bool avb_user_verification_set(AvbOps* ops,
+                               const char* ab_suffix,
+                               bool enable_verification) {
+  uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE]; /* 256 bytes. */
+  char partition_name[AVB_PART_NAME_MAX_SIZE];        /* 32 bytes. */
+  uint64_t vbmeta_offset;
+  AvbIOResult io_res;
+  AvbVBMetaImageHeader* header;
+  uint32_t flags;
+  bool ret = false;
+
+  if (!load_top_level_vbmeta_header(
+          ops, ab_suffix, vbmeta_image, partition_name, &vbmeta_offset)) {
+    goto out;
+  }
+
+  if (avb_memcmp(vbmeta_image, AVB_MAGIC, AVB_MAGIC_LEN) != 0) {
+    avb_errorv("Data from '",
+               partition_name,
+               "' does not look like a vbmeta header.\n",
+               NULL);
+    goto out;
+  }
+
+  /* Set/clear the VERIFICATION_DISABLED bit, as requested. */
+  header = (AvbVBMetaImageHeader*)vbmeta_image;
+  flags = avb_be32toh(header->flags);
+  flags &= ~AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED;
+  if (!enable_verification) {
+    flags |= AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED;
+  }
+  header->flags = avb_htobe32(flags);
+
+  /* Write the header. */
+  io_res = ops->write_to_partition(ops,
+                                   partition_name,
+                                   vbmeta_offset,
+                                   AVB_VBMETA_IMAGE_HEADER_SIZE,
+                                   vbmeta_image);
+  if (io_res != AVB_IO_RESULT_OK) {
+    avb_errorv("Error writing to partition '", partition_name, "'\n", NULL);
+    goto out;
+  }
+
+  ret = true;
+
+out:
+  return ret;
+}
diff --git a/libavb_user/avb_user_verification.h b/libavb_user/avb_user_verification.h
new file mode 100644
index 0000000..7a33c8d
--- /dev/null
+++ b/libavb_user/avb_user_verification.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef AVB_USER_VERIFICATION_H_
+#define AVB_USER_VERIFICATION_H_
+
+#include <libavb/libavb.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Function to enable or disable verification for an entire slot. The
+ * passed in |ops| should be obtained via avb_ops_user_new(). The
+ * |ab_suffix| parameter should specify the slot to modify including
+ * the leading underscore (e.g. "_a" or "_b"). The
+ * |enable_verification| parameter should be set to |true| to enable
+ * dm-verification and |false| to disable.
+ *
+ * Returns |true| if the operation succeeded, otherwise |false|.
+ */
+bool avb_user_verification_set(AvbOps* ops,
+                               const char* ab_suffix,
+                               bool enable_verification);
+
+/* Gets whether verification is enabled for an entire slot. The passed
+ * in |ops| should be obtained via avb_ops_user_new(). The |ab_suffix|
+ * parameter should specify the slot to query including the leading
+ * underscore (e.g. "_a" or "_b"). The result is returned in the
+ * |out_verification_enabled| parameter.
+ *
+ * Returns |true| if the operation succeeded, otherwise |false|.
+ */
+bool avb_user_verification_get(AvbOps* ops,
+                               const char* ab_suffix,
+                               bool* out_verification_enabled);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_USER_VERIFICATION_H_ */
diff --git a/libavb_user/avb_user_verity.c b/libavb_user/avb_user_verity.c
index 8098909..ecf0043 100644
--- a/libavb_user/avb_user_verity.c
+++ b/libavb_user/avb_user_verity.c
@@ -37,7 +37,7 @@
  * from is returned in |out_partition_name| and the offset on said
  * partition is returned in |out_vbmeta_offset|.
  */
-bool load_top_level_vbmeta_header(
+static bool load_top_level_vbmeta_header(
     AvbOps* ops,
     const char* ab_suffix,
     uint8_t vbmeta_image[AVB_VBMETA_IMAGE_HEADER_SIZE],
diff --git a/libavb_user/libavb_user.h b/libavb_user/libavb_user.h
index b9d10ab..a8b8c55 100644
--- a/libavb_user/libavb_user.h
+++ b/libavb_user/libavb_user.h
@@ -34,6 +34,7 @@
 
 #define AVB_INSIDE_LIBAVB_USER_H
 #include "avb_ops_user.h"
+#include "avb_user_verification.h"
 #include "avb_user_verity.h"
 #undef AVB_INSIDE_LIBAVB_USER_H
 
diff --git a/test/avb_slot_verify_unittest.cc b/test/avb_slot_verify_unittest.cc
index 8348bf2..88b87f4 100644
--- a/test/avb_slot_verify_unittest.cc
+++ b/test/avb_slot_verify_unittest.cc
@@ -46,6 +46,7 @@
 
   void CmdlineWithHashtreeVerification(bool hashtree_verification_on);
   void CmdlineWithChainedHashtreeVerification(bool hashtree_verification_on);
+  void VerificationDisabled(bool use_avbctl);
 
   FakeAvbOps ops_;
 };
@@ -1677,6 +1678,154 @@
   CmdlineWithChainedHashtreeVerification(true);
 }
 
+void AvbSlotVerifyTest::VerificationDisabled(bool use_avbctl) {
+  const size_t boot_part_size = 32 * 1024 * 1024;
+  const size_t dtbo_part_size = 4 * 1024 * 1024;
+  const size_t rootfs_size = 1028 * 1024;
+  const size_t partition_size = 1536 * 1024;
+
+  // Generate boot_a.img and dtbo_a.img since avb_slot_verify() will
+  // attempt to load them upon encountering the VERIFICATION_DISABLED
+  // flag.
+  base::FilePath boot_path = GenerateImage("boot_a.img", boot_part_size);
+  const size_t DTBO_DATA_OFFSET = 42;
+  base::FilePath dtbo_path =
+      GenerateImage("dtbo_a.img", dtbo_part_size, DTBO_DATA_OFFSET);
+
+  // Generate a 1028 KiB file with known content.
+  std::vector<uint8_t> rootfs;
+  rootfs.resize(rootfs_size);
+  for (size_t n = 0; n < rootfs_size; n++)
+    rootfs[n] = uint8_t(n);
+  base::FilePath rootfs_path = testdir_.Append("rootfs.bin");
+  EXPECT_EQ(rootfs_size,
+            static_cast<const size_t>(
+                base::WriteFile(rootfs_path,
+                                reinterpret_cast<const char*>(rootfs.data()),
+                                rootfs.size())));
+
+  EXPECT_COMMAND(0,
+                 "./avbtool add_hashtree_footer --salt d00df00d --image %s "
+                 "--partition_size %d --partition_name foobar "
+                 "--algorithm SHA256_RSA2048 "
+                 "--key test/data/testkey_rsa2048.pem "
+                 "--internal_release_string \"\" "
+                 "--do_not_generate_fec",
+                 rootfs_path.value().c_str(),
+                 (int)partition_size);
+
+  // Check that we correctly generate dm-verity kernel cmdline
+  // snippets, if requested.
+  GenerateVBMetaImage(
+      "vbmeta_a.img",
+      "SHA256_RSA2048",
+      4,
+      base::FilePath("test/data/testkey_rsa2048.pem"),
+      base::StringPrintf(
+          "--setup_rootfs_from_kernel %s "
+          "--kernel_cmdline should_be_in_both=1 "
+          "--algorithm SHA256_RSA2048 "
+          "--flags %d "
+          "--internal_release_string \"\"",
+          rootfs_path.value().c_str(),
+          use_avbctl ? 0 : AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED));
+
+  EXPECT_EQ(
+      base::StringPrintf(
+          "Minimum libavb version:   1.0\n"
+          "Header Block:             256 bytes\n"
+          "Authentication Block:     320 bytes\n"
+          "Auxiliary Block:          960 bytes\n"
+          "Algorithm:                SHA256_RSA2048\n"
+          "Rollback Index:           4\n"
+          "Flags:                    %d\n"
+          "Release String:           ''\n"
+          "Descriptors:\n"
+          "    Kernel Cmdline descriptor:\n"
+          "      Flags:                 1\n"
+          "      Kernel Cmdline:        'dm=\"1 vroot none ro 1,0 2056 verity "
+          "1 PARTUUID=$(ANDROID_SYSTEM_PARTUUID) "
+          "PARTUUID=$(ANDROID_SYSTEM_PARTUUID) 4096 4096 257 257 sha1 "
+          "e811611467dcd6e8dc4324e45f706c2bdd51db67 d00df00d 2 "
+          "$(ANDROID_VERITY_MODE) ignore_zero_blocks\" root=/dev/dm-0'\n"
+          "    Kernel Cmdline descriptor:\n"
+          "      Flags:                 2\n"
+          "      Kernel Cmdline:        "
+          "'root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)'\n"
+          "    Kernel Cmdline descriptor:\n"
+          "      Flags:                 0\n"
+          "      Kernel Cmdline:        'should_be_in_both=1'\n",
+          use_avbctl ? 0 : AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED),
+      InfoImage(vbmeta_image_path_));
+
+  ops_.set_expected_public_key(
+      PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+  // Manually set the flag the same way 'avbctl disable-verification'
+  // would do it.
+  if (use_avbctl) {
+    uint32_t flags_data;
+    flags_data = avb_htobe32(AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+    EXPECT_EQ(AVB_IO_RESULT_OK,
+              ops_.avb_ops()->write_to_partition(
+                  ops_.avb_ops(),
+                  "vbmeta_a",
+                  offsetof(AvbVBMetaImageHeader, flags),
+                  sizeof flags_data,
+                  &flags_data));
+  }
+
+  // Check that avb_slot_verify() doesn't return any of the
+  // descriptors and instead return a kernel command-line with
+  // root=PARTUUID=<whatever_for_system_a> and none of the
+  // androidboot.vbmeta.* options are set. Also ensure all the
+  // requested partitions are loaded.
+  //
+  // Also if we modified via avbctl we should expect
+  // ERROR_VERIFICATION instead of OK.
+  //
+  AvbSlotVerifyResult expected_result = AVB_SLOT_VERIFY_RESULT_OK;
+  if (use_avbctl) {
+    expected_result = AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
+  }
+  AvbSlotVerifyData* slot_data = NULL;
+  const char* requested_partitions[] = {"boot", "dtbo", NULL};
+  EXPECT_EQ(expected_result,
+            avb_slot_verify(ops_.avb_ops(),
+                            requested_partitions,
+                            "_a",
+                            AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,
+                            AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
+                            &slot_data));
+  EXPECT_NE(nullptr, slot_data);
+  EXPECT_EQ("root=PARTUUID=1234-fake-guid-for:system_a",
+            std::string(slot_data->cmdline));
+  // Also make sure that it actually loads the boot and dtbo partitions.
+  EXPECT_EQ(size_t(2), slot_data->num_loaded_partitions);
+  EXPECT_EQ("boot",
+            std::string(slot_data->loaded_partitions[0].partition_name));
+  EXPECT_EQ(boot_part_size, slot_data->loaded_partitions[0].data_size);
+  for (size_t n = 0; n < boot_part_size; n++) {
+    EXPECT_EQ(uint8_t(n), slot_data->loaded_partitions[0].data[n]);
+  }
+  EXPECT_EQ("dtbo",
+            std::string(slot_data->loaded_partitions[1].partition_name));
+  EXPECT_EQ(dtbo_part_size, slot_data->loaded_partitions[1].data_size);
+  for (size_t n = 0; n < dtbo_part_size; n++) {
+    EXPECT_EQ(uint8_t(n + DTBO_DATA_OFFSET),
+              slot_data->loaded_partitions[1].data[n]);
+  }
+  avb_slot_verify_data_free(slot_data);
+}
+
+TEST_F(AvbSlotVerifyTest, VerificationDisabledUnmodified) {
+  VerificationDisabled(false);  // use_avbctl
+}
+
+TEST_F(AvbSlotVerifyTest, VerificationDisabledModified) {
+  VerificationDisabled(true);  // use_avbctl
+}
+
 // In the event that there's no vbmeta partition, we treat the vbmeta
 // struct from 'boot' as the top-level partition. Check that this
 // works.
diff --git a/tools/avbctl/avbctl.cc b/tools/avbctl/avbctl.cc
index 9d83f0b..d0922b3 100644
--- a/tools/avbctl/avbctl.cc
+++ b/tools/avbctl/avbctl.cc
@@ -41,10 +41,17 @@
           "  %s COMMAND\n"
           "\n"
           "Commands:\n"
-          "  %s get-verity        - Prints whether verity is enabled in "
+          "  %s get-verity           - Prints whether verity is enabled in "
           "current slot.\n"
-          "  %s disable-verity    - Disable verity in current slot.\n"
-          "  %s enable-verity     - Enable verity in current slot.\n",
+          "  %s disable-verity       - Disable verity in current slot.\n"
+          "  %s enable-verity        - Enable verity in current slot.\n"
+          "  %s get-verification     - Prints whether verification is enabled "
+          "in current slot.\n"
+          "  %s disable-verification - Disable verification in current slot.\n"
+          "  %s enable-verification  - Enable verification in current slot.\n",
+          argv[0],
+          argv[0],
+          argv[0],
           argv[0],
           argv[0],
           argv[0],
@@ -52,6 +59,71 @@
           argv[0]);
 }
 
+/* Function to enable and disable verification. The |ops| parameter
+ * should be an |AvbOps| from libavb_user.
+ */
+int do_set_verification(AvbOps* ops,
+                        const std::string& ab_suffix,
+                        bool enable_verification) {
+  bool verification_enabled;
+
+  if (!avb_user_verification_get(
+          ops, ab_suffix.c_str(), &verification_enabled)) {
+    fprintf(stderr, "Error getting whether verification is enabled.\n");
+    return EX_SOFTWARE;
+  }
+
+  if ((verification_enabled && enable_verification) ||
+      (!verification_enabled && !enable_verification)) {
+    fprintf(stdout,
+            "verification is already %s",
+            verification_enabled ? "enabled" : "disabled");
+    if (ab_suffix != "") {
+      fprintf(stdout, " on slot with suffix %s", ab_suffix.c_str());
+    }
+    fprintf(stdout, ".\n");
+    return EX_OK;
+  }
+
+  if (!avb_user_verification_set(ops, ab_suffix.c_str(), enable_verification)) {
+    fprintf(stderr, "Error setting verification.\n");
+    return EX_SOFTWARE;
+  }
+
+  fprintf(stdout,
+          "Successfully %s verification",
+          enable_verification ? "enabled" : "disabled");
+  if (ab_suffix != "") {
+    fprintf(stdout, " on slot with suffix %s", ab_suffix.c_str());
+  }
+  fprintf(stdout, ". Reboot the device for changes to take effect.\n");
+
+  return EX_OK;
+}
+
+/* Function to query if verification. The |ops| parameter should be an
+ * |AvbOps| from libavb_user.
+ */
+int do_get_verification(AvbOps* ops, const std::string& ab_suffix) {
+  bool verification_enabled;
+
+  if (!avb_user_verification_get(
+          ops, ab_suffix.c_str(), &verification_enabled)) {
+    fprintf(stderr, "Error getting whether verification is enabled.\n");
+    return EX_SOFTWARE;
+  }
+
+  fprintf(stdout,
+          "verification is %s",
+          verification_enabled ? "enabled" : "disabled");
+  if (ab_suffix != "") {
+    fprintf(stdout, " on slot with suffix %s", ab_suffix.c_str());
+  }
+  fprintf(stdout, ".\n");
+
+  return EX_OK;
+}
+
 /* Function to enable and disable dm-verity. The |ops| parameter
  * should be an |AvbOps| from libavb_user.
  */
@@ -158,6 +230,12 @@
     ret = do_set_verity(ops, ab_suffix, true);
   } else if (strcmp(argv[1], "get-verity") == 0) {
     ret = do_get_verity(ops, ab_suffix);
+  } else if (strcmp(argv[1], "disable-verification") == 0) {
+    ret = do_set_verification(ops, ab_suffix, false);
+  } else if (strcmp(argv[1], "enable-verification") == 0) {
+    ret = do_set_verification(ops, ab_suffix, true);
+  } else if (strcmp(argv[1], "get-verification") == 0) {
+    ret = do_get_verification(ops, ab_suffix);
   } else {
     usage(stderr, argc, argv);
     ret = EX_USAGE;