Merge "Revert "Revert "Keystore 2.0: Add CREATE_DATETIME unconditionally."""
diff --git a/OWNERS b/OWNERS
index 93c024d..563a78c 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,7 +1,9 @@
+alanstokes@google.com
 cbrubaker@google.com
 hasinitg@google.com
 jbires@google.com
 jdanis@google.com
+jeffv@google.com
 kroot@google.com
 sethmo@google.com
 swillden@google.com
diff --git a/fsverity/Android.bp b/fsverity/Android.bp
new file mode 100644
index 0000000..5fb38ae
--- /dev/null
+++ b/fsverity/Android.bp
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+python_library_host {
+    name: "fsverity_digests_proto_python",
+    srcs: [
+        "fsverity_digests.proto",
+    ],
+    required: [
+        "fsverity",
+    ],
+    proto: {
+        canonical_path_from_root: false,
+    },
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+}
diff --git a/fsverity/AndroidManifest.xml b/fsverity/AndroidManifest.xml
new file mode 100644
index 0000000..434955c
--- /dev/null
+++ b/fsverity/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.security.fsverity_metadata" />
diff --git a/fsverity/OWNERS b/fsverity/OWNERS
new file mode 100644
index 0000000..f9e7b25
--- /dev/null
+++ b/fsverity/OWNERS
@@ -0,0 +1,5 @@
+alanstokes@google.com
+ebiggers@google.com
+jeffv@google.com
+jiyong@google.com
+victorhsieh@google.com
diff --git a/fsverity/fsverity_digests.proto b/fsverity/fsverity_digests.proto
new file mode 100644
index 0000000..816ae61
--- /dev/null
+++ b/fsverity/fsverity_digests.proto
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.security.fsverity;
+
+message FSVerityDigests {
+  message Digest {
+    bytes digest = 1;
+    string hash_alg = 2;
+  }
+  map<string, Digest> digests = 1;
+}
diff --git a/fsverity_init/Android.bp b/fsverity_init/Android.bp
index 39d4e6b..83c5945 100644
--- a/fsverity_init/Android.bp
+++ b/fsverity_init/Android.bp
@@ -10,17 +10,34 @@
 cc_binary {
     name: "fsverity_init",
     srcs: [
-        "fsverity_init.cpp",
+        "main.cpp",
     ],
     static_libs: [
         "libc++fs",
+        "libfsverity_init",
+        "libmini_keyctl_static",
+    ],
+    shared_libs: [
+        "libbase",
+        "libkeyutils",
+        "liblog",
+    ],
+    cflags: ["-Werror", "-Wall", "-Wextra"],
+}
+
+cc_library {
+    name: "libfsverity_init",
+    srcs: ["fsverity_init.cpp"],
+    static_libs: [
+        "libc++fs",
         "libmini_keyctl_static",
     ],
     shared_libs: [
         "libbase",
         "libkeyutils",
         "liblog",
-        "liblogwrap",
     ],
     cflags: ["-Werror", "-Wall", "-Wextra"],
+    export_include_dirs: ["include"],
+    recovery_available: true,
 }
diff --git a/fsverity_init/OWNERS b/fsverity_init/OWNERS
new file mode 100644
index 0000000..f9e7b25
--- /dev/null
+++ b/fsverity_init/OWNERS
@@ -0,0 +1,5 @@
+alanstokes@google.com
+ebiggers@google.com
+jeffv@google.com
+jiyong@google.com
+victorhsieh@google.com
diff --git a/fsverity_init/fsverity_init.cpp b/fsverity_init/fsverity_init.cpp
index 7bc6022..61f84dd 100644
--- a/fsverity_init/fsverity_init.cpp
+++ b/fsverity_init/fsverity_init.cpp
@@ -81,47 +81,3 @@
     LoadKeyFromDirectory(keyring_id, "fsv_system_", "/system/etc/security/fsverity");
     LoadKeyFromDirectory(keyring_id, "fsv_product_", "/product/etc/security/fsverity");
 }
