Merge "keystore2: Implement ECDH in wrapper."
diff --git a/keystore-engine/Android.bp b/keystore-engine/Android.bp
index b8127d3..9980765 100644
--- a/keystore-engine/Android.bp
+++ b/keystore-engine/Android.bp
@@ -27,6 +27,7 @@
     srcs: [
         "android_engine.cpp",
         "keystore_backend_binder.cpp",
+        "keystore2_engine.cpp",
     ],
 
     cflags: [
@@ -36,7 +37,9 @@
     ],
 
     shared_libs: [
+        "android.system.keystore2-V1-ndk_platform",
         "libbinder",
+        "libbinder_ndk",
         "libcrypto",
         "libcutils",
         "libhidlbase",
@@ -58,6 +61,7 @@
     srcs: [
         "android_engine.cpp",
         "keystore_backend_hidl.cpp",
+        "keystore2_engine.cpp",
     ],
 
     cflags: [
@@ -68,7 +72,10 @@
     ],
 
     shared_libs: [
+        "android.system.keystore2-V1-ndk_platform",
         "android.system.wifi.keystore@1.0",
+        "libbase",
+        "libbinder_ndk",
         "libcrypto",
         "liblog",
         "libhidlbase",
diff --git a/keystore-engine/android_engine.cpp b/keystore-engine/android_engine.cpp
index e3525b2..5881523 100644
--- a/keystore-engine/android_engine.cpp
+++ b/keystore-engine/android_engine.cpp
@@ -23,10 +23,7 @@
 #define LOG_TAG "keystore-engine"
 
 #include <pthread.h>
-#include <sys/socket.h>
-#include <stdarg.h>
 #include <string.h>
-#include <unistd.h>
 
 #include <log/log.h>
 
@@ -41,6 +38,8 @@
 
 #include <memory>
 
+#include "keystore2_engine.h"
+
 #ifndef BACKEND_WIFI_HIDL
 #include "keystore_backend_binder.h"
 #else
@@ -335,6 +334,10 @@
 EVP_PKEY* EVP_PKEY_from_keystore(const char* key_id) {
     ALOGV("EVP_PKEY_from_keystore(\"%s\")", key_id);
 
+    if (auto ks2_key = EVP_PKEY_from_keystore2(key_id)) {
+        return ks2_key;
+    }
+
     ensure_keystore_engine();
 
     uint8_t *pubkey = nullptr;
diff --git a/keystore-engine/keystore2_engine.cpp b/keystore-engine/keystore2_engine.cpp
new file mode 100644
index 0000000..8d25f48
--- /dev/null
+++ b/keystore-engine/keystore2_engine.cpp
@@ -0,0 +1,419 @@
+/*
+ * 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 "keystore2_engine.h"
+
+#include <aidl/android/system/keystore2/IKeystoreService.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android/binder_manager.h>
+
+#include <private/android_filesystem_config.h>
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/ec_key.h>
+#include <openssl/ecdsa.h>
+#include <openssl/engine.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+
+#define AT __func__ << ":" << __LINE__ << " "
+
+constexpr const char keystore2_service_name[] = "android.system.keystore2";
+const std::string keystore2_grant_id_prefix("ks2_keystore-engine_grant_id:");
+
+/**
+ * Keystore 2.0 namespace identifiers.
+ * Keep in sync with system/sepolicy/private/keystore2_key_contexts.
+ */
+constexpr const int64_t KS2_NAMESPACE_WIFI = 102;
+
+namespace ks2 = ::aidl::android::system::keystore2;
+namespace KMV1 = ::aidl::android::hardware::security::keymint;
+
+namespace {
+
+int64_t getNamespaceforCurrentUid() {
+    auto uid = getuid();
+    switch (uid) {
+    case AID_WIFI:
+        return KS2_NAMESPACE_WIFI;
+    // 0 is the super user namespace, and nothing has access to this namespace on user builds.
+    // So this will always fail.
+    default:
+        return 0;
+    }
+}
+
+struct Keystore2KeyBackend {
+    ks2::KeyDescriptor descriptor_;
+    std::shared_ptr<ks2::IKeystoreSecurityLevel> i_keystore_security_level_;
+};
+
+/* key_backend_dup is called when one of the RSA or EC_KEY objects is duplicated. */
+extern "C" int key_backend_dup(CRYPTO_EX_DATA* /* to */, const CRYPTO_EX_DATA* /* from */,
+                               void** from_d, int /* index */, long /* argl */, void* /* argp */) {
+    auto key_backend = reinterpret_cast<std::shared_ptr<Keystore2KeyBackend>*>(*from_d);
+    if (key_backend != nullptr) {
+        *from_d = new std::shared_ptr<Keystore2KeyBackend>(*key_backend);
+    }
+    return 1;
+}
+
+/* key_backend_free is called when one of the RSA, DSA or EC_KEY object is freed. */
+extern "C" void key_backend_free(void* /* parent */, void* ptr, CRYPTO_EX_DATA* /* ad */,
+                                 int /* index */, long /* argl */, void* /* argp */) {
+    delete reinterpret_cast<std::shared_ptr<Keystore2KeyBackend>*>(ptr);
+}
+
+extern "C" int rsa_private_transform(RSA* rsa, uint8_t* out, const uint8_t* in, size_t len);
+extern "C" int ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig,
+                          unsigned int* sig_len, EC_KEY* ec_key);
+/* KeystoreEngine is a BoringSSL ENGINE that implements RSA and ECDSA by
+ * forwarding the requested operations to Keystore. */
+class Keystore2Engine {
+  public:
+    Keystore2Engine()
+        : rsa_index_(RSA_get_ex_new_index(0 /* argl */, nullptr /* argp */, nullptr /* new_func */,
+                                          key_backend_dup, key_backend_free)),
+          ec_key_index_(EC_KEY_get_ex_new_index(0 /* argl */, nullptr /* argp */,
+                                                nullptr /* new_func */, key_backend_dup,
+                                                key_backend_free)),
+          engine_(ENGINE_new()) {
+        memset(&rsa_method_, 0, sizeof(rsa_method_));
+        rsa_method_.common.is_static = 1;
+        rsa_method_.private_transform = rsa_private_transform;
+        rsa_method_.flags = RSA_FLAG_OPAQUE;
+        ENGINE_set_RSA_method(engine_, &rsa_method_, sizeof(rsa_method_));
+
+        memset(&ecdsa_method_, 0, sizeof(ecdsa_method_));
+        ecdsa_method_.common.is_static = 1;
+        ecdsa_method_.sign = ecdsa_sign;
+        ecdsa_method_.flags = ECDSA_FLAG_OPAQUE;
+        ENGINE_set_ECDSA_method(engine_, &ecdsa_method_, sizeof(ecdsa_method_));
+    }
+
+    int rsa_ex_index() const { return rsa_index_; }
+    int ec_key_ex_index() const { return ec_key_index_; }
+
+    const ENGINE* engine() const { return engine_; }
+
+    static const Keystore2Engine& get() {
+        static Keystore2Engine engine;
+        return engine;
+    }
+
+  private:
+    const int rsa_index_;
+    const int ec_key_index_;
+    RSA_METHOD rsa_method_;
+    ECDSA_METHOD ecdsa_method_;
+    ENGINE* const engine_;
+};
+
+#define OWNERSHIP_TRANSFERRED(x) x.release()
+
+/* wrap_rsa returns an |EVP_PKEY| that contains an RSA key where the public
+ * part is taken from |public_rsa| and the private operations are forwarded to
+ * KeyStore and operate on the key named |key_id|. */
+bssl::UniquePtr<EVP_PKEY> wrap_rsa(std::shared_ptr<Keystore2KeyBackend> key_backend,
+                                   const RSA* public_rsa) {
+    bssl::UniquePtr<RSA> rsa(RSA_new_method(Keystore2Engine::get().engine()));
+    if (rsa.get() == nullptr) {
+        return nullptr;
+    }
+
+    auto key_backend_copy = new decltype(key_backend)(key_backend);
+
+    if (!RSA_set_ex_data(rsa.get(), Keystore2Engine::get().rsa_ex_index(), key_backend_copy)) {
+        delete key_backend_copy;
+        return nullptr;
+    }
+
+    rsa->n = BN_dup(public_rsa->n);
+    rsa->e = BN_dup(public_rsa->e);
+    if (rsa->n == nullptr || rsa->e == nullptr) {
+        return nullptr;
+    }
+
+    bssl::UniquePtr<EVP_PKEY> result(EVP_PKEY_new());
+    if (result.get() == nullptr || !EVP_PKEY_assign_RSA(result.get(), rsa.get())) {
+        return nullptr;
+    }
+    OWNERSHIP_TRANSFERRED(rsa);
+
+    return result;
+}
+
+/* wrap_ecdsa returns an |EVP_PKEY| that contains an ECDSA key where the public
+ * part is taken from |public_rsa| and the private operations are forwarded to
+ * KeyStore and operate on the key named |key_id|. */
+bssl::UniquePtr<EVP_PKEY> wrap_ecdsa(std::shared_ptr<Keystore2KeyBackend> key_backend,
+                                     const EC_KEY* public_ecdsa) {
+    bssl::UniquePtr<EC_KEY> ec(EC_KEY_new_method(Keystore2Engine::get().engine()));
+    if (ec.get() == nullptr) {
+        return nullptr;
+    }
+
+    if (!EC_KEY_set_group(ec.get(), EC_KEY_get0_group(public_ecdsa)) ||
+        !EC_KEY_set_public_key(ec.get(), EC_KEY_get0_public_key(public_ecdsa))) {
+        return nullptr;
+    }
+
+    auto key_backend_copy = new decltype(key_backend)(key_backend);
+
+    if (!EC_KEY_set_ex_data(ec.get(), Keystore2Engine::get().ec_key_ex_index(), key_backend_copy)) {
+        delete key_backend_copy;
+        return nullptr;
+    }
+
+    bssl::UniquePtr<EVP_PKEY> result(EVP_PKEY_new());
+    if (result.get() == nullptr || !EVP_PKEY_assign_EC_KEY(result.get(), ec.get())) {
+        return nullptr;
+    }
+    OWNERSHIP_TRANSFERRED(ec);
+
+    return result;
+}
+
+std::optional<std::vector<uint8_t>> keystore2_sign(const Keystore2KeyBackend& key_backend,
+                                                   std::vector<uint8_t> input,
+                                                   KMV1::Algorithm algorithm) {
+    auto sec_level = key_backend.i_keystore_security_level_;
+    ks2::CreateOperationResponse response;
+
+    std::vector<KMV1::KeyParameter> op_params(4);
+    op_params[0] = KMV1::KeyParameter{
+        .tag = KMV1::Tag::PURPOSE,
+        .value = KMV1::KeyParameterValue::make<KMV1::KeyParameterValue::keyPurpose>(
+            KMV1::KeyPurpose::SIGN)};
+    op_params[1] = KMV1::KeyParameter{
+        .tag = KMV1::Tag::ALGORITHM,
+        .value = KMV1::KeyParameterValue::make<KMV1::KeyParameterValue::algorithm>(algorithm)};
+    op_params[2] = KMV1::KeyParameter{
+        .tag = KMV1::Tag::PADDING,
+        .value = KMV1::KeyParameterValue::make<KMV1::KeyParameterValue::paddingMode>(
+            KMV1::PaddingMode::NONE)};
+    op_params[3] =
+        KMV1::KeyParameter{.tag = KMV1::Tag::DIGEST,
+                           .value = KMV1::KeyParameterValue::make<KMV1::KeyParameterValue::digest>(
+                               KMV1::Digest::NONE)};
+
+    auto rc = sec_level->createOperation(key_backend.descriptor_, op_params, false /* forced */,
+                                         &response);
+    if (!rc.isOk()) {
+        auto exception_code = rc.getExceptionCode();
+        if (exception_code == EX_SERVICE_SPECIFIC) {
+            LOG(ERROR) << AT << "Keystore createOperation returned service specific error: "
+                       << rc.getServiceSpecificError();
+        } else {
+            LOG(ERROR) << AT << "Communication with Keystore createOperation failed error: "
+                       << exception_code;
+        }
+        return std::nullopt;
+    }
+
+    auto op = response.iOperation;
+
+    std::optional<std::vector<uint8_t>> output = std::nullopt;
+    rc = op->finish(std::move(input), {}, &output);
+    if (!rc.isOk()) {
+        auto exception_code = rc.getExceptionCode();
+        if (exception_code == EX_SERVICE_SPECIFIC) {
+            LOG(ERROR) << AT << "Keystore finish returned service specific error: "
+                       << rc.getServiceSpecificError();
+        } else {
+            LOG(ERROR) << AT
+                       << "Communication with Keystore finish failed error: " << exception_code;
+        }
+        return std::nullopt;
+    }
+
+    if (!output) {
+        LOG(ERROR) << AT << "We did not get a signature from Keystore.";
+    }
+
+    return output;
+}
+
+/* rsa_private_transform takes a big-endian integer from |in|, calculates the
+ * d'th power of it, modulo the RSA modulus, and writes the result as a
+ * big-endian integer to |out|. Both |in| and |out| are |len| bytes long. It
+ * returns one on success and zero otherwise. */
+extern "C" int rsa_private_transform(RSA* rsa, uint8_t* out, const uint8_t* in, size_t len) {
+    auto key_backend = reinterpret_cast<std::shared_ptr<Keystore2KeyBackend>*>(
+        RSA_get_ex_data(rsa, Keystore2Engine::get().rsa_ex_index()));
+
+    if (key_backend == nullptr) {
+        LOG(ERROR) << AT << "Invalid key.";
+        return 0;
+    }
+
+    auto output =
+        keystore2_sign(**key_backend, std::vector<uint8_t>(in, in + len), KMV1::Algorithm::RSA);
+    if (!output) {
+        return 0;
+    }
+
+    if (output->size() > len) {
+        /* The result of the RSA operation can never be larger than the size of
+         * the modulus so we assume that the result has extra zeros on the
+         * left. This provides attackers with an oracle, but there's nothing
+         * that we can do about it here. */
+        LOG(WARNING) << "Reply len " << output->size() << " greater than expected " << len;
+        memcpy(out, &output->data()[output->size() - len], len);
+    } else if (output->size() < len) {
+        /* If the Keystore implementation returns a short value we assume that
+         * it's because it removed leading zeros from the left side. This is
+         * bad because it provides attackers with an oracle but we cannot do
+         * anything about a broken Keystore implementation here. */
+        LOG(WARNING) << "Reply len " << output->size() << " less than expected " << len;
+        memset(out, 0, len);
+        memcpy(out + len - output->size(), output->data(), output->size());
+    } else {
+        memcpy(out, output->data(), len);
+    }
+
+    return 1;
+}
+
+/* ecdsa_sign signs |digest_len| bytes from |digest| with |ec_key| and writes
+ * the resulting signature (an ASN.1 encoded blob) to |sig|. It returns one on
+ * success and zero otherwise. */
+extern "C" int ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig,
+                          unsigned int* sig_len, EC_KEY* ec_key) {
+    auto key_backend = reinterpret_cast<std::shared_ptr<Keystore2KeyBackend>*>(
+        EC_KEY_get_ex_data(ec_key, Keystore2Engine::get().ec_key_ex_index()));
+
+    if (key_backend == nullptr) {
+        LOG(ERROR) << AT << "Invalid key.";
+        return 0;
+    }
+
+    size_t ecdsa_size = ECDSA_size(ec_key);
+
+    auto output = keystore2_sign(**key_backend, std::vector<uint8_t>(digest, digest + digest_len),
+                                 KMV1::Algorithm::EC);
+    if (!output) {
+        LOG(ERROR) << "There was an error during ecdsa_sign.";
+        return 0;
+    }
+
+    if (output->size() == 0) {
+        LOG(ERROR) << "No valid signature returned";
+        return 0;
+    } else if (output->size() > ecdsa_size) {
+        LOG(ERROR) << "Signature is too large";
+        return 0;
+    }
+
+    memcpy(sig, output->data(), output->size());
+    *sig_len = output->size();
+
+    return 1;
+}
+
+}  // namespace
+
+/* EVP_PKEY_from_keystore returns an |EVP_PKEY| that contains either an RSA or
+ * ECDSA key where the public part of the key reflects the value of the key
+ * named |key_id| in Keystore and the private operations are forwarded onto
+ * KeyStore. */
+extern "C" EVP_PKEY* EVP_PKEY_from_keystore2(const char* key_id) {
+    ::ndk::SpAIBinder keystoreBinder(AServiceManager_checkService(keystore2_service_name));
+    auto keystore2 = ks2::IKeystoreService::fromBinder(keystoreBinder);
+
+    if (!keystore2) {
+        LOG(ERROR) << AT << "Unable to connect to Keystore 2.0.";
+        return nullptr;
+    }
+
+    std::string alias = key_id;
+    if (android::base::StartsWith(alias, "USRPKEY_")) {
+        LOG(WARNING) << AT << "Keystore backend used with legacy alias prefix - ignoring.";
+        alias = alias.substr(8);
+    }
+
+    ks2::KeyDescriptor descriptor = {
+        .domain = ks2::Domain::SELINUX,
+        .nspace = getNamespaceforCurrentUid(),
+        .alias = alias,
+        .blob = std::nullopt,
+    };
+
+    // If the key_id starts with the grant id prefix, we parse the following string as numeric
+    // grant id. We can then use the grant domain without alias to load the designated key.
+    if (alias.find(keystore2_grant_id_prefix) == 0) {
+        std::stringstream s(alias.substr(keystore2_grant_id_prefix.size()));
+        s >> std::hex >> reinterpret_cast<uint64_t&>(descriptor.nspace);
+        descriptor.domain = ks2::Domain::GRANT;
+        descriptor.alias = std::nullopt;
+    }
+
+    ks2::KeyEntryResponse response;
+    auto rc = keystore2->getKeyEntry(descriptor, &response);
+    if (!rc.isOk()) {
+        auto exception_code = rc.getExceptionCode();
+        if (exception_code == EX_SERVICE_SPECIFIC) {
+            LOG(ERROR) << AT << "Keystore getKeyEntry returned service specific error: "
+                       << rc.getServiceSpecificError();
+        } else {
+            LOG(ERROR) << AT << "Communication with Keystore getKeyEntry failed error: "
+                       << exception_code;
+        }
+        return nullptr;
+    }
+
+    if (!response.metadata.certificate) {
+        LOG(ERROR) << AT << "No public key found.";
+        return nullptr;
+    }
+
+    const uint8_t* p = response.metadata.certificate->data();
+    bssl::UniquePtr<X509> x509(d2i_X509(nullptr, &p, response.metadata.certificate->size()));
+    if (!x509) {
+        LOG(ERROR) << AT << "Failed to parse x509 certificate.";
+        return nullptr;
+    }
+    bssl::UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(x509.get()));
+    if (!pkey) {
+        LOG(ERROR) << AT << "Failed to extract public key.";
+        return nullptr;
+    }
+
+    auto key_backend = std::make_shared<Keystore2KeyBackend>(
+        Keystore2KeyBackend{response.metadata.key, response.iSecurityLevel});
+
+    bssl::UniquePtr<EVP_PKEY> result;
+    switch (EVP_PKEY_type(pkey->type)) {
+    case EVP_PKEY_RSA: {
+        bssl::UniquePtr<RSA> public_rsa(EVP_PKEY_get1_RSA(pkey.get()));
+        result = wrap_rsa(key_backend, public_rsa.get());
+        break;
+    }
+    case EVP_PKEY_EC: {
+        bssl::UniquePtr<EC_KEY> public_ecdsa(EVP_PKEY_get1_EC_KEY(pkey.get()));
+        result = wrap_ecdsa(key_backend, public_ecdsa.get());
+        break;
+    }
+    default:
+        LOG(ERROR) << AT << "Unsupported key type " << EVP_PKEY_type(pkey->type);
+        return nullptr;
+    }
+
+    return result.release();
+}
diff --git a/keystore-engine/keystore2_engine.h b/keystore-engine/keystore2_engine.h
new file mode 100644
index 0000000..a8381d9
--- /dev/null
+++ b/keystore-engine/keystore2_engine.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.
+ */
+
+#pragma once
+
+#include <openssl/evp.h>
+
+extern "C" EVP_PKEY* EVP_PKEY_from_keystore2(const char* key_id);
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 812d5e6..77ab83c 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -42,6 +42,7 @@
         "libkeystore2_crypto_rust",
         "libkeystore2_km_compat",
         "libkeystore2_selinux",
