Implement initialization of new persistent digest values

This enables migration to persistent value integrity checks via an OTA
update. When the new vbmeta is verified, if a persistent digest value
does not exist it is initialized with the current correct value. The
result is that a partition previously not integrity checked is now
verified against the contents at the point the feature is enabled. This
currently only works for partitions with hash descriptors.

Bug: 73020477
Test: unit
Change-Id: Iae709d31902cbcfe9f1e7e3f3d4e3b6dc783b716
diff --git a/README.md b/README.md
index 2fda62b..4912a6f 100644
--- a/README.md
+++ b/README.md
@@ -717,7 +717,7 @@
 is stored in a named persistent value. This allows configuration data
 which may differ from device to device to be verified by AVB. It must
 not be possible to modify the persistent digest when the device is in
-the LOCKED state.
+the LOCKED state, except if a digest does not exist it may be initialized.
 
 To specify that a descriptor should use a persistent digest, use the
 `--use_persistent_digest` option for the `add_hash_footer` or
diff --git a/avbtool b/avbtool
index 8732024..af5ccce 100755
--- a/avbtool
+++ b/avbtool
@@ -3969,7 +3969,7 @@
                                   'certificate'),
                             action='store_true')
     sub_parser.add_argument('--usage',
-                            help=('Override usage with a hash of the provided'
+                            help=('Override usage with a hash of the provided '
                                   'string'),
                             required=False)
     sub_parser.add_argument('--authority_key',
diff --git a/libavb/avb_slot_verify.c b/libavb/avb_slot_verify.c
index 64e2666..262b2f7 100644
--- a/libavb/avb_slot_verify.c
+++ b/libavb/avb_slot_verify.c
@@ -133,9 +133,25 @@
   return AVB_SLOT_VERIFY_RESULT_OK;
 }
 
+/* Reads a persistent digest stored as a named persistent value corresponding to
+ * the given |part_name|. The value is returned in |out_digest| which must point
+ * to |expected_digest_size| bytes. If there is no digest stored for |part_name|
+ * it can be initialized by providing a non-NULL |initial_digest| of length
+ * |expected_digest_size|. The |initial_digest| may be NULL.
+ *
+ * Returns AVB_SLOT_VERIFY_RESULT_OK on success, otherwise returns an
+ * AVB_SLOT_VERIFY_RESULT_ERROR_* error code.
+ *
+ * If the value does not exist, is not supported, or is not populated, and
+ * |initial_digest| is NULL, returns
+ * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA. If |expected_digest_size| does
+ * not match the stored digest size, also returns
+ * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA.
+ */
 static AvbSlotVerifyResult read_persistent_digest(AvbOps* ops,
                                                   const char* part_name,
                                                   size_t expected_digest_size,
+                                                  const uint8_t* initial_digest,
                                                   uint8_t* out_digest) {
   char* persistent_value_name = NULL;
   AvbIOResult io_ret = AVB_IO_RESULT_OK;
@@ -155,6 +171,19 @@
                                       expected_digest_size,
                                       out_digest,
                                       &stored_digest_size);
+  if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE && initial_digest) {
+    avb_debugv(part_name,
+               ": Digest does not exist, initializing persistent digest.\n",
+               NULL);
+    stored_digest_size = expected_digest_size;
+    io_ret = ops->write_persistent_value(
+        ops, persistent_value_name, expected_digest_size, initial_digest);
+    if (io_ret == AVB_IO_RESULT_OK) {
+      avb_memcpy(out_digest, initial_digest, expected_digest_size);
+    } else {
+      avb_errorv(part_name, ": Error initializing persistent digest.\n", NULL);
+    }
+  }
   avb_free(persistent_value_name);
   if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
     return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
@@ -314,18 +343,21 @@
   }
 
   if (hash_desc.digest_len == 0) {
-    // Expect a match to a persistent digest.
+    /* 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);
+    /* Pass |digest| as the |initial_digest| so devices not yet initialized get
+     * initialized to the current partition digest.
+     */
+    ret = read_persistent_digest(
+        ops, part_name, digest_len, digest, expected_digest_buf);
     if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
       goto out;
     }
   } else {
-    // Expect a match to the digest in the descriptor.
+    /* Expect a match to the digest in the descriptor. */
     expected_digest_len = hash_desc.digest_len;
     expected_digest = desc_digest;
   }