-
-int main(int argc, const char** argv) {
-    if (argc < 2) {
-        LOG(ERROR) << "Not enough arguments";
-        return -1;
-    }
-
-    key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
-    if (keyring_id < 0) {
-        LOG(ERROR) << "Failed to find .fs-verity keyring id";
-        return -1;
-    }
-
-    const std::string_view command = argv[1];
-
-    if (command == "--load-verified-keys") {
-        LoadKeyFromVerifiedPartitions(keyring_id);
-    } else if (command == "--load-extra-key") {
-        if (argc != 3) {
-            LOG(ERROR) << "--load-extra-key requires <key_name> argument.";
-            return -1;
-        }
-        if (!LoadKeyFromStdin(keyring_id, argv[2])) {
-            return -1;
-        }
-    } else if (command == "--lock") {
-        // Requires files backed by fs-verity to be verified with a key in .fs-verity
-        // keyring.
-        if (!android::base::WriteStringToFile("1", "/proc/sys/fs/verity/require_signatures")) {
-            PLOG(ERROR) << "Failed to enforce fs-verity signature";
-        }
-
-        if (!android::base::GetBoolProperty("ro.debuggable", false)) {
-            if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
-                PLOG(ERROR) << "Cannot restrict .fs-verity keyring";
-            }
-        }
-    } else {
-        LOG(ERROR) << "Unknown argument(s).";
-        return -1;
-    }
-
-    return 0;
-}
diff --git a/fsverity_init/include/fsverity_init.h b/fsverity_init/include/fsverity_init.h
new file mode 100644
index 0000000..c3bc93b
--- /dev/null
+++ b/fsverity_init/include/fsverity_init.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <mini_keyctl_utils.h>
+
+bool LoadKeyFromStdin(key_serial_t keyring_id, const char* keyname);
+void LoadKeyFromFile(key_serial_t keyring_id, const char* keyname, const std::string& path);
+void LoadKeyFromVerifiedPartitions(key_serial_t keyring_id);
diff --git a/fsverity_init/main.cpp b/fsverity_init/main.cpp
new file mode 100644
index 0000000..3f75dca
--- /dev/null
+++ b/fsverity_init/main.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <fsverity_init.h>
+#include <log/log.h>
+#include <mini_keyctl_utils.h>
+
+int main(int argc, const char** argv) {
+    if (argc < 2) {
+        LOG(ERROR) << "Not enough arguments";
+        return -1;
+    }
+
+    key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
+    if (keyring_id < 0) {
+        LOG(ERROR) << "Failed to find .fs-verity keyring id";
+        return -1;
+    }
+
+    const std::string_view command = argv[1];
+
+    if (command == "--load-verified-keys") {
+        LoadKeyFromVerifiedPartitions(keyring_id);
+    } else if (command == "--load-extra-key") {
+        if (argc != 3) {
+            LOG(ERROR) << "--load-extra-key requires <key_name> argument.";
+            return -1;
+        }
+        if (!LoadKeyFromStdin(keyring_id, argv[2])) {
+            return -1;
+        }
+    } else if (command == "--lock") {
+        // Requires files backed by fs-verity to be verified with a key in .fs-verity
+        // keyring.
+        if (!android::base::WriteStringToFile("1", "/proc/sys/fs/verity/require_signatures")) {
+            PLOG(ERROR) << "Failed to enforce fs-verity signature";
+        }
+
+        if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+            if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
+                PLOG(ERROR) << "Cannot restrict .fs-verity keyring";
+            }
+        }
+    } else {
+        LOG(ERROR) << "Unknown argument(s).";
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/identity/Android.bp b/identity/Android.bp
index ecdf9a4..ddb4335 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -24,7 +24,10 @@
 
 cc_binary {
     name: "credstore",
-    defaults: ["identity_defaults"],
+    defaults: [
+        "identity_defaults",
+        "keymint_use_latest_hal_aidl_ndk_shared",
+    ],
 
     srcs: [
         "main.cpp",
@@ -48,14 +51,13 @@
         "android.hardware.identity-support-lib",
         "libkeymaster4support",
         "libkeystore-attestation-application-id",
-        "android.hardware.security.keymint-V1-ndk",
         "android.security.authorization-ndk",
     ],
     static_libs: [
         "android.hardware.identity-V3-cpp",
         "android.hardware.keymaster-V3-cpp",
         "libcppbor_external",
-    ]
+    ],
 }
 
 filegroup {
diff --git a/keystore/Android.bp b/keystore/Android.bp
index ad4b4b1..892c5b4 100644
--- a/keystore/Android.bp
+++ b/keystore/Android.bp
@@ -36,7 +36,10 @@
 
 cc_binary {
     name: "keystore_cli_v2",
-    defaults: ["keystore_defaults"],
+    defaults: [
+        "keystore_defaults",
+        "keystore2_use_latest_aidl_ndk_shared",
+    ],
 
     cflags: [
         "-DKEYMASTER_NAME_TAGS",
@@ -48,7 +51,6 @@
     ],
     shared_libs: [
         "android.security.apc-ndk",
-        "android.system.keystore2-V1-ndk",
         "libbinder",
         "libbinder_ndk",
         "libchrome",
@@ -63,7 +65,7 @@
 
 // Library used by both keystore and credstore for generating the ASN.1 stored
 // in Tag::ATTESTATION_APPLICATION_ID
-cc_library_shared {
+cc_library {
     name: "libkeystore-attestation-application-id",
     defaults: ["keystore_defaults"],
 
diff --git a/keystore/keystore_cli_v2.cpp b/keystore/keystore_cli_v2.cpp
index 1cfb4f6..1e9126d 100644
--- a/keystore/keystore_cli_v2.cpp
+++ b/keystore/keystore_cli_v2.cpp
@@ -19,6 +19,7 @@
 #include <iostream>
 #include <memory>
 #include <string>
+#include <variant>
 #include <vector>
 
 #include <base/command_line.h>
diff --git a/keystore/tests/fuzzer/Android.bp b/keystore/tests/fuzzer/Android.bp
new file mode 100644
index 0000000..589cef7
--- /dev/null
+++ b/keystore/tests/fuzzer/Android.bp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+cc_fuzz {
+    name: "keystoreGetWifiHidl_fuzzer",
+    vendor: true,
+    srcs: [
+        "keystoreGetWifiHidl_fuzzer.cpp",
+    ],
+    static_libs: [
+        "libkeystore-wifi-hidl",
+        "libutils",
+    ],
+    shared_libs: [
+        "android.system.wifi.keystore@1.0",
+        "libhidlbase",
+        "liblog",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
+
+cc_defaults {
+    name: "keystoreAttestation_defaults",
+    static_libs: [
+        "libkeystore-attestation-application-id",
+        "liblog",
+        "libutils",
+        "libbase",
+        "libhidlbase",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libcrypto",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
+
+cc_fuzz {
+    name: "keystoreSignature_fuzzer",
+    srcs: [
+        "keystoreSignature_fuzzer.cpp",
+    ],
+    defaults: [
+        "keystoreAttestation_defaults",
+    ],
+}
+
+cc_fuzz {
+    name: "keystorePackageInfo_fuzzer",
+    srcs: [
+        "keystorePackageInfo_fuzzer.cpp",
+    ],
+    defaults: [
+        "keystoreAttestation_defaults",
+    ],
+}
+
+cc_fuzz {
+    name: "keystoreApplicationId_fuzzer",
+    srcs: [
+        "keystoreApplicationId_fuzzer.cpp",
+    ],
+    defaults: [
+        "keystoreAttestation_defaults",
+    ],
+}
+
+cc_fuzz {
+    name: "keystoreAttestationId_fuzzer",
+    srcs: [
+        "keystoreAttestationId_fuzzer.cpp",
+    ],
+    defaults: [
+        "keystoreAttestation_defaults",
+    ],
+}
diff --git a/keystore/tests/fuzzer/README.md b/keystore/tests/fuzzer/README.md
new file mode 100644
index 0000000..25d53ab
--- /dev/null
+++ b/keystore/tests/fuzzer/README.md
@@ -0,0 +1,103 @@
+# Fuzzer for libkeystore
+## Table of contents
++ [libkeystore-get-wifi-hidl](#libkeystore-get-wifi-hidl)
++ [libkeystore_attestation_application_id](#libkeystore_attestation_application_id)
+
+# <a name="libkeystore-get-wifi-hidl"></a> Fuzzer for libkeystore-get-wifi-hidl
+## Plugin Design Considerations
+The fuzzer plugin for libkeystore-get-wifi-hidl is designed based on the understanding of the library and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+libkeystore-get-wifi-hidl supports the following parameters:
+1. Key (parameter name: `key`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `key` | `String` | Value obtained from FuzzedDataProvider|
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the libkeystore-get-wifi-hidl module.
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build keystoreGetWifiHidl_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) keystoreGetWifiHidl_fuzzer
+```
+#### Steps to run
+
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreGetWifiHidl_fuzzer/keystoreGetWifiHidl_fuzzer
+```
+
+# <a name="libkeystore_attestation_application_id"></a> Fuzzer for libkeystore_attestation_application_id
+## Plugin Design Considerations
+The fuzzer plugin for libkeystore-attestation-application-id are designed based on the understanding of the library and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+libkeystore-attestation-application-id supports the following parameters:
+1. Package Name (parameter name: `packageName`)
+2. Version Code (parameter name: `versionCode`)
+3. Uid (parameter name: `uid`)
+
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `packageName` | `String` | Value obtained from FuzzedDataProvider|
+| `versionCode` | `INT64_MIN` to `INT64_MAX` | Value obtained from FuzzedDataProvider|
+| `uid` | `0` to `1000` | Value obtained from FuzzedDataProvider|
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugins feed the entire input data to the libkeystore_attestation_application_id module.
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build keystoreSignature_fuzzer, keystorePackageInfo_fuzzer, keystoreApplicationId_fuzzer and keystoreAttestationId_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) keystoreSignature_fuzzer
+  $ mm -j$(nproc) keystorePackageInfo_fuzzer
+  $ mm -j$(nproc) keystoreApplicationId_fuzzer
+  $ mm -j$(nproc) keystoreAttestationId_fuzzer
+```
+#### Steps to run
+
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreSignature_fuzzer/keystoreSignature_fuzzer
+  $ adb shell /data/fuzz/${TARGET_ARCH}/keystorePackageInfo_fuzzer/keystorePackageInfo_fuzzer
+  $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreApplicationId_fuzzer/keystoreApplicationId_fuzzer
+  $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreAttestationId_fuzzer/keystoreAttestationId_fuzzer
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp b/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp
new file mode 100644
index 0000000..0eddb9a
--- /dev/null
+++ b/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "keystoreCommon.h"
+#include <keystore/KeyAttestationApplicationId.h>
+
+using ::security::keymaster::KeyAttestationApplicationId;
+
+constexpr size_t kPackageVectorSizeMin = 1;
+constexpr size_t kPackageVectorSizeMax = 10;
+
+class KeystoreApplicationId {
+  public:
+    void process(const uint8_t* data, size_t size);
+    ~KeystoreApplicationId() {}
+
+  private:
+    void invokeApplicationId();
+    std::unique_ptr<FuzzedDataProvider> mFdp;
+};
+
+void KeystoreApplicationId::invokeApplicationId() {
+    std::optional<KeyAttestationApplicationId> applicationId;
+    bool shouldUsePackageInfoVector = mFdp->ConsumeBool();
+    if (shouldUsePackageInfoVector) {
+        KeyAttestationApplicationId::PackageInfoVector packageInfoVector;
+        int32_t packageVectorSize =
+            mFdp->ConsumeIntegralInRange<int32_t>(kPackageVectorSizeMin, kPackageVectorSizeMax);
+        for (int32_t packageSize = 0; packageSize < packageVectorSize; ++packageSize) {
+            auto packageInfoData = initPackageInfoData(mFdp.get());
+            packageInfoVector.push_back(make_optional<KeyAttestationPackageInfo>(
+                String16((packageInfoData.packageName).c_str()), packageInfoData.versionCode,
+                packageInfoData.sharedSignaturesVector));
+        }
+        applicationId = KeyAttestationApplicationId(std::move(packageInfoVector));
+    } else {
+        auto packageInfoData = initPackageInfoData(mFdp.get());
+        applicationId = KeyAttestationApplicationId(make_optional<KeyAttestationPackageInfo>(
+            String16((packageInfoData.packageName).c_str()), packageInfoData.versionCode,
+            packageInfoData.sharedSignaturesVector));
+    }
+    invokeReadWriteParcel(&applicationId.value());
+}
+
+void KeystoreApplicationId::process(const uint8_t* data, size_t size) {
+    mFdp = std::make_unique<FuzzedDataProvider>(data, size);
+    invokeApplicationId();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    KeystoreApplicationId keystoreApplicationId;
+    keystoreApplicationId.process(data, size);
+    return 0;
+}
diff --git a/keystore/tests/fuzzer/keystoreAttestationId_fuzzer.cpp b/keystore/tests/fuzzer/keystoreAttestationId_fuzzer.cpp
new file mode 100644
index 0000000..581da46
--- /dev/null
+++ b/keystore/tests/fuzzer/keystoreAttestationId_fuzzer.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <keystore/keystore_attestation_id.h>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+    uint32_t uid = fdp.ConsumeIntegral<uint32_t>();
+    auto result = android::security::gather_attestation_application_id(uid);
+    result.isOk();
+    result.status();
+    result.value();
+    return 0;
+}
diff --git a/keystore/tests/fuzzer/keystoreCommon.h b/keystore/tests/fuzzer/keystoreCommon.h
new file mode 100644
index 0000000..7af3ba8
--- /dev/null
+++ b/keystore/tests/fuzzer/keystoreCommon.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef KEYSTORECOMMON_H
+#define KEYSTORECOMMON_H
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <keystore/KeyAttestationPackageInfo.h>
+#include <keystore/Signature.h>
+#include <vector>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+using namespace android;
+using namespace std;
+using ::content::pm::Signature;
+using ::security::keymaster::KeyAttestationPackageInfo;
+
+constexpr size_t kSignatureSizeMin = 1;
+constexpr size_t kSignatureSizeMax = 1000;
+constexpr size_t kRandomStringLength = 256;
+constexpr size_t kSignatureVectorSizeMin = 1;
+constexpr size_t kSignatureVectorSizeMax = 1000;
+
+struct PackageInfoData {
+    string packageName;
+    int64_t versionCode;
+    KeyAttestationPackageInfo::SharedSignaturesVector sharedSignaturesVector;
+};
+
+inline void invokeReadWriteParcel(Parcelable* obj) {
+    Parcel parcel;
+    obj->writeToParcel(&parcel);
+    parcel.setDataPosition(0);
+    obj->readFromParcel(&parcel);
+}
+
+inline vector<uint8_t> initSignatureData(FuzzedDataProvider* fdp) {
+    size_t signatureSize = fdp->ConsumeIntegralInRange(kSignatureSizeMin, kSignatureSizeMax);
+    vector<uint8_t> signatureData = fdp->ConsumeBytes<uint8_t>(signatureSize);
+    return signatureData;
+}
+
+inline PackageInfoData initPackageInfoData(FuzzedDataProvider* fdp) {
+    PackageInfoData packageInfoData;
+    packageInfoData.packageName = fdp->ConsumeRandomLengthString(kRandomStringLength);
+    packageInfoData.versionCode = fdp->ConsumeIntegral<int64_t>();
+    size_t signatureVectorSize =
+        fdp->ConsumeIntegralInRange(kSignatureVectorSizeMin, kSignatureVectorSizeMax);
+    KeyAttestationPackageInfo::SignaturesVector signatureVector;
+    for (size_t size = 0; size < signatureVectorSize; ++size) {
+        bool shouldUseParameterizedConstructor = fdp->ConsumeBool();
+        if (shouldUseParameterizedConstructor) {
+            vector<uint8_t> signatureData = initSignatureData(fdp);
+            signatureVector.push_back(make_optional<Signature>(signatureData));
+        } else {
+            signatureVector.push_back(std::nullopt);
+        }
+    }
+    packageInfoData.sharedSignaturesVector =
+        make_shared<KeyAttestationPackageInfo::SignaturesVector>(move(signatureVector));
+    return packageInfoData;
+}
+#endif  // KEYSTORECOMMON_H
diff --git a/keystore/tests/fuzzer/keystoreGetWifiHidl_fuzzer.cpp b/keystore/tests/fuzzer/keystoreGetWifiHidl_fuzzer.cpp
new file mode 100644
index 0000000..1e033c8
--- /dev/null
+++ b/keystore/tests/fuzzer/keystoreGetWifiHidl_fuzzer.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include <inttypes.h>
+#include <keystore/keystore_get.h>
+
+using namespace std;
+
+constexpr int32_t kMaxKeySize = 256;
+const string kValidStrKeyPrefix[] = {"USRSKEY_",
+                                     "PLATFORM_VPN_",
+                                     "USRPKEY_",
+                                     "CACERT_",
+                                     "VPN_"
+                                     "USRCERT_",
+                                     "WIFI_"};
+constexpr char kStrGrantKeyPrefix[] = "ks2_keystore-engine_grant_id:";
+constexpr char kStrKeySuffix[] = "LOCKDOWN_VPN";
+constexpr size_t kGrantIdSize = 20;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+    size_t keyLength = fdp.ConsumeIntegralInRange<size_t>(0, kMaxKeySize);
+    bool usePrefix = fdp.ConsumeBool();
+    string strKeyPrefix;
+    size_t strKeyPrefixLength = 0;
+    size_t strKeySuffixLength = min(fdp.remaining_bytes(), keyLength);
+    if (usePrefix) {
+        strKeyPrefix = fdp.PickValueInArray(kValidStrKeyPrefix);
+        strKeyPrefixLength = sizeof(strKeyPrefix);
+        strKeySuffixLength =
+            (strKeySuffixLength > strKeyPrefixLength) ? strKeySuffixLength - strKeyPrefixLength : 0;
+    }
+    string strKeySuffix =
+        fdp.ConsumeBool() ? string(kStrKeySuffix) : fdp.ConsumeBytesAsString(strKeySuffixLength);
+    string strKey;
+    strKey = usePrefix ? strKeyPrefix + strKeySuffix : strKeySuffix;
+    if (fdp.ConsumeBool()) {
+        uint64_t grant = fdp.ConsumeIntegral<uint64_t>();
+        char grantId[kGrantIdSize] = "";
+        snprintf(grantId, kGrantIdSize, "%" PRIx64, grant);
+        strKey = strKey + string(kStrGrantKeyPrefix) + grantId;
+    }
+    const char* key = strKey.c_str();
+    uint8_t* value = nullptr;
+    keystore_get(key, strlen(key), &value);
+    free(value);
+    return 0;
+}
diff --git a/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp b/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp
new file mode 100644
index 0000000..63899ff
--- /dev/null
+++ b/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "keystoreCommon.h"
+
+class KeystorePackageInfoFuzzer {
+  public:
+    void process(const uint8_t* data, size_t size);
+    ~KeystorePackageInfoFuzzer() {}
+
+  private:
+    void invokePackageInfo();
+    std::unique_ptr<FuzzedDataProvider> mFdp;
+};
+
+void KeystorePackageInfoFuzzer::invokePackageInfo() {
+    auto packageInfoData = initPackageInfoData(mFdp.get());
+    KeyAttestationPackageInfo packageInfo(String16((packageInfoData.packageName).c_str()),
+                                          packageInfoData.versionCode,
+                                          packageInfoData.sharedSignaturesVector);
+    invokeReadWriteParcel(&packageInfo);
+}
+
+void KeystorePackageInfoFuzzer::process(const uint8_t* data, size_t size) {
+    mFdp = std::make_unique<FuzzedDataProvider>(data, size);
+    invokePackageInfo();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    KeystorePackageInfoFuzzer keystorePackageInfoFuzzer;
+    keystorePackageInfoFuzzer.process(data, size);
+    return 0;
+}
diff --git a/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp b/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp
new file mode 100644
index 0000000..b8f8a73
--- /dev/null
+++ b/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "keystoreCommon.h"
+#include <keystore/Signature.h>
+
+class KeystoreSignatureFuzzer {
+  public:
+    void process(const uint8_t* data, size_t size);
+    ~KeystoreSignatureFuzzer() {}
+
+  private:
+    void invokeSignature();
+    std::unique_ptr<FuzzedDataProvider> mFdp;
+};
+
+void KeystoreSignatureFuzzer::invokeSignature() {
+    std::optional<Signature> signature;
+    bool shouldUseParameterizedConstructor = mFdp->ConsumeBool();
+    if (shouldUseParameterizedConstructor) {
+        std::vector<uint8_t> signatureData = initSignatureData(mFdp.get());
+        signature = Signature(signatureData);
+    } else {
+        signature = Signature();
+    }
+    invokeReadWriteParcel(&signature.value());
+}
+
+void KeystoreSignatureFuzzer::process(const uint8_t* data, size_t size) {
+    mFdp = std::make_unique<FuzzedDataProvider>(data, size);
+    invokeSignature();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    KeystoreSignatureFuzzer keystoreSignatureFuzzer;
+    keystoreSignatureFuzzer.process(data, size);
+    return 0;
+}
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index e8cdb6c..520237a 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -25,9 +25,11 @@
     name: "libkeystore2_defaults",
     crate_name: "keystore2",
     srcs: ["src/lib.rs"],
+    defaults: [
+        "keymint_use_latest_hal_aidl_rust",
+    ],
 
     rustlibs: [
-        "android.hardware.security.keymint-V1-rust",
         "android.hardware.security.secureclock-V1-rust",
         "android.hardware.security.sharedsecret-V1-rust",
         "android.os.permissions_aidl-rust",
@@ -37,7 +39,7 @@
         "android.security.maintenance-rust",
         "android.security.metrics-rust",
         "android.security.remoteprovisioning-rust",
-        "android.system.keystore2-V1-rust",
+        "android.system.keystore2-V2-rust",
         "libanyhow",
         "libbinder_rs",
         "libkeystore2_aaid-rust",
@@ -52,6 +54,8 @@
         "liblog_rust",
         "librand",
         "librustutils",
+        "libserde",
+        "libserde_cbor",
         "libthiserror",
     ],
     shared_libs: [
@@ -149,28 +153,6 @@
     // selection available in the build system.
     prefer_rlib: true,
 
-    // TODO(b/187412695)
-    // This is a hack to work around the build system not installing
-    // dynamic dependencies of rlibs to the device. This section should
-    // be removed once that works correctly.
-    shared_libs: [
-        "android.hardware.confirmationui@1.0",
-        "android.hardware.security.sharedsecret-V1-ndk",
-        "android.security.compat-ndk",
-        "libc",
-        "libdl_android",
-        "libdl",
-        "libkeymint",
-        "libkeystore2_aaid",
-        "libkeystore2_apc_compat",
-        "libkeystore2_crypto",
-        "libkm_compat_service",
-        "libkm_compat",
-        "libm",
-        "libstatspull",
-        "libstatssocket",
-    ],
-
     vintf_fragments: ["android.system.keystore2-service.xml"],
 
     required: ["keystore_cli_v2"],
diff --git a/keystore2/TEST_MAPPING b/keystore2/TEST_MAPPING
index 127ff1e..049adc7 100644
--- a/keystore2/TEST_MAPPING
+++ b/keystore2/TEST_MAPPING
@@ -14,6 +14,19 @@
     },
     {
       "name": "CtsIdentityTestCases"
+    },
+    {
+      "name": "CtsKeystoreTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.RequiresDevice"
+        }
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "CtsKeystorePerformanceTestCases"
     }
   ]
 }
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
index 4a7b7b4..7eb2b83 100644
--- a/keystore2/aidl/Android.bp
+++ b/keystore2/aidl/Android.bp
@@ -24,7 +24,7 @@
 aidl_interface {
     name: "android.security.attestationmanager",
     srcs: [ "android/security/attestationmanager/*.aidl", ],
-    imports: [ "android.hardware.security.keymint-V1" ],
+    imports: [ "android.hardware.security.keymint-V2" ],
     unstable: true,
     backend: {
         java: {
@@ -45,7 +45,7 @@
     name: "android.security.authorization",
     srcs: [ "android/security/authorization/*.aidl" ],
     imports: [
-        "android.hardware.security.keymint-V1",
+        "android.hardware.security.keymint-V2",
         "android.hardware.security.secureclock-V1",
     ],
     unstable: true,
@@ -86,7 +86,7 @@
     name: "android.security.compat",
     srcs: [ "android/security/compat/*.aidl" ],
     imports: [
-        "android.hardware.security.keymint-V1",
+        "android.hardware.security.keymint-V2",
         "android.hardware.security.secureclock-V1",
         "android.hardware.security.sharedsecret-V1",
     ],
@@ -110,7 +110,7 @@
     name: "android.security.remoteprovisioning",
     srcs: [ "android/security/remoteprovisioning/*.aidl" ],
     imports: [
-        "android.hardware.security.keymint-V1",
+        "android.hardware.security.keymint-V2",
     ],
     unstable: true,
     backend: {
@@ -132,7 +132,7 @@
     name: "android.security.maintenance",
     srcs: [ "android/security/maintenance/*.aidl" ],
     imports: [
-        "android.system.keystore2-V1",
+        "android.system.keystore2-V2",
     ],
     unstable: true,
     backend: {
@@ -173,7 +173,7 @@
     name: "android.security.metrics",
     srcs: [ "android/security/metrics/*.aidl" ],
     imports: [
-        "android.system.keystore2-V1",
+        "android.system.keystore2-V2",
     ],
     unstable: true,
     backend: {
@@ -191,3 +191,19 @@
     },
 }
 
+// cc_defaults that includes the latest Keystore2 AIDL library.
+// Modules that depend on KeyMint directly can include this cc_defaults to avoid
+// managing dependency versions explicitly.
+cc_defaults {
+    name: "keystore2_use_latest_aidl_ndk_static",
+    static_libs: [
+        "android.system.keystore2-V2-ndk",
+    ],
+}
+
+cc_defaults {
+    name: "keystore2_use_latest_aidl_ndk_shared",
+    shared_libs: [
+        "android.system.keystore2-V2-ndk",
+    ],
+}
diff --git a/keystore2/aidl/android/security/metrics/EcCurve.aidl b/keystore2/aidl/android/security/metrics/EcCurve.aidl
index b190d83..7b1a5a2 100644
--- a/keystore2/aidl/android/security/metrics/EcCurve.aidl
+++ b/keystore2/aidl/android/security/metrics/EcCurve.aidl
@@ -29,4 +29,5 @@
     P_256 = 2,
     P_384 = 3,
     P_521 = 4,
+    CURVE_25519 = 5,
 }
\ No newline at end of file
diff --git a/keystore2/android.system.keystore2-service.xml b/keystore2/android.system.keystore2-service.xml
index 6b8d0cb..20c2fba 100644
--- a/keystore2/android.system.keystore2-service.xml
+++ b/keystore2/android.system.keystore2-service.xml
@@ -1,6 +1,7 @@
 <manifest version="1.0" type="framework">
     <hal format="aidl">
         <name>android.system.keystore2</name>
+        <version>2</version>
         <interface>
             <name>IKeystoreService</name>
             <instance>default</instance>
diff --git a/keystore2/src/crypto/error.rs b/keystore2/src/crypto/error.rs
index a369012..c6476f9 100644
--- a/keystore2/src/crypto/error.rs
+++ b/keystore2/src/crypto/error.rs
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 //! This module implements Error for the keystore2_crypto library.
+use crate::zvec;
 
 /// Crypto specific error codes.
 #[derive(Debug, thiserror::Error, Eq, PartialEq)]
@@ -93,4 +94,8 @@
     /// This is returned if the C implementation of extractSubjectFromCertificate failed.
     #[error("Failed to extract certificate subject.")]
     ExtractSubjectFailed,
+
+    /// Zvec error.
+    #[error(transparent)]
+    ZVec(#[from] zvec::Error),
 }
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index 5f8a2ef..92da965 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -16,7 +16,7 @@
 //! Keystore 2.0.
 
 mod error;
-mod zvec;
+pub mod zvec;
 pub use error::Error;
 use keystore2_crypto_bindgen::{
     extractSubjectFromCertificate, generateKeyFromPassword, randomBytes, AES_gcm_decrypt,
diff --git a/keystore2/src/crypto/zvec.rs b/keystore2/src/crypto/zvec.rs
index 78b474e..5a173c3 100644
--- a/keystore2/src/crypto/zvec.rs
+++ b/keystore2/src/crypto/zvec.rs
@@ -12,7 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::error::Error;
+//! Implements ZVec, a vector that is mlocked during its lifetime and zeroed
+//! when dropped.
+
 use nix::sys::mman::{mlock, munlock};
 use std::convert::TryFrom;
 use std::fmt;
@@ -29,6 +31,14 @@
     len: usize,
 }
 
+/// ZVec specific error codes.
+#[derive(Debug, thiserror::Error, Eq, PartialEq)]
+pub enum Error {
+    /// Underlying libc error.
+    #[error(transparent)]
+    NixError(#[from] nix::Error),
+}
+
 impl ZVec {
     /// Create a ZVec with the given size.
     pub fn new(size: usize) -> Result<Self, Error> {
@@ -48,6 +58,14 @@
             self.len = len;
         }
     }
+
+    /// Attempts to make a clone of the Zvec. This may fail due trying to mlock
+    /// the new memory region.
+    pub fn try_clone(&self) -> Result<Self, Error> {
+        let mut result = Self::new(self.len())?;
+        result[..].copy_from_slice(&self[..]);
+        Ok(result)
+    }
 }
 
 impl Drop for ZVec {
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 997e739..2407525 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -629,7 +629,7 @@
 
         let hat_and_last_off_body = if need_auth_token {
             let hat_and_last_off_body = Self::find_auth_token(|hat: &AuthTokenEntry| {
-                if let (Some(auth_type), true) = (user_auth_type, has_sids) {
+                if let (Some(auth_type), true) = (user_auth_type, timeout_bound) {
                     hat.satisfies(&user_secure_ids, auth_type)
                 } else {
                     unlocked_device_required
diff --git a/keystore2/src/km_compat/Android.bp b/keystore2/src/km_compat/Android.bp
index 32406ae..806f3dc 100644
--- a/keystore2/src/km_compat/Android.bp
+++ b/keystore2/src/km_compat/Android.bp
@@ -25,9 +25,10 @@
     name: "libkeystore2_km_compat",
     crate_name: "keystore2_km_compat",
     srcs: ["lib.rs"],
-
+    defaults: [
+        "keymint_use_latest_hal_aidl_rust",
+    ],
     rustlibs: [
-        "android.hardware.security.keymint-V1-rust",
         "android.security.compat-rust",
     ],
     shared_libs: [
@@ -41,8 +42,10 @@
     srcs: ["lib.rs"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
+    defaults: [
+        "keymint_use_latest_hal_aidl_rust",
+    ],
     rustlibs: [
-        "android.hardware.security.keymint-V1-rust",
         "android.security.compat-rust",
     ],
     shared_libs: [
@@ -53,15 +56,17 @@
 cc_library {
     name: "libkm_compat",
     srcs: ["km_compat.cpp"],
+    defaults: [
+        "keymint_use_latest_hal_aidl_ndk_shared",
+        "keystore2_use_latest_aidl_ndk_shared",
+    ],
     shared_libs: [
         "android.hardware.keymaster@3.0",
         "android.hardware.keymaster@4.0",
         "android.hardware.keymaster@4.1",
-        "android.hardware.security.keymint-V1-ndk",
         "android.hardware.security.secureclock-V1-ndk",
         "android.hardware.security.sharedsecret-V1-ndk",
         "android.security.compat-ndk",
-        "android.system.keystore2-V1-ndk",
         "libbase",
         "libbinder_ndk",
         "libcrypto",
@@ -77,8 +82,10 @@
 cc_library {
     name: "libkm_compat_service",
     srcs: ["km_compat_service.cpp"],
+    defaults: [
+        "keymint_use_latest_hal_aidl_ndk_shared",
+    ],
     shared_libs: [
-        "android.hardware.security.keymint-V1-ndk",
         "android.hardware.security.secureclock-V1-ndk",
         "android.hardware.security.sharedsecret-V1-ndk",
         "android.security.compat-ndk",
@@ -103,15 +110,17 @@
         "parameter_conversion_test.cpp",
         "slot_test.cpp",
     ],
+    defaults: [
+        "keymint_use_latest_hal_aidl_ndk_shared",
+        "keystore2_use_latest_aidl_ndk_shared",
+    ],
     shared_libs: [
         "android.hardware.keymaster@3.0",
         "android.hardware.keymaster@4.0",
         "android.hardware.keymaster@4.1",
-        "android.hardware.security.keymint-V1-ndk",
         "android.hardware.security.secureclock-V1-ndk",
         "android.hardware.security.sharedsecret-V1-ndk",
         "android.security.compat-ndk",
-        "android.system.keystore2-V1-ndk",
         "libbase",
         "libbinder_ndk",
         "libcrypto",
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index 40ca554..bb60047 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -762,7 +762,21 @@
     return convertErrorCode(errorCode);
 }
 
-ScopedAStatus KeyMintOperation::update(const std::vector<uint8_t>& input,
+void KeyMintOperation::setUpdateBuffer(std::vector<uint8_t> data) {
+    mUpdateBuffer = std::move(data);
+}
+
+const std::vector<uint8_t>&
+KeyMintOperation::getExtendedUpdateBuffer(const std::vector<uint8_t>& suffix) {
+    if (mUpdateBuffer.empty()) {
+        return suffix;
+    } else {
+        mUpdateBuffer.insert(mUpdateBuffer.end(), suffix.begin(), suffix.end());
+        return mUpdateBuffer;
+    }
+}
+
+ScopedAStatus KeyMintOperation::update(const std::vector<uint8_t>& input_raw,
                                        const std::optional<HardwareAuthToken>& optAuthToken,
                                        const std::optional<TimeStampToken>& optTimeStampToken,
                                        std::vector<uint8_t>* out_output) {
@@ -772,8 +786,10 @@
     size_t inputPos = 0;
     *out_output = {};
     KMV1::ErrorCode errorCode = KMV1::ErrorCode::OK;
+    auto input = getExtendedUpdateBuffer(input_raw);
 
     while (inputPos < input.size() && errorCode == KMV1::ErrorCode::OK) {
+        uint32_t consumed = 0;
         auto result =
             mDevice->update(mOperationHandle, {} /* inParams */,
                             {input.begin() + inputPos, input.end()}, authToken, verificationToken,
@@ -781,13 +797,22 @@
                                 const hidl_vec<uint8_t>& output) {
                                 errorCode = convert(error);
                                 out_output->insert(out_output->end(), output.begin(), output.end());
-                                inputPos += inputConsumed;
+                                consumed = inputConsumed;
                             });
 
         if (!result.isOk()) {
             LOG(ERROR) << __func__ << " transaction failed. " << result.description();
             errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
         }
+
+        if (errorCode == KMV1::ErrorCode::OK && consumed == 0) {
+            // Some very old KM implementations do not buffer sub blocks in certain block modes,
+            // instead, the simply return consumed == 0. So we buffer the input here in the
+            // hope that we complete the bock in a future call to update.
+            setUpdateBuffer({input.begin() + inputPos, input.end()});
+            return convertErrorCode(errorCode);
+        }
+        inputPos += consumed;
     }
 
     if (errorCode != KMV1::ErrorCode::OK) mOperationSlot.freeSlot();
@@ -802,7 +827,8 @@
                          const std::optional<TimeStampToken>& in_timeStampToken,
                          const std::optional<std::vector<uint8_t>>& in_confirmationToken,
                          std::vector<uint8_t>* out_output) {
-    auto input = in_input.value_or(std::vector<uint8_t>());
+    auto input_raw = in_input.value_or(std::vector<uint8_t>());
+    auto input = getExtendedUpdateBuffer(input_raw);
     auto signature = in_signature.value_or(std::vector<uint8_t>());
     V4_0_HardwareAuthToken authToken = convertAuthTokenToLegacy(in_authToken);
     V4_0_VerificationToken verificationToken = convertTimestampTokenToLegacy(in_timeStampToken);
diff --git a/keystore2/src/km_compat/km_compat.h b/keystore2/src/km_compat/km_compat.h
index 2d892da..70c7b86 100644
--- a/keystore2/src/km_compat/km_compat.h
+++ b/keystore2/src/km_compat/km_compat.h
@@ -140,11 +140,6 @@
 };
 
 class KeyMintOperation : public aidl::android::hardware::security::keymint::BnKeyMintOperation {
-  private:
-    ::android::sp<Keymaster> mDevice;
-    uint64_t mOperationHandle;
-    OperationSlot mOperationSlot;
-
   public:
     KeyMintOperation(::android::sp<Keymaster> device, uint64_t operationHandle,
                      OperationSlots* slots, bool isActive)
@@ -168,6 +163,25 @@
                          std::vector<uint8_t>* output) override;
 
     ScopedAStatus abort();
+
+  private:
+    /**
+     * Sets mUpdateBuffer to the given value.
+     * @param data
+     */
+    void setUpdateBuffer(std::vector<uint8_t> data);
+    /**
+     * If mUpdateBuffer is not empty, suffix is appended to mUpdateBuffer, and a reference to
+     * mUpdateBuffer is returned. Otherwise a reference to suffix is returned.
+     * @param suffix
+     * @return
+     */
+    const std::vector<uint8_t>& getExtendedUpdateBuffer(const std::vector<uint8_t>& suffix);
+
+    std::vector<uint8_t> mUpdateBuffer;
+    ::android::sp<Keymaster> mDevice;
+    uint64_t mOperationHandle;
+    OperationSlot mOperationSlot;
 };
 
 class SharedSecret : public aidl::android::hardware::security::sharedsecret::BnSharedSecret {
diff --git a/keystore2/src/km_compat/km_compat_type_conversion.h b/keystore2/src/km_compat/km_compat_type_conversion.h
index de09477..33248a4 100644
--- a/keystore2/src/km_compat/km_compat_type_conversion.h
+++ b/keystore2/src/km_compat/km_compat_type_conversion.h
@@ -16,6 +16,9 @@
 
 #pragma once
 
+#include <optional>
+
+#include <aidl/android/hardware/security/keymint/EcCurve.h>
 #include <aidl/android/hardware/security/keymint/ErrorCode.h>
 #include <keymasterV4_1/keymaster_tags.h>
 #include <keymint_support/keymint_tags.h>
@@ -278,7 +281,7 @@
     }
 }
 
-static V4_0::EcCurve convert(KMV1::EcCurve e) {
+static std::optional<V4_0::EcCurve> convert(KMV1::EcCurve e) {
     switch (e) {
     case KMV1::EcCurve::P_224:
         return V4_0::EcCurve::P_224;
@@ -288,7 +291,11 @@
         return V4_0::EcCurve::P_384;
     case KMV1::EcCurve::P_521:
         return V4_0::EcCurve::P_521;
+    case KMV1::EcCurve::CURVE_25519:
+        // KeyMaster did not support curve 25519
+        return std::nullopt;
     }
+    return std::nullopt;
 }
 
 static KMV1::EcCurve convert(V4_0::EcCurve e) {
@@ -490,7 +497,9 @@
         break;
     case KMV1::Tag::EC_CURVE:
         if (auto v = KMV1::authorizationValue(KMV1::TAG_EC_CURVE, kp)) {
-            return V4_0::makeKeyParameter(V4_0::TAG_EC_CURVE, convert(v->get()));
+            if (auto curve = convert(v->get())) {
+                return V4_0::makeKeyParameter(V4_0::TAG_EC_CURVE, curve.value());
+            }
         }
         break;
     case KMV1::Tag::RSA_PUBLIC_EXPONENT:
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index 3f6cf36..57abc26 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -215,6 +215,8 @@
 
     fn migrate_key_namespace(source: &KeyDescriptor, destination: &KeyDescriptor) -> Result<()> {
         let caller_uid = ThreadState::get_calling_uid();
+        let migrate_any_key_permission =
+            check_keystore_permission(KeystorePerm::MigrateAnyKey).is_ok();
 
         DB.with(|db| {
             let key_id_guard = match source.domain {
@@ -227,9 +229,12 @@
                                 KeyEntryLoadBits::NONE,
                                 caller_uid,
                                 |k, av| {
-                                    check_key_permission(KeyPerm::Use, k, &av)?;
-                                    check_key_permission(KeyPerm::Delete, k, &av)?;
-                                    check_key_permission(KeyPerm::Grant, k, &av)
+                                    if !migrate_any_key_permission {
+                                        check_key_permission(KeyPerm::Use, k, &av)?;
+                                        check_key_permission(KeyPerm::Delete, k, &av)?;
+                                        check_key_permission(KeyPerm::Grant, k, &av)?;
+                                    }
+                                    Ok(())
                                 },
                             )
                         })
@@ -245,7 +250,10 @@
             };
 
             db.borrow_mut().migrate_key_namespace(key_id_guard, destination, caller_uid, |k| {
-                check_key_permission(KeyPerm::Rebind, k, &None)
+                if !migrate_any_key_permission {
+                    check_key_permission(KeyPerm::Rebind, k, &None)?;
+                }
+                Ok(())
             })
         })
     }
diff --git a/keystore2/src/metrics_store.rs b/keystore2/src/metrics_store.rs
index 741d65e..b18d84c 100644
--- a/keystore2/src/metrics_store.rs
+++ b/keystore2/src/metrics_store.rs
@@ -17,7 +17,7 @@
 //!    stores them in an in-memory store.
 //! 2. Returns the collected metrics when requested by the statsd proxy.
 
-use crate::error::get_error_code;
+use crate::error::{get_error_code, Error};
 use crate::globals::DB;
 use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
 use crate::operation::Outcome;
@@ -44,6 +44,7 @@
     RkpPoolStats::RkpPoolStats, SecurityLevel::SecurityLevel as MetricsSecurityLevel,
     Storage::Storage as MetricsStorage,
 };
+use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
 use anyhow::{Context, Result};
 use lazy_static::lazy_static;
 use rustutils::system_properties::PropertyWatcherError;
@@ -287,6 +288,7 @@
                     EcCurve::P_256 => MetricsEcCurve::P_256,
                     EcCurve::P_384 => MetricsEcCurve::P_384,
                     EcCurve::P_521 => MetricsEcCurve::P_521,
+                    EcCurve::CURVE_25519 => MetricsEcCurve::CURVE_25519,
                     _ => MetricsEcCurve::EC_CURVE_UNSPECIFIED,
                 }
             }
@@ -560,10 +562,14 @@
 fn pull_attestation_pool_stats() -> Result<Vec<KeystoreAtom>> {
     let mut atoms = Vec::<KeystoreAtom>::new();
     for sec_level in &[SecurityLevel::TRUSTED_ENVIRONMENT, SecurityLevel::STRONGBOX] {
+        // set the expired_by date to be three days from now
         let expired_by = SystemTime::now()
+            .checked_add(Duration::from_secs(60 * 60 * 24 * 3))
+            .ok_or(Error::Rc(ResponseCode::SYSTEM_ERROR))
+            .context("In pull_attestation_pool_stats: Failed to compute expired by system time.")?
             .duration_since(UNIX_EPOCH)
-            .unwrap_or_else(|_| Duration::new(0, 0))
-            .as_secs() as i64;
+            .context("In pull_attestation_pool_stats: Failed to compute expired by duration.")?
+            .as_millis() as i64;
 
         let result = get_pool_status(expired_by, *sec_level);
 
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index f280341..e6d61b0 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -145,6 +145,10 @@
         /// Checked when IKeystoreMaintenance::deleteAllKeys is called.
         #[selinux(name = delete_all_keys)]
         DeleteAllKeys,
+        /// Checked when migrating any key from any namespace to any other namespace. It was
+        /// introduced for migrating keys when an app leaves a sharedUserId.
+        #[selinux(name = migrate_any_key)]
+        MigrateAnyKey,
     }
 );
 
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index a19462b..66e1988 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -38,6 +38,8 @@
 };
 use anyhow::{Context, Result};
 use keystore2_crypto::parse_subject_from_certificate;
+use serde_cbor::Value;
+use std::collections::BTreeMap;
 use std::sync::atomic::{AtomicBool, Ordering};
 
 use crate::database::{CertificateChain, KeystoreDB, Uuid};
@@ -56,6 +58,11 @@
     is_hal_present: AtomicBool,
 }
 
+static COSE_KEY_XCOORD: Value = Value::Integer(-2);
+static COSE_KEY_YCOORD: Value = Value::Integer(-3);
+static COSE_MAC0_LEN: usize = 4;
+static COSE_MAC0_PAYLOAD: usize = 2;
+
 impl RemProvState {
     /// Creates a RemProvState struct.
     pub fn new(security_level: SecurityLevel, km_uuid: Uuid) -> Self {
@@ -261,6 +268,27 @@
         Ok(BnRemoteProvisioning::new_binder(result, BinderFeatures::default()))
     }
 
+    fn extract_payload_from_cose_mac(data: &[u8]) -> Result<Value> {
+        let cose_mac0: Vec<Value> = serde_cbor::from_slice(data).context(
+            "In extract_payload_from_cose_mac: COSE_Mac0 returned from IRPC cannot be parsed",
+        )?;
+        if cose_mac0.len() != COSE_MAC0_LEN {
+            return Err(error::Error::sys()).context(format!(
+                "In extract_payload_from_cose_mac: COSE_Mac0 has improper length. \
+                    Expected: {}, Actual: {}",
+                COSE_MAC0_LEN,
+                cose_mac0.len(),
+            ));
+        }
+        match &cose_mac0[COSE_MAC0_PAYLOAD] {
+            Value::Bytes(key) => Ok(serde_cbor::from_slice(key)
+                .context("In extract_payload_from_cose_mac: COSE_Mac0 payload is malformed.")?),
+            _ => Err(error::Error::sys()).context(
+                "In extract_payload_from_cose_mac: COSE_Mac0 payload is the wrong type.",
+            )?,
+        }
+    }
+
     /// Generates a CBOR blob which will be assembled by the calling code into a larger
     /// CBOR blob intended for delivery to a provisioning serever. This blob will contain
     /// `num_csr` certificate signing requests for attestation keys generated in the TEE,
@@ -290,7 +318,7 @@
                 .map(|key| MacedPublicKey { macedKey: key.to_vec() })
                 .collect())
         })?;
-        let mut mac = map_rem_prov_error(dev.generateCertificateRequest(
+        let mac = map_rem_prov_error(dev.generateCertificateRequest(
             test_mode,
             &keys_to_sign,
             eek,
@@ -299,30 +327,16 @@
             protected_data,
         ))
         .context("In generate_csr: Failed to generate csr")?;
-        // TODO(b/180392379): Replace this manual CBOR generation with the cbor-serde crate as well.
-        //                    This generates an array consisting of the mac and the public key Maps.
-        //                    Just generate the actual MacedPublicKeys structure when the crate is
-        //                    available.
-        let mut cose_mac_0: Vec<u8> = vec![
-            (0b100_00000 | (keys_to_sign.len() + 1)) as u8,
-            0b010_11000, // mac
-            (mac.len() as u8),
-        ];
-        cose_mac_0.append(&mut mac);
-        // If this is a test mode key, there is an extra 6 bytes added as an additional entry in
-        // the COSE_Key struct to denote that.
-        let test_mode_entry_shift = if test_mode { 0 } else { 6 };
-        let byte_dist_mac0_payload = 8;
-        let cose_key_size = 83 - test_mode_entry_shift;
+        let mut mac_and_keys: Vec<Value> = vec![Value::from(mac)];
         for maced_public_key in keys_to_sign {
-            if maced_public_key.macedKey.len() > cose_key_size + byte_dist_mac0_payload {
-                cose_mac_0.extend_from_slice(
-                    &maced_public_key.macedKey
-                        [byte_dist_mac0_payload..cose_key_size + byte_dist_mac0_payload],
-                );
-            }
+            mac_and_keys.push(
+                Self::extract_payload_from_cose_mac(&maced_public_key.macedKey)
+                    .context("In generate_csr: Failed to get the payload from the COSE_Mac0")?,
+            )
         }
-        Ok(cose_mac_0)
+        let cbor_array: Value = Value::Array(mac_and_keys);
+        serde_cbor::to_vec(&cbor_array)
+            .context("In generate_csr: Failed to serialize the mac and keys array")
     }
 
     /// Provisions a certificate chain for a key whose CSR was included in generate_csr. The
@@ -351,6 +365,66 @@
         })
     }
 
+    fn parse_cose_mac0_for_coords(data: &[u8]) -> Result<Vec<u8>> {
+        let cose_mac0: Vec<Value> = serde_cbor::from_slice(data).context(
+            "In parse_cose_mac0_for_coords: COSE_Mac0 returned from IRPC cannot be parsed",
+        )?;
+        if cose_mac0.len() != COSE_MAC0_LEN {
+            return Err(error::Error::sys()).context(format!(
+                "In parse_cose_mac0_for_coords: COSE_Mac0 has improper length. \
+                    Expected: {}, Actual: {}",
+                COSE_MAC0_LEN,
+                cose_mac0.len(),
+            ));
+        }
+        let cose_key: BTreeMap<Value, Value> = match &cose_mac0[COSE_MAC0_PAYLOAD] {
+            Value::Bytes(key) => serde_cbor::from_slice(key)
+                .context("In parse_cose_mac0_for_coords: COSE_Key is malformed.")?,
+            _ => Err(error::Error::sys())
+                .context("In parse_cose_mac0_for_coords: COSE_Mac0 payload is the wrong type.")?,
+        };
+        if !cose_key.contains_key(&COSE_KEY_XCOORD) || !cose_key.contains_key(&COSE_KEY_YCOORD) {
+            return Err(error::Error::sys()).context(
+                "In parse_cose_mac0_for_coords: \
+                COSE_Key returned from IRPC is lacking required fields",
+            );
+        }
+        let mut raw_key: Vec<u8> = vec![0; 64];
+        match &cose_key[&COSE_KEY_XCOORD] {
+            Value::Bytes(x_coord) if x_coord.len() == 32 => {
+                raw_key[0..32].clone_from_slice(x_coord)
+            }
+            Value::Bytes(x_coord) => {
+                return Err(error::Error::sys()).context(format!(
+                "In parse_cose_mac0_for_coords: COSE_Key X-coordinate is not the right length. \
+                Expected: 32; Actual: {}",
+                    x_coord.len()
+                ))
+            }
+            _ => {
+                return Err(error::Error::sys())
+                    .context("In parse_cose_mac0_for_coords: COSE_Key X-coordinate is not a bstr")
+            }
+        }
+        match &cose_key[&COSE_KEY_YCOORD] {
+            Value::Bytes(y_coord) if y_coord.len() == 32 => {
+                raw_key[32..64].clone_from_slice(y_coord)
+            }
+            Value::Bytes(y_coord) => {
+                return Err(error::Error::sys()).context(format!(
+                "In parse_cose_mac0_for_coords: COSE_Key Y-coordinate is not the right length. \
+                Expected: 32; Actual: {}",
+                    y_coord.len()
+                ))
+            }
+            _ => {
+                return Err(error::Error::sys())
+                    .context("In parse_cose_mac0_for_coords: COSE_Key Y-coordinate is not a bstr")
+            }
+        }
+        Ok(raw_key)
+    }
+
     /// Submits a request to the Remote Provisioner HAL to generate a signing key pair.
     /// `is_test_mode` indicates whether or not the returned public key should be marked as being
     /// for testing in order to differentiate them from private keys. If the call is successful,
@@ -362,18 +436,8 @@
         let priv_key =
             map_rem_prov_error(dev.generateEcdsaP256KeyPair(is_test_mode, &mut maced_key))
                 .context("In generate_key_pair: Failed to generated ECDSA keypair.")?;
-        // TODO(b/180392379): This is a brittle hack that relies on the consistent formatting of
-        //                    the returned CBOR blob in order to extract the public key.
-        let data = &maced_key.macedKey;
-        if data.len() < 85 {
-            return Err(error::Error::sys()).context(concat!(
-                "In generate_key_pair: CBOR blob returned from",
-                "RemotelyProvisionedComponent is definitely malformatted or empty."
-            ));
-        }
-        let mut raw_key: Vec<u8> = vec![0; 64];
-        raw_key[0..32].clone_from_slice(&data[18..18 + 32]);
-        raw_key[32..64].clone_from_slice(&data[53..53 + 32]);
+        let raw_key = Self::parse_cose_mac0_for_coords(&maced_key.macedKey)
+            .context("In generate_key_pair: Failed to parse raw key")?;
         DB.with::<_, Result<()>>(|db| {
             let mut db = db.borrow_mut();
             db.create_attestation_key_entry(&maced_key.macedKey, &raw_key, &priv_key, &uuid)
@@ -489,3 +553,108 @@
         map_or_log_err(self.delete_all_keys(), Ok)
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use serde_cbor::Value;
+    use std::collections::BTreeMap;
+
+    #[test]
+    fn test_parse_cose_mac0_for_coords_raw_bytes() -> Result<()> {
+        let cose_mac0: Vec<u8> = vec![
+            0x84, 0x01, 0x02, 0x58, 0x4D, 0xA5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58,
+            0x20, 0x1A, 0xFB, 0xB2, 0xD9, 0x9D, 0xF6, 0x2D, 0xF0, 0xC3, 0xA8, 0xFC, 0x7E, 0xC9,
+            0x21, 0x26, 0xED, 0xB5, 0x4A, 0x98, 0x9B, 0xF3, 0x0D, 0x91, 0x3F, 0xC6, 0x42, 0x5C,
+            0x43, 0x22, 0xC8, 0xEE, 0x03, 0x22, 0x58, 0x20, 0x40, 0xB3, 0x9B, 0xFC, 0x47, 0x95,
+            0x90, 0xA7, 0x5C, 0x5A, 0x16, 0x31, 0x34, 0xAF, 0x0C, 0x5B, 0xF2, 0xB2, 0xD8, 0x2A,
+            0xA3, 0xB3, 0x1A, 0xB4, 0x4C, 0xA6, 0x3B, 0xE7, 0x22, 0xEC, 0x41, 0xDC, 0x03,
+        ];
+        let raw_key = RemoteProvisioningService::parse_cose_mac0_for_coords(&cose_mac0)?;
+        assert_eq!(
+            raw_key,
+            vec![
+                0x1A, 0xFB, 0xB2, 0xD9, 0x9D, 0xF6, 0x2D, 0xF0, 0xC3, 0xA8, 0xFC, 0x7E, 0xC9, 0x21,
+                0x26, 0xED, 0xB5, 0x4A, 0x98, 0x9B, 0xF3, 0x0D, 0x91, 0x3F, 0xC6, 0x42, 0x5C, 0x43,
+                0x22, 0xC8, 0xEE, 0x03, 0x40, 0xB3, 0x9B, 0xFC, 0x47, 0x95, 0x90, 0xA7, 0x5C, 0x5A,
+                0x16, 0x31, 0x34, 0xAF, 0x0C, 0x5B, 0xF2, 0xB2, 0xD8, 0x2A, 0xA3, 0xB3, 0x1A, 0xB4,
+                0x4C, 0xA6, 0x3B, 0xE7, 0x22, 0xEC, 0x41, 0xDC,
+            ]
+        );
+        Ok(())
+    }
+
+    #[test]
+    fn test_parse_cose_mac0_for_coords_constructed_mac() -> Result<()> {
+        let x_coord: Vec<u8> = vec![0; 32];
+        let y_coord: Vec<u8> = vec![1; 32];
+        let mut expected_key: Vec<u8> = Vec::new();
+        expected_key.extend(&x_coord);
+        expected_key.extend(&y_coord);
+        let key_map: BTreeMap<Value, Value> = BTreeMap::from([
+            (Value::Integer(1), Value::Integer(2)),
+            (Value::Integer(3), Value::Integer(-7)),
+            (Value::Integer(-1), Value::Integer(1)),
+            (Value::Integer(-2), Value::Bytes(x_coord)),
+            (Value::Integer(-3), Value::Bytes(y_coord)),
+        ]);
+        let cose_mac0: Vec<Value> = vec![
+            Value::Integer(0),
+            Value::Integer(1),
+            Value::from(serde_cbor::to_vec(&key_map)?),
+            Value::Integer(2),
+        ];
+        let raw_key = RemoteProvisioningService::parse_cose_mac0_for_coords(&serde_cbor::to_vec(
+            &Value::from(cose_mac0),
+        )?)?;
+        assert_eq!(expected_key, raw_key);
+        Ok(())
+    }
+
+    #[test]
+    fn test_extract_payload_from_cose_mac() -> Result<()> {
+        let key_map = Value::Map(BTreeMap::from([(Value::Integer(1), Value::Integer(2))]));
+        let payload = Value::Bytes(serde_cbor::to_vec(&key_map)?);
+        let cose_mac0 =
+            Value::Array(vec![Value::Integer(0), Value::Integer(1), payload, Value::Integer(3)]);
+        let extracted_map = RemoteProvisioningService::extract_payload_from_cose_mac(
+            &serde_cbor::to_vec(&cose_mac0)?,
+        )?;
+        assert_eq!(key_map, extracted_map);
+        Ok(())
+    }
+
+    #[test]
+    fn test_extract_payload_from_cose_mac_fails_malformed_payload() -> Result<()> {
+        let payload = Value::Bytes(vec![5; 10]);
+        let cose_mac0 =
+            Value::Array(vec![Value::Integer(0), Value::Integer(1), payload, Value::Integer(3)]);
+        let extracted_payload = RemoteProvisioningService::extract_payload_from_cose_mac(
+            &serde_cbor::to_vec(&cose_mac0)?,
+        );
+        assert!(extracted_payload.is_err());
+        Ok(())
+    }
+
+    #[test]
+    fn test_extract_payload_from_cose_mac_fails_type() -> Result<()> {
+        let payload = Value::Integer(1);
+        let cose_mac0 =
+            Value::Array(vec![Value::Integer(0), Value::Integer(1), payload, Value::Integer(3)]);
+        let extracted_payload = RemoteProvisioningService::extract_payload_from_cose_mac(
+            &serde_cbor::to_vec(&cose_mac0)?,
+        );
+        assert!(extracted_payload.is_err());
+        Ok(())
+    }
+
+    #[test]
+    fn test_extract_payload_from_cose_mac_fails_length() -> Result<()> {
+        let cose_mac0 = Value::Array(vec![Value::Integer(0), Value::Integer(1)]);
+        let extracted_payload = RemoteProvisioningService::extract_payload_from_cose_mac(
+            &serde_cbor::to_vec(&cose_mac0)?,
+        );
+        assert!(extracted_payload.is_err());
+        Ok(())
+    }
+}
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
index efa0389..42d3074 100644
--- a/ondevice-signing/Android.bp
+++ b/ondevice-signing/Android.bp
@@ -74,6 +74,34 @@
   ],
 }
 
+cc_library {
+  name: "libsigningutils",
+  defaults: [
+    "odsign_flags_defaults",
+  ],
+  cpp_std: "experimental",
+  srcs: [
+    "CertUtils.cpp",
+    "VerityUtils.cpp",
+  ],
+
+  static_libs: [
+    "libc++fs",
+    "lib_compos_proto",
+  ],
+
+  shared_libs: [
+    "libbase",
+    "libcrypto",
+    "libcrypto_utils",
+    "libfsverity",
+    "libprotobuf-cpp-lite",
+    "libutils",
+  ],
+  export_include_dirs: ["include"],
+  recovery_available: true,
+}
+
 cc_binary {
   name: "odsign",
   defaults: [
@@ -82,21 +110,19 @@
   cpp_std: "experimental",
   init_rc: ["odsign.rc"],
   srcs: [
-    "odsign_main.cpp",
-    "CertUtils.cpp",
     "KeystoreKey.cpp",
     "KeystoreHmacKey.cpp",
-    "VerityUtils.cpp",
+    "odsign_main.cpp",
   ],
 
   header_libs: ["odrefresh_headers"],
 
   static_libs: [
     "libc++fs",
+    "libsigningutils",
     "lib_odsign_proto",
     "lib_compos_proto",
   ],
-
   shared_libs: [
     "android.system.keystore2-V1-cpp",
     "android.hardware.security.keymint-V1-cpp",
diff --git a/ondevice-signing/TEST_MAPPING b/ondevice-signing/TEST_MAPPING
new file mode 100644
index 0000000..4b2c8c6
--- /dev/null
+++ b/ondevice-signing/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libsigningutils_test"
+    }
+  ]
+}
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
index 2beb7eb..54490bd 100644
--- a/ondevice-signing/VerityUtils.cpp
+++ b/ondevice-signing/VerityUtils.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <charconv>
 #include <filesystem>
 #include <map>
 #include <span>
@@ -33,7 +34,6 @@
 
 #include "CertUtils.h"
 #include "SigningKey.h"
-#include "compos_signature.pb.h"
 
 #define FS_VERITY_MAX_DIGEST_SIZE 64
 
@@ -42,14 +42,7 @@
 using android::base::Result;
 using android::base::unique_fd;
 
-using compos::proto::Signature;
-
 static const char* kFsVerityInitPath = "/system/bin/fsverity_init";
-static const char* kSignatureExtension = ".signature";
-
-static bool isSignatureFile(const std::filesystem::path& path) {
-    return path.extension().native() == kSignatureExtension;
-}
 
 static std::string toHex(std::span<const uint8_t> data) {
     std::stringstream ss;
@@ -59,6 +52,23 @@
     return ss.str();
 }
 
+static std::vector<uint8_t> fromHex(std::string_view hex) {
+    if (hex.size() % 2 != 0) {
+        return {};
+    }
+    std::vector<uint8_t> result;
+    result.reserve(hex.size() / 2);
+    for (size_t i = 0; i < hex.size(); i += 2) {
+        uint8_t byte;
+        auto conversion_result = std::from_chars(&hex[i], &hex[i + 2], byte, 16);
+        if (conversion_result.ptr != &hex[i + 2] || conversion_result.ec != std::errc()) {
+            return {};
+        }
+        result.push_back(byte);
+    }
+    return result;
+}
+
 static int read_callback(void* file, void* buf, size_t count) {
     int* fd = (int*)file;
     if (TEMP_FAILURE_RETRY(read(*fd, buf, count)) < 0) return errno ? -errno : -EIO;
@@ -155,15 +165,10 @@
     return {};
 }
 
-Result<std::string> enableFsVerity(const std::string& path, const SigningKey& key) {
-    unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
-    if (!fd.ok()) {
-        return ErrnoError() << "Failed to open " << path;
-    }
-
-    auto digest = createDigest(fd.get());
+Result<std::string> enableFsVerity(int fd, const SigningKey& key) {
+    auto digest = createDigest(fd);
     if (!digest.ok()) {
-        return Error() << digest.error() << ": " << path;
+        return Error() << digest.error();
     }
 
     auto signed_digest = signDigest(key, digest.value());
@@ -176,36 +181,26 @@
         return pkcs7_data.error();
     }
 
-    auto enabled = enableFsVerity(fd.get(), pkcs7_data.value());
+    auto enabled = enableFsVerity(fd, pkcs7_data.value());
     if (!enabled.ok()) {
-        return Error() << enabled.error() << ": " << path;
+        return Error() << enabled.error();
     }
 
     // Return the root hash as a hex string
     return toHex(digest.value());
 }
 
-Result<std::map<std::string, std::string>> addFilesToVerityRecursive(const std::string& path,
-                                                                     const SigningKey& key) {
-    std::map<std::string, std::string> digests;
-
-    std::error_code ec;
-    auto it = std::filesystem::recursive_directory_iterator(path, ec);
-    for (auto end = std::filesystem::recursive_directory_iterator(); it != end; it.increment(ec)) {
-        if (it->is_regular_file()) {
-            LOG(INFO) << "Adding " << it->path() << " to fs-verity...";
-            auto result = enableFsVerity(it->path(), key);
-            if (!result.ok()) {
-                return result.error();
-            }
-            digests[it->path()] = *result;
-        }
-    }
-    if (ec) {
-        return Error() << "Failed to iterate " << path << ": " << ec.message();
+Result<std::string> enableFsVerity(const std::string& path, const SigningKey& key) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (!fd.ok()) {
+        return ErrnoError() << "Failed to open " << path;
     }
 
-    return digests;
+    auto enableStatus = enableFsVerity(fd.get(), key);
+    if (!enableStatus.ok()) {
+        return Error() << path << ": " << enableStatus.error();
+    }
+    return enableStatus;
 }
 
 Result<std::string> isFileInVerity(int fd) {
@@ -228,7 +223,7 @@
         return ErrnoError() << "Failed to open " << path;
     }
 
-    auto digest = isFileInVerity(fd);
+    auto digest = isFileInVerity(fd.get());
     if (!digest.ok()) {
         return Error() << digest.error() << ": " << path;
     }
@@ -236,6 +231,39 @@
     return digest;
 }
 
+Result<std::map<std::string, std::string>> addFilesToVerityRecursive(const std::string& path,
+                                                                     const SigningKey& key) {
+    std::map<std::string, std::string> digests;
+
+    std::error_code ec;
+    auto it = std::filesystem::recursive_directory_iterator(path, ec);
+    for (auto end = std::filesystem::recursive_directory_iterator(); it != end; it.increment(ec)) {
+        if (it->is_regular_file()) {
+            unique_fd fd(TEMP_FAILURE_RETRY(open(it->path().c_str(), O_RDONLY | O_CLOEXEC)));
+            if (!fd.ok()) {
+                return ErrnoError() << "Failed to open " << path;
+            }
+            auto digest = isFileInVerity(fd);
+            if (!digest.ok()) {
+                LOG(INFO) << "Adding " << it->path() << " to fs-verity...";
+                auto result = enableFsVerity(fd, key);
+                if (!result.ok()) {
+                    return result.error();
+                }
+                digests[it->path()] = *result;
+            } else {
+                LOG(INFO) << it->path() << " was already in fs-verity.";
+                digests[it->path()] = *digest;
+            }
+        }
+    }
+    if (ec) {
+        return Error() << "Failed to iterate " << path << ": " << ec.message();
+    }
+
+    return digests;
+}
+
 Result<std::map<std::string, std::string>> verifyAllFilesInVerity(const std::string& path) {
     std::map<std::string, std::string> digests;
     std::error_code ec;
@@ -267,99 +295,62 @@
     return digests;
 }
 
-Result<Signature> readSignature(const std::filesystem::path& signature_path) {
-    unique_fd fd(TEMP_FAILURE_RETRY(open(signature_path.c_str(), O_RDONLY | O_CLOEXEC)));
-    if (fd == -1) {
-        return ErrnoError();
-    }
-    Signature signature;
-    if (!signature.ParseFromFileDescriptor(fd.get())) {
-        return Error() << "Failed to parse";
-    }
-    return signature;
-}
-
-Result<std::map<std::string, std::string>>
-verifyAllFilesUsingCompOs(const std::string& directory_path,
-                          const std::vector<uint8_t>& compos_key) {
-    std::map<std::string, std::string> new_digests;
-    std::vector<std::filesystem::path> signature_files;
-
+Result<void> verifyAllFilesUsingCompOs(const std::string& directory_path,
+                                       std::map<std::string, std::string> digests,
+                                       const SigningKey& signing_key) {
     std::error_code ec;
     auto it = std::filesystem::recursive_directory_iterator(directory_path, ec);
+    size_t verified_count = 0;
     for (auto end = std::filesystem::recursive_directory_iterator(); it != end; it.increment(ec)) {
         auto& path = it->path();
         if (it->is_regular_file()) {
-            if (isSignatureFile(path)) {
-                continue;
+            auto entry = digests.find(path);
+            if (entry == digests.end()) {
+                return Error() << "Unexpected file found: " << path;
             }
+            auto& compos_digest = entry->second;
 
             unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
             if (!fd.ok()) {
                 return ErrnoError() << "Can't open " << path;
             }
 
-            auto signature_path = path;
-            signature_path += kSignatureExtension;
-            auto signature = readSignature(signature_path);
-            if (!signature.ok()) {
-                return Error() << "Invalid signature " << signature_path << ": "
-                               << signature.error();
-            }
-            signature_files.push_back(signature_path);
-
-            // Note that these values are not yet trusted.
-            auto& raw_digest = signature->digest();
-            auto& raw_signature = signature->signature();
-
-            // Re-construct the fsverity_formatted_digest that was signed, so we
-            // can verify the signature.
-            std::vector<uint8_t> buffer(sizeof(fsverity_formatted_digest) + raw_digest.size());
-            auto signed_data = new (buffer.data()) fsverity_formatted_digest;
-            memcpy(signed_data->magic, "FSVerity", sizeof signed_data->magic);
-            signed_data->digest_algorithm = __cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
-            signed_data->digest_size = __cpu_to_le16(raw_digest.size());
-            memcpy(signed_data->digest, raw_digest.data(), raw_digest.size());
-
-            // Make sure the signature matches the CompOs public key, and not some other
-            // fs-verity trusted key.
-            std::string to_verify(reinterpret_cast<char*>(buffer.data()), buffer.size());
-
-            auto verified = verifyRsaPublicKeySignature(to_verify, raw_signature, compos_key);
-            if (!verified.ok()) {
-                return Error() << verified.error() << ": " << path;
-            }
-
-            std::span<const uint8_t> digest_bytes(
-                reinterpret_cast<const uint8_t*>(raw_digest.data()), raw_digest.size());
-            std::string compos_digest = toHex(digest_bytes);
-
             auto verity_digest = isFileInVerity(fd);
             if (verity_digest.ok()) {
                 // The file is already in fs-verity. We need to make sure it was signed
-                // by CompOs, so we just check that it has the digest we expect.
-                if (verity_digest.value() != compos_digest) {
-                    return Error() << "fs-verity digest does not match signature file: " << path;
+                // by CompOS, so we just check that it has the digest we expect.
+                if (verity_digest.value() == compos_digest) {
+                    ++verified_count;
+                } else {
+                    return Error() << "fs-verity digest does not match CompOS digest: " << path;
                 }
             } else {
-                // Not in fs-verity yet. But we have a valid signature of some
-                // digest. If it's not the correct digest for the file then
-                // enabling fs-verity will fail, so we don't need to check it
-                // explicitly ourselves. Otherwise we should be good.
-                std::vector<uint8_t> signature_bytes(raw_signature.begin(), raw_signature.end());
-                auto pkcs7 = createPkcs7(signature_bytes, kCompOsSubject);
-                if (!pkcs7.ok()) {
-                    return Error() << pkcs7.error() << ": " << path;
-                }
-
+                // Not in fs-verity yet. We know the digest CompOS provided; If
+                // it's not the correct digest for the file then enabling
+                // fs-verity will fail, so we don't need to check it explicitly
+                // ourselves. Otherwise we should be good.
                 LOG(INFO) << "Adding " << path << " to fs-verity...";
-                auto enabled = enableFsVerity(fd, pkcs7.value());
-                if (!enabled.ok()) {
-                    return Error() << enabled.error() << ": " << path;
-                }
-            }
 
-            new_digests[path] = compos_digest;
+                auto digest_bytes = fromHex(compos_digest);
+                if (digest_bytes.empty()) {
+                    return Error() << "Invalid digest " << compos_digest;
+                }
+                auto signed_digest = signDigest(signing_key, digest_bytes);
+                if (!signed_digest.ok()) {
+                    return signed_digest.error();
+                }
+
+                auto pkcs7_data = createPkcs7(signed_digest.value(), kRootSubject);
+                if (!pkcs7_data.ok()) {
+                    return pkcs7_data.error();
+                }
+
+                auto enabled = enableFsVerity(fd, pkcs7_data.value());
+                if (!enabled.ok()) {
+                    return Error() << enabled.error();
+                }
+                ++verified_count;
+            }
         } else if (it->is_directory()) {
             // These are fine to ignore
         } else if (it->is_symlink()) {
@@ -371,18 +362,12 @@
     if (ec) {
         return Error() << "Failed to iterate " << directory_path << ": " << ec.message();
     }
-
-    // Delete the signature files now that they have served their purpose.  (ART
-    // has no use for them, and their presence could cause verification to fail
-    // on subsequent boots.)
-    for (auto& signature_path : signature_files) {
-        std::filesystem::remove(signature_path, ec);
-        if (ec) {
-            return Error() << "Failed to delete " << signature_path << ": " << ec.message();
-        }
+    // Make sure all the files we expected have been seen
+    if (verified_count != digests.size()) {
+        return Error() << "Verified " << verified_count << "files, but expected " << digests.size();
     }
 
-    return new_digests;
+    return {};
 }
 
 Result<void> addCertToFsVerityKeyring(const std::string& path, const char* keyName) {
diff --git a/ondevice-signing/CertUtils.h b/ondevice-signing/include/CertUtils.h
similarity index 100%
rename from ondevice-signing/CertUtils.h
rename to ondevice-signing/include/CertUtils.h
diff --git a/ondevice-signing/SigningKey.h b/ondevice-signing/include/SigningKey.h
similarity index 100%
rename from ondevice-signing/SigningKey.h
rename to ondevice-signing/include/SigningKey.h
diff --git a/ondevice-signing/VerityUtils.h b/ondevice-signing/include/VerityUtils.h
similarity index 70%
rename from ondevice-signing/VerityUtils.h
rename to ondevice-signing/include/VerityUtils.h
index 8d8e62c..7715628 100644
--- a/ondevice-signing/VerityUtils.h
+++ b/ondevice-signing/include/VerityUtils.h
@@ -18,6 +18,10 @@
 
 #include <android-base/result.h>
 
+#include <map>
+#include <string>
+#include <vector>
+
 #include "SigningKey.h"
 
 android::base::Result<void> addCertToFsVerityKeyring(const std::string& path, const char* keyName);
@@ -25,8 +29,11 @@
 android::base::Result<std::map<std::string, std::string>>
 verifyAllFilesInVerity(const std::string& path);
 
+// Note that this function will skip files that are already in fs-verity, and
+// for those files it will return the existing digest.
 android::base::Result<std::map<std::string, std::string>>
 addFilesToVerityRecursive(const std::string& path, const SigningKey& key);
 
-android::base::Result<std::map<std::string, std::string>>
-verifyAllFilesUsingCompOs(const std::string& path, const std::vector<uint8_t>& compos_key);
+android::base::Result<void> verifyAllFilesUsingCompOs(const std::string& directory_path,
+                                                      std::map<std::string, std::string> digests,
+                                                      const SigningKey& signing_key);
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
index 6ea20ea..ec4a997 100644
--- a/ondevice-signing/odsign_main.cpp
+++ b/ondevice-signing/odsign_main.cpp
@@ -65,8 +65,14 @@
     "/data/misc/apexdata/com.android.compos/current/key.pubkey";
 const std::string kCompOsPendingPublicKey =
     "/data/misc/apexdata/com.android.compos/pending/key.pubkey";
-
 const std::string kCompOsPendingArtifactsDir = "/data/misc/apexdata/com.android.art/compos-pending";
+const std::string kCompOsInfo = kArtArtifactsDir + "/compos.info";
+const std::string kCompOsInfoSignature = kCompOsInfo + ".signature";
+
+constexpr const char* kCompOsPendingInfoPath =
+    "/data/misc/apexdata/com.android.art/compos-pending/compos.info";
+constexpr const char* kCompOsPendingInfoSignaturePath =
+    "/data/misc/apexdata/com.android.art/compos-pending/compos.info.signature";
 
 constexpr const char* kOdsignVerificationDoneProp = "odsign.verification.done";
 constexpr const char* kOdsignKeyDoneProp = "odsign.key.done";
@@ -241,7 +247,7 @@
         auto existing_key =
             extractRsaPublicKeyFromLeafCert(signingKey, kCompOsCert, kCompOsSubject.commonName);
         if (existing_key.ok()) {
-            LOG(INFO) << "Found and verified existing CompOs public key certificate: "
+            LOG(INFO) << "Found and verified existing CompOS public key certificate: "
                       << kCompOsCert;
             return existing_key.value();
         }
@@ -254,7 +260,7 @@
     }
 
     if (!verified) {
-        return Error() << "No valid CompOs key present.";
+        return Error() << "No valid CompOS key present.";
     }
 
     // If the pending key was verified it will have been promoted to current, so
@@ -262,7 +268,7 @@
     auto publicKey = readBytesFromFile(kCompOsCurrentPublicKey);
     if (publicKey.empty()) {
         // This shouldn`t really happen.
-        return Error() << "Failed to read CompOs key.";
+        return Error() << "Failed to read CompOS key.";
     }
 
     // One way or another we now have a valid public key. Persist a certificate so
@@ -274,10 +280,10 @@
     auto certStatus = createLeafCertificate(kCompOsSubject, publicKey, signFunction,
                                             kSigningKeyCert, kCompOsCert);
     if (!certStatus.ok()) {
-        return Error() << "Failed to create CompOs cert: " << certStatus.error();
+        return Error() << "Failed to create CompOS cert: " << certStatus.error();
     }
 
-    LOG(INFO) << "Verified key, wrote new CompOs cert";
+    LOG(INFO) << "Verified key, wrote new CompOS cert";
 
     return publicKey;
 }
@@ -447,23 +453,58 @@
     if (!cert_add_result.ok()) {
         // Best efforts only - nothing we can do if deletion fails.
         unlink(kCompOsCert.c_str());
-        return Error() << "Failed to add CompOs certificate to fs-verity keyring: "
+        return Error() << "Failed to add CompOS certificate to fs-verity keyring: "
                        << cert_add_result.error();
     }
 
     return publicKey;
 }
 
+Result<OdsignInfo> getComposInfo(const std::vector<uint8_t>& compos_key) {
+    std::string compos_signature;
+    if (!android::base::ReadFileToString(kCompOsInfoSignature, &compos_signature)) {
+        return ErrnoError() << "Failed to read " << kCompOsInfoSignature;
+    }
+
+    std::string compos_info_str;
+    if (!android::base::ReadFileToString(kCompOsInfo, &compos_info_str)) {
+        return ErrnoError() << "Failed to read " << kCompOsInfo;
+    }
+
+    // Delete the files - if they're valid we don't need them any more, and
+    // they'd confuse artifact verification; if they're not we never need to
+    // look at them again.
+    if (unlink(kCompOsInfo.c_str()) != 0 || unlink(kCompOsInfoSignature.c_str()) != 0) {
+        return ErrnoError() << "Unable to delete CompOS info/signature file";
+    }
+
+    // Verify the signature
+    auto verified = verifyRsaPublicKeySignature(compos_info_str, compos_signature, compos_key);
+    if (!verified.ok()) {
+        return Error() << kCompOsInfoSignature << " does not match.";
+    } else {
+        LOG(INFO) << kCompOsInfoSignature << " matches.";
+    }
+
+    OdsignInfo compos_info;
+    if (!compos_info.ParseFromString(compos_info_str)) {
+        return Error() << "Failed to parse " << kCompOsInfo;
+    }
+
+    LOG(INFO) << "Loaded " << kCompOsInfo;
+    return compos_info;
+}
+
 art::odrefresh::ExitCode checkCompOsPendingArtifacts(const std::vector<uint8_t>& compos_key,
-                                                     const SigningKey& signingKey,
+                                                     const SigningKey& signing_key,
                                                      bool* digests_verified) {
     if (!directoryHasContent(kCompOsPendingArtifactsDir)) {
         return art::odrefresh::ExitCode::kCompilationRequired;
     }
 
-    // CompOs has generated some artifacts that may, or may not, match the
+    // CompOS has generated some artifacts that may, or may not, match the
     // current state.  But if there are already valid artifacts present the
-    // CompOs ones are redundant.
+    // CompOS ones are redundant.
     art::odrefresh::ExitCode odrefresh_status = checkArtifacts();
     if (odrefresh_status != art::odrefresh::ExitCode::kCompilationRequired) {
         if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
@@ -473,7 +514,14 @@
         return odrefresh_status;
     }
 
-    // No useful current artifacts, lets see if the CompOs ones are ok
+    // No useful current artifacts, lets see if the CompOS ones are ok
+    if (access(kCompOsPendingInfoPath, R_OK) != 0 ||
+        access(kCompOsPendingInfoSignaturePath, R_OK) != 0) {
+        LOG(INFO) << "Missing CompOS info/signature, deleting pending artifacts";
+        removeDirectory(kCompOsPendingArtifactsDir);
+        return art::odrefresh::ExitCode::kCompilationRequired;
+    }
+
     LOG(INFO) << "Current artifacts are out of date, switching to pending artifacts";
     removeDirectory(kArtArtifactsDir);
     if (!rename(kCompOsPendingArtifactsDir, kArtArtifactsDir)) {
@@ -481,9 +529,6 @@
         return art::odrefresh::ExitCode::kCompilationRequired;
     }
 
-    // TODO: Make sure that we check here that the contents of the artifacts
-    // correspond to their filenames (and extensions) - the CompOs signatures
-    // can't guarantee that.
     odrefresh_status = checkArtifacts();
     if (odrefresh_status != art::odrefresh::ExitCode::kOkay) {
         LOG(WARNING) << "Pending artifacts are not OK";
@@ -492,24 +537,28 @@
 
     // The artifacts appear to be up to date - but we haven't
     // verified that they are genuine yet.
-    Result<std::map<std::string, std::string>> digests =
-        verifyAllFilesUsingCompOs(kArtArtifactsDir, compos_key);
 
-    if (digests.ok()) {
-        auto persisted = persistDigests(digests.value(), signingKey);
-
-        // Having signed the digests (or failed to), we're done with the signing key.
-        SetProperty(kOdsignKeyDoneProp, "1");
-
-        if (persisted.ok()) {
-            *digests_verified = true;
-            LOG(INFO) << "Pending artifacts successfully verified.";
-            return art::odrefresh::ExitCode::kOkay;
-        } else {
-            LOG(WARNING) << persisted.error();
-        }
+    auto compos_info = getComposInfo(compos_key);
+    if (!compos_info.ok()) {
+        LOG(WARNING) << compos_info.error();
     } else {
-        LOG(WARNING) << "Pending artifact verification failed: " << digests.error();
+        std::map<std::string, std::string> compos_digests(compos_info->file_hashes().begin(),
+                                                          compos_info->file_hashes().end());
+
+        auto status = verifyAllFilesUsingCompOs(kArtArtifactsDir, compos_digests, signing_key);
+        if (!status.ok()) {
+            LOG(WARNING) << status.error();
+        } else {
+            auto persisted = persistDigests(compos_digests, signing_key);
+
+            if (!persisted.ok()) {
+                LOG(WARNING) << persisted.error();
+            } else {
+                LOG(INFO) << "Pending artifacts successfully verified.";
+                *digests_verified = true;
+                return art::odrefresh::ExitCode::kOkay;
+            }
+        }
     }
 
     // We can't use the existing artifacts, so we will need to generate new
diff --git a/ondevice-signing/proto/Android.bp b/ondevice-signing/proto/Android.bp
index c042b8e..d6e3354 100644
--- a/ondevice-signing/proto/Android.bp
+++ b/ondevice-signing/proto/Android.bp
@@ -26,6 +26,21 @@
         type: "lite",
     },
     srcs: ["odsign_info.proto"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.compos",
+    ],
+}
+
+rust_protobuf {
+    name: "libodsign_proto_rust",
+    crate_name: "odsign_proto",
+    protos: ["odsign_info.proto"],
+    source_stem: "odsign_proto",
+    host_supported: true,
+    apex_available: [
+        "com.android.compos",
+    ],
 }
 
 cc_library_static {
@@ -40,4 +55,5 @@
         "//apex_available:platform",
         "com.android.compos",
     ],
+    recovery_available: true,
 }
diff --git a/ondevice-signing/tests/Android.bp b/ondevice-signing/tests/Android.bp
new file mode 100644
index 0000000..4027220
--- /dev/null
+++ b/ondevice-signing/tests/Android.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+cc_test {
+  name: "libsigningutils_test",
+  srcs: ["SigningUtilsTest.cpp"],
+  test_suites: ["device-tests"],
+  compile_multilib: "both",
+  defaults: [
+    "odsign_flags_defaults",
+  ],
+  static_libs: [
+    "libsigningutils",
+  ],
+  shared_libs: [
+    "libbase",
+    "libcrypto",
+  ],
+  data: [
+    "test_file",
+    "test_file.sig",
+    "SigningUtils.cert.der",
+  ],
+}
diff --git a/ondevice-signing/tests/SigningUtils.cert.der b/ondevice-signing/tests/SigningUtils.cert.der
new file mode 100644
index 0000000..0703d59
--- /dev/null
+++ b/ondevice-signing/tests/SigningUtils.cert.der
Binary files differ
diff --git a/ondevice-signing/tests/SigningUtils.pem b/ondevice-signing/tests/SigningUtils.pem
new file mode 100644
index 0000000..01dfa5e
--- /dev/null
+++ b/ondevice-signing/tests/SigningUtils.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAwyOUvcTpPuPxw2I9E28uhXT9pbJAgNE0dc1emOWRfapZ5iow
+6PC7am47DfHyyZ/dwtw0vvi3QWCLIpsyFQbspO5XfmJH7mHBTvG8SkV40rFjptf8
+iwMl5i1ZUc5LChxuRh6DIS1fT2tFiXQ8iX3FUNNqjj/tOOtp45JEsgFK7WDW8YU3
+UseUn2BpkccUHMbe78EkEb4F6/AJWISk2INMOMPzRPUwdE3eUPVrufkp6jVJs69I
+c6skRvAAqpiOx4rYpVzs3V5PuzhmCr7jt8hTQ0DA6q501UT06og6gIKPdqLjugKK
+jt88XNXhhofckSbIH7edwa20LhvE4vtM/x7E0u+sCJozr0uyNgRXWs6imsVrC7Hb
+cclecBUFqETVzP6RG49cCXVkuOkSNBm3HLt4291+O821gn3yygTE9bpf9uuDHSb1
+l13MOMbVeT3ipaRGMwD1bp7kBW3cEZ2zrQ4WSwwtADeWI6Z0k5SpcHfLxp+6dhPN
+JtqBmgUTHuycmvoAPZK6XMnV0wCo2gUYS2q3Vc9yqsJRG0q2CKhOtKnuEmSqcjcC
+52OnP06M5pLfvms+qoOLVUHMhidMtYs3yRPZ1ZPzScQhjg64QDsRiG+PC0lL/NFz
+XPFcgzSsoIJ8UNZNjw7l5hh9NehJam2pqMizIcECl3gwrqGtq7Qo/AIXc4sCAwEA
+AQKCAgBqkCqw+zBYvMgQ57vsugGQtdOyQcaB0j0wu6cWHf+2vWl8jLvK6XOfanTr
+Z54rRxcmS3SueUox9JPmoRPXccGXS+URyn/3iQC0qMQnVwrlHCQMP9TU4TI4Ibmu
+N9a4vc/mkNERNCLhTvZZWtWYS8uOGPYOmpBkTgK0WPMUtioBuamHmTUeCol6A3+D
+MVElaeDi0vlsivXW421nHoCbEBB2y2M03CTKzp9CXNOoao3eLZ2C94y8RdB4wKXM
+g6UtCQDIRRfAx7kIx4LKCXZ3rXjyuBDh18VLle2diilQdnv70HZF5Q9feD8Rf2c6
+PUVRKvmMgIww8Tf9GgMJ5SwmAdp/VblgcKsdjr8tnT7V9ljkYCL4K3H4FT5LKXjI
+FiGyqBie8jvLYCLx6DlVTIi+Q/kvxaFT9twazVlz9jfgufQ4ICBKlNp4A6kpwrzb
+9QnMrHI+gTOrCCEeklg90M+xk5UERueLPnXYbvAG8cv1FeVpi9ldSQds5VHN1ZJ4
+hWWeWfGgIDiNeZuKL141NLS/sX9rCGEsQyVLTKkSDIgh5ncepVcXhB40GZFfggml
+A3HfNRN5lHLwH5+JKWlVx7PfUGPOTgx62i7HF+qcV3bQJfqnAMLPA7hh1bo8xIPp
+hiAbaZkwCGlNCwadzOq6U99Dx7eorwfAgGDKWnAr/rMaK9n+OQKCAQEA8UItzBLq
+yyJ+Xj7n/M3x/+v2E6dcDYH64oeMpVEEdH6cSSNcdUm0vgX4p25W0uY5kgj1RsXV
+gOk+9W6cM84p+2+DIGI8fydnuIv8q6TFiouDCY+T3FVdj4MasNZuAeHR7c6Coc5N
+Eckv3F1sfoGlH4z2AzppY0T3VX5TkKkh719X7A/cpIirZBRLfddDA3Hr6pm/vgSo
+mX1X5BhjrujasRoZhU2RsXnAflXp18aP67zvlRlzGrpMGeueRjp2NXF3pItiMAjN
+EOSoY3elEi6Um+WomFGLsY5SAuId+TJy8SqsGKWNHN+UPx4tGYQmEPA1e76Nu5Ex
+xDxpHIWyZ4Hz/wKCAQEAzw/9bPFgihzSnrBbJiFZVGPhFxw39aeuB5/3ZMPjknfK
+fyWonbhrJF14/86JSd16Xime6J4a3OsXKzTo36sLBDsYfYXof7bwfKk/GUryMFOG
+0aZqiZbiRQ8uCfzd+MtnNxO0WxiyZvj8i7hMjK50yBhRs/5YAHSoLxFOfVVSLNAi
+nhIDqtzeLA2W05PGusgz/0w8FHI6J64jZY4EQLgr43K6DXoFLQjtsl8JZfMO9fEc
+0j5vRytXtYlTSlQEtKeQ8cvpcqbY0gmrEasZ4v1jEzemXzvFkx+ck+Ayl/vHx60A
+AZCom66BudFArAnuVdrMAWUH+78Xf2l5en7kz40QdQKCAQBWxkran+M7dQimtVGT
+qC9msWQs5YFCioHGgKKhw2Yq0G8+Dy3uMbiEsHkjH5iy+oOydu5hqj6Ew2AVvtcH
++xs2iIFNYIgJ5A52XkNfKUCz+EIFalLwaPPh7nHnMPkYTDTJqAFsWVt3DjnctO2V
+AuR1WKoTtyq4vdGIOour+GlwQ4bILVxbAZ1Dvdj5RjegQZVtKCfDHMHXkzHNpMgV
+3ULreEu9mozQnM4ToqsdJRoW3DoAEstHzcIZgJnJALYLuughktCaHlBDxzqZrCr/
+QynIeO4O+yWXk20EBHhrbS3SeFq18rWysOgNW7k0+EcIyJ00CPHJiQuxXVkhHSVx
+/VfZAoIBAGA1isg642No/wgS41c1OZ93hRfK2cl/ruIGFtowFqZwmJs5cT5PeSD9
+eYJKggnbKcdkyVxGUi8B4NMHk4iRnd3KY5e3R49H/je+H/5tj1ibBtKU432oqNvz
+sK2dW7oFMKEru6p0MDieSiHVcWQQj1yFyDi83kDf82FjRjgAE92Um/EcZ63VUDnh
+2onWaQlSiq59ypCpfpH/XJ0MPrefm2zkWsR2RL9nHaK6e9Bt/i6SaJTbw7Kq1ecY
+tqWbolAaZ8OhvoeyNJ5rNZxRBwcsOwOr4NbxG90/W+5txrRNnccOgCk6AM3NaKNh
+Mg590sr7jby8J9h2MsHVzUb4fPJfFh0CggEBANS+aqEzWflHMOR4knhM7QHHaTfS
+4wwR3zL3/tSaV/cxNGehtjyEg85/aKklt/ude3/sm/Z0m6jQEMWgAt5iwuBovPA+
+1/rGkWTHL+dQr0i81C3b/Ymx7izL7BobaYV0o00EoKotC0NP5qR0fBKSkTfFqAYG
+SxnHtw/vduxu2H6TyIrdtvNNqc1PbHdzDI/FwzcWZFNyHBzSWwZxB5w+21uRUayv
+Iz3zcytrZZbAuOjCnhxNL/6XgcttqWSVFB4Ul1xiXrXDx2Xq+FfM40UF7oKGd+Kt
+B0wMqoZJj+0CdFfRZxHA6/n8v1Al+8lYo8smp+R9fR6qZKcugEFgdVkIl7E=
+-----END RSA PRIVATE KEY-----
diff --git a/ondevice-signing/tests/SigningUtilsTest.cpp b/ondevice-signing/tests/SigningUtilsTest.cpp
new file mode 100644
index 0000000..10f7629
--- /dev/null
+++ b/ondevice-signing/tests/SigningUtilsTest.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include "CertUtils.h"
+#include "VerityUtils.h"
+
+// These files were created using the following commands:
+// openssl genrsa -out SigningUtils.pem 4096
+// openssl req -new -x509 -key SigningUtils.pem -out SigningUtils.cert.pem
+// openssl x509 -in SigningUtils.cert.pem -out SigningUtils.cert.der -outform DER
+// head -c 4096 </dev/urandom >test_file
+// openssl dgst -sign SigningUtils.pem -keyform PEM -sha256 -out test_file.sig -binary test_file
+const std::string kTestCert = "SigningUtils.cert.der";
+const std::string kTestFile = "test_file";
+const std::string kTestFileSignature = "test_file.sig";
+
+TEST(SigningUtilsTest, CheckVerifySignature) {
+    std::string signature;
+    std::string sigFile = android::base::GetExecutableDirectory() + "/" + kTestFileSignature;
+    ASSERT_TRUE(android::base::ReadFileToString(sigFile, &signature));
+
+    std::string data;
+    std::string testFile = android::base::GetExecutableDirectory() + "/" + kTestFile;
+    ASSERT_TRUE(android::base::ReadFileToString(testFile, &data));
+
+    std::string testCert = android::base::GetExecutableDirectory() + "/" + kTestCert;
+    auto trustedKey = extractPublicKeyFromX509(testCert.c_str());
+    ASSERT_TRUE(trustedKey.ok());
+
+    auto result = verifySignature(data, signature, *trustedKey);
+    ASSERT_TRUE(result.ok());
+}
diff --git a/ondevice-signing/tests/test_file b/ondevice-signing/tests/test_file
new file mode 100644
index 0000000..8a121be
--- /dev/null
+++ b/ondevice-signing/tests/test_file
Binary files differ
diff --git a/ondevice-signing/tests/test_file.sig b/ondevice-signing/tests/test_file.sig
new file mode 100644
index 0000000..ffd95dc
--- /dev/null
+++ b/ondevice-signing/tests/test_file.sig
Binary files differ
diff --git a/provisioner/rkp_factory_extraction_tool.cpp b/provisioner/rkp_factory_extraction_tool.cpp
index 2e59dbd..9786c3d 100644
--- a/provisioner/rkp_factory_extraction_tool.cpp
+++ b/provisioner/rkp_factory_extraction_tool.cpp
@@ -77,9 +77,13 @@
     uint8_t* writePtr = challenge.data();
     while (bytesRemaining > 0) {
         int bytesRead = getrandom(writePtr, bytesRemaining, /*flags=*/0);
-        if (bytesRead < 0 && errno != EINTR) {
-            std::cerr << errno << ": " << strerror(errno) << std::endl;
-            exit(-1);
+        if (bytesRead < 0) {
+            if (errno == EINTR) {
+                continue;
+            } else {
+                std::cerr << errno << ": " << strerror(errno) << std::endl;
+                exit(-1);
+            }
         }
         bytesRemaining -= bytesRead;
         writePtr += bytesRead;
@@ -158,7 +162,7 @@
     auto rkp_service = IRemotelyProvisionedComponent::fromBinder(rkp_binder);
     if (!rkp_service) {
         std::cerr << "Unable to get binder object for '" << fullName << "', skipping.";
-        return;
+        exit(-1);
     }
 
     std::vector<uint8_t> keysToSignMac;