+        "libkeystore2_vintf_rust",
         "liblazy_static",
         "liblibc",
         "liblibsqlite3_sys",
@@ -87,6 +88,7 @@
         "libkeystore2_km_compat",
         "libkeystore2_selinux",
         "libkeystore2_test_utils",
+        "libkeystore2_vintf_rust",
         "liblazy_static",
         "liblibc",
         "liblibsqlite3_sys",
@@ -105,6 +107,7 @@
         "libbinder_rs",
         "libkeystore2",
         "liblog_rust",
+        "libvpnprofilestore-rust",
     ],
     init_rc: ["keystore2.rc"],
 }
diff --git a/keystore2/TEST_MAPPING b/keystore2/TEST_MAPPING
index d4e20de..99a1e60 100644
--- a/keystore2/TEST_MAPPING
+++ b/keystore2/TEST_MAPPING
@@ -1,7 +1,10 @@
 {
   "presubmit": [
     {
-      "name": "keystore2_certificate_test"
+      "name": "keystore2_crypto_test"
+    },
+    {
+      "name": "keystore2_crypto_test_rust"
     },
     {
       "name": "keystore2_test"
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
index d529fa9..f30ad83 100644
--- a/keystore2/aidl/Android.bp
+++ b/keystore2/aidl/Android.bp
@@ -118,6 +118,9 @@
 aidl_interface {
     name: "android.security.usermanager",
     srcs: [ "android/security/usermanager/*.aidl" ],
+    imports: [
+        "android.system.keystore2",
+    ],
     unstable: true,
     backend: {
         java: {
@@ -131,3 +134,18 @@
         }
     },
 }
+
+aidl_interface {
+    name: "android.security.vpnprofilestore",
+    srcs: [ "android/security/vpnprofilestore/*.aidl" ],
+    unstable: true,
+    backend: {
+        java: {
+            sdk_version: "module_current",
+        },
+        rust: {
+            enabled: true,
+        },
+    },
+}
+
diff --git a/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl b/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
index d045345..0d4c30f 100644
--- a/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
+++ b/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
@@ -16,6 +16,7 @@
 
 package android.security.remoteprovisioning;
 
+import android.hardware.security.keymint.ProtectedData;
 import android.hardware.security.keymint.SecurityLevel;
 import android.security.remoteprovisioning.AttestationPoolStatus;
 
@@ -83,10 +84,13 @@
      * @param secLevel The security level to specify which KM instance from which to generate a
      *                         CSR.
      *
-     * @return A CBOR blob composed of various encrypted/signed elements from the TA in a byte[]
+     * @param protectedData The encrypted CBOR blob generated by the remote provisioner
+     *
+     * @return A CBOR blob composed of various elements required by the server to verify the
+     *                         request.
      */
     byte[] generateCsr(in boolean testMode, in int numCsr, in byte[] eek, in byte[] challenge,
-        in SecurityLevel secLevel);
+        in SecurityLevel secLevel, out ProtectedData protectedData);
 
     /**
      * This method provides a way for the returned attestation certificate chains to be provisioned
@@ -95,7 +99,10 @@
      *
      * @param publicKey The raw public key encoded in the leaf certificate.
      *
-     * @param cert An X.509, DER encoded certificate chain.
+     * @param batchCert The batch certificate corresponding to the attestation key. Separated for
+     *                          the purpose of making Subject lookup for KM attestation easier.
+     *
+     * @param certs An X.509, DER encoded certificate chain for the attestation key.
      *
      * @param expirationDate The expiration date on the certificate chain, provided by the caller
      *                          for convenience.
@@ -103,8 +110,8 @@
      * @param secLevel The security level representing the KM instance containing the key that this
      *                          chain corresponds to.
      */
-    void provisionCertChain(in byte[] publicKey, in byte[] certs, in long expirationDate,
-        in SecurityLevel secLevel);
+    void provisionCertChain(in byte[] publicKey, in byte[] batchCert, in byte[] certs,
+        in long expirationDate, in SecurityLevel secLevel);
 
     /**
      * This method allows the caller to instruct KeyStore to generate and store a key pair to be
@@ -117,4 +124,13 @@
      * @param secLevel The security level to specify which KM instance should generate a key pair.
      */
     void generateKeyPair(in boolean is_test_mode, in SecurityLevel secLevel);
+
+    /**
+     * This method returns the SecurityLevels of whichever instances of
+     * IRemotelyProvisionedComponent are running on the device. The RemoteProvisioner app needs to
+     * know which KM instances it should be generating and managing attestation keys for.
+     *
+     * @return The array of security levels.
+     */
+     SecurityLevel[] getSecurityLevels();
 }
diff --git a/keystore2/aidl/android/security/usermanager/IKeystoreUserManager.aidl b/keystore2/aidl/android/security/usermanager/IKeystoreUserManager.aidl
index 3690b1c..83edb1a 100644
--- a/keystore2/aidl/android/security/usermanager/IKeystoreUserManager.aidl
+++ b/keystore2/aidl/android/security/usermanager/IKeystoreUserManager.aidl
@@ -14,11 +14,14 @@
 
 package android.security.usermanager;
 
+import android.system.keystore2.Domain;
+
 // TODO: mark the interface with @SensitiveData when the annotation is ready (b/176110256).
 
 /**
  * IKeystoreUserManager interface exposes the methods for adding/removing users and changing the
  * user's password.
+ * @hide
  */
 interface IKeystoreUserManager {
 
@@ -31,6 +34,7 @@
      * user id.
      *
      * @param userId - Android user id
+     * @hide
      */
     void onUserAdded(in int userId);
 
@@ -42,6 +46,7 @@
      * `ResponseCode::SYSTEM_ERROR` - if failed to delete the keys of the user being deleted.
      *
      * @param userId - Android user id
+     * @hide
      */
     void onUserRemoved(in int userId);
 
@@ -56,6 +61,18 @@
      *
      * @param userId - Android user id
      * @param password - a secret derived from the synthetic password of the user
+     * @hide
      */
     void onUserPasswordChanged(in int userId, in @nullable byte[] password);
+
+    /**
+     * This function deletes all keys within a namespace. It mainly gets called when an app gets
+     * removed and all resources of this app need to be cleaned up.
+     *
+     * @param domain - One of Domain.APP or Domain.SELINUX.
+     * @param nspace - The UID of the app that is to be cleared if domain is Domain.APP or
+     *                 the SEPolicy namespace if domain is Domain.SELINUX.
+     * @hide
+     */
+     void clearNamespace(Domain domain, long nspace);
 }
diff --git a/keystore2/aidl/android/security/vpnprofilestore/IVpnProfileStore.aidl b/keystore2/aidl/android/security/vpnprofilestore/IVpnProfileStore.aidl
new file mode 100644
index 0000000..054a4d7
--- /dev/null
+++ b/keystore2/aidl/android/security/vpnprofilestore/IVpnProfileStore.aidl
@@ -0,0 +1,58 @@
+/*
+ * 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 android.security.vpnprofilestore;
+
+/**
+ * Internal interface for accessing and storing VPN profiles.
+ *
+ * @hide
+ */
+interface IVpnProfileStore {
+    /**
+     * Service specific error code indicating that the profile was not found.
+     */
+    const int ERROR_PROFILE_NOT_FOUND = 1;
+
+    /**
+     * Service specific error code indicating that an unexpected system error occurred.
+     */
+    const int ERROR_SYSTEM_ERROR = 2;
+
+    /**
+     * Returns the profile stored under the given alias.
+     *
+     * @param alias name of the profile.
+     * @return The unstructured blob that was passed as profile parameter into put()
+     */
+    byte[] get(in String alias);
+
+    /**
+     * Stores one profile as unstructured blob under the given alias.
+     */
+    void put(in String alias, in byte[] profile);
+
+    /**
+     * Deletes the profile under the given alias.
+     */
+    void remove(in String alias);
+
+    /**
+     * Returns a list of aliases of profiles stored. The list is filtered by prefix.
+     * The resulting strings are the full aliases including the prefix.
+     */
+    String[] list(in String prefix);
+}
\ No newline at end of file
diff --git a/keystore2/keystore2.rc b/keystore2/keystore2.rc
index c5fc72a..2d1f05a 100644
--- a/keystore2/keystore2.rc
+++ b/keystore2/keystore2.rc
@@ -8,13 +8,12 @@
 
 # Start Keystore 2 conditionally
 # TODO b/171563717 Remove when Keystore 2 migration is complete.
-on nonencrypted && property:persist.android.security.keystore2.enable=true
+on property:persist.android.security.keystore2.enable=true
     enable keystore2
 
 service keystore2 /system/bin/keystore2 /data/misc/keystore
-    class main
+    class early_hal
     user keystore
     group keystore readproc log
     writepid /dev/cpuset/foreground/tasks
-    # TODO b/171563717 Remove when Keystore 2 migration is complete.
     disabled
diff --git a/keystore2/src/async_task.rs b/keystore2/src/async_task.rs
index 9732e79..2b36f1f 100644
--- a/keystore2/src/async_task.rs
+++ b/keystore2/src/async_task.rs
@@ -208,3 +208,115 @@
         state.state = State::Running;
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::{AsyncTask, Shelf};
+    use std::sync::mpsc::channel;
+
+    #[test]
+    fn test_shelf() {
+        let mut shelf = Shelf::default();
+
+        let s = "A string".to_string();
+        assert_eq!(shelf.put(s), None);
+
+        let s2 = "Another string".to_string();
+        assert_eq!(shelf.put(s2), Some("A string".to_string()));
+
+        // Put something of a different type on the shelf.
+        #[derive(Debug, PartialEq, Eq)]
+        struct Elf {
+            pub name: String,
+        }
+        let e1 = Elf { name: "Glorfindel".to_string() };
+        assert_eq!(shelf.put(e1), None);
+
+        // The String value is still on the shelf.
+        let s3 = shelf.get_downcast_ref::<String>().unwrap();
+        assert_eq!(s3, "Another string");
+
+        // As is the Elf.
+        {
+            let e2 = shelf.get_downcast_mut::<Elf>().unwrap();
+            assert_eq!(e2.name, "Glorfindel");
+            e2.name = "Celeborn".to_string();
+        }
+
+        // Take the Elf off the shelf.
+        let e3 = shelf.remove_downcast_ref::<Elf>().unwrap();
+        assert_eq!(e3.name, "Celeborn");
+
+        assert_eq!(shelf.remove_downcast_ref::<Elf>(), None);
+
+        // No u64 value has been put on the shelf, so getting one gives the default value.
+        {
+            let i = shelf.get_mut::<u64>();
+            assert_eq!(*i, 0);
+            *i = 42;
+        }
+        let i2 = shelf.get_downcast_ref::<u64>().unwrap();
+        assert_eq!(*i2, 42);
+
+        // No i32 value has ever been seen near the shelf.
+        assert_eq!(shelf.get_downcast_ref::<i32>(), None);
+        assert_eq!(shelf.get_downcast_mut::<i32>(), None);
+        assert_eq!(shelf.remove_downcast_ref::<i32>(), None);
+    }
+
+    #[test]
+    fn test_async_task() {
+        let at = AsyncTask::default();
+
+        // First queue up a job that blocks until we release it, to avoid
+        // unpredictable synchronization.
+        let (start_sender, start_receiver) = channel();
+        at.queue_hi(move |shelf| {
+            start_receiver.recv().unwrap();
+            // Put a trace vector on the shelf
+            shelf.put(Vec::<String>::new());
+        });
+
+        // Queue up some high-priority and low-priority jobs.
+        for i in 0..3 {
+            let j = i;
+            at.queue_lo(move |shelf| {
+                let trace = shelf.get_mut::<Vec<String>>();
+                trace.push(format!("L{}", j));
+            });
+            let j = i;
+            at.queue_hi(move |shelf| {
+                let trace = shelf.get_mut::<Vec<String>>();
+                trace.push(format!("H{}", j));
+            });
+        }
+
+        // Finally queue up a low priority job that emits the trace.
+        let (trace_sender, trace_receiver) = channel();
+        at.queue_lo(move |shelf| {
+            let trace = shelf.get_downcast_ref::<Vec<String>>().unwrap();
+            trace_sender.send(trace.clone()).unwrap();
+        });
+
+        // Ready, set, go.
+        start_sender.send(()).unwrap();
+        let trace = trace_receiver.recv().unwrap();
+
+        assert_eq!(trace, vec!["H0", "H1", "H2", "L0", "L1", "L2"]);
+    }
+
+    #[test]
+    #[should_panic]
+    fn test_async_task_panic() {
+        let at = AsyncTask::default();
+        at.queue_hi(|_shelf| {
+            panic!("Panic from queued job");
+        });
+        // Queue another job afterwards to ensure that the async thread gets joined.
+        let (done_sender, done_receiver) = channel();
+        at.queue_hi(move |_shelf| {
+            done_sender.send(()).unwrap();
+        });
+        done_receiver.recv().unwrap();
+    }
+}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 3217857..40860be 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -584,6 +584,7 @@
 #[allow(dead_code)]
 pub struct CertificateChain {
     private_key: ZVec,
+    batch_cert: ZVec,
     cert_chain: ZVec,
 }
 
@@ -794,6 +795,7 @@
 pub struct PerBootDbKeepAlive(Connection);
 
 impl KeystoreDB {
+    const UNASSIGNED_KEY_ID: i64 = -1i64;
     const PERBOOT_DB_FILE_NAME: &'static str = &"file:perboot.sqlite?mode=memory&cache=shared";
 
     /// The alias of the user super key.
@@ -1459,6 +1461,24 @@
         .context("In set_blob.")
     }
 
+    /// Why would we insert a deleted blob? This weird function is for the purpose of legacy
+    /// key migration in the case where we bulk delete all the keys of an app or even a user.
+    /// We use this to insert key blobs into the database which can then be garbage collected
+    /// lazily by the key garbage collector.
+    pub fn set_deleted_blob(&mut self, blob: &[u8], blob_metadata: &BlobMetaData) -> Result<()> {
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            Self::set_blob_internal(
+                &tx,
+                Self::UNASSIGNED_KEY_ID,
+                SubComponentType::KEY_BLOB,
+                Some(blob),
+                Some(blob_metadata),
+            )
+            .need_gc()
+        })
+        .context("In set_deleted_blob.")
+    }
+
     fn set_blob_internal(
         tx: &Transaction,
         key_id: i64,
@@ -1551,6 +1571,7 @@
     pub fn store_signed_attestation_certificate_chain(
         &mut self,
         raw_public_key: &[u8],
+        batch_cert: &[u8],
         cert_chain: &[u8],
         expiration_date: i64,
         km_uuid: &Uuid,
@@ -1609,6 +1630,8 @@
                 None,
             )
             .context("Failed to insert cert chain")?;
+            Self::set_blob_internal(&tx, key_id, SubComponentType::CERT, Some(batch_cert), None)
+                .context("Failed to insert cert")?;
             Ok(()).no_gc()
         })
         .context("In store_signed_attestation_certificate_chain: ")
@@ -1859,19 +1882,21 @@
                     |row| Ok((row.get(0)?, row.get(1)?)),
                 )?
                 .collect::<rusqlite::Result<Vec<(SubComponentType, Vec<u8>)>>>()
-                .context("In retrieve_attestation_key_and_cert_chain: query failed.")?;
+                .context("query failed.")?;
             if rows.is_empty() {
                 return Ok(None).no_gc();
-            } else if rows.len() != 2 {
+            } else if rows.len() != 3 {
                 return Err(KsError::sys()).context(format!(
                     concat!(
-                "In retrieve_attestation_key_and_cert_chain: Expected to get a single attestation",
-                "key chain but instead got {}."),
+                        "Expected to get a single attestation",
+                        "key, cert, and cert chain for a total of 3 entries, but instead got {}."
+                    ),
                     rows.len()
                 ));
             }
             let mut km_blob: Vec<u8> = Vec::new();
             let mut cert_chain_blob: Vec<u8> = Vec::new();
+            let mut batch_cert_blob: Vec<u8> = Vec::new();
             for row in rows {
                 let sub_type: SubComponentType = row.0;
                 match sub_type {
@@ -1881,15 +1906,20 @@
                     SubComponentType::CERT_CHAIN => {
                         cert_chain_blob = row.1;
                     }
+                    SubComponentType::CERT => {
+                        batch_cert_blob = row.1;
+                    }
                     _ => Err(KsError::sys()).context("Unknown or incorrect subcomponent type.")?,
                 }
             }
             Ok(Some(CertificateChain {
                 private_key: ZVec::try_from(km_blob)?,
+                batch_cert: ZVec::try_from(batch_cert_blob)?,
                 cert_chain: ZVec::try_from(cert_chain_blob)?,
             }))
             .no_gc()
         })
+        .context("In retrieve_attestation_key_and_cert_chain:")
     }
 
     /// Updates the alias column of the given key id `newid` with the given alias,
@@ -2492,6 +2522,51 @@
         .context("In get_key_km_uuid.")
     }
 