@@ -1038,7 +1070,11 @@
             goto out;
           }
 
-          ret = read_persistent_digest(ops, part_name, digest_len, digest_buf);
+          ret = read_persistent_digest(ops,
+                                       part_name,
+                                       digest_len,
+                                       NULL /* initial_digest */,
+                                       digest_buf);
           if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
             goto out;
           }
diff --git a/test/avb_slot_verify_unittest.cc b/test/avb_slot_verify_unittest.cc
index 555aaa8..5771f1e 100644
--- a/test/avb_slot_verify_unittest.cc
+++ b/test/avb_slot_verify_unittest.cc
@@ -2671,6 +2671,12 @@
   Verify(false /* expect_success */);
 }
 
+TEST_F(AvbSlotVerifyTestWithPersistentDigest, Basic_WithAutoInit) {
+  SetupWithHashDescriptor();
+  // Explicitly do not write any digest as a persistent value.
+  Verify(true /* expect_success */);
+}
+
 class AvbSlotVerifyTestWithPersistentDigest_InvalidDigestLength
     : public AvbSlotVerifyTestWithPersistentDigest,
       public ::testing::WithParamInterface<size_t> {};
@@ -2693,7 +2699,18 @@
 
 class AvbSlotVerifyTestWithPersistentDigest_InvalidPersistentValueName
     : public AvbSlotVerifyTestWithPersistentDigest,
-      public ::testing::WithParamInterface<const char*> {};
+      public ::testing::WithParamInterface<const char*> {
+  // FakeAvbOpsDelegate override.
+  AvbIOResult write_persistent_value(const char* name,
+                                     size_t value_size,
+                                     const uint8_t* value) override {
+    // Fail attempted initialization with any name not under test.
+    if (std::string(name) != GetParam()) {
+      return AVB_IO_RESULT_ERROR_NO_SUCH_VALUE;
+    }
+    return ops_.write_persistent_value(name, value_size, value);
+  }
+};
 
 TEST_P(AvbSlotVerifyTestWithPersistentDigest_InvalidPersistentValueName,
        Param) {
@@ -2708,26 +2725,30 @@
     AvbSlotVerifyTestWithPersistentDigest_InvalidPersistentValueName,
     ::testing::Values(
         "",
-        "AVBPD_factory0",
-        "AVBPD_factor",
+        "avb.persistent_digest.factory0",
+        "avb.persistent_digest.factor",
         "loooooooooooooooooooooooooooooooooooooooooooooongvalue"));
 
 class AvbSlotVerifyTestWithPersistentDigest_ReadDigestFailure
     : public AvbSlotVerifyTestWithPersistentDigest,
       public ::testing::WithParamInterface<AvbIOResult> {
-  // FakeAvbOpsDelegate override.
+  // FakeAvbOpsDelegate overrides.
   AvbIOResult read_persistent_value(const char* name,
                                     size_t buffer_size,
                                     uint8_t* out_buffer,
                                     size_t* out_num_bytes_read) override {
     return GetParam();
   }
+  AvbIOResult write_persistent_value(const char* name,
+                                     size_t value_size,
+                                     const uint8_t* value) override {
+    // Fail any attempted initialization in response to the read error.
+    return GetParam();
+  }
 };
 
 TEST_P(AvbSlotVerifyTestWithPersistentDigest_ReadDigestFailure, Param) {
   SetupWithHashDescriptor();
-  ops_.write_persistent_value(
-      kPersistentValueName, AVB_SHA256_DIGEST_SIZE, kDigest);
   switch (GetParam()) {
     case AVB_IO_RESULT_ERROR_OOM:
       expected_error_code_ = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
@@ -2850,6 +2871,13 @@
   Verify(false /* expect_success */);
 }
 
+TEST_F(AvbSlotVerifyTestWithPersistentDigest, Basic_Hashtree_NoAutoInit) {
+  verity_hash_algorithm_ = "sha1";
+  SetupWithHashtreeDescriptor();
+  // Explicitly do not store any persistent digest.
+  Verify(false /* expect_success */);
+}
+
 class AvbSlotVerifyTestWithPersistentDigest_Hashtree_InvalidDigestLength
     : public AvbSlotVerifyTestWithPersistentDigest,
       public ::testing::WithParamInterface<size_t> {};