+    /// Delete all artifacts belonging to the namespace given by the domain-namespace tuple.
+    /// This leaves all of the blob entries orphaned for subsequent garbage collection.
+    pub fn unbind_keys_for_namespace(&mut self, domain: Domain, namespace: i64) -> Result<()> {
+        if !(domain == Domain::APP || domain == Domain::SELINUX) {
+            return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
+                .context("In unbind_keys_for_namespace.");
+        }
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            tx.execute(
+                "DELETE FROM persistent.keymetadata
+                WHERE keyentryid IN (
+                    SELECT id FROM persistent.keyentry
+                    WHERE domain = ? AND namespace = ?
+                );",
+                params![domain.0, namespace],
+            )
+            .context("Trying to delete keymetadata.")?;
+            tx.execute(
+                "DELETE FROM persistent.keyparameter
+                WHERE keyentryid IN (
+                    SELECT id FROM persistent.keyentry
+                    WHERE domain = ? AND namespace = ?
+                );",
+                params![domain.0, namespace],
+            )
+            .context("Trying to delete keyparameters.")?;
+            tx.execute(
+                "DELETE FROM persistent.grant
+                WHERE keyentryid IN (
+                    SELECT id FROM persistent.keyentry
+                    WHERE domain = ? AND namespace = ?
+                );",
+                params![domain.0, namespace],
+            )
+            .context("Trying to delete grants.")?;
+            tx.execute(
+                "DELETE FROM persistent.keyentry WHERE domain = ? AND namespace = ?;",
+                params![domain.0, namespace],
+            )
+            .context("Trying to delete keyentry.")?;
+            Ok(()).need_gc()
+        })
+        .context("In unbind_keys_for_namespace")
+    }
+
     /// Delete the keys created on behalf of the user, denoted by the user id.
     /// Delete all the keys unless 'keep_non_super_encrypted_keys' set to true.
     /// Returned boolean is to hint the garbage collector to delete the unbound keys.
@@ -2735,7 +2810,10 @@
     // otherwise return the id.
     fn insert_with_retry(inserter: impl Fn(i64) -> rusqlite::Result<usize>) -> Result<i64> {
         loop {
-            let newid: i64 = random();
+            let newid: i64 = match random() {
+                Self::UNASSIGNED_KEY_ID => continue, // UNASSIGNED_KEY_ID cannot be assigned.
+                i => i,
+            };
             match inserter(newid) {
                 // If the id already existed, try again.
                 Err(rusqlite::Error::SqliteFailure(
@@ -3133,8 +3211,9 @@
             db.retrieve_attestation_key_and_cert_chain(Domain::APP, namespace, &KEYSTORE_UUID)?;
         assert_eq!(true, chain.is_some());
         let cert_chain = chain.unwrap();
-        assert_eq!(cert_chain.private_key.to_vec(), loaded_values[2]);
-        assert_eq!(cert_chain.cert_chain.to_vec(), loaded_values[1]);
+        assert_eq!(cert_chain.private_key.to_vec(), loaded_values.priv_key);
+        assert_eq!(cert_chain.batch_cert.to_vec(), loaded_values.batch_cert);
+        assert_eq!(cert_chain.cert_chain.to_vec(), loaded_values.cert_chain);
         Ok(())
     }
 
@@ -3169,6 +3248,7 @@
         let private_key: Vec<u8> = vec![0x04, 0x05, 0x06];
         let raw_public_key: Vec<u8> = vec![0x07, 0x08, 0x09];
         let cert_chain: Vec<u8> = vec![0x0a, 0x0b, 0x0c];
+        let batch_cert: Vec<u8> = vec![0x0d, 0x0e, 0x0f];
         db.create_attestation_key_entry(
             &public_key,
             &raw_public_key,
@@ -3181,6 +3261,7 @@
         assert_eq!(status.total, 4);
         db.store_signed_attestation_certificate_chain(
             &raw_public_key,
+            &batch_cert,
             &cert_chain,
             20,
             &KEYSTORE_UUID,
@@ -3215,9 +3296,9 @@
             .conn
             .query_row("SELECT COUNT(id) FROM persistent.blobentry;", NO_PARAMS, |row| row.get(0))
             .expect("Failed to get blob entry row count.");
-        // We expect 6 rows here because there are two blobs per attestation key, i.e.,
-        // One key and one certificate.
-        assert_eq!(blob_entry_row_count, 6);
+        // We expect 9 rows here because there are three blobs per attestation key, i.e.,
+        // one key, one certificate chain, and one certificate.
+        assert_eq!(blob_entry_row_count, 9);
 
         assert_eq!(db.delete_expired_attestation_keys()?, 2);
 
@@ -3225,8 +3306,9 @@
             db.retrieve_attestation_key_and_cert_chain(Domain::APP, namespace, &KEYSTORE_UUID)?;
         assert!(cert_chain.is_some());
         let value = cert_chain.unwrap();
-        assert_eq!(entry_values[1], value.cert_chain.to_vec());
-        assert_eq!(entry_values[2], value.private_key.to_vec());
+        assert_eq!(entry_values.batch_cert, value.batch_cert.to_vec());
+        assert_eq!(entry_values.cert_chain, value.cert_chain.to_vec());
+        assert_eq!(entry_values.priv_key, value.private_key.to_vec());
 
         cert_chain = db.retrieve_attestation_key_and_cert_chain(
             Domain::APP,
@@ -3248,9 +3330,9 @@
             .conn
             .query_row("SELECT COUNT(id) FROM persistent.blobentry;", NO_PARAMS, |row| row.get(0))
             .expect("Failed to get blob entry row count.");
-        // There shound be 2 blob entries left, because we deleted two of the attestation
-        // key entries with two blobs each.
-        assert_eq!(blob_entry_row_count, 2);
+        // There shound be 3 blob entries left, because we deleted two of the attestation
+        // key entries with three blobs each.
+        assert_eq!(blob_entry_row_count, 3);
 
         Ok(())
     }
@@ -4323,30 +4405,33 @@
             .collect::<Result<Vec<_>>>()
     }
 
+    struct RemoteProvValues {
+        cert_chain: Vec<u8>,
+        priv_key: Vec<u8>,
+        batch_cert: Vec<u8>,
+    }
+
     fn load_attestation_key_pool(
         db: &mut KeystoreDB,
         expiration_date: i64,
         namespace: i64,
         base_byte: u8,
-    ) -> Result<Vec<Vec<u8>>> {
-        let mut chain: Vec<Vec<u8>> = Vec::new();
+    ) -> Result<RemoteProvValues> {
         let public_key: Vec<u8> = vec![base_byte, 0x02 * base_byte];
         let cert_chain: Vec<u8> = vec![0x03 * base_byte, 0x04 * base_byte];
         let priv_key: Vec<u8> = vec![0x05 * base_byte, 0x06 * base_byte];
         let raw_public_key: Vec<u8> = vec![0x0b * base_byte, 0x0c * base_byte];
+        let batch_cert: Vec<u8> = vec![base_byte * 0x0d, base_byte * 0x0e];
         db.create_attestation_key_entry(&public_key, &raw_public_key, &priv_key, &KEYSTORE_UUID)?;
         db.store_signed_attestation_certificate_chain(
             &raw_public_key,
+            &batch_cert,
             &cert_chain,
             expiration_date,
             &KEYSTORE_UUID,
         )?;
         db.assign_attestation_key(Domain::APP, namespace, &KEYSTORE_UUID)?;
-        chain.push(public_key);
-        chain.push(cert_chain);
-        chain.push(priv_key);
-        chain.push(raw_public_key);
-        Ok(chain)
+        Ok(RemoteProvValues { cert_chain, priv_key, batch_cert })
     }
 
     // Note: The parameters and SecurityLevel associations are nonsensical. This
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index 7227f62..d67f5f4 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -57,6 +57,10 @@
     /// Wraps a Binder status code.
     #[error("Binder transaction error {0:?}")]
     BinderTransaction(StatusCode),
+    /// Wraps a Remote Provisioning ErrorCode as defined by the IRemotelyProvisionedComponent
+    /// AIDL interface spec.
+    #[error("Error::Rp({0:?})")]
+    Rp(ErrorCode),
 }
 
 impl Error {
@@ -101,6 +105,16 @@
     })
 }
 
+/// Helper function to map the binder status we get from calls into a RemotelyProvisionedComponent
+/// to a Keystore Error. We don't create an anyhow error here to make
+/// it easier to evaluate service specific errors.
+pub fn map_rem_prov_error<T>(r: BinderResult<T>) -> Result<T, Error> {
+    r.map_err(|s| match s.exception_code() {
+        ExceptionCode::SERVICE_SPECIFIC => Error::Rp(ErrorCode(s.service_specific_error())),
+        e_code => Error::Binder(e_code, 0),
+    })
+}
+
 /// This function is similar to map_km_error only that we don't expect
 /// any KeyMint error codes, we simply preserve the exception code and optional
 /// service specific exception.
@@ -164,6 +178,7 @@
             let rc = match root_cause.downcast_ref::<Error>() {
                 Some(Error::Rc(rcode)) => rcode.0,
                 Some(Error::Km(ec)) => ec.0,
+                Some(Error::Rp(_)) => ResponseCode::SYSTEM_ERROR.0,
                 // If an Error::Binder reaches this stage we report a system error.
                 // The exception code and possible service specific error will be
                 // printed in the error log above.
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index 8cc0106..e1b41b5 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -29,12 +29,13 @@
 };
 use crate::{enforcements::Enforcements, error::map_km_error};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    IKeyMintDevice::IKeyMintDevice, KeyMintHardwareInfo::KeyMintHardwareInfo,
-    SecurityLevel::SecurityLevel,
+    IKeyMintDevice::IKeyMintDevice, IRemotelyProvisionedComponent::IRemotelyProvisionedComponent,
+    KeyMintHardwareInfo::KeyMintHardwareInfo, SecurityLevel::SecurityLevel,
 };
 use android_hardware_security_keymint::binder::{StatusCode, Strong};
 use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
 use anyhow::{Context, Result};
+use keystore2_vintf::get_aidl_instances;
 use lazy_static::lazy_static;
 use std::sync::{Arc, Mutex};
 use std::{cell::RefCell, sync::Once};
@@ -128,6 +129,21 @@
     }
 }
 
+#[derive(Default)]
+struct RemotelyProvisionedDevicesMap {
+    devices_by_sec_level: HashMap<SecurityLevel, Asp>,
+}
+
+impl RemotelyProvisionedDevicesMap {
+    fn dev_by_sec_level(&self, sec_level: &SecurityLevel) -> Option<Asp> {
+        self.devices_by_sec_level.get(sec_level).map(|dev| (*dev).clone())
+    }
+
+    fn insert(&mut self, sec_level: SecurityLevel, dev: Asp) {
+        self.devices_by_sec_level.insert(sec_level, dev);
+    }
+}
+
 lazy_static! {
     /// The path where keystore stores all its keys.
     pub static ref DB_PATH: Mutex<PathBuf> = Mutex::new(
@@ -138,6 +154,8 @@
     static ref KEY_MINT_DEVICES: Mutex<DevicesMap> = Default::default();
     /// Timestamp service.
     static ref TIME_STAMP_DEVICE: Mutex<Option<Asp>> = Default::default();
+    /// RemotelyProvisionedComponent HAL devices.
+    static ref REMOTELY_PROVISIONED_COMPONENT_DEVICES: Mutex<RemotelyProvisionedDevicesMap> = Default::default();
     /// A single on-demand worker thread that handles deferred tasks with two different
     /// priorities.
     pub static ref ASYNC_TASK: Arc<AsyncTask> = Default::default();
@@ -158,38 +176,49 @@
 /// If no native KeyMint device can be found this function also brings
 /// up the compatibility service and attempts to connect to the legacy wrapper.
 fn connect_keymint(security_level: &SecurityLevel) -> Result<(Asp, KeyMintHardwareInfo)> {
+    let keymint_instances =
+        get_aidl_instances("android.hardware.security.keymint", 1, "IKeyMintDevice");
+
     let service_name = match *security_level {
-        SecurityLevel::TRUSTED_ENVIRONMENT => format!("{}/default", KEYMINT_SERVICE_NAME),
-        SecurityLevel::STRONGBOX => format!("{}/strongbox", KEYMINT_SERVICE_NAME),
+        SecurityLevel::TRUSTED_ENVIRONMENT => {
+            if keymint_instances.as_vec()?.iter().any(|instance| *instance == "default") {
+                Some(format!("{}/default", KEYMINT_SERVICE_NAME))
+            } else {
+                None
+            }
+        }
+        SecurityLevel::STRONGBOX => {
+            if keymint_instances.as_vec()?.iter().any(|instance| *instance == "strongbox") {
+                Some(format!("{}/strongbox", KEYMINT_SERVICE_NAME))
+            } else {
+                None
+            }
+        }
         _ => {
             return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
                 .context("In connect_keymint.")
         }
     };
 
-    let keymint = map_binder_status_code(binder::get_interface(&service_name))
-        .context("In connect_keymint: Trying to connect to genuine KeyMint service.")
-        .or_else(|e| {
-            match e.root_cause().downcast_ref::<Error>() {
-                Some(Error::BinderTransaction(StatusCode::NAME_NOT_FOUND)) => {
-                    // This is a no-op if it was called before.
-                    keystore2_km_compat::add_keymint_device_service();
+    let keymint = if let Some(service_name) = service_name {
+        map_binder_status_code(binder::get_interface(&service_name))
+            .context("In connect_keymint: Trying to connect to genuine KeyMint service.")
+    } else {
+        // This is a no-op if it was called before.
+        keystore2_km_compat::add_keymint_device_service();
 
-                    let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
-                        map_binder_status_code(binder::get_interface("android.security.compat"))
-                            .context("In connect_keymint: Trying to connect to compat service.")?;
-                    map_binder_status(keystore_compat_service.getKeyMintDevice(*security_level))
-                        .map_err(|e| match e {
-                            Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
-                                Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
-                            }
-                            e => e,
-                        })
-                        .context("In connect_keymint: Trying to get Legacy wrapper.")
+        let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
+            map_binder_status_code(binder::get_interface("android.security.compat"))
+                .context("In connect_keymint: Trying to connect to compat service.")?;
+        map_binder_status(keystore_compat_service.getKeyMintDevice(*security_level))
+            .map_err(|e| match e {
+                Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
+                    Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
                 }
-                _ => Err(e),
-            }
-        })?;
+                e => e,
+            })
+            .context("In connect_keymint: Trying to get Legacy wrapper.")
+    }?;
 
     let hw_info = map_km_error(keymint.getHardwareInfo())
         .context("In connect_keymint: Failed to get hardware info.")?;
@@ -233,33 +262,33 @@
 /// If no native SecureClock device can be found brings up the compatibility service and attempts
 /// to connect to the legacy wrapper.
 fn connect_secureclock() -> Result<Asp> {
-    let secureclock = map_binder_status_code(binder::get_interface(TIME_STAMP_SERVICE_NAME))
-        .context("In connect_secureclock: Trying to connect to genuine secure clock service.")
-        .or_else(|e| {
-            match e.root_cause().downcast_ref::<Error>() {
-                Some(Error::BinderTransaction(StatusCode::NAME_NOT_FOUND)) => {
-                    // This is a no-op if it was called before.
-                    keystore2_km_compat::add_keymint_device_service();
+    let secureclock_instances =
+        get_aidl_instances("android.hardware.security.secureclock", 1, "ISecureClock");
 
-                    let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
-                        map_binder_status_code(binder::get_interface("android.security.compat"))
-                            .context(
-                                "In connect_secureclock: Trying to connect to compat service.",
-                            )?;
+    let secure_clock_available =
+        secureclock_instances.as_vec()?.iter().any(|instance| *instance == "default");
 
-                    // Legacy secure clock services were only implemented by TEE.
-                    map_binder_status(keystore_compat_service.getSecureClock())
-                        .map_err(|e| match e {
-                            Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
-                                Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
-                            }
-                            e => e,
-                        })
-                        .context("In connect_secureclock: Trying to get Legacy wrapper.")
+    let secureclock = if secure_clock_available {
+        map_binder_status_code(binder::get_interface(TIME_STAMP_SERVICE_NAME))
+            .context("In connect_secureclock: Trying to connect to genuine secure clock service.")
+    } else {
+        // This is a no-op if it was called before.
+        keystore2_km_compat::add_keymint_device_service();
+
+        let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
+            map_binder_status_code(binder::get_interface("android.security.compat"))
+                .context("In connect_secureclock: Trying to connect to compat service.")?;
+
+        // Legacy secure clock services were only implemented by TEE.
+        map_binder_status(keystore_compat_service.getSecureClock())
+            .map_err(|e| match e {
+                Error::BinderTransaction(StatusCode::NAME_NOT_FOUND) => {
+                    Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)
                 }
-                _ => Err(e),
-            }
-        })?;
+                e => e,
+            })
+            .context("In connect_secureclock: Trying to get Legacy wrapper.")
+    }?;
 
     Ok(Asp::new(secureclock.as_binder()))
 }
@@ -276,3 +305,55 @@
         Ok(dev)
     }
 }
+
+static REMOTE_PROVISIONING_HAL_SERVICE_NAME: &str =
+    "android.hardware.security.keymint.IRemotelyProvisionedComponent";
+
+fn connect_remotely_provisioned_component(security_level: &SecurityLevel) -> Result<Asp> {
+    let remotely_prov_instances =
+        get_aidl_instances("android.hardware.security.keymint", 1, "IRemotelyProvisionedComponent");
+
+    let service_name = match *security_level {
+        SecurityLevel::TRUSTED_ENVIRONMENT => {
+            if remotely_prov_instances.as_vec()?.iter().any(|instance| *instance == "default") {
+                Some(format!("{}/default", REMOTE_PROVISIONING_HAL_SERVICE_NAME))
+            } else {
+                None
+            }
+        }
+        SecurityLevel::STRONGBOX => {
+            if remotely_prov_instances.as_vec()?.iter().any(|instance| *instance == "strongbox") {
+                Some(format!("{}/strongbox", REMOTE_PROVISIONING_HAL_SERVICE_NAME))
+            } else {
+                None
+            }
+        }
+        _ => None,
+    }
+    .ok_or(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
+    .context("In connect_remotely_provisioned_component.")?;
+
+    let rem_prov_hal: Strong<dyn IRemotelyProvisionedComponent> =
+        map_binder_status_code(binder::get_interface(&service_name))
+            .context(concat!(
+                "In connect_remotely_provisioned_component: Trying to connect to",
+                " RemotelyProvisionedComponent service."
+            ))
+            .map_err(|e| e)?;
+    Ok(Asp::new(rem_prov_hal.as_binder()))
+}
+
+/// Get a remote provisiong component device for the given security level either from the cache or
+/// by making a new connection. Returns the device.
+pub fn get_remotely_provisioned_component(security_level: &SecurityLevel) -> Result<Asp> {
+    let mut devices_map = REMOTELY_PROVISIONED_COMPONENT_DEVICES.lock().unwrap();
+    if let Some(dev) = devices_map.dev_by_sec_level(&security_level) {
+        Ok(dev)
+    } else {
+        let dev = connect_remotely_provisioned_component(security_level)
+            .context("In get_remotely_provisioned_component.")?;
+        devices_map.insert(*security_level, dev);
+        // Unwrap must succeed because we just inserted it.
+        Ok(devices_map.dev_by_sec_level(security_level).unwrap())
+    }
+}
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index 30e3e22..0d14f84 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -17,15 +17,19 @@
 use keystore2::apc::ApcManager;
 use keystore2::authorization::AuthorizationManager;
 use keystore2::globals::ENFORCEMENTS;
+use keystore2::remote_provisioning::RemoteProvisioningService;
 use keystore2::service::KeystoreService;
 use keystore2::user_manager::UserManager;
 use log::{error, info};
 use std::{panic, path::Path, sync::mpsc::channel};
+use vpnprofilestore::VpnProfileStore;
 
 static KS2_SERVICE_NAME: &str = "android.system.keystore2";
 static APC_SERVICE_NAME: &str = "android.security.apc";
 static AUTHORIZATION_SERVICE_NAME: &str = "android.security.authorization";
+static REMOTE_PROVISIONING_SERVICE_NAME: &str = "android.security.remoteprovisioning";
 static USER_MANAGER_SERVICE_NAME: &str = "android.security.usermanager";
+static VPNPROFILESTORE_SERVICE_NAME: &str = "android.security.vpnprofilestore";
 
 /// Keystore 2.0 takes one argument which is a path indicating its designated working directory.
 fn main() {
@@ -98,6 +102,33 @@
         },
     );
 
+    // Devices with KS2 and KM 1.0 may not have any IRemotelyProvisionedComponent HALs at all. Do
+    // not panic if new_native_binder returns failure because it could not find the TEE HAL.
+    if let Ok(remote_provisioning_service) = RemoteProvisioningService::new_native_binder() {
+        binder::add_service(
+            REMOTE_PROVISIONING_SERVICE_NAME,
+            remote_provisioning_service.as_binder(),
+        )
+        .unwrap_or_else(|e| {
+            panic!(
+                "Failed to register service {} because of {:?}.",
+                REMOTE_PROVISIONING_SERVICE_NAME, e
+            );
+        });
+    }
+
+    let vpnprofilestore = VpnProfileStore::new_native_binder(
+        &keystore2::globals::DB_PATH.lock().expect("Could not get DB_PATH."),
+    );
+    binder::add_service(VPNPROFILESTORE_SERVICE_NAME, vpnprofilestore.as_binder()).unwrap_or_else(
+        |e| {
+            panic!(
+                "Failed to register service {} because of {:?}.",
+                VPNPROFILESTORE_SERVICE_NAME, e
+            );
+        },
+    );
+
     info!("Successfully registered Keystore 2.0 service.");
 
     info!("Joining thread pool now.");
diff --git a/keystore2/src/km_compat/Android.bp b/keystore2/src/km_compat/Android.bp
index 4127aa0..541788e 100644
--- a/keystore2/src/km_compat/Android.bp
+++ b/keystore2/src/km_compat/Android.bp
@@ -120,6 +120,7 @@
         "libkeymint_support",
         "libkeystore2_crypto",
         "libkm_compat",
+        "libkm_compat_service",
         "libutils",
     ],
 }
diff --git a/keystore2/src/km_compat/certificate_test.cpp b/keystore2/src/km_compat/certificate_test.cpp
index de1d60a..06cb0cb 100644
--- a/keystore2/src/km_compat/certificate_test.cpp
+++ b/keystore2/src/km_compat/certificate_test.cpp
@@ -23,6 +23,7 @@
 #include <aidl/android/hardware/security/keymint/BlockMode.h>
 #include <aidl/android/hardware/security/keymint/Digest.h>
 #include <aidl/android/hardware/security/keymint/PaddingMode.h>
+#include <android/binder_manager.h>
 
 #include <openssl/evp.h>
 #include <openssl/x509.h>
@@ -39,16 +40,29 @@
 using ::aidl::android::hardware::security::keymint::PaddingMode;
 using ::aidl::android::hardware::security::keymint::SecurityLevel;
 using ::aidl::android::hardware::security::keymint::Tag;
+using ::aidl::android::security::compat::IKeystoreCompatService;
 
 namespace KMV1 = ::aidl::android::hardware::security::keymint;
 
-static std::variant<std::vector<Certificate>, ScopedAStatus>
-getCertificate(const std::vector<KeyParameter>& keyParams) {
-    static std::shared_ptr<KeyMintDevice> device =
-        KeyMintDevice::createKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
+extern "C" int32_t addKeyMintDeviceService();
+
+static std::variant<std::shared_ptr<IKeyMintDevice>, ScopedAStatus> getDevice() {
+    addKeyMintDeviceService();
+    std::shared_ptr<IKeyMintDevice> device;
+    auto service = IKeystoreCompatService::fromBinder(
+        ndk::SpAIBinder(AServiceManager_getService("android.security.compat")));
+    if (!service) {
+        return ScopedAStatus::fromStatus(STATUS_NAME_NOT_FOUND);
+    }
+    service->getKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT, &device);
     if (!device) {
         return ScopedAStatus::fromStatus(STATUS_NAME_NOT_FOUND);
     }
+    return device;
+}
+
+static std::variant<std::vector<Certificate>, ScopedAStatus>
+getCertificate(std::shared_ptr<IKeyMintDevice> device, const std::vector<KeyParameter>& keyParams) {
     KeyCreationResult creationResult;
     auto status = device->generateKey(keyParams, std::nullopt /* attest_key */, &creationResult);
     if (!status.isOk()) {
@@ -76,6 +90,8 @@
         KMV1::makeKeyParameter(KMV1::TAG_ALGORITHM, Algorithm::RSA),
         KMV1::makeKeyParameter(KMV1::TAG_KEY_SIZE, 2048),
         KMV1::makeKeyParameter(KMV1::TAG_RSA_PUBLIC_EXPONENT, 65537),
+        KMV1::makeKeyParameter(KMV1::TAG_CERTIFICATE_NOT_BEFORE, 0),
+        KMV1::makeKeyParameter(KMV1::TAG_CERTIFICATE_NOT_AFTER, 253402300799000),
     });
     keyParams.insert(keyParams.end(), extraParams.begin(), extraParams.end());
     return keyParams;
@@ -87,10 +103,12 @@
         KMV1::makeKeyParameter(KMV1::TAG_PADDING, PaddingMode::RSA_PSS),
         KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED),
         KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::SIGN),
-        KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::ENCRYPT),
     });
-    auto result = getCertificate(keyParams);
-    ensureCertChainSize(result, 1);
+    auto device = getDevice();
+    if (std::holds_alternative<std::shared_ptr<IKeyMintDevice>>(device)) {
+        auto result = getCertificate(std::get<std::shared_ptr<IKeyMintDevice>>(device), keyParams);
+        ensureCertChainSize(result, 1);
+    }
 }
 
 TEST(CertificateTest, TestAES) {
@@ -101,8 +119,11 @@
         KMV1::makeKeyParameter(KMV1::TAG_PADDING, PaddingMode::NONE),
         KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::ENCRYPT),
     };
-    auto result = getCertificate(keyParams);
-    ensureCertChainSize(result, 0);
+    auto device = getDevice();
+    if (std::holds_alternative<std::shared_ptr<IKeyMintDevice>>(device)) {
+        auto result = getCertificate(std::get<std::shared_ptr<IKeyMintDevice>>(device), keyParams);
+        ensureCertChainSize(result, 0);
+    }
 }
 
 TEST(CertificateTest, TestAttestation) {
@@ -111,9 +132,12 @@
         KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_CHALLENGE, 42),
         KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_APPLICATION_ID, 42),
     });
-    auto result = getCertificate(keyParams);
-    ensureCertChainSize(result, 3);
-    verify(std::get<std::vector<Certificate>>(result).back());
+    auto device = getDevice();
+    if (std::holds_alternative<std::shared_ptr<IKeyMintDevice>>(device)) {
+        auto result = getCertificate(std::get<std::shared_ptr<IKeyMintDevice>>(device), keyParams);
+        ensureCertChainSize(result, 3);
+        verify(std::get<std::vector<Certificate>>(result).back());
+    }
 }
 
 TEST(CertificateTest, TestRSAKeygenNoEncryptNoAuthRequired) {
@@ -123,9 +147,12 @@
         KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED, true),
         KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::SIGN),
     });
-    auto result = getCertificate(keyParams);
-    ensureCertChainSize(result, 1);
-    verify(std::get<std::vector<Certificate>>(result)[0]);
+    auto device = getDevice();
+    if (std::holds_alternative<std::shared_ptr<IKeyMintDevice>>(device)) {
+        auto result = getCertificate(std::get<std::shared_ptr<IKeyMintDevice>>(device), keyParams);
+        ensureCertChainSize(result, 1);
+        verify(std::get<std::vector<Certificate>>(result)[0]);
+    }
 }
 
 TEST(CertificateTest, TestRSAKeygenNoEncryptAuthRequired) {
@@ -134,6 +161,9 @@
         KMV1::makeKeyParameter(KMV1::TAG_PADDING, PaddingMode::RSA_PSS),
         KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::SIGN),
     });
-    auto result = getCertificate(keyParams);
-    ensureCertChainSize(result, 1);
+    auto device = getDevice();
+    if (std::holds_alternative<std::shared_ptr<IKeyMintDevice>>(device)) {
+        auto result = getCertificate(std::get<std::shared_ptr<IKeyMintDevice>>(device), keyParams);
+        ensureCertChainSize(result, 1);
+    }
 }
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index f376e97..5c6e42a 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -301,7 +301,9 @@
     if (securityLevel == KeyMintSecurityLevel::SOFTWARE) {
         // If the security level of the backend is `software` we expect the hardware enforced list
         // to be empty. Log a warning otherwise.
-        CHECK(legacyKc.hardwareEnforced.size() == 0);
+        if (legacyKc.hardwareEnforced.size() != 0) {
+            LOG(WARNING) << "Unexpected hardware enforced parameters.";
+        }
         return {keystoreEnforced};
     }
 
@@ -314,26 +316,31 @@
     return static_cast<V4_0_KeyFormat>(kf);
 }
 
-static V4_0_HardwareAuthToken convertAuthTokenToLegacy(const HardwareAuthToken& at) {
+static V4_0_HardwareAuthToken convertAuthTokenToLegacy(const std::optional<HardwareAuthToken>& at) {
+    if (!at) return {};
+
     V4_0_HardwareAuthToken legacyAt;
-    legacyAt.challenge = at.challenge;
-    legacyAt.userId = at.userId;
-    legacyAt.authenticatorId = at.authenticatorId;
+    legacyAt.challenge = at->challenge;
+    legacyAt.userId = at->userId;
+    legacyAt.authenticatorId = at->authenticatorId;
     legacyAt.authenticatorType =
         static_cast<::android::hardware::keymaster::V4_0::HardwareAuthenticatorType>(
-            at.authenticatorType);
-    legacyAt.timestamp = at.timestamp.milliSeconds;
-    legacyAt.mac = at.mac;
+            at->authenticatorType);
+    legacyAt.timestamp = at->timestamp.milliSeconds;
+    legacyAt.mac = at->mac;
     return legacyAt;
 }
 
-static V4_0_VerificationToken convertTimestampTokenToLegacy(const TimeStampToken& tst) {
+static V4_0_VerificationToken
+convertTimestampTokenToLegacy(const std::optional<TimeStampToken>& tst) {
+    if (!tst) return {};
+
     V4_0_VerificationToken legacyVt;
-    legacyVt.challenge = tst.challenge;
-    legacyVt.timestamp = tst.timestamp.milliSeconds;
+    legacyVt.challenge = tst->challenge;
+    legacyVt.timestamp = tst->timestamp.milliSeconds;
     // Legacy verification tokens were always minted by TEE.
     legacyVt.securityLevel = V4_0::SecurityLevel::TRUSTED_ENVIRONMENT;
-    legacyVt.mac = tst.mac;
+    legacyVt.mac = tst->mac;
     return legacyVt;
 }
 
@@ -638,81 +645,85 @@
     }
 }
 
-ScopedAStatus KeyMintOperation::update(const std::optional<KeyParameterArray>& in_inParams,
-                                       const std::optional<std::vector<uint8_t>>& in_input,
-                                       const std::optional<HardwareAuthToken>& in_inAuthToken,
-                                       const std::optional<TimeStampToken>& in_inTimeStampToken,
-                                       std::optional<KeyParameterArray>* out_outParams,
-                                       std::optional<ByteArray>* out_output,
-                                       int32_t* _aidl_return) {
-    std::vector<V4_0_KeyParameter> legacyParams;
-    if (in_inParams.has_value()) {
-        legacyParams = convertKeyParametersToLegacy(in_inParams.value().params);
-    }
-    auto input = in_input.value_or(std::vector<uint8_t>());
-    V4_0_HardwareAuthToken authToken;
-    if (in_inAuthToken.has_value()) {
-        authToken = convertAuthTokenToLegacy(in_inAuthToken.value());
-    }
-    V4_0_VerificationToken verificationToken;
-    if (in_inTimeStampToken.has_value()) {
-        verificationToken = convertTimestampTokenToLegacy(in_inTimeStampToken.value());
-    }
+ScopedAStatus KeyMintOperation::updateAad(const std::vector<uint8_t>& input,
+                                          const std::optional<HardwareAuthToken>& optAuthToken,
+                                          const std::optional<TimeStampToken>& optTimeStampToken) {
+    V4_0_HardwareAuthToken authToken = convertAuthTokenToLegacy(optAuthToken);
+    V4_0_VerificationToken verificationToken = convertTimestampTokenToLegacy(optTimeStampToken);
 
     KMV1::ErrorCode errorCode;
     auto result = mDevice->update(
-        mOperationHandle, legacyParams, input, authToken, verificationToken,
-        [&](V4_0_ErrorCode error, uint32_t inputConsumed,
-            const hidl_vec<V4_0_KeyParameter>& outParams, const hidl_vec<uint8_t>& output) {
-            errorCode = convert(error);
-            out_outParams->emplace();
-            out_outParams->value().params = convertKeyParametersFromLegacy(outParams);
-            out_output->emplace();
-            out_output->value().data = output;
-            *_aidl_return = inputConsumed;
-        });
+        mOperationHandle, {V4_0::makeKeyParameter(V4_0::TAG_ASSOCIATED_DATA, input)}, input,
+        authToken, verificationToken,
+        [&](V4_0_ErrorCode error, auto, auto, auto) { errorCode = convert(error); });
 
     if (!result.isOk()) {
         LOG(ERROR) << __func__ << " transaction failed. " << result.description();
         errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
     }
-    if (errorCode != KMV1::ErrorCode::OK) {
-        mOperationSlot.freeSlot();
-    }
+    if (errorCode != KMV1::ErrorCode::OK) mOperationSlot.freeSlot();
+
     return convertErrorCode(errorCode);
 }
 
-ScopedAStatus KeyMintOperation::finish(const std::optional<KeyParameterArray>& in_inParams,
-                                       const std::optional<std::vector<uint8_t>>& in_input,
-                                       const std::optional<std::vector<uint8_t>>& in_inSignature,
-                                       const std::optional<HardwareAuthToken>& in_authToken,
-                                       const std::optional<TimeStampToken>& in_inTimeStampToken,
-                                       std::optional<KeyParameterArray>* out_outParams,
-                                       std::vector<uint8_t>* _aidl_return) {
-    KMV1::ErrorCode errorCode;
-    std::vector<V4_0_KeyParameter> legacyParams;
-    if (in_inParams.has_value()) {
-        legacyParams = convertKeyParametersToLegacy(in_inParams.value().params);
+ScopedAStatus KeyMintOperation::update(const std::vector<uint8_t>& input,
+                                       const std::optional<HardwareAuthToken>& optAuthToken,
+                                       const std::optional<TimeStampToken>& optTimeStampToken,
+                                       std::vector<uint8_t>* out_output) {
+    V4_0_HardwareAuthToken authToken = convertAuthTokenToLegacy(optAuthToken);
+    V4_0_VerificationToken verificationToken = convertTimestampTokenToLegacy(optTimeStampToken);
+
+    size_t inputPos = 0;
+    *out_output = {};
+    KMV1::ErrorCode errorCode = KMV1::ErrorCode::OK;
+
+    while (inputPos < input.size() && errorCode == KMV1::ErrorCode::OK) {
+        auto result =
+            mDevice->update(mOperationHandle, {} /* inParams */,
+                            {input.begin() + inputPos, input.end()}, authToken, verificationToken,
+                            [&](V4_0_ErrorCode error, uint32_t inputConsumed, auto /* outParams */,
+                                const hidl_vec<uint8_t>& output) {
+                                errorCode = convert(error);
+                                out_output->insert(out_output->end(), output.begin(), output.end());
+                                inputPos += inputConsumed;
+                            });
+
+        if (!result.isOk()) {
+            LOG(ERROR) << __func__ << " transaction failed. " << result.description();
+            errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
+        }
     }
+
+    if (errorCode != KMV1::ErrorCode::OK) mOperationSlot.freeSlot();
+
+    return convertErrorCode(errorCode);
+}
+
+ScopedAStatus
+KeyMintOperation::finish(const std::optional<std::vector<uint8_t>>& in_input,
+                         const std::optional<std::vector<uint8_t>>& in_signature,
+                         const std::optional<HardwareAuthToken>& in_authToken,
+                         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 signature = in_inSignature.value_or(std::vector<uint8_t>());
-    V4_0_HardwareAuthToken authToken;
-    if (in_authToken.has_value()) {
-        authToken = convertAuthTokenToLegacy(in_authToken.value());
+    auto signature = in_signature.value_or(std::vector<uint8_t>());
+    V4_0_HardwareAuthToken authToken = convertAuthTokenToLegacy(in_authToken);
+    V4_0_VerificationToken verificationToken = convertTimestampTokenToLegacy(in_timeStampToken);
+
+    std::vector<V4_0_KeyParameter> inParams;
+    if (in_confirmationToken) {
+        inParams.push_back(makeKeyParameter(V4_0::TAG_CONFIRMATION_TOKEN, *in_confirmationToken));
     }
-    V4_0_VerificationToken verificationToken;
-    if (in_inTimeStampToken.has_value()) {
-        verificationToken = convertTimestampTokenToLegacy(in_inTimeStampToken.value());
-    }
+
+    KMV1::ErrorCode errorCode;
     auto result = mDevice->finish(
-        mOperationHandle, legacyParams, input, signature, authToken, verificationToken,
-        [&](V4_0_ErrorCode error, const hidl_vec<V4_0_KeyParameter>& outParams,
-            const hidl_vec<uint8_t>& output) {
+        mOperationHandle, {} /* inParams */, input, signature, authToken, verificationToken,
+        [&](V4_0_ErrorCode error, auto /* outParams */, const hidl_vec<uint8_t>& output) {
             errorCode = convert(error);
-            out_outParams->emplace();
-            out_outParams->value().params = convertKeyParametersFromLegacy(outParams);
-            *_aidl_return = output;
+            *out_output = output;
         });
+
     mOperationSlot.freeSlot();
     if (!result.isOk()) {
         LOG(ERROR) << __func__ << " transaction failed. " << result.description();
@@ -991,18 +1002,14 @@
                 errorCode = toErrorCode(error);
                 return std::vector<uint8_t>();
             }
-            std::optional<KeyParameterArray> outParams;
-            std::optional<ByteArray> outByte;
-            int32_t status;
-            error = beginResult.operation->update(std::nullopt, dataVec, std::nullopt, std::nullopt,
-                                                  &outParams, &outByte, &status);
-            if (!error.isOk()) {
-                errorCode = toErrorCode(error);
-                return std::vector<uint8_t>();
-            }
+
             std::vector<uint8_t> result;
-            error = beginResult.operation->finish(std::nullopt, std::nullopt, std::nullopt,
-                                                  std::nullopt, std::nullopt, &outParams, &result);
+            error = beginResult.operation->finish(dataVec,                     //
+                                                  {} /* signature */,          //
+                                                  {} /* authToken */,          //
+                                                  {} /* timestampToken */,     //
+                                                  {} /* confirmationToken */,  //
+                                                  &result);
             if (!error.isOk()) {
                 errorCode = toErrorCode(error);
                 return std::vector<uint8_t>();
@@ -1232,11 +1239,11 @@
 // Constructors and helpers.
 
 KeyMintDevice::KeyMintDevice(sp<Keymaster> device, KeyMintSecurityLevel securityLevel)
-    : mDevice(device) {
+    : mDevice(device), securityLevel_(securityLevel) {
     if (securityLevel == KeyMintSecurityLevel::STRONGBOX) {
-        mOperationSlots.setNumFreeSlots(3);
+        setNumFreeSlots(3);
     } else {
-        mOperationSlots.setNumFreeSlots(15);
+        setNumFreeSlots(15);
     }
 
     softKeyMintDevice_.reset(CreateKeyMintDevice(KeyMintSecurityLevel::SOFTWARE));
diff --git a/keystore2/src/km_compat/km_compat.h b/keystore2/src/km_compat/km_compat.h
index 6edb8af..5edb0aa 100644
--- a/keystore2/src/km_compat/km_compat.h
+++ b/keystore2/src/km_compat/km_compat.h
@@ -30,7 +30,6 @@
 
 using ::aidl::android::hardware::security::keymint::AttestationKey;
 using ::aidl::android::hardware::security::keymint::BeginResult;
-using ::aidl::android::hardware::security::keymint::ByteArray;
 using ::aidl::android::hardware::security::keymint::Certificate;
 using ::aidl::android::hardware::security::keymint::HardwareAuthToken;
 using ::aidl::android::hardware::security::keymint::KeyCharacteristics;
@@ -38,7 +37,6 @@
 using ::aidl::android::hardware::security::keymint::KeyFormat;
 using ::aidl::android::hardware::security::keymint::KeyMintHardwareInfo;
 using ::aidl::android::hardware::security::keymint::KeyParameter;
-using ::aidl::android::hardware::security::keymint::KeyParameterArray;
 using ::aidl::android::hardware::security::keymint::KeyPurpose;
 using KeyMintSecurityLevel = ::aidl::android::hardware::security::keymint::SecurityLevel;
 using V4_0_ErrorCode = ::android::hardware::keymaster::V4_0::ErrorCode;
@@ -145,19 +143,22 @@
         : mDevice(device), mOperationHandle(operationHandle), mOperationSlot(slots, isActive) {}
     ~KeyMintOperation();
 
-    ScopedAStatus update(const std::optional<KeyParameterArray>& in_inParams,
-                         const std::optional<std::vector<uint8_t>>& in_input,
-                         const std::optional<HardwareAuthToken>& in_inAuthToken,
-                         const std::optional<TimeStampToken>& in_inTimestampToken,
-                         std::optional<KeyParameterArray>* out_outParams,
-                         std::optional<ByteArray>* out_output, int32_t* _aidl_return);
-    ScopedAStatus finish(const std::optional<KeyParameterArray>& in_inParams,
-                         const std::optional<std::vector<uint8_t>>& in_input,
-                         const std::optional<std::vector<uint8_t>>& in_inSignature,
-                         const std::optional<HardwareAuthToken>& in_authToken,
-                         const std::optional<TimeStampToken>& in_inTimestampToken,
-                         std::optional<KeyParameterArray>* out_outParams,
-                         std::vector<uint8_t>* _aidl_return);
+    ScopedAStatus updateAad(const std::vector<uint8_t>& input,
+                            const std::optional<HardwareAuthToken>& authToken,
+                            const std::optional<TimeStampToken>& timestampToken) override;
+
+    ScopedAStatus update(const std::vector<uint8_t>& input,
+                         const std::optional<HardwareAuthToken>& authToken,
+                         const std::optional<TimeStampToken>& timestampToken,
+                         std::vector<uint8_t>* output) override;
+
+    ScopedAStatus finish(const std::optional<std::vector<uint8_t>>& input,
+                         const std::optional<std::vector<uint8_t>>& signature,
+                         const std::optional<HardwareAuthToken>& authToken,
+                         const std::optional<TimeStampToken>& timeStampToken,
+                         const std::optional<std::vector<uint8_t>>& confirmationToken,
+                         std::vector<uint8_t>* output) override;
+
     ScopedAStatus abort();
 };
 
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
index 9be329f..6e27b5c 100644
--- a/keystore2/src/km_compat/lib.rs
+++ b/keystore2/src/km_compat/lib.rs
@@ -31,8 +31,8 @@
         Algorithm::Algorithm, BeginResult::BeginResult, BlockMode::BlockMode, Digest::Digest,
         ErrorCode::ErrorCode, HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::IKeyMintDevice,
         KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat, KeyParameter::KeyParameter,
-        KeyParameterArray::KeyParameterArray, KeyParameterValue::KeyParameterValue,
-        KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, SecurityLevel::SecurityLevel, Tag::Tag,
+        KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+        SecurityLevel::SecurityLevel, Tag::Tag,
     };
     use android_hardware_security_keymint::binder::{self, Strong};
     use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
@@ -283,49 +283,67 @@
 
         let begin_result = begin(legacy.as_ref(), &blob, KeyPurpose::ENCRYPT, None);
         let operation = begin_result.operation.unwrap();
-        let params = KeyParameterArray {
-            params: vec![KeyParameter {
-                tag: Tag::ASSOCIATED_DATA,
-                value: KeyParameterValue::Blob(b"foobar".to_vec()),
-            }],
-        };
+
+        let update_aad_result = operation.updateAad(
+            &b"foobar".to_vec(),
+            None, /* authToken */
+            None, /* timestampToken */
+        );
+        assert!(update_aad_result.is_ok(), "{:?}", update_aad_result);
+
         let message = [42; 128];
-        let mut out_params = None;
-        let result =
-            operation.finish(Some(&params), Some(&message), None, None, None, &mut out_params);
+        let result = operation.finish(
+            Some(&message),
+            None, /* signature */
+            None, /* authToken */
+            None, /* timestampToken */
+            None, /* confirmationToken */
+        );
         assert!(result.is_ok(), "{:?}", result);
         let ciphertext = result.unwrap();
         assert!(!ciphertext.is_empty());
-        assert!(out_params.is_some());
 
         let begin_result =
             begin(legacy.as_ref(), &blob, KeyPurpose::DECRYPT, Some(begin_result.params));
+
         let operation = begin_result.operation.unwrap();
-        let mut out_params = None;
-        let mut output = None;
+
+        let update_aad_result = operation.updateAad(
+            &b"foobar".to_vec(),
+            None, /* authToken */
+            None, /* timestampToken */
+        );
+        assert!(update_aad_result.is_ok(), "{:?}", update_aad_result);
+
         let result = operation.update(
-            Some(&params),
-            Some(&ciphertext),
-            None,
-            None,
-            &mut out_params,
-            &mut output,
+            &ciphertext,
+            None, /* authToken */
+            None, /* timestampToken */
         );
         assert!(result.is_ok(), "{:?}", result);
-        assert_eq!(result.unwrap(), message.len() as i32);
-        assert!(output.is_some());
-        assert_eq!(output.unwrap().data, message.to_vec());
-        let result = operation.finish(Some(&params), None, None, None, None, &mut out_params);
+        assert_eq!(result.unwrap(), message);
+        let result = operation.finish(
+            None, /* input */
+            None, /* signature */
+            None, /* authToken */
+            None, /* timestampToken */
+            None, /* confirmationToken */
+        );
         assert!(result.is_ok(), "{:?}", result);
-        assert!(out_params.is_some());
     }
 
     #[test]
     fn test_secure_clock() {
         add_keymint_device_service();
         let compat_service: binder::Strong<dyn IKeystoreCompatService> =
-            binder::get_interface(COMPAT_NAME).unwrap();
-        let secure_clock = compat_service.getSecureClock().unwrap();
+            match binder::get_interface(COMPAT_NAME) {
+                Ok(cs) => cs,
+                _ => return,
+            };
+        let secure_clock = match compat_service.getSecureClock() {
+            Ok(sc) => sc,
+            _ => return,
+        };
 
         let challenge = 42;
         let result = secure_clock.generateTimeStamp(challenge);
@@ -339,9 +357,15 @@
     fn test_shared_secret() {
         add_keymint_device_service();
         let compat_service: binder::Strong<dyn IKeystoreCompatService> =
-            binder::get_interface(COMPAT_NAME).unwrap();
-        let shared_secret =
-            compat_service.getSharedSecret(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+            match binder::get_interface(COMPAT_NAME) {
+                Ok(cs) => cs,
+                _ => return,
+            };
+        let shared_secret = match compat_service.getSharedSecret(SecurityLevel::TRUSTED_ENVIRONMENT)
+        {
+            Ok(ss) => ss,
+            _ => return,
+        };
 
         let result = shared_secret.getSharedSecretParameters();
         assert!(result.is_ok(), "{:?}", result);
diff --git a/keystore2/src/km_compat/slot_test.cpp b/keystore2/src/km_compat/slot_test.cpp
index 72d944c..43f3bc6 100644
--- a/keystore2/src/km_compat/slot_test.cpp
+++ b/keystore2/src/km_compat/slot_test.cpp
@@ -24,7 +24,6 @@
 
 using ::aidl::android::hardware::security::keymint::Algorithm;
 using ::aidl::android::hardware::security::keymint::BlockMode;
-using ::aidl::android::hardware::security::keymint::ByteArray;
 using ::aidl::android::hardware::security::keymint::Certificate;
 using ::aidl::android::hardware::security::keymint::Digest;
 using ::aidl::android::hardware::security::keymint::ErrorCode;
@@ -100,18 +99,19 @@
     // Calling finish should free up a slot.
     auto last = operations.back();
     operations.pop_back();
-    std::optional<KeyParameterArray> kpa;
     std::vector<uint8_t> byteVec;
-    auto status = last->finish(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
-                               &kpa, &byteVec);
+    auto status = last->finish(std::nullopt /* input */, std::nullopt /* signature */,
+                               std::nullopt /* authToken */, std::nullopt /* timestampToken */,
+                               std::nullopt /* confirmationToken */, &byteVec);
     ASSERT_TRUE(status.isOk());
     result = begin(device, true);
     ASSERT_TRUE(std::holds_alternative<BeginResult>(result));
     operations.push_back(std::get<BeginResult>(result).operation);
 
     // Calling finish and abort on an already-finished operation should not free up another slot.
-    status = last->finish(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
-                          &kpa, &byteVec);
+    status = last->finish(std::nullopt /* input */, std::nullopt /* signature */,
+                          std::nullopt /* authToken */, std::nullopt /* timestampToken */,
+                          std::nullopt /* confirmationToken */, &byteVec);
     ASSERT_TRUE(!status.isOk());
     status = last->abort();
     ASSERT_TRUE(!status.isOk());
@@ -130,8 +130,9 @@
     operations.push_back(std::get<BeginResult>(result).operation);
 
     // Calling finish and abort on an already-aborted operation should not free up another slot.
-    status = last->finish(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
-                          &kpa, &byteVec);
+    status = last->finish(std::nullopt /* input */, std::nullopt /* signature */,
+                          std::nullopt /* authToken */, std::nullopt /* timestampToken */,
+                          std::nullopt /* confirmationToken */, &byteVec);
     ASSERT_TRUE(!status.isOk());
     status = last->abort();
     ASSERT_TRUE(!status.isOk());
@@ -140,20 +141,20 @@
     ASSERT_EQ(std::get<ScopedAStatus>(result).getServiceSpecificError(),
               static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
 
-    // Generating a certificate with signWith also uses a slot.
+    // Generating a certificate with signWith uses a slot but falls back to not using one.
     auto kps = std::vector<KeyParameter>({
         KMV1::makeKeyParameter(KMV1::TAG_ALGORITHM, Algorithm::RSA),
         KMV1::makeKeyParameter(KMV1::TAG_KEY_SIZE, 2048),
         KMV1::makeKeyParameter(KMV1::TAG_RSA_PUBLIC_EXPONENT, 65537),
         KMV1::makeKeyParameter(KMV1::TAG_DIGEST, Digest::SHA_2_256),
         KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::SIGN),
+        KMV1::makeKeyParameter(KMV1::TAG_CERTIFICATE_NOT_BEFORE, 0),
+        KMV1::makeKeyParameter(KMV1::TAG_CERTIFICATE_NOT_AFTER, 253402300799000),
         KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED, true),
     });
     KeyCreationResult creationResult;
     status = device->generateKey(kps, std::nullopt /* attest_key */, &creationResult);
-    ASSERT_TRUE(!status.isOk());
-    ASSERT_EQ(status.getServiceSpecificError(),
-              static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
+    ASSERT_TRUE(status.isOk());
     // But generating a certificate with signCert does not use a slot.
     kps.pop_back();
     status = device->generateKey(kps, std::nullopt /* attest_key */, &creationResult);
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index 1981022..3fc77b7 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -27,6 +27,7 @@
 };
 use anyhow::{Context, Result};
 use keystore2_crypto::{aes_gcm_decrypt, derive_key_from_password, ZVec};
+use std::collections::{HashMap, HashSet};
 use std::{convert::TryInto, fs::File, path::Path, path::PathBuf};
 use std::{
     fs,
@@ -633,8 +634,98 @@
         Ok(Some(Self::new_from_stream(&mut file).context("In read_generic_blob.")?))
     }
 
-    /// This function constructs the blob file name which has the form:
+    /// Read a legacy vpn profile blob.
+    pub fn read_vpn_profile(&self, uid: u32, alias: &str) -> Result<Option<Vec<u8>>> {
+        let path = match self.make_vpn_profile_filename(uid, alias) {
+            Some(path) => path,
+            None => return Ok(None),
+        };
+
+        let blob =
+            Self::read_generic_blob(&path).context("In read_vpn_profile: Failed to read blob.")?;
+
+        Ok(blob.and_then(|blob| match blob.value {
+            BlobValue::Generic(blob) => Some(blob),
+            _ => {
+                log::info!("Unexpected vpn profile blob type. Ignoring");
+                None
+            }
+        }))
+    }
+
+    /// Remove a vpn profile by the name alias with owner uid.
+    pub fn remove_vpn_profile(&self, uid: u32, alias: &str) -> Result<()> {
+        let path = match self.make_vpn_profile_filename(uid, alias) {
+            Some(path) => path,
+            None => return Ok(()),
+        };
+
+        if let Err(e) = Self::with_retry_interrupted(|| fs::remove_file(path.as_path())) {
+            match e.kind() {
+                ErrorKind::NotFound => return Ok(()),
+                _ => return Err(e).context("In remove_vpn_profile."),
+            }
+        }
+
+        let user_id = uid_to_android_user(uid);
+        self.remove_user_dir_if_empty(user_id)
+            .context("In remove_vpn_profile: Trying to remove empty user dir.")
+    }
+
+    fn is_vpn_profile(encoded_alias: &str) -> bool {
+        // We can check the encoded alias because the prefixes we are interested
+        // in are all in the printable range that don't get mangled.
+        encoded_alias.starts_with("VPN_")
+            || encoded_alias.starts_with("PLATFORM_VPN_")
+            || encoded_alias == "LOCKDOWN_VPN"
+    }
+
+    /// List all profiles belonging to the given uid.
+    pub fn list_vpn_profiles(&self, uid: u32) -> Result<Vec<String>> {
+        let mut path = self.path.clone();
+        let user_id = uid_to_android_user(uid);
+        path.push(format!("user_{}", user_id));
+        let uid_str = uid.to_string();
+        let dir =
+            Self::with_retry_interrupted(|| fs::read_dir(path.as_path())).with_context(|| {
+                format!("In list_vpn_profiles: Failed to open legacy blob database. {:?}", path)
+            })?;
+        let mut result: Vec<String> = Vec::new();
+        for entry in dir {
+            let file_name =
+                entry.context("In list_vpn_profiles: Trying to access dir entry")?.file_name();
+            if let Some(f) = file_name.to_str() {
+                let encoded_alias = &f[uid_str.len() + 1..];
+                if f.starts_with(&uid_str) && Self::is_vpn_profile(encoded_alias) {
+                    result.push(
+                        Self::decode_alias(encoded_alias)
+                            .context("In list_vpn_profiles: Trying to decode alias.")?,
+                    )
+                }
+            }
+        }
+        Ok(result)
+    }
+
+    /// This function constructs the vpn_profile file name which has the form:
     /// user_<android user id>/<uid>_<alias>.
+    fn make_vpn_profile_filename(&self, uid: u32, alias: &str) -> Option<PathBuf> {
+        // legacy vpn entries must start with VPN_ or PLATFORM_VPN_ or are literally called
+        // LOCKDOWN_VPN.
+        if !Self::is_vpn_profile(alias) {
+            return None;
+        }
+
+        let mut path = self.path.clone();
+        let user_id = uid_to_android_user(uid);
+        let encoded_alias = Self::encode_alias(alias);
+        path.push(format!("user_{}", user_id));
+        path.push(format!("{}_{}", uid, encoded_alias));
+        Some(path)
+    }
+
+    /// This function constructs the blob file name which has the form:
+    /// user_<android user id>/<uid>_<prefix>_<alias>.
     fn make_blob_filename(&self, uid: u32, alias: &str, prefix: &str) -> PathBuf {
         let user_id = uid_to_android_user(uid);
         let encoded_alias = Self::encode_alias(&format!("{}_{}", prefix, alias));
@@ -724,6 +815,31 @@
         Ok(result)
     }
 
+    /// List all keystore entries belonging to the given user. Returns a map of UIDs
+    /// to sets of decoded aliases.
+    pub fn list_keystore_entries_for_user(
+        &self,
+        user_id: u32,
+    ) -> Result<HashMap<u32, HashSet<String>>> {
+        let user_entries = self
+            .list_user(user_id)
+            .context("In list_keystore_entries_for_user: Trying to list user.")?;
+
+        let result =
+            user_entries.into_iter().fold(HashMap::<u32, HashSet<String>>::new(), |mut acc, v| {
+                if let Some(sep_pos) = v.find('_') {
+                    if let Ok(uid) = v[0..sep_pos].parse::<u32>() {
+                        if let Some(alias) = Self::extract_alias(&v[sep_pos + 1..]) {
+                            let entry = acc.entry(uid).or_default();
+                            entry.insert(alias);
+                        }
+                    }
+                }
+                acc
+            });
+        Ok(result)
+    }
+
     /// List all keystore entries belonging to the given uid.
     pub fn list_keystore_entries_for_uid(&self, uid: u32) -> Result<Vec<String>> {
         let user_id = uid_to_android_user(uid);
@@ -812,18 +928,24 @@
 
         if something_was_deleted {
             let user_id = uid_to_android_user(uid);
-            if self
-                .is_empty_user(user_id)
-                .context("In remove_keystore_entry: Trying to check for empty user dir.")?
-            {
-                let user_path = self.make_user_path_name(user_id);
-                Self::with_retry_interrupted(|| fs::remove_dir(user_path.as_path())).ok();
-            }
+            self.remove_user_dir_if_empty(user_id)
+                .context("In remove_keystore_entry: Trying to remove empty user dir.")?;
         }
 
         Ok(something_was_deleted)
     }
 
+    fn remove_user_dir_if_empty(&self, user_id: u32) -> Result<()> {
+        if self
+            .is_empty_user(user_id)
+            .context("In remove_user_dir_if_empty: Trying to check for empty user dir.")?
+        {
+            let user_path = self.make_user_path_name(user_id);
+            Self::with_retry_interrupted(|| fs::remove_dir(user_path.as_path())).ok();
+        }
+        Ok(())
+    }
+
     /// Load a legacy key blob entry by uid and alias.
     pub fn load_by_uid_alias(
         &self,
diff --git a/keystore2/src/legacy_migrator.rs b/keystore2/src/legacy_migrator.rs
index 60c6bca..1ae8719 100644
--- a/keystore2/src/legacy_migrator.rs
+++ b/keystore2/src/legacy_migrator.rs
@@ -19,6 +19,7 @@
     KeystoreDB, Uuid, KEYSTORE_UUID,
 };
 use crate::error::Error;
+use crate::key_parameter::KeyParameterValue;
 use crate::legacy_blob::BlobValue;
 use crate::utils::uid_to_android_user;
 use crate::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader};
@@ -66,6 +67,11 @@
     }
 }
 
+enum BulkDeleteRequest {
+    Uid(u32),
+    User(u32),
+}
+
 struct LegacyMigratorState {
     recently_migrated: HashSet<RecentMigration>,
     recently_migrated_super_key: HashSet<u32>,
@@ -356,6 +362,38 @@
         }
     }
 
+    /// Deletes all keys belonging to the given namespace, migrating them into the database
+    /// for subsequent garbage collection if necessary.
+    pub fn bulk_delete_uid(&self, domain: Domain, nspace: i64) -> Result<()> {
+        let uid = match (domain, nspace) {
+            (Domain::APP, nspace) => nspace as u32,
+            (Domain::SELINUX, Self::WIFI_NAMESPACE) => Self::AID_WIFI,
+            // Nothing to do.
+            _ => return Ok(()),
+        };
+
+        let result = self.do_serialized(move |migrator_state| {
+            migrator_state.bulk_delete(BulkDeleteRequest::Uid(uid), false)
+        });
+
+        result.unwrap_or(Ok(()))
+    }
+
+    /// Deletes all keys belonging to the given android user, migrating them into the database
+    /// for subsequent garbage collection if necessary.
+    pub fn bulk_delete_user(
+        &self,
+        user_id: u32,
+        keep_non_super_encrypted_keys: bool,
+    ) -> Result<()> {
+        let result = self.do_serialized(move |migrator_state| {
+            migrator_state
+                .bulk_delete(BulkDeleteRequest::User(user_id), keep_non_super_encrypted_keys)
+        });
+
+        result.unwrap_or(Ok(()))
+    }
+
     /// Queries the legacy database for the presence of a super key for the given user.
     pub fn has_super_key(&self, user_id: u32) -> Result<bool> {
         let result =
@@ -539,6 +577,111 @@
         }
     }
 
+    /// Key migrator request to be run by do_serialized.
+    /// See LegacyMigrator::bulk_delete_uid and LegacyMigrator::bulk_delete_user.
+    fn bulk_delete(
+        &mut self,
+        bulk_delete_request: BulkDeleteRequest,
+        keep_non_super_encrypted_keys: bool,
+    ) -> Result<()> {
+        let (aliases, user_id) = match bulk_delete_request {
+            BulkDeleteRequest::Uid(uid) => (
+                self.legacy_loader
+                    .list_keystore_entries_for_uid(uid)
+                    .context("In bulk_delete: Trying to get aliases for uid.")
+                    .map(|aliases| {
+                        let mut h = HashMap::<u32, HashSet<String>>::new();
+                        h.insert(uid, aliases.into_iter().collect());
+                        h
+                    })?,
+                uid_to_android_user(uid),
+            ),
+            BulkDeleteRequest::User(user_id) => (
+                self.legacy_loader
+                    .list_keystore_entries_for_user(user_id)
+                    .context("In bulk_delete: Trying to get aliases for user_id.")?,
+                user_id,
+            ),
+        };
+
+        let super_key_id = self
+            .db
+            .load_super_key(user_id)
+            .context("In bulk_delete: Failed to load super key")?
+            .map(|(_, entry)| entry.id());
+
+        for (uid, alias) in aliases
+            .into_iter()
+            .map(|(uid, aliases)| aliases.into_iter().map(move |alias| (uid, alias)))
+            .flatten()
+        {
+            let (km_blob_params, _, _) = self
+                .legacy_loader
+                .load_by_uid_alias(uid, &alias, None)
+                .context("In bulk_delete: Trying to load legacy blob.")?;
+
+            // Determine if the key needs special handling to be deleted.
+            let (need_gc, is_super_encrypted) = km_blob_params
+                .as_ref()
+                .map(|(blob, params)| {
+                    (
+                        params.iter().any(|kp| {
+                            KeyParameterValue::RollbackResistance == *kp.key_parameter_value()
+                        }),
+                        blob.is_encrypted(),
+                    )
+                })
+                .unwrap_or((false, false));
+
+            if keep_non_super_encrypted_keys && !is_super_encrypted {
+                continue;
+            }
+
+            if need_gc {
+                let mark_deleted = match km_blob_params
+                    .map(|(blob, _)| (blob.is_strongbox(), blob.take_value()))
+                {
+                    Some((is_strongbox, BlobValue::Encrypted { iv, tag, data })) => {
+                        let mut blob_metadata = BlobMetaData::new();
+                        if let (Ok(km_uuid), Some(super_key_id)) =
+                            (self.get_km_uuid(is_strongbox), super_key_id)
+                        {
+                            blob_metadata.add(BlobMetaEntry::KmUuid(km_uuid));
+                            blob_metadata.add(BlobMetaEntry::Iv(iv.to_vec()));
+                            blob_metadata.add(BlobMetaEntry::AeadTag(tag.to_vec()));
+                            blob_metadata
+                                .add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key_id)));
+                            Some((LegacyBlob::Vec(data), blob_metadata))
+                        } else {
+                            // Oh well - we tried our best, but if we cannot determine which
+                            // KeyMint instance we have to send this blob to, we cannot
+                            // do more than delete the key from the file system.
+                            // And if we don't know which key wraps this key we cannot
+                            // unwrap it for KeyMint either.
+                            None
+                        }
+                    }
+                    Some((_, BlobValue::Decrypted(data))) => {
+                        Some((LegacyBlob::ZVec(data), BlobMetaData::new()))
+                    }
+                    _ => None,
+                };
+
+                if let Some((blob, blob_metadata)) = mark_deleted {
+                    self.db.set_deleted_blob(&blob, &blob_metadata).context(concat!(
+                        "In bulk_delete: Trying to insert deleted ",
+                        "blob into the database for garbage collection."
+                    ))?;
+                }
+            }
+
+            self.legacy_loader
+                .remove_keystore_entry(uid, &alias)
+                .context("In bulk_delete: Trying to remove migrated key.")?;
+        }
+        Ok(())
+    }
+
     fn has_super_key(&mut self, user_id: u32) -> Result<bool> {
         Ok(self.recently_migrated_super_key.contains(&user_id)
             || self.legacy_loader.has_super_key(user_id))
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 358fce8..8fef6cf 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -16,6 +16,7 @@
 #![recursion_limit = "256"]
 
 pub mod apc;
+pub mod async_task;
 pub mod authorization;
 pub mod database;
 pub mod enforcements;
@@ -33,7 +34,6 @@
 pub mod user_manager;
 pub mod utils;
 
-mod async_task;
 mod db_utils;
 mod gc;
 mod super_key;
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
index c98a76b..b6bb6ff 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -129,9 +129,7 @@
 use crate::error::{map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
 use crate::utils::Asp;
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
-    ByteArray::ByteArray, IKeyMintOperation::IKeyMintOperation,
-    KeyParameter::KeyParameter as KmParam, KeyParameterArray::KeyParameterArray,
-    KeyParameterValue::KeyParameterValue as KmParamValue, Tag::Tag,
+    IKeyMintOperation::IKeyMintOperation,
 };
 use android_system_keystore2::aidl::android::system::keystore2::{
     IKeystoreOperation::BnKeystoreOperation, IKeystoreOperation::IKeystoreOperation,
@@ -325,16 +323,6 @@
         Self::check_input_length(aad_input).context("In update_aad")?;
         self.touch();
 
-        let params = KeyParameterArray {
-            params: vec![KmParam {
-                tag: Tag::ASSOCIATED_DATA,
-                value: KmParamValue::Blob(aad_input.into()),
-            }],
-        };
-
-        let mut out_params: Option<KeyParameterArray> = None;
-        let mut output: Option<ByteArray> = None;
-
         let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
             self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
 
@@ -347,14 +335,7 @@
 
         self.update_outcome(
             &mut *outcome,
-            map_km_error(km_op.update(
-                Some(&params),
-                None,
-                hat.as_ref(),
-                tst.as_ref(),
-                &mut out_params,
-                &mut output,
-            )),
+            map_km_error(km_op.updateAad(aad_input, hat.as_ref(), tst.as_ref())),
         )
         .context("In update_aad: KeyMint::update failed.")?;
 
@@ -368,8 +349,6 @@
         Self::check_input_length(input).context("In update")?;
         self.touch();
 
-        let mut out_params: Option<KeyParameterArray> = None;
-
         let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
             self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
 
@@ -380,39 +359,17 @@
             .before_update()
             .context("In update: Trying to get auth tokens.")?;
 
-        let mut result: Option<Vec<u8>> = None;
-        let mut consumed = 0usize;
-        loop {
-            let mut output: Option<ByteArray> = None;
-            consumed += self
-                .update_outcome(
-                    &mut *outcome,
-                    map_km_error(km_op.update(
-                        None,
-                        Some(&input[consumed..]),
-                        hat.as_ref(),
-                        tst.as_ref(),
-                        &mut out_params,
-                        &mut output,
-                    )),
-                )
-                .context("In update: KeyMint::update failed.")? as usize;
+        let output = self
+            .update_outcome(
+                &mut *outcome,
+                map_km_error(km_op.update(input, hat.as_ref(), tst.as_ref())),
+            )
+            .context("In update: KeyMint::update failed.")?;
 
-            match (output, &mut result) {
-                (Some(blob), None) => {
-                    if !blob.data.is_empty() {
-                        result = Some(blob.data)
-                    }
-                }
-                (Some(mut blob), Some(ref mut result)) => {
-                    result.append(&mut blob.data);
-                }
-                (None, _) => {}
-            }
-
-            if consumed == input.len() {
-                return Ok(result);
-            }
+        if output.is_empty() {
+            Ok(None)
+        } else {
+            Ok(Some(output))
         }
     }
 
@@ -425,8 +382,6 @@
         }
         self.touch();
 
-        let mut out_params: Option<KeyParameterArray> = None;
-
         let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
             self.km_op.get_interface().context("In finish: Failed to get KeyMintOperation.")?;
 
@@ -437,23 +392,15 @@
             .before_finish()
             .context("In finish: Trying to get auth tokens.")?;
 
-        let in_params = confirmation_token.map(|token| KeyParameterArray {
-            params: vec![KmParam {
-                tag: Tag::CONFIRMATION_TOKEN,
-                value: KmParamValue::Blob(token),
-            }],
-        });
-
         let output = self
             .update_outcome(
                 &mut *outcome,
                 map_km_error(km_op.finish(
-                    in_params.as_ref(),
                     input,
                     signature,
                     hat.as_ref(),
                     tst.as_ref(),
-                    &mut out_params,
+                    confirmation_token.as_deref(),
                 )),
             )
             .context("In finish: KeyMint::finish failed.")?;
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index fe38504..d606b6a 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -19,28 +19,54 @@
 //! certificate chains signed by some root authority and stored in a keystore SQLite
 //! DB.
 
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use std::collections::HashMap;
 
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    IRemotelyProvisionedComponent::IRemotelyProvisionedComponent, MacedPublicKey::MacedPublicKey,
+    ProtectedData::ProtectedData, SecurityLevel::SecurityLevel,
+};
 use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
     AttestationPoolStatus::AttestationPoolStatus, IRemoteProvisioning::BnRemoteProvisioning,
     IRemoteProvisioning::IRemoteProvisioning,
 };
 use android_security_remoteprovisioning::binder::Strong;
-use anyhow::Result;
+use anyhow::{Context, Result};
 
-use crate::error::map_or_log_err;
-use crate::globals::{get_keymint_device, DB};
+use crate::error::{self, map_or_log_err, map_rem_prov_error};
+use crate::globals::{get_keymint_device, get_remotely_provisioned_component, DB};
+use crate::utils::Asp;
 
 /// Implementation of the IRemoteProvisioning service.
+#[derive(Default)]
 pub struct RemoteProvisioningService {
-    // TODO(b/179222809): Add the remote provisioner hal aidl interface when available
+    device_by_sec_level: HashMap<SecurityLevel, Asp>,
 }
 
 impl RemoteProvisioningService {
+    fn get_dev_by_sec_level(
+        &self,
+        sec_level: &SecurityLevel,
+    ) -> Result<Strong<dyn IRemotelyProvisionedComponent>> {
+        if let Some(dev) = self.device_by_sec_level.get(sec_level) {
+            dev.get_interface().context("In get_dev_by_sec_level.")
+        } else {
+            Err(error::Error::sys()).context(concat!(
+                "In get_dev_by_sec_level: Remote instance for requested security level",
+                " not found."
+            ))
+        }
+    }
+
     /// Creates a new instance of the remote provisioning service
     pub fn new_native_binder() -> Result<Strong<dyn IRemoteProvisioning>> {
-        let result = BnRemoteProvisioning::new_binder(Self {});
-        Ok(result)
+        let mut result: Self = Default::default();
+        let dev = get_remotely_provisioned_component(&SecurityLevel::TRUSTED_ENVIRONMENT)
+            .context("In new_native_binder: Failed to get TEE Remote Provisioner instance.")?;
+        result.device_by_sec_level.insert(SecurityLevel::TRUSTED_ENVIRONMENT, dev);
+        if let Ok(dev) = get_remotely_provisioned_component(&SecurityLevel::STRONGBOX) {
+            result.device_by_sec_level.insert(SecurityLevel::STRONGBOX, dev);
+        }
+        Ok(BnRemoteProvisioning::new_binder(result))
     }
 
     /// Populates the AttestationPoolStatus parcelable with information about how many
@@ -54,6 +80,11 @@
         let (_, _, uuid) = get_keymint_device(&sec_level)?;
         DB.with::<_, Result<AttestationPoolStatus>>(|db| {
             let mut db = db.borrow_mut();
+            // delete_expired_attestation_keys is always safe to call, and will remove anything
+            // older than the date at the time of calling. No work should be done on the
+            // attestation keys unless the pool status is checked first, so this call should be
+            // enough to routinely clean out expired keys.
+            db.delete_expired_attestation_keys()?;
             Ok(db.get_attestation_pool_status(expired_by, &uuid)?)
         })
     }
@@ -68,15 +99,34 @@
     /// baked in root of trust in the underlying IRemotelyProvisionedComponent instance.
     pub fn generate_csr(
         &self,
-        _test_mode: bool,
-        _num_csr: i32,
-        _eek: &[u8],
-        _challenge: &[u8],
-        _sec_level: SecurityLevel,
+        test_mode: bool,
+        num_csr: i32,
+        eek: &[u8],
+        challenge: &[u8],
+        sec_level: SecurityLevel,
+        protected_data: &mut ProtectedData,
     ) -> Result<Vec<u8>> {
-        // TODO(b/179222809): implement with actual remote provisioner AIDL when available. For now
-        //       it isnice to have some junk values
-        Ok(vec![0, 1, 3, 3])
+        let dev = self.get_dev_by_sec_level(&sec_level)?;
+        let (_, _, uuid) = get_keymint_device(&sec_level)?;
+        let keys_to_sign = DB.with::<_, Result<Vec<MacedPublicKey>>>(|db| {
+            let mut db = db.borrow_mut();
+            Ok(db
+                .fetch_unsigned_attestation_keys(num_csr, &uuid)?
+                .iter()
+                .map(|key| MacedPublicKey { macedKey: key.to_vec() })
+                .collect())
+        })?;
+        let mut mac = Vec::<u8>::with_capacity(32);
+        map_rem_prov_error(dev.generateCertificateRequest(
+            test_mode,
+            &keys_to_sign,
+            eek,
+            challenge,
+            &mut mac,
+            protected_data,
+        ))
+        .context("In generate_csr: Failed to generate csr")?;
+        Ok(mac)
     }
 
     /// Provisions a certificate chain for a key whose CSR was included in generate_csr. The
@@ -87,6 +137,7 @@
     pub fn provision_cert_chain(
         &self,
         public_key: &[u8],
+        batch_cert: &[u8],
         certs: &[u8],
         expiration_date: i64,
         sec_level: SecurityLevel,
@@ -96,6 +147,7 @@
             let (_, _, uuid) = get_keymint_device(&sec_level)?;
             Ok(db.store_signed_attestation_certificate_chain(
                 public_key,
+                batch_cert,
                 certs, /* DER encoded certificate chain */
                 expiration_date,
                 &uuid,
@@ -107,8 +159,35 @@
     /// `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,
     /// the key pair is then added to the database.
-    pub fn generate_key_pair(&self, _is_test_mode: bool, _sec_level: SecurityLevel) -> Result<()> {
-        Ok(())
+    pub fn generate_key_pair(&self, is_test_mode: bool, sec_level: SecurityLevel) -> Result<()> {
+        let (_, _, uuid) = get_keymint_device(&sec_level)?;
+        let dev = self.get_dev_by_sec_level(&sec_level)?;
+        let mut maced_key = MacedPublicKey { macedKey: Vec::new() };
+        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]);
+        DB.with::<_, Result<()>>(|db| {
+            let mut db = db.borrow_mut();
+            Ok(db.create_attestation_key_entry(&maced_key.macedKey, &raw_key, &priv_key, &uuid)?)
+        })
+    }
+
+    /// Checks the security level of each available IRemotelyProvisionedComponent hal and returns
+    /// all levels in an array to the caller.
+    pub fn get_security_levels(&self) -> Result<Vec<SecurityLevel>> {
+        Ok(self.device_by_sec_level.keys().cloned().collect())
     }
 }
 
@@ -132,18 +211,26 @@
         eek: &[u8],
         challenge: &[u8],
         sec_level: SecurityLevel,
+        protected_data: &mut ProtectedData,
     ) -> binder::public_api::Result<Vec<u8>> {
-        map_or_log_err(self.generate_csr(test_mode, num_csr, eek, challenge, sec_level), Ok)
+        map_or_log_err(
+            self.generate_csr(test_mode, num_csr, eek, challenge, sec_level, protected_data),
+            Ok,
+        )
     }
 
     fn provisionCertChain(
         &self,
         public_key: &[u8],
+        batch_cert: &[u8],
         certs: &[u8],
         expiration_date: i64,
         sec_level: SecurityLevel,
     ) -> binder::public_api::Result<()> {
-        map_or_log_err(self.provision_cert_chain(public_key, certs, expiration_date, sec_level), Ok)
+        map_or_log_err(
+            self.provision_cert_chain(public_key, batch_cert, certs, expiration_date, sec_level),
+            Ok,
+        )
     }
 
     fn generateKeyPair(
@@ -153,4 +240,8 @@
     ) -> binder::public_api::Result<()> {
         map_or_log_err(self.generate_key_pair(is_test_mode, sec_level), Ok)
     }
+
+    fn getSecurityLevels(&self) -> binder::public_api::Result<Vec<SecurityLevel>> {
+        map_or_log_err(self.get_security_levels(), Ok)
+    }
 }
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index 156d20d..5ee685a 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -451,9 +451,10 @@
         match key_blob_before_upgrade {
             KeyBlob::Sensitive(_, super_key) => {
                 let (key, metadata) = Self::encrypt_with_super_key(key_after_upgrade, super_key)
-                .context(
-                "In reencrypt_on_upgrade_if_required. Failed to re-super-encrypt key on key upgrade.",
-                )?;
+                    .context(concat!(
+                        "In reencrypt_on_upgrade_if_required. ",
+                        "Failed to re-super-encrypt key on key upgrade."
+                    ))?;
                 Ok((KeyBlob::NonSensitive(key), Some(metadata)))
             }
             _ => Ok((KeyBlob::Ref(key_after_upgrade), None)),
@@ -520,8 +521,9 @@
                 if password.is_none() {
                     //transitioning to swiping, delete only the super key in database and cache, and
                     //super-encrypted keys in database (and in KM)
-                    Self::reset_user(db, skm, user_id, true)
-                        .context("In get_with_password_changed.")?;
+                    Self::reset_user(db, skm, legacy_migrator, user_id, true).context(
+                        "In get_with_password_changed: Trying to delete keys from the db.",
+                    )?;
                     //Lskf is now removed in Keystore
                     Ok(UserState::Uninitialized)
                 } else {
@@ -570,10 +572,14 @@
     pub fn reset_user(
         db: &mut KeystoreDB,
         skm: &SuperKeyManager,
+        legacy_migrator: &LegacyMigrator,
         user_id: u32,
         keep_non_super_encrypted_keys: bool,
     ) -> Result<()> {
         // mark keys created on behalf of the user as unreferenced.
+        legacy_migrator
+            .bulk_delete_user(user_id, keep_non_super_encrypted_keys)
+            .context("In reset_user: Trying to delete legacy keys.")?;
         db.unbind_keys_for_user(user_id as u32, keep_non_super_encrypted_keys)
             .context("In reset user. Error in unbinding keys.")?;
 
@@ -583,18 +589,18 @@
     }
 }
 
-/// This enum represents two states a Keymint Blob can be in, w.r.t super encryption.
-/// Sensitive variant represents a Keymint blob that is supposed to be super encrypted,
-/// but unwrapped during usage. Therefore, it has the super key along with the unwrapped key.
-/// Ref variant represents a Keymint blob that is not required to super encrypt or that is
-/// already super encrypted.
+/// This enum represents three states a KeyMint Blob can be in, w.r.t super encryption.
+/// `Sensitive` holds the non encrypted key and a reference to its super key.
+/// `NonSensitive` holds a non encrypted key that is never supposed to be encrypted.
+/// `Ref` holds a reference to a key blob when it does not need to be modified if its
+/// life time allows it.
 pub enum KeyBlob<'a> {
     Sensitive(ZVec, SuperKey),
     NonSensitive(Vec<u8>),
     Ref(&'a [u8]),
 }
 
-/// Deref returns a reference to the key material in both variants.
+/// Deref returns a reference to the key material in any variant.
 impl<'a> Deref for KeyBlob<'a> {
     type Target = [u8];
 
diff --git a/keystore2/src/user_manager.rs b/keystore2/src/user_manager.rs
index 8b7aad9..3c393c5 100644
--- a/keystore2/src/user_manager.rs
+++ b/keystore2/src/user_manager.rs
@@ -24,6 +24,7 @@
     BnKeystoreUserManager, IKeystoreUserManager,
 };
 use android_security_usermanager::binder::{Interface, Result as BinderResult};
+use android_system_keystore2::aidl::android::system::keystore2::Domain::Domain;
 use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
 use anyhow::{Context, Result};
 use binder::{IBinder, Strong};
@@ -59,12 +60,12 @@
             .context("In on_user_password_changed.")?
         {
             UserState::LskfLocked => {
-                //error - password can not be changed when the device is locked
+                // Error - password can not be changed when the device is locked
                 Err(KeystoreError::Rc(ResponseCode::LOCKED))
                     .context("In on_user_password_changed. Device is locked.")
             }
             _ => {
-                //LskfLocked is the only error case for password change
+                // LskfLocked is the only error case for password change
                 Ok(())
             }
         }
@@ -74,7 +75,27 @@
         // Check permission. Function should return if this failed. Therefore having '?' at the end
         // is very important.
         check_keystore_permission(KeystorePerm::change_user()).context("In add_or_remove_user.")?;
-        DB.with(|db| UserState::reset_user(&mut db.borrow_mut(), &SUPER_KEY, user_id as u32, false))
+        DB.with(|db| {
+            UserState::reset_user(
+                &mut db.borrow_mut(),
+                &SUPER_KEY,
+                &LEGACY_MIGRATOR,
+                user_id as u32,
+                false,
+            )
+        })
+        .context("In add_or_remove_user: Trying to delete keys from db.")
+    }
+
+    fn clear_namespace(domain: Domain, nspace: i64) -> Result<()> {
+        // Permission check. Must return on error. Do not touch the '?'.
+        check_keystore_permission(KeystorePerm::clear_uid()).context("In clear_namespace.")?;
+
+        LEGACY_MIGRATOR
+            .bulk_delete_uid(domain, nspace)
+            .context("In clear_namespace: Trying to delete legacy keys.")?;
+        DB.with(|db| db.borrow_mut().unbind_keys_for_namespace(domain, nspace))
+            .context("In clear_namespace: Trying to delete keys from db.")
     }
 }
 
@@ -92,4 +113,8 @@
     fn onUserRemoved(&self, user_id: i32) -> BinderResult<()> {
         map_or_log_err(Self::add_or_remove_user(user_id), Ok)
     }
+
+    fn clearNamespace(&self, domain: Domain, nspace: i64) -> BinderResult<()> {
+        map_or_log_err(Self::clear_namespace(domain, nspace), Ok)
+    }
 }
diff --git a/keystore2/src/vintf/Android.bp b/keystore2/src/vintf/Android.bp
new file mode 100644
index 0000000..da7935a
--- /dev/null
+++ b/keystore2/src/vintf/Android.bp
@@ -0,0 +1,70 @@
+// Copyright 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.
+
+rust_library {
+    name: "libkeystore2_vintf_rust",
+    crate_name: "keystore2_vintf",
+    srcs: ["lib.rs"],
+    rustlibs: [
+        "libkeystore2_vintf_bindgen",
+    ],
+    shared_libs: [
+        "libkeystore2_vintf_cpp",
+        "libvintf",
+    ],
+}
+
+cc_library {
+    name: "libkeystore2_vintf_cpp",
+    srcs: [
+        "vintf.cpp",
+    ],
+    shared_libs: [
+        "libvintf",
+    ],
+}
+
+rust_bindgen {
+    name: "libkeystore2_vintf_bindgen",
+    wrapper_src: "vintf.hpp",
+    crate_name: "keystore2_vintf_bindgen",
+    source_stem: "bindings",
+    host_supported: true,
+    shared_libs: ["libvintf"],
+    bindgen_flags: [
+        "--size_t-is-usize",
+        "--whitelist-function", "getHalNames",
+        "--whitelist-function", "getHalNamesAndVersions",
+        "--whitelist-function", "getAidlInstances",
+        "--whitelist-function", "freeNames",
+    ],
+}
+
+rust_test {
+    name: "keystore2_vintf_test",
+    crate_name: "keystore2_vintf_test",
+    srcs: ["lib.rs"],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    rustlibs: [
+        "libkeystore2_vintf_bindgen",
+    ],
+    static_libs: [
+        "libkeystore2_vintf_cpp",
+    ],
+    shared_libs: [
+        "libc++",
+        "libvintf",
+    ],
+}
diff --git a/keystore2/src/vintf/lib.rs b/keystore2/src/vintf/lib.rs
new file mode 100644
index 0000000..c3d6d8a
--- /dev/null
+++ b/keystore2/src/vintf/lib.rs
@@ -0,0 +1,99 @@
+// Copyright 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.
+
+//! Bindings for getting the list of HALs.
+
+use keystore2_vintf_bindgen::{freeNames, getAidlInstances, getHalNames, getHalNamesAndVersions};
+use std::ffi::{CStr, CString};
+use std::os::raw::c_char;
+use std::str::Utf8Error;
+
+/// A struct that contains a list of HALs (optionally with version numbers).
+/// To use it, call as_vec to get a Vec view of the data it contains.
+pub struct HalNames {
+    data: *mut *mut c_char,
+    len: usize,
+}
+
+impl Drop for HalNames {
+    fn drop(&mut self) {
+        // Safety: The memory is allocated by our C shim so it must free it as well.
+        unsafe { freeNames(self.data, self.len) }
+    }
+}
+
+impl<'a> HalNames {
+    /// Get a Vec view of the list of HALs.
+    pub fn as_vec(&'a self) -> Result<Vec<&'a str>, Utf8Error> {
+        // Safety: self.data contains self.len C strings.
+        // The lifetimes ensure that the HalNames (and hence the strings) live
+        // at least as long as the returned vector.
+        unsafe { (0..self.len).map(|i| CStr::from_ptr(*self.data.add(i)).to_str()) }.collect()
+    }
+}
+
+/// Gets all HAL names.
+/// Note that this is not a zero-cost shim: it will make copies of the strings.
+pub fn get_hal_names() -> HalNames {
+    let mut len: usize = 0;
+    // Safety: We'll wrap this in HalNames to free the memory it allocates.
+    // It stores the size of the array it returns in len.
+    let raw_strs = unsafe { getHalNames(&mut len) };
+    HalNames { data: raw_strs, len }
+}
+
+/// Gets all HAL names and versions.
+/// Note that this is not a zero-cost shim: it will make copies of the strings.
+pub fn get_hal_names_and_versions() -> HalNames {
+    let mut len: usize = 0;
+    // Safety: We'll wrap this in HalNames to free the memory it allocates.
+    // It stores the size of the array it returns in len.
+    let raw_strs = unsafe { getHalNamesAndVersions(&mut len) };
+    HalNames { data: raw_strs, len }
+}
+
+/// Gets the instances of the given package, version, and interface tuple.
+/// Note that this is not a zero-cost shim: it will make copies of the strings.
+pub fn get_aidl_instances(package: &str, version: usize, interface_name: &str) -> HalNames {
+    let mut len: usize = 0;
+    let packages = CString::new(package).expect("Failed to make CString from package.");
+    let interface_name =
+        CString::new(interface_name).expect("Failed to make CString from interface_name.");
+    // Safety: We'll wrap this in HalNames to free the memory it allocates.
+    // It stores the size of the array it returns in len.
+    let raw_strs =
+        unsafe { getAidlInstances(&mut len, packages.as_ptr(), version, interface_name.as_ptr()) };
+    HalNames { data: raw_strs, len }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+
+    #[test]
+    fn test() -> Result<(), Utf8Error> {
+        let result = get_hal_names();
+        let names = result.as_vec()?;
+        assert_ne!(names.len(), 0);
+
+        let result = get_hal_names_and_versions();
+        let names_and_versions = result.as_vec()?;
+        assert_ne!(names_and_versions.len(), 0);
+
+        assert!(names_and_versions.len() >= names.len());
+
+        Ok(())
+    }
+}
diff --git a/keystore2/src/vintf/vintf.cpp b/keystore2/src/vintf/vintf.cpp
new file mode 100644
index 0000000..dbdc046
--- /dev/null
+++ b/keystore2/src/vintf/vintf.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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 "vintf.hpp"
+
+#include <vintf/HalManifest.h>
+#include <vintf/VintfObject.h>
+
+// Converts a set<string> into a C-style array of C strings.
+static char** convert(const std::set<std::string>& names) {
+    char** ret = new char*[names.size()];
+    char** ptr = ret;
+    for (const auto& name : names) {
+        *(ptr++) = strdup(name.c_str());
+    }
+    return ret;
+}
+
+char** getHalNames(size_t* len) {
+    auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+    const auto names = manifest->getHalNames();
+    *len = names.size();
+    return convert(names);
+}
+
+char** getHalNamesAndVersions(size_t* len) {
+    auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+    const auto names = manifest->getHalNamesAndVersions();
+    *len = names.size();
+    return convert(names);
+}
+
+char** getAidlInstances(size_t* len, const char* package, size_t version,
+                        const char* interfaceName) {
+    auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+    const auto names = manifest->getAidlInstances(package, version, interfaceName);
+    *len = names.size();
+    return convert(names);
+}
+
+void freeNames(char** names, size_t len) {
+    for (int i = 0; i < len; i++) {
+        free(names[i]);
+    }
+    delete[] names;
+}
diff --git a/keystore2/src/vintf/vintf.hpp b/keystore2/src/vintf/vintf.hpp
new file mode 100644
index 0000000..75e80f6
--- /dev/null
+++ b/keystore2/src/vintf/vintf.hpp
@@ -0,0 +1,31 @@
+/*
+ * 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 __VINTF_H__
+#define __VINTF_H__
+
+#include <stddef.h>
+
+extern "C" {
+
+char** getHalNames(size_t* len);
+char** getHalNamesAndVersions(size_t* len);
+char** getAidlInstances(size_t* len, const char* package, size_t version,
+                        const char* interfaceName);
+void freeNames(char** names, size_t len);
+}
+
+#endif  //  __VINTF_H__
diff --git a/keystore2/vpnprofilestore/Android.bp b/keystore2/vpnprofilestore/Android.bp
new file mode 100644
index 0000000..2fb9aab
--- /dev/null
+++ b/keystore2/vpnprofilestore/Android.bp
@@ -0,0 +1,48 @@
+// Copyright 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.
+
+rust_library {
+    name: "libvpnprofilestore-rust",
+    crate_name: "vpnprofilestore",
+    srcs: [
+        "lib.rs",
+    ],
+    rustlibs: [
+        "android.security.vpnprofilestore-rust",
+        "libanyhow",
+        "libbinder_rs",
+        "libkeystore2",
+        "liblog_rust",
+        "librusqlite",
+        "libthiserror",
+    ],
+}
+
+rust_test {
+    name: "vpnprofilestore_test",
+    crate_name: "vpnprofilestore",
+    srcs: ["lib.rs"],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    rustlibs: [
+        "android.security.vpnprofilestore-rust",
+        "libanyhow",
+        "libbinder_rs",
+        "libkeystore2",
+        "libkeystore2_test_utils",
+        "liblog_rust",
+        "librusqlite",
+        "libthiserror",
+    ],
+}
diff --git a/keystore2/vpnprofilestore/lib.rs b/keystore2/vpnprofilestore/lib.rs
new file mode 100644
index 0000000..f92eacd
--- /dev/null
+++ b/keystore2/vpnprofilestore/lib.rs
@@ -0,0 +1,443 @@
+// Copyright 2020, 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.
+
+//! Implements the android.security.vpnprofilestore interface.
+
+use android_security_vpnprofilestore::aidl::android::security::vpnprofilestore::{
+    IVpnProfileStore::BnVpnProfileStore, IVpnProfileStore::IVpnProfileStore,
+    IVpnProfileStore::ERROR_PROFILE_NOT_FOUND, IVpnProfileStore::ERROR_SYSTEM_ERROR,
+};
+use android_security_vpnprofilestore::binder::{Result as BinderResult, Status as BinderStatus};
+use anyhow::{Context, Result};
+use binder::{ExceptionCode, Strong, ThreadState};
+use keystore2::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader};
+use rusqlite::{
+    params, Connection, OptionalExtension, Transaction, TransactionBehavior, NO_PARAMS,
+};
+use std::{
+    collections::HashSet,
+    path::{Path, PathBuf},
+};
+
+struct DB {
+    conn: Connection,
+}
+
+impl DB {
+    fn new(db_file: &Path) -> Result<Self> {
+        let mut db = Self {
+            conn: Connection::open(db_file).context("Failed to initialize SQLite connection.")?,
+        };
+        db.init_tables().context("Trying to initialize vpnstore db.")?;
+        Ok(db)
+    }
+
+    fn with_transaction<T, F>(&mut self, behavior: TransactionBehavior, f: F) -> Result<T>
+    where
+        F: Fn(&Transaction) -> Result<T>,
+    {
+        loop {
+            match self
+                .conn
+                .transaction_with_behavior(behavior)
+                .context("In with_transaction.")
+                .and_then(|tx| f(&tx).map(|result| (result, tx)))
+                .and_then(|(result, tx)| {
+                    tx.commit().context("In with_transaction: Failed to commit transaction.")?;
+                    Ok(result)
+                }) {
+                Ok(result) => break Ok(result),
+                Err(e) => {
+                    if Self::is_locked_error(&e) {
+                        std::thread::sleep(std::time::Duration::from_micros(500));
+                        continue;
+                    } else {
+                        return Err(e).context("In with_transaction.");
+                    }
+                }
+            }
+        }
+    }
+
+    fn is_locked_error(e: &anyhow::Error) -> bool {
+        matches!(e.root_cause().downcast_ref::<rusqlite::ffi::Error>(),
+        Some(rusqlite::ffi::Error {
+            code: rusqlite::ErrorCode::DatabaseBusy,
+            ..
+        })
+        | Some(rusqlite::ffi::Error {
+            code: rusqlite::ErrorCode::DatabaseLocked,
+            ..
+        }))
+    }
+
+    fn init_tables(&mut self) -> Result<()> {
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            tx.execute(
+                "CREATE TABLE IF NOT EXISTS profiles (
+                     owner INTEGER,
+                     alias BLOB,
+                     profile BLOB,
+                     UNIQUE(owner, alias));",
+                NO_PARAMS,
+            )
+            .context("Failed to initialize \"profiles\" table.")?;
+            Ok(())
+        })
+    }
+
+    fn list(&mut self, caller_uid: u32) -> Result<Vec<String>> {
+        self.with_transaction(TransactionBehavior::Deferred, |tx| {
+            let mut stmt = tx
+                .prepare("SELECT alias FROM profiles WHERE owner = ? ORDER BY alias ASC;")
+                .context("In list: Failed to prepare statement.")?;
+
+            let aliases = stmt
+                .query_map(params![caller_uid], |row| row.get(0))?
+                .collect::<rusqlite::Result<Vec<String>>>()
+                .context("In list: query_map failed.");
+            aliases
+        })
+    }
+
+    fn put(&mut self, caller_uid: u32, alias: &str, profile: &[u8]) -> Result<()> {
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            tx.execute(
+                "INSERT OR REPLACE INTO profiles (owner, alias, profile) values (?, ?, ?)",
+                params![caller_uid, alias, profile,],
+            )
+            .context("In put: Failed to insert or replace.")?;
+            Ok(())
+        })
+    }
+
+    fn get(&mut self, caller_uid: u32, alias: &str) -> Result<Option<Vec<u8>>> {
+        self.with_transaction(TransactionBehavior::Deferred, |tx| {
+            tx.query_row(
+                "SELECT profile FROM profiles WHERE owner = ? AND alias = ?;",
+                params![caller_uid, alias],
+                |row| row.get(0),
+            )
+            .optional()
+            .context("In get: failed loading profile.")
+        })
+    }
+
+    fn remove(&mut self, caller_uid: u32, alias: &str) -> Result<bool> {
+        let removed = self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            tx.execute(
+                "DELETE FROM profiles WHERE owner = ? AND alias = ?;",
+                params![caller_uid, alias],
+            )
+            .context("In remove: Failed to delete row.")
+        })?;
+        Ok(removed == 1)
+    }
+}
+
+/// This is the main VpnProfileStore error type, it wraps binder exceptions and the
+/// VnpStore errors.
+#[derive(Debug, thiserror::Error, PartialEq)]
+pub enum Error {
+    /// Wraps a VpnProfileStore error code.
+    #[error("Error::Error({0:?})")]
+    Error(i32),
+    /// Wraps a Binder exception code other than a service specific exception.
+    #[error("Binder exception code {0:?}, {1:?}")]
+    Binder(ExceptionCode, i32),
+}
+
+impl Error {
+    /// Short hand for `Error::Error(ERROR_SYSTEM_ERROR)`
+    pub fn sys() -> Self {
+        Error::Error(ERROR_SYSTEM_ERROR)
+    }
+
+    /// Short hand for `Error::Error(ERROR_PROFILE_NOT_FOUND)`
+    pub fn not_found() -> Self {
+        Error::Error(ERROR_PROFILE_NOT_FOUND)
+    }
+}
+
+/// This function should be used by vpnprofilestore service calls to translate error conditions
+/// into service specific exceptions.
+///
+/// All error conditions get logged by this function.
+///
+/// `Error::Error(x)` variants get mapped onto a service specific error code of `x`.
+///
+/// All non `Error` error conditions get mapped onto `ERROR_SYSTEM_ERROR`.
+///
+/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
+/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
+/// typically returns Ok(value).
+fn map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T>
+where
+    F: FnOnce(U) -> BinderResult<T>,
+{
+    result.map_or_else(
+        |e| {
+            log::error!("{:#?}", e);
+            let root_cause = e.root_cause();
+            let rc = match root_cause.downcast_ref::<Error>() {
+                Some(Error::Error(e)) => *e,
+                Some(Error::Binder(_, _)) | None => ERROR_SYSTEM_ERROR,
+            };
+            Err(BinderStatus::new_service_specific_error(rc, None))
+        },
+        handle_ok,
+    )
+}
+
+/// Implements IVpnProfileStore AIDL interface.
+pub struct VpnProfileStore {
+    db_path: PathBuf,
+    async_task: AsyncTask,
+}
+
+struct AsyncState {
+    recently_imported: HashSet<(u32, String)>,
+    legacy_loader: LegacyBlobLoader,
+    db_path: PathBuf,
+}
+
+impl VpnProfileStore {
+    /// Creates a new VpnProfileStore instance.
+    pub fn new_native_binder(path: &Path) -> Strong<dyn IVpnProfileStore> {
+        let mut db_path = path.to_path_buf();
+        db_path.push("vpnprofilestore.sqlite");
+
+        let result = Self { db_path, async_task: Default::default() };
+        result.init_shelf(path);
+        BnVpnProfileStore::new_binder(result)
+    }
+
+    fn open_db(&self) -> Result<DB> {
+        DB::new(&self.db_path).context("In open_db: Failed to open db.")
+    }
+
+    fn get(&self, alias: &str) -> Result<Vec<u8>> {
+        let mut db = self.open_db().context("In get.")?;
+        let calling_uid = ThreadState::get_calling_uid();
+
+        if let Some(profile) =
+            db.get(calling_uid, alias).context("In get: Trying to load profile from DB.")?
+        {
+            return Ok(profile);
+        }
+        if self.get_legacy(calling_uid, alias).context("In get: Trying to migrate legacy blob.")? {
+            // If we were able to migrate a legacy blob try again.
+            if let Some(profile) =
+                db.get(calling_uid, alias).context("In get: Trying to load profile from DB.")?
+            {
+                return Ok(profile);
+            }
+        }
+        Err(Error::not_found()).context("In get: No such profile.")
+    }
+
+    fn put(&self, alias: &str, profile: &[u8]) -> Result<()> {
+        let calling_uid = ThreadState::get_calling_uid();
+        // In order to make sure that we don't have stale legacy profiles, make sure they are
+        // migrated before replacing them.
+        let _ = self.get_legacy(calling_uid, alias);
+        let mut db = self.open_db().context("In put.")?;
+        db.put(calling_uid, alias, profile).context("In put: Trying to insert profile into DB.")
+    }
+
+    fn remove(&self, alias: &str) -> Result<()> {
+        let calling_uid = ThreadState::get_calling_uid();
+        let mut db = self.open_db().context("In remove.")?;
+        // In order to make sure that we don't have stale legacy profiles, make sure they are
+        // migrated before removing them.
+        let _ = self.get_legacy(calling_uid, alias);
+        let removed = db
+            .remove(calling_uid, alias)
+            .context("In remove: Trying to remove profile from DB.")?;
+        if removed {
+            Ok(())
+        } else {
+            Err(Error::not_found()).context("In remove: No such profile.")
+        }
+    }
+
+    fn list(&self, prefix: &str) -> Result<Vec<String>> {
+        let mut db = self.open_db().context("In list.")?;
+        let calling_uid = ThreadState::get_calling_uid();
+        let mut result = self.list_legacy(calling_uid).context("In list.")?;
+        result
+            .append(&mut db.list(calling_uid).context("In list: Trying to get list of profiles.")?);
+        result = result.into_iter().filter(|s| s.starts_with(prefix)).collect();
+        result.sort_unstable();
+        result.dedup();
+        Ok(result)
+    }
+
+    fn init_shelf(&self, path: &Path) {
+        let mut db_path = path.to_path_buf();
+        self.async_task.queue_hi(move |shelf| {
+            let legacy_loader = LegacyBlobLoader::new(&db_path);
+            db_path.push("vpnprofilestore.sqlite");
+
+            shelf.put(AsyncState { legacy_loader, db_path, recently_imported: Default::default() });
+        })
+    }
+
+    fn do_serialized<F, T: Send + 'static>(&self, f: F) -> Result<T>
+    where
+        F: FnOnce(&mut AsyncState) -> Result<T> + Send + 'static,
+    {
+        let (sender, receiver) = std::sync::mpsc::channel::<Result<T>>();
+        self.async_task.queue_hi(move |shelf| {
+            let state = shelf.get_downcast_mut::<AsyncState>().expect("Failed to get shelf.");
+            sender.send(f(state)).expect("Failed to send result.");
+        });
+        receiver.recv().context("In do_serialized: Failed to receive result.")?
+    }
+
+    fn list_legacy(&self, uid: u32) -> Result<Vec<String>> {
+        self.do_serialized(move |state| {
+            state
+                .legacy_loader
+                .list_vpn_profiles(uid)
+                .context("Trying to list legacy vnp profiles.")
+        })
+        .context("In list_legacy.")
+    }
+
+    fn get_legacy(&self, uid: u32, alias: &str) -> Result<bool> {
+        let alias = alias.to_string();
+        self.do_serialized(move |state| {
+            if state.recently_imported.contains(&(uid, alias.clone())) {
+                return Ok(true);
+            }
+            let mut db = DB::new(&state.db_path).context("In open_db: Failed to open db.")?;
+            let migrated =
+                Self::migrate_one_legacy_profile(uid, &alias, &state.legacy_loader, &mut db)
+                    .context("Trying to migrate legacy vpn profile.")?;
+            if migrated {
+                state.recently_imported.insert((uid, alias));
+            }
+            Ok(migrated)
+        })
+        .context("In get_legacy.")
+    }
+
+    fn migrate_one_legacy_profile(
+        uid: u32,
+        alias: &str,
+        legacy_loader: &LegacyBlobLoader,
+        db: &mut DB,
+    ) -> Result<bool> {
+        let blob = legacy_loader
+            .read_vpn_profile(uid, alias)
+            .context("In migrate_one_legacy_profile: Trying to read legacy vpn profile.")?;
+        if let Some(profile) = blob {
+            db.put(uid, alias, &profile)
+                .context("In migrate_one_legacy_profile: Trying to insert profile into DB.")?;
+            legacy_loader
+                .remove_vpn_profile(uid, alias)
+                .context("In migrate_one_legacy_profile: Trying to delete legacy profile.")?;
+            Ok(true)
+        } else {
+            Ok(false)
+        }
+    }
+}
+
+impl binder::Interface for VpnProfileStore {}
+
+impl IVpnProfileStore for VpnProfileStore {
+    fn get(&self, alias: &str) -> BinderResult<Vec<u8>> {
+        map_or_log_err(self.get(alias), Ok)
+    }
+    fn put(&self, alias: &str, profile: &[u8]) -> BinderResult<()> {
+        map_or_log_err(self.put(alias, profile), Ok)
+    }
+    fn remove(&self, alias: &str) -> BinderResult<()> {
+        map_or_log_err(self.remove(alias), Ok)
+    }
+    fn list(&self, prefix: &str) -> BinderResult<Vec<String>> {
+        map_or_log_err(self.list(prefix), Ok)
+    }
+}
+
+#[cfg(test)]
+mod db_test {
+    use super::*;
+    use keystore2_test_utils::TempDir;
+
+    static TEST_BLOB1: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
+    static TEST_BLOB2: &[u8] = &[2, 2, 3, 4, 5, 6, 7, 8, 9, 0];
+    static TEST_BLOB3: &[u8] = &[3, 2, 3, 4, 5, 6, 7, 8, 9, 0];
+    static TEST_BLOB4: &[u8] = &[3, 2, 3, 4, 5, 6, 7, 8, 9, 0];
+
+    #[test]
+    fn test_profile_db() {
+        let test_dir = TempDir::new("profiledb_test_").expect("Failed to create temp dir.");
+        let mut db =
+            DB::new(&test_dir.build().push("vpnprofile.sqlite")).expect("Failed to open database.");
+
+        // Insert three profiles for owner 2.
+        db.put(2, "test1", TEST_BLOB1).expect("Failed to insert test1.");
+        db.put(2, "test2", TEST_BLOB2).expect("Failed to insert test2.");
+        db.put(2, "test3", TEST_BLOB3).expect("Failed to insert test3.");
+
+        // Check list returns all inserted aliases.
+        assert_eq!(
+            vec!["test1".to_string(), "test2".to_string(), "test3".to_string(),],
+            db.list(2).expect("Failed to list profiles.")
+        );
+
+        // There should be no profiles for owner 1.
+        assert_eq!(Vec::<String>::new(), db.list(1).expect("Failed to list profiles."));
+
+        // Check the content of the three entries.
+        assert_eq!(
+            Some(TEST_BLOB1),
+            db.get(2, "test1").expect("Failed to get profile.").as_deref()
+        );
+        assert_eq!(
+            Some(TEST_BLOB2),
+            db.get(2, "test2").expect("Failed to get profile.").as_deref()
+        );
+        assert_eq!(
+            Some(TEST_BLOB3),
+            db.get(2, "test3").expect("Failed to get profile.").as_deref()
+        );
+
+        // Remove test2 and check and check that it is no longer retrievable.
+        assert!(db.remove(2, "test2").expect("Failed to remove profile."));
+        assert!(db.get(2, "test2").expect("Failed to get profile.").is_none());
+
+        // test2 should now no longer be in the list.
+        assert_eq!(
+            vec!["test1".to_string(), "test3".to_string(),],
+            db.list(2).expect("Failed to list profiles.")
+        );
+
+        // Put on existing alias replaces it.
+        // Verify test1 is TEST_BLOB1.
+        assert_eq!(
+            Some(TEST_BLOB1),
+            db.get(2, "test1").expect("Failed to get profile.").as_deref()
+        );
+        db.put(2, "test1", TEST_BLOB4).expect("Failed to replace test1.");
+        // Verify test1 is TEST_BLOB4.
+        assert_eq!(
+            Some(TEST_BLOB4),
+            db.get(2, "test1").expect("Failed to get profile.").as_deref()
+        );
+    }
+}
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
index d47a04f..5db19b7 100644
--- a/ondevice-signing/Android.bp
+++ b/ondevice-signing/Android.bp
@@ -71,7 +71,7 @@
   tidy_checks: tidy_errors,
   tidy_checks_as_errors: tidy_errors,
   tidy_flags: [
-    "-format-style='file'",
+    "-format-style=file",
   ],
 }
 
diff --git a/ondevice-signing/Keymaster.cpp b/ondevice-signing/Keymaster.cpp
index d43828a..6cfb565 100644
--- a/ondevice-signing/Keymaster.cpp
+++ b/ondevice-signing/Keymaster.cpp
@@ -66,9 +66,12 @@
         }
     }
 
+    if (devToUse == nullptr) {
+        LOG(WARNING) << "Didn't find a keymaster to use.";
+    }
     mDevice = devToUse;
 
-    return true;
+    return mDevice != nullptr;
 }
 
 std::optional<Keymaster> Keymaster::getInstance() {
diff --git a/ondevice-signing/KeymasterSigningKey.cpp b/ondevice-signing/KeymasterSigningKey.cpp
index f35f92b..2b748e4 100644
--- a/ondevice-signing/KeymasterSigningKey.cpp
+++ b/ondevice-signing/KeymasterSigningKey.cpp
@@ -61,7 +61,11 @@
 
 Result<void> KeymasterSigningKey::createSigningKey() {
     KeymasterSigningKey signingKey;
-    mKeymaster = Keymaster::getInstance();
+    auto keymaster = Keymaster::getInstance();
+    if (!keymaster.has_value()) {
+        return Error() << "Failed to initialize keymaster.";
+    }
+    mKeymaster = keymaster;
 
     auto keyBlob = mKeymaster->createKey();
 
@@ -112,8 +116,12 @@
 }
 
 Result<void> KeymasterSigningKey::initializeFromKeyblob(const std::string& path) {
-    mKeymaster = Keymaster::getInstance();
     std::string keyBlobData;
+    auto keymaster = Keymaster::getInstance();
+    if (!keymaster.has_value()) {
+        return Error() << "Failed to initialize keymaster.";
+    }
+    mKeymaster = keymaster;
 
     bool result = ReadFileToString(path, &keyBlobData);
     if (!result) {
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
index 579d3d8..b4a6a54 100644
--- a/ondevice-signing/VerityUtils.cpp
+++ b/ondevice-signing/VerityUtils.cpp
@@ -76,9 +76,10 @@
 
 static Result<std::vector<uint8_t>> signDigest(const KeymasterSigningKey& key,
                                                const std::vector<uint8_t>& digest) {
-    struct fsverity_signed_digest* d = NULL;
+    fsverity_signed_digest* d;
     size_t signed_digest_size = sizeof(*d) + digest.size();
-    d = (struct fsverity_signed_digest*)malloc(signed_digest_size);
+    std::unique_ptr<uint8_t[]> digest_buffer{new uint8_t[signed_digest_size]};
+    d = (fsverity_signed_digest*)digest_buffer.get();
 
     memcpy(d->magic, "FSVerity", 8);
     d->digest_algorithm = cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
index 2ef9511..3baba68 100644
--- a/ondevice-signing/odsign_main.cpp
+++ b/ondevice-signing/odsign_main.cpp
@@ -50,6 +50,7 @@
 Result<void> addCertToFsVerityKeyring(const std::string& path) {
     const char* const argv[] = {kFsVerityInitPath, "--load-extra-key", "fsv_ods"};
 
+    // NOLINTNEXTLINE(android-cloexec-open): Deliberately not O_CLOEXEC
     int fd = open(path.c_str(), O_RDONLY);
     pid_t pid = fork();
     if (pid == 0) {
@@ -91,7 +92,7 @@
     return KeymasterSigningKey::loadFromBlobAndVerify(kSigningKeyBlob);
 }
 
-Result<void> verifyAndLoadExistingCert(const KeymasterSigningKey& key) {
+Result<void> verifyExistingCert(const KeymasterSigningKey& key) {
     if (access(kSigningKeyCert.c_str(), F_OK) < 0) {
         return ErrnoError() << "Key certificate not found: " << kSigningKeyCert;
     }
@@ -109,11 +110,6 @@
                        << " does not match signing public key.";
     }
 
-    auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
-    if (!cert_add_result.ok()) {
-        return cert_add_result.error();
-    }
-
     // At this point, we know the cert matches
     return {};
 }
@@ -174,7 +170,7 @@
         LOG(INFO) << "Found and verified existing key: " << kSigningKeyBlob;
     }
 
-    auto existing_cert = verifyAndLoadExistingCert(key.value());
+    auto existing_cert = verifyExistingCert(key.value());
     if (!existing_cert.ok()) {
         LOG(WARNING) << existing_cert.error().message();
 
@@ -185,15 +181,15 @@
             // TODO apparently the key become invalid - delete the blob / cert
             return -1;
         }
-        auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
-        if (!cert_add_result.ok()) {
-            LOG(ERROR) << "Failed to add certificate to fs-verity keyring: "
-                       << cert_add_result.error().message();
-            return -1;
-        }
     } else {
         LOG(INFO) << "Found and verified existing public key certificate: " << kSigningKeyCert;
     }
+    auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
+    if (!cert_add_result.ok()) {
+        LOG(ERROR) << "Failed to add certificate to fs-verity keyring: "
+                   << cert_add_result.error().message();
+        return -1;
+    }
 
     auto verityStatus = verifyAllFilesInVerity(kArtArtifactsDir);
     if (!verityStatus.ok()) {