DO NOT MERGE -  Mark RQ2A.210105.001 as merged.

Bug: 180401296
Merged-In: I001adb29a4c064620c648f8670690b900265cb8a
Change-Id: Id85a6d722a80633063f049081376c6e3a15f2c31
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..99cf5ff
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.*~
diff --git a/Android.bp b/Android.bp
index b44c296..4a0253c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1 +1,31 @@
+package {
+    default_applicable_licenses: ["system_security_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+    name: "system_security_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-BSD",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
 subdirs = ["*"]
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+  license_type: NOTICE
+}
diff --git a/OWNERS b/OWNERS
index 684cca3..bb51005 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,5 +1,6 @@
 swillden@google.com
 cbrubaker@google.com
 jdanis@google.com
+hasinitg@google.com
 kroot@google.com
-bdc@google.com
+zeuthen@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index dcf92be..9b96f36 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,8 +1,10 @@
 [Builtin Hooks]
 clang_format = true
+rustfmt = true
 
 [Builtin Hooks Options]
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+rustfmt = --config-path=rustfmt.toml
 
 [Hook Scripts]
 aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
diff --git a/fsverity_init/Android.bp b/fsverity_init/Android.bp
index 3c9ade0..39d4e6b 100644
--- a/fsverity_init/Android.bp
+++ b/fsverity_init/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
 cc_binary {
     name: "fsverity_init",
     srcs: [
diff --git a/fsverity_init/fsverity_init.cpp b/fsverity_init/fsverity_init.cpp
index 4ed25cd..7ab4097 100644
--- a/fsverity_init/fsverity_init.cpp
+++ b/fsverity_init/fsverity_init.cpp
@@ -37,48 +37,87 @@
     return true;
 }
 
-void LoadKeyFromDirectory(key_serial_t keyring_id, const char* keyname, const char* dir) {
+void LoadKeyFromStdin(key_serial_t keyring_id, const char* keyname) {
+    std::string content;
+    if (!android::base::ReadFdToString(STDIN_FILENO, &content)) {
+        LOG(ERROR) << "Failed to read key from stdin";
+        return;
+    }
+    if (!LoadKeyToKeyring(keyring_id, keyname, content.c_str(), content.size())) {
+        LOG(ERROR) << "Failed to load key from stdin";
+    }
+}
+
+void LoadKeyFromFile(key_serial_t keyring_id, const char* keyname, const std::string& path) {
+    LOG(INFO) << "LoadKeyFromFile path=" << path << " keyname=" << keyname;
+    std::string content;
+    if (!android::base::ReadFileToString(path, &content)) {
+        LOG(ERROR) << "Failed to read key from " << path;
+        return;
+    }
+    if (!LoadKeyToKeyring(keyring_id, keyname, content.c_str(), content.size())) {
+        LOG(ERROR) << "Failed to load key from " << path;
+    }
+}
+
+void LoadKeyFromDirectory(key_serial_t keyring_id, const char* keyname_prefix, const char* dir) {
     if (!std::filesystem::exists(dir)) {
         return;
     }
+    int counter = 0;
     for (const auto& entry : std::filesystem::directory_iterator(dir)) {
         if (!android::base::EndsWithIgnoreCase(entry.path().c_str(), ".der")) continue;
-        std::string content;
-        if (!android::base::ReadFileToString(entry.path(), &content)) {
-            continue;
-        }
-        if (!LoadKeyToKeyring(keyring_id, keyname, content.c_str(), content.size())) {
-            LOG(ERROR) << "Failed to load key from " << entry.path();
-        }
+        std::string keyname = keyname_prefix + std::to_string(counter);
+        counter++;
+        LoadKeyFromFile(keyring_id, keyname.c_str(), entry.path());
     }
 }
 
 void LoadKeyFromVerifiedPartitions(key_serial_t keyring_id) {
     // NB: Directories need to be synced with FileIntegrityService.java in
     // frameworks/base.
-    LoadKeyFromDirectory(keyring_id, "fsv_system", "/system/etc/security/fsverity");
-    LoadKeyFromDirectory(keyring_id, "fsv_product", "/product/etc/security/fsverity");
+    LoadKeyFromDirectory(keyring_id, "fsv_system_", "/system/etc/security/fsverity");
+    LoadKeyFromDirectory(keyring_id, "fsv_product_", "/product/etc/security/fsverity");
 }
 
-int main(int /*argc*/, const char** /*argv*/) {
+int main(int argc, const char** argv) {
+    if (argc < 2) {
+        LOG(ERROR) << "Not enough arguments";
+        return -1;
+    }
+
     key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
     if (keyring_id < 0) {
         LOG(ERROR) << "Failed to find .fs-verity keyring id";
         return -1;
     }
 
-    // Requires files backed by fs-verity to be verified with a key in .fs-verity
-    // keyring.
-    if (!android::base::WriteStringToFile("1", "/proc/sys/fs/verity/require_signatures")) {
-        PLOG(ERROR) << "Failed to enforce fs-verity signature";
-    }
+    const std::string_view command = argv[1];
 
-    LoadKeyFromVerifiedPartitions(keyring_id);
-
-    if (!android::base::GetBoolProperty("ro.debuggable", false)) {
-        if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
-            PLOG(ERROR) << "Cannot restrict .fs-verity keyring";
+    if (command == "--load-verified-keys") {
+        LoadKeyFromVerifiedPartitions(keyring_id);
+    } else if (command == "--load-extra-key") {
+        if (argc != 3) {
+            LOG(ERROR) << "--load-extra-key requires <key_name> argument.";
+            return -1;
         }
+        LoadKeyFromStdin(keyring_id, argv[2]);
+    } else if (command == "--lock") {
+        // Requires files backed by fs-verity to be verified with a key in .fs-verity
+        // keyring.
+        if (!android::base::WriteStringToFile("1", "/proc/sys/fs/verity/require_signatures")) {
+            PLOG(ERROR) << "Failed to enforce fs-verity signature";
+        }
+
+        if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+            if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
+                PLOG(ERROR) << "Cannot restrict .fs-verity keyring";
+            }
+        }
+    } else {
+        LOG(ERROR) << "Unknown argument(s).";
+        return -1;
     }
+
     return 0;
 }
diff --git a/identity/Android.bp b/identity/Android.bp
index c0f1635..8267a6b 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
 cc_defaults {
     name: "identity_defaults",
     cflags: [
@@ -5,6 +14,7 @@
         // "-Werror",
         "-Wextra",
         "-Wunused",
+        "-Wno-deprecated-declarations",
     ],
     sanitize: {
         misc_undefined : ["integer"],
@@ -29,18 +39,22 @@
     shared_libs: [
         "libbase",
         "libbinder",
-        "libkeystore_aidl",
+        "libbinder_ndk",
+        "android.hardware.keymaster@4.0",
         "libcredstore_aidl",
+        "libcrypto",
         "libutils",
         "libhidlbase",
         "android.hardware.identity-support-lib",
         "libkeymaster4support",
         "libkeystore-attestation-application-id",
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "android.security.authorization-ndk_platform",
     ],
     static_libs: [
-        "android.hardware.identity-cpp",
-        "android.hardware.keymaster-cpp",
-        "libcppbor",
+        "android.hardware.identity-V3-cpp",
+        "android.hardware.keymaster-V3-cpp",
+        "libcppbor_external",
     ]
 }
 
diff --git a/identity/Credential.cpp b/identity/Credential.cpp
index 28ba752..7c75d8a 100644
--- a/identity/Credential.cpp
+++ b/identity/Credential.cpp
@@ -14,16 +14,14 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "Credential"
+#define LOG_TAG "credstore"
 
 #include <android-base/logging.h>
-
+#include <android/binder_manager.h>
 #include <android/hardware/identity/support/IdentityCredentialSupport.h>
 
 #include <android/security/identity/ICredentialStore.h>
 
-#include <android/security/keystore/BnCredstoreTokenCallback.h>
-#include <android/security/keystore/IKeystoreService.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <keymasterV4_0/keymaster_utils.h>
@@ -33,9 +31,15 @@
 #include <future>
 #include <tuple>
 
+#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
+#include <aidl/android/hardware/security/secureclock/TimeStampToken.h>
+#include <aidl/android/security/authorization/AuthorizationTokens.h>
+#include <aidl/android/security/authorization/IKeystoreAuthorization.h>
+
 #include "Credential.h"
 #include "CredentialData.h"
 #include "Util.h"
+#include "WritableCredential.h"
 
 namespace android {
 namespace security {
@@ -45,38 +49,45 @@
 using std::promise;
 using std::tuple;
 
-using android::security::keystore::IKeystoreService;
+using ::android::hardware::identity::IWritableIdentityCredential;
 
 using ::android::hardware::identity::support::ecKeyPairGetPkcs12;
 using ::android::hardware::identity::support::ecKeyPairGetPrivateKey;
 using ::android::hardware::identity::support::ecKeyPairGetPublicKey;
 using ::android::hardware::identity::support::sha256;
 
+using android::hardware::keymaster::SecurityLevel;
 using android::hardware::keymaster::V4_0::HardwareAuthToken;
 using android::hardware::keymaster::V4_0::VerificationToken;
 using AidlHardwareAuthToken = android::hardware::keymaster::HardwareAuthToken;
 using AidlVerificationToken = android::hardware::keymaster::VerificationToken;
 
+using KeyMintAuthToken = ::aidl::android::hardware::security::keymint::HardwareAuthToken;
+using ::aidl::android::hardware::security::secureclock::TimeStampToken;
+using ::aidl::android::security::authorization::AuthorizationTokens;
+using ::aidl::android::security::authorization::IKeystoreAuthorization;
+
 Credential::Credential(CipherSuite cipherSuite, const std::string& dataPath,
-                       const std::string& credentialName)
-    : cipherSuite_(cipherSuite), dataPath_(dataPath), credentialName_(credentialName) {}
+                       const std::string& credentialName, uid_t callingUid,
+                       HardwareInformation hwInfo, sp<IIdentityCredentialStore> halStoreBinder,
+                       int halApiVersion)
+    : cipherSuite_(cipherSuite), dataPath_(dataPath), credentialName_(credentialName),
+      callingUid_(callingUid), hwInfo_(std::move(hwInfo)), halStoreBinder_(halStoreBinder),
+      halApiVersion_(halApiVersion) {}
 
 Credential::~Credential() {}
 
-Status Credential::loadCredential(sp<IIdentityCredentialStore> halStoreBinder) {
-    uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
-    sp<CredentialData> data = new CredentialData(dataPath_, callingUid, credentialName_);
+Status Credential::ensureOrReplaceHalBinder() {
+    sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
     if (!data->loadFromDisk()) {
         LOG(ERROR) << "Error loading data for credential";
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                 "Error loading data for credential");
     }
 
-    data_ = data;
-
     sp<IIdentityCredential> halBinder;
     Status status =
-        halStoreBinder->getCredential(cipherSuite_, data_->getCredentialData(), &halBinder);
+        halStoreBinder_->getCredential(cipherSuite_, data->getCredentialData(), &halBinder);
     if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
         int code = status.serviceSpecificErrorCode();
         if (code == IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED) {
@@ -87,103 +98,143 @@
         LOG(ERROR) << "Error getting HAL binder";
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC);
     }
-
     halBinder_ = halBinder;
 
     return Status::ok();
 }
 
 Status Credential::getCredentialKeyCertificateChain(std::vector<uint8_t>* _aidl_return) {
-    *_aidl_return = data_->getAttestationCertificate();
+    sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+    if (!data->loadFromDisk()) {
+        LOG(ERROR) << "Error loading data for credential";
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error loading data for credential");
+    }
+    *_aidl_return = data->getAttestationCertificate();
     return Status::ok();
 }
 
 // Returns operation handle
-Status Credential::selectAuthKey(bool allowUsingExhaustedKeys, int64_t* _aidl_return) {
+Status Credential::selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
+                                 int64_t* _aidl_return) {
+    sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+    if (!data->loadFromDisk()) {
+        LOG(ERROR) << "Error loading data for credential";
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error loading data for credential");
+    }
 
-    selectedAuthKey_ = data_->selectAuthKey(allowUsingExhaustedKeys);
-    if (selectedAuthKey_ == nullptr) {
+    // We just check if a key is available, we actually don't store it since we
+    // don't keep CredentialData around between binder calls.
+    const AuthKeyData* authKey =
+        data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
+    if (authKey == nullptr) {
         return Status::fromServiceSpecificError(
             ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
             "No suitable authentication key available");
     }
 
-    int64_t challenge;
-    Status status = halBinder_->createAuthChallenge(&challenge);
-    if (!status.isOk()) {
-        return halStatusToGenericError(status);
-    }
-    if (challenge == 0) {
+    if (!ensureChallenge()) {
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
-                                                "Returned challenge is 0 (bug in HAL or TA)");
+                                                "Error getting challenge (bug in HAL or TA)");
     }
-
-    selectedChallenge_ = challenge;
-    *_aidl_return = challenge;
+    *_aidl_return = selectedChallenge_;
     return Status::ok();
 }
 
-class CredstoreTokenCallback : public android::security::keystore::BnCredstoreTokenCallback,
-                               public promise<tuple<bool, vector<uint8_t>, vector<uint8_t>>> {
-  public:
-    CredstoreTokenCallback() {}
-    virtual Status onFinished(bool success, const vector<uint8_t>& authToken,
-                              const vector<uint8_t>& verificationToken) override {
-        this->set_value({success, authToken, verificationToken});
-        return Status::ok();
+bool Credential::ensureChallenge() {
+    if (selectedChallenge_ != 0) {
+        return true;
     }
-};
+
+    int64_t challenge;
+    Status status = halBinder_->createAuthChallenge(&challenge);
+    if (!status.isOk()) {
+        LOG(ERROR) << "Error getting challenge: " << status.exceptionMessage();
+        return false;
+    }
+    if (challenge == 0) {
+        LOG(ERROR) << "Returned challenge is 0 (bug in HAL or TA)";
+        return false;
+    }
+
+    selectedChallenge_ = challenge;
+    return true;
+}
 
 // Returns false if an error occurred communicating with keystore.
 //
-bool getTokensFromKeystore(uint64_t challenge, uint64_t secureUserId,
-                           unsigned int authTokenMaxAgeMillis, vector<uint8_t>& authToken,
-                           vector<uint8_t>& verificationToken) {
-    sp<IServiceManager> sm = defaultServiceManager();
-    sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
-    sp<IKeystoreService> keystore = interface_cast<IKeystoreService>(binder);
-    if (keystore == nullptr) {
-        return false;
-    }
+bool getTokensFromKeystore2(uint64_t challenge, uint64_t secureUserId,
+                            unsigned int authTokenMaxAgeMillis,
+                            AidlHardwareAuthToken& aidlAuthToken,
+                            AidlVerificationToken& aidlVerificationToken) {
+    // try to connect to IKeystoreAuthorization AIDL service first.
+    AIBinder* authzAIBinder = AServiceManager_checkService("android.security.authorization");
+    ::ndk::SpAIBinder authzBinder(authzAIBinder);
+    auto authzService = IKeystoreAuthorization::fromBinder(authzBinder);
+    if (authzService) {
+        AuthorizationTokens authzTokens;
+        auto result = authzService->getAuthTokensForCredStore(challenge, secureUserId,
+                                                              authTokenMaxAgeMillis, &authzTokens);
+        // Convert KeyMint auth token to KeyMaster authtoken, only if tokens are
+        // returned
+        if (result.isOk()) {
+            KeyMintAuthToken keymintAuthToken = authzTokens.authToken;
+            aidlAuthToken.challenge = keymintAuthToken.challenge;
+            aidlAuthToken.userId = keymintAuthToken.userId;
+            aidlAuthToken.authenticatorId = keymintAuthToken.authenticatorId;
+            aidlAuthToken.authenticatorType =
+                ::android::hardware::keymaster::HardwareAuthenticatorType(
+                    int32_t(keymintAuthToken.authenticatorType));
+            aidlAuthToken.timestamp.milliSeconds = keymintAuthToken.timestamp.milliSeconds;
+            aidlAuthToken.mac = keymintAuthToken.mac;
 
-    sp<CredstoreTokenCallback> callback = new CredstoreTokenCallback();
-    auto future = callback->get_future();
-
-    Status status =
-        keystore->getTokensForCredstore(challenge, secureUserId, authTokenMaxAgeMillis, callback);
-    if (!status.isOk()) {
+            // Convert timestamp token to KeyMaster verification token
+            TimeStampToken timestampToken = authzTokens.timestampToken;
+            aidlVerificationToken.challenge = timestampToken.challenge;
+            aidlVerificationToken.timestamp.milliSeconds = timestampToken.timestamp.milliSeconds;
+            // Legacy verification tokens were always minted by TEE.
+            aidlVerificationToken.securityLevel = SecurityLevel::TRUSTED_ENVIRONMENT;
+            aidlVerificationToken.mac = timestampToken.mac;
+        } else {
+            if (result.getServiceSpecificError() == 0) {
+                // Here we differentiate the errors occurred during communication
+                // from the service specific errors.
+                LOG(ERROR) << "Error getting tokens from keystore2: " << result.getDescription();
+                return false;
+            } else {
+                // Log the reason for not receiving auth tokens from keystore2.
+                LOG(INFO) << "Auth tokens were not received due to: " << result.getDescription();
+            }
+        }
+        return true;
+    } else {
+        LOG(ERROR) << "Error connecting to IKeystoreAuthorization service";
         return false;
     }
-
-    auto fstatus = future.wait_for(std::chrono::milliseconds(5000));
-    if (fstatus != std::future_status::ready) {
-        LOG(ERROR) << "Waited 5 seconds from tokens for credstore, aborting";
-        return false;
-    }
-    auto [success, returnedAuthToken, returnedVerificationToken] = future.get();
-    if (!success) {
-        LOG(ERROR) << "Error getting tokens from credstore";
-        return false;
-    }
-    authToken = returnedAuthToken;
-    verificationToken = returnedVerificationToken;
-    return true;
 }
 
 Status Credential::getEntries(const vector<uint8_t>& requestMessage,
                               const vector<RequestNamespaceParcel>& requestNamespaces,
                               const vector<uint8_t>& sessionTranscript,
                               const vector<uint8_t>& readerSignature, bool allowUsingExhaustedKeys,
-                              GetEntriesResultParcel* _aidl_return) {
+                              bool allowUsingExpiredKeys, GetEntriesResultParcel* _aidl_return) {
     GetEntriesResultParcel ret;
 
+    sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+    if (!data->loadFromDisk()) {
+        LOG(ERROR) << "Error loading data for credential";
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error loading data for credential");
+    }
+
     // Calculate requestCounts ahead of time and be careful not to include
     // elements that don't exist.
     //
     // Also go through and figure out which access control profiles to include
     // in the startRetrieval() call.
     vector<int32_t> requestCounts;
-    const vector<SecureAccessControlProfile>& allProfiles = data_->getSecureAccessControlProfiles();
+    const vector<SecureAccessControlProfile>& allProfiles = data->getSecureAccessControlProfiles();
 
     // We don't support ACP identifiers which isn't in the range 0 to 31. This
     // guarantee exists so it's feasible to implement the TA part of an Identity
@@ -202,13 +253,13 @@
     for (const RequestNamespaceParcel& rns : requestNamespaces) {
         size_t numEntriesInNsToRequest = 0;
         for (const RequestEntryParcel& rep : rns.entries) {
-            if (data_->hasEntryData(rns.namespaceName, rep.name)) {
+            if (data->hasEntryData(rns.namespaceName, rep.name)) {
                 numEntriesInNsToRequest++;
             }
 
-            optional<EntryData> data = data_->getEntryData(rns.namespaceName, rep.name);
-            if (data) {
-                for (int32_t id : data.value().accessControlProfileIds) {
+            optional<EntryData> eData = data->getEntryData(rns.namespaceName, rep.name);
+            if (eData) {
+                for (int32_t id : eData.value().accessControlProfileIds) {
                     if (id < 0 || id >= 32) {
                         LOG(ERROR) << "Invalid accessControlProfileId " << id << " for "
                                    << rns.namespaceName << ": " << rep.name;
@@ -256,13 +307,6 @@
         }
     }
 
-    // If requesting a challenge-based authToken the idea is that authentication
-    // happens as part of the transaction. As such, authTokenMaxAgeMillis should
-    // be nearly zero. We'll use 10 seconds for this.
-    if (userAuthNeeded && selectedChallenge_ != 0) {
-        authTokenMaxAgeMillis = 10 * 1000;
-    }
-
     // Reset tokens and only get them if they're actually needed, e.g. if user authentication
     // is needed in any of the access control profiles for data items being requested.
     //
@@ -280,63 +324,58 @@
     aidlVerificationToken.securityLevel = ::android::hardware::keymaster::SecurityLevel::SOFTWARE;
     aidlVerificationToken.mac.clear();
     if (userAuthNeeded) {
-        vector<uint8_t> authTokenBytes;
-        vector<uint8_t> verificationTokenBytes;
-        if (!getTokensFromKeystore(selectedChallenge_, data_->getSecureUserId(),
-                                   authTokenMaxAgeMillis, authTokenBytes, verificationTokenBytes)) {
-            LOG(ERROR) << "Error getting tokens from keystore";
+        // If user authentication is needed, always get a challenge from the
+        // HAL/TA since it'll need it to check the returned VerificationToken
+        // for freshness.
+        if (!ensureChallenge()) {
             return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
-                                                    "Error getting tokens from keystore");
+                                                    "Error getting challenge (bug in HAL or TA)");
         }
 
-        // It's entirely possible getTokensFromKeystore() succeeded but didn't
-        // return any tokens (in which case the returned byte-vectors are
-        // empty). For example, this can happen if no auth token is available
-        // which satifies e.g. |authTokenMaxAgeMillis|.
+        // Note: if all selected profiles require auth-on-every-presentation
+        // then authTokenMaxAgeMillis will be 0 (because timeoutMillis for each
+        // profile is 0). Which means that keystore will only return an
+        // AuthToken if its challenge matches what we pass, regardless of its
+        // age. This is intended b/c the HAL/TA will check not care about
+        // the age in this case, it only cares that the challenge matches.
         //
-        if (authTokenBytes.size() > 0) {
-            HardwareAuthToken authToken =
-                android::hardware::keymaster::V4_0::support::hidlVec2AuthToken(authTokenBytes);
-            // Convert from HIDL to AIDL...
-            aidlAuthToken.challenge = int64_t(authToken.challenge);
-            aidlAuthToken.userId = int64_t(authToken.userId);
-            aidlAuthToken.authenticatorId = int64_t(authToken.authenticatorId);
-            aidlAuthToken.authenticatorType =
-                ::android::hardware::keymaster::HardwareAuthenticatorType(
-                    int32_t(authToken.authenticatorType));
-            aidlAuthToken.timestamp.milliSeconds = int64_t(authToken.timestamp);
-            aidlAuthToken.mac = authToken.mac;
-        }
+        // Otherwise, if one or more of the profiles is auth-with-a-timeout then
+        // authTokenMaxAgeMillis will be set to the largest of those
+        // timeouts. We'll get an AuthToken which satisfies this deadline if it
+        // exists. This authToken _may_ have the requested challenge but it's
+        // not a guarantee and it's also not required.
+        //
 
-        if (verificationTokenBytes.size() > 0) {
-            optional<VerificationToken> token =
-                android::hardware::keymaster::V4_0::support::deserializeVerificationToken(
-                    verificationTokenBytes);
-            if (!token) {
-                LOG(ERROR) << "Error deserializing verification token";
-                return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
-                                                        "Error deserializing verification token");
-            }
-            aidlVerificationToken.challenge = token->challenge;
-            aidlVerificationToken.timestamp.milliSeconds = token->timestamp;
-            aidlVerificationToken.securityLevel =
-                ::android::hardware::keymaster::SecurityLevel(token->securityLevel);
-            aidlVerificationToken.mac = token->mac;
+        if (!getTokensFromKeystore2(selectedChallenge_, data->getSecureUserId(),
+                                    authTokenMaxAgeMillis, aidlAuthToken, aidlVerificationToken)) {
+            LOG(ERROR) << "Error getting tokens from keystore2";
+            return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                    "Error getting tokens from keystore2");
         }
     }
 
     // Note that the selectAuthKey() method is only called if a CryptoObject is involved at
     // the Java layer. So we could end up with no previously selected auth key and we may
     // need one.
-    const AuthKeyData* authKey = selectedAuthKey_;
-    if (sessionTranscript.size() > 0) {
-        if (authKey == nullptr) {
-            authKey = data_->selectAuthKey(allowUsingExhaustedKeys);
-            if (authKey == nullptr) {
-                return Status::fromServiceSpecificError(
-                    ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
-                    "No suitable authentication key available");
-            }
+    //
+    const AuthKeyData* authKey =
+        data->selectAuthKey(allowUsingExhaustedKeys, allowUsingExpiredKeys);
+    if (authKey == nullptr) {
+        // If no authKey is available, consider it an error only when a
+        // SessionTranscript was provided.
+        //
+        // We allow no SessionTranscript to be provided because it makes
+        // the API simpler to deal with insofar it can be used without having
+        // to generate any authentication keys.
+        //
+        // In this "no SessionTranscript is provided" mode we don't return
+        // DeviceNameSpaces nor a MAC over DeviceAuthentication so we don't
+        // need a device key.
+        //
+        if (sessionTranscript.size() > 0) {
+            return Status::fromServiceSpecificError(
+                ICredentialStore::ERROR_NO_AUTHENTICATION_KEY_AVAILABLE,
+                "No suitable authentication key available and one is needed");
         }
     }
     vector<uint8_t> signingKeyBlob;
@@ -351,7 +390,7 @@
         RequestNamespace ns;
         ns.namespaceName = rns.namespaceName;
         for (const RequestEntryParcel& rep : rns.entries) {
-            optional<EntryData> entryData = data_->getEntryData(rns.namespaceName, rep.name);
+            optional<EntryData> entryData = data->getEntryData(rns.namespaceName, rep.name);
             if (entryData) {
                 RequestDataItem di;
                 di.name = rep.name;
@@ -406,16 +445,16 @@
             ResultEntryParcel resultEntryParcel;
             resultEntryParcel.name = rep.name;
 
-            optional<EntryData> data = data_->getEntryData(rns.namespaceName, rep.name);
-            if (!data) {
+            optional<EntryData> eData = data->getEntryData(rns.namespaceName, rep.name);
+            if (!eData) {
                 resultEntryParcel.status = STATUS_NO_SUCH_ENTRY;
                 resultNamespaceParcel.entries.push_back(resultEntryParcel);
                 continue;
             }
 
             status =
-                halBinder_->startRetrieveEntryValue(rns.namespaceName, rep.name, data.value().size,
-                                                    data.value().accessControlProfileIds);
+                halBinder_->startRetrieveEntryValue(rns.namespaceName, rep.name, eData.value().size,
+                                                    eData.value().accessControlProfileIds);
             if (!status.isOk() && status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC) {
                 int code = status.serviceSpecificErrorCode();
                 if (code == IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED) {
@@ -441,7 +480,7 @@
             }
 
             vector<uint8_t> value;
-            for (const auto& encryptedChunk : data.value().encryptedChunks) {
+            for (const auto& encryptedChunk : eData.value().encryptedChunks) {
                 vector<uint8_t> chunk;
                 status = halBinder_->retrieveEntryValue(encryptedChunk, &chunk);
                 if (!status.isOk()) {
@@ -467,7 +506,7 @@
 
     // Ensure useCount is updated on disk.
     if (authKey != nullptr) {
-        if (!data_->saveToDisk()) {
+        if (!data->saveToDisk()) {
             return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                     "Error saving data");
         }
@@ -479,11 +518,19 @@
 
 Status Credential::deleteCredential(vector<uint8_t>* _aidl_return) {
     vector<uint8_t> proofOfDeletionSignature;
+
+    sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+    if (!data->loadFromDisk()) {
+        LOG(ERROR) << "Error loading data for credential";
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error loading data for credential");
+    }
+
     Status status = halBinder_->deleteCredential(&proofOfDeletionSignature);
     if (!status.isOk()) {
         return halStatusToGenericError(status);
     }
-    if (!data_->deleteCredential()) {
+    if (!data->deleteCredential()) {
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                 "Error deleting credential data on disk");
     }
@@ -491,6 +538,47 @@
     return Status::ok();
 }
 
+Status Credential::deleteWithChallenge(const vector<uint8_t>& challenge,
+                                       vector<uint8_t>* _aidl_return) {
+    if (halApiVersion_ < 3) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
+                                                "Not implemented by HAL");
+    }
+    vector<uint8_t> proofOfDeletionSignature;
+
+    sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+    if (!data->loadFromDisk()) {
+        LOG(ERROR) << "Error loading data for credential";
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error loading data for credential");
+    }
+
+    Status status = halBinder_->deleteCredentialWithChallenge(challenge, &proofOfDeletionSignature);
+    if (!status.isOk()) {
+        return halStatusToGenericError(status);
+    }
+    if (!data->deleteCredential()) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error deleting credential data on disk");
+    }
+    *_aidl_return = proofOfDeletionSignature;
+    return Status::ok();
+}
+
+Status Credential::proveOwnership(const vector<uint8_t>& challenge, vector<uint8_t>* _aidl_return) {
+    if (halApiVersion_ < 3) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
+                                                "Not implemented by HAL");
+    }
+    vector<uint8_t> proofOfOwnershipSignature;
+    Status status = halBinder_->proveOwnership(challenge, &proofOfOwnershipSignature);
+    if (!status.isOk()) {
+        return halStatusToGenericError(status);
+    }
+    *_aidl_return = proofOfOwnershipSignature;
+    return Status::ok();
+}
+
 Status Credential::createEphemeralKeyPair(vector<uint8_t>* _aidl_return) {
     vector<uint8_t> keyPair;
     Status status = halBinder_->createEphemeralKeyPair(&keyPair);
@@ -522,8 +610,14 @@
 }
 
 Status Credential::setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) {
-    data_->setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
-    if (!data_->saveToDisk()) {
+    sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+    if (!data->loadFromDisk()) {
+        LOG(ERROR) << "Error loading data for credential";
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error loading data for credential");
+    }
+    data->setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
+    if (!data->saveToDisk()) {
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                 "Error saving data");
     }
@@ -531,8 +625,14 @@
 }
 
 Status Credential::getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) {
+    sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+    if (!data->loadFromDisk()) {
+        LOG(ERROR) << "Error loading data for credential";
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error loading data for credential");
+    }
     optional<vector<vector<uint8_t>>> keysNeedingCert =
-        data_->getAuthKeysNeedingCertification(halBinder_);
+        data->getAuthKeysNeedingCertification(halBinder_);
     if (!keysNeedingCert) {
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                 "Error getting auth keys neededing certification");
@@ -543,7 +643,7 @@
         authKeyParcel.x509cert = key;
         authKeyParcels.push_back(authKeyParcel);
     }
-    if (!data_->saveToDisk()) {
+    if (!data->saveToDisk()) {
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                 "Error saving data");
     }
@@ -553,13 +653,48 @@
 
 Status Credential::storeStaticAuthenticationData(const AuthKeyParcel& authenticationKey,
                                                  const vector<uint8_t>& staticAuthData) {
-    if (!data_->storeStaticAuthenticationData(authenticationKey.x509cert, staticAuthData)) {
+    sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+    if (!data->loadFromDisk()) {
+        LOG(ERROR) << "Error loading data for credential";
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error loading data for credential");
+    }
+    if (!data->storeStaticAuthenticationData(authenticationKey.x509cert,
+                                             std::numeric_limits<int64_t>::max(), staticAuthData)) {
         return Status::fromServiceSpecificError(
             ICredentialStore::ERROR_AUTHENTICATION_KEY_NOT_FOUND,
             "Error finding authentication key to store static "
             "authentication data for");
     }
-    if (!data_->saveToDisk()) {
+    if (!data->saveToDisk()) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error saving data");
+    }
+    return Status::ok();
+}
+
+Status
+Credential::storeStaticAuthenticationDataWithExpiration(const AuthKeyParcel& authenticationKey,
+                                                        int64_t expirationDateMillisSinceEpoch,
+                                                        const vector<uint8_t>& staticAuthData) {
+    if (halApiVersion_ < 3) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
+                                                "Not implemented by HAL");
+    }
+    sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+    if (!data->loadFromDisk()) {
+        LOG(ERROR) << "Error loading data for credential";
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error loading data for credential");
+    }
+    if (!data->storeStaticAuthenticationData(authenticationKey.x509cert,
+                                             expirationDateMillisSinceEpoch, staticAuthData)) {
+        return Status::fromServiceSpecificError(
+            ICredentialStore::ERROR_AUTHENTICATION_KEY_NOT_FOUND,
+            "Error finding authentication key to store static "
+            "authentication data for");
+    }
+    if (!data->saveToDisk()) {
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                 "Error saving data");
     }
@@ -567,7 +702,13 @@
 }
 
 Status Credential::getAuthenticationDataUsageCount(vector<int32_t>* _aidl_return) {
-    const vector<AuthKeyData>& authKeyDatas = data_->getAuthKeyDatas();
+    sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+    if (!data->loadFromDisk()) {
+        LOG(ERROR) << "Error loading data for credential";
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error loading data for credential");
+    }
+    const vector<AuthKeyData>& authKeyDatas = data->getAuthKeyDatas();
     vector<int32_t> ret;
     for (const AuthKeyData& authKeyData : authKeyDatas) {
         ret.push_back(authKeyData.useCount);
@@ -576,6 +717,85 @@
     return Status::ok();
 }
 
+optional<string> extractDocType(const vector<uint8_t>& credentialData) {
+    auto [item, _ /* newPos */, message] = cppbor::parse(credentialData);
+    if (item == nullptr) {
+        LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
+        return {};
+    }
+    const cppbor::Array* array = item->asArray();
+    if (array == nullptr || array->size() < 1) {
+        LOG(ERROR) << "CredentialData array with at least one element";
+        return {};
+    }
+    const cppbor::Tstr* tstr = ((*array)[0])->asTstr();
+    if (tstr == nullptr) {
+        LOG(ERROR) << "First item in CredentialData is not a string";
+        return {};
+    }
+    return tstr->value();
+}
+
+Status Credential::update(sp<IWritableCredential>* _aidl_return) {
+    if (halApiVersion_ < 3) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_NOT_SUPPORTED,
+                                                "Not implemented by HAL");
+    }
+    sp<CredentialData> data = new CredentialData(dataPath_, callingUid_, credentialName_);
+    if (!data->loadFromDisk()) {
+        LOG(ERROR) << "Error loading data for credential";
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Error loading data for credential");
+    }
+
+    sp<IWritableIdentityCredential> halWritableCredential;
+    Status status = halBinder_->updateCredential(&halWritableCredential);
+    if (!status.isOk()) {
+        return halStatusToGenericError(status);
+    }
+
+    optional<string> docType = extractDocType(data->getCredentialData());
+    if (!docType) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Unable to extract DocType from CredentialData");
+    }
+
+    // NOTE: The caller is expected to call WritableCredential::personalize() which will
+    // write brand new data to disk, specifically it will overwrite any data already
+    // have _including_ authentication keys.
+    //
+    // It is because of this we need to set the CredentialKey certificate chain,
+    // keyCount, and maxUsesPerKey below.
+    sp<WritableCredential> writableCredential = new WritableCredential(
+        dataPath_, credentialName_, docType.value(), true, hwInfo_, halWritableCredential);
+
+    writableCredential->setAttestationCertificate(data->getAttestationCertificate());
+    auto [keyCount, maxUsesPerKey] = data->getAvailableAuthenticationKeys();
+    writableCredential->setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
+
+    // Because its data has changed, we need to replace the binder for the
+    // IIdentityCredential when the credential has been updated... otherwise the
+    // remote object will have stale data for future calls, for example
+    // getAuthKeysNeedingCertification().
+    //
+    // The way this is implemented is that setCredentialToReloadWhenUpdated()
+    // instructs the WritableCredential to call writableCredentialPersonalized()
+    // on |this|.
+    //
+    //
+    writableCredential->setCredentialToReloadWhenUpdated(this);
+
+    *_aidl_return = writableCredential;
+    return Status::ok();
+}
+
+void Credential::writableCredentialPersonalized() {
+    Status status = ensureOrReplaceHalBinder();
+    if (!status.isOk()) {
+        LOG(ERROR) << "Error reloading credential";
+    }
+}
+
 }  // namespace identity
 }  // namespace security
 }  // namespace android
diff --git a/identity/Credential.h b/identity/Credential.h
index e2880d9..a76f3cc 100644
--- a/identity/Credential.h
+++ b/identity/Credential.h
@@ -36,6 +36,7 @@
 using ::std::vector;
 
 using ::android::hardware::identity::CipherSuite;
+using ::android::hardware::identity::HardwareInformation;
 using ::android::hardware::identity::IIdentityCredential;
 using ::android::hardware::identity::IIdentityCredentialStore;
 using ::android::hardware::identity::RequestDataItem;
@@ -43,10 +44,13 @@
 
 class Credential : public BnCredential {
   public:
-    Credential(CipherSuite cipherSuite, const string& dataPath, const string& credentialName);
+    Credential(CipherSuite cipherSuite, const string& dataPath, const string& credentialName,
+               uid_t callingUid, HardwareInformation hwInfo,
+               sp<IIdentityCredentialStore> halStoreBinder, int halApiVersion);
     ~Credential();
 
-    Status loadCredential(sp<IIdentityCredentialStore> halStoreBinder);
+    Status ensureOrReplaceHalBinder();
+    void writableCredentialPersonalized();
 
     // ICredential overrides
     Status createEphemeralKeyPair(vector<uint8_t>* _aidl_return) override;
@@ -55,33 +59,48 @@
 
     Status deleteCredential(vector<uint8_t>* _aidl_return) override;
 
+    Status deleteWithChallenge(const vector<uint8_t>& challenge,
+                               vector<uint8_t>* _aidl_return) override;
+
+    Status proveOwnership(const vector<uint8_t>& challenge, vector<uint8_t>* _aidl_return) override;
+
     Status getCredentialKeyCertificateChain(vector<uint8_t>* _aidl_return) override;
 
-    Status selectAuthKey(bool allowUsingExhaustedKeys, int64_t* _aidl_return) override;
+    Status selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys,
+                         int64_t* _aidl_return) override;
 
     Status getEntries(const vector<uint8_t>& requestMessage,
                       const vector<RequestNamespaceParcel>& requestNamespaces,
                       const vector<uint8_t>& sessionTranscript,
                       const vector<uint8_t>& readerSignature, bool allowUsingExhaustedKeys,
-                      GetEntriesResultParcel* _aidl_return) override;
+                      bool allowUsingExpiredKeys, GetEntriesResultParcel* _aidl_return) override;
 
     Status setAvailableAuthenticationKeys(int32_t keyCount, int32_t maxUsesPerKey) override;
     Status getAuthKeysNeedingCertification(vector<AuthKeyParcel>* _aidl_return) override;
     Status storeStaticAuthenticationData(const AuthKeyParcel& authenticationKey,
                                          const vector<uint8_t>& staticAuthData) override;
+    Status
+    storeStaticAuthenticationDataWithExpiration(const AuthKeyParcel& authenticationKey,
+                                                int64_t expirationDateMillisSinceEpoch,
+                                                const vector<uint8_t>& staticAuthData) override;
     Status getAuthenticationDataUsageCount(vector<int32_t>* _aidl_return) override;
 
+    Status update(sp<IWritableCredential>* _aidl_return) override;
+
   private:
     CipherSuite cipherSuite_;
     string dataPath_;
     string credentialName_;
+    uid_t callingUid_;
+    HardwareInformation hwInfo_;
+    sp<IIdentityCredentialStore> halStoreBinder_;
 
-    const AuthKeyData* selectedAuthKey_ = nullptr;
     uint64_t selectedChallenge_ = 0;
 
-    sp<CredentialData> data_;
-
     sp<IIdentityCredential> halBinder_;
+    int halApiVersion_;
+
+    bool ensureChallenge();
 
     ssize_t
     calcExpectedDeviceNameSpacesSize(const vector<uint8_t>& requestMessage,
diff --git a/identity/CredentialData.cpp b/identity/CredentialData.cpp
index b4e6641..74b995d 100644
--- a/identity/CredentialData.cpp
+++ b/identity/CredentialData.cpp
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "CredentialData"
+#define LOG_TAG "credstore"
+
+#include <chrono>
 
 #include <fcntl.h>
 #include <stdlib.h>
@@ -119,12 +121,15 @@
     cppbor::Array authKeyDatasArray;
     for (const AuthKeyData& data : authKeyDatas_) {
         cppbor::Array array;
+        // Fields 0-6 was in the original version in Android 11
         array.add(data.certificate);
         array.add(data.keyBlob);
         array.add(data.staticAuthenticationData);
         array.add(data.pendingCertificate);
         array.add(data.pendingKeyBlob);
         array.add(data.useCount);
+        // Field 7 was added in Android 12
+        array.add(data.expirationDateMillisSinceEpoch);
         authKeyDatasArray.add(std::move(array));
     }
     map.add("authKeyData", std::move(authKeyDatasArray));
@@ -183,9 +188,17 @@
         LOG(ERROR) << "One or more items in AuthKeyData array in CBOR is of wrong type";
         return {};
     }
+    // expirationDateMillisSinceEpoch was added as the 7th element for Android 12. If not
+    // present, default to longest possible expiration date.
+    int64_t expirationDateMillisSinceEpoch = INT64_MAX;
+    if (array->size() >= 7) {
+        const cppbor::Int* itemExpirationDateMillisSinceEpoch = ((*array)[6])->asInt();
+        expirationDateMillisSinceEpoch = itemExpirationDateMillisSinceEpoch->value();
+    }
     AuthKeyData authKeyData;
     authKeyData.certificate = itemCertificate->value();
     authKeyData.keyBlob = itemKeyBlob->value();
+    authKeyData.expirationDateMillisSinceEpoch = expirationDateMillisSinceEpoch;
     authKeyData.staticAuthenticationData = itemStaticAuthenticationData->value();
     authKeyData.pendingCertificate = itemPendingCertificate->value();
     authKeyData.pendingKeyBlob = itemPendingKeyBlob->value();
@@ -232,7 +245,6 @@
 }
 
 bool CredentialData::loadFromDisk() {
-
     // Reset all data.
     credentialData_.clear();
     attestationCertificate_.clear();
@@ -261,7 +273,7 @@
     }
 
     for (size_t n = 0; n < map->size(); n++) {
-        auto [keyItem, valueItem] = (*map)[n];
+        auto& [keyItem, valueItem] = (*map)[n];
         const cppbor::Tstr* tstr = keyItem->asTstr();
         if (tstr == nullptr) {
             LOG(ERROR) << "Key item in top-level map is not a tstr";
@@ -313,7 +325,7 @@
                 return false;
             }
             for (size_t m = 0; m < map->size(); m++) {
-                auto [ecKeyItem, ecValueItem] = (*map)[m];
+                auto& [ecKeyItem, ecValueItem] = (*map)[m];
                 const cppbor::Tstr* ecTstr = ecKeyItem->asTstr();
                 if (ecTstr == nullptr) {
                     LOG(ERROR) << "Key item in encryptedChunks map is not a tstr";
@@ -487,16 +499,28 @@
     return authKeyDatas_;
 }
 
-const AuthKeyData* CredentialData::selectAuthKey(bool allowUsingExhaustedKeys) {
+pair<int /* keyCount */, int /*maxUsersPerKey */> CredentialData::getAvailableAuthenticationKeys() {
+    return std::make_pair(keyCount_, maxUsesPerKey_);
+}
+
+AuthKeyData* CredentialData::findAuthKey_(bool allowUsingExhaustedKeys,
+                                          bool allowUsingExpiredKeys) {
     AuthKeyData* candidate = nullptr;
 
+    int64_t nowMilliSeconds =
+        std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) * 1000;
+
     int n = 0;
-    int candidateNum = -1;
     for (AuthKeyData& data : authKeyDatas_) {
+        if (nowMilliSeconds > data.expirationDateMillisSinceEpoch) {
+            if (!allowUsingExpiredKeys) {
+                continue;
+            }
+        }
         if (data.certificate.size() != 0) {
+            // Not expired, include in normal check
             if (candidate == nullptr || data.useCount < candidate->useCount) {
                 candidate = &data;
-                candidateNum = n;
             }
         }
         n++;
@@ -510,6 +534,28 @@
         return nullptr;
     }
 
+    return candidate;
+}
+
+const AuthKeyData* CredentialData::selectAuthKey(bool allowUsingExhaustedKeys,
+                                                 bool allowUsingExpiredKeys) {
+    AuthKeyData* candidate;
+
+    // First try to find a un-expired key..
+    candidate = findAuthKey_(allowUsingExhaustedKeys, false);
+    if (candidate == nullptr) {
+        // That didn't work, there are no un-expired keys and we don't allow using expired keys.
+        if (!allowUsingExpiredKeys) {
+            return nullptr;
+        }
+
+        // See if there's an expired key then...
+        candidate = findAuthKey_(allowUsingExhaustedKeys, true);
+        if (candidate == nullptr) {
+            return nullptr;
+        }
+    }
+
     candidate->useCount += 1;
     return candidate;
 }
@@ -519,8 +565,14 @@
 
     vector<vector<uint8_t>> keysNeedingCert;
 
+    int64_t nowMilliSeconds =
+        std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) * 1000;
+
     for (AuthKeyData& data : authKeyDatas_) {
-        bool newKeyNeeded = (data.certificate.size() == 0) || (data.useCount >= maxUsesPerKey_);
+        bool keyExceedUseCount = (data.useCount >= maxUsesPerKey_);
+        bool keyBeyondExpirationDate = (nowMilliSeconds > data.expirationDateMillisSinceEpoch);
+        bool newKeyNeeded =
+            (data.certificate.size() == 0) || keyExceedUseCount || keyBeyondExpirationDate;
         bool certificationPending = (data.pendingCertificate.size() > 0);
         if (newKeyNeeded && !certificationPending) {
             vector<uint8_t> signingKeyBlob;
@@ -543,11 +595,13 @@
 }
 
 bool CredentialData::storeStaticAuthenticationData(const vector<uint8_t>& authenticationKey,
+                                                   int64_t expirationDateMillisSinceEpoch,
                                                    const vector<uint8_t>& staticAuthData) {
     for (AuthKeyData& data : authKeyDatas_) {
         if (data.pendingCertificate == authenticationKey) {
             data.certificate = data.pendingCertificate;
             data.keyBlob = data.pendingKeyBlob;
+            data.expirationDateMillisSinceEpoch = expirationDateMillisSinceEpoch;
             data.staticAuthenticationData = staticAuthData;
             data.pendingCertificate.clear();
             data.pendingKeyBlob.clear();
diff --git a/identity/CredentialData.h b/identity/CredentialData.h
index 7995828..24b55d3 100644
--- a/identity/CredentialData.h
+++ b/identity/CredentialData.h
@@ -55,6 +55,7 @@
 
     vector<uint8_t> certificate;
     vector<uint8_t> keyBlob;
+    int64_t expirationDateMillisSinceEpoch = 0;
     vector<uint8_t> staticAuthenticationData;
     vector<uint8_t> pendingCertificate;
     vector<uint8_t> pendingKeyBlob;
@@ -106,17 +107,22 @@
 
     const vector<AuthKeyData>& getAuthKeyDatas() const;
 
+    pair<int /* keyCount */, int /*maxUsersPerKey */> getAvailableAuthenticationKeys();
+
     // Returns |nullptr| if a suitable key cannot be found. Otherwise returns
     // the authentication and increases its use-count.
-    const AuthKeyData* selectAuthKey(bool allowUsingExhaustedKeys);
+    const AuthKeyData* selectAuthKey(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys);
 
     optional<vector<vector<uint8_t>>>
     getAuthKeysNeedingCertification(const sp<IIdentityCredential>& halBinder);
 
     bool storeStaticAuthenticationData(const vector<uint8_t>& authenticationKey,
+                                       int64_t expirationDateMillisSinceEpoch,
                                        const vector<uint8_t>& staticAuthData);
 
   private:
+    AuthKeyData* findAuthKey_(bool allowUsingExhaustedKeys, bool allowUsingExpiredKeys);
+
     // Set by constructor.
     //
     string dataPath_;
diff --git a/identity/CredentialStore.cpp b/identity/CredentialStore.cpp
index e3a825b..071cf24 100644
--- a/identity/CredentialStore.cpp
+++ b/identity/CredentialStore.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "CredentialStore"
+#define LOG_TAG "credstore"
 
 #include <algorithm>
 
@@ -41,11 +41,12 @@
         LOG(ERROR) << "Error getting hardware information: " << status.toString8();
         return false;
     }
+    halApiVersion_ = hal_->getInterfaceVersion();
 
-    LOG(INFO) << "Connected to Identity Credential HAL with name '" << hwInfo_.credentialStoreName
-              << "' authored by '" << hwInfo_.credentialStoreAuthorName << "' with chunk size "
-              << hwInfo_.dataChunkSize << " and directoAccess set to "
-              << (hwInfo_.isDirectAccess ? "true" : "false");
+    LOG(INFO) << "Connected to Identity Credential HAL with API version " << halApiVersion_
+              << " and name '" << hwInfo_.credentialStoreName << "' authored by '"
+              << hwInfo_.credentialStoreAuthorName << "' with chunk size " << hwInfo_.dataChunkSize
+              << " and directoAccess set to " << (hwInfo_.isDirectAccess ? "true" : "false");
     return true;
 }
 
@@ -89,7 +90,7 @@
     }
 
     sp<IWritableCredential> writableCredential = new WritableCredential(
-        dataPath_, credentialName, docType, hwInfo_.dataChunkSize, halWritableCredential);
+        dataPath_, credentialName, docType, false, hwInfo_, halWritableCredential);
     *_aidl_return = writableCredential;
     return Status::ok();
 }
@@ -112,9 +113,10 @@
 
     // Note: IdentityCredentialStore.java's CipherSuite enumeration and CipherSuite from the
     // HAL is manually kept in sync. So this cast is safe.
-    sp<Credential> credential = new Credential(CipherSuite(cipherSuite), dataPath_, credentialName);
+    sp<Credential> credential = new Credential(CipherSuite(cipherSuite), dataPath_, credentialName,
+                                               callingUid, hwInfo_, hal_, halApiVersion_);
 
-    Status loadStatus = credential->loadCredential(hal_);
+    Status loadStatus = credential->ensureOrReplaceHalBinder();
     if (!loadStatus.isOk()) {
         LOG(ERROR) << "Error loading credential";
     } else {
diff --git a/identity/CredentialStore.h b/identity/CredentialStore.h
index 24b2b4d..15da4eb 100644
--- a/identity/CredentialStore.h
+++ b/identity/CredentialStore.h
@@ -57,6 +57,7 @@
     string dataPath_;
 
     sp<IIdentityCredentialStore> hal_;
+    int halApiVersion_;
 
     HardwareInformation hwInfo_;
 };
diff --git a/identity/CredentialStoreFactory.cpp b/identity/CredentialStoreFactory.cpp
index 5c3bf36..0e901ba 100644
--- a/identity/CredentialStoreFactory.cpp
+++ b/identity/CredentialStoreFactory.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "CredentialStoreFactory"
+#define LOG_TAG "credstore"
 
 #include <android-base/logging.h>
 
diff --git a/identity/TEST_MAPPING b/identity/TEST_MAPPING
new file mode 100644
index 0000000..87707a8
--- /dev/null
+++ b/identity/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsIdentityTestCases"
+    }
+  ]
+}
diff --git a/identity/Util.cpp b/identity/Util.cpp
index a962dc3..3a46bca 100644
--- a/identity/Util.cpp
+++ b/identity/Util.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "Util"
+#define LOG_TAG "credstore"
 
 #include <fcntl.h>
 #include <stdlib.h>
@@ -110,7 +110,7 @@
         remaining -= numWritten;
     }
 
-    if (TEMP_FAILURE_RETRY(fsync(fd) == -1)) {
+    if (TEMP_FAILURE_RETRY(fsync(fd))) {
         PLOG(ERROR) << "Failed fsyncing temp file for '" << path << "'";
         close(fd);
         return false;
diff --git a/identity/WritableCredential.cpp b/identity/WritableCredential.cpp
index a932dcf..9827d75 100644
--- a/identity/WritableCredential.cpp
+++ b/identity/WritableCredential.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "WritableCredential"
+#define LOG_TAG "credstore"
 
 #include <android-base/logging.h>
 #include <android/hardware/identity/support/IdentityCredentialSupport.h>
@@ -39,13 +39,18 @@
 using ::android::hardware::identity::support::chunkVector;
 
 WritableCredential::WritableCredential(const string& dataPath, const string& credentialName,
-                                       const string& docType, size_t dataChunkSize,
+                                       const string& docType, bool isUpdate,
+                                       HardwareInformation hwInfo,
                                        sp<IWritableIdentityCredential> halBinder)
-    : dataPath_(dataPath), credentialName_(credentialName), docType_(docType),
-      dataChunkSize_(dataChunkSize), halBinder_(halBinder) {}
+    : dataPath_(dataPath), credentialName_(credentialName), docType_(docType), isUpdate_(isUpdate),
+      hwInfo_(std::move(hwInfo)), halBinder_(halBinder) {}
 
 WritableCredential::~WritableCredential() {}
 
+void WritableCredential::setCredentialToReloadWhenUpdated(sp<Credential> credential) {
+    credentialToReloadWhenUpdated_ = credential;
+}
+
 Status WritableCredential::ensureAttestationCertificateExists(const vector<uint8_t>& challenge) {
     if (!attestationCertificate_.empty()) {
         return Status::ok();
@@ -79,7 +84,10 @@
 
 Status WritableCredential::getCredentialKeyCertificateChain(const vector<uint8_t>& challenge,
                                                             vector<uint8_t>* _aidl_return) {
-
+    if (isUpdate_) {
+        return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
+                                                "Cannot be called for an update");
+    }
     Status ensureStatus = ensureAttestationCertificateExists(challenge);
     if (!ensureStatus.isOk()) {
         return ensureStatus;
@@ -89,6 +97,15 @@
     return Status::ok();
 }
 
+void WritableCredential::setAttestationCertificate(const vector<uint8_t>& attestationCertificate) {
+    attestationCertificate_ = attestationCertificate;
+}
+
+void WritableCredential::setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey) {
+    keyCount_ = keyCount;
+    maxUsesPerKey_ = maxUsesPerKey;
+}
+
 ssize_t WritableCredential::calcExpectedProofOfProvisioningSize(
     const vector<AccessControlProfileParcel>& accessControlProfiles,
     const vector<EntryNamespaceParcel>& entryNamespaces) {
@@ -149,9 +166,12 @@
 WritableCredential::personalize(const vector<AccessControlProfileParcel>& accessControlProfiles,
                                 const vector<EntryNamespaceParcel>& entryNamespaces,
                                 int64_t secureUserId, vector<uint8_t>* _aidl_return) {
-    Status ensureStatus = ensureAttestationCertificateExists({0x00});  // Challenge cannot be empty.
-    if (!ensureStatus.isOk()) {
-        return ensureStatus;
+    if (!isUpdate_) {
+        Status ensureStatus =
+            ensureAttestationCertificateExists({0x00});  // Challenge cannot be empty.
+        if (!ensureStatus.isOk()) {
+            return ensureStatus;
+        }
     }
 
     uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
@@ -203,7 +223,7 @@
 
     for (const EntryNamespaceParcel& ensParcel : entryNamespaces) {
         for (const EntryParcel& eParcel : ensParcel.entries) {
-            vector<vector<uint8_t>> chunks = chunkVector(eParcel.value, dataChunkSize_);
+            vector<vector<uint8_t>> chunks = chunkVector(eParcel.value, hwInfo_.dataChunkSize);
 
             vector<int32_t> ids;
             std::copy(eParcel.accessControlProfileIds.begin(),
@@ -240,11 +260,18 @@
     }
     data.setCredentialData(credentialData);
 
+    data.setAvailableAuthenticationKeys(keyCount_, maxUsesPerKey_);
+
     if (!data.saveToDisk()) {
         return Status::fromServiceSpecificError(ICredentialStore::ERROR_GENERIC,
                                                 "Error saving credential data to disk");
     }
 
+    if (credentialToReloadWhenUpdated_) {
+        credentialToReloadWhenUpdated_->writableCredentialPersonalized();
+        credentialToReloadWhenUpdated_.clear();
+    }
+
     *_aidl_return = proofOfProvisioningSignature;
     return Status::ok();
 }
diff --git a/identity/WritableCredential.h b/identity/WritableCredential.h
index eb63aca..838b956 100644
--- a/identity/WritableCredential.h
+++ b/identity/WritableCredential.h
@@ -24,11 +24,14 @@
 
 #include <android/hardware/identity/IIdentityCredentialStore.h>
 
+#include "Credential.h"
+
 namespace android {
 namespace security {
 namespace identity {
 
 using ::android::binder::Status;
+using ::android::hardware::identity::HardwareInformation;
 using ::android::hardware::identity::IWritableIdentityCredential;
 using ::std::string;
 using ::std::vector;
@@ -36,9 +39,17 @@
 class WritableCredential : public BnWritableCredential {
   public:
     WritableCredential(const string& dataPath, const string& credentialName, const string& docType,
-                       size_t dataChunkSize, sp<IWritableIdentityCredential> halBinder);
+                       bool isUpdate, HardwareInformation hwInfo,
+                       sp<IWritableIdentityCredential> halBinder);
     ~WritableCredential();
 
+    // Used when updating a credential
+    void setAttestationCertificate(const vector<uint8_t>& attestationCertificate);
+    void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey);
+
+    // Used by Credential::update()
+    void setCredentialToReloadWhenUpdated(sp<Credential> credential);
+
     // IWritableCredential overrides
     Status getCredentialKeyCertificateChain(const vector<uint8_t>& challenge,
                                             vector<uint8_t>* _aidl_return) override;
@@ -51,9 +62,15 @@
     string dataPath_;
     string credentialName_;
     string docType_;
-    size_t dataChunkSize_;
+    bool isUpdate_;
+    HardwareInformation hwInfo_;
     sp<IWritableIdentityCredential> halBinder_;
+
     vector<uint8_t> attestationCertificate_;
+    int keyCount_ = 0;
+    int maxUsesPerKey_ = 1;
+
+    sp<Credential> credentialToReloadWhenUpdated_;
 
     ssize_t calcExpectedProofOfProvisioningSize(
         const vector<AccessControlProfileParcel>& accessControlProfiles,
diff --git a/identity/binder/android/security/identity/ICredential.aidl b/identity/binder/android/security/identity/ICredential.aidl
index 7bd0df7..2165810 100644
--- a/identity/binder/android/security/identity/ICredential.aidl
+++ b/identity/binder/android/security/identity/ICredential.aidl
@@ -16,6 +16,8 @@
 
 package android.security.identity;
 
+import android.security.identity.IWritableCredential;
+
 import android.security.identity.RequestNamespaceParcel;
 import android.security.identity.GetEntriesResultParcel;
 import android.security.identity.AuthKeyParcel;
@@ -40,23 +42,35 @@
     void setReaderEphemeralPublicKey(in byte[] publicKey);
 
     byte[] deleteCredential();
+    byte[] deleteWithChallenge(in byte[] challenge);
+
+    byte[] proveOwnership(in byte[] challenge);
 
     byte[] getCredentialKeyCertificateChain();
 
-    long selectAuthKey(in boolean allowUsingExhaustedKeys);
+    long selectAuthKey(in boolean allowUsingExhaustedKeys,
+                       in boolean allowUsingExpiredKeys);
 
     GetEntriesResultParcel getEntries(in byte[] requestMessage,
                                       in RequestNamespaceParcel[] requestNamespaces,
                                       in byte[] sessionTranscript,
                                       in byte[] readerSignature,
-                                      in boolean allowUsingExhaustedKeys);
+                                      in boolean allowUsingExhaustedKeys,
+                                      in boolean allowUsingExpiredKeys);
 
     void setAvailableAuthenticationKeys(in int keyCount, in int maxUsesPerKey);
 
     AuthKeyParcel[] getAuthKeysNeedingCertification();
 
-    void storeStaticAuthenticationData(in AuthKeyParcel authenticationKey, in byte[] staticAuthData);
+    void storeStaticAuthenticationData(in AuthKeyParcel authenticationKey,
+                                       in byte[] staticAuthData);
+
+    void storeStaticAuthenticationDataWithExpiration(in AuthKeyParcel authenticationKey,
+                                       in long expirationDateMillisSinceEpoch,
+                                       in byte[] staticAuthData);
 
     int[] getAuthenticationDataUsageCount();
+
+    IWritableCredential update();
 }
 
diff --git a/identity/binder/android/security/identity/ICredentialStore.aidl b/identity/binder/android/security/identity/ICredentialStore.aidl
index 1039831..8357f47 100644
--- a/identity/binder/android/security/identity/ICredentialStore.aidl
+++ b/identity/binder/android/security/identity/ICredentialStore.aidl
@@ -39,6 +39,7 @@
     const int ERROR_AUTHENTICATION_KEY_NOT_FOUND = 9;
     const int ERROR_INVALID_ITEMS_REQUEST_MESSAGE = 10;
     const int ERROR_SESSION_TRANSCRIPT_MISMATCH = 11;
+    const int ERROR_NOT_SUPPORTED = 12;
 
     SecurityHardwareInfoParcel getSecurityHardwareInfo();
 
diff --git a/identity/main.cpp b/identity/main.cpp
index 8f4968d..2559789 100644
--- a/identity/main.cpp
+++ b/identity/main.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "android.security.identity"
+#define LOG_TAG "credstore"
 
 #include <filesystem>
 
@@ -40,7 +40,7 @@
 using ::android::security::identity::CredentialStoreFactory;
 
 int main(int argc, char* argv[]) {
-    InitLogging(argv, StderrLogger);
+    InitLogging(argv);
 
     CHECK(argc == 2) << "A directory must be specified";
     string data_dir = string(argv[1]);
@@ -51,11 +51,10 @@
 
     auto ret = sm->addService(String16("android.security.identity"), factory);
     CHECK(ret == ::android::OK) << "Couldn't register binder service";
-    LOG(ERROR) << "Registered binder service";
+    LOG(INFO) << "Registered binder service";
 
-    // This is needed for binder callbacks from keystore on a ICredstoreTokenCallback binder.
-    android::ProcessState::self()->startThreadPool();
-
+    // Credstore is a single-threaded process. So devote the main thread
+    // to handling binder messages.
     IPCThreadState::self()->joinThreadPool();
 
     return 0;
diff --git a/keystore-engine/Android.bp b/keystore-engine/Android.bp
index 6512c66..0cecfd8 100644
--- a/keystore-engine/Android.bp
+++ b/keystore-engine/Android.bp
@@ -12,12 +12,21 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-BSD
+    default_applicable_licenses: ["system_security_license"],
+}
+
 cc_library_shared {
     name: "libkeystore-engine",
 
     srcs: [
         "android_engine.cpp",
-        "keystore_backend_binder.cpp",
+        "keystore2_engine.cpp",
     ],
 
     cflags: [
@@ -27,13 +36,10 @@
     ],
 
     shared_libs: [
-        "libbinder",
+        "android.system.keystore2-V1-ndk_platform",
+        "libbinder_ndk",
         "libcrypto",
         "libcutils",
-        "libhidlbase",
-        "libkeystore_aidl",
-        "libkeystore_binder",
-        "libkeystore_parcelables",
         "liblog",
         "libbase",
         "libutils",
@@ -41,28 +47,30 @@
 
 }
 
-// This builds a variant of libkeystore-engine that uses a HIDL HAL
-// owned by the WiFi user to perform signing operations.
+// This builds a variant of libkeystore-engine that is available vendor.
+// It used to use a HIDL interface to connect to keystore through wificond.
+// Now That Keystore 2.0 has a vintf stable interface this library is
+// actually identical to libkeystore-engine.
 cc_library_shared {
     name: "libkeystore-engine-wifi-hidl",
 
     srcs: [
         "android_engine.cpp",
-        "keystore_backend_hidl.cpp",
+        "keystore2_engine.cpp",
     ],
 
     cflags: [
         "-fvisibility=hidden",
         "-Wall",
         "-Werror",
-        "-DBACKEND_WIFI_HIDL",
     ],
 
     shared_libs: [
-        "android.system.wifi.keystore@1.0",
+        "android.system.keystore2-V1-ndk_platform",
+        "libbase",
+        "libbinder_ndk",
         "libcrypto",
         "liblog",
-        "libhidlbase",
         "libcutils",
         "libutils",
     ],
diff --git a/keystore-engine/android_engine.cpp b/keystore-engine/android_engine.cpp
index e3525b2..e46204e 100644
--- a/keystore-engine/android_engine.cpp
+++ b/keystore-engine/android_engine.cpp
@@ -22,307 +22,9 @@
 
 #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>
 
-#include <openssl/bn.h>
-#include <openssl/ec.h>
-#include <openssl/ec_key.h>
-#include <openssl/ecdsa.h>
-#include <openssl/engine.h>
-#include <openssl/evp.h>
-#include <openssl/rsa.h>
-#include <openssl/x509.h>
-
-#include <memory>
-
-#ifndef BACKEND_WIFI_HIDL
-#include "keystore_backend_binder.h"
-#else
-#include "keystore_backend_hidl.h"
-#endif
-
-namespace {
-KeystoreBackend *g_keystore_backend;
-void ensure_keystore_engine();
-
-/* key_id_dup is called when one of the RSA or EC_KEY objects is duplicated. */
-int key_id_dup(CRYPTO_EX_DATA* /* to */,
-               const CRYPTO_EX_DATA* /* from */,
-               void** from_d,
-               int /* index */,
-               long /* argl */,
-               void* /* argp */) {
-    char *key_id = reinterpret_cast<char *>(*from_d);
-    if (key_id != nullptr) {
-        *from_d = strdup(key_id);
-    }
-    return 1;
-}
-
-/* key_id_free is called when one of the RSA, DSA or EC_KEY object is freed. */
-void key_id_free(void* /* parent */,
-                 void* ptr,
-                 CRYPTO_EX_DATA* /* ad */,
-                 int /* index */,
-                 long /* argl */,
-                 void* /* argp */) {
-    char *key_id = reinterpret_cast<char *>(ptr);
-    free(key_id);
-}
-
-/* Many OpenSSL APIs take ownership of an argument on success but don't free
- * the argument on failure. This means we need to tell our scoped pointers when
- * we've transferred ownership, without triggering a warning by not using the
- * result of release(). */
-#define OWNERSHIP_TRANSFERRED(obj) auto _dummy __attribute__((unused)) = (obj).release()
-
-const char* rsa_get_key_id(const RSA* rsa);
-
-/* 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. */
-int rsa_private_transform(RSA *rsa, uint8_t *out, const uint8_t *in, size_t len) {
-    ALOGV("rsa_private_transform(%p, %p, %p, %u)", rsa, out, in, (unsigned) len);
-
-    ensure_keystore_engine();
-
-    const char *key_id = rsa_get_key_id(rsa);
-    if (key_id == nullptr) {
-        ALOGE("key had no key_id!");
-        return 0;
-    }
-
-    uint8_t* reply = nullptr;
-    size_t reply_len;
-    int32_t ret = g_keystore_backend->sign(key_id, in, len, &reply, &reply_len);
-    if (ret < 0) {
-        ALOGW("There was an error during rsa_decrypt: could not connect");
-        return 0;
-    } else if (ret != 0) {
-        ALOGW("Error during sign from keystore: %d", ret);
-        return 0;
-    } else if (reply_len == 0 || reply == nullptr) {
-        ALOGW("No valid signature returned");
-        return 0;
-    }
-
-    if (reply_len > 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. */
-        ALOGW("Reply len %zu greater than expected %zu", reply_len, len);
-        memcpy(out, &reply[reply_len - len], len);
-    } else if (reply_len < 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. */
-        ALOGW("Reply len %zu lesser than expected %zu", reply_len, len);
-        memset(out, 0, len);
-        memcpy(out + len - reply_len, &reply[0], reply_len);
-    } else {
-        memcpy(out, &reply[0], len);
-    }
-
-    ALOGV("rsa=%p keystore_rsa_priv_dec successful", rsa);
-    return 1;
-}
-
-const char* ecdsa_get_key_id(const EC_KEY* ec_key);
-
-/* 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. */
-static int ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig,
-                      unsigned int* sig_len, EC_KEY* ec_key) {
-    ALOGV("ecdsa_sign(%p, %u, %p)", digest, (unsigned) digest_len, ec_key);
-
-    ensure_keystore_engine();
-
-    const char *key_id = ecdsa_get_key_id(ec_key);
-    if (key_id == nullptr) {
-        ALOGE("key had no key_id!");
-        return 0;
-    }
-
-    size_t ecdsa_size = ECDSA_size(ec_key);
-
-    uint8_t* reply = nullptr;
-    size_t reply_len;
-    int32_t ret = g_keystore_backend->sign(
-            key_id, digest, digest_len, &reply, &reply_len);
-    if (ret < 0) {
-        ALOGW("There was an error during ecdsa_sign: could not connect");
-        return 0;
-    } else if (reply_len == 0 || reply == nullptr) {
-        ALOGW("No valid signature returned");
-        return 0;
-    } else if (reply_len > ecdsa_size) {
-        ALOGW("Signature is too large");
-        return 0;
-    }
-
-    // Reviewer: should't sig_len be checked here? Or is it just assumed that it is at least ecdsa_size?
-    memcpy(sig, &reply[0], reply_len);
-    *sig_len = reply_len;
-
-    ALOGV("ecdsa_sign(%p, %u, %p) => success", digest, (unsigned)digest_len,
-          ec_key);
-    return 1;
-}
-
-/* KeystoreEngine is a BoringSSL ENGINE that implements RSA and ECDSA by
- * forwarding the requested operations to Keystore. */
-class KeystoreEngine {
- public:
-  KeystoreEngine()
-      : rsa_index_(RSA_get_ex_new_index(0 /* argl */,
-                                        nullptr /* argp */,
-                                        nullptr /* new_func */,
-                                        key_id_dup,
-                                        key_id_free)),
-        ec_key_index_(EC_KEY_get_ex_new_index(0 /* argl */,
-                                              nullptr /* argp */,
-                                              nullptr /* new_func */,
-                                              key_id_dup,
-                                              key_id_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_; }
-
- private:
-  const int rsa_index_;
-  const int ec_key_index_;
-  RSA_METHOD rsa_method_;
-  ECDSA_METHOD ecdsa_method_;
-  ENGINE* const engine_;
-};
-
-pthread_once_t g_keystore_engine_once = PTHREAD_ONCE_INIT;
-KeystoreEngine *g_keystore_engine;
-
-/* init_keystore_engine is called to initialize |g_keystore_engine|. This
- * should only be called by |pthread_once|. */
-void init_keystore_engine() {
-  g_keystore_engine = new KeystoreEngine;
-#ifndef BACKEND_WIFI_HIDL
-  g_keystore_backend = new KeystoreBackendBinder;
-#else
-  g_keystore_backend = new KeystoreBackendHidl;
-#endif
-}
-
-/* ensure_keystore_engine ensures that |g_keystore_engine| is pointing to a
- * valid |KeystoreEngine| object and creates one if not. */
-void ensure_keystore_engine() {
-  pthread_once(&g_keystore_engine_once, init_keystore_engine);
-}
-
-const char* rsa_get_key_id(const RSA* rsa) {
-  return reinterpret_cast<char*>(
-      RSA_get_ex_data(rsa, g_keystore_engine->rsa_ex_index()));
-}
-
-const char* ecdsa_get_key_id(const EC_KEY* ec_key) {
-  return reinterpret_cast<char*>(
-      EC_KEY_get_ex_data(ec_key, g_keystore_engine->ec_key_ex_index()));
-}
-
-/* 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|. */
-static EVP_PKEY *wrap_rsa(const char *key_id, const RSA *public_rsa) {
-    bssl::UniquePtr<RSA> rsa(RSA_new_method(g_keystore_engine->engine()));
-    if (rsa.get() == nullptr) {
-        return nullptr;
-    }
-
-    char *key_id_copy = strdup(key_id);
-    if (key_id_copy == nullptr) {
-        return nullptr;
-    }
-
-    if (!RSA_set_ex_data(rsa.get(), g_keystore_engine->rsa_ex_index(),
-                         key_id_copy)) {
-        free(key_id_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.release();
-}
-
-/* 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|. */
-static EVP_PKEY *wrap_ecdsa(const char *key_id, const EC_KEY *public_ecdsa) {
-    bssl::UniquePtr<EC_KEY> ec(EC_KEY_new_method(g_keystore_engine->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;
-    }
-
-    char *key_id_copy = strdup(key_id);
-    if (key_id_copy == nullptr) {
-        return nullptr;
-    }
-
-    if (!EC_KEY_set_ex_data(ec.get(), g_keystore_engine->ec_key_ex_index(),
-                            key_id_copy)) {
-        free(key_id_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.release();
-}
-
-}  /* anonymous namespace */
+#include "keystore2_engine.h"
 
 extern "C" {
 
@@ -335,44 +37,7 @@
 EVP_PKEY* EVP_PKEY_from_keystore(const char* key_id) {
     ALOGV("EVP_PKEY_from_keystore(\"%s\")", key_id);
 
-    ensure_keystore_engine();
-
-    uint8_t *pubkey = nullptr;
-    size_t pubkey_len;
-    int32_t ret = g_keystore_backend->get_pubkey(key_id, &pubkey, &pubkey_len);
-    if (ret < 0) {
-        ALOGW("could not contact keystore");
-        return nullptr;
-    } else if (ret != 0 || pubkey == nullptr) {
-        ALOGW("keystore reports error: %d", ret);
-        return nullptr;
-    }
-
-    const uint8_t *inp = pubkey;
-    bssl::UniquePtr<EVP_PKEY> pkey(d2i_PUBKEY(nullptr, &inp, pubkey_len));
-    if (pkey.get() == nullptr) {
-        ALOGW("Cannot convert pubkey");
-        return nullptr;
-    }
-
-    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_id, 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_id, public_ecdsa.get());
-        break;
-    }
-    default:
-        ALOGE("Unsupported key type %d", EVP_PKEY_type(pkey->type));
-        result = nullptr;
-    }
-
-    return result;
+    return EVP_PKEY_from_keystore2(key_id);
 }
 
 }  // extern "C"
diff --git a/keystore-engine/keystore2_engine.cpp b/keystore-engine/keystore2_engine.cpp
new file mode 100644
index 0000000..69d2ca6
--- /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.IKeystoreService/default";
+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/binder/android/security/keymaster/OperationResult.aidl b/keystore-engine/keystore2_engine.h
similarity index 76%
copy from keystore/binder/android/security/keymaster/OperationResult.aidl
copy to keystore-engine/keystore2_engine.h
index db689d4..a8381d9 100644
--- a/keystore/binder/android/security/keymaster/OperationResult.aidl
+++ b/keystore-engine/keystore2_engine.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-package android.security.keymaster;
+#pragma once
 
-/* @hide */
-parcelable OperationResult cpp_header "keystore/OperationResult.h";
+#include <openssl/evp.h>
+
+extern "C" EVP_PKEY* EVP_PKEY_from_keystore2(const char* key_id);
diff --git a/keystore-engine/keystore_backend.h b/keystore-engine/keystore_backend.h
deleted file mode 100644
index 88c94b3..0000000
--- a/keystore-engine/keystore_backend.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2017 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
-
-#ifndef ANDROID_KEYSTORE_BACKEND_H
-#define ANDROID_KEYSTORE_BACKEND_H
-
-#include <stdint.h>
-
-class KeystoreBackend {
-  public:
-    virtual ~KeystoreBackend() {}
-    virtual int32_t sign(const char *key_id, const uint8_t* in, size_t len,
-                         uint8_t** reply, size_t* reply_len) = 0;
-    virtual int32_t get_pubkey(const char *key_id, uint8_t** pubkey,
-                               size_t* reply_len) = 0;
-};
-
-#endif  // ANDROID_KEYSTORE_BACKEND_H
diff --git a/keystore-engine/keystore_backend_binder.cpp b/keystore-engine/keystore_backend_binder.cpp
deleted file mode 100644
index 8b5a584..0000000
--- a/keystore-engine/keystore_backend_binder.cpp
+++ /dev/null
@@ -1,286 +0,0 @@
-/* Copyright 2017 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
-
-#include "keystore_backend_binder.h"
-
-#include <android-base/logging.h>
-#include <android/security/keystore/IKeystoreService.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <keystore/KeyCharacteristics.h>
-#include <keystore/KeymasterArguments.h>
-#include <keystore/KeymasterBlob.h>
-#include <keystore/KeystoreResponse.h>
-#include <keystore/OperationResult.h>
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore.h>
-#include <keystore/keystore_hidl_support.h>
-#include <keystore/keystore_promises.h>
-#include <keystore/keystore_return_types.h>
-
-#include <future>
-#include <thread>
-
-using android::security::keystore::IKeystoreService;
-using namespace android;
-using keystore::hidl_vec;
-
-using android::hardware::keymaster::V4_0::Algorithm;
-using android::hardware::keymaster::V4_0::authorizationValue;
-using android::hardware::keymaster::V4_0::Digest;
-using android::hardware::keymaster::V4_0::KeyFormat;
-using android::hardware::keymaster::V4_0::KeyParameter;
-using android::hardware::keymaster::V4_0::KeyPurpose;
-using android::hardware::keymaster::V4_0::NullOr;
-using android::hardware::keymaster::V4_0::PaddingMode;
-using android::hardware::keymaster::V4_0::TAG_ALGORITHM;
-using android::hardware::keymaster::V4_0::TAG_DIGEST;
-using android::hardware::keymaster::V4_0::TAG_PADDING;
-using android::security::keymaster::ExportResult;
-using android::security::keymaster::KeyCharacteristics;
-using android::security::keymaster::KeymasterArguments;
-using android::security::keymaster::KeymasterBlob;
-using android::security::keymaster::OperationResult;
-
-using KSReturn = keystore::KeyStoreNativeReturnCode;
-
-namespace {
-const char keystore_service_name[] = "android.security.keystore";
-constexpr int32_t UID_SELF = -1;
-
-using keystore::KeyCharacteristicsPromise;
-using keystore::KeystoreExportPromise;
-using keystore::KeystoreResponsePromise;
-using keystore::OperationResultPromise;
-
-}  // namespace
-
-#define AT __func__ << ":" << __LINE__ << " "
-
-static NullOr<const Algorithm&> getKeyAlgoritmFromKeyCharacteristics(
-    const ::android::security::keymaster::KeyCharacteristics& characteristics) {
-    for (const auto& param : characteristics.hardwareEnforced.getParameters()) {
-        auto algo = authorizationValue(TAG_ALGORITHM, param);
-        if (algo.isOk()) return algo;
-    }
-    for (const auto& param : characteristics.softwareEnforced.getParameters()) {
-        auto algo = authorizationValue(TAG_ALGORITHM, param);
-        if (algo.isOk()) return algo;
-    }
-    return {};
-}
-
-KeystoreBackendBinder::KeystoreBackendBinder() {
-    android::ProcessState::self()->startThreadPool();
-}
-
-int32_t KeystoreBackendBinder::sign(const char* key_id, const uint8_t* in, size_t len,
-                                    uint8_t** reply, size_t* reply_len) {
-    sp<IServiceManager> sm = defaultServiceManager();
-    sp<IBinder> binder = sm->getService(String16(keystore_service_name));
-    sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
-
-    if (service == nullptr) {
-        LOG(ERROR) << AT << "could not contact keystore";
-        return -1;
-    }
-
-    String16 key_name16(key_id);
-    int32_t error_code;
-    android::sp<KeyCharacteristicsPromise> kc_promise(new KeyCharacteristicsPromise);
-    auto kc_future = kc_promise->get_future();
-    auto binder_result = service->getKeyCharacteristics(kc_promise, key_name16, KeymasterBlob(),
-                                                        KeymasterBlob(), UID_SELF, &error_code);
-    if (!binder_result.isOk()) {
-        LOG(ERROR) << AT << "communication error while calling keystore";
-        return -1;
-    }
-    if (!KSReturn(error_code).isOk()) {
-        LOG(ERROR) << AT << "getKeyCharacteristics failed: " << error_code;
-        return -1;
-    }
-
-    auto [km_response, characteristics] = kc_future.get();
-
-    if (!KSReturn(km_response.response_code()).isOk()) {
-        LOG(ERROR) << AT << "getKeyCharacteristics failed: " << km_response.response_code();
-        return -1;
-    }
-
-    auto algorithm = getKeyAlgoritmFromKeyCharacteristics(characteristics);
-    if (!algorithm.isOk()) {
-        LOG(ERROR) << AT << "could not get algorithm from key characteristics";
-        return -1;
-    }
-
-    hidl_vec<KeyParameter> params(3);
-    params[0] = Authorization(TAG_DIGEST, Digest::NONE);
-    params[1] = Authorization(TAG_PADDING, PaddingMode::NONE);
-    params[2] = Authorization(TAG_ALGORITHM, algorithm.value());
-
-    android::sp<android::IBinder> token(new android::BBinder);
-    sp<OperationResultPromise> promise(new OperationResultPromise());
-    auto future = promise->get_future();
-    binder_result = service->begin(promise, token, key_name16, (int)KeyPurpose::SIGN,
-                                   true /*pruneable*/, KeymasterArguments(params),
-                                   std::vector<uint8_t>() /* entropy */, UID_SELF, &error_code);
-    if (!binder_result.isOk()) {
-        LOG(ERROR) << AT << "communication error while calling keystore";
-        return -1;
-    }
-
-    keystore::KeyStoreNativeReturnCode rc(error_code);
-    if (!rc.isOk()) {
-        LOG(ERROR) << AT << "Keystore begin returned: " << error_code;
-        return -1;
-    }
-    OperationResult result = future.get();
-
-    if (!result.resultCode.isOk()) {
-        LOG(ERROR) << AT << "begin failed: " << result.resultCode;
-        return -1;
-    }
-    auto handle = std::move(result.token);
-
-    do {
-        future = {};
-        promise = new OperationResultPromise();
-        future = promise->get_future();
-        binder_result = service->update(promise, handle, KeymasterArguments(params),
-                                        std::vector<uint8_t>(in, in + len), &error_code);
-        if (!binder_result.isOk()) {
-            LOG(ERROR) << AT << "communication error while calling keystore";
-            return -1;
-        }
-
-        rc = keystore::KeyStoreNativeReturnCode(error_code);
-        if (!rc.isOk()) {
-            LOG(ERROR) << AT << "Keystore update returned: " << error_code;
-            return -1;
-        }
-        result = future.get();
-
-        if (!result.resultCode.isOk()) {
-            LOG(ERROR) << AT << "update failed: " << result.resultCode;
-            return -1;
-        }
-
-        if (result.inputConsumed > len) {
-            LOG(ERROR) << AT << "update consumed more data than provided";
-            sp<KeystoreResponsePromise> abortPromise(new KeystoreResponsePromise);
-            auto abortFuture = abortPromise->get_future();
-            binder_result = service->abort(abortPromise, handle, &error_code);
-            if (!binder_result.isOk()) {
-                LOG(ERROR) << AT << "communication error while calling keystore";
-                return -1;
-            }
-            // This is mainly for logging since we already failed.
-            // But if abort returned OK we have to wait untill abort calls the callback
-            // hence the call to abortFuture.get().
-            if (!KSReturn(error_code).isOk()) {
-                LOG(ERROR) << AT << "abort failed: " << error_code;
-            } else if (!(rc = KSReturn(abortFuture.get().response_code())).isOk()) {
-                LOG(ERROR) << AT << "abort failed: " << rc;
-            }
-            return -1;
-        }
-        len -= result.inputConsumed;
-        in += result.inputConsumed;
-    } while (len > 0);
-
-    future = {};
-    promise = new OperationResultPromise();
-    future = promise->get_future();
-
-    binder_result = service->finish(
-        promise, handle, KeymasterArguments(params), std::vector<uint8_t>() /* input */,
-        std::vector<uint8_t>() /* signature */, std::vector<uint8_t>() /* entropy */, &error_code);
-
-    if (!binder_result.isOk()) {
-        LOG(ERROR) << AT << "communication error while calling keystore";
-        return -1;
-    }
-
-    rc = keystore::KeyStoreNativeReturnCode(error_code);
-    if (!rc.isOk()) {
-        LOG(ERROR) << AT << "Keystore finish returned: " << error_code;
-        return -1;
-    }
-    result = future.get();
-
-    if (!result.resultCode.isOk()) {
-        LOG(ERROR) << AT << "finish failed: " << result.resultCode;
-        return -1;
-    }
-
-    hidl_vec<uint8_t> reply_hidl(result.data);
-    if (reply_len) {
-        *reply_len = reply_hidl.size();
-    }
-    if (reply) {
-        *reply = reply_hidl.releaseData();
-    }
-    return 0;
-}
-
-int32_t KeystoreBackendBinder::get_pubkey(const char* key_id, uint8_t** pubkey,
-                                          size_t* pubkey_len) {
-    sp<IServiceManager> sm = defaultServiceManager();
-    sp<IBinder> binder = sm->getService(String16(keystore_service_name));
-    sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
-
-    if (service == nullptr) {
-        LOG(ERROR) << AT << "could not contact keystore";
-        return -1;
-    }
-
-    int32_t error_code;
-    android::sp<KeystoreExportPromise> promise(new KeystoreExportPromise);
-    auto future = promise->get_future();
-    auto binder_result = service->exportKey(
-        promise, String16(key_id), static_cast<int32_t>(KeyFormat::X509),
-        KeymasterBlob() /* clientId */, KeymasterBlob() /* appData */, UID_SELF, &error_code);
-    if (!binder_result.isOk()) {
-        LOG(ERROR) << AT << "communication error while calling keystore";
-        return -1;
-    }
-
-    KSReturn rc(error_code);
-    if (!rc.isOk()) {
-        LOG(ERROR) << AT << "exportKey failed: " << error_code;
-        return -1;
-    }
-
-    auto export_result = future.get();
-    if (!export_result.resultCode.isOk()) {
-        LOG(ERROR) << AT << "exportKey failed: " << export_result.resultCode;
-        return -1;
-    }
-
-    if (pubkey_len) {
-        *pubkey_len = export_result.exportData.size();
-    }
-    if (pubkey) {
-        *pubkey = export_result.exportData.releaseData();
-    }
-    return 0;
-}
diff --git a/keystore-engine/keystore_backend_binder.h b/keystore-engine/keystore_backend_binder.h
deleted file mode 100644
index 4c828c5..0000000
--- a/keystore-engine/keystore_backend_binder.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Copyright 2017 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
-
-#ifndef ANDROID_KEYSTORE_BACKEND_BINDER_H
-#define ANDROID_KEYSTORE_BACKEND_BINDER_H
-
-#include "keystore_backend.h"
-
-class KeystoreBackendBinder : public KeystoreBackend {
-  public:
-    KeystoreBackendBinder();
-    virtual ~KeystoreBackendBinder() {}
-    int32_t sign(const char *key_id, const uint8_t* in, size_t len,
-                 uint8_t** reply, size_t* reply_len) override;
-    int32_t get_pubkey(const char *key_id, uint8_t** pubkey,
-                     size_t* reply_len) override;
-};
-
-#endif  // ANDROID_KEYSTORE_BACKEND_BINDER_H
diff --git a/keystore-engine/keystore_backend_hidl.cpp b/keystore-engine/keystore_backend_hidl.cpp
deleted file mode 100644
index 30cf890..0000000
--- a/keystore-engine/keystore_backend_hidl.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/* Copyright 2017 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
-
-#include "keystore_backend_hidl.h"
-
-#include <android/system/wifi/keystore/1.0/IKeystore.h>
-#include <log/log.h>
-
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::sp;
-using android::system::wifi::keystore::V1_0::IKeystore;
-
-int32_t KeystoreBackendHidl::sign(
-        const char *key_id, const uint8_t* in, size_t len, uint8_t** reply,
-        size_t* reply_len) {
-    if (key_id == nullptr || in == nullptr || reply == nullptr || reply_len == nullptr) {
-        ALOGE("Null pointer argument passed");
-        return -1;
-    }
-
-    sp<IKeystore> service = IKeystore::tryGetService();
-    if (service == nullptr) {
-        ALOGE("could not contact keystore HAL");
-        return -1;
-    }
-
-    bool success = false;
-    auto cb = [&](IKeystore::KeystoreStatusCode status,
-                  hidl_vec<uint8_t> signedData) {
-      if (status == IKeystore::KeystoreStatusCode::SUCCESS) {
-          *reply_len = signedData.size();
-          *reply = signedData.releaseData();
-          success = true;
-      }
-    };
-    Return<void> ret = service->sign(
-        key_id, std::vector<uint8_t>(in, in + len), cb);
-    if (!ret.isOk() || !success) {
-        return 1;
-    }
-    return 0;
-}
-
-int32_t KeystoreBackendHidl::get_pubkey(
-        const char *key_id, uint8_t** pubkey, size_t* pubkey_len) {
-    if (key_id == nullptr || pubkey == nullptr || pubkey_len == nullptr) {
-        ALOGE("Null pointer argument passed");
-        return -1;
-    }
-
-    sp<IKeystore> service = IKeystore::tryGetService();
-    if (service == nullptr) {
-        ALOGE("could not contact keystore HAL");
-        return -1;
-    }
-
-    bool success = false;
-    auto cb = [&](IKeystore::KeystoreStatusCode status,
-                  hidl_vec<uint8_t> publicKey) {
-      if (status == IKeystore::KeystoreStatusCode::SUCCESS) {
-          *pubkey_len = publicKey.size();
-          *pubkey = publicKey.releaseData();
-          success = true;
-      }
-    };
-    Return<void> ret = service->getPublicKey(key_id, cb);
-    if (!ret.isOk() || !success) {
-        return 1;
-    }
-    return 0;
-}
diff --git a/keystore-engine/keystore_backend_hidl.h b/keystore-engine/keystore_backend_hidl.h
deleted file mode 100644
index fd38f69..0000000
--- a/keystore-engine/keystore_backend_hidl.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Copyright 2017 The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
-
-#ifndef ANDROID_KEYSTORE_BACKEND_HIDL_H
-#define ANDROID_KEYSTORE_BACKEND_HIDL_H
-
-#include "keystore_backend.h"
-
-class KeystoreBackendHidl : public KeystoreBackend {
-  public:
-    KeystoreBackendHidl() {}
-    virtual ~KeystoreBackendHidl() {}
-    int32_t sign(const char *key_id, const uint8_t* in, size_t len,
-                 uint8_t** reply, size_t* reply_len) override;
-    int32_t get_pubkey(const char *key_id, uint8_t** pubkey,
-                     size_t* reply_len) override;
-};
-
-#endif  // ANDROID_KEYSTORE_BACKEND_HIDL_H
diff --git a/keystore/Android.bp b/keystore/Android.bp
index 45b721b..0f2000c 100644
--- a/keystore/Android.bp
+++ b/keystore/Android.bp
@@ -1,3 +1,13 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    //   SPDX-license-identifier-BSD
+    default_applicable_licenses: ["system_security_license"],
+}
+
 cc_defaults {
     name: "keystore_defaults",
 
@@ -25,97 +35,6 @@
 }
 
 cc_binary {
-    name: "keystore",
-    defaults: ["keystore_defaults"],
-
-    srcs: [
-        "KeyStore.cpp",
-        "auth_token_table.cpp",
-        "blob.cpp",
-        "confirmation_manager.cpp",
-        "grant_store.cpp",
-        "key_creation_log_handler.cpp",
-        "key_operation_log_handler.cpp",
-        "key_attestation_log_handler.cpp",
-        "key_store_service.cpp",
-        "keyblob_utils.cpp",
-        "keymaster_enforcement.cpp",
-        "keymaster_worker.cpp",
-        "keystore_main.cpp",
-        "keystore_utils.cpp",
-        "legacy_keymaster_device_wrapper.cpp",
-        "operation.cpp",
-        "permissions.cpp",
-        "user_state.cpp",
-    ],
-    shared_libs: [
-        "android.hardware.confirmationui@1.0",
-        "android.hardware.keymaster@3.0",
-        "android.hardware.keymaster@4.0",
-        "android.hardware.keymaster@4.1",
-        "libbase",
-        "libbinder",
-        "libcrypto",
-        "libcutils",
-        "libhardware",
-        "libhidlbase",
-        "libkeymaster4support",
-        "libkeymaster4_1support",
-        "libkeymaster_messages",
-        "libkeymaster_portable",
-        "libkeystore-attestation-application-id",
-        "libkeystore_aidl",
-        "libkeystore_binder",
-        "libkeystore_parcelables",
-        "liblog",
-        "libprotobuf-cpp-lite",
-        "libselinux",
-        "libservices",
-        "libsoftkeymasterdevice",
-        "libutils",
-        "libstatslog",
-    ],
-    init_rc: ["keystore.rc"],
-    aidl: {
-        include_dirs: ["frameworks/base/core/java/"],
-    },
-
-    product_variables: {
-        pdk: {
-            enabled: false,
-        },
-	debuggable: {
-            cflags: [
-                // Allow VTS tests running as root to have
-                // additional permissions.
-                "-DGRANT_ROOT_ALL_PERMISSIONS",
-            ],
-        },
-    },
-
-    required: ["keystore_cli_v2"],
-}
-
-cc_binary {
-    name: "keystore_cli",
-    defaults: ["keystore_defaults"],
-
-    srcs: ["keystore_cli.cpp"],
-    shared_libs: [
-        "android.hardware.keymaster@4.0",
-        "libbinder",
-        "libcrypto",
-        "libcutils",
-        "libhidlbase",
-        "libkeystore_aidl", // for IKeyStoreService.asInterface()
-        "libkeystore_binder",
-        "libkeystore_parcelables",
-        "liblog",
-        "libutils",
-    ],
-}
-
-cc_binary {
     name: "keystore_cli_v2",
     defaults: ["keystore_defaults"],
 
@@ -123,95 +42,25 @@
         "-DKEYMASTER_NAME_TAGS",
         "-Wno-unused-parameter",
     ],
-    srcs: ["keystore_cli_v2.cpp"],
+    srcs: [
+        "keystore_cli_v2.cpp",
+        "keystore_client.proto",
+    ],
     shared_libs: [
-        "android.hardware.confirmationui@1.0",
+        "android.security.apc-ndk_platform",
+        "android.system.keystore2-V1-ndk_platform",
         "libbinder",
-        "android.hardware.keymaster@4.0",
+        "libbinder_ndk",
         "libchrome",
+        "libcrypto",
+        "libkeymint_support",
+        "libprotobuf-cpp-lite",
         "libutils",
-        "libhidlbase",
-        "libkeymaster4support",
-        "libkeystore_aidl",
-        "libkeystore_binder",
-        "libkeystore_parcelables",
     ],
 
     local_include_dirs: ["include"],
 }
 
-cc_library_shared {
-    name: "libkeystore_parcelables",
-    defaults: ["keystore_defaults"],
-    export_include_dirs: ["include"],
-    srcs: [
-        "KeymasterArguments.cpp",
-        "keystore_aidl_hidl_marshalling_utils.cpp",
-        "KeystoreResponse.cpp",
-        "OperationResult.cpp",
-    ],
-    shared_libs: [
-        "android.hardware.keymaster@4.0",
-        "android.hardware.keymaster@4.1",
-        "libbinder",
-        "libhardware",
-        "libhidlbase",
-        "libkeymaster4support",
-        "libkeymaster4_1support",
-        "liblog",
-        "libprotobuf-cpp-lite",
-        "libutils",
-        "libkeystore-attestation-application-id",
-    ],
-    export_shared_lib_headers: [
-        "android.hardware.keymaster@4.0",
-        "android.hardware.keymaster@4.1",
-        "libbinder",
-        "libhidlbase",
-        "libkeymaster4_1support",
-    ],
-}
-// Library for keystore clients
-cc_library_shared {
-    name: "libkeystore_binder",
-    defaults: ["keystore_defaults"],
-
-    srcs: [
-        "keyblob_utils.cpp",
-        "keystore_client.proto",
-        "keystore_client_impl.cpp",
-        "keystore_get.cpp",
-    ],
-    shared_libs: [
-        "android.hardware.keymaster@4.0",
-        "libbinder",
-        "libhidlbase",
-        "libkeymaster4support",
-        "libkeystore_aidl",
-        "libkeystore_parcelables",
-        "liblog",
-        "libprotobuf-cpp-lite",
-        "libutils",
-    ],
-
-    proto: {
-        type: "lite",
-        export_proto_headers: true,
-    },
-    aidl: {
-        export_aidl_headers: true,
-        include_dirs: ["frameworks/base/core/java/"],
-    },
-    export_include_dirs: ["include"],
-    export_shared_lib_headers: [
-        "android.hardware.keymaster@4.0",
-        "libbinder",
-        "libhidlbase",
-        "libkeystore_aidl",
-        "libkeystore_parcelables",
-    ],
-}
-
 // Library used by both keystore and credstore for generating the ASN.1 stored
 // in Tag::ATTESTATION_APPLICATION_ID
 cc_library_shared {
@@ -255,77 +104,3 @@
 
     vendor: true,
 }
-
-// Library for unit tests
-cc_library_static {
-    name: "libkeystore_test",
-    defaults: ["keystore_defaults"],
-
-    srcs: [
-        "auth_token_table.cpp",
-        "blob.cpp",
-    ],
-    cflags: [ "-O0", ],
-    static_libs: ["libgtest_main"],
-    shared_libs: [
-        "android.hardware.keymaster@4.0",
-        "libbinder",
-        "libcrypto",
-        "libhidlbase",
-        "libkeymaster4support",
-        "libkeystore-attestation-application-id",
-        "libutils",
-        "libkeystore_aidl",
-        "libkeystore_parcelables",
-    ],
-    export_shared_lib_headers: [
-        "android.hardware.keymaster@4.0",
-        "libhidlbase",
-        "libkeymaster4support",
-    ],
-
-    aidl: {
-        include_dirs: ["frameworks/base/core/java/"],
-    },
-    export_include_dirs: ["include"],
-}
-
-filegroup {
-    name: "keystore_aidl",
-    srcs: [
-        "binder/android/security/IConfirmationPromptCallback.aidl",
-        "binder/android/security/keystore/ICredstoreTokenCallback.aidl",
-        "binder/android/security/keystore/IKeystoreCertificateChainCallback.aidl",
-        "binder/android/security/keystore/IKeystoreExportKeyCallback.aidl",
-        "binder/android/security/keystore/IKeystoreKeyCharacteristicsCallback.aidl",
-        "binder/android/security/keystore/IKeystoreOperationResultCallback.aidl",
-        "binder/android/security/keystore/IKeystoreResponseCallback.aidl",
-        "binder/android/security/keystore/IKeystoreService.aidl",
-    ],
-    path: "binder",
-}
-
-cc_library_shared {
-    name: "libkeystore_aidl",
-    srcs: [":keystore_aidl"],
-    aidl: {
-        export_aidl_headers: true,
-        include_dirs: [
-            "system/security/keystore/binder",
-        ],
-    },
-    shared_libs: [
-        "libbinder",
-        "libcutils",
-        "libhardware",
-        "libhidlbase",
-        "libkeystore_parcelables",
-        "liblog",
-        "libselinux",
-        "libutils",
-    ],
-    export_shared_lib_headers: [
-        "libbinder",
-        "libkeystore_parcelables",
-    ],
-}
diff --git a/keystore/KeyAttestationApplicationId.cpp b/keystore/KeyAttestationApplicationId.cpp
index c62571f..1838b07 100644
--- a/keystore/KeyAttestationApplicationId.cpp
+++ b/keystore/KeyAttestationApplicationId.cpp
@@ -26,8 +26,8 @@
 KeyAttestationApplicationId::KeyAttestationApplicationId() = default;
 
 KeyAttestationApplicationId::KeyAttestationApplicationId(
-        std::unique_ptr<KeyAttestationPackageInfo> package) :
-    packageInfos_(new std::vector<std::unique_ptr<KeyAttestationPackageInfo>>()) {
+    std::optional<KeyAttestationPackageInfo> package)
+    : packageInfos_(new std::vector<std::optional<KeyAttestationPackageInfo>>()) {
     packageInfos_->push_back(std::move(package));
 }
 
@@ -39,10 +39,13 @@
 }
 
 status_t KeyAttestationApplicationId::readFromParcel(const Parcel* parcel) {
-    std::unique_ptr<std::vector<std::unique_ptr<KeyAttestationPackageInfo>>> temp_vector;
+    std::optional<std::vector<std::optional<KeyAttestationPackageInfo>>> temp_vector;
     auto rc = parcel->readParcelableVector(&temp_vector);
     if (rc != NO_ERROR) return rc;
-    packageInfos_.reset(temp_vector.release());
+    packageInfos_.reset();
+    if (temp_vector) {
+        packageInfos_ = std::make_shared<PackageInfoVector>(std::move(*temp_vector));
+    }
     return NO_ERROR;
 }
 
diff --git a/keystore/KeyAttestationPackageInfo.cpp b/keystore/KeyAttestationPackageInfo.cpp
index 75fbb7a..8e9a36a 100644
--- a/keystore/KeyAttestationPackageInfo.cpp
+++ b/keystore/KeyAttestationPackageInfo.cpp
@@ -28,7 +28,7 @@
 KeyAttestationPackageInfo::KeyAttestationPackageInfo(const String16& packageName,
                                                      int64_t versionCode,
                                                      SharedSignaturesVector signatures)
-    : packageName_(new String16(packageName)), versionCode_(versionCode), signatures_(signatures) {}
+    : packageName_(packageName), versionCode_(versionCode), signatures_(signatures) {}
 
 status_t KeyAttestationPackageInfo::writeToParcel(Parcel* parcel) const {
     auto rc = parcel->writeString16(packageName_);
@@ -44,10 +44,13 @@
     rc = parcel->readInt64(&versionCode_);
     if (rc != NO_ERROR) return rc;
 
-    std::unique_ptr<SignaturesVector> temp_vector;
+    std::optional<SignaturesVector> temp_vector;
     rc = parcel->readParcelableVector(&temp_vector);
     if (rc != NO_ERROR) return rc;
-    signatures_.reset(temp_vector.release());
+    signatures_.reset();
+    if (temp_vector) {
+        signatures_ = std::make_shared<SignaturesVector>(std::move(*temp_vector));
+    }
     return NO_ERROR;
 }
 
diff --git a/keystore/KeyStore.cpp b/keystore/KeyStore.cpp
deleted file mode 100644
index 7545397..0000000
--- a/keystore/KeyStore.cpp
+++ /dev/null
@@ -1,512 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#define LOG_TAG "keystore"
-
-#include "KeyStore.h"
-
-#include <dirent.h>
-#include <fcntl.h>
-
-#include <openssl/bio.h>
-
-#include <utils/String16.h>
-#include <utils/String8.h>
-
-#include <android-base/scopeguard.h>
-#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
-#include <android/security/keystore/IKeystoreService.h>
-#include <log/log_event_list.h>
-
-#include <private/android_logger.h>
-
-#include "keystore_utils.h"
-#include "permissions.h"
-#include <keystore/keystore_hidl_support.h>
-
-#include "keymaster_worker.h"
-
-namespace keystore {
-
-const char* KeyStore::kOldMasterKey = ".masterkey";
-const char* KeyStore::kMetaDataFile = ".metadata";
-
-const android::String16 KeyStore::kRsaKeyType("RSA");
-const android::String16 KeyStore::kEcKeyType("EC");
-
-using android::String8;
-
-KeyStore::KeyStore(const KeymasterDevices& kmDevices,
-                   SecurityLevel minimalAllowedSecurityLevelForNewKeys)
-    : mAllowNewFallback(minimalAllowedSecurityLevelForNewKeys == SecurityLevel::SOFTWARE),
-      mConfirmationManager(new ConfirmationManager(this)) {
-    memset(&mMetaData, '\0', sizeof(mMetaData));
-
-    static_assert(std::tuple_size<std::decay_t<decltype(kmDevices)>>::value ==
-                      std::tuple_size<decltype(mKmDevices)>::value,
-                  "KmasterDevices and KeymasterWorkers must have the same size");
-    for (size_t i = 0; i < kmDevices.size(); ++i) {
-        if (kmDevices[SecurityLevel(i)]) {
-            mKmDevices[SecurityLevel(i)] =
-                std::make_shared<KeymasterWorker>(kmDevices[SecurityLevel(i)], this);
-        }
-    }
-}
-
-KeyStore::~KeyStore() {
-}
-
-ResponseCode KeyStore::initialize() {
-    readMetaData();
-    if (upgradeKeystore()) {
-        writeMetaData();
-    }
-
-    return ResponseCode::NO_ERROR;
-}
-
-ResponseCode KeyStore::initializeUser(const android::String8& pw, uid_t userId) {
-    auto userState = mUserStateDB.getUserState(userId);
-    return userState->initialize(pw);
-}
-
-ResponseCode KeyStore::copyMasterKey(uid_t srcUser, uid_t dstUser) {
-    auto userState = mUserStateDB.getUserState(dstUser);
-    auto initState = mUserStateDB.getUserState(srcUser);
-    return userState->copyMasterKey(&initState);
-}
-
-ResponseCode KeyStore::writeMasterKey(const android::String8& pw, uid_t userId) {
-    auto userState = mUserStateDB.getUserState(userId);
-    return userState->writeMasterKey(pw);
-}
-
-ResponseCode KeyStore::readMasterKey(const android::String8& pw, uid_t userId) {
-    auto userState = mUserStateDB.getUserState(userId);
-    return userState->readMasterKey(pw);
-}
-
-LockedKeyBlobEntry KeyStore::getLockedBlobEntryIfNotExists(const std::string& alias, uid_t uid) {
-    KeyBlobEntry kbe(alias, mUserStateDB.getUserStateByUid(uid)->getUserDirName(), uid);
-    auto result = LockedKeyBlobEntry::get(std::move(kbe));
-    if (result->hasKeyBlob()) return {};
-    return result;
-}
-
-std::optional<KeyBlobEntry> KeyStore::getBlobEntryIfExists(const std::string& alias, uid_t uid) {
-    KeyBlobEntry kbe(alias, mUserStateDB.getUserStateByUid(uid)->getUserDirName(), uid);
-    if (kbe.hasKeyBlob()) return kbe;
-
-    // If this is one of the legacy UID->UID mappings, use it.
-    uid_t euid = get_keystore_euid(uid);
-    if (euid != uid) {
-        kbe = KeyBlobEntry(alias, mUserStateDB.getUserStateByUid(euid)->getUserDirName(), euid);
-        if (kbe.hasKeyBlob()) return kbe;
-    }
-
-    // They might be using a granted key.
-    auto grant = mGrants.get(uid, alias);
-    if (grant) {
-        kbe = grant->entry_;
-        if (kbe.hasKeyBlob()) return kbe;
-    }
-    return {};
-}
-LockedKeyBlobEntry KeyStore::getLockedBlobEntryIfExists(const std::string& alias, uid_t uid) {
-    auto blobentry = getBlobEntryIfExists(alias, uid);
-    if (!blobentry) return {};
-    LockedKeyBlobEntry lockedentry = LockedKeyBlobEntry::get(std::move(*blobentry));
-    if (!lockedentry || !lockedentry->hasKeyBlob()) return {};
-    return lockedentry;
-}
-
-void KeyStore::resetUser(uid_t userId, bool keepUnenryptedEntries) {
-    android::String8 prefix("");
-    android::Vector<android::String16> aliases;
-
-    auto userState = mUserStateDB.getUserState(userId);
-    std::string userDirName = userState->getUserDirName();
-    auto encryptionKey = userState->getEncryptionKey();
-    auto state = userState->getState();
-    // userState is a proxy that holds a lock which may be required by a worker.
-    // LockedKeyBlobEntry::list has a fence that waits until all workers have finished which may
-    // not happen if a user state lock is held. The following line relinquishes the lock.
-    userState = {};
-
-    ResponseCode rc;
-    std::list<LockedKeyBlobEntry> matches;
-
-    // must not be called by a keymaster worker. List waits for workers to relinquish all access
-    // to blob entries
-    std::tie(rc, matches) = LockedKeyBlobEntry::list(userDirName);
-    if (rc != ResponseCode::NO_ERROR) {
-        return;
-    }
-
-    for (LockedKeyBlobEntry& lockedEntry : matches) {
-        bool shouldDelete = true;
-
-        if (keepUnenryptedEntries) {
-            Blob blob;
-            Blob charBlob;
-            ResponseCode rc;
-
-            std::tie(rc, blob, charBlob) = lockedEntry.readBlobs(encryptionKey, state);
-
-            switch (rc) {
-            case ResponseCode::SYSTEM_ERROR:
-            case ResponseCode::VALUE_CORRUPTED:
-                // If we can't read blobs, delete them.
-                shouldDelete = true;
-                break;
-
-            case ResponseCode::NO_ERROR:
-            case ResponseCode::LOCKED:
-                // Delete encrypted blobs but keep unencrypted blobs and super-encrypted blobs.  We
-                // need to keep super-encrypted blobs so we can report that the user is
-                // unauthenticated if a caller tries to use them, rather than reporting that they
-                // don't exist.
-                shouldDelete = blob.isEncrypted();
-                break;
-
-            default:
-                ALOGE("Got unexpected return code %d from readBlobs", rc);
-                // This shouldn't happen.  To be on the safe side, delete it.
-                shouldDelete = true;
-                break;
-            }
-        }
-        if (shouldDelete) {
-            del(lockedEntry);
-        }
-    }
-
-    userState = mUserStateDB.getUserState(userId);
-    if (!userState->deleteMasterKey()) {
-        ALOGE("Failed to delete user %d's master key", userId);
-    }
-    if (!keepUnenryptedEntries) {
-        if (!userState->reset()) {
-            ALOGE("Failed to remove user %d's directory", userId);
-        }
-    }
-}
-
-bool KeyStore::isEmpty(uid_t userId) const {
-    std::string userDirName;
-    {
-        // userState holds a lock which must be relinquished before list is called. This scope
-        // prevents deadlocks.
-        auto userState = mUserStateDB.getUserState(userId);
-        if (!userState) {
-            return true;
-        }
-        userDirName = userState->getUserDirName();
-    }
-
-    ResponseCode rc;
-    std::list<LockedKeyBlobEntry> matches;
-
-    // must not be called by a keymaster worker. List waits for workers to relinquish all access
-    // to blob entries
-    std::tie(rc, matches) = LockedKeyBlobEntry::list(userDirName);
-
-    return rc == ResponseCode::SYSTEM_ERROR || matches.size() == 0;
-}
-
-void KeyStore::lock(uid_t userId) {
-    auto userState = mUserStateDB.getUserState(userId);
-    userState->zeroizeMasterKeysInMemory();
-    userState->setState(STATE_LOCKED);
-}
-
-static void maybeLogKeyIntegrityViolation(const LockedKeyBlobEntry& lockedEntry,
-                                          const BlobType type) {
-    if (!__android_log_security() || (type != TYPE_KEY_PAIR && type != TYPE_KEYMASTER_10)) return;
-    log_key_integrity_violation(lockedEntry->alias().c_str(), lockedEntry->uid());
-}
-
-std::tuple<ResponseCode, Blob, Blob> KeyStore::get(const LockedKeyBlobEntry& blobfile) {
-    std::tuple<ResponseCode, Blob, Blob> result;
-
-    uid_t userId = get_user_id(blobfile->uid());
-    Blob& keyBlob = std::get<1>(result);
-    ResponseCode& rc = std::get<0>(result);
-
-    auto userState = mUserStateDB.getUserState(userId);
-    BlobType type = BlobType::TYPE_ANY;
-    auto logOnScopeExit = android::base::make_scope_guard([&] {
-        if (rc == ResponseCode::VALUE_CORRUPTED) {
-            maybeLogKeyIntegrityViolation(blobfile, type);
-        }
-    });
-
-    result = blobfile.readBlobs(userState->getEncryptionKey(), userState->getState());
-    if (rc != ResponseCode::NO_ERROR) {
-        return result;
-    }
-
-    // update the type for logging (see scope_guard above)
-    type = keyBlob.getType();
-
-    const uint8_t version = keyBlob.getVersion();
-    if (version < CURRENT_BLOB_VERSION) {
-        /* If we upgrade the key, we need to write it to disk again. Then
-         * it must be read it again since the blob is encrypted each time
-         * it's written.
-         */
-        if (upgradeBlob(&keyBlob, version)) {
-            if ((rc = this->put(blobfile, keyBlob, {})) != ResponseCode::NO_ERROR ||
-                (result = blobfile.readBlobs(userState->getEncryptionKey(), userState->getState()),
-                 rc) != ResponseCode::NO_ERROR) {
-                return result;
-            }
-        }
-    }
-
-    return result;
-}
-
-ResponseCode KeyStore::put(const LockedKeyBlobEntry& blobfile, Blob keyBlob,
-                           Blob characteristicsBlob) {
-    auto userState = mUserStateDB.getUserStateByUid(blobfile->uid());
-    return blobfile.writeBlobs(std::move(keyBlob), std::move(characteristicsBlob),
-                               userState->getEncryptionKey(), userState->getState());
-}
-
-ResponseCode KeyStore::del(const LockedKeyBlobEntry& blobfile) {
-    Blob keyBlob;
-    Blob charactaristicsBlob;
-    ResponseCode rc;
-    uid_t uid = blobfile->uid();
-    std::string alias = blobfile->alias();
-
-    std::tie(rc, keyBlob, charactaristicsBlob) = get(blobfile);
-
-    // after getting the blob from the file system we scrub the filesystem.
-    mGrants.removeAllGrantsToKey(uid, alias);
-    auto result = blobfile.deleteBlobs();
-
-    if (rc != ResponseCode::NO_ERROR) {
-        LOG(ERROR) << "get keyblob failed " << int(rc);
-        return rc;
-    }
-
-    // if we got the blob successfully, we try and delete it from the keymaster device
-    auto dev = getDevice(keyBlob);
-
-    if (keyBlob.getType() == ::TYPE_KEYMASTER_10) {
-        dev->deleteKey(blob2hidlVec(keyBlob), [dev, alias, uid](Return<ErrorCode> rc) {
-            auto ret = KS_HANDLE_HIDL_ERROR(dev, rc);
-            // A device doesn't have to implement delete_key.
-            bool success = ret == ErrorCode::OK || ret == ErrorCode::UNIMPLEMENTED;
-            if (__android_log_security()) {
-                android_log_event_list(SEC_TAG_KEY_DESTROYED)
-                    << int32_t(success) << alias << int32_t(uid) << LOG_ID_SECURITY;
-            }
-            if (!success) {
-                LOG(ERROR) << "Keymaster delete for key " << alias << " of uid " << uid
-                           << " failed";
-            }
-        });
-    }
-
-    return result;
-}
-
-std::string KeyStore::addGrant(const LockedKeyBlobEntry& blobfile, uid_t granteeUid) {
-    return mGrants.put(granteeUid, blobfile);
-}
-
-bool KeyStore::removeGrant(const LockedKeyBlobEntry& blobfile, const uid_t granteeUid) {
-    return mGrants.removeByFileAlias(granteeUid, blobfile);
-}
-void KeyStore::removeAllGrantsToUid(const uid_t granteeUid) {
-    mGrants.removeAllGrantsToUid(granteeUid);
-}
-
-bool KeyStore::isHardwareBacked(const android::String16& keyType) const {
-    // if strongbox device is present TEE must also be present and of sufficiently high version
-    // to support all keys in hardware
-    if (getDevice(SecurityLevel::STRONGBOX)) return true;
-    if (!getDevice(SecurityLevel::TRUSTED_ENVIRONMENT)) {
-        ALOGW("can't get keymaster device");
-        return false;
-    }
-
-    auto version = getDevice(SecurityLevel::TRUSTED_ENVIRONMENT)->halVersion();
-    if (keyType == kRsaKeyType) return true;  // All versions support RSA
-    return keyType == kEcKeyType && version.supportsEc;
-}
-
-std::tuple<ResponseCode, Blob, Blob, LockedKeyBlobEntry>
-KeyStore::getKeyForName(const android::String8& keyName, const uid_t uid, const BlobType type) {
-    std::tuple<ResponseCode, Blob, Blob, LockedKeyBlobEntry> result;
-    auto& [rc, keyBlob, charBlob, lockedEntry] = result;
-
-    lockedEntry = getLockedBlobEntryIfExists(keyName.string(), uid);
-
-    if (!lockedEntry) return rc = ResponseCode::KEY_NOT_FOUND, std::move(result);
-
-    std::tie(rc, keyBlob, charBlob) = get(lockedEntry);
-
-    if (rc == ResponseCode::NO_ERROR) {
-        if (keyBlob.getType() != type) return rc = ResponseCode::KEY_NOT_FOUND, std::move(result);
-    }
-    return result;
-}
-
-bool KeyStore::upgradeBlob(Blob* blob, const uint8_t oldVersion) {
-    bool updated = false;
-    uint8_t version = oldVersion;
-
-    if (!blob || !(*blob)) return false;
-
-    /* From V0 -> V1: All old types were unknown */
-    if (version == 0) {
-        ALOGE("Failed to upgrade key blob. Ancient blob version 0 is no longer supported");
-
-        return false;
-    }
-
-    /* From V1 -> V2: All old keys were encrypted */
-    if (version == 1) {
-        ALOGV("upgrading to version 2");
-
-        blob->setEncrypted(true);
-        version = 2;
-        updated = true;
-    }
-
-    /*
-     * If we've updated, set the key blob to the right version
-     * and write it.
-     */
-    if (updated) {
-        blob->setVersion(version);
-    }
-
-    return updated;
-}
-
-void KeyStore::readMetaData() {
-    int in = TEMP_FAILURE_RETRY(open(kMetaDataFile, O_RDONLY));
-    if (in < 0) {
-        return;
-    }
-    size_t fileLength = readFully(in, (uint8_t*)&mMetaData, sizeof(mMetaData));
-    if (fileLength != sizeof(mMetaData)) {
-        ALOGI("Metadata file is %zd bytes (%zd experted); upgrade?", fileLength, sizeof(mMetaData));
-    }
-    close(in);
-}
-
-void KeyStore::writeMetaData() {
-    const char* tmpFileName = ".metadata.tmp";
-    int out =
-        TEMP_FAILURE_RETRY(open(tmpFileName, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR));
-    if (out < 0) {
-        ALOGE("couldn't write metadata file: %s", strerror(errno));
-        return;
-    }
-    size_t fileLength = writeFully(out, (uint8_t*)&mMetaData, sizeof(mMetaData));
-    if (fileLength != sizeof(mMetaData)) {
-        ALOGI("Could only write %zd bytes to metadata file (%zd expected)", fileLength,
-              sizeof(mMetaData));
-    }
-    close(out);
-    rename(tmpFileName, kMetaDataFile);
-}
-
-bool KeyStore::upgradeKeystore() {
-    bool upgraded = false;
-
-    if (mMetaData.version == 0) {
-        auto userState = getUserStateDB().getUserStateByUid(0);
-
-        // Initialize first so the directory is made.
-        userState->initialize();
-
-        // Migrate the old .masterkey file to user 0.
-        if (access(kOldMasterKey, R_OK) == 0) {
-            if (rename(kOldMasterKey, userState->getMasterKeyFileName().c_str()) < 0) {
-                ALOGE("couldn't migrate old masterkey: %s", strerror(errno));
-                return false;
-            }
-        }
-
-        // Initialize again in case we had a key.
-        userState->initialize();
-
-        // Try to migrate existing keys.
-        DIR* dir = opendir(".");
-        if (!dir) {
-            // Give up now; maybe we can upgrade later.
-            ALOGE("couldn't open keystore's directory; something is wrong");
-            return false;
-        }
-
-        struct dirent* file;
-        while ((file = readdir(dir)) != nullptr) {
-            // We only care about files.
-            if (file->d_type != DT_REG) {
-                continue;
-            }
-
-            // Skip anything that starts with a "."
-            if (file->d_name[0] == '.') {
-                continue;
-            }
-
-            // Find the current file's user.
-            char* end;
-            unsigned long thisUid = strtoul(file->d_name, &end, 10);
-            if (end[0] != '_' || end[1] == 0) {
-                continue;
-            }
-            auto otherUser = getUserStateDB().getUserStateByUid(thisUid);
-            if (otherUser->getUserId() != 0) {
-                unlinkat(dirfd(dir), file->d_name, 0);
-            }
-
-            // Rename the file into user directory.
-            DIR* otherdir = opendir(otherUser->getUserDirName().c_str());
-            if (otherdir == nullptr) {
-                ALOGW("couldn't open user directory for rename");
-                continue;
-            }
-            if (renameat(dirfd(dir), file->d_name, dirfd(otherdir), file->d_name) < 0) {
-                ALOGW("couldn't rename blob: %s: %s", file->d_name, strerror(errno));
-            }
-            closedir(otherdir);
-        }
-        closedir(dir);
-
-        mMetaData.version = 1;
-        upgraded = true;
-    }
-
-    return upgraded;
-}
-
-void KeyStore::binderDied(const ::android::wp<IBinder>& who) {
-    for (unsigned i = 0; i < mKmDevices.size(); ++i) {
-        if (mKmDevices[SecurityLevel(i)]) mKmDevices[SecurityLevel(i)]->binderDied(who);
-    }
-    getConfirmationManager().binderDied(who);
-}
-
-}  // namespace keystore
diff --git a/keystore/KeyStore.h b/keystore/KeyStore.h
deleted file mode 100644
index 0027ec8..0000000
--- a/keystore/KeyStore.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2016 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 KEYSTORE_KEYSTORE_H_
-#define KEYSTORE_KEYSTORE_H_
-
-#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
-#include <keymasterV4_1/Keymaster.h>
-#include <utils/Vector.h>
-
-#include <keystore/keymaster_types.h>
-
-#include "auth_token_table.h"
-#include "blob.h"
-#include "confirmation_manager.h"
-#include "grant_store.h"
-#include "keymaster_worker.h"
-#include "keystore_keymaster_enforcement.h"
-#include "operation.h"
-#include "user_state.h"
-
-#include <array>
-#include <optional>
-#include <tuple>
-
-namespace keystore {
-
-using ::android::sp;
-using keymaster::support::Keymaster;
-
-template <typename T, size_t count> class Devices : public std::array<T, count> {
-  public:
-    T& operator[](SecurityLevel secLevel) {
-        static_assert(uint32_t(SecurityLevel::SOFTWARE) == 0 &&
-                          uint32_t(SecurityLevel::TRUSTED_ENVIRONMENT) == 1 &&
-                          uint32_t(SecurityLevel::STRONGBOX) == 2,
-                      "Numeric values of security levels have changed");
-        return std::array<T, count>::at(static_cast<uint32_t>(secLevel));
-    }
-    T operator[](SecurityLevel secLevel) const {
-        if (static_cast<uint32_t>(secLevel) > static_cast<uint32_t>(SecurityLevel::STRONGBOX)) {
-            LOG(ERROR) << "Invalid security level requested";
-            return {};
-        }
-        return (*const_cast<Devices*>(this))[secLevel];
-    }
-};
-
-}  // namespace keystore
-
-namespace std {
-template <typename T, size_t count> struct tuple_size<keystore::Devices<T, count>> {
-  public:
-    static constexpr size_t value = std::tuple_size<std::array<T, count>>::value;
-};
-}  // namespace std
-
-namespace keystore {
-
-using KeymasterWorkers = Devices<std::shared_ptr<KeymasterWorker>, 3>;
-using KeymasterDevices = Devices<sp<Keymaster>, 3>;
-
-class KeyStore : public ::android::IBinder::DeathRecipient {
-  public:
-    KeyStore(const KeymasterDevices& kmDevices,
-             SecurityLevel minimalAllowedSecurityLevelForNewKeys);
-    ~KeyStore();
-
-    std::shared_ptr<KeymasterWorker> getDevice(SecurityLevel securityLevel) const {
-        return mKmDevices[securityLevel];
-    }
-
-    std::shared_ptr<KeymasterWorker> getFallbackDevice() const {
-        // we only return the fallback device if the creation of new fallback key blobs is
-        // allowed. (also see getDevice below)
-        if (mAllowNewFallback) {
-            return mKmDevices[SecurityLevel::SOFTWARE];
-        } else {
-            return nullptr;
-        }
-    }
-
-    std::shared_ptr<KeymasterWorker> getDevice(const Blob& blob) {
-        return mKmDevices[blob.getSecurityLevel()];
-    }
-
-    ResponseCode initialize();
-
-    State getState(uid_t userId) { return mUserStateDB.getUserState(userId)->getState(); }
-
-    ResponseCode initializeUser(const android::String8& pw, uid_t userId);
-
-    ResponseCode copyMasterKey(uid_t srcUser, uid_t dstUser);
-    ResponseCode writeMasterKey(const android::String8& pw, uid_t userId);
-    ResponseCode readMasterKey(const android::String8& pw, uid_t userId);
-
-    LockedKeyBlobEntry getLockedBlobEntryIfNotExists(const std::string& alias, uid_t uid);
-    std::optional<KeyBlobEntry> getBlobEntryIfExists(const std::string& alias, uid_t uid);
-    LockedKeyBlobEntry getLockedBlobEntryIfExists(const std::string& alias, uid_t uid);
-    /*
-     * Delete entries owned by userId. If keepUnencryptedEntries is true
-     * then only encrypted entries will be removed, otherwise all entries will
-     * be removed.
-     */
-    void resetUser(uid_t userId, bool keepUnenryptedEntries);
-    bool isEmpty(uid_t userId) const;
-
-    void lock(uid_t userId);
-
-    std::tuple<ResponseCode, Blob, Blob> get(const LockedKeyBlobEntry& blobfile);
-    ResponseCode put(const LockedKeyBlobEntry& blobfile, Blob keyBlob, Blob characteristicsBlob);
-    ResponseCode del(const LockedKeyBlobEntry& blobfile);
-
-    std::string addGrant(const LockedKeyBlobEntry& blobfile, uid_t granteeUid);
-    bool removeGrant(const LockedKeyBlobEntry& blobfile, const uid_t granteeUid);
-    void removeAllGrantsToUid(const uid_t granteeUid);
-
-    ResponseCode importKey(const uint8_t* key, size_t keyLen, const LockedKeyBlobEntry& blobfile,
-                           uid_t userId, int32_t flags);
-
-    bool isHardwareBacked(const android::String16& keyType) const;
-
-    std::tuple<ResponseCode, Blob, Blob, LockedKeyBlobEntry>
-    getKeyForName(const android::String8& keyName, const uid_t uid, const BlobType type);
-
-    void binderDied(const ::android::wp<IBinder>& who) override;
-
-    UserStateDB& getUserStateDB() { return mUserStateDB; }
-    AuthTokenTable& getAuthTokenTable() { return mAuthTokenTable; }
-    KeystoreKeymasterEnforcement& getEnforcementPolicy() { return mEnforcementPolicy; }
-    ConfirmationManager& getConfirmationManager() { return *mConfirmationManager; }
-
-    void addOperationDevice(sp<IBinder> token, std::shared_ptr<KeymasterWorker> dev) {
-        std::lock_guard<std::mutex> lock(operationDeviceMapMutex_);
-        operationDeviceMap_.emplace(std::move(token), std::move(dev));
-    }
-    std::shared_ptr<KeymasterWorker> getOperationDevice(const sp<IBinder>& token) {
-        std::lock_guard<std::mutex> lock(operationDeviceMapMutex_);
-        auto it = operationDeviceMap_.find(token);
-        if (it != operationDeviceMap_.end()) {
-            return it->second;
-        }
-        return {};
-    }
-    void removeOperationDevice(const sp<IBinder>& token) {
-        std::lock_guard<std::mutex> lock(operationDeviceMapMutex_);
-        operationDeviceMap_.erase(token);
-    }
-
-  private:
-    static const char* kOldMasterKey;
-    static const char* kMetaDataFile;
-    static const android::String16 kRsaKeyType;
-    static const android::String16 kEcKeyType;
-
-    KeymasterWorkers mKmDevices;
-
-    bool mAllowNewFallback;
-
-    UserStateDB mUserStateDB;
-    AuthTokenTable mAuthTokenTable;
-    KeystoreKeymasterEnforcement mEnforcementPolicy;
-    sp<ConfirmationManager> mConfirmationManager;
-
-    ::keystore::GrantStore mGrants;
-
-    typedef struct { uint32_t version; } keystore_metadata_t;
-
-    keystore_metadata_t mMetaData;
-
-    /**
-     * Upgrade the key from the current version to whatever is newest.
-     */
-    bool upgradeBlob(Blob* blob, const uint8_t oldVersion);
-
-    void readMetaData();
-    void writeMetaData();
-
-    bool upgradeKeystore();
-
-    std::mutex operationDeviceMapMutex_;
-    std::map<sp<IBinder>, std::shared_ptr<KeymasterWorker>> operationDeviceMap_;
-};
-
-}  // namespace keystore
-
-#endif  // KEYSTORE_KEYSTORE_H_
diff --git a/keystore/KeymasterArguments.cpp b/keystore/KeymasterArguments.cpp
deleted file mode 100644
index 60b86cc..0000000
--- a/keystore/KeymasterArguments.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
-**
-** Copyright 2017, 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 "include/keystore/KeymasterArguments.h"
-#include "keystore_aidl_hidl_marshalling_utils.h"
-
-#include <binder/Parcel.h>
-
-namespace android {
-namespace security {
-namespace keymaster {
-
-using ::android::status_t;
-status_t KeymasterArguments::readFromParcel(const android::Parcel* in) {
-    data_ = keystore::readParamSetFromParcel(*in);
-    return OK;
-};
-
-status_t KeymasterArguments::writeToParcel(android::Parcel* out) const {
-    return keystore::writeParamSetToParcel(data_, out);
-};
-
-KeymasterArguments::KeymasterArguments(hardware::hidl_vec<keystore::KeyParameter>&& other)
-    : data_(std::move(other)) {}
-
-KeymasterArguments::KeymasterArguments(const hardware::hidl_vec<keystore::KeyParameter>& other)
-    : data_(other) {}
-
-}  // namespace keymaster
-}  // namespace security
-}  // namespace android
diff --git a/keystore/KeystoreResponse.cpp b/keystore/KeystoreResponse.cpp
deleted file mode 100644
index c46973a..0000000
--- a/keystore/KeystoreResponse.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-**
-** Copyright 2018, 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 <binder/Parcel.h>
-#include <keystore/keymaster_types.h>
-#include <utility>
-#include <utils/String16.h>
-
-#include "include/keystore/KeystoreResponse.h"
-
-namespace android {
-namespace security {
-namespace keystore {
-
-status_t KeystoreResponse::readFromParcel(const Parcel* in) {
-    auto rc = in->readInt32(&response_code_);
-    if (rc != NO_ERROR) return rc;
-    return in->readString16(&error_msg_);
-}
-
-status_t KeystoreResponse::writeToParcel(Parcel* out) const {
-    auto rc = out->writeInt32(response_code_);
-    if (rc != NO_ERROR) return rc;
-    return out->writeString16(error_msg_);
-}
-
-}  // namespace keystore
-}  // namespace security
-}  // namespace android
diff --git a/keystore/OperationResult.cpp b/keystore/OperationResult.cpp
deleted file mode 100644
index dec4d40..0000000
--- a/keystore/OperationResult.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-**
-** Copyright 2017, 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 "include/keystore/OperationResult.h"
-
-#include <utility>
-
-#include <binder/Parcel.h>
-
-#include <keystore/keymaster_types.h>
-
-#include "keystore_aidl_hidl_marshalling_utils.h"
-
-namespace android {
-namespace security {
-namespace keymaster {
-
-using ::android::status_t;
-using ::keystore::ErrorCode;
-
-OperationResult::OperationResult() : resultCode(), token(), handle(0), inputConsumed(0), data() {}
-
-status_t OperationResult::readFromParcel(const Parcel* inn) {
-    const Parcel& in = *inn;
-    resultCode = ErrorCode(in.readInt32());
-    token = in.readStrongBinder();
-    handle = static_cast<uint64_t>(in.readInt64());
-    inputConsumed = in.readInt32();
-    data = keystore::readKeymasterBlob(in);
-    outParams = keystore::readParamSetFromParcel(in);
-    return OK;
-}
-
-status_t OperationResult::writeToParcel(Parcel* out) const {
-    out->writeInt32(resultCode.getErrorCode());
-    out->writeStrongBinder(token);
-    out->writeInt64(handle);
-    out->writeInt32(inputConsumed);
-    keystore::writeKeymasterBlob(data, out);
-    keystore::writeParamSetToParcel(outParams, out);
-    return OK;
-}
-
-OperationResult operationFailed(const ::keystore::KeyStoreServiceReturnCode& error) {
-    OperationResult opResult = {};
-    opResult.resultCode = error;
-    return opResult;
-}
-
-}  // namespace keymaster
-}  // namespace security
-}  // namespace android
diff --git a/keystore/TEST_MAPPING b/keystore/TEST_MAPPING
new file mode 100644
index 0000000..0511967
--- /dev/null
+++ b/keystore/TEST_MAPPING
@@ -0,0 +1,74 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsKeystoreTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.RequiresDevice"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.SignatureTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.RsaSignaturePerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.RsaKeyGenPerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.RsaCipherPerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.MacTest#testLargeMsgKat"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.KeyPairGeneratorTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.KeyGeneratorTest#testHmacKeySupportedSizes"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.HmacMacPerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.EcdsaSignaturePerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.EcKeyGenPerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.DesCipherPerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.CipherTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.AttestationPerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.AndroidKeyStoreTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.AesCipherPerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.AESCipherNistCavpKatTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.DESedeECBPKCS7PaddingCipherTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.DESedeECBNoPaddingCipherTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.DESedeECBPKCS7PaddingCipherTest"
+        }
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "CtsKeystoreTestCases"
+    }
+  ]
+}
diff --git a/keystore/auth_token_table.cpp b/keystore/auth_token_table.cpp
deleted file mode 100644
index 5e6d572..0000000
--- a/keystore/auth_token_table.cpp
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#define LOG_TAG "keystore"
-
-#include "auth_token_table.h"
-
-#include <assert.h>
-#include <time.h>
-
-#include <algorithm>
-
-#include <log/log.h>
-
-namespace keystore {
-
-template <typename IntType, uint32_t byteOrder> struct choose_hton;
-
-template <typename IntType> struct choose_hton<IntType, __ORDER_LITTLE_ENDIAN__> {
-    inline static IntType hton(const IntType& value) {
-        IntType result = 0;
-        const unsigned char* inbytes = reinterpret_cast<const unsigned char*>(&value);
-        unsigned char* outbytes = reinterpret_cast<unsigned char*>(&result);
-        for (int i = sizeof(IntType) - 1; i >= 0; --i) {
-            *(outbytes++) = inbytes[i];
-        }
-        return result;
-    }
-};
-
-template <typename IntType> struct choose_hton<IntType, __ORDER_BIG_ENDIAN__> {
-    inline static IntType hton(const IntType& value) { return value; }
-};
-
-template <typename IntType> inline IntType hton(const IntType& value) {
-    return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
-}
-
-template <typename IntType> inline IntType ntoh(const IntType& value) {
-    // same operation and hton
-    return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
-}
-
-//
-// Some trivial template wrappers around std algorithms, so they take containers not ranges.
-//
-template <typename Container, typename Predicate>
-typename Container::iterator find_if(Container& container, Predicate pred) {
-    return std::find_if(container.begin(), container.end(), pred);
-}
-
-template <typename Container, typename Predicate>
-typename Container::iterator remove_if(Container& container, Predicate pred) {
-    return std::remove_if(container.begin(), container.end(), pred);
-}
-
-template <typename Container> typename Container::iterator min_element(Container& container) {
-    return std::min_element(container.begin(), container.end());
-}
-
-time_t clock_gettime_raw() {
-    struct timespec time;
-    clock_gettime(CLOCK_MONOTONIC_RAW, &time);
-    return time.tv_sec;
-}
-
-void AuthTokenTable::AddAuthenticationToken(HardwareAuthToken&& auth_token) {
-    Entry new_entry(std::move(auth_token), clock_function_());
-    // STOPSHIP: debug only, to be removed
-    ALOGD("AddAuthenticationToken: timestamp = %llu, time_received = %lld",
-          static_cast<unsigned long long>(new_entry.token().timestamp),
-          static_cast<long long>(new_entry.time_received()));
-
-    std::lock_guard<std::mutex> lock(entries_mutex_);
-    RemoveEntriesSupersededBy(new_entry);
-    if (entries_.size() >= max_entries_) {
-        ALOGW("Auth token table filled up; replacing oldest entry");
-        *min_element(entries_) = std::move(new_entry);
-    } else {
-        entries_.push_back(std::move(new_entry));
-    }
-}
-
-inline bool is_secret_key_operation(Algorithm algorithm, KeyPurpose purpose) {
-    if ((algorithm != Algorithm::RSA && algorithm != Algorithm::EC)) return true;
-    if (purpose == KeyPurpose::SIGN || purpose == KeyPurpose::DECRYPT) return true;
-    return false;
-}
-
-inline bool KeyRequiresAuthentication(const AuthorizationSet& key_info, KeyPurpose purpose) {
-    auto algorithm = defaultOr(key_info.GetTagValue(TAG_ALGORITHM), Algorithm::AES);
-    return is_secret_key_operation(algorithm, purpose) &&
-           key_info.find(Tag::NO_AUTH_REQUIRED) == -1;
-}
-
-inline bool KeyRequiresAuthPerOperation(const AuthorizationSet& key_info, KeyPurpose purpose) {
-    auto algorithm = defaultOr(key_info.GetTagValue(TAG_ALGORITHM), Algorithm::AES);
-    return is_secret_key_operation(algorithm, purpose) && key_info.find(Tag::AUTH_TIMEOUT) == -1;
-}
-
-std::tuple<AuthTokenTable::Error, HardwareAuthToken>
-AuthTokenTable::FindAuthorization(const AuthorizationSet& key_info, KeyPurpose purpose,
-                                  uint64_t op_handle) {
-
-    std::lock_guard<std::mutex> lock(entries_mutex_);
-
-    if (!KeyRequiresAuthentication(key_info, purpose)) return {AUTH_NOT_REQUIRED, {}};
-
-    auto auth_type =
-        defaultOr(key_info.GetTagValue(TAG_USER_AUTH_TYPE), HardwareAuthenticatorType::NONE);
-
-    std::vector<uint64_t> key_sids;
-    ExtractSids(key_info, &key_sids);
-
-    if (KeyRequiresAuthPerOperation(key_info, purpose))
-        return FindAuthPerOpAuthorization(key_sids, auth_type, op_handle);
-    else
-        return FindTimedAuthorization(key_sids, auth_type, key_info);
-}
-
-std::tuple<AuthTokenTable::Error, HardwareAuthToken> AuthTokenTable::FindAuthPerOpAuthorization(
-    const std::vector<uint64_t>& sids, HardwareAuthenticatorType auth_type, uint64_t op_handle) {
-    if (op_handle == 0) return {OP_HANDLE_REQUIRED, {}};
-
-    auto matching_op = find_if(
-        entries_, [&](Entry& e) { return e.token().challenge == op_handle && !e.completed(); });
-
-    if (matching_op == entries_.end()) return {AUTH_TOKEN_NOT_FOUND, {}};
-
-    if (!matching_op->SatisfiesAuth(sids, auth_type)) return {AUTH_TOKEN_WRONG_SID, {}};
-
-    return {OK, matching_op->token()};
-}
-
-std::tuple<AuthTokenTable::Error, HardwareAuthToken>
-AuthTokenTable::FindTimedAuthorization(const std::vector<uint64_t>& sids,
-                                       HardwareAuthenticatorType auth_type,
-                                       const AuthorizationSet& key_info) {
-    Entry* newest_match = nullptr;
-    for (auto& entry : entries_)
-        if (entry.SatisfiesAuth(sids, auth_type) && entry.is_newer_than(newest_match))
-            newest_match = &entry;
-
-    if (!newest_match) return {AUTH_TOKEN_NOT_FOUND, {}};
-
-    auto timeout = defaultOr(key_info.GetTagValue(TAG_AUTH_TIMEOUT), 0);
-
-    time_t now = clock_function_();
-    if (static_cast<int64_t>(newest_match->time_received()) + timeout < static_cast<int64_t>(now))
-        return {AUTH_TOKEN_EXPIRED, {}};
-
-    if (key_info.GetTagValue(TAG_ALLOW_WHILE_ON_BODY).isOk()) {
-        if (static_cast<int64_t>(newest_match->time_received()) <
-            static_cast<int64_t>(last_off_body_)) {
-            return {AUTH_TOKEN_EXPIRED, {}};
-        }
-    }
-
-    newest_match->UpdateLastUse(now);
-    return {OK, newest_match->token()};
-}
-
-std::tuple<AuthTokenTable::Error, HardwareAuthToken>
-AuthTokenTable::FindAuthorizationForCredstore(uint64_t challenge, uint64_t secureUserId,
-                                              int64_t authTokenMaxAgeMillis) {
-    std::vector<uint64_t> sids = {secureUserId};
-    HardwareAuthenticatorType auth_type = HardwareAuthenticatorType::ANY;
-
-    time_t now = clock_function_();
-
-    // challenge-based - the authToken has to contain the given challenge.
-    if (challenge != 0) {
-        auto matching_op = find_if(
-            entries_, [&](Entry& e) { return e.token().challenge == challenge && !e.completed(); });
-        if (matching_op == entries_.end()) {
-            return {AUTH_TOKEN_NOT_FOUND, {}};
-        }
-
-        if (!matching_op->SatisfiesAuth(sids, auth_type)) {
-            return {AUTH_TOKEN_WRONG_SID, {}};
-        }
-
-        if (authTokenMaxAgeMillis > 0) {
-            if (static_cast<int64_t>(matching_op->time_received()) + authTokenMaxAgeMillis <
-                static_cast<int64_t>(now)) {
-                return {AUTH_TOKEN_EXPIRED, {}};
-            }
-        }
-
-        return {OK, matching_op->token()};
-    }
-
-    // Otherwise, no challenge - any authToken younger than the specified maximum
-    // age will do.
-    Entry* newest_match = nullptr;
-    for (auto& entry : entries_) {
-        if (entry.SatisfiesAuth(sids, auth_type) && entry.is_newer_than(newest_match)) {
-            newest_match = &entry;
-        }
-    }
-
-    if (newest_match == nullptr) {
-        return {AUTH_TOKEN_NOT_FOUND, {}};
-    }
-
-    if (authTokenMaxAgeMillis > 0) {
-        if (static_cast<int64_t>(newest_match->time_received()) + authTokenMaxAgeMillis <
-            static_cast<int64_t>(now)) {
-            return {AUTH_TOKEN_EXPIRED, {}};
-        }
-    }
-
-    newest_match->UpdateLastUse(now);
-    return {OK, newest_match->token()};
-}
-
-void AuthTokenTable::ExtractSids(const AuthorizationSet& key_info, std::vector<uint64_t>* sids) {
-    assert(sids);
-    for (auto& param : key_info)
-        if (param.tag == Tag::USER_SECURE_ID)
-            sids->push_back(authorizationValue(TAG_USER_SECURE_ID, param).value());
-}
-
-void AuthTokenTable::RemoveEntriesSupersededBy(const Entry& entry) {
-    entries_.erase(remove_if(entries_, [&](Entry& e) { return entry.Supersedes(e); }),
-                   entries_.end());
-}
-
-void AuthTokenTable::onDeviceOffBody() {
-    last_off_body_ = clock_function_();
-}
-
-void AuthTokenTable::Clear() {
-    std::lock_guard<std::mutex> lock(entries_mutex_);
-
-    entries_.clear();
-}
-
-size_t AuthTokenTable::size() const {
-    std::lock_guard<std::mutex> lock(entries_mutex_);
-    return entries_.size();
-}
-
-bool AuthTokenTable::IsSupersededBySomeEntry(const Entry& entry) {
-    return std::any_of(entries_.begin(), entries_.end(),
-                       [&](Entry& e) { return e.Supersedes(entry); });
-}
-
-void AuthTokenTable::MarkCompleted(const uint64_t op_handle) {
-    std::lock_guard<std::mutex> lock(entries_mutex_);
-
-    auto found = find_if(entries_, [&](Entry& e) { return e.token().challenge == op_handle; });
-    if (found == entries_.end()) return;
-
-    assert(!IsSupersededBySomeEntry(*found));
-    found->mark_completed();
-
-    if (IsSupersededBySomeEntry(*found)) entries_.erase(found);
-}
-
-AuthTokenTable::Entry::Entry(HardwareAuthToken&& token, time_t current_time)
-    : token_(std::move(token)), time_received_(current_time), last_use_(current_time),
-      operation_completed_(token_.challenge == 0) {}
-
-bool AuthTokenTable::Entry::SatisfiesAuth(const std::vector<uint64_t>& sids,
-                                          HardwareAuthenticatorType auth_type) {
-    for (auto sid : sids) {
-        if (SatisfiesAuth(sid, auth_type)) return true;
-    }
-    return false;
-}
-
-void AuthTokenTable::Entry::UpdateLastUse(time_t time) {
-    this->last_use_ = time;
-}
-
-bool AuthTokenTable::Entry::Supersedes(const Entry& entry) const {
-    if (!entry.completed()) return false;
-
-    return (token_.userId == entry.token_.userId &&
-            token_.authenticatorType == entry.token_.authenticatorType &&
-            token_.authenticatorId == entry.token_.authenticatorId && is_newer_than(&entry));
-}
-
-}  // namespace keystore
diff --git a/keystore/auth_token_table.h b/keystore/auth_token_table.h
deleted file mode 100644
index 787b9b1..0000000
--- a/keystore/auth_token_table.h
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2015 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 <memory>
-#include <mutex>
-#include <vector>
-
-#include <keystore/keymaster_types.h>
-
-#ifndef KEYSTORE_AUTH_TOKEN_TABLE_H_
-#define KEYSTORE_AUTH_TOKEN_TABLE_H_
-
-namespace keystore {
-
-namespace test {
-class AuthTokenTableTest;
-}  // namespace test
-
-time_t clock_gettime_raw();
-
-/**
- * AuthTokenTable manages a set of received authorization tokens and can provide the appropriate
- * token for authorizing a key operation.
- *
- * To keep the table from growing without bound, superseded entries are removed when possible, and
- * least recently used entries are automatically pruned when when the table exceeds a size limit,
- * which is expected to be relatively small, since the implementation uses a linear search.
- */
-class AuthTokenTable {
-  public:
-    explicit AuthTokenTable(size_t max_entries = 32, time_t (*clock_function)() = clock_gettime_raw)
-        : max_entries_(max_entries), last_off_body_(clock_function()),
-          clock_function_(clock_function) {}
-
-    enum Error {
-        OK,
-        AUTH_NOT_REQUIRED = -1,
-        AUTH_TOKEN_EXPIRED = -2,    // Found a matching token, but it's too old.
-        AUTH_TOKEN_WRONG_SID = -3,  // Found a token with the right challenge, but wrong SID.  This
-                                    // most likely indicates that the authenticator was updated
-                                    // (e.g. new fingerprint enrolled).
-        OP_HANDLE_REQUIRED = -4,    // The key requires auth per use but op_handle was zero.
-        AUTH_TOKEN_NOT_FOUND = -5,
-    };
-
-    /**
-     * Add an authorization token to the table.
-     */
-    void AddAuthenticationToken(HardwareAuthToken&& auth_token);
-
-    /**
-     * Find an authorization token that authorizes the operation specified by \p operation_handle on
-     * a key with the characteristics specified in \p key_info.
-     *
-     * This method is O(n * m), where n is the number of KM_TAG_USER_SECURE_ID entries in key_info
-     * and m is the number of entries in the table.  It could be made better, but n and m should
-     * always be small.
-     *
-     * The table retains ownership of the returned object.
-     */
-    std::tuple<Error, HardwareAuthToken> FindAuthorization(const AuthorizationSet& key_info,
-                                                           KeyPurpose purpose, uint64_t op_handle);
-
-    std::tuple<Error, HardwareAuthToken>
-    FindAuthorizationForCredstore(uint64_t challenge, uint64_t secureUserId,
-                                  int64_t authTokenMaxAgeMillis);
-
-    /**
-     * Mark operation completed.  This allows tokens associated with the specified operation to be
-     * superseded by new tokens.
-     */
-    void MarkCompleted(const uint64_t op_handle);
-
-    /**
-     * Update the last_off_body_ timestamp so that tokens which remain authorized only so long as
-     * the device stays on body can be revoked.
-     */
-    void onDeviceOffBody();
-
-    void Clear();
-
-    /**
-     * This function shall only be used for testing.
-     *
-     * BEWARE: Since the auth token table can be accessed
-     * concurrently, the size may be out dated as soon as it returns.
-     */
-    size_t size() const;
-
-  private:
-    friend class AuthTokenTableTest;
-
-    class Entry {
-      public:
-        Entry(HardwareAuthToken&& token, time_t current_time);
-        Entry(Entry&& entry) noexcept { *this = std::move(entry); }
-
-        void operator=(Entry&& rhs) noexcept {
-            token_ = std::move(rhs.token_);
-            time_received_ = rhs.time_received_;
-            last_use_ = rhs.last_use_;
-            operation_completed_ = rhs.operation_completed_;
-        }
-
-        bool operator<(const Entry& rhs) const { return last_use_ < rhs.last_use_; }
-
-        void UpdateLastUse(time_t time);
-
-        bool Supersedes(const Entry& entry) const;
-        bool SatisfiesAuth(const std::vector<uint64_t>& sids, HardwareAuthenticatorType auth_type);
-
-        bool is_newer_than(const Entry* entry) const {
-            if (!entry) return true;
-            uint64_t ts = token_.timestamp;
-            uint64_t other_ts = entry->token_.timestamp;
-            // Normally comparing timestamp_host_order alone is sufficient, but here is an
-            // additional hack to compare time_received value for some devices where their auth
-            // tokens contain fixed timestamp (due to the a stuck secure RTC on them)
-            return (ts > other_ts) ||
-                   ((ts == other_ts) && (time_received_ > entry->time_received_));
-        }
-
-        void mark_completed() { operation_completed_ = true; }
-
-        const HardwareAuthToken& token() const & { return token_; }
-        time_t time_received() const { return time_received_; }
-        bool completed() const { return operation_completed_; }
-
-      private:
-        bool SatisfiesAuth(uint64_t sid, HardwareAuthenticatorType auth_type) const {
-            return (sid == token_.userId || sid == token_.authenticatorId) &&
-                   (auth_type & token_.authenticatorType) != 0;
-        }
-
-        HardwareAuthToken token_;
-        time_t time_received_;
-        time_t last_use_;
-        bool operation_completed_;
-    };
-
-    std::tuple<Error, HardwareAuthToken>
-    FindAuthPerOpAuthorization(const std::vector<uint64_t>& sids,
-                               HardwareAuthenticatorType auth_type, uint64_t op_handle);
-    std::tuple<Error, HardwareAuthToken> FindTimedAuthorization(const std::vector<uint64_t>& sids,
-                                                                HardwareAuthenticatorType auth_type,
-                                                                const AuthorizationSet& key_info);
-    void ExtractSids(const AuthorizationSet& key_info, std::vector<uint64_t>* sids);
-    void RemoveEntriesSupersededBy(const Entry& entry);
-    bool IsSupersededBySomeEntry(const Entry& entry);
-
-    /**
-     * Guards the entries_ vector against concurrent modification. All public facing methods
-     * reading of modifying the vector must grab this mutex.
-     */
-    mutable std::mutex entries_mutex_;
-    std::vector<Entry> entries_;
-    size_t max_entries_;
-    time_t last_off_body_;
-    time_t (*clock_function_)();
-};
-
-}  // namespace keystore
-
-#endif  // KEYSTORE_AUTH_TOKEN_TABLE_H_
diff --git a/keystore/binder/android/security/IConfirmationPromptCallback.aidl b/keystore/binder/android/security/IConfirmationPromptCallback.aidl
deleted file mode 100644
index 96a1a04..0000000
--- a/keystore/binder/android/security/IConfirmationPromptCallback.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2017, 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;
-
-/**
- * This must be kept manually in sync with system/security/keystore until AIDL
- * can generate both Java and C++ bindings.
- *
- * @hide
- */
-interface IConfirmationPromptCallback {
-    oneway void onConfirmationPromptCompleted(in int result, in byte[] dataThatWasConfirmed);
-}
diff --git a/keystore/binder/android/security/keymaster/ExportResult.aidl b/keystore/binder/android/security/keymaster/ExportResult.aidl
deleted file mode 100644
index 1748653..0000000
--- a/keystore/binder/android/security/keymaster/ExportResult.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 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.keymaster;
-
-/* @hide */
-parcelable ExportResult cpp_header "keystore/ExportResult.h";
diff --git a/keystore/binder/android/security/keymaster/KeyCharacteristics.aidl b/keystore/binder/android/security/keymaster/KeyCharacteristics.aidl
deleted file mode 100644
index 32e75ad..0000000
--- a/keystore/binder/android/security/keymaster/KeyCharacteristics.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 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.keymaster;
-
-/* @hide */
-parcelable KeyCharacteristics cpp_header "keystore/KeyCharacteristics.h";
diff --git a/keystore/binder/android/security/keymaster/KeymasterArguments.aidl b/keystore/binder/android/security/keymaster/KeymasterArguments.aidl
deleted file mode 100644
index 44d9f09..0000000
--- a/keystore/binder/android/security/keymaster/KeymasterArguments.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 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.keymaster;
-
-/* @hide */
-parcelable KeymasterArguments cpp_header "keystore/KeymasterArguments.h";
diff --git a/keystore/binder/android/security/keymaster/KeymasterBlob.aidl b/keystore/binder/android/security/keymaster/KeymasterBlob.aidl
deleted file mode 100644
index 5c5db9e..0000000
--- a/keystore/binder/android/security/keymaster/KeymasterBlob.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 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.keymaster;
-
-/* @hide */
-parcelable KeymasterBlob cpp_header "keystore/KeymasterBlob.h";
diff --git a/keystore/binder/android/security/keymaster/KeymasterCertificateChain.aidl b/keystore/binder/android/security/keymaster/KeymasterCertificateChain.aidl
deleted file mode 100644
index ddb5cae..0000000
--- a/keystore/binder/android/security/keymaster/KeymasterCertificateChain.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2016 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.keymaster;
-
-/* @hide */
-parcelable KeymasterCertificateChain cpp_header "keystore/KeymasterCertificateChain.h";
diff --git a/keystore/binder/android/security/keystore/IKeystoreCertificateChainCallback.aidl b/keystore/binder/android/security/keystore/IKeystoreCertificateChainCallback.aidl
deleted file mode 100644
index dca928d..0000000
--- a/keystore/binder/android/security/keystore/IKeystoreCertificateChainCallback.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2018, 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.keystore;
-
-import android.security.keystore.KeystoreResponse;
-import android.security.keymaster.KeymasterCertificateChain;
-
-/**
- * @hide
- */
-oneway interface IKeystoreCertificateChainCallback {
-    void onFinished(in KeystoreResponse response, in KeymasterCertificateChain chain);
-}
\ No newline at end of file
diff --git a/keystore/binder/android/security/keystore/IKeystoreExportKeyCallback.aidl b/keystore/binder/android/security/keystore/IKeystoreExportKeyCallback.aidl
deleted file mode 100644
index e42e927..0000000
--- a/keystore/binder/android/security/keystore/IKeystoreExportKeyCallback.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2018, 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.keystore;
-
-import android.security.keystore.KeystoreResponse;
-import android.security.keymaster.ExportResult;
-
-/**
- * @hide
- */
-oneway interface IKeystoreExportKeyCallback {
-	void onFinished(in ExportResult result);
-}
\ No newline at end of file
diff --git a/keystore/binder/android/security/keystore/IKeystoreKeyCharacteristicsCallback.aidl b/keystore/binder/android/security/keystore/IKeystoreKeyCharacteristicsCallback.aidl
deleted file mode 100644
index e1f0ffe..0000000
--- a/keystore/binder/android/security/keystore/IKeystoreKeyCharacteristicsCallback.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2018, 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.keystore;
-
-import android.security.keystore.KeystoreResponse;
-import android.security.keymaster.KeyCharacteristics;
-
-/**
- * @hide
- */
-oneway interface IKeystoreKeyCharacteristicsCallback {
-	void onFinished(in KeystoreResponse response, in KeyCharacteristics charactersistics);
-}
\ No newline at end of file
diff --git a/keystore/binder/android/security/keystore/IKeystoreOperationResultCallback.aidl b/keystore/binder/android/security/keystore/IKeystoreOperationResultCallback.aidl
deleted file mode 100644
index 0a51511..0000000
--- a/keystore/binder/android/security/keystore/IKeystoreOperationResultCallback.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2018, 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.keystore;
-
-import android.security.keystore.KeystoreResponse;
-import android.security.keymaster.OperationResult;
-
-/**
- * @hide
- */
-oneway interface IKeystoreOperationResultCallback {
-    void onFinished(in OperationResult result);
-}
\ No newline at end of file
diff --git a/keystore/binder/android/security/keystore/IKeystoreResponseCallback.aidl b/keystore/binder/android/security/keystore/IKeystoreResponseCallback.aidl
deleted file mode 100644
index 912e605..0000000
--- a/keystore/binder/android/security/keystore/IKeystoreResponseCallback.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Copyright (c) 2018, 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.keystore;
-
-import android.security.keystore.KeystoreResponse;
-
-/**
- * @hide
- */
-oneway interface IKeystoreResponseCallback {
-    void onFinished(in KeystoreResponse response);
-}
\ No newline at end of file
diff --git a/keystore/binder/android/security/keystore/IKeystoreService.aidl b/keystore/binder/android/security/keystore/IKeystoreService.aidl
deleted file mode 100644
index 6edca56..0000000
--- a/keystore/binder/android/security/keystore/IKeystoreService.aidl
+++ /dev/null
@@ -1,92 +0,0 @@
-/**
- * Copyright (c) 2018, 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.keystore;
-
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterBlob;
-import android.security.keymaster.OperationResult;
-import android.security.keystore.ICredstoreTokenCallback;
-import android.security.keystore.IKeystoreResponseCallback;
-import android.security.keystore.IKeystoreKeyCharacteristicsCallback;
-import android.security.keystore.IKeystoreExportKeyCallback;
-import android.security.keystore.IKeystoreOperationResultCallback;
-import android.security.keystore.IKeystoreCertificateChainCallback;
-
-/**
- * @hide
- */
-interface IKeystoreService {
-    @UnsupportedAppUsage
-    int getState(int userId);
-    @UnsupportedAppUsage
-    byte[] get(String name, int uid);
-    @UnsupportedAppUsage
-    int insert(String name, in byte[] item, int uid, int flags);
-    @UnsupportedAppUsage
-    int del(String name, int uid);
-    @UnsupportedAppUsage
-    int exist(String name, int uid);
-    @UnsupportedAppUsage
-    String[] list(String namePrefix, int uid);
-    int onUserPasswordChanged(int userId, String newPassword);
-    int lock(int userId);
-    int unlock(int userId, String userPassword);
-    int isEmpty(int userId);
-    String grant(String name, int granteeUid);
-    @UnsupportedAppUsage
-    int ungrant(String name, int granteeUid);
-    long getmtime(String name, int uid);
-    @UnsupportedAppUsage
-    int is_hardware_backed(String string);
-    @UnsupportedAppUsage
-    int clear_uid(long uid);
-
-    int addRngEntropy(IKeystoreResponseCallback cb, in byte[] data, int flags);
-    int generateKey(IKeystoreKeyCharacteristicsCallback cb, String alias, in KeymasterArguments arguments, in byte[] entropy, int uid,
-        int flags);
-    int getKeyCharacteristics (IKeystoreKeyCharacteristicsCallback cb, String alias, in KeymasterBlob clientId, in KeymasterBlob appData,
-        int uid);
-    int importKey(IKeystoreKeyCharacteristicsCallback cb, String alias, in KeymasterArguments arguments, int format,
-        in byte[] keyData, int uid, int flags);
-    int exportKey(IKeystoreExportKeyCallback cb, String alias, int format, in KeymasterBlob clientId,
-        in KeymasterBlob appData, int uid);
-    int begin(in IKeystoreOperationResultCallback cb, IBinder appToken, String alias, int purpose, boolean pruneable,
-        in KeymasterArguments params, in byte[] entropy, int uid);
-    int update(in IKeystoreOperationResultCallback cb, IBinder token, in KeymasterArguments params, in byte[] input);
-    int finish(in IKeystoreOperationResultCallback cb, IBinder token, in KeymasterArguments params, in byte[] input, in byte[] signature,
-        in byte[] entropy);
-    int abort(in IKeystoreResponseCallback cb, IBinder token);
-    int addAuthToken(in byte[] authToken);
-    int onUserAdded(int userId, int parentId);
-    int onUserRemoved(int userId);
-    int attestKey(in IKeystoreCertificateChainCallback cb, String alias, in KeymasterArguments params);
-    int attestDeviceIds(in IKeystoreCertificateChainCallback cb, in KeymasterArguments params);
-    int onDeviceOffBody();
-    int importWrappedKey(in IKeystoreKeyCharacteristicsCallback cb, String wrappedKeyAlias, in byte[] wrappedKey,
-        in String wrappingKeyAlias, in byte[] maskingKey, in KeymasterArguments arguments,
-        in long rootSid, in long fingerprintSid);
-    int presentConfirmationPrompt(IBinder listener, String promptText, in byte[] extraData,
-        in String locale, in int uiOptionsAsFlags);
-    int cancelConfirmationPrompt(IBinder listener);
-    boolean isConfirmationPromptSupported();
-    int onKeyguardVisibilityChanged(in boolean isShowing, in int userId);
-    int listUidsOfAuthBoundKeys(out @utf8InCpp List<String> uids);
-
-    // Called by credstore (and only credstore).
-    void getTokensForCredstore(in long challenge, in long secureUserId, in int authTokenMaxAgeMillis,
-                               in ICredstoreTokenCallback cb);
-}
diff --git a/keystore/binder/android/security/keystore/KeystoreResponse.aidl b/keystore/binder/android/security/keystore/KeystoreResponse.aidl
deleted file mode 100644
index 128b456..0000000
--- a/keystore/binder/android/security/keystore/KeystoreResponse.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2018 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.keystore;
-
-/* @hide */
-parcelable KeystoreResponse cpp_header "keystore/KeystoreResponse.h";
diff --git a/keystore/blob.cpp b/keystore/blob.cpp
deleted file mode 100644
index ffdb454..0000000
--- a/keystore/blob.cpp
+++ /dev/null
@@ -1,791 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#define LOG_TAG "keystore"
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-
-#include <log/log.h>
-
-#include "blob.h"
-
-#include "keystore_utils.h"
-
-#include <openssl/evp.h>
-#include <openssl/rand.h>
-
-#include <istream>
-#include <ostream>
-#include <streambuf>
-#include <string>
-
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-
-namespace {
-
-constexpr size_t kGcmIvSizeBytes = 96 / 8;
-
-#if defined(__clang__)
-#define OPTNONE __attribute__((optnone))
-#elif defined(__GNUC__)
-#define OPTNONE __attribute__((optimize("O0")))
-#else
-#error Need a definition for OPTNONE
-#endif
-
-class ArrayEraser {
-  public:
-    ArrayEraser(uint8_t* arr, size_t size) : mArr(arr), mSize(size) {}
-    OPTNONE ~ArrayEraser() { std::fill(mArr, mArr + mSize, 0); }
-
-  private:
-    volatile uint8_t* mArr;
-    size_t mSize;
-};
-
-/**
- * Returns a EVP_CIPHER appropriate for the given key, based on the key's size.
- */
-const EVP_CIPHER* getAesCipherForKey(const std::vector<uint8_t>& key) {
-    const EVP_CIPHER* cipher = EVP_aes_256_gcm();
-    if (key.size() == kAes128KeySizeBytes) {
-        cipher = EVP_aes_128_gcm();
-    }
-    return cipher;
-}
-
-/*
- * Encrypt 'len' data at 'in' with AES-GCM, using 128-bit or 256-bit key at 'key', 96-bit IV at
- * 'iv' and write output to 'out' (which may be the same location as 'in') and 128-bit tag to
- * 'tag'.
- */
-ResponseCode AES_gcm_encrypt(const uint8_t* in, uint8_t* out, size_t len,
-                             const std::vector<uint8_t>& key, const uint8_t* iv, uint8_t* tag) {
-
-    // There can be 128-bit and 256-bit keys
-    const EVP_CIPHER* cipher = getAesCipherForKey(key);
-
-    bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
-
-    EVP_EncryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key.data(), iv);
-    EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
-
-    std::unique_ptr<uint8_t[]> out_tmp(new uint8_t[len]);
-    uint8_t* out_pos = out_tmp.get();
-    int out_len;
-
-    EVP_EncryptUpdate(ctx.get(), out_pos, &out_len, in, len);
-    out_pos += out_len;
-    EVP_EncryptFinal_ex(ctx.get(), out_pos, &out_len);
-    out_pos += out_len;
-    if (out_pos - out_tmp.get() != static_cast<ssize_t>(len)) {
-        ALOGD("Encrypted ciphertext is the wrong size, expected %zu, got %zd", len,
-              out_pos - out_tmp.get());
-        return ResponseCode::SYSTEM_ERROR;
-    }
-
-    std::copy(out_tmp.get(), out_pos, out);
-    EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, kGcmTagLength, tag);
-
-    return ResponseCode::NO_ERROR;
-}
-
-/*
- * Decrypt 'len' data at 'in' with AES-GCM, using 128-bit or 256-bit key at 'key', 96-bit IV at
- * 'iv', checking 128-bit tag at 'tag' and writing plaintext to 'out'(which may be the same
- * location as 'in').
- */
-ResponseCode AES_gcm_decrypt(const uint8_t* in, uint8_t* out, size_t len,
-                             const std::vector<uint8_t> key, const uint8_t* iv,
-                             const uint8_t* tag) {
-
-    // There can be 128-bit and 256-bit keys
-    const EVP_CIPHER* cipher = getAesCipherForKey(key);
-
-    bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
-
-    EVP_DecryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key.data(), iv);
-    EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
-    EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, kGcmTagLength, const_cast<uint8_t*>(tag));
-
-    std::unique_ptr<uint8_t[]> out_tmp(new uint8_t[len]);
-    ArrayEraser out_eraser(out_tmp.get(), len);
-    uint8_t* out_pos = out_tmp.get();
-    int out_len;
-
-    EVP_DecryptUpdate(ctx.get(), out_pos, &out_len, in, len);
-    out_pos += out_len;
-    if (!EVP_DecryptFinal_ex(ctx.get(), out_pos, &out_len)) {
-        ALOGE("Failed to decrypt blob; ciphertext or tag is likely corrupted");
-        return ResponseCode::VALUE_CORRUPTED;
-    }
-    out_pos += out_len;
-    if (out_pos - out_tmp.get() != static_cast<ssize_t>(len)) {
-        ALOGE("Encrypted plaintext is the wrong size, expected %zu, got %zd", len,
-              out_pos - out_tmp.get());
-        return ResponseCode::VALUE_CORRUPTED;
-    }
-
-    std::copy(out_tmp.get(), out_pos, out);
-
-    return ResponseCode::NO_ERROR;
-}
-
-class ArrayStreamBuffer : public std::streambuf {
-  public:
-    template <typename T, size_t size> explicit ArrayStreamBuffer(const T (&data)[size]) {
-        static_assert(sizeof(T) == 1, "Array element size too large");
-        std::streambuf::char_type* d = const_cast<std::streambuf::char_type*>(
-            reinterpret_cast<const std::streambuf::char_type*>(&data[0]));
-        setg(d, d, d + size);
-        setp(d, d + size);
-    }
-
-  protected:
-    pos_type seekoff(off_type off, std::ios_base::seekdir dir,
-                     std::ios_base::openmode which = std::ios_base::in |
-                                                     std::ios_base::out) override {
-        bool in = which & std::ios_base::in;
-        bool out = which & std::ios_base::out;
-        if ((!in && !out) || (in && out && dir == std::ios_base::cur)) return -1;
-        std::streambuf::char_type* newPosPtr;
-        switch (dir) {
-        case std::ios_base::beg:
-            newPosPtr = pbase();
-            break;
-        case std::ios_base::cur:
-            // if dir == cur then in xor out due to
-            // if ((!in && !out) || (in && out && dir == std::ios_base::cur)) return -1; above
-            if (in)
-                newPosPtr = gptr();
-            else
-                newPosPtr = pptr();
-            break;
-        case std::ios_base::end:
-            // in and out bounds are the same and cannot change, so we can take either range
-            // regardless of the value of "which"
-            newPosPtr = epptr();
-            break;
-        }
-        newPosPtr += off;
-        if (newPosPtr < pbase() || newPosPtr > epptr()) return -1;
-        if (in) {
-            gbump(newPosPtr - gptr());
-        }
-        if (out) {
-            pbump(newPosPtr - pptr());
-        }
-        return newPosPtr - pbase();
-    }
-};
-
-}  // namespace
-
-Blob::Blob(const uint8_t* value, size_t valueLength, const uint8_t* info, uint8_t infoLength,
-           BlobType type) {
-    mBlob = std::make_unique<blobv3>();
-    memset(mBlob.get(), 0, sizeof(blobv3));
-    if (valueLength > kValueSize) {
-        valueLength = kValueSize;
-        ALOGW("Provided blob length too large");
-    }
-    if (infoLength + valueLength > kValueSize) {
-        infoLength = kValueSize - valueLength;
-        ALOGW("Provided info length too large");
-    }
-    mBlob->length = valueLength;
-    memcpy(mBlob->value, value, valueLength);
-
-    mBlob->info = infoLength;
-    memcpy(mBlob->value + valueLength, info, infoLength);
-
-    mBlob->version = CURRENT_BLOB_VERSION;
-    mBlob->type = uint8_t(type);
-
-    if (type == TYPE_MASTER_KEY || type == TYPE_MASTER_KEY_AES256) {
-        mBlob->flags = KEYSTORE_FLAG_ENCRYPTED;
-    } else {
-        mBlob->flags = KEYSTORE_FLAG_NONE;
-    }
-}
-
-Blob::Blob(blobv3 b) {
-    mBlob = std::make_unique<blobv3>(b);
-}
-
-Blob::Blob() {
-    if (mBlob) *mBlob = {};
-}
-
-Blob::Blob(const Blob& rhs) {
-    if (rhs.mBlob) {
-        mBlob = std::make_unique<blobv3>(*rhs.mBlob);
-    }
-}
-
-Blob::Blob(Blob&& rhs) : mBlob(std::move(rhs.mBlob)) {}
-
-Blob& Blob::operator=(const Blob& rhs) {
-    if (&rhs != this) {
-        if (mBlob) *mBlob = {};
-        if (rhs) {
-            mBlob = std::make_unique<blobv3>(*rhs.mBlob);
-        } else {
-            mBlob = {};
-        }
-    }
-    return *this;
-}
-
-Blob& Blob::operator=(Blob&& rhs) {
-    if (mBlob) *mBlob = {};
-    mBlob = std::move(rhs.mBlob);
-    return *this;
-}
-
-template <typename BlobType> static bool rawBlobIsEncrypted(const BlobType& blob) {
-    if (blob.version < 2) return true;
-
-    return blob.flags & (KEYSTORE_FLAG_ENCRYPTED | KEYSTORE_FLAG_SUPER_ENCRYPTED);
-}
-
-bool Blob::isEncrypted() const {
-    if (mBlob->version < 2) {
-        return true;
-    }
-
-    return mBlob->flags & KEYSTORE_FLAG_ENCRYPTED;
-}
-
-bool Blob::isSuperEncrypted() const {
-    return mBlob->flags & KEYSTORE_FLAG_SUPER_ENCRYPTED;
-}
-
-bool Blob::isCriticalToDeviceEncryption() const {
-    return mBlob->flags & KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
-}
-
-inline uint8_t setFlag(uint8_t flags, bool set, KeyStoreFlag flag) {
-    return set ? (flags | flag) : (flags & ~flag);
-}
-
-void Blob::setEncrypted(bool encrypted) {
-    mBlob->flags = setFlag(mBlob->flags, encrypted, KEYSTORE_FLAG_ENCRYPTED);
-}
-
-void Blob::setSuperEncrypted(bool superEncrypted) {
-    mBlob->flags = setFlag(mBlob->flags, superEncrypted, KEYSTORE_FLAG_SUPER_ENCRYPTED);
-}
-
-void Blob::setCriticalToDeviceEncryption(bool critical) {
-    mBlob->flags = setFlag(mBlob->flags, critical, KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION);
-}
-
-void Blob::setFallback(bool fallback) {
-    if (fallback) {
-        mBlob->flags |= KEYSTORE_FLAG_FALLBACK;
-    } else {
-        mBlob->flags &= ~KEYSTORE_FLAG_FALLBACK;
-    }
-}
-
-static ResponseCode writeBlob(const std::string& filename, Blob blob, blobv3* rawBlob,
-                              const std::vector<uint8_t>& aes_key, State state) {
-    ALOGV("writing blob %s", filename.c_str());
-
-    const size_t dataLength = rawBlob->length;
-    rawBlob->length = htonl(rawBlob->length);
-
-    if (blob.isEncrypted() || blob.isSuperEncrypted()) {
-        if (state != STATE_NO_ERROR) {
-            ALOGD("couldn't insert encrypted blob while not unlocked");
-            return ResponseCode::LOCKED;
-        }
-
-        memset(rawBlob->initialization_vector, 0, AES_BLOCK_SIZE);
-        if (!RAND_bytes(rawBlob->initialization_vector, kGcmIvSizeBytes)) {
-            ALOGW("Could not read random data for: %s", filename.c_str());
-            return ResponseCode::SYSTEM_ERROR;
-        }
-
-        auto rc = AES_gcm_encrypt(rawBlob->value /* in */, rawBlob->value /* out */, dataLength,
-                                  aes_key, rawBlob->initialization_vector, rawBlob->aead_tag);
-        if (rc != ResponseCode::NO_ERROR) return rc;
-    }
-
-    size_t fileLength = offsetof(blobv3, value) + dataLength + rawBlob->info;
-
-    char tmpFileName[] = ".tmpXXXXXX";
-    {
-        android::base::unique_fd out(TEMP_FAILURE_RETRY(mkstemp(tmpFileName)));
-        if (out < 0) {
-            LOG(ERROR) << "could not open temp file: " << tmpFileName
-                       << " for writing blob file: " << filename.c_str()
-                       << " because: " << strerror(errno);
-            return ResponseCode::SYSTEM_ERROR;
-        }
-
-        const size_t writtenBytes =
-            writeFully(out, reinterpret_cast<uint8_t*>(rawBlob), fileLength);
-
-        if (writtenBytes != fileLength) {
-            LOG(ERROR) << "blob not fully written " << writtenBytes << " != " << fileLength;
-            unlink(tmpFileName);
-            return ResponseCode::SYSTEM_ERROR;
-        }
-    }
-
-    if (rename(tmpFileName, filename.c_str()) == -1) {
-        LOG(ERROR) << "could not rename blob file to " << filename
-                   << " because: " << strerror(errno);
-        unlink(tmpFileName);
-        return ResponseCode::SYSTEM_ERROR;
-    }
-
-    fsyncDirectory(getContainingDirectory(filename));
-
-    return ResponseCode::NO_ERROR;
-}
-
-ResponseCode LockedKeyBlobEntry::writeBlobs(Blob keyBlob, Blob characteristicsBlob,
-                                            const std::vector<uint8_t>& aes_key,
-                                            State state) const {
-    if (entry_ == nullptr) {
-        return ResponseCode::SYSTEM_ERROR;
-    }
-    ResponseCode rc;
-    if (keyBlob) {
-        blobv3* rawBlob = keyBlob.mBlob.get();
-        rc = writeBlob(entry_->getKeyBlobPath(), std::move(keyBlob), rawBlob, aes_key, state);
-        if (rc != ResponseCode::NO_ERROR) {
-            return rc;
-        }
-    }
-
-    if (characteristicsBlob) {
-        blobv3* rawBlob = characteristicsBlob.mBlob.get();
-        rc = writeBlob(entry_->getCharacteristicsBlobPath(), std::move(characteristicsBlob),
-                       rawBlob, aes_key, state);
-    }
-    return rc;
-}
-
-ResponseCode Blob::readBlob(const std::string& filename, const std::vector<uint8_t>& aes_key,
-                            State state) {
-    ResponseCode rc;
-    ALOGV("reading blob %s", filename.c_str());
-    std::unique_ptr<blobv3> rawBlob = std::make_unique<blobv3>();
-
-    const int in = TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY));
-    if (in < 0) {
-        return (errno == ENOENT) ? ResponseCode::KEY_NOT_FOUND : ResponseCode::SYSTEM_ERROR;
-    }
-
-    // fileLength may be less than sizeof(mBlob)
-    const size_t fileLength = readFully(in, (uint8_t*)rawBlob.get(), sizeof(blobv3));
-    if (close(in) != 0) {
-        return ResponseCode::SYSTEM_ERROR;
-    }
-
-    if (fileLength == 0) {
-        LOG(ERROR) << __func__ << " VALUE_CORRUPTED file length == 0";
-        return ResponseCode::VALUE_CORRUPTED;
-    }
-
-    if (rawBlobIsEncrypted(*rawBlob)) {
-        if (state == STATE_LOCKED) {
-            mBlob = std::move(rawBlob);
-            return ResponseCode::LOCKED;
-        }
-        if (state == STATE_UNINITIALIZED) return ResponseCode::UNINITIALIZED;
-    }
-
-    if (fileLength < offsetof(blobv3, value)) {
-        LOG(ERROR) << __func__ << " VALUE_CORRUPTED blob file too short: " << fileLength;
-        return ResponseCode::VALUE_CORRUPTED;
-    }
-
-    if (rawBlob->version == 3) {
-        const ssize_t encryptedLength = ntohl(rawBlob->length);
-
-        if (rawBlobIsEncrypted(*rawBlob)) {
-            rc = AES_gcm_decrypt(rawBlob->value /* in */, rawBlob->value /* out */, encryptedLength,
-                                 aes_key, rawBlob->initialization_vector, rawBlob->aead_tag);
-            if (rc != ResponseCode::NO_ERROR) {
-                // If the blob was superencrypted and decryption failed, it is
-                // almost certain that decryption is failing due to a user's
-                // changed master key.
-                if ((rawBlob->flags & KEYSTORE_FLAG_SUPER_ENCRYPTED) &&
-                    (rc == ResponseCode::VALUE_CORRUPTED)) {
-                    return ResponseCode::KEY_PERMANENTLY_INVALIDATED;
-                }
-                LOG(ERROR) << __func__ << " AES_gcm_decrypt returned: " << uint32_t(rc);
-
-                return rc;
-            }
-        }
-    } else if (rawBlob->version < 3) {
-        blobv2& v2blob = reinterpret_cast<blobv2&>(*rawBlob);
-        const size_t headerLength = offsetof(blobv2, encrypted);
-        const ssize_t encryptedLength = fileLength - headerLength - v2blob.info;
-        if (encryptedLength < 0) {
-            LOG(ERROR) << __func__ << " VALUE_CORRUPTED v2blob file too short";
-            return ResponseCode::VALUE_CORRUPTED;
-        }
-
-        if (rawBlobIsEncrypted(*rawBlob)) {
-            if (encryptedLength % AES_BLOCK_SIZE != 0) {
-                LOG(ERROR) << __func__
-                           << " VALUE_CORRUPTED encrypted length is not a multiple"
-                              " of the AES block size";
-                return ResponseCode::VALUE_CORRUPTED;
-            }
-
-            AES_KEY key;
-            AES_set_decrypt_key(aes_key.data(), kAesKeySize * 8, &key);
-            AES_cbc_encrypt(v2blob.encrypted, v2blob.encrypted, encryptedLength, &key,
-                            v2blob.vector, AES_DECRYPT);
-            key = {};  // clear key
-
-            uint8_t computedDigest[MD5_DIGEST_LENGTH];
-            ssize_t digestedLength = encryptedLength - MD5_DIGEST_LENGTH;
-            MD5(v2blob.digested, digestedLength, computedDigest);
-            if (memcmp(v2blob.digest, computedDigest, MD5_DIGEST_LENGTH) != 0) {
-                LOG(ERROR) << __func__ << " v2blob MD5 digest mismatch";
-                return ResponseCode::VALUE_CORRUPTED;
-            }
-        }
-    }
-
-    const ssize_t maxValueLength = fileLength - offsetof(blobv3, value) - rawBlob->info;
-    rawBlob->length = ntohl(rawBlob->length);
-    if (rawBlob->length < 0 || rawBlob->length > maxValueLength ||
-        rawBlob->length + rawBlob->info + AES_BLOCK_SIZE >
-            static_cast<ssize_t>(sizeof(rawBlob->value))) {
-        LOG(ERROR) << __func__ << " raw blob length is out of bounds";
-        return ResponseCode::VALUE_CORRUPTED;
-    }
-
-    if (rawBlob->info != 0 && rawBlob->version < 3) {
-        // move info from after padding to after data
-        memmove(rawBlob->value + rawBlob->length, rawBlob->value + maxValueLength, rawBlob->info);
-    }
-
-    mBlob = std::move(rawBlob);
-    return ResponseCode::NO_ERROR;
-}
-
-std::tuple<ResponseCode, Blob, Blob>
-LockedKeyBlobEntry::readBlobs(const std::vector<uint8_t>& aes_key, State state) const {
-    std::tuple<ResponseCode, Blob, Blob> result;
-    auto& [rc, keyBlob, characteristicsBlob] = result;
-    if (entry_ == nullptr) return rc = ResponseCode::SYSTEM_ERROR, result;
-
-    rc = keyBlob.readBlob(entry_->getKeyBlobPath(), aes_key, state);
-    if (rc != ResponseCode::NO_ERROR && rc != ResponseCode::UNINITIALIZED) {
-        return result;
-    }
-
-    if (entry_->hasCharacteristicsBlob()) {
-        characteristicsBlob.readBlob(entry_->getCharacteristicsBlobPath(), aes_key, state);
-    }
-    return result;
-}
-
-ResponseCode LockedKeyBlobEntry::deleteBlobs() const {
-    if (entry_ == nullptr) return ResponseCode::NO_ERROR;
-
-    // always try to delete both
-    ResponseCode rc1 = (unlink(entry_->getKeyBlobPath().c_str()) && errno != ENOENT)
-                           ? ResponseCode::SYSTEM_ERROR
-                           : ResponseCode::NO_ERROR;
-    if (rc1 != ResponseCode::NO_ERROR) {
-        ALOGW("Failed to delete key blob file \"%s\"", entry_->getKeyBlobPath().c_str());
-    }
-    ResponseCode rc2 = (unlink(entry_->getCharacteristicsBlobPath().c_str()) && errno != ENOENT)
-                           ? ResponseCode::SYSTEM_ERROR
-                           : ResponseCode::NO_ERROR;
-    if (rc2 != ResponseCode::NO_ERROR) {
-        ALOGW("Failed to delete key characteristics file \"%s\"",
-              entry_->getCharacteristicsBlobPath().c_str());
-    }
-    // then report the first error that occured
-    if (rc1 != ResponseCode::NO_ERROR) return rc1;
-    return rc2;
-}
-
-keystore::SecurityLevel Blob::getSecurityLevel() const {
-    return keystore::flagsToSecurityLevel(mBlob->flags);
-}
-
-void Blob::setSecurityLevel(keystore::SecurityLevel secLevel) {
-    mBlob->flags &= ~(KEYSTORE_FLAG_FALLBACK | KEYSTORE_FLAG_STRONGBOX);
-    mBlob->flags |= keystore::securityLevelToFlags(secLevel);
-}
-
-std::tuple<bool, keystore::AuthorizationSet, keystore::AuthorizationSet>
-Blob::getKeyCharacteristics() const {
-    std::tuple<bool, keystore::AuthorizationSet, keystore::AuthorizationSet> result;
-    auto& [success, hwEnforced, swEnforced] = result;
-    success = false;
-    ArrayStreamBuffer buf(mBlob->value);
-    std::istream in(&buf);
-
-    // only the characteristics cache has both sets
-    if (getType() == TYPE_KEY_CHARACTERISTICS_CACHE) {
-        hwEnforced.Deserialize(&in);
-    } else if (getType() != TYPE_KEY_CHARACTERISTICS) {
-        // if its not the cache and not the legacy characteristics file we have no business
-        // here
-        return result;
-    }
-    swEnforced.Deserialize(&in);
-    success = !in.bad();
-
-    return result;
-}
-bool Blob::putKeyCharacteristics(const keystore::AuthorizationSet& hwEnforced,
-                                 const keystore::AuthorizationSet& swEnforced) {
-    if (!mBlob) mBlob = std::make_unique<blobv3>();
-    mBlob->version = CURRENT_BLOB_VERSION;
-    ArrayStreamBuffer buf(mBlob->value);
-    std::ostream out(&buf);
-    hwEnforced.Serialize(&out);
-    swEnforced.Serialize(&out);
-    if (out.bad()) return false;
-    setType(TYPE_KEY_CHARACTERISTICS_CACHE);
-    mBlob->length = out.tellp();
-    return true;
-}
-
-void LockedKeyBlobEntry::put(const KeyBlobEntry& entry) {
-    std::unique_lock<std::mutex> lock(locked_blobs_mutex_);
-    locked_blobs_.erase(entry);
-    lock.unlock();
-    locked_blobs_mutex_cond_var_.notify_all();
-}
-
-LockedKeyBlobEntry::~LockedKeyBlobEntry() {
-    if (entry_ != nullptr) put(*entry_);
-}
-
-LockedKeyBlobEntry LockedKeyBlobEntry::get(KeyBlobEntry entry) {
-    std::unique_lock<std::mutex> lock(locked_blobs_mutex_);
-    locked_blobs_mutex_cond_var_.wait(
-        lock, [&] { return locked_blobs_.find(entry) == locked_blobs_.end(); });
-    auto [iterator, success] = locked_blobs_.insert(std::move(entry));
-    if (!success) return {};
-    return LockedKeyBlobEntry(*iterator);
-}
-
-std::set<KeyBlobEntry> LockedKeyBlobEntry::locked_blobs_;
-std::mutex LockedKeyBlobEntry::locked_blobs_mutex_;
-std::condition_variable LockedKeyBlobEntry::locked_blobs_mutex_cond_var_;
-
-/* Here is the encoding of key names. This is necessary in order to allow arbitrary
- * characters in key names. Characters in [0-~] are not encoded. Others are encoded
- * into two bytes. The first byte is one of [+-.] which represents the first
- * two bits of the character. The second byte encodes the rest of the bits into
- * [0-o]. Therefore in the worst case the length of a key gets doubled. Note
- * that Base64 cannot be used here due to the need of prefix match on keys. */
-
-std::string encodeKeyName(const std::string& keyName) {
-    std::string encodedName;
-    encodedName.reserve(keyName.size() * 2);
-    auto in = keyName.begin();
-    while (in != keyName.end()) {
-        // Input character needs to be encoded.
-        if (*in < '0' || *in > '~') {
-            // Encode the two most-significant bits of the input char in the first
-            // output character, by counting up from 43 ('+').
-            encodedName.append(1, '+' + (uint8_t(*in) >> 6));
-            // Encode the six least-significant bits of the input char in the second
-            // output character, by counting up from 48 ('0').
-            // This is safe because the maximum value is 112, which is the
-            // character 'p'.
-            encodedName.append(1, '0' + (*in & 0x3F));
-        } else {
-            // No need to encode input char - append as-is.
-            encodedName.append(1, *in);
-        }
-        ++in;
-    }
-    return encodedName;
-}
-
-std::string decodeKeyName(const std::string& encodedName) {
-    std::string decodedName;
-    decodedName.reserve(encodedName.size());
-    auto in = encodedName.begin();
-    bool multichar = false;
-    char c;
-    while (in != encodedName.end()) {
-        if (multichar) {
-            // Second part of a multi-character encoding. Turn off the multichar
-            // flag and set the six least-significant bits of c to the value originally
-            // encoded by counting up from '0'.
-            multichar = false;
-            decodedName.append(1, c | (uint8_t(*in) - '0'));
-        } else if (*in >= '+' && *in <= '.') {
-            // First part of a multi-character encoding. Set the multichar flag
-            // and set the two most-significant bits of c to be the two bits originally
-            // encoded by counting up from '+'.
-            multichar = true;
-            c = (*in - '+') << 6;
-        } else {
-            // Regular character, append as-is.
-            decodedName.append(1, *in);
-        }
-        ++in;
-    }
-    // mulitchars at the end get truncated
-    return decodedName;
-}
-
-std::string KeyBlobEntry::getKeyBlobBaseName() const {
-    std::stringstream s;
-    if (masterkey_) {
-        s << alias_;
-    } else {
-        s << uid_ << "_" << encodeKeyName(alias_);
-    }
-    return s.str();
-}
-
-std::string KeyBlobEntry::getKeyBlobPath() const {
-    std::stringstream s;
-    if (masterkey_) {
-        s << user_dir_ << "/" << alias_;
-    } else {
-        s << user_dir_ << "/" << uid_ << "_" << encodeKeyName(alias_);
-    }
-    return s.str();
-}
-
-std::string KeyBlobEntry::getCharacteristicsBlobBaseName() const {
-    std::stringstream s;
-    if (!masterkey_) s << "." << uid_ << "_chr_" << encodeKeyName(alias_);
-    return s.str();
-}
-
-std::string KeyBlobEntry::getCharacteristicsBlobPath() const {
-    std::stringstream s;
-    if (!masterkey_)
-        s << user_dir_ << "/"
-          << "." << uid_ << "_chr_" << encodeKeyName(alias_);
-    return s.str();
-}
-
-bool KeyBlobEntry::hasKeyBlob() const {
-    int trys = 3;
-    while (trys--) {
-        if (!access(getKeyBlobPath().c_str(), R_OK | W_OK)) return true;
-        if (errno == ENOENT) return false;
-        LOG(WARNING) << "access encountered " << strerror(errno) << " (" << errno << ")"
-                     << " while checking for key blob";
-        if (errno != EAGAIN) break;
-    }
-    return false;
-}
-
-bool KeyBlobEntry::hasCharacteristicsBlob() const {
-    int trys = 3;
-    while (trys--) {
-        if (!access(getCharacteristicsBlobPath().c_str(), R_OK | W_OK)) return true;
-        if (errno == ENOENT) return false;
-        LOG(WARNING) << "access encountered " << strerror(errno) << " (" << errno << ")"
-                     << " while checking for key characteristics blob";
-        if (errno != EAGAIN) break;
-    }
-    return false;
-}
-
-static std::tuple<bool, uid_t, std::string> filename2UidAlias(const std::string& filepath) {
-    std::tuple<bool, uid_t, std::string> result;
-
-    auto& [success, uid, alias] = result;
-
-    success = false;
-
-    auto filenamebase = filepath.find_last_of('/');
-    std::string filename =
-        filenamebase == std::string::npos ? filepath : filepath.substr(filenamebase + 1);
-
-    if (filename[0] == '.') return result;
-
-    auto sep = filename.find('_');
-    if (sep == std::string::npos) return result;
-
-    std::stringstream s(filename.substr(0, sep));
-    s >> uid;
-    if (!s) return result;
-
-    alias = decodeKeyName(filename.substr(sep + 1));
-    success = true;
-    return result;
-}
-
-std::tuple<ResponseCode, std::list<LockedKeyBlobEntry>>
-LockedKeyBlobEntry::list(const std::string& user_dir,
-                         std::function<bool(uid_t, const std::string&)> filter) {
-    std::list<LockedKeyBlobEntry> matches;
-
-    // This is a fence against any concurrent database accesses during database iteration.
-    // Only the keystore thread can lock entries. So it cannot be starved
-    // by workers grabbing new individual locks. We just wait here until all
-    // workers have relinquished their locked files.
-    std::unique_lock<std::mutex> lock(locked_blobs_mutex_);
-    locked_blobs_mutex_cond_var_.wait(lock, [&] { return locked_blobs_.empty(); });
-
-    DIR* dir = opendir(user_dir.c_str());
-    if (!dir) {
-        ALOGW("can't open directory for user: %s", strerror(errno));
-        return std::tuple<ResponseCode, std::list<LockedKeyBlobEntry>&&>{ResponseCode::SYSTEM_ERROR,
-                                                                         std::move(matches)};
-    }
-
-    struct dirent* file;
-    while ((file = readdir(dir)) != nullptr) {
-        // We only care about files.
-        if (file->d_type != DT_REG) {
-            continue;
-        }
-
-        // Skip anything that starts with a "."
-        if (file->d_name[0] == '.') {
-            continue;
-        }
-
-        auto [success, uid, alias] = filename2UidAlias(file->d_name);
-
-        if (!success) {
-            ALOGW("could not parse key filename \"%s\"", file->d_name);
-            continue;
-        }
-
-        if (!filter(uid, alias)) continue;
-
-        auto [iterator, dummy] = locked_blobs_.emplace(alias, user_dir, uid);
-        matches.push_back(*iterator);
-    }
-    closedir(dir);
-    return std::tuple<ResponseCode, std::list<LockedKeyBlobEntry>&&>{ResponseCode::NO_ERROR,
-                                                                     std::move(matches)};
-}
diff --git a/keystore/blob.h b/keystore/blob.h
deleted file mode 100644
index e0bd146..0000000
--- a/keystore/blob.h
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright (C) 2015 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 KEYSTORE_BLOB_H_
-#define KEYSTORE_BLOB_H_
-
-#include <stdint.h>
-
-#include <openssl/aes.h>
-#include <openssl/md5.h>
-
-#include <condition_variable>
-#include <functional>
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore.h>
-#include <list>
-#include <mutex>
-#include <set>
-#include <sstream>
-#include <vector>
-
-constexpr size_t kValueSize = 32768;
-constexpr size_t kAesKeySize = 128 / 8;
-constexpr size_t kGcmTagLength = 128 / 8;
-constexpr size_t kGcmIvLength = 96 / 8;
-constexpr size_t kAes128KeySizeBytes = 128 / 8;
-constexpr size_t kAes256KeySizeBytes = 256 / 8;
-
-/* Here is the file format. There are two parts in blob.value, the secret and
- * the description. The secret is stored in ciphertext, and its original size
- * can be found in blob.length. The description is stored after the secret in
- * plaintext, and its size is specified in blob.info. The total size of the two
- * parts must be no more than kValueSize bytes. The first field is the version,
- * the second is the blob's type, and the third byte is flags. Fields other
- * than blob.info, blob.length, and blob.value are modified by encryptBlob()
- * and decryptBlob(). Thus they should not be accessed from outside. */
-
-struct __attribute__((packed)) blobv3 {
-    uint8_t version;
-    uint8_t type;
-    uint8_t flags;
-    uint8_t info;
-    uint8_t initialization_vector[AES_BLOCK_SIZE];  // Only 96 bits is used, rest is zeroed.
-    uint8_t aead_tag[kGcmTagLength];
-    int32_t length;  // in network byte order, only for backward compatibility
-    uint8_t value[kValueSize + AES_BLOCK_SIZE];
-};
-
-struct __attribute__((packed)) blobv2 {
-    uint8_t version;
-    uint8_t type;
-    uint8_t flags;
-    uint8_t info;
-    uint8_t vector[AES_BLOCK_SIZE];
-    uint8_t encrypted[0];  // Marks offset to encrypted data.
-    uint8_t digest[MD5_DIGEST_LENGTH];
-    uint8_t digested[0];  // Marks offset to digested data.
-    int32_t length;       // in network byte order
-    uint8_t value[kValueSize + AES_BLOCK_SIZE];
-};
-
-static_assert(sizeof(blobv3) == sizeof(blobv2) &&
-                  offsetof(blobv3, initialization_vector) == offsetof(blobv2, vector) &&
-                  offsetof(blobv3, aead_tag) == offsetof(blobv2, digest) &&
-                  offsetof(blobv3, aead_tag) == offsetof(blobv2, encrypted) &&
-                  offsetof(blobv3, length) == offsetof(blobv2, length) &&
-                  offsetof(blobv3, value) == offsetof(blobv2, value),
-              "Oops.  Blob layout changed.");
-
-static const uint8_t CURRENT_BLOB_VERSION = 3;
-
-typedef enum {
-    TYPE_ANY = 0,  // meta type that matches anything
-    TYPE_GENERIC = 1,
-    TYPE_MASTER_KEY = 2,
-    TYPE_KEY_PAIR = 3,
-    TYPE_KEYMASTER_10 = 4,
-    TYPE_KEY_CHARACTERISTICS = 5,
-    TYPE_KEY_CHARACTERISTICS_CACHE = 6,
-    TYPE_MASTER_KEY_AES256 = 7,
-} BlobType;
-
-class LockedKeyBlobEntry;
-
-/**
- * The Blob represents the content of a KeyBlobEntry.
- *
- * BEWARE: It is only save to call any member function of a Blob b if bool(b) yields true.
- *         Exceptions are putKeyCharacteristics(), the assignment operators and operator bool.
- */
-class Blob {
-    friend LockedKeyBlobEntry;
-
-  public:
-    Blob(const uint8_t* value, size_t valueLength, const uint8_t* info, uint8_t infoLength,
-         BlobType type);
-    explicit Blob(blobv3 b);
-    Blob();
-    Blob(const Blob& rhs);
-    Blob(Blob&& rhs);
-
-    ~Blob() {
-        if (mBlob) *mBlob = {};
-    }
-
-    Blob& operator=(const Blob& rhs);
-    Blob& operator=(Blob&& rhs);
-    explicit operator bool() const { return bool(mBlob); }
-
-    const uint8_t* getValue() const { return mBlob->value; }
-
-    int32_t getLength() const { return mBlob->length; }
-
-    const uint8_t* getInfo() const { return mBlob->value + mBlob->length; }
-    uint8_t getInfoLength() const { return mBlob->info; }
-
-    uint8_t getVersion() const { return mBlob->version; }
-
-    bool isEncrypted() const;
-    void setEncrypted(bool encrypted);
-
-    bool isSuperEncrypted() const;
-    void setSuperEncrypted(bool superEncrypted);
-
-    bool isCriticalToDeviceEncryption() const;
-    void setCriticalToDeviceEncryption(bool critical);
-
-    bool isFallback() const { return mBlob->flags & KEYSTORE_FLAG_FALLBACK; }
-    void setFallback(bool fallback);
-
-    void setVersion(uint8_t version) { mBlob->version = version; }
-    BlobType getType() const { return BlobType(mBlob->type); }
-    void setType(BlobType type) { mBlob->type = uint8_t(type); }
-
-    keystore::SecurityLevel getSecurityLevel() const;
-    void setSecurityLevel(keystore::SecurityLevel);
-
-    std::tuple<bool, keystore::AuthorizationSet, keystore::AuthorizationSet>
-    getKeyCharacteristics() const;
-
-    bool putKeyCharacteristics(const keystore::AuthorizationSet& hwEnforced,
-                               const keystore::AuthorizationSet& swEnforced);
-
-  private:
-    std::unique_ptr<blobv3> mBlob;
-
-    ResponseCode readBlob(const std::string& filename, const std::vector<uint8_t>& aes_key,
-                          State state);
-};
-
-/**
- * A KeyBlobEntry represents a full qualified key blob as known by Keystore. The key blob
- * is given by the uid of the owning app and the alias used by the app to refer to this key.
- * The user_dir_ is technically implied by the uid, but computation of the user directory is
- * done in the user state database. Which is why we also cache it here.
- *
- * The KeyBlobEntry knows the location of the key blob files (which may include a characteristics
- * cache file) but does not allow read or write access to the content. It also does not imply
- * the existence of the files.
- *
- * KeyBlobEntry abstracts, to some extent, from the the file system based storage of key blobs.
- * An evolution of KeyBlobEntry may be used for key blob storage based on a back end other than
- * file system, e.g., SQL database or other.
- *
- * For access to the key blob content the programmer has to acquire a LockedKeyBlobEntry (see
- * below).
- */
-class KeyBlobEntry {
-  private:
-    std::string alias_;
-    std::string user_dir_;
-    uid_t uid_;
-    bool masterkey_;
-
-  public:
-    KeyBlobEntry(std::string alias, std::string user_dir, uid_t uid, bool masterkey = false)
-        : alias_(std::move(alias)), user_dir_(std::move(user_dir)), uid_(uid),
-          masterkey_(masterkey) {}
-
-    std::string getKeyBlobBaseName() const;
-    std::string getKeyBlobPath() const;
-
-    std::string getCharacteristicsBlobBaseName() const;
-    std::string getCharacteristicsBlobPath() const;
-
-    bool hasKeyBlob() const;
-    bool hasCharacteristicsBlob() const;
-
-    bool operator<(const KeyBlobEntry& rhs) const {
-        return std::tie(uid_, alias_, user_dir_) < std::tie(rhs.uid_, rhs.alias_, rhs.user_dir_);
-    }
-    bool operator==(const KeyBlobEntry& rhs) const {
-        return std::tie(uid_, alias_, user_dir_) == std::tie(rhs.uid_, rhs.alias_, rhs.user_dir_);
-    }
-    bool operator!=(const KeyBlobEntry& rhs) const { return !(*this == rhs); }
-
-    inline const std::string& alias() const { return alias_; }
-    inline const std::string& user_dir() const { return user_dir_; }
-    inline uid_t uid() const { return uid_; }
-};
-
-/**
- * The LockedKeyBlobEntry is a proxy object to KeyBlobEntry that expresses exclusive ownership
- * of a KeyBlobEntry. LockedKeyBlobEntries can be acquired by calling
- * LockedKeyBlobEntry::get() or LockedKeyBlobEntry::list().
- *
- * LockedKeyBlobEntries are movable but not copyable. By convention they can only
- * be taken by the dispatcher thread of keystore, but not by any keymaster worker thread.
- * The dispatcher thread may transfer ownership of a locked entry to a keymaster worker thread.
- *
- * Locked entries are tracked on the stack or as members of movable functor objects passed to the
- * keymaster worker request queues. Locks are relinquished as the locked entry gets destroyed, e.g.,
- * when it goes out of scope or when the owning request functor gets destroyed.
- *
- * LockedKeyBlobEntry::list(), which must only be called by the dispatcher, blocks until all
- * LockedKeyBlobEntries have been destroyed. Thereby list acts as a fence to make sure it gets a
- * consistent view of the key blob database. Under the assumption that keymaster worker requests
- * cannot run or block indefinitely and cannot grab new locked entries, progress is guaranteed.
- * It then grabs locked entries in accordance with the given filter rule.
- *
- * LockedKeyBlobEntry allow access to the proxied KeyBlobEntry interface through the operator->.
- * They add additional functionality to access and modify the key blob's content on disk.
- * LockedKeyBlobEntry ensures atomic operations on the persistently stored key blobs on a per
- * entry granularity.
- */
-class LockedKeyBlobEntry {
-  private:
-    static std::set<KeyBlobEntry> locked_blobs_;
-    static std::mutex locked_blobs_mutex_;
-    static std::condition_variable locked_blobs_mutex_cond_var_;
-
-    const KeyBlobEntry* entry_;
-    // NOLINTNEXTLINE(google-explicit-constructor)
-    LockedKeyBlobEntry(const KeyBlobEntry& entry) : entry_(&entry) {}
-
-    static void put(const KeyBlobEntry& entry);
-    LockedKeyBlobEntry(const LockedKeyBlobEntry&) = delete;
-    LockedKeyBlobEntry& operator=(const LockedKeyBlobEntry&) = delete;
-
-  public:
-    LockedKeyBlobEntry() : entry_(nullptr){};
-    ~LockedKeyBlobEntry();
-    LockedKeyBlobEntry(LockedKeyBlobEntry&& rhs) : entry_(rhs.entry_) { rhs.entry_ = nullptr; }
-    LockedKeyBlobEntry& operator=(LockedKeyBlobEntry&& rhs) {
-        // as dummy goes out of scope it relinquishes the lock on this
-        LockedKeyBlobEntry dummy(std::move(*this));
-        entry_ = rhs.entry_;
-        rhs.entry_ = nullptr;
-        return *this;
-    }
-    static LockedKeyBlobEntry get(KeyBlobEntry entry);
-    static std::tuple<ResponseCode, std::list<LockedKeyBlobEntry>>
-    list(const std::string& user_dir,
-         std::function<bool(uid_t, const std::string&)> filter =
-             [](uid_t, const std::string&) -> bool { return true; });
-
-    ResponseCode writeBlobs(Blob keyBlob, Blob characteristicsBlob,
-                            const std::vector<uint8_t>& aes_key, State state) const;
-    std::tuple<ResponseCode, Blob, Blob> readBlobs(const std::vector<uint8_t>& aes_key,
-                                                   State state) const;
-    ResponseCode deleteBlobs() const;
-
-    inline explicit operator bool() const { return entry_ != nullptr; }
-    inline const KeyBlobEntry& operator*() const { return *entry_; }
-    inline const KeyBlobEntry* operator->() const { return entry_; }
-};
-
-// Visible for testing
-std::string encodeKeyName(const std::string& keyName);
-std::string decodeKeyName(const std::string& encodedName);
-
-#endif  // KEYSTORE_BLOB_H_
diff --git a/keystore/confirmation_manager.cpp b/keystore/confirmation_manager.cpp
deleted file mode 100644
index 76df1cc..0000000
--- a/keystore/confirmation_manager.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#define LOG_TAG "ConfirmationManager"
-
-#include "confirmation_manager.h"
-
-#include <android/hardware/confirmationui/1.0/IConfirmationResultCallback.h>
-#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
-#include <android/hardware/confirmationui/1.0/types.h>
-#include <android/security/BpConfirmationPromptCallback.h>
-#include <binder/BpBinder.h>
-#include <binder/IPCThreadState.h>
-#include <binder/Parcel.h>
-
-#include "keystore_aidl_hidl_marshalling_utils.h"
-
-namespace keystore {
-
-using android::IBinder;
-using android::sp;
-using android::String16;
-using android::String8;
-using android::wp;
-using android::binder::Status;
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::confirmationui::V1_0::IConfirmationResultCallback;
-using android::hardware::confirmationui::V1_0::IConfirmationUI;
-using android::hardware::confirmationui::V1_0::UIOption;
-
-using android::security::BpConfirmationPromptCallback;
-using std::lock_guard;
-using std::mutex;
-using std::vector;
-
-ConfirmationManager::ConfirmationManager(IBinder::DeathRecipient* deathRecipient)
-    : IConfirmationResultCallback(), mDeathRecipient(deathRecipient) {}
-
-// Called by keystore main thread.
-Status ConfirmationManager::presentConfirmationPrompt(const sp<IBinder>& listener,
-                                                      const String16& promptText,
-                                                      const hidl_vec<uint8_t>& extraData,
-                                                      const String16& locale, int uiOptionsAsFlags,
-                                                      int32_t* aidl_return) {
-    lock_guard<mutex> lock(mMutex);
-
-    if (mCurrentListener != nullptr) {
-        *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OperationPending);
-        return Status::ok();
-    }
-
-    sp<IConfirmationUI> confirmationUI = IConfirmationUI::tryGetService();
-    if (confirmationUI == nullptr) {
-        ALOGW("Error getting confirmationUI service\n");
-        *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::Unimplemented);
-        return Status::ok();
-    }
-
-    uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
-    if (!mRateLimiting.tryPrompt(callingUid)) {
-        *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::SystemError);
-        return Status::ok();
-    }
-
-    String8 promptText8(promptText);
-    String8 locale8(locale);
-    vector<UIOption> uiOptionsVector;
-    for (int n = 0; n < 32; n++) {
-        if (uiOptionsAsFlags & (1 << n)) {
-            uiOptionsVector.push_back(UIOption(n));
-        }
-    }
-    ConfirmationResponseCode responseCode;
-    responseCode = confirmationUI->promptUserConfirmation(sp<IConfirmationResultCallback>(this),
-                                                          promptText8.string(), extraData,
-                                                          locale8.string(), uiOptionsVector);
-    if (responseCode != ConfirmationResponseCode::OK) {
-        ALOGW("Unexpecxted responseCode %d from promptUserConfirmation\n", responseCode);
-        *aidl_return = static_cast<int32_t>(responseCode);
-        return Status::ok();
-    }
-
-    listener->linkToDeath(mDeathRecipient);
-    confirmationUI->linkToDeath(this, 0);
-    mCurrentListener = listener;
-    mCurrentConfirmationUI = confirmationUI;
-
-    *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OK);
-    return Status::ok();
-}
-
-// Called by keystore main thread.
-Status ConfirmationManager::cancelConfirmationPrompt(const sp<IBinder>& listener,
-                                                     int32_t* aidl_return) {
-    mMutex.lock();
-    if (mCurrentListener != listener) {
-        // If the prompt was displayed by another application, return
-        // OperationPending.
-        mMutex.unlock();
-        *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OperationPending);
-        return Status::ok();
-    }
-    mMutex.unlock();
-
-    cancelPrompt();
-
-    *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OK);
-    return Status::ok();
-}
-
-void ConfirmationManager::cancelPrompt() {
-    mMutex.lock();
-    mRateLimiting.cancelPrompt();
-    sp<IConfirmationUI> confirmationUI = mCurrentConfirmationUI;
-    mMutex.unlock();
-    if (confirmationUI != nullptr) {
-        confirmationUI->abort();
-    }
-}
-
-// Called by keystore main thread.
-Status ConfirmationManager::isConfirmationPromptSupported(bool* aidl_return) {
-    sp<IConfirmationUI> confirmationUI = IConfirmationUI::tryGetService();
-    if (confirmationUI == nullptr) {
-        ALOGW("Error getting confirmationUI service\n");
-        *aidl_return = false;
-        return Status::ok();
-    }
-
-    *aidl_return = true;
-    return Status::ok();
-}
-
-void ConfirmationManager::finalizeTransaction(ConfirmationResponseCode responseCode,
-                                              hidl_vec<uint8_t> dataThatWasConfirmed) {
-    mMutex.lock();
-    mRateLimiting.processResult(responseCode);
-    sp<IBinder> listener = mCurrentListener;
-    if (mCurrentListener != nullptr) {
-        mCurrentListener->unlinkToDeath(mDeathRecipient);
-        mCurrentListener = nullptr;
-    }
-    if (mCurrentConfirmationUI != nullptr) {
-        mCurrentConfirmationUI->unlinkToDeath(this);
-        mCurrentConfirmationUI = nullptr;
-    }
-    mMutex.unlock();
-
-    // Deliver result to the application that started the operation.
-    if (listener != nullptr) {
-        sp<BpConfirmationPromptCallback> obj = new BpConfirmationPromptCallback(listener);
-        Status status = obj->onConfirmationPromptCompleted(static_cast<int32_t>(responseCode),
-                                                           dataThatWasConfirmed);
-        if (!status.isOk()) {
-            ALOGW("Error sending onConfirmationPromptCompleted - status: %s\n",
-                  status.toString8().c_str());
-        }
-    }
-}
-
-// Called by hwbinder thread (not keystore main thread).
-Return<void> ConfirmationManager::result(ConfirmationResponseCode responseCode,
-                                         const hidl_vec<uint8_t>& dataThatWasConfirmed,
-                                         const hidl_vec<uint8_t>& confirmationToken) {
-    finalizeTransaction(responseCode, dataThatWasConfirmed);
-    lock_guard<mutex> lock(mMutex);
-    mLatestConfirmationToken = confirmationToken;
-    return Return<void>();
-}
-
-// Called by keystore main thread or keymaster worker
-hidl_vec<uint8_t> ConfirmationManager::getLatestConfirmationToken() {
-    lock_guard<mutex> lock(mMutex);
-    return mLatestConfirmationToken;
-}
-
-void ConfirmationManager::binderDied(const wp<IBinder>& who) {
-    // This is also called for other binders so need to check it's for
-    // us before acting on it.
-    mMutex.lock();
-    if (who == mCurrentListener) {
-        // Clear this so we don't call back into the already dead
-        // binder in finalizeTransaction().
-        mCurrentListener->unlinkToDeath(mDeathRecipient);
-        mCurrentListener = nullptr;
-        mMutex.unlock();
-        ALOGW("The process which requested the confirmation dialog died.\n");
-        cancelPrompt();
-    } else {
-        mMutex.unlock();
-    }
-}
-
-void ConfirmationManager::serviceDied(uint64_t /* cookie */,
-                                      const wp<android::hidl::base::V1_0::IBase>& /* who */) {
-    ALOGW("The ConfirmationUI HAL died.\n");
-    finalizeTransaction(ConfirmationResponseCode::SystemError, {});
-}
-
-}  // namespace keystore
diff --git a/keystore/confirmation_manager.h b/keystore/confirmation_manager.h
deleted file mode 100644
index 7f0a11d..0000000
--- a/keystore/confirmation_manager.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2017 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 KEYSTORE_CONFIRMATION_MANAGER_H_
-#define KEYSTORE_CONFIRMATION_MANAGER_H_
-
-#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
-#include <android/hardware/confirmationui/1.0/types.h>
-#include <binder/Binder.h>
-#include <binder/IBinder.h>
-#include <binder/Status.h>
-#include <keystore/keymaster_types.h>
-#include <map>
-#include <mutex>
-#include <utils/LruCache.h>
-#include <utils/StrongPointer.h>
-#include <vector>
-
-#include "confirmationui_rate_limiting.h"
-
-namespace keystore {
-
-using android::binder::Status;
-using android::hardware::confirmationui::V1_0::IConfirmationResultCallback;
-using ConfirmationResponseCode = android::hardware::confirmationui::V1_0::ResponseCode;
-
-class ConfirmationManager;
-
-class ConfirmationManager : public android::hardware::hidl_death_recipient,
-                            public IConfirmationResultCallback {
-  public:
-    explicit ConfirmationManager(android::IBinder::DeathRecipient* deathRecipient);
-
-    // Calls into the confirmationui HAL to start a new prompt.
-    //
-    // Returns OperationPending if another application is already
-    // showing a confirmation. Otherwise returns the return code from
-    // the HAL.
-    Status presentConfirmationPrompt(const android::sp<android::IBinder>& listener,
-                                     const android::String16& promptText,
-                                     const hidl_vec<uint8_t>& extraData,
-                                     const android::String16& locale, int uiOptionsAsFlags,
-                                     int32_t* aidl_return);
-
-    // Calls into the confirmationui HAL to cancel displaying a
-    // prompt.
-    //
-    // Returns OperatingPending if another application is showing a
-    // confirmation. Otherwise returns the return code from the HAL.
-    Status cancelConfirmationPrompt(const android::sp<android::IBinder>& listener,
-                                    int32_t* aidl_return);
-
-    // Checks if the confirmationUI HAL is available.
-    Status isConfirmationPromptSupported(bool* aidl_return);
-
-    // Gets the latest confirmation token received from the ConfirmationUI HAL.
-    hidl_vec<uint8_t> getLatestConfirmationToken();
-
-    // Called by KeyStoreService when a client binder has died.
-    void binderDied(const android::wp<android::IBinder>& who);
-
-    // hidl_death_recipient overrides:
-    virtual void serviceDied(uint64_t cookie,
-                             const android::wp<android::hidl::base::V1_0::IBase>& who) override;
-
-    // IConfirmationResultCallback overrides:
-    android::hardware::Return<void> result(ConfirmationResponseCode responseCode,
-                                           const hidl_vec<uint8_t>& dataThatWasConfirmed,
-                                           const hidl_vec<uint8_t>& confirmationToken) override;
-
-  private:
-    friend class ConfirmationResultCallback;
-
-    // Set rate limiting to not decrement on next abort and aborts
-    // confirmationui.
-    void cancelPrompt();
-
-    void finalizeTransaction(ConfirmationResponseCode responseCode,
-                             hidl_vec<uint8_t> dataThatWasConfirmed);
-
-    // This mutex protects all data below it.
-    std::mutex mMutex;
-
-    // The mCurrentListener and mCurrentConfirmationUI fields are set
-    // if and only if a prompt is currently showing.
-    android::sp<android::IBinder> mCurrentListener;
-    android::sp<android::hardware::confirmationui::V1_0::IConfirmationUI> mCurrentConfirmationUI;
-    android::IBinder::DeathRecipient* mDeathRecipient;
-    hidl_vec<uint8_t> mLatestConfirmationToken;
-    RateLimiting<> mRateLimiting;
-};
-
-}  // namespace keystore
-
-#endif  // KEYSTORE_CONFIRMATION_MANAGER_H_
diff --git a/keystore/confirmationui_rate_limiting.h b/keystore/confirmationui_rate_limiting.h
deleted file mode 100644
index 658bf41..0000000
--- a/keystore/confirmationui_rate_limiting.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
-**
-** Copyright 2018, 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 KEYSTORE_CONFIRMATIONUI_RATE_LIMITING_H_
-#define KEYSTORE_CONFIRMATIONUI_RATE_LIMITING_H_
-
-#include <android/hardware/confirmationui/1.0/types.h>
-#include <chrono>
-#include <stdint.h>
-#include <sys/types.h>
-#include <tuple>
-#include <unordered_map>
-
-namespace keystore {
-
-using ConfirmationResponseCode = android::hardware::confirmationui::V1_0::ResponseCode;
-
-using std::chrono::duration;
-using std::chrono::time_point;
-
-template <typename Clock = std::chrono::steady_clock> class RateLimiting {
-  private:
-    struct Slot {
-        Slot() : previous_start{}, prompt_start{}, counter(0) {}
-        typename Clock::time_point previous_start;
-        typename Clock::time_point prompt_start;
-        uint32_t counter;
-    };
-
-    std::unordered_map<uid_t, Slot> slots_;
-
-    uint_t latest_requester_;
-
-    static std::chrono::seconds getBackoff(uint32_t counter) {
-        using namespace std::chrono_literals;
-        switch (counter) {
-        case 0:
-        case 1:
-        case 2:
-            return 0s;
-        case 3:
-        case 4:
-        case 5:
-            return 30s;
-        default:
-            return 60s * (1ULL << (counter - 6));
-        }
-    }
-
-  public:
-    // Exposes the number of used slots. This is only used by the test to verify the assumption
-    // about used counter slots.
-    size_t usedSlots() const { return slots_.size(); }
-    void doGC() {
-        using namespace std::chrono_literals;
-        using std::chrono::system_clock;
-        using std::chrono::time_point_cast;
-        auto then = Clock::now() - 24h;
-        auto iter = slots_.begin();
-        while (iter != slots_.end()) {
-            if (iter->second.prompt_start <= then) {
-                iter = slots_.erase(iter);
-            } else {
-                ++iter;
-            }
-        }
-    }
-
-    bool tryPrompt(uid_t id) {
-        using namespace std::chrono_literals;
-        // remove slots that have not been touched in 24 hours
-        doGC();
-        auto& slot = slots_[id];
-        auto now = Clock::now();
-        if (!slot.counter || slot.prompt_start <= now - getBackoff(slot.counter)) {
-            latest_requester_ = id;
-            slot.counter += 1;
-            slot.previous_start = slot.prompt_start;
-            slot.prompt_start = now;
-            return true;
-        }
-        return false;
-    }
-
-    // The app is penalized for cancelling a request. Request are rolled back only if
-    // the prompt was cancelled by the system: e.g. a system error or asynchronous event.
-    // When the user cancels the prompt, it is subject to rate limiting.
-    static constexpr const uint_t kInvalidRequester = -1;
-
-    void cancelPrompt() { latest_requester_ = kInvalidRequester; }
-
-    void processResult(ConfirmationResponseCode rc) {
-        if (latest_requester_ == kInvalidRequester) {
-            return;
-        }
-        switch (rc) {
-        case ConfirmationResponseCode::OK:
-            // reset the counter slot
-            slots_.erase(latest_requester_);
-            return;
-        case ConfirmationResponseCode::Canceled:
-            // nothing to do here
-            return;
-        default:;
-        }
-
-        // roll back latest request
-        auto& slot = slots_[latest_requester_];
-        if (slot.counter <= 1) {
-            slots_.erase(latest_requester_);
-            return;
-        }
-        slot.counter -= 1;
-        slot.prompt_start = slot.previous_start;
-    }
-};
-
-}  // namespace keystore
-
-#endif  // KEYSTORE_CONFIRMATIONUI_RATE_LIMITING_H_
diff --git a/keystore/defaults.h b/keystore/defaults.h
deleted file mode 100644
index 6f7ff2d..0000000
--- a/keystore/defaults.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2013 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 KEYSTORE_DEFAULTS_H_
-#define KEYSTORE_DEFAULTS_H_
-
-/*
- * These must be kept in sync with
- * frameworks/base/keystore/java/android/security/KeyPairGeneratorSpec.java
- */
-
-/* DSA */
-constexpr int32_t DSA_DEFAULT_KEY_SIZE = 1024;
-constexpr int32_t DSA_MIN_KEY_SIZE = 512;
-constexpr int32_t DSA_MAX_KEY_SIZE = 8192;
-
-/* EC */
-constexpr int32_t EC_DEFAULT_KEY_SIZE = 256;
-constexpr int32_t EC_MIN_KEY_SIZE = 192;
-constexpr int32_t EC_MAX_KEY_SIZE = 521;
-
-/* RSA */
-constexpr int32_t RSA_DEFAULT_KEY_SIZE = 2048;
-constexpr int32_t RSA_DEFAULT_EXPONENT = 0x10001;
-constexpr int32_t RSA_MIN_KEY_SIZE = 512;
-constexpr int32_t RSA_MAX_KEY_SIZE = 8192;
-
-#endif /* KEYSTORE_DEFAULTS_H_ */
diff --git a/keystore/grant_store.cpp b/keystore/grant_store.cpp
deleted file mode 100644
index 9e627ce..0000000
--- a/keystore/grant_store.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2017 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 "grant_store.h"
-
-#include "blob.h"
-#include <algorithm>
-#include <sstream>
-
-namespace keystore {
-
-static constexpr uint64_t kInvalidGrantNo = std::numeric_limits<uint64_t>::max();
-static const char* kKeystoreGrantInfix = "_KEYSTOREGRANT_";
-static constexpr size_t kKeystoreGrantInfixLength = 15;
-
-Grant::Grant(const KeyBlobEntry& entry, const uint64_t grant_no)
-    : entry_(entry), grant_no_(grant_no) {}
-
-static std::pair<uint64_t, std::string> parseGrantAlias(const std::string& grantAlias) {
-    auto pos = grantAlias.rfind(kKeystoreGrantInfix);
-    if (pos == std::string::npos) return {kInvalidGrantNo, ""};
-    std::stringstream s(grantAlias.substr(pos + kKeystoreGrantInfixLength));
-    std::string wrapped_alias = grantAlias.substr(0, pos);
-    uint64_t grant_no = kInvalidGrantNo;
-    s >> grant_no;
-    if (s.fail() || grant_no == kInvalidGrantNo) return {kInvalidGrantNo, ""};
-    return {grant_no, wrapped_alias};
-}
-
-std::string GrantStore::put(const uid_t uid, const LockedKeyBlobEntry& lockedEntry) {
-    std::unique_lock<std::shared_mutex> lock(mutex_);
-    std::stringstream s;
-    KeyBlobEntry blobEntry = *lockedEntry;
-    s << blobEntry.alias() << kKeystoreGrantInfix;
-
-    std::set<Grant, std::less<>>& uid_grant_list = grants_[uid];
-
-    bool success = false;
-    auto iterator =
-        std::find_if(uid_grant_list.begin(), uid_grant_list.end(),
-                     [&](const Grant& entry) { return success = entry.entry_ == blobEntry; });
-    while (!success) {
-        std::tie(iterator, success) = uid_grant_list.emplace(blobEntry, std::rand());
-    }
-    s << iterator->grant_no_;
-    return s.str();
-}
-
-ReadLockedGrant GrantStore::get(const uid_t uid, const std::string& alias) const {
-    std::shared_lock<std::shared_mutex> lock(mutex_);
-    uint64_t grant_no;
-    std::string wrappedAlias;
-    std::tie(grant_no, wrappedAlias) = parseGrantAlias(alias);
-    if (grant_no == kInvalidGrantNo) return {};
-    auto uid_set_iter = grants_.find(uid);
-    if (uid_set_iter == grants_.end()) return {};
-    auto& uid_grant_list = uid_set_iter->second;
-    auto grant = uid_grant_list.find(grant_no);
-    if (grant == uid_grant_list.end()) return {};
-    if (grant->entry_.alias() != wrappedAlias) return {};
-    return {&(*grant), std::move(lock)};
-}
-
-bool GrantStore::removeByFileAlias(const uid_t granteeUid, const LockedKeyBlobEntry& lockedEntry) {
-    std::unique_lock<std::shared_mutex> lock(mutex_);
-    auto& uid_grant_list = grants_[granteeUid];
-    for (auto i = uid_grant_list.begin(); i != uid_grant_list.end(); ++i) {
-        if (i->entry_ == *lockedEntry) {
-            uid_grant_list.erase(i);
-            return true;
-        }
-    }
-    return false;
-}
-
-void GrantStore::removeAllGrantsToKey(const uid_t granterUid, const std::string& alias) {
-    std::unique_lock<std::shared_mutex> lock(mutex_);
-    for (auto& uid_grant_list : grants_) {
-        for (auto i = uid_grant_list.second.begin(); i != uid_grant_list.second.end(); ++i) {
-            if (i->entry_.alias() == alias && i->entry_.uid() == granterUid) {
-                uid_grant_list.second.erase(i);
-                break;
-            }
-        }
-    }
-}
-
-void GrantStore::removeAllGrantsToUid(const uid_t granteeUid) {
-    std::unique_lock<std::shared_mutex> lock(mutex_);
-    grants_.erase(granteeUid);
-}
-
-}  // namespace keystore
diff --git a/keystore/grant_store.h b/keystore/grant_store.h
deleted file mode 100644
index 1baf32c..0000000
--- a/keystore/grant_store.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2017 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 KEYSTORE_GRANT_STORE_H_
-#define KEYSTORE_GRANT_STORE_H_
-
-#include <mutex>
-#include <set>
-#include <shared_mutex>
-#include <string>
-#include <unordered_map>
-
-#include <keystore/keystore_concurrency.h>
-
-#include "blob.h"
-
-namespace keystore {
-
-class Grant;
-
-using ReadLockedGrant =
-    ProxyLock<MutexProxyLockHelper<const Grant, std::shared_mutex, std::shared_lock>>;
-
-/**
- * Grant represents a mapping from an alias to a key file.
- * Normally, key file names are derived from the alias chosen by the client
- * and the clients UID, to generate a per client name space.
- * Grants allow assotiating a key file with a new name, thereby making
- * it visible in another client's - the grantee's - namespace.
- */
-class Grant {
-public:
-  Grant(const KeyBlobEntry& entry, const uint64_t grant_no);
-  KeyBlobEntry entry_;
-
-  uint64_t grant_no_;  ///< numeric grant identifier - randomly assigned
-
-  // NOLINTNEXTLINE(google-explicit-constructor)
-  operator const uint64_t&() const { return grant_no_; }
-};
-
-/**
- * The GrantStore holds a set of sets of Grants. One set of Grants for each grantee.
- * The uid parameter to each of the GrantStore function determines the grantee's
- * name space. The methods put, get, and removeByAlias/ByFileName create, lookup, and
- * remove a Grant, respectively.
- * put also returns a new alias for the newly granted key which has to be returned
- * to the granter. The grantee, and only the grantee, can use the granted key
- * by this new alias.
- */
-class GrantStore {
-public:
-    GrantStore() : grants_() {}
-    std::string put(const uid_t uid, const LockedKeyBlobEntry& blobfile);
-    ReadLockedGrant get(const uid_t uid, const std::string& alias) const;
-    bool removeByFileAlias(const uid_t granteeUid, const LockedKeyBlobEntry& lockedEntry);
-    void removeAllGrantsToKey(const uid_t granterUid, const std::string& alias);
-    void removeAllGrantsToUid(const uid_t granteeUid);
-
-    // GrantStore is neither copyable nor movable.
-    GrantStore(const GrantStore&) = delete;
-    GrantStore& operator=(const GrantStore&) = delete;
-private:
-    std::unordered_map<uid_t, std::set<Grant, std::less<>>> grants_;
-    mutable std::shared_mutex mutex_;
-};
-
-}  // namespace keystore
-
-#endif  // KEYSTORE_GRANT_STORE_H_
diff --git a/keystore/include/keystore/KeyAttestationApplicationId.h b/keystore/include/keystore/KeyAttestationApplicationId.h
index 861c2e1..0bf1aad 100644
--- a/keystore/include/keystore/KeyAttestationApplicationId.h
+++ b/keystore/include/keystore/KeyAttestationApplicationId.h
@@ -16,6 +16,7 @@
 #define KEYSTORE_INCLUDE_KEYSTORE_KEYATTESTATIONAPPLICATIONID_H_
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include <binder/Parcelable.h>
@@ -30,10 +31,10 @@
   public:
     typedef SharedNullableIterator<const KeyAttestationPackageInfo, std::vector>
         ConstKeyAttestationPackageInfoIterator;
-    typedef std::vector<std::unique_ptr<KeyAttestationPackageInfo>> PackageInfoVector;
+    typedef std::vector<std::optional<KeyAttestationPackageInfo>> PackageInfoVector;
     KeyAttestationApplicationId();
     // Following c'tors are for initializing instances containing test data.
-    explicit KeyAttestationApplicationId(std::unique_ptr<KeyAttestationPackageInfo> package);
+    explicit KeyAttestationApplicationId(std::optional<KeyAttestationPackageInfo> package);
     explicit KeyAttestationApplicationId(PackageInfoVector packages);
 
     status_t writeToParcel(Parcel*) const override;
@@ -47,7 +48,7 @@
     }
 
   private:
-    std::shared_ptr<std::vector<std::unique_ptr<KeyAttestationPackageInfo>>> packageInfos_;
+    std::shared_ptr<PackageInfoVector> packageInfos_;
 };
 
 }  // namespace keymaster
diff --git a/keystore/include/keystore/KeyAttestationPackageInfo.h b/keystore/include/keystore/KeyAttestationPackageInfo.h
index 92d4863..fa638f9 100644
--- a/keystore/include/keystore/KeyAttestationPackageInfo.h
+++ b/keystore/include/keystore/KeyAttestationPackageInfo.h
@@ -18,6 +18,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include <binder/Parcelable.h>
@@ -33,8 +34,7 @@
   public:
     typedef SharedNullableIterator<const content::pm::Signature, std::vector>
         ConstSignatureIterator;
-    typedef std::vector<std::unique_ptr<content::pm::Signature>>
-        SignaturesVector;
+    typedef std::vector<std::optional<content::pm::Signature>> SignaturesVector;
     typedef std::shared_ptr<SignaturesVector> SharedSignaturesVector;
 
     KeyAttestationPackageInfo(const String16& packageName, int64_t versionCode,
@@ -44,14 +44,14 @@
     status_t writeToParcel(Parcel*) const override;
     status_t readFromParcel(const Parcel* parcel) override;
 
-    const std::unique_ptr<String16>& package_name() const { return packageName_; }
+    const std::optional<String16>& package_name() const { return packageName_; }
     int64_t version_code() const { return versionCode_; }
 
     ConstSignatureIterator sigs_begin() const { return ConstSignatureIterator(signatures_); }
     ConstSignatureIterator sigs_end() const { return ConstSignatureIterator(); }
 
   private:
-    std::unique_ptr<String16> packageName_;
+    std::optional<String16> packageName_;
     int64_t versionCode_;
     SharedSignaturesVector signatures_;
 };
diff --git a/keystore/include/keystore/KeystoreResponse.h b/keystore/include/keystore/KeystoreResponse.h
index 20f7274..4a7ef0d 100644
--- a/keystore/include/keystore/KeystoreResponse.h
+++ b/keystore/include/keystore/KeystoreResponse.h
@@ -31,29 +31,24 @@
   public:
     KeystoreResponse() = default;
     explicit KeystoreResponse(const int response_code, const String16& error_msg)
-        : response_code_(response_code), error_msg_(std::make_unique<String16>(error_msg)) {}
+        : response_code_(response_code), error_msg_(error_msg) {}
     explicit KeystoreResponse(const int response_code)
         : response_code_(response_code), error_msg_() {}
     // NOLINTNEXTLINE(google-explicit-constructor)
     KeystoreResponse(const ::keystore::KeyStoreServiceReturnCode& rc)
         : response_code_(rc.getErrorCode()), error_msg_() {}
-    KeystoreResponse(const KeystoreResponse& other)
-        : response_code_(other.response_code_), error_msg_() {
-        if (other.error_msg_) {
-            error_msg_ = std::make_unique<String16>(*other.error_msg_);
-        }
-    }
+    KeystoreResponse(const KeystoreResponse& other) = default;
     KeystoreResponse(KeystoreResponse&& other) = default;
 
     status_t readFromParcel(const Parcel* in) override;
     status_t writeToParcel(Parcel* out) const override;
 
     int response_code() const { return response_code_; }
-    const String16* error_msg() const { return error_msg_.get(); }
+    const std::optional<String16>& error_msg() const { return error_msg_; }
 
   private:
     int response_code_;
-    std::unique_ptr<String16> error_msg_;
+    std::optional<String16> error_msg_;
 };
 
 }  // namespace keystore
diff --git a/keystore/include/keystore/utils.h b/keystore/include/keystore/utils.h
index 544dd21..2143d3a 100644
--- a/keystore/include/keystore/utils.h
+++ b/keystore/include/keystore/utils.h
@@ -5,6 +5,7 @@
 
 #include <iterator>
 #include <memory>
+#include <optional>
 #include <vector>
 
 namespace android {
@@ -12,7 +13,7 @@
 
 /*
  * This iterator abstracts from a collection of the form
- * std::shared_ptr<COLLECTION_TYPE<std::unique_ptr<T>>>
+ * std::shared_ptr<COLLECTION_TYPE<std::optional<T>>>
  * such that it is defined both for nulled outer pointer and
  * nulled entries. If shared_ptr(nullptr) is passed in, the iterator behaves
  * like the end iterator yielding an empty collection. Nulled
@@ -25,7 +26,7 @@
 template <typename T, template <typename...> class Coll = std::vector>
 class SharedNullableIterator {
   public:
-    typedef Coll<std::unique_ptr<typename std::remove_const<T>::type>> CollectionType;
+    typedef Coll<std::optional<typename std::remove_const<T>::type>> CollectionType;
     typedef std::shared_ptr<CollectionType> CollectionPtr;
 
     SharedNullableIterator() {}
diff --git a/keystore/key_attestation_log_handler.cpp b/keystore/key_attestation_log_handler.cpp
deleted file mode 100644
index c3278cb..0000000
--- a/keystore/key_attestation_log_handler.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2018 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 <statslog.h>
-namespace keystore {
-
-void logKeystoreKeyAttestationEvent(bool wasSuccessful, int32_t errorCode) {
-    // Due to a requirement in stats-write() method, the optional fields
-    // which are not required for attestation logging, are marked with -1 for
-    // non-repeated fields and 0 for repeated fields.
-    android::util::stats_write(android::util::KEYSTORE_KEY_EVENT_REPORTED, -1, -1, -1, -1, -1, 0, 0,
-                               0, 0, -1, -1,
-                               android::util::KEYSTORE_KEY_EVENT_REPORTED__TYPE__KEY_ATTESTATION,
-                               wasSuccessful, errorCode);
-}
-
-}  // namespace keystore
\ No newline at end of file
diff --git a/keystore/key_creation_log_handler.cpp b/keystore/key_creation_log_handler.cpp
deleted file mode 100644
index d846257..0000000
--- a/keystore/key_creation_log_handler.cpp
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-#define LOG_TAG "KeystoreOperation"
-
-#include "key_creation_log_handler.h"
-#include <statslog.h>
-
-namespace keystore {
-
-template <typename Tag>
-int32_t getEnumTagValue(const AuthorizationSet& authorization_set, Tag tag) {
-    auto tagValue = authorization_set.GetTagValue(tag);
-    if (tagValue.isOk()) {
-        static_assert(sizeof(decltype(tagValue.value())) <= sizeof(int32_t),
-                      "Tag type value will be truncated, if cast to int32_t");
-        return static_cast<int32_t>(tagValue.value());
-    }
-    // Usually, if the value is not present, 0 is set. However, since 0 is a valid
-    // enum value, -1 is set for single enum fields.
-    return -1;
-}
-
-int32_t generateBitMapForPaddingModeValues(const AuthorizationSet& authorization_set) {
-    int32_t bitMap = 0;
-    int32_t tagValueCount = authorization_set.GetTagCount(TAG_PADDING);
-    if (tagValueCount == 0) {
-        // unlike in the single enum fields, if no value is provided,
-        // 0 is set for the bitmap
-        return bitMap;
-    }
-    int current_offset = -1;
-    while (tagValueCount > 0) {
-        current_offset = authorization_set.find(TAG_PADDING, current_offset);
-        KeyParameter keyParam = authorization_set[current_offset];
-        auto tagValue = accessTagValue(TAG_PADDING, keyParam);
-        switch (tagValue) {
-        case PaddingMode::NONE:
-            bitMap |= (1 << NONE_BIT_POS);
-            break;
-        case PaddingMode::RSA_OAEP:
-            bitMap |= (1 << PaddingModeBitPosition::RSA_OAEP_BIT_POS);
-            break;
-        case PaddingMode::RSA_PSS:
-            bitMap |= (1 << PaddingModeBitPosition::RSA_PSS_BIT_POS);
-            break;
-        case PaddingMode::RSA_PKCS1_1_5_ENCRYPT:
-            bitMap |= (1 << PaddingModeBitPosition::RSA_PKCS1_1_5_ENCRYPT_BIT_POS);
-            break;
-        case PaddingMode::RSA_PKCS1_1_5_SIGN:
-            bitMap |= (1 << PaddingModeBitPosition::RSA_PKCS1_1_5_SIGN_BIT_POS);
-            break;
-        case PaddingMode::PKCS7:
-            bitMap |= (1 << PaddingModeBitPosition::PKCS7_BIT_POS);
-            break;
-        default:
-            break;
-        }
-        tagValueCount -= 1;
-    }
-    return bitMap;
-}
-
-int32_t generateBitMapForDigestValues(const AuthorizationSet& authorization_set) {
-    int32_t bitMap = 0;
-    int32_t tagValueCount = authorization_set.GetTagCount(TAG_DIGEST);
-    if (tagValueCount == 0) {
-        // unlike in the single enum fields, if no value is provided,
-        // 0 is set for the bitmap
-        return bitMap;
-    }
-    int current_offset = -1;
-    while (tagValueCount > 0) {
-        current_offset = authorization_set.find(TAG_DIGEST, current_offset);
-        KeyParameter keyParam = authorization_set[current_offset];
-        auto tagValue = accessTagValue(TAG_DIGEST, keyParam);
-        switch (tagValue) {
-        case Digest::NONE:
-            bitMap |= (1 << NONE_BIT_POS);
-            break;
-        case Digest::MD5:
-            bitMap |= (1 << DigestBitPosition::MD5_BIT_POS);
-            break;
-        case Digest::SHA1:
-            bitMap |= (1 << DigestBitPosition::SHA1_BIT_POS);
-            break;
-        case Digest::SHA_2_224:
-            bitMap |= (1 << DigestBitPosition::SHA_2_224_BIT_POS);
-            break;
-        case Digest::SHA_2_256:
-            bitMap |= (1 << DigestBitPosition::SHA_2_256_BIT_POS);
-            break;
-        case Digest::SHA_2_384:
-            bitMap |= (1 << DigestBitPosition::SHA_2_384_BIT_POS);
-            break;
-        case Digest::SHA_2_512:
-            bitMap |= (1 << DigestBitPosition::SHA_2_512_BIT_POS);
-            break;
-        default:
-            break;
-        }
-        tagValueCount -= 1;
-    }
-    return bitMap;
-}
-
-int32_t generateBitMapForBlockModeValues(const AuthorizationSet& authorization_set) {
-    int32_t bitMap = 0;
-    int32_t tagValueCount = authorization_set.GetTagCount(TAG_BLOCK_MODE);
-    if (tagValueCount == 0) {
-        // unlike in the single enum fields, if no value is provided,
-        // 0 is set for the bitmap
-        return bitMap;
-    }
-    int current_offset = -1;
-    while (tagValueCount > 0) {
-        current_offset = authorization_set.find(TAG_BLOCK_MODE, current_offset);
-        KeyParameter keyParam = authorization_set[current_offset];
-        auto tagValue = accessTagValue(TAG_BLOCK_MODE, keyParam);
-        switch (tagValue) {
-        case BlockMode::ECB:
-            bitMap |= (1 << BlockModeBitPosition::ECB_BIT_POS);
-            break;
-        case BlockMode::CBC:
-            bitMap |= (1 << BlockModeBitPosition::CBC_BIT_POS);
-            break;
-        case BlockMode::CTR:
-            bitMap |= (1 << BlockModeBitPosition::CTR_BIT_POS);
-            break;
-        case BlockMode::GCM:
-            bitMap |= (1 << BlockModeBitPosition::GCM_BIT_POS);
-            break;
-        default:
-            break;
-        }
-        tagValueCount -= 1;
-    }
-    return bitMap;
-}
-
-int32_t generateBitMapForKeyPurposeValues(const AuthorizationSet& authorization_set) {
-    int32_t bitMap = 0;
-    int32_t tagValueCount = authorization_set.GetTagCount(TAG_PURPOSE);
-    if (tagValueCount == 0) {
-        // unlike in the single enum fields, if no value is provided,
-        // 0 is set for the bitmap
-        return bitMap;
-    }
-    int current_offset = -1;
-    while (tagValueCount > 0) {
-        current_offset = authorization_set.find(TAG_PURPOSE, current_offset);
-        KeyParameter keyParam = authorization_set[current_offset];
-        auto tagValue = accessTagValue(TAG_PURPOSE, keyParam);
-        switch (tagValue) {
-        case KeyPurpose::ENCRYPT:
-            bitMap |= (1 << KeyPurposeBitPosition::ENCRYPT_BIT_POS);
-            break;
-        case KeyPurpose::DECRYPT:
-            bitMap |= (1 << KeyPurposeBitPosition::DECRYPT_BIT_POS);
-            break;
-        case KeyPurpose::SIGN:
-            bitMap |= (1 << KeyPurposeBitPosition::SIGN_BIT_POS);
-            break;
-        case KeyPurpose::VERIFY:
-            bitMap |= (1 << KeyPurposeBitPosition::VERIFY_BIT_POS);
-            break;
-        case KeyPurpose::WRAP_KEY:
-            bitMap |= (1 << KeyPurposeBitPosition::WRAP_KEY_BIT_POS);
-            break;
-        default:
-            break;
-        }
-        tagValueCount -= 1;
-    }
-    return bitMap;
-}
-
-void logKeystoreKeyCreationEvent(const hidl_vec<KeyParameter>& keyParams,
-                                 bool wasCreationSuccessful, int32_t errorCode) {
-    AuthorizationSet authorization_set(keyParams);
-    authorization_set.Deduplicate();
-
-    android::util::stats_write(android::util::KEYSTORE_KEY_EVENT_REPORTED,
-                               getEnumTagValue(authorization_set, TAG_ALGORITHM),
-                               getEnumTagValue(authorization_set, TAG_KEY_SIZE),
-                               getEnumTagValue(authorization_set, TAG_ORIGIN),
-                               getEnumTagValue(authorization_set, TAG_USER_AUTH_TYPE),
-                               getEnumTagValue(authorization_set, TAG_AUTH_TIMEOUT),
-                               generateBitMapForPaddingModeValues(authorization_set),
-                               generateBitMapForDigestValues(authorization_set),
-                               generateBitMapForBlockModeValues(authorization_set),
-                               generateBitMapForKeyPurposeValues(authorization_set),
-                               getEnumTagValue(authorization_set, TAG_EC_CURVE),
-                               getEnumTagValue(authorization_set, TAG_BLOB_USAGE_REQUIREMENTS),
-                               android::util::KEYSTORE_KEY_EVENT_REPORTED__TYPE__KEY_CREATION,
-                               wasCreationSuccessful, errorCode);
-}
-
-}  // namespace keystore
diff --git a/keystore/key_creation_log_handler.h b/keystore/key_creation_log_handler.h
deleted file mode 100644
index a314eb1..0000000
--- a/keystore/key_creation_log_handler.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2018 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 KEY_CREATION_LOG_HANDLER_H_
-#define KEY_CREATION_LOG_HANDLER_H_
-
-#include <keystore/keystore_hidl_support.h>
-
-namespace keystore {
-
-/**
- * Following enums are defined as a part of the workaround to log the repeated
- * values of ENUM_REP type. The workaround is to represent the repeated values
- * of ENUM_REP type as a bitmap and the following enums define their positions
- * in the bitmap.
- */
-
-enum PaddingModeBitPosition : int32_t {
-    RSA_OAEP_BIT_POS = 1,
-    RSA_PSS_BIT_POS = 2,
-    RSA_PKCS1_1_5_ENCRYPT_BIT_POS = 3,
-    RSA_PKCS1_1_5_SIGN_BIT_POS = 4,
-    PKCS7_BIT_POS = 5,
-};
-
-enum DigestBitPosition : int32_t {
-    MD5_BIT_POS = 1,
-    SHA1_BIT_POS = 2,
-    SHA_2_224_BIT_POS = 3,
-    SHA_2_256_BIT_POS = 4,
-    SHA_2_384_BIT_POS = 5,
-    SHA_2_512_BIT_POS = 6,
-};
-
-enum BlockModeBitPosition : int32_t {
-    ECB_BIT_POS = 1,
-    CBC_BIT_POS = 2,
-    CTR_BIT_POS = 3,
-    GCM_BIT_POS = 4,
-};
-
-enum KeyPurposeBitPosition : int32_t {
-    ENCRYPT_BIT_POS = 1,
-    DECRYPT_BIT_POS = 2,
-    SIGN_BIT_POS = 3,
-    VERIFY_BIT_POS = 4,
-    WRAP_KEY_BIT_POS = 5,
-};
-
-// None is an enum value for digest and a deprecated value for padding mode
-const int32_t NONE_BIT_POS = 0;
-
-void logKeystoreKeyCreationEvent(const hidl_vec<KeyParameter>& keyParams,
-                                 bool wasCreationSuccessful, int32_t errorCode);
-
-}  // namespace keystore
-
-#endif  // KEY_CREATION_LOG_HANDLER_H_
diff --git a/keystore/key_operation_log_handler.cpp b/keystore/key_operation_log_handler.cpp
deleted file mode 100644
index e7f4345..0000000
--- a/keystore/key_operation_log_handler.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-#define LOG_TAG "KeystoreOperation"
-
-#include "key_operation_log_handler.h"
-#include "key_creation_log_handler.h"
-
-#include <keystore/keystore_hidl_support.h>
-#include <statslog.h>
-
-namespace keystore {
-
-template <typename Tag>
-int32_t getOptionalEnumTagValue(const AuthorizationSet& authorization_set, Tag tag) {
-    auto tagValue = authorization_set.GetTagValue(tag);
-    if (tagValue.isOk()) {
-        static_assert(sizeof(decltype(tagValue.value())) <= sizeof(int32_t),
-                      "Tag type value will be truncated, if cast to int32_t");
-        return static_cast<int32_t>(tagValue.value());
-    }
-    //-1 is an invalid value for all enum types.
-    return -1;
-}
-
-int32_t generateBitMapForPaddingModeValue(const AuthorizationSet& authorization_set) {
-    auto tagValue = authorization_set.GetTagValue(TAG_PADDING);
-    if (tagValue.isOk()) {
-        auto value = tagValue.value();
-        switch (value) {
-        case PaddingMode::NONE:
-            return (1 << NONE_BIT_POS);
-        case PaddingMode::RSA_OAEP:
-            return (1 << PaddingModeBitPosition::RSA_OAEP_BIT_POS);
-        case PaddingMode::RSA_PSS:
-            return (1 << PaddingModeBitPosition::RSA_PSS_BIT_POS);
-        case PaddingMode::RSA_PKCS1_1_5_ENCRYPT:
-            return (1 << PaddingModeBitPosition::RSA_PKCS1_1_5_ENCRYPT_BIT_POS);
-        case PaddingMode::RSA_PKCS1_1_5_SIGN:
-            return (1 << PaddingModeBitPosition::RSA_PKCS1_1_5_SIGN_BIT_POS);
-        case PaddingMode::PKCS7:
-            return (1 << PaddingModeBitPosition::PKCS7_BIT_POS);
-        default:
-            break;
-        }
-    }
-    // unlike in the single enum fields, if no value is provided,
-    // 0 is set for the bitmap
-    return 0;
-}
-
-int32_t generateBitMapForDigestValue(const AuthorizationSet& authorization_set) {
-    auto tagValue = authorization_set.GetTagValue(TAG_DIGEST);
-    if (tagValue.isOk()) {
-        auto value = tagValue.value();
-        switch (value) {
-        case Digest::NONE:
-            return (1 << NONE_BIT_POS);
-        case Digest::MD5:
-            return (1 << DigestBitPosition::MD5_BIT_POS);
-        case Digest::SHA1:
-            return (1 << DigestBitPosition::SHA1_BIT_POS);
-        case Digest::SHA_2_224:
-            return (1 << DigestBitPosition::SHA_2_224_BIT_POS);
-        case Digest::SHA_2_256:
-            return (1 << DigestBitPosition::SHA_2_256_BIT_POS);
-        case Digest::SHA_2_384:
-            return (1 << DigestBitPosition::SHA_2_384_BIT_POS);
-        case Digest::SHA_2_512:
-            return (1 << DigestBitPosition::SHA_2_512_BIT_POS);
-        default:
-            break;
-        }
-    }
-    // unlike in the single enum fields, if no value is provided,
-    // 0 is set for the bitmap
-    return 0;
-}
-
-int32_t generateBitMapForBlockModeValue(const AuthorizationSet& authorization_set) {
-    auto tagValue = authorization_set.GetTagValue(TAG_BLOCK_MODE);
-    if (tagValue.isOk()) {
-        auto value = tagValue.value();
-        switch (value) {
-        case BlockMode::ECB:
-            return (1 << BlockModeBitPosition::ECB_BIT_POS);
-        case BlockMode::CBC:
-            return (1 << BlockModeBitPosition::CBC_BIT_POS);
-        case BlockMode::CTR:
-            return (1 << BlockModeBitPosition::CTR_BIT_POS);
-        case BlockMode::GCM:
-            return (1 << BlockModeBitPosition::GCM_BIT_POS);
-        default:
-            break;
-        }
-    }
-    // unlike in the single enum fields, if no value is provided,
-    // 0 is set for the bitmap
-    return 0;
-}
-
-void logKeystoreKeyOperationEvent(const Operation& op, bool wasOperationSuccessful,
-                                  int32_t responseCode) {
-    AuthorizationSet authorization_set(op.characteristics.softwareEnforced);
-    authorization_set.Union(op.characteristics.hardwareEnforced);
-    AuthorizationSet operation_params(op.params);
-
-    android::util::stats_write(
-        android::util::KEYSTORE_KEY_EVENT_REPORTED,
-        getOptionalEnumTagValue(authorization_set, TAG_ALGORITHM),
-        getOptionalEnumTagValue(authorization_set, TAG_KEY_SIZE),
-        getOptionalEnumTagValue(authorization_set, TAG_ORIGIN),
-        getOptionalEnumTagValue(authorization_set, TAG_USER_AUTH_TYPE),
-        getOptionalEnumTagValue(authorization_set, TAG_AUTH_TIMEOUT),
-        generateBitMapForPaddingModeValue(operation_params),
-        generateBitMapForDigestValue(operation_params),
-        generateBitMapForBlockModeValue(operation_params), static_cast<int32_t>(op.purpose),
-        getOptionalEnumTagValue(authorization_set, TAG_EC_CURVE),
-        getOptionalEnumTagValue(authorization_set, TAG_BLOB_USAGE_REQUIREMENTS),
-        android::util::KEYSTORE_KEY_EVENT_REPORTED__TYPE__KEY_OPERATION, wasOperationSuccessful,
-        responseCode);
-}
-
-}  // namespace keystore
\ No newline at end of file
diff --git a/keystore/key_operation_log_handler.h b/keystore/key_operation_log_handler.h
deleted file mode 100644
index ba27747..0000000
--- a/keystore/key_operation_log_handler.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2018 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 KEY_OPERATION_LOG_HANDLER_H_
-#define KEY_OPERATION_LOG_HANDLER_H_
-
-#include "operation_struct.h"
-
-namespace keystore {
-
-void logKeystoreKeyOperationEvent(const Operation& op, bool wasSuccessful, int32_t errorCode);
-
-}  // namespace keystore
-
-#endif  // KEY_OPERATION_LOG_HANDLER_H_
\ No newline at end of file
diff --git a/keystore/key_store_service.cpp b/keystore/key_store_service.cpp
deleted file mode 100644
index 1b38643..0000000
--- a/keystore/key_store_service.cpp
+++ /dev/null
@@ -1,1394 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#define LOG_TAG "keystore"
-
-#include "key_store_service.h"
-
-#include <fcntl.h>
-#include <sys/stat.h>
-
-#include <algorithm>
-#include <atomic>
-#include <sstream>
-
-#include <android-base/scopeguard.h>
-#include <binder/IInterface.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IPermissionController.h>
-#include <binder/IServiceManager.h>
-#include <cutils/multiuser.h>
-#include <log/log_event_list.h>
-
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-
-#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
-#include <android/hardware/keymaster/3.0/IHwKeymasterDevice.h>
-#include <keymasterV4_0/keymaster_utils.h>
-
-#include "defaults.h"
-#include "key_attestation_log_handler.h"
-#include "keystore_keymaster_enforcement.h"
-#include "keystore_utils.h"
-#include <keystore/keystore_attestation_id.h>
-#include <keystore/keystore_hidl_support.h>
-#include <keystore/keystore_return_types.h>
-
-#include <hardware/hw_auth_token.h>
-
-namespace keystore {
-
-using namespace android;
-
-namespace {
-
-using ::android::binder::Status;
-using android::hardware::keymaster::V4_0::support::authToken2HidlVec;
-using android::hardware::keymaster::V4_0::support::serializeVerificationToken;
-using android::security::keymaster::ExportResult;
-using android::security::keymaster::KeymasterArguments;
-using android::security::keymaster::KeymasterBlob;
-using android::security::keymaster::KeymasterCertificateChain;
-using android::security::keymaster::operationFailed;
-using android::security::keymaster::OperationResult;
-using ConfirmationResponseCode = android::hardware::confirmationui::V1_0::ResponseCode;
-using ::android::security::keystore::ICredstoreTokenCallback;
-using ::android::security::keystore::IKeystoreOperationResultCallback;
-using ::android::security::keystore::IKeystoreResponseCallback;
-using ::android::security::keystore::KeystoreResponse;
-
-constexpr double kIdRotationPeriod = 30 * 24 * 60 * 60; /* Thirty days, in seconds */
-const char* kTimestampFilePath = "timestamp";
-
-bool containsTag(const hidl_vec<KeyParameter>& params, Tag tag) {
-    return params.end() !=
-           std::find_if(params.begin(), params.end(),
-                        [&](const KeyParameter& param) { return param.tag == tag; });
-}
-
-#define AIDL_RETURN(rc) (*_aidl_return = KeyStoreServiceReturnCode(rc).getErrorCode(), Status::ok())
-
-std::pair<KeyStoreServiceReturnCode, bool> hadFactoryResetSinceIdRotation() {
-    struct stat sbuf;
-    if (stat(kTimestampFilePath, &sbuf) == 0) {
-        double diff_secs = difftime(time(nullptr), sbuf.st_ctime);
-        return {ResponseCode::NO_ERROR, diff_secs < kIdRotationPeriod};
-    }
-
-    if (errno != ENOENT) {
-        ALOGE("Failed to stat \"timestamp\" file, with error %d", errno);
-        return {ResponseCode::SYSTEM_ERROR, false /* don't care */};
-    }
-
-    int fd = creat(kTimestampFilePath, 0600);
-    if (fd < 0) {
-        ALOGE("Couldn't create \"timestamp\" file, with error %d", errno);
-        return {ResponseCode::SYSTEM_ERROR, false /* don't care */};
-    }
-
-    if (close(fd)) {
-        ALOGE("Couldn't close \"timestamp\" file, with error %d", errno);
-        return {ResponseCode::SYSTEM_ERROR, false /* don't care */};
-    }
-
-    return {ResponseCode::NO_ERROR, true};
-}
-
-using ::android::security::KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE;
-
-KeyStoreServiceReturnCode updateParamsForAttestation(uid_t callingUid, AuthorizationSet* params) {
-    KeyStoreServiceReturnCode responseCode;
-    bool factoryResetSinceIdRotation;
-    std::tie(responseCode, factoryResetSinceIdRotation) = hadFactoryResetSinceIdRotation();
-
-    if (!responseCode.isOk()) return responseCode;
-    if (factoryResetSinceIdRotation) params->push_back(TAG_RESET_SINCE_ID_ROTATION);
-
-    auto asn1_attestation_id_result = security::gather_attestation_application_id(callingUid);
-    if (!asn1_attestation_id_result.isOk()) {
-        ALOGE("failed to gather attestation_id");
-        // Couldn't get attestation ID; just use an empty one rather than failing.
-        asn1_attestation_id_result = std::vector<uint8_t>();
-    }
-    std::vector<uint8_t>& asn1_attestation_id = asn1_attestation_id_result;
-
-    /*
-     * The attestation application ID must not be longer than
-     * KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE, error out if gather_attestation_application_id
-     * returned such an invalid vector.
-     */
-    if (asn1_attestation_id.size() > KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE) {
-        ALOGE("BUG: Gathered Attestation Application ID is too big (%d)",
-              static_cast<int32_t>(asn1_attestation_id.size()));
-        return ErrorCode::CANNOT_ATTEST_IDS;
-    }
-
-    params->push_back(TAG_ATTESTATION_APPLICATION_ID, asn1_attestation_id);
-
-    return ResponseCode::NO_ERROR;
-}
-
-}  // anonymous namespace
-
-Status KeyStoreService::getState(int32_t userId, int32_t* aidl_return) {
-    if (!checkBinderPermission(P_GET_STATE)) {
-        *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
-        return Status::ok();
-    }
-    *aidl_return = mKeyStore->getState(userId);
-    return Status::ok();
-}
-
-Status KeyStoreService::get(const String16& name, int32_t uid, ::std::vector<uint8_t>* item) {
-    uid_t targetUid = getEffectiveUid(uid);
-    if (!checkBinderPermission(P_GET, targetUid)) {
-        // see keystore/keystore.h
-        return Status::fromServiceSpecificError(
-            static_cast<int32_t>(ResponseCode::PERMISSION_DENIED));
-    }
-
-    String8 name8(name);
-    ResponseCode rc;
-    Blob keyBlob;
-    Blob charBlob;
-    LockedKeyBlobEntry lockedEntry;
-
-    std::tie(rc, keyBlob, charBlob, lockedEntry) =
-        mKeyStore->getKeyForName(name8, targetUid, TYPE_GENERIC);
-    if (rc != ResponseCode::NO_ERROR) {
-        *item = ::std::vector<uint8_t>();
-        // Return empty array if key is not found
-        // TODO: consider having returned value nullable or parse exception on the client.
-        return Status::fromServiceSpecificError(static_cast<int32_t>(rc));
-    }
-    auto resultBlob = blob2hidlVec(keyBlob);
-    // The static_cast here is needed to prevent a move, forcing a deep copy.
-    if (item) *item = static_cast<const hidl_vec<uint8_t>&>(blob2hidlVec(keyBlob));
-    return Status::ok();
-}
-
-Status KeyStoreService::insert(const String16& name, const ::std::vector<uint8_t>& item,
-                               int targetUid, int32_t flags, int32_t* aidl_return) {
-    targetUid = getEffectiveUid(targetUid);
-    KeyStoreServiceReturnCode result =
-        checkBinderPermissionAndKeystoreState(P_INSERT, targetUid, flags & KEYSTORE_FLAG_ENCRYPTED);
-    if (!result.isOk()) {
-        *aidl_return = result.getErrorCode();
-        return Status::ok();
-    }
-
-    String8 name8(name);
-    auto lockedEntry = mKeyStore->getLockedBlobEntryIfNotExists(name8.string(), targetUid);
-
-    if (!lockedEntry) {
-        ALOGE("failed to grab lock on blob entry %u_%s", targetUid, name8.string());
-        *aidl_return = static_cast<int32_t>(ResponseCode::KEY_ALREADY_EXISTS);
-        return Status::ok();
-    }
-
-    Blob keyBlob(&item[0], item.size(), nullptr, 0, ::TYPE_GENERIC);
-    keyBlob.setEncrypted(flags & KEYSTORE_FLAG_ENCRYPTED);
-
-    *aidl_return = static_cast<int32_t>(mKeyStore->put(lockedEntry, keyBlob, {}));
-    return Status::ok();
-}
-
-Status KeyStoreService::del(const String16& name, int targetUid, int32_t* aidl_return) {
-    targetUid = getEffectiveUid(targetUid);
-    if (!checkBinderPermission(P_DELETE, targetUid)) {
-        *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
-        return Status::ok();
-    }
-    String8 name8(name);
-    ALOGI("del %s %d", name8.string(), targetUid);
-    auto lockedEntry = mKeyStore->getLockedBlobEntryIfExists(name8.string(), targetUid);
-    if (!lockedEntry) {
-        *aidl_return = static_cast<int32_t>(ResponseCode::KEY_NOT_FOUND);
-        return Status::ok();
-    }
-
-    ResponseCode result = mKeyStore->del(lockedEntry);
-
-    *aidl_return = static_cast<int32_t>(result);
-    return Status::ok();
-}
-
-Status KeyStoreService::exist(const String16& name, int targetUid, int32_t* aidl_return) {
-    targetUid = getEffectiveUid(targetUid);
-    if (!checkBinderPermission(P_EXIST, targetUid)) {
-        *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
-        return Status::ok();
-    }
-
-    LockedKeyBlobEntry lockedEntry =
-        mKeyStore->getLockedBlobEntryIfExists(String8(name).string(), targetUid);
-    *aidl_return =
-        static_cast<int32_t>(lockedEntry ? ResponseCode::NO_ERROR : ResponseCode::KEY_NOT_FOUND);
-    return Status::ok();
-}
-
-Status KeyStoreService::list(const String16& prefix, int32_t targetUid,
-                             ::std::vector<::android::String16>* matches) {
-    targetUid = getEffectiveUid(targetUid);
-    if (!checkBinderPermission(P_LIST, targetUid)) {
-        return Status::fromServiceSpecificError(
-            static_cast<int32_t>(ResponseCode::PERMISSION_DENIED));
-    }
-    const String8 prefix8(prefix);
-    const std::string stdPrefix(prefix8.string());
-
-    ResponseCode rc;
-    std::list<LockedKeyBlobEntry> internal_matches;
-    auto userDirName = mKeyStore->getUserStateDB().getUserStateByUid(targetUid)->getUserDirName();
-
-    std::tie(rc, internal_matches) =
-        LockedKeyBlobEntry::list(userDirName, [&](uid_t uid, const std::string& alias) {
-            std::mismatch(stdPrefix.begin(), stdPrefix.end(), alias.begin(), alias.end());
-            return uid == static_cast<uid_t>(targetUid) &&
-                   std::mismatch(stdPrefix.begin(), stdPrefix.end(), alias.begin(), alias.end())
-                           .first == stdPrefix.end();
-        });
-
-    if (rc != ResponseCode::NO_ERROR) {
-        return Status::fromServiceSpecificError(static_cast<int32_t>(rc));
-    }
-
-    for (LockedKeyBlobEntry& entry : internal_matches) {
-        matches->push_back(String16(entry->alias().substr(prefix8.size()).c_str()));
-    }
-    return Status::ok();
-}
-
-/*
- * This method will return the uids of all auth bound keys for the calling user.
- * This is intended to be used for alerting the user about which apps will be affected
- * if the password/pin is removed. Only allowed to be called by system.
- * The output is bound by the initial size of uidsOut to be compatible with Java.
- */
-Status KeyStoreService::listUidsOfAuthBoundKeys(std::vector<std::string>* uidsOut,
-                                                int32_t* aidl_return) {
-    const int32_t callingUid = IPCThreadState::self()->getCallingUid();
-    const int32_t userId = get_user_id(callingUid);
-    const int32_t appId = get_app_id(callingUid);
-    if (appId != AID_SYSTEM) {
-        ALOGE("Permission listUidsOfAuthBoundKeys denied for aid %d", appId);
-        *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
-        return Status::ok();
-    }
-
-    const String8 prefix8("");
-    auto userState = mKeyStore->getUserStateDB().getUserState(userId);
-    const std::string userDirName = userState->getUserDirName();
-    auto encryptionKey = userState->getEncryptionKey();
-    auto state = userState->getState();
-    // unlock the user state
-    userState = {};
-
-    ResponseCode rc;
-    std::list<LockedKeyBlobEntry> internal_matches;
-    std::tie(rc, internal_matches) =
-        LockedKeyBlobEntry::list(userDirName, [&](uid_t, const std::string&) {
-            // Need to filter on auth bound state, so just return true.
-            return true;
-        });
-    if (rc != ResponseCode::NO_ERROR) {
-        ALOGE("Error listing blob entries for user %d", userId);
-        return Status::fromServiceSpecificError(static_cast<int32_t>(rc));
-    }
-
-    for (LockedKeyBlobEntry& entry : internal_matches) {
-        // Need to store uids as a list of strings because integer list output
-        // parameters is not supported in aidl-cpp.
-        std::string entryUid = std::to_string(entry->uid());
-        if (std::find(uidsOut->begin(), uidsOut->end(), entryUid) != uidsOut->end()) {
-            // uid already in list, skip
-            continue;
-        }
-
-        auto [rc, blob, charBlob] = entry.readBlobs(encryptionKey, state);
-        if (rc != ResponseCode::NO_ERROR && rc != ResponseCode::LOCKED) {
-            ALOGE("Error reading blob for key %s", entry->alias().c_str());
-            continue;
-        }
-
-        if (blob && blob.isEncrypted()) {
-            uidsOut->push_back(entryUid);
-        } else if (charBlob) {
-            auto [success, hwEnforced, swEnforced] = charBlob.getKeyCharacteristics();
-            if (!success) {
-                ALOGE("Error reading blob characteristics for key %s", entry->alias().c_str());
-                continue;
-            }
-            if (hwEnforced.Contains(TAG_USER_SECURE_ID) ||
-                swEnforced.Contains(TAG_USER_SECURE_ID)) {
-                uidsOut->push_back(entryUid);
-            }
-        }
-    }
-    *aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
-    return Status::ok();
-}
-
-Status KeyStoreService::onUserPasswordChanged(int32_t userId, const String16& password,
-                                              int32_t* aidl_return) {
-    if (!checkBinderPermission(P_PASSWORD)) {
-        *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
-        return Status::ok();
-    }
-
-    if (password.size() == 0) {
-        ALOGI("Secure lockscreen for user %d removed, deleting encrypted entries", userId);
-        mKeyStore->resetUser(userId, true);
-        *aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
-        return Status::ok();
-    } else {
-        const String8 password8(password);
-        switch (mKeyStore->getState(userId)) {
-        case ::STATE_UNINITIALIZED: {
-            // generate master key, encrypt with password, write to file,
-            // initialize mMasterKey*.
-            *aidl_return = static_cast<int32_t>(mKeyStore->initializeUser(password8, userId));
-            return Status::ok();
-        }
-        case ::STATE_NO_ERROR: {
-            // rewrite master key with new password.
-            *aidl_return = static_cast<int32_t>(mKeyStore->writeMasterKey(password8, userId));
-            return Status::ok();
-        }
-        case ::STATE_LOCKED: {
-            ALOGE("Changing user %d's password while locked, clearing old encryption", userId);
-            mKeyStore->resetUser(userId, true);
-            *aidl_return = static_cast<int32_t>(mKeyStore->initializeUser(password8, userId));
-            return Status::ok();
-        }
-        }
-        *aidl_return = static_cast<int32_t>(ResponseCode::SYSTEM_ERROR);
-        return Status::ok();
-    }
-}
-
-Status KeyStoreService::onUserAdded(int32_t userId, int32_t parentId, int32_t* aidl_return) {
-    if (!checkBinderPermission(P_USER_CHANGED)) {
-        *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
-        return Status::ok();
-    }
-
-    // Sanity check that the new user has an empty keystore.
-    if (!mKeyStore->isEmpty(userId)) {
-        ALOGW("New user %d's keystore not empty. Clearing old entries.", userId);
-    }
-    // Unconditionally clear the keystore, just to be safe.
-    mKeyStore->resetUser(userId, false);
-    if (parentId != -1) {
-        // This profile must share the same master key password as the parent profile. Because the
-        // password of the parent profile is not known here, the best we can do is copy the parent's
-        // master key and master key file. This makes this profile use the same master key as the
-        // parent profile, forever.
-        *aidl_return = static_cast<int32_t>(mKeyStore->copyMasterKey(parentId, userId));
-        return Status::ok();
-    } else {
-        *aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
-        return Status::ok();
-    }
-}
-
-Status KeyStoreService::onUserRemoved(int32_t userId, int32_t* aidl_return) {
-    if (!checkBinderPermission(P_USER_CHANGED)) {
-        *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
-        return Status::ok();
-    }
-
-    mKeyStore->resetUser(userId, false);
-    *aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
-    return Status::ok();
-}
-
-Status KeyStoreService::lock(int32_t userId, int32_t* aidl_return) {
-    if (!checkBinderPermission(P_LOCK)) {
-        *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
-        return Status::ok();
-    }
-
-    State state = mKeyStore->getState(userId);
-    if (state != ::STATE_NO_ERROR) {
-        ALOGD("calling lock in state: %d", state);
-        *aidl_return = static_cast<int32_t>(ResponseCode(state));
-        return Status::ok();
-    }
-
-    mKeyStore->getEnforcementPolicy().set_device_locked(true, userId);
-    mKeyStore->lock(userId);
-    *aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
-    return Status::ok();
-}
-
-Status KeyStoreService::unlock(int32_t userId, const String16& pw, int32_t* aidl_return) {
-    if (!checkBinderPermission(P_UNLOCK)) {
-        *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
-        return Status::ok();
-    }
-
-    State state = mKeyStore->getState(userId);
-    if (state != ::STATE_LOCKED) {
-        switch (state) {
-        case ::STATE_NO_ERROR:
-            ALOGI("calling unlock when already unlocked, ignoring.");
-            break;
-        case ::STATE_UNINITIALIZED:
-            ALOGE("unlock called on uninitialized keystore.");
-            break;
-        default:
-            ALOGE("unlock called on keystore in unknown state: %d", state);
-            break;
-        }
-        *aidl_return = static_cast<int32_t>(ResponseCode(state));
-        return Status::ok();
-    }
-
-    mKeyStore->getEnforcementPolicy().set_device_locked(false, userId);
-    const String8 password8(pw);
-    // read master key, decrypt with password, initialize mMasterKey*.
-    *aidl_return = static_cast<int32_t>(mKeyStore->readMasterKey(password8, userId));
-    return Status::ok();
-}
-
-Status KeyStoreService::isEmpty(int32_t userId, int32_t* aidl_return) {
-    if (!checkBinderPermission(P_IS_EMPTY)) {
-        *aidl_return = static_cast<int32_t>(false);
-        return Status::ok();
-    }
-
-    *aidl_return = static_cast<int32_t>(mKeyStore->isEmpty(userId));
-    return Status::ok();
-}
-
-Status KeyStoreService::grant(const String16& name, int32_t granteeUid,
-                              ::android::String16* aidl_return) {
-    uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    auto result =
-        checkBinderPermissionAndKeystoreState(P_GRANT, /*targetUid=*/-1, /*checkUnlocked=*/false);
-    if (!result.isOk()) {
-        *aidl_return = String16();
-        return Status::ok();
-    }
-
-    String8 name8(name);
-    auto lockedEntry = mKeyStore->getLockedBlobEntryIfExists(name8.string(), callingUid);
-    if (!lockedEntry) {
-        *aidl_return = String16();
-        return Status::ok();
-    }
-
-    *aidl_return = String16(mKeyStore->addGrant(lockedEntry, granteeUid).c_str());
-    return Status::ok();
-}
-
-Status KeyStoreService::ungrant(const String16& name, int32_t granteeUid, int32_t* aidl_return) {
-    uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    KeyStoreServiceReturnCode result =
-        checkBinderPermissionAndKeystoreState(P_GRANT, /*targetUid=*/-1, /*checkUnlocked=*/false);
-    if (!result.isOk()) {
-        *aidl_return = result.getErrorCode();
-        return Status::ok();
-    }
-
-    String8 name8(name);
-
-    auto lockedEntry = mKeyStore->getLockedBlobEntryIfExists(name8.string(), callingUid);
-    if (!lockedEntry) {
-        *aidl_return = static_cast<int32_t>(ResponseCode::KEY_NOT_FOUND);
-    }
-
-    *aidl_return = mKeyStore->removeGrant(lockedEntry, granteeUid);
-    return Status::ok();
-}
-
-Status KeyStoreService::getmtime(const String16& name, int32_t uid, int64_t* time) {
-    uid_t targetUid = getEffectiveUid(uid);
-    if (!checkBinderPermission(P_GET, targetUid)) {
-        ALOGW("permission denied for %d: getmtime", targetUid);
-        *time = -1L;
-        return Status::ok();
-    }
-    String8 name8(name);
-
-    auto lockedEntry = mKeyStore->getLockedBlobEntryIfExists(name8.string(), targetUid);
-    if (!lockedEntry) {
-        ALOGW("could not access key with alias %s for getmtime", name8.string());
-        *time = -1L;
-        return Status::ok();
-    }
-
-    std::string filename = lockedEntry->getKeyBlobPath();
-
-    int fd = TEMP_FAILURE_RETRY(open(filename.c_str(), O_NOFOLLOW, O_RDONLY));
-    if (fd < 0) {
-        ALOGW("could not open %s for getmtime", filename.c_str());
-        *time = -1L;
-        return Status::ok();
-    }
-
-    struct stat s;
-    int ret = fstat(fd, &s);
-    close(fd);
-    if (ret == -1) {
-        ALOGW("could not stat %s for getmtime", filename.c_str());
-        *time = -1L;
-        return Status::ok();
-    }
-
-    *time = static_cast<int64_t>(s.st_mtime);
-    return Status::ok();
-}
-
-Status KeyStoreService::is_hardware_backed(const String16& keyType, int32_t* aidl_return) {
-    *aidl_return = static_cast<int32_t>(mKeyStore->isHardwareBacked(keyType) ? 1 : 0);
-    return Status::ok();
-}
-
-Status KeyStoreService::clear_uid(int64_t targetUid64, int32_t* _aidl_return) {
-    uid_t targetUid = getEffectiveUid(targetUid64);
-    if (!checkBinderPermissionSelfOrSystem(P_CLEAR_UID, targetUid)) {
-        return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
-    }
-    ALOGI("clear_uid %" PRId64, targetUid64);
-
-    mKeyStore->removeAllGrantsToUid(targetUid);
-
-    ResponseCode rc;
-    std::list<LockedKeyBlobEntry> entries;
-    auto userDirName = mKeyStore->getUserStateDB().getUserStateByUid(targetUid)->getUserDirName();
-
-    // list has a fence making sure no workers are modifying blob files before iterating the
-    // data base. All returned entries are locked.
-    std::tie(rc, entries) = LockedKeyBlobEntry::list(
-        userDirName, [&](uid_t uid, const std::string&) -> bool { return uid == targetUid; });
-
-    if (rc != ResponseCode::NO_ERROR) {
-        return AIDL_RETURN(rc);
-    }
-
-    for (LockedKeyBlobEntry& lockedEntry : entries) {
-        if (get_app_id(targetUid) == AID_SYSTEM) {
-            Blob keyBlob;
-            Blob charBlob;
-            std::tie(rc, keyBlob, charBlob) = mKeyStore->get(lockedEntry);
-            if (rc == ResponseCode::NO_ERROR && keyBlob.isCriticalToDeviceEncryption()) {
-                // Do not clear keys critical to device encryption under system uid.
-                continue;
-            }
-        }
-        mKeyStore->del(lockedEntry);
-    }
-    return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::addRngEntropy(
-    const ::android::sp<::android::security::keystore::IKeystoreResponseCallback>& cb,
-    const ::std::vector<uint8_t>& entropy, int32_t flags, int32_t* _aidl_return) {
-    auto device = mKeyStore->getDevice(flagsToSecurityLevel(flags));
-    if (!device) {
-        return AIDL_RETURN(ErrorCode::HARDWARE_TYPE_UNAVAILABLE);
-    }
-
-    device->addRngEntropy(entropy, [device, cb](Return<ErrorCode> rc) {
-        cb->onFinished(KeyStoreServiceReturnCode(KS_HANDLE_HIDL_ERROR(device, rc)));
-    });
-
-    return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::generateKey(
-    const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
-    const String16& name, const KeymasterArguments& params, const ::std::vector<uint8_t>& entropy,
-    int uid, int flags, int32_t* _aidl_return) {
-    uid = getEffectiveUid(uid);
-    auto logOnScopeExit = android::base::make_scope_guard([&] {
-        if (__android_log_security()) {
-            android_log_event_list(SEC_TAG_AUTH_KEY_GENERATED)
-                << int32_t(*_aidl_return == static_cast<int32_t>(ResponseCode::NO_ERROR))
-                << String8(name) << int32_t(uid) << LOG_ID_SECURITY;
-        }
-    });
-    KeyStoreServiceReturnCode rc =
-        checkBinderPermissionAndKeystoreState(P_INSERT, uid, flags & KEYSTORE_FLAG_ENCRYPTED);
-    if (!rc.isOk()) {
-        return AIDL_RETURN(rc);
-    }
-    if ((flags & KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION) && get_app_id(uid) != AID_SYSTEM) {
-        ALOGE("Non-system uid %d cannot set FLAG_CRITICAL_TO_DEVICE_ENCRYPTION", uid);
-        return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
-    }
-
-    if (containsTag(params.getParameters(), Tag::INCLUDE_UNIQUE_ID)) {
-        if (!checkBinderPermission(P_GEN_UNIQUE_ID)) {
-            return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
-        }
-    }
-
-    SecurityLevel securityLevel = flagsToSecurityLevel(flags);
-    auto dev = mKeyStore->getDevice(securityLevel);
-    if (!dev) {
-        return AIDL_RETURN(ErrorCode::HARDWARE_TYPE_UNAVAILABLE);
-    }
-
-    String8 name8(name);
-    auto lockedEntry = mKeyStore->getLockedBlobEntryIfNotExists(name8.string(), uid);
-    if (!lockedEntry) {
-        return AIDL_RETURN(ResponseCode::KEY_ALREADY_EXISTS);
-    }
-
-    logOnScopeExit.Disable();
-
-    dev->generateKey(
-        std::move(lockedEntry), params.getParameters(), entropy, flags,
-        [cb, uid, name](KeyStoreServiceReturnCode rc, KeyCharacteristics keyCharacteristics) {
-            if (__android_log_security()) {
-                android_log_event_list(SEC_TAG_AUTH_KEY_GENERATED)
-                    << rc.isOk() << String8(name) << int32_t(uid) << LOG_ID_SECURITY;
-            }
-            cb->onFinished(rc,
-                           android::security::keymaster::KeyCharacteristics(keyCharacteristics));
-        });
-
-    return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::getKeyCharacteristics(
-    const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
-    const String16& name, const ::android::security::keymaster::KeymasterBlob& clientId,
-    const ::android::security::keymaster::KeymasterBlob& appData, int32_t uid,
-    int32_t* _aidl_return) {
-
-    uid_t targetUid = getEffectiveUid(uid);
-    uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    if (!is_granted_to(callingUid, targetUid)) {
-        ALOGW("uid %d not permitted to act for uid %d in getKeyCharacteristics", callingUid,
-              targetUid);
-        return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
-    }
-
-    String8 name8(name);
-
-    ResponseCode rc;
-    Blob keyBlob;
-    Blob charBlob;
-    LockedKeyBlobEntry lockedEntry;
-
-    std::tie(rc, keyBlob, charBlob, lockedEntry) =
-        mKeyStore->getKeyForName(name8, targetUid, TYPE_KEYMASTER_10);
-
-    if (rc != ResponseCode::NO_ERROR) {
-        return AIDL_RETURN(rc);
-    }
-
-    auto dev = mKeyStore->getDevice(keyBlob);
-    if (!dev) {
-        return AIDL_RETURN(ResponseCode::SYSTEM_ERROR);
-    }
-
-    // If the charBlob is up to date, it simply moves the argument blobs to the returned blobs
-    // and extracts the characteristics on the way. Otherwise it updates the cache file with data
-    // from keymaster. It may also upgrade the key blob.
-    dev->getKeyCharacteristics(
-        std::move(lockedEntry), clientId.getData(), appData.getData(), std::move(keyBlob),
-        std::move(charBlob),
-        [cb](KeyStoreServiceReturnCode rc, KeyCharacteristics keyCharacteristics) {
-            cb->onFinished(rc,
-                           android::security::keymaster::KeyCharacteristics(keyCharacteristics));
-        });
-
-    return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::importKey(
-    const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
-    const String16& name, const KeymasterArguments& params, int32_t format,
-    const ::std::vector<uint8_t>& keyData, int uid, int flags, int32_t* _aidl_return) {
-    uid = getEffectiveUid(uid);
-    auto logOnScopeExit = android::base::make_scope_guard([&] {
-        if (__android_log_security()) {
-            android_log_event_list(SEC_TAG_KEY_IMPORTED)
-                << int32_t(*_aidl_return == static_cast<int32_t>(ResponseCode::NO_ERROR))
-                << String8(name) << int32_t(uid) << LOG_ID_SECURITY;
-        }
-    });
-    KeyStoreServiceReturnCode rc =
-        checkBinderPermissionAndKeystoreState(P_INSERT, uid, flags & KEYSTORE_FLAG_ENCRYPTED);
-    if (!rc.isOk()) {
-        LOG(ERROR) << "permissission denied";
-        return AIDL_RETURN(rc);
-    }
-    if ((flags & KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION) && get_app_id(uid) != AID_SYSTEM) {
-        ALOGE("Non-system uid %d cannot set FLAG_CRITICAL_TO_DEVICE_ENCRYPTION", uid);
-        return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
-    }
-
-    SecurityLevel securityLevel = flagsToSecurityLevel(flags);
-    auto dev = mKeyStore->getDevice(securityLevel);
-    if (!dev) {
-        LOG(ERROR) << "importKey - cound not get keymaster device";
-        return AIDL_RETURN(ErrorCode::HARDWARE_TYPE_UNAVAILABLE);
-    }
-
-    String8 name8(name);
-    auto lockedEntry = mKeyStore->getLockedBlobEntryIfNotExists(name8.string(), uid);
-    if (!lockedEntry) {
-        LOG(ERROR) << "importKey - key: " << name8.string() << " " << int(uid)
-                   << " already exists.";
-        return AIDL_RETURN(ResponseCode::KEY_ALREADY_EXISTS);
-    }
-
-    logOnScopeExit.Disable();
-
-    dev->importKey(
-        std::move(lockedEntry), params.getParameters(), KeyFormat(format), keyData, flags,
-        [cb, uid, name](KeyStoreServiceReturnCode rc, KeyCharacteristics keyCharacteristics) {
-            if (__android_log_security()) {
-                android_log_event_list(SEC_TAG_KEY_IMPORTED)
-                    << rc.isOk() << String8(name) << int32_t(uid) << LOG_ID_SECURITY;
-            }
-            cb->onFinished(rc,
-                           android::security::keymaster::KeyCharacteristics(keyCharacteristics));
-        });
-
-    return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::exportKey(
-    const ::android::sp<::android::security::keystore::IKeystoreExportKeyCallback>& cb,
-    const String16& name, int32_t format,
-    const ::android::security::keymaster::KeymasterBlob& clientId,
-    const ::android::security::keymaster::KeymasterBlob& appData, int32_t uid,
-    int32_t* _aidl_return) {
-
-    uid_t targetUid = getEffectiveUid(uid);
-    uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    if (!is_granted_to(callingUid, targetUid)) {
-        ALOGW("uid %d not permitted to act for uid %d in exportKey", callingUid, targetUid);
-        return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
-    }
-
-    String8 name8(name);
-
-    KeyStoreServiceReturnCode rc;
-    Blob keyBlob;
-    Blob charBlob;
-    LockedKeyBlobEntry lockedEntry;
-
-    std::tie(rc, keyBlob, charBlob, lockedEntry) =
-        mKeyStore->getKeyForName(name8, targetUid, TYPE_KEYMASTER_10);
-    if (!rc.isOk()) {
-        return AIDL_RETURN(rc);
-    }
-
-    auto dev = mKeyStore->getDevice(keyBlob);
-
-    dev->exportKey(std::move(lockedEntry), KeyFormat(format), clientId.getData(), appData.getData(),
-                   std::move(keyBlob), std::move(charBlob),
-                   [cb](ExportResult exportResult) { cb->onFinished(exportResult); });
-
-    return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::begin(const sp<IKeystoreOperationResultCallback>& cb,
-                              const sp<IBinder>& appToken, const String16& name, int32_t purpose,
-                              bool pruneable, const KeymasterArguments& params,
-                              const ::std::vector<uint8_t>& entropy, int32_t uid,
-                              int32_t* _aidl_return) {
-    uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    uid_t targetUid = getEffectiveUid(uid);
-    if (!is_granted_to(callingUid, targetUid)) {
-        ALOGW("uid %d not permitted to act for uid %d in begin", callingUid, targetUid);
-        return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
-    }
-    if (!pruneable && get_app_id(callingUid) != AID_SYSTEM) {
-        ALOGE("Non-system uid %d trying to start non-pruneable operation", callingUid);
-        return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
-    }
-    if (!checkAllowedOperationParams(params.getParameters())) {
-        return AIDL_RETURN(ErrorCode::INVALID_ARGUMENT);
-    }
-
-    String8 name8(name);
-    Blob keyBlob;
-    Blob charBlob;
-    LockedKeyBlobEntry lockedEntry;
-    ResponseCode rc;
-
-    std::tie(rc, keyBlob, charBlob, lockedEntry) =
-        mKeyStore->getKeyForName(name8, targetUid, TYPE_KEYMASTER_10);
-
-    if (rc == ResponseCode::LOCKED && keyBlob.isSuperEncrypted()) {
-        return AIDL_RETURN(ErrorCode::KEY_USER_NOT_AUTHENTICATED);
-    }
-    if (rc != ResponseCode::NO_ERROR) return AIDL_RETURN(rc);
-
-    auto dev = mKeyStore->getDevice(keyBlob);
-    AuthorizationSet opParams = params.getParameters();
-
-    dev->begin(std::move(lockedEntry), appToken, std::move(keyBlob), std::move(charBlob), pruneable,
-               static_cast<KeyPurpose>(purpose), std::move(opParams), entropy,
-               [this, cb, dev](OperationResult result_) {
-                   if (result_.resultCode.isOk() ||
-                       result_.resultCode == ResponseCode::OP_AUTH_NEEDED) {
-                       mKeyStore->addOperationDevice(result_.token, dev);
-                   }
-                   cb->onFinished(result_);
-               });
-
-    return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::update(const ::android::sp<IKeystoreOperationResultCallback>& cb,
-                               const ::android::sp<::android::IBinder>& token,
-                               const ::android::security::keymaster::KeymasterArguments& params,
-                               const ::std::vector<uint8_t>& input, int32_t* _aidl_return) {
-    if (!checkAllowedOperationParams(params.getParameters())) {
-        return AIDL_RETURN(ErrorCode::INVALID_ARGUMENT);
-    }
-
-    auto dev = mKeyStore->getOperationDevice(token);
-    if (!dev) {
-        return AIDL_RETURN(ErrorCode::INVALID_OPERATION_HANDLE);
-    }
-
-    dev->update(token, params.getParameters(), input, [this, cb, token](OperationResult result_) {
-        if (!result_.resultCode.isOk()) {
-            mKeyStore->removeOperationDevice(token);
-        }
-        cb->onFinished(result_);
-    });
-
-    return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::finish(const ::android::sp<IKeystoreOperationResultCallback>& cb,
-                               const ::android::sp<::android::IBinder>& token,
-                               const ::android::security::keymaster::KeymasterArguments& params,
-                               const ::std::vector<uint8_t>& input,
-                               const ::std::vector<uint8_t>& signature,
-                               const ::std::vector<uint8_t>& entropy, int32_t* _aidl_return) {
-    if (!checkAllowedOperationParams(params.getParameters())) {
-        return AIDL_RETURN(ErrorCode::INVALID_ARGUMENT);
-    }
-
-    auto dev = mKeyStore->getOperationDevice(token);
-    if (!dev) {
-        return AIDL_RETURN(ErrorCode::INVALID_OPERATION_HANDLE);
-    }
-
-    dev->finish(token, params.getParameters(), input, signature, entropy,
-                [this, cb, token](OperationResult result_) {
-                    mKeyStore->removeOperationDevice(token);
-                    cb->onFinished(result_);
-                });
-
-    return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::abort(const ::android::sp<IKeystoreResponseCallback>& cb,
-                              const ::android::sp<::android::IBinder>& token,
-                              int32_t* _aidl_return) {
-    auto dev = mKeyStore->getOperationDevice(token);
-    if (!dev) {
-        return AIDL_RETURN(ErrorCode::INVALID_OPERATION_HANDLE);
-    }
-
-    dev->abort(token, [this, cb, token](KeyStoreServiceReturnCode rc) {
-        mKeyStore->removeOperationDevice(token);
-        cb->onFinished(rc);
-    });
-
-    return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::addAuthToken(const ::std::vector<uint8_t>& authTokenAsVector,
-                                     int32_t* aidl_return) {
-
-    // TODO(swillden): When gatekeeper and fingerprint are ready, this should be updated to
-    // receive a HardwareAuthToken, rather than an opaque byte array.
-
-    if (!checkBinderPermission(P_ADD_AUTH)) {
-        ALOGW("addAuthToken: permission denied for %d", IPCThreadState::self()->getCallingUid());
-        *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
-        return Status::ok();
-    }
-    if (authTokenAsVector.size() != sizeof(hw_auth_token_t)) {
-        *aidl_return = KeyStoreServiceReturnCode(ErrorCode::INVALID_ARGUMENT).getErrorCode();
-        return Status::ok();
-    }
-
-    hw_auth_token_t authToken;
-    memcpy(reinterpret_cast<void*>(&authToken), authTokenAsVector.data(), sizeof(hw_auth_token_t));
-    if (authToken.version != 0) {
-        *aidl_return = KeyStoreServiceReturnCode(ErrorCode::INVALID_ARGUMENT).getErrorCode();
-        return Status::ok();
-    }
-
-    mKeyStore->getAuthTokenTable().AddAuthenticationToken(
-        hidlVec2AuthToken(hidl_vec<uint8_t>(authTokenAsVector)));
-    *aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
-    return Status::ok();
-}
-
-Status KeyStoreService::getTokensForCredstore(int64_t challenge, int64_t secureUserId,
-                                              int32_t authTokenMaxAgeMillis,
-                                              const ::android::sp<ICredstoreTokenCallback>& cb) {
-    uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    if (callingUid != AID_CREDSTORE) {
-        return Status::fromServiceSpecificError(static_cast<int32_t>(0));
-    }
-
-    auto [err, authToken] = mKeyStore->getAuthTokenTable().FindAuthorizationForCredstore(
-        challenge, secureUserId, authTokenMaxAgeMillis);
-    // It's entirely possible we couldn't find an authToken (e.g. no user auth
-    // happened within the requested deadline) and in that case, we just
-    // callback immediately signaling success but just not returning any tokens.
-    if (err != AuthTokenTable::OK) {
-        cb->onFinished(true, {} /* serializedAuthToken */, {} /* serializedVerificationToken */);
-        return Status::ok();
-    }
-
-    // If we did find an authToken, get a verificationToken as well...
-    //
-    std::vector<uint8_t> serializedAuthToken = authToken2HidlVec(authToken);
-    std::vector<uint8_t> serializedVerificationToken;
-    std::shared_ptr<KeymasterWorker> dev = mKeyStore->getDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
-    if (!dev) {
-        LOG(ERROR) << "Unable to get KM device for SecurityLevel::TRUSTED_ENVIRONMENT";
-        dev = mKeyStore->getDevice(SecurityLevel::SOFTWARE);
-        if (!dev) {
-            LOG(ERROR) << "Unable to get KM device for SecurityLevel::SOFTWARE";
-            cb->onFinished(false, {}, {});
-            return Status::fromServiceSpecificError(static_cast<int32_t>(0));
-        }
-    }
-
-    dev->verifyAuthorization(
-        challenge, {} /* params */, authToken,
-        [serializedAuthToken, cb](KeyStoreServiceReturnCode rc, HardwareAuthToken,
-                                  VerificationToken verificationToken) {
-            if (rc != ErrorCode::OK) {
-                LOG(ERROR) << "verifyAuthorization failed, rc=" << rc;
-                cb->onFinished(false, {}, {});
-                return;
-            }
-            std::optional<std::vector<uint8_t>> serializedVerificationToken =
-                serializeVerificationToken(verificationToken);
-            if (!serializedVerificationToken) {
-                LOG(ERROR) << "Error serializing verificationToken";
-                cb->onFinished(false, {}, {});
-                return;
-            }
-            cb->onFinished(true, serializedAuthToken, serializedVerificationToken.value());
-        });
-
-    return Status::ok();
-}
-
-bool isDeviceIdAttestationRequested(const KeymasterArguments& params) {
-    const hardware::hidl_vec<KeyParameter>& paramsVec = params.getParameters();
-    for (size_t i = 0; i < paramsVec.size(); ++i) {
-        switch (paramsVec[i].tag) {
-        case Tag::ATTESTATION_ID_BRAND:
-        case Tag::ATTESTATION_ID_DEVICE:
-        case Tag::ATTESTATION_ID_MANUFACTURER:
-        case Tag::ATTESTATION_ID_MODEL:
-        case Tag::ATTESTATION_ID_PRODUCT:
-        case Tag::ATTESTATION_ID_IMEI:
-        case Tag::ATTESTATION_ID_MEID:
-        case Tag::ATTESTATION_ID_SERIAL:
-            return true;
-        default:
-            continue;
-        }
-    }
-    return false;
-}
-
-Status KeyStoreService::attestKey(
-    const ::android::sp<::android::security::keystore::IKeystoreCertificateChainCallback>& cb,
-    const String16& name, const KeymasterArguments& params, int32_t* _aidl_return) {
-    // check null output if method signature is updated and return ErrorCode::OUTPUT_PARAMETER_NULL
-    if (!checkAllowedOperationParams(params.getParameters())) {
-        return AIDL_RETURN(ErrorCode::INVALID_ARGUMENT);
-    }
-
-    uid_t callingUid = IPCThreadState::self()->getCallingUid();
-
-    if (isDeviceIdAttestationRequested(params) && (get_app_id(callingUid) != AID_SYSTEM)) {
-        return AIDL_RETURN(KeyStoreServiceReturnCode(ErrorCode::INVALID_ARGUMENT));
-    }
-
-    AuthorizationSet mutableParams = params.getParameters();
-    KeyStoreServiceReturnCode rc = updateParamsForAttestation(callingUid, &mutableParams);
-
-    auto logErrorOnReturn = android::base::make_scope_guard(
-        [&] { logKeystoreKeyAttestationEvent(false /*wasSuccessful*/, rc.getErrorCode()); });
-
-    if (!rc.isOk()) {
-        return AIDL_RETURN(rc);
-    }
-
-    String8 name8(name);
-    Blob keyBlob;
-    Blob charBlob;
-    LockedKeyBlobEntry lockedEntry;
-
-    std::tie(rc, keyBlob, charBlob, lockedEntry) =
-        mKeyStore->getKeyForName(name8, callingUid, TYPE_KEYMASTER_10);
-
-    if (!rc.isOk()) {
-        return AIDL_RETURN(rc);
-    }
-
-    logErrorOnReturn.Disable();
-
-    auto dev = mKeyStore->getDevice(keyBlob);
-    auto hidlKey = blob2hidlVec(keyBlob);
-    dev->attestKey(
-        std::move(hidlKey), mutableParams.hidl_data(),
-        [dev, cb](Return<void> rc,
-                  std::tuple<ErrorCode, hidl_vec<hidl_vec<uint8_t>>>&& hidlResult) {
-            auto& [ret, certChain] = hidlResult;
-            if (!rc.isOk()) {
-                logKeystoreKeyAttestationEvent(false /*wasSuccessful*/,
-                                               static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
-                cb->onFinished(KeyStoreServiceReturnCode(ResponseCode::SYSTEM_ERROR), {});
-            } else if (ret != ErrorCode::OK) {
-                KeyStoreServiceReturnCode ksrc(ret);
-                logKeystoreKeyAttestationEvent(false /*wasSuccessful*/, ksrc.getErrorCode());
-                dev->logIfKeymasterVendorError(ret);
-                cb->onFinished(ksrc, {});
-            } else {
-                KeyStoreServiceReturnCode ksrc(ret);
-                logKeystoreKeyAttestationEvent(true /*wasSuccessful*/, ksrc.getErrorCode());
-                cb->onFinished(ksrc, KeymasterCertificateChain(std::move(certChain)));
-            }
-        });
-
-    return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-// My IDE defines "CAPTURE_MOVE(x) x" because it does not understand generalized lambda captures.
-// It should never be redefined by a build system though.
-#ifndef CAPTURE_MOVE
-#define CAPTURE_MOVE(x) x = std::move(x)
-#endif
-
-Status KeyStoreService::attestDeviceIds(
-    const ::android::sp<::android::security::keystore::IKeystoreCertificateChainCallback>& cb,
-    const KeymasterArguments& params, int32_t* _aidl_return) {
-    // check null output if method signature is updated and return ErrorCode::OUTPUT_PARAMETER_NULL
-
-    if (!checkAllowedOperationParams(params.getParameters())) {
-        return AIDL_RETURN(ErrorCode::INVALID_ARGUMENT);
-    }
-
-    if (!isDeviceIdAttestationRequested(params)) {
-        // There is an attestKey() method for attesting keys without device ID attestation.
-        return AIDL_RETURN(ErrorCode::INVALID_ARGUMENT);
-    }
-
-    uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
-    if (binder == nullptr) {
-        return AIDL_RETURN(ErrorCode::CANNOT_ATTEST_IDS);
-    }
-    if (!interface_cast<IPermissionController>(binder)->checkPermission(
-            String16("android.permission.READ_PRIVILEGED_PHONE_STATE"),
-            IPCThreadState::self()->getCallingPid(), callingUid)) {
-        return AIDL_RETURN(ErrorCode::CANNOT_ATTEST_IDS);
-    }
-
-    AuthorizationSet mutableParams = params.getParameters();
-    KeyStoreServiceReturnCode rc = updateParamsForAttestation(callingUid, &mutableParams);
-    if (!rc.isOk()) {
-        return AIDL_RETURN(rc);
-    }
-
-    // Generate temporary key.
-    auto dev = mKeyStore->getDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
-
-    if (!dev) {
-        return AIDL_RETURN(ResponseCode::SYSTEM_ERROR);
-    }
-
-
-    AuthorizationSet keyCharacteristics;
-    keyCharacteristics.push_back(TAG_PURPOSE, KeyPurpose::VERIFY);
-    keyCharacteristics.push_back(TAG_ALGORITHM, Algorithm::EC);
-    keyCharacteristics.push_back(TAG_DIGEST, Digest::SHA_2_256);
-    keyCharacteristics.push_back(TAG_NO_AUTH_REQUIRED);
-    keyCharacteristics.push_back(TAG_EC_CURVE, EcCurve::P_256);
-
-    std::promise<KeyStoreServiceReturnCode> resultPromise;
-    auto resultFuture = resultPromise.get_future();
-
-    dev->generateKey(
-        keyCharacteristics.hidl_data(),
-        [cb, dev, CAPTURE_MOVE(mutableParams)](
-            Return<void> rc,
-            std::tuple<ErrorCode, ::std::vector<uint8_t>, KeyCharacteristics>&& hidlResult) {
-            auto& [ret, hidlKeyBlob_, dummyCharacteristics] = hidlResult;
-            auto hidlKeyBlob = std::move(hidlKeyBlob_);
-            if (!rc.isOk()) {
-                cb->onFinished(KeyStoreServiceReturnCode(ResponseCode::SYSTEM_ERROR), {});
-                return;
-            }
-            if (ret != ErrorCode::OK) {
-                dev->logIfKeymasterVendorError(ret);
-                cb->onFinished(KeyStoreServiceReturnCode(ret), {});
-                return;
-            }
-            dev->attestKey(
-                hidlKeyBlob, mutableParams.hidl_data(),
-                [cb, dev,
-                 hidlKeyBlob](Return<void> rc,
-                              std::tuple<ErrorCode, hidl_vec<hidl_vec<uint8_t>>>&& hidlResult) {
-                    auto& [ret, certChain] = hidlResult;
-                    // schedule temp key for deletion
-                    dev->deleteKey(std::move(hidlKeyBlob), [dev](Return<ErrorCode> rc) {
-                        // log error but don't return an error
-                        KS_HANDLE_HIDL_ERROR(dev, rc);
-                    });
-                    if (!rc.isOk()) {
-                        cb->onFinished(KeyStoreServiceReturnCode(ResponseCode::SYSTEM_ERROR), {});
-                        return;
-                    }
-                    if (ret == ErrorCode::OK) {
-                        cb->onFinished(
-                            KeyStoreServiceReturnCode(ret),
-                            ::android::security::keymaster::KeymasterCertificateChain(certChain));
-                    } else {
-                        dev->logIfKeymasterVendorError(ret);
-                        cb->onFinished(KeyStoreServiceReturnCode(ret), {});
-                    }
-                });
-        });
-
-    return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::onDeviceOffBody(int32_t* aidl_return) {
-    // TODO(tuckeris): add permission check.  This should be callable from ClockworkHome only.
-    mKeyStore->getAuthTokenTable().onDeviceOffBody();
-    *aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
-    return Status::ok();
-}
-
-Status KeyStoreService::importWrappedKey(
-    const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
-    const ::android::String16& wrappedKeyAlias, const ::std::vector<uint8_t>& wrappedKey,
-    const ::android::String16& wrappingKeyAlias, const ::std::vector<uint8_t>& maskingKey,
-    const KeymasterArguments& params, int64_t rootSid, int64_t fingerprintSid,
-    int32_t* _aidl_return) {
-
-    uid_t callingUid = IPCThreadState::self()->getCallingUid();
-
-    if (!checkBinderPermission(P_INSERT, callingUid)) {
-        return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
-    }
-
-    String8 wrappingKeyName8(wrappingKeyAlias);
-
-    KeyStoreServiceReturnCode rc;
-    Blob wrappingKeyBlob;
-    Blob wrappingCharBlob;
-    LockedKeyBlobEntry wrappingLockedEntry;
-
-    std::tie(rc, wrappingKeyBlob, wrappingCharBlob, wrappingLockedEntry) =
-        mKeyStore->getKeyForName(wrappingKeyName8, callingUid, TYPE_KEYMASTER_10);
-    if (!rc.isOk()) {
-        return AIDL_RETURN(rc);
-    }
-
-    String8 wrappedKeyName8(wrappedKeyAlias);
-    auto wrappedLockedEntry =
-        mKeyStore->getLockedBlobEntryIfNotExists(wrappedKeyName8.string(), callingUid);
-    if (!wrappedLockedEntry) {
-        return AIDL_RETURN(ResponseCode::KEY_ALREADY_EXISTS);
-    }
-
-    SecurityLevel securityLevel = wrappingKeyBlob.getSecurityLevel();
-    auto dev = mKeyStore->getDevice(securityLevel);
-    if (!dev) {
-        return AIDL_RETURN(ErrorCode::HARDWARE_TYPE_UNAVAILABLE);
-    }
-
-    dev->importWrappedKey(
-        std::move(wrappingLockedEntry), std::move(wrappedLockedEntry), wrappedKey, maskingKey,
-        params.getParameters(), std::move(wrappingKeyBlob), std::move(wrappingCharBlob), rootSid,
-        fingerprintSid, [cb](KeyStoreServiceReturnCode rc, KeyCharacteristics keyCharacteristics) {
-            cb->onFinished(rc,
-                           ::android::security::keymaster::KeyCharacteristics(keyCharacteristics));
-        });
-
-    return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-Status KeyStoreService::presentConfirmationPrompt(const sp<IBinder>& listener,
-                                                  const String16& promptText,
-                                                  const ::std::vector<uint8_t>& extraData,
-                                                  const String16& locale, int32_t uiOptionsAsFlags,
-                                                  int32_t* aidl_return) {
-    return mKeyStore->getConfirmationManager().presentConfirmationPrompt(
-        listener, promptText, extraData, locale, uiOptionsAsFlags, aidl_return);
-}
-
-Status KeyStoreService::cancelConfirmationPrompt(const sp<IBinder>& listener,
-                                                 int32_t* aidl_return) {
-    return mKeyStore->getConfirmationManager().cancelConfirmationPrompt(listener, aidl_return);
-}
-
-Status KeyStoreService::isConfirmationPromptSupported(bool* aidl_return) {
-    return mKeyStore->getConfirmationManager().isConfirmationPromptSupported(aidl_return);
-}
-
-/**
- * Get the effective target uid for a binder operation that takes an
- * optional uid as the target.
- */
-uid_t KeyStoreService::getEffectiveUid(int32_t targetUid) {
-    if (targetUid == UID_SELF) {
-        return IPCThreadState::self()->getCallingUid();
-    }
-    return static_cast<uid_t>(targetUid);
-}
-
-/**
- * Check if the caller of the current binder method has the required
- * permission and if acting on other uids the grants to do so.
- */
-bool KeyStoreService::checkBinderPermission(perm_t permission, int32_t targetUid) {
-    uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    pid_t spid = IPCThreadState::self()->getCallingPid();
-    const char* ssid = IPCThreadState::self()->getCallingSid();
-    if (!has_permission(callingUid, permission, spid, ssid)) {
-        ALOGW("permission %s denied for %d", get_perm_label(permission), callingUid);
-        return false;
-    }
-    if (!is_granted_to(callingUid, getEffectiveUid(targetUid))) {
-        ALOGW("uid %d not granted to act for %d", callingUid, targetUid);
-        return false;
-    }
-    return true;
-}
-
-/**
- * Check if the caller of the current binder method has the required
- * permission and the target uid is the caller or the caller is system.
- */
-bool KeyStoreService::checkBinderPermissionSelfOrSystem(perm_t permission, int32_t targetUid) {
-    uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    pid_t spid = IPCThreadState::self()->getCallingPid();
-    const char* ssid = IPCThreadState::self()->getCallingSid();
-    if (!has_permission(callingUid, permission, spid, ssid)) {
-        ALOGW("permission %s denied for %d", get_perm_label(permission), callingUid);
-        return false;
-    }
-    return getEffectiveUid(targetUid) == callingUid || callingUid == AID_SYSTEM;
-}
-
-/**
- * Check if the caller of the current binder method has the required
- * permission or the target of the operation is the caller's uid. This is
- * for operation where the permission is only for cross-uid activity and all
- * uids are allowed to act on their own (ie: clearing all entries for a
- * given uid).
- */
-bool KeyStoreService::checkBinderPermissionOrSelfTarget(perm_t permission, int32_t targetUid) {
-    uid_t callingUid = IPCThreadState::self()->getCallingUid();
-    if (getEffectiveUid(targetUid) == callingUid) {
-        return true;
-    } else {
-        return checkBinderPermission(permission, targetUid);
-    }
-}
-
-/**
- * Helper method to check that the caller has the required permission as
- * well as the keystore is in the unlocked state if checkUnlocked is true.
- *
- * Returns NO_ERROR on success, PERMISSION_DENIED on a permission error and
- * otherwise the state of keystore when not unlocked and checkUnlocked is
- * true.
- */
-KeyStoreServiceReturnCode
-KeyStoreService::checkBinderPermissionAndKeystoreState(perm_t permission, int32_t targetUid,
-                                                       bool checkUnlocked) {
-    if (!checkBinderPermission(permission, targetUid)) {
-        return ResponseCode::PERMISSION_DENIED;
-    }
-    State state = mKeyStore->getState(get_user_id(getEffectiveUid(targetUid)));
-    if (checkUnlocked && !isKeystoreUnlocked(state)) {
-        // All State values coincide with ResponseCodes
-        return static_cast<ResponseCode>(state);
-    }
-
-    return ResponseCode::NO_ERROR;
-}
-
-bool KeyStoreService::isKeystoreUnlocked(State state) {
-    switch (state) {
-    case ::STATE_NO_ERROR:
-        return true;
-    case ::STATE_UNINITIALIZED:
-    case ::STATE_LOCKED:
-        return false;
-    }
-    return false;
-}
-
-/**
- * Check that all KeyParameters provided by the application are allowed. Any parameter that keystore
- * adds itself should be disallowed here.
- */
-bool KeyStoreService::checkAllowedOperationParams(const hidl_vec<KeyParameter>& params) {
-    for (size_t i = 0; i < params.size(); ++i) {
-        switch (params[i].tag) {
-        case Tag::ATTESTATION_APPLICATION_ID:
-        case Tag::RESET_SINCE_ID_ROTATION:
-            return false;
-        default:
-            break;
-        }
-    }
-    return true;
-}
-
-Status KeyStoreService::onKeyguardVisibilityChanged(bool isShowing, int32_t userId,
-                                                    int32_t* _aidl_return) {
-    if (isShowing) {
-        if (!checkBinderPermission(P_LOCK, UID_SELF)) {
-            LOG(WARNING) << "onKeyguardVisibilityChanged called with isShowing == true but "
-                            "without LOCK permission";
-            return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
-        }
-    } else {
-        if (!checkBinderPermission(P_UNLOCK, UID_SELF)) {
-            LOG(WARNING) << "onKeyguardVisibilityChanged called with isShowing == false but "
-                            "without UNLOCK permission";
-            return AIDL_RETURN(ResponseCode::PERMISSION_DENIED);
-        }
-    }
-    mKeyStore->getEnforcementPolicy().set_device_locked(isShowing, userId);
-    return AIDL_RETURN(ResponseCode::NO_ERROR);
-}
-
-}  // namespace keystore
diff --git a/keystore/key_store_service.h b/keystore/key_store_service.h
deleted file mode 100644
index 5fdddb9..0000000
--- a/keystore/key_store_service.h
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (C) 2016 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 KEYSTORE_KEYSTORE_SERVICE_H_
-#define KEYSTORE_KEYSTORE_SERVICE_H_
-
-#include <android/security/keystore/BnKeystoreService.h>
-
-#include "auth_token_table.h"
-#include "confirmation_manager.h"
-
-#include "KeyStore.h"
-#include "keystore_keymaster_enforcement.h"
-#include "operation.h"
-#include "permissions.h"
-
-#include <keystore/ExportResult.h>
-#include <keystore/KeyCharacteristics.h>
-#include <keystore/KeymasterArguments.h>
-#include <keystore/KeymasterBlob.h>
-#include <keystore/KeymasterCertificateChain.h>
-#include <keystore/OperationResult.h>
-#include <keystore/keystore_return_types.h>
-
-#include <mutex>
-
-namespace keystore {
-
-// Class provides implementation for generated BnKeystoreService.h based on
-// gen/aidl/android/security/BnKeystoreService.h generated from
-// java/android/security/IKeystoreService.aidl Note that all generated methods return binder::Status
-// and use last arguments to send actual result to the caller. Private methods don't need to handle
-// binder::Status. Input parameters cannot be null unless annotated with @nullable in .aidl file.
-class KeyStoreService : public android::security::keystore::BnKeystoreService {
-  public:
-    explicit KeyStoreService(sp<KeyStore> keyStore) : mKeyStore(keyStore) {}
-    virtual ~KeyStoreService() = default;
-
-    void binderDied(const android::wp<android::IBinder>& who);
-
-    ::android::binder::Status getState(int32_t userId, int32_t* _aidl_return) override;
-    ::android::binder::Status get(const ::android::String16& name, int32_t uid,
-                                  ::std::vector<uint8_t>* _aidl_return) override;
-    ::android::binder::Status insert(const ::android::String16& name,
-                                     const ::std::vector<uint8_t>& item, int32_t uid, int32_t flags,
-                                     int32_t* _aidl_return) override;
-    ::android::binder::Status del(const ::android::String16& name, int32_t uid,
-                                  int32_t* _aidl_return) override;
-    ::android::binder::Status exist(const ::android::String16& name, int32_t uid,
-                                    int32_t* _aidl_return) override;
-    ::android::binder::Status list(const ::android::String16& namePrefix, int32_t uid,
-                                   ::std::vector<::android::String16>* _aidl_return) override;
-    ::android::binder::Status listUidsOfAuthBoundKeys(std::vector<::std::string>* uids,
-                                                      int32_t* _aidl_return) override;
-
-    ::android::binder::Status onUserPasswordChanged(int32_t userId,
-                                                    const ::android::String16& newPassword,
-                                                    int32_t* _aidl_return) override;
-    ::android::binder::Status lock(int32_t userId, int32_t* _aidl_return) override;
-    ::android::binder::Status unlock(int32_t userId, const ::android::String16& userPassword,
-                                     int32_t* _aidl_return) override;
-    ::android::binder::Status isEmpty(int32_t userId, int32_t* _aidl_return) override;
-    ::android::binder::Status grant(const ::android::String16& name, int32_t granteeUid,
-                                    ::android::String16* _aidl_return) override;
-    ::android::binder::Status ungrant(const ::android::String16& name, int32_t granteeUid,
-                                      int32_t* _aidl_return) override;
-    ::android::binder::Status getmtime(const ::android::String16& name, int32_t uid,
-                                       int64_t* _aidl_return) override;
-    ::android::binder::Status is_hardware_backed(const ::android::String16& string,
-                                                 int32_t* _aidl_return) override;
-    ::android::binder::Status clear_uid(int64_t uid, int32_t* _aidl_return) override;
-    ::android::binder::Status
-    addRngEntropy(const ::android::sp<::android::security::keystore::IKeystoreResponseCallback>& cb,
-                  const ::std::vector<uint8_t>& data, int32_t flags,
-                  int32_t* _aidl_return) override;
-    ::android::binder::Status generateKey(
-        const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
-        const ::android::String16& alias,
-        const ::android::security::keymaster::KeymasterArguments& arguments,
-        const ::std::vector<uint8_t>& entropy, int32_t uid, int32_t flags,
-        int32_t* _aidl_return) override;
-    ::android::binder::Status getKeyCharacteristics(
-        const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
-        const ::android::String16& alias,
-        const ::android::security::keymaster::KeymasterBlob& clientId,
-        const ::android::security::keymaster::KeymasterBlob& appId, int32_t uid,
-        int32_t* _aidl_return) override;
-    ::android::binder::Status importKey(
-        const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
-        const ::android::String16& alias,
-        const ::android::security::keymaster::KeymasterArguments& arguments, int32_t format,
-        const ::std::vector<uint8_t>& keyData, int32_t uid, int32_t flags,
-        int32_t* _aidl_return) override;
-    ::android::binder::Status
-    exportKey(const ::android::sp<::android::security::keystore::IKeystoreExportKeyCallback>& cb,
-              const ::android::String16& alias, int32_t format,
-              const ::android::security::keymaster::KeymasterBlob& clientId,
-              const ::android::security::keymaster::KeymasterBlob& appId, int32_t uid,
-              int32_t* _aidl_return) override;
-    ::android::binder::Status
-    begin(const ::android::sp<::android::security::keystore::IKeystoreOperationResultCallback>& cb,
-          const ::android::sp<::android::IBinder>& appToken, const ::android::String16& alias,
-          int32_t purpose, bool pruneable,
-          const ::android::security::keymaster::KeymasterArguments& params,
-          const ::std::vector<uint8_t>& entropy, int32_t uid, int32_t* _aidl_return) override;
-    ::android::binder::Status
-    update(const ::android::sp<::android::security::keystore::IKeystoreOperationResultCallback>& cb,
-           const ::android::sp<::android::IBinder>& token,
-           const ::android::security::keymaster::KeymasterArguments& params,
-           const ::std::vector<uint8_t>& input, int32_t* _aidl_return) override;
-    ::android::binder::Status
-    finish(const ::android::sp<::android::security::keystore::IKeystoreOperationResultCallback>& cb,
-           const ::android::sp<::android::IBinder>& token,
-           const ::android::security::keymaster::KeymasterArguments& params,
-           const ::std::vector<uint8_t>& input, const ::std::vector<uint8_t>& signature,
-           const ::std::vector<uint8_t>& entropy, int32_t* _aidl_return) override;
-    ::android::binder::Status
-    abort(const ::android::sp<::android::security::keystore::IKeystoreResponseCallback>& cb,
-          const ::android::sp<::android::IBinder>& token, int32_t* _aidl_return) override;
-    ::android::binder::Status addAuthToken(const ::std::vector<uint8_t>& authToken,
-                                           int32_t* _aidl_return) override;
-    ::android::binder::Status getTokensForCredstore(
-        int64_t challenge, int64_t secureUserId, int32_t authTokenMaxAge,
-        const ::android::sp<::android::security::keystore::ICredstoreTokenCallback>& cb) override;
-    ::android::binder::Status onUserAdded(int32_t userId, int32_t parentId,
-                                          int32_t* _aidl_return) override;
-    ::android::binder::Status onUserRemoved(int32_t userId, int32_t* _aidl_return) override;
-    ::android::binder::Status attestKey(
-        const ::android::sp<::android::security::keystore::IKeystoreCertificateChainCallback>& cb,
-        const ::android::String16& alias,
-        const ::android::security::keymaster::KeymasterArguments& params,
-        int32_t* _aidl_return) override;
-    ::android::binder::Status attestDeviceIds(
-        const ::android::sp<::android::security::keystore::IKeystoreCertificateChainCallback>& cb,
-        const ::android::security::keymaster::KeymasterArguments& params,
-        int32_t* _aidl_return) override;
-    ::android::binder::Status onDeviceOffBody(int32_t* _aidl_return) override;
-
-    ::android::binder::Status importWrappedKey(
-        const ::android::sp<::android::security::keystore::IKeystoreKeyCharacteristicsCallback>& cb,
-        const ::android::String16& wrappedKeyAlias, const ::std::vector<uint8_t>& wrappedKey,
-        const ::android::String16& wrappingKeyAlias, const ::std::vector<uint8_t>& maskingKey,
-        const ::android::security::keymaster::KeymasterArguments& params, int64_t rootSid,
-        int64_t fingerprintSid, int32_t* _aidl_return) override;
-
-    ::android::binder::Status presentConfirmationPrompt(
-        const ::android::sp<::android::IBinder>& listener, const ::android::String16& promptText,
-        const ::std::vector<uint8_t>& extraData, const ::android::String16& locale,
-        int32_t uiOptionsAsFlags, int32_t* _aidl_return) override;
-    ::android::binder::Status
-    cancelConfirmationPrompt(const ::android::sp<::android::IBinder>& listener,
-                             int32_t* _aidl_return) override;
-    ::android::binder::Status isConfirmationPromptSupported(bool* _aidl_return) override;
-
-    ::android::binder::Status onKeyguardVisibilityChanged(bool isShowing, int32_t userId,
-                                                          int32_t* _aidl_return) override;
-
-  private:
-    static const int32_t UID_SELF = -1;
-
-    /**
-     * Get the effective target uid for a binder operation that takes an
-     * optional uid as the target.
-     */
-    uid_t getEffectiveUid(int32_t targetUid);
-
-    /**
-     * Check if the caller of the current binder method has the required
-     * permission and if acting on other uids the grants to do so.
-     */
-    bool checkBinderPermission(perm_t permission, int32_t targetUid = UID_SELF);
-
-    /**
-     * Check if the caller of the current binder method has the required
-     * permission and the target uid is the caller or the caller is system.
-     */
-    bool checkBinderPermissionSelfOrSystem(perm_t permission, int32_t targetUid);
-
-    /**
-     * Check if the caller of the current binder method has the required
-     * permission or the target of the operation is the caller's uid. This is
-     * for operation where the permission is only for cross-uid activity and all
-     * uids are allowed to act on their own (ie: clearing all entries for a
-     * given uid).
-     */
-    bool checkBinderPermissionOrSelfTarget(perm_t permission, int32_t targetUid);
-
-    /**
-     * Helper method to check that the caller has the required permission as
-     * well as the keystore is in the unlocked state if checkUnlocked is true.
-     *
-     * Returns NO_ERROR on success, PERMISSION_DENIED on a permission error and
-     * otherwise the state of keystore when not unlocked and checkUnlocked is
-     * true.
-     */
-    KeyStoreServiceReturnCode checkBinderPermissionAndKeystoreState(perm_t permission,
-                                                                    int32_t targetUid = -1,
-                                                                    bool checkUnlocked = true);
-
-    bool isKeystoreUnlocked(State state);
-
-    /**
-     * Check that all keymaster_key_param_t's provided by the application are
-     * allowed. Any parameter that keystore adds itself should be disallowed here.
-     */
-    bool checkAllowedOperationParams(const hidl_vec<KeyParameter>& params);
-
-    void addLegacyBeginParams(const android::String16& name, AuthorizationSet* params);
-
-    KeyStoreServiceReturnCode doLegacySignVerify(const android::String16& name,
-                                                 const hidl_vec<uint8_t>& data,
-                                                 hidl_vec<uint8_t>* out,
-                                                 const hidl_vec<uint8_t>& signature,
-                                                 KeyPurpose purpose);
-
-    /**
-     * Adds a Confirmation Token to the key parameters if needed.
-     */
-    void appendConfirmationTokenIfNeeded(const KeyCharacteristics& keyCharacteristics,
-                                         std::vector<KeyParameter>* params);
-
-    sp<KeyStore> mKeyStore;
-};
-
-};  // namespace keystore
-
-#endif  // KEYSTORE_KEYSTORE_SERVICE_H_
diff --git a/keystore/keyblob_utils.cpp b/keystore/keyblob_utils.cpp
deleted file mode 100644
index 6c2fac9..0000000
--- a/keystore/keyblob_utils.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2012 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 <stdint.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <keystore/keystore.h>
-
-/**
- * When a key is being migrated from a software keymaster implementation
- * to a hardware keymaster implementation, the first 4 bytes of the key_blob
- * given to the hardware implementation will be equal to SOFT_KEY_MAGIC.
- * The hardware implementation should import these PKCS#8 format keys which
- * are encoded like this:
- *
- * 4-byte SOFT_KEY_MAGIC
- *
- * 4-byte 32-bit integer big endian for public_key_length. This may be zero
- *     length which indicates the public key should be derived from the
- *     private key.
- *
- * public_key_length bytes of public key (may be empty)
- *
- * 4-byte 32-bit integer big endian for private_key_length
- *
- * private_key_length bytes of private key
- */
-static const uint8_t SOFT_KEY_MAGIC[] = { 'P', 'K', '#', '8' };
-
-size_t get_softkey_header_size() {
-    return sizeof(SOFT_KEY_MAGIC);
-}
-
-uint8_t* add_softkey_header(uint8_t* key_blob, size_t key_blob_length) {
-    if (key_blob_length < sizeof(SOFT_KEY_MAGIC)) {
-        return nullptr;
-    }
-
-    memcpy(key_blob, SOFT_KEY_MAGIC, sizeof(SOFT_KEY_MAGIC));
-
-    return key_blob + sizeof(SOFT_KEY_MAGIC);
-}
-
-bool is_softkey(const uint8_t* key_blob, const size_t key_blob_length) {
-    if (key_blob_length < sizeof(SOFT_KEY_MAGIC)) {
-        return false;
-    }
-
-    return !memcmp(key_blob, SOFT_KEY_MAGIC, sizeof(SOFT_KEY_MAGIC));
-}
diff --git a/keystore/keymaster_enforcement.cpp b/keystore/keymaster_enforcement.cpp
deleted file mode 100644
index a17cd94..0000000
--- a/keystore/keymaster_enforcement.cpp
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#define LOG_TAG "keystore"
-
-#include "keymaster_enforcement.h"
-
-#include <assert.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <string.h>
-
-#include <openssl/evp.h>
-
-#include <hardware/hw_auth_token.h>
-#include <log/log.h>
-
-#include <list>
-
-#include <keystore/keystore_hidl_support.h>
-
-namespace keystore {
-
-bool is_public_key_algorithm(const AuthorizationSet& auth_set) {
-    auto algorithm = auth_set.GetTagValue(TAG_ALGORITHM);
-    return algorithm.isOk() &&
-           (algorithm.value() == Algorithm::RSA || algorithm.value() == Algorithm::EC);
-}
-
-static ErrorCode authorized_purpose(const KeyPurpose purpose, const AuthorizationSet& auth_set) {
-    switch (purpose) {
-    case KeyPurpose::VERIFY:
-    case KeyPurpose::ENCRYPT:
-    case KeyPurpose::SIGN:
-    case KeyPurpose::DECRYPT:
-        if (auth_set.Contains(TAG_PURPOSE, purpose)) return ErrorCode::OK;
-        return ErrorCode::INCOMPATIBLE_PURPOSE;
-
-    default:
-        return ErrorCode::UNSUPPORTED_PURPOSE;
-    }
-}
-
-inline bool is_origination_purpose(KeyPurpose purpose) {
-    return purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN;
-}
-
-inline bool is_usage_purpose(KeyPurpose purpose) {
-    return purpose == KeyPurpose::DECRYPT || purpose == KeyPurpose::VERIFY;
-}
-
-KeymasterEnforcement::KeymasterEnforcement(uint32_t max_access_time_map_size,
-                                           uint32_t max_access_count_map_size)
-    : access_time_map_(max_access_time_map_size), access_count_map_(max_access_count_map_size) {}
-
-KeymasterEnforcement::~KeymasterEnforcement() {
-}
-
-ErrorCode KeymasterEnforcement::AuthorizeOperation(const KeyPurpose purpose, const km_id_t keyid,
-                                                   const AuthorizationSet& auth_set,
-                                                   const AuthorizationSet& operation_params,
-                                                   const HardwareAuthToken& auth_token,
-                                                   uint64_t op_handle, bool is_begin_operation) {
-    if (is_public_key_algorithm(auth_set)) {
-        switch (purpose) {
-        case KeyPurpose::ENCRYPT:
-        case KeyPurpose::VERIFY:
-            /* Public key operations are always authorized. */
-            return ErrorCode::OK;
-
-        case KeyPurpose::DECRYPT:
-        case KeyPurpose::SIGN:
-            break;
-
-        case KeyPurpose::WRAP_KEY:
-            return ErrorCode::INCOMPATIBLE_PURPOSE;
-        };
-    };
-
-    if (is_begin_operation)
-        return AuthorizeBegin(purpose, keyid, auth_set, operation_params, auth_token);
-    else
-        return AuthorizeUpdateOrFinish(auth_set, auth_token, op_handle);
-}
-
-// For update and finish the only thing to check is user authentication, and then only if it's not
-// timeout-based.
-ErrorCode KeymasterEnforcement::AuthorizeUpdateOrFinish(const AuthorizationSet& auth_set,
-                                                        const HardwareAuthToken& auth_token,
-                                                        uint64_t op_handle) {
-    int auth_type_index = -1;
-    for (size_t pos = 0; pos < auth_set.size(); ++pos) {
-        switch (auth_set[pos].tag) {
-        case Tag::NO_AUTH_REQUIRED:
-        case Tag::AUTH_TIMEOUT:
-            // If no auth is required or if auth is timeout-based, we have nothing to check.
-            return ErrorCode::OK;
-
-        case Tag::USER_AUTH_TYPE:
-            auth_type_index = pos;
-            break;
-
-        default:
-            break;
-        }
-    }
-
-    // Note that at this point we should be able to assume that authentication is required, because
-    // authentication is required if KM_TAG_NO_AUTH_REQUIRED is absent.  However, there are legacy
-    // keys which have no authentication-related tags, so we assume that absence is equivalent to
-    // presence of KM_TAG_NO_AUTH_REQUIRED.
-    //
-    // So, if we found KM_TAG_USER_AUTH_TYPE or if we find KM_TAG_USER_SECURE_ID then authentication
-    // is required.  If we find neither, then we assume authentication is not required and return
-    // success.
-    bool authentication_required = (auth_type_index != -1);
-    for (auto& param : auth_set) {
-        auto user_secure_id = authorizationValue(TAG_USER_SECURE_ID, param);
-        if (user_secure_id.isOk()) {
-            authentication_required = true;
-            int auth_timeout_index = -1;
-            if (auth_token.mac.size() &&
-                AuthTokenMatches(auth_set, auth_token, user_secure_id.value(), auth_type_index,
-                                 auth_timeout_index, op_handle, false /* is_begin_operation */))
-                return ErrorCode::OK;
-        }
-    }
-
-    if (authentication_required) return ErrorCode::KEY_USER_NOT_AUTHENTICATED;
-
-    return ErrorCode::OK;
-}
-
-ErrorCode KeymasterEnforcement::AuthorizeBegin(const KeyPurpose purpose, const km_id_t keyid,
-                                               const AuthorizationSet& auth_set,
-                                               const AuthorizationSet& operation_params,
-                                               NullOr<const HardwareAuthToken&> auth_token) {
-    // Find some entries that may be needed to handle KM_TAG_USER_SECURE_ID
-    int auth_timeout_index = -1;
-    int auth_type_index = -1;
-    int no_auth_required_index = -1;
-    for (size_t pos = 0; pos < auth_set.size(); ++pos) {
-        switch (auth_set[pos].tag) {
-        case Tag::AUTH_TIMEOUT:
-            auth_timeout_index = pos;
-            break;
-        case Tag::USER_AUTH_TYPE:
-            auth_type_index = pos;
-            break;
-        case Tag::NO_AUTH_REQUIRED:
-            no_auth_required_index = pos;
-            break;
-        default:
-            break;
-        }
-    }
-
-    ErrorCode error = authorized_purpose(purpose, auth_set);
-    if (error != ErrorCode::OK) return error;
-
-    // If successful, and if key has a min time between ops, this will be set to the time limit
-    uint32_t min_ops_timeout = UINT32_MAX;
-
-    bool update_access_count = false;
-    bool caller_nonce_authorized_by_key = false;
-    bool authentication_required = false;
-    bool auth_token_matched = false;
-    bool unlocked_device_required = false;
-    int32_t user_id = -1;
-
-    for (auto& param : auth_set) {
-
-        // KM_TAG_PADDING_OLD and KM_TAG_DIGEST_OLD aren't actually members of the enum, so we can't
-        // switch on them.  There's nothing to validate for them, though, so just ignore them.
-        if (int32_t(param.tag) == KM_TAG_PADDING_OLD || int32_t(param.tag) == KM_TAG_DIGEST_OLD)
-            continue;
-
-        switch (param.tag) {
-
-        case Tag::ACTIVE_DATETIME: {
-            auto date = authorizationValue(TAG_ACTIVE_DATETIME, param);
-            if (date.isOk() && !activation_date_valid(date.value()))
-                return ErrorCode::KEY_NOT_YET_VALID;
-            break;
-        }
-        case Tag::ORIGINATION_EXPIRE_DATETIME: {
-            auto date = authorizationValue(TAG_ORIGINATION_EXPIRE_DATETIME, param);
-            if (is_origination_purpose(purpose) && date.isOk() &&
-                expiration_date_passed(date.value()))
-                return ErrorCode::KEY_EXPIRED;
-            break;
-        }
-        case Tag::USAGE_EXPIRE_DATETIME: {
-            auto date = authorizationValue(TAG_USAGE_EXPIRE_DATETIME, param);
-            if (is_usage_purpose(purpose) && date.isOk() && expiration_date_passed(date.value()))
-                return ErrorCode::KEY_EXPIRED;
-            break;
-        }
-        case Tag::MIN_SECONDS_BETWEEN_OPS: {
-            auto min_ops_timeout = authorizationValue(TAG_MIN_SECONDS_BETWEEN_OPS, param);
-            if (min_ops_timeout.isOk() && !MinTimeBetweenOpsPassed(min_ops_timeout.value(), keyid))
-                return ErrorCode::KEY_RATE_LIMIT_EXCEEDED;
-            break;
-        }
-        case Tag::MAX_USES_PER_BOOT: {
-            auto max_users = authorizationValue(TAG_MAX_USES_PER_BOOT, param);
-            update_access_count = true;
-            if (max_users.isOk() && !MaxUsesPerBootNotExceeded(keyid, max_users.value()))
-                return ErrorCode::KEY_MAX_OPS_EXCEEDED;
-            break;
-        }
-        case Tag::USER_SECURE_ID:
-            if (no_auth_required_index != -1) {
-                // Key has both KM_TAG_USER_SECURE_ID and KM_TAG_NO_AUTH_REQUIRED
-                return ErrorCode::INVALID_KEY_BLOB;
-            }
-
-            if (auth_timeout_index != -1) {
-                auto secure_id = authorizationValue(TAG_USER_SECURE_ID, param);
-                authentication_required = true;
-                if (secure_id.isOk() && auth_token.isOk() &&
-                    AuthTokenMatches(auth_set, auth_token.value(), secure_id.value(),
-                                     auth_type_index, auth_timeout_index, 0 /* op_handle */,
-                                     true /* is_begin_operation */))
-                    auth_token_matched = true;
-            }
-            break;
-
-        case Tag::USER_ID:
-            user_id = authorizationValue(TAG_USER_ID, param).value();
-            break;
-
-        case Tag::CALLER_NONCE:
-            caller_nonce_authorized_by_key = true;
-            break;
-
-        case Tag::UNLOCKED_DEVICE_REQUIRED:
-            unlocked_device_required = true;
-            break;
-
-        /* Tags should never be in key auths. */
-        case Tag::INVALID:
-        case Tag::ROOT_OF_TRUST:
-        case Tag::APPLICATION_DATA:
-        case Tag::ATTESTATION_CHALLENGE:
-        case Tag::ATTESTATION_APPLICATION_ID:
-        case Tag::ATTESTATION_ID_BRAND:
-        case Tag::ATTESTATION_ID_DEVICE:
-        case Tag::ATTESTATION_ID_PRODUCT:
-        case Tag::ATTESTATION_ID_SERIAL:
-        case Tag::ATTESTATION_ID_IMEI:
-        case Tag::ATTESTATION_ID_MEID:
-        case Tag::ATTESTATION_ID_MANUFACTURER:
-        case Tag::ATTESTATION_ID_MODEL:
-            return ErrorCode::INVALID_KEY_BLOB;
-
-        /* Tags used for cryptographic parameters in keygen.  Nothing to enforce. */
-        case Tag::PURPOSE:
-        case Tag::ALGORITHM:
-        case Tag::KEY_SIZE:
-        case Tag::BLOCK_MODE:
-        case Tag::DIGEST:
-        case Tag::MAC_LENGTH:
-        case Tag::PADDING:
-        case Tag::NONCE:
-        case Tag::MIN_MAC_LENGTH:
-        case Tag::EC_CURVE:
-
-        /* Tags not used for operations. */
-        case Tag::BLOB_USAGE_REQUIREMENTS:
-
-        /* Algorithm specific parameters not used for access control. */
-        case Tag::RSA_PUBLIC_EXPONENT:
-
-        /* Informational tags. */
-        case Tag::CREATION_DATETIME:
-        case Tag::ORIGIN:
-        case Tag::ROLLBACK_RESISTANCE:
-
-        /* Tags handled when KM_TAG_USER_SECURE_ID is handled */
-        case Tag::NO_AUTH_REQUIRED:
-        case Tag::USER_AUTH_TYPE:
-        case Tag::AUTH_TIMEOUT:
-
-        /* Tag to provide data to operations. */
-        case Tag::ASSOCIATED_DATA:
-
-        /* Tags that are implicitly verified by secure side */
-        case Tag::APPLICATION_ID:
-        case Tag::BOOT_PATCHLEVEL:
-        case Tag::OS_PATCHLEVEL:
-        case Tag::OS_VERSION:
-        case Tag::TRUSTED_USER_PRESENCE_REQUIRED:
-        case Tag::VENDOR_PATCHLEVEL:
-
-        /* TODO(swillden): Handle these */
-        case Tag::INCLUDE_UNIQUE_ID:
-        case Tag::UNIQUE_ID:
-        case Tag::RESET_SINCE_ID_ROTATION:
-        case Tag::ALLOW_WHILE_ON_BODY:
-        case Tag::HARDWARE_TYPE:
-        case Tag::TRUSTED_CONFIRMATION_REQUIRED:
-        case Tag::CONFIRMATION_TOKEN:
-            break;
-
-        case Tag::BOOTLOADER_ONLY:
-            return ErrorCode::INVALID_KEY_BLOB;
-        }
-    }
-
-    if (unlocked_device_required && is_device_locked(user_id)) {
-        switch (purpose) {
-        case KeyPurpose::ENCRYPT:
-        case KeyPurpose::VERIFY:
-            /* These are okay */
-            break;
-        case KeyPurpose::DECRYPT:
-        case KeyPurpose::SIGN:
-        case KeyPurpose::WRAP_KEY:
-            return ErrorCode::DEVICE_LOCKED;
-        };
-    }
-
-    if (authentication_required && !auth_token_matched) {
-        ALOGE("Auth required but no matching auth token found");
-        return ErrorCode::KEY_USER_NOT_AUTHENTICATED;
-    }
-
-    if (!caller_nonce_authorized_by_key && is_origination_purpose(purpose) &&
-        operation_params.Contains(Tag::NONCE))
-        return ErrorCode::CALLER_NONCE_PROHIBITED;
-
-    if (min_ops_timeout != UINT32_MAX) {
-        if (!access_time_map_.UpdateKeyAccessTime(keyid, get_current_time(), min_ops_timeout)) {
-            ALOGE("Rate-limited keys table full.  Entries will time out.");
-            return ErrorCode::TOO_MANY_OPERATIONS;
-        }
-    }
-
-    if (update_access_count) {
-        if (!access_count_map_.IncrementKeyAccessCount(keyid)) {
-            ALOGE("Usage count-limited keys table full, until reboot.");
-            return ErrorCode::TOO_MANY_OPERATIONS;
-        }
-    }
-
-    return ErrorCode::OK;
-}
-
-class EvpMdCtx {
-  public:
-    EvpMdCtx() { EVP_MD_CTX_init(&ctx_); }
-    ~EvpMdCtx() { EVP_MD_CTX_cleanup(&ctx_); }
-
-    EVP_MD_CTX* get() { return &ctx_; }
-
-  private:
-    EVP_MD_CTX ctx_;
-};
-
-/* static */
-std::optional<km_id_t> KeymasterEnforcement::CreateKeyId(const hidl_vec<uint8_t>& key_blob) {
-    EvpMdCtx ctx;
-    km_id_t keyid;
-
-    uint8_t hash[EVP_MAX_MD_SIZE];
-    unsigned int hash_len;
-    if (EVP_DigestInit_ex(ctx.get(), EVP_sha256(), nullptr /* ENGINE */) &&
-        EVP_DigestUpdate(ctx.get(), &key_blob[0], key_blob.size()) &&
-        EVP_DigestFinal_ex(ctx.get(), hash, &hash_len)) {
-        assert(hash_len >= sizeof(keyid));
-        memcpy(&keyid, hash, sizeof(keyid));
-        return keyid;
-    }
-
-    return {};
-}
-
-bool KeymasterEnforcement::MinTimeBetweenOpsPassed(uint32_t min_time_between, const km_id_t keyid) {
-    uint32_t last_access_time;
-    if (!access_time_map_.LastKeyAccessTime(keyid, &last_access_time)) return true;
-    return min_time_between <= static_cast<int64_t>(get_current_time()) - last_access_time;
-}
-
-bool KeymasterEnforcement::MaxUsesPerBootNotExceeded(const km_id_t keyid, uint32_t max_uses) {
-    uint32_t key_access_count;
-    if (!access_count_map_.KeyAccessCount(keyid, &key_access_count)) return true;
-    return key_access_count < max_uses;
-}
-
-template <typename IntType, uint32_t byteOrder> struct choose_hton;
-
-template <typename IntType> struct choose_hton<IntType, __ORDER_LITTLE_ENDIAN__> {
-    inline static IntType hton(const IntType& value) {
-        IntType result = 0;
-        const unsigned char* inbytes = reinterpret_cast<const unsigned char*>(&value);
-        unsigned char* outbytes = reinterpret_cast<unsigned char*>(&result);
-        for (int i = sizeof(IntType) - 1; i >= 0; --i) {
-            *(outbytes++) = inbytes[i];
-        }
-        return result;
-    }
-};
-
-template <typename IntType> struct choose_hton<IntType, __ORDER_BIG_ENDIAN__> {
-    inline static IntType hton(const IntType& value) { return value; }
-};
-
-template <typename IntType> inline IntType hton(const IntType& value) {
-    return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
-}
-
-template <typename IntType> inline IntType ntoh(const IntType& value) {
-    // same operation and hton
-    return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
-}
-
-bool KeymasterEnforcement::AuthTokenMatches(const AuthorizationSet& auth_set,
-                                            const HardwareAuthToken& auth_token,
-                                            const uint64_t user_secure_id,
-                                            const int auth_type_index, const int auth_timeout_index,
-                                            const uint64_t op_handle,
-                                            bool is_begin_operation) const {
-    assert(auth_type_index < static_cast<int>(auth_set.size()));
-    assert(auth_timeout_index < static_cast<int>(auth_set.size()));
-
-    if (!ValidateTokenSignature(auth_token)) {
-        ALOGE("Auth token signature invalid");
-        return false;
-    }
-
-    if (auth_timeout_index == -1 && op_handle && op_handle != auth_token.challenge) {
-        ALOGE("Auth token has the challenge %" PRIu64 ", need %" PRIu64, auth_token.challenge,
-              op_handle);
-        return false;
-    }
-
-    if (user_secure_id != auth_token.userId && user_secure_id != auth_token.authenticatorId) {
-        ALOGI("Auth token SIDs %" PRIu64 " and %" PRIu64 " do not match key SID %" PRIu64,
-              auth_token.userId, auth_token.authenticatorId, user_secure_id);
-        return false;
-    }
-
-    if (auth_type_index < 0 || auth_type_index > static_cast<int>(auth_set.size())) {
-        ALOGE("Auth required but no auth type found");
-        return false;
-    }
-
-    assert(auth_set[auth_type_index].tag == TAG_USER_AUTH_TYPE);
-    auto key_auth_type_mask = authorizationValue(TAG_USER_AUTH_TYPE, auth_set[auth_type_index]);
-    if (!key_auth_type_mask.isOk()) return false;
-
-    if ((uint32_t(key_auth_type_mask.value()) & auth_token.authenticatorType) == 0) {
-        ALOGE("Key requires match of auth type mask 0%uo, but token contained 0%uo",
-              key_auth_type_mask.value(), auth_token.authenticatorType);
-        return false;
-    }
-
-    if (auth_timeout_index != -1 && is_begin_operation) {
-        assert(auth_set[auth_timeout_index].tag == TAG_AUTH_TIMEOUT);
-        auto auth_token_timeout =
-            authorizationValue(TAG_AUTH_TIMEOUT, auth_set[auth_timeout_index]);
-        if (!auth_token_timeout.isOk()) return false;
-
-        if (auth_token_timed_out(auth_token, auth_token_timeout.value())) {
-            ALOGE("Auth token has timed out");
-            return false;
-        }
-    }
-
-    // Survived the whole gauntlet.  We have authentage!
-    return true;
-}
-
-bool AccessTimeMap::LastKeyAccessTime(km_id_t keyid, uint32_t* last_access_time) const {
-    std::lock_guard<std::mutex> lock(list_lock_);
-    for (auto& entry : last_access_list_)
-        if (entry.keyid == keyid) {
-            *last_access_time = entry.access_time;
-            return true;
-        }
-    return false;
-}
-
-bool AccessTimeMap::UpdateKeyAccessTime(km_id_t keyid, uint32_t current_time, uint32_t timeout) {
-    std::lock_guard<std::mutex> lock(list_lock_);
-    for (auto iter = last_access_list_.begin(); iter != last_access_list_.end();) {
-        if (iter->keyid == keyid) {
-            iter->access_time = current_time;
-            return true;
-        }
-
-        // Expire entry if possible.
-        assert(current_time >= iter->access_time);
-        if (current_time - iter->access_time >= iter->timeout)
-            iter = last_access_list_.erase(iter);
-        else
-            ++iter;
-    }
-
-    if (last_access_list_.size() >= max_size_) return false;
-
-    AccessTime new_entry;
-    new_entry.keyid = keyid;
-    new_entry.access_time = current_time;
-    new_entry.timeout = timeout;
-    last_access_list_.push_front(new_entry);
-    return true;
-}
-
-bool AccessCountMap::KeyAccessCount(km_id_t keyid, uint32_t* count) const {
-    std::lock_guard<std::mutex> lock(list_lock_);
-    for (auto& entry : access_count_list_)
-        if (entry.keyid == keyid) {
-            *count = entry.access_count;
-            return true;
-        }
-    return false;
-}
-
-bool AccessCountMap::IncrementKeyAccessCount(km_id_t keyid) {
-    std::lock_guard<std::mutex> lock(list_lock_);
-    for (auto& entry : access_count_list_)
-        if (entry.keyid == keyid) {
-            // Note that the 'if' below will always be true because KM_TAG_MAX_USES_PER_BOOT is a
-            // uint32_t, and as soon as entry.access_count reaches the specified maximum value
-            // operation requests will be rejected and access_count won't be incremented any more.
-            // And, besides, UINT64_MAX is huge.  But we ensure that it doesn't wrap anyway, out of
-            // an abundance of caution.
-            if (entry.access_count < UINT64_MAX) ++entry.access_count;
-            return true;
-        }
-
-    if (access_count_list_.size() >= max_size_) return false;
-
-    AccessCount new_entry;
-    new_entry.keyid = keyid;
-    new_entry.access_count = 1;
-    access_count_list_.push_front(new_entry);
-    return true;
-}
-}; /* namespace keystore */
diff --git a/keystore/keymaster_enforcement.h b/keystore/keymaster_enforcement.h
deleted file mode 100644
index 9bfb225..0000000
--- a/keystore/keymaster_enforcement.h
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2014 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 KEYSTORE_KEYMASTER_ENFORCEMENT_H
-#define KEYSTORE_KEYMASTER_ENFORCEMENT_H
-
-#include <stdio.h>
-
-#include <keystore/keymaster_types.h>
-
-#include <list>
-#include <mutex>
-#include <optional>
-
-namespace keystore {
-
-typedef uint64_t km_id_t;
-
-class KeymasterEnforcementContext {
-  public:
-    virtual ~KeymasterEnforcementContext() {}
-    /*
-     * Get current time.
-     */
-};
-
-class AccessTimeMap {
-  public:
-    explicit AccessTimeMap(uint32_t max_size) : max_size_(max_size) {}
-
-    /* If the key is found, returns true and fills \p last_access_time.  If not found returns
-     * false. */
-    bool LastKeyAccessTime(km_id_t keyid, uint32_t* last_access_time) const;
-
-    /* Updates the last key access time with the currentTime parameter.  Adds the key if
-     * needed, returning false if key cannot be added because list is full. */
-    bool UpdateKeyAccessTime(km_id_t keyid, uint32_t current_time, uint32_t timeout);
-
-  private:
-    mutable std::mutex list_lock_;
-    struct AccessTime {
-        km_id_t keyid;
-        uint32_t access_time;
-        uint32_t timeout;
-    };
-    std::list<AccessTime> last_access_list_;
-    const uint32_t max_size_;
-};
-
-class AccessCountMap {
-  public:
-    explicit AccessCountMap(uint32_t max_size) : max_size_(max_size) {}
-
-    /* If the key is found, returns true and fills \p count.  If not found returns
-     * false. */
-    bool KeyAccessCount(km_id_t keyid, uint32_t* count) const;
-
-    /* Increments key access count, adding an entry if the key has never been used.  Returns
-     * false if the list has reached maximum size. */
-    bool IncrementKeyAccessCount(km_id_t keyid);
-
-  private:
-    mutable std::mutex list_lock_;
-    struct AccessCount {
-        km_id_t keyid;
-        uint64_t access_count;
-    };
-    std::list<AccessCount> access_count_list_;
-    const uint32_t max_size_;
-};
-
-class KeymasterEnforcement {
-  public:
-    /**
-     * Construct a KeymasterEnforcement.
-     */
-    KeymasterEnforcement(uint32_t max_access_time_map_size, uint32_t max_access_count_map_size);
-    virtual ~KeymasterEnforcement();
-
-    /**
-     * Iterates through the authorization set and returns the corresponding keymaster error. Will
-     * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with
-     * the given operation params and handle. Used for encrypt, decrypt sign, and verify.
-     */
-    ErrorCode AuthorizeOperation(const KeyPurpose purpose, const km_id_t keyid,
-                                 const AuthorizationSet& auth_set,
-                                 const AuthorizationSet& operation_params,
-                                 const HardwareAuthToken& auth_token, uint64_t op_handle,
-                                 bool is_begin_operation);
-
-    /**
-     * Iterates through the authorization set and returns the corresponding keymaster error. Will
-     * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with
-     * the given operation params. Used for encrypt, decrypt sign, and verify.
-     */
-    ErrorCode AuthorizeBegin(const KeyPurpose purpose, const km_id_t keyid,
-                             const AuthorizationSet& auth_set,
-                             const AuthorizationSet& operation_params,
-                             NullOr<const HardwareAuthToken&> auth_token);
-
-    /**
-     * Iterates through the authorization set and returns the corresponding keymaster error. Will
-     * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with
-     * the given operation params and handle. Used for encrypt, decrypt sign, and verify.
-     */
-    ErrorCode AuthorizeUpdate(const AuthorizationSet& auth_set, const HardwareAuthToken& auth_token,
-                              uint64_t op_handle) {
-        return AuthorizeUpdateOrFinish(auth_set, auth_token, op_handle);
-    }
-
-    /**
-     * Iterates through the authorization set and returns the corresponding keymaster error. Will
-     * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with
-     * the given operation params and handle. Used for encrypt, decrypt sign, and verify.
-     */
-    ErrorCode AuthorizeFinish(const AuthorizationSet& auth_set, const HardwareAuthToken& auth_token,
-                              uint64_t op_handle) {
-        return AuthorizeUpdateOrFinish(auth_set, auth_token, op_handle);
-    }
-
-    /**
-     * Creates a key ID for use in subsequent calls to AuthorizeOperation.  Clients needn't use this
-     * method of creating key IDs, as long as they use something consistent and unique.  This method
-     * hashes the key blob.
-     *
-     * Returns false if an error in the crypto library prevents creation of an ID.
-     */
-    static std::optional<km_id_t> CreateKeyId(const hidl_vec<uint8_t>& key_blob);
-
-    //
-    // Methods that must be implemented by subclasses
-    //
-    // The time-related methods address the fact that different enforcement contexts may have
-    // different time-related capabilities.  In particular:
-    //
-    // - They may or may not be able to check dates against real-world clocks.
-    //
-    // - They may or may not be able to check timestampls against authentication trustlets (minters
-    //   of hw_auth_token_t structs).
-    //
-    // - They must have some time source for relative times, but may not be able to provide more
-    //   than reliability and monotonicity.
-
-    /*
-     * Returns true if the specified activation date has passed, or if activation cannot be
-     * enforced.
-     */
-    virtual bool activation_date_valid(uint64_t activation_date) const = 0;
-
-    /*
-     * Returns true if the specified expiration date has passed.  Returns false if it has not, or if
-     * expiration cannot be enforced.
-     */
-    virtual bool expiration_date_passed(uint64_t expiration_date) const = 0;
-
-    /*
-     * Returns true if the specified auth_token is older than the specified timeout.
-     */
-    virtual bool auth_token_timed_out(const HardwareAuthToken& token, uint32_t timeout) const = 0;
-
-    /*
-     * Get current time in seconds from some starting point.  This value is used to compute relative
-     * times between events.  It must be monotonically increasing, and must not skip or lag.  It
-     * need not have any relation to any external time standard (other than the duration of
-     * "second").
-     *
-     * On POSIX systems, it's recommented to use clock_gettime(CLOCK_MONOTONIC, ...) to implement
-     * this method.
-     */
-    virtual uint32_t get_current_time() const = 0;
-
-    /*
-     * Returns true if the specified auth_token has a valid signature, or if signature validation is
-     * not available.
-     */
-    virtual bool ValidateTokenSignature(const HardwareAuthToken& token) const = 0;
-
-    /*
-     * Returns true if the device screen is currently locked for the specified user.
-     */
-    virtual bool is_device_locked(int32_t userId) const = 0;
-
-  private:
-    ErrorCode AuthorizeUpdateOrFinish(const AuthorizationSet& auth_set,
-                                      const HardwareAuthToken& auth_token, uint64_t op_handle);
-
-    bool MinTimeBetweenOpsPassed(uint32_t min_time_between, const km_id_t keyid);
-    bool MaxUsesPerBootNotExceeded(const km_id_t keyid, uint32_t max_uses);
-    bool AuthTokenMatches(const AuthorizationSet& auth_set, const HardwareAuthToken& auth_token,
-                          const uint64_t user_secure_id, const int auth_type_index,
-                          const int auth_timeout_index, const uint64_t op_handle,
-                          bool is_begin_operation) const;
-
-    AccessTimeMap access_time_map_;
-    AccessCountMap access_count_map_;
-};
-
-}; /* namespace keystore */
-
-#endif  // KEYSTORE_KEYMASTER_ENFORCEMENT_H
diff --git a/keystore/keymaster_worker.cpp b/keystore/keymaster_worker.cpp
deleted file mode 100644
index 7481a1e..0000000
--- a/keystore/keymaster_worker.cpp
+++ /dev/null
@@ -1,1144 +0,0 @@
-/*
-**
-** Copyright 2018, 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.
-*/
-#define LOG_TAG "keymaster_worker"
-
-#include "keymaster_worker.h"
-
-#include "keystore_utils.h"
-
-#include <android-base/logging.h>
-
-#include <log/log_event_list.h>
-
-#include <private/android_logger.h>
-
-#include "KeyStore.h"
-#include "keymaster_enforcement.h"
-
-#include "key_creation_log_handler.h"
-#include "keystore_utils.h"
-
-#include <chrono>
-
-namespace keystore {
-
-using namespace std::chrono;
-
-constexpr size_t kMaxOperations = 15;
-
-using AndroidKeymasterArguments = android::security::keymaster::KeymasterArguments;
-using android::security::keymaster::ExportResult;
-using android::security::keymaster::operationFailed;
-using android::security::keymaster::OperationResult;
-
-Worker::Worker() {}
-Worker::~Worker() {
-    std::unique_lock<std::mutex> lock(pending_requests_mutex_);
-    terminate_ = true;
-    pending_requests_cond_var_.notify_all();
-    pending_requests_cond_var_.wait(lock, [this] { return !running_; });
-}
-void Worker::addRequest(WorkerTask request) {
-    std::unique_lock<std::mutex> lock(pending_requests_mutex_);
-    bool start_thread = !running_;
-    running_ = true;
-    pending_requests_.push(std::move(request));
-    lock.unlock();
-    pending_requests_cond_var_.notify_all();
-    if (start_thread) {
-        auto worker = std::thread([this] {
-            std::unique_lock<std::mutex> lock(pending_requests_mutex_);
-            while (running_) {
-                // Wait for 30s if the request queue is empty, then kill die.
-                // Die immediately if termiate_ was set which happens in the destructor.
-                auto status = pending_requests_cond_var_.wait_for(
-                    lock, 30s, [this]() { return !pending_requests_.empty() || terminate_; });
-                if (status && !terminate_) {
-                    auto request = std::move(pending_requests_.front());
-                    lock.unlock();
-                    request();
-                    lock.lock();
-                    pending_requests_.pop();
-                } else {
-                    running_ = false;
-                }
-                pending_requests_cond_var_.notify_all();
-            }
-        });
-        worker.detach();
-    }
-}
-
-KeymasterWorker::KeymasterWorker(sp<Keymaster> keymasterDevice, KeyStore* keyStore)
-    : keymasterDevice_(std::move(keymasterDevice)), operationMap_(keyStore), keyStore_(keyStore) {
-    // make sure that hal version is cached.
-    if (keymasterDevice_) keymasterDevice_->halVersion();
-}
-
-void KeymasterWorker::logIfKeymasterVendorError(ErrorCode ec) const {
-    keymasterDevice_->logIfKeymasterVendorError(ec);
-}
-
-void KeymasterWorker::deleteOldKeyOnUpgrade(const LockedKeyBlobEntry& blobfile, Blob keyBlob) {
-    // if we got the blob successfully, we try and delete it from the keymaster device
-    auto& dev = keymasterDevice_;
-    uid_t uid = blobfile->uid();
-    const auto& alias = blobfile->alias();
-
-    if (keyBlob.getType() == ::TYPE_KEYMASTER_10) {
-        auto ret = KS_HANDLE_HIDL_ERROR(dev, dev->deleteKey(blob2hidlVec(keyBlob)));
-        // A device doesn't have to implement delete_key.
-        bool success = ret == ErrorCode::OK || ret == ErrorCode::UNIMPLEMENTED;
-        if (__android_log_security()) {
-            android_log_event_list(SEC_TAG_KEY_DESTROYED)
-                << int32_t(success) << alias << int32_t(uid) << LOG_ID_SECURITY;
-        }
-        if (!success) {
-            LOG(ERROR) << "Keymaster delete for key " << alias << " of uid " << uid << " failed";
-        }
-    }
-}
-
-std::tuple<KeyStoreServiceReturnCode, Blob>
-KeymasterWorker::upgradeKeyBlob(const LockedKeyBlobEntry& lockedEntry,
-                                const AuthorizationSet& params) {
-    LOG(INFO) << "upgradeKeyBlob " << lockedEntry->alias() << " " << (uint32_t)lockedEntry->uid();
-
-    std::tuple<KeyStoreServiceReturnCode, Blob> result;
-
-    auto userState = keyStore_->getUserStateDB().getUserStateByUid(lockedEntry->uid());
-
-    Blob& blob = std::get<1>(result);
-    KeyStoreServiceReturnCode& error = std::get<0>(result);
-
-    Blob charBlob;
-    ResponseCode rc;
-
-    std::tie(rc, blob, charBlob) =
-        lockedEntry.readBlobs(userState->getEncryptionKey(), userState->getState());
-
-    userState = {};
-
-    if (rc != ResponseCode::NO_ERROR) {
-        return error = rc, result;
-    }
-
-    auto hidlKey = blob2hidlVec(blob);
-    auto& dev = keymasterDevice_;
-
-    auto hidlCb = [&](ErrorCode ret, const ::std::vector<uint8_t>& upgradedKeyBlob) {
-        dev->logIfKeymasterVendorError(ret);
-        error = ret;
-        if (!error.isOk()) {
-            if (error == ErrorCode::INVALID_KEY_BLOB) {
-                log_key_integrity_violation(lockedEntry->alias().c_str(), lockedEntry->uid());
-            }
-            return;
-        }
-
-        Blob newBlob(&upgradedKeyBlob[0], upgradedKeyBlob.size(), nullptr /* info */,
-                     0 /* infoLength */, ::TYPE_KEYMASTER_10);
-        newBlob.setSecurityLevel(blob.getSecurityLevel());
-        newBlob.setEncrypted(blob.isEncrypted());
-        newBlob.setSuperEncrypted(blob.isSuperEncrypted());
-        newBlob.setCriticalToDeviceEncryption(blob.isCriticalToDeviceEncryption());
-
-        error = keyStore_->put(lockedEntry, newBlob, charBlob);
-        if (!error.isOk()) {
-            ALOGI("upgradeKeyBlob keystore->put failed %d", error.getErrorCode());
-            return;
-        }
-
-        deleteOldKeyOnUpgrade(lockedEntry, std::move(blob));
-        blob = std::move(newBlob);
-    };
-
-    KeyStoreServiceReturnCode error2;
-    error2 = KS_HANDLE_HIDL_ERROR(dev, dev->upgradeKey(hidlKey, params.hidl_data(), hidlCb));
-    if (!error2.isOk()) {
-        return error = error2, result;
-    }
-
-    return result;
-}
-
-std::tuple<KeyStoreServiceReturnCode, KeyCharacteristics, Blob, Blob>
-KeymasterWorker::createKeyCharacteristicsCache(const LockedKeyBlobEntry& lockedEntry,
-                                               const hidl_vec<uint8_t>& clientId,
-                                               const hidl_vec<uint8_t>& appData, Blob keyBlob,
-                                               Blob charBlob) {
-    std::tuple<KeyStoreServiceReturnCode, KeyCharacteristics, Blob, Blob> result;
-
-#if __cplusplus == 201703L
-    auto& [rc, resultCharacteristics, outBlob, charOutBlob] = result;
-#else
-    KeyStoreServiceReturnCode& rc = std::get<0>(result);
-    KeyCharacteristics& resultCharacteristics = std::get<1>(result);
-    Blob& outBlob = std::get<2>(result);
-    Blob& charOutBlob = std::get<3>(result);
-#endif
-
-    rc = ResponseCode::SYSTEM_ERROR;
-    if (!keyBlob) return result;
-    auto hidlKeyBlob = blob2hidlVec(keyBlob);
-    auto& dev = keymasterDevice_;
-
-    KeyStoreServiceReturnCode error;
-
-    AuthorizationSet hwEnforced, swEnforced;
-    bool success = true;
-
-    if (charBlob) {
-        std::tie(success, hwEnforced, swEnforced) = charBlob.getKeyCharacteristics();
-    }
-    if (!success) {
-        LOG(ERROR) << "Failed to read cached key characteristics";
-        return rc = ResponseCode::SYSTEM_ERROR, result;
-    }
-
-    auto hidlCb = [&](ErrorCode ret, const KeyCharacteristics& keyCharacteristics) {
-        dev->logIfKeymasterVendorError(ret);
-        error = ret;
-        if (!error.isOk()) {
-            if (error == ErrorCode::INVALID_KEY_BLOB) {
-                log_key_integrity_violation(lockedEntry->alias().c_str(), lockedEntry->uid());
-            }
-            return;
-        }
-
-        // Replace the sw_enforced set with those persisted to disk, minus hw_enforced
-        AuthorizationSet softwareEnforced = keyCharacteristics.softwareEnforced;
-        hwEnforced = keyCharacteristics.hardwareEnforced;
-        swEnforced.Union(softwareEnforced);
-        softwareEnforced.Subtract(hwEnforced);
-
-        // We only get the characteristics from keymaster if there was no cache file or the
-        // the chach file was a legacy cache file. So lets write a new cache file for the next time.
-        Blob newCharBlob;
-        success = newCharBlob.putKeyCharacteristics(hwEnforced, swEnforced);
-        if (!success) {
-            error = ResponseCode::SYSTEM_ERROR;
-            LOG(ERROR) << "Failed to serialize cached key characteristics";
-            return;
-        }
-
-        error = keyStore_->put(lockedEntry, {}, newCharBlob);
-        if (!error.isOk()) {
-            ALOGE("Failed to write key characteristics cache");
-            return;
-        }
-        charBlob = std::move(newCharBlob);
-    };
-
-    if (!charBlob || charBlob.getType() == TYPE_KEY_CHARACTERISTICS) {
-        // this updates the key characteristics cache file to the new format or creates one in
-        // in the first place
-        rc = KS_HANDLE_HIDL_ERROR(
-            dev, dev->getKeyCharacteristics(hidlKeyBlob, clientId, appData, hidlCb));
-        if (!rc.isOk()) {
-            return result;
-        }
-
-        if (error == ErrorCode::KEY_REQUIRES_UPGRADE) {
-            AuthorizationSet upgradeParams;
-            if (clientId.size()) {
-                upgradeParams.push_back(TAG_APPLICATION_ID, clientId);
-            }
-            if (appData.size()) {
-                upgradeParams.push_back(TAG_APPLICATION_DATA, appData);
-            }
-            std::tie(rc, keyBlob) = upgradeKeyBlob(lockedEntry, upgradeParams);
-            if (!rc.isOk()) {
-                return result;
-            }
-
-            auto upgradedHidlKeyBlob = blob2hidlVec(keyBlob);
-
-            rc = KS_HANDLE_HIDL_ERROR(
-                dev, dev->getKeyCharacteristics(upgradedHidlKeyBlob, clientId, appData, hidlCb));
-            if (!rc.isOk()) {
-                return result;
-            }
-        }
-    }
-
-    resultCharacteristics.hardwareEnforced = hwEnforced.hidl_data();
-    resultCharacteristics.softwareEnforced = swEnforced.hidl_data();
-
-    outBlob = std::move(keyBlob);
-    charOutBlob = std::move(charBlob);
-    rc = error;
-    return result;
-}
-
-/**
- * Get the auth token for this operation from the auth token table.
- *
- * Returns ResponseCode::NO_ERROR if the auth token was set or none was required.
- *         ::OP_AUTH_NEEDED if it is a per op authorization, no
- *         authorization token exists for that operation and
- *         failOnTokenMissing is false.
- *         KM_ERROR_KEY_USER_NOT_AUTHENTICATED if there is no valid auth
- *         token for the operation
- */
-std::pair<KeyStoreServiceReturnCode, HardwareAuthToken>
-KeymasterWorker::getAuthToken(const KeyCharacteristics& characteristics, uint64_t handle,
-                              KeyPurpose purpose, bool failOnTokenMissing) {
-
-    AuthorizationSet allCharacteristics(characteristics.softwareEnforced);
-    allCharacteristics.append(characteristics.hardwareEnforced.begin(),
-                              characteristics.hardwareEnforced.end());
-
-    HardwareAuthToken authToken;
-    AuthTokenTable::Error err;
-    std::tie(err, authToken) = keyStore_->getAuthTokenTable().FindAuthorization(
-        allCharacteristics, static_cast<KeyPurpose>(purpose), handle);
-
-    KeyStoreServiceReturnCode rc;
-
-    switch (err) {
-    case AuthTokenTable::OK:
-    case AuthTokenTable::AUTH_NOT_REQUIRED:
-        rc = ResponseCode::NO_ERROR;
-        break;
-
-    case AuthTokenTable::AUTH_TOKEN_NOT_FOUND:
-    case AuthTokenTable::AUTH_TOKEN_EXPIRED:
-    case AuthTokenTable::AUTH_TOKEN_WRONG_SID:
-        ALOGE("getAuthToken failed: %d", err);  // STOPSHIP: debug only, to be removed
-        rc = ErrorCode::KEY_USER_NOT_AUTHENTICATED;
-        break;
-
-    case AuthTokenTable::OP_HANDLE_REQUIRED:
-        rc = failOnTokenMissing ? KeyStoreServiceReturnCode(ErrorCode::KEY_USER_NOT_AUTHENTICATED)
-                                : KeyStoreServiceReturnCode(ResponseCode::OP_AUTH_NEEDED);
-        break;
-
-    default:
-        ALOGE("Unexpected FindAuthorization return value %d", err);
-        rc = ErrorCode::INVALID_ARGUMENT;
-    }
-
-    return {rc, std::move(authToken)};
-}
-
-KeyStoreServiceReturnCode KeymasterWorker::abort(const sp<IBinder>& token,
-                                                 ResponseCode reason_for_abort) {
-    auto op = operationMap_.removeOperation(token, false /* wasOpSuccessful */,
-                                            static_cast<int32_t>(reason_for_abort));
-    if (op) {
-        keyStore_->getAuthTokenTable().MarkCompleted(op->handle);
-        return KS_HANDLE_HIDL_ERROR(keymasterDevice_, keymasterDevice_->abort(op->handle));
-    } else {
-        return ErrorCode::INVALID_OPERATION_HANDLE;
-    }
-}
-
-/**
- * Prune the oldest pruneable operation.
- */
-bool KeymasterWorker::pruneOperation() {
-    sp<IBinder> oldest = operationMap_.getOldestPruneableOperation();
-    ALOGD("Trying to prune operation %p", oldest.get());
-    size_t op_count_before_abort = operationMap_.getOperationCount();
-    // We mostly ignore errors from abort() because all we care about is whether at least
-    // one operation has been removed.
-    auto rc = abort(oldest, ResponseCode::PRUNED);
-    keyStore_->removeOperationDevice(oldest);
-    if (operationMap_.getOperationCount() >= op_count_before_abort) {
-        ALOGE("Failed to abort pruneable operation %p, error: %d", oldest.get(), rc.getErrorCode());
-        return false;
-    }
-    return true;
-}
-
-// My IDE defines "CAPTURE_MOVE(x) x" because it does not understand generalized lambda captures.
-// It should never be redefined by a build system though.
-#ifndef CAPTURE_MOVE
-#define CAPTURE_MOVE(x) x = std::move(x)
-#endif
-
-void KeymasterWorker::begin(LockedKeyBlobEntry lockedEntry, sp<IBinder> appToken, Blob keyBlob,
-                            Blob charBlob, bool pruneable, KeyPurpose purpose,
-                            AuthorizationSet opParams, hidl_vec<uint8_t> entropy,
-                            worker_begin_cb worker_cb) {
-
-    Worker::addRequest([this, CAPTURE_MOVE(lockedEntry), CAPTURE_MOVE(appToken),
-                        CAPTURE_MOVE(keyBlob), CAPTURE_MOVE(charBlob), pruneable, purpose,
-                        CAPTURE_MOVE(opParams), CAPTURE_MOVE(entropy),
-                        CAPTURE_MOVE(worker_cb)]() mutable {
-        // Concurrently executed
-
-        auto& dev = keymasterDevice_;
-
-        KeyCharacteristics characteristics;
-
-        {
-            hidl_vec<uint8_t> clientId;
-            hidl_vec<uint8_t> appData;
-            for (const auto& param : opParams) {
-                if (param.tag == Tag::APPLICATION_ID) {
-                    clientId = authorizationValue(TAG_APPLICATION_ID, param).value();
-                } else if (param.tag == Tag::APPLICATION_DATA) {
-                    appData = authorizationValue(TAG_APPLICATION_DATA, param).value();
-                }
-            }
-            KeyStoreServiceReturnCode error;
-            std::tie(error, characteristics, keyBlob, charBlob) = createKeyCharacteristicsCache(
-                lockedEntry, clientId, appData, std::move(keyBlob), std::move(charBlob));
-            if (!error.isOk()) {
-                worker_cb(operationFailed(error));
-                return;
-            }
-        }
-
-        KeyStoreServiceReturnCode rc, authRc;
-        HardwareAuthToken authToken;
-        std::tie(authRc, authToken) = getAuthToken(characteristics, 0 /* no challenge */, purpose,
-                                                   /*failOnTokenMissing*/ false);
-
-        // If per-operation auth is needed we need to begin the operation and
-        // the client will need to authorize that operation before calling
-        // update. Any other auth issues stop here.
-        if (!authRc.isOk() && authRc != ResponseCode::OP_AUTH_NEEDED) {
-            return worker_cb(operationFailed(authRc));
-        }
-
-        // Add entropy to the device first.
-        if (entropy.size()) {
-            rc = KS_HANDLE_HIDL_ERROR(dev, dev->addRngEntropy(entropy));
-            if (!rc.isOk()) {
-                return worker_cb(operationFailed(rc));
-            }
-        }
-
-        // Create a keyid for this key.
-        auto keyid = KeymasterEnforcement::CreateKeyId(blob2hidlVec(keyBlob));
-        if (!keyid) {
-            ALOGE("Failed to create a key ID for authorization checking.");
-            return worker_cb(operationFailed(ErrorCode::UNKNOWN_ERROR));
-        }
-
-        // Check that all key authorization policy requirements are met.
-        AuthorizationSet key_auths = characteristics.hardwareEnforced;
-        key_auths.append(characteristics.softwareEnforced.begin(),
-                         characteristics.softwareEnforced.end());
-
-        rc = keyStore_->getEnforcementPolicy().AuthorizeOperation(
-            purpose, *keyid, key_auths, opParams, authToken, 0 /* op_handle */,
-            true /* is_begin_operation */);
-        if (!rc.isOk()) {
-            return worker_cb(operationFailed(rc));
-        }
-
-        // If there are more than kMaxOperations, abort the oldest operation that was started as
-        // pruneable.
-        while (operationMap_.getOperationCount() >= kMaxOperations) {
-            ALOGD("Reached or exceeded concurrent operations limit");
-            if (!pruneOperation()) {
-                break;
-            }
-        }
-
-        android::security::keymaster::OperationResult result;
-
-        auto hidlCb = [&](ErrorCode ret, const hidl_vec<KeyParameter>& outParams,
-                          uint64_t operationHandle) {
-            dev->logIfKeymasterVendorError(ret);
-            result.resultCode = ret;
-            if (!result.resultCode.isOk()) {
-                if (result.resultCode == ErrorCode::INVALID_KEY_BLOB) {
-                    log_key_integrity_violation(lockedEntry->alias().c_str(), lockedEntry->uid());
-                }
-                return;
-            }
-            result.handle = operationHandle;
-            result.outParams = outParams;
-        };
-
-        do {
-            rc = KS_HANDLE_HIDL_ERROR(dev, dev->begin(purpose, blob2hidlVec(keyBlob),
-                                                      opParams.hidl_data(), authToken, hidlCb));
-            if (!rc.isOk()) {
-                LOG(ERROR) << "Got error " << rc << " from begin()";
-                return worker_cb(operationFailed(ResponseCode::SYSTEM_ERROR));
-            }
-
-            if (result.resultCode == ErrorCode::KEY_REQUIRES_UPGRADE) {
-                std::tie(rc, keyBlob) = upgradeKeyBlob(lockedEntry, opParams);
-                if (!rc.isOk()) {
-                    return worker_cb(operationFailed(rc));
-                }
-
-                rc = KS_HANDLE_HIDL_ERROR(dev, dev->begin(purpose, blob2hidlVec(keyBlob),
-                                                          opParams.hidl_data(), authToken, hidlCb));
-                if (!rc.isOk()) {
-                    LOG(ERROR) << "Got error " << rc << " from begin()";
-                    return worker_cb(operationFailed(ResponseCode::SYSTEM_ERROR));
-                }
-            }
-            // If there are too many operations abort the oldest operation that was
-            // started as pruneable and try again.
-        } while (result.resultCode == ErrorCode::TOO_MANY_OPERATIONS && pruneOperation());
-
-        rc = result.resultCode;
-        if (!rc.isOk()) {
-            return worker_cb(operationFailed(rc));
-        }
-
-        // Note: The operation map takes possession of the contents of "characteristics".
-        // It is safe to use characteristics after the following line but it will be empty.
-        sp<IBinder> operationToken =
-            operationMap_.addOperation(result.handle, *keyid, purpose, dev, appToken,
-                                       std::move(characteristics), opParams.hidl_data(), pruneable);
-        assert(characteristics.hardwareEnforced.size() == 0);
-        assert(characteristics.softwareEnforced.size() == 0);
-        result.token = operationToken;
-
-        auto operation = operationMap_.getOperation(operationToken);
-        if (!operation) {
-            return worker_cb(operationFailed(ResponseCode::SYSTEM_ERROR));
-        }
-
-        if (authRc.isOk() && authToken.mac.size() &&
-            dev->halVersion().securityLevel == SecurityLevel::STRONGBOX) {
-            operation->authTokenFuture = operation->authTokenPromise.get_future();
-            std::weak_ptr<Operation> weak_operation = operation;
-
-            auto verifyTokenCB = [weak_operation](KeyStoreServiceReturnCode rc,
-                                                  HardwareAuthToken authToken,
-                                                  VerificationToken verificationToken) {
-                auto operation = weak_operation.lock();
-                if (!operation) {
-                    // operation aborted, nothing to do
-                    return;
-                }
-                if (rc.isOk()) {
-                    operation->authToken = std::move(authToken);
-                    operation->verificationToken = std::move(verificationToken);
-                }
-                operation->authTokenPromise.set_value(rc);
-            };
-            auto teeKmDevice = keyStore_->getDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
-            teeKmDevice->verifyAuthorization(result.handle, {}, std::move(authToken),
-                                             std::move(verifyTokenCB));
-        }
-
-        // Return the authentication lookup result. If this is a per operation
-        // auth'd key then the resultCode will be ::OP_AUTH_NEEDED and the
-        // application should get an auth token using the handle before the
-        // first call to update, which will fail if keystore hasn't received the
-        // auth token.
-        if (result.resultCode.isOk()) {
-            result.resultCode = authRc;
-        }
-        return worker_cb(result);
-    });
-}
-
-KeyStoreServiceReturnCode
-KeymasterWorker::getOperationAuthTokenIfNeeded(std::shared_ptr<Operation> op) {
-    if (!op) return ErrorCode::INVALID_OPERATION_HANDLE;
-
-    if (op->authTokenFuture.valid()) {
-        LOG(INFO) << "Waiting for verification token";
-        op->authTokenFuture.wait();
-        auto rc = op->authTokenFuture.get();
-        if (!rc.isOk()) {
-            return rc;
-        }
-        op->authTokenFuture = {};
-    } else if (!op->hasAuthToken()) {
-        KeyStoreServiceReturnCode rc;
-        HardwareAuthToken found;
-        std::tie(rc, found) = getAuthToken(op->characteristics, op->handle, op->purpose);
-        if (!rc.isOk()) return rc;
-        op->authToken = std::move(found);
-    }
-
-    return ResponseCode::NO_ERROR;
-}
-
-namespace {
-
-class Finalize {
-  private:
-    std::function<void()> f_;
-
-  public:
-    explicit Finalize(std::function<void()> f) : f_(f) {}
-    ~Finalize() {
-        if (f_) f_();
-    }
-    void release() { f_ = {}; }
-};
-
-}  // namespace
-
-void KeymasterWorker::update(sp<IBinder> token, AuthorizationSet params, hidl_vec<uint8_t> data,
-                             update_cb worker_cb) {
-    Worker::addRequest([this, CAPTURE_MOVE(token), CAPTURE_MOVE(params), CAPTURE_MOVE(data),
-                        CAPTURE_MOVE(worker_cb)]() {
-        KeyStoreServiceReturnCode rc;
-        auto op = operationMap_.getOperation(token);
-        if (!op) {
-            return worker_cb(operationFailed(ErrorCode::INVALID_OPERATION_HANDLE));
-        }
-
-        Finalize abort_operation_in_case_of_error([&] {
-            operationMap_.removeOperation(token, false, rc.getErrorCode());
-            keyStore_->getAuthTokenTable().MarkCompleted(op->handle);
-            KS_HANDLE_HIDL_ERROR(keymasterDevice_, keymasterDevice_->abort(op->handle));
-        });
-
-        rc = getOperationAuthTokenIfNeeded(op);
-        if (!rc.isOk()) return worker_cb(operationFailed(rc));
-
-        // Check that all key authorization policy requirements are met.
-        AuthorizationSet key_auths(op->characteristics.hardwareEnforced);
-        key_auths.append(op->characteristics.softwareEnforced.begin(),
-                         op->characteristics.softwareEnforced.end());
-
-        rc = keyStore_->getEnforcementPolicy().AuthorizeOperation(op->purpose, op->keyid, key_auths,
-                                                                  params, op->authToken, op->handle,
-                                                                  false /* is_begin_operation */);
-        if (!rc.isOk()) return worker_cb(operationFailed(rc));
-
-        OperationResult result;
-        auto hidlCb = [&](ErrorCode ret, uint32_t inputConsumed,
-                          const hidl_vec<KeyParameter>& outParams,
-                          const ::std::vector<uint8_t>& output) {
-            op->device->logIfKeymasterVendorError(ret);
-            result.resultCode = ret;
-            if (result.resultCode.isOk()) {
-                result.inputConsumed = inputConsumed;
-                result.outParams = outParams;
-                result.data = output;
-            }
-        };
-
-        rc = KS_HANDLE_HIDL_ERROR(op->device,
-                                  op->device->update(op->handle, params.hidl_data(), data,
-                                                     op->authToken, op->verificationToken, hidlCb));
-
-        // just a reminder: on success result->resultCode was set in the callback. So we only
-        // overwrite it if there was a communication error indicated by the ErrorCode.
-        if (!rc.isOk()) result.resultCode = rc;
-        if (result.resultCode.isOk()) {
-            // if everything went well we don't abort the operation.
-            abort_operation_in_case_of_error.release();
-        }
-        return worker_cb(std::move(result));
-    });
-}
-
-/**
- * Check that all KeyParameters provided by the application are allowed. Any parameter that keystore
- * adds itself should be disallowed here.
- */
-template <typename ParamsIter>
-static bool checkAllowedOperationParams(ParamsIter begin, const ParamsIter end) {
-    while (begin != end) {
-        switch (begin->tag) {
-        case Tag::ATTESTATION_APPLICATION_ID:
-        case Tag::RESET_SINCE_ID_ROTATION:
-            return false;
-        default:
-            break;
-        }
-        ++begin;
-    }
-    return true;
-}
-
-void KeymasterWorker::finish(sp<IBinder> token, AuthorizationSet params, hidl_vec<uint8_t> input,
-                             hidl_vec<uint8_t> signature, hidl_vec<uint8_t> entropy,
-                             finish_cb worker_cb) {
-    Worker::addRequest([this, CAPTURE_MOVE(token), CAPTURE_MOVE(params), CAPTURE_MOVE(input),
-                        CAPTURE_MOVE(signature), CAPTURE_MOVE(entropy),
-                        CAPTURE_MOVE(worker_cb)]() mutable {
-        KeyStoreServiceReturnCode rc;
-        auto op = operationMap_.getOperation(token);
-        if (!op) {
-            return worker_cb(operationFailed(ErrorCode::INVALID_OPERATION_HANDLE));
-        }
-
-        bool finished = false;
-        Finalize abort_operation_in_case_of_error([&] {
-            operationMap_.removeOperation(token, finished && rc.isOk(), rc.getErrorCode());
-            keyStore_->getAuthTokenTable().MarkCompleted(op->handle);
-            if (!finished)
-                KS_HANDLE_HIDL_ERROR(keymasterDevice_, keymasterDevice_->abort(op->handle));
-        });
-
-        if (!checkAllowedOperationParams(params.begin(), params.end())) {
-            return worker_cb(operationFailed(ErrorCode::INVALID_ARGUMENT));
-        }
-
-        rc = getOperationAuthTokenIfNeeded(op);
-        if (!rc.isOk()) return worker_cb(operationFailed(rc));
-
-        // Check that all key authorization policy requirements are met.
-        AuthorizationSet key_auths(op->characteristics.hardwareEnforced);
-        key_auths.append(op->characteristics.softwareEnforced.begin(),
-                         op->characteristics.softwareEnforced.end());
-
-        if (key_auths.Contains(Tag::TRUSTED_CONFIRMATION_REQUIRED)) {
-            hidl_vec<uint8_t> confirmationToken =
-                keyStore_->getConfirmationManager().getLatestConfirmationToken();
-            if (confirmationToken.size() == 0) {
-                LOG(ERROR) << "Confirmation token required but none found";
-                return worker_cb(operationFailed(ErrorCode::NO_USER_CONFIRMATION));
-            }
-            params.push_back(keymaster::TAG_CONFIRMATION_TOKEN, std::move(confirmationToken));
-        }
-
-        rc = keyStore_->getEnforcementPolicy().AuthorizeOperation(op->purpose, op->keyid, key_auths,
-                                                                  params, op->authToken, op->handle,
-                                                                  false /* is_begin_operation */);
-        if (!rc.isOk()) return worker_cb(operationFailed(rc));
-
-        if (entropy.size()) {
-            rc = KS_HANDLE_HIDL_ERROR(op->device, op->device->addRngEntropy(entropy));
-            if (!rc.isOk()) {
-                return worker_cb(operationFailed(rc));
-            }
-        }
-
-        OperationResult result;
-        auto hidlCb = [&](ErrorCode ret, const hidl_vec<KeyParameter>& outParams,
-                          const ::std::vector<uint8_t>& output) {
-            op->device->logIfKeymasterVendorError(ret);
-            result.resultCode = ret;
-            if (result.resultCode.isOk()) {
-                result.outParams = outParams;
-                result.data = output;
-            }
-        };
-
-        rc = KS_HANDLE_HIDL_ERROR(op->device, op->device->finish(op->handle, params.hidl_data(),
-                                                                 input, signature, op->authToken,
-                                                                 op->verificationToken, hidlCb));
-
-        if (rc.isOk()) {
-            // inform the finalizer that the finish call went through
-            finished = true;
-            // and what the result was
-            rc = result.resultCode;
-        } else {
-            return worker_cb(operationFailed(rc));
-        }
-        return worker_cb(std::move(result));
-    });
-}
-
-void KeymasterWorker::abort(sp<IBinder> token, abort_cb worker_cb) {
-    Worker::addRequest([this, CAPTURE_MOVE(token), CAPTURE_MOVE(worker_cb)]() {
-        return worker_cb(abort(token, ResponseCode::ABORT_CALLED));
-    });
-}
-
-void KeymasterWorker::verifyAuthorization(uint64_t challenge, hidl_vec<KeyParameter> params,
-                                          HardwareAuthToken token,
-                                          verifyAuthorization_cb worker_cb) {
-    Worker::addRequest([this, challenge, CAPTURE_MOVE(params), CAPTURE_MOVE(token),
-                        CAPTURE_MOVE(worker_cb)]() {
-        KeyStoreServiceReturnCode error;
-        VerificationToken verificationToken;
-        KeyStoreServiceReturnCode rc = KS_HANDLE_HIDL_ERROR(
-            keymasterDevice_,
-            keymasterDevice_->verifyAuthorization(
-                challenge, params, token, [&](ErrorCode ret, const VerificationToken& vToken) {
-                    keymasterDevice_->logIfKeymasterVendorError(ret);
-                    error = ret;
-                    verificationToken = vToken;
-                }));
-        worker_cb(rc.isOk() ? error : rc, std::move(token), std::move(verificationToken));
-    });
-}
-
-void KeymasterWorker::addRngEntropy(hidl_vec<uint8_t> data, addRngEntropy_cb _hidl_cb) {
-    addRequest(&Keymaster::addRngEntropy, std::move(_hidl_cb), std::move(data));
-}
-
-namespace {
-bool containsTag(const hidl_vec<KeyParameter>& params, Tag tag) {
-    return params.end() !=
-           std::find_if(params.begin(), params.end(),
-                        [&](const KeyParameter& param) { return param.tag == tag; });
-}
-
-bool isAuthenticationBound(const hidl_vec<KeyParameter>& params) {
-    return !containsTag(params, Tag::NO_AUTH_REQUIRED);
-}
-}  // namespace
-
-void KeymasterWorker::generateKey(LockedKeyBlobEntry lockedEntry, hidl_vec<KeyParameter> keyParams,
-                                  hidl_vec<uint8_t> entropy, int flags, generateKey_cb worker_cb) {
-    Worker::addRequest([this, CAPTURE_MOVE(lockedEntry), CAPTURE_MOVE(keyParams),
-                        CAPTURE_MOVE(entropy), CAPTURE_MOVE(worker_cb), flags]() mutable {
-        KeyStoreServiceReturnCode rc =
-            KS_HANDLE_HIDL_ERROR(keymasterDevice_, keymasterDevice_->addRngEntropy(entropy));
-        if (!rc.isOk()) {
-            return worker_cb(rc, {});
-        }
-
-        SecurityLevel securityLevel = keymasterDevice_->halVersion().securityLevel;
-
-        // Fallback cannot be considered for Strongbox. Further versions restrictions are enforced
-        // by KeyStore::getFallbackDevice()
-        bool consider_fallback = securityLevel == SecurityLevel::TRUSTED_ENVIRONMENT;
-
-        Finalize logOnFail([&] {
-            logKeystoreKeyCreationEvent(keyParams, false /*wasCreationSuccessful*/,
-                                        rc.getErrorCode());
-        });
-
-        KeyCharacteristics outCharacteristics;
-        KeyStoreServiceReturnCode error;
-        auto hidl_cb = [&](ErrorCode ret, const hidl_vec<uint8_t>& hidlKeyBlob,
-                           const KeyCharacteristics& keyCharacteristics) {
-            keymasterDevice_->logIfKeymasterVendorError(ret);
-            error = ret;
-            if (!error.isOk()) {
-                return;
-            }
-            consider_fallback = false;
-            outCharacteristics = keyCharacteristics;
-
-            Blob keyBlob(&hidlKeyBlob[0], hidlKeyBlob.size(), nullptr, 0, ::TYPE_KEYMASTER_10);
-            keyBlob.setSecurityLevel(securityLevel);
-            keyBlob.setCriticalToDeviceEncryption(flags &
-                                                  KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION);
-            if (isAuthenticationBound(keyParams) && !keyBlob.isCriticalToDeviceEncryption()) {
-                keyBlob.setSuperEncrypted(true);
-            }
-            keyBlob.setEncrypted(flags & KEYSTORE_FLAG_ENCRYPTED);
-
-            AuthorizationSet sw_enforced = keyParams;
-            sw_enforced.Subtract(outCharacteristics.hardwareEnforced);
-            sw_enforced.Union(outCharacteristics.softwareEnforced);
-            sw_enforced.Filter([](const KeyParameter& param) -> bool {
-                return !(param.tag == Tag::APPLICATION_DATA || param.tag == Tag::APPLICATION_ID);
-            });
-            if (!sw_enforced.Contains(Tag::USER_ID)) {
-                // Most Java processes don't have access to this tag
-                sw_enforced.push_back(keymaster::TAG_USER_ID, get_user_id(lockedEntry->uid()));
-            }
-            Blob keyCharBlob;
-            keyCharBlob.putKeyCharacteristics(outCharacteristics.hardwareEnforced, sw_enforced);
-            error = keyStore_->put(lockedEntry, std::move(keyBlob), std::move(keyCharBlob));
-        };
-
-        rc = KS_HANDLE_HIDL_ERROR(keymasterDevice_,
-                                  keymasterDevice_->generateKey(keyParams, hidl_cb));
-        if (!rc.isOk()) {
-            return worker_cb(rc, {});
-        }
-
-        if (consider_fallback && !error.isOk()) {
-            auto fallback = keyStore_->getFallbackDevice();
-            if (!fallback) {
-                return worker_cb(error, {});
-            }
-            // No fallback for 3DES
-            for (auto& param : keyParams) {
-                auto algorithm = authorizationValue(TAG_ALGORITHM, param);
-                if (algorithm.isOk() && algorithm.value() == Algorithm::TRIPLE_DES) {
-                    return worker_cb(ErrorCode::UNSUPPORTED_ALGORITHM, {});
-                }
-            }
-
-            // delegate to fallback worker
-            fallback->generateKey(std::move(lockedEntry), std::move(keyParams), std::move(entropy),
-                                  flags, std::move(worker_cb));
-            // let fallback do the logging
-            logOnFail.release();
-            return;
-        }
-
-        if (!error.isOk()) return worker_cb(error, {});
-
-        // log on success
-        logOnFail.release();
-        logKeystoreKeyCreationEvent(keyParams, true /*wasCreationSuccessful*/,
-                                    error.getErrorCode());
-
-        return worker_cb(error, std::move(outCharacteristics));
-    });
-}
-
-void KeymasterWorker::generateKey(hidl_vec<KeyParameter> keyParams, generateKey2_cb worker_cb) {
-    addRequest(&Keymaster::generateKey, std::move(worker_cb), std::move(keyParams));
-}
-
-void KeymasterWorker::getKeyCharacteristics(LockedKeyBlobEntry lockedEntry,
-                                            hidl_vec<uint8_t> clientId, hidl_vec<uint8_t> appData,
-                                            Blob keyBlob, Blob charBlob,
-                                            getKeyCharacteristics_cb worker_cb) {
-    Worker::addRequest([this, CAPTURE_MOVE(lockedEntry), CAPTURE_MOVE(clientId),
-                        CAPTURE_MOVE(appData), CAPTURE_MOVE(keyBlob), CAPTURE_MOVE(charBlob),
-                        CAPTURE_MOVE(worker_cb)]() {
-        auto result = createKeyCharacteristicsCache(lockedEntry, clientId, appData,
-                                                    std::move(keyBlob), std::move(charBlob));
-        return worker_cb(std::get<0>(result), std::move(std::get<1>(result)));
-    });
-}
-
-void KeymasterWorker::importKey(LockedKeyBlobEntry lockedEntry, hidl_vec<KeyParameter> keyParams,
-                                KeyFormat keyFormat, hidl_vec<uint8_t> keyData, int flags,
-                                importKey_cb worker_cb) {
-    Worker::addRequest([this, CAPTURE_MOVE(lockedEntry), CAPTURE_MOVE(keyParams), keyFormat,
-                        CAPTURE_MOVE(keyData), flags, CAPTURE_MOVE(worker_cb)]() mutable {
-        SecurityLevel securityLevel = keymasterDevice_->halVersion().securityLevel;
-
-        // Fallback cannot be considered for Strongbox. Further versions restrictions are enforced
-        // by KeyStore::getFallbackDevice()
-        bool consider_fallback = securityLevel == SecurityLevel::TRUSTED_ENVIRONMENT;
-
-        KeyStoreServiceReturnCode error;
-        Finalize logOnFail([&] {
-            logKeystoreKeyCreationEvent(keyParams, false /*wasCreationSuccessful*/,
-                                        error.getErrorCode());
-        });
-
-        KeyCharacteristics outCharacteristics;
-        auto hidl_cb = [&](ErrorCode ret, const hidl_vec<uint8_t>& hidlKeyBlob,
-                           const KeyCharacteristics& keyCharacteristics) {
-            keymasterDevice_->logIfKeymasterVendorError(ret);
-            error = ret;
-            if (!error.isOk()) {
-                LOG(INFO) << "importKey failed";
-                return;
-            }
-            consider_fallback = false;
-            outCharacteristics = keyCharacteristics;
-
-            Blob keyBlob(&hidlKeyBlob[0], hidlKeyBlob.size(), nullptr, 0, ::TYPE_KEYMASTER_10);
-            keyBlob.setSecurityLevel(securityLevel);
-            keyBlob.setCriticalToDeviceEncryption(flags &
-                                                  KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION);
-            if (isAuthenticationBound(keyParams) && !keyBlob.isCriticalToDeviceEncryption()) {
-                keyBlob.setSuperEncrypted(true);
-            }
-            keyBlob.setEncrypted(flags & KEYSTORE_FLAG_ENCRYPTED);
-
-            AuthorizationSet sw_enforced = keyParams;
-            sw_enforced.Subtract(outCharacteristics.hardwareEnforced);
-            sw_enforced.Union(outCharacteristics.softwareEnforced);
-            sw_enforced.Filter([](const KeyParameter& param) -> bool {
-                return !(param.tag == Tag::APPLICATION_DATA || param.tag == Tag::APPLICATION_ID);
-            });
-            if (!sw_enforced.Contains(Tag::USER_ID)) {
-                // Most Java processes don't have access to this tag
-                sw_enforced.push_back(keymaster::TAG_USER_ID, get_user_id(lockedEntry->uid()));
-            }
-            Blob keyCharBlob;
-            keyCharBlob.putKeyCharacteristics(outCharacteristics.hardwareEnforced, sw_enforced);
-            error = keyStore_->put(lockedEntry, std::move(keyBlob), std::move(keyCharBlob));
-        };
-
-        KeyStoreServiceReturnCode rc = KS_HANDLE_HIDL_ERROR(
-            keymasterDevice_, keymasterDevice_->importKey(keyParams, keyFormat, keyData, hidl_cb));
-        if (!rc.isOk()) {
-            return worker_cb(rc, {});
-        }
-
-        if (consider_fallback && !error.isOk()) {
-            auto fallback = keyStore_->getFallbackDevice();
-            if (!fallback) {
-                return worker_cb(error, {});
-            }
-            // No fallback for 3DES
-            for (auto& param : keyParams) {
-                auto algorithm = authorizationValue(TAG_ALGORITHM, param);
-                if (algorithm.isOk() && algorithm.value() == Algorithm::TRIPLE_DES) {
-                    return worker_cb(ErrorCode::UNSUPPORTED_ALGORITHM, {});
-                }
-            }
-
-            // delegate to fallback worker
-            fallback->importKey(std::move(lockedEntry), std::move(keyParams), keyFormat,
-                                std::move(keyData), flags, std::move(worker_cb));
-            // let fallback to the logging
-            logOnFail.release();
-            return;
-        }
-
-        if (!error.isOk()) return worker_cb(error, {});
-
-        // log on success
-        logOnFail.release();
-        logKeystoreKeyCreationEvent(keyParams, true /*wasCreationSuccessful*/,
-                                    error.getErrorCode());
-
-        return worker_cb(error, std::move(outCharacteristics));
-    });
-}
-
-void KeymasterWorker::importWrappedKey(LockedKeyBlobEntry wrappingLockedEntry,
-                                       LockedKeyBlobEntry wrapppedLockedEntry,
-                                       hidl_vec<uint8_t> wrappedKeyData,
-                                       hidl_vec<uint8_t> maskingKey,
-                                       hidl_vec<KeyParameter> unwrappingParams, Blob wrappingBlob,
-                                       Blob wrappingCharBlob, uint64_t passwordSid,
-                                       uint64_t biometricSid, importWrappedKey_cb worker_cb) {
-    Worker::addRequest([this, CAPTURE_MOVE(wrappingLockedEntry), CAPTURE_MOVE(wrapppedLockedEntry),
-                        CAPTURE_MOVE(wrappedKeyData), CAPTURE_MOVE(maskingKey),
-                        CAPTURE_MOVE(unwrappingParams), CAPTURE_MOVE(wrappingBlob),
-                        CAPTURE_MOVE(wrappingCharBlob), passwordSid, biometricSid,
-                        CAPTURE_MOVE(worker_cb)]() mutable {
-        auto hidlWrappingKey = blob2hidlVec(wrappingBlob);
-
-        SecurityLevel securityLevel = keymasterDevice_->halVersion().securityLevel;
-
-        KeyCharacteristics outCharacteristics;
-        KeyStoreServiceReturnCode error;
-
-        auto hidlCb = [&](ErrorCode ret, const hidl_vec<uint8_t>& hidlKeyBlob,
-                          const KeyCharacteristics& keyCharacteristics) {
-            keymasterDevice_->logIfKeymasterVendorError(ret);
-            error = ret;
-            if (!error.isOk()) {
-                return;
-            }
-            outCharacteristics = keyCharacteristics;
-
-            Blob keyBlob(hidlKeyBlob.data(), hidlKeyBlob.size(), nullptr, 0, ::TYPE_KEYMASTER_10);
-            keyBlob.setSecurityLevel(securityLevel);
-            if (isAuthenticationBound(keyCharacteristics.hardwareEnforced)) {
-                keyBlob.setSuperEncrypted(true);
-            }
-
-            AuthorizationSet sw_enforced = outCharacteristics.softwareEnforced;
-            if (!sw_enforced.Contains(Tag::USER_ID)) {
-                // Most Java processes don't have access to this tag
-                sw_enforced.push_back(keymaster::TAG_USER_ID,
-                                      get_user_id(wrapppedLockedEntry->uid()));
-            }
-            Blob keyCharBlob;
-            keyCharBlob.putKeyCharacteristics(outCharacteristics.hardwareEnforced, sw_enforced);
-            error = keyStore_->put(wrapppedLockedEntry, std::move(keyBlob), std::move(keyCharBlob));
-        };
-
-        KeyStoreServiceReturnCode rc = KS_HANDLE_HIDL_ERROR(
-            keymasterDevice_, keymasterDevice_->importWrappedKey(
-                                  wrappedKeyData, hidlWrappingKey, maskingKey, unwrappingParams,
-                                  passwordSid, biometricSid, hidlCb));
-
-        // possible hidl error
-        if (!rc.isOk()) {
-            return worker_cb(rc, {});
-        }
-
-        if (error == ErrorCode::KEY_REQUIRES_UPGRADE) {
-            std::tie(rc, wrappingBlob) = upgradeKeyBlob(wrappingLockedEntry, {});
-            if (!rc.isOk()) {
-                return worker_cb(rc, {});
-            }
-
-            auto upgradedHidlKeyBlob = blob2hidlVec(wrappingBlob);
-
-            rc = KS_HANDLE_HIDL_ERROR(keymasterDevice_,
-                                      keymasterDevice_->importWrappedKey(
-                                          wrappedKeyData, upgradedHidlKeyBlob, maskingKey,
-                                          unwrappingParams, passwordSid, biometricSid, hidlCb));
-            if (!rc.isOk()) {
-                error = rc;
-            }
-        }
-        return worker_cb(error, std::move(outCharacteristics));
-    });
-}
-
-void KeymasterWorker::exportKey(LockedKeyBlobEntry lockedEntry, KeyFormat exportFormat,
-                                hidl_vec<uint8_t> clientId, hidl_vec<uint8_t> appData, Blob keyBlob,
-                                Blob charBlob, exportKey_cb worker_cb) {
-    Worker::addRequest([this, CAPTURE_MOVE(lockedEntry), exportFormat, CAPTURE_MOVE(clientId),
-                        CAPTURE_MOVE(appData), CAPTURE_MOVE(keyBlob), CAPTURE_MOVE(charBlob),
-                        CAPTURE_MOVE(worker_cb)]() mutable {
-        auto key = blob2hidlVec(keyBlob);
-
-        ExportResult result;
-        auto hidlCb = [&](ErrorCode ret,
-                          const ::android::hardware::hidl_vec<uint8_t>& keyMaterial) {
-            keymasterDevice_->logIfKeymasterVendorError(ret);
-            result.resultCode = ret;
-            if (!result.resultCode.isOk()) {
-                if (result.resultCode == ErrorCode::INVALID_KEY_BLOB) {
-                    log_key_integrity_violation(lockedEntry->alias().c_str(), lockedEntry->uid());
-                }
-                return;
-            }
-            result.exportData = keyMaterial;
-        };
-        KeyStoreServiceReturnCode rc = KS_HANDLE_HIDL_ERROR(
-            keymasterDevice_,
-            keymasterDevice_->exportKey(exportFormat, key, clientId, appData, hidlCb));
-
-        // Overwrite result->resultCode only on HIDL error. Otherwise we want the result set in the
-        // callback hidlCb.
-        if (!rc.isOk()) {
-            result.resultCode = rc;
-        }
-
-        if (result.resultCode == ErrorCode::KEY_REQUIRES_UPGRADE) {
-            AuthorizationSet upgradeParams;
-            if (clientId.size()) {
-                upgradeParams.push_back(TAG_APPLICATION_ID, clientId);
-            }
-            if (appData.size()) {
-                upgradeParams.push_back(TAG_APPLICATION_DATA, appData);
-            }
-            std::tie(rc, keyBlob) = upgradeKeyBlob(lockedEntry, upgradeParams);
-            if (!rc.isOk()) {
-                return worker_cb(std::move(result));
-            }
-
-            auto upgradedHidlKeyBlob = blob2hidlVec(keyBlob);
-
-            rc = KS_HANDLE_HIDL_ERROR(keymasterDevice_,
-                                      keymasterDevice_->exportKey(exportFormat, upgradedHidlKeyBlob,
-                                                                  clientId, appData, hidlCb));
-            if (!rc.isOk()) {
-                result.resultCode = rc;
-            }
-        }
-        return worker_cb(std::move(result));
-    });
-}
-void KeymasterWorker::attestKey(hidl_vec<uint8_t> keyToAttest, hidl_vec<KeyParameter> attestParams,
-                                attestKey_cb worker_cb) {
-    addRequest(&Keymaster::attestKey, std::move(worker_cb), std::move(keyToAttest),
-               std::move(attestParams));
-}
-
-void KeymasterWorker::deleteKey(hidl_vec<uint8_t> keyBlob, deleteKey_cb _hidl_cb) {
-    addRequest(&Keymaster::deleteKey, std::move(_hidl_cb), std::move(keyBlob));
-}
-
-void KeymasterWorker::binderDied(android::wp<IBinder> who) {
-    Worker::addRequest([this, who]() {
-        auto operations = operationMap_.getOperationsForToken(who.unsafe_get());
-        for (const auto& token : operations) {
-            abort(token, ResponseCode::BINDER_DIED);
-            keyStore_->removeOperationDevice(token);
-        }
-    });
-}
-
-}  // namespace keystore
diff --git a/keystore/keymaster_worker.h b/keystore/keymaster_worker.h
deleted file mode 100644
index f11af29..0000000
--- a/keystore/keymaster_worker.h
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
-**
-** Copyright 2018, 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 KEYSTORE_KEYMASTER_WORKER_H_
-#define KEYSTORE_KEYMASTER_WORKER_H_
-
-#include <condition_variable>
-#include <functional>
-#include <keymasterV4_1/Keymaster.h>
-#include <memory>
-#include <mutex>
-#include <optional>
-#include <queue>
-#include <thread>
-#include <tuple>
-
-#include <keystore/ExportResult.h>
-#include <keystore/KeyCharacteristics.h>
-#include <keystore/KeymasterBlob.h>
-#include <keystore/OperationResult.h>
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore_return_types.h>
-
-#include "blob.h"
-#include "operation.h"
-
-namespace keystore {
-
-using android::sp;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using android::hardware::keymaster::V4_1::support::Keymaster;
-using ::android::security::keymaster::KeymasterBlob;
-
-class KeyStore;
-
-class Worker {
-
-    /*
-     * NonCopyableFunction works similar to std::function in that it wraps callable objects and
-     * erases their type. The rationale for using a custom class instead of
-     * std::function is that std::function requires the wrapped object to be copy contructible.
-     * NonCopyableFunction is itself not copyable and never attempts to copy the wrapped object.
-     * TODO use similar optimization as std::function to remove the extra make_unique allocation.
-     */
-    template <typename Fn> class NonCopyableFunction;
-
-    template <typename Ret, typename... Args> class NonCopyableFunction<Ret(Args...)> {
-
-        class NonCopyableFunctionBase {
-          public:
-            NonCopyableFunctionBase() = default;
-            virtual ~NonCopyableFunctionBase() {}
-            virtual Ret operator()(Args... args) = 0;
-            NonCopyableFunctionBase(const NonCopyableFunctionBase&) = delete;
-            NonCopyableFunctionBase& operator=(const NonCopyableFunctionBase&) = delete;
-        };
-
-        template <typename Fn>
-        class NonCopyableFunctionTypeEraser : public NonCopyableFunctionBase {
-          private:
-            Fn f_;
-
-          public:
-            NonCopyableFunctionTypeEraser() = default;
-            explicit NonCopyableFunctionTypeEraser(Fn f) : f_(std::move(f)) {}
-            Ret operator()(Args... args) override { return f_(std::move(args)...); }
-        };
-
-      private:
-        std::unique_ptr<NonCopyableFunctionBase> f_;
-
-      public:
-        NonCopyableFunction() = default;
-        // NOLINTNEXTLINE(google-explicit-constructor)
-        template <typename F> NonCopyableFunction(F f) {
-            f_ = std::make_unique<NonCopyableFunctionTypeEraser<F>>(std::move(f));
-        }
-        NonCopyableFunction(NonCopyableFunction&& other) = default;
-        NonCopyableFunction& operator=(NonCopyableFunction&& other) = default;
-        NonCopyableFunction(const NonCopyableFunction& other) = delete;
-        NonCopyableFunction& operator=(const NonCopyableFunction& other) = delete;
-
-        Ret operator()(Args... args) {
-            if (f_) return (*f_)(std::move(args)...);
-        }
-    };
-
-    using WorkerTask = NonCopyableFunction<void()>;
-
-    std::queue<WorkerTask> pending_requests_;
-    std::mutex pending_requests_mutex_;
-    std::condition_variable pending_requests_cond_var_;
-    bool running_ = false;
-    bool terminate_ = false;
-
-  public:
-    Worker();
-    ~Worker();
-    void addRequest(WorkerTask request);
-};
-
-template <typename... Args> struct MakeKeymasterWorkerCB;
-
-template <typename ErrorType, typename... Args>
-struct MakeKeymasterWorkerCB<ErrorType, std::function<void(Args...)>> {
-    using type = std::function<void(ErrorType, std::tuple<std::decay_t<Args>...>&&)>;
-};
-
-template <typename ErrorType> struct MakeKeymasterWorkerCB<ErrorType> {
-    using type = std::function<void(ErrorType)>;
-};
-
-template <typename... Args>
-using MakeKeymasterWorkerCB_t = typename MakeKeymasterWorkerCB<Args...>::type;
-
-class KeymasterWorker : protected Worker {
-  private:
-    sp<Keymaster> keymasterDevice_;
-    OperationMap operationMap_;
-    KeyStore* keyStore_;
-
-    template <typename KMFn, typename ErrorType, typename... Args, size_t... I>
-    void unwrap_tuple(KMFn kmfn, std::function<void(ErrorType)> cb,
-                      const std::tuple<Args...>& tuple, std::index_sequence<I...>) {
-        cb(((*keymasterDevice_).*kmfn)(std::get<I>(tuple)...));
-    }
-
-    template <typename KMFn, typename ErrorType, typename... ReturnTypes, typename... Args,
-              size_t... I>
-    void unwrap_tuple(KMFn kmfn, std::function<void(ErrorType, std::tuple<ReturnTypes...>&&)> cb,
-                      const std::tuple<Args...>& tuple, std::index_sequence<I...>) {
-        std::tuple<ReturnTypes...> returnValue;
-        auto result = ((*keymasterDevice_).*kmfn)(
-            std::get<I>(tuple)...,
-            [&returnValue](const ReturnTypes&... args) { returnValue = std::make_tuple(args...); });
-        cb(std::move(result), std::move(returnValue));
-    }
-
-    template <typename KMFn, typename ErrorType, typename... Args>
-    void addRequest(KMFn kmfn, std::function<void(ErrorType)> cb, Args&&... args) {
-        Worker::addRequest([this, kmfn, cb = std::move(cb),
-                            tuple = std::make_tuple(std::forward<Args>(args)...)]() {
-            unwrap_tuple(kmfn, std::move(cb), tuple, std::index_sequence_for<Args...>{});
-        });
-    }
-
-    template <typename KMFn, typename ErrorType, typename... ReturnTypes, typename... Args>
-    void addRequest(KMFn kmfn, std::function<void(ErrorType, std::tuple<ReturnTypes...>&&)> cb,
-                    Args&&... args) {
-        Worker::addRequest([this, kmfn, cb = std::move(cb),
-                            tuple = std::make_tuple(std::forward<Args>(args)...)]() {
-            unwrap_tuple(kmfn, std::move(cb), tuple, std::index_sequence_for<Args...>{});
-        });
-    }
-
-    void deleteOldKeyOnUpgrade(const LockedKeyBlobEntry& blobfile, Blob keyBlob);
-    std::tuple<KeyStoreServiceReturnCode, Blob>
-    upgradeKeyBlob(const LockedKeyBlobEntry& lockedEntry, const AuthorizationSet& params);
-    std::tuple<KeyStoreServiceReturnCode, KeyCharacteristics, Blob, Blob>
-    createKeyCharacteristicsCache(const LockedKeyBlobEntry& lockedEntry,
-                                  const hidl_vec<uint8_t>& clientId,
-                                  const hidl_vec<uint8_t>& appData, Blob keyBlob, Blob charBlob);
-
-    /**
-     * Get the auth token for this operation from the auth token table.
-     *
-     * Returns NO_ERROR if the auth token was found or none was required.  If not needed, the
-     *             token will be empty (which keymaster interprets as no auth token).
-     *         OP_AUTH_NEEDED if it is a per op authorization, no authorization token exists for
-     *             that operation and  failOnTokenMissing is false.
-     *         KM_ERROR_KEY_USER_NOT_AUTHENTICATED if there is no valid auth token for the operation
-     */
-    std::pair<KeyStoreServiceReturnCode, HardwareAuthToken>
-    getAuthToken(const KeyCharacteristics& characteristics, uint64_t handle, KeyPurpose purpose,
-                 bool failOnTokenMissing = true);
-
-    KeyStoreServiceReturnCode abort(const sp<IBinder>& token, ResponseCode reason_for_abort);
-
-    bool pruneOperation();
-
-    KeyStoreServiceReturnCode getOperationAuthTokenIfNeeded(std::shared_ptr<Operation> op);
-
-    void appendConfirmationTokenIfNeeded(const KeyCharacteristics& keyCharacteristics,
-                                         hidl_vec<KeyParameter>* params);
-
-  public:
-    KeymasterWorker(sp<Keymaster> keymasterDevice, KeyStore* keyStore);
-
-    void logIfKeymasterVendorError(ErrorCode ec) const;
-
-    using worker_begin_cb = std::function<void(::android::security::keymaster::OperationResult)>;
-    void begin(LockedKeyBlobEntry, sp<IBinder> appToken, Blob keyBlob, Blob charBlob,
-               bool pruneable, KeyPurpose purpose, AuthorizationSet opParams,
-               hidl_vec<uint8_t> entropy, worker_begin_cb worker_cb);
-
-    using update_cb = std::function<void(::android::security::keymaster::OperationResult)>;
-    void update(sp<IBinder> token, AuthorizationSet params, hidl_vec<uint8_t> data,
-                update_cb _hidl_cb);
-
-    using finish_cb = std::function<void(::android::security::keymaster::OperationResult)>;
-    void finish(sp<IBinder> token, AuthorizationSet params, hidl_vec<uint8_t> input,
-                hidl_vec<uint8_t> signature, hidl_vec<uint8_t> entorpy, finish_cb worker_cb);
-
-    using abort_cb = std::function<void(KeyStoreServiceReturnCode)>;
-    void abort(sp<IBinder> token, abort_cb _hidl_cb);
-
-    using getHardwareInfo_cb = MakeKeymasterWorkerCB_t<Return<void>, Keymaster::getHardwareInfo_cb>;
-    void getHardwareInfo(getHardwareInfo_cb _hidl_cb);
-
-    using getHmacSharingParameters_cb =
-        MakeKeymasterWorkerCB_t<Return<void>, Keymaster::getHmacSharingParameters_cb>;
-    void getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb);
-
-    using computeSharedHmac_cb =
-        MakeKeymasterWorkerCB_t<Return<void>, Keymaster::computeSharedHmac_cb>;
-    void computeSharedHmac(hidl_vec<HmacSharingParameters> params, computeSharedHmac_cb _hidl_cb);
-
-    using verifyAuthorization_cb =
-        std::function<void(KeyStoreServiceReturnCode ec, HardwareAuthToken, VerificationToken)>;
-    void verifyAuthorization(uint64_t challenge, hidl_vec<KeyParameter> params,
-                             HardwareAuthToken token, verifyAuthorization_cb _hidl_cb);
-
-    using addRngEntropy_cb = MakeKeymasterWorkerCB_t<Return<ErrorCode>>;
-    void addRngEntropy(hidl_vec<uint8_t> data, addRngEntropy_cb _hidl_cb);
-
-    using generateKey_cb = std::function<void(
-        KeyStoreServiceReturnCode, ::android::hardware::keymaster::V4_0::KeyCharacteristics)>;
-    void generateKey(LockedKeyBlobEntry, hidl_vec<KeyParameter> keyParams,
-                     hidl_vec<uint8_t> entropy, int flags, generateKey_cb _hidl_cb);
-
-    using generateKey2_cb = MakeKeymasterWorkerCB_t<Return<void>, Keymaster::generateKey_cb>;
-    void generateKey(hidl_vec<KeyParameter> keyParams, generateKey2_cb _hidl_cb);
-
-    using getKeyCharacteristics_cb = std::function<void(
-        KeyStoreServiceReturnCode, ::android::hardware::keymaster::V4_0::KeyCharacteristics)>;
-    void getKeyCharacteristics(LockedKeyBlobEntry lockedEntry, hidl_vec<uint8_t> clientId,
-                               hidl_vec<uint8_t> appData, Blob keyBlob, Blob charBlob,
-                               getKeyCharacteristics_cb _hidl_cb);
-
-    using importKey_cb = std::function<void(
-        KeyStoreServiceReturnCode, ::android::hardware::keymaster::V4_0::KeyCharacteristics)>;
-    void importKey(LockedKeyBlobEntry lockedEntry, hidl_vec<KeyParameter> params,
-                   KeyFormat keyFormat, hidl_vec<uint8_t> keyData, int flags,
-                   importKey_cb _hidl_cb);
-
-    using importWrappedKey_cb = std::function<void(
-        KeyStoreServiceReturnCode, ::android::hardware::keymaster::V4_0::KeyCharacteristics)>;
-    void importWrappedKey(LockedKeyBlobEntry wrappingLockedEntry,
-                          LockedKeyBlobEntry wrapppedLockedEntry, hidl_vec<uint8_t> wrappedKeyData,
-                          hidl_vec<uint8_t> maskingKey, hidl_vec<KeyParameter> unwrappingParams,
-                          Blob wrappingBlob, Blob wrappingCharBlob, uint64_t passwordSid,
-                          uint64_t biometricSid, importWrappedKey_cb worker_cb);
-
-    using exportKey_cb = std::function<void(::android::security::keymaster::ExportResult)>;
-    void exportKey(LockedKeyBlobEntry lockedEntry, KeyFormat exportFormat,
-                   hidl_vec<uint8_t> clientId, hidl_vec<uint8_t> appData, Blob keyBlob,
-                   Blob charBlob, exportKey_cb _hidl_cb);
-
-    using attestKey_cb = MakeKeymasterWorkerCB_t<Return<void>, Keymaster::attestKey_cb>;
-    void attestKey(hidl_vec<uint8_t> keyToAttest, hidl_vec<KeyParameter> attestParams,
-                   attestKey_cb _hidl_cb);
-
-    using deleteKey_cb = MakeKeymasterWorkerCB_t<Return<ErrorCode>>;
-    void deleteKey(hidl_vec<uint8_t> keyBlob, deleteKey_cb _hidl_cb);
-
-    using begin_cb = MakeKeymasterWorkerCB_t<Return<void>, Keymaster::begin_cb>;
-    void begin(KeyPurpose purpose, hidl_vec<uint8_t> key, hidl_vec<KeyParameter> inParams,
-               HardwareAuthToken authToken, begin_cb _hidl_cb);
-
-    void binderDied(android::wp<IBinder> who);
-
-    const Keymaster::VersionResult& halVersion() { return keymasterDevice_->halVersion(); }
-};
-
-}  // namespace keystore
-
-#endif  // KEYSTORE_KEYMASTER_WORKER_H_
diff --git a/keystore/keystore.rc b/keystore/keystore.rc
deleted file mode 100644
index 132039a..0000000
--- a/keystore/keystore.rc
+++ /dev/null
@@ -1,5 +0,0 @@
-service keystore /system/bin/keystore /data/misc/keystore
-    class main
-    user keystore
-    group keystore drmrpc readproc log
-    writepid /dev/cpuset/foreground/tasks
diff --git a/keystore/keystore_aidl_hidl_marshalling_utils.cpp b/keystore/keystore_aidl_hidl_marshalling_utils.cpp
deleted file mode 100644
index 823ca58..0000000
--- a/keystore/keystore_aidl_hidl_marshalling_utils.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
-**
-** Copyright 2016, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include "keystore_aidl_hidl_marshalling_utils.h"
-
-#include <keystore/ExportResult.h>
-#include <keystore/KeyCharacteristics.h>
-#include <keystore/KeymasterBlob.h>
-#include <keystore/KeymasterCertificateChain.h>
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore_hidl_support.h>
-
-namespace keystore {
-
-// reads byte[]
-hidl_vec<uint8_t> readKeymasterBlob(const android::Parcel& in) {
-
-    ssize_t length = in.readInt32();
-    if (length <= 0) {
-        return {};
-    }
-
-    const void* buf = in.readInplace(length);
-    if (!buf) return {};
-
-    return blob2hidlVec(reinterpret_cast<const uint8_t*>(buf), size_t(length));
-}
-
-android::status_t writeKeymasterBlob(const hidl_vec<uint8_t>& blob, android::Parcel* out) {
-    int32_t size = int32_t(std::min<size_t>(blob.size(), std::numeric_limits<int32_t>::max()));
-
-    auto rc = out->writeInt32(size);
-    if (rc != ::android::OK) return rc;
-
-    if (!size) return ::android::OK;
-
-    return out->write(blob.data(), size);
-}
-
-android::status_t writeKeymasterBlob(const ::std::vector<int32_t>& blob, android::Parcel* out) {
-
-    int32_t size = int32_t(std::min<size_t>(blob.size(), std::numeric_limits<int32_t>::max()));
-
-    auto rc = out->writeInt32(size);
-    if (rc != ::android::OK) return rc;
-
-    if (!size) return ::android::OK;
-
-    return out->write(blob.data(), size);
-}
-
-NullOr<KeyParameter> readKeyParameterFromParcel(const android::Parcel& in) {
-    // Method must be in sync with KeymasterArgument.java
-    if (in.readInt32() == 0) {
-        return {};
-    }
-    KeyParameter result;
-
-    Tag tag = static_cast<Tag>(in.readInt32());
-    result.tag = tag;
-    switch (typeFromTag(tag)) {
-    case TagType::ENUM:
-    case TagType::ENUM_REP:
-    case TagType::UINT:
-    case TagType::UINT_REP:
-        result.f.integer = in.readInt32();
-        break;
-    case TagType::ULONG:
-    case TagType::ULONG_REP:
-    case TagType::DATE:
-        result.f.longInteger = in.readInt64();
-        break;
-    case TagType::BOOL:
-        result.f.boolValue = true;
-        break;
-    case TagType::BIGNUM:
-    case TagType::BYTES:
-        result.blob = readKeymasterBlob(in);  // byte array
-        break;
-    default:
-        ALOGE("Unsupported KeyParameter tag %d", tag);
-        return {};
-    }
-    return result;
-}
-
-android::status_t writeKeyParameterToParcel(const KeyParameter& param, android::Parcel* out) {
-    // Method must be in sync with with KeymasterArgument.java
-    // Presence flag must be written by caller.
-
-    auto tag = param.tag;
-    auto rc = out->writeInt32(uint32_t(tag));
-    if (rc != ::android::OK) return rc;
-    switch (typeFromTag(param.tag)) {
-    case TagType::ENUM:
-    case TagType::ENUM_REP:
-    case TagType::UINT:
-    case TagType::UINT_REP:
-        rc = out->writeInt32(param.f.integer);
-        break;
-    case TagType::ULONG:
-    case TagType::ULONG_REP:
-    case TagType::DATE:
-        rc = out->writeInt64(param.f.longInteger);
-        break;
-    case TagType::BOOL:
-        // nothing to do here presence indicates true
-        break;
-    case TagType::BIGNUM:
-    case TagType::BYTES:
-        rc = writeKeymasterBlob(param.blob, out);
-        break;
-    default:
-        ALOGE("Failed to write KeyParameter: Unsupported tag %d", param.tag);
-        rc = android::BAD_VALUE;
-        break;
-    }
-    return rc;
-}
-
-hidl_vec<KeyParameter> readParamSetFromParcel(const android::Parcel& in) {
-
-    ssize_t length = in.readInt32();  // -1 for null
-    size_t ulength = (size_t)length;
-    if (length < 0) {
-        ulength = 0;
-    }
-    hidl_vec<KeyParameter> result;
-    result.resize(ulength);
-    for (size_t i = 0; i < ulength; ++i) {
-        auto param = readKeyParameterFromParcel(in);
-        if (!param.isOk()) {
-            ALOGE("Error reading KeyParameter from parcel");
-            return {};
-        }
-        result[i] = param.value();
-    }
-    return result;
-}
-
-android::status_t writeParamSetToParcel(const hidl_vec<KeyParameter>& params,
-                                        android::Parcel* out) {
-    int32_t size = int32_t(std::min<size_t>(params.size(), std::numeric_limits<int32_t>::max()));
-
-    auto rc = out->writeInt32(size);
-    if (rc != ::android::OK) return rc;
-    for (int32_t i = 0; i < size; ++i) {
-        rc = out->writeInt32(1);  // writeTypedObject presence flag.
-        if (rc != ::android::OK) return rc;
-        rc = writeKeyParameterToParcel(params[i], out);
-        if (rc != ::android::OK) return rc;
-    }
-    return rc;
-}
-
-hidl_vec<hidl_vec<uint8_t>> readCertificateChainFromParcel(const android::Parcel& in) {
-    hidl_vec<hidl_vec<uint8_t>> result;
-
-    ssize_t count = in.readInt32();
-    size_t ucount = count;
-    if (count <= 0) {
-        return result;
-    }
-
-    result.resize(ucount);
-
-    for (size_t i = 0; i < ucount; ++i) {
-        result[i] = readKeymasterBlob(in);
-    }
-    return result;
-};
-
-android::status_t writeCertificateChainToParcel(const hidl_vec<hidl_vec<uint8_t>>& certs,
-                                                android::Parcel* out) {
-    int32_t count = int32_t(std::min<size_t>(certs.size(), std::numeric_limits<int32_t>::max()));
-    auto rc = out->writeInt32(count);
-
-    for (int32_t i = 0; i < count; ++i) {
-        rc = writeKeymasterBlob(certs[i], out);
-        if (rc != ::android::OK) return rc;
-    }
-    return rc;
-}
-
-};  // namespace keystore
-
-// Implementation for  keystore parcelables.
-// TODO: split implementation into separate classes
-namespace android {
-namespace security {
-namespace keymaster {
-
-using ::android::status_t;
-using ::keystore::ErrorCode;
-
-ExportResult::ExportResult() : resultCode() {}
-
-ExportResult::~ExportResult() {}
-
-status_t ExportResult::readFromParcel(const Parcel* inn) {
-    const Parcel& in = *inn;
-    resultCode = ErrorCode(in.readInt32());
-    exportData = keystore::readKeymasterBlob(in);
-    return OK;
-}
-
-status_t ExportResult::writeToParcel(Parcel* out) const {
-    out->writeInt32(resultCode.getErrorCode());
-    return keystore::writeKeymasterBlob(exportData, out);
-}
-
-status_t KeyCharacteristics::readFromParcel(const Parcel* in) {
-    softwareEnforced.readFromParcel(in);
-    return hardwareEnforced.readFromParcel(in);
-}
-
-status_t KeyCharacteristics::writeToParcel(Parcel* out) const {
-    softwareEnforced.writeToParcel(out);
-    return hardwareEnforced.writeToParcel(out);
-}
-
-status_t KeymasterBlob::readFromParcel(const Parcel* in) {
-    data_ = keystore::readKeymasterBlob(*in);
-    return OK;
-}
-
-status_t KeymasterBlob::writeToParcel(Parcel* out) const {
-    return keystore::writeKeymasterBlob(data_, out);
-}
-
-status_t KeymasterCertificateChain::readFromParcel(const Parcel* in) {
-    chain = keystore::readCertificateChainFromParcel(*in);
-    return OK;
-}
-
-status_t KeymasterCertificateChain::writeToParcel(Parcel* out) const {
-    return keystore::writeCertificateChainToParcel(chain, out);
-}
-
-}  // namespace keymaster
-}  // namespace security
-
-}  // namespace android
diff --git a/keystore/keystore_aidl_hidl_marshalling_utils.h b/keystore/keystore_aidl_hidl_marshalling_utils.h
deleted file mode 100644
index ea72197..0000000
--- a/keystore/keystore_aidl_hidl_marshalling_utils.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
-**
-** Copyright 2016, 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 KEYSTORE_KEYSTORE_AIDL_HIDL_MARSHALLING_UTILS_H_
-#define KEYSTORE_KEYSTORE_AIDL_HIDL_MARSHALLING_UTILS_H_
-
-#include <utility>
-
-#include <binder/Parcel.h>
-
-#include <keystore/keymaster_types.h>
-
-namespace keystore {
-
-template <typename Fn, typename... Args>
-inline auto nullable(Fn fn, const android::Parcel& in, Args&&... args)
-    -> NullOr<decltype(fn(in, std::forward<Args>(args)...))> {
-    if (in.readInt32() != 1) {
-        return {};
-    }
-
-    return fn(in, std::forward<Args>(args)...);
-}
-template <typename Fn, typename Arg>
-inline android::status_t nullable(Fn fn, const NullOr<Arg>& arg, android::Parcel* out) {
-    if (!arg.isOk()) {
-        return out->writeInt32(0);
-    }
-    auto rc = out->writeInt32(1);
-    if (rc != ::android::OK) return rc;
-
-    return fn(arg.value(), out);
-}
-template <typename Fn, typename Arg>
-inline android::status_t nullable(Fn fn, Arg&& arg, android::Parcel* out) {
-    auto rc = out->writeInt32(1);
-    if (rc != ::android::OK) return rc;
-
-    return fn(std::forward<Arg>(arg), out);
-}
-
-inline android::status_t nullable(android::Parcel* out) {
-    return out->writeInt32(0);
-}
-
-/**
- * makes a copy only if inPlace is false
- */
-hidl_vec<uint8_t> readKeymasterBlob(const android::Parcel& in);
-android::status_t writeKeymasterBlob(const hidl_vec<uint8_t>& blob, android::Parcel* out);
-
-NullOr<hidl_vec<uint8_t>> readBlobAsByteArray(const android::Parcel& in, bool inPlace = true);
-android::status_t writeBlobAsByteArray(const NullOr<const hidl_vec<uint8_t>&>& blob,
-                                       android::Parcel* out);
-
-NullOr<KeyParameter> readKeyParameterFromParcel(const android::Parcel& in);
-android::status_t writeKeyParameterToParcel(const KeyParameter& param, android::Parcel* out);
-
-hidl_vec<KeyParameter> readParamSetFromParcel(const android::Parcel& in);
-android::status_t writeParamSetToParcel(const hidl_vec<KeyParameter>& params, android::Parcel* out);
-
-hidl_vec<hidl_vec<uint8_t>> readCertificateChainFromParcel(const android::Parcel& in);
-}
-
-#endif  // KEYSTORE_KEYSTORE_AIDL_HIDL_MARSHALLING_UTILS_H_
diff --git a/keystore/keystore_attestation_id.cpp b/keystore/keystore_attestation_id.cpp
index 3d9e87e..ccd3808 100644
--- a/keystore/keystore_attestation_id.cpp
+++ b/keystore/keystore_attestation_id.cpp
@@ -271,7 +271,7 @@
 
     if (uid == AID_SYSTEM) {
         /* Use a fixed ID for system callers */
-        auto pinfo = std::make_unique<KeyAttestationPackageInfo>(
+        auto pinfo = std::make_optional<KeyAttestationPackageInfo>(
             String16(kAttestationSystemPackageName), 1 /* version code */,
             std::make_shared<KeyAttestationPackageInfo::SignaturesVector>());
         key_attestation_id = KeyAttestationApplicationId(std::move(pinfo));
@@ -284,7 +284,7 @@
         if (!status.isOk()) {
             ALOGW("package manager request for key attestation ID failed with: %s %d",
                   status.exceptionMessage().string(), status.exceptionCode());
-            auto pinfo = std::make_unique<KeyAttestationPackageInfo>(
+            auto pinfo = std::make_optional<KeyAttestationPackageInfo>(
                 String16(kUnknownPackageName), 1 /* version code */,
                 std::make_shared<KeyAttestationPackageInfo::SignaturesVector>());
             key_attestation_id = KeyAttestationApplicationId(std::move(pinfo));
diff --git a/keystore/keystore_cli.cpp b/keystore/keystore_cli.cpp
deleted file mode 100644
index 428a9bc..0000000
--- a/keystore/keystore_cli.cpp
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2009 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 <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/types.h>
-#include <vector>
-
-#include <android/security/keystore/IKeystoreService.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-
-#include <keystore/keystore.h>
-
-using namespace android;
-using namespace keystore;
-using android::security::keystore::IKeystoreService;
-
-static const char* responses[] = {
-    nullptr,
-    /* [NO_ERROR]           = */ "No error",
-    /* [LOCKED]             = */ "Locked",
-    /* [UNINITIALIZED]      = */ "Uninitialized",
-    /* [SYSTEM_ERROR]       = */ "System error",
-    /* [PROTOCOL_ERROR]     = */ "Protocol error",
-    /* [PERMISSION_DENIED]  = */ "Permission denied",
-    /* [KEY_NOT_FOUND]      = */ "Key not found",
-    /* [VALUE_CORRUPTED]    = */ "Value corrupted",
-    /* [UNDEFINED_ACTION]   = */ "Undefined action",
-    /* [WRONG_PASSWORD]     = */ "Wrong password (last chance)",
-    /* [WRONG_PASSWORD + 1] = */ "Wrong password (2 tries left)",
-    /* [WRONG_PASSWORD + 2] = */ "Wrong password (3 tries left)",
-    /* [WRONG_PASSWORD + 3] = */ "Wrong password (4 tries left)",
-};
-
-#define SINGLE_ARG_INT_RETURN(cmd) \
-    do { \
-        if (strcmp(argv[1], #cmd) == 0) { \
-            if (argc < 3) { \
-                fprintf(stderr, "Usage: %s " #cmd " <name>\n", argv[0]); \
-                return 1; \
-            } \
-            int32_t ret = -1; \
-            service->cmd(String16(argv[2]), &ret); \
-            if (ret < 0) { \
-                fprintf(stderr, "%s: could not connect: %d\n", argv[0], ret); \
-                return 1; \
-            } else { \
-                printf(#cmd ": %s (%d)\n", responses[ret], ret); \
-                return 0; \
-            } \
-        } \
-    } while (0)
-
-#define SINGLE_INT_ARG_INT_RETURN(cmd) \
-    do { \
-        if (strcmp(argv[1], #cmd) == 0) { \
-            if (argc < 3) { \
-                fprintf(stderr, "Usage: %s " #cmd " <name>\n", argv[0]); \
-                return 1; \
-            } \
-            int32_t ret = -1; \
-            service->cmd(atoi(argv[2]), &ret); \
-            if (ret < 0) { \
-                fprintf(stderr, "%s: could not connect: %d\n", argv[0], ret); \
-                return 1; \
-            } else { \
-                printf(#cmd ": %s (%d)\n", responses[ret], ret); \
-                return 0; \
-            } \
-        } \
-    } while (0)
-
-#define SINGLE_ARG_PLUS_UID_INT_RETURN(cmd) \
-    do { \
-        if (strcmp(argv[1], #cmd) == 0) { \
-            if (argc < 3) { \
-                fprintf(stderr, "Usage: %s " #cmd " <name> <uid>\n", argv[0]); \
-                return 1; \
-            } \
-            int uid = -1; \
-            if (argc > 3) { \
-                uid = atoi(argv[3]); \
-                fprintf(stderr, "Running as uid %d\n", uid); \
-            } \
-            int32_t ret = -1; \
-            service->cmd(String16(argv[2]), uid, &ret); \
-            if (ret < 0) { \
-                fprintf(stderr, "%s: could not connect: %d\n", argv[0], ret); \
-                return 1; \
-            } else { \
-                printf(#cmd ": %s (%d)\n", responses[ret], ret); \
-                return 0; \
-            } \
-        } \
-    } while (0)
-
-#define SINGLE_ARG_PLUS_UID_DATA_RETURN(cmd) \
-    do { \
-        if (strcmp(argv[1], #cmd) == 0) { \
-            if (argc < 3) { \
-                fprintf(stderr, "Usage: %s " #cmd " <name> <uid>\n", argv[0]); \
-                return 1; \
-            } \
-            std::vector<uint8_t> data; \
-            int uid = -1; \
-            if (argc > 3) { \
-                uid = atoi(argv[3]); \
-                fprintf(stderr, "Running as uid %d\n", uid); \
-            } \
-            ::android::binder::Status ret = service->cmd(String16(argv[2]), uid, &data); \
-            if (!ret.isOk()) { \
-                fprintf(stderr, "Exception code: %d\n", ret.exceptionCode()); \
-                return 1; \
-            } else { \
-                fwrite(&data[0], data.size(), 1, stdout); \
-                fflush(stdout); \
-                return 0; \
-            } \
-        } \
-    } while (0)
-
-#define STRING_ARG_DATA_STDIN_INT_RETURN(cmd) \
-    do { \
-        if (strcmp(argv[1], #cmd) == 0) { \
-            if (argc < 3) { \
-                fprintf(stderr, "Usage: %s " #cmd " <name>\n", argv[0]); \
-                return 1; \
-            } \
-            uint8_t* data; \
-            size_t dataSize; \
-            read_input(&data, &dataSize); \
-            int32_t ret = -1; \
-            service->cmd(String16(argv[2]), data, dataSize, &ret); \
-            if (ret < 0) { \
-                fprintf(stderr, "%s: could not connect: %d\n", argv[0], ret); \
-                return 1; \
-            } else { \
-                printf(#cmd ": %s (%d)\n", responses[ret], ret); \
-                return 0; \
-            } \
-        } \
-    } while (0)
-
-#define SINGLE_ARG_DATA_RETURN(cmd) \
-    do { \
-        if (strcmp(argv[1], #cmd) == 0) { \
-            if (argc < 3) { \
-                fprintf(stderr, "Usage: %s " #cmd " <name>\n", argv[0]); \
-                return 1; \
-            } \
-            std::vector<uint8_t> data; \
-            ::android::binder::Status ret = service->cmd(String16(argv[2]), &data); \
-            if (!ret.isOk()) { \
-                fprintf(stderr, "Exception code: %d\n", ret.exceptionCode()); \
-                return 1; \
-            } else { \
-                fwrite(&data[0], data.size(), 1, stdout); \
-                fflush(stdout); \
-                return 0; \
-            } \
-        } \
-    } while (0)
-
-static int list(const sp<IKeystoreService>& service, const String16& name, int uid) {
-    std::vector<String16> matches;
-    ::android::binder::Status ret = service->list(name, uid, &matches);
-
-    if (!ret.isOk()) {
-        fprintf(stderr, "list: exception (%d)\n", ret.exceptionCode());
-        return 1;
-    } else {
-        std::vector<String16>::const_iterator it = matches.begin();
-        for (; it != matches.end(); ++it) {
-            printf("%s\n", String8(*it).string());
-        }
-        return 0;
-    }
-}
-
-int main(int argc, char* argv[])
-{
-    if (argc < 2) {
-        fprintf(stderr, "Usage: %s action [parameter ...]\n", argv[0]);
-        return 1;
-    }
-
-    sp<IServiceManager> sm = defaultServiceManager();
-    sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
-    sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
-
-    if (service == nullptr) {
-        fprintf(stderr, "%s: error: could not connect to keystore service\n", argv[0]);
-        return 1;
-    }
-
-    /*
-     * All the commands should return a value
-     */
-
-    SINGLE_INT_ARG_INT_RETURN(getState);
-
-    SINGLE_ARG_PLUS_UID_DATA_RETURN(get);
-
-    // TODO: insert
-
-    SINGLE_ARG_PLUS_UID_INT_RETURN(del);
-
-    SINGLE_ARG_PLUS_UID_INT_RETURN(exist);
-
-    if (strcmp(argv[1], "list") == 0) {
-        return list(service, argc < 3 ? String16("") : String16(argv[2]),
-                argc < 4 ? -1 : atoi(argv[3]));
-    }
-
-    // TODO: notifyUserPasswordChanged
-
-    SINGLE_INT_ARG_INT_RETURN(lock);
-
-    // TODO: unlock
-
-    SINGLE_INT_ARG_INT_RETURN(isEmpty);
-
-    // TODO: generate
-
-    // TODO: grant
-
-    // TODO: ungrant
-
-    // TODO: getmtime
-
-    fprintf(stderr, "%s: unknown command: %s\n", argv[0], argv[1]);
-    return 1;
-}
diff --git a/keystore/keystore_cli_v2.cpp b/keystore/keystore_cli_v2.cpp
index 4f69eb0..6e45ee2 100644
--- a/keystore/keystore_cli_v2.cpp
+++ b/keystore/keystore_cli_v2.cpp
@@ -15,6 +15,8 @@
 #include <chrono>
 #include <cstdio>
 #include <future>
+#include <iomanip>
+#include <iostream>
 #include <memory>
 #include <string>
 #include <vector>
@@ -24,38 +26,56 @@
 #include <base/strings/string_number_conversions.h>
 #include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
-#include <base/strings/utf_string_conversions.h>
-#include <base/threading/platform_thread.h>
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore_client_impl.h>
 
-#include <android/hardware/confirmationui/1.0/types.h>
-#include <android/security/BnConfirmationPromptCallback.h>
-#include <android/security/keystore/IKeystoreService.h>
+#include <aidl/android/security/apc/BnConfirmationCallback.h>
+#include <aidl/android/security/apc/IProtectedConfirmation.h>
+#include <aidl/android/system/keystore2/IKeystoreService.h>
+#include <aidl/android/system/keystore2/ResponseCode.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <keymint_support/authorization_set.h>
 
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/x509.h>
 
-//#include <keystore/keystore.h>
+#include "keystore_client.pb.h"
+
+namespace apc = ::aidl::android::security::apc;
+namespace keymint = ::aidl::android::hardware::security::keymint;
+namespace ks2 = ::aidl::android::system::keystore2;
 
 using base::CommandLine;
-using keystore::KeystoreClient;
-
-using android::sp;
-using android::String16;
-using android::security::keystore::IKeystoreService;
-using base::CommandLine;
-using ConfirmationResponseCode = android::hardware::confirmationui::V1_0::ResponseCode;
+using keystore::EncryptedData;
 
 namespace {
-using namespace keystore;
 
 struct TestCase {
     std::string name;
     bool required_for_brillo_pts;
-    AuthorizationSet parameters;
+    keymint::AuthorizationSet parameters;
 };
 
+constexpr const char keystore2_service_name[] = "android.system.keystore2";
+
+int unwrapError(const ndk::ScopedAStatus& status) {
+    if (status.isOk()) return 0;
+    if (status.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+        return status.getServiceSpecificError();
+    } else {
+        return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+    }
+}
+
+ks2::KeyDescriptor keyDescriptor(const std::string& alias) {
+    return {
+        .domain = ks2::Domain::APP,
+        .nspace = -1,  // ignored - should be -1.
+        .alias = alias,
+        .blob = {},
+    };
+}
+
 void PrintUsageAndExit() {
     printf("Usage: keystore_client_v2 <command> [options]\n");
     printf("Commands: brillo-platform-test [--prefix=<test_name_prefix>] [--test_for_0_3]\n"
@@ -78,52 +98,487 @@
     exit(1);
 }
 
-std::unique_ptr<KeystoreClient> CreateKeystoreInstance() {
-    return std::unique_ptr<KeystoreClient>(
-        static_cast<KeystoreClient*>(new keystore::KeystoreClientImpl));
+std::shared_ptr<ks2::IKeystoreService> CreateKeystoreInstance() {
+    ::ndk::SpAIBinder keystoreBinder(AServiceManager_checkService(keystore2_service_name));
+    auto result = ks2::IKeystoreService::fromBinder(keystoreBinder);
+    if (result) return result;
+    std::cerr << "Unable to connect to Keystore.";
+    exit(-1);
 }
 
-void PrintTags(const AuthorizationSet& parameters) {
-    for (auto iter = parameters.begin(); iter != parameters.end(); ++iter) {
-        auto tag_str = toString(iter->tag);
-        printf("  %s\n", tag_str.c_str());
+std::shared_ptr<ks2::IKeystoreSecurityLevel>
+GetSecurityLevelInterface(std::shared_ptr<ks2::IKeystoreService> keystore,
+                          keymint::SecurityLevel securitylevel) {
+    std::shared_ptr<ks2::IKeystoreSecurityLevel> sec_level;
+    auto rc = keystore->getSecurityLevel(securitylevel, &sec_level);
+    if (rc.isOk()) return sec_level;
+    std::cerr << "Unable to get security level interface from Keystore: " << rc.getDescription();
+    exit(-1);
+}
+
+bool isHardwareEnforced(const ks2::Authorization& a) {
+    return !(a.securityLevel == keymint::SecurityLevel::SOFTWARE ||
+             a.securityLevel == keymint::SecurityLevel::KEYSTORE);
+}
+
+void PrintTags(const std::vector<ks2::Authorization>& characteristics, bool printHardwareEnforced) {
+    for (const auto& a : characteristics) {
+        if (isHardwareEnforced(a) == printHardwareEnforced) {
+            std::cout << toString(a.keyParameter.tag) << "\n";
+        }
     }
 }
 
-void PrintKeyCharacteristics(const AuthorizationSet& hardware_enforced_characteristics,
-                             const AuthorizationSet& software_enforced_characteristics) {
+void PrintKeyCharacteristics(const std::vector<ks2::Authorization>& characteristics) {
     printf("Hardware:\n");
-    PrintTags(hardware_enforced_characteristics);
+    PrintTags(characteristics, true /* printHardwareEnforced */);
     printf("Software:\n");
-    PrintTags(software_enforced_characteristics);
+    PrintTags(characteristics, false /* printHardwareEnforced */);
 }
 
-bool TestKey(const std::string& name, bool required, const AuthorizationSet& parameters) {
-    std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
-    AuthorizationSet hardware_enforced_characteristics;
-    AuthorizationSet software_enforced_characteristics;
-    auto result =
-        keystore->generateKey("tmp", parameters, 0 /*flags*/, &hardware_enforced_characteristics,
-                              &software_enforced_characteristics);
+const char kEncryptSuffix[] = "_ENC";
+const char kAuthenticateSuffix[] = "_AUTH";
+constexpr uint32_t kAESKeySize = 256;      // bits
+constexpr uint32_t kHMACKeySize = 256;     // bits
+constexpr uint32_t kHMACOutputSize = 256;  // bits
+
+bool verifyEncryptionKeyAttributes(const std::vector<ks2::Authorization> authorizations) {
+    bool verified = true;
+    verified =
+        verified &&
+        std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+            return a.keyParameter.tag == keymint::Tag::ALGORITHM &&
+                   a.keyParameter.value ==
+                       keymint::KeyParameterValue::make<keymint::KeyParameterValue::algorithm>(
+                           keymint::Algorithm::AES);
+        });
+
+    verified =
+        verified &&
+        std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+            return a.keyParameter.tag == keymint::Tag::KEY_SIZE &&
+                   a.keyParameter.value ==
+                       keymint::KeyParameterValue::make<keymint::KeyParameterValue::integer>(
+                           kAESKeySize);
+        });
+
+    verified =
+        verified &&
+        std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+            return a.keyParameter.tag == keymint::Tag::BLOCK_MODE &&
+                   a.keyParameter.value ==
+                       keymint::KeyParameterValue::make<keymint::KeyParameterValue::blockMode>(
+                           keymint::BlockMode::CBC);
+        });
+
+    verified =
+        verified &&
+        std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+            return a.keyParameter.tag == keymint::Tag::PADDING &&
+                   a.keyParameter.value ==
+                       keymint::KeyParameterValue::make<keymint::KeyParameterValue::paddingMode>(
+                           keymint::PaddingMode::PKCS7);
+        });
+
+    return verified;
+}
+
+bool verifyAuthenticationKeyAttributes(const std::vector<ks2::Authorization> authorizations) {
+    bool verified = true;
+    verified =
+        verified &&
+        std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+            return a.keyParameter.tag == keymint::Tag::ALGORITHM &&
+                   a.keyParameter.value ==
+                       keymint::KeyParameterValue::make<keymint::KeyParameterValue::algorithm>(
+                           keymint::Algorithm::HMAC);
+        });
+
+    verified =
+        verified &&
+        std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+            return a.keyParameter.tag == keymint::Tag::KEY_SIZE &&
+                   a.keyParameter.value ==
+                       keymint::KeyParameterValue::make<keymint::KeyParameterValue::integer>(
+                           kHMACKeySize);
+        });
+
+    verified =
+        verified &&
+        std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+            return a.keyParameter.tag == keymint::Tag::MIN_MAC_LENGTH &&
+                   a.keyParameter.value ==
+                       keymint::KeyParameterValue::make<keymint::KeyParameterValue::integer>(
+                           kHMACOutputSize);
+        });
+
+    verified =
+        verified &&
+        std::any_of(authorizations.begin(), authorizations.end(), [&](const ks2::Authorization& a) {
+            return a.keyParameter.tag == keymint::Tag::DIGEST &&
+                   a.keyParameter.value ==
+                       keymint::KeyParameterValue::make<keymint::KeyParameterValue::digest>(
+                           keymint::Digest::SHA_2_256);
+        });
+    return verified;
+}
+
+std::variant<int, ks2::KeyEntryResponse>
+loadOrCreateAndVerifyEncryptionKey(const std::string& name, keymint::SecurityLevel securityLevel,
+                                   bool create) {
+    auto keystore = CreateKeystoreInstance();
+
+    ks2::KeyEntryResponse keyEntryResponse;
+
+    bool foundKey = true;
+    auto rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+    if (!rc.isOk()) {
+        auto error = unwrapError(rc);
+        if (ks2::ResponseCode(error) == ks2::ResponseCode::KEY_NOT_FOUND && create) {
+            foundKey = false;
+        } else {
+            std::cerr << "Failed to get key entry: " << rc.getDescription() << std::endl;
+            return error;
+        }
+    }
+
+    if (!foundKey) {
+        auto sec_level = GetSecurityLevelInterface(keystore, securityLevel);
+        auto params = keymint::AuthorizationSetBuilder()
+                          .AesEncryptionKey(kAESKeySize)
+                          .Padding(keymint::PaddingMode::PKCS7)
+                          .Authorization(keymint::TAG_BLOCK_MODE, keymint::BlockMode::CBC)
+                          .Authorization(keymint::TAG_NO_AUTH_REQUIRED);
+
+        ks2::KeyMetadata keyMetadata;
+
+        rc = sec_level->generateKey(keyDescriptor(name), {} /* attestationKey */,
+                                    params.vector_data(), 0 /* flags */, {} /* entropy */,
+                                    &keyMetadata);
+        if (!rc.isOk()) {
+            std::cerr << "Failed to generate key: " << rc.getDescription() << std::endl;
+            return unwrapError(rc);
+        }
+
+        rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+        if (!rc.isOk()) {
+            std::cerr << "Failed to get key entry (second try): " << rc.getDescription()
+                      << std::endl;
+            return unwrapError(rc);
+        }
+    }
+
+    if (!verifyEncryptionKeyAttributes(keyEntryResponse.metadata.authorizations)) {
+        std::cerr << "Key has wrong set of parameters." << std::endl;
+        return static_cast<int>(ks2::ResponseCode::INVALID_ARGUMENT);
+    }
+
+    return keyEntryResponse;
+}
+
+std::variant<int, ks2::KeyEntryResponse>
+loadOrCreateAndVerifyAuthenticationKey(const std::string& name,
+                                       keymint::SecurityLevel securityLevel, bool create) {
+    auto keystore = CreateKeystoreInstance();
+
+    ks2::KeyEntryResponse keyEntryResponse;
+
+    bool foundKey = true;
+    auto rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+    if (!rc.isOk()) {
+        auto error = unwrapError(rc);
+        if (ks2::ResponseCode(error) == ks2::ResponseCode::KEY_NOT_FOUND && create) {
+            foundKey = false;
+        } else {
+            std::cerr << "Failed to get HMAC key entry: " << rc.getDescription() << std::endl;
+            return error;
+        }
+    }
+
+    if (!foundKey) {
+        auto sec_level = GetSecurityLevelInterface(keystore, securityLevel);
+        auto params = keymint::AuthorizationSetBuilder()
+                          .HmacKey(kHMACKeySize)
+                          .Digest(keymint::Digest::SHA_2_256)
+                          .Authorization(keymint::TAG_MIN_MAC_LENGTH, kHMACOutputSize)
+                          .Authorization(keymint::TAG_NO_AUTH_REQUIRED);
+
+        ks2::KeyMetadata keyMetadata;
+
+        rc = sec_level->generateKey(keyDescriptor(name), {} /* attestationKey */,
+                                    params.vector_data(), 0 /* flags */, {} /* entropy */,
+                                    &keyMetadata);
+        if (!rc.isOk()) {
+            std::cerr << "Failed to generate HMAC key: " << rc.getDescription() << std::endl;
+            return unwrapError(rc);
+        }
+
+        rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+        if (!rc.isOk()) {
+            std::cerr << "Failed to get HMAC key entry (second try): " << rc.getDescription()
+                      << std::endl;
+            return unwrapError(rc);
+        }
+    }
+
+    if (!verifyAuthenticationKeyAttributes(keyEntryResponse.metadata.authorizations)) {
+        std::cerr << "Key has wrong set of parameters." << std::endl;
+        return static_cast<int>(ks2::ResponseCode::INVALID_ARGUMENT);
+    }
+
+    return keyEntryResponse;
+}
+
+std::variant<int, std::vector<uint8_t>>
+encryptWithAuthentication(const std::string& name, const std::vector<uint8_t>& data,
+                          keymint::SecurityLevel securityLevel) {
+    // The encryption algorithm is AES-256-CBC with PKCS #7 padding and a random
+    // IV. The authentication algorithm is HMAC-SHA256 and is computed over the
+    // cipher-text (i.e. Encrypt-then-MAC approach). This was chosen over AES-GCM
+    // because hardware support for GCM is not mandatory for all Brillo devices.
+    std::string encryption_key_name = name + kEncryptSuffix;
+    auto encryption_key_result =
+        loadOrCreateAndVerifyEncryptionKey(encryption_key_name, securityLevel, true /* create */);
+    if (auto error = std::get_if<int>(&encryption_key_result)) {
+        return *error;
+    }
+    auto encryption_key = std::get<ks2::KeyEntryResponse>(encryption_key_result);
+
+    std::string authentication_key_name = name + kAuthenticateSuffix;
+    auto authentication_key_result = loadOrCreateAndVerifyAuthenticationKey(
+        authentication_key_name, securityLevel, true /* create */);
+    if (auto error = std::get_if<int>(&authentication_key_result)) {
+        return *error;
+    }
+    auto authentication_key = std::get<ks2::KeyEntryResponse>(authentication_key_result);
+
+    ks2::CreateOperationResponse encOperationResponse;
+    auto encrypt_params = keymint::AuthorizationSetBuilder()
+                              .Authorization(keymint::TAG_PURPOSE, keymint::KeyPurpose::ENCRYPT)
+                              .Padding(keymint::PaddingMode::PKCS7)
+                              .Authorization(keymint::TAG_BLOCK_MODE, keymint::BlockMode::CBC);
+
+    auto rc = encryption_key.iSecurityLevel->createOperation(
+        encryption_key.metadata.key, encrypt_params.vector_data(), false /* forced */,
+        &encOperationResponse);
+    if (!rc.isOk()) {
+        std::cerr << "Failed to begin encryption operation: " << rc.getDescription() << std::endl;
+        return unwrapError(rc);
+    }
+
+    std::optional<std::vector<uint8_t>> optCiphertext;
+
+    rc = encOperationResponse.iOperation->finish(data, {}, &optCiphertext);
+    if (!rc.isOk()) {
+        std::cerr << "Failed to finish encryption operation: " << rc.getDescription() << std::endl;
+        return unwrapError(rc);
+    }
+
+    std::vector<uint8_t> initVector;
+    if (auto params = encOperationResponse.parameters) {
+        for (auto& p : params->keyParameter) {
+            if (auto iv = keymint::authorizationValue(keymint::TAG_NONCE, p)) {
+                initVector = std::move(iv->get());
+                break;
+            }
+        }
+        if (initVector.empty()) {
+            std::cerr << "Encryption operation did not return an IV." << std::endl;
+            return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+        }
+    }
+
+    if (!optCiphertext) {
+        std::cerr << "Encryption succeeded but no ciphertext returned." << std::endl;
+        return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+    }
+
+    auto ciphertext = std::move(*optCiphertext);
+    auto toBeSigned = initVector;
+    toBeSigned.insert(toBeSigned.end(), ciphertext.begin(), ciphertext.end());
+
+    ks2::CreateOperationResponse signOperationResponse;
+    auto sign_params = keymint::AuthorizationSetBuilder()
+                           .Authorization(keymint::TAG_PURPOSE, keymint::KeyPurpose::SIGN)
+                           .Digest(keymint::Digest::SHA_2_256)
+                           .Authorization(keymint::TAG_MAC_LENGTH, kHMACOutputSize);
+
+    rc = authentication_key.iSecurityLevel->createOperation(
+        authentication_key.metadata.key, sign_params.vector_data(), false /* forced */,
+        &signOperationResponse);
+    if (!rc.isOk()) {
+        std::cerr << "Failed to begin signing operation: " << rc.getDescription() << std::endl;
+        return unwrapError(rc);
+    }
+
+    std::optional<std::vector<uint8_t>> optMac;
+
+    rc = signOperationResponse.iOperation->finish(toBeSigned, {}, &optMac);
+    if (!rc.isOk()) {
+        std::cerr << "Failed to finish encryption operation: " << rc.getDescription() << std::endl;
+        return unwrapError(rc);
+    }
+
+    if (!optMac) {
+        std::cerr << "Signing succeeded but no MAC returned." << std::endl;
+        return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+    }
+
+    auto mac = std::move(*optMac);
+
+    EncryptedData protobuf;
+    protobuf.set_init_vector(initVector.data(), initVector.size());
+    protobuf.set_authentication_data(mac.data(), mac.size());
+    protobuf.set_encrypted_data(ciphertext.data(), ciphertext.size());
+    std::string resultString;
+    if (!protobuf.SerializeToString(&resultString)) {
+        std::cerr << "Encrypt: Failed to serialize EncryptedData protobuf.";
+        return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+    }
+
+    std::vector<uint8_t> result(reinterpret_cast<const uint8_t*>(resultString.data()),
+                                reinterpret_cast<const uint8_t*>(resultString.data()) +
+                                    resultString.size());
+    return result;
+}
+
+std::variant<int, std::vector<uint8_t>>
+decryptWithAuthentication(const std::string& name, const std::vector<uint8_t>& data) {
+
+    // Decode encrypted data
+    EncryptedData protobuf;
+    if (!protobuf.ParseFromArray(data.data(), data.size())) {
+        std::cerr << "Decrypt: Failed to parse EncryptedData protobuf." << std::endl;
+        return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+    }
+
+    // Load encryption and authentication keys.
+    std::string encryption_key_name = name + kEncryptSuffix;
+    auto encryption_key_result = loadOrCreateAndVerifyEncryptionKey(
+        encryption_key_name, keymint::SecurityLevel::KEYSTORE /* ignored */, false /* create */);
+    if (auto error = std::get_if<int>(&encryption_key_result)) {
+        return *error;
+    }
+    auto encryption_key = std::get<ks2::KeyEntryResponse>(encryption_key_result);
+
+    std::string authentication_key_name = name + kAuthenticateSuffix;
+    auto authentication_key_result = loadOrCreateAndVerifyAuthenticationKey(
+        authentication_key_name, keymint::SecurityLevel::KEYSTORE /* ignored */,
+        false /* create */);
+    if (auto error = std::get_if<int>(&authentication_key_result)) {
+        return *error;
+    }
+    auto authentication_key = std::get<ks2::KeyEntryResponse>(authentication_key_result);
+
+    // Begin authentication operation
+    ks2::CreateOperationResponse signOperationResponse;
+    auto sign_params = keymint::AuthorizationSetBuilder()
+                           .Authorization(keymint::TAG_PURPOSE, keymint::KeyPurpose::VERIFY)
+                           .Digest(keymint::Digest::SHA_2_256)
+                           .Authorization(keymint::TAG_MAC_LENGTH, kHMACOutputSize);
+
+    auto rc = authentication_key.iSecurityLevel->createOperation(
+        authentication_key.metadata.key, sign_params.vector_data(), false /* forced */,
+        &signOperationResponse);
+    if (!rc.isOk()) {
+        std::cerr << "Failed to begin verify operation: " << rc.getDescription() << std::endl;
+        return unwrapError(rc);
+    }
+
+    const uint8_t* p = reinterpret_cast<const uint8_t*>(protobuf.init_vector().data());
+    std::vector<uint8_t> toBeVerified(p, p + protobuf.init_vector().size());
+
+    p = reinterpret_cast<const uint8_t*>(protobuf.encrypted_data().data());
+    toBeVerified.insert(toBeVerified.end(), p, p + protobuf.encrypted_data().size());
+
+    p = reinterpret_cast<const uint8_t*>(protobuf.authentication_data().data());
+    std::vector<uint8_t> signature(p, p + protobuf.authentication_data().size());
+
+    std::optional<std::vector<uint8_t>> optOut;
+    rc = signOperationResponse.iOperation->finish(toBeVerified, signature, &optOut);
+    if (!rc.isOk()) {
+        std::cerr << "Decrypt: HMAC verification failed: " << rc.getDescription() << std::endl;
+        return unwrapError(rc);
+    }
+
+    // Begin decryption operation
+    ks2::CreateOperationResponse encOperationResponse;
+    auto encrypt_params = keymint::AuthorizationSetBuilder()
+                              .Authorization(keymint::TAG_PURPOSE, keymint::KeyPurpose::DECRYPT)
+                              .Authorization(keymint::TAG_NONCE, protobuf.init_vector().data(),
+                                             protobuf.init_vector().size())
+                              .Padding(keymint::PaddingMode::PKCS7)
+                              .Authorization(keymint::TAG_BLOCK_MODE, keymint::BlockMode::CBC);
+
+    rc = encryption_key.iSecurityLevel->createOperation(encryption_key.metadata.key,
+                                                        encrypt_params.vector_data(),
+                                                        false /* forced */, &encOperationResponse);
+    if (!rc.isOk()) {
+        std::cerr << "Failed to begin encryption operation: " << rc.getDescription() << std::endl;
+        return unwrapError(rc);
+    }
+
+    std::optional<std::vector<uint8_t>> optPlaintext;
+
+    p = reinterpret_cast<const uint8_t*>(protobuf.encrypted_data().data());
+    std::vector<uint8_t> cyphertext(p, p + protobuf.encrypted_data().size());
+
+    rc = encOperationResponse.iOperation->finish(cyphertext, {}, &optPlaintext);
+    if (!rc.isOk()) {
+        std::cerr << "Failed to finish encryption operation: " << rc.getDescription() << std::endl;
+        return unwrapError(rc);
+    }
+
+    if (!optPlaintext) {
+        std::cerr << "Decryption succeeded but no plaintext returned." << std::endl;
+        return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+    }
+
+    return *optPlaintext;
+}
+
+bool TestKey(const std::string& name, bool required,
+             const std::vector<keymint::KeyParameter>& parameters) {
+    auto keystore = CreateKeystoreInstance();
+    auto sec_level =
+        GetSecurityLevelInterface(keystore, keymint::SecurityLevel::TRUSTED_ENVIRONMENT);
+
+    ks2::KeyDescriptor keyDescriptor = {
+        .domain = ks2::Domain::APP,
+        .nspace = -1,
+        .alias = "tmp",
+        .blob = {},
+    };
+
+    ks2::KeyMetadata keyMetadata;
+
+    auto rc = sec_level->generateKey(keyDescriptor, {} /* attestationKey */, parameters,
+                                     0 /* flags */, {} /* entropy */, &keyMetadata);
     const char kBoldRedAbort[] = "\033[1;31mABORT\033[0m";
-    if (!result.isOk()) {
-        LOG(ERROR) << "Failed to generate key: " << result;
+    if (!rc.isOk()) {
+        LOG(ERROR) << "Failed to generate key: " << rc.getDescription();
         printf("[%s] %s\n", kBoldRedAbort, name.c_str());
         return false;
     }
-    result = keystore->deleteKey("tmp");
-    if (!result.isOk()) {
-        LOG(ERROR) << "Failed to delete key: " << result;
+
+    rc = keystore->deleteKey(keyDescriptor);
+    if (!rc.isOk()) {
+        LOG(ERROR) << "Failed to delete key: " << rc.getDescription();
         printf("[%s] %s\n", kBoldRedAbort, name.c_str());
         return false;
     }
     printf("===============================================================\n");
     printf("%s Key Characteristics:\n", name.c_str());
-    PrintKeyCharacteristics(hardware_enforced_characteristics, software_enforced_characteristics);
-    bool hardware_backed = (hardware_enforced_characteristics.size() > 0);
-    if (software_enforced_characteristics.GetTagCount(TAG_ALGORITHM) > 0 ||
-        software_enforced_characteristics.GetTagCount(TAG_KEY_SIZE) > 0 ||
-        software_enforced_characteristics.GetTagCount(TAG_RSA_PUBLIC_EXPONENT) > 0) {
+    PrintKeyCharacteristics(keyMetadata.authorizations);
+    bool hardware_backed = std::any_of(keyMetadata.authorizations.begin(),
+                                       keyMetadata.authorizations.end(), isHardwareEnforced);
+    if (std::any_of(keyMetadata.authorizations.begin(), keyMetadata.authorizations.end(),
+                    [&](const auto& a) {
+                        return !isHardwareEnforced(a) &&
+                               (a.keyParameter.tag == keymint::Tag::ALGORITHM ||
+                                a.keyParameter.tag == keymint::Tag::KEY_SIZE ||
+                                a.keyParameter.tag == keymint::Tag::RSA_PUBLIC_EXPONENT);
+                    })) {
         VLOG(1) << "Hardware-backed key but required characteristics enforced in software.";
         hardware_backed = false;
     }
@@ -137,60 +592,64 @@
     return (hardware_backed || !required);
 }
 
-AuthorizationSet GetRSASignParameters(uint32_t key_size, bool sha256_only) {
-    AuthorizationSetBuilder parameters;
+keymint::AuthorizationSet GetRSASignParameters(uint32_t key_size, bool sha256_only) {
+    keymint::AuthorizationSetBuilder parameters;
     parameters.RsaSigningKey(key_size, 65537)
-        .Digest(Digest::SHA_2_256)
-        .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
-        .Padding(PaddingMode::RSA_PSS)
-        .Authorization(TAG_NO_AUTH_REQUIRED);
+        .Digest(keymint::Digest::SHA_2_256)
+        .Padding(keymint::PaddingMode::RSA_PKCS1_1_5_SIGN)
+        .Padding(keymint::PaddingMode::RSA_PSS)
+        .Authorization(keymint::TAG_NO_AUTH_REQUIRED);
     if (!sha256_only) {
-        parameters.Digest(Digest::SHA_2_224).Digest(Digest::SHA_2_384).Digest(Digest::SHA_2_512);
+        parameters.Digest(keymint::Digest::SHA_2_224)
+            .Digest(keymint::Digest::SHA_2_384)
+            .Digest(keymint::Digest::SHA_2_512);
     }
     return std::move(parameters);
 }
 
-AuthorizationSet GetRSAEncryptParameters(uint32_t key_size) {
-    AuthorizationSetBuilder parameters;
+keymint::AuthorizationSet GetRSAEncryptParameters(uint32_t key_size) {
+    keymint::AuthorizationSetBuilder parameters;
     parameters.RsaEncryptionKey(key_size, 65537)
-        .Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT)
-        .Padding(PaddingMode::RSA_OAEP)
-        .Authorization(TAG_NO_AUTH_REQUIRED);
+        .Padding(keymint::PaddingMode::RSA_PKCS1_1_5_ENCRYPT)
+        .Padding(keymint::PaddingMode::RSA_OAEP)
+        .Authorization(keymint::TAG_NO_AUTH_REQUIRED);
     return std::move(parameters);
 }
 
-AuthorizationSet GetECDSAParameters(uint32_t key_size, bool sha256_only) {
-    AuthorizationSetBuilder parameters;
+keymint::AuthorizationSet GetECDSAParameters(uint32_t key_size, bool sha256_only) {
+    keymint::AuthorizationSetBuilder parameters;
     parameters.EcdsaSigningKey(key_size)
-        .Digest(Digest::SHA_2_256)
-        .Authorization(TAG_NO_AUTH_REQUIRED);
+        .Digest(keymint::Digest::SHA_2_256)
+        .Authorization(keymint::TAG_NO_AUTH_REQUIRED);
     if (!sha256_only) {
-        parameters.Digest(Digest::SHA_2_224).Digest(Digest::SHA_2_384).Digest(Digest::SHA_2_512);
+        parameters.Digest(keymint::Digest::SHA_2_224)
+            .Digest(keymint::Digest::SHA_2_384)
+            .Digest(keymint::Digest::SHA_2_512);
     }
     return std::move(parameters);
 }
 
-AuthorizationSet GetAESParameters(uint32_t key_size, bool with_gcm_mode) {
-    AuthorizationSetBuilder parameters;
-    parameters.AesEncryptionKey(key_size).Authorization(TAG_NO_AUTH_REQUIRED);
+keymint::AuthorizationSet GetAESParameters(uint32_t key_size, bool with_gcm_mode) {
+    keymint::AuthorizationSetBuilder parameters;
+    parameters.AesEncryptionKey(key_size).Authorization(keymint::TAG_NO_AUTH_REQUIRED);
     if (with_gcm_mode) {
-        parameters.Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
-            .Authorization(TAG_MIN_MAC_LENGTH, 128);
+        parameters.Authorization(keymint::TAG_BLOCK_MODE, keymint::BlockMode::GCM)
+            .Authorization(keymint::TAG_MIN_MAC_LENGTH, 128);
     } else {
-        parameters.Authorization(TAG_BLOCK_MODE, BlockMode::ECB);
-        parameters.Authorization(TAG_BLOCK_MODE, BlockMode::CBC);
-        parameters.Authorization(TAG_BLOCK_MODE, BlockMode::CTR);
-        parameters.Padding(PaddingMode::NONE);
+        parameters.Authorization(keymint::TAG_BLOCK_MODE, keymint::BlockMode::ECB);
+        parameters.Authorization(keymint::TAG_BLOCK_MODE, keymint::BlockMode::CBC);
+        parameters.Authorization(keymint::TAG_BLOCK_MODE, keymint::BlockMode::CTR);
+        parameters.Padding(keymint::PaddingMode::NONE);
     }
     return std::move(parameters);
 }
 
-AuthorizationSet GetHMACParameters(uint32_t key_size, Digest digest) {
-    AuthorizationSetBuilder parameters;
+keymint::AuthorizationSet GetHMACParameters(uint32_t key_size, keymint::Digest digest) {
+    keymint::AuthorizationSetBuilder parameters;
     parameters.HmacKey(key_size)
         .Digest(digest)
-        .Authorization(TAG_MIN_MAC_LENGTH, 224)
-        .Authorization(TAG_NO_AUTH_REQUIRED);
+        .Authorization(keymint::TAG_MIN_MAC_LENGTH, 224)
+        .Authorization(keymint::TAG_NO_AUTH_REQUIRED);
     return std::move(parameters);
 }
 
@@ -212,12 +671,12 @@
         {"AES-256", true, GetAESParameters(256, false)},
         {"AES-128-GCM", false, GetAESParameters(128, true)},
         {"AES-256-GCM", false, GetAESParameters(256, true)},
-        {"HMAC-SHA256-16", true, GetHMACParameters(16, Digest::SHA_2_256)},
-        {"HMAC-SHA256-32", true, GetHMACParameters(32, Digest::SHA_2_256)},
-        {"HMAC-SHA256-64", false, GetHMACParameters(64, Digest::SHA_2_256)},
-        {"HMAC-SHA224-32", false, GetHMACParameters(32, Digest::SHA_2_224)},
-        {"HMAC-SHA384-32", false, GetHMACParameters(32, Digest::SHA_2_384)},
-        {"HMAC-SHA512-32", false, GetHMACParameters(32, Digest::SHA_2_512)},
+        {"HMAC-SHA256-16", true, GetHMACParameters(16, keymint::Digest::SHA_2_256)},
+        {"HMAC-SHA256-32", true, GetHMACParameters(32, keymint::Digest::SHA_2_256)},
+        {"HMAC-SHA256-64", false, GetHMACParameters(64, keymint::Digest::SHA_2_256)},
+        {"HMAC-SHA224-32", false, GetHMACParameters(32, keymint::Digest::SHA_2_224)},
+        {"HMAC-SHA384-32", false, GetHMACParameters(32, keymint::Digest::SHA_2_384)},
+        {"HMAC-SHA512-32", false, GetHMACParameters(32, keymint::Digest::SHA_2_512)},
     };
     return std::vector<TestCase>(&test_cases[0], &test_cases[arraysize(test_cases)]);
 }
@@ -243,7 +702,8 @@
             continue;
         }
         ++test_count;
-        if (!TestKey(test_case.name, test_case.required_for_brillo_pts, test_case.parameters)) {
+        if (!TestKey(test_case.name, test_case.required_for_brillo_pts,
+                     test_case.parameters.vector_data())) {
             VLOG(1) << "Test failed: " << test_case.name;
             ++fail_count;
         }
@@ -262,248 +722,274 @@
     return 0;
 }
 
-std::string ReadFile(const std::string& filename) {
+std::vector<uint8_t> ReadFile(const std::string& filename) {
     std::string content;
     base::FilePath path(filename);
     if (!base::ReadFileToString(path, &content)) {
         printf("Failed to read file: %s\n", filename.c_str());
         exit(1);
     }
-    return content;
+    std::vector<uint8_t> buffer(reinterpret_cast<const uint8_t*>(content.data()),
+                                reinterpret_cast<const uint8_t*>(content.data()) + content.size());
+    return buffer;
 }
 
-void WriteFile(const std::string& filename, const std::string& content) {
+void WriteFile(const std::string& filename, const std::vector<uint8_t>& content) {
     base::FilePath path(filename);
     int size = content.size();
-    if (base::WriteFile(path, content.data(), size) != size) {
+    if (base::WriteFile(path, reinterpret_cast<const char*>(content.data()), size) != size) {
         printf("Failed to write file: %s\n", filename.c_str());
         exit(1);
     }
 }
 
-int AddEntropy(const std::string& input, int32_t flags) {
-    std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
-    int32_t result = keystore->addRandomNumberGeneratorEntropy(input, flags).getErrorCode();
-    printf("AddEntropy: %d\n", result);
-    return result;
-}
-
 // Note: auth_bound keys created with this tool will not be usable.
-int GenerateKey(const std::string& name, int32_t flags, bool auth_bound) {
-    std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
-    AuthorizationSetBuilder params;
+int GenerateKey(const std::string& name, keymint::SecurityLevel securityLevel, bool auth_bound) {
+    auto keystore = CreateKeystoreInstance();
+    auto sec_level = GetSecurityLevelInterface(keystore, securityLevel);
+    keymint::AuthorizationSetBuilder params;
     params.RsaSigningKey(2048, 65537)
-        .Digest(Digest::SHA_2_224)
-        .Digest(Digest::SHA_2_256)
-        .Digest(Digest::SHA_2_384)
-        .Digest(Digest::SHA_2_512)
-        .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
-        .Padding(PaddingMode::RSA_PSS);
+        .Digest(keymint::Digest::SHA_2_224)
+        .Digest(keymint::Digest::SHA_2_256)
+        .Digest(keymint::Digest::SHA_2_384)
+        .Digest(keymint::Digest::SHA_2_512)
+        .Padding(keymint::PaddingMode::RSA_PKCS1_1_5_SIGN)
+        .Padding(keymint::PaddingMode::RSA_PSS);
     if (auth_bound) {
         // Gatekeeper normally generates the secure user id.
         // Using zero allows the key to be created, but it will not be usuable.
-        params.Authorization(TAG_USER_SECURE_ID, 0);
+        params.Authorization(keymint::TAG_USER_SECURE_ID, 0);
     } else {
-        params.Authorization(TAG_NO_AUTH_REQUIRED);
+        params.Authorization(keymint::TAG_NO_AUTH_REQUIRED);
     }
-    AuthorizationSet hardware_enforced_characteristics;
-    AuthorizationSet software_enforced_characteristics;
-    auto result = keystore->generateKey(name, params, flags, &hardware_enforced_characteristics,
-                                        &software_enforced_characteristics);
-    printf("GenerateKey: %d\n", result.getErrorCode());
-    if (result.isOk()) {
-        PrintKeyCharacteristics(hardware_enforced_characteristics,
-                                software_enforced_characteristics);
+
+    ks2::KeyMetadata keyMetadata;
+
+    auto rc =
+        sec_level->generateKey(keyDescriptor(name), {} /* attestationKey */, params.vector_data(),
+                               0 /* flags */, {} /* entropy */, &keyMetadata);
+
+    if (rc.isOk()) {
+        std::cerr << "GenerateKey failed: " << rc.getDescription() << std::endl;
+        return unwrapError(rc);
     }
-    return result.getErrorCode();
+    std::cout << "GenerateKey: success" << std::endl;
+    PrintKeyCharacteristics(keyMetadata.authorizations);
+    return 0;
 }
 
 int GetCharacteristics(const std::string& name) {
-    std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
-    AuthorizationSet hardware_enforced_characteristics;
-    AuthorizationSet software_enforced_characteristics;
-    auto result = keystore->getKeyCharacteristics(name, &hardware_enforced_characteristics,
-                                                  &software_enforced_characteristics);
-    printf("GetCharacteristics: %d\n", result.getErrorCode());
-    if (result.isOk()) {
-        PrintKeyCharacteristics(hardware_enforced_characteristics,
-                                software_enforced_characteristics);
+    auto keystore = CreateKeystoreInstance();
+
+    ks2::KeyEntryResponse keyEntryResponse;
+
+    auto rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+    if (!rc.isOk()) {
+        std::cerr << "Failed to get key entry: " << rc.getDescription() << std::endl;
+        return unwrapError(rc);
     }
-    return result.getErrorCode();
+
+    std::cout << "GetCharacteristics: success" << std::endl;
+    PrintKeyCharacteristics(keyEntryResponse.metadata.authorizations);
+    return 0;
 }
 
 int ExportKey(const std::string& name) {
-    std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
-    std::string data;
-    int32_t result = keystore->exportKey(KeyFormat::X509, name, &data).getErrorCode();
-    printf("ExportKey: %d (%zu)\n", result, data.size());
-    return result;
+    auto keystore = CreateKeystoreInstance();
+
+    ks2::KeyEntryResponse keyEntryResponse;
+
+    auto rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+    if (!rc.isOk()) {
+        std::cerr << "Failed to get key entry: " << rc.getDescription() << std::endl;
+        return unwrapError(rc);
+    }
+
+    if (auto cert = keyEntryResponse.metadata.certificate) {
+        std::cout << "ExportKey: Got certificate of length (" << cert->size() << ")" << std::endl;
+    } else {
+        std::cout << "ExportKey: Key entry does not have a public component.\n";
+        std::cout << "Possibly a symmetric key?" << std::endl;
+    }
+    return 0;
 }
 
 int DeleteKey(const std::string& name) {
-    std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
-    int32_t result = keystore->deleteKey(name).getErrorCode();
-    printf("DeleteKey: %d\n", result);
-    return result;
-}
+    auto keystore = CreateKeystoreInstance();
 
-int DeleteAllKeys() {
-    std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
-    int32_t result = keystore->deleteAllKeys().getErrorCode();
-    printf("DeleteAllKeys: %d\n", result);
-    return result;
+    auto rc = keystore->deleteKey(keyDescriptor(name));
+    if (!rc.isOk()) {
+        std::cerr << "Failed to delete key: " << rc.getDescription();
+        return unwrapError(rc);
+    }
+    std::cout << "Successfully deleted key." << std::endl;
+    return 0;
 }
 
 int DoesKeyExist(const std::string& name) {
-    std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
-    printf("DoesKeyExist: %s\n", keystore->doesKeyExist(name) ? "yes" : "no");
+    auto keystore = CreateKeystoreInstance();
+    ks2::KeyEntryResponse keyEntryResponse;
+
+    bool keyExists = true;
+    auto rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+    if (!rc.isOk()) {
+        auto responseCode = unwrapError(rc);
+        if (ks2::ResponseCode(responseCode) == ks2::ResponseCode::KEY_NOT_FOUND) {
+            keyExists = false;
+        } else {
+            std::cerr << "Failed to get key entry: " << rc.getDescription() << std::endl;
+            return unwrapError(rc);
+        }
+    }
+    std::cout << "DoesKeyExists: " << (keyExists ? "yes" : "no") << std::endl;
     return 0;
 }
 
-int List(const std::string& prefix) {
-    std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
-    std::vector<std::string> key_list;
-    if (!keystore->listKeys(prefix, &key_list)) {
-        printf("ListKeys failed.\n");
-        return 1;
+int List() {
+    auto keystore = CreateKeystoreInstance();
+    std::vector<ks2::KeyDescriptor> key_list;
+    auto rc = keystore->listEntries(ks2::Domain::APP, -1 /* nspace ignored */, &key_list);
+    if (!rc.isOk()) {
+        std::cerr << "ListKeys failed: " << rc.getDescription() << std::endl;
+        return unwrapError(rc);
     }
-    printf("Keys:\n");
-    for (const auto& key_name : key_list) {
-        printf("  %s\n", key_name.c_str());
-    }
-    return 0;
-}
-
-int ListAppsWithKeys() {
-
-    sp<android::IServiceManager> sm = android::defaultServiceManager();
-    sp<android::IBinder> binder = sm->getService(String16("android.security.keystore"));
-    sp<IKeystoreService> service = android::interface_cast<IKeystoreService>(binder);
-    if (service == nullptr) {
-        fprintf(stderr, "Error connecting to keystore service.\n");
-        return 1;
-    }
-    int32_t aidl_return;
-    ::std::vector<::std::string> uids;
-    android::binder::Status status = service->listUidsOfAuthBoundKeys(&uids, &aidl_return);
-    if (!status.isOk()) {
-        fprintf(stderr, "Requesting uids of auth bound keys failed with error %s.\n",
-                status.toString8().c_str());
-        return 1;
-    }
-    if (!KeyStoreNativeReturnCode(aidl_return).isOk()) {
-        fprintf(stderr, "Requesting uids of auth bound keys failed with code %d.\n", aidl_return);
-        return 1;
-    }
-    printf("Apps with auth bound keys:\n");
-    for (auto i = uids.begin(); i != uids.end(); ++i) {
-        printf("%s\n", i->c_str());
+    std::cout << "Keys:\n";
+    for (const auto& key : key_list) {
+        std::cout << "  "
+                  << (key.alias ? *key.alias : "Whoopsi - no alias, this should not happen.")
+                  << std::endl;
     }
     return 0;
 }
 
 int SignAndVerify(const std::string& name) {
-    std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
-    AuthorizationSetBuilder sign_params;
-    sign_params.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
-    sign_params.Digest(Digest::SHA_2_256);
-    AuthorizationSet output_params;
-    uint64_t handle;
-    auto result =
-        keystore->beginOperation(KeyPurpose::SIGN, name, sign_params, &output_params, &handle);
-    if (!result.isOk()) {
-        printf("Sign: BeginOperation failed: %d\n", result.getErrorCode());
-        return result.getErrorCode();
+    auto keystore = CreateKeystoreInstance();
+    auto sign_params = keymint::AuthorizationSetBuilder()
+                           .Authorization(keymint::TAG_PURPOSE, keymint::KeyPurpose::SIGN)
+                           .Padding(keymint::PaddingMode::RSA_PKCS1_1_5_SIGN)
+                           .Digest(keymint::Digest::SHA_2_256);
+
+    keymint::AuthorizationSet output_params;
+
+    ks2::KeyEntryResponse keyEntryResponse;
+
+    auto rc = keystore->getKeyEntry(keyDescriptor(name), &keyEntryResponse);
+    if (!rc.isOk()) {
+        std::cerr << "Failed to get key entry: " << rc.getDescription() << std::endl;
+        return unwrapError(rc);
     }
-    AuthorizationSet empty_params;
-    std::string output_data;
-    result = keystore->finishOperation(handle, empty_params, "data_to_sign",
-                                       std::string() /*signature_to_verify*/, &output_params,
-                                       &output_data);
-    if (!result.isOk()) {
-        printf("Sign: FinishOperation failed: %d\n", result.getErrorCode());
-        return result.getErrorCode();
+
+    ks2::CreateOperationResponse operationResponse;
+
+    rc = keyEntryResponse.iSecurityLevel->createOperation(keyEntryResponse.metadata.key,
+                                                          sign_params.vector_data(),
+                                                          false /* forced */, &operationResponse);
+    if (!rc.isOk()) {
+        std::cerr << "Failed to create operation: " << rc.getDescription() << std::endl;
+        return unwrapError(rc);
     }
-    printf("Sign: %zu bytes.\n", output_data.size());
-    // We have a signature, now verify it.
-    std::string signature_to_verify = output_data;
-    output_data.clear();
-    result =
-        keystore->beginOperation(KeyPurpose::VERIFY, name, sign_params, &output_params, &handle);
-    result = keystore->finishOperation(handle, empty_params, "data_to_sign", signature_to_verify,
-                                       &output_params, &output_data);
-    if (result == ErrorCode::VERIFICATION_FAILED) {
-        printf("Verify: Failed to verify signature.\n");
-        return result.getErrorCode();
+
+    const std::vector<uint8_t> data_to_sign{0x64, 0x61, 0x74, 0x61, 0x5f, 0x74,
+                                            0x6f, 0x5f, 0x73, 0x69, 0x67, 0x6e};
+    std::optional<std::vector<uint8_t>> output_data;
+    rc = operationResponse.iOperation->finish(data_to_sign, {}, &output_data);
+    if (!rc.isOk()) {
+        std::cerr << "Failed to finalize operation: " << rc.getDescription() << std::endl;
+        return unwrapError(rc);
     }
-    if (!result.isOk()) {
-        printf("Verify: FinishOperation failed: %d\n", result.getErrorCode());
-        return result.getErrorCode();
+
+    if (!output_data) {
+        std::cerr << "Odd signing succeeded but no signature was returned." << std::endl;
+        return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
     }
-    printf("Verify: OK\n");
+    auto signature = std::move(*output_data);
+
+    std::cout << "Sign: " << signature.size() << " bytes." << std::endl;
+
+    if (auto cert = keyEntryResponse.metadata.certificate) {
+        const uint8_t* p = cert->data();
+        bssl::UniquePtr<X509> decoded_cert(d2i_X509(nullptr, &p, (long)cert->size()));
+        bssl::UniquePtr<EVP_PKEY> decoded_pkey(X509_get_pubkey(decoded_cert.get()));
+        bssl::UniquePtr<EVP_MD_CTX> ctx(EVP_MD_CTX_new());
+        if (!ctx) {
+            std::cerr << "Failed to created EVP_MD context. << std::endl";
+            return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+        }
+
+        if (!EVP_DigestVerifyInit(ctx.get(), nullptr, EVP_sha256(), nullptr, decoded_pkey.get()) ||
+            !EVP_DigestVerifyUpdate(ctx.get(), data_to_sign.data(), data_to_sign.size()) ||
+            EVP_DigestVerifyFinal(ctx.get(), signature.data(), signature.size()) != 1) {
+            std::cerr << "Failed to verify signature." << std::endl;
+            return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+        }
+    } else {
+        std::cerr << "No public key to check signature against." << std::endl;
+        return static_cast<int>(ks2::ResponseCode::SYSTEM_ERROR);
+    }
+
+    std::cout << "Verify: OK" << std::endl;
     return 0;
 }
 
 int Encrypt(const std::string& key_name, const std::string& input_filename,
-            const std::string& output_filename, int32_t flags) {
-    std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
-    std::string input = ReadFile(input_filename);
-    std::string output;
-    if (!keystore->encryptWithAuthentication(key_name, input, flags, &output)) {
-        printf("EncryptWithAuthentication failed.\n");
-        return 1;
+            const std::string& output_filename, keymint::SecurityLevel securityLevel) {
+    auto input = ReadFile(input_filename);
+    auto result = encryptWithAuthentication(key_name, input, securityLevel);
+    if (auto error = std::get_if<int>(&result)) {
+        std::cerr << "EncryptWithAuthentication failed." << std::endl;
+        return *error;
     }
-    WriteFile(output_filename, output);
+    WriteFile(output_filename, std::get<std::vector<uint8_t>>(result));
     return 0;
 }
 
 int Decrypt(const std::string& key_name, const std::string& input_filename,
             const std::string& output_filename) {
-    std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
-    std::string input = ReadFile(input_filename);
-    std::string output;
-    if (!keystore->decryptWithAuthentication(key_name, input, &output)) {
-        printf("DecryptWithAuthentication failed.\n");
-        return 1;
+    auto input = ReadFile(input_filename);
+    auto result = decryptWithAuthentication(key_name, input);
+    if (auto error = std::get_if<int>(&result)) {
+        std::cerr << "DecryptWithAuthentication failed." << std::endl;
+        return *error;
     }
-    WriteFile(output_filename, output);
+    WriteFile(output_filename, std::get<std::vector<uint8_t>>(result));
     return 0;
 }
 
-uint32_t securityLevelOption2Flags(const CommandLine& cmd) {
+keymint::SecurityLevel securityLevelOption2SecurlityLevel(const CommandLine& cmd) {
     if (cmd.HasSwitch("seclevel")) {
         auto str = cmd.GetSwitchValueASCII("seclevel");
         if (str == "strongbox") {
-            return KEYSTORE_FLAG_STRONGBOX;
-        } else if (str == "software") {
-            return KEYSTORE_FLAG_FALLBACK;
+            return keymint::SecurityLevel::STRONGBOX;
+        } else if (str == "tee") {
+            return keymint::SecurityLevel::TRUSTED_ENVIRONMENT;
         }
+        std::cerr << "Unknown Security level: " << str << std::endl;
+        std::cerr << "Supported security levels: \"strongbox\" or \"tee\" (default)" << std::endl;
     }
-    return KEYSTORE_FLAG_NONE;
+    return keymint::SecurityLevel::TRUSTED_ENVIRONMENT;
 }
 
 class ConfirmationListener
-    : public android::security::BnConfirmationPromptCallback,
-      public std::promise<std::tuple<ConfirmationResponseCode, std::vector<uint8_t>>> {
+    : public apc::BnConfirmationCallback,
+      public std::promise<std::tuple<apc::ResponseCode, std::optional<std::vector<uint8_t>>>> {
   public:
     ConfirmationListener() {}
 
-    virtual ::android::binder::Status
-    onConfirmationPromptCompleted(int32_t result,
-                                  const ::std::vector<uint8_t>& dataThatWasConfirmed) override {
-        this->set_value({static_cast<ConfirmationResponseCode>(result), dataThatWasConfirmed});
-        return ::android::binder::Status::ok();
-    }
+    virtual ::ndk::ScopedAStatus
+    onCompleted(::aidl::android::security::apc::ResponseCode result,
+                const std::optional<std::vector<uint8_t>>& dataConfirmed) override {
+        this->set_value({result, dataConfirmed});
+        return ::ndk::ScopedAStatus::ok();
+    };
 };
 
 int Confirmation(const std::string& promptText, const std::string& extraDataHex,
                  const std::string& locale, const std::string& uiOptionsStr,
                  const std::string& cancelAfter) {
-    sp<android::IServiceManager> sm = android::defaultServiceManager();
-    sp<android::IBinder> binder = sm->getService(String16("android.security.keystore"));
-    sp<IKeystoreService> service = android::interface_cast<IKeystoreService>(binder);
-    if (service == nullptr) {
-        printf("error: could not connect to keystore service.\n");
+    ::ndk::SpAIBinder apcBinder(AServiceManager_getService("android.security.apc"));
+    auto apcService = apc::IProtectedConfirmation::fromBinder(apcBinder);
+    if (!apcService) {
+        std::cerr << "Error: could not connect to apc service." << std::endl;
         return 1;
     }
 
@@ -537,44 +1023,28 @@
         return 1;
     }
 
-    String16 promptText16(promptText.data(), promptText.size());
-    String16 locale16(locale.data(), locale.size());
-
-    sp<ConfirmationListener> listener = new ConfirmationListener();
+    auto listener = std::make_shared<ConfirmationListener>();
 
     auto future = listener->get_future();
-    int32_t aidl_return;
-    android::binder::Status status = service->presentConfirmationPrompt(
-        listener, promptText16, extraData, locale16, uiOptionsAsFlags, &aidl_return);
-    if (!status.isOk()) {
-        printf("Presenting confirmation prompt failed with binder status '%s'.\n",
-               status.toString8().c_str());
+    auto rc = apcService->presentPrompt(listener, promptText, extraData, locale, uiOptionsAsFlags);
+
+    if (!rc.isOk()) {
+        std::cerr << "Presenting confirmation prompt failed: " << rc.getDescription() << std::endl;
         return 1;
     }
-    ConfirmationResponseCode responseCode = static_cast<ConfirmationResponseCode>(aidl_return);
-    if (responseCode != ConfirmationResponseCode::OK) {
-        printf("Presenting confirmation prompt failed with response code %d.\n", responseCode);
-        return 1;
-    }
-    printf("Waiting for prompt to complete - use Ctrl+C to abort...\n");
+
+    std::cerr << "Waiting for prompt to complete - use Ctrl+C to abort..." << std::endl;
 
     if (cancelAfterValue > 0.0) {
-        printf("Sleeping %.1f seconds before canceling prompt...\n", cancelAfterValue);
+        std::cerr << "Sleeping " << cancelAfterValue << " seconds before canceling prompt..."
+                  << std::endl;
         auto fstatus =
             future.wait_for(std::chrono::milliseconds(uint64_t(cancelAfterValue * 1000)));
         if (fstatus == std::future_status::timeout) {
-            status = service->cancelConfirmationPrompt(listener, &aidl_return);
-            if (!status.isOk()) {
-                printf("Canceling confirmation prompt failed with binder status '%s'.\n",
-                       status.toString8().c_str());
-                return 1;
-            }
-            responseCode = static_cast<ConfirmationResponseCode>(aidl_return);
-            if (responseCode == ConfirmationResponseCode::Ignored) {
-                // The confirmation was completed by the user so take the response
-            } else if (responseCode != ConfirmationResponseCode::OK) {
-                printf("Canceling confirmation prompt failed with response code %d.\n",
-                       responseCode);
+            rc = apcService->cancelPrompt(listener);
+            if (!rc.isOk()) {
+                std::cerr << "Canceling confirmation prompt failed: " << rc.getDescription()
+                          << std::endl;
                 return 1;
             }
         }
@@ -582,27 +1052,28 @@
 
     future.wait();
 
-    auto [rc, dataThatWasConfirmed] = future.get();
+    auto [responseCode, dataThatWasConfirmed] = future.get();
 
-    printf("Confirmation prompt completed\n"
-           "responseCode = %d\n",
-           rc);
-    printf("dataThatWasConfirmed[%zd] = {", dataThatWasConfirmed.size());
+    std::cerr << "Confirmation prompt completed\n"
+              << "responseCode = " << toString(responseCode);
     size_t newLineCountDown = 16;
     bool hasPrinted = false;
-    for (uint8_t element : dataThatWasConfirmed) {
-        if (hasPrinted) {
-            printf(", ");
-        }
-        if (newLineCountDown == 0) {
-            printf("\n  ");
-            newLineCountDown = 32;
-        }
-        printf("0x%02x", element);
-        hasPrinted = true;
-    }
-    printf("}\n");
+    if (dataThatWasConfirmed) {
+        std::cerr << "dataThatWasConfirmed[" << dataThatWasConfirmed->size() << "] = {";
+        for (uint8_t element : *dataThatWasConfirmed) {
+            if (hasPrinted) {
+                std::cerr << ", ";
+            }
+            if (newLineCountDown == 0) {
+                std::cerr << "\n  ";
+                newLineCountDown = 32;
+            }
+            std::cerr << "0x" << std::hex << std::setw(2) << std::setfill('0') << (unsigned)element;
 
+            hasPrinted = true;
+        }
+    }
+    std::cerr << std::endl;
     return 0;
 }
 
@@ -613,7 +1084,7 @@
     CommandLine* command_line = CommandLine::ForCurrentProcess();
     CommandLine::StringVector args = command_line->GetArgs();
 
-    android::ProcessState::self()->startThreadPool();
+    ABinderProcess_startThreadPool();
 
     if (args.empty()) {
         PrintUsageAndExit();
@@ -623,12 +1094,9 @@
                                   command_line->HasSwitch("test_for_0_3"));
     } else if (args[0] == "list-brillo-tests") {
         return ListTestCases();
-    } else if (args[0] == "add-entropy") {
-        return AddEntropy(command_line->GetSwitchValueASCII("input"),
-                          securityLevelOption2Flags(*command_line));
     } else if (args[0] == "generate") {
         return GenerateKey(command_line->GetSwitchValueASCII("name"),
-                           securityLevelOption2Flags(*command_line),
+                           securityLevelOption2SecurlityLevel(*command_line),
                            command_line->HasSwitch("auth_bound"));
     } else if (args[0] == "get-chars") {
         return GetCharacteristics(command_line->GetSwitchValueASCII("name"));
@@ -636,20 +1104,17 @@
         return ExportKey(command_line->GetSwitchValueASCII("name"));
     } else if (args[0] == "delete") {
         return DeleteKey(command_line->GetSwitchValueASCII("name"));
-    } else if (args[0] == "delete-all") {
-        return DeleteAllKeys();
     } else if (args[0] == "exists") {
         return DoesKeyExist(command_line->GetSwitchValueASCII("name"));
     } else if (args[0] == "list") {
-        return List(command_line->GetSwitchValueASCII("prefix"));
-    } else if (args[0] == "list-apps-with-keys") {
-        return ListAppsWithKeys();
+        return List();
     } else if (args[0] == "sign-verify") {
         return SignAndVerify(command_line->GetSwitchValueASCII("name"));
     } else if (args[0] == "encrypt") {
-        return Encrypt(
-            command_line->GetSwitchValueASCII("name"), command_line->GetSwitchValueASCII("in"),
-            command_line->GetSwitchValueASCII("out"), securityLevelOption2Flags(*command_line));
+        return Encrypt(command_line->GetSwitchValueASCII("name"),
+                       command_line->GetSwitchValueASCII("in"),
+                       command_line->GetSwitchValueASCII("out"),
+                       securityLevelOption2SecurlityLevel(*command_line));
     } else if (args[0] == "decrypt") {
         return Decrypt(command_line->GetSwitchValueASCII("name"),
                        command_line->GetSwitchValueASCII("in"),
diff --git a/keystore/keystore_client_impl.cpp b/keystore/keystore_client_impl.cpp
deleted file mode 100644
index f888683..0000000
--- a/keystore/keystore_client_impl.cpp
+++ /dev/null
@@ -1,629 +0,0 @@
-// Copyright 2015 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.
-
-#define LOG_TAG "keystore_client"
-
-#include "keystore/keystore_client_impl.h"
-
-#include <future>
-#include <optional>
-#include <string>
-#include <vector>
-
-#include <android/security/keystore/IKeystoreService.h>
-#include <binder/IBinder.h>
-#include <binder/IInterface.h>
-#include <binder/IServiceManager.h>
-#include <keystore/keystore.h>
-#include <log/log.h>
-#include <utils/String16.h>
-#include <utils/String8.h>
-
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore_hidl_support.h>
-#include <keystore/keystore_promises.h>
-
-#include "keystore_client.pb.h"
-
-namespace {
-
-// Use the UID of the current process.
-const int kDefaultUID = -1;
-const char kEncryptSuffix[] = "_ENC";
-const char kAuthenticateSuffix[] = "_AUTH";
-constexpr uint32_t kAESKeySize = 256;      // bits
-constexpr uint32_t kHMACKeySize = 256;     // bits
-constexpr uint32_t kHMACOutputSize = 256;  // bits
-
-using android::String16;
-using android::security::keymaster::ExportResult;
-using android::security::keymaster::OperationResult;
-using android::security::keystore::KeystoreResponse;
-using keystore::AuthorizationSet;
-using keystore::AuthorizationSetBuilder;
-using keystore::KeyCharacteristics;
-using keystore::KeyStoreServiceReturnCode;
-}  // namespace
-
-namespace keystore {
-
-KeystoreClientImpl::KeystoreClientImpl() {
-    service_manager_ = android::defaultServiceManager();
-    keystore_binder_ = service_manager_->getService(String16("android.security.keystore"));
-    keystore_ =
-        android::interface_cast<android::security::keystore::IKeystoreService>(keystore_binder_);
-}
-
-bool KeystoreClientImpl::encryptWithAuthentication(const std::string& key_name,
-                                                   const std::string& data, int32_t flags,
-                                                   std::string* encrypted_data) {
-    // The encryption algorithm is AES-256-CBC with PKCS #7 padding and a random
-    // IV. The authentication algorithm is HMAC-SHA256 and is computed over the
-    // cipher-text (i.e. Encrypt-then-MAC approach). This was chosen over AES-GCM
-    // because hardware support for GCM is not mandatory for all Brillo devices.
-    std::string encryption_key_name = key_name + kEncryptSuffix;
-    if (!createOrVerifyEncryptionKey(encryption_key_name, flags)) {
-        return false;
-    }
-    std::string authentication_key_name = key_name + kAuthenticateSuffix;
-    if (!createOrVerifyAuthenticationKey(authentication_key_name, flags)) {
-        return false;
-    }
-    AuthorizationSetBuilder encrypt_params;
-    encrypt_params.Padding(PaddingMode::PKCS7);
-    encrypt_params.Authorization(TAG_BLOCK_MODE, BlockMode::CBC);
-    AuthorizationSet output_params;
-    std::string raw_encrypted_data;
-    if (!oneShotOperation(KeyPurpose::ENCRYPT, encryption_key_name, encrypt_params, data,
-                          std::string(), /* signature_to_verify */
-                          &output_params, &raw_encrypted_data)) {
-        ALOGE("Encrypt: AES operation failed.");
-        return false;
-    }
-    auto init_vector_blob = output_params.GetTagValue(TAG_NONCE);
-    if (!init_vector_blob.isOk()) {
-        ALOGE("Encrypt: Missing initialization vector.");
-        return false;
-    }
-    std::string init_vector = hidlVec2String(init_vector_blob.value());
-
-    AuthorizationSetBuilder authenticate_params;
-    authenticate_params.Digest(Digest::SHA_2_256);
-    authenticate_params.Authorization(TAG_MAC_LENGTH, kHMACOutputSize);
-    std::string raw_authentication_data;
-    if (!oneShotOperation(KeyPurpose::SIGN, authentication_key_name, authenticate_params,
-                          init_vector + raw_encrypted_data, std::string(), /* signature_to_verify */
-                          &output_params, &raw_authentication_data)) {
-        ALOGE("Encrypt: HMAC operation failed.");
-        return false;
-    }
-    EncryptedData protobuf;
-    protobuf.set_init_vector(init_vector);
-    protobuf.set_authentication_data(raw_authentication_data);
-    protobuf.set_encrypted_data(raw_encrypted_data);
-    if (!protobuf.SerializeToString(encrypted_data)) {
-        ALOGE("Encrypt: Failed to serialize EncryptedData protobuf.");
-        return false;
-    }
-    return true;
-}
-
-bool KeystoreClientImpl::decryptWithAuthentication(const std::string& key_name,
-                                                   const std::string& encrypted_data,
-                                                   std::string* data) {
-    EncryptedData protobuf;
-    if (!protobuf.ParseFromString(encrypted_data)) {
-        ALOGE("Decrypt: Failed to parse EncryptedData protobuf.");
-    }
-    // Verify authentication before attempting decryption.
-    std::string authentication_key_name = key_name + kAuthenticateSuffix;
-    AuthorizationSetBuilder authenticate_params;
-    authenticate_params.Digest(Digest::SHA_2_256);
-    AuthorizationSet output_params;
-    std::string output_data;
-    if (!oneShotOperation(KeyPurpose::VERIFY, authentication_key_name, authenticate_params,
-                          protobuf.init_vector() + protobuf.encrypted_data(),
-                          protobuf.authentication_data(), &output_params, &output_data)) {
-        ALOGE("Decrypt: HMAC operation failed.");
-        return false;
-    }
-    std::string encryption_key_name = key_name + kEncryptSuffix;
-    AuthorizationSetBuilder encrypt_params;
-    encrypt_params.Padding(PaddingMode::PKCS7);
-    encrypt_params.Authorization(TAG_BLOCK_MODE, BlockMode::CBC);
-    encrypt_params.Authorization(TAG_NONCE, protobuf.init_vector().data(),
-                                 protobuf.init_vector().size());
-    if (!oneShotOperation(KeyPurpose::DECRYPT, encryption_key_name, encrypt_params,
-                          protobuf.encrypted_data(), std::string(), /* signature_to_verify */
-                          &output_params, data)) {
-        ALOGE("Decrypt: AES operation failed.");
-        return false;
-    }
-    return true;
-}
-
-bool KeystoreClientImpl::oneShotOperation(KeyPurpose purpose, const std::string& key_name,
-                                          const AuthorizationSet& input_parameters,
-                                          const std::string& input_data,
-                                          const std::string& signature_to_verify,
-                                          AuthorizationSet* output_parameters,
-                                          std::string* output_data) {
-    uint64_t handle;
-    auto result = beginOperation(purpose, key_name, input_parameters, output_parameters, &handle);
-    if (!result.isOk()) {
-        ALOGE("BeginOperation failed: %d", result.getErrorCode());
-        return false;
-    }
-    AuthorizationSet empty_params;
-    AuthorizationSet ignored_params;
-    result = finishOperation(handle, empty_params, input_data, signature_to_verify, &ignored_params,
-                             output_data);
-    if (!result.isOk()) {
-        ALOGE("FinishOperation failed: %d", result.getErrorCode());
-        return false;
-    }
-    return true;
-}
-
-KeyStoreNativeReturnCode
-KeystoreClientImpl::addRandomNumberGeneratorEntropy(const std::string& entropy, int32_t flags) {
-    int32_t error_code;
-
-    android::sp<KeystoreResponsePromise> promise(new KeystoreResponsePromise());
-    auto future = promise->get_future();
-
-    auto binder_result =
-        keystore_->addRngEntropy(promise, blob2hidlVec(entropy), flags, &error_code);
-    if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-
-    KeyStoreNativeReturnCode rc(error_code);
-    if (!rc.isOk()) return rc;
-
-    auto result = future.get();
-
-    return KeyStoreNativeReturnCode(result.response_code());
-}
-
-KeyStoreNativeReturnCode
-KeystoreClientImpl::generateKey(const std::string& key_name, const AuthorizationSet& key_parameters,
-                                int32_t flags, AuthorizationSet* hardware_enforced_characteristics,
-                                AuthorizationSet* software_enforced_characteristics) {
-    String16 key_name16(key_name.data(), key_name.size());
-    int32_t error_code;
-    android::sp<KeyCharacteristicsPromise> promise(new KeyCharacteristicsPromise);
-    auto future = promise->get_future();
-    auto binder_result = keystore_->generateKey(
-        promise, key_name16,
-        ::android::security::keymaster::KeymasterArguments(key_parameters.hidl_data()),
-        hidl_vec<uint8_t>() /* entropy */, kDefaultUID, flags, &error_code);
-    if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-
-    KeyStoreNativeReturnCode rc(error_code);
-    if (!rc.isOk()) return rc;
-
-    auto [km_response, characteristics] = future.get();
-
-    /* assignment (hidl_vec<KeyParameter> -> AuthorizationSet) makes a deep copy.
-     * There are no references to Parcel memory after that, and ownership of the newly acquired
-     * memory is with the AuthorizationSet objects. */
-    *hardware_enforced_characteristics = characteristics.hardwareEnforced.getParameters();
-    *software_enforced_characteristics = characteristics.softwareEnforced.getParameters();
-    return KeyStoreNativeReturnCode(km_response.response_code());
-}
-
-KeyStoreNativeReturnCode
-KeystoreClientImpl::getKeyCharacteristics(const std::string& key_name,
-                                          AuthorizationSet* hardware_enforced_characteristics,
-                                          AuthorizationSet* software_enforced_characteristics) {
-    String16 key_name16(key_name.data(), key_name.size());
-    int32_t error_code;
-    android::sp<KeyCharacteristicsPromise> promise(new KeyCharacteristicsPromise);
-    auto future = promise->get_future();
-    auto binder_result = keystore_->getKeyCharacteristics(
-        promise, key_name16, android::security::keymaster::KeymasterBlob(),
-        android::security::keymaster::KeymasterBlob(), kDefaultUID, &error_code);
-    if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-
-    KeyStoreNativeReturnCode rc(error_code);
-    if (!rc.isOk()) return rc;
-
-    auto [km_response, characteristics] = future.get();
-
-    /* assignment (hidl_vec<KeyParameter> -> AuthorizationSet) makes a deep copy.
-     * There are no references to Parcel memory after that, and ownership of the newly acquired
-     * memory is with the AuthorizationSet objects. */
-    *hardware_enforced_characteristics = characteristics.hardwareEnforced.getParameters();
-    *software_enforced_characteristics = characteristics.softwareEnforced.getParameters();
-    return KeyStoreNativeReturnCode(km_response.response_code());
-}
-
-KeyStoreNativeReturnCode
-KeystoreClientImpl::importKey(const std::string& key_name, const AuthorizationSet& key_parameters,
-                              KeyFormat key_format, const std::string& key_data,
-                              AuthorizationSet* hardware_enforced_characteristics,
-                              AuthorizationSet* software_enforced_characteristics) {
-    String16 key_name16(key_name.data(), key_name.size());
-    auto hidlKeyData = blob2hidlVec(key_data);
-    int32_t error_code;
-    android::sp<KeyCharacteristicsPromise> promise(new KeyCharacteristicsPromise);
-    auto future = promise->get_future();
-    auto binder_result = keystore_->importKey(
-        promise, key_name16,
-        ::android::security::keymaster::KeymasterArguments(key_parameters.hidl_data()),
-        (int)key_format, hidlKeyData, kDefaultUID, KEYSTORE_FLAG_NONE, &error_code);
-    if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-
-    KeyStoreNativeReturnCode rc(error_code);
-    if (!rc.isOk()) return rc;
-
-    auto [km_response, characteristics] = future.get();
-
-    /* assignment (hidl_vec<KeyParameter> -> AuthorizationSet) makes a deep copy.
-     * There are no references to Parcel memory after that, and ownership of the newly acquired
-     * memory is with the AuthorizationSet objects. */
-    *hardware_enforced_characteristics = characteristics.hardwareEnforced.getParameters();
-    *software_enforced_characteristics = characteristics.softwareEnforced.getParameters();
-    return KeyStoreNativeReturnCode(km_response.response_code());
-}
-
-KeyStoreNativeReturnCode KeystoreClientImpl::exportKey(KeyFormat export_format,
-                                                       const std::string& key_name,
-                                                       std::string* export_data) {
-    String16 key_name16(key_name.data(), key_name.size());
-    int32_t error_code;
-    android::sp<KeystoreExportPromise> promise(new KeystoreExportPromise);
-    auto future = promise->get_future();
-    auto binder_result = keystore_->exportKey(
-        promise, key_name16, (int)export_format, android::security::keymaster::KeymasterBlob(),
-        android::security::keymaster::KeymasterBlob(), kDefaultUID, &error_code);
-    if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-
-    KeyStoreNativeReturnCode rc(error_code);
-    if (!rc.isOk()) return rc;
-
-    auto export_result = future.get();
-    if (!export_result.resultCode.isOk()) return export_result.resultCode;
-
-    *export_data = hidlVec2String(export_result.exportData);
-
-    return export_result.resultCode;
-}
-
-KeyStoreNativeReturnCode KeystoreClientImpl::deleteKey(const std::string& key_name) {
-    String16 key_name16(key_name.data(), key_name.size());
-    int32_t result;
-    auto binder_result = keystore_->del(key_name16, kDefaultUID, &result);
-    if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-    return KeyStoreNativeReturnCode(result);
-}
-
-KeyStoreNativeReturnCode KeystoreClientImpl::deleteAllKeys() {
-    int32_t result;
-    auto binder_result = keystore_->clear_uid(kDefaultUID, &result);
-    if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-    return KeyStoreNativeReturnCode(result);
-}
-
-KeyStoreNativeReturnCode
-KeystoreClientImpl::beginOperation(KeyPurpose purpose, const std::string& key_name,
-                                   const AuthorizationSet& input_parameters,
-                                   AuthorizationSet* output_parameters, uint64_t* handle) {
-    android::sp<android::IBinder> token(new android::BBinder);
-    String16 key_name16(key_name.data(), key_name.size());
-    int32_t error_code;
-    android::sp<OperationResultPromise> promise(new OperationResultPromise{});
-    auto future = promise->get_future();
-    auto binder_result = keystore_->begin(
-        promise, token, key_name16, (int)purpose, true /*pruneable*/,
-        android::security::keymaster::KeymasterArguments(input_parameters.hidl_data()),
-        hidl_vec<uint8_t>() /* entropy */, kDefaultUID, &error_code);
-    if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-    KeyStoreNativeReturnCode rc(error_code);
-    if (!rc.isOk()) return rc;
-
-    OperationResult result = future.get();
-    if (result.resultCode.isOk()) {
-        *handle = getNextVirtualHandle();
-        active_operations_[*handle] = result.token;
-        if (result.outParams.size()) {
-            *output_parameters = result.outParams;
-        }
-    }
-    return result.resultCode;
-}
-
-KeyStoreNativeReturnCode
-KeystoreClientImpl::updateOperation(uint64_t handle, const AuthorizationSet& input_parameters,
-                                    const std::string& input_data, size_t* num_input_bytes_consumed,
-                                    AuthorizationSet* output_parameters, std::string* output_data) {
-    if (active_operations_.count(handle) == 0) {
-        return ErrorCode::INVALID_OPERATION_HANDLE;
-    }
-    auto hidlInputData = blob2hidlVec(input_data);
-    int32_t error_code;
-    android::sp<OperationResultPromise> promise(new OperationResultPromise{});
-    auto future = promise->get_future();
-    auto binder_result = keystore_->update(
-        promise, active_operations_[handle],
-        android::security::keymaster::KeymasterArguments(input_parameters.hidl_data()),
-        hidlInputData, &error_code);
-    if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-    KeyStoreNativeReturnCode rc(error_code);
-    if (!rc.isOk()) return rc;
-
-    OperationResult result = future.get();
-
-    if (result.resultCode.isOk()) {
-        *num_input_bytes_consumed = result.inputConsumed;
-        if (result.outParams.size()) {
-            *output_parameters = result.outParams;
-        }
-        // TODO verify that append should not be assign
-        output_data->append(hidlVec2String(result.data));
-    }
-    return result.resultCode;
-}
-
-KeyStoreNativeReturnCode
-KeystoreClientImpl::finishOperation(uint64_t handle, const AuthorizationSet& input_parameters,
-                                    const std::string& input_data,
-                                    const std::string& signature_to_verify,
-                                    AuthorizationSet* output_parameters, std::string* output_data) {
-    if (active_operations_.count(handle) == 0) {
-        return ErrorCode::INVALID_OPERATION_HANDLE;
-    }
-    int32_t error_code;
-    auto hidlSignature = blob2hidlVec(signature_to_verify);
-    auto hidlInput = blob2hidlVec(input_data);
-    android::sp<OperationResultPromise> promise(new OperationResultPromise{});
-    auto future = promise->get_future();
-    auto binder_result = keystore_->finish(
-        promise, active_operations_[handle],
-        android::security::keymaster::KeymasterArguments(input_parameters.hidl_data()),
-        (std::vector<uint8_t>)hidlInput, (std::vector<uint8_t>)hidlSignature, hidl_vec<uint8_t>(),
-        &error_code);
-    if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-    KeyStoreNativeReturnCode rc(error_code);
-    if (!rc.isOk()) return rc;
-
-    OperationResult result = future.get();
-    if (result.resultCode.isOk()) {
-        if (result.outParams.size()) {
-            *output_parameters = result.outParams;
-        }
-        // TODO verify that append should not be assign
-        output_data->append(hidlVec2String(result.data));
-        active_operations_.erase(handle);
-    }
-    return result.resultCode;
-}
-
-KeyStoreNativeReturnCode KeystoreClientImpl::abortOperation(uint64_t handle) {
-    if (active_operations_.count(handle) == 0) {
-        return ErrorCode::INVALID_OPERATION_HANDLE;
-    }
-    int32_t result;
-    android::sp<KeystoreResponsePromise> promise(new KeystoreResponsePromise{});
-    auto future = promise->get_future();
-    // Current implementation does not return exceptions in android::binder::Status
-    auto binder_result = keystore_->abort(promise, active_operations_[handle], &result);
-    if (!binder_result.isOk()) return ResponseCode::SYSTEM_ERROR;
-    KeyStoreNativeReturnCode rc(result);
-    if (!rc.isOk()) return rc;
-    rc = KeyStoreNativeReturnCode(future.get().response_code());
-    if (rc.isOk()) {
-        active_operations_.erase(handle);
-    }
-    return rc;
-}
-
-bool KeystoreClientImpl::doesKeyExist(const std::string& key_name) {
-    String16 key_name16(key_name.data(), key_name.size());
-    int32_t result;
-    auto binder_result = keystore_->exist(key_name16, kDefaultUID, &result);
-    if (!binder_result.isOk()) return false;  // binder error
-    return result == static_cast<int32_t>(ResponseCode::NO_ERROR);
-}
-
-bool KeystoreClientImpl::listKeys(const std::string& prefix,
-                                  std::vector<std::string>* key_name_list) {
-    return listKeysOfUid(prefix, kDefaultUID, key_name_list);
-}
-
-bool KeystoreClientImpl::listKeysOfUid(const std::string& prefix, int uid,
-                                       std::vector<std::string>* key_name_list) {
-    String16 prefix16(prefix.data(), prefix.size());
-    std::vector<::android::String16> matches;
-    auto binder_result = keystore_->list(prefix16, uid, &matches);
-    if (!binder_result.isOk()) return false;
-
-    for (const auto& match : matches) {
-        android::String8 key_name(match);
-        key_name_list->push_back(prefix + std::string(key_name.string(), key_name.size()));
-    }
-    return true;
-}
-
-std::optional<std::vector<uint8_t>> KeystoreClientImpl::getKey(const std::string& alias, int uid) {
-    String16 alias16(alias.data(), alias.size());
-    std::vector<uint8_t> output;
-    auto binder_result = keystore_->get(alias16, uid, &output);
-    if (!binder_result.isOk()) return std::nullopt;
-    return output;
-}
-
-uint64_t KeystoreClientImpl::getNextVirtualHandle() {
-    return next_virtual_handle_++;
-}
-
-bool KeystoreClientImpl::createOrVerifyEncryptionKey(const std::string& key_name, int32_t flags) {
-    bool key_exists = doesKeyExist(key_name);
-    if (key_exists) {
-        bool verified = false;
-        if (!verifyEncryptionKeyAttributes(key_name, &verified)) {
-            return false;
-        }
-        if (!verified) {
-            auto result = deleteKey(key_name);
-            if (!result.isOk()) {
-                ALOGE("Failed to delete invalid encryption key: %d", result.getErrorCode());
-                return false;
-            }
-            key_exists = false;
-        }
-    }
-    if (!key_exists) {
-        AuthorizationSetBuilder key_parameters;
-        key_parameters.AesEncryptionKey(kAESKeySize)
-            .Padding(PaddingMode::PKCS7)
-            .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
-            .Authorization(TAG_NO_AUTH_REQUIRED);
-        AuthorizationSet hardware_enforced_characteristics;
-        AuthorizationSet software_enforced_characteristics;
-        auto result =
-            generateKey(key_name, key_parameters, flags, &hardware_enforced_characteristics,
-                        &software_enforced_characteristics);
-        if (!result.isOk()) {
-            ALOGE("Failed to generate encryption key: %d", result.getErrorCode());
-            return false;
-        }
-        if (hardware_enforced_characteristics.size() == 0) {
-            ALOGW("WARNING: Encryption key is not hardware-backed.");
-        }
-    }
-    return true;
-}
-
-bool KeystoreClientImpl::createOrVerifyAuthenticationKey(const std::string& key_name,
-                                                         int32_t flags) {
-    bool key_exists = doesKeyExist(key_name);
-    if (key_exists) {
-        bool verified = false;
-        if (!verifyAuthenticationKeyAttributes(key_name, &verified)) {
-            return false;
-        }
-        if (!verified) {
-            auto result = deleteKey(key_name);
-            if (!result.isOk()) {
-                ALOGE("Failed to delete invalid authentication key: %d", result.getErrorCode());
-                return false;
-            }
-            key_exists = false;
-        }
-    }
-    if (!key_exists) {
-        AuthorizationSetBuilder key_parameters;
-        key_parameters.HmacKey(kHMACKeySize)
-            .Digest(Digest::SHA_2_256)
-            .Authorization(TAG_MIN_MAC_LENGTH, kHMACOutputSize)
-            .Authorization(TAG_NO_AUTH_REQUIRED);
-        AuthorizationSet hardware_enforced_characteristics;
-        AuthorizationSet software_enforced_characteristics;
-        auto result =
-            generateKey(key_name, key_parameters, flags, &hardware_enforced_characteristics,
-                        &software_enforced_characteristics);
-        if (!result.isOk()) {
-            ALOGE("Failed to generate authentication key: %d", result.getErrorCode());
-            return false;
-        }
-        if (hardware_enforced_characteristics.size() == 0) {
-            ALOGW("WARNING: Authentication key is not hardware-backed.");
-        }
-    }
-    return true;
-}
-
-bool KeystoreClientImpl::verifyEncryptionKeyAttributes(const std::string& key_name,
-                                                       bool* verified) {
-    AuthorizationSet hardware_enforced_characteristics;
-    AuthorizationSet software_enforced_characteristics;
-    auto result = getKeyCharacteristics(key_name, &hardware_enforced_characteristics,
-                                        &software_enforced_characteristics);
-    if (!result.isOk()) {
-        ALOGE("Failed to query encryption key: %d", result.getErrorCode());
-        return false;
-    }
-    *verified = true;
-    auto algorithm = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_ALGORITHM),
-                              software_enforced_characteristics.GetTagValue(TAG_ALGORITHM));
-    if (!algorithm.isOk() || algorithm.value() != Algorithm::AES) {
-        ALOGW("Found encryption key with invalid algorithm.");
-        *verified = false;
-    }
-    auto key_size = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_KEY_SIZE),
-                             software_enforced_characteristics.GetTagValue(TAG_KEY_SIZE));
-    if (!key_size.isOk() || key_size.value() != kAESKeySize) {
-        ALOGW("Found encryption key with invalid size.");
-        *verified = false;
-    }
-    auto block_mode = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_BLOCK_MODE),
-                               software_enforced_characteristics.GetTagValue(TAG_BLOCK_MODE));
-    if (!block_mode.isOk() || block_mode.value() != BlockMode::CBC) {
-        ALOGW("Found encryption key with invalid block mode.");
-        *verified = false;
-    }
-    auto padding_mode = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_PADDING),
-                                 software_enforced_characteristics.GetTagValue(TAG_PADDING));
-    if (!padding_mode.isOk() || padding_mode.value() != PaddingMode::PKCS7) {
-        ALOGW("Found encryption key with invalid padding mode.");
-        *verified = false;
-    }
-    if (hardware_enforced_characteristics.size() == 0) {
-        ALOGW("WARNING: Encryption key is not hardware-backed.");
-    }
-    return true;
-}
-
-bool KeystoreClientImpl::verifyAuthenticationKeyAttributes(const std::string& key_name,
-                                                           bool* verified) {
-    AuthorizationSet hardware_enforced_characteristics;
-    AuthorizationSet software_enforced_characteristics;
-    auto result = getKeyCharacteristics(key_name, &hardware_enforced_characteristics,
-                                        &software_enforced_characteristics);
-    if (!result.isOk()) {
-        ALOGE("Failed to query authentication key: %d", result.getErrorCode());
-        return false;
-    }
-    *verified = true;
-    auto algorithm = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_ALGORITHM),
-                              software_enforced_characteristics.GetTagValue(TAG_ALGORITHM));
-    if (!algorithm.isOk() || algorithm.value() != Algorithm::HMAC) {
-        ALOGW("Found authentication key with invalid algorithm.");
-        *verified = false;
-    }
-    auto key_size = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_KEY_SIZE),
-                             software_enforced_characteristics.GetTagValue(TAG_KEY_SIZE));
-    if (!key_size.isOk() || key_size.value() != kHMACKeySize) {
-        ALOGW("Found authentication key with invalid size.");
-        *verified = false;
-    }
-    auto mac_size = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_MIN_MAC_LENGTH),
-                             software_enforced_characteristics.GetTagValue(TAG_MIN_MAC_LENGTH));
-    if (!mac_size.isOk() || mac_size.value() != kHMACOutputSize) {
-        ALOGW("Found authentication key with invalid minimum mac size.");
-        *verified = false;
-    }
-    auto digest = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_DIGEST),
-                           software_enforced_characteristics.GetTagValue(TAG_DIGEST));
-    if (!digest.isOk() || digest.value() != Digest::SHA_2_256) {
-        ALOGW("Found authentication key with invalid digest list.");
-        *verified = false;
-    }
-    if (hardware_enforced_characteristics.size() == 0) {
-        ALOGW("WARNING: Authentication key is not hardware-backed.");
-    }
-    return true;
-}
-
-}  // namespace keystore
diff --git a/keystore/keystore_main.cpp b/keystore/keystore_main.cpp
deleted file mode 100644
index 02c2139..0000000
--- a/keystore/keystore_main.cpp
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2009 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.
- */
-
-#define LOG_TAG "keystore"
-
-#include <android-base/logging.h>
-#include <android/hidl/manager/1.2/IServiceManager.h>
-#include <android/security/keystore/IKeystoreService.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <keymasterV4_1/Keymaster3.h>
-#include <keymasterV4_1/Keymaster4.h>
-#include <utils/StrongPointer.h>
-
-#include <keystore/keystore_hidl_support.h>
-#include <keystore/keystore_return_types.h>
-
-#include "KeyStore.h"
-#include "key_store_service.h"
-#include "legacy_keymaster_device_wrapper.h"
-#include "permissions.h"
-
-/* KeyStore is a secured storage for key-value pairs. In this implementation,
- * each file stores one key-value pair. Keys are encoded in file names, and
- * values are encrypted with checksums. The encryption key is protected by a
- * user-defined password. To keep things simple, buffers are always larger than
- * the maximum space we needed, so boundary checks on buffers are omitted. */
-
-using ::android::sp;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::keymaster::V4_0::ErrorCode;
-using ::android::hardware::keymaster::V4_0::HmacSharingParameters;
-using ::android::hardware::keymaster::V4_0::SecurityLevel;
-using ::android::hidl::manager::V1_2::IServiceManager;
-
-using ::keystore::keymaster::support::Keymaster;
-using ::keystore::keymaster::support::Keymaster3;
-using ::keystore::keymaster::support::Keymaster4;
-
-using keystore::KeymasterDevices;
-
-template <typename Wrapper>
-KeymasterDevices enumerateKeymasterDevices(IServiceManager* serviceManager) {
-    KeymasterDevices result;
-    serviceManager->listManifestByInterface(
-        Wrapper::WrappedIKeymasterDevice::descriptor, [&](const hidl_vec<hidl_string>& names) {
-            auto try_get_device = [&](const auto& name, bool fail_silent) {
-                auto device = Wrapper::WrappedIKeymasterDevice::getService(name);
-                if (fail_silent && !device) return;
-                CHECK(device) << "Failed to get service for \""
-                              << Wrapper::WrappedIKeymasterDevice::descriptor
-                              << "\" with interface name \"" << name << "\"";
-
-                sp<Keymaster> kmDevice(new Wrapper(device, name));
-                auto halVersion = kmDevice->halVersion();
-                SecurityLevel securityLevel = halVersion.securityLevel;
-                LOG(INFO) << "found " << Wrapper::WrappedIKeymasterDevice::descriptor
-                          << " with interface name " << name << " and seclevel "
-                          << toString(securityLevel);
-                CHECK(static_cast<uint32_t>(securityLevel) < result.size())
-                    << "Security level of \"" << Wrapper::WrappedIKeymasterDevice::descriptor
-                    << "\" with interface name \"" << name << "\" out of range";
-                auto& deviceSlot = result[securityLevel];
-                if (deviceSlot) {
-                    if (!fail_silent) {
-                        LOG(WARNING) << "Implementation of \""
-                                     << Wrapper::WrappedIKeymasterDevice::descriptor
-                                     << "\" with interface name \"" << name
-                                     << "\" and security level: " << toString(securityLevel)
-                                     << " Masked by other implementation of Keymaster";
-                    }
-                } else {
-                    deviceSlot = kmDevice;
-                }
-            };
-            bool has_default = false;
-            for (auto& n : names) {
-                try_get_device(n, false);
-                if (n == "default") has_default = true;
-            }
-            // Make sure that we always check the default device. If we enumerate only what is
-            // known to hwservicemanager, we miss a possible passthrough HAL.
-            if (!has_default) {
-                try_get_device("default", true /* fail_silent */);
-            }
-        });
-    return result;
-}
-
-KeymasterDevices initializeKeymasters() {
-    auto serviceManager = IServiceManager::getService();
-    CHECK(serviceManager.get()) << "Failed to get ServiceManager";
-    auto result = enumerateKeymasterDevices<Keymaster4>(serviceManager.get());
-    auto softKeymaster = result[SecurityLevel::SOFTWARE];
-    if (!result[SecurityLevel::TRUSTED_ENVIRONMENT]) {
-        result = enumerateKeymasterDevices<Keymaster3>(serviceManager.get());
-    }
-    if (softKeymaster) result[SecurityLevel::SOFTWARE] = softKeymaster;
-    if (result[SecurityLevel::SOFTWARE] && !result[SecurityLevel::TRUSTED_ENVIRONMENT]) {
-        LOG(WARNING) << "No secure Keymaster implementation found, but device offers insecure"
-                        " Keymaster HAL. Using as default.";
-        result[SecurityLevel::TRUSTED_ENVIRONMENT] = result[SecurityLevel::SOFTWARE];
-        result[SecurityLevel::SOFTWARE] = nullptr;
-    }
-    if (!result[SecurityLevel::SOFTWARE]) {
-        auto fbdev = android::keystore::makeSoftwareKeymasterDevice();
-        CHECK(fbdev.get()) << "Unable to create Software Keymaster Device";
-        result[SecurityLevel::SOFTWARE] = new Keymaster3(fbdev, "Software");
-    }
-    return result;
-}
-
-int main(int argc, char* argv[]) {
-    using android::hardware::hidl_string;
-    CHECK(argc >= 2) << "A directory must be specified!";
-    CHECK(chdir(argv[1]) != -1) << "chdir: " << argv[1] << ": " << strerror(errno);
-
-    auto kmDevices = initializeKeymasters();
-
-    CHECK(kmDevices[SecurityLevel::SOFTWARE]) << "Missing software Keymaster device";
-    CHECK(kmDevices[SecurityLevel::TRUSTED_ENVIRONMENT])
-        << "Error no viable keymaster device found";
-
-    CHECK(configure_selinux() != -1) << "Failed to configure SELinux.";
-
-    auto halVersion = kmDevices[SecurityLevel::TRUSTED_ENVIRONMENT]->halVersion();
-
-    // If the hardware is keymaster 2.0 or higher we will not allow the fallback device for import
-    // or generation of keys. The fallback device is only used for legacy keys present on the
-    // device.
-    SecurityLevel minimalAllowedSecurityLevelForNewKeys =
-        halVersion.majorVersion >= 2 ? SecurityLevel::TRUSTED_ENVIRONMENT : SecurityLevel::SOFTWARE;
-
-    android::sp<keystore::KeyStore> keyStore(
-        new keystore::KeyStore(kmDevices, minimalAllowedSecurityLevelForNewKeys));
-    keyStore->initialize();
-    android::sp<android::IServiceManager> sm = android::defaultServiceManager();
-    android::sp<keystore::KeyStoreService> service = new keystore::KeyStoreService(keyStore);
-    service->setRequestingSid(true);
-    android::status_t ret = sm->addService(android::String16("android.security.keystore"), service);
-    CHECK(ret == android::OK) << "Couldn't register binder service!";
-
-    /*
-     * This thread is just going to process Binder transactions.
-     */
-    android::IPCThreadState::self()->joinThreadPool();
-    return 1;
-}
diff --git a/keystore/legacy_keymaster_device_wrapper.cpp b/keystore/legacy_keymaster_device_wrapper.cpp
deleted file mode 100644
index 86d286e..0000000
--- a/keystore/legacy_keymaster_device_wrapper.cpp
+++ /dev/null
@@ -1,547 +0,0 @@
-/*
- **
- ** Copyright 2016, 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.
- */
-
-#define LOG_TAG "android.hardware.keymaster@3.0-impl"
-
-#include "legacy_keymaster_device_wrapper.h"
-
-#include <log/log.h>
-
-#include <hardware/keymaster2.h>
-#include <hardware/keymaster_defs.h>
-#include <keymaster/keymaster_configuration.h>
-#include <keymaster/soft_keymaster_device.h>
-
-namespace android {
-namespace keystore {
-
-using ::keymaster::SoftKeymasterDevice;
-
-LegacyKeymasterDeviceWrapper::LegacyKeymasterDeviceWrapper(keymaster2_device_t* dev)
-    : keymaster_device_(dev) {}
-
-LegacyKeymasterDeviceWrapper::~LegacyKeymasterDeviceWrapper() {
-    if (keymaster_device_) keymaster_device_->common.close(&keymaster_device_->common);
-}
-
-static inline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) {
-    return keymaster_tag_get_type(tag);
-}
-
-/**
- * legacy_enum_conversion converts enums from hidl to keymaster and back. Currently, this is just a
- * cast to make the compiler happy. One of two thigs should happen though:
- * TODO The keymaster enums should become aliases for the hidl generated enums so that we have a
- *      single point of truth. Then this cast function can go away.
- */
-inline static keymaster_tag_t legacy_enum_conversion(const Tag value) {
-    return keymaster_tag_t(value);
-}
-inline static Tag legacy_enum_conversion(const keymaster_tag_t value) {
-    return Tag(value);
-}
-inline static keymaster_purpose_t legacy_enum_conversion(const KeyPurpose value) {
-    return keymaster_purpose_t(value);
-}
-inline static keymaster_key_format_t legacy_enum_conversion(const KeyFormat value) {
-    return keymaster_key_format_t(value);
-}
-inline static ErrorCode legacy_enum_conversion(const keymaster_error_t value) {
-    return ErrorCode(value);
-}
-
-class KmParamSet : public keymaster_key_param_set_t {
-  public:
-    explicit KmParamSet(const hidl_vec<KeyParameter>& keyParams) {
-        params = new keymaster_key_param_t[keyParams.size()];
-        length = keyParams.size();
-        for (size_t i = 0; i < keyParams.size(); ++i) {
-            auto tag = legacy_enum_conversion(keyParams[i].tag);
-            switch (typeFromTag(tag)) {
-            case KM_ENUM:
-            case KM_ENUM_REP:
-                params[i] = keymaster_param_enum(tag, keyParams[i].f.integer);
-                break;
-            case KM_UINT:
-            case KM_UINT_REP:
-                params[i] = keymaster_param_int(tag, keyParams[i].f.integer);
-                break;
-            case KM_ULONG:
-            case KM_ULONG_REP:
-                params[i] = keymaster_param_long(tag, keyParams[i].f.longInteger);
-                break;
-            case KM_DATE:
-                params[i] = keymaster_param_date(tag, keyParams[i].f.dateTime);
-                break;
-            case KM_BOOL:
-                if (keyParams[i].f.boolValue)
-                    params[i] = keymaster_param_bool(tag);
-                else
-                    params[i].tag = KM_TAG_INVALID;
-                break;
-            case KM_BIGNUM:
-            case KM_BYTES:
-                params[i] =
-                    keymaster_param_blob(tag, &keyParams[i].blob[0], keyParams[i].blob.size());
-                break;
-            case KM_INVALID:
-            default:
-                params[i].tag = KM_TAG_INVALID;
-                /* just skip */
-                break;
-            }
-        }
-    }
-    KmParamSet(KmParamSet&& other) noexcept
-        : keymaster_key_param_set_t{other.params, other.length} {
-        other.length = 0;
-        other.params = nullptr;
-    }
-    KmParamSet(const KmParamSet&) = delete;
-    ~KmParamSet() { delete[] params; }
-};
-
-inline static KmParamSet hidlParams2KmParamSet(const hidl_vec<KeyParameter>& params) {
-    return KmParamSet(params);
-}
-
-inline static keymaster_blob_t hidlVec2KmBlob(const hidl_vec<uint8_t>& blob) {
-    /* hidl unmarshals funny pointers if the the blob is empty */
-    if (blob.size()) return {&blob[0], blob.size()};
-    return {};
-}
-
-inline static keymaster_key_blob_t hidlVec2KmKeyBlob(const hidl_vec<uint8_t>& blob) {
-    /* hidl unmarshals funny pointers if the the blob is empty */
-    if (blob.size()) return {&blob[0], blob.size()};
-    return {};
-}
-
-inline static hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_key_blob_t& blob) {
-    if (blob.key_material == nullptr || blob.key_material_size == 0) {
-        return {};
-    } else {
-        return hidl_vec<uint8_t>(blob.key_material, blob.key_material + blob.key_material_size);
-    }
-}
-inline static hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_blob_t& blob) {
-    if (blob.data == nullptr || blob.data_length == 0) {
-        return {};
-    } else {
-        return hidl_vec<uint8_t>(blob.data, blob.data + blob.data_length);
-    }
-}
-
-inline static hidl_vec<hidl_vec<uint8_t>>
-kmCertChain2Hidl(const keymaster_cert_chain_t* cert_chain) {
-    hidl_vec<hidl_vec<uint8_t>> result;
-    if (!cert_chain || cert_chain->entry_count == 0 || !cert_chain->entries) return result;
-
-    result.resize(cert_chain->entry_count);
-    for (size_t i = 0; i < cert_chain->entry_count; ++i) {
-        auto& entry = cert_chain->entries[i];
-        result[i] = kmBlob2hidlVec(entry);
-    }
-
-    return result;
-}
-
-static inline hidl_vec<KeyParameter> kmParamSet2Hidl(const keymaster_key_param_set_t& set) {
-    hidl_vec<KeyParameter> result;
-    if (set.length == 0 || set.params == nullptr) return result;
-
-    result.resize(set.length);
-    keymaster_key_param_t* params = set.params;
-    for (size_t i = 0; i < set.length; ++i) {
-        auto tag = params[i].tag;
-        result[i].tag = legacy_enum_conversion(tag);
-        switch (typeFromTag(tag)) {
-        case KM_ENUM:
-        case KM_ENUM_REP:
-            result[i].f.integer = params[i].enumerated;
-            break;
-        case KM_UINT:
-        case KM_UINT_REP:
-            result[i].f.integer = params[i].integer;
-            break;
-        case KM_ULONG:
-        case KM_ULONG_REP:
-            result[i].f.longInteger = params[i].long_integer;
-            break;
-        case KM_DATE:
-            result[i].f.dateTime = params[i].date_time;
-            break;
-        case KM_BOOL:
-            result[i].f.boolValue = params[i].boolean;
-            break;
-        case KM_BIGNUM:
-        case KM_BYTES:
-            result[i].blob = kmBlob2hidlVec(params[i].blob);
-            break;
-        case KM_INVALID:
-        default:
-            params[i].tag = KM_TAG_INVALID;
-            /* just skip */
-            break;
-        }
-    }
-    return result;
-}
-
-// Methods from ::android::hardware::keymaster::V3_0::IKeymasterDevice follow.
-Return<void> LegacyKeymasterDeviceWrapper::getHardwareFeatures(getHardwareFeatures_cb _hidl_cb) {
-    _hidl_cb(false, false, false, false, false, "Fallback Device", "Google Android Security");
-    return Void();
-}
-
-Return<ErrorCode> LegacyKeymasterDeviceWrapper::addRngEntropy(const hidl_vec<uint8_t>& data) {
-    return legacy_enum_conversion(
-        keymaster_device_->add_rng_entropy(keymaster_device_, &data[0], data.size()));
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::generateKey(const hidl_vec<KeyParameter>& keyParams,
-                                                       generateKey_cb _hidl_cb) {
-    // result variables for the wire
-    KeyCharacteristics resultCharacteristics;
-    hidl_vec<uint8_t> resultKeyBlob;
-
-    // result variables the backend understands
-    keymaster_key_blob_t key_blob{nullptr, 0};
-    keymaster_key_characteristics_t key_characteristics{{nullptr, 0}, {nullptr, 0}};
-
-    // convert the parameter set to something our backend understands
-    auto kmParams = hidlParams2KmParamSet(keyParams);
-
-    auto rc = keymaster_device_->generate_key(keymaster_device_, &kmParams, &key_blob,
-                                              &key_characteristics);
-
-    if (rc == KM_ERROR_OK) {
-        // on success convert the result to wire format
-        resultKeyBlob = kmBlob2hidlVec(key_blob);
-        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(key_characteristics.sw_enforced);
-        resultCharacteristics.teeEnforced = kmParamSet2Hidl(key_characteristics.hw_enforced);
-    }
-
-    // send results off to the client
-    _hidl_cb(legacy_enum_conversion(rc), resultKeyBlob, resultCharacteristics);
-
-    // free buffers that we are responsible for
-    if (key_blob.key_material) free(const_cast<uint8_t*>(key_blob.key_material));
-    keymaster_free_characteristics(&key_characteristics);
-
-    return Void();
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::getKeyCharacteristics(
-    const hidl_vec<uint8_t>& keyBlob, const hidl_vec<uint8_t>& clientId,
-    const hidl_vec<uint8_t>& appData, getKeyCharacteristics_cb _hidl_cb) {
-    // result variables for the wire
-    KeyCharacteristics resultCharacteristics;
-
-    // result variables the backend understands
-    keymaster_key_characteristics_t key_characteristics{{nullptr, 0}, {nullptr, 0}};
-
-    auto kmKeyBlob = hidlVec2KmKeyBlob(keyBlob);
-    auto kmClientId = hidlVec2KmBlob(clientId);
-    auto kmAppData = hidlVec2KmBlob(appData);
-
-    auto rc = keymaster_device_->get_key_characteristics(
-        keymaster_device_, keyBlob.size() ? &kmKeyBlob : nullptr,
-        clientId.size() ? &kmClientId : nullptr, appData.size() ? &kmAppData : nullptr,
-        &key_characteristics);
-
-    if (rc == KM_ERROR_OK) {
-        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(key_characteristics.sw_enforced);
-        resultCharacteristics.teeEnforced = kmParamSet2Hidl(key_characteristics.hw_enforced);
-    }
-
-    _hidl_cb(legacy_enum_conversion(rc), resultCharacteristics);
-
-    keymaster_free_characteristics(&key_characteristics);
-
-    return Void();
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::importKey(const hidl_vec<KeyParameter>& params,
-                                                     KeyFormat keyFormat,
-                                                     const hidl_vec<uint8_t>& keyData,
-                                                     importKey_cb _hidl_cb) {
-    // result variables for the wire
-    KeyCharacteristics resultCharacteristics;
-    hidl_vec<uint8_t> resultKeyBlob;
-
-    // result variables the backend understands
-    keymaster_key_blob_t key_blob{nullptr, 0};
-    keymaster_key_characteristics_t key_characteristics{{nullptr, 0}, {nullptr, 0}};
-
-    auto kmParams = hidlParams2KmParamSet(params);
-    auto kmKeyData = hidlVec2KmBlob(keyData);
-
-    auto rc = keymaster_device_->import_key(keymaster_device_, &kmParams,
-                                            legacy_enum_conversion(keyFormat), &kmKeyData,
-                                            &key_blob, &key_characteristics);
-
-    if (rc == KM_ERROR_OK) {
-        // on success convert the result to wire format
-        resultKeyBlob = kmBlob2hidlVec(key_blob);
-        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(key_characteristics.sw_enforced);
-        resultCharacteristics.teeEnforced = kmParamSet2Hidl(key_characteristics.hw_enforced);
-    }
-
-    _hidl_cb(legacy_enum_conversion(rc), resultKeyBlob, resultCharacteristics);
-
-    // free buffers that we are responsible for
-    if (key_blob.key_material) free(const_cast<uint8_t*>(key_blob.key_material));
-    keymaster_free_characteristics(&key_characteristics);
-
-    return Void();
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::exportKey(KeyFormat exportFormat,
-                                                     const hidl_vec<uint8_t>& keyBlob,
-                                                     const hidl_vec<uint8_t>& clientId,
-                                                     const hidl_vec<uint8_t>& appData,
-                                                     exportKey_cb _hidl_cb) {
-
-    // result variables for the wire
-    hidl_vec<uint8_t> resultKeyBlob;
-
-    // result variables the backend understands
-    keymaster_blob_t out_blob = {};
-
-    auto kmKeyBlob = hidlVec2KmKeyBlob(keyBlob);
-    auto kmClientId = hidlVec2KmBlob(clientId);
-    auto kmAppData = hidlVec2KmBlob(appData);
-
-    auto rc = keymaster_device_->export_key(keymaster_device_, legacy_enum_conversion(exportFormat),
-                                            keyBlob.size() ? &kmKeyBlob : nullptr,
-                                            clientId.size() ? &kmClientId : nullptr,
-                                            appData.size() ? &kmAppData : nullptr, &out_blob);
-
-    if (rc == KM_ERROR_OK) {
-        // on success convert the result to wire format
-        // (Can we assume that key_blob is {nullptr, 0} or a valid buffer description?)
-        resultKeyBlob = kmBlob2hidlVec(out_blob);
-    }
-
-    _hidl_cb(legacy_enum_conversion(rc), resultKeyBlob);
-
-    // free buffers that we are responsible for
-    if (out_blob.data) free(const_cast<uint8_t*>(out_blob.data));
-
-    return Void();
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::attestKey(const hidl_vec<uint8_t>& keyToAttest,
-                                                     const hidl_vec<KeyParameter>& attestParams,
-                                                     attestKey_cb _hidl_cb) {
-
-    hidl_vec<hidl_vec<uint8_t>> resultCertChain;
-
-    for (size_t i = 0; i < attestParams.size(); ++i) {
-        switch (attestParams[i].tag) {
-            case Tag::ATTESTATION_ID_BRAND:
-            case Tag::ATTESTATION_ID_DEVICE:
-            case Tag::ATTESTATION_ID_PRODUCT:
-            case Tag::ATTESTATION_ID_SERIAL:
-            case Tag::ATTESTATION_ID_IMEI:
-            case Tag::ATTESTATION_ID_MEID:
-            case Tag::ATTESTATION_ID_MANUFACTURER:
-            case Tag::ATTESTATION_ID_MODEL:
-                // Device id attestation may only be supported if the device is able to permanently
-                // destroy its knowledge of the ids. This device is unable to do this, so it must
-                // never perform any device id attestation.
-                _hidl_cb(ErrorCode::CANNOT_ATTEST_IDS, resultCertChain);
-                return Void();
-            default:
-                break;
-        }
-    }
-
-    keymaster_cert_chain_t cert_chain = {};
-
-    auto kmKeyToAttest = hidlVec2KmKeyBlob(keyToAttest);
-    auto kmAttestParams = hidlParams2KmParamSet(attestParams);
-
-    auto rc = keymaster_device_->attest_key(keymaster_device_, &kmKeyToAttest, &kmAttestParams,
-                                            &cert_chain);
-
-    if (rc == KM_ERROR_OK) {
-        resultCertChain = kmCertChain2Hidl(&cert_chain);
-    }
-
-    _hidl_cb(legacy_enum_conversion(rc), resultCertChain);
-
-    keymaster_free_cert_chain(&cert_chain);
-
-    return Void();
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
-                                                      const hidl_vec<KeyParameter>& upgradeParams,
-                                                      upgradeKey_cb _hidl_cb) {
-
-    // result variables for the wire
-    hidl_vec<uint8_t> resultKeyBlob;
-
-    // result variables the backend understands
-    keymaster_key_blob_t key_blob = {};
-
-    auto kmKeyBlobToUpgrade = hidlVec2KmKeyBlob(keyBlobToUpgrade);
-    auto kmUpgradeParams = hidlParams2KmParamSet(upgradeParams);
-
-    auto rc = keymaster_device_->upgrade_key(keymaster_device_, &kmKeyBlobToUpgrade,
-                                             &kmUpgradeParams, &key_blob);
-
-    if (rc == KM_ERROR_OK) {
-        // on success convert the result to wire format
-        resultKeyBlob = kmBlob2hidlVec(key_blob);
-    }
-
-    _hidl_cb(legacy_enum_conversion(rc), resultKeyBlob);
-
-    if (key_blob.key_material) free(const_cast<uint8_t*>(key_blob.key_material));
-
-    return Void();
-}
-
-Return<ErrorCode> LegacyKeymasterDeviceWrapper::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
-    auto kmKeyBlob = hidlVec2KmKeyBlob(keyBlob);
-    return legacy_enum_conversion(keymaster_device_->delete_key(keymaster_device_, &kmKeyBlob));
-}
-
-Return<ErrorCode> LegacyKeymasterDeviceWrapper::deleteAllKeys() {
-    return legacy_enum_conversion(keymaster_device_->delete_all_keys(keymaster_device_));
-}
-
-Return<ErrorCode> LegacyKeymasterDeviceWrapper::destroyAttestationIds() {
-    return ErrorCode::UNIMPLEMENTED;
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
-                                                 const hidl_vec<KeyParameter>& inParams,
-                                                 begin_cb _hidl_cb) {
-
-    // result variables for the wire
-    hidl_vec<KeyParameter> resultParams;
-    uint64_t resultOpHandle = 0;
-
-    // result variables the backend understands
-    keymaster_key_param_set_t out_params{nullptr, 0};
-    keymaster_operation_handle_t& operation_handle = resultOpHandle;
-
-    auto kmKey = hidlVec2KmKeyBlob(key);
-    auto kmInParams = hidlParams2KmParamSet(inParams);
-
-    auto rc = keymaster_device_->begin(keymaster_device_, legacy_enum_conversion(purpose), &kmKey,
-                                       &kmInParams, &out_params, &operation_handle);
-
-    if (rc == KM_ERROR_OK) resultParams = kmParamSet2Hidl(out_params);
-
-    _hidl_cb(legacy_enum_conversion(rc), resultParams, resultOpHandle);
-
-    keymaster_free_param_set(&out_params);
-
-    return Void();
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::update(uint64_t operationHandle,
-                                                  const hidl_vec<KeyParameter>& inParams,
-                                                  const hidl_vec<uint8_t>& input,
-                                                  update_cb _hidl_cb) {
-    // result variables for the wire
-    uint32_t resultConsumed = 0;
-    hidl_vec<KeyParameter> resultParams;
-    hidl_vec<uint8_t> resultBlob;
-
-    // result variables the backend understands
-    size_t consumed = 0;
-    keymaster_key_param_set_t out_params = {};
-    keymaster_blob_t out_blob = {};
-
-    auto kmInParams = hidlParams2KmParamSet(inParams);
-    auto kmInput = hidlVec2KmBlob(input);
-
-    auto rc = keymaster_device_->update(keymaster_device_, operationHandle, &kmInParams, &kmInput,
-                                        &consumed, &out_params, &out_blob);
-
-    if (rc == KM_ERROR_OK) {
-        resultConsumed = consumed;
-        resultParams = kmParamSet2Hidl(out_params);
-        resultBlob = kmBlob2hidlVec(out_blob);
-    }
-
-    _hidl_cb(legacy_enum_conversion(rc), resultConsumed, resultParams, resultBlob);
-
-    keymaster_free_param_set(&out_params);
-    if (out_blob.data) free(const_cast<uint8_t*>(out_blob.data));
-
-    return Void();
-}
-
-Return<void> LegacyKeymasterDeviceWrapper::finish(uint64_t operationHandle,
-                                                  const hidl_vec<KeyParameter>& inParams,
-                                                  const hidl_vec<uint8_t>& input,
-                                                  const hidl_vec<uint8_t>& signature,
-                                                  finish_cb _hidl_cb) {
-    // result variables for the wire
-    hidl_vec<KeyParameter> resultParams;
-    hidl_vec<uint8_t> resultBlob;
-
-    // result variables the backend understands
-    keymaster_key_param_set_t out_params = {};
-    keymaster_blob_t out_blob = {};
-
-    auto kmInParams = hidlParams2KmParamSet(inParams);
-    auto kmInput = hidlVec2KmBlob(input);
-    auto kmSignature = hidlVec2KmBlob(signature);
-
-    auto rc = keymaster_device_->finish(keymaster_device_, operationHandle, &kmInParams, &kmInput,
-                                        &kmSignature, &out_params, &out_blob);
-
-    if (rc == KM_ERROR_OK) {
-        resultParams = kmParamSet2Hidl(out_params);
-        resultBlob = kmBlob2hidlVec(out_blob);
-    }
-
-    _hidl_cb(legacy_enum_conversion(rc), resultParams, resultBlob);
-
-    keymaster_free_param_set(&out_params);
-    if (out_blob.data) free(const_cast<uint8_t*>(out_blob.data));
-
-    return Void();
-}
-
-Return<ErrorCode> LegacyKeymasterDeviceWrapper::abort(uint64_t operationHandle) {
-    return legacy_enum_conversion(keymaster_device_->abort(keymaster_device_, operationHandle));
-}
-
-sp<IKeymasterDevice> makeSoftwareKeymasterDevice() {
-    keymaster2_device_t* dev = nullptr;
-    dev = (new SoftKeymasterDevice)->keymaster2_device();
-
-    auto kmrc = ::keymaster::ConfigureDevice(dev);
-    if (kmrc != KM_ERROR_OK) {
-        dev->common.close(&dev->common);
-        return nullptr;
-    }
-
-    return new LegacyKeymasterDeviceWrapper(dev);
-}
-
-}  // namespace keystore
-}  // namespace android
diff --git a/keystore/legacy_keymaster_device_wrapper.h b/keystore/legacy_keymaster_device_wrapper.h
deleted file mode 100644
index cd2e5a7..0000000
--- a/keystore/legacy_keymaster_device_wrapper.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- **
- ** Copyright 2016, 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 LEGACY_KEYMASTER_DEVICE_WRAPPER_H_
-#define LEGACY_KEYMASTER_DEVICE_WRAPPER_H_
-
-#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
-#include <hidl/Status.h>
-#include <hidl/MQDescriptor.h>
-
-struct keymaster2_device;
-typedef struct keymaster2_device keymaster2_device_t;
-
-namespace android {
-namespace keystore {
-
-using ::android::hardware::keymaster::V3_0::ErrorCode;
-using ::android::hardware::keymaster::V3_0::IKeymasterDevice;
-using ::android::hardware::keymaster::V3_0::KeyCharacteristics;
-using ::android::hardware::keymaster::V3_0::KeyFormat;
-using ::android::hardware::keymaster::V3_0::KeyParameter;
-using ::android::hardware::keymaster::V3_0::KeyPurpose;
-using ::android::hardware::keymaster::V3_0::Tag;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::hidl_string;
-using ::android::sp;
-
-class LegacyKeymasterDeviceWrapper : public IKeymasterDevice {
-  public:
-    explicit LegacyKeymasterDeviceWrapper(keymaster2_device_t* dev);
-    virtual ~LegacyKeymasterDeviceWrapper();
-
-    // Methods from ::android::hardware::keymaster::V3_0::IKeymasterDevice follow.
-    Return<void> getHardwareFeatures(getHardwareFeatures_cb _hidl_cb);
-    Return<ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override;
-    Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
-                             generateKey_cb _hidl_cb) override;
-    Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
-                                       const hidl_vec<uint8_t>& clientId,
-                                       const hidl_vec<uint8_t>& appData,
-                                       getKeyCharacteristics_cb _hidl_cb) override;
-    Return<void> importKey(const hidl_vec<KeyParameter>& params, KeyFormat keyFormat,
-                           const hidl_vec<uint8_t>& keyData, importKey_cb _hidl_cb) override;
-    Return<void> exportKey(KeyFormat exportFormat, const hidl_vec<uint8_t>& keyBlob,
-                           const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
-                           exportKey_cb _hidl_cb) override;
-    Return<void> attestKey(const hidl_vec<uint8_t>& keyToAttest,
-                           const hidl_vec<KeyParameter>& attestParams,
-                           attestKey_cb _hidl_cb) override;
-    Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
-                            const hidl_vec<KeyParameter>& upgradeParams,
-                            upgradeKey_cb _hidl_cb) override;
-    Return<ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override;
-    Return<ErrorCode> deleteAllKeys() override;
-    Return<ErrorCode> destroyAttestationIds() override;
-    Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
-                       const hidl_vec<KeyParameter>& inParams, begin_cb _hidl_cb) override;
-    Return<void> update(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
-                        const hidl_vec<uint8_t>& input, update_cb _hidl_cb) override;
-    Return<void> finish(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
-                        const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,
-                        finish_cb _hidl_cb) override;
-    Return<ErrorCode> abort(uint64_t operationHandle) override;
-
-  private:
-    keymaster2_device_t* keymaster_device_;
-};
-
-sp<IKeymasterDevice> makeSoftwareKeymasterDevice();
-
-}  // namespace keystore
-}  // namespace android
-
-#endif  // LEGACY_KEYMASTER_DEVICE_WRAPPER_H_
diff --git a/keystore/operation.cpp b/keystore/operation.cpp
deleted file mode 100644
index bd4bd5e..0000000
--- a/keystore/operation.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-#define LOG_TAG "KeystoreOperation"
-
-#include "operation.h"
-#include "key_operation_log_handler.h"
-
-#include <algorithm>
-#include <android-base/logging.h>
-#include <mutex>
-
-namespace keystore {
-
-OperationMap::OperationMap(IBinder::DeathRecipient* deathRecipient)
-    : mDeathRecipient(deathRecipient) {}
-
-sp<IBinder> OperationMap::addOperation(uint64_t handle, uint64_t keyid, KeyPurpose purpose,
-                                       const sp<Keymaster>& dev, const sp<IBinder>& appToken,
-                                       KeyCharacteristics&& characteristics,
-                                       const hidl_vec<KeyParameter>& params, bool pruneable) {
-    sp<IBinder> token = new ::android::BBinder();
-    mMap.emplace(token, std::make_shared<Operation>(handle, keyid, purpose, dev,
-                                                    std::move(characteristics), appToken, params));
-    if (pruneable) mLru.push_back(token);
-    if (mAppTokenMap.find(appToken) == mAppTokenMap.end()) appToken->linkToDeath(mDeathRecipient);
-    mAppTokenMap[appToken].push_back(token);
-    return token;
-}
-
-std::shared_ptr<Operation> OperationMap::getOperation(const sp<IBinder>& token) {
-    auto entry = mMap.find(token);
-    if (entry == mMap.end()) return {};
-
-    auto op = entry->second;
-
-    updateLru(token);
-    return op;
-}
-
-void OperationMap::updateLru(const sp<IBinder>& token) {
-    auto lruEntry = std::find(mLru.begin(), mLru.end(), token);
-    if (lruEntry != mLru.end()) {
-        mLru.erase(lruEntry);
-        mLru.push_back(token);
-    }
-}
-
-std::shared_ptr<Operation> OperationMap::removeOperation(const sp<IBinder>& token,
-                                                         bool wasSuccessful, int32_t responseCode) {
-    auto entry = mMap.find(token);
-    if (entry == mMap.end()) return {};
-
-    auto op = entry->second;
-    logKeystoreKeyOperationEvent(*op, wasSuccessful, responseCode);
-    mMap.erase(entry);
-
-    auto lruEntry = std::find(mLru.begin(), mLru.end(), token);
-    if (lruEntry != mLru.end()) mLru.erase(lruEntry);
-    removeOperationTracking(token, op->appToken);
-    return op;
-}
-
-void OperationMap::removeOperationTracking(const sp<IBinder>& token, const sp<IBinder>& appToken) {
-    auto appEntry = mAppTokenMap.find(appToken);
-    if (appEntry == mAppTokenMap.end()) {
-        ALOGE("Entry for %p contains unmapped application token %p", token.get(), appToken.get());
-        return;
-    }
-    auto tokenEntry = std::find(appEntry->second.begin(), appEntry->second.end(), token);
-    appEntry->second.erase(tokenEntry);
-    // Stop listening for death if all operations tied to the token have finished.
-    if (appEntry->second.size() == 0) {
-        appToken->unlinkToDeath(mDeathRecipient);
-        mAppTokenMap.erase(appEntry);
-    }
-}
-
-sp<IBinder> OperationMap::getOldestPruneableOperation() {
-    if (mLru.size() == 0) return {};
-
-    return {mLru.front()};
-}
-
-std::vector<sp<IBinder>> OperationMap::getOperationsForToken(const sp<IBinder>& appToken) {
-    auto appEntry = mAppTokenMap.find(appToken);
-    if (appEntry == mAppTokenMap.end()) return {};
-    return appEntry->second;
-}
-
-}  // namespace keystore
diff --git a/keystore/operation.h b/keystore/operation.h
deleted file mode 100644
index 8423db5..0000000
--- a/keystore/operation.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2015 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 KEYSTORE_OPERATION_H_
-#define KEYSTORE_OPERATION_H_
-
-#include <list>
-#include <map>
-#include <memory>
-#include <mutex>
-#include <optional>
-#include <vector>
-
-#include <binder/Binder.h>
-#include <binder/IBinder.h>
-#include <keymasterV4_1/Keymaster.h>
-#include <utils/StrongPointer.h>
-
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore_concurrency.h>
-#include <keystore/keystore_hidl_support.h>
-
-#include "operation_struct.h"
-
-namespace keystore {
-
-using ::android::IBinder;
-using ::android::sp;
-using keymaster::support::Keymaster;
-
-/**
- * OperationMap handles the translation of uint64_t's and keymaster2_device_t's to opaque binder
- * tokens that can be used to reference that operation at a later time by applications. It also does
- * LRU tracking for operation pruning and keeps a mapping of clients to operations to allow for
- * graceful handling of application death.
- */
-
-class OperationMap {
-  public:
-    explicit OperationMap(IBinder::DeathRecipient* deathRecipient);
-    sp<IBinder> addOperation(uint64_t handle, uint64_t keyid, KeyPurpose purpose,
-                             const sp<Keymaster>& dev, const sp<IBinder>& appToken,
-                             KeyCharacteristics&& characteristics,
-                             const hidl_vec<KeyParameter>& params, bool pruneable);
-    std::shared_ptr<Operation> getOperation(const sp<IBinder>& token);
-    std::shared_ptr<Operation> removeOperation(const sp<IBinder>& token, bool wasSuccessful,
-                                               int32_t responseCode);
-    size_t getOperationCount() const { return mMap.size(); }
-    sp<IBinder> getOldestPruneableOperation();
-    std::vector<sp<IBinder>> getOperationsForToken(const sp<IBinder>& appToken);
-
-  private:
-    void updateLru(const sp<IBinder>& token);
-    void removeOperationTracking(const sp<IBinder>& token, const sp<IBinder>& appToken);
-
-    std::map<sp<IBinder>, std::shared_ptr<Operation>> mMap;
-    std::list<sp<IBinder>> mLru;
-    std::map<sp<IBinder>, std::vector<sp<IBinder>>> mAppTokenMap;
-    IBinder::DeathRecipient* mDeathRecipient;
-};
-
-}  // namespace keystore
-
-#endif
diff --git a/keystore/operation_struct.h b/keystore/operation_struct.h
deleted file mode 100644
index 23e79fc..0000000
--- a/keystore/operation_struct.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2018 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 KEYSTORE_OPERATION_STRUCT_H_
-#define KEYSTORE_OPERATION_STRUCT_H_
-
-#include <binder/Binder.h>
-#include <binder/IBinder.h>
-#include <keymasterV4_1/Keymaster.h>
-#include <utils/StrongPointer.h>
-
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore_hidl_support.h>
-#include <keystore/keystore_return_types.h>
-
-#include <future>
-
-namespace keystore {
-
-using ::android::IBinder;
-using ::android::sp;
-using keymaster::support::Keymaster;
-
-struct Operation {
-    Operation() = default;
-    Operation(uint64_t handle_, uint64_t keyid_, KeyPurpose purpose_, const sp<Keymaster>& device_,
-              KeyCharacteristics&& characteristics_, sp<IBinder> appToken_,
-              const hidl_vec<KeyParameter> params_)
-        : handle(handle_), keyid(keyid_), purpose(purpose_), device(device_),
-          characteristics(characteristics_), appToken(appToken_), authToken(), verificationToken(),
-          params(params_) {}
-    Operation(Operation&&) = default;
-    Operation(const Operation&) = delete;
-
-    bool hasAuthToken() const { return authToken.mac.size() != 0; }
-
-    uint64_t handle;
-    uint64_t keyid;
-    KeyPurpose purpose;
-    sp<Keymaster> device;
-    KeyCharacteristics characteristics;
-    sp<IBinder> appToken;
-    std::promise<KeyStoreServiceReturnCode> authTokenPromise;
-    std::future<KeyStoreServiceReturnCode> authTokenFuture;
-    HardwareAuthToken authToken;
-    VerificationToken verificationToken;
-    const hidl_vec<KeyParameter> params;
-};
-
-}  // namespace keystore
-
-#endif
diff --git a/keystore/permissions.cpp b/keystore/permissions.cpp
deleted file mode 100644
index 2cd42cf..0000000
--- a/keystore/permissions.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#define LOG_TAG "keystore"
-
-#include "permissions.h"
-
-#include <cutils/sockets.h>
-#include <log/log.h>
-#include <private/android_filesystem_config.h>
-
-#include <selinux/android.h>
-
-#include "keystore_utils.h"
-
-/* perm_labels associcated with keystore_key SELinux class verbs. */
-const char* perm_labels[] = {
-    "get_state",
-    "get",
-    "insert",
-    "delete",
-    "exist",
-    "list",
-    "reset",
-    "password",
-    "lock",
-    "unlock",
-    "is_empty",
-    "sign",
-    "verify",
-    "grant",
-    "duplicate",
-    "clear_uid",
-    "add_auth",
-    "user_changed",
-    "gen_unique_id",
-};
-
-struct user_euid {
-    uid_t uid;
-    uid_t euid;
-};
-
-user_euid user_euids[] = {{AID_VPN, AID_SYSTEM},
-                          {AID_WIFI, AID_SYSTEM},
-                          {AID_ROOT, AID_SYSTEM},
-
-#ifdef GRANT_ROOT_ALL_PERMISSIONS
-                          // Allow VTS tests to act on behalf of the wifi user
-                          {AID_WIFI, AID_ROOT}
-#endif
-};
-
-struct user_perm {
-    uid_t uid;
-    perm_t perms;
-};
-
-static user_perm user_perms[] = {
-    {AID_SYSTEM, static_cast<perm_t>((uint32_t)(~0))},
-    {AID_VPN, static_cast<perm_t>(P_GET | P_SIGN | P_VERIFY)},
-    {AID_WIFI, static_cast<perm_t>(P_GET | P_SIGN | P_VERIFY)},
-    {AID_BLUETOOTH, static_cast<perm_t>(P_GET | P_INSERT | P_DELETE | P_EXIST | P_SIGN | P_VERIFY)},
-
-#ifdef GRANT_ROOT_ALL_PERMISSIONS
-    // Allow VTS tests running as root to perform all operations
-    {AID_ROOT, static_cast<perm_t>((uint32_t)(~0))},
-#else
-    {AID_ROOT, static_cast<perm_t>(P_GET)},
-#endif
-};
-
-static const perm_t DEFAULT_PERMS = static_cast<perm_t>(
-    P_GET_STATE | P_GET | P_INSERT | P_DELETE | P_EXIST | P_LIST | P_SIGN | P_VERIFY |
-    P_GEN_UNIQUE_ID /* Only privileged apps can do this, but enforcement is done by SELinux */);
-
-struct audit_data {
-    pid_t pid;
-    uid_t uid;
-    const char* sid;
-};
-
-const char* get_perm_label(perm_t perm) {
-    unsigned int index = ffs(perm);
-    if (index > 0 && index <= (sizeof(perm_labels) / sizeof(perm_labels[0]))) {
-        return perm_labels[index - 1];
-    } else {
-        ALOGE("Keystore: Failed to retrieve permission label.\n");
-        abort();
-    }
-}
-
-static int audit_callback(void* data, security_class_t /* cls */, char* buf, size_t len) {
-    struct audit_data* ad = reinterpret_cast<struct audit_data*>(data);
-    if (!ad) {
-        ALOGE("No keystore audit data");
-        return 0;
-    }
-
-    const char* sid = ad->sid ? ad->sid : "N/A";
-    snprintf(buf, len, "pid=%d uid=%d sid=%s", ad->pid, ad->uid, sid);
-    return 0;
-}
-
-static char* tctx;
-
-int configure_selinux() {
-    union selinux_callback cb;
-    cb.func_audit = audit_callback;
-    selinux_set_callback(SELINUX_CB_AUDIT, cb);
-    cb.func_log = selinux_log_callback;
-    selinux_set_callback(SELINUX_CB_LOG, cb);
-    if (getcon(&tctx) != 0) {
-        ALOGE("SELinux: Could not acquire target context. Aborting keystore.\n");
-        return -1;
-    }
-
-    return 0;
-}
-
-static bool keystore_selinux_check_access(uid_t uid, perm_t perm, pid_t spid, const char* ssid) {
-    audit_data ad;
-    char* sctx = nullptr;
-    const char* selinux_class = "keystore_key";
-    const char* str_perm = get_perm_label(perm);
-
-    if (!str_perm) {
-        return false;
-    }
-
-    if (ssid == nullptr && getpidcon(spid, &sctx) != 0) {
-        ALOGE("SELinux: Failed to get source pid context.\n");
-        return false;
-    }
-
-    const char* use_sid = ssid ? ssid : sctx;
-
-    ad.pid = spid;
-    ad.uid = uid;
-    ad.sid = use_sid;
-
-    bool allowed = selinux_check_access(use_sid, tctx, selinux_class, str_perm,
-                                        reinterpret_cast<void*>(&ad)) == 0;
-    freecon(sctx);
-    return allowed;
-}
-
-/**
- * Returns the UID that the callingUid should act as. This is here for
- * legacy support of the WiFi and VPN systems and should be removed
- * when WiFi can operate in its own namespace.
- */
-uid_t get_keystore_euid(uid_t uid) {
-    for (size_t i = 0; i < sizeof(user_euids) / sizeof(user_euids[0]); i++) {
-        struct user_euid user = user_euids[i];
-        if (user.uid == uid) {
-            return user.euid;
-        }
-    }
-
-    return uid;
-}
-
-bool has_permission(uid_t uid, perm_t perm, pid_t spid, const char* sid) {
-    // All system users are equivalent for multi-user support.
-    if (get_app_id(uid) == AID_SYSTEM) {
-        uid = AID_SYSTEM;
-    }
-
-    if (sid == nullptr) {
-        android_errorWriteLog(0x534e4554, "121035042");
-    }
-
-    for (size_t i = 0; i < sizeof(user_perms) / sizeof(user_perms[0]); i++) {
-        struct user_perm user = user_perms[i];
-        if (user.uid == uid) {
-            return (user.perms & perm) && keystore_selinux_check_access(uid, perm, spid, sid);
-        }
-    }
-
-    return (DEFAULT_PERMS & perm) && keystore_selinux_check_access(uid, perm, spid, sid);
-}
-
-/**
- * Returns true if the callingUid is allowed to interact in the targetUid's
- * namespace.
- */
-bool is_granted_to(uid_t callingUid, uid_t targetUid) {
-    if (callingUid == targetUid) {
-        return true;
-    }
-    for (size_t i = 0; i < sizeof(user_euids) / sizeof(user_euids[0]); i++) {
-        struct user_euid user = user_euids[i];
-        if (user.euid == callingUid && user.uid == targetUid) {
-            return true;
-        }
-    }
-
-    return false;
-}
diff --git a/keystore/permissions.h b/keystore/permissions.h
deleted file mode 100644
index 1dd0089..0000000
--- a/keystore/permissions.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2016 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 KEYSTORE_PERMISSIONS_H_
-#define KEYSTORE_PERMISSIONS_H_
-
-#include <unistd.h>
-
-/* Here are the permissions, actions, users, and the main function. */
-enum perm_t {
-    P_GET_STATE = 1 << 0,
-    P_GET = 1 << 1,
-    P_INSERT = 1 << 2,
-    P_DELETE = 1 << 3,
-    P_EXIST = 1 << 4,
-    P_LIST = 1 << 5,
-    P_RESET = 1 << 6,
-    P_PASSWORD = 1 << 7,
-    P_LOCK = 1 << 8,
-    P_UNLOCK = 1 << 9,
-    P_IS_EMPTY = 1 << 10,
-    P_SIGN = 1 << 11,
-    P_VERIFY = 1 << 12,
-    P_GRANT = 1 << 13,
-    P_DUPLICATE = 1 << 14,
-    P_CLEAR_UID = 1 << 15,
-    P_ADD_AUTH = 1 << 16,
-    P_USER_CHANGED = 1 << 17,
-    P_GEN_UNIQUE_ID = 1 << 18,
-};
-
-const char* get_perm_label(perm_t perm);
-
-/**
- * Returns the UID that the callingUid should act as. This is here for
- * legacy support of the WiFi and VPN systems and should be removed
- * when WiFi can operate in its own namespace.
- */
-uid_t get_keystore_euid(uid_t uid);
-
-/**
- * Returns true if the uid/pid/sid has a permission. Checks based on sid if available.
- *
- * sid may be null on older kernels
- */
-bool has_permission(uid_t uid, perm_t perm, pid_t spid, const char* sid);
-
-/**
- * Returns true if the callingUid is allowed to interact in the targetUid's
- * namespace.
- */
-bool is_granted_to(uid_t callingUid, uid_t targetUid);
-
-int configure_selinux();
-
-/*
- * Keystore grants.
- *
- * What are keystore grants?
- *
- * Keystore grants are a mechanism that allows an app to grant the permission to use one of its
- * keys to an other app.
- *
- * Liftime of a grant:
- *
- * A keystore grant is ephemeral in that is never persistently stored. When the keystore process
- * exits, all grants are lost. Also, grants can be explicitly revoked by the granter by invoking
- * the ungrant operation.
- *
- * What happens when a grant is created?
- *
- * The grant operation expects a valid key alias and the uid of the grantee, i.e., the app that
- * shall be allowed to use the key denoted by the alias. It then makes an entry in the grant store
- * which generates a new alias of the form <alias>_KEYSTOREGRANT_<random_grant_no_>. This grant
- * alias is returned to the caller which can pass the new alias to the grantee. For every grantee,
- * the grant store keeps a set of grants, an entry of which holds the following information:
- *  - the owner of the key by uid, aka granter uid,
- *  - the original alias of the granted key, and
- *  - the random grant number.
- * (See "grant_store.h:class Grant")
- *
- * What happens when a grant is used?
- *
- * Upon any keystore operation that expects an alias, the alias and the caller's uid are used
- * to retrieve a key file. If that fails some operations try to retrieve a key file indirectly
- * through a grant. These operations include:
- *  - attestKey
- *  - begin
- *  - exportKey
- *  - get
- *  - getKeyCharacteristics
- *  - del
- *  - exist
- *  - getmtime
- * Operations that DO NOT follow the grant indirection are:
- *  - import
- *  - generate
- *  - grant
- *  - ungrant
- * Especially, the latter two mean that neither can a grantee transitively grant a granted key
- * to a third, nor can they relinquish access to the key or revoke access to the key by a third.
- */
-
-#endif  // KEYSTORE_PERMISSIONS_H_
diff --git a/keystore/tests/Android.bp b/keystore/tests/Android.bp
index 883e020..249cb77 100644
--- a/keystore/tests/Android.bp
+++ b/keystore/tests/Android.bp
@@ -1,5 +1,14 @@
 // Unit test for AuthTokenTable
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
 cc_test {
     cflags: [
         "-Wall",
@@ -9,10 +18,6 @@
     ],
     srcs: [
         "aaid_truncation_test.cpp",
-        "auth_token_table_test.cpp",
-        "auth_token_formatting_test.cpp",
-        "blob_test.cpp",
-        "confirmationui_rate_limiting_test.cpp",
         "verification_token_seralization_test.cpp",
         "gtest_main.cpp",
     ],
@@ -26,7 +31,6 @@
         "libhidlbase",
         "libkeymaster4support",
         "libkeymaster4_1support",
-        "libkeystore_test",
         "liblog",
         "libutils",
     ],
@@ -54,17 +58,14 @@
     ],
     name: "confirmationui_invocation_test",
     static_libs: [
-        "android.hardware.confirmationui@1.0",
         "libbase",
         "libgtest_main",
         "libutils",
         "liblog",
     ],
     shared_libs: [
-        "libbinder",
-        "libkeystore_aidl", // for IKeyStoreService.asInterface()
-        "libkeystore_binder",
-        "libkeystore_parcelables",
+        "android.security.apc-ndk_platform",
+        "libbinder_ndk",
     ],
    sanitize: {
      cfi: false,
diff --git a/keystore/tests/Makefile b/keystore/tests/Makefile
index 2720b0f..b50b94a 100644
--- a/keystore/tests/Makefile
+++ b/keystore/tests/Makefile
@@ -17,7 +17,6 @@
 KEYMASTER=$(BASE)/system/keymaster
 
 INCLUDES=$(foreach dir,$(SUBS),-I $(BASE)/$(dir)/include) \
-	-I $(BASE)/libnativehelper/include/nativehelper \
 	-I $(GTEST) -Iinclude
 
 # Add USE_CLANG=1 to the make command line to build with clang, which has better error
diff --git a/keystore/tests/aaid_truncation_test.cpp b/keystore/tests/aaid_truncation_test.cpp
index 45c54df..fa4d769 100644
--- a/keystore/tests/aaid_truncation_test.cpp
+++ b/keystore/tests/aaid_truncation_test.cpp
@@ -75,15 +75,15 @@
 using ::android::content::pm::Signature;
 using ::android::security::build_attestation_application_id;
 
-std::unique_ptr<KeyAttestationPackageInfo>
+std::optional<KeyAttestationPackageInfo>
 make_package_info_with_signatures(const char* package_name,
                                   KeyAttestationPackageInfo::SignaturesVector signatures) {
-    return std::make_unique<KeyAttestationPackageInfo>(
+    return std::make_optional<KeyAttestationPackageInfo>(
         String16(package_name), 1 /* version code */,
         std::make_shared<KeyAttestationPackageInfo::SignaturesVector>(std::move(signatures)));
 }
 
-std::unique_ptr<KeyAttestationPackageInfo> make_package_info(const char* package_name) {
+std::optional<KeyAttestationPackageInfo> make_package_info(const char* package_name) {
     return make_package_info_with_signatures(package_name,
                                              KeyAttestationPackageInfo::SignaturesVector());
 }
@@ -111,7 +111,7 @@
     KeyAttestationPackageInfo::SignaturesVector signatures;
     // Add 35 signatures which will surely exceed the 1K limit.
     for (size_t i = 0; i < kTooManySignatures; ++i) {
-        signatures.push_back(std::make_unique<Signature>(dummy_sig_data));
+        signatures.push_back(std::make_optional<Signature>(dummy_sig_data));
     }
 
     KeyAttestationApplicationId app_id(
@@ -131,7 +131,7 @@
         KeyAttestationPackageInfo::SignaturesVector signatures;
         // Add a few signatures for each package
         for (int j = 0; j < 3; ++j) {
-            signatures.push_back(std::make_unique<Signature>(dummy_sig_data));
+            signatures.push_back(std::make_optional<Signature>(dummy_sig_data));
         }
         packages.push_back(
             make_package_info_with_signatures(kReasonablePackageName, std::move(signatures)));
diff --git a/keystore/tests/confirmationui_invocation_test.cpp b/keystore/tests/confirmationui_invocation_test.cpp
index f5182b5..7f8a373 100644
--- a/keystore/tests/confirmationui_invocation_test.cpp
+++ b/keystore/tests/confirmationui_invocation_test.cpp
@@ -15,11 +15,10 @@
 ** limitations under the License.
 */
 
-#include <android/hardware/confirmationui/1.0/types.h>
-#include <android/security/BnConfirmationPromptCallback.h>
-#include <android/security/keystore/IKeystoreService.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
+#include <aidl/android/security/apc/BnConfirmationCallback.h>
+#include <aidl/android/security/apc/IProtectedConfirmation.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
 
 #include <gtest/gtest.h>
 
@@ -28,65 +27,50 @@
 #include <tuple>
 #include <vector>
 
-using ConfirmationResponseCode = android::hardware::confirmationui::V1_0::ResponseCode;
-using android::IBinder;
-using android::IServiceManager;
-using android::sp;
-using android::String16;
-using android::security::keystore::IKeystoreService;
-
 using namespace std::literals::chrono_literals;
+namespace apc = ::aidl::android::security::apc;
 
 class ConfirmationListener
-    : public android::security::BnConfirmationPromptCallback,
-      public std::promise<std::tuple<ConfirmationResponseCode, std::vector<uint8_t>>> {
+    : public apc::BnConfirmationCallback,
+      public std::promise<std::tuple<apc::ResponseCode, std::optional<std::vector<uint8_t>>>> {
   public:
     ConfirmationListener() {}
 
-    virtual ::android::binder::Status
-    onConfirmationPromptCompleted(int32_t result,
-                                  const ::std::vector<uint8_t>& dataThatWasConfirmed) override {
-        this->set_value({static_cast<ConfirmationResponseCode>(result), dataThatWasConfirmed});
-        return ::android::binder::Status::ok();
-    }
+    virtual ::ndk::ScopedAStatus
+    onCompleted(::aidl::android::security::apc::ResponseCode result,
+                const std::optional<std::vector<uint8_t>>& dataConfirmed) override {
+        this->set_value({result, dataConfirmed});
+        return ::ndk::ScopedAStatus::ok();
+    };
 };
 
 TEST(ConfirmationInvocationTest, InvokeAndCancel) {
-    android::ProcessState::self()->startThreadPool();
+    ABinderProcess_startThreadPool();
 
-    sp<IServiceManager> sm = android::defaultServiceManager();
-    sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
-    sp<IKeystoreService> service = android::interface_cast<IKeystoreService>(binder);
-    ASSERT_TRUE(service);
+    ::ndk::SpAIBinder apcBinder(AServiceManager_getService("android.security.apc"));
+    auto apcService = apc::IProtectedConfirmation::fromBinder(apcBinder);
+    ASSERT_TRUE(apcService);
 
-    String16 promptText16("Just a little test!");
-    String16 locale16("en");
+    std::string promptText("Just a little test!");
+    std::string locale("en");
     std::vector<uint8_t> extraData{0xaa, 0xff, 0x00, 0x55};
 
-    sp<ConfirmationListener> listener = new ConfirmationListener();
+    auto listener = std::make_shared<ConfirmationListener>();
 
     auto future = listener->get_future();
-    int32_t aidl_return;
 
-    android::binder::Status status = service->presentConfirmationPrompt(
-        listener, promptText16, extraData, locale16, 0, &aidl_return);
-    ASSERT_TRUE(status.isOk()) << "Presenting confirmation prompt failed with binder status '"
-                               << status.toString8().c_str() << "'.\n";
-    ConfirmationResponseCode responseCode = static_cast<ConfirmationResponseCode>(aidl_return);
-    ASSERT_EQ(responseCode, ConfirmationResponseCode::OK)
-        << "Presenting confirmation prompt failed with response code " << aidl_return << ".\n";
+    auto rc = apcService->presentPrompt(listener, promptText, extraData, locale, 0);
+
+    ASSERT_TRUE(rc.isOk());
 
     auto fstatus = future.wait_for(2s);
     EXPECT_EQ(fstatus, std::future_status::timeout);
 
-    status = service->cancelConfirmationPrompt(listener, &aidl_return);
-    ASSERT_TRUE(status.isOk());
-
-    responseCode = static_cast<ConfirmationResponseCode>(aidl_return);
-    ASSERT_EQ(responseCode, ConfirmationResponseCode::OK);
+    rc = apcService->cancelPrompt(listener);
+    ASSERT_TRUE(rc.isOk());
 
     future.wait();
-    auto [rc, dataThatWasConfirmed] = future.get();
+    auto [responseCode, dataThatWasConfirmed] = future.get();
 
-    ASSERT_EQ(rc, ConfirmationResponseCode::Aborted);
+    ASSERT_EQ(responseCode, apc::ResponseCode::ABORTED);
 }
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
new file mode 100644
index 0000000..0ba49ed
--- /dev/null
+++ b/keystore2/Android.bp
@@ -0,0 +1,148 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+rust_defaults {
+    name: "libkeystore2_defaults",
+    crate_name: "keystore2",
+    srcs: ["src/lib.rs"],
+
+    rustlibs: [
+        "android.hardware.security.keymint-V1-rust",
+        "android.hardware.security.secureclock-V1-rust",
+        "android.hardware.security.sharedsecret-V1-rust",
+        "android.os.permissions_aidl-rust",
+        "android.security.apc-rust",
+        "android.security.authorization-rust",
+        "android.security.compat-rust",
+        "android.security.maintenance-rust",
+        "android.security.remoteprovisioning-rust",
+        "android.system.keystore2-V1-rust",
+        "libanyhow",
+        "libbinder_rs",
+        "libcutils_bindgen",
+        "libkeystore2_aaid-rust",
+        "libkeystore2_apc_compat-rust",
+        "libkeystore2_crypto_rust",
+        "libkeystore2_km_compat",
+        "libkeystore2_selinux",
+        "libkeystore2_system_property-rust",
+        "libkeystore2_vintf_rust",
+        "liblazy_static",
+        "liblibc",
+        "liblibsqlite3_sys",
+        "liblog_event_list",
+        "liblog_rust",
+        "librand",
+        "librusqlite",
+        "libstatslog_rust",
+        "libstatslog_rust_header",
+        "libstatspull_rust",
+        "libthiserror",
+    ],
+    shared_libs: [
+        "libcutils",
+    ],
+    features: [
+        "watchdog",
+    ],
+}
+
+rust_library {
+    name: "libkeystore2",
+    defaults: ["libkeystore2_defaults"],
+}
+
+rust_library {
+    name: "libkeystore2_test_utils",
+    crate_name: "keystore2_test_utils",
+    srcs: ["test_utils/lib.rs"],
+    rustlibs: [
+        "liblog_rust",
+        "librand",
+    ],
+}
+
+rust_test {
+    name: "keystore2_test",
+    crate_name: "keystore2",
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    compile_multilib: "first",
+    defaults: ["libkeystore2_defaults"],
+    rustlibs: [
+        "libandroid_logger",
+        "libkeystore2_test_utils",
+        "libnix",
+    ],
+    // The test should always include watchdog.
+    features: [
+        "watchdog",
+    ],
+}
+
+rust_binary {
+    name: "keystore2",
+    srcs: ["src/keystore2_main.rs"],
+    rustlibs: [
+        "libandroid_logger",
+        "libbinder_rs",
+        "libkeystore2",
+        "liblog_rust",
+        "libvpnprofilestore-rust",
+    ],
+    init_rc: ["keystore2.rc"],
+
+    // In S, keystore2 is the only process using dynamically linked Rust from
+    // /system. As a result, the usual savings from sharing libraries don't
+    // apply.
+    // Remove `prefer_rlib: true` once we have several processes, once a space
+    // calculation shows net RAM savings, or once we have automatic variant
+    // selection available in the build system.
+    prefer_rlib: true,
+
+    // TODO(b/187412695)
+    // This is a hack to work around the build system not installing
+    // dynamic dependencies of rlibs to the device. This section should
+    // be removed once that works correctly.
+    shared_libs: [
+        "android.hardware.confirmationui@1.0",
+        "android.hardware.security.sharedsecret-V1-ndk_platform",
+        "android.security.compat-ndk_platform",
+        "libc",
+        "libdl_android",
+        "libdl",
+        "libandroidicu",
+        "libkeymint",
+        "libkeystore2_aaid",
+        "libkeystore2_apc_compat",
+        "libkeystore2_crypto",
+        "libkeystore2_vintf_cpp",
+        "libkm_compat_service",
+        "libkm_compat",
+        "libm",
+        "libstatspull",
+        "libstatssocket",
+    ],
+
+    vintf_fragments: ["android.system.keystore2-service.xml"],
+}
diff --git a/keystore2/TEST_MAPPING b/keystore2/TEST_MAPPING
new file mode 100644
index 0000000..16b6f85
--- /dev/null
+++ b/keystore2/TEST_MAPPING
@@ -0,0 +1,16 @@
+{
+  "presubmit": [
+    {
+      "name": "keystore2_crypto_test"
+    },
+    {
+      "name": "keystore2_crypto_test_rust"
+    },
+    {
+      "name": "keystore2_test"
+    },
+    {
+      "name": "CtsIdentityTestCases"
+    }
+  ]
+}
diff --git a/keystore2/aaid/Android.bp b/keystore2/aaid/Android.bp
new file mode 100644
index 0000000..c04ce51
--- /dev/null
+++ b/keystore2/aaid/Android.bp
@@ -0,0 +1,59 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+cc_library {
+    name: "libkeystore2_aaid",
+    srcs: [
+        "aaid.cpp",
+    ],
+    shared_libs: [
+        "libkeystore-attestation-application-id"
+    ],
+}
+
+rust_bindgen {
+    name: "libkeystore2_aaid_bindgen",
+    wrapper_src: "aaid.hpp",
+    crate_name: "keystore2_aaid_bindgen",
+    source_stem: "bindings",
+
+    bindgen_flags: [
+        "--size_t-is-usize",
+        "--allowlist-function=aaid_keystore_attestation_id",
+        "--allowlist-var=KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE",
+    ],
+}
+
+rust_library {
+    name: "libkeystore2_aaid-rust",
+    crate_name: "keystore2_aaid",
+    srcs: [
+        "lib.rs",
+    ],
+    rustlibs: [
+        "libkeystore2_aaid_bindgen",
+    ],
+    shared_libs: [
+        "libkeystore2_aaid",
+    ],
+}
diff --git a/keystore2/aaid/aaid.cpp b/keystore2/aaid/aaid.cpp
new file mode 100644
index 0000000..fd3faf6
--- /dev/null
+++ b/keystore2/aaid/aaid.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "aaid.hpp"
+
+#include <keystore/keystore_attestation_id.h>
+
+using android::security::gather_attestation_application_id;
+
+uint32_t aaid_keystore_attestation_id(uint32_t uid, uint8_t* aaid, size_t* aaid_size) {
+    static_assert(sizeof(uint32_t) == sizeof(uid_t), "uid_t has unexpected size");
+    static_assert(sizeof(uint32_t) == sizeof(android::status_t), "status_t has unexpected size");
+    static_assert(KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE ==
+                      android::security::KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE,
+                  "KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE sizes don't match.");
+    auto result = gather_attestation_application_id(uid);
+    if (!result.isOk()) {
+        return result.status();
+    }
+    if (result.value().size() > KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE) {
+        return ::android::NO_MEMORY;
+    }
+    if (*aaid_size != KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE) {
+        return ::android::BAD_VALUE;
+    }
+    std::copy(result.value().begin(), result.value().end(), aaid);
+    *aaid_size = result.value().size();
+    return ::android::OK;
+}
diff --git a/keystore2/aaid/aaid.hpp b/keystore2/aaid/aaid.hpp
new file mode 100644
index 0000000..9e2c148
--- /dev/null
+++ b/keystore2/aaid/aaid.hpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 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.
+ */
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h>
+
+/**
+ * This is a redefinition of KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE in
+ * system/security/keystore/keystore_attestation_id.h and must be kept in sync.
+ * There is a static assert in aaid.cpp to assure that they are in sync.
+ * We redefine this here to avoid unnecessary build dependencies for
+ * the rust bindgen target.
+ */
+constexpr const size_t KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE = 1024;
+
+extern "C" {
+    /**
+     * Fills the buffer at aaid with the attestation application id of the app uid.
+     * The buffer must be exactly KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE bytes in size.
+     * *aaid_size is set to the number of bytes written to aaid.
+     *
+     * @param uid the uid of the app to retrieve the aaid for.
+     * @param aaid output buffer for the attestation id.
+     * @param aaid_size must be set to the size of the output buffer, which must be exactly
+     *          KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE bytes in size, by the caller. On success
+     *          it is set to the number of bytes written.
+     * @return OK on success.
+     */
+    uint32_t aaid_keystore_attestation_id(uint32_t uid, uint8_t* aaid, size_t* aaid_size);
+}
diff --git a/keystore2/aaid/lib.rs b/keystore2/aaid/lib.rs
new file mode 100644
index 0000000..3187198
--- /dev/null
+++ b/keystore2/aaid/lib.rs
@@ -0,0 +1,35 @@
+// 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.
+
+//! Rust binding for getting the attestation application id.
+
+use keystore2_aaid_bindgen::{
+    aaid_keystore_attestation_id, KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE,
+};
+
+/// Returns the attestation application id for the given uid or an error code
+/// corresponding to ::android::status_t.
+pub fn get_aaid(uid: u32) -> Result<Vec<u8>, u32> {
+    let mut buffer = [0u8; KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE];
+    let mut size = KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE;
+    // Safety:
+    // aaid_keystore_attestation_id expects a buffer of exactly
+    // KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE bytes and returns the number of bytes written
+    // in the second pointer argument.
+    let status = unsafe { aaid_keystore_attestation_id(uid, buffer.as_mut_ptr(), &mut size) };
+    match status {
+        0 => Ok(buffer[0..size as usize].to_vec()),
+        status => Err(status),
+    }
+}
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
new file mode 100644
index 0000000..06fdb48
--- /dev/null
+++ b/keystore2/aidl/Android.bp
@@ -0,0 +1,167 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+aidl_interface {
+    name: "android.security.attestationmanager",
+    srcs: [ "android/security/attestationmanager/*.aidl", ],
+    imports: [ "android.hardware.security.keymint" ],
+    unstable: true,
+    backend: {
+        java: {
+            platform_apis: true,
+            srcs_available: true,
+        },
+        rust: {
+            enabled: true,
+        },
+        ndk: {
+            enabled: true,
+            apps_enabled: false,
+        }
+    },
+}
+
+aidl_interface {
+    name: "android.security.authorization",
+    srcs: [ "android/security/authorization/*.aidl" ],
+    imports: [
+        "android.hardware.security.keymint",
+        "android.hardware.security.secureclock",
+    ],
+    unstable: true,
+    backend: {
+        java: {
+            platform_apis: true,
+            srcs_available: true,
+        },
+        rust: {
+            enabled: true,
+        },
+        ndk: {
+            enabled: true,
+            apps_enabled: false,
+        }
+    },
+}
+
+aidl_interface {
+    name: "android.security.apc",
+    srcs: [ "android/security/apc/*.aidl" ],
+    unstable: true,
+    backend: {
+        java: {
+            enabled: true,
+            srcs_available: true,
+        },
+        rust: {
+            enabled: true,
+        },
+        ndk: {
+            enabled: true,
+        }
+    },
+}
+
+aidl_interface {
+    name: "android.security.compat",
+    srcs: [ "android/security/compat/*.aidl" ],
+    imports: [
+        "android.hardware.security.keymint",
+        "android.hardware.security.secureclock",
+        "android.hardware.security.sharedsecret",
+    ],
+    unstable: true,
+    backend: {
+        java: {
+            platform_apis: true,
+            srcs_available: true,
+        },
+        rust: {
+            enabled: true,
+        },
+        ndk: {
+            enabled: true,
+            apps_enabled: false,
+        },
+    },
+}
+
+aidl_interface {
+    name: "android.security.remoteprovisioning",
+    srcs: [ "android/security/remoteprovisioning/*.aidl" ],
+    imports: [
+        "android.hardware.security.keymint",
+    ],
+    unstable: true,
+    backend: {
+        java: {
+            platform_apis: true,
+            srcs_available: true,
+        },
+        ndk: {
+            enabled: true,
+            apps_enabled: false,
+        },
+        rust: {
+            enabled: true,
+        },
+    },
+}
+
+aidl_interface {
+    name: "android.security.maintenance",
+    srcs: [ "android/security/maintenance/*.aidl" ],
+    imports: [
+        "android.system.keystore2",
+    ],
+    unstable: true,
+    backend: {
+        java: {
+            platform_apis: true,
+            srcs_available: true,
+        },
+        rust: {
+            enabled: true,
+        },
+        ndk: {
+            enabled: true,
+            apps_enabled: false,
+        }
+    },
+}
+
+aidl_interface {
+    name: "android.security.vpnprofilestore",
+    srcs: [ "android/security/vpnprofilestore/*.aidl" ],
+    unstable: true,
+    backend: {
+        java: {
+            platform_apis: true,
+            srcs_available: true,
+        },
+        rust: {
+            enabled: true,
+        },
+    },
+}
+
diff --git a/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl b/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
new file mode 100644
index 0000000..277b9dd
--- /dev/null
+++ b/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.security.apc;
+
+import android.security.apc.ResponseCode;
+
+/**
+ * This callback interface must be implemented by the client to receive the result of the user
+ * confirmation.
+ * @hide
+ */
+interface IConfirmationCallback {
+    /**
+     * This callback gets called by the implementing service when a pending confirmation prompt
+     * gets finalized.
+     *
+     * @param result
+     *  - ResponseCode.OK On success. In this case dataConfirmed must be non null.
+     *  - ResponseCode.CANCELLED If the user cancelled the prompt. In this case dataConfirmed must
+     *           be null.
+     *  - ResponseCode.ABORTED If the client called IProtectedConfirmation.cancelPrompt() or if the
+     *           prompt was cancelled by the system due to an asynchronous event. In this case
+     *           dataConfirmed must be null.
+     *
+     * @param dataConfirmed This is the message that was confirmed and for which a confirmation
+     *           token is now available in implementing service. A subsequent attempt to sign this
+     *           message with a confirmation bound key will succeed. The message is a CBOR map
+     *           including the prompt text and the extra data.
+     */
+    oneway void onCompleted(in ResponseCode result, in @nullable byte[] dataConfirmed);
+}
diff --git a/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl b/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
new file mode 100644
index 0000000..3162224
--- /dev/null
+++ b/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.security.apc;
+
+import android.security.apc.IConfirmationCallback;
+
+/** @hide */
+interface IProtectedConfirmation {
+
+    /**
+     * When set in the uiOptionFlags parameter of presentPrompt, indicates to the implementation
+     * that it shall use inverted color mode.
+     */
+    const int FLAG_UI_OPTION_INVERTED = 1;
+    /**
+     * When set in the uiOptionFlags parameter of presentPrompt, indicates to the implementation
+     * that it shall use magnified font mode.
+     */
+    const int FLAG_UI_OPTION_MAGNIFIED = 2;
+
+    /**
+     * Present the confirmation prompt. The caller must implement IConfirmationCallback and pass
+     * it to this function as listener.
+     *
+     * @param listener Must implement IConfirmationCallback. Doubles as session identifier when
+     *           passed to cancelPrompt.
+     * @param promptText The text that will be displayed to the user using the protected
+     *           confirmation UI.
+     * @param extraData Extra data, e.g., a nonce, that will be included in the to-be-signed
+     *           message.
+     * @param locale The locale string is used to select the language for the instructions
+     *           displayed by the confirmation prompt.
+     * @param uiOptionFlags Bitwise combination of FLAG_UI_OPTION_* see above.
+     *
+     * Service specific error codes:
+     *  - ResponseCode.OPERATION_PENDING If another prompt is already pending.
+     *  - ResponseCode.SYSTEM_ERROR An unexpected error occurred.
+     */
+    void presentPrompt(in IConfirmationCallback listener, in String promptText,
+            in byte[] extraData, in String locale, in int uiOptionFlags);
+
+    /**
+     * Cancel an ongoing prompt.
+     *
+     * @param listener Must implement IConfirmationCallback, although in this context this binder
+     *            token is only used to identify the session that is to be cancelled.
+     *
+     * Service specific error code:
+     *  - ResponseCode.IGNORED If the listener does not represent an ongoing prompt session.
+     */
+    void cancelPrompt(IConfirmationCallback listener);
+
+    /**
+     * Returns true if the device supports Android Protected Confirmation.
+     */
+    boolean isSupported();
+}
diff --git a/keystore2/aidl/android/security/apc/ResponseCode.aidl b/keystore2/aidl/android/security/apc/ResponseCode.aidl
new file mode 100644
index 0000000..9a3619f
--- /dev/null
+++ b/keystore2/aidl/android/security/apc/ResponseCode.aidl
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+package android.security.apc;
+
+/**
+ * Used as service specific exception code by IProtectedConfirmation and as result
+ * code by IConfirmationCallback
+ * @hide
+ */
+@Backing(type="int")
+enum ResponseCode {
+    /**
+     * The prompt completed successfully with the user confirming the message (callback result).
+     */
+    OK = 0,
+    /**
+     * The user cancelled the TUI (callback result).
+     */
+    CANCELLED = 1,
+    /**
+     * The prompt was aborted (callback result). This may happen when the app cancels the prompt,
+     * or when the prompt was cancelled due to an unexpected asynchronous event, such as an
+     * incoming phone call.
+     */
+    ABORTED = 2,
+    /**
+     * Another prompt cannot be started because another prompt is pending.
+     */
+    OPERATION_PENDING = 3,
+    /**
+     * The request was ignored.
+     */
+    IGNORED = 4,
+    /**
+     * An unexpected system error occurred.
+     */
+    SYSTEM_ERROR = 5,
+    /**
+     * Backend is not implemented.
+     */
+    UNIMPLEMENTED = 6,
+    /**
+     * Permission Denied.
+     */
+    PERMISSION_DENIED = 30,
+}
diff --git a/keystore/binder/android/security/keymaster/OperationResult.aidl b/keystore2/aidl/android/security/attestationmanager/ByteArray.aidl
similarity index 69%
copy from keystore/binder/android/security/keymaster/OperationResult.aidl
copy to keystore2/aidl/android/security/attestationmanager/ByteArray.aidl
index db689d4..dc37b1b 100644
--- a/keystore/binder/android/security/keymaster/OperationResult.aidl
+++ b/keystore2/aidl/android/security/attestationmanager/ByteArray.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 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.
@@ -14,7 +14,12 @@
  * limitations under the License.
  */
 
-package android.security.keymaster;
+package android.security.attestationmanager;
 
-/* @hide */
-parcelable OperationResult cpp_header "keystore/OperationResult.h";
+/**
+ * Simple data holder for a byte array, allowing for multidimensional arrays in AIDL.
+ * @hide
+ */
+parcelable ByteArray {
+    byte[] data;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/attestationmanager/IAttestationManager.aidl b/keystore2/aidl/android/security/attestationmanager/IAttestationManager.aidl
new file mode 100644
index 0000000..e77a21e
--- /dev/null
+++ b/keystore2/aidl/android/security/attestationmanager/IAttestationManager.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.security.attestationmanager;
+
+import android.security.attestationmanager.ByteArray;
+import android.hardware.security.keymint.KeyParameter;
+
+/**
+ * Internal interface for performing device attestation.
+ * @hide
+ */
+interface IAttestationManager {
+    /**
+     * Attest a provided list of device identifiers.
+     *
+     * @return The signed certificate chain, with each individual certificate encoded as a byte
+     *         array.
+     */
+    ByteArray[] attestDevice(
+            in KeyParameter[] deviceIdentifiers, boolean useIndividualAttestation,
+            in byte[] attestationChallenge, int securityLevel);
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/authorization/AuthorizationTokens.aidl b/keystore2/aidl/android/security/authorization/AuthorizationTokens.aidl
new file mode 100644
index 0000000..9061998
--- /dev/null
+++ b/keystore2/aidl/android/security/authorization/AuthorizationTokens.aidl
@@ -0,0 +1,33 @@
+// 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.
+
+package android.security.authorization;
+
+import android.hardware.security.keymint.HardwareAuthToken;
+import android.hardware.security.secureclock.TimeStampToken;
+
+/**
+ * This parcelable is returned by `IKeystoreAuthorization::getAuthTokensForCredStore`.
+ * @hide
+ */
+parcelable AuthorizationTokens {
+    /**
+     * HardwareAuthToken provided by an authenticator.
+     */
+    HardwareAuthToken authToken;
+    /**
+     * TimeStampToken provided by a SecureClock.
+     */
+    TimeStampToken timestampToken;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
new file mode 100644
index 0000000..3f33431
--- /dev/null
+++ b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
@@ -0,0 +1,116 @@
+// 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.
+
+package android.security.authorization;
+
+import android.hardware.security.keymint.HardwareAuthToken;
+import android.security.authorization.LockScreenEvent;
+import android.security.authorization.AuthorizationTokens;
+
+// TODO: mark the interface with @SensitiveData when the annotation is ready (b/176110256).
+
+/**
+ * IKeystoreAuthorization interface exposes the methods for other system components to
+ * provide keystore with the information required to enforce authorizations on key usage.
+ * @hide
+ */
+ @SensitiveData
+interface IKeystoreAuthorization {
+    /**
+     * Allows the Android authenticators to hand over an auth token to Keystore.
+     * Callers require 'AddAuth' permission.
+     * ## Error conditions:
+     * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'AddAuth' permission.
+     * `ResponseCode::SYSTEM_ERROR` - if failed to store the auth token in the database or if failed
+     * to add the auth token to the operation, if it is a per-op auth token.
+     *
+     * @param authToken The auth token created by an authenticator, upon user authentication.
+     */
+    void addAuthToken(in HardwareAuthToken authToken);
+
+    /**
+     * Unlocks the keystore for the given user id.
+     * Callers require 'Unlock' permission.
+     * If a password was set, a password must be given on unlock or the operation fails.
+     *
+     * ## Error conditions:
+     * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'Unlock' permission.
+     * `ResponseCode::SYSTEM_ERROR` - if failed to perform lock/unlock operations due to various
+     * `ResponseCode::VALUE_CORRUPTED` - if the super key can not be decrypted.
+     * `ResponseCode::KEY_NOT_FOUND` - if the super key is not found.
+     *
+     * @lockScreenEvent - Indicates what happened.
+     *                    * LockScreenEvent.UNLOCK if the screen was unlocked.
+     *                    * LockScreenEvent.LOCK if the screen was locked.
+     *
+     * @param userId - Android user id
+     *
+     * @param password - synthetic password derived by the user denoted by the user id
+     *
+     * @param unlockingSids - list of biometric SIDs for this user. This will be null when
+     *                        lockScreenEvent is UNLOCK, but may be non-null when
+     *                        lockScreenEvent is LOCK.
+     *
+     *                        When the device is unlocked, Keystore stores in memory
+     *                        a super-encryption key that protects UNLOCKED_DEVICE_REQUIRED
+     *                        keys; this key is wiped from memory when the device is locked.
+     *
+     *                        If unlockingSids is non-empty on lock, then before the
+     *                        super-encryption key is wiped from memory, a copy of it
+     *                        is stored in memory encrypted with a fresh AES key.
+     *                        This key is then imported into KM, tagged such that it can be
+     *                        used given a valid, recent auth token for any of the
+     *                        unlockingSids.
+     *
+     *                        Then, when the device is unlocked again, if a suitable auth token
+     *                        has been sent to keystore, it is used to recover the
+     *                        super-encryption key, so that UNLOCKED_DEVICE_REQUIRED keys can
+     *                        be used once again.
+     */
+    void onLockScreenEvent(in LockScreenEvent lockScreenEvent, in int userId,
+                           in @nullable byte[] password, in @nullable long[] unlockingSids);
+
+    /**
+     * Allows Credstore to retrieve a HardwareAuthToken and a TimestampToken.
+     * Identity Credential Trusted App can run either in the TEE or in other secure Hardware.
+     * So, credstore always need to retrieve a TimestampToken along with a HardwareAuthToken.
+     *
+     * The passed in |challenge| parameter must always be non-zero.
+     *
+     * The returned TimestampToken will always have its |challenge| field set to
+     * the |challenge| parameter.
+     *
+     * This method looks through auth-tokens cached by keystore which match
+     * the passed-in |secureUserId|.
+     * The most recent matching auth token which has a |challenge| field which matches
+     * the passed-in |challenge| parameter is returned.
+     * In this case the |authTokenMaxAgeMillis| parameter is not used.
+     *
+     * Otherwise, the most recent matching auth token which is younger
+     * than |authTokenMaxAgeMillis| is returned.
+     *
+     * This method is called by credstore (and only credstore).
+     *
+     * The caller requires 'get_auth_token' permission.
+     *
+     * ## Error conditions:
+     * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the 'get_auth_token'
+     *                                     permission.
+     * `ResponseCode::SYSTEM_ERROR` - if failed to obtain an authtoken from the database.
+     * `ResponseCode::NO_AUTH_TOKEN_FOUND` - a matching auth token is not found.
+     * `ResponseCode::INVALID_ARGUMENT` - if the passed-in |challenge| parameter is zero.
+     */
+    AuthorizationTokens getAuthTokensForCredStore(in long challenge, in long secureUserId,
+     in long authTokenMaxAgeMillis);
+}
diff --git a/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl b/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl
new file mode 100644
index 0000000..c7553a2
--- /dev/null
+++ b/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl
@@ -0,0 +1,22 @@
+// 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.
+
+package android.security.authorization;
+
+/** @hide */
+@Backing(type="int")
+enum LockScreenEvent {
+    UNLOCK = 0,
+    LOCK = 1,
+}
diff --git a/keystore2/aidl/android/security/authorization/ResponseCode.aidl b/keystore2/aidl/android/security/authorization/ResponseCode.aidl
new file mode 100644
index 0000000..169dc7b
--- /dev/null
+++ b/keystore2/aidl/android/security/authorization/ResponseCode.aidl
@@ -0,0 +1,57 @@
+// 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.
+
+package android.security.authorization;
+
+/**
+ * Used as exception codes by IKeystoreAuthorization.
+ * @hide
+ */
+@Backing(type="int")
+enum ResponseCode {
+    /**
+     * A matching auth token is not found.
+     */
+    NO_AUTH_TOKEN_FOUND = 1,
+    /**
+     * The matching auth token is expired.
+     */
+    AUTH_TOKEN_EXPIRED = 2,
+    /**
+     * Same as in keystore2/ResponseCode.aidl.
+     * Any unexpected Error such as IO or communication errors.
+     */
+    SYSTEM_ERROR = 4,
+    /**
+     * Same as in keystore2/ResponseCode.aidl.
+     * Indicates that the caller does not have the permissions for the attempted request.
+     */
+    PERMISSION_DENIED = 6,
+    /**
+     * Same as in keystore2/ResponseCode.aidl.
+     * Indicates that the requested key does not exist.
+     */
+    KEY_NOT_FOUND = 7,
+    /**
+     * Same as in keystore2/ResponseCode.aidl.
+     * Indicates that a value being processed is corrupted.
+     */
+    VALUE_CORRUPTED = 8,
+    /**
+     * Same as in keystore2/ResponseCode.aidl.
+     * Indicates that an invalid argument was passed to an API call.
+     */
+    INVALID_ARGUMENT = 20,
+
+ }
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
new file mode 100644
index 0000000..50bfa19
--- /dev/null
+++ b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.security.compat;
+
+import android.hardware.security.keymint.IKeyMintDevice;
+import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.secureclock.ISecureClock;
+import android.hardware.security.sharedsecret.ISharedSecret;
+
+/**
+ * The compatibility service allows Keystore 2.0 to connect to legacy wrapper implementations that
+ * it hosts itself without registering them as a service. Keystore 2.0 would not be allowed to
+ * register a HAL service, so instead it registers this service which it can then connect to.
+ * @hide
+ */
+interface IKeystoreCompatService {
+    /**
+     * Return an implementation of IKeyMintDevice, that it implemented by Keystore 2.0 itself
+     * by means of Keymaster 4.1 or lower.
+     */
+    IKeyMintDevice getKeyMintDevice (SecurityLevel securityLevel);
+
+    /**
+     * Returns an implementation of ISecureClock, that is implemented by Keystore 2.0 itself
+     * by means of Keymaster 4.x.
+     */
+    ISecureClock getSecureClock ();
+
+    /**
+     * Returns an implementation of ISharedSecret, that is implemented by Keystore 2.0 itself
+     * by means of Keymaster 4.x.
+     */
+    ISharedSecret getSharedSecret (SecurityLevel securityLevel);
+}
diff --git a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
new file mode 100644
index 0000000..5f91e79
--- /dev/null
+++ b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
@@ -0,0 +1,126 @@
+// 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.
+
+package android.security.maintenance;
+
+import android.system.keystore2.Domain;
+import android.system.keystore2.KeyDescriptor;
+import android.security.maintenance.UserState;
+
+/**
+ * IKeystoreMaintenance interface exposes the methods for adding/removing users and changing the
+ * user's password.
+ * @hide
+ */
+ @SensitiveData
+interface IKeystoreMaintenance {
+
+    /**
+     * Allows LockSettingsService to inform keystore about adding a new user.
+     * Callers require 'AddUser' permission.
+     *
+     * ## Error conditions:
+     * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'AddUser' permission.
+     * `ResponseCode::SYSTEM_ERROR` - if failed to delete the keys of an existing user with the same
+     * user id.
+     *
+     * @param userId - Android user id
+     */
+    void onUserAdded(in int userId);
+
+    /**
+     * Allows LockSettingsService to inform keystore about removing a user.
+     * Callers require 'RemoveUser' permission.
+     *
+     * ## Error conditions:
+     * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'RemoveUser' permission.
+     * `ResponseCode::SYSTEM_ERROR` - if failed to delete the keys of the user being deleted.
+     *
+     * @param userId - Android user id
+     */
+    void onUserRemoved(in int userId);
+
+    /**
+     * Allows LockSettingsService to inform keystore about password change of a user.
+     * Callers require 'ChangePassword' permission.
+     *
+     * ## Error conditions:
+     * `ResponseCode::PERMISSION_DENIED` - if the callers does not have the 'ChangePassword'
+     *                                     permission.
+     * `ResponseCode::SYSTEM_ERROR` - if failed to delete the super encrypted keys of the user.
+     * `ResponseCode::Locked' -  if the keystore is locked for the given user.
+     *
+     * @param userId - Android user id
+     * @param password - a secret derived from the synthetic password of the user
+     */
+    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.
+     */
+    void clearNamespace(Domain domain, long nspace);
+
+    /**
+     * Allows querying user state, given user id.
+     * Callers require 'GetState' permission.
+     *
+     * ## Error conditions:
+     * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'GetState'
+     *                                     permission.
+     * `ResponseCode::SYSTEM_ERROR` - if an error occurred when querying the user state.
+     *
+     * @param userId - Android user id
+     */
+    UserState getState(in int userId);
+
+    /**
+     * This function notifies the Keymint device of the specified securityLevel that
+     * early boot has ended, so that they no longer allow early boot keys to be used.
+     * ## Error conditions:
+     * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the 'EarlyBootEnded'
+     *                                     permission.
+     * A KeyMint ErrorCode may be returned indicating a backend diagnosed error.
+     */
+     void earlyBootEnded();
+
+    /**
+     * Informs Keystore 2.0 that the an off body event was detected.
+     *
+     * ## Error conditions:
+     * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the `ReportOffBody`
+     *                                     permission.
+     * `ResponseCode::SYSTEM_ERROR` - if an unexpected error occurred.
+     */
+    void onDeviceOffBody();
+
+    /**
+     * Migrate a key from one namespace to another. The caller must have use, grant, and delete
+     * permissions on the source namespace and rebind permissions on the destination namespace.
+     * The source may be specified by Domain::APP, Domain::SELINUX, or Domain::KEY_ID. The target
+     * may be specified by Domain::APP or Domain::SELINUX.
+     *
+     * ## Error conditions:
+     * `ResponseCode::PERMISSION_DENIED` - If the caller lacks any of the required permissions.
+     * `ResponseCode::KEY_NOT_FOUND` - If the source did not exist.
+     * `ResponseCode::INVALID_ARGUMENT` - If the target exists or if any of the above mentioned
+     *                                    requirements for the domain parameter are not met.
+     * `ResponseCode::SYSTEM_ERROR` - An unexpected system error occurred.
+     */
+    void migrateKeyNamespace(in KeyDescriptor source, in KeyDescriptor destination);
+}
diff --git a/keystore2/aidl/android/security/maintenance/UserState.aidl b/keystore2/aidl/android/security/maintenance/UserState.aidl
new file mode 100644
index 0000000..376f4fb
--- /dev/null
+++ b/keystore2/aidl/android/security/maintenance/UserState.aidl
@@ -0,0 +1,23 @@
+// 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.
+
+package android.security.maintenance;
+
+/** @hide */
+@Backing(type="int")
+enum UserState {
+    UNINITIALIZED = 0,
+    LSKF_UNLOCKED = 1,
+    LSKF_LOCKED = 2,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/remoteprovisioning/AttestationPoolStatus.aidl b/keystore2/aidl/android/security/remoteprovisioning/AttestationPoolStatus.aidl
new file mode 100644
index 0000000..3528b42
--- /dev/null
+++ b/keystore2/aidl/android/security/remoteprovisioning/AttestationPoolStatus.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+package android.security.remoteprovisioning;
+
+/**
+ * This parcelable provides information about the state of the attestation key pool.
+ * @hide
+ */
+parcelable AttestationPoolStatus {
+    /**
+     * The number of signed attestation certificate chains which will expire when the date provided
+     * to keystore to check against is reached.
+     */
+    int expiring;
+    /**
+     * The number of signed attestation certificate chains which have not yet been assigned to an
+     * app. This should be less than or equal to signed keys. The remainder of `signed` -
+     * `unassigned` gives the number of signed keys that have been assigned to an app.
+     */
+    int unassigned;
+    /**
+     * The number of signed attestation keys. This should be less than or equal to `total`. The
+     * remainder of `total` - `attested` gives the number of keypairs available to be sent off to
+     * the server for signing.
+     */
+    int attested;
+    /**
+     * The total number of attestation keys.
+     */
+    int total;
+}
diff --git a/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl b/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
new file mode 100644
index 0000000..4a092af
--- /dev/null
+++ b/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.security.remoteprovisioning;
+
+import android.hardware.security.keymint.DeviceInfo;
+import android.hardware.security.keymint.ProtectedData;
+import android.hardware.security.keymint.SecurityLevel;
+import android.security.remoteprovisioning.AttestationPoolStatus;
+
+/**
+ * `IRemoteProvisioning` is the interface provided to use the remote provisioning functionality
+ * provided through KeyStore. The intent is for a higher level system component to use these
+ * functions in order to drive the process through which the device can receive functioning
+ * attestation certificates.
+ *
+ * ## Error conditions
+ * Error conditions are reported as service specific errors.
+ * Positive codes correspond to `android.security.remoteprovisioning.ResponseCode`
+ * and indicate error conditions diagnosed by the Keystore 2.0 service.
+ * TODO: Remote Provisioning HAL error code info
+ *
+ * `ResponseCode::PERMISSION_DENIED` if the caller does not have the permissions
+ * to use the RemoteProvisioning API. This permission is defined under access_vectors in SEPolicy
+ * in the keystore2 class: remotely_provision
+ *
+ * `ResponseCode::SYSTEM_ERROR` for any unexpected errors like IO or IPC failures.
+ *
+ * @hide
+ */
+interface IRemoteProvisioning {
+
+    /**
+     * Returns the status of the attestation key pool in the database.
+     *
+     * @param expiredBy The date as seconds since epoch by which to judge expiration status of
+     *                        certificates.
+     *
+     * @param secLevel The security level to specify which KM instance to get the pool for.
+     *
+     * @return The `AttestationPoolStatus` parcelable contains fields communicating information
+     *                        relevant to making decisions about when to generate and provision
+     *                        more attestation keys.
+     */
+    AttestationPoolStatus getPoolStatus(in long expiredBy, in SecurityLevel secLevel);
+
+    /**
+     * This is the primary entry point for beginning a remote provisioning flow. The caller
+     * specifies how many CSRs should be generated and provides an X25519 ECDH public key along
+     * with a challenge to encrypt privacy sensitive portions of the returned CBOR blob and
+     * guarantee freshness of the request to the certifying third party.
+     *
+     * ## Error conditions
+     * `ResponseCode::NO_UNSIGNED_KEYS` if there are no unsigned keypairs in the database that can
+     *                         be used for the CSRs.
+     *
+     * A RemoteProvisioning HAL response code may indicate backend errors such as failed EEK
+     *                         verification.
+     *
+     * @param testMode Whether or not the TA implementing the Remote Provisioning HAL should accept
+     *                         any EEK (Endpoint Encryption Key), or only one signed by a chain
+     *                         that verifies back to the Root of Trust baked into the TA. True
+     *                         means that any key is accepted.
+     *
+     * @param numCsr How many certificate signing requests should be generated.
+     *
+     * @param eek A chain of certificates terminating in an X25519 public key, the Endpoint
+     *                         Encryption Key.
+     *
+     * @param challenge A challenge to be included and MACed in the returned CBOR blob.
+     *
+     * @param secLevel The security level to specify which KM instance from which to generate a
+     *                         CSR.
+     *
+     * @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, out ProtectedData protectedData, out DeviceInfo deviceInfo);
+
+    /**
+     * This method provides a way for the returned attestation certificate chains to be provisioned
+     * to the attestation key database. When an app requests an attesation key, it will be assigned
+     * one of these certificate chains along with the corresponding private key.
+     *
+     * @param publicKey The raw public key encoded in the leaf certificate.
+     *
+     * @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.
+     *
+     * @param secLevel The security level representing the KM instance containing the key that this
+     *                          chain corresponds to.
+     */
+    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
+     * used for attestation in the `generateCsr` method. The caller should handle spacing out these
+     * requests so as not to jam up the KeyStore work queue.
+     *
+     * @param is_test_mode Instructs the underlying HAL interface to mark the generated key with a
+     *                        tag to indicate that it's for testing.
+     *
+     * @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();
+
+    /**
+     * This method deletes all remotely provisioned attestation keys in the database, regardless
+     * of what state in their life cycle they are in. This is primarily useful to facilitate
+     * testing.
+     *
+     * @return Number of keys deleted
+     */
+    long deleteAllKeys();
+}
diff --git a/keystore2/aidl/android/security/remoteprovisioning/ResponseCode.aidl b/keystore2/aidl/android/security/remoteprovisioning/ResponseCode.aidl
new file mode 100644
index 0000000..c9877db
--- /dev/null
+++ b/keystore2/aidl/android/security/remoteprovisioning/ResponseCode.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package android.security.remoteprovisioning;
+
+@Backing(type="int")
+/** @hide */
+enum ResponseCode {
+    /**
+     * Returned if there are no keys available in the database to be used in a CSR
+     */
+    NO_UNSIGNED_KEYS = 1,
+    /**
+     * The caller has imrproper SELinux permissions to access the Remote Provisioning API.
+     */
+    PERMISSION_DENIED = 2,
+    /**
+     * An unexpected error occurred, likely with IO or IPC.
+     */
+    SYSTEM_ERROR = 3,
+}
diff --git a/keystore2/aidl/android/security/vpnprofilestore/IVpnProfileStore.aidl b/keystore2/aidl/android/security/vpnprofilestore/IVpnProfileStore.aidl
new file mode 100644
index 0000000..8375b7b
--- /dev/null
+++ b/keystore2/aidl/android/security/vpnprofilestore/IVpnProfileStore.aidl
@@ -0,0 +1,57 @@
+/*
+ * 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/android.system.keystore2-service.xml b/keystore2/android.system.keystore2-service.xml
new file mode 100644
index 0000000..6b8d0cb
--- /dev/null
+++ b/keystore2/android.system.keystore2-service.xml
@@ -0,0 +1,9 @@
+<manifest version="1.0" type="framework">
+    <hal format="aidl">
+        <name>android.system.keystore2</name>
+        <interface>
+            <name>IKeystoreService</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/keystore2/apc_compat/Android.bp b/keystore2/apc_compat/Android.bp
new file mode 100644
index 0000000..bf21675
--- /dev/null
+++ b/keystore2/apc_compat/Android.bp
@@ -0,0 +1,65 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+cc_library {
+    name: "libkeystore2_apc_compat",
+    srcs: [
+        "apc_compat.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.confirmationui@1.0",
+        "libbase",
+        "libhidlbase",
+        "libutils",
+    ],
+}
+
+rust_bindgen {
+    name: "libkeystore2_apc_compat_bindgen",
+    wrapper_src: "apc_compat.hpp",
+    crate_name: "keystore2_apc_compat_bindgen",
+    source_stem: "bindings",
+
+    bindgen_flags: [
+        "--allowlist-function=tryGetUserConfirmationService",
+        "--allowlist-function=promptUserConfirmation",
+        "--allowlist-function=abortUserConfirmation",
+        "--allowlist-function=closeUserConfirmationService",
+        "--allowlist-var=INVALID_SERVICE_HANDLE",
+        "--allowlist-var=APC_COMPAT_.*",
+    ],
+}
+
+rust_library {
+    name: "libkeystore2_apc_compat-rust",
+    crate_name: "keystore2_apc_compat",
+    srcs: [
+        "apc_compat.rs",
+    ],
+    rustlibs: [
+        "libkeystore2_apc_compat_bindgen",
+    ],
+    shared_libs: [
+        "libkeystore2_apc_compat",
+    ],
+}
diff --git a/keystore2/apc_compat/apc_compat.cpp b/keystore2/apc_compat/apc_compat.cpp
new file mode 100644
index 0000000..08a8e45
--- /dev/null
+++ b/keystore2/apc_compat/apc_compat.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "apc_compat.hpp"
+#include <android-base/logging.h>
+#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
+#include <hwbinder/IBinder.h>
+
+#include <memory>
+#include <string>
+#include <thread>
+#include <vector>
+
+#define LOG_TAG "keystore2_apc_compat"
+
+namespace keystore2 {
+
+using android::sp;
+using android::hardware::hidl_death_recipient;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Status;
+using android::hardware::confirmationui::V1_0::IConfirmationResultCallback;
+using android::hardware::confirmationui::V1_0::IConfirmationUI;
+using android::hardware::confirmationui::V1_0::ResponseCode;
+using android::hardware::confirmationui::V1_0::UIOption;
+
+static uint32_t responseCode2Compat(ResponseCode rc) {
+    switch (rc) {
+    case ResponseCode::OK:
+        return APC_COMPAT_ERROR_OK;
+    case ResponseCode::Canceled:
+        return APC_COMPAT_ERROR_CANCELLED;
+    case ResponseCode::Aborted:
+        return APC_COMPAT_ERROR_ABORTED;
+    case ResponseCode::OperationPending:
+        return APC_COMPAT_ERROR_OPERATION_PENDING;
+    case ResponseCode::Ignored:
+        return APC_COMPAT_ERROR_IGNORED;
+    case ResponseCode::SystemError:
+    case ResponseCode::Unimplemented:
+    case ResponseCode::Unexpected:
+    case ResponseCode::UIError:
+    case ResponseCode::UIErrorMissingGlyph:
+    case ResponseCode::UIErrorMessageTooLong:
+    case ResponseCode::UIErrorMalformedUTF8Encoding:
+    default:
+        return APC_COMPAT_ERROR_SYSTEM_ERROR;
+    }
+}
+
+class ConfuiCompatSession : public IConfirmationResultCallback, public hidl_death_recipient {
+  public:
+    static sp<ConfuiCompatSession>* tryGetService() {
+        sp<IConfirmationUI> service = IConfirmationUI::tryGetService();
+        if (service) {
+            return new sp(new ConfuiCompatSession(std::move(service)));
+        } else {
+            return nullptr;
+        }
+    }
+
+    uint32_t promptUserConfirmation(ApcCompatCallback callback, const char* prompt_text,
+                                    const uint8_t* extra_data, size_t extra_data_size,
+                                    const char* locale, ApcCompatUiOptions ui_options) {
+        std::string hidl_prompt(prompt_text);
+        std::vector<uint8_t> hidl_extra(extra_data, extra_data + extra_data_size);
+        std::string hidl_locale(locale);
+        std::vector<UIOption> hidl_ui_options;
+        if (ui_options.inverted) {
+            hidl_ui_options.push_back(UIOption::AccessibilityInverted);
+        }
+        if (ui_options.magnified) {
+            hidl_ui_options.push_back(UIOption::AccessibilityMagnified);
+        }
+        auto lock = std::lock_guard(callback_lock_);
+        if (callback_.result != nullptr) {
+            return APC_COMPAT_ERROR_OPERATION_PENDING;
+        }
+        auto err = service_->linkToDeath(sp(this), 0);
+        if (!err.isOk()) {
+            LOG(ERROR) << "Communication error: promptUserConfirmation: "
+                          "Trying to register death recipient: "
+                       << err.description();
+            return APC_COMPAT_ERROR_SYSTEM_ERROR;
+        }
+
+        auto rc = service_->promptUserConfirmation(sp(this), hidl_prompt, hidl_extra, hidl_locale,
+                                                   hidl_ui_options);
+        if (!rc.isOk()) {
+            LOG(ERROR) << "Communication error: promptUserConfirmation: " << rc.description();
+        }
+        if (rc == ResponseCode::OK) {
+            callback_ = callback;
+        }
+        return responseCode2Compat(rc.withDefault(ResponseCode::SystemError));
+    }
+
+    void abort() { service_->abort(); }
+
+    void
+    finalize(ResponseCode responseCode,
+             std::optional<std::reference_wrapper<const hidl_vec<uint8_t>>> dataConfirmed,
+             std::optional<std::reference_wrapper<const hidl_vec<uint8_t>>> confirmationToken) {
+        ApcCompatCallback callback;
+        {
+            auto lock = std::lock_guard(callback_lock_);
+            // Calling the callback consumes the callback data structure. We have to make
+            // sure that it can only be called once.
+            callback = callback_;
+            callback_ = {nullptr, nullptr};
+            // Unlock the callback_lock_ here. It must never be held while calling the callback.
+        }
+
+        if (callback.result != nullptr) {
+            service_->unlinkToDeath(sp(this));
+
+            size_t dataConfirmedSize = 0;
+            const uint8_t* dataConfirmedPtr = nullptr;
+            size_t confirmationTokenSize = 0;
+            const uint8_t* confirmationTokenPtr = nullptr;
+            if (responseCode == ResponseCode::OK) {
+                if (dataConfirmed) {
+                    dataConfirmedPtr = dataConfirmed->get().data();
+                    dataConfirmedSize = dataConfirmed->get().size();
+                }
+                if (dataConfirmed) {
+                    confirmationTokenPtr = confirmationToken->get().data();
+                    confirmationTokenSize = confirmationToken->get().size();
+                }
+            }
+            callback.result(callback.data, responseCode2Compat(responseCode), dataConfirmedPtr,
+                            dataConfirmedSize, confirmationTokenPtr, confirmationTokenSize);
+        }
+    }
+
+    // IConfirmationResultCallback overrides:
+    android::hardware::Return<void> result(ResponseCode responseCode,
+                                           const hidl_vec<uint8_t>& dataConfirmed,
+                                           const hidl_vec<uint8_t>& confirmationToken) override {
+        finalize(responseCode, dataConfirmed, confirmationToken);
+        return Status::ok();
+    };
+
+    void serviceDied(uint64_t /* cookie */,
+                     const ::android::wp<::android::hidl::base::V1_0::IBase>& /* who */) override {
+        finalize(ResponseCode::SystemError, {}, {});
+    }
+
+  private:
+    ConfuiCompatSession(sp<IConfirmationUI> service)
+        : service_(service), callback_{nullptr, nullptr} {}
+    sp<IConfirmationUI> service_;
+
+    // The callback_lock_ protects the callback_ field against concurrent modification.
+    // IMPORTANT: It must never be held while calling the call back.
+    std::mutex callback_lock_;
+    ApcCompatCallback callback_;
+};
+
+}  // namespace keystore2
+
+using namespace keystore2;
+
+ApcCompatServiceHandle tryGetUserConfirmationService() {
+    return reinterpret_cast<ApcCompatServiceHandle>(ConfuiCompatSession::tryGetService());
+}
+
+uint32_t promptUserConfirmation(ApcCompatServiceHandle handle, ApcCompatCallback callback,
+                                const char* prompt_text, const uint8_t* extra_data,
+                                size_t extra_data_size, char const* locale,
+                                ApcCompatUiOptions ui_options) {
+    auto session = reinterpret_cast<sp<ConfuiCompatSession>*>(handle);
+    return (*session)->promptUserConfirmation(callback, prompt_text, extra_data, extra_data_size,
+                                              locale, ui_options);
+}
+
+void abortUserConfirmation(ApcCompatServiceHandle handle) {
+    auto session = reinterpret_cast<sp<ConfuiCompatSession>*>(handle);
+    (*session)->abort();
+}
+
+void closeUserConfirmationService(ApcCompatServiceHandle handle) {
+    // Closing the handle implicitly aborts an ongoing sessions.
+    // Note that a resulting callback is still safely conducted, because we only delete a
+    // StrongPointer below. libhwbinder still owns another StrongPointer to this session.
+    abortUserConfirmation(handle);
+    delete reinterpret_cast<sp<ConfuiCompatSession>*>(handle);
+}
+
+const ApcCompatServiceHandle INVALID_SERVICE_HANDLE = nullptr;
diff --git a/keystore2/apc_compat/apc_compat.hpp b/keystore2/apc_compat/apc_compat.hpp
new file mode 100644
index 0000000..15fa5f4
--- /dev/null
+++ b/keystore2/apc_compat/apc_compat.hpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 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.
+ */
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+using ApcCompatServiceHandle = void*;
+
+#define APC_COMPAT_ERROR_OK 0
+#define APC_COMPAT_ERROR_CANCELLED 1
+#define APC_COMPAT_ERROR_ABORTED 2
+#define APC_COMPAT_ERROR_OPERATION_PENDING 3
+#define APC_COMPAT_ERROR_IGNORED 4
+#define APC_COMPAT_ERROR_SYSTEM_ERROR 5
+
+extern "C" {
+
+extern const ApcCompatServiceHandle INVALID_SERVICE_HANDLE;
+
+/**
+ * This struct holds the ui options for the protected confirmation dialog.
+ */
+struct ApcCompatUiOptions {
+    /**
+     * If set to true inverted color mode is used.
+     */
+    bool inverted;
+    /**
+     * If set to true magnified fonts are used.
+     */
+    bool magnified;
+};
+
+/**
+ * Represents a result callback that is called when a confirmation session was successfully
+ * started.
+ * The field `data` is an opaque callback context handle. It must be passed to the `result`
+ * function.
+ *
+ * IMPORTANT: The life cycle of `data` ends when `result` is called. The callback must not
+ *            be called a second time.
+ *
+ * The callback function `result` has the prototype:
+ * void result(
+ *     void* data,
+ *     uint32_t rc,
+ *     const uint8_t* tbs_message,
+ *     size_t tbs_message_size,
+ *     const uint8_t* confirmation_token,
+ *     size_t confirmation_token_size)
+ *
+ * * data - must be the data field of the structure.
+ * * rc - response code, one of:
+ *      * APC_COMPAT_ERROR_OK - The user confirmed the prompt text.
+ *      * APC_COMPAT_ERROR_CANCELLED - The user rejected the prompt text.
+ *      * APC_COMPAT_ERROR_ABORTED - `abortUserConfirmation` was called.
+ *      * APC_COMPAT_ERROR_SYSTEM_ERROR - An unspecified system error occurred.
+ * * tbs_message(_size) - Pointer to and size of the to-be-signed message. Must
+ *      be NULL and 0 respectively if `rc != APC_COMPAT_ERROR_OK`.
+ * * confirmation_token(_size) - Pointer to and size of the confirmation token. Must
+ *      be NULL and 0 respectively if `rc != APC_COMPAT_ERROR_OK`.
+ */
+struct ApcCompatCallback {
+    void* data;
+    void (*result)(void*, uint32_t, const uint8_t*, size_t, const uint8_t*, size_t);
+};
+
+/**
+ * Attempts to make a connection to the confirmationui HIDL backend.
+ * If a valid service handle is returned it stays valid until
+ * `closeUserConfirmationService` is called.
+ *
+ * @return A valid service handle on success or INVALID_SERVICE_HANDLE
+ *         on failure.
+ */
+ApcCompatServiceHandle tryGetUserConfirmationService();
+
+/**
+ * Attempts to start a protected confirmation session on the given service handle.
+ * The function takes ownership of the callback object (`cb`) IFF APC_COMPAT_ERROR_OK
+ * is returned. The resources referenced by the callback object must stay valid
+ * until the callback is called.
+ *
+ * @param handle A valid service handle as returned by `tryGetUserConfirmationService()`.
+ * @cb A ApcCompatCallback structure that represents a callback function with session data.
+ * @param prompt_text A UTF-8 encoded prompt string.
+ * @param extra_data Free form extra data.
+ * @param extra_data_size size of the extra data buffer in bytes.
+ * @param locale A locale string.
+ * @param ui_options A UI options. See ApcCompatUiOptions above.
+ * @retval APC_COMPAT_ERROR_OK on success.
+ * @retval APC_COMPAT_ERROR_OPERATION_PENDING if another operation was already in progress.
+ * @retval APC_COMPAT_ERROR_SYSTEM_ERROR if an unspecified system error occurred.
+ */
+uint32_t promptUserConfirmation(ApcCompatServiceHandle handle, struct ApcCompatCallback cb,
+                                const char* prompt_text, const uint8_t* extra_data,
+                                size_t extra_data_size, char const* locale,
+                                ApcCompatUiOptions ui_options);
+
+/**
+ * Aborts a running confirmation session or no-op if no session is running.
+ * If a session is running this results in a `result` callback with
+ * `rc == APC_COMPAT_ERROR_ABORTED`. Mind though that the callback can still yield other
+ * results even after this function was called, because it may race with an actual user
+ * response. In any case, there will be only one callback response for each session
+ * successfully started with promptUserConfirmation.
+ *
+ * @param handle A valid session handle as returned by `tryGetUserConfirmationService()`
+ */
+void abortUserConfirmation(ApcCompatServiceHandle handle);
+
+/**
+ * Closes a valid service session as returned by `tryGetUserConfirmationService()`.
+ * If a session is still running it is implicitly aborted. In this case, freeing up of the resources
+ * referenced by the service handle is deferred until the callback has completed.
+ *
+ * @param handle A valid session handle as returned by `tryGetUserConfirmationService()`
+ */
+void closeUserConfirmationService(ApcCompatServiceHandle);
+
+}
diff --git a/keystore2/apc_compat/apc_compat.rs b/keystore2/apc_compat/apc_compat.rs
new file mode 100644
index 0000000..57f8710
--- /dev/null
+++ b/keystore2/apc_compat/apc_compat.rs
@@ -0,0 +1,205 @@
+// 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.
+
+//! This crate implements a safe wrapper around the ConfirmationUI HIDL spec, which
+//! is the backend for Android Protected Confirmation (APC).
+//!
+//! It provides a safe wrapper around a C++ implementation of ConfirmationUI
+//! client.
+
+use keystore2_apc_compat_bindgen::{
+    abortUserConfirmation, closeUserConfirmationService, promptUserConfirmation, size_t,
+    tryGetUserConfirmationService, ApcCompatCallback, ApcCompatServiceHandle,
+};
+pub use keystore2_apc_compat_bindgen::{
+    ApcCompatUiOptions, APC_COMPAT_ERROR_ABORTED, APC_COMPAT_ERROR_CANCELLED,
+    APC_COMPAT_ERROR_IGNORED, APC_COMPAT_ERROR_OK, APC_COMPAT_ERROR_OPERATION_PENDING,
+    APC_COMPAT_ERROR_SYSTEM_ERROR, INVALID_SERVICE_HANDLE,
+};
+use std::{ffi::CString, slice};
+
+/// Safe wrapper around the ConfirmationUI HIDL spec.
+///
+/// # Example
+/// ```
+/// struct Cb();
+/// impl ApcHalCallback for Cb {
+///     fn result(
+///         &self,
+///         rc: u32,
+///         message: Option<&[u8]>,
+///         token: Option<&[u8]>,
+///     ) {
+///         println!("Callback called with rc: {}, message: {}, token: {}", rc, message, token);
+///     }
+/// };
+///
+/// fn prompt() -> Result<(), u32> {
+///     let hal = ApcHal::try_get_service()?;
+///     hal.prompt_user_confirmation(Box::new(Cb()), "Do you agree?", b"extra data", "en", 0)?;
+/// }
+///
+/// ```
+pub struct ApcHal(ApcCompatServiceHandle);
+
+unsafe impl Send for ApcHal {}
+unsafe impl Sync for ApcHal {}
+
+impl Drop for ApcHal {
+    fn drop(&mut self) {
+        // # Safety:
+        // This ends the life cycle of the contained `ApcCompatServiceHandle` owned by this
+        // `ApcHal` object.
+        //
+        // `ApcHal` objects are only created if a valid handle was acquired so self.0 is
+        // always valid when dropped.
+        unsafe {
+            closeUserConfirmationService(self.0);
+        }
+    }
+}
+
+type Callback = dyn FnOnce(u32, Option<&[u8]>, Option<&[u8]>);
+
+extern "C" fn confirmation_result_callback(
+    handle: *mut ::std::os::raw::c_void,
+    rc: u32,
+    tbs_message: *const u8,
+    tbs_message_size: size_t,
+    confirmation_token: *const u8,
+    confirmation_token_size: size_t,
+) {
+    // # Safety:
+    // The C/C++ implementation must pass to us the handle that was created
+    // and assigned to the `ApcCompatCallback::data` field in
+    // `ApcHal::prompt_user_confirmation` below. Also we consume the handle,
+    // by letting `hal_cb` go out of scope with this function call. So
+    // the C/C++ implementation must assure that each `ApcCompatCallback` is only used once.
+    let hal_cb: Box<Box<Callback>> = unsafe { Box::from_raw(handle as *mut Box<Callback>) };
+    let tbs_message = match (tbs_message.is_null(), tbs_message_size) {
+        (true, _) | (_, 0) => None,
+        (false, s) => Some(
+            // # Safety:
+            // If the pointer and size is not nullptr and not 0 respectively, the C/C++
+            // implementation must pass a valid pointer to an allocation of at least size bytes,
+            // and the pointer must be valid until this function returns.
+            unsafe { slice::from_raw_parts(tbs_message, s as usize) },
+        ),
+    };
+    let confirmation_token = match (confirmation_token.is_null(), confirmation_token_size) {
+        (true, _) | (_, 0) => None,
+        (false, s) => Some(
+            // # Safety:
+            // If the pointer and size is not nullptr and not 0 respectively, the C/C++
+            // implementation must pass a valid pointer to an allocation of at least size bytes,
+            // and the pointer must be valid until this function returns.
+            unsafe { slice::from_raw_parts(confirmation_token, s as usize) },
+        ),
+    };
+    hal_cb(rc, tbs_message, confirmation_token)
+}
+
+impl ApcHal {
+    /// Attempts to connect to the APC (confirmationui) backend. On success, it returns an
+    /// initialized `ApcHal` object.
+    pub fn try_get_service() -> Option<Self> {
+        // # Safety:
+        // `tryGetUserConfirmationService` returns a valid handle or INVALID_SERVICE_HANDLE.
+        // On success, `ApcHal` takes ownership of this handle and frees it with
+        // `closeUserConfirmationService` when dropped.
+        let handle = unsafe { tryGetUserConfirmationService() };
+        match handle {
+            h if h == unsafe { INVALID_SERVICE_HANDLE } => None,
+            h => Some(Self(h)),
+        }
+    }
+
+    /// Attempts to start a confirmation prompt. The given callback is consumed, and it is
+    /// guaranteed to be called eventually IFF this function returns `APC_COMPAT_ERROR_OK`.
+    ///
+    /// The callback has the following arguments:
+    /// rc: u32 - The reason for the termination which takes one of the values.
+    ///       * `APC_COMPAT_ERROR_OK` - The user confirmed the prompted message.
+    ///       * `APC_COMPAT_ERROR_CANCELLED` - The user rejected the prompted message.
+    ///       * `APC_COMPAT_ERROR_ABORTED` - The prompt was aborted either because the client
+    ///          aborted. the session or an asynchronous system event occurred that ended the
+    ///          prompt prematurely.
+    ///       * `APC_COMPAT_ERROR_SYSTEMERROR` - An unspecified system error occurred. Logs may
+    ///          have more information.
+    ///
+    /// data_confirmed: Option<&[u8]> and
+    /// confirmation_token: Option<&[u8]> hold the confirmed message and the confirmation token
+    /// respectively. They must be `Some()` if `rc == APC_COMPAT_ERROR_OK` and `None` otherwise.
+    ///
+    /// `cb` does not get called if this function returns an error.
+    /// (Thus the allow(unused_must_use))
+    #[allow(unused_must_use)]
+    pub fn prompt_user_confirmation<F>(
+        &self,
+        prompt_text: &str,
+        extra_data: &[u8],
+        locale: &str,
+        ui_opts: ApcCompatUiOptions,
+        cb: F,
+    ) -> Result<(), u32>
+    where
+        F: FnOnce(u32, Option<&[u8]>, Option<&[u8]>) + 'static,
+    {
+        let cb_data_ptr = Box::into_raw(Box::new(Box::new(cb) as Box<Callback>));
+        let cb = ApcCompatCallback {
+            data: cb_data_ptr as *mut std::ffi::c_void,
+            result: Some(confirmation_result_callback),
+        };
+        let prompt_text = CString::new(prompt_text).unwrap();
+        let locale = CString::new(locale).unwrap();
+        // # Safety:
+        // The `ApcCompatCallback` object (`cb`) is passed to the callee by value, and with it
+        // ownership of the `data` field pointer. The data pointer is guaranteed to be valid
+        // until the C/C++ implementation calls the callback. Calling the callback consumes
+        // the data pointer. The C/C++ implementation must not access it after calling the
+        // callback and it must not call the callback a second time.
+        //
+        // The C/C++ must make no assumptions about the life time of the other parameters after
+        // the function returns.
+        let rc = unsafe {
+            promptUserConfirmation(
+                self.0,
+                cb,
+                prompt_text.as_ptr(),
+                extra_data.as_ptr(),
+                extra_data.len() as size_t,
+                locale.as_ptr(),
+                ui_opts,
+            )
+        };
+        match rc {
+            APC_COMPAT_ERROR_OK => Ok(()),
+            rc => {
+                // # Safety:
+                // If promptUserConfirmation does not succeed, it must not take ownership of the
+                // callback, so we must destroy it.
+                unsafe { Box::from_raw(cb_data_ptr) };
+                Err(rc)
+            }
+        }
+    }
+
+    /// Aborts a running confirmation session, or no-op if none is running.
+    pub fn abort(&self) {
+        // # Safety:
+        // It is always safe to call `abortUserConfirmation`, because spurious calls are ignored.
+        // The handle argument must be valid, but this is an invariant of `ApcHal`.
+        unsafe { abortUserConfirmation(self.0) }
+    }
+}
diff --git a/keystore2/keystore2.rc b/keystore2/keystore2.rc
new file mode 100644
index 0000000..82bf3b8
--- /dev/null
+++ b/keystore2/keystore2.rc
@@ -0,0 +1,13 @@
+# Start the keystore2 service.
+# Keystore 2.0 changes its working directory to the first positional
+# command line option, i.e., /data/misc/keystore, where it stores its
+# database.
+# Keystore shall run as user keystore and groups keystore, readproc, and log.
+#
+# See system/core/init/README.md for information on the init.rc language.
+
+service keystore2 /system/bin/keystore2 /data/misc/keystore
+    class early_hal
+    user keystore
+    group keystore readproc log
+    writepid /dev/cpuset/foreground/tasks
diff --git a/keystore2/selinux/Android.bp b/keystore2/selinux/Android.bp
new file mode 100644
index 0000000..18063d3
--- /dev/null
+++ b/keystore2/selinux/Android.bp
@@ -0,0 +1,63 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+rust_library {
+    name: "libkeystore2_selinux",
+    crate_name: "keystore2_selinux",
+    srcs: [
+        "src/lib.rs",
+    ],
+
+    shared_libs: [
+        "libselinux",
+    ],
+
+    rustlibs: [
+        "libanyhow",
+        "liblog_rust",
+        "libselinux_bindgen",
+        "libthiserror",
+    ],
+}
+
+rust_test {
+    name: "keystore2_selinux_test",
+    srcs: [
+        "src/lib.rs",
+    ],
+    crate_name: "keystore2_selinux_test",
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+
+    shared_libs: [
+        "libselinux",
+    ],
+
+    rustlibs: [
+        "libandroid_logger",
+        "libanyhow",
+        "liblog_rust",
+        "libselinux_bindgen",
+        "libthiserror",
+    ],
+}
diff --git a/keystore2/selinux/TEST_MAPPING b/keystore2/selinux/TEST_MAPPING
new file mode 100644
index 0000000..0e68257
--- /dev/null
+++ b/keystore2/selinux/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "keystore2_selinux_test"
+    }
+  ]
+}
diff --git a/keystore2/selinux/src/lib.rs b/keystore2/selinux/src/lib.rs
new file mode 100644
index 0000000..cc707e7
--- /dev/null
+++ b/keystore2/selinux/src/lib.rs
@@ -0,0 +1,470 @@
+// 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.
+
+//! This crate provides some safe wrappers around the libselinux API. It is currently limited
+//! to the API surface that Keystore 2.0 requires to perform permission checks against
+//! the SEPolicy. Notably, it provides wrappers for:
+//!  * getcon
+//!  * selinux_check_access
+//!  * selabel_lookup for the keystore2_key backend.
+//! And it provides an owning wrapper around context strings `Context`.
+
+use std::ffi::{CStr, CString};
+use std::fmt;
+use std::io;
+use std::marker::{Send, Sync};
+pub use std::ops::Deref;
+use std::os::raw::c_char;
+use std::ptr;
+use std::sync;
+
+use selinux_bindgen as selinux;
+
+use anyhow::Context as AnyhowContext;
+use anyhow::{anyhow, Result};
+
+use selinux::SELABEL_CTX_ANDROID_KEYSTORE2_KEY;
+use selinux::SELINUX_CB_LOG;
+
+pub use selinux::pid_t;
+
+static SELINUX_LOG_INIT: sync::Once = sync::Once::new();
+
+fn redirect_selinux_logs_to_logcat() {
+    // `selinux_set_callback` assigns the static lifetime function pointer
+    // `selinux_log_callback` to a static lifetime variable.
+    let cb = selinux::selinux_callback { func_log: Some(selinux::selinux_log_callback) };
+    unsafe {
+        selinux::selinux_set_callback(SELINUX_CB_LOG as i32, cb);
+    }
+}
+
+// This function must be called before any entry point into lib selinux.
+// Or leave a comment reasoning why calling this macro is not necessary
+// for a given entry point.
+fn init_logger_once() {
+    SELINUX_LOG_INIT.call_once(redirect_selinux_logs_to_logcat)
+}
+
+/// Selinux Error code.
+#[derive(thiserror::Error, Debug, PartialEq)]
+pub enum Error {
+    /// Indicates that an access check yielded no access.
+    #[error("Permission Denied")]
+    PermissionDenied,
+    /// Indicates an unexpected system error. Nested string provides some details.
+    #[error("Selinux SystemError: {0}")]
+    SystemError(String),
+}
+
+impl Error {
+    /// Constructs a `PermissionDenied` error.
+    pub fn perm() -> Self {
+        Error::PermissionDenied
+    }
+    fn sys<T: Into<String>>(s: T) -> Self {
+        Error::SystemError(s.into())
+    }
+}
+
+/// Context represents an SELinux context string. It can take ownership of a raw
+/// s-string as allocated by `getcon` or `selabel_lookup`. In this case it uses
+/// `freecon` to free the resources when dropped. In its second variant it stores
+/// an `std::ffi::CString` that can be initialized from a Rust string slice.
+#[derive(Debug)]
+pub enum Context {
+    /// Wraps a raw context c-string as returned by libselinux.
+    Raw(*mut ::std::os::raw::c_char),
+    /// Stores a context string as `std::ffi::CString`.
+    CString(CString),
+}
+
+impl PartialEq for Context {
+    fn eq(&self, other: &Self) -> bool {
+        // We dereference both and thereby delegate the comparison
+        // to `CStr`'s implementation of `PartialEq`.
+        **self == **other
+    }
+}
+
+impl Eq for Context {}
+
+impl fmt::Display for Context {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", (**self).to_str().unwrap_or("Invalid context"))
+    }
+}
+
+impl Drop for Context {
+    fn drop(&mut self) {
+        if let Self::Raw(p) = self {
+            // No need to initialize the logger here, because
+            // `freecon` cannot run unless `Backend::lookup` or `getcon`
+            // has run.
+            unsafe { selinux::freecon(*p) };
+        }
+    }
+}
+
+impl Deref for Context {
+    type Target = CStr;
+
+    fn deref(&self) -> &Self::Target {
+        match self {
+            Self::Raw(p) => unsafe { CStr::from_ptr(*p) },
+            Self::CString(cstr) => &cstr,
+        }
+    }
+}
+
+impl Context {
+    /// Initializes the `Context::CString` variant from a Rust string slice.
+    pub fn new(con: &str) -> Result<Self> {
+        Ok(Self::CString(
+            CString::new(con)
+                .with_context(|| format!("Failed to create Context with \"{}\"", con))?,
+        ))
+    }
+}
+
+/// The backend trait provides a uniform interface to all libselinux context backends.
+/// Currently, we only implement the KeystoreKeyBackend though.
+pub trait Backend {
+    /// Implementers use libselinux `selabel_lookup` to lookup the context for the given `key`.
+    fn lookup(&self, key: &str) -> Result<Context>;
+}
+
+/// Keystore key backend takes onwnership of the SELinux context handle returned by
+/// `selinux_android_keystore2_key_context_handle` and uses `selabel_close` to free
+/// the handle when dropped.
+/// It implements `Backend` to provide keystore_key label lookup functionality.
+pub struct KeystoreKeyBackend {
+    handle: *mut selinux::selabel_handle,
+}
+
+// KeystoreKeyBackend is Sync because selabel_lookup is thread safe.
+unsafe impl Sync for KeystoreKeyBackend {}
+unsafe impl Send for KeystoreKeyBackend {}
+
+impl KeystoreKeyBackend {
+    const BACKEND_TYPE: i32 = SELABEL_CTX_ANDROID_KEYSTORE2_KEY as i32;
+
+    /// Creates a new instance representing an SELinux context handle as returned by
+    /// `selinux_android_keystore2_key_context_handle`.
+    pub fn new() -> Result<Self> {
+        init_logger_once();
+        let handle = unsafe { selinux::selinux_android_keystore2_key_context_handle() };
+        if handle.is_null() {
+            return Err(anyhow!(Error::sys("Failed to open KeystoreKeyBackend")));
+        }
+        Ok(KeystoreKeyBackend { handle })
+    }
+}
+
+impl Drop for KeystoreKeyBackend {
+    fn drop(&mut self) {
+        // No need to initialize the logger here because it cannot be called unless
+        // KeystoreKeyBackend::new has run.
+        unsafe { selinux::selabel_close(self.handle) };
+    }
+}
+
+// Because KeystoreKeyBackend is Sync and Send, member function must never call
+// non thread safe libselinux functions. As of this writing no non thread safe
+// functions exist that could be called on a label backend handle.
+impl Backend for KeystoreKeyBackend {
+    fn lookup(&self, key: &str) -> Result<Context> {
+        let mut con: *mut c_char = ptr::null_mut();
+        let c_key = CString::new(key).with_context(|| {
+            format!("selabel_lookup: Failed to convert key \"{}\" to CString.", key)
+        })?;
+        match unsafe {
+            // No need to initialize the logger here because it cannot run unless
+            // KeystoreKeyBackend::new has run.
+            selinux::selabel_lookup(self.handle, &mut con, c_key.as_ptr(), Self::BACKEND_TYPE)
+        } {
+            0 => {
+                if !con.is_null() {
+                    Ok(Context::Raw(con))
+                } else {
+                    Err(anyhow!(Error::sys(format!(
+                        "selabel_lookup returned a NULL context for key \"{}\"",
+                        key
+                    ))))
+                }
+            }
+            _ => Err(anyhow!(io::Error::last_os_error()))
+                .with_context(|| format!("selabel_lookup failed for key \"{}\"", key)),
+        }
+    }
+}
+
+/// Safe wrapper around libselinux `getcon`. It initializes the `Context::Raw` variant of the
+/// returned `Context`.
+///
+/// ## Return
+///  * Ok(Context::Raw()) if successful.
+///  * Err(Error::sys()) if getcon succeeded but returned a NULL pointer.
+///  * Err(io::Error::last_os_error()) if getcon failed.
+pub fn getcon() -> Result<Context> {
+    init_logger_once();
+    let mut con: *mut c_char = ptr::null_mut();
+    match unsafe { selinux::getcon(&mut con) } {
+        0 => {
+            if !con.is_null() {
+                Ok(Context::Raw(con))
+            } else {
+                Err(anyhow!(Error::sys("getcon returned a NULL context")))
+            }
+        }
+        _ => Err(anyhow!(io::Error::last_os_error())).context("getcon failed"),
+    }
+}
+
+/// Safe wrapper around libselinux `getpidcon`. It initializes the `Context::Raw` variant of the
+/// returned `Context`.
+///
+/// ## Return
+///  * Ok(Context::Raw()) if successful.
+///  * Err(Error::sys()) if getpidcon succeeded but returned a NULL pointer.
+///  * Err(io::Error::last_os_error()) if getpidcon failed.
+pub fn getpidcon(pid: selinux::pid_t) -> Result<Context> {
+    init_logger_once();
+    let mut con: *mut c_char = ptr::null_mut();
+    match unsafe { selinux::getpidcon(pid, &mut con) } {
+        0 => {
+            if !con.is_null() {
+                Ok(Context::Raw(con))
+            } else {
+                Err(anyhow!(Error::sys(format!(
+                    "getpidcon returned a NULL context for pid {}",
+                    pid
+                ))))
+            }
+        }
+        _ => Err(anyhow!(io::Error::last_os_error()))
+            .context(format!("getpidcon failed for pid {}", pid)),
+    }
+}
+
+/// Safe wrapper around selinux_check_access.
+///
+/// ## Return
+///  * Ok(()) iff the requested access was granted.
+///  * Err(anyhow!(Error::perm()))) if the permission was denied.
+///  * Err(anyhow!(ioError::last_os_error())) if any other error occurred while performing
+///            the access check.
+pub fn check_access(source: &CStr, target: &CStr, tclass: &str, perm: &str) -> Result<()> {
+    init_logger_once();
+    let c_tclass = CString::new(tclass).with_context(|| {
+        format!("check_access: Failed to convert tclass \"{}\" to CString.", tclass)
+    })?;
+    let c_perm = CString::new(perm).with_context(|| {
+        format!("check_access: Failed to convert perm \"{}\" to CString.", perm)
+    })?;
+
+    match unsafe {
+        selinux::selinux_check_access(
+            source.as_ptr(),
+            target.as_ptr(),
+            c_tclass.as_ptr(),
+            c_perm.as_ptr(),
+            ptr::null_mut(),
+        )
+    } {
+        0 => Ok(()),
+        _ => {
+            let e = io::Error::last_os_error();
+            match e.kind() {
+                io::ErrorKind::PermissionDenied => Err(anyhow!(Error::perm())),
+                _ => Err(anyhow!(e)),
+            }
+            .with_context(|| {
+                format!(
+                    concat!(
+                        "check_access: Failed with sctx: {:?} tctx: {:?}",
+                        " with target class: \"{}\" perm: \"{}\""
+                    ),
+                    source, target, tclass, perm
+                )
+            })
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use anyhow::Result;
+
+    /// The su_key namespace as defined in su.te and keystore_key_contexts of the
+    /// SePolicy (system/sepolicy).
+    static SU_KEY_NAMESPACE: &str = "0";
+    /// The shell_key namespace as defined in shell.te and keystore_key_contexts of the
+    /// SePolicy (system/sepolicy).
+    static SHELL_KEY_NAMESPACE: &str = "1";
+
+    fn check_context() -> Result<(Context, &'static str, bool)> {
+        let context = getcon()?;
+        match context.to_str().unwrap() {
+            "u:r:su:s0" => Ok((context, SU_KEY_NAMESPACE, true)),
+            "u:r:shell:s0" => Ok((context, SHELL_KEY_NAMESPACE, false)),
+            c => Err(anyhow!(format!(
+                "This test must be run as \"su\" or \"shell\". Current context: \"{}\"",
+                c
+            ))),
+        }
+    }
+
+    #[test]
+    fn test_getcon() -> Result<()> {
+        check_context()?;
+        Ok(())
+    }
+
+    #[test]
+    fn test_label_lookup() -> Result<()> {
+        let (_context, namespace, is_su) = check_context()?;
+        let backend = crate::KeystoreKeyBackend::new()?;
+        let context = backend.lookup(namespace)?;
+        if is_su {
+            assert_eq!(context.to_str(), Ok("u:object_r:su_key:s0"));
+        } else {
+            assert_eq!(context.to_str(), Ok("u:object_r:shell_key:s0"));
+        }
+        Ok(())
+    }
+
+    #[test]
+    fn context_from_string() -> Result<()> {
+        let tctx = Context::new("u:object_r:keystore:s0").unwrap();
+        let sctx = Context::new("u:r:system_server:s0").unwrap();
+        check_access(&sctx, &tctx, "keystore2_key", "use")?;
+        Ok(())
+    }
+
+    mod perm {
+        use super::super::*;
+        use super::*;
+        use anyhow::Result;
+
+        /// check_key_perm(perm, privileged, priv_domain)
+        /// `perm` is a permission of the keystore2_key class and `privileged` is a boolean
+        /// indicating whether the permission is considered privileged.
+        /// Privileged permissions are expected to be denied to `shell` users but granted
+        /// to the given priv_domain.
+        macro_rules! check_key_perm {
+            // "use" is a keyword and cannot be used as an identifier, but we must keep
+            // the permission string intact. So we map the identifier name on use_ while using
+            // the permission string "use". In all other cases we can simply use the stringified
+            // identifier as permission string.
+            (use, $privileged:expr) => {
+                check_key_perm!(use_, $privileged, "use");
+            };
+            ($perm:ident, $privileged:expr) => {
+                check_key_perm!($perm, $privileged, stringify!($perm));
+            };
+            ($perm:ident, $privileged:expr, $p_str:expr) => {
+                #[test]
+                fn $perm() -> Result<()> {
+                    android_logger::init_once(
+                        android_logger::Config::default()
+                            .with_tag("keystore_selinux_tests")
+                            .with_min_level(log::Level::Debug),
+                    );
+                    let scontext = Context::new("u:r:shell:s0")?;
+                    let backend = KeystoreKeyBackend::new()?;
+                    let tcontext = backend.lookup(SHELL_KEY_NAMESPACE)?;
+
+                    if $privileged {
+                        assert_eq!(
+                            Some(&Error::perm()),
+                            check_access(
+                                &scontext,
+                                &tcontext,
+                                "keystore2_key",
+                                $p_str
+                            )
+                            .err()
+                            .unwrap()
+                            .root_cause()
+                            .downcast_ref::<Error>()
+                        );
+                    } else {
+                        assert!(check_access(
+                            &scontext,
+                            &tcontext,
+                            "keystore2_key",
+                            $p_str
+                        )
+                        .is_ok());
+                    }
+                    Ok(())
+                }
+            };
+        }
+
+        check_key_perm!(manage_blob, true);
+        check_key_perm!(delete, false);
+        check_key_perm!(use_dev_id, true);
+        check_key_perm!(req_forced_op, true);
+        check_key_perm!(gen_unique_id, true);
+        check_key_perm!(grant, true);
+        check_key_perm!(get_info, false);
+        check_key_perm!(rebind, false);
+        check_key_perm!(update, false);
+        check_key_perm!(use, false);
+
+        macro_rules! check_keystore_perm {
+            ($perm:ident) => {
+                #[test]
+                fn $perm() -> Result<()> {
+                    let ks_context = Context::new("u:object_r:keystore:s0")?;
+                    let priv_context = Context::new("u:r:system_server:s0")?;
+                    let unpriv_context = Context::new("u:r:shell:s0")?;
+                    assert!(check_access(
+                        &priv_context,
+                        &ks_context,
+                        "keystore2",
+                        stringify!($perm)
+                    )
+                    .is_ok());
+                    assert_eq!(
+                        Some(&Error::perm()),
+                        check_access(&unpriv_context, &ks_context, "keystore2", stringify!($perm))
+                            .err()
+                            .unwrap()
+                            .root_cause()
+                            .downcast_ref::<Error>()
+                    );
+                    Ok(())
+                }
+            };
+        }
+
+        check_keystore_perm!(add_auth);
+        check_keystore_perm!(clear_ns);
+        check_keystore_perm!(lock);
+        check_keystore_perm!(reset);
+        check_keystore_perm!(unlock);
+    }
+
+    #[test]
+    fn test_getpidcon() {
+        // Check that `getpidcon` of our pid is equal to what `getcon` returns.
+        // And by using `unwrap` we make sure that both also have to return successfully
+        // fully to pass the test.
+        assert_eq!(getpidcon(std::process::id() as i32).unwrap(), getcon().unwrap());
+    }
+}
diff --git a/keystore2/src/apc.rs b/keystore2/src/apc.rs
new file mode 100644
index 0000000..0096686
--- /dev/null
+++ b/keystore2/src/apc.rs
@@ -0,0 +1,384 @@
+// 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.
+
+//! This module implements the Android Protected Confirmation (APC) service as defined
+//! in the android.security.apc AIDL spec.
+
+use std::{
+    cmp::PartialEq,
+    collections::HashMap,
+    sync::{mpsc::Sender, Arc, Mutex},
+};
+
+use crate::utils::{compat_2_response_code, ui_opts_2_compat, watchdog as wd};
+use android_security_apc::aidl::android::security::apc::{
+    IConfirmationCallback::IConfirmationCallback,
+    IProtectedConfirmation::{BnProtectedConfirmation, IProtectedConfirmation},
+    ResponseCode::ResponseCode,
+};
+use android_security_apc::binder::{
+    BinderFeatures, ExceptionCode, Interface, Result as BinderResult, SpIBinder,
+    Status as BinderStatus, Strong, ThreadState,
+};
+use anyhow::{Context, Result};
+use keystore2_apc_compat::ApcHal;
+use keystore2_selinux as selinux;
+use std::time::{Duration, Instant};
+
+/// This is the main APC error type, it wraps binder exceptions and the
+/// APC ResponseCode.
+#[derive(Debug, thiserror::Error, PartialEq)]
+pub enum Error {
+    /// Wraps an Android Protected Confirmation (APC) response code as defined by the
+    /// android.security.apc AIDL interface specification.
+    #[error("Error::Rc({0:?})")]
+    Rc(ResponseCode),
+    /// 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::Rc(ResponseCode::SYSTEM_ERROR)`
+    pub fn sys() -> Self {
+        Error::Rc(ResponseCode::SYSTEM_ERROR)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::OPERATION_PENDING)`
+    pub fn pending() -> Self {
+        Error::Rc(ResponseCode::OPERATION_PENDING)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::CANCELLED)`
+    pub fn cancelled() -> Self {
+        Error::Rc(ResponseCode::CANCELLED)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::ABORTED)`
+    pub fn aborted() -> Self {
+        Error::Rc(ResponseCode::ABORTED)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::IGNORED)`
+    pub fn ignored() -> Self {
+        Error::Rc(ResponseCode::IGNORED)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::UNIMPLEMENTED)`
+    pub fn unimplemented() -> Self {
+        Error::Rc(ResponseCode::UNIMPLEMENTED)
+    }
+}
+
+/// This function should be used by confirmation service calls to translate error conditions
+/// into service specific exceptions.
+///
+/// All error conditions get logged by this function.
+///
+/// `Error::Rc(x)` variants get mapped onto a service specific error code of `x`.
+/// `selinux::Error::perm()` is mapped on `ResponseCode::PERMISSION_DENIED`.
+///
+/// All non `Error` error conditions get mapped onto ResponseCode::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).
+pub 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::Rc(rcode)) => rcode.0,
+                Some(Error::Binder(_, _)) => ResponseCode::SYSTEM_ERROR.0,
+                None => match root_cause.downcast_ref::<selinux::Error>() {
+                    Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
+                    _ => ResponseCode::SYSTEM_ERROR.0,
+                },
+            };
+            Err(BinderStatus::new_service_specific_error(rc, None))
+        },
+        handle_ok,
+    )
+}
+
+/// Rate info records how many failed attempts a client has made to display a protected
+/// confirmation prompt. Clients are penalized for attempts that get declined by the user
+/// or attempts that get aborted by the client itself.
+///
+/// After the third failed attempt the client has to cool down for 30 seconds before it
+/// it can retry. After the sixth failed attempt, the time doubles with every failed attempt
+/// until it goes into saturation at 24h.
+///
+/// A successful user prompt resets the counter.
+#[derive(Debug, Clone)]
+struct RateInfo {
+    counter: u32,
+    timestamp: Instant,
+}
+
+impl RateInfo {
+    const ONE_DAY: Duration = Duration::from_secs(60u64 * 60u64 * 24u64);
+
+    fn get_remaining_back_off(&self) -> Option<Duration> {
+        let back_off = match self.counter {
+            // The first three attempts come without penalty.
+            0..=2 => return None,
+            // The next three attempts are are penalized with 30 seconds back off time.
+            3..=5 => Duration::from_secs(30),
+            // After that we double the back off time the with every additional attempt
+            // until we reach 1024m (~17h).
+            6..=16 => Duration::from_secs(60)
+                .checked_mul(1u32 << (self.counter - 6))
+                .unwrap_or(Self::ONE_DAY),
+            // After that we cap of at 24h between attempts.
+            _ => Self::ONE_DAY,
+        };
+        let elapsed = self.timestamp.elapsed();
+        // This does exactly what we want.
+        // `back_off - elapsed` is the remaining back off duration or None if elapsed is larger
+        // than back_off. Also, this operation cannot overflow as long as elapsed is less than
+        // back_off, which is all that we care about.
+        back_off.checked_sub(elapsed)
+    }
+}
+
+impl Default for RateInfo {
+    fn default() -> Self {
+        Self { counter: 0u32, timestamp: Instant::now() }
+    }
+}
+
+/// The APC session state represents the state of an APC session.
+struct ApcSessionState {
+    /// A reference to the APC HAL backend.
+    hal: Arc<ApcHal>,
+    /// The client callback object.
+    cb: SpIBinder,
+    /// The uid of the owner of this APC session.
+    uid: u32,
+    /// The time when this session was started.
+    start: Instant,
+    /// This is set when the client calls abort.
+    /// This is used by the rate limiting logic to determine
+    /// if the client needs to be penalized for this attempt.
+    client_aborted: bool,
+}
+
+struct ApcState {
+    session: Option<ApcSessionState>,
+    rate_limiting: HashMap<u32, RateInfo>,
+    confirmation_token_sender: Sender<Vec<u8>>,
+}
+
+impl ApcState {
+    fn new(confirmation_token_sender: Sender<Vec<u8>>) -> Self {
+        Self { session: None, rate_limiting: Default::default(), confirmation_token_sender }
+    }
+}
+
+/// Implementation of the APC service.
+pub struct ApcManager {
+    state: Arc<Mutex<ApcState>>,
+}
+
+impl Interface for ApcManager {}
+
+impl ApcManager {
+    /// Create a new instance of the Android Protected Confirmation service.
+    pub fn new_native_binder(
+        confirmation_token_sender: Sender<Vec<u8>>,
+    ) -> Result<Strong<dyn IProtectedConfirmation>> {
+        Ok(BnProtectedConfirmation::new_binder(
+            Self { state: Arc::new(Mutex::new(ApcState::new(confirmation_token_sender))) },
+            BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+        ))
+    }
+
+    fn result(
+        state: Arc<Mutex<ApcState>>,
+        rc: u32,
+        data_confirmed: Option<&[u8]>,
+        confirmation_token: Option<&[u8]>,
+    ) {
+        let mut state = state.lock().unwrap();
+        let (callback, uid, start, client_aborted) = match state.session.take() {
+            None => return, // Nothing to do
+            Some(ApcSessionState { cb: callback, uid, start, client_aborted, .. }) => {
+                (callback, uid, start, client_aborted)
+            }
+        };
+
+        let rc = compat_2_response_code(rc);
+
+        // Update rate limiting information.
+        match (rc, client_aborted, confirmation_token) {
+            // If the user confirmed the dialog.
+            (ResponseCode::OK, _, Some(confirmation_token)) => {
+                // Reset counter.
+                state.rate_limiting.remove(&uid);
+                // Send confirmation token to the enforcement module.
+                if let Err(e) = state.confirmation_token_sender.send(confirmation_token.to_vec()) {
+                    log::error!("Got confirmation token, but receiver would not have it. {:?}", e);
+                }
+            }
+            // If cancelled by the user or if aborted by the client.
+            (ResponseCode::CANCELLED, _, _) | (ResponseCode::ABORTED, true, _) => {
+                // Penalize.
+                let mut rate_info = state.rate_limiting.entry(uid).or_default();
+                rate_info.counter += 1;
+                rate_info.timestamp = start;
+            }
+            (ResponseCode::OK, _, None) => {
+                log::error!(
+                    "Confirmation prompt was successful but no confirmation token was returned."
+                );
+            }
+            // In any other case this try does not count at all.
+            _ => {}
+        }
+        drop(state);
+
+        if let Ok(listener) = callback.into_interface::<dyn IConfirmationCallback>() {
+            if let Err(e) = listener.onCompleted(rc, data_confirmed) {
+                log::error!(
+                    "In ApcManagerCallback::result: Reporting completion to client failed {:?}",
+                    e
+                )
+            }
+        } else {
+            log::error!("In ApcManagerCallback::result: SpIBinder is not a IConfirmationCallback.");
+        }
+    }
+
+    fn present_prompt(
+        &self,
+        listener: &binder::Strong<dyn IConfirmationCallback>,
+        prompt_text: &str,
+        extra_data: &[u8],
+        locale: &str,
+        ui_option_flags: i32,
+    ) -> Result<()> {
+        let mut state = self.state.lock().unwrap();
+        if state.session.is_some() {
+            return Err(Error::pending())
+                .context("In ApcManager::present_prompt: Session pending.");
+        }
+
+        // Perform rate limiting.
+        let uid = ThreadState::get_calling_uid();
+        match state.rate_limiting.get(&uid) {
+            None => {}
+            Some(rate_info) => {
+                if let Some(back_off) = rate_info.get_remaining_back_off() {
+                    return Err(Error::sys()).context(format!(
+                        "In ApcManager::present_prompt: Cooling down. Remaining back-off: {}s",
+                        back_off.as_secs()
+                    ));
+                }
+            }
+        }
+
+        let hal = ApcHal::try_get_service();
+        let hal = match hal {
+            None => {
+                return Err(Error::unimplemented())
+                    .context("In ApcManager::present_prompt: APC not supported.")
+            }
+            Some(h) => Arc::new(h),
+        };
+
+        let ui_opts = ui_opts_2_compat(ui_option_flags);
+
+        let state_clone = self.state.clone();
+        hal.prompt_user_confirmation(
+            prompt_text,
+            extra_data,
+            locale,
+            ui_opts,
+            move |rc, data_confirmed, confirmation_token| {
+                Self::result(state_clone, rc, data_confirmed, confirmation_token)
+            },
+        )
+        .map_err(|rc| Error::Rc(compat_2_response_code(rc)))
+        .context("In present_prompt: Failed to present prompt.")?;
+        state.session = Some(ApcSessionState {
+            hal,
+            cb: listener.as_binder(),
+            uid,
+            start: Instant::now(),
+            client_aborted: false,
+        });
+        Ok(())
+    }
+
+    fn cancel_prompt(&self, listener: &binder::Strong<dyn IConfirmationCallback>) -> Result<()> {
+        let mut state = self.state.lock().unwrap();
+        let hal = match &mut state.session {
+            None => {
+                return Err(Error::ignored())
+                    .context("In cancel_prompt: Attempt to cancel non existing session. Ignoring.")
+            }
+            Some(session) => {
+                if session.cb != listener.as_binder() {
+                    return Err(Error::ignored()).context(concat!(
+                        "In cancel_prompt: Attempt to cancel session not belonging to caller. ",
+                        "Ignoring."
+                    ));
+                }
+                session.client_aborted = true;
+                session.hal.clone()
+            }
+        };
+        drop(state);
+        hal.abort();
+        Ok(())
+    }
+
+    fn is_supported() -> Result<bool> {
+        Ok(ApcHal::try_get_service().is_some())
+    }
+}
+
+impl IProtectedConfirmation for ApcManager {
+    fn presentPrompt(
+        &self,
+        listener: &binder::Strong<dyn IConfirmationCallback>,
+        prompt_text: &str,
+        extra_data: &[u8],
+        locale: &str,
+        ui_option_flags: i32,
+    ) -> BinderResult<()> {
+        // presentPrompt can take more time than other operations.
+        let _wp = wd::watch_millis("IProtectedConfirmation::presentPrompt", 3000);
+        map_or_log_err(
+            self.present_prompt(listener, prompt_text, extra_data, locale, ui_option_flags),
+            Ok,
+        )
+    }
+    fn cancelPrompt(
+        &self,
+        listener: &binder::Strong<dyn IConfirmationCallback>,
+    ) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IProtectedConfirmation::cancelPrompt", 500);
+        map_or_log_err(self.cancel_prompt(listener), Ok)
+    }
+    fn isSupported(&self) -> BinderResult<bool> {
+        let _wp = wd::watch_millis("IProtectedConfirmation::isSupported", 500);
+        map_or_log_err(Self::is_supported(), Ok)
+    }
+}
diff --git a/keystore2/src/async_task.rs b/keystore2/src/async_task.rs
new file mode 100644
index 0000000..4d0034a
--- /dev/null
+++ b/keystore2/src/async_task.rs
@@ -0,0 +1,531 @@
+// 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.
+
+//! This module implements the handling of async tasks.
+//! The worker thread has a high priority and a low priority queue. Adding a job to either
+//! will cause one thread to be spawned if none exists. As a compromise between performance
+//! and resource consumption, the thread will linger for about 30 seconds after it has
+//! processed all tasks before it terminates.
+//! Note that low priority tasks are processed only when the high priority queue is empty.
+
+use std::{any::Any, any::TypeId, time::Duration};
+use std::{
+    collections::{HashMap, VecDeque},
+    sync::Arc,
+    sync::{Condvar, Mutex, MutexGuard},
+    thread,
+};
+
+#[derive(Debug, PartialEq, Eq)]
+enum State {
+    Exiting,
+    Running,
+}
+
+/// The Shelf allows async tasks to store state across invocations.
+/// Note: Store elves at your own peril ;-).
+#[derive(Debug, Default)]
+pub struct Shelf(HashMap<TypeId, Box<dyn Any + Send>>);
+
+impl Shelf {
+    /// Get a reference to the shelved data of type T. Returns Some if the data exists.
+    pub fn get_downcast_ref<T: Any + Send>(&self) -> Option<&T> {
+        self.0.get(&TypeId::of::<T>()).and_then(|v| v.downcast_ref::<T>())
+    }
+
+    /// Get a mutable reference to the shelved data of type T. If a T was inserted using put,
+    /// get_mut, or get_or_put_with.
+    pub fn get_downcast_mut<T: Any + Send>(&mut self) -> Option<&mut T> {
+        self.0.get_mut(&TypeId::of::<T>()).and_then(|v| v.downcast_mut::<T>())
+    }
+
+    /// Remove the entry of the given type and returns the stored data if it existed.
+    pub fn remove_downcast_ref<T: Any + Send>(&mut self) -> Option<T> {
+        self.0.remove(&TypeId::of::<T>()).and_then(|v| v.downcast::<T>().ok().map(|b| *b))
+    }
+
+    /// Puts data `v` on the shelf. If there already was an entry of type T it is returned.
+    pub fn put<T: Any + Send>(&mut self, v: T) -> Option<T> {
+        self.0
+            .insert(TypeId::of::<T>(), Box::new(v) as Box<dyn Any + Send>)
+            .and_then(|v| v.downcast::<T>().ok().map(|b| *b))
+    }
+
+    /// Gets a mutable reference to the entry of the given type and default creates it if necessary.
+    /// The type must implement Default.
+    pub fn get_mut<T: Any + Send + Default>(&mut self) -> &mut T {
+        self.0
+            .entry(TypeId::of::<T>())
+            .or_insert_with(|| Box::new(T::default()) as Box<dyn Any + Send>)
+            .downcast_mut::<T>()
+            .unwrap()
+    }
+
+    /// Gets a mutable reference to the entry of the given type or creates it using the init
+    /// function. Init is not executed if the entry already existed.
+    pub fn get_or_put_with<T: Any + Send, F>(&mut self, init: F) -> &mut T
+    where
+        F: FnOnce() -> T,
+    {
+        self.0
+            .entry(TypeId::of::<T>())
+            .or_insert_with(|| Box::new(init()) as Box<dyn Any + Send>)
+            .downcast_mut::<T>()
+            .unwrap()
+    }
+}
+
+struct AsyncTaskState {
+    state: State,
+    thread: Option<thread::JoinHandle<()>>,
+    timeout: Duration,
+    hi_prio_req: VecDeque<Box<dyn FnOnce(&mut Shelf) + Send>>,
+    lo_prio_req: VecDeque<Box<dyn FnOnce(&mut Shelf) + Send>>,
+    idle_fns: Vec<Arc<dyn Fn(&mut Shelf) + Send + Sync>>,
+    /// The store allows tasks to store state across invocations. It is passed to each invocation
+    /// of each task. Tasks need to cooperate on the ids they use for storing state.
+    shelf: Option<Shelf>,
+}
+
+/// AsyncTask spawns one worker thread on demand to process jobs inserted into
+/// a low and a high priority work queue. The queues are processed FIFO, and low
+/// priority queue is processed if the high priority queue is empty.
+/// Note: Because there is only one worker thread at a time for a given AsyncTask instance,
+/// all scheduled requests are guaranteed to be serialized with respect to one another.
+pub struct AsyncTask {
+    state: Arc<(Condvar, Mutex<AsyncTaskState>)>,
+}
+
+impl Default for AsyncTask {
+    fn default() -> Self {
+        Self::new(Duration::from_secs(30))
+    }
+}
+
+impl AsyncTask {
+    /// Construct an [`AsyncTask`] with a specific timeout value.
+    pub fn new(timeout: Duration) -> Self {
+        Self {
+            state: Arc::new((
+                Condvar::new(),
+                Mutex::new(AsyncTaskState {
+                    state: State::Exiting,
+                    thread: None,
+                    timeout,
+                    hi_prio_req: VecDeque::new(),
+                    lo_prio_req: VecDeque::new(),
+                    idle_fns: Vec::new(),
+                    shelf: None,
+                }),
+            )),
+        }
+    }
+
+    /// Adds a one-off job to the high priority queue. High priority jobs are
+    /// completed before low priority jobs and can also overtake low priority
+    /// jobs. But they cannot preempt them.
+    pub fn queue_hi<F>(&self, f: F)
+    where
+        F: for<'r> FnOnce(&'r mut Shelf) + Send + 'static,
+    {
+        self.queue(f, true)
+    }
+
+    /// Adds a one-off job to the low priority queue. Low priority jobs are
+    /// completed after high priority. And they are not executed as long as high
+    /// priority jobs are present. Jobs always run to completion and are never
+    /// preempted by high priority jobs.
+    pub fn queue_lo<F>(&self, f: F)
+    where
+        F: FnOnce(&mut Shelf) + Send + 'static,
+    {
+        self.queue(f, false)
+    }
+
+    /// Adds an idle callback. This will be invoked whenever the worker becomes
+    /// idle (all high and low priority jobs have been performed).
+    pub fn add_idle<F>(&self, f: F)
+    where
+        F: Fn(&mut Shelf) + Send + Sync + 'static,
+    {
+        let (ref _condvar, ref state) = *self.state;
+        let mut state = state.lock().unwrap();
+        state.idle_fns.push(Arc::new(f));
+    }
+
+    fn queue<F>(&self, f: F, hi_prio: bool)
+    where
+        F: for<'r> FnOnce(&'r mut Shelf) + Send + 'static,
+    {
+        let (ref condvar, ref state) = *self.state;
+        let mut state = state.lock().unwrap();
+        if hi_prio {
+            state.hi_prio_req.push_back(Box::new(f));
+        } else {
+            state.lo_prio_req.push_back(Box::new(f));
+        }
+
+        if state.state != State::Running {
+            self.spawn_thread(&mut state);
+        }
+        drop(state);
+        condvar.notify_all();
+    }
+
+    fn spawn_thread(&self, state: &mut MutexGuard<AsyncTaskState>) {
+        if let Some(t) = state.thread.take() {
+            t.join().expect("AsyncTask panicked.");
+        }
+
+        let cloned_state = self.state.clone();
+        let timeout_period = state.timeout;
+
+        state.thread = Some(thread::spawn(move || {
+            let (ref condvar, ref state) = *cloned_state;
+
+            enum Action {
+                QueuedFn(Box<dyn FnOnce(&mut Shelf) + Send>),
+                IdleFns(Vec<Arc<dyn Fn(&mut Shelf) + Send + Sync>>),
+            }
+            let mut done_idle = false;
+
+            // When the worker starts, it takes the shelf and puts it on the stack.
+            let mut shelf = state.lock().unwrap().shelf.take().unwrap_or_default();
+            loop {
+                if let Some(action) = {
+                    let state = state.lock().unwrap();
+                    if !done_idle && state.hi_prio_req.is_empty() && state.lo_prio_req.is_empty() {
+                        // No jobs queued so invoke the idle callbacks.
+                        Some(Action::IdleFns(state.idle_fns.clone()))
+                    } else {
+                        // Wait for either a queued job to arrive or a timeout.
+                        let (mut state, timeout) = condvar
+                            .wait_timeout_while(state, timeout_period, |state| {
+                                state.hi_prio_req.is_empty() && state.lo_prio_req.is_empty()
+                            })
+                            .unwrap();
+                        match (
+                            state.hi_prio_req.pop_front(),
+                            state.lo_prio_req.is_empty(),
+                            timeout.timed_out(),
+                        ) {
+                            (Some(f), _, _) => Some(Action::QueuedFn(f)),
+                            (None, false, _) => {
+                                state.lo_prio_req.pop_front().map(|f| Action::QueuedFn(f))
+                            }
+                            (None, true, true) => {
+                                // When the worker exits it puts the shelf back into the shared
+                                // state for the next worker to use. So state is preserved not
+                                // only across invocations but also across worker thread shut down.
+                                state.shelf = Some(shelf);
+                                state.state = State::Exiting;
+                                break;
+                            }
+                            (None, true, false) => None,
+                        }
+                    }
+                } {
+                    // Now that the lock has been dropped, perform the action.
+                    match action {
+                        Action::QueuedFn(f) => {
+                            f(&mut shelf);
+                            done_idle = false;
+                        }
+                        Action::IdleFns(idle_fns) => {
+                            for idle_fn in idle_fns {
+                                idle_fn(&mut shelf);
+                            }
+                            done_idle = true;
+                        }
+                    }
+                }
+            }
+        }));
+        state.state = State::Running;
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{AsyncTask, Shelf};
+    use std::sync::{
+        mpsc::{channel, sync_channel, RecvTimeoutError},
+        Arc,
+    };
+    use std::time::Duration;
+
+    #[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]
+    fn test_async_task_chain() {
+        let at = Arc::new(AsyncTask::default());
+        let (sender, receiver) = channel();
+        // Queue up a job that will queue up another job. This confirms
+        // that the job is not invoked with any internal AsyncTask locks held.
+        let at_clone = at.clone();
+        at.queue_hi(move |_shelf| {
+            at_clone.queue_lo(move |_shelf| {
+                sender.send(()).unwrap();
+            });
+        });
+        receiver.recv().unwrap();
+    }
+
+    #[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();
+    }
+
+    #[test]
+    fn test_async_task_idle() {
+        let at = AsyncTask::new(Duration::from_secs(3));
+        // Need a SyncSender as it is Send+Sync.
+        let (idle_done_sender, idle_done_receiver) = sync_channel::<()>(3);
+        at.add_idle(move |_shelf| {
+            idle_done_sender.send(()).unwrap();
+        });
+
+        // Queue up some high-priority and low-priority jobs that take time.
+        for _i in 0..3 {
+            at.queue_lo(|_shelf| {
+                std::thread::sleep(Duration::from_millis(500));
+            });
+            at.queue_hi(|_shelf| {
+                std::thread::sleep(Duration::from_millis(500));
+            });
+        }
+        // Final low-priority job.
+        let (done_sender, done_receiver) = channel();
+        at.queue_lo(move |_shelf| {
+            done_sender.send(()).unwrap();
+        });
+
+        // Nothing happens until the last job completes.
+        assert_eq!(
+            idle_done_receiver.recv_timeout(Duration::from_secs(1)),
+            Err(RecvTimeoutError::Timeout)
+        );
+        done_receiver.recv().unwrap();
+        idle_done_receiver.recv_timeout(Duration::from_millis(1)).unwrap();
+
+        // Idle callback not executed again even if we wait for a while.
+        assert_eq!(
+            idle_done_receiver.recv_timeout(Duration::from_secs(3)),
+            Err(RecvTimeoutError::Timeout)
+        );
+
+        // However, if more work is done then there's another chance to go idle.
+        let (done_sender, done_receiver) = channel();
+        at.queue_hi(move |_shelf| {
+            std::thread::sleep(Duration::from_millis(500));
+            done_sender.send(()).unwrap();
+        });
+        // Idle callback not immediately executed, because the high priority
+        // job is taking a while.
+        assert_eq!(
+            idle_done_receiver.recv_timeout(Duration::from_millis(1)),
+            Err(RecvTimeoutError::Timeout)
+        );
+        done_receiver.recv().unwrap();
+        idle_done_receiver.recv_timeout(Duration::from_millis(1)).unwrap();
+    }
+
+    #[test]
+    fn test_async_task_multiple_idle() {
+        let at = AsyncTask::new(Duration::from_secs(3));
+        let (idle_sender, idle_receiver) = sync_channel::<i32>(5);
+        // Queue a high priority job to start things off
+        at.queue_hi(|_shelf| {
+            std::thread::sleep(Duration::from_millis(500));
+        });
+
+        // Multiple idle callbacks.
+        for i in 0..3 {
+            let idle_sender = idle_sender.clone();
+            at.add_idle(move |_shelf| {
+                idle_sender.send(i).unwrap();
+            });
+        }
+
+        // Nothing happens immediately.
+        assert_eq!(
+            idle_receiver.recv_timeout(Duration::from_millis(1)),
+            Err(RecvTimeoutError::Timeout)
+        );
+        // Wait for a moment and the idle jobs should have run.
+        std::thread::sleep(Duration::from_secs(1));
+
+        let mut results = Vec::new();
+        while let Ok(i) = idle_receiver.recv_timeout(Duration::from_millis(1)) {
+            results.push(i);
+        }
+        assert_eq!(results, [0, 1, 2]);
+    }
+
+    #[test]
+    fn test_async_task_idle_queues_job() {
+        let at = Arc::new(AsyncTask::new(Duration::from_secs(1)));
+        let at_clone = at.clone();
+        let (idle_sender, idle_receiver) = sync_channel::<i32>(100);
+        // Add an idle callback that queues a low-priority job.
+        at.add_idle(move |shelf| {
+            at_clone.queue_lo(|_shelf| {
+                // Slow things down so the channel doesn't fill up.
+                std::thread::sleep(Duration::from_millis(50));
+            });
+            let i = shelf.get_mut::<i32>();
+            idle_sender.send(*i).unwrap();
+            *i += 1;
+        });
+
+        // Nothing happens immediately.
+        assert_eq!(
+            idle_receiver.recv_timeout(Duration::from_millis(1500)),
+            Err(RecvTimeoutError::Timeout)
+        );
+
+        // Once we queue a normal job, things start.
+        at.queue_hi(|_shelf| {});
+        assert_eq!(0, idle_receiver.recv_timeout(Duration::from_millis(200)).unwrap());
+
+        // The idle callback queues a job, and completion of that job
+        // means the task is going idle again...so the idle callback will
+        // be called repeatedly.
+        assert_eq!(1, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
+        assert_eq!(2, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
+        assert_eq!(3, idle_receiver.recv_timeout(Duration::from_millis(100)).unwrap());
+    }
+
+    #[test]
+    #[should_panic]
+    fn test_async_task_idle_panic() {
+        let at = AsyncTask::new(Duration::from_secs(1));
+        let (idle_sender, idle_receiver) = sync_channel::<()>(3);
+        // Add an idle callback that panics.
+        at.add_idle(move |_shelf| {
+            idle_sender.send(()).unwrap();
+            panic!("Panic from idle callback");
+        });
+        // Queue a job to trigger idleness and ensuing panic.
+        at.queue_hi(|_shelf| {});
+        idle_receiver.recv().unwrap();
+
+        // Queue another job afterwards to ensure that the async thread gets joined
+        // and the panic detected.
+        let (done_sender, done_receiver) = channel();
+        at.queue_hi(move |_shelf| {
+            done_sender.send(()).unwrap();
+        });
+        done_receiver.recv().unwrap();
+    }
+}
diff --git a/keystore2/src/attestation_key_utils.rs b/keystore2/src/attestation_key_utils.rs
new file mode 100644
index 0000000..425eec6
--- /dev/null
+++ b/keystore2/src/attestation_key_utils.rs
@@ -0,0 +1,126 @@
+// 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.
+
+//! Implements get_attestation_key_info which loads remote provisioned or user
+//! generated attestation keys.
+
+use crate::database::{BlobMetaData, KeyEntryLoadBits, KeyType};
+use crate::database::{KeyIdGuard, KeystoreDB};
+use crate::error::{Error, ErrorCode};
+use crate::permission::KeyPerm;
+use crate::remote_provisioning::RemProvState;
+use crate::utils::check_key_permission;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    AttestationKey::AttestationKey, Certificate::Certificate, KeyParameter::KeyParameter,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
+use anyhow::{Context, Result};
+use keystore2_crypto::parse_subject_from_certificate;
+
+/// KeyMint takes two different kinds of attestation keys. Remote provisioned keys
+/// and those that have been generated by the user. Unfortunately, they need to be
+/// handled quite differently, thus the different representations.
+pub enum AttestationKeyInfo {
+    RemoteProvisioned {
+        attestation_key: AttestationKey,
+        attestation_certs: Certificate,
+    },
+    UserGenerated {
+        key_id_guard: KeyIdGuard,
+        blob: Vec<u8>,
+        blob_metadata: BlobMetaData,
+        issuer_subject: Vec<u8>,
+    },
+}
+
+/// This function loads and, optionally, assigns the caller's remote provisioned
+/// attestation key or, if `attest_key_descriptor` is given, it loads the user
+/// generated attestation key from the database.
+pub fn get_attest_key_info(
+    key: &KeyDescriptor,
+    caller_uid: u32,
+    attest_key_descriptor: Option<&KeyDescriptor>,
+    params: &[KeyParameter],
+    rem_prov_state: &RemProvState,
+    db: &mut KeystoreDB,
+) -> Result<Option<AttestationKeyInfo>> {
+    match attest_key_descriptor {
+        None => rem_prov_state
+            .get_remotely_provisioned_attestation_key_and_certs(&key, caller_uid, params, db)
+            .context(concat!(
+                "In get_attest_key_and_cert_chain: ",
+                "Trying to get remotely provisioned attestation key."
+            ))
+            .map(|result| {
+                result.map(|(attestation_key, attestation_certs)| {
+                    AttestationKeyInfo::RemoteProvisioned { attestation_key, attestation_certs }
+                })
+            }),
+        Some(attest_key) => get_user_generated_attestation_key(&attest_key, caller_uid, db)
+            .context("In get_attest_key_and_cert_chain: Trying to load attest key")
+            .map(Some),
+    }
+}
+
+fn get_user_generated_attestation_key(
+    key: &KeyDescriptor,
+    caller_uid: u32,
+    db: &mut KeystoreDB,
+) -> Result<AttestationKeyInfo> {
+    let (key_id_guard, blob, cert, blob_metadata) =
+        load_attest_key_blob_and_cert(&key, caller_uid, db)
+            .context("In get_user_generated_attestation_key: Failed to load blob and cert")?;
+
+    let issuer_subject: Vec<u8> = parse_subject_from_certificate(&cert).context(
+        "In get_user_generated_attestation_key: Failed to parse subject from certificate.",
+    )?;
+
+    Ok(AttestationKeyInfo::UserGenerated { key_id_guard, blob, issuer_subject, blob_metadata })
+}
+
+fn load_attest_key_blob_and_cert(
+    key: &KeyDescriptor,
+    caller_uid: u32,
+    db: &mut KeystoreDB,
+) -> Result<(KeyIdGuard, Vec<u8>, Vec<u8>, BlobMetaData)> {
+    match key.domain {
+        Domain::BLOB => Err(Error::Km(ErrorCode::INVALID_ARGUMENT)).context(
+            "In load_attest_key_blob_and_cert: Domain::BLOB attestation keys not supported",
+        ),
+        _ => {
+            let (key_id_guard, mut key_entry) = db
+                .load_key_entry(
+                    &key,
+                    KeyType::Client,
+                    KeyEntryLoadBits::BOTH,
+                    caller_uid,
+                    |k, av| check_key_permission(KeyPerm::use_(), k, &av),
+                )
+                .context("In load_attest_key_blob_and_cert: Failed to load key.")?;
+
+            let (blob, blob_metadata) =
+                key_entry.take_key_blob_info().ok_or_else(Error::sys).context(concat!(
+                    "In load_attest_key_blob_and_cert: Successfully loaded key entry,",
+                    " but KM blob was missing."
+                ))?;
+            let cert = key_entry.take_cert().ok_or_else(Error::sys).context(concat!(
+                "In load_attest_key_blob_and_cert: Successfully loaded key entry,",
+                " but cert was missing."
+            ))?;
+            Ok((key_id_guard, blob, cert, blob_metadata))
+        }
+    }
+}
diff --git a/keystore2/src/audit_log.rs b/keystore2/src/audit_log.rs
new file mode 100644
index 0000000..30fc155
--- /dev/null
+++ b/keystore2/src/audit_log.rs
@@ -0,0 +1,68 @@
+// 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.
+
+//! This module implements functions to log audit events to binary security log buffer for NIAP
+//! compliance.
+
+use crate::globals::LOGS_HANDLER;
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
+use libc::uid_t;
+use log_event_list::{LogContext, LogIdSecurity};
+
+const TAG_KEY_GENERATED: u32 = 210024;
+const TAG_KEY_IMPORTED: u32 = 210025;
+const TAG_KEY_DESTROYED: u32 = 210026;
+
+const NAMESPACE_MASK: i64 = 0x80000000;
+
+/// For app domain returns calling app uid, for SELinux domain returns masked namespace.
+fn key_owner(key: &KeyDescriptor, calling_app: uid_t) -> i32 {
+    match key.domain {
+        Domain::APP => calling_app as i32,
+        Domain::SELINUX => (key.nspace | NAMESPACE_MASK) as i32,
+        _ => {
+            log::info!("Not logging audit event for key with unexpected domain");
+            0
+        }
+    }
+}
+
+/// Logs key generation event to NIAP audit log.
+pub fn log_key_generated(key: &KeyDescriptor, calling_app: uid_t, success: bool) {
+    log_key_event(TAG_KEY_GENERATED, key, calling_app, success);
+}
+
+/// Logs key import event to NIAP audit log.
+pub fn log_key_imported(key: &KeyDescriptor, calling_app: uid_t, success: bool) {
+    log_key_event(TAG_KEY_IMPORTED, key, calling_app, success);
+}
+
+/// Logs key deletion event to NIAP audit log.
+pub fn log_key_deleted(key: &KeyDescriptor, calling_app: uid_t, success: bool) {
+    log_key_event(TAG_KEY_DESTROYED, key, calling_app, success);
+}
+
+fn log_key_event(tag: u32, key: &KeyDescriptor, calling_app: uid_t, success: bool) {
+    if let Some(ctx) = LogContext::new(LogIdSecurity, tag) {
+        let event = ctx
+            .append_i32(if success { 1 } else { 0 })
+            .append_str(key.alias.as_ref().map_or("none", String::as_str))
+            .append_i32(key_owner(key, calling_app));
+        LOGS_HANDLER.queue_lo(move |_| {
+            event.write();
+        });
+    }
+}
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
new file mode 100644
index 0000000..d07dab5
--- /dev/null
+++ b/keystore2/src/authorization.rs
@@ -0,0 +1,279 @@
+// 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.
+
+//! This module implements IKeystoreAuthorization AIDL interface.
+
+use crate::error::Error as KeystoreError;
+use crate::globals::{ENFORCEMENTS, SUPER_KEY, DB, LEGACY_MIGRATOR};
+use crate::permission::KeystorePerm;
+use crate::super_key::UserState;
+use crate::utils::{check_keystore_permission, watchdog as wd};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    HardwareAuthToken::HardwareAuthToken,
+};
+use android_security_authorization::binder::{BinderFeatures,ExceptionCode, Interface, Result as BinderResult,
+     Strong, Status as BinderStatus};
+use android_security_authorization::aidl::android::security::authorization::{
+    IKeystoreAuthorization::BnKeystoreAuthorization, IKeystoreAuthorization::IKeystoreAuthorization,
+    LockScreenEvent::LockScreenEvent, AuthorizationTokens::AuthorizationTokens,
+    ResponseCode::ResponseCode,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    ResponseCode::ResponseCode as KsResponseCode };
+use anyhow::{Context, Result};
+use keystore2_crypto::Password;
+use keystore2_selinux as selinux;
+
+/// This is the Authorization error type, it wraps binder exceptions and the
+/// Authorization ResponseCode
+#[derive(Debug, thiserror::Error, PartialEq)]
+pub enum Error {
+    /// Wraps an IKeystoreAuthorization response code as defined by
+    /// android.security.authorization AIDL interface specification.
+    #[error("Error::Rc({0:?})")]
+    Rc(ResponseCode),
+    /// Wraps a Binder exception code other than a service specific exception.
+    #[error("Binder exception code {0:?}, {1:?}")]
+    Binder(ExceptionCode, i32),
+}
+
+/// This function should be used by authorization service calls to translate error conditions
+/// into service specific exceptions.
+///
+/// All error conditions get logged by this function.
+///
+/// `Error::Rc(x)` variants get mapped onto a service specific error code of `x`.
+/// Certain response codes may be returned from keystore/ResponseCode.aidl by the keystore2 modules,
+/// which are then converted to the corresponding response codes of android.security.authorization
+/// AIDL interface specification.
+///
+/// `selinux::Error::perm()` is mapped on `ResponseCode::PERMISSION_DENIED`.
+///
+/// All non `Error` error conditions get mapped onto ResponseCode::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).
+pub 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();
+            if let Some(KeystoreError::Rc(ks_rcode)) = root_cause.downcast_ref::<KeystoreError>() {
+                let rc = match *ks_rcode {
+                    // Although currently keystore2/ResponseCode.aidl and
+                    // authorization/ResponseCode.aidl share the same integer values for the
+                    // common response codes, this may deviate in the future, hence the
+                    // conversion here.
+                    KsResponseCode::SYSTEM_ERROR => ResponseCode::SYSTEM_ERROR.0,
+                    KsResponseCode::KEY_NOT_FOUND => ResponseCode::KEY_NOT_FOUND.0,
+                    KsResponseCode::VALUE_CORRUPTED => ResponseCode::VALUE_CORRUPTED.0,
+                    KsResponseCode::INVALID_ARGUMENT => ResponseCode::INVALID_ARGUMENT.0,
+                    // If the code paths of IKeystoreAuthorization aidl's methods happen to return
+                    // other error codes from KsResponseCode in the future, they should be converted
+                    // as well.
+                    _ => ResponseCode::SYSTEM_ERROR.0,
+                };
+                return Err(BinderStatus::new_service_specific_error(rc, None));
+            }
+            let rc = match root_cause.downcast_ref::<Error>() {
+                Some(Error::Rc(rcode)) => rcode.0,
+                Some(Error::Binder(_, _)) => ResponseCode::SYSTEM_ERROR.0,
+                None => match root_cause.downcast_ref::<selinux::Error>() {
+                    Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
+                    _ => ResponseCode::SYSTEM_ERROR.0,
+                },
+            };
+            Err(BinderStatus::new_service_specific_error(rc, None))
+        },
+        handle_ok,
+    )
+}
+
+/// This struct is defined to implement the aforementioned AIDL interface.
+/// As of now, it is an empty struct.
+pub struct AuthorizationManager;
+
+impl AuthorizationManager {
+    /// Create a new instance of Keystore Authorization service.
+    pub fn new_native_binder() -> Result<Strong<dyn IKeystoreAuthorization>> {
+        Ok(BnKeystoreAuthorization::new_binder(
+            Self,
+            BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+        ))
+    }
+
+    fn add_auth_token(&self, auth_token: &HardwareAuthToken) -> Result<()> {
+        // Check keystore permission.
+        check_keystore_permission(KeystorePerm::add_auth()).context("In add_auth_token.")?;
+
+        ENFORCEMENTS.add_auth_token(auth_token.clone())?;
+        Ok(())
+    }
+
+    fn on_lock_screen_event(
+        &self,
+        lock_screen_event: LockScreenEvent,
+        user_id: i32,
+        password: Option<Password>,
+        unlocking_sids: Option<&[i64]>,
+    ) -> Result<()> {
+        log::info!(
+            "on_lock_screen_event({:?}, user_id={:?}, password.is_some()={}, unlocking_sids={:?})",
+            lock_screen_event,
+            user_id,
+            password.is_some(),
+            unlocking_sids
+        );
+        match (lock_screen_event, password) {
+            (LockScreenEvent::UNLOCK, Some(password)) => {
+                // This corresponds to the unlock() method in legacy keystore API.
+                // check permission
+                check_keystore_permission(KeystorePerm::unlock())
+                    .context("In on_lock_screen_event: Unlock with password.")?;
+                ENFORCEMENTS.set_device_locked(user_id, false);
+
+                DB.with(|db| {
+                    SUPER_KEY.unlock_screen_lock_bound_key(
+                        &mut db.borrow_mut(),
+                        user_id as u32,
+                        &password,
+                    )
+                })
+                .context("In on_lock_screen_event: unlock_screen_lock_bound_key failed")?;
+
+                // Unlock super key.
+                if let UserState::Uninitialized = DB
+                    .with(|db| {
+                        UserState::get_with_password_unlock(
+                            &mut db.borrow_mut(),
+                            &LEGACY_MIGRATOR,
+                            &SUPER_KEY,
+                            user_id as u32,
+                            &password,
+                        )
+                    })
+                    .context("In on_lock_screen_event: Unlock with password.")?
+                {
+                    log::info!(
+                        "In on_lock_screen_event. Trying to unlock when LSKF is uninitialized."
+                    );
+                }
+
+                Ok(())
+            }
+            (LockScreenEvent::UNLOCK, None) => {
+                check_keystore_permission(KeystorePerm::unlock())
+                    .context("In on_lock_screen_event: Unlock.")?;
+                ENFORCEMENTS.set_device_locked(user_id, false);
+                DB.with(|db| {
+                    SUPER_KEY.try_unlock_user_with_biometric(&mut db.borrow_mut(), user_id as u32)
+                })
+                .context("In on_lock_screen_event: try_unlock_user_with_biometric failed")?;
+                Ok(())
+            }
+            (LockScreenEvent::LOCK, None) => {
+                check_keystore_permission(KeystorePerm::lock())
+                    .context("In on_lock_screen_event: Lock")?;
+                ENFORCEMENTS.set_device_locked(user_id, true);
+                DB.with(|db| {
+                    SUPER_KEY.lock_screen_lock_bound_key(
+                        &mut db.borrow_mut(),
+                        user_id as u32,
+                        unlocking_sids.unwrap_or(&[]),
+                    );
+                });
+                Ok(())
+            }
+            _ => {
+                // Any other combination is not supported.
+                Err(Error::Rc(ResponseCode::INVALID_ARGUMENT))
+                    .context("In on_lock_screen_event: Unknown event.")
+            }
+        }
+    }
+
+    fn get_auth_tokens_for_credstore(
+        &self,
+        challenge: i64,
+        secure_user_id: i64,
+        auth_token_max_age_millis: i64,
+    ) -> Result<AuthorizationTokens> {
+        // Check permission. Function should return if this failed. Therefore having '?' at the end
+        // is very important.
+        check_keystore_permission(KeystorePerm::get_auth_token())
+            .context("In get_auth_tokens_for_credstore.")?;
+
+        // If the challenge is zero, return error
+        if challenge == 0 {
+            return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT))
+                .context("In get_auth_tokens_for_credstore. Challenge can not be zero.");
+        }
+        // Obtain the auth token and the timestamp token from the enforcement module.
+        let (auth_token, ts_token) =
+            ENFORCEMENTS.get_auth_tokens(challenge, secure_user_id, auth_token_max_age_millis)?;
+        Ok(AuthorizationTokens { authToken: auth_token, timestampToken: ts_token })
+    }
+}
+
+impl Interface for AuthorizationManager {}
+
+impl IKeystoreAuthorization for AuthorizationManager {
+    fn addAuthToken(&self, auth_token: &HardwareAuthToken) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreAuthorization::addAuthToken", 500);
+        map_or_log_err(self.add_auth_token(auth_token), Ok)
+    }
+
+    fn onLockScreenEvent(
+        &self,
+        lock_screen_event: LockScreenEvent,
+        user_id: i32,
+        password: Option<&[u8]>,
+        unlocking_sids: Option<&[i64]>,
+    ) -> BinderResult<()> {
+        let _wp =
+            wd::watch_millis_with("IKeystoreAuthorization::onLockScreenEvent", 500, move || {
+                format!("lock event: {}", lock_screen_event.0)
+            });
+        map_or_log_err(
+            self.on_lock_screen_event(
+                lock_screen_event,
+                user_id,
+                password.map(|pw| pw.into()),
+                unlocking_sids,
+            ),
+            Ok,
+        )
+    }
+
+    fn getAuthTokensForCredStore(
+        &self,
+        challenge: i64,
+        secure_user_id: i64,
+        auth_token_max_age_millis: i64,
+    ) -> binder::public_api::Result<AuthorizationTokens> {
+        let _wp = wd::watch_millis("IKeystoreAuthorization::getAuthTokensForCredStore", 500);
+        map_or_log_err(
+            self.get_auth_tokens_for_credstore(
+                challenge,
+                secure_user_id,
+                auth_token_max_age_millis,
+            ),
+            Ok,
+        )
+    }
+}
diff --git a/keystore2/src/boot_level_keys.rs b/keystore2/src/boot_level_keys.rs
new file mode 100644
index 0000000..3084195
--- /dev/null
+++ b/keystore2/src/boot_level_keys.rs
@@ -0,0 +1,227 @@
+// 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.
+
+//! Offer keys based on the "boot level" for superencryption.
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, Digest::Digest, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+};
+use anyhow::{Context, Result};
+use keystore2_crypto::{hkdf_expand, ZVec, AES_256_KEY_LENGTH};
+use std::{collections::VecDeque, convert::TryFrom};
+
+use crate::{database::KeystoreDB, key_parameter::KeyParameterValue, raw_device::KeyMintDevice};
+
+/// This is not thread safe; caller must hold a lock before calling.
+/// In practice the caller is SuperKeyManager and the lock is the
+/// Mutex on its internal state.
+pub fn get_level_zero_key(db: &mut KeystoreDB) -> Result<ZVec> {
+    let key_desc = KeyMintDevice::internal_descriptor("boot_level_key".to_string());
+    let params = [
+        KeyParameterValue::Algorithm(Algorithm::HMAC).into(),
+        KeyParameterValue::Digest(Digest::SHA_2_256).into(),
+        KeyParameterValue::KeySize(256).into(),
+        KeyParameterValue::MinMacLength(256).into(),
+        KeyParameterValue::KeyPurpose(KeyPurpose::SIGN).into(),
+        KeyParameterValue::NoAuthRequired.into(),
+        KeyParameterValue::MaxUsesPerBoot(1).into(),
+    ];
+    // We use TRUSTED_ENVIRONMENT here because it is the authority on when
+    // the device has rebooted.
+    let km_dev: KeyMintDevice = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
+        .context("In get_level_zero_key: KeyMintDevice::get failed")?;
+    let (key_id_guard, key_entry) = km_dev
+        .lookup_or_generate_key(db, &key_desc, &params)
+        .context("In get_level_zero_key: lookup_or_generate_key failed")?;
+
+    let params = [KeyParameterValue::MacLength(256).into()];
+    let level_zero_key = km_dev
+        .use_key_in_one_step(
+            db,
+            &key_id_guard,
+            &key_entry,
+            KeyPurpose::SIGN,
+            &params,
+            None,
+            b"Create boot level key",
+        )
+        .context("In get_level_zero_key: use_key_in_one_step failed")?;
+    // TODO: this is rather unsatisfactory, we need a better way to handle
+    // sensitive binder returns.
+    let level_zero_key = ZVec::try_from(level_zero_key)
+        .context("In get_level_zero_key: conversion to ZVec failed")?;
+    Ok(level_zero_key)
+}
+
+/// Holds the key for the current boot level, and a cache of future keys generated as required.
+/// When the boot level advances, keys prior to the current boot level are securely dropped.
+pub struct BootLevelKeyCache {
+    /// Least boot level currently accessible, if any is.
+    current: usize,
+    /// Invariant: cache entry *i*, if it exists, holds the HKDF key for boot level
+    /// *i* + `current`. If the cache is non-empty it can be grown forwards, but it cannot be
+    /// grown backwards, so keys below `current` are inaccessible.
+    /// `cache.clear()` makes all keys inaccessible.
+    cache: VecDeque<ZVec>,
+}
+
+impl BootLevelKeyCache {
+    const HKDF_ADVANCE: &'static [u8] = b"Advance KDF one step";
+    const HKDF_AES: &'static [u8] = b"Generate AES-256-GCM key";
+    const HKDF_KEY_SIZE: usize = 32;
+
+    /// Initialize the cache with the level zero key.
+    pub fn new(level_zero_key: ZVec) -> Self {
+        let mut cache: VecDeque<ZVec> = VecDeque::new();
+        cache.push_back(level_zero_key);
+        Self { current: 0, cache }
+    }
+
+    /// Report whether the key for the given level can be inferred.
+    pub fn level_accessible(&self, boot_level: usize) -> bool {
+        // If the requested boot level is lower than the current boot level
+        // or if we have reached the end (`cache.empty()`) we can't retrieve
+        // the boot key.
+        boot_level >= self.current && !self.cache.is_empty()
+    }
+
+    /// Get the HKDF key for boot level `boot_level`. The key for level *i*+1
+    /// is calculated from the level *i* key using `hkdf_expand`.
+    fn get_hkdf_key(&mut self, boot_level: usize) -> Result<Option<&ZVec>> {
+        if !self.level_accessible(boot_level) {
+            return Ok(None);
+        }
+        // `self.cache.len()` represents the first entry not in the cache,
+        // so `self.current + self.cache.len()` is the first boot level not in the cache.
+        let first_not_cached = self.current + self.cache.len();
+
+        // Grow the cache forwards until it contains the desired boot level.
+        for _level in first_not_cached..=boot_level {
+            // We check at the start that cache is non-empty and future iterations only push,
+            // so this must unwrap.
+            let highest_key = self.cache.back().unwrap();
+            let next_key = hkdf_expand(Self::HKDF_KEY_SIZE, highest_key, Self::HKDF_ADVANCE)
+                .context("In BootLevelKeyCache::get_hkdf_key: Advancing key one step")?;
+            self.cache.push_back(next_key);
+        }
+
+        // If we reach this point, we should have a key at index boot_level - current.
+        Ok(Some(self.cache.get(boot_level - self.current).unwrap()))
+    }
+
+    /// Drop keys prior to the given boot level, while retaining the ability to generate keys for
+    /// that level and later.
+    pub fn advance_boot_level(&mut self, new_boot_level: usize) -> Result<()> {
+        if !self.level_accessible(new_boot_level) {
+            log::error!(
+                concat!(
+                    "In BootLevelKeyCache::advance_boot_level: ",
+                    "Failed to advance boot level to {}, current is {}, cache size {}"
+                ),
+                new_boot_level,
+                self.current,
+                self.cache.len()
+            );
+            return Ok(());
+        }
+
+        // We `get` the new boot level for the side effect of advancing the cache to a point
+        // where the new boot level is present.
+        self.get_hkdf_key(new_boot_level)
+            .context("In BootLevelKeyCache::advance_boot_level: Advancing cache")?;
+
+        // Then we split the queue at the index of the new boot level and discard the front,
+        // keeping only the keys with the current boot level or higher.
+        self.cache = self.cache.split_off(new_boot_level - self.current);
+
+        // The new cache has the new boot level at index 0, so we set `current` to
+        // `new_boot_level`.
+        self.current = new_boot_level;
+
+        Ok(())
+    }
+
+    /// Drop all keys, effectively raising the current boot level to infinity; no keys can
+    /// be inferred from this point on.
+    pub fn finish(&mut self) {
+        self.cache.clear();
+    }
+
+    fn expand_key(
+        &mut self,
+        boot_level: usize,
+        out_len: usize,
+        info: &[u8],
+    ) -> Result<Option<ZVec>> {
+        self.get_hkdf_key(boot_level)
+            .context("In BootLevelKeyCache::expand_key: Looking up HKDF key")?
+            .map(|k| hkdf_expand(out_len, k, info))
+            .transpose()
+            .context("In BootLevelKeyCache::expand_key: Calling hkdf_expand")
+    }
+
+    /// Return the AES-256-GCM key for the current boot level.
+    pub fn aes_key(&mut self, boot_level: usize) -> Result<Option<ZVec>> {
+        self.expand_key(boot_level, AES_256_KEY_LENGTH, BootLevelKeyCache::HKDF_AES)
+            .context("In BootLevelKeyCache::aes_key: expand_key failed")
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_output_is_consistent() -> Result<()> {
+        let initial_key = b"initial key";
+        let mut blkc = BootLevelKeyCache::new(ZVec::try_from(initial_key as &[u8])?);
+        assert_eq!(true, blkc.level_accessible(0));
+        assert_eq!(true, blkc.level_accessible(9));
+        assert_eq!(true, blkc.level_accessible(10));
+        assert_eq!(true, blkc.level_accessible(100));
+        let v0 = blkc.aes_key(0).unwrap().unwrap();
+        let v10 = blkc.aes_key(10).unwrap().unwrap();
+        assert_eq!(Some(&v0), blkc.aes_key(0)?.as_ref());
+        assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref());
+        blkc.advance_boot_level(5)?;
+        assert_eq!(false, blkc.level_accessible(0));
+        assert_eq!(true, blkc.level_accessible(9));
+        assert_eq!(true, blkc.level_accessible(10));
+        assert_eq!(true, blkc.level_accessible(100));
+        assert_eq!(None, blkc.aes_key(0)?);
+        assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref());
+        blkc.advance_boot_level(10)?;
+        assert_eq!(false, blkc.level_accessible(0));
+        assert_eq!(false, blkc.level_accessible(9));
+        assert_eq!(true, blkc.level_accessible(10));
+        assert_eq!(true, blkc.level_accessible(100));
+        assert_eq!(None, blkc.aes_key(0)?);
+        assert_eq!(Some(&v10), blkc.aes_key(10)?.as_ref());
+        blkc.advance_boot_level(0)?;
+        assert_eq!(false, blkc.level_accessible(0));
+        assert_eq!(false, blkc.level_accessible(9));
+        assert_eq!(true, blkc.level_accessible(10));
+        assert_eq!(true, blkc.level_accessible(100));
+        assert_eq!(None, blkc.aes_key(0)?);
+        assert_eq!(Some(v10), blkc.aes_key(10)?);
+        blkc.finish();
+        assert_eq!(false, blkc.level_accessible(0));
+        assert_eq!(false, blkc.level_accessible(9));
+        assert_eq!(false, blkc.level_accessible(10));
+        assert_eq!(false, blkc.level_accessible(100));
+        assert_eq!(None, blkc.aes_key(0)?);
+        assert_eq!(None, blkc.aes_key(10)?);
+        Ok(())
+    }
+}
diff --git a/keystore2/src/crypto/Android.bp b/keystore2/src/crypto/Android.bp
new file mode 100644
index 0000000..3ba47cd
--- /dev/null
+++ b/keystore2/src/crypto/Android.bp
@@ -0,0 +1,127 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+rust_library {
+    name: "libkeystore2_crypto_rust",
+    crate_name: "keystore2_crypto",
+    srcs: ["lib.rs"],
+    rustlibs: [
+        "libkeystore2_crypto_bindgen",
+        "liblog_rust",
+        "libnix",
+        "libthiserror",
+    ],
+    shared_libs: [
+        "libkeystore2_crypto",
+        "libcrypto",
+    ],
+}
+
+cc_library {
+    name: "libkeystore2_crypto",
+    srcs: [
+        "crypto.cpp",
+        "certificate_utils.cpp",
+    ],
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libcrypto",
+        "liblog",
+    ],
+}
+
+rust_bindgen {
+    name: "libkeystore2_crypto_bindgen",
+    wrapper_src: "crypto.hpp",
+    crate_name: "keystore2_crypto_bindgen",
+    source_stem: "bindings",
+    host_supported: true,
+    shared_libs: ["libcrypto"],
+    bindgen_flags: [
+        "--size_t-is-usize",
+        "--allowlist-function", "randomBytes",
+        "--allowlist-function", "AES_gcm_encrypt",
+        "--allowlist-function", "AES_gcm_decrypt",
+        "--allowlist-function", "CreateKeyId",
+        "--allowlist-function", "generateKeyFromPassword",
+        "--allowlist-function", "HKDFExtract",
+        "--allowlist-function", "HKDFExpand",
+        "--allowlist-function", "ECDHComputeKey",
+        "--allowlist-function", "ECKEYGenerateKey",
+        "--allowlist-function", "ECKEYMarshalPrivateKey",
+        "--allowlist-function", "ECKEYParsePrivateKey",
+        "--allowlist-function", "EC_KEY_get0_public_key",
+        "--allowlist-function", "ECPOINTPoint2Oct",
+        "--allowlist-function", "ECPOINTOct2Point",
+        "--allowlist-function", "EC_KEY_free",
+        "--allowlist-function", "EC_POINT_free",
+        "--allowlist-function", "extractSubjectFromCertificate",
+        "--allowlist-type", "EC_KEY",
+        "--allowlist-type", "EC_POINT",
+        "--allowlist-var", "EC_MAX_BYTES",
+        "--allowlist-var", "EVP_MAX_MD_SIZE",
+    ],
+    cflags: ["-DBORINGSSL_NO_CXX"],
+}
+
+rust_test {
+    name: "keystore2_crypto_test_rust",
+    crate_name: "keystore2_crypto_test_rust",
+    srcs: ["lib.rs"],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    rustlibs: [
+        "libkeystore2_crypto_bindgen",
+        "liblog_rust",
+        "libnix",
+        "libthiserror",
+    ],
+    static_libs: [
+        "libkeystore2_crypto",
+    ],
+    shared_libs: [
+        "libc++",
+        "libcrypto",
+        "liblog",
+    ],
+}
+
+cc_test {
+    name: "keystore2_crypto_test",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    srcs: [
+        "tests/certificate_utils_test.cpp",
+        "tests/gtest_main.cpp",
+    ],
+    test_suites: ["general-tests"],
+    static_libs: [
+        "libkeystore2_crypto",
+    ],
+    shared_libs: [
+        "libcrypto",
+    ],
+}
diff --git a/keystore2/src/crypto/TEST_MAPPING b/keystore2/src/crypto/TEST_MAPPING
new file mode 100644
index 0000000..f6fb97a
--- /dev/null
+++ b/keystore2/src/crypto/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "presubmit": [
+    {
+      "name": "keystore2_crypto_test_rust"
+    },
+    {
+      "name": "keystore2_crypto_test"
+    }
+  ]
+}
diff --git a/keystore2/src/crypto/certificate_utils.cpp b/keystore2/src/crypto/certificate_utils.cpp
new file mode 100644
index 0000000..31c7fb4
--- /dev/null
+++ b/keystore2/src/crypto/certificate_utils.cpp
@@ -0,0 +1,628 @@
+/*
+ * 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.
+ */
+
+#include <certificate_utils.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/x509v3.h>
+
+#include <functional>
+#include <limits>
+#include <string>
+#include <variant>
+#include <vector>
+
+namespace keystore {
+
+namespace {
+
+constexpr int kDigitalSignatureKeyUsageBit = 0;
+constexpr int kKeyEnciphermentKeyUsageBit = 2;
+constexpr int kDataEnciphermentKeyUsageBit = 3;
+constexpr int kKeyCertSignBit = 5;
+constexpr int kMaxKeyUsageBit = 8;
+
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_STRING);
+DEFINE_OPENSSL_OBJECT_POINTER(RSA_PSS_PARAMS);
+DEFINE_OPENSSL_OBJECT_POINTER(AUTHORITY_KEYID);
+DEFINE_OPENSSL_OBJECT_POINTER(BASIC_CONSTRAINTS);
+DEFINE_OPENSSL_OBJECT_POINTER(X509_ALGOR);
+DEFINE_OPENSSL_OBJECT_POINTER(BIGNUM);
+
+}  // namespace
+
+constexpr const char kDefaultCommonName[] = "Default Common Name";
+
+std::variant<CertUtilsError, X509_NAME_Ptr>
+makeCommonName(std::optional<std::reference_wrapper<const std::vector<uint8_t>>> name) {
+    if (name) {
+        const uint8_t* p = name->get().data();
+        X509_NAME_Ptr x509_name(d2i_X509_NAME(nullptr, &p, name->get().size()));
+        if (!x509_name) {
+            return CertUtilsError::MemoryAllocation;
+        }
+        return x509_name;
+    }
+
+    X509_NAME_Ptr x509_name(X509_NAME_new());
+    if (!x509_name) {
+        return CertUtilsError::MemoryAllocation;
+    }
+    if (!X509_NAME_add_entry_by_txt(x509_name.get(), "CN", MBSTRING_ASC,
+                                    reinterpret_cast<const uint8_t*>(kDefaultCommonName),
+                                    sizeof(kDefaultCommonName) - 1, -1 /* loc */, 0 /* set */)) {
+        return CertUtilsError::BoringSsl;
+    }
+    return x509_name;
+}
+
+std::variant<CertUtilsError, std::vector<uint8_t>> makeKeyId(const X509* cert) {
+    std::vector<uint8_t> keyid(20);
+    unsigned int len;
+    if (!X509_pubkey_digest(cert, EVP_sha1(), keyid.data(), &len)) {
+        return CertUtilsError::Encoding;
+    }
+    return keyid;
+}
+
+std::variant<CertUtilsError, AUTHORITY_KEYID_Ptr>
+makeAuthorityKeyExtension(const std::vector<uint8_t>& keyid) {
+    AUTHORITY_KEYID_Ptr auth_key(AUTHORITY_KEYID_new());
+    if (!auth_key) {
+        return CertUtilsError::MemoryAllocation;
+    }
+
+    auth_key->keyid = ASN1_OCTET_STRING_new();
+    if (auth_key->keyid == nullptr) {
+        return CertUtilsError::MemoryAllocation;
+    }
+
+    if (!ASN1_OCTET_STRING_set(auth_key->keyid, keyid.data(), keyid.size())) {
+        return CertUtilsError::BoringSsl;
+    }
+
+    return auth_key;
+}
+
+std::variant<CertUtilsError, ASN1_OCTET_STRING_Ptr>
+makeSubjectKeyExtension(const std::vector<uint8_t>& keyid) {
+
+    // Build OCTET_STRING
+    ASN1_OCTET_STRING_Ptr keyid_str(ASN1_OCTET_STRING_new());
+    if (!keyid_str || !ASN1_OCTET_STRING_set(keyid_str.get(), keyid.data(), keyid.size())) {
+        return CertUtilsError::BoringSsl;
+    }
+
+    return keyid_str;
+}
+
+std::variant<CertUtilsError, BASIC_CONSTRAINTS_Ptr>
+makeBasicConstraintsExtension(bool is_ca, std::optional<int> path_length) {
+
+    BASIC_CONSTRAINTS_Ptr bcons(BASIC_CONSTRAINTS_new());
+    if (!bcons) {
+        return CertUtilsError::MemoryAllocation;
+    }
+
+    bcons->ca = is_ca;
+    bcons->pathlen = nullptr;
+    if (path_length) {
+        bcons->pathlen = ASN1_INTEGER_new();
+        if (bcons->pathlen == nullptr || !ASN1_INTEGER_set(bcons->pathlen, *path_length)) {
+            return CertUtilsError::BoringSsl;
+        }
+    }
+
+    return bcons;
+}
+
+std::variant<CertUtilsError, ASN1_BIT_STRING_Ptr>
+makeKeyUsageExtension(bool is_signing_key, bool is_encryption_key, bool is_cert_key) {
+    // Build BIT_STRING with correct contents.
+    ASN1_BIT_STRING_Ptr key_usage(ASN1_BIT_STRING_new());
+    if (!key_usage) {
+        return CertUtilsError::BoringSsl;
+    }
+
+    for (size_t i = 0; i <= kMaxKeyUsageBit; ++i) {
+        if (!ASN1_BIT_STRING_set_bit(key_usage.get(), i, 0)) {
+            return CertUtilsError::BoringSsl;
+        }
+    }
+
+    if (is_signing_key) {
+        if (!ASN1_BIT_STRING_set_bit(key_usage.get(), kDigitalSignatureKeyUsageBit, 1)) {
+            return CertUtilsError::BoringSsl;
+        }
+    }
+
+    if (is_encryption_key) {
+        if (!ASN1_BIT_STRING_set_bit(key_usage.get(), kKeyEnciphermentKeyUsageBit, 1) ||
+            !ASN1_BIT_STRING_set_bit(key_usage.get(), kDataEnciphermentKeyUsageBit, 1)) {
+            return CertUtilsError::BoringSsl;
+        }
+    }
+
+    if (is_cert_key) {
+        if (!ASN1_BIT_STRING_set_bit(key_usage.get(), kKeyCertSignBit, 1)) {
+            return CertUtilsError::BoringSsl;
+        }
+    }
+
+    return key_usage;
+}
+
+template <typename Out, typename In> static Out saturate(In in) {
+    if constexpr (std::is_signed_v<Out> == std::is_signed_v<In>) {
+        if constexpr (sizeof(Out) >= sizeof(In)) {
+            // Same sign, and In fits into Out. Cast is lossless.
+            return static_cast<Out>(in);
+        } else {
+            // Out is smaller than In we may need to truncate.
+            // We pick the smaller of `out::max()` and the greater of `out::min()` and `in`.
+            return static_cast<Out>(
+                std::min(static_cast<In>(std::numeric_limits<Out>::max()),
+                         std::max(static_cast<In>(std::numeric_limits<Out>::min()), in)));
+        }
+    } else {
+        // So we have different signs. This puts the lower bound at 0 because either input or output
+        // is unsigned. The upper bound is max of the smaller type or, if they are equal the max of
+        // the signed type.
+        if constexpr (std::is_signed_v<Out>) {
+            if constexpr (sizeof(Out) > sizeof(In)) {
+                return static_cast<Out>(in);
+            } else {
+                // Because `out` is the signed one, the lower bound of `in` is 0 and fits into
+                // `out`. We just have to compare the maximum and we do it in type In because it has
+                // a greater range than Out, so Out::max() is guaranteed to fit.
+                return static_cast<Out>(
+                    std::min(static_cast<In>(std::numeric_limits<Out>::max()), in));
+            }
+        } else {
+            // Out is unsigned. So we can return 0 if in is negative.
+            if (in < 0) return 0;
+            if constexpr (sizeof(Out) >= sizeof(In)) {
+                // If Out is wider or equal we can assign lossless.
+                return static_cast<Out>(in);
+            } else {
+                // Otherwise we have to take the minimum of Out::max() and `in`.
+                return static_cast<Out>(
+                    std::min(static_cast<In>(std::numeric_limits<Out>::max()), in));
+            }
+        }
+    }
+}
+
+// Creates a rump certificate structure with serial, subject and issuer names, as well as
+// activation and expiry date.
+// Callers should pass an empty X509_Ptr and check the return value for CertUtilsError::Ok (0)
+// before accessing the result.
+std::variant<CertUtilsError, X509_Ptr>
+makeCertRump(std::optional<std::reference_wrapper<const std::vector<uint8_t>>> serial,
+             std::optional<std::reference_wrapper<const std::vector<uint8_t>>> subject,
+             const int64_t activeDateTimeMilliSeconds,
+             const int64_t usageExpireDateTimeMilliSeconds) {
+
+    // Create certificate structure.
+    X509_Ptr certificate(X509_new());
+    if (!certificate) {
+        return CertUtilsError::BoringSsl;
+    }
+
+    // Set the X509 version.
+    if (!X509_set_version(certificate.get(), 2 /* version 3, but zero-based */)) {
+        return CertUtilsError::BoringSsl;
+    }
+
+    BIGNUM_Ptr bn_serial;
+    if (serial) {
+        bn_serial = BIGNUM_Ptr(BN_bin2bn(serial->get().data(), serial->get().size(), nullptr));
+        if (!bn_serial) {
+            return CertUtilsError::MemoryAllocation;
+        }
+    } else {
+        bn_serial = BIGNUM_Ptr(BN_new());
+        if (!bn_serial) {
+            return CertUtilsError::MemoryAllocation;
+        }
+        BN_zero(bn_serial.get());
+    }
+
+    // Set the certificate serialNumber
+    ASN1_INTEGER_Ptr serialNumber(ASN1_INTEGER_new());
+    if (!serialNumber || !BN_to_ASN1_INTEGER(bn_serial.get(), serialNumber.get()) ||
+        !X509_set_serialNumber(certificate.get(), serialNumber.get() /* Don't release; copied */))
+        return CertUtilsError::BoringSsl;
+
+    // Set Subject Name
+    auto subjectName = makeCommonName(subject);
+    if (auto x509_subject = std::get_if<X509_NAME_Ptr>(&subjectName)) {
+        if (!X509_set_subject_name(certificate.get(), x509_subject->get() /* copied */)) {
+            return CertUtilsError::BoringSsl;
+        }
+    } else {
+        return std::get<CertUtilsError>(subjectName);
+    }
+
+    time_t notBeforeTime = saturate<time_t>(activeDateTimeMilliSeconds / 1000);
+    // Set activation date.
+    ASN1_TIME_Ptr notBefore(ASN1_TIME_new());
+    if (!notBefore || !ASN1_TIME_set(notBefore.get(), notBeforeTime) ||
+        !X509_set_notBefore(certificate.get(), notBefore.get() /* Don't release; copied */))
+        return CertUtilsError::BoringSsl;
+
+    // Set expiration date.
+    time_t notAfterTime;
+    notAfterTime = saturate<time_t>(usageExpireDateTimeMilliSeconds / 1000);
+
+    ASN1_TIME_Ptr notAfter(ASN1_TIME_new());
+    if (!notAfter || !ASN1_TIME_set(notAfter.get(), notAfterTime) ||
+        !X509_set_notAfter(certificate.get(), notAfter.get() /* Don't release; copied */)) {
+        return CertUtilsError::BoringSsl;
+    }
+
+    return certificate;
+}
+
+std::variant<CertUtilsError, X509_Ptr>
+makeCert(const EVP_PKEY* evp_pkey,
+         std::optional<std::reference_wrapper<const std::vector<uint8_t>>> serial,
+         std::optional<std::reference_wrapper<const std::vector<uint8_t>>> subject,
+         const int64_t activeDateTimeMilliSeconds, const int64_t usageExpireDateTimeMilliSeconds,
+         bool addSubjectKeyIdEx, std::optional<KeyUsageExtension> keyUsageEx,
+         std::optional<BasicConstraintsExtension> basicConstraints) {
+
+    // Make the rump certificate with serial, subject, not before and not after dates.
+    auto certificateV =
+        makeCertRump(serial, subject, activeDateTimeMilliSeconds, usageExpireDateTimeMilliSeconds);
+    if (auto error = std::get_if<CertUtilsError>(&certificateV)) {
+        return *error;
+    }
+    auto certificate = std::move(std::get<X509_Ptr>(certificateV));
+
+    // Set the public key.
+    if (!X509_set_pubkey(certificate.get(), const_cast<EVP_PKEY*>(evp_pkey))) {
+        return CertUtilsError::BoringSsl;
+    }
+
+    if (keyUsageEx) {
+        // Make and add the key usage extension.
+        auto key_usage_extensionV = makeKeyUsageExtension(
+            keyUsageEx->isSigningKey, keyUsageEx->isEncryptionKey, keyUsageEx->isCertificationKey);
+        if (auto error = std::get_if<CertUtilsError>(&key_usage_extensionV)) {
+            return *error;
+        }
+        auto key_usage_extension = std::move(std::get<ASN1_BIT_STRING_Ptr>(key_usage_extensionV));
+        if (!X509_add1_ext_i2d(certificate.get(), NID_key_usage,
+                               key_usage_extension.get() /* Don't release; copied */,
+                               true /* critical */, 0 /* flags */)) {
+            return CertUtilsError::BoringSsl;
+        }
+    }
+
+    if (basicConstraints) {
+        // Make and add basic constraints
+        auto basic_constraints_extensionV =
+            makeBasicConstraintsExtension(basicConstraints->isCa, basicConstraints->pathLength);
+        if (auto error = std::get_if<CertUtilsError>(&basic_constraints_extensionV)) {
+            return *error;
+        }
+        auto basic_constraints_extension =
+            std::move(std::get<BASIC_CONSTRAINTS_Ptr>(basic_constraints_extensionV));
+        if (!X509_add1_ext_i2d(certificate.get(), NID_basic_constraints,
+                               basic_constraints_extension.get() /* Don't release; copied */,
+                               true /* critical */, 0 /* flags */)) {
+            return CertUtilsError::BoringSsl;
+        }
+    }
+
+    if (addSubjectKeyIdEx) {
+        // Make and add subject key id extension.
+        auto keyidV = makeKeyId(certificate.get());
+        if (auto error = std::get_if<CertUtilsError>(&keyidV)) {
+            return *error;
+        }
+        auto& keyid = std::get<std::vector<uint8_t>>(keyidV);
+
+        auto subject_key_extensionV = makeSubjectKeyExtension(keyid);
+        if (auto error = std::get_if<CertUtilsError>(&subject_key_extensionV)) {
+            return *error;
+        }
+        auto subject_key_extension =
+            std::move(std::get<ASN1_OCTET_STRING_Ptr>(subject_key_extensionV));
+        if (!X509_add1_ext_i2d(certificate.get(), NID_subject_key_identifier,
+                               subject_key_extension.get() /* Don't release; copied */,
+                               false /* critical */, 0 /* flags */)) {
+            return CertUtilsError::BoringSsl;
+        }
+    }
+
+    return certificate;
+}
+
+CertUtilsError setIssuer(X509* cert, const X509* signingCert, bool addAuthKeyExt) {
+
+    X509_NAME* issuerName(X509_get_subject_name(signingCert));
+
+    // Set Issuer Name
+    if (issuerName) {
+        if (!X509_set_issuer_name(cert, issuerName /* copied */)) {
+            return CertUtilsError::BoringSsl;
+        }
+    } else {
+        return CertUtilsError::Encoding;
+    }
+
+    if (addAuthKeyExt) {
+        // Make and add authority key extension - self signed.
+        auto keyidV = makeKeyId(signingCert);
+        if (auto error = std::get_if<CertUtilsError>(&keyidV)) {
+            return *error;
+        }
+        auto& keyid = std::get<std::vector<uint8_t>>(keyidV);
+
+        auto auth_key_extensionV = makeAuthorityKeyExtension(keyid);
+        if (auto error = std::get_if<CertUtilsError>(&auth_key_extensionV)) {
+            return *error;
+        }
+        auto auth_key_extension = std::move(std::get<AUTHORITY_KEYID_Ptr>(auth_key_extensionV));
+        if (!X509_add1_ext_i2d(cert, NID_authority_key_identifier, auth_key_extension.get(), false,
+                               0)) {
+            return CertUtilsError::BoringSsl;
+        }
+    }
+    return CertUtilsError::Ok;
+}
+
+// Takes a certificate a signing certificate and the raw private signing_key. And signs
+// the certificate with the latter.
+CertUtilsError signCert(X509* certificate, EVP_PKEY* signing_key) {
+
+    if (certificate == nullptr) {
+        return CertUtilsError::UnexpectedNullPointer;
+    }
+
+    if (!X509_sign(certificate, signing_key, EVP_sha256())) {
+        return CertUtilsError::BoringSsl;
+    }
+
+    return CertUtilsError::Ok;
+}
+
+std::variant<CertUtilsError, std::vector<uint8_t>> encodeCert(X509* certificate) {
+    int len = i2d_X509(certificate, nullptr);
+    if (len < 0) {
+        return CertUtilsError::BoringSsl;
+    }
+
+    auto result = std::vector<uint8_t>(len);
+    uint8_t* p = result.data();
+
+    if (i2d_X509(certificate, &p) < 0) {
+        return CertUtilsError::BoringSsl;
+    }
+    return result;
+}
+
+CertUtilsError setRsaDigestAlgorField(X509_ALGOR** alg_ptr, const EVP_MD* digest) {
+    if (alg_ptr == nullptr || digest == nullptr) {
+        return CertUtilsError::UnexpectedNullPointer;
+    }
+    *alg_ptr = X509_ALGOR_new();
+    if (*alg_ptr == nullptr) {
+        return CertUtilsError::MemoryAllocation;
+    }
+    X509_ALGOR_set_md(*alg_ptr, digest);
+    return CertUtilsError::Ok;
+}
+
+CertUtilsError setPssMaskGeneratorField(X509_ALGOR** alg_ptr, const EVP_MD* digest) {
+    X509_ALGOR* mgf1_digest = nullptr;
+    if (auto error = setRsaDigestAlgorField(&mgf1_digest, digest)) {
+        return error;
+    }
+    X509_ALGOR_Ptr mgf1_digest_ptr(mgf1_digest);
+
+    ASN1_OCTET_STRING* mgf1_digest_algor_str = nullptr;
+    if (!ASN1_item_pack(mgf1_digest, ASN1_ITEM_rptr(X509_ALGOR), &mgf1_digest_algor_str)) {
+        return CertUtilsError::Encoding;
+    }
+    ASN1_OCTET_STRING_Ptr mgf1_digest_algor_str_ptr(mgf1_digest_algor_str);
+
+    *alg_ptr = X509_ALGOR_new();
+    if (*alg_ptr == nullptr) {
+        return CertUtilsError::MemoryAllocation;
+    }
+    X509_ALGOR_set0(*alg_ptr, OBJ_nid2obj(NID_mgf1), V_ASN1_SEQUENCE, mgf1_digest_algor_str);
+    // *alg_ptr took ownership of the octet string
+    mgf1_digest_algor_str_ptr.release();
+    return CertUtilsError::Ok;
+}
+
+static CertUtilsError setSaltLength(RSA_PSS_PARAMS* pss_params, unsigned length) {
+    pss_params->saltLength = ASN1_INTEGER_new();
+    if (pss_params->saltLength == nullptr) {
+        return CertUtilsError::MemoryAllocation;
+    }
+    if (!ASN1_INTEGER_set(pss_params->saltLength, length)) {
+        return CertUtilsError::Encoding;
+    };
+    return CertUtilsError::Ok;
+}
+
+std::variant<CertUtilsError, ASN1_STRING_Ptr> buildRsaPssParameter(Digest digest) {
+    RSA_PSS_PARAMS_Ptr pss(RSA_PSS_PARAMS_new());
+    if (!pss) {
+        return CertUtilsError::MemoryAllocation;
+    }
+
+    const EVP_MD* md = nullptr;
+
+    switch (digest) {
+    case Digest::SHA1:
+        break;
+    case Digest::SHA224:
+        md = EVP_sha224();
+        break;
+    case Digest::SHA256:
+        md = EVP_sha256();
+        break;
+    case Digest::SHA384:
+        md = EVP_sha384();
+        break;
+    case Digest::SHA512:
+        md = EVP_sha512();
+        break;
+    default:
+        return CertUtilsError::InvalidArgument;
+    }
+
+    if (md != nullptr) {
+        if (auto error = setSaltLength(pss.get(), EVP_MD_size(md))) {
+            return error;
+        }
+        if (auto error = setRsaDigestAlgorField(&pss->hashAlgorithm, md)) {
+            return error;
+        }
+        if (auto error = setPssMaskGeneratorField(&pss->maskGenAlgorithm, md)) {
+            return error;
+        }
+    }
+
+    ASN1_STRING* algo_str = nullptr;
+    if (!ASN1_item_pack(pss.get(), ASN1_ITEM_rptr(RSA_PSS_PARAMS), &algo_str)) {
+        return CertUtilsError::BoringSsl;
+    }
+
+    return ASN1_STRING_Ptr(algo_str);
+}
+
+CertUtilsError makeAndSetAlgo(X509_ALGOR* algo_field, Algo algo, Padding padding, Digest digest) {
+    if (algo_field == nullptr) {
+        return CertUtilsError::UnexpectedNullPointer;
+    }
+    ASN1_STRING_Ptr param;
+    int param_type = V_ASN1_UNDEF;
+    int nid = 0;
+    switch (algo) {
+    case Algo::ECDSA:
+        switch (digest) {
+        case Digest::SHA1:
+            nid = NID_ecdsa_with_SHA1;
+            break;
+        case Digest::SHA224:
+            nid = NID_ecdsa_with_SHA224;
+            break;
+        case Digest::SHA256:
+            nid = NID_ecdsa_with_SHA256;
+            break;
+        case Digest::SHA384:
+            nid = NID_ecdsa_with_SHA384;
+            break;
+        case Digest::SHA512:
+            nid = NID_ecdsa_with_SHA512;
+            break;
+        default:
+            return CertUtilsError::InvalidArgument;
+        }
+        break;
+    case Algo::RSA:
+        switch (padding) {
+        case Padding::PKCS1_5:
+            param_type = V_ASN1_NULL;
+            switch (digest) {
+            case Digest::SHA1:
+                nid = NID_sha1WithRSAEncryption;
+                break;
+            case Digest::SHA224:
+                nid = NID_sha224WithRSAEncryption;
+                break;
+            case Digest::SHA256:
+                nid = NID_sha256WithRSAEncryption;
+                break;
+            case Digest::SHA384:
+                nid = NID_sha384WithRSAEncryption;
+                break;
+            case Digest::SHA512:
+                nid = NID_sha512WithRSAEncryption;
+                break;
+            default:
+                return CertUtilsError::InvalidArgument;
+            }
+            break;
+        case Padding::PSS: {
+            auto v = buildRsaPssParameter(digest);
+            if (auto param_str = std::get_if<ASN1_STRING_Ptr>(&v)) {
+                param = std::move(*param_str);
+                param_type = V_ASN1_SEQUENCE;
+                nid = NID_rsassaPss;
+            } else {
+                return std::get<CertUtilsError>(v);
+            }
+            break;
+        }
+        default:
+            return CertUtilsError::InvalidArgument;
+        }
+        break;
+    default:
+        return CertUtilsError::InvalidArgument;
+    }
+
+    if (!X509_ALGOR_set0(algo_field, OBJ_nid2obj(nid), param_type, param.get())) {
+        return CertUtilsError::Encoding;
+    }
+    // The X509 struct took ownership.
+    param.release();
+    return CertUtilsError::Ok;
+}
+
+// This function allows for signing a
+CertUtilsError signCertWith(X509* certificate,
+                            std::function<std::vector<uint8_t>(const uint8_t*, size_t)> sign,
+                            Algo algo, Padding padding, Digest digest) {
+    if (auto error = makeAndSetAlgo(certificate->sig_alg, algo, padding, digest)) {
+        return error;
+    }
+    if (auto error = makeAndSetAlgo(certificate->cert_info->signature, algo, padding, digest)) {
+        return error;
+    }
+
+    uint8_t* cert_buf = nullptr;
+    int buf_len = i2d_re_X509_tbs(certificate, &cert_buf);
+    if (buf_len < 0) {
+        return CertUtilsError::Encoding;
+    }
+
+    bssl::UniquePtr<uint8_t> free_cert_buf(cert_buf);
+    auto signature = sign(cert_buf, buf_len);
+    if (signature.empty()) {
+        return CertUtilsError::SignatureFailed;
+    }
+
+    if (!ASN1_STRING_set(certificate->signature, signature.data(), signature.size())) {
+        return CertUtilsError::BoringSsl;
+    }
+
+    certificate->signature->flags &= ~(0x07);
+    certificate->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
+    return CertUtilsError::Ok;
+}
+
+}  // namespace keystore
diff --git a/keystore2/src/crypto/crypto.cpp b/keystore2/src/crypto/crypto.cpp
new file mode 100644
index 0000000..e4a1ac3
--- /dev/null
+++ b/keystore2/src/crypto/crypto.cpp
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "keystore2"
+
+#include "crypto.hpp"
+
+#include <log/log.h>
+#include <openssl/aes.h>
+#include <openssl/ec.h>
+#include <openssl/ec_key.h>
+#include <openssl/ecdh.h>
+#include <openssl/evp.h>
+#include <openssl/hkdf.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+
+#include <vector>
+
+// Copied from system/security/keystore/blob.h.
+
+constexpr size_t kGcmTagLength = 128 / 8;
+constexpr size_t kAes128KeySizeBytes = 128 / 8;
+
+// Copied from system/security/keystore/blob.cpp.
+
+#if defined(__clang__)
+#define OPTNONE __attribute__((optnone))
+#elif defined(__GNUC__)
+#define OPTNONE __attribute__((optimize("O0")))
+#else
+#error Need a definition for OPTNONE
+#endif
+
+class ArrayEraser {
+  public:
+    ArrayEraser(uint8_t* arr, size_t size) : mArr(arr), mSize(size) {}
+    OPTNONE ~ArrayEraser() { std::fill(mArr, mArr + mSize, 0); }
+
+  private:
+    volatile uint8_t* mArr;
+    size_t mSize;
+};
+
+/**
+ * Returns a EVP_CIPHER appropriate for the given key size.
+ */
+const EVP_CIPHER* getAesCipherForKey(size_t key_size) {
+    const EVP_CIPHER* cipher = EVP_aes_256_gcm();
+    if (key_size == kAes128KeySizeBytes) {
+        cipher = EVP_aes_128_gcm();
+    }
+    return cipher;
+}
+
+bool randomBytes(uint8_t* out, size_t len) {
+    return RAND_bytes(out, len);
+}
+
+/*
+ * Encrypt 'len' data at 'in' with AES-GCM, using 128-bit or 256-bit key at 'key', 96-bit IV at
+ * 'iv' and write output to 'out' (which may be the same location as 'in') and 128-bit tag to
+ * 'tag'.
+ */
+bool AES_gcm_encrypt(const uint8_t* in, uint8_t* out, size_t len, const uint8_t* key,
+                     size_t key_size, const uint8_t* iv, uint8_t* tag) {
+
+    // There can be 128-bit and 256-bit keys
+    const EVP_CIPHER* cipher = getAesCipherForKey(key_size);
+
+    bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
+
+    EVP_EncryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key, iv);
+    EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
+
+    std::vector<uint8_t> out_tmp(len);
+    uint8_t* out_pos = out_tmp.data();
+    int out_len;
+
+    EVP_EncryptUpdate(ctx.get(), out_pos, &out_len, in, len);
+    out_pos += out_len;
+    EVP_EncryptFinal_ex(ctx.get(), out_pos, &out_len);
+    out_pos += out_len;
+    if (out_pos - out_tmp.data() != static_cast<ssize_t>(len)) {
+        ALOGD("Encrypted ciphertext is the wrong size, expected %zu, got %zd", len,
+              out_pos - out_tmp.data());
+        return false;
+    }
+
+    std::copy(out_tmp.data(), out_pos, out);
+    EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, kGcmTagLength, tag);
+
+    return true;
+}
+
+/*
+ * Decrypt 'len' data at 'in' with AES-GCM, using 128-bit or 256-bit key at 'key', 96-bit IV at
+ * 'iv', checking 128-bit tag at 'tag' and writing plaintext to 'out'(which may be the same
+ * location as 'in').
+ */
+bool AES_gcm_decrypt(const uint8_t* in, uint8_t* out, size_t len, const uint8_t* key,
+                     size_t key_size, const uint8_t* iv, const uint8_t* tag) {
+
+    // There can be 128-bit and 256-bit keys
+    const EVP_CIPHER* cipher = getAesCipherForKey(key_size);
+
+    bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
+
+    EVP_DecryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key, iv);
+    EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
+    EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, kGcmTagLength, const_cast<uint8_t*>(tag));
+
+    std::vector<uint8_t> out_tmp(len);
+    ArrayEraser out_eraser(out_tmp.data(), len);
+    uint8_t* out_pos = out_tmp.data();
+    int out_len;
+
+    EVP_DecryptUpdate(ctx.get(), out_pos, &out_len, in, len);
+    out_pos += out_len;
+    if (!EVP_DecryptFinal_ex(ctx.get(), out_pos, &out_len)) {
+        ALOGE("Failed to decrypt blob; ciphertext or tag is likely corrupted");
+        return false;
+    }
+    out_pos += out_len;
+    if (out_pos - out_tmp.data() != static_cast<ssize_t>(len)) {
+        ALOGE("Encrypted plaintext is the wrong size, expected %zu, got %zd", len,
+              out_pos - out_tmp.data());
+        return false;
+    }
+
+    std::copy(out_tmp.data(), out_pos, out);
+
+    return true;
+}
+
+// Copied from system/security/keystore/keymaster_enforcement.cpp.
+
+class EvpMdCtx {
+  public:
+    EvpMdCtx() { EVP_MD_CTX_init(&ctx_); }
+    ~EvpMdCtx() { EVP_MD_CTX_cleanup(&ctx_); }
+
+    EVP_MD_CTX* get() { return &ctx_; }
+
+  private:
+    EVP_MD_CTX ctx_;
+};
+
+bool CreateKeyId(const uint8_t* key_blob, size_t len, km_id_t* out_id) {
+    EvpMdCtx ctx;
+
+    uint8_t hash[EVP_MAX_MD_SIZE];
+    unsigned int hash_len;
+    if (EVP_DigestInit_ex(ctx.get(), EVP_sha256(), nullptr /* ENGINE */) &&
+        EVP_DigestUpdate(ctx.get(), key_blob, len) &&
+        EVP_DigestFinal_ex(ctx.get(), hash, &hash_len)) {
+        assert(hash_len >= sizeof(*out_id));
+        memcpy(out_id, hash, sizeof(*out_id));
+        return true;
+    }
+
+    return false;
+}
+
+// Copied from system/security/keystore/user_state.h
+
+static constexpr size_t SALT_SIZE = 16;
+
+// Copied from system/security/keystore/user_state.cpp.
+
+void generateKeyFromPassword(uint8_t* key, size_t key_len, const char* pw, size_t pw_len,
+                             const uint8_t* salt) {
+    size_t saltSize;
+    if (salt != nullptr) {
+        saltSize = SALT_SIZE;
+    } else {
+        // Pre-gingerbread used this hardwired salt, readMasterKey will rewrite these when found
+        salt = reinterpret_cast<const uint8_t*>("keystore");
+        // sizeof = 9, not strlen = 8
+        saltSize = sizeof("keystore");
+    }
+
+    const EVP_MD* digest = EVP_sha256();
+
+    // SHA1 was used prior to increasing the key size
+    if (key_len == kAes128KeySizeBytes) {
+        digest = EVP_sha1();
+    }
+
+    PKCS5_PBKDF2_HMAC(pw, pw_len, salt, saltSize, 8192, digest, key_len, key);
+}
+
+// New code.
+
+bool HKDFExtract(uint8_t* out_key, size_t* out_len, const uint8_t* secret, size_t secret_len,
+                 const uint8_t* salt, size_t salt_len) {
+    const EVP_MD* digest = EVP_sha256();
+    auto result = HKDF_extract(out_key, out_len, digest, secret, secret_len, salt, salt_len);
+    return result == 1;
+}
+
+bool HKDFExpand(uint8_t* out_key, size_t out_len, const uint8_t* prk, size_t prk_len,
+                const uint8_t* info, size_t info_len) {
+    const EVP_MD* digest = EVP_sha256();
+    auto result = HKDF_expand(out_key, out_len, digest, prk, prk_len, info, info_len);
+    return result == 1;
+}
+
+int ECDHComputeKey(void* out, const EC_POINT* pub_key, const EC_KEY* priv_key) {
+    return ECDH_compute_key(out, EC_MAX_BYTES, pub_key, priv_key, nullptr);
+}
+
+EC_KEY* ECKEYGenerateKey() {
+    EC_KEY* key = EC_KEY_new();
+    EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+    EC_KEY_set_group(key, group);
+    auto result = EC_KEY_generate_key(key);
+    if (result == 0) {
+        EC_GROUP_free(group);
+        EC_KEY_free(key);
+        return nullptr;
+    }
+    return key;
+}
+
+size_t ECKEYMarshalPrivateKey(const EC_KEY* priv_key, uint8_t* buf, size_t len) {
+    CBB cbb;
+    size_t out_len;
+    if (!CBB_init_fixed(&cbb, buf, len) ||
+        !EC_KEY_marshal_private_key(&cbb, priv_key, EC_PKEY_NO_PARAMETERS | EC_PKEY_NO_PUBKEY) ||
+        !CBB_finish(&cbb, nullptr, &out_len)) {
+        return 0;
+    } else {
+        return out_len;
+    }
+}
+
+EC_KEY* ECKEYParsePrivateKey(const uint8_t* buf, size_t len) {
+    CBS cbs;
+    CBS_init(&cbs, buf, len);
+    EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+    auto result = EC_KEY_parse_private_key(&cbs, group);
+    EC_GROUP_free(group);
+    if (result != nullptr && CBS_len(&cbs) != 0) {
+        EC_KEY_free(result);
+        return nullptr;
+    }
+    return result;
+}
+
+size_t ECPOINTPoint2Oct(const EC_POINT* point, uint8_t* buf, size_t len) {
+    EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+    point_conversion_form_t form = POINT_CONVERSION_UNCOMPRESSED;
+    auto result = EC_POINT_point2oct(group, point, form, buf, len, nullptr);
+    EC_GROUP_free(group);
+    return result;
+}
+
+EC_POINT* ECPOINTOct2Point(const uint8_t* buf, size_t len) {
+    EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+    EC_POINT* point = EC_POINT_new(group);
+    auto result = EC_POINT_oct2point(group, point, buf, len, nullptr);
+    EC_GROUP_free(group);
+    if (result == 0) {
+        EC_POINT_free(point);
+        return nullptr;
+    }
+    return point;
+}
+
+int extractSubjectFromCertificate(const uint8_t* cert_buf, size_t cert_len, uint8_t* subject_buf,
+                                  size_t subject_buf_len) {
+    if (!cert_buf || !subject_buf) {
+        ALOGE("extractSubjectFromCertificate: received null pointer");
+        return 0;
+    }
+
+    const uint8_t* p = cert_buf;
+    bssl::UniquePtr<X509> cert(d2i_X509(nullptr /* Allocate X509 struct */, &p, cert_len));
+    if (!cert) {
+        ALOGE("extractSubjectFromCertificate: failed to parse certificate");
+        return 0;
+    }
+
+    X509_NAME* subject = X509_get_subject_name(cert.get());
+    if (!subject) {
+        ALOGE("extractSubjectFromCertificate: failed to retrieve subject name");
+        return 0;
+    }
+
+    int subject_len = i2d_X509_NAME(subject, nullptr /* Don't copy the data */);
+    if (subject_len < 0) {
+        ALOGE("extractSubjectFromCertificate: error obtaining encoded subject name length");
+        return 0;
+    }
+
+    if (subject_len > subject_buf_len) {
+        // Return the subject length, negated, so the caller knows how much
+        // buffer space is required.
+        ALOGI("extractSubjectFromCertificate: needed %d bytes for subject, caller provided %zu",
+              subject_len, subject_buf_len);
+        return -subject_len;
+    }
+
+    // subject_buf has enough space.
+    uint8_t* tmp = subject_buf;
+    return i2d_X509_NAME(subject, &tmp);
+}
diff --git a/keystore2/src/crypto/crypto.hpp b/keystore2/src/crypto/crypto.hpp
new file mode 100644
index 0000000..f841eb3
--- /dev/null
+++ b/keystore2/src/crypto/crypto.hpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef __CRYPTO_H__
+#define __CRYPTO_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+extern "C" {
+  bool randomBytes(uint8_t* out, size_t len);
+  bool AES_gcm_encrypt(const uint8_t* in, uint8_t* out, size_t len,
+                       const uint8_t* key, size_t key_size, const uint8_t* iv, uint8_t* tag);
+  bool AES_gcm_decrypt(const uint8_t* in, uint8_t* out, size_t len,
+                       const uint8_t* key, size_t key_size, const uint8_t* iv,
+                       const uint8_t* tag);
+
+  // Copied from system/security/keystore/keymaster_enforcement.h.
+  typedef uint64_t km_id_t;
+
+  bool CreateKeyId(const uint8_t* key_blob, size_t len, km_id_t* out_id);
+
+  void generateKeyFromPassword(uint8_t* key, size_t key_len, const char* pw,
+                               size_t pw_len, const uint8_t* salt);
+
+  #include "openssl/digest.h"
+  #include "openssl/ec_key.h"
+
+  bool HKDFExtract(uint8_t *out_key, size_t *out_len,
+                   const uint8_t *secret, size_t secret_len,
+                   const uint8_t *salt, size_t salt_len);
+
+  bool HKDFExpand(uint8_t *out_key, size_t out_len,
+                  const uint8_t *prk, size_t prk_len,
+                  const uint8_t *info, size_t info_len);
+
+  // We define this as field_elem_size.
+  static const size_t EC_MAX_BYTES = 32;
+
+  int ECDHComputeKey(void *out, const EC_POINT *pub_key, const EC_KEY *priv_key);
+
+  EC_KEY* ECKEYGenerateKey();
+
+  size_t ECKEYMarshalPrivateKey(const EC_KEY *priv_key, uint8_t *buf, size_t len);
+
+  EC_KEY* ECKEYParsePrivateKey(const uint8_t *buf, size_t len);
+
+  size_t ECPOINTPoint2Oct(const EC_POINT *point, uint8_t *buf, size_t len);
+
+  EC_POINT* ECPOINTOct2Point(const uint8_t *buf, size_t len);
+
+}
+
+// Parse a DER-encoded X.509 certificate contained in cert_buf, with length
+// cert_len, extract the subject, DER-encode it and write the result to
+// subject_buf, which has subject_buf_len capacity.
+//
+// Because the length of the subject is unknown, and because we'd like to (a) be
+// able to handle subjects of any size and (b) avoid parsing the certificate
+// twice most of the time, once to discover the length and once to parse it, the
+// return value is overloaded.
+//
+// If the return value > 0 it specifies the number of bytes written into
+// subject_buf; the operation was successful.
+//
+// If the return value == 0, certificate parsing failed unrecoverably.  The
+// reason will be logged.
+//
+// If the return value < 0, the operation failed because the subject size >
+// subject_buf_len.  The return value is -(subject_size), where subject_size is
+// the size of the extracted DER-encoded subject field.  Call
+// extractSubjectFromCertificate again with a sufficiently-large buffer.
+int extractSubjectFromCertificate(const uint8_t* cert_buf, size_t cert_len,
+                                  uint8_t* subject_buf, size_t subject_buf_len);
+
+#endif  //  __CRYPTO_H__
diff --git a/keystore2/src/crypto/error.rs b/keystore2/src/crypto/error.rs
new file mode 100644
index 0000000..a369012
--- /dev/null
+++ b/keystore2/src/crypto/error.rs
@@ -0,0 +1,96 @@
+// 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.
+
+//! This module implements Error for the keystore2_crypto library.
+
+/// Crypto specific error codes.
+#[derive(Debug, thiserror::Error, Eq, PartialEq)]
+pub enum Error {
+    /// This is returned if the C/C++ implementation of AES_gcm_decrypt returned false.
+    #[error("Failed to decrypt.")]
+    DecryptionFailed,
+
+    /// This is returned if the C/C++ implementation of AES_gcm_encrypt returned false.
+    #[error("Failed to encrypt.")]
+    EncryptionFailed,
+
+    /// The initialization vector has the wrong length.
+    #[error("Invalid IV length.")]
+    InvalidIvLength,
+
+    /// The aead tag has the wrong length.
+    #[error("Invalid AEAD tag length.")]
+    InvalidAeadTagLength,
+
+    /// The key has the wrong length.
+    #[error("Invalid key length.")]
+    InvalidKeyLength,
+
+    /// Invalid data length.
+    #[error("Invalid data length.")]
+    InvalidDataLength,
+
+    /// Invalid salt length.
+    #[error("Invalid salt length.")]
+    InvalidSaltLength,
+
+    /// Random number generation failed.
+    #[error("Random number generation failed.")]
+    RandomNumberGenerationFailed,
+
+    /// ZVec construction failed.
+    #[error(transparent)]
+    LayoutError(#[from] std::alloc::LayoutErr),
+
+    /// Nix error.
+    #[error(transparent)]
+    NixError(#[from] nix::Error),
+
+    /// This is returned if the C implementation of HKDFExtract returned false
+    /// or otherwise failed.
+    #[error("Failed to extract.")]
+    HKDFExtractFailed,
+
+    /// This is returned if the C implementation of HKDFExpand returned false.
+    #[error("Failed to expand.")]
+    HKDFExpandFailed,
+
+    /// This is returned if the C implementation of ECDHComputeKey returned -1.
+    #[error("Failed to compute ecdh key.")]
+    ECDHComputeKeyFailed,
+
+    /// This is returned if the C implementation of ECKEYGenerateKey returned null.
+    #[error("Failed to generate key.")]
+    ECKEYGenerateKeyFailed,
+
+    /// This is returned if the C implementation of ECKEYMarshalPrivateKey returned 0.
+    #[error("Failed to marshal private key.")]
+    ECKEYMarshalPrivateKeyFailed,
+
+    /// This is returned if the C implementation of ECKEYParsePrivateKey returned null.
+    #[error("Failed to parse private key.")]
+    ECKEYParsePrivateKeyFailed,
+
+    /// This is returned if the C implementation of ECPOINTPoint2Oct returned 0.
+    #[error("Failed to convert point to oct.")]
+    ECPoint2OctFailed,
+
+    /// This is returned if the C implementation of ECPOINTOct2Point returned null.
+    #[error("Failed to convert oct to point.")]
+    ECOct2PointFailed,
+
+    /// This is returned if the C implementation of extractSubjectFromCertificate failed.
+    #[error("Failed to extract certificate subject.")]
+    ExtractSubjectFailed,
+}
diff --git a/keystore2/src/crypto/include/certificate_utils.h b/keystore2/src/crypto/include/certificate_utils.h
new file mode 100644
index 0000000..6c25b9a
--- /dev/null
+++ b/keystore2/src/crypto/include/certificate_utils.h
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <stdint.h>
+
+#include <memory>
+#include <optional>
+#include <variant>
+
+namespace keystore {
+// We use boringssl error codes. Error codes that we add are folded into LIB_USER.
+// The CertificateUtilsInternallErrorCodes enum should not be used by callers, instead use the
+// BoringSslError constant definitions below for error codes.
+using BoringSslError = unsigned long;
+
+#define DEFINE_OPENSSL_OBJECT_POINTER(name) using name##_Ptr = bssl::UniquePtr<name>
+
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_BIT_STRING);
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_STRING);
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_INTEGER);
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_OCTET_STRING);
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_TIME);
+DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY);
+DEFINE_OPENSSL_OBJECT_POINTER(X509);
+DEFINE_OPENSSL_OBJECT_POINTER(X509_EXTENSION);
+DEFINE_OPENSSL_OBJECT_POINTER(X509_NAME);
+DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY_CTX);
+
+class CertUtilsError {
+  public:
+    enum Error {
+        Ok = 0,
+        BoringSsl,
+        Encoding,
+        MemoryAllocation,
+        InvalidArgument,
+        UnexpectedNullPointer,
+        SignatureFailed,
+    };
+
+  private:
+    Error e_;
+
+  public:
+    constexpr CertUtilsError(Error e) : e_(e) {}
+    explicit constexpr operator bool() const { return e_ != Ok; }
+};
+
+struct KeyUsageExtension {
+    bool isSigningKey;
+    bool isEncryptionKey;
+    bool isCertificationKey;
+};
+
+struct BasicConstraintsExtension {
+    bool isCa;
+    std::optional<int> pathLength;
+};
+
+/**
+ * This function allocates and prepares an X509 certificate structure with all of the information
+ * given. Next steps would be to set an Issuer with `setIssuer` and sign it with either
+ * `signCert` or `signCertWith`.
+ * @param evp_pkey The public key that the certificate is issued for.
+ * @param serial The certificate serial number.
+ * @param subject The X509 name encoded subject common name.
+ * @param activeDateTimeMilliSeconds The not before date in epoch milliseconds.
+ * @param usageExpireDateTimeMilliSeconds The not after date in epoch milliseconds.
+ * @param addSubjectKeyIdEx If true, adds the subject key id extension.
+ * @param keyUsageEx If given adds, the key usage extension with the given flags.
+ * @param basicConstraints If given, adds the basic constraints extension with the given data.
+ * @return CertUtilsError::Ok on success.
+ */
+std::variant<CertUtilsError, X509_Ptr>
+makeCert(const EVP_PKEY* evp_pkey,                                                   //
+         std::optional<std::reference_wrapper<const std::vector<uint8_t>>> serial,   //
+         std::optional<std::reference_wrapper<const std::vector<uint8_t>>> subject,  //
+         const int64_t activeDateTimeMilliSeconds,                                   //
+         const int64_t usageExpireDateTimeMilliSeconds,                              //
+         bool addSubjectKeyIdEx,                                                     //
+         std::optional<KeyUsageExtension> keyUsageEx,                                //
+         std::optional<BasicConstraintsExtension> basicConstraints);                 //
+
+/**
+ * Takes the subject name from `signingCert` and sets it as issuer name in `cert`.
+ * if `addAuthKeyExt` is true it also generates the digest of the signing certificates's public key
+ * and sets it as authority key id extension in `cert`.
+ * For self signed certificates pass the same pointer to both `cert` and `signingCert`.
+ *
+ * @param cert
+ * @param signingCert
+ * @param addAuthKeyExt
+ * @return CertUtilsError::Ok on success.
+ */
+CertUtilsError setIssuer(X509* cert, const X509* signingCert, bool addAuthKeyExt);
+
+/**
+ * Takes a certificate, and private signing_key.
+ * Signs the certificate with the latter.
+ */
+CertUtilsError signCert(X509* certificate, EVP_PKEY* signing_key);
+
+enum class Digest {
+    SHA1,
+    SHA224,
+    SHA256,
+    SHA384,
+    SHA512,
+};
+
+enum class Algo {
+    ECDSA,
+    RSA,
+};
+
+enum class Padding {
+    Ignored,
+    PKCS1_5,
+    PSS,
+};
+
+/**
+ * Sets the signature specifier of the certificate and the signature according to the parameters
+ * c. Then it signs the certificate with the `sign` callback.
+ * IMPORTANT: The parameters `algo`, `padding`, and `digest` do not control the actual signing
+ * algorithm. The caller is responsible to provide a callback that actually performs the signature
+ * as described by this triplet.
+ * The `padding` argument is ignored if `algo` is Algo::EC.
+ * The `digest` field controls the message digest used, and, in case of RSA with PSS padding,
+ *              also the MGF1 digest.
+ *
+ * @param certificate X509 certificate structure to be signed.
+ * @param sign Callback function used to digest and sign the DER encoded to-be-signed certificate.
+ * @param algo Algorithm specifier used to encode the signing algorithm id of the X509 certificate.
+ * @param padding Padding specifier used to encode the signing algorithm id of the X509 certificate.
+ * @param digest Digest specifier used to encode the signing algorithm id of the X509 certificate.
+ * @return CertUtilsError::Ok on success.
+ */
+CertUtilsError signCertWith(X509* certificate,
+                            std::function<std::vector<uint8_t>(const uint8_t*, size_t)> sign,
+                            Algo algo, Padding padding, Digest digest);
+
+/**
+ * Generates the DER representation of the given signed X509 certificate structure.
+ * @param certificate
+ * @return std::vector<uint8_t> with the DER encoded certificate on success. An error code
+ *         otherwise.
+ */
+std::variant<CertUtilsError, std::vector<uint8_t>> encodeCert(X509* certificate);
+
+}  // namespace keystore
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
new file mode 100644
index 0000000..db23d1f
--- /dev/null
+++ b/keystore2/src/crypto/lib.rs
@@ -0,0 +1,568 @@
+// 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.
+
+//! This module implements safe wrappers for some crypto operations required by
+//! Keystore 2.0.
+
+mod error;
+mod zvec;
+pub use error::Error;
+use keystore2_crypto_bindgen::{
+    extractSubjectFromCertificate, generateKeyFromPassword, randomBytes, AES_gcm_decrypt,
+    AES_gcm_encrypt, ECDHComputeKey, ECKEYGenerateKey, ECKEYMarshalPrivateKey,
+    ECKEYParsePrivateKey, ECPOINTOct2Point, ECPOINTPoint2Oct, EC_KEY_free, EC_KEY_get0_public_key,
+    EC_POINT_free, HKDFExpand, HKDFExtract, EC_KEY, EC_MAX_BYTES, EC_POINT, EVP_MAX_MD_SIZE,
+};
+use std::convert::TryFrom;
+use std::convert::TryInto;
+use std::marker::PhantomData;
+pub use zvec::ZVec;
+
+/// Length of the expected initialization vector.
+pub const GCM_IV_LENGTH: usize = 12;
+/// Length of the expected AEAD TAG.
+pub const TAG_LENGTH: usize = 16;
+/// Length of an AES 256 key in bytes.
+pub const AES_256_KEY_LENGTH: usize = 32;
+/// Length of an AES 128 key in bytes.
+pub const AES_128_KEY_LENGTH: usize = 16;
+/// Length of the expected salt for key from password generation.
+pub const SALT_LENGTH: usize = 16;
+
+/// Older versions of keystore produced IVs with four extra
+/// ignored zero bytes at the end; recognise and trim those.
+pub const LEGACY_IV_LENGTH: usize = 16;
+
+/// Generate an AES256 key, essentially 32 random bytes from the underlying
+/// boringssl library discretely stuffed into a ZVec.
+pub fn generate_aes256_key() -> Result<ZVec, Error> {
+    // Safety: key has the same length as the requested number of random bytes.
+    let mut key = ZVec::new(AES_256_KEY_LENGTH)?;
+    if unsafe { randomBytes(key.as_mut_ptr(), AES_256_KEY_LENGTH) } {
+        Ok(key)
+    } else {
+        Err(Error::RandomNumberGenerationFailed)
+    }
+}
+
+/// Generate a salt.
+pub fn generate_salt() -> Result<Vec<u8>, Error> {
+    generate_random_data(SALT_LENGTH)
+}
+
+/// Generate random data of the given size.
+pub fn generate_random_data(size: usize) -> Result<Vec<u8>, Error> {
+    // Safety: data has the same length as the requested number of random bytes.
+    let mut data = vec![0; size];
+    if unsafe { randomBytes(data.as_mut_ptr(), size) } {
+        Ok(data)
+    } else {
+        Err(Error::RandomNumberGenerationFailed)
+    }
+}
+
+/// Uses AES GCM to decipher a message given an initialization vector, aead tag, and key.
+/// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based
+/// on the key length.
+/// This function returns the plaintext message in a ZVec because it is assumed that
+/// it contains sensitive information that should be zeroed from memory before its buffer is
+/// freed. Input key is taken as a slice for flexibility, but it is recommended that it is held
+/// in a ZVec as well.
+pub fn aes_gcm_decrypt(data: &[u8], iv: &[u8], tag: &[u8], key: &[u8]) -> Result<ZVec, Error> {
+    // Old versions of aes_gcm_encrypt produced 16 byte IVs, but the last four bytes were ignored
+    // so trim these to the correct size.
+    let iv = match iv.len() {
+        GCM_IV_LENGTH => iv,
+        LEGACY_IV_LENGTH => &iv[..GCM_IV_LENGTH],
+        _ => return Err(Error::InvalidIvLength),
+    };
+    if tag.len() != TAG_LENGTH {
+        return Err(Error::InvalidAeadTagLength);
+    }
+
+    match key.len() {
+        AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
+        _ => return Err(Error::InvalidKeyLength),
+    }
+
+    let mut result = ZVec::new(data.len())?;
+
+    // Safety: The first two arguments must point to buffers with a size given by the third
+    // argument. We pass the length of the key buffer along with the key.
+    // The `iv` buffer must be 12 bytes and the `tag` buffer 16, which we check above.
+    match unsafe {
+        AES_gcm_decrypt(
+            data.as_ptr(),
+            result.as_mut_ptr(),
+            data.len(),
+            key.as_ptr(),
+            key.len(),
+            iv.as_ptr(),
+            tag.as_ptr(),
+        )
+    } {
+        true => Ok(result),
+        false => Err(Error::DecryptionFailed),
+    }
+}
+
+/// Uses AES GCM to encrypt a message given a key.
+/// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based on
+/// the key length. The function generates an initialization vector. The return value is a tuple
+/// of `(ciphertext, iv, tag)`.
+pub fn aes_gcm_encrypt(plaintext: &[u8], key: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>), Error> {
+    let mut iv = vec![0; GCM_IV_LENGTH];
+    // Safety: iv is GCM_IV_LENGTH bytes long.
+    if !unsafe { randomBytes(iv.as_mut_ptr(), GCM_IV_LENGTH) } {
+        return Err(Error::RandomNumberGenerationFailed);
+    }
+
+    match key.len() {
+        AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
+        _ => return Err(Error::InvalidKeyLength),
+    }
+
+    let mut ciphertext: Vec<u8> = vec![0; plaintext.len()];
+    let mut tag: Vec<u8> = vec![0; TAG_LENGTH];
+    // Safety: The first two arguments must point to buffers with a size given by the third
+    // argument. We pass the length of the key buffer along with the key.
+    // The `iv` buffer must be 12 bytes and the `tag` buffer 16, which we check above.
+    if unsafe {
+        AES_gcm_encrypt(
+            plaintext.as_ptr(),
+            ciphertext.as_mut_ptr(),
+            plaintext.len(),
+            key.as_ptr(),
+            key.len(),
+            iv.as_ptr(),
+            tag.as_mut_ptr(),
+        )
+    } {
+        Ok((ciphertext, iv, tag))
+    } else {
+        Err(Error::EncryptionFailed)
+    }
+}
+
+/// Represents a "password" that can be used to key the PBKDF2 algorithm.
+pub enum Password<'a> {
+    /// Borrow an existing byte array
+    Ref(&'a [u8]),
+    /// Use an owned ZVec to store the key
+    Owned(ZVec),
+}
+
+impl<'a> From<&'a [u8]> for Password<'a> {
+    fn from(pw: &'a [u8]) -> Self {
+        Self::Ref(pw)
+    }
+}
+
+impl<'a> Password<'a> {
+    fn get_key(&'a self) -> &'a [u8] {
+        match self {
+            Self::Ref(b) => b,
+            Self::Owned(z) => &*z,
+        }
+    }
+
+    /// Generate a key from the given password and salt.
+    /// The salt must be exactly 16 bytes long.
+    /// Two key sizes are accepted: 16 and 32 bytes.
+    pub fn derive_key(&self, salt: Option<&[u8]>, key_length: usize) -> Result<ZVec, Error> {
+        let pw = self.get_key();
+
+        let salt: *const u8 = match salt {
+            Some(s) => {
+                if s.len() != SALT_LENGTH {
+                    return Err(Error::InvalidSaltLength);
+                }
+                s.as_ptr()
+            }
+            None => std::ptr::null(),
+        };
+
+        match key_length {
+            AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
+            _ => return Err(Error::InvalidKeyLength),
+        }
+
+        let mut result = ZVec::new(key_length)?;
+
+        unsafe {
+            generateKeyFromPassword(
+                result.as_mut_ptr(),
+                result.len(),
+                pw.as_ptr() as *const std::os::raw::c_char,
+                pw.len(),
+                salt,
+            )
+        };
+
+        Ok(result)
+    }
+
+    /// Try to make another Password object with the same data.
+    pub fn try_clone(&self) -> Result<Password<'static>, Error> {
+        Ok(Password::Owned(ZVec::try_from(self.get_key())?))
+    }
+}
+
+/// Calls the boringssl HKDF_extract function.
+pub fn hkdf_extract(secret: &[u8], salt: &[u8]) -> Result<ZVec, Error> {
+    let max_size: usize = EVP_MAX_MD_SIZE.try_into().unwrap();
+    let mut buf = ZVec::new(max_size)?;
+    let mut out_len = 0;
+    // Safety: HKDF_extract writes at most EVP_MAX_MD_SIZE bytes.
+    // Secret and salt point to valid buffers.
+    let result = unsafe {
+        HKDFExtract(
+            buf.as_mut_ptr(),
+            &mut out_len,
+            secret.as_ptr(),
+            secret.len(),
+            salt.as_ptr(),
+            salt.len(),
+        )
+    };
+    if !result {
+        return Err(Error::HKDFExtractFailed);
+    }
+    // According to the boringssl API, this should never happen.
+    if out_len > max_size {
+        return Err(Error::HKDFExtractFailed);
+    }
+    // HKDF_extract may write fewer than the maximum number of bytes, so we
+    // truncate the buffer.
+    buf.reduce_len(out_len);
+    Ok(buf)
+}
+
+/// Calls the boringssl HKDF_expand function.
+pub fn hkdf_expand(out_len: usize, prk: &[u8], info: &[u8]) -> Result<ZVec, Error> {
+    let mut buf = ZVec::new(out_len)?;
+    // Safety: HKDF_expand writes out_len bytes to the buffer.
+    // prk and info are valid buffers.
+    let result = unsafe {
+        HKDFExpand(buf.as_mut_ptr(), out_len, prk.as_ptr(), prk.len(), info.as_ptr(), info.len())
+    };
+    if !result {
+        return Err(Error::HKDFExpandFailed);
+    }
+    Ok(buf)
+}
+
+/// A wrapper around the boringssl EC_KEY type that frees it on drop.
+pub struct ECKey(*mut EC_KEY);
+
+impl Drop for ECKey {
+    fn drop(&mut self) {
+        // Safety: We only create ECKey objects for valid EC_KEYs
+        // and they are the sole owners of those keys.
+        unsafe { EC_KEY_free(self.0) };
+    }
+}
+
+// Wrappers around the boringssl EC_POINT type.
+// The EC_POINT can either be owned (and therefore mutable) or a pointer to an
+// EC_POINT owned by someone else (and thus immutable).  The former are freed
+// on drop.
+
+/// An owned EC_POINT object.
+pub struct OwnedECPoint(*mut EC_POINT);
+
+/// A pointer to an EC_POINT object.
+pub struct BorrowedECPoint<'a> {
+    data: *const EC_POINT,
+    phantom: PhantomData<&'a EC_POINT>,
+}
+
+impl OwnedECPoint {
+    /// Get the wrapped EC_POINT object.
+    pub fn get_point(&self) -> &EC_POINT {
+        // Safety: We only create OwnedECPoint objects for valid EC_POINTs.
+        unsafe { self.0.as_ref().unwrap() }
+    }
+}
+
+impl<'a> BorrowedECPoint<'a> {
+    /// Get the wrapped EC_POINT object.
+    pub fn get_point(&self) -> &EC_POINT {
+        // Safety: We only create BorrowedECPoint objects for valid EC_POINTs.
+        unsafe { self.data.as_ref().unwrap() }
+    }
+}
+
+impl Drop for OwnedECPoint {
+    fn drop(&mut self) {
+        // Safety: We only create OwnedECPoint objects for valid
+        // EC_POINTs and they are the sole owners of those points.
+        unsafe { EC_POINT_free(self.0) };
+    }
+}
+
+/// Calls the boringssl ECDH_compute_key function.
+pub fn ecdh_compute_key(pub_key: &EC_POINT, priv_key: &ECKey) -> Result<ZVec, Error> {
+    let mut buf = ZVec::new(EC_MAX_BYTES)?;
+    // Safety: Our ECDHComputeKey wrapper passes EC_MAX_BYES to ECDH_compute_key, which
+    // writes at most that many bytes to the output.
+    // The two keys are valid objects.
+    let result =
+        unsafe { ECDHComputeKey(buf.as_mut_ptr() as *mut std::ffi::c_void, pub_key, priv_key.0) };
+    if result == -1 {
+        return Err(Error::ECDHComputeKeyFailed);
+    }
+    let out_len = result.try_into().unwrap();
+    // According to the boringssl API, this should never happen.
+    if out_len > buf.len() {
+        return Err(Error::ECDHComputeKeyFailed);
+    }
+    // ECDH_compute_key may write fewer than the maximum number of bytes, so we
+    // truncate the buffer.
+    buf.reduce_len(out_len);
+    Ok(buf)
+}
+
+/// Calls the boringssl EC_KEY_generate_key function.
+pub fn ec_key_generate_key() -> Result<ECKey, Error> {
+    // Safety: Creates a new key on its own.
+    let key = unsafe { ECKEYGenerateKey() };
+    if key.is_null() {
+        return Err(Error::ECKEYGenerateKeyFailed);
+    }
+    Ok(ECKey(key))
+}
+
+/// Calls the boringssl EC_KEY_marshal_private_key function.
+pub fn ec_key_marshal_private_key(key: &ECKey) -> Result<ZVec, Error> {
+    let len = 39; // Empirically observed length of private key
+    let mut buf = ZVec::new(len)?;
+    // Safety: the key is valid.
+    // This will not write past the specified length of the buffer; if the
+    // len above is too short, it returns 0.
+    let written_len =
+        unsafe { ECKEYMarshalPrivateKey(key.0, buf.as_mut_ptr(), buf.len()) } as usize;
+    if written_len == len {
+        Ok(buf)
+    } else {
+        Err(Error::ECKEYMarshalPrivateKeyFailed)
+    }
+}
+
+/// Calls the boringssl EC_KEY_parse_private_key function.
+pub fn ec_key_parse_private_key(buf: &[u8]) -> Result<ECKey, Error> {
+    // Safety: this will not read past the specified length of the buffer.
+    // It fails if less than the whole buffer is consumed.
+    let key = unsafe { ECKEYParsePrivateKey(buf.as_ptr(), buf.len()) };
+    if key.is_null() {
+        Err(Error::ECKEYParsePrivateKeyFailed)
+    } else {
+        Ok(ECKey(key))
+    }
+}
+
+/// Calls the boringssl EC_KEY_get0_public_key function.
+pub fn ec_key_get0_public_key(key: &ECKey) -> BorrowedECPoint {
+    // Safety: The key is valid.
+    // This returns a pointer to a key, so we create an immutable variant.
+    BorrowedECPoint { data: unsafe { EC_KEY_get0_public_key(key.0) }, phantom: PhantomData }
+}
+
+/// Calls the boringssl EC_POINT_point2oct.
+pub fn ec_point_point_to_oct(point: &EC_POINT) -> Result<Vec<u8>, Error> {
+    // We fix the length to 65 (1 + 2 * field_elem_size), as we get an error if it's too small.
+    let len = 65;
+    let mut buf = vec![0; len];
+    // Safety: EC_POINT_point2oct writes at most len bytes. The point is valid.
+    let result = unsafe { ECPOINTPoint2Oct(point, buf.as_mut_ptr(), len) };
+    if result == 0 {
+        return Err(Error::ECPoint2OctFailed);
+    }
+    // According to the boringssl API, this should never happen.
+    if result > len {
+        return Err(Error::ECPoint2OctFailed);
+    }
+    buf.resize(result, 0);
+    Ok(buf)
+}
+
+/// Calls the boringssl EC_POINT_oct2point function.
+pub fn ec_point_oct_to_point(buf: &[u8]) -> Result<OwnedECPoint, Error> {
+    // Safety: The buffer is valid.
+    let result = unsafe { ECPOINTOct2Point(buf.as_ptr(), buf.len()) };
+    if result.is_null() {
+        return Err(Error::ECPoint2OctFailed);
+    }
+    // Our C wrapper creates a new EC_POINT, so we mark this mutable and free
+    // it on drop.
+    Ok(OwnedECPoint(result))
+}
+
+/// Uses BoringSSL to extract the DER-encoded subject from a DER-encoded X.509 certificate.
+pub fn parse_subject_from_certificate(cert_buf: &[u8]) -> Result<Vec<u8>, Error> {
+    // Try with a 200-byte output buffer, should be enough in all but bizarre cases.
+    let mut retval = vec![0; 200];
+
+    // Safety: extractSubjectFromCertificate reads at most cert_buf.len() bytes from cert_buf and
+    // writes at most retval.len() bytes to retval.
+    let mut size = unsafe {
+        extractSubjectFromCertificate(
+            cert_buf.as_ptr(),
+            cert_buf.len(),
+            retval.as_mut_ptr(),
+            retval.len(),
+        )
+    };
+
+    if size == 0 {
+        return Err(Error::ExtractSubjectFailed);
+    }
+
+    if size < 0 {
+        // Our buffer wasn't big enough.  Make one that is just the right size and try again.
+        let negated_size = usize::try_from(-size).map_err(|_e| Error::ExtractSubjectFailed)?;
+        retval = vec![0; negated_size];
+
+        // Safety: extractSubjectFromCertificate reads at most cert_buf.len() bytes from cert_buf
+        // and writes at most retval.len() bytes to retval.
+        size = unsafe {
+            extractSubjectFromCertificate(
+                cert_buf.as_ptr(),
+                cert_buf.len(),
+                retval.as_mut_ptr(),
+                retval.len(),
+            )
+        };
+
+        if size <= 0 {
+            return Err(Error::ExtractSubjectFailed);
+        }
+    }
+
+    // Reduce buffer size to the amount written.
+    let safe_size = usize::try_from(size).map_err(|_e| Error::ExtractSubjectFailed)?;
+    retval.truncate(safe_size);
+
+    Ok(retval)
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+    use keystore2_crypto_bindgen::{
+        generateKeyFromPassword, AES_gcm_decrypt, AES_gcm_encrypt, CreateKeyId,
+    };
+
+    #[test]
+    fn test_wrapper_roundtrip() {
+        let key = generate_aes256_key().unwrap();
+        let message = b"totally awesome message";
+        let (cipher_text, iv, tag) = aes_gcm_encrypt(message, &key).unwrap();
+        let message2 = aes_gcm_decrypt(&cipher_text, &iv, &tag, &key).unwrap();
+        assert_eq!(message[..], message2[..])
+    }
+
+    #[test]
+    fn test_encrypt_decrypt() {
+        let input = vec![0; 16];
+        let mut out = vec![0; 16];
+        let mut out2 = vec![0; 16];
+        let key = vec![0; 16];
+        let iv = vec![0; 12];
+        let mut tag = vec![0; 16];
+        unsafe {
+            let res = AES_gcm_encrypt(
+                input.as_ptr(),
+                out.as_mut_ptr(),
+                16,
+                key.as_ptr(),
+                16,
+                iv.as_ptr(),
+                tag.as_mut_ptr(),
+            );
+            assert!(res);
+            assert_ne!(out, input);
+            assert_ne!(tag, input);
+            let res = AES_gcm_decrypt(
+                out.as_ptr(),
+                out2.as_mut_ptr(),
+                16,
+                key.as_ptr(),
+                16,
+                iv.as_ptr(),
+                tag.as_ptr(),
+            );
+            assert!(res);
+            assert_eq!(out2, input);
+        }
+    }
+
+    #[test]
+    fn test_create_key_id() {
+        let blob = vec![0; 16];
+        let mut out: u64 = 0;
+        unsafe {
+            let res = CreateKeyId(blob.as_ptr(), 16, &mut out);
+            assert!(res);
+            assert_ne!(out, 0);
+        }
+    }
+
+    #[test]
+    fn test_generate_key_from_password() {
+        let mut key = vec![0; 16];
+        let pw = vec![0; 16];
+        let mut salt = vec![0; 16];
+        unsafe {
+            generateKeyFromPassword(key.as_mut_ptr(), 16, pw.as_ptr(), 16, salt.as_mut_ptr());
+        }
+        assert_ne!(key, vec![0; 16]);
+    }
+
+    #[test]
+    fn test_hkdf() {
+        let result = hkdf_extract(&[0; 16], &[0; 16]);
+        assert!(result.is_ok());
+        for out_len in 4..=8 {
+            let result = hkdf_expand(out_len, &[0; 16], &[0; 16]);
+            assert!(result.is_ok());
+            assert_eq!(result.unwrap().len(), out_len);
+        }
+    }
+
+    #[test]
+    fn test_ec() -> Result<(), Error> {
+        let priv0 = ec_key_generate_key()?;
+        assert!(!priv0.0.is_null());
+        let pub0 = ec_key_get0_public_key(&priv0);
+
+        let priv1 = ec_key_generate_key()?;
+        let pub1 = ec_key_get0_public_key(&priv1);
+
+        let priv0s = ec_key_marshal_private_key(&priv0)?;
+        let pub0s = ec_point_point_to_oct(pub0.get_point())?;
+        let pub1s = ec_point_point_to_oct(pub1.get_point())?;
+
+        let priv0 = ec_key_parse_private_key(&priv0s)?;
+        let pub0 = ec_point_oct_to_point(&pub0s)?;
+        let pub1 = ec_point_oct_to_point(&pub1s)?;
+
+        let left_key = ecdh_compute_key(pub0.get_point(), &priv1)?;
+        let right_key = ecdh_compute_key(pub1.get_point(), &priv0)?;
+
+        assert_eq!(left_key, right_key);
+        Ok(())
+    }
+}
diff --git a/keystore2/src/crypto/tests/certificate_utils_test.cpp b/keystore2/src/crypto/tests/certificate_utils_test.cpp
new file mode 100644
index 0000000..119c3fa
--- /dev/null
+++ b/keystore2/src/crypto/tests/certificate_utils_test.cpp
@@ -0,0 +1,317 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "certificate_utils.h"
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <variant>
+
+#include "test_keys.h"
+
+using namespace keystore;
+
+// I leave these here in case they are needed for debugging.
+namespace debug_utils {
+
+void log_ssl_error() {
+    unsigned long error = ERR_peek_last_error();
+
+    char buf[128];
+    ERR_error_string_n(error, buf, sizeof(buf));
+    std::cout << "BoringSslError: " << buf << std::endl;
+}
+
+std::string hexdump(const std::vector<uint8_t>& data) {
+    std::stringstream s;
+    size_t column_count = 0;
+    for (auto& c : data) {
+        s << std::setw(2) << std::setfill('0') << std::hex << (unsigned int)c;
+        if (++column_count % 40 == 0) s << "\n";
+    }
+    return s.str();
+}
+
+}  // namespace debug_utils
+
+constexpr uint64_t kValidity = 24 * 60 * 60 * 1000;  // 24 hours in milliseconds
+
+const EVP_MD* getMD(Digest digest) {
+    switch (digest) {
+    case Digest::SHA1:
+        return EVP_sha1();
+    case Digest::SHA224:
+        return EVP_sha224();
+    case Digest::SHA256:
+        return EVP_sha256();
+    case Digest::SHA384:
+        return EVP_sha384();
+    case Digest::SHA512:
+        return EVP_sha512();
+    }
+}
+
+std::array<Digest, 5> digests = {
+    Digest::SHA1, Digest::SHA224, Digest::SHA256, Digest::SHA384, Digest::SHA512,
+};
+
+static const char* toString(Digest d) {
+    switch (d) {
+    case Digest::SHA1:
+        return "SHA1";
+    case Digest::SHA224:
+        return "SHA224";
+    case Digest::SHA256:
+        return "SHA256";
+    case Digest::SHA384:
+        return "SHA384";
+    case Digest::SHA512:
+        return "SHA512";
+    }
+}
+
+std::array<Padding, 2> rsa_paddings = {
+    Padding::PSS,
+    Padding::PKCS1_5,
+};
+
+enum class EcCurve {
+    P224,
+    P256,
+    P384,
+    P521,
+};
+
+std::array<int, 4> ec_curves = {
+    NID_secp224r1,
+    NID_X9_62_prime256v1,
+    NID_secp384r1,
+    NID_secp521r1,
+};
+
+static const char* curveNidToString(int nid) {
+    switch (nid) {
+    case NID_secp224r1:
+        return "P224";
+    case NID_X9_62_prime256v1:
+        return "P256";
+    case NID_secp384r1:
+        return "P384";
+    case NID_secp521r1:
+        return "P521";
+    default:
+        return "Unknown";
+    }
+}
+
+std::array<long, 2> rsa_key_sizes = {
+    2048,
+    4096,
+};
+
+using EcParam = std::tuple<int /* EC curve NID */, Digest>;
+
+class CertificateUtilsWithEcCurve : public testing::TestWithParam<EcParam> {};
+
+static std::string paramToStringEc(testing::TestParamInfo<EcParam> param) {
+    std::stringstream s;
+    auto [curve_nid, digest] = param.param;
+    s << param.index << "_" << curveNidToString(curve_nid) << "_" << toString(digest);
+    return s.str();
+}
+
+INSTANTIATE_TEST_SUITE_P(CertSigningWithCallbackEC, CertificateUtilsWithEcCurve,
+                         testing::Combine(testing::ValuesIn(ec_curves), testing::ValuesIn(digests)),
+                         paramToStringEc);
+
+TEST_P(CertificateUtilsWithEcCurve, CertSigningWithCallbackEC) {
+    // Structured decomposition (e.g.: auto [a, b, c] = ...) does not work here because
+    // names bound this way cannot be captured in lambda expressions so we use std::tie instead.
+    int curve_nid;
+    Digest digest;
+    std::tie(curve_nid, digest) = GetParam();
+    EVP_PKEY_CTX_Ptr pkey_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL));
+    ASSERT_TRUE((bool)pkey_ctx);
+    ASSERT_TRUE(EVP_PKEY_keygen_init(pkey_ctx.get()));
+    ASSERT_TRUE(EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pkey_ctx.get(), curve_nid));
+
+    EVP_PKEY* pkey_ptr = nullptr;
+    ASSERT_TRUE(EVP_PKEY_keygen(pkey_ctx.get(), &pkey_ptr));
+    EVP_PKEY_Ptr pkey(pkey_ptr);
+    ASSERT_TRUE(pkey);
+
+    uint64_t now_ms = (uint64_t)time(nullptr) * 1000;
+
+    BasicConstraintsExtension bcons{
+        .isCa = true,
+        .pathLength = {},
+    };
+
+    KeyUsageExtension keyUsage{
+        .isSigningKey = true,
+        .isEncryptionKey = false,
+        .isCertificationKey = true,
+    };
+
+    auto certV = makeCert(pkey.get(), std::nullopt, std::nullopt, now_ms - kValidity,
+                          now_ms + kValidity, true /* subject key id extension */, keyUsage, bcons);
+    ASSERT_TRUE(std::holds_alternative<X509_Ptr>(certV));
+    auto& cert = std::get<X509_Ptr>(certV);
+    ASSERT_TRUE(!setIssuer(cert.get(), cert.get(), true));
+
+    ASSERT_TRUE(!signCertWith(
+        cert.get(),
+        [&](const uint8_t* data, size_t len) {
+            bssl::ScopedEVP_MD_CTX sign_ctx;
+            EXPECT_TRUE(
+                EVP_DigestSignInit(sign_ctx.get(), nullptr, getMD(digest), nullptr, pkey.get()));
+
+            std::vector<uint8_t> sig_buf(512);
+            size_t sig_len = 512;
+            EVP_DigestSign(sign_ctx.get(), sig_buf.data(), &sig_len, data, len);
+            sig_buf.resize(sig_len);
+            return sig_buf;
+        },
+        Algo::ECDSA, Padding::Ignored, digest));
+
+    auto encCertV = encodeCert(cert.get());
+    ASSERT_TRUE(std::holds_alternative<std::vector<uint8_t>>(encCertV));
+
+    auto& encCert = std::get<1>(encCertV);
+    // Uncomment the next line to dump the DER encoded signed certificate as hex string.
+    // You can pipe this dump into  "xxd -r -p | openssl x509 -inform der -text -noout"
+    // to inspect the certificate.
+    // std::cout << "DER encoded cert:\n" << debug_utils::hexdump(encCert) << std::endl;
+
+    const uint8_t* p = encCert.data();
+    X509_Ptr decoded_cert(d2i_X509(nullptr, &p, (long)encCert.size()));
+    EVP_PKEY_Ptr decoded_pkey(X509_get_pubkey(decoded_cert.get()));
+    ASSERT_TRUE(X509_verify(decoded_cert.get(), decoded_pkey.get()));
+}
+
+using RsaParams = std::tuple<long /* key size */, Padding, Digest>;
+
+class CertificateUtilsWithRsa : public testing::TestWithParam<RsaParams> {};
+
+static std::string paramsToStringRsa(testing::TestParamInfo<RsaParams> param) {
+    std::stringstream s;
+    auto [key_size, padding, digest] = param.param;
+    s << param.index << "_" << key_size << "_";
+    switch (padding) {
+    case Padding::PSS:
+        s << "PSS";
+        break;
+    case Padding::PKCS1_5:
+        s << "PKCS1_5";
+        break;
+    case Padding::Ignored:
+        s << "Ignored";
+    }
+    s << "_" << toString(digest);
+    return s.str();
+}
+
+INSTANTIATE_TEST_SUITE_P(CertSigningWithCallbackRsa, CertificateUtilsWithRsa,
+                         testing::Combine(testing::ValuesIn(rsa_key_sizes),
+                                          testing::ValuesIn(rsa_paddings),
+                                          testing::ValuesIn(digests)),
+                         paramsToStringRsa);
+
+TEST_P(CertificateUtilsWithRsa, CertSigningWithCallbackRsa) {
+    // Structured decomposition (e.g.: auto [a, b, c] = ...) does not work here because
+    // names bound this way cannot be captured in lambda expressions so we use std::tie instead.
+    long key_size;
+    Padding padding;
+    Digest digest;
+    std::tie(key_size, padding, digest) = GetParam();
+
+    CBS cbs;
+    switch (key_size) {
+    case 2048:
+        CBS_init(&cbs, rsa_key_2k, rsa_key_2k_len);
+        break;
+    case 4096:
+        CBS_init(&cbs, rsa_key_4k, rsa_key_4k_len);
+        break;
+    default:
+        FAIL();
+    }
+    EVP_PKEY_Ptr pkey(EVP_parse_private_key(&cbs));
+    ASSERT_TRUE(pkey);
+
+    uint64_t now_ms = (uint64_t)time(nullptr) * 1000;
+
+    BasicConstraintsExtension bcons{
+        .isCa = true,
+        .pathLength = 0,
+    };
+
+    KeyUsageExtension keyUsage{
+        .isSigningKey = true,
+        .isEncryptionKey = false,
+        .isCertificationKey = true,
+    };
+
+    auto certV = makeCert(pkey.get(), std::nullopt, std::nullopt, now_ms - kValidity,
+                          now_ms + kValidity, true /* subject key id extension */, keyUsage, bcons);
+    ASSERT_TRUE(std::holds_alternative<X509_Ptr>(certV));
+    auto& cert = std::get<X509_Ptr>(certV);
+    ASSERT_TRUE(!setIssuer(cert.get(), cert.get(), true));
+
+    ASSERT_TRUE(!signCertWith(
+        cert.get(),
+        [&](const uint8_t* data, size_t len) {
+            bssl::ScopedEVP_MD_CTX sign_ctx;
+            EVP_PKEY_CTX* pkey_sign_ctx_ptr;
+            EXPECT_TRUE(EVP_DigestSignInit(sign_ctx.get(), &pkey_sign_ctx_ptr, getMD(digest),
+                                           nullptr, pkey.get()));
+
+            if (padding == Padding::PSS) {
+                EXPECT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pkey_sign_ctx_ptr, RSA_PKCS1_PSS_PADDING));
+                EXPECT_TRUE(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_sign_ctx_ptr, -1));
+            } else {
+                EXPECT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pkey_sign_ctx_ptr, RSA_PKCS1_PADDING));
+            }
+
+            std::vector<uint8_t> sig_buf(1024);
+            size_t sig_len = 1024;
+            EVP_DigestSign(sign_ctx.get(), sig_buf.data(), &sig_len, data, len);
+            sig_buf.resize(sig_len);
+            return sig_buf;
+        },
+        Algo::RSA, padding, digest));
+
+    auto encCertV = encodeCert(cert.get());
+    ASSERT_TRUE(std::holds_alternative<std::vector<uint8_t>>(encCertV));
+
+    auto& encCert = std::get<1>(encCertV);
+    // Uncomment the next line to dump the DER encoded signed certificate as hex string.
+    // You can pipe this dump into  "xxd -r -p | openssl x509 -inform der -text -noout"
+    // to inspect the certificate.
+    // std::cout << "DER encoded cert:\n" << debug_utils::hexdump(encCert) << std::endl;
+
+    const uint8_t* p = encCert.data();
+    X509_Ptr decoded_cert(d2i_X509(nullptr, &p, (long)encCert.size()));
+    EVP_PKEY_Ptr decoded_pkey(X509_get_pubkey(decoded_cert.get()));
+    ASSERT_TRUE(X509_verify(decoded_cert.get(), decoded_pkey.get()));
+}
diff --git a/keystore/binder/android/security/keymaster/OperationResult.aidl b/keystore2/src/crypto/tests/gtest_main.cpp
similarity index 74%
copy from keystore/binder/android/security/keymaster/OperationResult.aidl
copy to keystore2/src/crypto/tests/gtest_main.cpp
index db689d4..149cbbc 100644
--- a/keystore/binder/android/security/keymaster/OperationResult.aidl
+++ b/keystore2/src/crypto/tests/gtest_main.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 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.
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-package android.security.keymaster;
-
-/* @hide */
-parcelable OperationResult cpp_header "keystore/OperationResult.h";
+#include <gtest/gtest.h>
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/keystore2/src/crypto/tests/test_keys.h b/keystore2/src/crypto/tests/test_keys.h
new file mode 100644
index 0000000..d3b1175
--- /dev/null
+++ b/keystore2/src/crypto/tests/test_keys.h
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+constexpr const unsigned char rsa_key_4k[] = {
+    0x30, 0x82, 0x09, 0x41, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+    0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x09, 0x2b, 0x30, 0x82, 0x09, 0x27, 0x02, 0x01,
+    0x00, 0x02, 0x82, 0x02, 0x01, 0x00, 0xac, 0x07, 0x4e, 0xc6, 0x20, 0xb9, 0x78, 0x03, 0xd0, 0x53,
+    0x22, 0xa4, 0xef, 0x6d, 0xd1, 0x7a, 0x7e, 0x5e, 0x5e, 0x1d, 0xe7, 0x9b, 0x30, 0x31, 0x99, 0xe8,
+    0x1c, 0xdf, 0x32, 0x0c, 0xdb, 0xdb, 0xc2, 0x4c, 0x22, 0x10, 0x71, 0xd0, 0x76, 0x83, 0xb0, 0x50,
+    0x66, 0xa4, 0x43, 0x75, 0xe8, 0x46, 0xde, 0x58, 0x9e, 0x31, 0x82, 0x81, 0x8c, 0x36, 0x20, 0xbb,
+    0x42, 0xcd, 0xb9, 0x21, 0x29, 0x67, 0x39, 0x4d, 0x2a, 0x1c, 0xc2, 0x89, 0x48, 0x23, 0x5f, 0xdd,
+    0x60, 0xc4, 0x04, 0x42, 0xbe, 0x6f, 0x01, 0xa5, 0xf8, 0xac, 0xba, 0x27, 0xd1, 0x87, 0x08, 0x6b,
+    0xe6, 0x1b, 0xfc, 0xba, 0xff, 0x7e, 0xc9, 0xaf, 0x76, 0x28, 0xe8, 0x93, 0x2c, 0xbf, 0x6f, 0xed,
+    0xf3, 0x6f, 0x21, 0xc1, 0xe7, 0x56, 0xf5, 0x15, 0x56, 0xa8, 0xa1, 0xfd, 0xb7, 0xb3, 0x8c, 0x3d,
+    0x1f, 0xf3, 0x80, 0xea, 0x79, 0xff, 0x0c, 0xc6, 0xb6, 0x59, 0xa0, 0x3f, 0x13, 0xb0, 0x4f, 0xb3,
+    0x1c, 0xe1, 0x2a, 0xa5, 0x45, 0x02, 0x51, 0xa3, 0x74, 0x15, 0xee, 0xf0, 0xca, 0xba, 0x7d, 0xca,
+    0xc6, 0x87, 0xd6, 0x12, 0x9b, 0xbb, 0x4b, 0x86, 0xc7, 0xcb, 0x88, 0xc6, 0x10, 0xee, 0x34, 0x51,
+    0x4b, 0xe9, 0x9d, 0xbb, 0x59, 0x03, 0xff, 0xbf, 0x98, 0x0a, 0xfd, 0xf2, 0xd0, 0x06, 0x0e, 0x51,
+    0xd1, 0xbb, 0x8b, 0xbd, 0xca, 0x26, 0x4d, 0x05, 0x0e, 0xee, 0x82, 0x0f, 0xf6, 0x38, 0x46, 0x2a,
+    0x7f, 0xfd, 0x37, 0xe6, 0xcf, 0xaa, 0x6a, 0x84, 0xbc, 0xa7, 0xd2, 0x06, 0x29, 0x49, 0x4c, 0xd2,
+    0x28, 0xd4, 0x48, 0x09, 0xfc, 0x91, 0x1e, 0xb0, 0x06, 0xf9, 0x5b, 0xbe, 0xd9, 0xb8, 0x01, 0xec,
+    0x3a, 0xc3, 0xa2, 0x1d, 0x89, 0x0b, 0x22, 0xf8, 0xf0, 0x8e, 0x95, 0xe0, 0x3d, 0x5b, 0x80, 0x38,
+    0x21, 0x80, 0x10, 0x04, 0x54, 0xaa, 0xa5, 0x81, 0x8b, 0x47, 0x73, 0x54, 0xcb, 0x06, 0xa9, 0x01,
+    0xf5, 0x57, 0x72, 0xdd, 0x5f, 0x9f, 0xe1, 0x14, 0x1f, 0xc1, 0x3b, 0xda, 0xd1, 0xad, 0xc7, 0x9c,
+    0x70, 0x18, 0xfd, 0x07, 0x7e, 0x37, 0x0d, 0xb4, 0x30, 0xff, 0x6d, 0x4b, 0x99, 0xdb, 0x3f, 0x23,
+    0xd7, 0x15, 0x3d, 0x4c, 0x14, 0x63, 0x98, 0xf5, 0x03, 0x63, 0x4e, 0x63, 0x15, 0x6a, 0xce, 0x7f,
+    0xfc, 0x4f, 0xa0, 0x72, 0x49, 0x3f, 0xd9, 0xa2, 0xde, 0x65, 0xac, 0x35, 0x88, 0x03, 0x55, 0xf5,
+    0x27, 0x57, 0x4e, 0x4b, 0xef, 0x44, 0x1d, 0xd1, 0xe9, 0xaf, 0x89, 0x7e, 0x19, 0x2a, 0xc4, 0x48,
+    0x80, 0x8d, 0x22, 0xd6, 0x4f, 0x79, 0x39, 0x15, 0x00, 0xc0, 0xa8, 0x93, 0x5a, 0x05, 0xce, 0xc8,
+    0x3f, 0x27, 0x40, 0x09, 0x1f, 0x2a, 0x0c, 0x32, 0x6a, 0x2d, 0xef, 0x51, 0x79, 0x51, 0xf1, 0xfe,
+    0xdb, 0x2c, 0xb5, 0x2f, 0x4d, 0x94, 0x1d, 0xd2, 0xb8, 0xed, 0x53, 0x58, 0x3e, 0xb4, 0x65, 0xfb,
+    0xca, 0x9f, 0xfe, 0x5c, 0x68, 0x20, 0x58, 0xd9, 0xfd, 0xc7, 0x4d, 0x0f, 0x03, 0x37, 0x61, 0xaf,
+    0x2b, 0x90, 0xe1, 0xfc, 0x15, 0xa6, 0xf1, 0xac, 0x5d, 0xad, 0xb4, 0xbe, 0xf6, 0xb4, 0x7b, 0x42,
+    0xe6, 0x17, 0xe9, 0x79, 0xde, 0x65, 0x50, 0x70, 0x6b, 0x46, 0x24, 0x73, 0x4d, 0xa3, 0x54, 0x3a,
+    0x21, 0xc2, 0x1c, 0x80, 0x1d, 0xf6, 0xd4, 0x0e, 0x7a, 0xa5, 0x4e, 0x97, 0xc1, 0x15, 0x37, 0x02,
+    0xea, 0x55, 0xff, 0xad, 0x59, 0x95, 0xa0, 0x24, 0xd0, 0x58, 0x59, 0xf7, 0x9a, 0x4e, 0x2b, 0x78,
+    0x89, 0x91, 0x9c, 0x67, 0x14, 0xe4, 0x24, 0xc3, 0xb8, 0xd3, 0x7d, 0x06, 0xaf, 0x46, 0x8f, 0xe7,
+    0x07, 0x88, 0xf0, 0x4c, 0xf4, 0xb8, 0x3a, 0x2a, 0x9c, 0x89, 0x2f, 0x7c, 0x32, 0xfd, 0x52, 0x55,
+    0xd8, 0xe1, 0x58, 0x46, 0xe5, 0xc3, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x02, 0x00, 0x08,
+    0x79, 0xcf, 0xb2, 0xc9, 0x57, 0xfa, 0x06, 0xce, 0x13, 0xda, 0x88, 0x1f, 0xd7, 0xdc, 0x53, 0x59,
+    0xb8, 0x92, 0x90, 0x8c, 0xa1, 0xc3, 0xcd, 0x1d, 0xd5, 0x26, 0xdf, 0x04, 0x5b, 0x47, 0xd5, 0xdb,
+    0x0b, 0xdf, 0x3d, 0xca, 0x2d, 0xc8, 0x39, 0x12, 0xcd, 0xd3, 0x50, 0xd2, 0x96, 0x13, 0x9c, 0xb2,
+    0x45, 0xd1, 0x7d, 0x84, 0xfd, 0x97, 0x07, 0xef, 0xb2, 0xea, 0x46, 0xb2, 0x91, 0x64, 0xb5, 0xd6,
+    0x47, 0xec, 0x04, 0x40, 0xbd, 0x7c, 0xd5, 0x69, 0x5a, 0xc1, 0xf2, 0xc6, 0x76, 0xf7, 0x65, 0x06,
+    0xc2, 0xc3, 0xae, 0xd6, 0xf9, 0x31, 0x44, 0xa2, 0xf0, 0x96, 0x04, 0xd8, 0xfd, 0xe9, 0xaa, 0xb8,
+    0x8b, 0x31, 0x9a, 0x30, 0x63, 0x57, 0xf8, 0x12, 0xae, 0xb9, 0xa8, 0xc7, 0x14, 0x03, 0xae, 0xf0,
+    0x22, 0x5e, 0x03, 0xae, 0xff, 0x8e, 0x36, 0x85, 0x79, 0x59, 0x82, 0xa8, 0xde, 0x64, 0xa6, 0x61,
+    0x5d, 0xc5, 0x0c, 0x43, 0x6d, 0xf8, 0x2d, 0x5e, 0xaf, 0xe7, 0x83, 0x5c, 0x93, 0x8a, 0x03, 0xe4,
+    0x3b, 0xd6, 0x73, 0x62, 0x33, 0x70, 0xf9, 0xa1, 0x4b, 0x05, 0x5f, 0x19, 0xf8, 0x0e, 0xbe, 0x3a,
+    0xa9, 0x68, 0x5c, 0xa9, 0xdf, 0x80, 0x64, 0x0c, 0x25, 0xd9, 0x44, 0xa8, 0x65, 0xdb, 0xab, 0xeb,
+    0xc4, 0xe7, 0xdb, 0xda, 0xc9, 0x44, 0xe0, 0x97, 0x82, 0x06, 0x80, 0x64, 0x11, 0x34, 0xcd, 0x90,
+    0x4b, 0xe8, 0x81, 0x6d, 0xdd, 0x15, 0x77, 0x8e, 0x55, 0x77, 0xba, 0xe5, 0x2f, 0x35, 0x1a, 0x23,
+    0x67, 0x68, 0xd8, 0x27, 0xeb, 0xef, 0xca, 0xd1, 0xc3, 0x25, 0x09, 0xd8, 0x86, 0xcd, 0x6f, 0xe5,
+    0x00, 0x2d, 0x47, 0xc9, 0xf2, 0x6c, 0x4d, 0xb9, 0xa2, 0x86, 0xfe, 0xae, 0x95, 0x1f, 0xf4, 0x71,
+    0x83, 0xac, 0x6e, 0x8a, 0x09, 0xe3, 0x5c, 0x07, 0xc4, 0x3c, 0x3a, 0x50, 0x0f, 0xb6, 0x90, 0x21,
+    0x25, 0xdf, 0x2d, 0x61, 0x89, 0xb6, 0x3c, 0xb3, 0xc3, 0xc0, 0xd9, 0x2e, 0x73, 0x76, 0xf6, 0x46,
+    0x46, 0xf8, 0x01, 0xbd, 0x0b, 0x94, 0x80, 0xf6, 0x94, 0x5b, 0x17, 0x9e, 0xbc, 0xdb, 0x9d, 0xfd,
+    0xc2, 0x3e, 0x56, 0xa1, 0xa0, 0x1a, 0xed, 0x82, 0xcb, 0xb0, 0xb3, 0xd4, 0x58, 0xb4, 0x91, 0x8e,
+    0xd9, 0x84, 0xb8, 0x94, 0xc2, 0x86, 0x42, 0xa6, 0x86, 0xf2, 0x9c, 0x1d, 0xc7, 0xc9, 0xed, 0x6d,
+    0x0d, 0x8d, 0x98, 0x08, 0xf3, 0x52, 0xe7, 0x7f, 0xfe, 0xe0, 0x9f, 0xdf, 0x43, 0x8e, 0xbc, 0x3f,
+    0x93, 0x41, 0x44, 0x7b, 0x26, 0x0e, 0x2e, 0xc1, 0xc2, 0x4e, 0xc7, 0xb4, 0x5c, 0xc5, 0x55, 0xae,
+    0xbb, 0xb4, 0x2f, 0xd5, 0x1f, 0x34, 0xce, 0x94, 0x66, 0x12, 0x41, 0x8a, 0x15, 0x8b, 0xcb, 0xdd,
+    0x00, 0xf9, 0xa0, 0x55, 0xa6, 0x8b, 0xe9, 0x13, 0x14, 0xc6, 0x0b, 0x98, 0x8f, 0xff, 0x2f, 0x9a,
+    0xe8, 0x78, 0x68, 0x94, 0xeb, 0x53, 0xeb, 0x4b, 0xed, 0x5a, 0x6d, 0x1c, 0xe2, 0xe5, 0xc7, 0xde,
+    0x13, 0xa9, 0xaf, 0xa3, 0xc7, 0x54, 0x26, 0xa0, 0x3f, 0x44, 0xdd, 0x9f, 0x14, 0xe2, 0xc4, 0x9a,
+    0xf5, 0x30, 0xea, 0x2e, 0x75, 0xc6, 0xd3, 0xf8, 0x5e, 0x9b, 0x02, 0x0d, 0xc8, 0x81, 0x6c, 0x13,
+    0x3b, 0x9c, 0x89, 0x54, 0x52, 0x41, 0x62, 0xad, 0xa0, 0x52, 0xaa, 0x2c, 0xc4, 0x2c, 0x63, 0x58,
+    0xf6, 0xb1, 0xfa, 0xdc, 0x77, 0xed, 0x3e, 0x8c, 0x12, 0x94, 0x09, 0x57, 0x18, 0xb3, 0x04, 0x53,
+    0x3c, 0xad, 0xa8, 0x45, 0x4b, 0x19, 0xa0, 0xcb, 0x8f, 0x6f, 0x5d, 0x2d, 0xcc, 0xe1, 0xfe, 0x4a,
+    0xe6, 0x20, 0xed, 0x9a, 0x76, 0x4f, 0x17, 0xfd, 0xed, 0x1e, 0x6e, 0x41, 0x21, 0x43, 0xe4, 0xe9,
+    0x47, 0x01, 0x1f, 0x76, 0x68, 0x4d, 0xbb, 0xd3, 0xba, 0x68, 0x34, 0x06, 0x6a, 0xf5, 0xe9, 0x02,
+    0x82, 0x01, 0x01, 0x00, 0xdb, 0xc5, 0x0d, 0x95, 0x88, 0x5f, 0x2c, 0xa9, 0x7c, 0xb7, 0x06, 0xd8,
+    0x20, 0x60, 0x72, 0x80, 0xec, 0xae, 0xf4, 0xdc, 0x48, 0x5d, 0x9d, 0x6d, 0x2a, 0x54, 0x36, 0xe1,
+    0x32, 0x25, 0x10, 0x4d, 0x56, 0x0f, 0x51, 0xe9, 0xbc, 0x95, 0x3f, 0x38, 0x43, 0xed, 0xc6, 0x6a,
+    0x16, 0xb0, 0x06, 0x90, 0x85, 0x51, 0x07, 0xea, 0x7b, 0x1d, 0x64, 0x12, 0x39, 0x7e, 0x32, 0x92,
+    0x3e, 0xcc, 0x97, 0x91, 0x02, 0x6c, 0xf8, 0x95, 0xc7, 0x39, 0xa1, 0x5c, 0xd3, 0x62, 0xcc, 0x29,
+    0x83, 0x27, 0xe6, 0x36, 0xba, 0xf6, 0x62, 0x80, 0xf6, 0x15, 0xda, 0x0f, 0xc4, 0x47, 0x81, 0x44,
+    0x96, 0xba, 0xec, 0x37, 0x8c, 0x95, 0xb2, 0x30, 0x12, 0xc0, 0x4d, 0x62, 0x88, 0x5e, 0x7d, 0x13,
+    0x6d, 0x83, 0x14, 0x1e, 0xd7, 0xc2, 0xeb, 0x9d, 0xbd, 0xa4, 0x70, 0x78, 0xf3, 0xb1, 0x6b, 0x6c,
+    0xe9, 0x37, 0x63, 0x47, 0x6f, 0xed, 0xea, 0xd6, 0x32, 0x80, 0x71, 0xa2, 0x7f, 0x38, 0x67, 0xb4,
+    0x1f, 0xf0, 0x7f, 0xb6, 0xb7, 0x56, 0xe7, 0x24, 0xaa, 0x2f, 0xb9, 0x93, 0xb8, 0xab, 0x2a, 0xec,
+    0xc4, 0x19, 0x9a, 0xea, 0x5d, 0x01, 0x1e, 0xdd, 0x5c, 0x05, 0x37, 0x12, 0x04, 0x32, 0xb2, 0x5d,
+    0x16, 0xdc, 0x88, 0x0c, 0xfe, 0x1e, 0xf6, 0x9f, 0x63, 0xbf, 0xb2, 0x00, 0x6b, 0x99, 0xbe, 0x42,
+    0x96, 0x41, 0xe0, 0x3d, 0xd1, 0x35, 0x6a, 0xf1, 0x81, 0x09, 0xd6, 0x4c, 0xdd, 0xc0, 0x20, 0x6e,
+    0x0d, 0xcb, 0x30, 0x11, 0x73, 0x11, 0x82, 0x5e, 0x1b, 0x9a, 0xa0, 0x57, 0xcd, 0xd7, 0xab, 0xa2,
+    0xe9, 0xed, 0xe7, 0x26, 0x8d, 0x30, 0x09, 0xe7, 0x5a, 0xa1, 0xd0, 0x62, 0xac, 0x7f, 0x15, 0xd1,
+    0x9a, 0x0a, 0x97, 0x5d, 0x8a, 0xc0, 0x47, 0xa4, 0xa8, 0x8b, 0x26, 0x75, 0x35, 0x5d, 0xa9, 0xf1,
+    0x1a, 0x61, 0xdc, 0xbf, 0x02, 0x82, 0x01, 0x01, 0x00, 0xc8, 0x63, 0x6e, 0x9c, 0x88, 0xcd, 0x33,
+    0xef, 0x72, 0x80, 0x00, 0x6b, 0x8e, 0xb0, 0xd2, 0xa9, 0x5c, 0x78, 0xf7, 0x25, 0x8f, 0xba, 0x49,
+    0x60, 0xa3, 0x33, 0xf4, 0x16, 0x1c, 0x81, 0xad, 0x82, 0x39, 0xa5, 0xa8, 0x12, 0xc2, 0x7e, 0x05,
+    0x33, 0x9c, 0xd9, 0xa9, 0xa4, 0x02, 0xf8, 0x43, 0xdf, 0xa6, 0x39, 0xdb, 0xc0, 0x60, 0xdc, 0x53,
+    0x76, 0x7c, 0xef, 0xf0, 0x58, 0x44, 0x3d, 0xc7, 0x77, 0xe6, 0x6f, 0x2a, 0xbd, 0x89, 0x8b, 0x40,
+    0xe3, 0x02, 0x80, 0x8c, 0x46, 0x24, 0x4c, 0x63, 0xc4, 0x06, 0xb1, 0x48, 0x92, 0x0a, 0x86, 0x58,
+    0xe3, 0x08, 0x2a, 0x2e, 0x4b, 0xdd, 0xa6, 0x64, 0x68, 0xe9, 0xac, 0x07, 0x2b, 0x3d, 0x2f, 0x42,
+    0x68, 0x5a, 0x42, 0xdb, 0xb7, 0x5a, 0x67, 0xc4, 0x5f, 0x19, 0xba, 0xff, 0xed, 0x17, 0x0b, 0xf8,
+    0x26, 0x36, 0xd7, 0x9d, 0x3a, 0x3d, 0x0e, 0x5f, 0x31, 0x29, 0x3f, 0xf4, 0x24, 0x2f, 0x0a, 0xa2,
+    0x21, 0x0e, 0x6e, 0x86, 0xf5, 0x1e, 0xb7, 0xce, 0x81, 0x95, 0xd0, 0xcd, 0x97, 0xd1, 0x2c, 0xd4,
+    0x9f, 0x95, 0xd6, 0x7c, 0x68, 0x80, 0x4f, 0xe6, 0x5c, 0xab, 0xfe, 0x6b, 0xf9, 0x8b, 0x12, 0x99,
+    0xad, 0xc9, 0x64, 0x21, 0x99, 0xef, 0xfb, 0xd3, 0x16, 0x8b, 0x95, 0x93, 0xa7, 0xb7, 0x24, 0x36,
+    0x00, 0xb2, 0xa2, 0xbe, 0x79, 0x4e, 0x66, 0xbd, 0xe1, 0x2d, 0xd0, 0xa9, 0x76, 0xe1, 0x13, 0x0d,
+    0xb5, 0x4b, 0xc5, 0xe9, 0x63, 0x23, 0x45, 0xcf, 0x5f, 0x0d, 0x5c, 0xce, 0x93, 0x7f, 0xa9, 0x68,
+    0xf6, 0xfc, 0x2c, 0x54, 0x40, 0x43, 0xca, 0x9d, 0xfd, 0x43, 0x81, 0x4d, 0xbe, 0x98, 0x87, 0xca,
+    0x2c, 0x82, 0x32, 0xb7, 0xcf, 0xa1, 0xc5, 0xf8, 0x55, 0xea, 0x2b, 0x6d, 0xfc, 0xe7, 0x5d, 0xcf,
+    0xde, 0xf8, 0x15, 0xc2, 0xc3, 0xa2, 0xe6, 0x83, 0xfd, 0x02, 0x82, 0x01, 0x00, 0x2c, 0x83, 0x3a,
+    0xff, 0x20, 0x81, 0xf6, 0x6f, 0xd5, 0xbc, 0xd4, 0x7c, 0x0e, 0x02, 0xba, 0xee, 0x76, 0x01, 0xf1,
+    0xc2, 0x74, 0x3d, 0xd1, 0xd6, 0xfc, 0x8d, 0xd6, 0x17, 0xc2, 0xaa, 0x53, 0x24, 0xf6, 0xdb, 0x5f,
+    0x81, 0xf2, 0x1a, 0x60, 0x95, 0xaa, 0xdc, 0x8c, 0x25, 0x8c, 0xb6, 0xd6, 0x7d, 0x8b, 0x23, 0x20,
+    0x71, 0x53, 0xc2, 0x5e, 0x34, 0x7a, 0xc4, 0x9e, 0xc5, 0x94, 0x46, 0xa8, 0x24, 0x4c, 0xd3, 0x79,
+    0x7e, 0x0c, 0xbe, 0x15, 0x7a, 0xd1, 0xad, 0xdf, 0x20, 0x41, 0x5a, 0x61, 0x7c, 0x90, 0x5d, 0xbb,
+    0x11, 0xd7, 0xc6, 0x11, 0x46, 0xc4, 0x40, 0x9f, 0x64, 0x1f, 0x0b, 0x79, 0x30, 0xbf, 0x1e, 0xca,
+    0xda, 0x85, 0xd1, 0xc1, 0x5a, 0xc5, 0xb8, 0x2d, 0xa9, 0x33, 0xb3, 0x2a, 0xee, 0x1c, 0x51, 0x74,
+    0x9b, 0x9c, 0x7f, 0xa3, 0xf0, 0x3b, 0x9b, 0xa1, 0xe0, 0x8b, 0x54, 0x16, 0x9d, 0xaf, 0x84, 0x06,
+    0xde, 0x9f, 0x97, 0xf8, 0x6c, 0x2b, 0x4c, 0x67, 0x64, 0xca, 0x5b, 0x51, 0xe2, 0xd6, 0x3b, 0x99,
+    0xd1, 0x89, 0x4e, 0xe5, 0x4d, 0x90, 0x47, 0xcb, 0x07, 0xed, 0xa8, 0x2a, 0x02, 0x72, 0x17, 0xfa,
+    0x02, 0x67, 0xd2, 0xfe, 0x96, 0x7d, 0x97, 0x2f, 0x1d, 0x3f, 0xb6, 0x27, 0x30, 0x4a, 0x80, 0x46,
+    0xff, 0x7d, 0x9a, 0xa4, 0x19, 0x05, 0xb2, 0x3c, 0x21, 0x0c, 0x82, 0x07, 0x43, 0x3e, 0x0e, 0x8d,
+    0xbc, 0xa0, 0xa0, 0x37, 0x71, 0x96, 0x30, 0x85, 0xe1, 0x04, 0x96, 0x35, 0x04, 0x33, 0xc4, 0x46,
+    0x1d, 0x7d, 0x85, 0xd2, 0x18, 0x36, 0xaf, 0x0a, 0x2a, 0x93, 0x2b, 0x06, 0x78, 0x7e, 0x7c, 0x4e,
+    0x65, 0x37, 0xac, 0x32, 0xa2, 0xe9, 0xc1, 0x4b, 0xd0, 0x0a, 0x5d, 0x3e, 0xcf, 0x49, 0x7d, 0x2c,
+    0x85, 0xa3, 0x45, 0x9b, 0xe2, 0x7d, 0x8e, 0x9d, 0x0f, 0x22, 0x82, 0xd3, 0xcd, 0x02, 0x82, 0x01,
+    0x00, 0x46, 0xe8, 0x18, 0x3d, 0xbf, 0x92, 0x8c, 0xec, 0x0f, 0xa2, 0x07, 0x84, 0x07, 0xab, 0xbd,
+    0xff, 0x3b, 0xbf, 0x7a, 0x04, 0x8a, 0x85, 0x2a, 0x6d, 0xcd, 0x92, 0x16, 0xae, 0xb4, 0x4b, 0x96,
+    0xaf, 0xdb, 0xe2, 0x28, 0x44, 0xeb, 0x19, 0x58, 0x91, 0xd8, 0xd0, 0x94, 0x5c, 0x7a, 0xc8, 0x8a,
+    0x8b, 0xda, 0xef, 0xe2, 0x38, 0x82, 0x8d, 0xb3, 0xe2, 0xdb, 0x76, 0xb3, 0x9f, 0x28, 0x16, 0x8c,
+    0x3c, 0x7b, 0x07, 0x9f, 0x22, 0x0e, 0x47, 0x7e, 0x20, 0x55, 0xc4, 0x52, 0xde, 0x86, 0xfd, 0x98,
+    0xd7, 0xc6, 0x5e, 0x79, 0x05, 0x64, 0x40, 0x01, 0xb7, 0xe4, 0x2d, 0xb8, 0xd0, 0x13, 0x90, 0x4b,
+    0x3b, 0x6c, 0x63, 0xf8, 0xed, 0x6d, 0xeb, 0x09, 0x1e, 0x8f, 0xc1, 0xd4, 0xa9, 0x5e, 0x8e, 0x15,
+    0x48, 0x69, 0x7c, 0x68, 0x0e, 0xe6, 0xf6, 0xcf, 0x4a, 0x06, 0x61, 0xe9, 0x3a, 0xb0, 0x5c, 0x23,
+    0x86, 0xeb, 0xc7, 0xbb, 0x86, 0x0a, 0x37, 0x43, 0x03, 0x5b, 0x6d, 0xf4, 0xc7, 0x4b, 0xa5, 0x52,
+    0xa7, 0x3b, 0xf1, 0xf4, 0xad, 0xe1, 0xd0, 0x71, 0x34, 0x3e, 0xfa, 0xf4, 0x6e, 0xad, 0xe8, 0x97,
+    0xe4, 0xf6, 0xdf, 0x42, 0x29, 0xbc, 0xf2, 0x49, 0xfa, 0xda, 0xa6, 0x59, 0xd5, 0x74, 0xbb, 0xb1,
+    0x07, 0xeb, 0x40, 0x74, 0x4d, 0x06, 0x5b, 0x03, 0xd8, 0xdf, 0x5d, 0x02, 0xf5, 0x3d, 0xae, 0xd1,
+    0x45, 0x9a, 0xc6, 0x99, 0x10, 0x7d, 0xb8, 0x16, 0x43, 0xae, 0x9a, 0x4b, 0x69, 0x4f, 0x13, 0xe6,
+    0xbb, 0x05, 0xa9, 0x6f, 0x57, 0x75, 0xf6, 0xe6, 0x33, 0x6f, 0x2b, 0xe8, 0x6c, 0x0d, 0x10, 0xe7,
+    0x32, 0xb4, 0xee, 0x4e, 0x2a, 0x41, 0x22, 0xdb, 0x81, 0x40, 0x58, 0xdd, 0xfd, 0xd4, 0x8a, 0x8e,
+    0xc3, 0x27, 0xe7, 0x52, 0x36, 0x09, 0x50, 0x82, 0xbb, 0xad, 0x21, 0x56, 0x17, 0x8f, 0xce, 0xed,
+    0xa9, 0x02, 0x82, 0x01, 0x00, 0x78, 0x0c, 0x4f, 0x07, 0x4a, 0x2d, 0x91, 0xa3, 0xfd, 0x1c, 0xb3,
+    0xb5, 0xcb, 0x11, 0x1c, 0x04, 0xc9, 0x7f, 0xbf, 0x86, 0x7d, 0x5e, 0x2e, 0xf4, 0x2c, 0xad, 0x9e,
+    0x35, 0x74, 0x2e, 0x55, 0xc6, 0xaf, 0x1c, 0x76, 0xb9, 0x3d, 0x44, 0xe7, 0xdd, 0x5f, 0x32, 0x0e,
+    0xce, 0x4b, 0x43, 0x6d, 0x7c, 0x10, 0xd3, 0x01, 0x6f, 0x22, 0xa5, 0xb3, 0xaf, 0x40, 0x80, 0x57,
+    0xf0, 0x96, 0x7c, 0xb8, 0xae, 0xbc, 0x35, 0x8b, 0xa1, 0x59, 0xdc, 0xc4, 0xf7, 0x8b, 0xda, 0xe7,
+    0x91, 0x9b, 0xa7, 0x54, 0x61, 0xad, 0x9d, 0x0a, 0x68, 0xc3, 0xc5, 0x10, 0x9e, 0x39, 0x16, 0x9c,
+    0x3b, 0xf3, 0x3a, 0xdf, 0xb9, 0x72, 0xed, 0x4f, 0x56, 0x1e, 0x5c, 0x8b, 0x35, 0x0b, 0xa6, 0x08,
+    0x83, 0x96, 0xd4, 0x6d, 0x3f, 0xe3, 0x6a, 0x25, 0xa2, 0xe8, 0x0f, 0xce, 0x0c, 0x26, 0x85, 0x02,
+    0xba, 0xe1, 0xfc, 0xdc, 0xa0, 0x18, 0xfc, 0x7c, 0x87, 0xa6, 0x6b, 0xf5, 0x92, 0xa8, 0x83, 0x5f,
+    0xbf, 0xa5, 0xe3, 0x0e, 0x21, 0x10, 0xbf, 0x44, 0x49, 0xc7, 0x62, 0x36, 0x46, 0x0b, 0x97, 0xdc,
+    0xd4, 0x30, 0x02, 0xd4, 0x04, 0xcb, 0x8f, 0xfe, 0xde, 0x76, 0x44, 0x9f, 0xd6, 0x55, 0x8e, 0x45,
+    0xaa, 0x10, 0xd4, 0xa0, 0x2c, 0x49, 0x72, 0x6e, 0x5f, 0xf1, 0xd5, 0x0a, 0x1e, 0xba, 0xc6, 0xb4,
+    0xb6, 0x93, 0x9d, 0x6a, 0xe4, 0xaa, 0x61, 0x3a, 0xca, 0x77, 0xa4, 0x16, 0xa2, 0x4f, 0x2f, 0xad,
+    0xe6, 0xb5, 0x64, 0x93, 0x37, 0xf9, 0xdc, 0x28, 0x70, 0x54, 0x0b, 0xb2, 0x15, 0x96, 0x60, 0xbc,
+    0x86, 0x16, 0xfc, 0x5c, 0xf0, 0x75, 0xc2, 0x6f, 0x51, 0x4f, 0x71, 0xbb, 0x77, 0xed, 0xe8, 0x2b,
+    0xf2, 0xe3, 0x7d, 0x02, 0xa8, 0xfe, 0x26, 0x8a, 0x40, 0x63, 0x8f, 0x14, 0x84, 0xd2, 0x00, 0x70,
+    0x6a, 0xf3, 0xde, 0xf8, 0x66};
+constexpr const unsigned int rsa_key_4k_len = sizeof(rsa_key_4k);
+
+constexpr const unsigned char rsa_key_2k[] = {
+    0x30, 0x82, 0x04, 0xbd, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+    0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x04, 0xa7, 0x30, 0x82, 0x04, 0xa3, 0x02, 0x01,
+    0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0x9d, 0xd0, 0xca, 0x63, 0xaa, 0x59, 0x73, 0x13, 0x55, 0x53,
+    0xdc, 0x5d, 0x4d, 0xf9, 0x5c, 0x4f, 0x31, 0xe3, 0x34, 0x45, 0xbf, 0xc5, 0x8b, 0x43, 0x8f, 0x10,
+    0xb6, 0x22, 0xaf, 0xe0, 0x12, 0xff, 0xf5, 0xa8, 0x71, 0x0a, 0xe9, 0x67, 0x8d, 0x05, 0xd2, 0x69,
+    0x35, 0x32, 0x8d, 0x94, 0x9f, 0x52, 0x63, 0x4e, 0x54, 0x0b, 0x29, 0x3d, 0x7b, 0x5e, 0xc0, 0x98,
+    0x94, 0x23, 0x43, 0x46, 0x39, 0xce, 0xc2, 0xe1, 0x2b, 0xcd, 0x00, 0x6c, 0x9d, 0xe7, 0x81, 0xb9,
+    0x6b, 0x97, 0xea, 0x3a, 0xe7, 0x09, 0x06, 0x6b, 0xf3, 0xe1, 0x7a, 0x03, 0xcd, 0x51, 0x2c, 0x16,
+    0x19, 0x6c, 0xdf, 0x2c, 0xd5, 0x96, 0x92, 0x19, 0xaa, 0x91, 0xe3, 0xa5, 0xc7, 0xe1, 0x0b, 0x07,
+    0xb6, 0x84, 0xd3, 0xa7, 0x1f, 0x0d, 0x22, 0xb9, 0xc1, 0x76, 0x16, 0x81, 0x53, 0x50, 0x7d, 0x54,
+    0x2a, 0x26, 0x9e, 0xfa, 0xb1, 0xb7, 0x83, 0x05, 0x24, 0x81, 0xea, 0x5a, 0x6c, 0xb5, 0x92, 0x69,
+    0x63, 0x35, 0xfa, 0x04, 0xae, 0xee, 0xc5, 0xdb, 0xf0, 0x9b, 0xfe, 0xe6, 0xc4, 0x73, 0x18, 0x3d,
+    0xd3, 0x01, 0xaf, 0x03, 0x43, 0x95, 0xca, 0xab, 0x43, 0x04, 0x64, 0x49, 0xe7, 0x47, 0xf8, 0x97,
+    0xe5, 0x63, 0xd1, 0x3d, 0x21, 0x9b, 0xd5, 0x13, 0x2a, 0xb0, 0xf1, 0xf9, 0xff, 0xd2, 0xb7, 0x12,
+    0xa8, 0xa0, 0x20, 0x38, 0xde, 0x41, 0x8e, 0xa3, 0xc7, 0xce, 0x5b, 0x9c, 0x30, 0x1a, 0xaf, 0x13,
+    0x11, 0xd1, 0xd0, 0x71, 0x7f, 0x1e, 0x47, 0xa8, 0x32, 0x3b, 0x4a, 0x36, 0xa8, 0x6d, 0x8c, 0xd7,
+    0x5f, 0x93, 0x95, 0xa8, 0xe0, 0xfa, 0x59, 0xb5, 0x6c, 0x1f, 0xfb, 0x01, 0x64, 0xf5, 0x5d, 0xf5,
+    0x17, 0x75, 0x53, 0xfb, 0xc3, 0x3f, 0xd7, 0xc5, 0x45, 0x53, 0xb4, 0xa2, 0xcf, 0xa5, 0x71, 0xf0,
+    0x7a, 0x8b, 0x66, 0x09, 0xfa, 0x4d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x00, 0x03,
+    0xe9, 0x06, 0x4d, 0x6a, 0xe2, 0xa0, 0x2b, 0x24, 0xa1, 0x99, 0x23, 0xb2, 0x08, 0xbc, 0x2f, 0x4a,
+    0xd2, 0xa6, 0x30, 0x6b, 0x91, 0xd6, 0xf8, 0xb1, 0x0f, 0x9e, 0x71, 0x04, 0x94, 0xe8, 0xe8, 0xf1,
+    0x00, 0x1f, 0xf0, 0xea, 0x70, 0x97, 0x8f, 0x6d, 0xde, 0x65, 0x24, 0x35, 0x5a, 0xd9, 0xdf, 0x13,
+    0x8e, 0x7f, 0x74, 0x44, 0x02, 0x28, 0x7a, 0x39, 0x61, 0x19, 0x1b, 0xe3, 0x3b, 0xac, 0x62, 0x01,
+    0x9f, 0xcc, 0xfd, 0xdd, 0x85, 0x53, 0x72, 0x46, 0xdb, 0x69, 0x1d, 0x90, 0xa9, 0xad, 0xf6, 0x22,
+    0x1e, 0x6e, 0xf8, 0x06, 0xc0, 0x6d, 0x08, 0x16, 0x5a, 0x0e, 0x7e, 0x37, 0xec, 0xbc, 0xa1, 0x68,
+    0x49, 0xa7, 0x84, 0x1e, 0xdf, 0xb4, 0x30, 0xa6, 0x1d, 0x26, 0x4f, 0x6b, 0x39, 0x1d, 0xad, 0x58,
+    0x7a, 0x16, 0xca, 0x38, 0xc0, 0xdd, 0x3c, 0xc6, 0x26, 0x32, 0xe1, 0xd5, 0xc2, 0xeb, 0x6a, 0xa6,
+    0x70, 0x1f, 0x50, 0x82, 0x2d, 0xf6, 0x09, 0x27, 0x56, 0x4f, 0xf1, 0xed, 0x62, 0x60, 0xe5, 0x55,
+    0x0f, 0x8d, 0xbe, 0xd7, 0x5a, 0xb7, 0x7c, 0x57, 0x25, 0xe5, 0xa3, 0x46, 0xb6, 0x97, 0xe0, 0x87,
+    0x43, 0x04, 0x46, 0xd8, 0x4f, 0xbe, 0x80, 0x75, 0x40, 0x48, 0x2d, 0xc0, 0x57, 0xf8, 0x76, 0xbd,
+    0xd8, 0x14, 0xa0, 0x7b, 0x39, 0x4f, 0xcf, 0xdc, 0x34, 0x5b, 0x1e, 0x91, 0xef, 0xa7, 0xc8, 0x82,
+    0x2c, 0xe8, 0xe4, 0x01, 0x2b, 0xa6, 0x92, 0x4e, 0x0b, 0xd1, 0x98, 0xdc, 0x45, 0x46, 0x3f, 0x89,
+    0xb1, 0x01, 0xb5, 0xa7, 0xeb, 0x71, 0x2f, 0x09, 0x10, 0x3c, 0x71, 0x14, 0xf3, 0x86, 0x18, 0xa8,
+    0x5a, 0x30, 0xef, 0xfe, 0x87, 0x65, 0xb1, 0xaf, 0x9c, 0x8d, 0x3e, 0xc8, 0x8d, 0x72, 0xf5, 0x16,
+    0xcb, 0x3a, 0xb8, 0xb1, 0x18, 0xa5, 0x43, 0x1d, 0x24, 0xa1, 0x1c, 0x2d, 0x2d, 0x87, 0xc1, 0x02,
+    0x81, 0x81, 0x00, 0xde, 0x87, 0xff, 0x74, 0x9d, 0x86, 0x9b, 0x0b, 0x18, 0xb7, 0xa4, 0x50, 0xda,
+    0x2d, 0x27, 0xff, 0x0e, 0x4d, 0xae, 0x40, 0x21, 0x92, 0x0b, 0x1d, 0x8b, 0xdd, 0x81, 0xdc, 0x40,
+    0x1c, 0xed, 0x7d, 0x39, 0xd6, 0x1d, 0xdd, 0x88, 0xd0, 0x92, 0xee, 0x74, 0xca, 0x96, 0xa7, 0x6a,
+    0x58, 0xd3, 0xc6, 0xf4, 0x3e, 0x93, 0x43, 0x54, 0x07, 0x72, 0x3a, 0x8d, 0x3b, 0x09, 0xe0, 0x16,
+    0x16, 0x71, 0xf1, 0xae, 0xc4, 0xc0, 0x46, 0xce, 0x40, 0x6d, 0xba, 0xf0, 0x4d, 0x1d, 0x04, 0x9a,
+    0x32, 0x31, 0xe1, 0x07, 0x9b, 0x90, 0x7c, 0xc2, 0xb2, 0xfc, 0x4e, 0xb9, 0x61, 0xd4, 0xdb, 0x4a,
+    0xa1, 0xb9, 0x78, 0x46, 0x98, 0xa9, 0xfb, 0x21, 0x60, 0xb8, 0x07, 0x6b, 0x24, 0x4d, 0x4d, 0x35,
+    0x83, 0x0b, 0x21, 0xac, 0xdf, 0x93, 0x2f, 0xb5, 0xec, 0xe5, 0x99, 0x1a, 0x59, 0xaa, 0xd3, 0xbb,
+    0x8f, 0xe9, 0xad, 0x02, 0x81, 0x81, 0x00, 0xb5, 0x8d, 0x14, 0x8e, 0x85, 0xd9, 0x7c, 0x9e, 0xfc,
+    0xa1, 0x1f, 0x1a, 0x84, 0x31, 0x07, 0x71, 0x1a, 0x72, 0x91, 0xc0, 0xc5, 0xac, 0x8f, 0xa7, 0x0f,
+    0x37, 0x48, 0x51, 0x12, 0xda, 0x0d, 0x30, 0x6d, 0x97, 0x21, 0x20, 0x90, 0x49, 0x4d, 0x2b, 0xc1,
+    0x89, 0x8e, 0x00, 0x66, 0x18, 0x47, 0xd5, 0x68, 0x62, 0xe7, 0x29, 0xf4, 0x95, 0x59, 0x5b, 0xba,
+    0x4b, 0xc2, 0x20, 0xda, 0xef, 0x4f, 0x33, 0x0e, 0x99, 0xfe, 0x6c, 0xec, 0xf9, 0xd8, 0x81, 0x3a,
+    0x46, 0x1a, 0xbd, 0xba, 0xf7, 0xfc, 0xd7, 0x19, 0xf8, 0x2d, 0xd1, 0x81, 0x88, 0xce, 0x55, 0x98,
+    0xe6, 0xbc, 0x25, 0x67, 0xa6, 0xbe, 0x2b, 0x0f, 0x1d, 0xb6, 0x0d, 0xea, 0xc6, 0xb6, 0x95, 0xee,
+    0x42, 0x3e, 0x1b, 0xf5, 0x8c, 0xf3, 0x19, 0x8e, 0x59, 0xfc, 0xe1, 0x42, 0x1b, 0x26, 0x20, 0x09,
+    0x8a, 0x1b, 0x57, 0x8e, 0xe1, 0xa7, 0x21, 0x02, 0x81, 0x80, 0x78, 0xc7, 0x33, 0x7d, 0x25, 0xaa,
+    0x53, 0x28, 0x38, 0xa8, 0x23, 0x84, 0xc6, 0x85, 0xcf, 0xb9, 0x7d, 0x17, 0xe8, 0x45, 0x62, 0x73,
+    0x13, 0x99, 0x5b, 0xba, 0x43, 0xab, 0x39, 0x18, 0xfa, 0x45, 0x07, 0x49, 0x11, 0x38, 0x95, 0xf3,
+    0x2e, 0x6c, 0x41, 0xf3, 0x5a, 0xc5, 0x4e, 0xd1, 0x1b, 0x50, 0x56, 0x6c, 0x48, 0x1d, 0x38, 0xd4,
+    0x39, 0xc9, 0x51, 0xb2, 0x03, 0x70, 0x1e, 0x4c, 0xdc, 0x57, 0x22, 0x56, 0x23, 0x4d, 0xca, 0xcf,
+    0xe9, 0x3e, 0x97, 0x02, 0x23, 0x87, 0xc5, 0xf1, 0x0c, 0x65, 0x68, 0x6d, 0xa4, 0x84, 0x32, 0x60,
+    0x56, 0xd4, 0x9b, 0x85, 0x5f, 0xb4, 0x0d, 0xd3, 0xad, 0x08, 0x7c, 0xb8, 0x8b, 0x39, 0x84, 0x2a,
+    0x2c, 0x77, 0xca, 0x4d, 0x0f, 0xaf, 0xa2, 0x25, 0x97, 0xbb, 0x15, 0x4a, 0xdb, 0x65, 0xff, 0xc5,
+    0xad, 0xef, 0xe4, 0xff, 0x59, 0xda, 0x45, 0x68, 0x9c, 0x99, 0x02, 0x81, 0x80, 0x16, 0x45, 0x0a,
+    0xfb, 0x7c, 0x91, 0xb4, 0x06, 0xb0, 0x88, 0x77, 0x0f, 0x42, 0x9d, 0xdd, 0x02, 0xd3, 0xb2, 0xb0,
+    0x0c, 0x4c, 0x73, 0x21, 0x5f, 0xe5, 0xae, 0xeb, 0x50, 0xfe, 0x95, 0xfe, 0xbe, 0x2d, 0x03, 0x37,
+    0xce, 0x0d, 0xc4, 0xe0, 0x11, 0x78, 0xf9, 0x0d, 0x91, 0x20, 0xf4, 0xe3, 0x82, 0xda, 0xfe, 0x1e,
+    0xca, 0xf7, 0xb4, 0x86, 0x34, 0x89, 0x42, 0x97, 0xba, 0x7e, 0x00, 0x92, 0xdf, 0x79, 0x70, 0x0c,
+    0x54, 0x82, 0x31, 0x17, 0x8c, 0xaa, 0x80, 0x44, 0xf1, 0x77, 0x08, 0xca, 0x5b, 0xfc, 0x54, 0x84,
+    0x12, 0x49, 0xe8, 0x65, 0x1e, 0xfc, 0xd5, 0x78, 0xc8, 0xc1, 0xd1, 0x23, 0x4c, 0x96, 0xdb, 0x17,
+    0x24, 0xd7, 0xe2, 0xae, 0x2c, 0xef, 0xff, 0xf2, 0x2c, 0x6d, 0xcf, 0x6f, 0x56, 0x78, 0x2e, 0xb3,
+    0xa5, 0x51, 0xfd, 0x90, 0x8c, 0xa7, 0x7e, 0xe8, 0x61, 0xb2, 0x26, 0x1d, 0xe1, 0x02, 0x81, 0x81,
+    0x00, 0x88, 0xf6, 0x9a, 0xec, 0xab, 0xb6, 0x25, 0x2c, 0x12, 0x4e, 0x90, 0x8f, 0xea, 0xa2, 0x7e,
+    0x62, 0x41, 0xd7, 0xfd, 0x7c, 0x5d, 0xaa, 0x83, 0xfa, 0xc7, 0x48, 0x51, 0x54, 0xed, 0x72, 0x59,
+    0x95, 0xc0, 0x61, 0xdc, 0xfa, 0xc6, 0xb8, 0xd5, 0x5f, 0x9a, 0xd0, 0x7e, 0xcb, 0x3f, 0xc4, 0xfd,
+    0xb4, 0x4d, 0x46, 0x74, 0xc4, 0xf0, 0x45, 0xd4, 0x62, 0xdc, 0x27, 0x37, 0x4a, 0x8a, 0xcf, 0x27,
+    0x1a, 0x6f, 0x00, 0x50, 0x12, 0x99, 0x8d, 0xd7, 0xa8, 0xdf, 0xf3, 0xa4, 0x61, 0x18, 0x4f, 0xed,
+    0x1d, 0xac, 0x51, 0xd4, 0x44, 0x2f, 0x73, 0xea, 0xe7, 0xd7, 0xd2, 0x81, 0xe1, 0xe8, 0x4e, 0x0a,
+    0xeb, 0xbd, 0x40, 0x2b, 0x62, 0xb3, 0x43, 0x60, 0x72, 0x73, 0x31, 0xc0, 0x7a, 0x16, 0x15, 0x83,
+    0x71, 0x2f, 0x2e, 0xb4, 0x17, 0x43, 0x12, 0x30, 0x08, 0xef, 0x72, 0xdb, 0xc9, 0x50, 0x28, 0x5a,
+    0xda};
+constexpr const unsigned int rsa_key_2k_len = sizeof(rsa_key_2k);
\ No newline at end of file
diff --git a/keystore2/src/crypto/zvec.rs b/keystore2/src/crypto/zvec.rs
new file mode 100644
index 0000000..78b474e
--- /dev/null
+++ b/keystore2/src/crypto/zvec.rs
@@ -0,0 +1,119 @@
+// 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.
+
+use crate::error::Error;
+use nix::sys::mman::{mlock, munlock};
+use std::convert::TryFrom;
+use std::fmt;
+use std::ops::{Deref, DerefMut};
+use std::ptr::write_volatile;
+
+/// A semi fixed size u8 vector that is zeroed when dropped.  It can shrink in
+/// size but cannot grow larger than the original size (and if it shrinks it
+/// still owns the entire buffer).  Also the data is pinned in memory with
+/// mlock.
+#[derive(Default, Eq, PartialEq)]
+pub struct ZVec {
+    elems: Box<[u8]>,
+    len: usize,
+}
+
+impl ZVec {
+    /// Create a ZVec with the given size.
+    pub fn new(size: usize) -> Result<Self, Error> {
+        let v: Vec<u8> = vec![0; size];
+        let b = v.into_boxed_slice();
+        if size > 0 {
+            unsafe { mlock(b.as_ptr() as *const std::ffi::c_void, b.len()) }?;
+        }
+        Ok(Self { elems: b, len: size })
+    }
+
+    /// Reduce the length to the given value.  Does nothing if that length is
+    /// greater than the length of the vector.  Note that it still owns the
+    /// original allocation even if the length is reduced.
+    pub fn reduce_len(&mut self, len: usize) {
+        if len <= self.elems.len() {
+            self.len = len;
+        }
+    }
+}
+
+impl Drop for ZVec {
+    fn drop(&mut self) {
+        for i in 0..self.elems.len() {
+            unsafe { write_volatile(self.elems.as_mut_ptr().add(i), 0) };
+        }
+        if !self.elems.is_empty() {
+            if let Err(e) =
+                unsafe { munlock(self.elems.as_ptr() as *const std::ffi::c_void, self.elems.len()) }
+            {
+                log::error!("In ZVec::drop: `munlock` failed: {:?}.", e);
+            }
+        }
+    }
+}
+
+impl Deref for ZVec {
+    type Target = [u8];
+
+    fn deref(&self) -> &Self::Target {
+        &self.elems[0..self.len]
+    }
+}
+
+impl DerefMut for ZVec {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.elems[0..self.len]
+    }
+}
+
+impl fmt::Debug for ZVec {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        if self.elems.is_empty() {
+            write!(f, "Zvec empty")
+        } else {
+            write!(f, "Zvec size: {} [ Sensitive information redacted ]", self.len)
+        }
+    }
+}
+
+impl TryFrom<&[u8]> for ZVec {
+    type Error = Error;
+
+    fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
+        let mut z = ZVec::new(v.len())?;
+        if !v.is_empty() {
+            z.clone_from_slice(v);
+        }
+        Ok(z)
+    }
+}
+
+impl TryFrom<Vec<u8>> for ZVec {
+    type Error = Error;
+
+    fn try_from(mut v: Vec<u8>) -> Result<Self, Self::Error> {
+        let len = v.len();
+        // into_boxed_slice calls shrink_to_fit, which may move the pointer.
+        // But sometimes the contents of the Vec are already sensitive and
+        // mustn't be copied. So ensure the shrink_to_fit call is a NOP.
+        v.resize(v.capacity(), 0);
+        let b = v.into_boxed_slice();
+        if !b.is_empty() {
+            unsafe { mlock(b.as_ptr() as *const std::ffi::c_void, b.len()) }?;
+        }
+        Ok(Self { elems: b, len })
+    }
+}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
new file mode 100644
index 0000000..e1185f3
--- /dev/null
+++ b/keystore2/src/database.rs
@@ -0,0 +1,5593 @@
+// 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.
+
+//! This is the Keystore 2.0 database module.
+//! The database module provides a connection to the backing SQLite store.
+//! We have two databases one for persistent key blob storage and one for
+//! items that have a per boot life cycle.
+//!
+//! ## Persistent database
+//! The persistent database has tables for key blobs. They are organized
+//! as follows:
+//! The `keyentry` table is the primary table for key entries. It is
+//! accompanied by two tables for blobs and parameters.
+//! Each key entry occupies exactly one row in the `keyentry` table and
+//! zero or more rows in the tables `blobentry` and `keyparameter`.
+//!
+//! ## Per boot database
+//! The per boot database stores items with a per boot lifecycle.
+//! Currently, there is only the `grant` table in this database.
+//! Grants are references to a key that can be used to access a key by
+//! clients that don't own that key. Grants can only be created by the
+//! owner of a key. And only certain components can create grants.
+//! This is governed by SEPolicy.
+//!
+//! ## Access control
+//! Some database functions that load keys or create grants perform
+//! access control. This is because in some cases access control
+//! can only be performed after some information about the designated
+//! key was loaded from the database. To decouple the permission checks
+//! from the database module these functions take permission check
+//! callbacks.
+
+use crate::impl_metadata; // This is in db_utils.rs
+use crate::key_parameter::{KeyParameter, Tag};
+use crate::permission::KeyPermSet;
+use crate::utils::{get_current_time_in_seconds, watchdog as wd, AID_USER_OFFSET};
+use crate::{
+    db_utils::{self, SqlField},
+    gc::Gc,
+    super_key::USER_SUPER_KEY,
+};
+use crate::{
+    error::{Error as KsError, ErrorCode, ResponseCode},
+    super_key::SuperKeyType,
+};
+use anyhow::{anyhow, Context, Result};
+use std::{convert::TryFrom, convert::TryInto, ops::Deref, time::SystemTimeError};
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    HardwareAuthToken::HardwareAuthToken,
+    HardwareAuthenticatorType::HardwareAuthenticatorType, SecurityLevel::SecurityLevel,
+};
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
+    Timestamp::Timestamp,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
+use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
+    AttestationPoolStatus::AttestationPoolStatus,
+};
+use statslog_rust::keystore2_storage_stats::{
+	Keystore2StorageStats, StorageType as StatsdStorageType,
+};
+
+use keystore2_crypto::ZVec;
+use lazy_static::lazy_static;
+use log::error;
+#[cfg(not(test))]
+use rand::prelude::random;
+use rusqlite::{
+    params,
+    types::FromSql,
+    types::FromSqlResult,
+    types::ToSqlOutput,
+    types::{FromSqlError, Value, ValueRef},
+    Connection, OptionalExtension, ToSql, Transaction, TransactionBehavior, NO_PARAMS,
+};
+
+use std::{
+    collections::{HashMap, HashSet},
+    path::Path,
+    sync::{Arc, Condvar, Mutex},
+    time::{Duration, SystemTime},
+};
+
+#[cfg(test)]
+use tests::random;
+
+impl_metadata!(
+    /// A set of metadata for key entries.
+    #[derive(Debug, Default, Eq, PartialEq)]
+    pub struct KeyMetaData;
+    /// A metadata entry for key entries.
+    #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
+    pub enum KeyMetaEntry {
+        /// Date of the creation of the key entry.
+        CreationDate(DateTime) with accessor creation_date,
+        /// Expiration date for attestation keys.
+        AttestationExpirationDate(DateTime) with accessor attestation_expiration_date,
+        /// CBOR Blob that represents a COSE_Key and associated metadata needed for remote
+        /// provisioning
+        AttestationMacedPublicKey(Vec<u8>) with accessor attestation_maced_public_key,
+        /// Vector representing the raw public key so results from the server can be matched
+        /// to the right entry
+        AttestationRawPubKey(Vec<u8>) with accessor attestation_raw_pub_key,
+        /// SEC1 public key for ECDH encryption
+        Sec1PublicKey(Vec<u8>) with accessor sec1_public_key,
+        //  --- ADD NEW META DATA FIELDS HERE ---
+        // For backwards compatibility add new entries only to
+        // end of this list and above this comment.
+    };
+);
+
+impl KeyMetaData {
+    fn load_from_db(key_id: i64, tx: &Transaction) -> Result<Self> {
+        let mut stmt = tx
+            .prepare(
+                "SELECT tag, data from persistent.keymetadata
+                    WHERE keyentryid = ?;",
+            )
+            .context("In KeyMetaData::load_from_db: prepare statement failed.")?;
+
+        let mut metadata: HashMap<i64, KeyMetaEntry> = Default::default();
+
+        let mut rows =
+            stmt.query(params![key_id]).context("In KeyMetaData::load_from_db: query failed.")?;
+        db_utils::with_rows_extract_all(&mut rows, |row| {
+            let db_tag: i64 = row.get(0).context("Failed to read tag.")?;
+            metadata.insert(
+                db_tag,
+                KeyMetaEntry::new_from_sql(db_tag, &SqlField::new(1, &row))
+                    .context("Failed to read KeyMetaEntry.")?,
+            );
+            Ok(())
+        })
+        .context("In KeyMetaData::load_from_db.")?;
+
+        Ok(Self { data: metadata })
+    }
+
+    fn store_in_db(&self, key_id: i64, tx: &Transaction) -> Result<()> {
+        let mut stmt = tx
+            .prepare(
+                "INSERT or REPLACE INTO persistent.keymetadata (keyentryid, tag, data)
+                    VALUES (?, ?, ?);",
+            )
+            .context("In KeyMetaData::store_in_db: Failed to prepare statement.")?;
+
+        let iter = self.data.iter();
+        for (tag, entry) in iter {
+            stmt.insert(params![key_id, tag, entry,]).with_context(|| {
+                format!("In KeyMetaData::store_in_db: Failed to insert {:?}", entry)
+            })?;
+        }
+        Ok(())
+    }
+}
+
+impl_metadata!(
+    /// A set of metadata for key blobs.
+    #[derive(Debug, Default, Eq, PartialEq)]
+    pub struct BlobMetaData;
+    /// A metadata entry for key blobs.
+    #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
+    pub enum BlobMetaEntry {
+        /// If present, indicates that the blob is encrypted with another key or a key derived
+        /// from a password.
+        EncryptedBy(EncryptedBy) with accessor encrypted_by,
+        /// If the blob is password encrypted this field is set to the
+        /// salt used for the key derivation.
+        Salt(Vec<u8>) with accessor salt,
+        /// If the blob is encrypted, this field is set to the initialization vector.
+        Iv(Vec<u8>) with accessor iv,
+        /// If the blob is encrypted, this field holds the AEAD TAG.
+        AeadTag(Vec<u8>) with accessor aead_tag,
+        /// The uuid of the owning KeyMint instance.
+        KmUuid(Uuid) with accessor km_uuid,
+        /// If the key is ECDH encrypted, this is the ephemeral public key
+        PublicKey(Vec<u8>) with accessor public_key,
+        /// If the key is encrypted with a MaxBootLevel key, this is the boot level
+        /// of that key
+        MaxBootLevel(i32) with accessor max_boot_level,
+        //  --- ADD NEW META DATA FIELDS HERE ---
+        // For backwards compatibility add new entries only to
+        // end of this list and above this comment.
+    };
+);
+
+impl BlobMetaData {
+    fn load_from_db(blob_id: i64, tx: &Transaction) -> Result<Self> {
+        let mut stmt = tx
+            .prepare(
+                "SELECT tag, data from persistent.blobmetadata
+                    WHERE blobentryid = ?;",
+            )
+            .context("In BlobMetaData::load_from_db: prepare statement failed.")?;
+
+        let mut metadata: HashMap<i64, BlobMetaEntry> = Default::default();
+
+        let mut rows =
+            stmt.query(params![blob_id]).context("In BlobMetaData::load_from_db: query failed.")?;
+        db_utils::with_rows_extract_all(&mut rows, |row| {
+            let db_tag: i64 = row.get(0).context("Failed to read tag.")?;
+            metadata.insert(
+                db_tag,
+                BlobMetaEntry::new_from_sql(db_tag, &SqlField::new(1, &row))
+                    .context("Failed to read BlobMetaEntry.")?,
+            );
+            Ok(())
+        })
+        .context("In BlobMetaData::load_from_db.")?;
+
+        Ok(Self { data: metadata })
+    }
+
+    fn store_in_db(&self, blob_id: i64, tx: &Transaction) -> Result<()> {
+        let mut stmt = tx
+            .prepare(
+                "INSERT or REPLACE INTO persistent.blobmetadata (blobentryid, tag, data)
+                    VALUES (?, ?, ?);",
+            )
+            .context("In BlobMetaData::store_in_db: Failed to prepare statement.")?;
+
+        let iter = self.data.iter();
+        for (tag, entry) in iter {
+            stmt.insert(params![blob_id, tag, entry,]).with_context(|| {
+                format!("In BlobMetaData::store_in_db: Failed to insert {:?}", entry)
+            })?;
+        }
+        Ok(())
+    }
+}
+
+/// Indicates the type of the keyentry.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub enum KeyType {
+    /// This is a client key type. These keys are created or imported through the Keystore 2.0
+    /// AIDL interface android.system.keystore2.
+    Client,
+    /// This is a super key type. These keys are created by keystore itself and used to encrypt
+    /// other key blobs to provide LSKF binding.
+    Super,
+    /// This is an attestation key. These keys are created by the remote provisioning mechanism.
+    Attestation,
+}
+
+impl ToSql for KeyType {
+    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
+        Ok(ToSqlOutput::Owned(Value::Integer(match self {
+            KeyType::Client => 0,
+            KeyType::Super => 1,
+            KeyType::Attestation => 2,
+        })))
+    }
+}
+
+impl FromSql for KeyType {
+    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
+        match i64::column_result(value)? {
+            0 => Ok(KeyType::Client),
+            1 => Ok(KeyType::Super),
+            2 => Ok(KeyType::Attestation),
+            v => Err(FromSqlError::OutOfRange(v)),
+        }
+    }
+}
+
+/// Uuid representation that can be stored in the database.
+/// Right now it can only be initialized from SecurityLevel.
+/// Once KeyMint provides a UUID type a corresponding From impl shall be added.
+#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Uuid([u8; 16]);
+
+impl Deref for Uuid {
+    type Target = [u8; 16];
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl From<SecurityLevel> for Uuid {
+    fn from(sec_level: SecurityLevel) -> Self {
+        Self((sec_level.0 as u128).to_be_bytes())
+    }
+}
+
+impl ToSql for Uuid {
+    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
+        self.0.to_sql()
+    }
+}
+
+impl FromSql for Uuid {
+    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
+        let blob = Vec::<u8>::column_result(value)?;
+        if blob.len() != 16 {
+            return Err(FromSqlError::OutOfRange(blob.len() as i64));
+        }
+        let mut arr = [0u8; 16];
+        arr.copy_from_slice(&blob);
+        Ok(Self(arr))
+    }
+}
+
+/// Key entries that are not associated with any KeyMint instance, such as pure certificate
+/// entries are associated with this UUID.
+pub static KEYSTORE_UUID: Uuid = Uuid([
+    0x41, 0xe3, 0xb9, 0xce, 0x27, 0x58, 0x4e, 0x91, 0xbc, 0xfd, 0xa5, 0x5d, 0x91, 0x85, 0xab, 0x11,
+]);
+
+/// Indicates how the sensitive part of this key blob is encrypted.
+#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub enum EncryptedBy {
+    /// The keyblob is encrypted by a user password.
+    /// In the database this variant is represented as NULL.
+    Password,
+    /// The keyblob is encrypted by another key with wrapped key id.
+    /// In the database this variant is represented as non NULL value
+    /// that is convertible to i64, typically NUMERIC.
+    KeyId(i64),
+}
+
+impl ToSql for EncryptedBy {
+    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
+        match self {
+            Self::Password => Ok(ToSqlOutput::Owned(Value::Null)),
+            Self::KeyId(id) => id.to_sql(),
+        }
+    }
+}
+
+impl FromSql for EncryptedBy {
+    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
+        match value {
+            ValueRef::Null => Ok(Self::Password),
+            _ => Ok(Self::KeyId(i64::column_result(value)?)),
+        }
+    }
+}
+
+/// A database representation of wall clock time. DateTime stores unix epoch time as
+/// i64 in milliseconds.
+#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)]
+pub struct DateTime(i64);
+
+/// Error type returned when creating DateTime or converting it from and to
+/// SystemTime.
+#[derive(thiserror::Error, Debug)]
+pub enum DateTimeError {
+    /// This is returned when SystemTime and Duration computations fail.
+    #[error(transparent)]
+    SystemTimeError(#[from] SystemTimeError),
+
+    /// This is returned when type conversions fail.
+    #[error(transparent)]
+    TypeConversion(#[from] std::num::TryFromIntError),
+
+    /// This is returned when checked time arithmetic failed.
+    #[error("Time arithmetic failed.")]
+    TimeArithmetic,
+}
+
+impl DateTime {
+    /// Constructs a new DateTime object denoting the current time. This may fail during
+    /// conversion to unix epoch time and during conversion to the internal i64 representation.
+    pub fn now() -> Result<Self, DateTimeError> {
+        Ok(Self(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis().try_into()?))
+    }
+
+    /// Constructs a new DateTime object from milliseconds.
+    pub fn from_millis_epoch(millis: i64) -> Self {
+        Self(millis)
+    }
+
+    /// Returns unix epoch time in milliseconds.
+    pub fn to_millis_epoch(&self) -> i64 {
+        self.0
+    }
+
+    /// Returns unix epoch time in seconds.
+    pub fn to_secs_epoch(&self) -> i64 {
+        self.0 / 1000
+    }
+}
+
+impl ToSql for DateTime {
+    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
+        Ok(ToSqlOutput::Owned(Value::Integer(self.0)))
+    }
+}
+
+impl FromSql for DateTime {
+    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
+        Ok(Self(i64::column_result(value)?))
+    }
+}
+
+impl TryInto<SystemTime> for DateTime {
+    type Error = DateTimeError;
+
+    fn try_into(self) -> Result<SystemTime, Self::Error> {
+        // We want to construct a SystemTime representation equivalent to self, denoting
+        // a point in time THEN, but we cannot set the time directly. We can only construct
+        // a SystemTime denoting NOW, and we can get the duration between EPOCH and NOW,
+        // and between EPOCH and THEN. With this common reference we can construct the
+        // duration between NOW and THEN which we can add to our SystemTime representation
+        // of NOW to get a SystemTime representation of THEN.
+        // Durations can only be positive, thus the if statement below.
+        let now = SystemTime::now();
+        let now_epoch = now.duration_since(SystemTime::UNIX_EPOCH)?;
+        let then_epoch = Duration::from_millis(self.0.try_into()?);
+        Ok(if now_epoch > then_epoch {
+            // then = now - (now_epoch - then_epoch)
+            now_epoch
+                .checked_sub(then_epoch)
+                .and_then(|d| now.checked_sub(d))
+                .ok_or(DateTimeError::TimeArithmetic)?
+        } else {
+            // then = now + (then_epoch - now_epoch)
+            then_epoch
+                .checked_sub(now_epoch)
+                .and_then(|d| now.checked_add(d))
+                .ok_or(DateTimeError::TimeArithmetic)?
+        })
+    }
+}
+
+impl TryFrom<SystemTime> for DateTime {
+    type Error = DateTimeError;
+
+    fn try_from(t: SystemTime) -> Result<Self, Self::Error> {
+        Ok(Self(t.duration_since(SystemTime::UNIX_EPOCH)?.as_millis().try_into()?))
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
+enum KeyLifeCycle {
+    /// Existing keys have a key ID but are not fully populated yet.
+    /// This is a transient state. If Keystore finds any such keys when it starts up, it must move
+    /// them to Unreferenced for garbage collection.
+    Existing,
+    /// A live key is fully populated and usable by clients.
+    Live,
+    /// An unreferenced key is scheduled for garbage collection.
+    Unreferenced,
+}
+
+impl ToSql for KeyLifeCycle {
+    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
+        match self {
+            Self::Existing => Ok(ToSqlOutput::Owned(Value::Integer(0))),
+            Self::Live => Ok(ToSqlOutput::Owned(Value::Integer(1))),
+            Self::Unreferenced => Ok(ToSqlOutput::Owned(Value::Integer(2))),
+        }
+    }
+}
+
+impl FromSql for KeyLifeCycle {
+    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
+        match i64::column_result(value)? {
+            0 => Ok(KeyLifeCycle::Existing),
+            1 => Ok(KeyLifeCycle::Live),
+            2 => Ok(KeyLifeCycle::Unreferenced),
+            v => Err(FromSqlError::OutOfRange(v)),
+        }
+    }
+}
+
+/// Keys have a KeyMint blob component and optional public certificate and
+/// certificate chain components.
+/// KeyEntryLoadBits is a bitmap that indicates to `KeystoreDB::load_key_entry`
+/// which components shall be loaded from the database if present.
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
+pub struct KeyEntryLoadBits(u32);
+
+impl KeyEntryLoadBits {
+    /// Indicate to `KeystoreDB::load_key_entry` that no component shall be loaded.
+    pub const NONE: KeyEntryLoadBits = Self(0);
+    /// Indicate to `KeystoreDB::load_key_entry` that the KeyMint component shall be loaded.
+    pub const KM: KeyEntryLoadBits = Self(1);
+    /// Indicate to `KeystoreDB::load_key_entry` that the Public components shall be loaded.
+    pub const PUBLIC: KeyEntryLoadBits = Self(2);
+    /// Indicate to `KeystoreDB::load_key_entry` that both components shall be loaded.
+    pub const BOTH: KeyEntryLoadBits = Self(3);
+
+    /// Returns true if this object indicates that the public components shall be loaded.
+    pub const fn load_public(&self) -> bool {
+        self.0 & Self::PUBLIC.0 != 0
+    }
+
+    /// Returns true if the object indicates that the KeyMint component shall be loaded.
+    pub const fn load_km(&self) -> bool {
+        self.0 & Self::KM.0 != 0
+    }
+}
+
+lazy_static! {
+    static ref KEY_ID_LOCK: KeyIdLockDb = KeyIdLockDb::new();
+}
+
+struct KeyIdLockDb {
+    locked_keys: Mutex<HashSet<i64>>,
+    cond_var: Condvar,
+}
+
+/// A locked key. While a guard exists for a given key id, the same key cannot be loaded
+/// from the database a second time. Most functions manipulating the key blob database
+/// require a KeyIdGuard.
+#[derive(Debug)]
+pub struct KeyIdGuard(i64);
+
+impl KeyIdLockDb {
+    fn new() -> Self {
+        Self { locked_keys: Mutex::new(HashSet::new()), cond_var: Condvar::new() }
+    }
+
+    /// This function blocks until an exclusive lock for the given key entry id can
+    /// be acquired. It returns a guard object, that represents the lifecycle of the
+    /// acquired lock.
+    pub fn get(&self, key_id: i64) -> KeyIdGuard {
+        let mut locked_keys = self.locked_keys.lock().unwrap();
+        while locked_keys.contains(&key_id) {
+            locked_keys = self.cond_var.wait(locked_keys).unwrap();
+        }
+        locked_keys.insert(key_id);
+        KeyIdGuard(key_id)
+    }
+
+    /// This function attempts to acquire an exclusive lock on a given key id. If the
+    /// given key id is already taken the function returns None immediately. If a lock
+    /// can be acquired this function returns a guard object, that represents the
+    /// lifecycle of the acquired lock.
+    pub fn try_get(&self, key_id: i64) -> Option<KeyIdGuard> {
+        let mut locked_keys = self.locked_keys.lock().unwrap();
+        if locked_keys.insert(key_id) {
+            Some(KeyIdGuard(key_id))
+        } else {
+            None
+        }
+    }
+}
+
+impl KeyIdGuard {
+    /// Get the numeric key id of the locked key.
+    pub fn id(&self) -> i64 {
+        self.0
+    }
+}
+
+impl Drop for KeyIdGuard {
+    fn drop(&mut self) {
+        let mut locked_keys = KEY_ID_LOCK.locked_keys.lock().unwrap();
+        locked_keys.remove(&self.0);
+        drop(locked_keys);
+        KEY_ID_LOCK.cond_var.notify_all();
+    }
+}
+
+/// This type represents a certificate and certificate chain entry for a key.
+#[derive(Debug, Default)]
+pub struct CertificateInfo {
+    cert: Option<Vec<u8>>,
+    cert_chain: Option<Vec<u8>>,
+}
+
+impl CertificateInfo {
+    /// Constructs a new CertificateInfo object from `cert` and `cert_chain`
+    pub fn new(cert: Option<Vec<u8>>, cert_chain: Option<Vec<u8>>) -> Self {
+        Self { cert, cert_chain }
+    }
+
+    /// Take the cert
+    pub fn take_cert(&mut self) -> Option<Vec<u8>> {
+        self.cert.take()
+    }
+
+    /// Take the cert chain
+    pub fn take_cert_chain(&mut self) -> Option<Vec<u8>> {
+        self.cert_chain.take()
+    }
+}
+
+/// This type represents a certificate chain with a private key corresponding to the leaf
+/// certificate. TODO(jbires): This will be used in a follow-on CL, for now it's used in the tests.
+pub struct CertificateChain {
+    /// A KM key blob
+    pub private_key: ZVec,
+    /// A batch cert for private_key
+    pub batch_cert: Vec<u8>,
+    /// A full certificate chain from root signing authority to private_key, including batch_cert
+    /// for convenience.
+    pub cert_chain: Vec<u8>,
+}
+
+/// This type represents a Keystore 2.0 key entry.
+/// An entry has a unique `id` by which it can be found in the database.
+/// It has a security level field, key parameters, and three optional fields
+/// for the KeyMint blob, public certificate and a public certificate chain.
+#[derive(Debug, Default, Eq, PartialEq)]
+pub struct KeyEntry {
+    id: i64,
+    key_blob_info: Option<(Vec<u8>, BlobMetaData)>,
+    cert: Option<Vec<u8>>,
+    cert_chain: Option<Vec<u8>>,
+    km_uuid: Uuid,
+    parameters: Vec<KeyParameter>,
+    metadata: KeyMetaData,
+    pure_cert: bool,
+}
+
+impl KeyEntry {
+    /// Returns the unique id of the Key entry.
+    pub fn id(&self) -> i64 {
+        self.id
+    }
+    /// Exposes the optional KeyMint blob.
+    pub fn key_blob_info(&self) -> &Option<(Vec<u8>, BlobMetaData)> {
+        &self.key_blob_info
+    }
+    /// Extracts the Optional KeyMint blob including its metadata.
+    pub fn take_key_blob_info(&mut self) -> Option<(Vec<u8>, BlobMetaData)> {
+        self.key_blob_info.take()
+    }
+    /// Exposes the optional public certificate.
+    pub fn cert(&self) -> &Option<Vec<u8>> {
+        &self.cert
+    }
+    /// Extracts the optional public certificate.
+    pub fn take_cert(&mut self) -> Option<Vec<u8>> {
+        self.cert.take()
+    }
+    /// Exposes the optional public certificate chain.
+    pub fn cert_chain(&self) -> &Option<Vec<u8>> {
+        &self.cert_chain
+    }
+    /// Extracts the optional public certificate_chain.
+    pub fn take_cert_chain(&mut self) -> Option<Vec<u8>> {
+        self.cert_chain.take()
+    }
+    /// Returns the uuid of the owning KeyMint instance.
+    pub fn km_uuid(&self) -> &Uuid {
+        &self.km_uuid
+    }
+    /// Exposes the key parameters of this key entry.
+    pub fn key_parameters(&self) -> &Vec<KeyParameter> {
+        &self.parameters
+    }
+    /// Consumes this key entry and extracts the keyparameters from it.
+    pub fn into_key_parameters(self) -> Vec<KeyParameter> {
+        self.parameters
+    }
+    /// Exposes the key metadata of this key entry.
+    pub fn metadata(&self) -> &KeyMetaData {
+        &self.metadata
+    }
+    /// This returns true if the entry is a pure certificate entry with no
+    /// private key component.
+    pub fn pure_cert(&self) -> bool {
+        self.pure_cert
+    }
+    /// Consumes this key entry and extracts the keyparameters and metadata from it.
+    pub fn into_key_parameters_and_metadata(self) -> (Vec<KeyParameter>, KeyMetaData) {
+        (self.parameters, self.metadata)
+    }
+}
+
+/// Indicates the sub component of a key entry for persistent storage.
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
+pub struct SubComponentType(u32);
+impl SubComponentType {
+    /// Persistent identifier for a key blob.
+    pub const KEY_BLOB: SubComponentType = Self(0);
+    /// Persistent identifier for a certificate blob.
+    pub const CERT: SubComponentType = Self(1);
+    /// Persistent identifier for a certificate chain blob.
+    pub const CERT_CHAIN: SubComponentType = Self(2);
+}
+
+impl ToSql for SubComponentType {
+    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
+        self.0.to_sql()
+    }
+}
+
+impl FromSql for SubComponentType {
+    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
+        Ok(Self(u32::column_result(value)?))
+    }
+}
+
+/// This trait is private to the database module. It is used to convey whether or not the garbage
+/// collector shall be invoked after a database access. All closures passed to
+/// `KeystoreDB::with_transaction` return a tuple (bool, T) where the bool indicates if the
+/// gc needs to be triggered. This convenience function allows to turn any anyhow::Result<T>
+/// into anyhow::Result<(bool, T)> by simply appending one of `.do_gc(bool)`, `.no_gc()`, or
+/// `.need_gc()`.
+trait DoGc<T> {
+    fn do_gc(self, need_gc: bool) -> Result<(bool, T)>;
+
+    fn no_gc(self) -> Result<(bool, T)>;
+
+    fn need_gc(self) -> Result<(bool, T)>;
+}
+
+impl<T> DoGc<T> for Result<T> {
+    fn do_gc(self, need_gc: bool) -> Result<(bool, T)> {
+        self.map(|r| (need_gc, r))
+    }
+
+    fn no_gc(self) -> Result<(bool, T)> {
+        self.do_gc(false)
+    }
+
+    fn need_gc(self) -> Result<(bool, T)> {
+        self.do_gc(true)
+    }
+}
+
+/// KeystoreDB wraps a connection to an SQLite database and tracks its
+/// ownership. It also implements all of Keystore 2.0's database functionality.
+pub struct KeystoreDB {
+    conn: Connection,
+    gc: Option<Arc<Gc>>,
+}
+
+/// Database representation of the monotonic time retrieved from the system call clock_gettime with
+/// CLOCK_MONOTONIC_RAW. Stores monotonic time as i64 in seconds.
+#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)]
+pub struct MonotonicRawTime(i64);
+
+impl MonotonicRawTime {
+    /// Constructs a new MonotonicRawTime
+    pub fn now() -> Self {
+        Self(get_current_time_in_seconds())
+    }
+
+    /// Constructs a new MonotonicRawTime from a given number of seconds.
+    pub fn from_secs(val: i64) -> Self {
+        Self(val)
+    }
+
+    /// Returns the integer value of MonotonicRawTime as i64
+    pub fn seconds(&self) -> i64 {
+        self.0
+    }
+
+    /// Returns the value of MonotonicRawTime in milli seconds as i64
+    pub fn milli_seconds(&self) -> i64 {
+        self.0 * 1000
+    }
+
+    /// Like i64::checked_sub.
+    pub fn checked_sub(&self, other: &Self) -> Option<Self> {
+        self.0.checked_sub(other.0).map(Self)
+    }
+}
+
+impl ToSql for MonotonicRawTime {
+    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
+        Ok(ToSqlOutput::Owned(Value::Integer(self.0)))
+    }
+}
+
+impl FromSql for MonotonicRawTime {
+    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
+        Ok(Self(i64::column_result(value)?))
+    }
+}
+
+/// This struct encapsulates the information to be stored in the database about the auth tokens
+/// received by keystore.
+pub struct AuthTokenEntry {
+    auth_token: HardwareAuthToken,
+    time_received: MonotonicRawTime,
+}
+
+impl AuthTokenEntry {
+    fn new(auth_token: HardwareAuthToken, time_received: MonotonicRawTime) -> Self {
+        AuthTokenEntry { auth_token, time_received }
+    }
+
+    /// Checks if this auth token satisfies the given authentication information.
+    pub fn satisfies(&self, user_secure_ids: &[i64], auth_type: HardwareAuthenticatorType) -> bool {
+        user_secure_ids.iter().any(|&sid| {
+            (sid == self.auth_token.userId || sid == self.auth_token.authenticatorId)
+                && (((auth_type.0 as i32) & (self.auth_token.authenticatorType.0 as i32)) != 0)
+        })
+    }
+
+    /// Returns the auth token wrapped by the AuthTokenEntry
+    pub fn auth_token(&self) -> &HardwareAuthToken {
+        &self.auth_token
+    }
+
+    /// Returns the auth token wrapped by the AuthTokenEntry
+    pub fn take_auth_token(self) -> HardwareAuthToken {
+        self.auth_token
+    }
+
+    /// Returns the time that this auth token was received.
+    pub fn time_received(&self) -> MonotonicRawTime {
+        self.time_received
+    }
+
+    /// Returns the challenge value of the auth token.
+    pub fn challenge(&self) -> i64 {
+        self.auth_token.challenge
+    }
+}
+
+/// Shared in-memory databases get destroyed as soon as the last connection to them gets closed.
+/// This object does not allow access to the database connection. But it keeps a database
+/// connection alive in order to keep the in memory per boot database alive.
+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";
+
+    /// Name of the file that holds the cross-boot persistent database.
+    pub const PERSISTENT_DB_FILENAME: &'static str = &"persistent.sqlite";
+
+    /// This creates a PerBootDbKeepAlive object to keep the per boot database alive.
+    pub fn keep_perboot_db_alive() -> Result<PerBootDbKeepAlive> {
+        let conn = Connection::open_in_memory()
+            .context("In keep_perboot_db_alive: Failed to initialize SQLite connection.")?;
+
+        conn.execute("ATTACH DATABASE ? as perboot;", params![Self::PERBOOT_DB_FILE_NAME])
+            .context("In keep_perboot_db_alive: Failed to attach database perboot.")?;
+        Ok(PerBootDbKeepAlive(conn))
+    }
+
+    /// This will create a new database connection connecting the two
+    /// files persistent.sqlite and perboot.sqlite in the given directory.
+    /// It also attempts to initialize all of the tables.
+    /// KeystoreDB cannot be used by multiple threads.
+    /// Each thread should open their own connection using `thread_local!`.
+    pub fn new(db_root: &Path, gc: Option<Arc<Gc>>) -> Result<Self> {
+        let _wp = wd::watch_millis("KeystoreDB::new", 500);
+
+        // Build the path to the sqlite file.
+        let mut persistent_path = db_root.to_path_buf();
+        persistent_path.push(Self::PERSISTENT_DB_FILENAME);
+
+        // Now convert them to strings prefixed with "file:"
+        let mut persistent_path_str = "file:".to_owned();
+        persistent_path_str.push_str(&persistent_path.to_string_lossy());
+
+        let conn = Self::make_connection(&persistent_path_str, &Self::PERBOOT_DB_FILE_NAME)?;
+
+        // On busy fail Immediately. It is unlikely to succeed given a bug in sqlite.
+        conn.busy_handler(None).context("In KeystoreDB::new: Failed to set busy handler.")?;
+
+        let mut db = Self { conn, gc };
+        db.with_transaction(TransactionBehavior::Immediate, |tx| {
+            Self::init_tables(tx).context("Trying to initialize tables.").no_gc()
+        })?;
+        Ok(db)
+    }
+
+    fn init_tables(tx: &Transaction) -> Result<()> {
+        tx.execute(
+            "CREATE TABLE IF NOT EXISTS persistent.keyentry (
+                     id INTEGER UNIQUE,
+                     key_type INTEGER,
+                     domain INTEGER,
+                     namespace INTEGER,
+                     alias BLOB,
+                     state INTEGER,
+                     km_uuid BLOB);",
+            NO_PARAMS,
+        )
+        .context("Failed to initialize \"keyentry\" table.")?;
+
+        tx.execute(
+            "CREATE INDEX IF NOT EXISTS persistent.keyentry_id_index
+            ON keyentry(id);",
+            NO_PARAMS,
+        )
+        .context("Failed to create index keyentry_id_index.")?;
+
+        tx.execute(
+            "CREATE INDEX IF NOT EXISTS persistent.keyentry_domain_namespace_index
+            ON keyentry(domain, namespace, alias);",
+            NO_PARAMS,
+        )
+        .context("Failed to create index keyentry_domain_namespace_index.")?;
+
+        tx.execute(
+            "CREATE TABLE IF NOT EXISTS persistent.blobentry (
+                    id INTEGER PRIMARY KEY,
+                    subcomponent_type INTEGER,
+                    keyentryid INTEGER,
+                    blob BLOB);",
+            NO_PARAMS,
+        )
+        .context("Failed to initialize \"blobentry\" table.")?;
+
+        tx.execute(
+            "CREATE INDEX IF NOT EXISTS persistent.blobentry_keyentryid_index
+            ON blobentry(keyentryid);",
+            NO_PARAMS,
+        )
+        .context("Failed to create index blobentry_keyentryid_index.")?;
+
+        tx.execute(
+            "CREATE TABLE IF NOT EXISTS persistent.blobmetadata (
+                     id INTEGER PRIMARY KEY,
+                     blobentryid INTEGER,
+                     tag INTEGER,
+                     data ANY,
+                     UNIQUE (blobentryid, tag));",
+            NO_PARAMS,
+        )
+        .context("Failed to initialize \"blobmetadata\" table.")?;
+
+        tx.execute(
+            "CREATE INDEX IF NOT EXISTS persistent.blobmetadata_blobentryid_index
+            ON blobmetadata(blobentryid);",
+            NO_PARAMS,
+        )
+        .context("Failed to create index blobmetadata_blobentryid_index.")?;
+
+        tx.execute(
+            "CREATE TABLE IF NOT EXISTS persistent.keyparameter (
+                     keyentryid INTEGER,
+                     tag INTEGER,
+                     data ANY,
+                     security_level INTEGER);",
+            NO_PARAMS,
+        )
+        .context("Failed to initialize \"keyparameter\" table.")?;
+
+        tx.execute(
+            "CREATE INDEX IF NOT EXISTS persistent.keyparameter_keyentryid_index
+            ON keyparameter(keyentryid);",
+            NO_PARAMS,
+        )
+        .context("Failed to create index keyparameter_keyentryid_index.")?;
+
+        tx.execute(
+            "CREATE TABLE IF NOT EXISTS persistent.keymetadata (
+                     keyentryid INTEGER,
+                     tag INTEGER,
+                     data ANY,
+                     UNIQUE (keyentryid, tag));",
+            NO_PARAMS,
+        )
+        .context("Failed to initialize \"keymetadata\" table.")?;
+
+        tx.execute(
+            "CREATE INDEX IF NOT EXISTS persistent.keymetadata_keyentryid_index
+            ON keymetadata(keyentryid);",
+            NO_PARAMS,
+        )
+        .context("Failed to create index keymetadata_keyentryid_index.")?;
+
+        tx.execute(
+            "CREATE TABLE IF NOT EXISTS persistent.grant (
+                    id INTEGER UNIQUE,
+                    grantee INTEGER,
+                    keyentryid INTEGER,
+                    access_vector INTEGER);",
+            NO_PARAMS,
+        )
+        .context("Failed to initialize \"grant\" table.")?;
+
+        //TODO: only drop the following two perboot tables if this is the first start up
+        //during the boot (b/175716626).
+        // tx.execute("DROP TABLE IF EXISTS perboot.authtoken;", NO_PARAMS)
+        //     .context("Failed to drop perboot.authtoken table")?;
+        tx.execute(
+            "CREATE TABLE IF NOT EXISTS perboot.authtoken (
+                        id INTEGER PRIMARY KEY,
+                        challenge INTEGER,
+                        user_id INTEGER,
+                        auth_id INTEGER,
+                        authenticator_type INTEGER,
+                        timestamp INTEGER,
+                        mac BLOB,
+                        time_received INTEGER,
+                        UNIQUE(user_id, auth_id, authenticator_type));",
+            NO_PARAMS,
+        )
+        .context("Failed to initialize \"authtoken\" table.")?;
+
+        // tx.execute("DROP TABLE IF EXISTS perboot.metadata;", NO_PARAMS)
+        //     .context("Failed to drop perboot.metadata table")?;
+        // metadata table stores certain miscellaneous information required for keystore functioning
+        // during a boot cycle, as key-value pairs.
+        tx.execute(
+            "CREATE TABLE IF NOT EXISTS perboot.metadata (
+                        key TEXT,
+                        value BLOB,
+                        UNIQUE(key));",
+            NO_PARAMS,
+        )
+        .context("Failed to initialize \"metadata\" table.")?;
+        Ok(())
+    }
+
+    fn make_connection(persistent_file: &str, perboot_file: &str) -> Result<Connection> {
+        let conn =
+            Connection::open_in_memory().context("Failed to initialize SQLite connection.")?;
+
+        loop {
+            if let Err(e) = conn
+                .execute("ATTACH DATABASE ? as persistent;", params![persistent_file])
+                .context("Failed to attach database persistent.")
+            {
+                if Self::is_locked_error(&e) {
+                    std::thread::sleep(std::time::Duration::from_micros(500));
+                    continue;
+                } else {
+                    return Err(e);
+                }
+            }
+            break;
+        }
+        loop {
+            if let Err(e) = conn
+                .execute("ATTACH DATABASE ? as perboot;", params![perboot_file])
+                .context("Failed to attach database perboot.")
+            {
+                if Self::is_locked_error(&e) {
+                    std::thread::sleep(std::time::Duration::from_micros(500));
+                    continue;
+                } else {
+                    return Err(e);
+                }
+            }
+            break;
+        }
+
+        Ok(conn)
+    }
+
+    fn do_table_size_query(
+        &mut self,
+        storage_type: StatsdStorageType,
+        query: &str,
+        params: &[&str],
+    ) -> Result<Keystore2StorageStats> {
+        let (total, unused) = self.with_transaction(TransactionBehavior::Deferred, |tx| {
+            tx.query_row(query, params, |row| Ok((row.get(0)?, row.get(1)?)))
+                .with_context(|| {
+                    format!("get_storage_stat: Error size of storage type {}", storage_type as i32)
+                })
+                .no_gc()
+        })?;
+        Ok(Keystore2StorageStats { storage_type, size: total, unused_size: unused })
+    }
+
+    fn get_total_size(&mut self) -> Result<Keystore2StorageStats> {
+        self.do_table_size_query(
+            StatsdStorageType::Database,
+            "SELECT page_count * page_size, freelist_count * page_size
+             FROM pragma_page_count('persistent'),
+                  pragma_page_size('persistent'),
+                  persistent.pragma_freelist_count();",
+            &[],
+        )
+    }
+
+    fn get_table_size(
+        &mut self,
+        storage_type: StatsdStorageType,
+        schema: &str,
+        table: &str,
+    ) -> Result<Keystore2StorageStats> {
+        self.do_table_size_query(
+            storage_type,
+            "SELECT pgsize,unused FROM dbstat(?1)
+             WHERE name=?2 AND aggregate=TRUE;",
+            &[schema, table],
+        )
+    }
+
+    /// Fetches a storage statisitics atom for a given storage type. For storage
+    /// types that map to a table, information about the table's storage is
+    /// returned. Requests for storage types that are not DB tables return None.
+    pub fn get_storage_stat(
+        &mut self,
+        storage_type: StatsdStorageType,
+    ) -> Result<Keystore2StorageStats> {
+        let _wp = wd::watch_millis("KeystoreDB::get_storage_stat", 500);
+
+        match storage_type {
+            StatsdStorageType::Database => self.get_total_size(),
+            StatsdStorageType::KeyEntry => {
+                self.get_table_size(storage_type, "persistent", "keyentry")
+            }
+            StatsdStorageType::KeyEntryIdIndex => {
+                self.get_table_size(storage_type, "persistent", "keyentry_id_index")
+            }
+            StatsdStorageType::KeyEntryDomainNamespaceIndex => {
+                self.get_table_size(storage_type, "persistent", "keyentry_domain_namespace_index")
+            }
+            StatsdStorageType::BlobEntry => {
+                self.get_table_size(storage_type, "persistent", "blobentry")
+            }
+            StatsdStorageType::BlobEntryKeyEntryIdIndex => {
+                self.get_table_size(storage_type, "persistent", "blobentry_keyentryid_index")
+            }
+            StatsdStorageType::KeyParameter => {
+                self.get_table_size(storage_type, "persistent", "keyparameter")
+            }
+            StatsdStorageType::KeyParameterKeyEntryIdIndex => {
+                self.get_table_size(storage_type, "persistent", "keyparameter_keyentryid_index")
+            }
+            StatsdStorageType::KeyMetadata => {
+                self.get_table_size(storage_type, "persistent", "keymetadata")
+            }
+            StatsdStorageType::KeyMetadataKeyEntryIdIndex => {
+                self.get_table_size(storage_type, "persistent", "keymetadata_keyentryid_index")
+            }
+            StatsdStorageType::Grant => self.get_table_size(storage_type, "persistent", "grant"),
+            StatsdStorageType::AuthToken => {
+                self.get_table_size(storage_type, "perboot", "authtoken")
+            }
+            StatsdStorageType::BlobMetadata => {
+                self.get_table_size(storage_type, "persistent", "blobmetadata")
+            }
+            StatsdStorageType::BlobMetadataBlobEntryIdIndex => {
+                self.get_table_size(storage_type, "persistent", "blobmetadata_blobentryid_index")
+            }
+            _ => Err(anyhow::Error::msg(format!(
+                "Unsupported storage type: {}",
+                storage_type as i32
+            ))),
+        }
+    }
+
+    /// This function is intended to be used by the garbage collector.
+    /// It deletes the blobs given by `blob_ids_to_delete`. It then tries to find up to `max_blobs`
+    /// superseded key blobs that might need special handling by the garbage collector.
+    /// If no further superseded blobs can be found it deletes all other superseded blobs that don't
+    /// need special handling and returns None.
+    pub fn handle_next_superseded_blobs(
+        &mut self,
+        blob_ids_to_delete: &[i64],
+        max_blobs: usize,
+    ) -> Result<Vec<(i64, Vec<u8>, BlobMetaData)>> {
+        let _wp = wd::watch_millis("KeystoreDB::handle_next_superseded_blob", 500);
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            // Delete the given blobs.
+            for blob_id in blob_ids_to_delete {
+                tx.execute(
+                    "DELETE FROM persistent.blobmetadata WHERE blobentryid = ?;",
+                    params![blob_id],
+                )
+                .context("Trying to delete blob metadata.")?;
+                tx.execute("DELETE FROM persistent.blobentry WHERE id = ?;", params![blob_id])
+                    .context("Trying to blob.")?;
+            }
+
+            Self::cleanup_unreferenced(tx).context("Trying to cleanup unreferenced.")?;
+
+            // Find up to max_blobx more superseded key blobs, load their metadata and return it.
+            let result: Vec<(i64, Vec<u8>)> = {
+                let mut stmt = tx
+                    .prepare(
+                        "SELECT id, blob FROM persistent.blobentry
+                        WHERE subcomponent_type = ?
+                        AND (
+                            id NOT IN (
+                                SELECT MAX(id) FROM persistent.blobentry
+                                WHERE subcomponent_type = ?
+                                GROUP BY keyentryid, subcomponent_type
+                            )
+                        OR keyentryid NOT IN (SELECT id FROM persistent.keyentry)
+                    ) LIMIT ?;",
+                    )
+                    .context("Trying to prepare query for superseded blobs.")?;
+
+                let rows = stmt
+                    .query_map(
+                        params![
+                            SubComponentType::KEY_BLOB,
+                            SubComponentType::KEY_BLOB,
+                            max_blobs as i64,
+                        ],
+                        |row| Ok((row.get(0)?, row.get(1)?)),
+                    )
+                    .context("Trying to query superseded blob.")?;
+
+                rows.collect::<Result<Vec<(i64, Vec<u8>)>, rusqlite::Error>>()
+                    .context("Trying to extract superseded blobs.")?
+            };
+
+            let result = result
+                .into_iter()
+                .map(|(blob_id, blob)| {
+                    Ok((blob_id, blob, BlobMetaData::load_from_db(blob_id, tx)?))
+                })
+                .collect::<Result<Vec<(i64, Vec<u8>, BlobMetaData)>>>()
+                .context("Trying to load blob metadata.")?;
+            if !result.is_empty() {
+                return Ok(result).no_gc();
+            }
+
+            // We did not find any superseded key blob, so let's remove other superseded blob in
+            // one transaction.
+            tx.execute(
+                "DELETE FROM persistent.blobentry
+                 WHERE NOT subcomponent_type = ?
+                 AND (
+                     id NOT IN (
+                        SELECT MAX(id) FROM persistent.blobentry
+                        WHERE NOT subcomponent_type = ?
+                        GROUP BY keyentryid, subcomponent_type
+                     ) OR keyentryid NOT IN (SELECT id FROM persistent.keyentry)
+                 );",
+                params![SubComponentType::KEY_BLOB, SubComponentType::KEY_BLOB],
+            )
+            .context("Trying to purge superseded blobs.")?;
+
+            Ok(vec![]).no_gc()
+        })
+        .context("In handle_next_superseded_blobs.")
+    }
+
+    /// This maintenance function should be called only once before the database is used for the
+    /// first time. It restores the invariant that `KeyLifeCycle::Existing` is a transient state.
+    /// The function transitions all key entries from Existing to Unreferenced unconditionally and
+    /// returns the number of rows affected. If this returns a value greater than 0, it means that
+    /// Keystore crashed at some point during key generation. Callers may want to log such
+    /// occurrences.
+    /// Unlike with `mark_unreferenced`, we don't need to purge grants, because only keys that made
+    /// it to `KeyLifeCycle::Live` may have grants.
+    pub fn cleanup_leftovers(&mut self) -> Result<usize> {
+        let _wp = wd::watch_millis("KeystoreDB::cleanup_leftovers", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            tx.execute(
+                "UPDATE persistent.keyentry SET state = ? WHERE state = ?;",
+                params![KeyLifeCycle::Unreferenced, KeyLifeCycle::Existing],
+            )
+            .context("Failed to execute query.")
+            .need_gc()
+        })
+        .context("In cleanup_leftovers.")
+    }
+
+    /// Checks if a key exists with given key type and key descriptor properties.
+    pub fn key_exists(
+        &mut self,
+        domain: Domain,
+        nspace: i64,
+        alias: &str,
+        key_type: KeyType,
+    ) -> Result<bool> {
+        let _wp = wd::watch_millis("KeystoreDB::key_exists", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let key_descriptor =
+                KeyDescriptor { domain, nspace, alias: Some(alias.to_string()), blob: None };
+            let result = Self::load_key_entry_id(&tx, &key_descriptor, key_type);
+            match result {
+                Ok(_) => Ok(true),
+                Err(error) => match error.root_cause().downcast_ref::<KsError>() {
+                    Some(KsError::Rc(ResponseCode::KEY_NOT_FOUND)) => Ok(false),
+                    _ => Err(error).context("In key_exists: Failed to find if the key exists."),
+                },
+            }
+            .no_gc()
+        })
+        .context("In key_exists.")
+    }
+
+    /// Stores a super key in the database.
+    pub fn store_super_key(
+        &mut self,
+        user_id: u32,
+        key_type: &SuperKeyType,
+        blob: &[u8],
+        blob_metadata: &BlobMetaData,
+        key_metadata: &KeyMetaData,
+    ) -> Result<KeyEntry> {
+        let _wp = wd::watch_millis("KeystoreDB::store_super_key", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let key_id = Self::insert_with_retry(|id| {
+                tx.execute(
+                    "INSERT into persistent.keyentry
+                            (id, key_type, domain, namespace, alias, state, km_uuid)
+                            VALUES(?, ?, ?, ?, ?, ?, ?);",
+                    params![
+                        id,
+                        KeyType::Super,
+                        Domain::APP.0,
+                        user_id as i64,
+                        key_type.alias,
+                        KeyLifeCycle::Live,
+                        &KEYSTORE_UUID,
+                    ],
+                )
+            })
+            .context("Failed to insert into keyentry table.")?;
+
+            key_metadata.store_in_db(key_id, tx).context("KeyMetaData::store_in_db failed")?;
+
+            Self::set_blob_internal(
+                &tx,
+                key_id,
+                SubComponentType::KEY_BLOB,
+                Some(blob),
+                Some(blob_metadata),
+            )
+            .context("Failed to store key blob.")?;
+
+            Self::load_key_components(tx, KeyEntryLoadBits::KM, key_id)
+                .context("Trying to load key components.")
+                .no_gc()
+        })
+        .context("In store_super_key.")
+    }
+
+    /// Loads super key of a given user, if exists
+    pub fn load_super_key(
+        &mut self,
+        key_type: &SuperKeyType,
+        user_id: u32,
+    ) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
+        let _wp = wd::watch_millis("KeystoreDB::load_super_key", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let key_descriptor = KeyDescriptor {
+                domain: Domain::APP,
+                nspace: user_id as i64,
+                alias: Some(key_type.alias.into()),
+                blob: None,
+            };
+            let id = Self::load_key_entry_id(&tx, &key_descriptor, KeyType::Super);
+            match id {
+                Ok(id) => {
+                    let key_entry = Self::load_key_components(&tx, KeyEntryLoadBits::KM, id)
+                        .context("In load_super_key. Failed to load key entry.")?;
+                    Ok(Some((KEY_ID_LOCK.get(id), key_entry)))
+                }
+                Err(error) => match error.root_cause().downcast_ref::<KsError>() {
+                    Some(KsError::Rc(ResponseCode::KEY_NOT_FOUND)) => Ok(None),
+                    _ => Err(error).context("In load_super_key."),
+                },
+            }
+            .no_gc()
+        })
+        .context("In load_super_key.")
+    }
+
+    /// Atomically loads a key entry and associated metadata or creates it using the
+    /// callback create_new_key callback. The callback is called during a database
+    /// transaction. This means that implementers should be mindful about using
+    /// blocking operations such as IPC or grabbing mutexes.
+    pub fn get_or_create_key_with<F>(
+        &mut self,
+        domain: Domain,
+        namespace: i64,
+        alias: &str,
+        km_uuid: Uuid,
+        create_new_key: F,
+    ) -> Result<(KeyIdGuard, KeyEntry)>
+    where
+        F: Fn() -> Result<(Vec<u8>, BlobMetaData)>,
+    {
+        let _wp = wd::watch_millis("KeystoreDB::get_or_create_key_with", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let id = {
+                let mut stmt = tx
+                    .prepare(
+                        "SELECT id FROM persistent.keyentry
+                    WHERE
+                    key_type = ?
+                    AND domain = ?
+                    AND namespace = ?
+                    AND alias = ?
+                    AND state = ?;",
+                    )
+                    .context("In get_or_create_key_with: Failed to select from keyentry table.")?;
+                let mut rows = stmt
+                    .query(params![KeyType::Super, domain.0, namespace, alias, KeyLifeCycle::Live])
+                    .context("In get_or_create_key_with: Failed to query from keyentry table.")?;
+
+                db_utils::with_rows_extract_one(&mut rows, |row| {
+                    Ok(match row {
+                        Some(r) => r.get(0).context("Failed to unpack id.")?,
+                        None => None,
+                    })
+                })
+                .context("In get_or_create_key_with.")?
+            };
+
+            let (id, entry) = match id {
+                Some(id) => (
+                    id,
+                    Self::load_key_components(&tx, KeyEntryLoadBits::KM, id)
+                        .context("In get_or_create_key_with.")?,
+                ),
+
+                None => {
+                    let id = Self::insert_with_retry(|id| {
+                        tx.execute(
+                            "INSERT into persistent.keyentry
+                        (id, key_type, domain, namespace, alias, state, km_uuid)
+                        VALUES(?, ?, ?, ?, ?, ?, ?);",
+                            params![
+                                id,
+                                KeyType::Super,
+                                domain.0,
+                                namespace,
+                                alias,
+                                KeyLifeCycle::Live,
+                                km_uuid,
+                            ],
+                        )
+                    })
+                    .context("In get_or_create_key_with.")?;
+
+                    let (blob, metadata) =
+                        create_new_key().context("In get_or_create_key_with.")?;
+                    Self::set_blob_internal(
+                        &tx,
+                        id,
+                        SubComponentType::KEY_BLOB,
+                        Some(&blob),
+                        Some(&metadata),
+                    )
+                    .context("In get_or_create_key_with.")?;
+                    (
+                        id,
+                        KeyEntry {
+                            id,
+                            key_blob_info: Some((blob, metadata)),
+                            pure_cert: false,
+                            ..Default::default()
+                        },
+                    )
+                }
+            };
+            Ok((KEY_ID_LOCK.get(id), entry)).no_gc()
+        })
+        .context("In get_or_create_key_with.")
+    }
+
+    /// SQLite3 seems to hold a shared mutex while running the busy handler when
+    /// waiting for the database file to become available. This makes it
+    /// impossible to successfully recover from a locked database when the
+    /// transaction holding the device busy is in the same process on a
+    /// different connection. As a result the busy handler has to time out and
+    /// fail in order to make progress.
+    ///
+    /// Instead, we set the busy handler to None (return immediately). And catch
+    /// Busy and Locked errors (the latter occur on in memory databases with
+    /// shared cache, e.g., the per-boot database.) and restart the transaction
+    /// after a grace period of half a millisecond.
+    ///
+    /// Creates a transaction with the given behavior and executes f with the new transaction.
+    /// The transaction is committed only if f returns Ok and retried if DatabaseBusy
+    /// or DatabaseLocked is encountered.
+    fn with_transaction<T, F>(&mut self, behavior: TransactionBehavior, f: F) -> Result<T>
+    where
+        F: Fn(&Transaction) -> Result<(bool, 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.");
+                    }
+                }
+            }
+        }
+        .map(|(need_gc, result)| {
+            if need_gc {
+                if let Some(ref gc) = self.gc {
+                    gc.notify_gc();
+                }
+            }
+            result
+        })
+    }
+
+    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, .. })
+        )
+    }
+
+    /// Creates a new key entry and allocates a new randomized id for the new key.
+    /// The key id gets associated with a domain and namespace but not with an alias.
+    /// To complete key generation `rebind_alias` should be called after all of the
+    /// key artifacts, i.e., blobs and parameters have been associated with the new
+    /// key id. Finalizing with `rebind_alias` makes the creation of a new key entry
+    /// atomic even if key generation is not.
+    pub fn create_key_entry(
+        &mut self,
+        domain: &Domain,
+        namespace: &i64,
+        km_uuid: &Uuid,
+    ) -> Result<KeyIdGuard> {
+        let _wp = wd::watch_millis("KeystoreDB::create_key_entry", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            Self::create_key_entry_internal(tx, domain, namespace, km_uuid).no_gc()
+        })
+        .context("In create_key_entry.")
+    }
+
+    fn create_key_entry_internal(
+        tx: &Transaction,
+        domain: &Domain,
+        namespace: &i64,
+        km_uuid: &Uuid,
+    ) -> Result<KeyIdGuard> {
+        match *domain {
+            Domain::APP | Domain::SELINUX => {}
+            _ => {
+                return Err(KsError::sys())
+                    .context(format!("Domain {:?} must be either App or SELinux.", domain));
+            }
+        }
+        Ok(KEY_ID_LOCK.get(
+            Self::insert_with_retry(|id| {
+                tx.execute(
+                    "INSERT into persistent.keyentry
+                     (id, key_type, domain, namespace, alias, state, km_uuid)
+                     VALUES(?, ?, ?, ?, NULL, ?, ?);",
+                    params![
+                        id,
+                        KeyType::Client,
+                        domain.0 as u32,
+                        *namespace,
+                        KeyLifeCycle::Existing,
+                        km_uuid,
+                    ],
+                )
+            })
+            .context("In create_key_entry_internal")?,
+        ))
+    }
+
+    /// Creates a new attestation key entry and allocates a new randomized id for the new key.
+    /// The key id gets associated with a domain and namespace later but not with an alias. The
+    /// alias will be used to denote if a key has been signed as each key can only be bound to one
+    /// domain and namespace pairing so there is no need to use them as a value for indexing into
+    /// a key.
+    pub fn create_attestation_key_entry(
+        &mut self,
+        maced_public_key: &[u8],
+        raw_public_key: &[u8],
+        private_key: &[u8],
+        km_uuid: &Uuid,
+    ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::create_attestation_key_entry", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let key_id = KEY_ID_LOCK.get(
+                Self::insert_with_retry(|id| {
+                    tx.execute(
+                        "INSERT into persistent.keyentry
+                            (id, key_type, domain, namespace, alias, state, km_uuid)
+                            VALUES(?, ?, NULL, NULL, NULL, ?, ?);",
+                        params![id, KeyType::Attestation, KeyLifeCycle::Live, km_uuid],
+                    )
+                })
+                .context("In create_key_entry")?,
+            );
+            Self::set_blob_internal(
+                &tx,
+                key_id.0,
+                SubComponentType::KEY_BLOB,
+                Some(private_key),
+                None,
+            )?;
+            let mut metadata = KeyMetaData::new();
+            metadata.add(KeyMetaEntry::AttestationMacedPublicKey(maced_public_key.to_vec()));
+            metadata.add(KeyMetaEntry::AttestationRawPubKey(raw_public_key.to_vec()));
+            metadata.store_in_db(key_id.0, &tx)?;
+            Ok(()).no_gc()
+        })
+        .context("In create_attestation_key_entry")
+    }
+
+    /// Set a new blob and associates it with the given key id. Each blob
+    /// has a sub component type.
+    /// Each key can have one of each sub component type associated. If more
+    /// are added only the most recent can be retrieved, and superseded blobs
+    /// will get garbage collected.
+    /// Components SubComponentType::CERT and SubComponentType::CERT_CHAIN can be
+    /// removed by setting blob to None.
+    pub fn set_blob(
+        &mut self,
+        key_id: &KeyIdGuard,
+        sc_type: SubComponentType,
+        blob: Option<&[u8]>,
+        blob_metadata: Option<&BlobMetaData>,
+    ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::set_blob", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            Self::set_blob_internal(&tx, key_id.0, sc_type, blob, blob_metadata).need_gc()
+        })
+        .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<()> {
+        let _wp = wd::watch_millis("KeystoreDB::set_deleted_blob", 500);
+
+        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,
+        sc_type: SubComponentType,
+        blob: Option<&[u8]>,
+        blob_metadata: Option<&BlobMetaData>,
+    ) -> Result<()> {
+        match (blob, sc_type) {
+            (Some(blob), _) => {
+                tx.execute(
+                    "INSERT INTO persistent.blobentry
+                     (subcomponent_type, keyentryid, blob) VALUES (?, ?, ?);",
+                    params![sc_type, key_id, blob],
+                )
+                .context("In set_blob_internal: Failed to insert blob.")?;
+                if let Some(blob_metadata) = blob_metadata {
+                    let blob_id = tx
+                        .query_row("SELECT MAX(id) FROM persistent.blobentry;", NO_PARAMS, |row| {
+                            row.get(0)
+                        })
+                        .context("In set_blob_internal: Failed to get new blob id.")?;
+                    blob_metadata
+                        .store_in_db(blob_id, tx)
+                        .context("In set_blob_internal: Trying to store blob metadata.")?;
+                }
+            }
+            (None, SubComponentType::CERT) | (None, SubComponentType::CERT_CHAIN) => {
+                tx.execute(
+                    "DELETE FROM persistent.blobentry
+                    WHERE subcomponent_type = ? AND keyentryid = ?;",
+                    params![sc_type, key_id],
+                )
+                .context("In set_blob_internal: Failed to delete blob.")?;
+            }
+            (None, _) => {
+                return Err(KsError::sys())
+                    .context("In set_blob_internal: Other blobs cannot be deleted in this way.");
+            }
+        }
+        Ok(())
+    }
+
+    /// Inserts a collection of key parameters into the `persistent.keyparameter` table
+    /// and associates them with the given `key_id`.
+    #[cfg(test)]
+    fn insert_keyparameter(&mut self, key_id: &KeyIdGuard, params: &[KeyParameter]) -> Result<()> {
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            Self::insert_keyparameter_internal(tx, key_id, params).no_gc()
+        })
+        .context("In insert_keyparameter.")
+    }
+
+    fn insert_keyparameter_internal(
+        tx: &Transaction,
+        key_id: &KeyIdGuard,
+        params: &[KeyParameter],
+    ) -> Result<()> {
+        let mut stmt = tx
+            .prepare(
+                "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level)
+                VALUES (?, ?, ?, ?);",
+            )
+            .context("In insert_keyparameter_internal: Failed to prepare statement.")?;
+
+        for p in params.iter() {
+            stmt.insert(params![
+                key_id.0,
+                p.get_tag().0,
+                p.key_parameter_value(),
+                p.security_level().0
+            ])
+            .with_context(|| {
+                format!("In insert_keyparameter_internal: Failed to insert {:?}", p)
+            })?;
+        }
+        Ok(())
+    }
+
+    /// Insert a set of key entry specific metadata into the database.
+    #[cfg(test)]
+    fn insert_key_metadata(&mut self, key_id: &KeyIdGuard, metadata: &KeyMetaData) -> Result<()> {
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            metadata.store_in_db(key_id.0, &tx).no_gc()
+        })
+        .context("In insert_key_metadata.")
+    }
+
+    /// Stores a signed certificate chain signed by a remote provisioning server, keyed
+    /// on the public key.
+    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,
+    ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::store_signed_attestation_certificate_chain", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let mut stmt = tx
+                .prepare(
+                    "SELECT keyentryid
+                    FROM persistent.keymetadata
+                    WHERE tag = ? AND data = ? AND keyentryid IN
+                    (SELECT id
+                     FROM persistent.keyentry
+                     WHERE
+                        alias IS NULL AND
+                        domain IS NULL AND
+                        namespace IS NULL AND
+                        key_type = ? AND
+                        km_uuid = ?);",
+                )
+                .context("Failed to store attestation certificate chain.")?;
+            let mut rows = stmt
+                .query(params![
+                    KeyMetaData::AttestationRawPubKey,
+                    raw_public_key,
+                    KeyType::Attestation,
+                    km_uuid
+                ])
+                .context("Failed to fetch keyid")?;
+            let key_id = db_utils::with_rows_extract_one(&mut rows, |row| {
+                row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?
+                    .get(0)
+                    .context("Failed to unpack id.")
+            })
+            .context("Failed to get key_id.")?;
+            let num_updated = tx
+                .execute(
+                    "UPDATE persistent.keyentry
+                    SET alias = ?
+                    WHERE id = ?;",
+                    params!["signed", key_id],
+                )
+                .context("Failed to update alias.")?;
+            if num_updated != 1 {
+                return Err(KsError::sys()).context("Alias not updated for the key.");
+            }
+            let mut metadata = KeyMetaData::new();
+            metadata.add(KeyMetaEntry::AttestationExpirationDate(DateTime::from_millis_epoch(
+                expiration_date,
+            )));
+            metadata.store_in_db(key_id, &tx).context("Failed to insert key metadata.")?;
+            Self::set_blob_internal(
+                &tx,
+                key_id,
+                SubComponentType::CERT_CHAIN,
+                Some(cert_chain),
+                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: ")
+    }
+
+    /// Assigns the next unassigned attestation key to a domain/namespace combo that does not
+    /// currently have a key assigned to it.
+    pub fn assign_attestation_key(
+        &mut self,
+        domain: Domain,
+        namespace: i64,
+        km_uuid: &Uuid,
+    ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::assign_attestation_key", 500);
+
+        match domain {
+            Domain::APP | Domain::SELINUX => {}
+            _ => {
+                return Err(KsError::sys()).context(format!(
+                    concat!(
+                        "In assign_attestation_key: Domain {:?} ",
+                        "must be either App or SELinux.",
+                    ),
+                    domain
+                ));
+            }
+        }
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let result = tx
+                .execute(
+                    "UPDATE persistent.keyentry
+                        SET domain=?1, namespace=?2
+                        WHERE
+                            id =
+                                (SELECT MIN(id)
+                                FROM persistent.keyentry
+                                WHERE ALIAS IS NOT NULL
+                                    AND domain IS NULL
+                                    AND key_type IS ?3
+                                    AND state IS ?4
+                                    AND km_uuid IS ?5)
+                            AND
+                                (SELECT COUNT(*)
+                                FROM persistent.keyentry
+                                WHERE domain=?1
+                                    AND namespace=?2
+                                    AND key_type IS ?3
+                                    AND state IS ?4
+                                    AND km_uuid IS ?5) = 0;",
+                    params![
+                        domain.0 as u32,
+                        namespace,
+                        KeyType::Attestation,
+                        KeyLifeCycle::Live,
+                        km_uuid,
+                    ],
+                )
+                .context("Failed to assign attestation key")?;
+            if result == 0 {
+                return Err(KsError::Rc(ResponseCode::OUT_OF_KEYS)).context("Out of keys.");
+            } else if result > 1 {
+                return Err(KsError::sys())
+                    .context(format!("Expected to update 1 entry, instead updated {}", result));
+            }
+            Ok(()).no_gc()
+        })
+        .context("In assign_attestation_key: ")
+    }
+
+    /// Retrieves num_keys number of attestation keys that have not yet been signed by a remote
+    /// provisioning server, or the maximum number available if there are not num_keys number of
+    /// entries in the table.
+    pub fn fetch_unsigned_attestation_keys(
+        &mut self,
+        num_keys: i32,
+        km_uuid: &Uuid,
+    ) -> Result<Vec<Vec<u8>>> {
+        let _wp = wd::watch_millis("KeystoreDB::fetch_unsigned_attestation_keys", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let mut stmt = tx
+                .prepare(
+                    "SELECT data
+                    FROM persistent.keymetadata
+                    WHERE tag = ? AND keyentryid IN
+                        (SELECT id
+                        FROM persistent.keyentry
+                        WHERE
+                            alias IS NULL AND
+                            domain IS NULL AND
+                            namespace IS NULL AND
+                            key_type = ? AND
+                            km_uuid = ?
+                        LIMIT ?);",
+                )
+                .context("Failed to prepare statement")?;
+            let rows = stmt
+                .query_map(
+                    params![
+                        KeyMetaData::AttestationMacedPublicKey,
+                        KeyType::Attestation,
+                        km_uuid,
+                        num_keys
+                    ],
+                    |row| row.get(0),
+                )?
+                .collect::<rusqlite::Result<Vec<Vec<u8>>>>()
+                .context("Failed to execute statement")?;
+            Ok(rows).no_gc()
+        })
+        .context("In fetch_unsigned_attestation_keys")
+    }
+
+    /// Removes any keys that have expired as of the current time. Returns the number of keys
+    /// marked unreferenced that are bound to be garbage collected.
+    pub fn delete_expired_attestation_keys(&mut self) -> Result<i32> {
+        let _wp = wd::watch_millis("KeystoreDB::delete_expired_attestation_keys", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let mut stmt = tx
+                .prepare(
+                    "SELECT keyentryid, data
+                     FROM persistent.keymetadata
+                     WHERE tag = ? AND keyentryid IN
+                         (SELECT id
+                         FROM persistent.keyentry
+                         WHERE key_type = ?);",
+                )
+                .context("Failed to prepare query")?;
+            let key_ids_to_check = stmt
+                .query_map(
+                    params![KeyMetaData::AttestationExpirationDate, KeyType::Attestation],
+                    |row| Ok((row.get(0)?, row.get(1)?)),
+                )?
+                .collect::<rusqlite::Result<Vec<(i64, DateTime)>>>()
+                .context("Failed to get date metadata")?;
+            let curr_time = DateTime::from_millis_epoch(
+                SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis() as i64,
+            );
+            let mut num_deleted = 0;
+            for id in key_ids_to_check.iter().filter(|kt| kt.1 < curr_time).map(|kt| kt.0) {
+                if Self::mark_unreferenced(&tx, id)? {
+                    num_deleted += 1;
+                }
+            }
+            Ok(num_deleted).do_gc(num_deleted != 0)
+        })
+        .context("In delete_expired_attestation_keys: ")
+    }
+
+    /// Deletes all remotely provisioned attestation keys in the system, regardless of the state
+    /// they are in. This is useful primarily as a testing mechanism.
+    pub fn delete_all_attestation_keys(&mut self) -> Result<i64> {
+        let _wp = wd::watch_millis("KeystoreDB::delete_all_attestation_keys", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let mut stmt = tx
+                .prepare(
+                    "SELECT id FROM persistent.keyentry
+                    WHERE key_type IS ?;",
+                )
+                .context("Failed to prepare statement")?;
+            let keys_to_delete = stmt
+                .query_map(params![KeyType::Attestation], |row| row.get(0))?
+                .collect::<rusqlite::Result<Vec<i64>>>()
+                .context("Failed to execute statement")?;
+            let num_deleted = keys_to_delete
+                .iter()
+                .map(|id| Self::mark_unreferenced(&tx, *id))
+                .collect::<Result<Vec<bool>>>()
+                .context("Failed to execute mark_unreferenced on a keyid")?
+                .into_iter()
+                .filter(|result| *result)
+                .count() as i64;
+            Ok(num_deleted).do_gc(num_deleted != 0)
+        })
+        .context("In delete_all_attestation_keys: ")
+    }
+
+    /// Counts the number of keys that will expire by the provided epoch date and the number of
+    /// keys not currently assigned to a domain.
+    pub fn get_attestation_pool_status(
+        &mut self,
+        date: i64,
+        km_uuid: &Uuid,
+    ) -> Result<AttestationPoolStatus> {
+        let _wp = wd::watch_millis("KeystoreDB::get_attestation_pool_status", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let mut stmt = tx.prepare(
+                "SELECT data
+                 FROM persistent.keymetadata
+                 WHERE tag = ? AND keyentryid IN
+                     (SELECT id
+                      FROM persistent.keyentry
+                      WHERE alias IS NOT NULL
+                            AND key_type = ?
+                            AND km_uuid = ?
+                            AND state = ?);",
+            )?;
+            let times = stmt
+                .query_map(
+                    params![
+                        KeyMetaData::AttestationExpirationDate,
+                        KeyType::Attestation,
+                        km_uuid,
+                        KeyLifeCycle::Live
+                    ],
+                    |row| row.get(0),
+                )?
+                .collect::<rusqlite::Result<Vec<DateTime>>>()
+                .context("Failed to execute metadata statement")?;
+            let expiring =
+                times.iter().filter(|time| time < &&DateTime::from_millis_epoch(date)).count()
+                    as i32;
+            stmt = tx.prepare(
+                "SELECT alias, domain
+                 FROM persistent.keyentry
+                 WHERE key_type = ? AND km_uuid = ? AND state = ?;",
+            )?;
+            let rows = stmt
+                .query_map(params![KeyType::Attestation, km_uuid, KeyLifeCycle::Live], |row| {
+                    Ok((row.get(0)?, row.get(1)?))
+                })?
+                .collect::<rusqlite::Result<Vec<(Option<String>, Option<u32>)>>>()
+                .context("Failed to execute keyentry statement")?;
+            let mut unassigned = 0i32;
+            let mut attested = 0i32;
+            let total = rows.len() as i32;
+            for (alias, domain) in rows {
+                match (alias, domain) {
+                    (Some(_alias), None) => {
+                        attested += 1;
+                        unassigned += 1;
+                    }
+                    (Some(_alias), Some(_domain)) => {
+                        attested += 1;
+                    }
+                    _ => {}
+                }
+            }
+            Ok(AttestationPoolStatus { expiring, unassigned, attested, total }).no_gc()
+        })
+        .context("In get_attestation_pool_status: ")
+    }
+
+    /// Fetches the private key and corresponding certificate chain assigned to a
+    /// domain/namespace pair. Will either return nothing if the domain/namespace is
+    /// not assigned, or one CertificateChain.
+    pub fn retrieve_attestation_key_and_cert_chain(
+        &mut self,
+        domain: Domain,
+        namespace: i64,
+        km_uuid: &Uuid,
+    ) -> Result<Option<CertificateChain>> {
+        let _wp = wd::watch_millis("KeystoreDB::retrieve_attestation_key_and_cert_chain", 500);
+
+        match domain {
+            Domain::APP | Domain::SELINUX => {}
+            _ => {
+                return Err(KsError::sys())
+                    .context(format!("Domain {:?} must be either App or SELinux.", domain));
+            }
+        }
+        self.with_transaction(TransactionBehavior::Deferred, |tx| {
+            let mut stmt = tx.prepare(
+                "SELECT subcomponent_type, blob
+             FROM persistent.blobentry
+             WHERE keyentryid IN
+                (SELECT id
+                 FROM persistent.keyentry
+                 WHERE key_type = ?
+                       AND domain = ?
+                       AND namespace = ?
+                       AND state = ?
+                       AND km_uuid = ?);",
+            )?;
+            let rows = stmt
+                .query_map(
+                    params![
+                        KeyType::Attestation,
+                        domain.0 as u32,
+                        namespace,
+                        KeyLifeCycle::Live,
+                        km_uuid
+                    ],
+                    |row| Ok((row.get(0)?, row.get(1)?)),
+                )?
+                .collect::<rusqlite::Result<Vec<(SubComponentType, Vec<u8>)>>>()
+                .context("query failed.")?;
+            if rows.is_empty() {
+                return Ok(None).no_gc();
+            } else if rows.len() != 3 {
+                return Err(KsError::sys()).context(format!(
+                    concat!(
+                        "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 {
+                    SubComponentType::KEY_BLOB => {
+                        km_blob = row.1;
+                    }
+                    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: batch_cert_blob,
+                cert_chain: 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,
+    /// and atomically, removes the alias, domain, and namespace from another row
+    /// with the same alias-domain-namespace tuple if such row exits.
+    /// Returns Ok(true) if an old key was marked unreferenced as a hint to the garbage
+    /// collector.
+    fn rebind_alias(
+        tx: &Transaction,
+        newid: &KeyIdGuard,
+        alias: &str,
+        domain: &Domain,
+        namespace: &i64,
+    ) -> Result<bool> {
+        match *domain {
+            Domain::APP | Domain::SELINUX => {}
+            _ => {
+                return Err(KsError::sys()).context(format!(
+                    "In rebind_alias: Domain {:?} must be either App or SELinux.",
+                    domain
+                ));
+            }
+        }
+        let updated = tx
+            .execute(
+                "UPDATE persistent.keyentry
+                 SET alias = NULL, domain = NULL, namespace = NULL, state = ?
+                 WHERE alias = ? AND domain = ? AND namespace = ?;",
+                params![KeyLifeCycle::Unreferenced, alias, domain.0 as u32, namespace],
+            )
+            .context("In rebind_alias: Failed to rebind existing entry.")?;
+        let result = tx
+            .execute(
+                "UPDATE persistent.keyentry
+                    SET alias = ?, state = ?
+                    WHERE id = ? AND domain = ? AND namespace = ? AND state = ?;",
+                params![
+                    alias,
+                    KeyLifeCycle::Live,
+                    newid.0,
+                    domain.0 as u32,
+                    *namespace,
+                    KeyLifeCycle::Existing,
+                ],
+            )
+            .context("In rebind_alias: Failed to set alias.")?;
+        if result != 1 {
+            return Err(KsError::sys()).context(format!(
+                "In rebind_alias: Expected to update a single entry but instead updated {}.",
+                result
+            ));
+        }
+        Ok(updated != 0)
+    }
+
+    /// Moves the key given by KeyIdGuard to the new location at `destination`. If the destination
+    /// is already occupied by a key, this function fails with `ResponseCode::INVALID_ARGUMENT`.
+    pub fn migrate_key_namespace(
+        &mut self,
+        key_id_guard: KeyIdGuard,
+        destination: &KeyDescriptor,
+        caller_uid: u32,
+        check_permission: impl Fn(&KeyDescriptor) -> Result<()>,
+    ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::migrate_key_namespace", 500);
+
+        let destination = match destination.domain {
+            Domain::APP => KeyDescriptor { nspace: caller_uid as i64, ..(*destination).clone() },
+            Domain::SELINUX => (*destination).clone(),
+            domain => {
+                return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
+                    .context(format!("Domain {:?} must be either APP or SELINUX.", domain));
+            }
+        };
+
+        // Security critical: Must return immediately on failure. Do not remove the '?';
+        check_permission(&destination)
+            .context("In migrate_key_namespace: Trying to check permission.")?;
+
+        let alias = destination
+            .alias
+            .as_ref()
+            .ok_or(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
+            .context("In migrate_key_namespace: Alias must be specified.")?;
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            // Query the destination location. If there is a key, the migration request fails.
+            if tx
+                .query_row(
+                    "SELECT id FROM persistent.keyentry
+                     WHERE alias = ? AND domain = ? AND namespace = ?;",
+                    params![alias, destination.domain.0, destination.nspace],
+                    |_| Ok(()),
+                )
+                .optional()
+                .context("Failed to query destination.")?
+                .is_some()
+            {
+                return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
+                    .context("Target already exists.");
+            }
+
+            let updated = tx
+                .execute(
+                    "UPDATE persistent.keyentry
+                 SET alias = ?, domain = ?, namespace = ?
+                 WHERE id = ?;",
+                    params![alias, destination.domain.0, destination.nspace, key_id_guard.id()],
+                )
+                .context("Failed to update key entry.")?;
+
+            if updated != 1 {
+                return Err(KsError::sys())
+                    .context(format!("Update succeeded, but {} rows were updated.", updated));
+            }
+            Ok(()).no_gc()
+        })
+        .context("In migrate_key_namespace:")
+    }
+
+    /// Store a new key in a single transaction.
+    /// The function creates a new key entry, populates the blob, key parameter, and metadata
+    /// fields, and rebinds the given alias to the new key.
+    /// The boolean returned is a hint for the garbage collector. If true, a key was replaced,
+    /// is now unreferenced and needs to be collected.
+    pub fn store_new_key(
+        &mut self,
+        key: &KeyDescriptor,
+        params: &[KeyParameter],
+        blob_info: &(&[u8], &BlobMetaData),
+        cert_info: &CertificateInfo,
+        metadata: &KeyMetaData,
+        km_uuid: &Uuid,
+    ) -> Result<KeyIdGuard> {
+        let _wp = wd::watch_millis("KeystoreDB::store_new_key", 500);
+
+        let (alias, domain, namespace) = match key {
+            KeyDescriptor { alias: Some(alias), domain: Domain::APP, nspace, blob: None }
+            | KeyDescriptor { alias: Some(alias), domain: Domain::SELINUX, nspace, blob: None } => {
+                (alias, key.domain, nspace)
+            }
+            _ => {
+                return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
+                    .context("In store_new_key: Need alias and domain must be APP or SELINUX.")
+            }
+        };
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let key_id = Self::create_key_entry_internal(tx, &domain, namespace, km_uuid)
+                .context("Trying to create new key entry.")?;
+            let (blob, blob_metadata) = *blob_info;
+            Self::set_blob_internal(
+                tx,
+                key_id.id(),
+                SubComponentType::KEY_BLOB,
+                Some(blob),
+                Some(&blob_metadata),
+            )
+            .context("Trying to insert the key blob.")?;
+            if let Some(cert) = &cert_info.cert {
+                Self::set_blob_internal(tx, key_id.id(), SubComponentType::CERT, Some(&cert), None)
+                    .context("Trying to insert the certificate.")?;
+            }
+            if let Some(cert_chain) = &cert_info.cert_chain {
+                Self::set_blob_internal(
+                    tx,
+                    key_id.id(),
+                    SubComponentType::CERT_CHAIN,
+                    Some(&cert_chain),
+                    None,
+                )
+                .context("Trying to insert the certificate chain.")?;
+            }
+            Self::insert_keyparameter_internal(tx, &key_id, params)
+                .context("Trying to insert key parameters.")?;
+            metadata.store_in_db(key_id.id(), tx).context("Trying to insert key metadata.")?;
+            let need_gc = Self::rebind_alias(tx, &key_id, &alias, &domain, namespace)
+                .context("Trying to rebind alias.")?;
+            Ok(key_id).do_gc(need_gc)
+        })
+        .context("In store_new_key.")
+    }
+
+    /// Store a new certificate
+    /// The function creates a new key entry, populates the blob field and metadata, and rebinds
+    /// the given alias to the new cert.
+    pub fn store_new_certificate(
+        &mut self,
+        key: &KeyDescriptor,
+        cert: &[u8],
+        km_uuid: &Uuid,
+    ) -> Result<KeyIdGuard> {
+        let _wp = wd::watch_millis("KeystoreDB::store_new_certificate", 500);
+
+        let (alias, domain, namespace) = match key {
+            KeyDescriptor { alias: Some(alias), domain: Domain::APP, nspace, blob: None }
+            | KeyDescriptor { alias: Some(alias), domain: Domain::SELINUX, nspace, blob: None } => {
+                (alias, key.domain, nspace)
+            }
+            _ => {
+                return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT)).context(
+                    "In store_new_certificate: Need alias and domain must be APP or SELINUX.",
+                )
+            }
+        };
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let key_id = Self::create_key_entry_internal(tx, &domain, namespace, km_uuid)
+                .context("Trying to create new key entry.")?;
+
+            Self::set_blob_internal(
+                tx,
+                key_id.id(),
+                SubComponentType::CERT_CHAIN,
+                Some(cert),
+                None,
+            )
+            .context("Trying to insert certificate.")?;
+
+            let mut metadata = KeyMetaData::new();
+            metadata.add(KeyMetaEntry::CreationDate(
+                DateTime::now().context("Trying to make creation time.")?,
+            ));
+
+            metadata.store_in_db(key_id.id(), tx).context("Trying to insert key metadata.")?;
+
+            let need_gc = Self::rebind_alias(tx, &key_id, &alias, &domain, namespace)
+                .context("Trying to rebind alias.")?;
+            Ok(key_id).do_gc(need_gc)
+        })
+        .context("In store_new_certificate.")
+    }
+
+    // Helper function loading the key_id given the key descriptor
+    // tuple comprising domain, namespace, and alias.
+    // Requires a valid transaction.
+    fn load_key_entry_id(tx: &Transaction, key: &KeyDescriptor, key_type: KeyType) -> Result<i64> {
+        let alias = key
+            .alias
+            .as_ref()
+            .map_or_else(|| Err(KsError::sys()), Ok)
+            .context("In load_key_entry_id: Alias must be specified.")?;
+        let mut stmt = tx
+            .prepare(
+                "SELECT id FROM persistent.keyentry
+                    WHERE
+                    key_type = ?
+                    AND domain = ?
+                    AND namespace = ?
+                    AND alias = ?
+                    AND state = ?;",
+            )
+            .context("In load_key_entry_id: Failed to select from keyentry table.")?;
+        let mut rows = stmt
+            .query(params![key_type, key.domain.0 as u32, key.nspace, alias, KeyLifeCycle::Live])
+            .context("In load_key_entry_id: Failed to read from keyentry table.")?;
+        db_utils::with_rows_extract_one(&mut rows, |row| {
+            row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?
+                .get(0)
+                .context("Failed to unpack id.")
+        })
+        .context("In load_key_entry_id.")
+    }
+
+    /// This helper function completes the access tuple of a key, which is required
+    /// to perform access control. The strategy depends on the `domain` field in the
+    /// key descriptor.
+    /// * Domain::SELINUX: The access tuple is complete and this function only loads
+    ///       the key_id for further processing.
+    /// * Domain::APP: Like Domain::SELINUX, but the tuple is completed by `caller_uid`
+    ///       which serves as the namespace.
+    /// * Domain::GRANT: The grant table is queried for the `key_id` and the
+    ///       `access_vector`.
+    /// * Domain::KEY_ID: The keyentry table is queried for the owning `domain` and
+    ///       `namespace`.
+    /// In each case the information returned is sufficient to perform the access
+    /// check and the key id can be used to load further key artifacts.
+    fn load_access_tuple(
+        tx: &Transaction,
+        key: &KeyDescriptor,
+        key_type: KeyType,
+        caller_uid: u32,
+    ) -> Result<(i64, KeyDescriptor, Option<KeyPermSet>)> {
+        match key.domain {
+            // Domain App or SELinux. In this case we load the key_id from
+            // the keyentry database for further loading of key components.
+            // We already have the full access tuple to perform access control.
+            // The only distinction is that we use the caller_uid instead
+            // of the caller supplied namespace if the domain field is
+            // Domain::APP.
+            Domain::APP | Domain::SELINUX => {
+                let mut access_key = key.clone();
+                if access_key.domain == Domain::APP {
+                    access_key.nspace = caller_uid as i64;
+                }
+                let key_id = Self::load_key_entry_id(&tx, &access_key, key_type)
+                    .with_context(|| format!("With key.domain = {:?}.", access_key.domain))?;
+
+                Ok((key_id, access_key, None))
+            }
+
+            // Domain::GRANT. In this case we load the key_id and the access_vector
+            // from the grant table.
+            Domain::GRANT => {
+                let mut stmt = tx
+                    .prepare(
+                        "SELECT keyentryid, access_vector FROM persistent.grant
+                            WHERE grantee = ? AND id = ?;",
+                    )
+                    .context("Domain::GRANT prepare statement failed")?;
+                let mut rows = stmt
+                    .query(params![caller_uid as i64, key.nspace])
+                    .context("Domain:Grant: query failed.")?;
+                let (key_id, access_vector): (i64, i32) =
+                    db_utils::with_rows_extract_one(&mut rows, |row| {
+                        let r =
+                            row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?;
+                        Ok((
+                            r.get(0).context("Failed to unpack key_id.")?,
+                            r.get(1).context("Failed to unpack access_vector.")?,
+                        ))
+                    })
+                    .context("Domain::GRANT.")?;
+                Ok((key_id, key.clone(), Some(access_vector.into())))
+            }
+
+            // Domain::KEY_ID. In this case we load the domain and namespace from the
+            // keyentry database because we need them for access control.
+            Domain::KEY_ID => {
+                let (domain, namespace): (Domain, i64) = {
+                    let mut stmt = tx
+                        .prepare(
+                            "SELECT domain, namespace FROM persistent.keyentry
+                                WHERE
+                                id = ?
+                                AND state = ?;",
+                        )
+                        .context("Domain::KEY_ID: prepare statement failed")?;
+                    let mut rows = stmt
+                        .query(params![key.nspace, KeyLifeCycle::Live])
+                        .context("Domain::KEY_ID: query failed.")?;
+                    db_utils::with_rows_extract_one(&mut rows, |row| {
+                        let r =
+                            row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?;
+                        Ok((
+                            Domain(r.get(0).context("Failed to unpack domain.")?),
+                            r.get(1).context("Failed to unpack namespace.")?,
+                        ))
+                    })
+                    .context("Domain::KEY_ID.")?
+                };
+
+                // We may use a key by id after loading it by grant.
+                // In this case we have to check if the caller has a grant for this particular
+                // key. We can skip this if we already know that the caller is the owner.
+                // But we cannot know this if domain is anything but App. E.g. in the case
+                // of Domain::SELINUX we have to speculatively check for grants because we have to
+                // consult the SEPolicy before we know if the caller is the owner.
+                let access_vector: Option<KeyPermSet> =
+                    if domain != Domain::APP || namespace != caller_uid as i64 {
+                        let access_vector: Option<i32> = tx
+                            .query_row(
+                                "SELECT access_vector FROM persistent.grant
+                                WHERE grantee = ? AND keyentryid = ?;",
+                                params![caller_uid as i64, key.nspace],
+                                |row| row.get(0),
+                            )
+                            .optional()
+                            .context("Domain::KEY_ID: query grant failed.")?;
+                        access_vector.map(|p| p.into())
+                    } else {
+                        None
+                    };
+
+                let key_id = key.nspace;
+                let mut access_key: KeyDescriptor = key.clone();
+                access_key.domain = domain;
+                access_key.nspace = namespace;
+
+                Ok((key_id, access_key, access_vector))
+            }
+            _ => Err(anyhow!(KsError::sys())),
+        }
+    }
+
+    fn load_blob_components(
+        key_id: i64,
+        load_bits: KeyEntryLoadBits,
+        tx: &Transaction,
+    ) -> Result<(bool, Option<(Vec<u8>, BlobMetaData)>, Option<Vec<u8>>, Option<Vec<u8>>)> {
+        let mut stmt = tx
+            .prepare(
+                "SELECT MAX(id), subcomponent_type, blob FROM persistent.blobentry
+                    WHERE keyentryid = ? GROUP BY subcomponent_type;",
+            )
+            .context("In load_blob_components: prepare statement failed.")?;
+
+        let mut rows =
+            stmt.query(params![key_id]).context("In load_blob_components: query failed.")?;
+
+        let mut key_blob: Option<(i64, Vec<u8>)> = None;
+        let mut cert_blob: Option<Vec<u8>> = None;
+        let mut cert_chain_blob: Option<Vec<u8>> = None;
+        let mut has_km_blob: bool = false;
+        db_utils::with_rows_extract_all(&mut rows, |row| {
+            let sub_type: SubComponentType =
+                row.get(1).context("Failed to extract subcomponent_type.")?;
+            has_km_blob = has_km_blob || sub_type == SubComponentType::KEY_BLOB;
+            match (sub_type, load_bits.load_public(), load_bits.load_km()) {
+                (SubComponentType::KEY_BLOB, _, true) => {
+                    key_blob = Some((
+                        row.get(0).context("Failed to extract key blob id.")?,
+                        row.get(2).context("Failed to extract key blob.")?,
+                    ));
+                }
+                (SubComponentType::CERT, true, _) => {
+                    cert_blob =
+                        Some(row.get(2).context("Failed to extract public certificate blob.")?);
+                }
+                (SubComponentType::CERT_CHAIN, true, _) => {
+                    cert_chain_blob =
+                        Some(row.get(2).context("Failed to extract certificate chain blob.")?);
+                }
+                (SubComponentType::CERT, _, _)
+                | (SubComponentType::CERT_CHAIN, _, _)
+                | (SubComponentType::KEY_BLOB, _, _) => {}
+                _ => Err(KsError::sys()).context("Unknown subcomponent type.")?,
+            }
+            Ok(())
+        })
+        .context("In load_blob_components.")?;
+
+        let blob_info = key_blob.map_or::<Result<_>, _>(Ok(None), |(blob_id, blob)| {
+            Ok(Some((
+                blob,
+                BlobMetaData::load_from_db(blob_id, tx)
+                    .context("In load_blob_components: Trying to load blob_metadata.")?,
+            )))
+        })?;
+
+        Ok((has_km_blob, blob_info, cert_blob, cert_chain_blob))
+    }
+
+    fn load_key_parameters(key_id: i64, tx: &Transaction) -> Result<Vec<KeyParameter>> {
+        let mut stmt = tx
+            .prepare(
+                "SELECT tag, data, security_level from persistent.keyparameter
+                    WHERE keyentryid = ?;",
+            )
+            .context("In load_key_parameters: prepare statement failed.")?;
+
+        let mut parameters: Vec<KeyParameter> = Vec::new();
+
+        let mut rows =
+            stmt.query(params![key_id]).context("In load_key_parameters: query failed.")?;
+        db_utils::with_rows_extract_all(&mut rows, |row| {
+            let tag = Tag(row.get(0).context("Failed to read tag.")?);
+            let sec_level = SecurityLevel(row.get(2).context("Failed to read sec_level.")?);
+            parameters.push(
+                KeyParameter::new_from_sql(tag, &SqlField::new(1, &row), sec_level)
+                    .context("Failed to read KeyParameter.")?,
+            );
+            Ok(())
+        })
+        .context("In load_key_parameters.")?;
+
+        Ok(parameters)
+    }
+
+    /// Decrements the usage count of a limited use key. This function first checks whether the
+    /// usage has been exhausted, if not, decreases the usage count. If the usage count reaches
+    /// zero, the key also gets marked unreferenced and scheduled for deletion.
+    /// Returns Ok(true) if the key was marked unreferenced as a hint to the garbage collector.
+    pub fn check_and_update_key_usage_count(&mut self, key_id: i64) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::check_and_update_key_usage_count", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let limit: Option<i32> = tx
+                .query_row(
+                    "SELECT data FROM persistent.keyparameter WHERE keyentryid = ? AND tag = ?;",
+                    params![key_id, Tag::USAGE_COUNT_LIMIT.0],
+                    |row| row.get(0),
+                )
+                .optional()
+                .context("Trying to load usage count")?;
+
+            let limit = limit
+                .ok_or(KsError::Km(ErrorCode::INVALID_KEY_BLOB))
+                .context("The Key no longer exists. Key is exhausted.")?;
+
+            tx.execute(
+                "UPDATE persistent.keyparameter
+                 SET data = data - 1
+                 WHERE keyentryid = ? AND tag = ? AND data > 0;",
+                params![key_id, Tag::USAGE_COUNT_LIMIT.0],
+            )
+            .context("Failed to update key usage count.")?;
+
+            match limit {
+                1 => Self::mark_unreferenced(tx, key_id)
+                    .map(|need_gc| (need_gc, ()))
+                    .context("Trying to mark limited use key for deletion."),
+                0 => Err(KsError::Km(ErrorCode::INVALID_KEY_BLOB)).context("Key is exhausted."),
+                _ => Ok(()).no_gc(),
+            }
+        })
+        .context("In check_and_update_key_usage_count.")
+    }
+
+    /// Load a key entry by the given key descriptor.
+    /// It uses the `check_permission` callback to verify if the access is allowed
+    /// given the key access tuple read from the database using `load_access_tuple`.
+    /// With `load_bits` the caller may specify which blobs shall be loaded from
+    /// the blob database.
+    pub fn load_key_entry(
+        &mut self,
+        key: &KeyDescriptor,
+        key_type: KeyType,
+        load_bits: KeyEntryLoadBits,
+        caller_uid: u32,
+        check_permission: impl Fn(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>,
+    ) -> Result<(KeyIdGuard, KeyEntry)> {
+        let _wp = wd::watch_millis("KeystoreDB::load_key_entry", 500);
+
+        loop {
+            match self.load_key_entry_internal(
+                key,
+                key_type,
+                load_bits,
+                caller_uid,
+                &check_permission,
+            ) {
+                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 load_key_entry.");
+                    }
+                }
+            }
+        }
+    }
+
+    fn load_key_entry_internal(
+        &mut self,
+        key: &KeyDescriptor,
+        key_type: KeyType,
+        load_bits: KeyEntryLoadBits,
+        caller_uid: u32,
+        check_permission: &impl Fn(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>,
+    ) -> Result<(KeyIdGuard, KeyEntry)> {
+        // KEY ID LOCK 1/2
+        // If we got a key descriptor with a key id we can get the lock right away.
+        // Otherwise we have to defer it until we know the key id.
+        let key_id_guard = match key.domain {
+            Domain::KEY_ID => Some(KEY_ID_LOCK.get(key.nspace)),
+            _ => None,
+        };
+
+        let tx = self
+            .conn
+            .unchecked_transaction()
+            .context("In load_key_entry: Failed to initialize transaction.")?;
+
+        // Load the key_id and complete the access control tuple.
+        let (key_id, access_key_descriptor, access_vector) =
+            Self::load_access_tuple(&tx, key, key_type, caller_uid)
+                .context("In load_key_entry.")?;
+
+        // Perform access control. It is vital that we return here if the permission is denied.
+        // So do not touch that '?' at the end.
+        check_permission(&access_key_descriptor, access_vector).context("In load_key_entry.")?;
+
+        // KEY ID LOCK 2/2
+        // If we did not get a key id lock by now, it was because we got a key descriptor
+        // without a key id. At this point we got the key id, so we can try and get a lock.
+        // However, we cannot block here, because we are in the middle of the transaction.
+        // So first we try to get the lock non blocking. If that fails, we roll back the
+        // transaction and block until we get the lock. After we successfully got the lock,
+        // we start a new transaction and load the access tuple again.
+        //
+        // We don't need to perform access control again, because we already established
+        // that the caller had access to the given key. But we need to make sure that the
+        // key id still exists. So we have to load the key entry by key id this time.
+        let (key_id_guard, tx) = match key_id_guard {
+            None => match KEY_ID_LOCK.try_get(key_id) {
+                None => {
+                    // Roll back the transaction.
+                    tx.rollback().context("In load_key_entry: Failed to roll back transaction.")?;
+
+                    // Block until we have a key id lock.
+                    let key_id_guard = KEY_ID_LOCK.get(key_id);
+
+                    // Create a new transaction.
+                    let tx = self
+                        .conn
+                        .unchecked_transaction()
+                        .context("In load_key_entry: Failed to initialize transaction.")?;
+
+                    Self::load_access_tuple(
+                        &tx,
+                        // This time we have to load the key by the retrieved key id, because the
+                        // alias may have been rebound after we rolled back the transaction.
+                        &KeyDescriptor {
+                            domain: Domain::KEY_ID,
+                            nspace: key_id,
+                            ..Default::default()
+                        },
+                        key_type,
+                        caller_uid,
+                    )
+                    .context("In load_key_entry. (deferred key lock)")?;
+                    (key_id_guard, tx)
+                }
+                Some(l) => (l, tx),
+            },
+            Some(key_id_guard) => (key_id_guard, tx),
+        };
+
+        let key_entry = Self::load_key_components(&tx, load_bits, key_id_guard.id())
+            .context("In load_key_entry.")?;
+
+        tx.commit().context("In load_key_entry: Failed to commit transaction.")?;
+
+        Ok((key_id_guard, key_entry))
+    }
+
+    fn mark_unreferenced(tx: &Transaction, key_id: i64) -> Result<bool> {
+        let updated = tx
+            .execute("DELETE FROM persistent.keyentry WHERE id = ?;", params![key_id])
+            .context("Trying to delete keyentry.")?;
+        tx.execute("DELETE FROM persistent.keymetadata WHERE keyentryid = ?;", params![key_id])
+            .context("Trying to delete keymetadata.")?;
+        tx.execute("DELETE FROM persistent.keyparameter WHERE keyentryid = ?;", params![key_id])
+            .context("Trying to delete keyparameters.")?;
+        tx.execute("DELETE FROM persistent.grant WHERE keyentryid = ?;", params![key_id])
+            .context("Trying to delete grants.")?;
+        Ok(updated != 0)
+    }
+
+    /// Marks the given key as unreferenced and removes all of the grants to this key.
+    /// Returns Ok(true) if a key was marked unreferenced as a hint for the garbage collector.
+    pub fn unbind_key(
+        &mut self,
+        key: &KeyDescriptor,
+        key_type: KeyType,
+        caller_uid: u32,
+        check_permission: impl Fn(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>,
+    ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::unbind_key", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let (key_id, access_key_descriptor, access_vector) =
+                Self::load_access_tuple(tx, key, key_type, caller_uid)
+                    .context("Trying to get access tuple.")?;
+
+            // Perform access control. It is vital that we return here if the permission is denied.
+            // So do not touch that '?' at the end.
+            check_permission(&access_key_descriptor, access_vector)
+                .context("While checking permission.")?;
+
+            Self::mark_unreferenced(tx, key_id)
+                .map(|need_gc| (need_gc, ()))
+                .context("Trying to mark the key unreferenced.")
+        })
+        .context("In unbind_key.")
+    }
+
+    fn get_key_km_uuid(tx: &Transaction, key_id: i64) -> Result<Uuid> {
+        tx.query_row(
+            "SELECT km_uuid FROM persistent.keyentry WHERE id = ?",
+            params![key_id],
+            |row| row.get(0),
+        )
+        .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<()> {
+        let _wp = wd::watch_millis("KeystoreDB::unbind_keys_for_namespace", 500);
+
+        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 = ? AND key_type = ?
+                );",
+                params![domain.0, namespace, KeyType::Client],
+            )
+            .context("Trying to delete keymetadata.")?;
+            tx.execute(
+                "DELETE FROM persistent.keyparameter
+                WHERE keyentryid IN (
+                    SELECT id FROM persistent.keyentry
+                    WHERE domain = ? AND namespace = ? AND key_type = ?
+                );",
+                params![domain.0, namespace, KeyType::Client],
+            )
+            .context("Trying to delete keyparameters.")?;
+            tx.execute(
+                "DELETE FROM persistent.grant
+                WHERE keyentryid IN (
+                    SELECT id FROM persistent.keyentry
+                    WHERE domain = ? AND namespace = ? AND key_type = ?
+                );",
+                params![domain.0, namespace, KeyType::Client],
+            )
+            .context("Trying to delete grants.")?;
+            tx.execute(
+                "DELETE FROM persistent.keyentry
+                 WHERE domain = ? AND namespace = ? AND key_type = ?;",
+                params![domain.0, namespace, KeyType::Client],
+            )
+            .context("Trying to delete keyentry.")?;
+            Ok(()).need_gc()
+        })
+        .context("In unbind_keys_for_namespace")
+    }
+
+    fn cleanup_unreferenced(tx: &Transaction) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::cleanup_unreferenced", 500);
+        {
+            tx.execute(
+                "DELETE FROM persistent.keymetadata
+            WHERE keyentryid IN (
+                SELECT id FROM persistent.keyentry
+                WHERE state = ?
+            );",
+                params![KeyLifeCycle::Unreferenced],
+            )
+            .context("Trying to delete keymetadata.")?;
+            tx.execute(
+                "DELETE FROM persistent.keyparameter
+            WHERE keyentryid IN (
+                SELECT id FROM persistent.keyentry
+                WHERE state = ?
+            );",
+                params![KeyLifeCycle::Unreferenced],
+            )
+            .context("Trying to delete keyparameters.")?;
+            tx.execute(
+                "DELETE FROM persistent.grant
+            WHERE keyentryid IN (
+                SELECT id FROM persistent.keyentry
+                WHERE state = ?
+            );",
+                params![KeyLifeCycle::Unreferenced],
+            )
+            .context("Trying to delete grants.")?;
+            tx.execute(
+                "DELETE FROM persistent.keyentry
+                WHERE state = ?;",
+                params![KeyLifeCycle::Unreferenced],
+            )
+            .context("Trying to delete keyentry.")?;
+            Result::<()>::Ok(())
+        }
+        .context("In cleanup_unreferenced")
+    }
+
+    /// 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.
+    /// The caller of this function should notify the gc if the returned value is true.
+    pub fn unbind_keys_for_user(
+        &mut self,
+        user_id: u32,
+        keep_non_super_encrypted_keys: bool,
+    ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::unbind_keys_for_user", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            let mut stmt = tx
+                .prepare(&format!(
+                    "SELECT id from persistent.keyentry
+                     WHERE (
+                         key_type = ?
+                         AND domain = ?
+                         AND cast ( (namespace/{aid_user_offset}) as int) = ?
+                         AND state = ?
+                     ) OR (
+                         key_type = ?
+                         AND namespace = ?
+                         AND alias = ?
+                         AND state = ?
+                     );",
+                    aid_user_offset = AID_USER_OFFSET
+                ))
+                .context(concat!(
+                    "In unbind_keys_for_user. ",
+                    "Failed to prepare the query to find the keys created by apps."
+                ))?;
+
+            let mut rows = stmt
+                .query(params![
+                    // WHERE client key:
+                    KeyType::Client,
+                    Domain::APP.0 as u32,
+                    user_id,
+                    KeyLifeCycle::Live,
+                    // OR super key:
+                    KeyType::Super,
+                    user_id,
+                    USER_SUPER_KEY.alias,
+                    KeyLifeCycle::Live
+                ])
+                .context("In unbind_keys_for_user. Failed to query the keys created by apps.")?;
+
+            let mut key_ids: Vec<i64> = Vec::new();
+            db_utils::with_rows_extract_all(&mut rows, |row| {
+                key_ids
+                    .push(row.get(0).context("Failed to read key id of a key created by an app.")?);
+                Ok(())
+            })
+            .context("In unbind_keys_for_user.")?;
+
+            let mut notify_gc = false;
+            for key_id in key_ids {
+                if keep_non_super_encrypted_keys {
+                    // Load metadata and filter out non-super-encrypted keys.
+                    if let (_, Some((_, blob_metadata)), _, _) =
+                        Self::load_blob_components(key_id, KeyEntryLoadBits::KM, tx)
+                            .context("In unbind_keys_for_user: Trying to load blob info.")?
+                    {
+                        if blob_metadata.encrypted_by().is_none() {
+                            continue;
+                        }
+                    }
+                }
+                notify_gc = Self::mark_unreferenced(&tx, key_id)
+                    .context("In unbind_keys_for_user.")?
+                    || notify_gc;
+            }
+            Ok(()).do_gc(notify_gc)
+        })
+        .context("In unbind_keys_for_user.")
+    }
+
+    fn load_key_components(
+        tx: &Transaction,
+        load_bits: KeyEntryLoadBits,
+        key_id: i64,
+    ) -> Result<KeyEntry> {
+        let metadata = KeyMetaData::load_from_db(key_id, &tx).context("In load_key_components.")?;
+
+        let (has_km_blob, key_blob_info, cert_blob, cert_chain_blob) =
+            Self::load_blob_components(key_id, load_bits, &tx)
+                .context("In load_key_components.")?;
+
+        let parameters = Self::load_key_parameters(key_id, &tx)
+            .context("In load_key_components: Trying to load key parameters.")?;
+
+        let km_uuid = Self::get_key_km_uuid(&tx, key_id)
+            .context("In load_key_components: Trying to get KM uuid.")?;
+
+        Ok(KeyEntry {
+            id: key_id,
+            key_blob_info,
+            cert: cert_blob,
+            cert_chain: cert_chain_blob,
+            km_uuid,
+            parameters,
+            metadata,
+            pure_cert: !has_km_blob,
+        })
+    }
+
+    /// Returns a list of KeyDescriptors in the selected domain/namespace.
+    /// The key descriptors will have the domain, nspace, and alias field set.
+    /// Domain must be APP or SELINUX, the caller must make sure of that.
+    pub fn list(&mut self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
+        let _wp = wd::watch_millis("KeystoreDB::list", 500);
+
+        self.with_transaction(TransactionBehavior::Deferred, |tx| {
+            let mut stmt = tx
+                .prepare(
+                    "SELECT alias FROM persistent.keyentry
+             WHERE domain = ? AND namespace = ? AND alias IS NOT NULL AND state = ?;",
+                )
+                .context("In list: Failed to prepare.")?;
+
+            let mut rows = stmt
+                .query(params![domain.0 as u32, namespace, KeyLifeCycle::Live])
+                .context("In list: Failed to query.")?;
+
+            let mut descriptors: Vec<KeyDescriptor> = Vec::new();
+            db_utils::with_rows_extract_all(&mut rows, |row| {
+                descriptors.push(KeyDescriptor {
+                    domain,
+                    nspace: namespace,
+                    alias: Some(row.get(0).context("Trying to extract alias.")?),
+                    blob: None,
+                });
+                Ok(())
+            })
+            .context("In list: Failed to extract rows.")?;
+            Ok(descriptors).no_gc()
+        })
+    }
+
+    /// Adds a grant to the grant table.
+    /// Like `load_key_entry` this function loads the access tuple before
+    /// it uses the callback for a permission check. Upon success,
+    /// it inserts the `grantee_uid`, `key_id`, and `access_vector` into the
+    /// grant table. The new row will have a randomized id, which is used as
+    /// grant id in the namespace field of the resulting KeyDescriptor.
+    pub fn grant(
+        &mut self,
+        key: &KeyDescriptor,
+        caller_uid: u32,
+        grantee_uid: u32,
+        access_vector: KeyPermSet,
+        check_permission: impl Fn(&KeyDescriptor, &KeyPermSet) -> Result<()>,
+    ) -> Result<KeyDescriptor> {
+        let _wp = wd::watch_millis("KeystoreDB::grant", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            // Load the key_id and complete the access control tuple.
+            // We ignore the access vector here because grants cannot be granted.
+            // The access vector returned here expresses the permissions the
+            // grantee has if key.domain == Domain::GRANT. But this vector
+            // cannot include the grant permission by design, so there is no way the
+            // subsequent permission check can pass.
+            // We could check key.domain == Domain::GRANT and fail early.
+            // But even if we load the access tuple by grant here, the permission
+            // check denies the attempt to create a grant by grant descriptor.
+            let (key_id, access_key_descriptor, _) =
+                Self::load_access_tuple(&tx, key, KeyType::Client, caller_uid)
+                    .context("In grant")?;
+
+            // Perform access control. It is vital that we return here if the permission
+            // was denied. So do not touch that '?' at the end of the line.
+            // This permission check checks if the caller has the grant permission
+            // for the given key and in addition to all of the permissions
+            // expressed in `access_vector`.
+            check_permission(&access_key_descriptor, &access_vector)
+                .context("In grant: check_permission failed.")?;
+
+            let grant_id = if let Some(grant_id) = tx
+                .query_row(
+                    "SELECT id FROM persistent.grant
+                WHERE keyentryid = ? AND grantee = ?;",
+                    params![key_id, grantee_uid],
+                    |row| row.get(0),
+                )
+                .optional()
+                .context("In grant: Failed get optional existing grant id.")?
+            {
+                tx.execute(
+                    "UPDATE persistent.grant
+                    SET access_vector = ?
+                    WHERE id = ?;",
+                    params![i32::from(access_vector), grant_id],
+                )
+                .context("In grant: Failed to update existing grant.")?;
+                grant_id
+            } else {
+                Self::insert_with_retry(|id| {
+                    tx.execute(
+                        "INSERT INTO persistent.grant (id, grantee, keyentryid, access_vector)
+                        VALUES (?, ?, ?, ?);",
+                        params![id, grantee_uid, key_id, i32::from(access_vector)],
+                    )
+                })
+                .context("In grant")?
+            };
+
+            Ok(KeyDescriptor { domain: Domain::GRANT, nspace: grant_id, alias: None, blob: None })
+                .no_gc()
+        })
+    }
+
+    /// This function checks permissions like `grant` and `load_key_entry`
+    /// before removing a grant from the grant table.
+    pub fn ungrant(
+        &mut self,
+        key: &KeyDescriptor,
+        caller_uid: u32,
+        grantee_uid: u32,
+        check_permission: impl Fn(&KeyDescriptor) -> Result<()>,
+    ) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::ungrant", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            // Load the key_id and complete the access control tuple.
+            // We ignore the access vector here because grants cannot be granted.
+            let (key_id, access_key_descriptor, _) =
+                Self::load_access_tuple(&tx, key, KeyType::Client, caller_uid)
+                    .context("In ungrant.")?;
+
+            // Perform access control. We must return here if the permission
+            // was denied. So do not touch the '?' at the end of this line.
+            check_permission(&access_key_descriptor)
+                .context("In grant: check_permission failed.")?;
+
+            tx.execute(
+                "DELETE FROM persistent.grant
+                WHERE keyentryid = ? AND grantee = ?;",
+                params![key_id, grantee_uid],
+            )
+            .context("Failed to delete grant.")?;
+
+            Ok(()).no_gc()
+        })
+    }
+
+    // Generates a random id and passes it to the given function, which will
+    // try to insert it into a database.  If that insertion fails, retry;
+    // otherwise return the id.
+    fn insert_with_retry(inserter: impl Fn(i64) -> rusqlite::Result<usize>) -> Result<i64> {
+        loop {
+            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(
+                    libsqlite3_sys::Error {
+                        code: libsqlite3_sys::ErrorCode::ConstraintViolation,
+                        extended_code: libsqlite3_sys::SQLITE_CONSTRAINT_UNIQUE,
+                    },
+                    _,
+                )) => (),
+                Err(e) => {
+                    return Err(e).context("In insert_with_retry: failed to insert into database.")
+                }
+                _ => return Ok(newid),
+            }
+        }
+    }
+
+    /// Insert or replace the auth token based on the UNIQUE constraint of the auth token table
+    pub fn insert_auth_token(&mut self, auth_token: &HardwareAuthToken) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::insert_auth_token", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            tx.execute(
+                "INSERT OR REPLACE INTO perboot.authtoken (challenge, user_id, auth_id,
+            authenticator_type, timestamp, mac, time_received) VALUES(?, ?, ?, ?, ?, ?, ?);",
+                params![
+                    auth_token.challenge,
+                    auth_token.userId,
+                    auth_token.authenticatorId,
+                    auth_token.authenticatorType.0 as i32,
+                    auth_token.timestamp.milliSeconds as i64,
+                    auth_token.mac,
+                    MonotonicRawTime::now(),
+                ],
+            )
+            .context("In insert_auth_token: failed to insert auth token into the database")?;
+            Ok(()).no_gc()
+        })
+    }
+
+    /// Find the newest auth token matching the given predicate.
+    pub fn find_auth_token_entry<F>(
+        &mut self,
+        p: F,
+    ) -> Result<Option<(AuthTokenEntry, MonotonicRawTime)>>
+    where
+        F: Fn(&AuthTokenEntry) -> bool,
+    {
+        let _wp = wd::watch_millis("KeystoreDB::find_auth_token_entry", 500);
+
+        self.with_transaction(TransactionBehavior::Deferred, |tx| {
+            let mut stmt = tx
+                .prepare("SELECT * from perboot.authtoken ORDER BY time_received DESC;")
+                .context("Prepare statement failed.")?;
+
+            let mut rows = stmt.query(NO_PARAMS).context("Failed to query.")?;
+
+            while let Some(row) = rows.next().context("Failed to get next row.")? {
+                let entry = AuthTokenEntry::new(
+                    HardwareAuthToken {
+                        challenge: row.get(1)?,
+                        userId: row.get(2)?,
+                        authenticatorId: row.get(3)?,
+                        authenticatorType: HardwareAuthenticatorType(row.get(4)?),
+                        timestamp: Timestamp { milliSeconds: row.get(5)? },
+                        mac: row.get(6)?,
+                    },
+                    row.get(7)?,
+                );
+                if p(&entry) {
+                    return Ok(Some((
+                        entry,
+                        Self::get_last_off_body(tx)
+                            .context("In find_auth_token_entry: Trying to get last off body")?,
+                    )))
+                    .no_gc();
+                }
+            }
+            Ok(None).no_gc()
+        })
+        .context("In find_auth_token_entry.")
+    }
+
+    /// Insert last_off_body into the metadata table at the initialization of auth token table
+    pub fn insert_last_off_body(&mut self, last_off_body: MonotonicRawTime) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::insert_last_off_body", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            tx.execute(
+                "INSERT OR REPLACE INTO perboot.metadata (key, value) VALUES (?, ?);",
+                params!["last_off_body", last_off_body],
+            )
+            .context("In insert_last_off_body: failed to insert.")?;
+            Ok(()).no_gc()
+        })
+    }
+
+    /// Update last_off_body when on_device_off_body is called
+    pub fn update_last_off_body(&mut self, last_off_body: MonotonicRawTime) -> Result<()> {
+        let _wp = wd::watch_millis("KeystoreDB::update_last_off_body", 500);
+
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            tx.execute(
+                "UPDATE perboot.metadata SET value = ? WHERE key = ?;",
+                params![last_off_body, "last_off_body"],
+            )
+            .context("In update_last_off_body: failed to update.")?;
+            Ok(()).no_gc()
+        })
+    }
+
+    /// Get last_off_body time when finding auth tokens
+    fn get_last_off_body(tx: &Transaction) -> Result<MonotonicRawTime> {
+        let _wp = wd::watch_millis("KeystoreDB::get_last_off_body", 500);
+
+        tx.query_row(
+            "SELECT value from perboot.metadata WHERE key = ?;",
+            params!["last_off_body"],
+            |row| row.get(0),
+        )
+        .context("In get_last_off_body: query_row failed.")
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+    use crate::key_parameter::{
+        Algorithm, BlockMode, Digest, EcCurve, HardwareAuthenticatorType, KeyOrigin, KeyParameter,
+        KeyParameterValue, KeyPurpose, PaddingMode, SecurityLevel,
+    };
+    use crate::key_perm_set;
+    use crate::permission::{KeyPerm, KeyPermSet};
+    use crate::super_key::SuperKeyManager;
+    use keystore2_test_utils::TempDir;
+    use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+        HardwareAuthToken::HardwareAuthToken,
+        HardwareAuthenticatorType::HardwareAuthenticatorType as kmhw_authenticator_type,
+    };
+    use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
+        Timestamp::Timestamp,
+    };
+    use rusqlite::NO_PARAMS;
+    use rusqlite::{Error, TransactionBehavior};
+    use std::cell::RefCell;
+    use std::collections::BTreeMap;
+    use std::fmt::Write;
+    use std::sync::atomic::{AtomicU8, Ordering};
+    use std::sync::Arc;
+    use std::thread;
+    use std::time::{Duration, SystemTime};
+    #[cfg(disabled)]
+    use std::time::Instant;
+
+    fn new_test_db() -> Result<KeystoreDB> {
+        let conn = KeystoreDB::make_connection("file::memory:", "file::memory:")?;
+
+        let mut db = KeystoreDB { conn, gc: None };
+        db.with_transaction(TransactionBehavior::Immediate, |tx| {
+            KeystoreDB::init_tables(tx).context("Failed to initialize tables.").no_gc()
+        })?;
+        Ok(db)
+    }
+
+    fn new_test_db_with_gc<F>(path: &Path, cb: F) -> Result<KeystoreDB>
+    where
+        F: Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static,
+    {
+        let super_key: Arc<SuperKeyManager> = Default::default();
+
+        let gc_db = KeystoreDB::new(path, None).expect("Failed to open test gc db_connection.");
+        let gc = Gc::new_init_with(Default::default(), move || (Box::new(cb), gc_db, super_key));
+
+        KeystoreDB::new(path, Some(Arc::new(gc)))
+    }
+
+    fn rebind_alias(
+        db: &mut KeystoreDB,
+        newid: &KeyIdGuard,
+        alias: &str,
+        domain: Domain,
+        namespace: i64,
+    ) -> Result<bool> {
+        db.with_transaction(TransactionBehavior::Immediate, |tx| {
+            KeystoreDB::rebind_alias(tx, newid, alias, &domain, &namespace).no_gc()
+        })
+        .context("In rebind_alias.")
+    }
+
+    #[test]
+    fn datetime() -> Result<()> {
+        let conn = Connection::open_in_memory()?;
+        conn.execute("CREATE TABLE test (ts DATETIME);", NO_PARAMS)?;
+        let now = SystemTime::now();
+        let duration = Duration::from_secs(1000);
+        let then = now.checked_sub(duration).unwrap();
+        let soon = now.checked_add(duration).unwrap();
+        conn.execute(
+            "INSERT INTO test (ts) VALUES (?), (?), (?);",
+            params![DateTime::try_from(now)?, DateTime::try_from(then)?, DateTime::try_from(soon)?],
+        )?;
+        let mut stmt = conn.prepare("SELECT ts FROM test ORDER BY ts ASC;")?;
+        let mut rows = stmt.query(NO_PARAMS)?;
+        assert_eq!(DateTime::try_from(then)?, rows.next()?.unwrap().get(0)?);
+        assert_eq!(DateTime::try_from(now)?, rows.next()?.unwrap().get(0)?);
+        assert_eq!(DateTime::try_from(soon)?, rows.next()?.unwrap().get(0)?);
+        assert!(rows.next()?.is_none());
+        assert!(DateTime::try_from(then)? < DateTime::try_from(now)?);
+        assert!(DateTime::try_from(then)? < DateTime::try_from(soon)?);
+        assert!(DateTime::try_from(now)? < DateTime::try_from(soon)?);
+        Ok(())
+    }
+
+    // Ensure that we're using the "injected" random function, not the real one.
+    #[test]
+    fn test_mocked_random() {
+        let rand1 = random();
+        let rand2 = random();
+        let rand3 = random();
+        if rand1 == rand2 {
+            assert_eq!(rand2 + 1, rand3);
+        } else {
+            assert_eq!(rand1 + 1, rand2);
+            assert_eq!(rand2, rand3);
+        }
+    }
+
+    // Test that we have the correct tables.
+    #[test]
+    fn test_tables() -> Result<()> {
+        let db = new_test_db()?;
+        let tables = db
+            .conn
+            .prepare("SELECT name from persistent.sqlite_master WHERE type='table' ORDER BY name;")?
+            .query_map(params![], |row| row.get(0))?
+            .collect::<rusqlite::Result<Vec<String>>>()?;
+        assert_eq!(tables.len(), 6);
+        assert_eq!(tables[0], "blobentry");
+        assert_eq!(tables[1], "blobmetadata");
+        assert_eq!(tables[2], "grant");
+        assert_eq!(tables[3], "keyentry");
+        assert_eq!(tables[4], "keymetadata");
+        assert_eq!(tables[5], "keyparameter");
+        let tables = db
+            .conn
+            .prepare("SELECT name from perboot.sqlite_master WHERE type='table' ORDER BY name;")?
+            .query_map(params![], |row| row.get(0))?
+            .collect::<rusqlite::Result<Vec<String>>>()?;
+
+        assert_eq!(tables.len(), 2);
+        assert_eq!(tables[0], "authtoken");
+        assert_eq!(tables[1], "metadata");
+        Ok(())
+    }
+
+    #[test]
+    fn test_auth_token_table_invariant() -> Result<()> {
+        let mut db = new_test_db()?;
+        let auth_token1 = HardwareAuthToken {
+            challenge: i64::MAX,
+            userId: 200,
+            authenticatorId: 200,
+            authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+            timestamp: Timestamp { milliSeconds: 500 },
+            mac: String::from("mac").into_bytes(),
+        };
+        db.insert_auth_token(&auth_token1)?;
+        let auth_tokens_returned = get_auth_tokens(&mut db)?;
+        assert_eq!(auth_tokens_returned.len(), 1);
+
+        // insert another auth token with the same values for the columns in the UNIQUE constraint
+        // of the auth token table and different value for timestamp
+        let auth_token2 = HardwareAuthToken {
+            challenge: i64::MAX,
+            userId: 200,
+            authenticatorId: 200,
+            authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+            timestamp: Timestamp { milliSeconds: 600 },
+            mac: String::from("mac").into_bytes(),
+        };
+
+        db.insert_auth_token(&auth_token2)?;
+        let mut auth_tokens_returned = get_auth_tokens(&mut db)?;
+        assert_eq!(auth_tokens_returned.len(), 1);
+
+        if let Some(auth_token) = auth_tokens_returned.pop() {
+            assert_eq!(auth_token.auth_token.timestamp.milliSeconds, 600);
+        }
+
+        // insert another auth token with the different values for the columns in the UNIQUE
+        // constraint of the auth token table
+        let auth_token3 = HardwareAuthToken {
+            challenge: i64::MAX,
+            userId: 201,
+            authenticatorId: 200,
+            authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+            timestamp: Timestamp { milliSeconds: 600 },
+            mac: String::from("mac").into_bytes(),
+        };
+
+        db.insert_auth_token(&auth_token3)?;
+        let auth_tokens_returned = get_auth_tokens(&mut db)?;
+        assert_eq!(auth_tokens_returned.len(), 2);
+
+        Ok(())
+    }
+
+    // utility function for test_auth_token_table_invariant()
+    fn get_auth_tokens(db: &mut KeystoreDB) -> Result<Vec<AuthTokenEntry>> {
+        let mut stmt = db.conn.prepare("SELECT * from perboot.authtoken;")?;
+
+        let auth_token_entries: Vec<AuthTokenEntry> = stmt
+            .query_map(NO_PARAMS, |row| {
+                Ok(AuthTokenEntry::new(
+                    HardwareAuthToken {
+                        challenge: row.get(1)?,
+                        userId: row.get(2)?,
+                        authenticatorId: row.get(3)?,
+                        authenticatorType: HardwareAuthenticatorType(row.get(4)?),
+                        timestamp: Timestamp { milliSeconds: row.get(5)? },
+                        mac: row.get(6)?,
+                    },
+                    row.get(7)?,
+                ))
+            })?
+            .collect::<Result<Vec<AuthTokenEntry>, Error>>()?;
+        Ok(auth_token_entries)
+    }
+
+    #[test]
+    fn test_persistence_for_files() -> Result<()> {
+        let temp_dir = TempDir::new("persistent_db_test")?;
+        let mut db = KeystoreDB::new(temp_dir.path(), None)?;
+
+        db.create_key_entry(&Domain::APP, &100, &KEYSTORE_UUID)?;
+        let entries = get_keyentry(&db)?;
+        assert_eq!(entries.len(), 1);
+
+        let db = KeystoreDB::new(temp_dir.path(), None)?;
+
+        let entries_new = get_keyentry(&db)?;
+        assert_eq!(entries, entries_new);
+        Ok(())
+    }
+
+    #[test]
+    fn test_create_key_entry() -> Result<()> {
+        fn extractor(ke: &KeyEntryRow) -> (Domain, i64, Option<&str>, Uuid) {
+            (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref(), ke.km_uuid.unwrap())
+        }
+
+        let mut db = new_test_db()?;
+
+        db.create_key_entry(&Domain::APP, &100, &KEYSTORE_UUID)?;
+        db.create_key_entry(&Domain::SELINUX, &101, &KEYSTORE_UUID)?;
+
+        let entries = get_keyentry(&db)?;
+        assert_eq!(entries.len(), 2);
+        assert_eq!(extractor(&entries[0]), (Domain::APP, 100, None, KEYSTORE_UUID));
+        assert_eq!(extractor(&entries[1]), (Domain::SELINUX, 101, None, KEYSTORE_UUID));
+
+        // Test that we must pass in a valid Domain.
+        check_result_is_error_containing_string(
+            db.create_key_entry(&Domain::GRANT, &102, &KEYSTORE_UUID),
+            "Domain Domain(1) must be either App or SELinux.",
+        );
+        check_result_is_error_containing_string(
+            db.create_key_entry(&Domain::BLOB, &103, &KEYSTORE_UUID),
+            "Domain Domain(3) must be either App or SELinux.",
+        );
+        check_result_is_error_containing_string(
+            db.create_key_entry(&Domain::KEY_ID, &104, &KEYSTORE_UUID),
+            "Domain Domain(4) must be either App or SELinux.",
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_add_unsigned_key() -> Result<()> {
+        let mut db = new_test_db()?;
+        let public_key: Vec<u8> = vec![0x01, 0x02, 0x03];
+        let private_key: Vec<u8> = vec![0x04, 0x05, 0x06];
+        let raw_public_key: Vec<u8> = vec![0x07, 0x08, 0x09];
+        db.create_attestation_key_entry(
+            &public_key,
+            &raw_public_key,
+            &private_key,
+            &KEYSTORE_UUID,
+        )?;
+        let keys = db.fetch_unsigned_attestation_keys(5, &KEYSTORE_UUID)?;
+        assert_eq!(keys.len(), 1);
+        assert_eq!(keys[0], public_key);
+        Ok(())
+    }
+
+    #[test]
+    fn test_store_signed_attestation_certificate_chain() -> Result<()> {
+        let mut db = new_test_db()?;
+        let expiration_date: i64 = 20;
+        let namespace: i64 = 30;
+        let base_byte: u8 = 1;
+        let loaded_values =
+            load_attestation_key_pool(&mut db, expiration_date, namespace, base_byte)?;
+        let chain =
+            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.priv_key);
+        assert_eq!(cert_chain.batch_cert, loaded_values.batch_cert);
+        assert_eq!(cert_chain.cert_chain, loaded_values.cert_chain);
+        Ok(())
+    }
+
+    #[test]
+    fn test_get_attestation_pool_status() -> Result<()> {
+        let mut db = new_test_db()?;
+        let namespace: i64 = 30;
+        load_attestation_key_pool(
+            &mut db, 10, /* expiration */
+            namespace, 0x01, /* base_byte */
+        )?;
+        load_attestation_key_pool(&mut db, 20 /* expiration */, namespace + 1, 0x02)?;
+        load_attestation_key_pool(&mut db, 40 /* expiration */, namespace + 2, 0x03)?;
+        let mut status = db.get_attestation_pool_status(9 /* expiration */, &KEYSTORE_UUID)?;
+        assert_eq!(status.expiring, 0);
+        assert_eq!(status.attested, 3);
+        assert_eq!(status.unassigned, 0);
+        assert_eq!(status.total, 3);
+        assert_eq!(
+            db.get_attestation_pool_status(15 /* expiration */, &KEYSTORE_UUID)?.expiring,
+            1
+        );
+        assert_eq!(
+            db.get_attestation_pool_status(25 /* expiration */, &KEYSTORE_UUID)?.expiring,
+            2
+        );
+        assert_eq!(
+            db.get_attestation_pool_status(60 /* expiration */, &KEYSTORE_UUID)?.expiring,
+            3
+        );
+        let public_key: Vec<u8> = vec![0x01, 0x02, 0x03];
+        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,
+            &private_key,
+            &KEYSTORE_UUID,
+        )?;
+        status = db.get_attestation_pool_status(0 /* expiration */, &KEYSTORE_UUID)?;
+        assert_eq!(status.attested, 3);
+        assert_eq!(status.unassigned, 0);
+        assert_eq!(status.total, 4);
+        db.store_signed_attestation_certificate_chain(
+            &raw_public_key,
+            &batch_cert,
+            &cert_chain,
+            20,
+            &KEYSTORE_UUID,
+        )?;
+        status = db.get_attestation_pool_status(0 /* expiration */, &KEYSTORE_UUID)?;
+        assert_eq!(status.attested, 4);
+        assert_eq!(status.unassigned, 1);
+        assert_eq!(status.total, 4);
+        Ok(())
+    }
+
+    #[test]
+    fn test_remove_expired_certs() -> Result<()> {
+        let temp_dir =
+            TempDir::new("test_remove_expired_certs_").expect("Failed to create temp dir.");
+        let mut db = new_test_db_with_gc(temp_dir.path(), |_, _| Ok(()))?;
+        let expiration_date: i64 =
+            SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis() as i64 + 10000;
+        let namespace: i64 = 30;
+        let namespace_del1: i64 = 45;
+        let namespace_del2: i64 = 60;
+        let entry_values = load_attestation_key_pool(
+            &mut db,
+            expiration_date,
+            namespace,
+            0x01, /* base_byte */
+        )?;
+        load_attestation_key_pool(&mut db, 45, namespace_del1, 0x02)?;
+        load_attestation_key_pool(&mut db, 60, namespace_del2, 0x03)?;
+
+        let blob_entry_row_count: u32 = db
+            .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 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);
+
+        let mut cert_chain =
+            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.batch_cert, value.batch_cert);
+        assert_eq!(entry_values.cert_chain, value.cert_chain);
+        assert_eq!(entry_values.priv_key, value.private_key.to_vec());
+
+        cert_chain = db.retrieve_attestation_key_and_cert_chain(
+            Domain::APP,
+            namespace_del1,
+            &KEYSTORE_UUID,
+        )?;
+        assert!(!cert_chain.is_some());
+        cert_chain = db.retrieve_attestation_key_and_cert_chain(
+            Domain::APP,
+            namespace_del2,
+            &KEYSTORE_UUID,
+        )?;
+        assert!(!cert_chain.is_some());
+
+        // Give the garbage collector half a second to catch up.
+        std::thread::sleep(Duration::from_millis(500));
+
+        let blob_entry_row_count: u32 = db
+            .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 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(())
+    }
+
+    #[test]
+    fn test_delete_all_attestation_keys() -> Result<()> {
+        let mut db = new_test_db()?;
+        load_attestation_key_pool(&mut db, 45 /* expiration */, 1 /* namespace */, 0x02)?;
+        load_attestation_key_pool(&mut db, 80 /* expiration */, 2 /* namespace */, 0x03)?;
+        db.create_key_entry(&Domain::APP, &42, &KEYSTORE_UUID)?;
+        let result = db.delete_all_attestation_keys()?;
+
+        // Give the garbage collector half a second to catch up.
+        std::thread::sleep(Duration::from_millis(500));
+
+        // Attestation keys should be deleted, and the regular key should remain.
+        assert_eq!(result, 2);
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_rebind_alias() -> Result<()> {
+        fn extractor(
+            ke: &KeyEntryRow,
+        ) -> (Option<Domain>, Option<i64>, Option<&str>, Option<Uuid>) {
+            (ke.domain, ke.namespace, ke.alias.as_deref(), ke.km_uuid)
+        }
+
+        let mut db = new_test_db()?;
+        db.create_key_entry(&Domain::APP, &42, &KEYSTORE_UUID)?;
+        db.create_key_entry(&Domain::APP, &42, &KEYSTORE_UUID)?;
+        let entries = get_keyentry(&db)?;
+        assert_eq!(entries.len(), 2);
+        assert_eq!(
+            extractor(&entries[0]),
+            (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID))
+        );
+        assert_eq!(
+            extractor(&entries[1]),
+            (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID))
+        );
+
+        // Test that the first call to rebind_alias sets the alias.
+        rebind_alias(&mut db, &KEY_ID_LOCK.get(entries[0].id), "foo", Domain::APP, 42)?;
+        let entries = get_keyentry(&db)?;
+        assert_eq!(entries.len(), 2);
+        assert_eq!(
+            extractor(&entries[0]),
+            (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
+        );
+        assert_eq!(
+            extractor(&entries[1]),
+            (Some(Domain::APP), Some(42), None, Some(KEYSTORE_UUID))
+        );
+
+        // Test that the second call to rebind_alias also empties the old one.
+        rebind_alias(&mut db, &KEY_ID_LOCK.get(entries[1].id), "foo", Domain::APP, 42)?;
+        let entries = get_keyentry(&db)?;
+        assert_eq!(entries.len(), 2);
+        assert_eq!(extractor(&entries[0]), (None, None, None, Some(KEYSTORE_UUID)));
+        assert_eq!(
+            extractor(&entries[1]),
+            (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
+        );
+
+        // Test that we must pass in a valid Domain.
+        check_result_is_error_containing_string(
+            rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::GRANT, 42),
+            "Domain Domain(1) must be either App or SELinux.",
+        );
+        check_result_is_error_containing_string(
+            rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::BLOB, 42),
+            "Domain Domain(3) must be either App or SELinux.",
+        );
+        check_result_is_error_containing_string(
+            rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::KEY_ID, 42),
+            "Domain Domain(4) must be either App or SELinux.",
+        );
+
+        // Test that we correctly handle setting an alias for something that does not exist.
+        check_result_is_error_containing_string(
+            rebind_alias(&mut db, &KEY_ID_LOCK.get(0), "foo", Domain::SELINUX, 42),
+            "Expected to update a single entry but instead updated 0",
+        );
+        // Test that we correctly abort the transaction in this case.
+        let entries = get_keyentry(&db)?;
+        assert_eq!(entries.len(), 2);
+        assert_eq!(extractor(&entries[0]), (None, None, None, Some(KEYSTORE_UUID)));
+        assert_eq!(
+            extractor(&entries[1]),
+            (Some(Domain::APP), Some(42), Some("foo"), Some(KEYSTORE_UUID))
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_grant_ungrant() -> Result<()> {
+        const CALLER_UID: u32 = 15;
+        const GRANTEE_UID: u32 = 12;
+        const SELINUX_NAMESPACE: i64 = 7;
+
+        let mut db = new_test_db()?;
+        db.conn.execute(
+            "INSERT INTO persistent.keyentry (id, key_type, domain, namespace, alias, state, km_uuid)
+                VALUES (1, 0, 0, 15, 'key', 1, ?), (2, 0, 2, 7, 'yek', 1, ?);",
+            params![KEYSTORE_UUID, KEYSTORE_UUID],
+        )?;
+        let app_key = KeyDescriptor {
+            domain: super::Domain::APP,
+            nspace: 0,
+            alias: Some("key".to_string()),
+            blob: None,
+        };
+        const PVEC1: KeyPermSet = key_perm_set![KeyPerm::use_(), KeyPerm::get_info()];
+        const PVEC2: KeyPermSet = key_perm_set![KeyPerm::use_()];
+
+        // Reset totally predictable random number generator in case we
+        // are not the first test running on this thread.
+        reset_random();
+        let next_random = 0i64;
+
+        let app_granted_key = db
+            .grant(&app_key, CALLER_UID, GRANTEE_UID, PVEC1, |k, a| {
+                assert_eq!(*a, PVEC1);
+                assert_eq!(
+                    *k,
+                    KeyDescriptor {
+                        domain: super::Domain::APP,
+                        // namespace must be set to the caller_uid.
+                        nspace: CALLER_UID as i64,
+                        alias: Some("key".to_string()),
+                        blob: None,
+                    }
+                );
+                Ok(())
+            })
+            .unwrap();
+
+        assert_eq!(
+            app_granted_key,
+            KeyDescriptor {
+                domain: super::Domain::GRANT,
+                // The grantid is next_random due to the mock random number generator.
+                nspace: next_random,
+                alias: None,
+                blob: None,
+            }
+        );
+
+        let selinux_key = KeyDescriptor {
+            domain: super::Domain::SELINUX,
+            nspace: SELINUX_NAMESPACE,
+            alias: Some("yek".to_string()),
+            blob: None,
+        };
+
+        let selinux_granted_key = db
+            .grant(&selinux_key, CALLER_UID, 12, PVEC1, |k, a| {
+                assert_eq!(*a, PVEC1);
+                assert_eq!(
+                    *k,
+                    KeyDescriptor {
+                        domain: super::Domain::SELINUX,
+                        // namespace must be the supplied SELinux
+                        // namespace.
+                        nspace: SELINUX_NAMESPACE,
+                        alias: Some("yek".to_string()),
+                        blob: None,
+                    }
+                );
+                Ok(())
+            })
+            .unwrap();
+
+        assert_eq!(
+            selinux_granted_key,
+            KeyDescriptor {
+                domain: super::Domain::GRANT,
+                // The grantid is next_random + 1 due to the mock random number generator.
+                nspace: next_random + 1,
+                alias: None,
+                blob: None,
+            }
+        );
+
+        // This should update the existing grant with PVEC2.
+        let selinux_granted_key = db
+            .grant(&selinux_key, CALLER_UID, 12, PVEC2, |k, a| {
+                assert_eq!(*a, PVEC2);
+                assert_eq!(
+                    *k,
+                    KeyDescriptor {
+                        domain: super::Domain::SELINUX,
+                        // namespace must be the supplied SELinux
+                        // namespace.
+                        nspace: SELINUX_NAMESPACE,
+                        alias: Some("yek".to_string()),
+                        blob: None,
+                    }
+                );
+                Ok(())
+            })
+            .unwrap();
+
+        assert_eq!(
+            selinux_granted_key,
+            KeyDescriptor {
+                domain: super::Domain::GRANT,
+                // Same grant id as before. The entry was only updated.
+                nspace: next_random + 1,
+                alias: None,
+                blob: None,
+            }
+        );
+
+        {
+            // Limiting scope of stmt, because it borrows db.
+            let mut stmt = db
+                .conn
+                .prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
+            let mut rows =
+                stmt.query_map::<(i64, u32, i64, KeyPermSet), _, _>(NO_PARAMS, |row| {
+                    Ok((
+                        row.get(0)?,
+                        row.get(1)?,
+                        row.get(2)?,
+                        KeyPermSet::from(row.get::<_, i32>(3)?),
+                    ))
+                })?;
+
+            let r = rows.next().unwrap().unwrap();
+            assert_eq!(r, (next_random, GRANTEE_UID, 1, PVEC1));
+            let r = rows.next().unwrap().unwrap();
+            assert_eq!(r, (next_random + 1, GRANTEE_UID, 2, PVEC2));
+            assert!(rows.next().is_none());
+        }
+
+        debug_dump_keyentry_table(&mut db)?;
+        println!("app_key {:?}", app_key);
+        println!("selinux_key {:?}", selinux_key);
+
+        db.ungrant(&app_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
+        db.ungrant(&selinux_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
+
+        Ok(())
+    }
+
+    static TEST_KEY_BLOB: &[u8] = b"my test blob";
+    static TEST_CERT_BLOB: &[u8] = b"my test cert";
+    static TEST_CERT_CHAIN_BLOB: &[u8] = b"my test cert_chain";
+
+    #[test]
+    fn test_set_blob() -> Result<()> {
+        let key_id = KEY_ID_LOCK.get(3000);
+        let mut db = new_test_db()?;
+        let mut blob_metadata = BlobMetaData::new();
+        blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+        db.set_blob(
+            &key_id,
+            SubComponentType::KEY_BLOB,
+            Some(TEST_KEY_BLOB),
+            Some(&blob_metadata),
+        )?;
+        db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
+        db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
+        drop(key_id);
+
+        let mut stmt = db.conn.prepare(
+            "SELECT subcomponent_type, keyentryid, blob, id FROM persistent.blobentry
+                ORDER BY subcomponent_type ASC;",
+        )?;
+        let mut rows = stmt
+            .query_map::<((SubComponentType, i64, Vec<u8>), i64), _, _>(NO_PARAMS, |row| {
+                Ok(((row.get(0)?, row.get(1)?, row.get(2)?), row.get(3)?))
+            })?;
+        let (r, id) = rows.next().unwrap().unwrap();
+        assert_eq!(r, (SubComponentType::KEY_BLOB, 3000, TEST_KEY_BLOB.to_vec()));
+        let (r, _) = rows.next().unwrap().unwrap();
+        assert_eq!(r, (SubComponentType::CERT, 3000, TEST_CERT_BLOB.to_vec()));
+        let (r, _) = rows.next().unwrap().unwrap();
+        assert_eq!(r, (SubComponentType::CERT_CHAIN, 3000, TEST_CERT_CHAIN_BLOB.to_vec()));
+
+        drop(rows);
+        drop(stmt);
+
+        assert_eq!(
+            db.with_transaction(TransactionBehavior::Immediate, |tx| {
+                BlobMetaData::load_from_db(id, tx).no_gc()
+            })
+            .expect("Should find blob metadata."),
+            blob_metadata
+        );
+        Ok(())
+    }
+
+    static TEST_ALIAS: &str = "my super duper key";
+
+    #[test]
+    fn test_insert_and_load_full_keyentry_domain_app() -> Result<()> {
+        let mut db = new_test_db()?;
+        let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)
+            .context("test_insert_and_load_full_keyentry_domain_app")?
+            .0;
+        let (_key_guard, key_entry) = db
+            .load_key_entry(
+                &KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: 0,
+                    alias: Some(TEST_ALIAS.to_string()),
+                    blob: None,
+                },
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                1,
+                |_k, _av| Ok(()),
+            )
+            .unwrap();
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+        db.unbind_key(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: 0,
+                alias: Some(TEST_ALIAS.to_string()),
+                blob: None,
+            },
+            KeyType::Client,
+            1,
+            |_, _| Ok(()),
+        )
+        .unwrap();
+
+        assert_eq!(
+            Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+            db.load_key_entry(
+                &KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: 0,
+                    alias: Some(TEST_ALIAS.to_string()),
+                    blob: None,
+                },
+                KeyType::Client,
+                KeyEntryLoadBits::NONE,
+                1,
+                |_k, _av| Ok(()),
+            )
+            .unwrap_err()
+            .root_cause()
+            .downcast_ref::<KsError>()
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_insert_and_load_certificate_entry_domain_app() -> Result<()> {
+        let mut db = new_test_db()?;
+
+        db.store_new_certificate(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: 1,
+                alias: Some(TEST_ALIAS.to_string()),
+                blob: None,
+            },
+            TEST_CERT_BLOB,
+            &KEYSTORE_UUID,
+        )
+        .expect("Trying to insert cert.");
+
+        let (_key_guard, mut key_entry) = db
+            .load_key_entry(
+                &KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: 1,
+                    alias: Some(TEST_ALIAS.to_string()),
+                    blob: None,
+                },
+                KeyType::Client,
+                KeyEntryLoadBits::PUBLIC,
+                1,
+                |_k, _av| Ok(()),
+            )
+            .expect("Trying to read certificate entry.");
+
+        assert!(key_entry.pure_cert());
+        assert!(key_entry.cert().is_none());
+        assert_eq!(key_entry.take_cert_chain(), Some(TEST_CERT_BLOB.to_vec()));
+
+        db.unbind_key(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: 1,
+                alias: Some(TEST_ALIAS.to_string()),
+                blob: None,
+            },
+            KeyType::Client,
+            1,
+            |_, _| Ok(()),
+        )
+        .unwrap();
+
+        assert_eq!(
+            Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+            db.load_key_entry(
+                &KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: 1,
+                    alias: Some(TEST_ALIAS.to_string()),
+                    blob: None,
+                },
+                KeyType::Client,
+                KeyEntryLoadBits::NONE,
+                1,
+                |_k, _av| Ok(()),
+            )
+            .unwrap_err()
+            .root_cause()
+            .downcast_ref::<KsError>()
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_insert_and_load_full_keyentry_domain_selinux() -> Result<()> {
+        let mut db = new_test_db()?;
+        let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, None)
+            .context("test_insert_and_load_full_keyentry_domain_selinux")?
+            .0;
+        let (_key_guard, key_entry) = db
+            .load_key_entry(
+                &KeyDescriptor {
+                    domain: Domain::SELINUX,
+                    nspace: 1,
+                    alias: Some(TEST_ALIAS.to_string()),
+                    blob: None,
+                },
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                1,
+                |_k, _av| Ok(()),
+            )
+            .unwrap();
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+        db.unbind_key(
+            &KeyDescriptor {
+                domain: Domain::SELINUX,
+                nspace: 1,
+                alias: Some(TEST_ALIAS.to_string()),
+                blob: None,
+            },
+            KeyType::Client,
+            1,
+            |_, _| Ok(()),
+        )
+        .unwrap();
+
+        assert_eq!(
+            Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+            db.load_key_entry(
+                &KeyDescriptor {
+                    domain: Domain::SELINUX,
+                    nspace: 1,
+                    alias: Some(TEST_ALIAS.to_string()),
+                    blob: None,
+                },
+                KeyType::Client,
+                KeyEntryLoadBits::NONE,
+                1,
+                |_k, _av| Ok(()),
+            )
+            .unwrap_err()
+            .root_cause()
+            .downcast_ref::<KsError>()
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_insert_and_load_full_keyentry_domain_key_id() -> Result<()> {
+        let mut db = new_test_db()?;
+        let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, None)
+            .context("test_insert_and_load_full_keyentry_domain_key_id")?
+            .0;
+        let (_, key_entry) = db
+            .load_key_entry(
+                &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                1,
+                |_k, _av| Ok(()),
+            )
+            .unwrap();
+
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+        db.unbind_key(
+            &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+            KeyType::Client,
+            1,
+            |_, _| Ok(()),
+        )
+        .unwrap();
+
+        assert_eq!(
+            Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+            db.load_key_entry(
+                &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+                KeyType::Client,
+                KeyEntryLoadBits::NONE,
+                1,
+                |_k, _av| Ok(()),
+            )
+            .unwrap_err()
+            .root_cause()
+            .downcast_ref::<KsError>()
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_check_and_update_key_usage_count_with_limited_use_key() -> Result<()> {
+        let mut db = new_test_db()?;
+        let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(123))
+            .context("test_check_and_update_key_usage_count_with_limited_use_key")?
+            .0;
+        // Update the usage count of the limited use key.
+        db.check_and_update_key_usage_count(key_id)?;
+
+        let (_key_guard, key_entry) = db.load_key_entry(
+            &KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+            KeyType::Client,
+            KeyEntryLoadBits::BOTH,
+            1,
+            |_k, _av| Ok(()),
+        )?;
+
+        // The usage count is decremented now.
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, Some(122)));
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_check_and_update_key_usage_count_with_exhausted_limited_use_key() -> Result<()> {
+        let mut db = new_test_db()?;
+        let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, Some(1))
+            .context("test_check_and_update_key_usage_count_with_exhausted_limited_use_key")?
+            .0;
+        // Update the usage count of the limited use key.
+        db.check_and_update_key_usage_count(key_id).expect(concat!(
+            "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
+            "This should succeed."
+        ));
+
+        // Try to update the exhausted limited use key.
+        let e = db.check_and_update_key_usage_count(key_id).expect_err(concat!(
+            "In test_check_and_update_key_usage_count_with_exhausted_limited_use_key: ",
+            "This should fail."
+        ));
+        assert_eq!(
+            &KsError::Km(ErrorCode::INVALID_KEY_BLOB),
+            e.root_cause().downcast_ref::<KsError>().unwrap()
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_insert_and_load_full_keyentry_from_grant() -> Result<()> {
+        let mut db = new_test_db()?;
+        let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS, None)
+            .context("test_insert_and_load_full_keyentry_from_grant")?
+            .0;
+
+        let granted_key = db
+            .grant(
+                &KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: 0,
+                    alias: Some(TEST_ALIAS.to_string()),
+                    blob: None,
+                },
+                1,
+                2,
+                key_perm_set![KeyPerm::use_()],
+                |_k, _av| Ok(()),
+            )
+            .unwrap();
+
+        debug_dump_grant_table(&mut db)?;
+
+        let (_key_guard, key_entry) = db
+            .load_key_entry(&granted_key, KeyType::Client, KeyEntryLoadBits::BOTH, 2, |k, av| {
+                assert_eq!(Domain::GRANT, k.domain);
+                assert!(av.unwrap().includes(KeyPerm::use_()));
+                Ok(())
+            })
+            .unwrap();
+
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+        db.unbind_key(&granted_key, KeyType::Client, 2, |_, _| Ok(())).unwrap();
+
+        assert_eq!(
+            Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+            db.load_key_entry(
+                &granted_key,
+                KeyType::Client,
+                KeyEntryLoadBits::NONE,
+                2,
+                |_k, _av| Ok(()),
+            )
+            .unwrap_err()
+            .root_cause()
+            .downcast_ref::<KsError>()
+        );
+
+        Ok(())
+    }
+
+    // This test attempts to load a key by key id while the caller is not the owner
+    // but a grant exists for the given key and the caller.
+    #[test]
+    fn test_insert_and_load_full_keyentry_from_grant_by_key_id() -> Result<()> {
+        let mut db = new_test_db()?;
+        const OWNER_UID: u32 = 1u32;
+        const GRANTEE_UID: u32 = 2u32;
+        const SOMEONE_ELSE_UID: u32 = 3u32;
+        let key_id = make_test_key_entry(&mut db, Domain::APP, OWNER_UID as i64, TEST_ALIAS, None)
+            .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?
+            .0;
+
+        db.grant(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: 0,
+                alias: Some(TEST_ALIAS.to_string()),
+                blob: None,
+            },
+            OWNER_UID,
+            GRANTEE_UID,
+            key_perm_set![KeyPerm::use_()],
+            |_k, _av| Ok(()),
+        )
+        .unwrap();
+
+        debug_dump_grant_table(&mut db)?;
+
+        let id_descriptor =
+            KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, ..Default::default() };
+
+        let (_, key_entry) = db
+            .load_key_entry(
+                &id_descriptor,
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                GRANTEE_UID,
+                |k, av| {
+                    assert_eq!(Domain::APP, k.domain);
+                    assert_eq!(OWNER_UID as i64, k.nspace);
+                    assert!(av.unwrap().includes(KeyPerm::use_()));
+                    Ok(())
+                },
+            )
+            .unwrap();
+
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+        let (_, key_entry) = db
+            .load_key_entry(
+                &id_descriptor,
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                SOMEONE_ELSE_UID,
+                |k, av| {
+                    assert_eq!(Domain::APP, k.domain);
+                    assert_eq!(OWNER_UID as i64, k.nspace);
+                    assert!(av.is_none());
+                    Ok(())
+                },
+            )
+            .unwrap();
+
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+        db.unbind_key(&id_descriptor, KeyType::Client, OWNER_UID, |_, _| Ok(())).unwrap();
+
+        assert_eq!(
+            Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+            db.load_key_entry(
+                &id_descriptor,
+                KeyType::Client,
+                KeyEntryLoadBits::NONE,
+                GRANTEE_UID,
+                |_k, _av| Ok(()),
+            )
+            .unwrap_err()
+            .root_cause()
+            .downcast_ref::<KsError>()
+        );
+
+        Ok(())
+    }
+
+    // Creates a key migrates it to a different location and then tries to access it by the old
+    // and new location.
+    #[test]
+    fn test_migrate_key_app_to_app() -> Result<()> {
+        let mut db = new_test_db()?;
+        const SOURCE_UID: u32 = 1u32;
+        const DESTINATION_UID: u32 = 2u32;
+        static SOURCE_ALIAS: &str = &"SOURCE_ALIAS";
+        static DESTINATION_ALIAS: &str = &"DESTINATION_ALIAS";
+        let key_id_guard =
+            make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
+                .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+
+        let source_descriptor: KeyDescriptor = KeyDescriptor {
+            domain: Domain::APP,
+            nspace: -1,
+            alias: Some(SOURCE_ALIAS.to_string()),
+            blob: None,
+        };
+
+        let destination_descriptor: KeyDescriptor = KeyDescriptor {
+            domain: Domain::APP,
+            nspace: -1,
+            alias: Some(DESTINATION_ALIAS.to_string()),
+            blob: None,
+        };
+
+        let key_id = key_id_guard.id();
+
+        db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| {
+            Ok(())
+        })
+        .unwrap();
+
+        let (_, key_entry) = db
+            .load_key_entry(
+                &destination_descriptor,
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                DESTINATION_UID,
+                |k, av| {
+                    assert_eq!(Domain::APP, k.domain);
+                    assert_eq!(DESTINATION_UID as i64, k.nspace);
+                    assert!(av.is_none());
+                    Ok(())
+                },
+            )
+            .unwrap();
+
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+        assert_eq!(
+            Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+            db.load_key_entry(
+                &source_descriptor,
+                KeyType::Client,
+                KeyEntryLoadBits::NONE,
+                SOURCE_UID,
+                |_k, _av| Ok(()),
+            )
+            .unwrap_err()
+            .root_cause()
+            .downcast_ref::<KsError>()
+        );
+
+        Ok(())
+    }
+
+    // Creates a key migrates it to a different location and then tries to access it by the old
+    // and new location.
+    #[test]
+    fn test_migrate_key_app_to_selinux() -> Result<()> {
+        let mut db = new_test_db()?;
+        const SOURCE_UID: u32 = 1u32;
+        const DESTINATION_UID: u32 = 2u32;
+        const DESTINATION_NAMESPACE: i64 = 1000i64;
+        static SOURCE_ALIAS: &str = &"SOURCE_ALIAS";
+        static DESTINATION_ALIAS: &str = &"DESTINATION_ALIAS";
+        let key_id_guard =
+            make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
+                .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+
+        let source_descriptor: KeyDescriptor = KeyDescriptor {
+            domain: Domain::APP,
+            nspace: -1,
+            alias: Some(SOURCE_ALIAS.to_string()),
+            blob: None,
+        };
+
+        let destination_descriptor: KeyDescriptor = KeyDescriptor {
+            domain: Domain::SELINUX,
+            nspace: DESTINATION_NAMESPACE,
+            alias: Some(DESTINATION_ALIAS.to_string()),
+            blob: None,
+        };
+
+        let key_id = key_id_guard.id();
+
+        db.migrate_key_namespace(key_id_guard, &destination_descriptor, DESTINATION_UID, |_k| {
+            Ok(())
+        })
+        .unwrap();
+
+        let (_, key_entry) = db
+            .load_key_entry(
+                &destination_descriptor,
+                KeyType::Client,
+                KeyEntryLoadBits::BOTH,
+                DESTINATION_UID,
+                |k, av| {
+                    assert_eq!(Domain::SELINUX, k.domain);
+                    assert_eq!(DESTINATION_NAMESPACE as i64, k.nspace);
+                    assert!(av.is_none());
+                    Ok(())
+                },
+            )
+            .unwrap();
+
+        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+
+        assert_eq!(
+            Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+            db.load_key_entry(
+                &source_descriptor,
+                KeyType::Client,
+                KeyEntryLoadBits::NONE,
+                SOURCE_UID,
+                |_k, _av| Ok(()),
+            )
+            .unwrap_err()
+            .root_cause()
+            .downcast_ref::<KsError>()
+        );
+
+        Ok(())
+    }
+
+    // Creates two keys and tries to migrate the first to the location of the second which
+    // is expected to fail.
+    #[test]
+    fn test_migrate_key_destination_occupied() -> Result<()> {
+        let mut db = new_test_db()?;
+        const SOURCE_UID: u32 = 1u32;
+        const DESTINATION_UID: u32 = 2u32;
+        static SOURCE_ALIAS: &str = &"SOURCE_ALIAS";
+        static DESTINATION_ALIAS: &str = &"DESTINATION_ALIAS";
+        let key_id_guard =
+            make_test_key_entry(&mut db, Domain::APP, SOURCE_UID as i64, SOURCE_ALIAS, None)
+                .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+        make_test_key_entry(&mut db, Domain::APP, DESTINATION_UID as i64, DESTINATION_ALIAS, None)
+            .context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?;
+
+        let destination_descriptor: KeyDescriptor = KeyDescriptor {
+            domain: Domain::APP,
+            nspace: -1,
+            alias: Some(DESTINATION_ALIAS.to_string()),
+            blob: None,
+        };
+
+        assert_eq!(
+            Some(&KsError::Rc(ResponseCode::INVALID_ARGUMENT)),
+            db.migrate_key_namespace(
+                key_id_guard,
+                &destination_descriptor,
+                DESTINATION_UID,
+                |_k| Ok(())
+            )
+            .unwrap_err()
+            .root_cause()
+            .downcast_ref::<KsError>()
+        );
+
+        Ok(())
+    }
+
+    static KEY_LOCK_TEST_ALIAS: &str = "my super duper locked key";
+
+    #[test]
+    fn test_insert_and_load_full_keyentry_domain_app_concurrently() -> Result<()> {
+        let handle = {
+            let temp_dir = Arc::new(TempDir::new("id_lock_test")?);
+            let temp_dir_clone = temp_dir.clone();
+            let mut db = KeystoreDB::new(temp_dir.path(), None)?;
+            let key_id = make_test_key_entry(&mut db, Domain::APP, 33, KEY_LOCK_TEST_ALIAS, None)
+                .context("test_insert_and_load_full_keyentry_domain_app")?
+                .0;
+            let (_key_guard, key_entry) = db
+                .load_key_entry(
+                    &KeyDescriptor {
+                        domain: Domain::APP,
+                        nspace: 0,
+                        alias: Some(KEY_LOCK_TEST_ALIAS.to_string()),
+                        blob: None,
+                    },
+                    KeyType::Client,
+                    KeyEntryLoadBits::BOTH,
+                    33,
+                    |_k, _av| Ok(()),
+                )
+                .unwrap();
+            assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
+            let state = Arc::new(AtomicU8::new(1));
+            let state2 = state.clone();
+
+            // Spawning a second thread that attempts to acquire the key id lock
+            // for the same key as the primary thread. The primary thread then
+            // waits, thereby forcing the secondary thread into the second stage
+            // of acquiring the lock (see KEY ID LOCK 2/2 above).
+            // The test succeeds if the secondary thread observes the transition
+            // of `state` from 1 to 2, despite having a whole second to overtake
+            // the primary thread.
+            let handle = thread::spawn(move || {
+                let temp_dir = temp_dir_clone;
+                let mut db = KeystoreDB::new(temp_dir.path(), None).unwrap();
+                assert!(db
+                    .load_key_entry(
+                        &KeyDescriptor {
+                            domain: Domain::APP,
+                            nspace: 0,
+                            alias: Some(KEY_LOCK_TEST_ALIAS.to_string()),
+                            blob: None,
+                        },
+                        KeyType::Client,
+                        KeyEntryLoadBits::BOTH,
+                        33,
+                        |_k, _av| Ok(()),
+                    )
+                    .is_ok());
+                // We should only see a 2 here because we can only return
+                // from load_key_entry when the `_key_guard` expires,
+                // which happens at the end of the scope.
+                assert_eq!(2, state2.load(Ordering::Relaxed));
+            });
+
+            thread::sleep(std::time::Duration::from_millis(1000));
+
+            assert_eq!(Ok(1), state.compare_exchange(1, 2, Ordering::Relaxed, Ordering::Relaxed));
+
+            // Return the handle from this scope so we can join with the
+            // secondary thread after the key id lock has expired.
+            handle
+            // This is where the `_key_guard` goes out of scope,
+            // which is the reason for concurrent load_key_entry on the same key
+            // to unblock.
+        };
+        // Join with the secondary thread and unwrap, to propagate failing asserts to the
+        // main test thread. We will not see failing asserts in secondary threads otherwise.
+        handle.join().unwrap();
+        Ok(())
+    }
+
+    #[test]
+    fn test_database_busy_error_code() {
+        let temp_dir =
+            TempDir::new("test_database_busy_error_code_").expect("Failed to create temp dir.");
+
+        let mut db1 = KeystoreDB::new(temp_dir.path(), None).expect("Failed to open database1.");
+        let mut db2 = KeystoreDB::new(temp_dir.path(), None).expect("Failed to open database2.");
+
+        let _tx1 = db1
+            .conn
+            .transaction_with_behavior(TransactionBehavior::Immediate)
+            .expect("Failed to create first transaction.");
+
+        let error = db2
+            .conn
+            .transaction_with_behavior(TransactionBehavior::Immediate)
+            .context("Transaction begin failed.")
+            .expect_err("This should fail.");
+        let root_cause = error.root_cause();
+        if let Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseBusy, .. }) =
+            root_cause.downcast_ref::<rusqlite::ffi::Error>()
+        {
+            return;
+        }
+        panic!(
+            "Unexpected error {:?} \n{:?} \n{:?}",
+            error,
+            root_cause,
+            root_cause.downcast_ref::<rusqlite::ffi::Error>()
+        )
+    }
+
+    #[cfg(disabled)]
+    #[test]
+    fn test_large_number_of_concurrent_db_manipulations() -> Result<()> {
+        let temp_dir = Arc::new(
+            TempDir::new("test_large_number_of_concurrent_db_manipulations_")
+                .expect("Failed to create temp dir."),
+        );
+
+        let test_begin = Instant::now();
+
+        let mut db = KeystoreDB::new(temp_dir.path()).expect("Failed to open database.");
+        const KEY_COUNT: u32 = 500u32;
+        const OPEN_DB_COUNT: u32 = 50u32;
+
+        let mut actual_key_count = KEY_COUNT;
+        // First insert KEY_COUNT keys.
+        for count in 0..KEY_COUNT {
+            if Instant::now().duration_since(test_begin) >= Duration::from_secs(15) {
+                actual_key_count = count;
+                break;
+            }
+            let alias = format!("test_alias_{}", count);
+            make_test_key_entry(&mut db, Domain::APP, 1, &alias, None)
+                .expect("Failed to make key entry.");
+        }
+
+        // Insert more keys from a different thread and into a different namespace.
+        let temp_dir1 = temp_dir.clone();
+        let handle1 = thread::spawn(move || {
+            let mut db = KeystoreDB::new(temp_dir1.path()).expect("Failed to open database.");
+
+            for count in 0..actual_key_count {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let alias = format!("test_alias_{}", count);
+                make_test_key_entry(&mut db, Domain::APP, 2, &alias, None)
+                    .expect("Failed to make key entry.");
+            }
+
+            // then unbind them again.
+            for count in 0..actual_key_count {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let key = KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: -1,
+                    alias: Some(format!("test_alias_{}", count)),
+                    blob: None,
+                };
+                db.unbind_key(&key, KeyType::Client, 2, |_, _| Ok(())).expect("Unbind Failed.");
+            }
+        });
+
+        // And start unbinding the first set of keys.
+        let temp_dir2 = temp_dir.clone();
+        let handle2 = thread::spawn(move || {
+            let mut db = KeystoreDB::new(temp_dir2.path()).expect("Failed to open database.");
+
+            for count in 0..actual_key_count {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let key = KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: -1,
+                    alias: Some(format!("test_alias_{}", count)),
+                    blob: None,
+                };
+                db.unbind_key(&key, KeyType::Client, 1, |_, _| Ok(())).expect("Unbind Failed.");
+            }
+        });
+
+        let stop_deleting = Arc::new(AtomicU8::new(0));
+        let stop_deleting2 = stop_deleting.clone();
+
+        // And delete anything that is unreferenced keys.
+        let temp_dir3 = temp_dir.clone();
+        let handle3 = thread::spawn(move || {
+            let mut db = KeystoreDB::new(temp_dir3.path()).expect("Failed to open database.");
+
+            while stop_deleting2.load(Ordering::Relaxed) != 1 {
+                while let Some((key_guard, _key)) =
+                    db.get_unreferenced_key().expect("Failed to get unreferenced Key.")
+                {
+                    if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                        return;
+                    }
+                    db.purge_key_entry(key_guard).expect("Failed to purge key.");
+                }
+                std::thread::sleep(std::time::Duration::from_millis(100));
+            }
+        });
+
+        // While a lot of inserting and deleting is going on we have to open database connections
+        // successfully and use them.
+        // This clone is not redundant, because temp_dir needs to be kept alive until db goes
+        // out of scope.
+        #[allow(clippy::redundant_clone)]
+        let temp_dir4 = temp_dir.clone();
+        let handle4 = thread::spawn(move || {
+            for count in 0..OPEN_DB_COUNT {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let mut db = KeystoreDB::new(temp_dir4.path()).expect("Failed to open database.");
+
+                let alias = format!("test_alias_{}", count);
+                make_test_key_entry(&mut db, Domain::APP, 3, &alias, None)
+                    .expect("Failed to make key entry.");
+                let key = KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: -1,
+                    alias: Some(alias),
+                    blob: None,
+                };
+                db.unbind_key(&key, KeyType::Client, 3, |_, _| Ok(())).expect("Unbind Failed.");
+            }
+        });
+
+        handle1.join().expect("Thread 1 panicked.");
+        handle2.join().expect("Thread 2 panicked.");
+        handle4.join().expect("Thread 4 panicked.");
+
+        stop_deleting.store(1, Ordering::Relaxed);
+        handle3.join().expect("Thread 3 panicked.");
+
+        Ok(())
+    }
+
+    #[test]
+    fn list() -> Result<()> {
+        let temp_dir = TempDir::new("list_test")?;
+        let mut db = KeystoreDB::new(temp_dir.path(), None)?;
+        static LIST_O_ENTRIES: &[(Domain, i64, &str)] = &[
+            (Domain::APP, 1, "test1"),
+            (Domain::APP, 1, "test2"),
+            (Domain::APP, 1, "test3"),
+            (Domain::APP, 1, "test4"),
+            (Domain::APP, 1, "test5"),
+            (Domain::APP, 1, "test6"),
+            (Domain::APP, 1, "test7"),
+            (Domain::APP, 2, "test1"),
+            (Domain::APP, 2, "test2"),
+            (Domain::APP, 2, "test3"),
+            (Domain::APP, 2, "test4"),
+            (Domain::APP, 2, "test5"),
+            (Domain::APP, 2, "test6"),
+            (Domain::APP, 2, "test8"),
+            (Domain::SELINUX, 100, "test1"),
+            (Domain::SELINUX, 100, "test2"),
+            (Domain::SELINUX, 100, "test3"),
+            (Domain::SELINUX, 100, "test4"),
+            (Domain::SELINUX, 100, "test5"),
+            (Domain::SELINUX, 100, "test6"),
+            (Domain::SELINUX, 100, "test9"),
+        ];
+
+        let list_o_keys: Vec<(i64, i64)> = LIST_O_ENTRIES
+            .iter()
+            .map(|(domain, ns, alias)| {
+                let entry = make_test_key_entry(&mut db, *domain, *ns, *alias, None)
+                    .unwrap_or_else(|e| {
+                        panic!("Failed to insert {:?} {} {}. Error {:?}", domain, ns, alias, e)
+                    });
+                (entry.id(), *ns)
+            })
+            .collect();
+
+        for (domain, namespace) in
+            &[(Domain::APP, 1i64), (Domain::APP, 2i64), (Domain::SELINUX, 100i64)]
+        {
+            let mut list_o_descriptors: Vec<KeyDescriptor> = LIST_O_ENTRIES
+                .iter()
+                .filter_map(|(domain, ns, alias)| match ns {
+                    ns if *ns == *namespace => Some(KeyDescriptor {
+                        domain: *domain,
+                        nspace: *ns,
+                        alias: Some(alias.to_string()),
+                        blob: None,
+                    }),
+                    _ => None,
+                })
+                .collect();
+            list_o_descriptors.sort();
+            let mut list_result = db.list(*domain, *namespace)?;
+            list_result.sort();
+            assert_eq!(list_o_descriptors, list_result);
+
+            let mut list_o_ids: Vec<i64> = list_o_descriptors
+                .into_iter()
+                .map(|d| {
+                    let (_, entry) = db
+                        .load_key_entry(
+                            &d,
+                            KeyType::Client,
+                            KeyEntryLoadBits::NONE,
+                            *namespace as u32,
+                            |_, _| Ok(()),
+                        )
+                        .unwrap();
+                    entry.id()
+                })
+                .collect();
+            list_o_ids.sort_unstable();
+            let mut loaded_entries: Vec<i64> = list_o_keys
+                .iter()
+                .filter_map(|(id, ns)| match ns {
+                    ns if *ns == *namespace => Some(*id),
+                    _ => None,
+                })
+                .collect();
+            loaded_entries.sort_unstable();
+            assert_eq!(list_o_ids, loaded_entries);
+        }
+        assert_eq!(Vec::<KeyDescriptor>::new(), db.list(Domain::SELINUX, 101)?);
+
+        Ok(())
+    }
+
+    // Helpers
+
+    // Checks that the given result is an error containing the given string.
+    fn check_result_is_error_containing_string<T>(result: Result<T>, target: &str) {
+        let error_str = format!(
+            "{:#?}",
+            result.err().unwrap_or_else(|| panic!("Expected the error: {}", target))
+        );
+        assert!(
+            error_str.contains(target),
+            "The string \"{}\" should contain \"{}\"",
+            error_str,
+            target
+        );
+    }
+
+    #[derive(Debug, PartialEq)]
+    struct KeyEntryRow {
+        id: i64,
+        key_type: KeyType,
+        domain: Option<Domain>,
+        namespace: Option<i64>,
+        alias: Option<String>,
+        state: KeyLifeCycle,
+        km_uuid: Option<Uuid>,
+    }
+
+    fn get_keyentry(db: &KeystoreDB) -> Result<Vec<KeyEntryRow>> {
+        db.conn
+            .prepare("SELECT * FROM persistent.keyentry;")?
+            .query_map(NO_PARAMS, |row| {
+                Ok(KeyEntryRow {
+                    id: row.get(0)?,
+                    key_type: row.get(1)?,
+                    domain: match row.get(2)? {
+                        Some(i) => Some(Domain(i)),
+                        None => None,
+                    },
+                    namespace: row.get(3)?,
+                    alias: row.get(4)?,
+                    state: row.get(5)?,
+                    km_uuid: row.get(6)?,
+                })
+            })?
+            .map(|r| r.context("Could not read keyentry row."))
+            .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<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)?;
+        Ok(RemoteProvValues { cert_chain, priv_key, batch_cert })
+    }
+
+    // Note: The parameters and SecurityLevel associations are nonsensical. This
+    // collection is only used to check if the parameters are preserved as expected by the
+    // database.
+    fn make_test_params(max_usage_count: Option<i32>) -> Vec<KeyParameter> {
+        let mut params = vec![
+            KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::TRUSTED_ENVIRONMENT),
+            KeyParameter::new(
+                KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::Algorithm(Algorithm::RSA),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(KeyParameterValue::KeySize(1024), SecurityLevel::TRUSTED_ENVIRONMENT),
+            KeyParameter::new(
+                KeyParameterValue::BlockMode(BlockMode::ECB),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::BlockMode(BlockMode::GCM),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(KeyParameterValue::Digest(Digest::NONE), SecurityLevel::STRONGBOX),
+            KeyParameter::new(
+                KeyParameterValue::Digest(Digest::MD5),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::Digest(Digest::SHA_2_224),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::Digest(Digest::SHA_2_256),
+                SecurityLevel::STRONGBOX,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::PaddingMode(PaddingMode::NONE),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::PaddingMode(PaddingMode::RSA_OAEP),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS),
+                SecurityLevel::STRONGBOX,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::PaddingMode(PaddingMode::RSA_PKCS1_1_5_SIGN),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::TRUSTED_ENVIRONMENT),
+            KeyParameter::new(KeyParameterValue::MinMacLength(256), SecurityLevel::STRONGBOX),
+            KeyParameter::new(
+                KeyParameterValue::EcCurve(EcCurve::P_224),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(KeyParameterValue::EcCurve(EcCurve::P_256), SecurityLevel::STRONGBOX),
+            KeyParameter::new(
+                KeyParameterValue::EcCurve(EcCurve::P_384),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::EcCurve(EcCurve::P_521),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::RSAPublicExponent(3),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::IncludeUniqueID,
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(KeyParameterValue::BootLoaderOnly, SecurityLevel::STRONGBOX),
+            KeyParameter::new(KeyParameterValue::RollbackResistance, SecurityLevel::STRONGBOX),
+            KeyParameter::new(
+                KeyParameterValue::ActiveDateTime(1234567890),
+                SecurityLevel::STRONGBOX,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::OriginationExpireDateTime(1234567890),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::UsageExpireDateTime(1234567890),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::MinSecondsBetweenOps(1234567890),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::MaxUsesPerBoot(1234567890),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(KeyParameterValue::UserID(1), SecurityLevel::STRONGBOX),
+            KeyParameter::new(KeyParameterValue::UserSecureID(42), SecurityLevel::STRONGBOX),
+            KeyParameter::new(
+                KeyParameterValue::NoAuthRequired,
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType::PASSWORD),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(KeyParameterValue::AuthTimeout(1234567890), SecurityLevel::SOFTWARE),
+            KeyParameter::new(KeyParameterValue::AllowWhileOnBody, SecurityLevel::SOFTWARE),
+            KeyParameter::new(
+                KeyParameterValue::TrustedUserPresenceRequired,
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::TrustedConfirmationRequired,
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::UnlockedDeviceRequired,
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::ApplicationID(vec![1u8, 2u8, 3u8, 4u8]),
+                SecurityLevel::SOFTWARE,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::ApplicationData(vec![4u8, 3u8, 2u8, 1u8]),
+                SecurityLevel::SOFTWARE,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::CreationDateTime(12345677890),
+                SecurityLevel::SOFTWARE,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::KeyOrigin(KeyOrigin::GENERATED),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::RootOfTrust(vec![3u8, 2u8, 1u8, 4u8]),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(KeyParameterValue::OSVersion(1), SecurityLevel::TRUSTED_ENVIRONMENT),
+            KeyParameter::new(KeyParameterValue::OSPatchLevel(2), SecurityLevel::SOFTWARE),
+            KeyParameter::new(
+                KeyParameterValue::UniqueID(vec![4u8, 3u8, 1u8, 2u8]),
+                SecurityLevel::SOFTWARE,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::AttestationChallenge(vec![4u8, 3u8, 1u8, 2u8]),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::AttestationApplicationID(vec![4u8, 3u8, 1u8, 2u8]),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::AttestationIdBrand(vec![4u8, 3u8, 1u8, 2u8]),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::AttestationIdDevice(vec![4u8, 3u8, 1u8, 2u8]),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::AttestationIdProduct(vec![4u8, 3u8, 1u8, 2u8]),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::AttestationIdSerial(vec![4u8, 3u8, 1u8, 2u8]),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::AttestationIdIMEI(vec![4u8, 3u8, 1u8, 2u8]),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::AttestationIdMEID(vec![4u8, 3u8, 1u8, 2u8]),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::AttestationIdManufacturer(vec![4u8, 3u8, 1u8, 2u8]),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::AttestationIdModel(vec![4u8, 3u8, 1u8, 2u8]),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::VendorPatchLevel(3),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::BootPatchLevel(4),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::AssociatedData(vec![4u8, 3u8, 1u8, 2u8]),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::Nonce(vec![4u8, 3u8, 1u8, 2u8]),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::MacLength(256),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::ResetSinceIdRotation,
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+            KeyParameter::new(
+                KeyParameterValue::ConfirmationToken(vec![5u8, 5u8, 5u8, 5u8]),
+                SecurityLevel::TRUSTED_ENVIRONMENT,
+            ),
+        ];
+        if let Some(value) = max_usage_count {
+            params.push(KeyParameter::new(
+                KeyParameterValue::UsageCountLimit(value),
+                SecurityLevel::SOFTWARE,
+            ));
+        }
+        params
+    }
+
+    fn make_test_key_entry(
+        db: &mut KeystoreDB,
+        domain: Domain,
+        namespace: i64,
+        alias: &str,
+        max_usage_count: Option<i32>,
+    ) -> Result<KeyIdGuard> {
+        let key_id = db.create_key_entry(&domain, &namespace, &KEYSTORE_UUID)?;
+        let mut blob_metadata = BlobMetaData::new();
+        blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
+        blob_metadata.add(BlobMetaEntry::Salt(vec![1, 2, 3]));
+        blob_metadata.add(BlobMetaEntry::Iv(vec![2, 3, 1]));
+        blob_metadata.add(BlobMetaEntry::AeadTag(vec![3, 1, 2]));
+        blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+        db.set_blob(
+            &key_id,
+            SubComponentType::KEY_BLOB,
+            Some(TEST_KEY_BLOB),
+            Some(&blob_metadata),
+        )?;
+        db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
+        db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
+
+        let params = make_test_params(max_usage_count);
+        db.insert_keyparameter(&key_id, &params)?;
+
+        let mut metadata = KeyMetaData::new();
+        metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+        db.insert_key_metadata(&key_id, &metadata)?;
+        rebind_alias(db, &key_id, alias, domain, namespace)?;
+        Ok(key_id)
+    }
+
+    fn make_test_key_entry_test_vector(key_id: i64, max_usage_count: Option<i32>) -> KeyEntry {
+        let params = make_test_params(max_usage_count);
+
+        let mut blob_metadata = BlobMetaData::new();
+        blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
+        blob_metadata.add(BlobMetaEntry::Salt(vec![1, 2, 3]));
+        blob_metadata.add(BlobMetaEntry::Iv(vec![2, 3, 1]));
+        blob_metadata.add(BlobMetaEntry::AeadTag(vec![3, 1, 2]));
+        blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+
+        let mut metadata = KeyMetaData::new();
+        metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+
+        KeyEntry {
+            id: key_id,
+            key_blob_info: Some((TEST_KEY_BLOB.to_vec(), blob_metadata)),
+            cert: Some(TEST_CERT_BLOB.to_vec()),
+            cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
+            km_uuid: KEYSTORE_UUID,
+            parameters: params,
+            metadata,
+            pure_cert: false,
+        }
+    }
+
+    fn debug_dump_keyentry_table(db: &mut KeystoreDB) -> Result<()> {
+        let mut stmt = db.conn.prepare(
+            "SELECT id, key_type, domain, namespace, alias, state, km_uuid FROM persistent.keyentry;",
+        )?;
+        let rows = stmt.query_map::<(i64, KeyType, i32, i64, String, KeyLifeCycle, Uuid), _, _>(
+            NO_PARAMS,
+            |row| {
+                Ok((
+                    row.get(0)?,
+                    row.get(1)?,
+                    row.get(2)?,
+                    row.get(3)?,
+                    row.get(4)?,
+                    row.get(5)?,
+                    row.get(6)?,
+                ))
+            },
+        )?;
+
+        println!("Key entry table rows:");
+        for r in rows {
+            let (id, key_type, domain, namespace, alias, state, km_uuid) = r.unwrap();
+            println!(
+                "    id: {} KeyType: {:?} Domain: {} Namespace: {} Alias: {} State: {:?} KmUuid: {:?}",
+                id, key_type, domain, namespace, alias, state, km_uuid
+            );
+        }
+        Ok(())
+    }
+
+    fn debug_dump_grant_table(db: &mut KeystoreDB) -> Result<()> {
+        let mut stmt = db
+            .conn
+            .prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
+        let rows = stmt.query_map::<(i64, i64, i64, i64), _, _>(NO_PARAMS, |row| {
+            Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?))
+        })?;
+
+        println!("Grant table rows:");
+        for r in rows {
+            let (id, gt, ki, av) = r.unwrap();
+            println!("    id: {} grantee: {} key_id: {} access_vector: {}", id, gt, ki, av);
+        }
+        Ok(())
+    }
+
+    // Use a custom random number generator that repeats each number once.
+    // This allows us to test repeated elements.
+
+    thread_local! {
+        static RANDOM_COUNTER: RefCell<i64> = RefCell::new(0);
+    }
+
+    fn reset_random() {
+        RANDOM_COUNTER.with(|counter| {
+            *counter.borrow_mut() = 0;
+        })
+    }
+
+    pub fn random() -> i64 {
+        RANDOM_COUNTER.with(|counter| {
+            let result = *counter.borrow() / 2;
+            *counter.borrow_mut() += 1;
+            result
+        })
+    }
+
+    #[test]
+    fn test_last_off_body() -> Result<()> {
+        let mut db = new_test_db()?;
+        db.insert_last_off_body(MonotonicRawTime::now())?;
+        let tx = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?;
+        let last_off_body_1 = KeystoreDB::get_last_off_body(&tx)?;
+        tx.commit()?;
+        let one_second = Duration::from_secs(1);
+        thread::sleep(one_second);
+        db.update_last_off_body(MonotonicRawTime::now())?;
+        let tx2 = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?;
+        let last_off_body_2 = KeystoreDB::get_last_off_body(&tx2)?;
+        tx2.commit()?;
+        assert!(last_off_body_1.seconds() < last_off_body_2.seconds());
+        Ok(())
+    }
+
+    #[test]
+    fn test_unbind_keys_for_user() -> Result<()> {
+        let mut db = new_test_db()?;
+        db.unbind_keys_for_user(1, false)?;
+
+        make_test_key_entry(&mut db, Domain::APP, 210000, TEST_ALIAS, None)?;
+        make_test_key_entry(&mut db, Domain::APP, 110000, TEST_ALIAS, None)?;
+        db.unbind_keys_for_user(2, false)?;
+
+        assert_eq!(1, db.list(Domain::APP, 110000)?.len());
+        assert_eq!(0, db.list(Domain::APP, 210000)?.len());
+
+        db.unbind_keys_for_user(1, true)?;
+        assert_eq!(0, db.list(Domain::APP, 110000)?.len());
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_store_super_key() -> Result<()> {
+        let mut db = new_test_db()?;
+        let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
+        let super_key = keystore2_crypto::generate_aes256_key()?;
+        let secret_bytes = b"keystore2 is great.";
+        let (encrypted_secret, iv, tag) =
+            keystore2_crypto::aes_gcm_encrypt(secret_bytes, &super_key)?;
+
+        let (encrypted_super_key, metadata) =
+            SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
+        db.store_super_key(
+            1,
+            &USER_SUPER_KEY,
+            &encrypted_super_key,
+            &metadata,
+            &KeyMetaData::new(),
+        )?;
+
+        //check if super key exists
+        assert!(db.key_exists(Domain::APP, 1, &USER_SUPER_KEY.alias, KeyType::Super)?);
+
+        let (_, key_entry) = db.load_super_key(&USER_SUPER_KEY, 1)?.unwrap();
+        let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(
+            USER_SUPER_KEY.algorithm,
+            key_entry,
+            &pw,
+            None,
+        )?;
+
+        let decrypted_secret_bytes =
+            loaded_super_key.aes_gcm_decrypt(&encrypted_secret, &iv, &tag)?;
+        assert_eq!(secret_bytes, &*decrypted_secret_bytes);
+        Ok(())
+    }
+
+    fn get_valid_statsd_storage_types() -> Vec<StatsdStorageType> {
+        vec![
+            StatsdStorageType::KeyEntry,
+            StatsdStorageType::KeyEntryIdIndex,
+            StatsdStorageType::KeyEntryDomainNamespaceIndex,
+            StatsdStorageType::BlobEntry,
+            StatsdStorageType::BlobEntryKeyEntryIdIndex,
+            StatsdStorageType::KeyParameter,
+            StatsdStorageType::KeyParameterKeyEntryIdIndex,
+            StatsdStorageType::KeyMetadata,
+            StatsdStorageType::KeyMetadataKeyEntryIdIndex,
+            StatsdStorageType::Grant,
+            StatsdStorageType::AuthToken,
+            StatsdStorageType::BlobMetadata,
+            StatsdStorageType::BlobMetadataBlobEntryIdIndex,
+        ]
+    }
+
+    /// Perform a simple check to ensure that we can query all the storage types
+    /// that are supported by the DB. Check for reasonable values.
+    #[test]
+    fn test_query_all_valid_table_sizes() -> Result<()> {
+        const PAGE_SIZE: i64 = 4096;
+
+        let mut db = new_test_db()?;
+
+        for t in get_valid_statsd_storage_types() {
+            let stat = db.get_storage_stat(t)?;
+            assert!(stat.size >= PAGE_SIZE);
+            assert!(stat.size >= stat.unused_size);
+        }
+
+        Ok(())
+    }
+
+    fn get_storage_stats_map(db: &mut KeystoreDB) -> BTreeMap<i32, Keystore2StorageStats> {
+        get_valid_statsd_storage_types()
+            .into_iter()
+            .map(|t| (t as i32, db.get_storage_stat(t).unwrap()))
+            .collect()
+    }
+
+    fn assert_storage_increased(
+        db: &mut KeystoreDB,
+        increased_storage_types: Vec<StatsdStorageType>,
+        baseline: &mut BTreeMap<i32, Keystore2StorageStats>,
+    ) {
+        for storage in increased_storage_types {
+            // Verify the expected storage increased.
+            let new = db.get_storage_stat(storage).unwrap();
+            let storage = storage as i32;
+            let old = &baseline[&storage];
+            assert!(new.size >= old.size, "{}: {} >= {}", storage, new.size, old.size);
+            assert!(
+                new.unused_size <= old.unused_size,
+                "{}: {} <= {}",
+                storage,
+                new.unused_size,
+                old.unused_size
+            );
+
+            // Update the baseline with the new value so that it succeeds in the
+            // later comparison.
+            baseline.insert(storage, new);
+        }
+
+        // Get an updated map of the storage and verify there were no unexpected changes.
+        let updated_stats = get_storage_stats_map(db);
+        assert_eq!(updated_stats.len(), baseline.len());
+
+        for &k in baseline.keys() {
+            let stringify = |map: &BTreeMap<i32, Keystore2StorageStats>| -> String {
+                let mut s = String::new();
+                for &k in map.keys() {
+                    writeln!(&mut s, "  {}: {}, {}", &k, map[&k].size, map[&k].unused_size)
+                        .expect("string concat failed");
+                }
+                s
+            };
+
+            assert!(
+                updated_stats[&k].size == baseline[&k].size
+                    && updated_stats[&k].unused_size == baseline[&k].unused_size,
+                "updated_stats:\n{}\nbaseline:\n{}",
+                stringify(&updated_stats),
+                stringify(&baseline)
+            );
+        }
+    }
+
+    #[test]
+    fn test_verify_key_table_size_reporting() -> Result<()> {
+        let mut db = new_test_db()?;
+        let mut working_stats = get_storage_stats_map(&mut db);
+
+        let key_id = db.create_key_entry(&Domain::APP, &42, &KEYSTORE_UUID)?;
+        assert_storage_increased(
+            &mut db,
+            vec![
+                StatsdStorageType::KeyEntry,
+                StatsdStorageType::KeyEntryIdIndex,
+                StatsdStorageType::KeyEntryDomainNamespaceIndex,
+            ],
+            &mut working_stats,
+        );
+
+        let mut blob_metadata = BlobMetaData::new();
+        blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
+        db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB), None)?;
+        assert_storage_increased(
+            &mut db,
+            vec![
+                StatsdStorageType::BlobEntry,
+                StatsdStorageType::BlobEntryKeyEntryIdIndex,
+                StatsdStorageType::BlobMetadata,
+                StatsdStorageType::BlobMetadataBlobEntryIdIndex,
+            ],
+            &mut working_stats,
+        );
+
+        let params = make_test_params(None);
+        db.insert_keyparameter(&key_id, &params)?;
+        assert_storage_increased(
+            &mut db,
+            vec![StatsdStorageType::KeyParameter, StatsdStorageType::KeyParameterKeyEntryIdIndex],
+            &mut working_stats,
+        );
+
+        let mut metadata = KeyMetaData::new();
+        metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+        db.insert_key_metadata(&key_id, &metadata)?;
+        assert_storage_increased(
+            &mut db,
+            vec![StatsdStorageType::KeyMetadata, StatsdStorageType::KeyMetadataKeyEntryIdIndex],
+            &mut working_stats,
+        );
+
+        let mut sum = 0;
+        for stat in working_stats.values() {
+            sum += stat.size;
+        }
+        let total = db.get_storage_stat(StatsdStorageType::Database)?.size;
+        assert!(sum <= total, "Expected sum <= total. sum: {}, total: {}", sum, total);
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_verify_auth_table_size_reporting() -> Result<()> {
+        let mut db = new_test_db()?;
+        let mut working_stats = get_storage_stats_map(&mut db);
+        db.insert_auth_token(&HardwareAuthToken {
+            challenge: 123,
+            userId: 456,
+            authenticatorId: 789,
+            authenticatorType: kmhw_authenticator_type::ANY,
+            timestamp: Timestamp { milliSeconds: 10 },
+            mac: b"mac".to_vec(),
+        })?;
+        assert_storage_increased(&mut db, vec![StatsdStorageType::AuthToken], &mut working_stats);
+        Ok(())
+    }
+
+    #[test]
+    fn test_verify_grant_table_size_reporting() -> Result<()> {
+        const OWNER: i64 = 1;
+        let mut db = new_test_db()?;
+        make_test_key_entry(&mut db, Domain::APP, OWNER, TEST_ALIAS, None)?;
+
+        let mut working_stats = get_storage_stats_map(&mut db);
+        db.grant(
+            &KeyDescriptor {
+                domain: Domain::APP,
+                nspace: 0,
+                alias: Some(TEST_ALIAS.to_string()),
+                blob: None,
+            },
+            OWNER as u32,
+            123,
+            key_perm_set![KeyPerm::use_()],
+            |_, _| Ok(()),
+        )?;
+
+        assert_storage_increased(&mut db, vec![StatsdStorageType::Grant], &mut working_stats);
+
+        Ok(())
+    }
+}
diff --git a/keystore2/src/db_utils.rs b/keystore2/src/db_utils.rs
new file mode 100644
index 0000000..90f5616
--- /dev/null
+++ b/keystore2/src/db_utils.rs
@@ -0,0 +1,241 @@
+// 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.
+
+use crate::error::Error as KsError;
+use anyhow::{Context, Result};
+use rusqlite::{types::FromSql, Row, Rows};
+
+// Takes Rows as returned by a query call on prepared statement.
+// Extracts exactly one row with the `row_extractor` and fails if more
+// rows are available.
+// If no row was found, `None` is passed to the `row_extractor`.
+// This allows the row extractor to decide on an error condition or
+// a different default behavior.
+pub fn with_rows_extract_one<'a, T, F>(rows: &mut Rows<'a>, row_extractor: F) -> Result<T>
+where
+    F: FnOnce(Option<&Row<'a>>) -> Result<T>,
+{
+    let result =
+        row_extractor(rows.next().context("with_rows_extract_one: Failed to unpack row.")?);
+
+    rows.next()
+        .context("In with_rows_extract_one: Failed to unpack unexpected row.")?
+        .map_or_else(|| Ok(()), |_| Err(KsError::sys()))
+        .context("In with_rows_extract_one: Unexpected row.")?;
+
+    result
+}
+
+pub fn with_rows_extract_all<'a, F>(rows: &mut Rows<'a>, mut row_extractor: F) -> Result<()>
+where
+    F: FnMut(&Row<'a>) -> Result<()>,
+{
+    loop {
+        match rows.next().context("In with_rows_extract_all: Failed to unpack row")? {
+            Some(row) => {
+                row_extractor(&row).context("In with_rows_extract_all.")?;
+            }
+            None => break Ok(()),
+        }
+    }
+}
+
+/// This struct is defined to postpone converting rusqlite column value to the
+/// appropriate key parameter value until we know the corresponding tag value.
+/// Wraps the column index and a rusqlite row.
+pub struct SqlField<'a>(usize, &'a Row<'a>);
+
+impl<'a> SqlField<'a> {
+    /// Creates a new SqlField with the given index and row.
+    pub fn new(index: usize, row: &'a Row<'a>) -> Self {
+        Self(index, row)
+    }
+    /// Returns the column value from the row, when we know the expected type.
+    pub fn get<T: FromSql>(&self) -> rusqlite::Result<T> {
+        self.1.get(self.0)
+    }
+}
+
+/// This macro implements two types to aid in the implementation of a type safe metadata
+/// store. The first is a collection of metadata and the second is the entry in that
+/// collection. The caller has to provide the infrastructure to load and store the
+/// the collection or individual entries in a SQLite database. The idea is that once
+/// the infrastructure for a metadata collection is in place all it takes to add another
+/// field is make a new entry in the list of variants (see details below).
+///
+/// # Usage
+/// ```
+/// impl_metadata!{
+///     /// This is the name of the collection.
+///     #[derive(Debug, Default)]
+///     pub struct CollectionName;
+///     /// This is the name of the Entry type followed by a list of variants, accessor function
+///     /// names, and types.
+///     #[derive(Debug, Eq, PartialEq)]
+///     pub enum EntryName {
+///         /// An enum variant with an accessor function name.
+///         VariantA(u32) with accessor get_variant_a,
+///         /// A second variant. `MyType` must implement rusqlite::types::ToSql and FromSql.
+///         VariantB(MyType) with accessor get_variant_b,
+///         //  --- ADD NEW META DATA FIELDS HERE ---
+///         // For backwards compatibility add new entries only to
+///         // end of this list and above this comment.
+///     };
+/// }
+/// ```
+///
+/// expands to:
+///
+/// ```
+/// pub enum EntryName {
+///     VariantA(u32),
+///     VariantB(MyType),
+/// }
+///
+/// impl EntryName {}
+///     /// Returns a numeric variant id that can be used for persistent storage.
+///     fn db_tag(&self) -> i64 {...}
+///     /// Helper function that constructs a new `EntryName` given a variant identifier
+///     /// and a to-be-extracted `SqlFiled`
+///     fn new_from_sql(db_tag: i64, data: &SqlField) -> Result<Self> {...}
+/// }
+///
+/// impl ToSql for EntryName {...}
+///
+/// pub struct CollectionName {
+///     data: std::collections::HashMap<i64, EntryName>,
+/// }
+///
+/// impl CollectionName {
+///     /// Create a new collection of meta data.
+///     pub fn new() -> Self {...}
+///     /// Add a new entry to this collection. Replaces existing entries of the
+///     /// same variant unconditionally.
+///     pub fn add(&mut self, e: EntryName) {...}
+///     /// Type safe accessor function for the defined fields.
+///     pub fn get_variant_a() -> Option<u32> {...}
+///     pub fn get_variant_b() -> Option<MyType> {...}
+/// }
+///
+/// let mut collection = CollectionName::new();
+/// collection.add(EntryName::VariantA(3));
+/// let three: u32 = collection.get_variant_a().unwrap()
+/// ```
+///
+/// The caller of this macro must implement the actual database queries to load and store
+/// either a whole collection of metadata or individual fields. For example by associating
+/// with the given type:
+/// ```
+/// impl CollectionName {
+///     fn load(tx: &Transaction) -> Result<Self> {...}
+/// }
+/// ```
+#[macro_export]
+macro_rules! impl_metadata {
+    // These two macros assign incrementing numeric ids to each field which are used as
+    // database tags.
+    (@gen_consts {} {$($n:ident $nid:tt,)*} {$($count:tt)*}) => {
+        $(
+            // This allows us to reuse the variant name for these constants. The constants
+            // are private so that this exception does not spoil the public interface.
+            #[allow(non_upper_case_globals)]
+            const $n: i64 = $nid;
+        )*
+    };
+    (@gen_consts {$first:ident $(,$tail:ident)*} {$($out:tt)*} {$($count:tt)*}) => {
+        impl_metadata!(@gen_consts {$($tail),*} {$($out)* $first ($($count)*),} {$($count)* + 1});
+    };
+    (
+        $(#[$nmeta:meta])*
+        $nvis:vis struct $name:ident;
+        $(#[$emeta:meta])*
+        $evis:vis enum $entry:ident {
+            $($(#[$imeta:meta])* $vname:ident($t:ty) with accessor $func:ident),* $(,)?
+        };
+    ) => {
+        $(#[$emeta])*
+        $evis enum $entry {
+            $(
+                $(#[$imeta])*
+                $vname($t),
+            )*
+        }
+
+        impl $entry {
+            fn db_tag(&self) -> i64 {
+                match self {
+                    $(Self::$vname(_) => $name::$vname,)*
+                }
+            }
+
+            fn new_from_sql(db_tag: i64, data: &SqlField) -> anyhow::Result<Self> {
+                match db_tag {
+                    $(
+                        $name::$vname => {
+                            Ok($entry::$vname(
+                                data.get()
+                                .with_context(|| format!(
+                                    "In {}::new_from_sql: Unable to get {}.",
+                                    stringify!($entry),
+                                    stringify!($vname)
+                                ))?
+                            ))
+                        },
+                    )*
+                    _ => Err(anyhow!(format!(
+                        "In {}::new_from_sql: unknown db tag {}.",
+                        stringify!($entry), db_tag
+                    ))),
+                }
+            }
+        }
+
+        impl rusqlite::types::ToSql for $entry {
+            fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> {
+                match self {
+                    $($entry::$vname(v) => v.to_sql(),)*
+                }
+            }
+        }
+
+        $(#[$nmeta])*
+        $nvis struct $name {
+            data: std::collections::HashMap<i64, $entry>,
+        }
+
+        impl $name {
+            /// Create a new instance of $name
+            pub fn new() -> Self {
+                Self{data: std::collections::HashMap::new()}
+            }
+
+            impl_metadata!{@gen_consts {$($vname),*} {} {0}}
+
+            /// Add a new instance of $entry to this collection of metadata.
+            pub fn add(&mut self, entry: $entry) {
+                self.data.insert(entry.db_tag(), entry);
+            }
+            $(
+                /// If the variant $vname is set, returns the wrapped value or None otherwise.
+                pub fn $func(&self) -> Option<&$t> {
+                    if let Some($entry::$vname(v)) = self.data.get(&Self::$vname) {
+                        Some(v)
+                    } else {
+                        None
+                    }
+                }
+            )*
+        }
+    };
+}
diff --git a/keystore2/src/ec_crypto.rs b/keystore2/src/ec_crypto.rs
new file mode 100644
index 0000000..0425d4a
--- /dev/null
+++ b/keystore2/src/ec_crypto.rs
@@ -0,0 +1,137 @@
+// 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.
+
+//! Implement ECDH-based encryption.
+
+use anyhow::{Context, Result};
+use keystore2_crypto::{
+    aes_gcm_decrypt, aes_gcm_encrypt, ec_key_generate_key, ec_key_get0_public_key,
+    ec_key_marshal_private_key, ec_key_parse_private_key, ec_point_oct_to_point,
+    ec_point_point_to_oct, ecdh_compute_key, generate_salt, hkdf_expand, hkdf_extract, ECKey, ZVec,
+    AES_256_KEY_LENGTH,
+};
+
+/// Private key for ECDH encryption.
+pub struct ECDHPrivateKey(ECKey);
+
+impl ECDHPrivateKey {
+    /// Randomly generate a fresh keypair.
+    pub fn generate() -> Result<ECDHPrivateKey> {
+        ec_key_generate_key()
+            .map(ECDHPrivateKey)
+            .context("In ECDHPrivateKey::generate: generation failed")
+    }
+
+    /// Deserialize bytes into an ECDH keypair
+    pub fn from_private_key(buf: &[u8]) -> Result<ECDHPrivateKey> {
+        ec_key_parse_private_key(buf)
+            .map(ECDHPrivateKey)
+            .context("In ECDHPrivateKey::from_private_key: parsing failed")
+    }
+
+    /// Serialize the ECDH key into bytes
+    pub fn private_key(&self) -> Result<ZVec> {
+        ec_key_marshal_private_key(&self.0)
+            .context("In ECDHPrivateKey::private_key: marshalling failed")
+    }
+
+    /// Generate the serialization of the corresponding public key
+    pub fn public_key(&self) -> Result<Vec<u8>> {
+        let point = ec_key_get0_public_key(&self.0);
+        ec_point_point_to_oct(point.get_point())
+            .context("In ECDHPrivateKey::public_key: marshalling failed")
+    }
+
+    /// Use ECDH to agree an AES key with another party whose public key we have.
+    /// Sender and recipient public keys are passed separately because they are
+    /// switched in encryption vs decryption.
+    fn agree_key(
+        &self,
+        salt: &[u8],
+        other_public_key: &[u8],
+        sender_public_key: &[u8],
+        recipient_public_key: &[u8],
+    ) -> Result<ZVec> {
+        let hkdf = hkdf_extract(sender_public_key, salt)
+            .context("In ECDHPrivateKey::agree_key: hkdf_extract on sender_public_key failed")?;
+        let hkdf = hkdf_extract(recipient_public_key, &hkdf)
+            .context("In ECDHPrivateKey::agree_key: hkdf_extract on recipient_public_key failed")?;
+        let other_public_key = ec_point_oct_to_point(other_public_key)
+            .context("In ECDHPrivateKey::agree_key: ec_point_oct_to_point failed")?;
+        let secret = ecdh_compute_key(other_public_key.get_point(), &self.0)
+            .context("In ECDHPrivateKey::agree_key: ecdh_compute_key failed")?;
+        let prk = hkdf_extract(&secret, &hkdf)
+            .context("In ECDHPrivateKey::agree_key: hkdf_extract on secret failed")?;
+
+        let aes_key = hkdf_expand(AES_256_KEY_LENGTH, &prk, b"AES-256-GCM key")
+            .context("In ECDHPrivateKey::agree_key: hkdf_expand failed")?;
+        Ok(aes_key)
+    }
+
+    /// Encrypt a message to the party with the given public key
+    pub fn encrypt_message(
+        recipient_public_key: &[u8],
+        message: &[u8],
+    ) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>)> {
+        let sender_key =
+            Self::generate().context("In ECDHPrivateKey::encrypt_message: generate failed")?;
+        let sender_public_key = sender_key
+            .public_key()
+            .context("In ECDHPrivateKey::encrypt_message: public_key failed")?;
+        let salt =
+            generate_salt().context("In ECDHPrivateKey::encrypt_message: generate_salt failed")?;
+        let aes_key = sender_key
+            .agree_key(&salt, recipient_public_key, &sender_public_key, recipient_public_key)
+            .context("In ECDHPrivateKey::encrypt_message: agree_key failed")?;
+        let (ciphertext, iv, tag) = aes_gcm_encrypt(message, &aes_key)
+            .context("In ECDHPrivateKey::encrypt_message: aes_gcm_encrypt failed")?;
+        Ok((sender_public_key, salt, iv, ciphertext, tag))
+    }
+
+    /// Decrypt a message sent to us
+    pub fn decrypt_message(
+        &self,
+        sender_public_key: &[u8],
+        salt: &[u8],
+        iv: &[u8],
+        ciphertext: &[u8],
+        tag: &[u8],
+    ) -> Result<ZVec> {
+        let recipient_public_key = self.public_key()?;
+        let aes_key = self
+            .agree_key(salt, sender_public_key, sender_public_key, &recipient_public_key)
+            .context("In ECDHPrivateKey::decrypt_message: agree_key failed")?;
+        aes_gcm_decrypt(ciphertext, iv, tag, &aes_key)
+            .context("In ECDHPrivateKey::decrypt_message: aes_gcm_decrypt failed")
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_crypto_roundtrip() -> Result<()> {
+        let message = b"Hello world";
+        let recipient = ECDHPrivateKey::generate()?;
+        let (sender_public_key, salt, iv, ciphertext, tag) =
+            ECDHPrivateKey::encrypt_message(&recipient.public_key()?, message)?;
+        let recipient = ECDHPrivateKey::from_private_key(&recipient.private_key()?)?;
+        let decrypted =
+            recipient.decrypt_message(&sender_public_key, &salt, &iv, &ciphertext, &tag)?;
+        let dc: &[u8] = &decrypted;
+        assert_eq!(message, dc);
+        Ok(())
+    }
+}
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
new file mode 100644
index 0000000..04d1f77
--- /dev/null
+++ b/keystore2/src/enforcements.rs
@@ -0,0 +1,868 @@
+// 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.
+
+//! This is the Keystore 2.0 Enforcements module.
+// TODO: more description to follow.
+use crate::error::{map_binder_status, Error, ErrorCode};
+use crate::globals::{get_timestamp_service, ASYNC_TASK, DB, ENFORCEMENTS};
+use crate::key_parameter::{KeyParameter, KeyParameterValue};
+use crate::{authorization::Error as AuthzError, super_key::SuperEncryptionType};
+use crate::{
+    database::{AuthTokenEntry, MonotonicRawTime},
+    globals::SUPER_KEY,
+};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
+    HardwareAuthenticatorType::HardwareAuthenticatorType,
+    KeyParameter::KeyParameter as KmKeyParameter, KeyPurpose::KeyPurpose, Tag::Tag,
+};
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
+    ISecureClock::ISecureClock, TimeStampToken::TimeStampToken,
+};
+use android_security_authorization::aidl::android::security::authorization::ResponseCode::ResponseCode as AuthzResponseCode;
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, IKeystoreSecurityLevel::KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING,
+    OperationChallenge::OperationChallenge,
+};
+use android_system_keystore2::binder::Strong;
+use anyhow::{Context, Result};
+use std::{
+    collections::{HashMap, HashSet},
+    sync::{
+        mpsc::{channel, Receiver, Sender, TryRecvError},
+        Arc, Mutex, Weak,
+    },
+    time::SystemTime,
+};
+
+#[derive(Debug)]
+enum AuthRequestState {
+    /// An outstanding per operation authorization request.
+    OpAuth,
+    /// An outstanding request for per operation authorization and secure timestamp.
+    TimeStampedOpAuth(Receiver<Result<TimeStampToken, Error>>),
+    /// An outstanding request for a timestamp token.
+    TimeStamp(Receiver<Result<TimeStampToken, Error>>),
+}
+
+#[derive(Debug)]
+struct AuthRequest {
+    state: AuthRequestState,
+    /// This need to be set to Some to fulfill a AuthRequestState::OpAuth or
+    /// AuthRequestState::TimeStampedOpAuth.
+    hat: Mutex<Option<HardwareAuthToken>>,
+}
+
+unsafe impl Sync for AuthRequest {}
+
+impl AuthRequest {
+    fn op_auth() -> Arc<Self> {
+        Arc::new(Self { state: AuthRequestState::OpAuth, hat: Mutex::new(None) })
+    }
+
+    fn timestamped_op_auth(receiver: Receiver<Result<TimeStampToken, Error>>) -> Arc<Self> {
+        Arc::new(Self {
+            state: AuthRequestState::TimeStampedOpAuth(receiver),
+            hat: Mutex::new(None),
+        })
+    }
+
+    fn timestamp(
+        hat: HardwareAuthToken,
+        receiver: Receiver<Result<TimeStampToken, Error>>,
+    ) -> Arc<Self> {
+        Arc::new(Self { state: AuthRequestState::TimeStamp(receiver), hat: Mutex::new(Some(hat)) })
+    }
+
+    fn add_auth_token(&self, hat: HardwareAuthToken) {
+        *self.hat.lock().unwrap() = Some(hat)
+    }
+
+    fn get_auth_tokens(&self) -> Result<(HardwareAuthToken, Option<TimeStampToken>)> {
+        let hat = self
+            .hat
+            .lock()
+            .unwrap()
+            .take()
+            .ok_or(Error::Km(ErrorCode::KEY_USER_NOT_AUTHENTICATED))
+            .context("In get_auth_tokens: No operation auth token received.")?;
+
+        let tst = match &self.state {
+            AuthRequestState::TimeStampedOpAuth(recv) | AuthRequestState::TimeStamp(recv) => {
+                let result = recv.recv().context("In get_auth_tokens: Sender disconnected.")?;
+                Some(result.context(concat!(
+                    "In get_auth_tokens: Worker responded with error ",
+                    "from generating timestamp token."
+                ))?)
+            }
+            AuthRequestState::OpAuth => None,
+        };
+        Ok((hat, tst))
+    }
+}
+
+/// DeferredAuthState describes how auth tokens and timestamp tokens need to be provided when
+/// updating and finishing an operation.
+#[derive(Debug)]
+enum DeferredAuthState {
+    /// Used when an operation does not require further authorization.
+    NoAuthRequired,
+    /// Indicates that the operation requires an operation specific token. This means we have
+    /// to return an operation challenge to the client which should reward us with an
+    /// operation specific auth token. If it is not provided before the client calls update
+    /// or finish, the operation fails as not authorized.
+    OpAuthRequired,
+    /// Indicates that the operation requires a time stamp token. The auth token was already
+    /// loaded from the database, but it has to be accompanied by a time stamp token to inform
+    /// the target KM with a different clock about the time on the authenticators.
+    TimeStampRequired(HardwareAuthToken),
+    /// Indicates that both an operation bound auth token and a verification token are
+    /// before the operation can commence.
+    TimeStampedOpAuthRequired,
+    /// In this state the auth info is waiting for the deferred authorizations to come in.
+    /// We block on timestamp tokens, because we can always make progress on these requests.
+    /// The per-op auth tokens might never come, which means we fail if the client calls
+    /// update or finish before we got a per-op auth token.
+    Waiting(Arc<AuthRequest>),
+    /// In this state we have gotten all of the required tokens, we just cache them to
+    /// be used when the operation progresses.
+    Token(HardwareAuthToken, Option<TimeStampToken>),
+}
+
+/// Auth info hold all of the authorization related information of an operation. It is stored
+/// in and owned by the operation. It is constructed by authorize_create and stays with the
+/// operation until it completes.
+#[derive(Debug)]
+pub struct AuthInfo {
+    state: DeferredAuthState,
+    /// An optional key id required to update the usage count if the key usage is limited.
+    key_usage_limited: Option<i64>,
+    confirmation_token_receiver: Option<Arc<Mutex<Option<Receiver<Vec<u8>>>>>>,
+}
+
+struct TokenReceiverMap {
+    /// The map maps an outstanding challenge to a TokenReceiver. If an incoming Hardware Auth
+    /// Token (HAT) has the map key in its challenge field, it gets passed to the TokenReceiver
+    /// and the entry is removed from the map. In the case where no HAT is received before the
+    /// corresponding operation gets dropped, the entry goes stale. So every time the cleanup
+    /// counter (second field in the tuple) turns 0, the map is cleaned from stale entries.
+    /// The cleanup counter is decremented every time a new receiver is added.
+    /// and reset to TokenReceiverMap::CLEANUP_PERIOD + 1 after each cleanup.
+    map_and_cleanup_counter: Mutex<(HashMap<i64, TokenReceiver>, u8)>,
+}
+
+impl Default for TokenReceiverMap {
+    fn default() -> Self {
+        Self { map_and_cleanup_counter: Mutex::new((HashMap::new(), Self::CLEANUP_PERIOD + 1)) }
+    }
+}
+
+impl TokenReceiverMap {
+    /// There is a chance that receivers may become stale because their operation is dropped
+    /// without ever being authorized. So occasionally we iterate through the map and throw
+    /// out obsolete entries.
+    /// This is the number of calls to add_receiver between cleanups.
+    const CLEANUP_PERIOD: u8 = 25;
+
+    pub fn add_auth_token(&self, hat: HardwareAuthToken) {
+        let recv = {
+            // Limit the scope of the mutex guard, so that it is not held while the auth token is
+            // added.
+            let mut map = self.map_and_cleanup_counter.lock().unwrap();
+            let (ref mut map, _) = *map;
+            map.remove_entry(&hat.challenge)
+        };
+
+        if let Some((_, recv)) = recv {
+            recv.add_auth_token(hat);
+        }
+    }
+
+    pub fn add_receiver(&self, challenge: i64, recv: TokenReceiver) {
+        let mut map = self.map_and_cleanup_counter.lock().unwrap();
+        let (ref mut map, ref mut cleanup_counter) = *map;
+        map.insert(challenge, recv);
+
+        *cleanup_counter -= 1;
+        if *cleanup_counter == 0 {
+            map.retain(|_, v| !v.is_obsolete());
+            map.shrink_to_fit();
+            *cleanup_counter = Self::CLEANUP_PERIOD + 1;
+        }
+    }
+}
+
+#[derive(Debug)]
+struct TokenReceiver(Weak<AuthRequest>);
+
+impl TokenReceiver {
+    fn is_obsolete(&self) -> bool {
+        self.0.upgrade().is_none()
+    }
+
+    fn add_auth_token(&self, hat: HardwareAuthToken) {
+        if let Some(state_arc) = self.0.upgrade() {
+            state_arc.add_auth_token(hat);
+        }
+    }
+}
+
+fn get_timestamp_token(challenge: i64) -> Result<TimeStampToken, Error> {
+    let dev: Strong<dyn ISecureClock> = get_timestamp_service()
+        .expect(concat!(
+            "Secure Clock service must be present ",
+            "if TimeStampTokens are required."
+        ))
+        .get_interface()
+        .expect("Fatal: Timestamp service does not implement ISecureClock.");
+    map_binder_status(dev.generateTimeStamp(challenge))
+}
+
+fn timestamp_token_request(challenge: i64, sender: Sender<Result<TimeStampToken, Error>>) {
+    if let Err(e) = sender.send(get_timestamp_token(challenge)) {
+        log::info!(
+            concat!(
+                "In timestamp_token_request: Receiver hung up ",
+                "before timestamp token could be delivered. {:?}"
+            ),
+            e
+        );
+    }
+}
+
+impl AuthInfo {
+    /// This function gets called after an operation was successfully created.
+    /// It makes all the preparations required, so that the operation has all the authentication
+    /// related artifacts to advance on update and finish.
+    pub fn finalize_create_authorization(&mut self, challenge: i64) -> Option<OperationChallenge> {
+        match &self.state {
+            DeferredAuthState::OpAuthRequired => {
+                let auth_request = AuthRequest::op_auth();
+                let token_receiver = TokenReceiver(Arc::downgrade(&auth_request));
+                ENFORCEMENTS.register_op_auth_receiver(challenge, token_receiver);
+
+                self.state = DeferredAuthState::Waiting(auth_request);
+                Some(OperationChallenge { challenge })
+            }
+            DeferredAuthState::TimeStampedOpAuthRequired => {
+                let (sender, receiver) = channel::<Result<TimeStampToken, Error>>();
+                let auth_request = AuthRequest::timestamped_op_auth(receiver);
+                let token_receiver = TokenReceiver(Arc::downgrade(&auth_request));
+                ENFORCEMENTS.register_op_auth_receiver(challenge, token_receiver);
+
+                ASYNC_TASK.queue_hi(move |_| timestamp_token_request(challenge, sender));
+                self.state = DeferredAuthState::Waiting(auth_request);
+                Some(OperationChallenge { challenge })
+            }
+            DeferredAuthState::TimeStampRequired(hat) => {
+                let hat = (*hat).clone();
+                let (sender, receiver) = channel::<Result<TimeStampToken, Error>>();
+                let auth_request = AuthRequest::timestamp(hat, receiver);
+                ASYNC_TASK.queue_hi(move |_| timestamp_token_request(challenge, sender));
+                self.state = DeferredAuthState::Waiting(auth_request);
+                None
+            }
+            _ => None,
+        }
+    }
+
+    /// This function is the authorization hook called before operation update.
+    /// It returns the auth tokens required by the operation to commence update.
+    pub fn before_update(&mut self) -> Result<(Option<HardwareAuthToken>, Option<TimeStampToken>)> {
+        self.get_auth_tokens()
+    }
+
+    /// This function is the authorization hook called before operation finish.
+    /// It returns the auth tokens required by the operation to commence finish.
+    /// The third token is a confirmation token.
+    pub fn before_finish(
+        &mut self,
+    ) -> Result<(Option<HardwareAuthToken>, Option<TimeStampToken>, Option<Vec<u8>>)> {
+        let mut confirmation_token: Option<Vec<u8>> = None;
+        if let Some(ref confirmation_token_receiver) = self.confirmation_token_receiver {
+            let locked_receiver = confirmation_token_receiver.lock().unwrap();
+            if let Some(ref receiver) = *locked_receiver {
+                loop {
+                    match receiver.try_recv() {
+                        // As long as we get tokens we loop and discard all but the most
+                        // recent one.
+                        Ok(t) => confirmation_token = Some(t),
+                        Err(TryRecvError::Empty) => break,
+                        Err(TryRecvError::Disconnected) => {
+                            log::error!(concat!(
+                                "We got disconnected from the APC service, ",
+                                "this should never happen."
+                            ));
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        self.get_auth_tokens().map(|(hat, tst)| (hat, tst, confirmation_token))
+    }
+
+    /// This function is the authorization hook called after finish succeeded.
+    /// As of this writing it checks if the key was a limited use key. If so it updates the
+    /// use counter of the key in the database. When the use counter is depleted, the key gets
+    /// marked for deletion and the garbage collector is notified.
+    pub fn after_finish(&self) -> Result<()> {
+        if let Some(key_id) = self.key_usage_limited {
+            // On the last successful use, the key gets deleted. In this case we
+            // have to notify the garbage collector.
+            DB.with(|db| {
+                db.borrow_mut()
+                    .check_and_update_key_usage_count(key_id)
+                    .context("Trying to update key usage count.")
+            })
+            .context("In after_finish.")?;
+        }
+        Ok(())
+    }
+
+    /// This function returns the auth tokens as needed by the ongoing operation or fails
+    /// with ErrorCode::KEY_USER_NOT_AUTHENTICATED. If this was called for the first time
+    /// after a deferred authorization was requested by finalize_create_authorization, this
+    /// function may block on the generation of a time stamp token. It then moves the
+    /// tokens into the DeferredAuthState::Token state for future use.
+    fn get_auth_tokens(&mut self) -> Result<(Option<HardwareAuthToken>, Option<TimeStampToken>)> {
+        let deferred_tokens = if let DeferredAuthState::Waiting(ref auth_request) = self.state {
+            Some(auth_request.get_auth_tokens().context("In AuthInfo::get_auth_tokens.")?)
+        } else {
+            None
+        };
+
+        if let Some((hat, tst)) = deferred_tokens {
+            self.state = DeferredAuthState::Token(hat, tst);
+        }
+
+        match &self.state {
+            DeferredAuthState::NoAuthRequired => Ok((None, None)),
+            DeferredAuthState::Token(hat, tst) => Ok((Some((*hat).clone()), (*tst).clone())),
+            DeferredAuthState::OpAuthRequired
+            | DeferredAuthState::TimeStampedOpAuthRequired
+            | DeferredAuthState::TimeStampRequired(_) => {
+                Err(Error::Km(ErrorCode::KEY_USER_NOT_AUTHENTICATED)).context(concat!(
+                    "In AuthInfo::get_auth_tokens: No operation auth token requested??? ",
+                    "This should not happen."
+                ))
+            }
+            // This should not be reachable, because it should have been handled above.
+            DeferredAuthState::Waiting(_) => {
+                Err(Error::sys()).context("In AuthInfo::get_auth_tokens: Cannot be reached.")
+            }
+        }
+    }
+}
+
+/// Enforcements data structure
+#[derive(Default)]
+pub struct Enforcements {
+    /// This hash set contains the user ids for whom the device is currently unlocked. If a user id
+    /// is not in the set, it implies that the device is locked for the user.
+    device_unlocked_set: Mutex<HashSet<i32>>,
+    /// This field maps outstanding auth challenges to their operations. When an auth token
+    /// with the right challenge is received it is passed to the map using
+    /// TokenReceiverMap::add_auth_token() which removes the entry from the map. If an entry goes
+    /// stale, because the operation gets dropped before an auth token is received, the map
+    /// is cleaned up in regular intervals.
+    op_auth_map: TokenReceiverMap,
+    /// The enforcement module will try to get a confirmation token from this channel whenever
+    /// an operation that requires confirmation finishes.
+    confirmation_token_receiver: Arc<Mutex<Option<Receiver<Vec<u8>>>>>,
+}
+
+impl Enforcements {
+    /// Install the confirmation token receiver. The enforcement module will try to get a
+    /// confirmation token from this channel whenever an operation that requires confirmation
+    /// finishes.
+    pub fn install_confirmation_token_receiver(
+        &self,
+        confirmation_token_receiver: Receiver<Vec<u8>>,
+    ) {
+        *self.confirmation_token_receiver.lock().unwrap() = Some(confirmation_token_receiver);
+    }
+
+    /// Checks if a create call is authorized, given key parameters and operation parameters.
+    /// It returns an optional immediate auth token which can be presented to begin, and an
+    /// AuthInfo object which stays with the authorized operation and is used to obtain
+    /// auth tokens and timestamp tokens as required by the operation.
+    /// With regard to auth tokens, the following steps are taken:
+    ///
+    /// If no key parameters are given (typically when the client is self managed
+    /// (see Domain.Blob)) nothing is enforced.
+    /// If the key is time-bound, find a matching auth token from the database.
+    /// If the above step is successful, and if requires_timestamp is given, the returned
+    /// AuthInfo will provide a Timestamp token as appropriate.
+    pub fn authorize_create(
+        &self,
+        purpose: KeyPurpose,
+        key_properties: Option<&(i64, Vec<KeyParameter>)>,
+        op_params: &[KmKeyParameter],
+        requires_timestamp: bool,
+    ) -> Result<(Option<HardwareAuthToken>, AuthInfo)> {
+        let (key_id, key_params) = match key_properties {
+            Some((key_id, key_params)) => (*key_id, key_params),
+            None => {
+                return Ok((
+                    None,
+                    AuthInfo {
+                        state: DeferredAuthState::NoAuthRequired,
+                        key_usage_limited: None,
+                        confirmation_token_receiver: None,
+                    },
+                ))
+            }
+        };
+
+        match purpose {
+            // Allow SIGN, DECRYPT for both symmetric and asymmetric keys.
+            KeyPurpose::SIGN | KeyPurpose::DECRYPT => {}
+            // Rule out WRAP_KEY purpose
+            KeyPurpose::WRAP_KEY => {
+                return Err(Error::Km(Ec::INCOMPATIBLE_PURPOSE))
+                    .context("In authorize_create: WRAP_KEY purpose is not allowed here.");
+            }
+            // Allow AGREE_KEY for EC keys only.
+            KeyPurpose::AGREE_KEY => {
+                for kp in key_params.iter() {
+                    if kp.get_tag() == Tag::ALGORITHM
+                        && *kp.key_parameter_value() != KeyParameterValue::Algorithm(Algorithm::EC)
+                    {
+                        return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE)).context(
+                            "In authorize_create: key agreement is only supported for EC keys.",
+                        );
+                    }
+                }
+            }
+            KeyPurpose::VERIFY | KeyPurpose::ENCRYPT => {
+                // We do not support ENCRYPT and VERIFY (the remaining two options of purpose) for
+                // asymmetric keys.
+                for kp in key_params.iter() {
+                    match *kp.key_parameter_value() {
+                        KeyParameterValue::Algorithm(Algorithm::RSA)
+                        | KeyParameterValue::Algorithm(Algorithm::EC) => {
+                            return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE)).context(
+                                "In authorize_create: public operations on asymmetric keys are not
+                                 supported.",
+                            );
+                        }
+                        _ => {}
+                    }
+                }
+            }
+            _ => {
+                return Err(Error::Km(Ec::UNSUPPORTED_PURPOSE))
+                    .context("In authorize_create: specified purpose is not supported.");
+            }
+        }
+        // The following variables are to record information from key parameters to be used in
+        // enforcements, when two or more such pieces of information are required for enforcements.
+        // There is only one additional variable than what legacy keystore has, but this helps
+        // reduce the number of for loops on key parameters from 3 to 1, compared to legacy keystore
+        let mut key_purpose_authorized: bool = false;
+        let mut user_auth_type: Option<HardwareAuthenticatorType> = None;
+        let mut no_auth_required: bool = false;
+        let mut caller_nonce_allowed = false;
+        let mut user_id: i32 = -1;
+        let mut user_secure_ids = Vec::<i64>::new();
+        let mut key_time_out: Option<i64> = None;
+        let mut allow_while_on_body = false;
+        let mut unlocked_device_required = false;
+        let mut key_usage_limited: Option<i64> = None;
+        let mut confirmation_token_receiver: Option<Arc<Mutex<Option<Receiver<Vec<u8>>>>>> = None;
+        let mut max_boot_level: Option<i32> = None;
+
+        // iterate through key parameters, recording information we need for authorization
+        // enforcements later, or enforcing authorizations in place, where applicable
+        for key_param in key_params.iter() {
+            match key_param.key_parameter_value() {
+                KeyParameterValue::NoAuthRequired => {
+                    no_auth_required = true;
+                }
+                KeyParameterValue::AuthTimeout(t) => {
+                    key_time_out = Some(*t as i64);
+                }
+                KeyParameterValue::HardwareAuthenticatorType(a) => {
+                    user_auth_type = Some(*a);
+                }
+                KeyParameterValue::KeyPurpose(p) => {
+                    // The following check has the effect of key_params.contains(purpose)
+                    // Also, authorizing purpose can not be completed here, if there can be multiple
+                    // key parameters for KeyPurpose.
+                    key_purpose_authorized = key_purpose_authorized || *p == purpose;
+                }
+                KeyParameterValue::CallerNonce => {
+                    caller_nonce_allowed = true;
+                }
+                KeyParameterValue::ActiveDateTime(a) => {
+                    if !Enforcements::is_given_time_passed(*a, true) {
+                        return Err(Error::Km(Ec::KEY_NOT_YET_VALID))
+                            .context("In authorize_create: key is not yet active.");
+                    }
+                }
+                KeyParameterValue::OriginationExpireDateTime(o) => {
+                    if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN)
+                        && Enforcements::is_given_time_passed(*o, false)
+                    {
+                        return Err(Error::Km(Ec::KEY_EXPIRED))
+                            .context("In authorize_create: key is expired.");
+                    }
+                }
+                KeyParameterValue::UsageExpireDateTime(u) => {
+                    if (purpose == KeyPurpose::DECRYPT || purpose == KeyPurpose::VERIFY)
+                        && Enforcements::is_given_time_passed(*u, false)
+                    {
+                        return Err(Error::Km(Ec::KEY_EXPIRED))
+                            .context("In authorize_create: key is expired.");
+                    }
+                }
+                KeyParameterValue::UserSecureID(s) => {
+                    user_secure_ids.push(*s);
+                }
+                KeyParameterValue::UserID(u) => {
+                    user_id = *u;
+                }
+                KeyParameterValue::UnlockedDeviceRequired => {
+                    unlocked_device_required = true;
+                }
+                KeyParameterValue::AllowWhileOnBody => {
+                    allow_while_on_body = true;
+                }
+                KeyParameterValue::UsageCountLimit(_) => {
+                    // We don't examine the limit here because this is enforced on finish.
+                    // Instead, we store the key_id so that finish can look up the key
+                    // in the database again and check and update the counter.
+                    key_usage_limited = Some(key_id);
+                }
+                KeyParameterValue::TrustedConfirmationRequired => {
+                    confirmation_token_receiver = Some(self.confirmation_token_receiver.clone());
+                }
+                KeyParameterValue::MaxBootLevel(level) => {
+                    max_boot_level = Some(*level);
+                }
+                // NOTE: as per offline discussion, sanitizing key parameters and rejecting
+                // create operation if any non-allowed tags are present, is not done in
+                // authorize_create (unlike in legacy keystore where AuthorizeBegin is rejected if
+                // a subset of non-allowed tags are present). Because sanitizing key parameters
+                // should have been done during generate/import key, by KeyMint.
+                _ => { /*Do nothing on all the other key parameters, as in legacy keystore*/ }
+            }
+        }
+
+        // authorize the purpose
+        if !key_purpose_authorized {
+            return Err(Error::Km(Ec::INCOMPATIBLE_PURPOSE))
+                .context("In authorize_create: the purpose is not authorized.");
+        }
+
+        // if both NO_AUTH_REQUIRED and USER_SECURE_ID tags are present, return error
+        if !user_secure_ids.is_empty() && no_auth_required {
+            return Err(Error::Km(Ec::INVALID_KEY_BLOB)).context(
+                "In authorize_create: key has both NO_AUTH_REQUIRED
+                and USER_SECURE_ID tags.",
+            );
+        }
+
+        // if either of auth_type or secure_id is present and the other is not present, return error
+        if (user_auth_type.is_some() && user_secure_ids.is_empty())
+            || (user_auth_type.is_none() && !user_secure_ids.is_empty())
+        {
+            return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
+                "In authorize_create: Auth required, but either auth type or secure ids
+                are not present.",
+            );
+        }
+
+        // validate caller nonce for origination purposes
+        if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN)
+            && !caller_nonce_allowed
+            && op_params.iter().any(|kp| kp.tag == Tag::NONCE)
+        {
+            return Err(Error::Km(Ec::CALLER_NONCE_PROHIBITED)).context(
+                "In authorize_create, NONCE is present,
+                    although CALLER_NONCE is not present",
+            );
+        }
+
+        if unlocked_device_required {
+            // check the device locked status. If locked, operations on the key are not
+            // allowed.
+            if self.is_device_locked(user_id) {
+                return Err(Error::Km(Ec::DEVICE_LOCKED))
+                    .context("In authorize_create: device is locked.");
+            }
+        }
+
+        if let Some(level) = max_boot_level {
+            if !SUPER_KEY.level_accessible(level) {
+                return Err(Error::Km(Ec::BOOT_LEVEL_EXCEEDED))
+                    .context("In authorize_create: boot level is too late.");
+            }
+        }
+
+        if !unlocked_device_required && no_auth_required {
+            return Ok((
+                None,
+                AuthInfo {
+                    state: DeferredAuthState::NoAuthRequired,
+                    key_usage_limited,
+                    confirmation_token_receiver,
+                },
+            ));
+        }
+
+        let has_sids = !user_secure_ids.is_empty();
+
+        let timeout_bound = key_time_out.is_some() && has_sids;
+
+        let per_op_bound = key_time_out.is_none() && has_sids;
+
+        let need_auth_token = timeout_bound || unlocked_device_required;
+
+        let hat_and_last_off_body = if need_auth_token {
+            let hat_and_last_off_body = Self::find_auth_token(|hat: &AuthTokenEntry| {
+                if let (Some(auth_type), true) = (user_auth_type, has_sids) {
+                    hat.satisfies(&user_secure_ids, auth_type)
+                } else {
+                    unlocked_device_required
+                }
+            })
+            .context("In authorize_create: Trying to get required auth token.")?;
+            Some(
+                hat_and_last_off_body
+                    .ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
+                    .context("In authorize_create: No suitable auth token found.")?,
+            )
+        } else {
+            None
+        };
+
+        // Now check the validity of the auth token if the key is timeout bound.
+        let hat = match (hat_and_last_off_body, key_time_out) {
+            (Some((hat, last_off_body)), Some(key_time_out)) => {
+                let now = MonotonicRawTime::now();
+                let token_age = now
+                    .checked_sub(&hat.time_received())
+                    .ok_or_else(Error::sys)
+                    .context(concat!(
+                        "In authorize_create: Overflow while computing Auth token validity. ",
+                        "Validity cannot be established."
+                    ))?;
+
+                let on_body_extended = allow_while_on_body && last_off_body < hat.time_received();
+
+                if token_age.seconds() > key_time_out && !on_body_extended {
+                    return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
+                        .context("In authorize_create: matching auth token is expired.");
+                }
+                Some(hat)
+            }
+            (Some((hat, _)), None) => Some(hat),
+            // If timeout_bound is true, above code must have retrieved a HAT or returned with
+            // KEY_USER_NOT_AUTHENTICATED. This arm should not be reachable.
+            (None, Some(_)) => panic!("Logical error."),
+            _ => None,
+        };
+
+        Ok(match (hat, requires_timestamp, per_op_bound) {
+            // Per-op-bound and Some(hat) can only happen if we are both per-op bound and unlocked
+            // device required. In addition, this KM instance needs a timestamp token.
+            // So the HAT cannot be presented on create. So on update/finish we present both
+            // an per-op-bound auth token and a timestamp token.
+            (Some(_), true, true) => (None, DeferredAuthState::TimeStampedOpAuthRequired),
+            (Some(hat), true, false) => (
+                Some(hat.auth_token().clone()),
+                DeferredAuthState::TimeStampRequired(hat.take_auth_token()),
+            ),
+            (Some(hat), false, true) => {
+                (Some(hat.take_auth_token()), DeferredAuthState::OpAuthRequired)
+            }
+            (Some(hat), false, false) => {
+                (Some(hat.take_auth_token()), DeferredAuthState::NoAuthRequired)
+            }
+            (None, _, true) => (None, DeferredAuthState::OpAuthRequired),
+            (None, _, false) => (None, DeferredAuthState::NoAuthRequired),
+        })
+        .map(|(hat, state)| {
+            (hat, AuthInfo { state, key_usage_limited, confirmation_token_receiver })
+        })
+    }
+
+    fn find_auth_token<F>(p: F) -> Result<Option<(AuthTokenEntry, MonotonicRawTime)>>
+    where
+        F: Fn(&AuthTokenEntry) -> bool,
+    {
+        DB.with(|db| {
+            let mut db = db.borrow_mut();
+            db.find_auth_token_entry(p).context("Trying to find auth token.")
+        })
+        .context("In find_auth_token.")
+    }
+
+    /// Checks if the time now since epoch is greater than (or equal, if is_given_time_inclusive is
+    /// set) the given time (in milliseconds)
+    fn is_given_time_passed(given_time: i64, is_given_time_inclusive: bool) -> bool {
+        let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH);
+
+        let time_since_epoch = match duration_since_epoch {
+            Ok(duration) => duration.as_millis(),
+            Err(_) => return false,
+        };
+
+        if is_given_time_inclusive {
+            time_since_epoch >= (given_time as u128)
+        } else {
+            time_since_epoch > (given_time as u128)
+        }
+    }
+
+    /// Check if the device is locked for the given user. If there's no entry yet for the user,
+    /// we assume that the device is locked
+    fn is_device_locked(&self, user_id: i32) -> bool {
+        // unwrap here because there's no way this mutex guard can be poisoned and
+        // because there's no way to recover, even if it is poisoned.
+        let set = self.device_unlocked_set.lock().unwrap();
+        !set.contains(&user_id)
+    }
+
+    /// Sets the device locked status for the user. This method is called externally.
+    pub fn set_device_locked(&self, user_id: i32, device_locked_status: bool) {
+        // unwrap here because there's no way this mutex guard can be poisoned and
+        // because there's no way to recover, even if it is poisoned.
+        let mut set = self.device_unlocked_set.lock().unwrap();
+        if device_locked_status {
+            set.remove(&user_id);
+        } else {
+            set.insert(user_id);
+        }
+    }
+
+    /// Add this auth token to the database.
+    /// Then present the auth token to the op auth map. If an operation is waiting for this
+    /// auth token this fulfills the request and removes the receiver from the map.
+    pub fn add_auth_token(&self, hat: HardwareAuthToken) -> Result<()> {
+        DB.with(|db| db.borrow_mut().insert_auth_token(&hat)).context("In add_auth_token.")?;
+
+        self.op_auth_map.add_auth_token(hat);
+        Ok(())
+    }
+
+    /// This allows adding an entry to the op_auth_map, indexed by the operation challenge.
+    /// This is to be called by create_operation, once it has received the operation challenge
+    /// from keymint for an operation whose authorization decision is OpAuthRequired, as signalled
+    /// by the DeferredAuthState.
+    fn register_op_auth_receiver(&self, challenge: i64, recv: TokenReceiver) {
+        self.op_auth_map.add_receiver(challenge, recv);
+    }
+
+    /// Given the set of key parameters and flags, check if super encryption is required.
+    pub fn super_encryption_required(
+        domain: &Domain,
+        key_parameters: &[KeyParameter],
+        flags: Option<i32>,
+    ) -> SuperEncryptionType {
+        if let Some(flags) = flags {
+            if (flags & KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING) != 0 {
+                return SuperEncryptionType::None;
+            }
+        }
+        // Each answer has a priority, numerically largest priority wins.
+        struct Candidate {
+            priority: u32,
+            enc_type: SuperEncryptionType,
+        }
+        let mut result = Candidate { priority: 0, enc_type: SuperEncryptionType::None };
+        for kp in key_parameters {
+            let t = match kp.key_parameter_value() {
+                KeyParameterValue::MaxBootLevel(level) => {
+                    Candidate { priority: 3, enc_type: SuperEncryptionType::BootLevel(*level) }
+                }
+                KeyParameterValue::UnlockedDeviceRequired if *domain == Domain::APP => {
+                    Candidate { priority: 2, enc_type: SuperEncryptionType::ScreenLockBound }
+                }
+                KeyParameterValue::UserSecureID(_) if *domain == Domain::APP => {
+                    Candidate { priority: 1, enc_type: SuperEncryptionType::LskfBound }
+                }
+                _ => Candidate { priority: 0, enc_type: SuperEncryptionType::None },
+            };
+            if t.priority > result.priority {
+                result = t;
+            }
+        }
+        result.enc_type
+    }
+
+    /// Finds a matching auth token along with a timestamp token.
+    /// This method looks through auth-tokens cached by keystore which satisfy the given
+    /// authentication information (i.e. |secureUserId|).
+    /// The most recent matching auth token which has a |challenge| field which matches
+    /// the passed-in |challenge| parameter is returned.
+    /// In this case the |authTokenMaxAgeMillis| parameter is not used.
+    ///
+    /// Otherwise, the most recent matching auth token which is younger than |authTokenMaxAgeMillis|
+    /// is returned.
+    pub fn get_auth_tokens(
+        &self,
+        challenge: i64,
+        secure_user_id: i64,
+        auth_token_max_age_millis: i64,
+    ) -> Result<(HardwareAuthToken, TimeStampToken)> {
+        let auth_type = HardwareAuthenticatorType::ANY;
+        let sids: Vec<i64> = vec![secure_user_id];
+        // Filter the matching auth tokens by challenge
+        let result = Self::find_auth_token(|hat: &AuthTokenEntry| {
+            (challenge == hat.challenge()) && hat.satisfies(&sids, auth_type)
+        })
+        .context(
+            "In get_auth_tokens: Failed to get a matching auth token filtered by challenge.",
+        )?;
+
+        let auth_token = if let Some((auth_token_entry, _)) = result {
+            auth_token_entry.take_auth_token()
+        } else {
+            // Filter the matching auth tokens by age.
+            if auth_token_max_age_millis != 0 {
+                let now_in_millis = MonotonicRawTime::now().milli_seconds();
+                let result = Self::find_auth_token(|auth_token_entry: &AuthTokenEntry| {
+                    let token_valid = now_in_millis
+                        .checked_sub(auth_token_entry.time_received().milli_seconds())
+                        .map_or(false, |token_age_in_millis| {
+                            auth_token_max_age_millis > token_age_in_millis
+                        });
+                    token_valid && auth_token_entry.satisfies(&sids, auth_type)
+                })
+                .context(
+                    "In get_auth_tokens: Failed to get a matching auth token filtered by age.",
+                )?;
+
+                if let Some((auth_token_entry, _)) = result {
+                    auth_token_entry.take_auth_token()
+                } else {
+                    return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND))
+                        .context("In get_auth_tokens: No auth token found.");
+                }
+            } else {
+                return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND))
+                    .context("In get_auth_tokens: Passed-in auth token max age is zero.");
+            }
+        };
+        // Wait and obtain the timestamp token from secure clock service.
+        let tst = get_timestamp_token(challenge)
+            .context("In get_auth_tokens. Error in getting timestamp token.")?;
+        Ok((auth_token, tst))
+    }
+}
+
+// TODO: Add tests to enforcement module (b/175578618).
diff --git a/keystore2/src/entropy.rs b/keystore2/src/entropy.rs
new file mode 100644
index 0000000..de38187
--- /dev/null
+++ b/keystore2/src/entropy.rs
@@ -0,0 +1,98 @@
+// 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.
+
+//! This module holds functionality for retrieving and distributing entropy.
+
+use anyhow::{Context, Result};
+use log::error;
+use std::time::{Duration, Instant};
+
+static ENTROPY_SIZE: usize = 64;
+static MIN_FEED_INTERVAL_SECS: u64 = 30;
+
+#[derive(Default)]
+struct FeederInfo {
+    last_feed: Option<Instant>,
+}
+
+/// Register the entropy feeder as an idle callback.
+pub fn register_feeder() {
+    crate::globals::ASYNC_TASK.add_idle(|shelf| {
+        let mut info = shelf.get_mut::<FeederInfo>();
+        let now = Instant::now();
+        let feed_needed = match info.last_feed {
+            None => true,
+            Some(last) => now.duration_since(last) > Duration::from_secs(MIN_FEED_INTERVAL_SECS),
+        };
+        if feed_needed {
+            info.last_feed = Some(now);
+            feed_devices();
+        }
+    });
+}
+
+fn get_entropy(size: usize) -> Result<Vec<u8>> {
+    keystore2_crypto::generate_random_data(size).context("Retrieving entropy for KeyMint device")
+}
+
+/// Feed entropy to all known KeyMint devices.
+pub fn feed_devices() {
+    let km_devs = crate::globals::get_keymint_devices();
+    if km_devs.is_empty() {
+        return;
+    }
+    let data = match get_entropy(km_devs.len() * ENTROPY_SIZE) {
+        Ok(data) => data,
+        Err(e) => {
+            error!(
+                "Failed to retrieve {}*{} bytes of entropy: {:?}",
+                km_devs.len(),
+                ENTROPY_SIZE,
+                e
+            );
+            return;
+        }
+    };
+    for (i, km_dev) in km_devs.iter().enumerate() {
+        let offset = i * ENTROPY_SIZE;
+        let sub_data = &data[offset..(offset + ENTROPY_SIZE)];
+        if let Err(e) = km_dev.addRngEntropy(sub_data) {
+            error!("Failed to feed entropy to KeyMint device: {:?}", e);
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use std::collections::HashSet;
+
+    #[test]
+    fn test_entropy_size() {
+        for size in &[0, 1, 4, 8, 256, 4096] {
+            let data = get_entropy(*size).expect("failed to get entropy");
+            assert_eq!(data.len(), *size);
+        }
+    }
+    #[test]
+    fn test_entropy_uniqueness() {
+        let count = 10;
+        let mut seen = HashSet::new();
+        for _i in 0..count {
+            let data = get_entropy(16).expect("failed to get entropy");
+            seen.insert(data);
+        }
+        assert_eq!(seen.len(), count);
+    }
+}
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
new file mode 100644
index 0000000..f969cb6
--- /dev/null
+++ b/keystore2/src/error.rs
@@ -0,0 +1,399 @@
+// 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.
+
+//! Keystore error provides convenience methods and types for Keystore error handling.
+//! Clients of Keystore expect one of two error codes, i.e., a Keystore ResponseCode as
+//! defined by the Keystore AIDL interface, or a Keymint ErrorCode as defined by
+//! the Keymint HAL specification.
+//! This crate provides `Error` which can wrap both. It is to be used
+//! internally by Keystore to diagnose error conditions that need to be reported to
+//! the client. To report the error condition to the client the Keystore AIDL
+//! interface defines a wire type `Result` which is distinctly different from Rust's
+//! `enum Result<T,E>`.
+//!
+//! This crate provides the convenience method `map_or_log_err` to convert `anyhow::Error`
+//! into this wire type. In addition to handling the conversion of `Error`
+//! to the `Result` wire type it handles any other error by mapping it to
+//! `ResponseCode::SYSTEM_ERROR` and logs any error condition.
+//!
+//! Keystore functions should use `anyhow::Result` to return error conditions, and
+//! context should be added every time an error is forwarded.
+
+pub use android_hardware_security_keymint::aidl::android::hardware::security::keymint::ErrorCode::ErrorCode;
+pub use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
+use android_system_keystore2::binder::{
+    ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode,
+};
+use keystore2_selinux as selinux;
+use std::cmp::PartialEq;
+
+/// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
+/// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
+#[derive(Debug, thiserror::Error, PartialEq)]
+pub enum Error {
+    /// Wraps a Keystore `ResponseCode` as defined by the Keystore AIDL interface specification.
+    #[error("Error::Rc({0:?})")]
+    Rc(ResponseCode),
+    /// Wraps a Keymint `ErrorCode` as defined by the Keymint AIDL interface specification.
+    #[error("Error::Km({0:?})")]
+    Km(ErrorCode),
+    /// Wraps a Binder exception code other than a service specific exception.
+    #[error("Binder exception code {0:?}, {1:?}")]
+    Binder(ExceptionCode, i32),
+    /// 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 {
+    /// Short hand for `Error::Rc(ResponseCode::SYSTEM_ERROR)`
+    pub fn sys() -> Self {
+        Error::Rc(ResponseCode::SYSTEM_ERROR)
+    }
+
+    /// Short hand for `Error::Rc(ResponseCode::PERMISSION_DENIED`
+    pub fn perm() -> Self {
+        Error::Rc(ResponseCode::PERMISSION_DENIED)
+    }
+}
+
+/// Helper function to map the binder status we get from calls into KeyMint
+/// to a Keystore Error. We don't create an anyhow error here to make
+/// it easier to evaluate KeyMint errors, which we must do in some cases, e.g.,
+/// when diagnosing authentication requirements, update requirements, and running
+/// out of operation slots.
+pub fn map_km_error<T>(r: BinderResult<T>) -> Result<T, Error> {
+    r.map_err(|s| {
+        match s.exception_code() {
+            ExceptionCode::SERVICE_SPECIFIC => {
+                let se = s.service_specific_error();
+                if se < 0 {
+                    // Negative service specific errors are KM error codes.
+                    Error::Km(ErrorCode(s.service_specific_error()))
+                } else {
+                    // Non negative error codes cannot be KM error codes.
+                    // So we create an `Error::Binder` variant to preserve
+                    // the service specific error code for logging.
+                    // `map_or_log_err` will map this on a system error,
+                    // but not before logging the details to logcat.
+                    Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
+                }
+            }
+            // We create `Error::Binder` to preserve the exception code
+            // for logging.
+            // `map_or_log_err` will map this on a system error.
+            e_code => Error::Binder(e_code, 0),
+        }
+    })
+}
+
+/// 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.
+pub fn map_binder_status<T>(r: BinderResult<T>) -> Result<T, Error> {
+    r.map_err(|s| match s.exception_code() {
+        ExceptionCode::SERVICE_SPECIFIC => {
+            let se = s.service_specific_error();
+            Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
+        }
+        ExceptionCode::TRANSACTION_FAILED => {
+            let e = s.transaction_error();
+            Error::BinderTransaction(e)
+        }
+        e_code => Error::Binder(e_code, 0),
+    })
+}
+
+/// This function maps a status code onto a Keystore Error.
+pub fn map_binder_status_code<T>(r: Result<T, StatusCode>) -> Result<T, Error> {
+    r.map_err(Error::BinderTransaction)
+}
+
+/// This function should be used by Keystore service calls to translate error conditions
+/// into service specific exceptions.
+///
+/// All error conditions get logged by this function, except for KEY_NOT_FOUND error.
+///
+/// All `Error::Rc(x)` and `Error::Km(x)` variants get mapped onto a service specific error
+/// code of x. This is possible because KeyMint `ErrorCode` errors are always negative and
+/// `ResponseCode` codes are always positive.
+/// `selinux::Error::PermissionDenied` is mapped on `ResponseCode::PERMISSION_DENIED`.
+///
+/// All non `Error` error conditions and the Error::Binder variant get mapped onto
+/// ResponseCode::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).
+///
+/// # Examples
+///
+/// ```
+/// fn loadKey() -> anyhow::Result<Vec<u8>> {
+///     if (good_but_auth_required) {
+///         Ok(vec!['k', 'e', 'y'])
+///     } else {
+///         Err(anyhow!(Error::Rc(ResponseCode::KEY_NOT_FOUND)))
+///     }
+/// }
+///
+/// map_or_log_err(loadKey(), Ok)
+/// ```
+pub fn map_or_log_err<T, U, F>(result: anyhow::Result<U>, handle_ok: F) -> BinderResult<T>
+where
+    F: FnOnce(U) -> BinderResult<T>,
+{
+    map_err_with(
+        result,
+        |e| {
+            // Make the key not found errors silent.
+            if !matches!(
+                e.root_cause().downcast_ref::<Error>(),
+                Some(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+            ) {
+                log::error!("{:?}", e);
+            }
+            e
+        },
+        handle_ok,
+    )
+}
+
+/// This function behaves similar to map_or_log_error, but it does not log the errors, instead
+/// it calls map_err on the error before mapping it to a binder result allowing callers to
+/// log or transform the error before mapping it.
+pub fn map_err_with<T, U, F1, F2>(
+    result: anyhow::Result<U>,
+    map_err: F1,
+    handle_ok: F2,
+) -> BinderResult<T>
+where
+    F1: FnOnce(anyhow::Error) -> anyhow::Error,
+    F2: FnOnce(U) -> BinderResult<T>,
+{
+    result.map_or_else(
+        |e| {
+            let e = map_err(e);
+            let rc = get_error_code(&e);
+            Err(BinderStatus::new_service_specific_error(rc, None))
+        },
+        handle_ok,
+    )
+}
+
+/// Returns the error code given a reference to the error
+pub fn get_error_code(e: &anyhow::Error) -> i32 {
+    let root_cause = e.root_cause();
+    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.
+        Some(Error::Binder(_, _)) | Some(Error::BinderTransaction(_)) => {
+            ResponseCode::SYSTEM_ERROR.0
+        }
+        None => match root_cause.downcast_ref::<selinux::Error>() {
+            Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
+            _ => ResponseCode::SYSTEM_ERROR.0,
+        },
+    }
+}
+
+#[cfg(test)]
+pub mod tests {
+
+    use super::*;
+    use android_system_keystore2::binder::{
+        ExceptionCode, Result as BinderResult, Status as BinderStatus,
+    };
+    use anyhow::{anyhow, Context};
+
+    fn nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
+        Err(anyhow!(Error::Rc(rc))).context("nested nested rc")
+    }
+
+    fn nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
+        nested_nested_rc(rc).context("nested rc")
+    }
+
+    fn nested_nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
+        Err(anyhow!(Error::Km(ec))).context("nested nested ec")
+    }
+
+    fn nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
+        nested_nested_ec(ec).context("nested ec")
+    }
+
+    fn nested_nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
+        Ok(rc)
+    }
+
+    fn nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
+        nested_nested_ok(rc).context("nested ok")
+    }
+
+    fn nested_nested_selinux_perm() -> anyhow::Result<()> {
+        Err(anyhow!(selinux::Error::perm())).context("nested nexted selinux permission denied")
+    }
+
+    fn nested_selinux_perm() -> anyhow::Result<()> {
+        nested_nested_selinux_perm().context("nested selinux permission denied")
+    }
+
+    #[derive(Debug, thiserror::Error)]
+    enum TestError {
+        #[error("TestError::Fail")]
+        Fail = 0,
+    }
+
+    fn nested_nested_other_error() -> anyhow::Result<()> {
+        Err(anyhow!(TestError::Fail)).context("nested nested other error")
+    }
+
+    fn nested_other_error() -> anyhow::Result<()> {
+        nested_nested_other_error().context("nested other error")
+    }
+
+    fn binder_sse_error(sse: i32) -> BinderResult<()> {
+        Err(BinderStatus::new_service_specific_error(sse, None))
+    }
+
+    fn binder_exception(ex: ExceptionCode) -> BinderResult<()> {
+        Err(BinderStatus::new_exception(ex, None))
+    }
+
+    #[test]
+    fn keystore_error_test() -> anyhow::Result<(), String> {
+        android_logger::init_once(
+            android_logger::Config::default()
+                .with_tag("keystore_error_tests")
+                .with_min_level(log::Level::Debug),
+        );
+        // All Error::Rc(x) get mapped on a service specific error
+        // code of x.
+        for rc in ResponseCode::LOCKED.0..ResponseCode::BACKEND_BUSY.0 {
+            assert_eq!(
+                Result::<(), i32>::Err(rc),
+                map_or_log_err(nested_rc(ResponseCode(rc)), |_| Err(BinderStatus::ok()))
+                    .map_err(|s| s.service_specific_error())
+            );
+        }
+
+        // All Keystore Error::Km(x) get mapped on a service
+        // specific error of x.
+        for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
+            assert_eq!(
+                Result::<(), i32>::Err(ec),
+                map_or_log_err(nested_ec(ErrorCode(ec)), |_| Err(BinderStatus::ok()))
+                    .map_err(|s| s.service_specific_error())
+            );
+        }
+
+        // All Keymint errors x received through a Binder Result get mapped on
+        // a service specific error of x.
+        for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
+            assert_eq!(
+                Result::<(), i32>::Err(ec),
+                map_or_log_err(
+                    map_km_error(binder_sse_error(ec))
+                        .with_context(|| format!("Km error code: {}.", ec)),
+                    |_| Err(BinderStatus::ok())
+                )
+                .map_err(|s| s.service_specific_error())
+            );
+        }
+
+        // map_km_error creates an Error::Binder variant storing
+        // ExceptionCode::SERVICE_SPECIFIC and the given
+        // service specific error.
+        let sse = map_km_error(binder_sse_error(1));
+        assert_eq!(Err(Error::Binder(ExceptionCode::SERVICE_SPECIFIC, 1)), sse);
+        // map_or_log_err then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
+        assert_eq!(
+            Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
+            map_or_log_err(sse.context("Non negative service specific error."), |_| Err(
+                BinderStatus::ok()
+            ))
+            .map_err(|s| ResponseCode(s.service_specific_error()))
+        );
+
+        // map_km_error creates a Error::Binder variant storing the given exception code.
+        let binder_exception = map_km_error(binder_exception(ExceptionCode::TRANSACTION_FAILED));
+        assert_eq!(Err(Error::Binder(ExceptionCode::TRANSACTION_FAILED, 0)), binder_exception);
+        // map_or_log_err then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
+        assert_eq!(
+            Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
+            map_or_log_err(binder_exception.context("Binder Exception."), |_| Err(
+                BinderStatus::ok()
+            ))
+            .map_err(|s| ResponseCode(s.service_specific_error()))
+        );
+
+        // selinux::Error::Perm() needs to be mapped to ResponseCode::PERMISSION_DENIED
+        assert_eq!(
+            Result::<(), ResponseCode>::Err(ResponseCode::PERMISSION_DENIED),
+            map_or_log_err(nested_selinux_perm(), |_| Err(BinderStatus::ok()))
+                .map_err(|s| ResponseCode(s.service_specific_error()))
+        );
+
+        // All other errors get mapped on System Error.
+        assert_eq!(
+            Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
+            map_or_log_err(nested_other_error(), |_| Err(BinderStatus::ok()))
+                .map_err(|s| ResponseCode(s.service_specific_error()))
+        );
+
+        // Result::Ok variants get passed to the ok handler.
+        assert_eq!(Ok(ResponseCode::LOCKED), map_or_log_err(nested_ok(ResponseCode::LOCKED), Ok));
+        assert_eq!(
+            Ok(ResponseCode::SYSTEM_ERROR),
+            map_or_log_err(nested_ok(ResponseCode::SYSTEM_ERROR), Ok)
+        );
+
+        Ok(())
+    }
+
+    //Helper function to test whether error cases are handled as expected.
+    pub fn check_result_contains_error_string<T>(
+        result: anyhow::Result<T>,
+        expected_error_string: &str,
+    ) {
+        let error_str = format!(
+            "{:#?}",
+            result.err().unwrap_or_else(|| panic!("Expected the error: {}", expected_error_string))
+        );
+        assert!(
+            error_str.contains(expected_error_string),
+            "The string \"{}\" should contain \"{}\"",
+            error_str,
+            expected_error_string
+        );
+    }
+} // mod tests
diff --git a/keystore2/src/gc.rs b/keystore2/src/gc.rs
new file mode 100644
index 0000000..2010c79
--- /dev/null
+++ b/keystore2/src/gc.rs
@@ -0,0 +1,152 @@
+// 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.
+
+//! This module implements the key garbage collector.
+//! The key garbage collector has one public function `notify_gc()`. This will create
+//! a thread on demand which will query the database for unreferenced key entries,
+//! optionally dispose of sensitive key material appropriately, and then delete
+//! the key entry from the database.
+
+use crate::{
+    async_task,
+    database::{BlobMetaData, KeystoreDB, Uuid},
+    super_key::SuperKeyManager,
+};
+use anyhow::{Context, Result};
+use async_task::AsyncTask;
+use std::sync::{
+    atomic::{AtomicU8, Ordering},
+    Arc,
+};
+
+pub struct Gc {
+    async_task: Arc<AsyncTask>,
+    notified: Arc<AtomicU8>,
+}
+
+impl Gc {
+    /// Creates a garbage collector using the given async_task.
+    /// The garbage collector needs a function to invalidate key blobs, a database connection,
+    /// and a reference to the `SuperKeyManager`. They are obtained from the init function.
+    /// The function is only called if this is first time a garbage collector was initialized
+    /// with the given AsyncTask instance.
+    /// Note: It is a logical error to initialize different Gc instances with the same `AsyncTask`.
+    pub fn new_init_with<F>(async_task: Arc<AsyncTask>, init: F) -> Self
+    where
+        F: FnOnce() -> (
+                Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
+                KeystoreDB,
+                Arc<SuperKeyManager>,
+            ) + Send
+            + 'static,
+    {
+        let weak_at = Arc::downgrade(&async_task);
+        let notified = Arc::new(AtomicU8::new(0));
+        let notified_clone = notified.clone();
+        // Initialize the task's shelf.
+        async_task.queue_hi(move |shelf| {
+            let (invalidate_key, db, super_key) = init();
+            let notified = notified_clone;
+            shelf.get_or_put_with(|| GcInternal {
+                deleted_blob_ids: vec![],
+                superseded_blobs: vec![],
+                invalidate_key,
+                db,
+                async_task: weak_at,
+                super_key,
+                notified,
+            });
+        });
+        Self { async_task, notified }
+    }
+
+    /// Notifies the key garbage collector to iterate through orphaned and superseded blobs and
+    /// attempts their deletion. We only process one key at a time and then schedule another
+    /// attempt by queueing it in the async_task (low priority) queue.
+    pub fn notify_gc(&self) {
+        if let Ok(0) = self.notified.compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed) {
+            self.async_task.queue_lo(|shelf| shelf.get_downcast_mut::<GcInternal>().unwrap().step())
+        }
+    }
+}
+
+struct GcInternal {
+    deleted_blob_ids: Vec<i64>,
+    superseded_blobs: Vec<(i64, Vec<u8>, BlobMetaData)>,
+    invalidate_key: Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
+    db: KeystoreDB,
+    async_task: std::sync::Weak<AsyncTask>,
+    super_key: Arc<SuperKeyManager>,
+    notified: Arc<AtomicU8>,
+}
+
+impl GcInternal {
+    /// Attempts to process one blob from the database.
+    /// We process one key at a time, because deleting a key is a time consuming process which
+    /// may involve calling into the KeyMint backend and we don't want to hog neither the backend
+    /// nor the database for extended periods of time.
+    /// To limit the number of database transactions, which are also expensive and competing
+    /// with threads on the critical path, deleted blobs are loaded in batches.
+    fn process_one_key(&mut self) -> Result<()> {
+        if self.superseded_blobs.is_empty() {
+            let blobs = self
+                .db
+                .handle_next_superseded_blobs(&self.deleted_blob_ids, 20)
+                .context("In process_one_key: Trying to handle superseded blob.")?;
+            self.deleted_blob_ids = vec![];
+            self.superseded_blobs = blobs;
+        }
+
+        if let Some((blob_id, blob, blob_metadata)) = self.superseded_blobs.pop() {
+            // Add the next blob_id to the deleted blob ids list. So it will be
+            // removed from the database regardless of whether the following
+            // succeeds or not.
+            self.deleted_blob_ids.push(blob_id);
+
+            // If the key has a km_uuid we try to get the corresponding device
+            // and delete the key, unwrapping if necessary and possible.
+            // (At this time keys may get deleted without having the super encryption
+            // key in this case we can only delete the key from the database.)
+            if let Some(uuid) = blob_metadata.km_uuid() {
+                let blob = self
+                    .super_key
+                    .unwrap_key_if_required(&blob_metadata, &blob)
+                    .context("In process_one_key: Trying to unwrap to-be-deleted blob.")?;
+                (self.invalidate_key)(&uuid, &*blob)
+                    .context("In process_one_key: Trying to invalidate key.")?;
+            }
+        }
+        Ok(())
+    }
+
+    /// Processes one key and then schedules another attempt until it runs out of blobs to delete.
+    fn step(&mut self) {
+        self.notified.store(0, Ordering::Relaxed);
+        if let Err(e) = self.process_one_key() {
+            log::error!("Error trying to delete blob entry. {:?}", e);
+        }
+        // Schedule the next step. This gives high priority requests a chance to interleave.
+        if !self.deleted_blob_ids.is_empty() {
+            if let Some(at) = self.async_task.upgrade() {
+                if let Ok(0) =
+                    self.notified.compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed)
+                {
+                    at.queue_lo(move |shelf| {
+                        shelf.get_downcast_mut::<GcInternal>().unwrap().step()
+                    });
+                }
+            }
+        }
+    }
+}
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
new file mode 100644
index 0000000..c492120
--- /dev/null
+++ b/keystore2/src/globals.rs
@@ -0,0 +1,379 @@
+// 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.
+
+//! This module holds global state of Keystore such as the thread local
+//! database connections and connections to services that Keystore needs
+//! to talk to.
+
+use crate::gc::Gc;
+use crate::legacy_blob::LegacyBlobLoader;
+use crate::legacy_migrator::LegacyMigrator;
+use crate::super_key::SuperKeyManager;
+use crate::utils::watchdog as wd;
+use crate::utils::Asp;
+use crate::{async_task::AsyncTask, database::MonotonicRawTime};
+use crate::{
+    database::KeystoreDB,
+    database::Uuid,
+    error::{map_binder_status, map_binder_status_code, Error, ErrorCode},
+};
+use crate::{enforcements::Enforcements, error::map_km_error};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    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 binder::FromIBinder;
+use keystore2_vintf::get_aidl_instances;
+use lazy_static::lazy_static;
+use std::sync::{Arc, Mutex};
+use std::{cell::RefCell, sync::Once};
+use std::{collections::HashMap, path::Path, path::PathBuf};
+
+static DB_INIT: Once = Once::new();
+
+/// Open a connection to the Keystore 2.0 database. This is called during the initialization of
+/// the thread local DB field. It should never be called directly. The first time this is called
+/// we also call KeystoreDB::cleanup_leftovers to restore the key lifecycle invariant. See the
+/// documentation of cleanup_leftovers for more details. The function also constructs a blob
+/// garbage collector. The initializing closure constructs another database connection without
+/// a gc. Although one GC is created for each thread local database connection, this closure
+/// is run only once, as long as the ASYNC_TASK instance is the same. So only one additional
+/// database connection is created for the garbage collector worker.
+pub fn create_thread_local_db() -> KeystoreDB {
+    let mut db = KeystoreDB::new(
+        &DB_PATH.lock().expect("Could not get the database directory."),
+        Some(GC.clone()),
+    )
+    .expect("Failed to open database.");
+    DB_INIT.call_once(|| {
+        log::info!("Touching Keystore 2.0 database for this first time since boot.");
+        db.insert_last_off_body(MonotonicRawTime::now())
+            .expect("Could not initialize database with last off body.");
+        log::info!("Calling cleanup leftovers.");
+        let n = db.cleanup_leftovers().expect("Failed to cleanup database on startup.");
+        if n != 0 {
+            log::info!(
+                concat!(
+                    "Cleaned up {} failed entries. ",
+                    "This indicates keystore crashed during key generation."
+                ),
+                n
+            );
+        }
+    });
+    db
+}
+
+thread_local! {
+    /// Database connections are not thread safe, but connecting to the
+    /// same database multiple times is safe as long as each connection is
+    /// used by only one thread. So we store one database connection per
+    /// thread in this thread local key.
+    pub static DB: RefCell<KeystoreDB> =
+            RefCell::new(create_thread_local_db());
+}
+
+#[derive(Default)]
+struct DevicesMap {
+    devices_by_uuid: HashMap<Uuid, (Asp, KeyMintHardwareInfo)>,
+    uuid_by_sec_level: HashMap<SecurityLevel, Uuid>,
+}
+
+impl DevicesMap {
+    fn dev_by_sec_level(
+        &self,
+        sec_level: &SecurityLevel,
+    ) -> Option<(Asp, KeyMintHardwareInfo, Uuid)> {
+        self.uuid_by_sec_level.get(sec_level).and_then(|uuid| self.dev_by_uuid(uuid))
+    }
+
+    fn dev_by_uuid(&self, uuid: &Uuid) -> Option<(Asp, KeyMintHardwareInfo, Uuid)> {
+        self.devices_by_uuid
+            .get(uuid)
+            .map(|(dev, hw_info)| ((*dev).clone(), (*hw_info).clone(), *uuid))
+    }
+
+    fn devices<T: FromIBinder + ?Sized>(&self) -> Vec<Strong<T>> {
+        self.devices_by_uuid.values().filter_map(|(asp, _)| asp.get_interface::<T>().ok()).collect()
+    }
+
+    /// The requested security level and the security level of the actual implementation may
+    /// differ. So we map the requested security level to the uuid of the implementation
+    /// so that there cannot be any confusion as to which KeyMint instance is requested.
+    fn insert(&mut self, sec_level: SecurityLevel, dev: Asp, hw_info: KeyMintHardwareInfo) {
+        // For now we use the reported security level of the KM instance as UUID.
+        // TODO update this section once UUID was added to the KM hardware info.
+        let uuid: Uuid = sec_level.into();
+        self.devices_by_uuid.insert(uuid, (dev, hw_info));
+        self.uuid_by_sec_level.insert(sec_level, uuid);
+    }
+}
+
+#[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(
+        Path::new("/data/misc/keystore").to_path_buf());
+    /// Runtime database of unwrapped super keys.
+    pub static ref SUPER_KEY: Arc<SuperKeyManager> = Default::default();
+    /// Map of KeyMint devices.
+    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();
+    /// Singleton for enforcements.
+    pub static ref ENFORCEMENTS: Enforcements = Default::default();
+    /// LegacyBlobLoader is initialized and exists globally.
+    /// The same directory used by the database is used by the LegacyBlobLoader as well.
+    pub static ref LEGACY_BLOB_LOADER: Arc<LegacyBlobLoader> = Arc::new(LegacyBlobLoader::new(
+        &DB_PATH.lock().expect("Could not get the database path for legacy blob loader.")));
+    /// Legacy migrator. Atomically migrates legacy blobs to the database.
+    pub static ref LEGACY_MIGRATOR: Arc<LegacyMigrator> =
+        Arc::new(LegacyMigrator::new(Arc::new(Default::default())));
+    /// Background thread which handles logging via statsd and logd
+    pub static ref LOGS_HANDLER: Arc<AsyncTask> = Default::default();
+
+    static ref GC: Arc<Gc> = Arc::new(Gc::new_init_with(ASYNC_TASK.clone(), || {
+        (
+            Box::new(|uuid, blob| {
+                let km_dev: Strong<dyn IKeyMintDevice> =
+                    get_keymint_dev_by_uuid(uuid).map(|(dev, _)| dev)?.get_interface()?;
+                let _wp = wd::watch_millis("In invalidate key closure: calling deleteKey", 500);
+                map_km_error(km_dev.deleteKey(&*blob))
+                    .context("In invalidate key closure: Trying to invalidate key blob.")
+            }),
+            KeystoreDB::new(&DB_PATH.lock().expect("Could not get the database directory."), None)
+                .expect("Failed to open database."),
+            SUPER_KEY.clone(),
+        )
+    }));
+}
+
+static KEYMINT_SERVICE_NAME: &str = "android.hardware.security.keymint.IKeyMintDevice";
+
+/// Make a new connection to a KeyMint device of the given security level.
+/// 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 => {
+            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 = 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 wp = wd::watch_millis("In connect_keymint: calling getHardwareInfo()", 500);
+    let hw_info = map_km_error(keymint.getHardwareInfo())
+        .context("In connect_keymint: Failed to get hardware info.")?;
+    drop(wp);
+
+    Ok((Asp::new(keymint.as_binder()), hw_info))
+}
+
+/// Get a keymint device for the given security level either from our cache or
+/// by making a new connection. Returns the device, the hardware info and the uuid.
+/// TODO the latter can be removed when the uuid is part of the hardware info.
+pub fn get_keymint_device(
+    security_level: &SecurityLevel,
+) -> Result<(Asp, KeyMintHardwareInfo, Uuid)> {
+    let mut devices_map = KEY_MINT_DEVICES.lock().unwrap();
+    if let Some((dev, hw_info, uuid)) = devices_map.dev_by_sec_level(&security_level) {
+        Ok((dev, hw_info, uuid))
+    } else {
+        let (dev, hw_info) = connect_keymint(security_level).context("In get_keymint_device.")?;
+        devices_map.insert(*security_level, dev, hw_info);
+        // Unwrap must succeed because we just inserted it.
+        Ok(devices_map.dev_by_sec_level(security_level).unwrap())
+    }
+}
+
+/// Get a keymint device for the given uuid. This will only access the cache, but will not
+/// attempt to establish a new connection. It is assumed that the cache is already populated
+/// when this is called. This is a fair assumption, because service.rs iterates through all
+/// security levels when it gets instantiated.
+pub fn get_keymint_dev_by_uuid(uuid: &Uuid) -> Result<(Asp, KeyMintHardwareInfo)> {
+    let devices_map = KEY_MINT_DEVICES.lock().unwrap();
+    if let Some((dev, hw_info, _)) = devices_map.dev_by_uuid(uuid) {
+        Ok((dev, hw_info))
+    } else {
+        Err(Error::sys()).context("In get_keymint_dev_by_uuid: No KeyMint instance found.")
+    }
+}
+
+/// Return all known keymint devices.
+pub fn get_keymint_devices() -> Vec<Strong<dyn IKeyMintDevice>> {
+    KEY_MINT_DEVICES.lock().unwrap().devices()
+}
+
+static TIME_STAMP_SERVICE_NAME: &str = "android.hardware.security.secureclock.ISecureClock";
+
+/// Make a new connection to a secure clock service.
+/// 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_instances =
+        get_aidl_instances("android.hardware.security.secureclock", 1, "ISecureClock");
+
+    let secure_clock_available =
+        secureclock_instances.as_vec()?.iter().any(|instance| *instance == "default");
+
+    let default_time_stamp_service_name = format!("{}/default", TIME_STAMP_SERVICE_NAME);
+
+    let secureclock = if secure_clock_available {
+        map_binder_status_code(binder::get_interface(&default_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)
+                }
+                e => e,
+            })
+            .context("In connect_secureclock: Trying to get Legacy wrapper.")
+    }?;
+
+    Ok(Asp::new(secureclock.as_binder()))
+}
+
+/// Get the timestamp service that verifies auth token timeliness towards security levels with
+/// different clocks.
+pub fn get_timestamp_service() -> Result<Asp> {
+    let mut ts_device = TIME_STAMP_DEVICE.lock().unwrap();
+    if let Some(dev) = &*ts_device {
+        Ok(dev.clone())
+    } else {
+        let dev = connect_secureclock().context("In get_timestamp_service.")?;
+        *ts_device = Some(dev.clone());
+        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/id_rotation.rs b/keystore2/src/id_rotation.rs
new file mode 100644
index 0000000..dbf0fc9
--- /dev/null
+++ b/keystore2/src/id_rotation.rs
@@ -0,0 +1,122 @@
+// 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.
+
+//! This module implements the unique id rotation privacy feature. Certain system components
+//! have the ability to include a per-app unique id into the key attestation. The key rotation
+//! feature assures that the unique id is rotated on factory reset at least once in a 30 day
+//! key rotation period.
+//!
+//! It is assumed that the timestamp file does not exist after a factory reset. So the creation
+//! time of the timestamp file provides a lower bound for the time since factory reset.
+
+use anyhow::{Context, Result};
+use std::fs;
+use std::io::ErrorKind;
+use std::path::{Path, PathBuf};
+use std::time::Duration;
+
+const ID_ROTATION_PERIOD: Duration = Duration::from_secs(30 * 24 * 60 * 60); // Thirty days.
+static TIMESTAMP_FILE_NAME: &str = &"timestamp";
+
+/// The IdRotationState stores the path to the timestamp file for deferred usage. The data
+/// partition is usually not available when Keystore 2.0 starts up. So this object is created
+/// and passed down to the users of the feature which can then query the timestamp on demand.
+#[derive(Debug, Clone)]
+pub struct IdRotationState {
+    timestamp_path: PathBuf,
+}
+
+impl IdRotationState {
+    /// Creates a new IdRotationState. It holds the path to the timestamp file for deferred usage.
+    pub fn new(keystore_db_path: &Path) -> Self {
+        let mut timestamp_path = keystore_db_path.to_owned();
+        timestamp_path.push(TIMESTAMP_FILE_NAME);
+        Self { timestamp_path }
+    }
+
+    /// Reads the metadata of or creates the timestamp file. It returns true if the timestamp
+    /// file is younger than `ID_ROTATION_PERIOD`, i.e., 30 days.
+    pub fn had_factory_reset_since_id_rotation(&self) -> Result<bool> {
+        match fs::metadata(&self.timestamp_path) {
+            Ok(metadata) => {
+                let duration_since_factory_reset = metadata
+                    .modified()
+                    .context("File creation time not supported.")?
+                    .elapsed()
+                    .context("Failed to compute time elapsed since factory reset.")?;
+                Ok(duration_since_factory_reset < ID_ROTATION_PERIOD)
+            }
+            Err(e) => match e.kind() {
+                ErrorKind::NotFound => {
+                    fs::File::create(&self.timestamp_path)
+                        .context("Failed to create timestamp file.")?;
+                    Ok(true)
+                }
+                _ => Err(e).context("Failed to open timestamp file."),
+            },
+        }
+        .context("In had_factory_reset_since_id_rotation:")
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use keystore2_test_utils::TempDir;
+    use nix::sys::stat::utimes;
+    use nix::sys::time::{TimeVal, TimeValLike};
+    use std::convert::TryInto;
+    use std::time::UNIX_EPOCH;
+
+    #[test]
+    fn test_had_factory_reset_since_id_rotation() -> Result<()> {
+        let temp_dir = TempDir::new("test_had_factory_reset_since_id_rotation_")
+            .expect("Failed to create temp dir.");
+        let id_rotation_state = IdRotationState::new(&temp_dir.path());
+
+        let mut temp_file_path = temp_dir.path().to_owned();
+        temp_file_path.push(TIMESTAMP_FILE_NAME);
+
+        // The timestamp file should not exist.
+        assert!(!temp_file_path.exists());
+
+        // This should return true.
+        assert!(id_rotation_state.had_factory_reset_since_id_rotation()?);
+
+        // Now the timestamp file should exist.
+        assert!(temp_file_path.exists());
+
+        // We should still return true because the timestamp file is young.
+        assert!(id_rotation_state.had_factory_reset_since_id_rotation()?);
+
+        // Now let's age the timestamp file by backdating the modification time.
+        let metadata = fs::metadata(&temp_file_path)?;
+        let mtime = metadata.modified()?;
+        let mtime = mtime.duration_since(UNIX_EPOCH)?;
+        let mtime =
+            mtime.checked_sub(ID_ROTATION_PERIOD).expect("Failed to subtract id rotation period");
+        let mtime = TimeVal::seconds(mtime.as_secs().try_into().unwrap());
+
+        let atime = metadata.accessed()?;
+        let atime = atime.duration_since(UNIX_EPOCH)?;
+        let atime = TimeVal::seconds(atime.as_secs().try_into().unwrap());
+
+        utimes(&temp_file_path, &atime, &mtime)?;
+
+        // Now that the file has aged we should see false.
+        assert!(!id_rotation_state.had_factory_reset_since_id_rotation()?);
+
+        Ok(())
+    }
+}
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
new file mode 100644
index 0000000..74a9b23
--- /dev/null
+++ b/keystore2/src/key_parameter.rs
@@ -0,0 +1,1507 @@
+// 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.
+
+//! Key parameters are declared by KeyMint to describe properties of keys and operations.
+//! During key generation and import, key parameters are used to characterize a key, its usage
+//! restrictions, and additional parameters for attestation. During the lifetime of the key,
+//! the key characteristics are expressed as set of key parameters. During cryptographic
+//! operations, clients may specify additional operation specific parameters.
+//! This module provides a Keystore 2.0 internal representation for key parameters and
+//! implements traits to convert it from and into KeyMint KeyParameters and store it in
+//! the SQLite database.
+//!
+//! ## Synopsis
+//!
+//! enum KeyParameterValue {
+//!     Invalid,
+//!     Algorithm(Algorithm),
+//!     ...
+//! }
+//!
+//! impl KeyParameterValue {
+//!     pub fn get_tag(&self) -> Tag;
+//!     pub fn new_from_sql(tag: Tag, data: &SqlField) -> Result<Self>;
+//!     pub fn new_from_tag_primitive_pair<T: Into<Primitive>>(tag: Tag, v: T)
+//!        -> Result<Self, PrimitiveError>;
+//!     fn to_sql(&self) -> SqlResult<ToSqlOutput>
+//! }
+//!
+//! use ...::keymint::KeyParameter as KmKeyParameter;
+//! impl Into<KmKeyParameter> for KeyParameterValue {}
+//! impl From<KmKeyParameter> for KeyParameterValue {}
+//!
+//! ## Implementation
+//! Each of the six functions is implemented as match statement over each key parameter variant.
+//! We bootstrap these function as well as the KeyParameterValue enum itself from a single list
+//! of key parameters, that needs to be kept in sync with the KeyMint AIDL specification.
+//!
+//! The list resembles an enum declaration with a few extra fields.
+//! enum KeyParameterValue {
+//!    Invalid with tag INVALID and field Invalid,
+//!    Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+//!    ...
+//! }
+//! The tag corresponds to the variant of the keymint::Tag, and the field corresponds to the
+//! variant of the keymint::KeyParameterValue union. There is no one to one mapping between
+//! tags and union fields, e.g., the values of both tags BOOT_PATCHLEVEL and VENDOR_PATCHLEVEL
+//! are stored in the Integer field.
+//!
+//! The macros interpreting them all follow a similar pattern and follow the following fragment
+//! naming scheme:
+//!
+//!    Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+//!    $vname $(($vtype ))? with tag $tag_name and field $field_name,
+//!
+//! Further, KeyParameterValue appears in the macro as $enum_name.
+//! Note that $vtype is optional to accommodate variants like Invalid which don't wrap a value.
+//!
+//! In some cases $vtype is not part of the expansion, but we still have to modify the expansion
+//! depending on the presence of $vtype. In these cases we recurse through the list following the
+//! following pattern:
+//!
+//! (@<marker> <non repeating args>, [<out list>], [<in list>])
+//!
+//! These macros usually have four rules:
+//!  * Two main recursive rules, of the form:
+//!    (
+//!        @<marker>
+//!        <non repeating args>,
+//!        [<out list>],
+//!        [<one element pattern> <in tail>]
+//!    ) => {
+//!        macro!{@<marker> <non repeating args>, [<out list>
+//!            <one element expansion>
+//!        ], [<in tail>]}
+//!    };
+//!    They pop one element off the <in list> and add one expansion to the out list.
+//!    The element expansion is kept on a separate line (or lines) for better readability.
+//!    The two variants differ in whether or not $vtype is expected.
+//!  * The termination condition which has an empty in list.
+//!  * The public interface, which does not have @marker and calls itself with an empty out list.
+
+#![allow(clippy::from_over_into, clippy::needless_question_mark)]
+
+use std::convert::TryInto;
+
+use crate::db_utils::SqlField;
+use crate::error::Error as KeystoreError;
+use crate::error::ResponseCode;
+
+pub use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+    HardwareAuthenticatorType::HardwareAuthenticatorType, KeyOrigin::KeyOrigin,
+    KeyParameter::KeyParameter as KmKeyParameter,
+    KeyParameterValue::KeyParameterValue as KmKeyParameterValue, KeyPurpose::KeyPurpose,
+    PaddingMode::PaddingMode, SecurityLevel::SecurityLevel, Tag::Tag,
+};
+use android_system_keystore2::aidl::android::system::keystore2::Authorization::Authorization;
+use anyhow::{Context, Result};
+use rusqlite::types::{Null, ToSql, ToSqlOutput};
+use rusqlite::Result as SqlResult;
+
+/// This trait is used to associate a primitive to any type that can be stored inside a
+/// KeyParameterValue, especially the AIDL enum types, e.g., keymint::{Algorithm, Digest, ...}.
+/// This allows for simplifying the macro rules, e.g., for reading from the SQL database.
+/// An expression like `KeyParameterValue::Algorithm(row.get(0))` would not work because
+/// a type of `Algorithm` is expected which does not implement `FromSql` and we cannot
+/// implement it because we own neither the type nor the trait.
+/// With AssociatePrimitive we can write an expression
+/// `KeyParameter::Algorithm(<Algorithm>::from_primitive(row.get(0)))` to inform `get`
+/// about the expected primitive type that it can convert into. By implementing this
+/// trait for all inner types we can write a single rule to cover all cases (except where
+/// there is no wrapped type):
+/// `KeyParameterValue::$vname(<$vtype>::from_primitive(row.get(0)))`
+trait AssociatePrimitive {
+    type Primitive;
+
+    fn from_primitive(v: Self::Primitive) -> Self;
+    fn to_primitive(&self) -> Self::Primitive;
+}
+
+/// Associates the given type with i32. The macro assumes that the given type is actually a
+/// tuple struct wrapping i32, such as AIDL enum types.
+macro_rules! implement_associate_primitive_for_aidl_enum {
+    ($t:ty) => {
+        impl AssociatePrimitive for $t {
+            type Primitive = i32;
+
+            fn from_primitive(v: Self::Primitive) -> Self {
+                Self(v)
+            }
+            fn to_primitive(&self) -> Self::Primitive {
+                self.0
+            }
+        }
+    };
+}
+
+/// Associates the given type with itself.
+macro_rules! implement_associate_primitive_identity {
+    ($t:ty) => {
+        impl AssociatePrimitive for $t {
+            type Primitive = $t;
+
+            fn from_primitive(v: Self::Primitive) -> Self {
+                v
+            }
+            fn to_primitive(&self) -> Self::Primitive {
+                self.clone()
+            }
+        }
+    };
+}
+
+implement_associate_primitive_for_aidl_enum! {Algorithm}
+implement_associate_primitive_for_aidl_enum! {BlockMode}
+implement_associate_primitive_for_aidl_enum! {Digest}
+implement_associate_primitive_for_aidl_enum! {EcCurve}
+implement_associate_primitive_for_aidl_enum! {HardwareAuthenticatorType}
+implement_associate_primitive_for_aidl_enum! {KeyOrigin}
+implement_associate_primitive_for_aidl_enum! {KeyPurpose}
+implement_associate_primitive_for_aidl_enum! {PaddingMode}
+implement_associate_primitive_for_aidl_enum! {SecurityLevel}
+
+implement_associate_primitive_identity! {Vec<u8>}
+implement_associate_primitive_identity! {i64}
+implement_associate_primitive_identity! {i32}
+
+/// This enum allows passing a primitive value to `KeyParameterValue::new_from_tag_primitive_pair`
+/// Usually, it is not necessary to use this type directly because the function uses
+/// `Into<Primitive>` as a trait bound.
+pub enum Primitive {
+    /// Wraps an i64.
+    I64(i64),
+    /// Wraps an i32.
+    I32(i32),
+    /// Wraps a Vec<u8>.
+    Vec(Vec<u8>),
+}
+
+impl From<i64> for Primitive {
+    fn from(v: i64) -> Self {
+        Self::I64(v)
+    }
+}
+impl From<i32> for Primitive {
+    fn from(v: i32) -> Self {
+        Self::I32(v)
+    }
+}
+impl From<Vec<u8>> for Primitive {
+    fn from(v: Vec<u8>) -> Self {
+        Self::Vec(v)
+    }
+}
+
+/// This error is returned by `KeyParameterValue::new_from_tag_primitive_pair`.
+#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum PrimitiveError {
+    /// Returned if this primitive is unsuitable for the given tag type.
+    #[error("Primitive does not match the expected tag type.")]
+    TypeMismatch,
+    /// Return if the tag type is unknown.
+    #[error("Unknown tag.")]
+    UnknownTag,
+}
+
+impl TryInto<i64> for Primitive {
+    type Error = PrimitiveError;
+
+    fn try_into(self) -> Result<i64, Self::Error> {
+        match self {
+            Self::I64(v) => Ok(v),
+            _ => Err(Self::Error::TypeMismatch),
+        }
+    }
+}
+impl TryInto<i32> for Primitive {
+    type Error = PrimitiveError;
+
+    fn try_into(self) -> Result<i32, Self::Error> {
+        match self {
+            Self::I32(v) => Ok(v),
+            _ => Err(Self::Error::TypeMismatch),
+        }
+    }
+}
+impl TryInto<Vec<u8>> for Primitive {
+    type Error = PrimitiveError;
+
+    fn try_into(self) -> Result<Vec<u8>, Self::Error> {
+        match self {
+            Self::Vec(v) => Ok(v),
+            _ => Err(Self::Error::TypeMismatch),
+        }
+    }
+}
+
+/// Expands the list of KeyParameterValue variants as follows:
+///
+/// Input:
+/// Invalid with tag INVALID and field Invalid,
+/// Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+///
+/// Output:
+/// ```
+/// pub fn new_from_tag_primitive_pair<T: Into<Primitive>>(
+///     tag: Tag,
+///     v: T
+/// ) -> Result<KeyParameterValue, PrimitiveError> {
+///     let p: Primitive = v.into();
+///     Ok(match tag {
+///         Tag::INVALID => KeyParameterValue::Invalid,
+///         Tag::ALGORITHM => KeyParameterValue::Algorithm(
+///             <Algorithm>::from_primitive(p.try_into()?)
+///         ),
+///         _ => return Err(PrimitiveError::UnknownTag),
+///     })
+/// }
+/// ```
+macro_rules! implement_from_tag_primitive_pair {
+    ($enum_name:ident; $($vname:ident$(($vtype:ty))? $tag_name:ident),*) => {
+        /// Returns the an instance of $enum_name or an error if the given primitive does not match
+        /// the tag type or the tag is unknown.
+        pub fn new_from_tag_primitive_pair<T: Into<Primitive>>(
+            tag: Tag,
+            v: T
+        ) -> Result<$enum_name, PrimitiveError> {
+            let p: Primitive = v.into();
+            Ok(match tag {
+                $(Tag::$tag_name => $enum_name::$vname$((
+                    <$vtype>::from_primitive(p.try_into()?)
+                ))?,)*
+                _ => return Err(PrimitiveError::UnknownTag),
+            })
+        }
+    };
+}
+
+/// Expands the list of KeyParameterValue variants as follows:
+///
+/// Input:
+/// pub enum KeyParameterValue {
+///     Invalid with tag INVALID and field Invalid,
+///     Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+/// }
+///
+/// Output:
+/// ```
+/// pub enum KeyParameterValue {
+///     Invalid,
+///     Algorithm(Algorithm),
+/// }
+/// ```
+macro_rules! implement_enum {
+    (
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+             $($(#[$emeta:meta])* $vname:ident$(($vtype:ty))?),* $(,)?
+        }
+    ) => {
+        $(#[$enum_meta])*
+        $enum_vis enum $enum_name {
+            $(
+                $(#[$emeta])*
+                $vname$(($vtype))?
+            ),*
+        }
+    };
+}
+
+/// Expands the list of KeyParameterValue variants as follows:
+///
+/// Input:
+/// Invalid with tag INVALID and field Invalid,
+/// Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+///
+/// Output:
+/// ```
+/// pub fn get_tag(&self) -> Tag {
+///     match self {
+///         KeyParameterValue::Invalid => Tag::INVALID,
+///         KeyParameterValue::Algorithm(_) => Tag::ALGORITHM,
+///     }
+/// }
+/// ```
+macro_rules! implement_get_tag {
+    (
+        @replace_type_spec
+        $enum_name:ident,
+        [$($out:tt)*],
+        [$vname:ident($vtype:ty) $tag_name:ident, $($in:tt)*]
+    ) => {
+        implement_get_tag!{@replace_type_spec $enum_name, [$($out)*
+            $enum_name::$vname(_) => Tag::$tag_name,
+        ], [$($in)*]}
+    };
+    (
+        @replace_type_spec
+        $enum_name:ident,
+        [$($out:tt)*],
+        [$vname:ident $tag_name:ident, $($in:tt)*]
+    ) => {
+        implement_get_tag!{@replace_type_spec $enum_name, [$($out)*
+            $enum_name::$vname => Tag::$tag_name,
+        ], [$($in)*]}
+    };
+    (@replace_type_spec $enum_name:ident, [$($out:tt)*], []) => {
+        /// Returns the tag of the given instance.
+        pub fn get_tag(&self) -> Tag {
+            match self {
+                $($out)*
+            }
+        }
+    };
+
+    ($enum_name:ident; $($vname:ident$(($vtype:ty))? $tag_name:ident),*) => {
+        implement_get_tag!{@replace_type_spec $enum_name, [], [$($vname$(($vtype))? $tag_name,)*]}
+    };
+}
+
+/// Expands the list of KeyParameterValue variants as follows:
+///
+/// Input:
+/// Invalid with tag INVALID and field Invalid,
+/// Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+///
+/// Output:
+/// ```
+/// fn to_sql(&self) -> SqlResult<ToSqlOutput> {
+///     match self {
+///         KeyParameterValue::Invalid => Ok(ToSqlOutput::from(Null)),
+///         KeyParameterValue::Algorithm(v) => Ok(ToSqlOutput::from(v.to_primitive())),
+///     }
+/// }
+/// ```
+macro_rules! implement_to_sql {
+    (
+        @replace_type_spec
+        $enum_name:ident,
+        [$($out:tt)*],
+        [$vname:ident($vtype:ty), $($in:tt)*]
+    ) => {
+        implement_to_sql!{@replace_type_spec $enum_name, [ $($out)*
+            $enum_name::$vname(v) => Ok(ToSqlOutput::from(v.to_primitive())),
+        ], [$($in)*]}
+    };
+    (
+        @replace_type_spec
+        $enum_name:ident,
+        [$($out:tt)*],
+        [$vname:ident, $($in:tt)*]
+    ) => {
+        implement_to_sql!{@replace_type_spec $enum_name, [ $($out)*
+            $enum_name::$vname => Ok(ToSqlOutput::from(Null)),
+        ], [$($in)*]}
+    };
+    (@replace_type_spec $enum_name:ident, [$($out:tt)*], []) => {
+        /// Converts $enum_name to be stored in a rusqlite database.
+        fn to_sql(&self) -> SqlResult<ToSqlOutput> {
+            match self {
+                $($out)*
+            }
+        }
+    };
+
+
+    ($enum_name:ident; $($vname:ident$(($vtype:ty))?),*) => {
+        impl ToSql for $enum_name {
+            implement_to_sql!{@replace_type_spec $enum_name, [], [$($vname$(($vtype))?,)*]}
+        }
+
+    }
+}
+
+/// Expands the list of KeyParameterValue variants as follows:
+///
+/// Input:
+/// Invalid with tag INVALID and field Invalid,
+/// Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+///
+/// Output:
+/// ```
+/// pub fn new_from_sql(
+///     tag: Tag,
+///     data: &SqlField,
+/// ) -> Result<Self> {
+///     Ok(match self {
+///         Tag::Invalid => KeyParameterValue::Invalid,
+///         Tag::ALGORITHM => {
+///             KeyParameterValue::Algorithm(<Algorithm>::from_primitive(data
+///                 .get()
+///                 .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
+///                 .context(concat!("Failed to read sql data for tag: ", "ALGORITHM", "."))?
+///             ))
+///         },
+///     })
+/// }
+/// ```
+macro_rules! implement_new_from_sql {
+    ($enum_name:ident; $($vname:ident$(($vtype:ty))? $tag_name:ident),*) => {
+        /// Takes a tag and an SqlField and attempts to construct a KeyParameter value.
+        /// This function may fail if the parameter value cannot be extracted from the
+        /// database cell.
+        pub fn new_from_sql(
+            tag: Tag,
+            data: &SqlField,
+        ) -> Result<Self> {
+            Ok(match tag {
+                $(
+                    Tag::$tag_name => {
+                        $enum_name::$vname$((<$vtype>::from_primitive(data
+                            .get()
+                            .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
+                            .context(concat!(
+                                "Failed to read sql data for tag: ",
+                                stringify!($tag_name),
+                                "."
+                            ))?
+                        )))?
+                    },
+                )*
+                _ => $enum_name::Invalid,
+            })
+        }
+    };
+}
+
+/// This key parameter default is used during the conversion from KeyParameterValue
+/// to keymint::KeyParameterValue. Keystore's version does not have wrapped types
+/// for boolean tags and the tag Invalid. The AIDL version uses bool and integer
+/// variants respectively. This default function is invoked in these cases to
+/// homogenize the rules for boolean and invalid tags.
+/// The bool variant returns true because boolean parameters are implicitly true
+/// if present.
+trait KpDefault {
+    fn default() -> Self;
+}
+
+impl KpDefault for i32 {
+    fn default() -> Self {
+        0
+    }
+}
+
+impl KpDefault for bool {
+    fn default() -> Self {
+        true
+    }
+}
+
+/// Expands the list of KeyParameterValue variants as follows:
+///
+/// Input:
+/// Invalid with tag INVALID and field Invalid,
+/// Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+///
+/// Output:
+/// ```
+/// impl From<KmKeyParameter> for KeyParameterValue {
+///     fn from(kp: KmKeyParameter) -> Self {
+///         match kp {
+///             KmKeyParameter { tag: Tag::INVALID, value: KmKeyParameterValue::Invalid(_) }
+///                 => $enum_name::$vname,
+///             KmKeyParameter { tag: Tag::Algorithm, value: KmKeyParameterValue::Algorithm(v) }
+///                 => $enum_name::Algorithm(v),
+///             _ => $enum_name::Invalid,
+///         }
+///     }
+/// }
+///
+/// impl Into<KmKeyParameter> for KeyParameterValue {
+///     fn into(self) -> KmKeyParameter {
+///         match self {
+///             KeyParameterValue::Invalid => KmKeyParameter {
+///                 tag: Tag::INVALID,
+///                 value: KmKeyParameterValue::Invalid(KpDefault::default())
+///             },
+///             KeyParameterValue::Algorithm(v) => KmKeyParameter {
+///                 tag: Tag::ALGORITHM,
+///                 value: KmKeyParameterValue::Algorithm(v)
+///             },
+///         }
+///     }
+/// }
+/// ```
+macro_rules! implement_try_from_to_km_parameter {
+    // The first three rules expand From<KmKeyParameter>.
+    (
+        @from
+        $enum_name:ident,
+        [$($out:tt)*],
+        [$vname:ident($vtype:ty) $tag_name:ident $field_name:ident, $($in:tt)*]
+    ) => {
+        implement_try_from_to_km_parameter!{@from $enum_name, [$($out)*
+            KmKeyParameter {
+                tag: Tag::$tag_name,
+                value: KmKeyParameterValue::$field_name(v)
+            } => $enum_name::$vname(v),
+        ], [$($in)*]
+    }};
+    (
+        @from
+        $enum_name:ident,
+        [$($out:tt)*],
+        [$vname:ident $tag_name:ident $field_name:ident, $($in:tt)*]
+    ) => {
+        implement_try_from_to_km_parameter!{@from $enum_name, [$($out)*
+            KmKeyParameter {
+                tag: Tag::$tag_name,
+                value: KmKeyParameterValue::$field_name(_)
+            } => $enum_name::$vname,
+        ], [$($in)*]
+    }};
+    (@from $enum_name:ident, [$($out:tt)*], []) => {
+        impl From<KmKeyParameter> for $enum_name {
+            fn from(kp: KmKeyParameter) -> Self {
+                match kp {
+                    $($out)*
+                    _ => $enum_name::Invalid,
+                }
+            }
+        }
+    };
+
+    // The next three rules expand Into<KmKeyParameter>.
+    (
+        @into
+        $enum_name:ident,
+        [$($out:tt)*],
+        [$vname:ident($vtype:ty) $tag_name:ident $field_name:ident, $($in:tt)*]
+    ) => {
+        implement_try_from_to_km_parameter!{@into $enum_name, [$($out)*
+            $enum_name::$vname(v) => KmKeyParameter {
+                tag: Tag::$tag_name,
+                value: KmKeyParameterValue::$field_name(v)
+            },
+        ], [$($in)*]
+    }};
+    (
+        @into
+        $enum_name:ident,
+        [$($out:tt)*],
+        [$vname:ident $tag_name:ident $field_name:ident, $($in:tt)*]
+    ) => {
+        implement_try_from_to_km_parameter!{@into $enum_name, [$($out)*
+            $enum_name::$vname => KmKeyParameter {
+                tag: Tag::$tag_name,
+                value: KmKeyParameterValue::$field_name(KpDefault::default())
+            },
+        ], [$($in)*]
+    }};
+    (@into $enum_name:ident, [$($out:tt)*], []) => {
+        impl Into<KmKeyParameter> for $enum_name {
+            fn into(self) -> KmKeyParameter {
+                match self {
+                    $($out)*
+                }
+            }
+        }
+    };
+
+
+    ($enum_name:ident; $($vname:ident$(($vtype:ty))? $tag_name:ident $field_name:ident),*) => {
+        implement_try_from_to_km_parameter!(
+            @from $enum_name,
+            [],
+            [$($vname$(($vtype))? $tag_name $field_name,)*]
+        );
+        implement_try_from_to_km_parameter!(
+            @into $enum_name,
+            [],
+            [$($vname$(($vtype))? $tag_name $field_name,)*]
+        );
+    };
+}
+
+/// This is the top level macro. While the other macros do most of the heavy lifting, this takes
+/// the key parameter list and passes it on to the other macros to generate all of the conversion
+/// functions. In addition, it generates an important test vector for verifying that tag type of the
+/// keymint tag matches the associated keymint KeyParameterValue field.
+macro_rules! implement_key_parameter_value {
+    (
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            $(
+                $(#[$($emeta:tt)+])*
+                $vname:ident$(($vtype:ty))?
+            ),* $(,)?
+        }
+    ) => {
+        implement_key_parameter_value!{
+            @extract_attr
+            $(#[$enum_meta])*
+            $enum_vis enum $enum_name {
+                []
+                [$(
+                    [] [$(#[$($emeta)+])*]
+                    $vname$(($vtype))?,
+                )*]
+            }
+        }
+    };
+
+    (
+        @extract_attr
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            [$($out:tt)*]
+            [
+                [$(#[$mout:meta])*]
+                [
+                    #[key_param(tag = $tag_name:ident, field = $field_name:ident)]
+                    $(#[$($mtail:tt)+])*
+                ]
+                $vname:ident$(($vtype:ty))?,
+                $($tail:tt)*
+            ]
+        }
+    ) => {
+        implement_key_parameter_value!{
+            @extract_attr
+            $(#[$enum_meta])*
+            $enum_vis enum $enum_name {
+                [
+                    $($out)*
+                    $(#[$mout])*
+                    $(#[$($mtail)+])*
+                    $tag_name $field_name $vname$(($vtype))?,
+                ]
+                [$($tail)*]
+            }
+        }
+    };
+
+    (
+        @extract_attr
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            [$($out:tt)*]
+            [
+                [$(#[$mout:meta])*]
+                [
+                    #[$front:meta]
+                    $(#[$($mtail:tt)+])*
+                ]
+                $vname:ident$(($vtype:ty))?,
+                $($tail:tt)*
+            ]
+        }
+    ) => {
+        implement_key_parameter_value!{
+            @extract_attr
+            $(#[$enum_meta])*
+            $enum_vis enum $enum_name {
+                [$($out)*]
+                [
+                    [
+                        $(#[$mout])*
+                        #[$front]
+                    ]
+                    [$(#[$($mtail)+])*]
+                    $vname$(($vtype))?,
+                    $($tail)*
+                ]
+            }
+        }
+    };
+
+    (
+        @extract_attr
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            [$($out:tt)*]
+            []
+        }
+    ) => {
+        implement_key_parameter_value!{
+            @spill
+            $(#[$enum_meta])*
+            $enum_vis enum $enum_name {
+                $($out)*
+            }
+        }
+    };
+
+    (
+        @spill
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            $(
+                $(#[$emeta:meta])*
+                $tag_name:ident $field_name:ident $vname:ident$(($vtype:ty))?,
+            )*
+        }
+    ) => {
+        implement_enum!(
+            $(#[$enum_meta])*
+            $enum_vis enum $enum_name {
+            $(
+                $(#[$emeta])*
+                $vname$(($vtype))?
+            ),*
+        });
+
+        impl $enum_name {
+            implement_new_from_sql!($enum_name; $($vname$(($vtype))? $tag_name),*);
+            implement_get_tag!($enum_name; $($vname$(($vtype))? $tag_name),*);
+            implement_from_tag_primitive_pair!($enum_name; $($vname$(($vtype))? $tag_name),*);
+
+            #[cfg(test)]
+            fn make_field_matches_tag_type_test_vector() -> Vec<KmKeyParameter> {
+                vec![$(KmKeyParameter{
+                    tag: Tag::$tag_name,
+                    value: KmKeyParameterValue::$field_name(Default::default())}
+                ),*]
+            }
+        }
+
+        implement_try_from_to_km_parameter!(
+            $enum_name;
+            $($vname$(($vtype))? $tag_name $field_name),*
+        );
+
+        implement_to_sql!($enum_name; $($vname$(($vtype))?),*);
+    };
+}
+
+implement_key_parameter_value! {
+/// KeyParameterValue holds a value corresponding to one of the Tags defined in
+/// the AIDL spec at hardware/interfaces/security/keymint
+#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub enum KeyParameterValue {
+    /// Associated with Tag:INVALID
+    #[key_param(tag = INVALID, field = Invalid)]
+    Invalid,
+    /// Set of purposes for which the key may be used
+    #[key_param(tag = PURPOSE, field = KeyPurpose)]
+    KeyPurpose(KeyPurpose),
+    /// Cryptographic algorithm with which the key is used
+    #[key_param(tag = ALGORITHM, field = Algorithm)]
+    Algorithm(Algorithm),
+    /// Size of the key , in bits
+    #[key_param(tag = KEY_SIZE, field = Integer)]
+    KeySize(i32),
+    /// Block cipher mode(s) with which the key may be used
+    #[key_param(tag = BLOCK_MODE, field = BlockMode)]
+    BlockMode(BlockMode),
+    /// Digest algorithms that may be used with the key to perform signing and verification
+    #[key_param(tag = DIGEST, field = Digest)]
+    Digest(Digest),
+    /// Padding modes that may be used with the key.  Relevant to RSA, AES and 3DES keys.
+    #[key_param(tag = PADDING, field = PaddingMode)]
+    PaddingMode(PaddingMode),
+    /// Can the caller provide a nonce for nonce-requiring operations
+    #[key_param(tag = CALLER_NONCE, field = BoolValue)]
+    CallerNonce,
+    /// Minimum length of MAC for HMAC keys and AES keys that support GCM mode
+    #[key_param(tag = MIN_MAC_LENGTH, field = Integer)]
+    MinMacLength(i32),
+    /// The elliptic curve
+    #[key_param(tag = EC_CURVE, field = EcCurve)]
+    EcCurve(EcCurve),
+    /// Value of the public exponent for an RSA key pair
+    #[key_param(tag = RSA_PUBLIC_EXPONENT, field = LongInteger)]
+    RSAPublicExponent(i64),
+    /// An attestation certificate for the generated key should contain an application-scoped
+    /// and time-bounded device-unique ID
+    #[key_param(tag = INCLUDE_UNIQUE_ID, field = BoolValue)]
+    IncludeUniqueID,
+    //TODO: find out about this
+    // /// Necessary system environment conditions for the generated key to be used
+    // KeyBlobUsageRequirements(KeyBlobUsageRequirements),
+    /// Only the boot loader can use the key
+    #[key_param(tag = BOOTLOADER_ONLY, field = BoolValue)]
+    BootLoaderOnly,
+    /// When deleted, the key is guaranteed to be permanently deleted and unusable
+    #[key_param(tag = ROLLBACK_RESISTANCE, field = BoolValue)]
+    RollbackResistance,
+    /// The date and time at which the key becomes active
+    #[key_param(tag = ACTIVE_DATETIME, field = DateTime)]
+    ActiveDateTime(i64),
+    /// The date and time at which the key expires for signing and encryption
+    #[key_param(tag = ORIGINATION_EXPIRE_DATETIME, field = DateTime)]
+    OriginationExpireDateTime(i64),
+    /// The date and time at which the key expires for verification and decryption
+    #[key_param(tag = USAGE_EXPIRE_DATETIME, field = DateTime)]
+    UsageExpireDateTime(i64),
+    /// Minimum amount of time that elapses between allowed operations
+    #[key_param(tag = MIN_SECONDS_BETWEEN_OPS, field = Integer)]
+    MinSecondsBetweenOps(i32),
+    /// Maximum number of times that a key may be used between system reboots
+    #[key_param(tag = MAX_USES_PER_BOOT, field = Integer)]
+    MaxUsesPerBoot(i32),
+    /// The number of times that a limited use key can be used
+    #[key_param(tag = USAGE_COUNT_LIMIT, field = Integer)]
+    UsageCountLimit(i32),
+    /// ID of the Android user that is permitted to use the key
+    #[key_param(tag = USER_ID, field = Integer)]
+    UserID(i32),
+    /// A key may only be used under a particular secure user authentication state
+    #[key_param(tag = USER_SECURE_ID, field = LongInteger)]
+    UserSecureID(i64),
+    /// No authentication is required to use this key
+    #[key_param(tag = NO_AUTH_REQUIRED, field = BoolValue)]
+    NoAuthRequired,
+    /// The types of user authenticators that may be used to authorize this key
+    #[key_param(tag = USER_AUTH_TYPE, field = HardwareAuthenticatorType)]
+    HardwareAuthenticatorType(HardwareAuthenticatorType),
+    /// The time in seconds for which the key is authorized for use, after user authentication
+    #[key_param(tag = AUTH_TIMEOUT, field = Integer)]
+    AuthTimeout(i32),
+    /// The key may be used after authentication timeout if device is still on-body
+    #[key_param(tag = ALLOW_WHILE_ON_BODY, field = BoolValue)]
+    AllowWhileOnBody,
+    /// The key must be unusable except when the user has provided proof of physical presence
+    #[key_param(tag = TRUSTED_USER_PRESENCE_REQUIRED, field = BoolValue)]
+    TrustedUserPresenceRequired,
+    /// Applicable to keys with KeyPurpose SIGN, and specifies that this key must not be usable
+    /// unless the user provides confirmation of the data to be signed
+    #[key_param(tag = TRUSTED_CONFIRMATION_REQUIRED, field = BoolValue)]
+    TrustedConfirmationRequired,
+    /// The key may only be used when the device is unlocked
+    #[key_param(tag = UNLOCKED_DEVICE_REQUIRED, field = BoolValue)]
+    UnlockedDeviceRequired,
+    /// When provided to generateKey or importKey, this tag specifies data
+    /// that is necessary during all uses of the key
+    #[key_param(tag = APPLICATION_ID, field = Blob)]
+    ApplicationID(Vec<u8>),
+    /// When provided to generateKey or importKey, this tag specifies data
+    /// that is necessary during all uses of the key
+    #[key_param(tag = APPLICATION_DATA, field = Blob)]
+    ApplicationData(Vec<u8>),
+    /// Specifies the date and time the key was created
+    #[key_param(tag = CREATION_DATETIME, field = DateTime)]
+    CreationDateTime(i64),
+    /// Specifies where the key was created, if known
+    #[key_param(tag = ORIGIN, field = Origin)]
+    KeyOrigin(KeyOrigin),
+    /// The key used by verified boot to validate the operating system booted
+    #[key_param(tag = ROOT_OF_TRUST, field = Blob)]
+    RootOfTrust(Vec<u8>),
+    /// System OS version with which the key may be used
+    #[key_param(tag = OS_VERSION, field = Integer)]
+    OSVersion(i32),
+    /// Specifies the system security patch level with which the key may be used
+    #[key_param(tag = OS_PATCHLEVEL, field = Integer)]
+    OSPatchLevel(i32),
+    /// Specifies a unique, time-based identifier
+    #[key_param(tag = UNIQUE_ID, field = Blob)]
+    UniqueID(Vec<u8>),
+    /// Used to deliver a "challenge" value to the attestKey() method
+    #[key_param(tag = ATTESTATION_CHALLENGE, field = Blob)]
+    AttestationChallenge(Vec<u8>),
+    /// The set of applications which may use a key, used only with attestKey()
+    #[key_param(tag = ATTESTATION_APPLICATION_ID, field = Blob)]
+    AttestationApplicationID(Vec<u8>),
+    /// Provides the device's brand name, to attestKey()
+    #[key_param(tag = ATTESTATION_ID_BRAND, field = Blob)]
+    AttestationIdBrand(Vec<u8>),
+    /// Provides the device's device name, to attestKey()
+    #[key_param(tag = ATTESTATION_ID_DEVICE, field = Blob)]
+    AttestationIdDevice(Vec<u8>),
+    /// Provides the device's product name, to attestKey()
+    #[key_param(tag = ATTESTATION_ID_PRODUCT, field = Blob)]
+    AttestationIdProduct(Vec<u8>),
+    /// Provides the device's serial number, to attestKey()
+    #[key_param(tag = ATTESTATION_ID_SERIAL, field = Blob)]
+    AttestationIdSerial(Vec<u8>),
+    /// Provides the IMEIs for all radios on the device, to attestKey()
+    #[key_param(tag = ATTESTATION_ID_IMEI, field = Blob)]
+    AttestationIdIMEI(Vec<u8>),
+    /// Provides the MEIDs for all radios on the device, to attestKey()
+    #[key_param(tag = ATTESTATION_ID_MEID, field = Blob)]
+    AttestationIdMEID(Vec<u8>),
+    /// Provides the device's manufacturer name, to attestKey()
+    #[key_param(tag = ATTESTATION_ID_MANUFACTURER, field = Blob)]
+    AttestationIdManufacturer(Vec<u8>),
+    /// Provides the device's model name, to attestKey()
+    #[key_param(tag = ATTESTATION_ID_MODEL, field = Blob)]
+    AttestationIdModel(Vec<u8>),
+    /// Specifies the vendor image security patch level with which the key may be used
+    #[key_param(tag = VENDOR_PATCHLEVEL, field = Integer)]
+    VendorPatchLevel(i32),
+    /// Specifies the boot image (kernel) security patch level with which the key may be used
+    #[key_param(tag = BOOT_PATCHLEVEL, field = Integer)]
+    BootPatchLevel(i32),
+    /// Provides "associated data" for AES-GCM encryption or decryption
+    #[key_param(tag = ASSOCIATED_DATA, field = Blob)]
+    AssociatedData(Vec<u8>),
+    /// Provides or returns a nonce or Initialization Vector (IV) for AES-GCM,
+    /// AES-CBC, AES-CTR, or 3DES-CBC encryption or decryption
+    #[key_param(tag = NONCE, field = Blob)]
+    Nonce(Vec<u8>),
+    /// Provides the requested length of a MAC or GCM authentication tag, in bits
+    #[key_param(tag = MAC_LENGTH, field = Integer)]
+    MacLength(i32),
+    /// Specifies whether the device has been factory reset since the
+    /// last unique ID rotation.  Used for key attestation
+    #[key_param(tag = RESET_SINCE_ID_ROTATION, field = BoolValue)]
+    ResetSinceIdRotation,
+    /// Used to deliver a cryptographic token proving that the user
+    /// confirmed a signing request
+    #[key_param(tag = CONFIRMATION_TOKEN, field = Blob)]
+    ConfirmationToken(Vec<u8>),
+    /// Used to deliver the certificate serial number to the KeyMint instance
+    /// certificate generation.
+    #[key_param(tag = CERTIFICATE_SERIAL, field = Blob)]
+    CertificateSerial(Vec<u8>),
+    /// Used to deliver the certificate subject to the KeyMint instance
+    /// certificate generation. This must be DER encoded X509 name.
+    #[key_param(tag = CERTIFICATE_SUBJECT, field = Blob)]
+    CertificateSubject(Vec<u8>),
+    /// Used to deliver the not before date in milliseconds to KeyMint during key generation/import.
+    #[key_param(tag = CERTIFICATE_NOT_BEFORE, field = DateTime)]
+    CertificateNotBefore(i64),
+    /// Used to deliver the not after date in milliseconds to KeyMint during key generation/import.
+    #[key_param(tag = CERTIFICATE_NOT_AFTER, field = DateTime)]
+    CertificateNotAfter(i64),
+    /// Specifies a maximum boot level at which a key should function
+    #[key_param(tag = MAX_BOOT_LEVEL, field = Integer)]
+    MaxBootLevel(i32),
+}
+}
+
+impl From<&KmKeyParameter> for KeyParameterValue {
+    fn from(kp: &KmKeyParameter) -> Self {
+        kp.clone().into()
+    }
+}
+
+/// KeyParameter wraps the KeyParameterValue and the security level at which it is enforced.
+#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub struct KeyParameter {
+    value: KeyParameterValue,
+    security_level: SecurityLevel,
+}
+
+impl KeyParameter {
+    /// Create an instance of KeyParameter, given the value and the security level.
+    pub fn new(value: KeyParameterValue, security_level: SecurityLevel) -> Self {
+        KeyParameter { value, security_level }
+    }
+
+    /// Construct a KeyParameter from the data from a rusqlite row.
+    /// Note that following variants of KeyParameterValue should not be stored:
+    /// IncludeUniqueID, ApplicationID, ApplicationData, RootOfTrust, UniqueID,
+    /// Attestation*, AssociatedData, Nonce, MacLength, ResetSinceIdRotation, ConfirmationToken.
+    /// This filtering is enforced at a higher level and here we support conversion for all the
+    /// variants.
+    pub fn new_from_sql(
+        tag_val: Tag,
+        data: &SqlField,
+        security_level_val: SecurityLevel,
+    ) -> Result<Self> {
+        Ok(Self {
+            value: KeyParameterValue::new_from_sql(tag_val, data)?,
+            security_level: security_level_val,
+        })
+    }
+
+    /// Get the KeyMint Tag of this this key parameter.
+    pub fn get_tag(&self) -> Tag {
+        self.value.get_tag()
+    }
+
+    /// Returns key parameter value.
+    pub fn key_parameter_value(&self) -> &KeyParameterValue {
+        &self.value
+    }
+
+    /// Returns the security level of this key parameter.
+    pub fn security_level(&self) -> &SecurityLevel {
+        &self.security_level
+    }
+
+    /// An authorization is a KeyParameter with an associated security level that is used
+    /// to convey the key characteristics to keystore clients. This function consumes
+    /// an internal KeyParameter representation to produce the Authorization wire type.
+    pub fn into_authorization(self) -> Authorization {
+        Authorization { securityLevel: self.security_level, keyParameter: self.value.into() }
+    }
+}
+
+#[cfg(test)]
+mod generated_key_parameter_tests {
+    use super::*;
+    use android_hardware_security_keymint::aidl::android::hardware::security::keymint::TagType::TagType;
+
+    fn get_field_by_tag_type(tag: Tag) -> KmKeyParameterValue {
+        let tag_type = TagType((tag.0 as u32 & 0xF0000000) as i32);
+        match tag {
+            Tag::ALGORITHM => return KmKeyParameterValue::Algorithm(Default::default()),
+            Tag::BLOCK_MODE => return KmKeyParameterValue::BlockMode(Default::default()),
+            Tag::PADDING => return KmKeyParameterValue::PaddingMode(Default::default()),
+            Tag::DIGEST => return KmKeyParameterValue::Digest(Default::default()),
+            Tag::EC_CURVE => return KmKeyParameterValue::EcCurve(Default::default()),
+            Tag::ORIGIN => return KmKeyParameterValue::Origin(Default::default()),
+            Tag::PURPOSE => return KmKeyParameterValue::KeyPurpose(Default::default()),
+            Tag::USER_AUTH_TYPE => {
+                return KmKeyParameterValue::HardwareAuthenticatorType(Default::default())
+            }
+            Tag::HARDWARE_TYPE => return KmKeyParameterValue::SecurityLevel(Default::default()),
+            _ => {}
+        }
+        match tag_type {
+            TagType::INVALID => return KmKeyParameterValue::Invalid(Default::default()),
+            TagType::ENUM | TagType::ENUM_REP => {}
+            TagType::UINT | TagType::UINT_REP => {
+                return KmKeyParameterValue::Integer(Default::default())
+            }
+            TagType::ULONG | TagType::ULONG_REP => {
+                return KmKeyParameterValue::LongInteger(Default::default())
+            }
+            TagType::DATE => return KmKeyParameterValue::DateTime(Default::default()),
+            TagType::BOOL => return KmKeyParameterValue::BoolValue(Default::default()),
+            TagType::BIGNUM | TagType::BYTES => {
+                return KmKeyParameterValue::Blob(Default::default())
+            }
+            _ => {}
+        }
+        panic!("Unknown tag/tag_type: {:?} {:?}", tag, tag_type);
+    }
+
+    fn check_field_matches_tag_type(list_o_parameters: &[KmKeyParameter]) {
+        for kp in list_o_parameters.iter() {
+            match (&kp.value, get_field_by_tag_type(kp.tag)) {
+                (&KmKeyParameterValue::Algorithm(_), KmKeyParameterValue::Algorithm(_))
+                | (&KmKeyParameterValue::BlockMode(_), KmKeyParameterValue::BlockMode(_))
+                | (&KmKeyParameterValue::PaddingMode(_), KmKeyParameterValue::PaddingMode(_))
+                | (&KmKeyParameterValue::Digest(_), KmKeyParameterValue::Digest(_))
+                | (&KmKeyParameterValue::EcCurve(_), KmKeyParameterValue::EcCurve(_))
+                | (&KmKeyParameterValue::Origin(_), KmKeyParameterValue::Origin(_))
+                | (&KmKeyParameterValue::KeyPurpose(_), KmKeyParameterValue::KeyPurpose(_))
+                | (
+                    &KmKeyParameterValue::HardwareAuthenticatorType(_),
+                    KmKeyParameterValue::HardwareAuthenticatorType(_),
+                )
+                | (&KmKeyParameterValue::SecurityLevel(_), KmKeyParameterValue::SecurityLevel(_))
+                | (&KmKeyParameterValue::Invalid(_), KmKeyParameterValue::Invalid(_))
+                | (&KmKeyParameterValue::Integer(_), KmKeyParameterValue::Integer(_))
+                | (&KmKeyParameterValue::LongInteger(_), KmKeyParameterValue::LongInteger(_))
+                | (&KmKeyParameterValue::DateTime(_), KmKeyParameterValue::DateTime(_))
+                | (&KmKeyParameterValue::BoolValue(_), KmKeyParameterValue::BoolValue(_))
+                | (&KmKeyParameterValue::Blob(_), KmKeyParameterValue::Blob(_)) => {}
+                (actual, expected) => panic!(
+                    "Tag {:?} associated with variant {:?} expected {:?}",
+                    kp.tag, actual, expected
+                ),
+            }
+        }
+    }
+
+    #[test]
+    fn key_parameter_value_field_matches_tag_type() {
+        check_field_matches_tag_type(&KeyParameterValue::make_field_matches_tag_type_test_vector());
+    }
+}
+
+#[cfg(test)]
+mod basic_tests {
+    use crate::key_parameter::*;
+
+    // Test basic functionality of KeyParameter.
+    #[test]
+    fn test_key_parameter() {
+        let key_parameter = KeyParameter::new(
+            KeyParameterValue::Algorithm(Algorithm::RSA),
+            SecurityLevel::STRONGBOX,
+        );
+
+        assert_eq!(key_parameter.get_tag(), Tag::ALGORITHM);
+
+        assert_eq!(
+            *key_parameter.key_parameter_value(),
+            KeyParameterValue::Algorithm(Algorithm::RSA)
+        );
+
+        assert_eq!(*key_parameter.security_level(), SecurityLevel::STRONGBOX);
+    }
+}
+
+/// The storage_tests module first tests the 'new_from_sql' method for KeyParameters of different
+/// data types and then tests 'to_sql' method for KeyParameters of those
+/// different data types. The five different data types for KeyParameter values are:
+/// i) enums of u32
+/// ii) u32
+/// iii) u64
+/// iv) Vec<u8>
+/// v) bool
+#[cfg(test)]
+mod storage_tests {
+    use crate::error::*;
+    use crate::key_parameter::*;
+    use anyhow::Result;
+    use rusqlite::types::ToSql;
+    use rusqlite::{params, Connection, NO_PARAMS};
+
+    /// Test initializing a KeyParameter (with key parameter value corresponding to an enum of i32)
+    /// from a database table row.
+    #[test]
+    fn test_new_from_sql_enum_i32() -> Result<()> {
+        let db = init_db()?;
+        insert_into_keyparameter(
+            &db,
+            1,
+            Tag::ALGORITHM.0,
+            &Algorithm::RSA.0,
+            SecurityLevel::STRONGBOX.0,
+        )?;
+        let key_param = query_from_keyparameter(&db)?;
+        assert_eq!(Tag::ALGORITHM, key_param.get_tag());
+        assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::Algorithm(Algorithm::RSA));
+        assert_eq!(*key_param.security_level(), SecurityLevel::STRONGBOX);
+        Ok(())
+    }
+
+    /// Test initializing a KeyParameter (with key parameter value which is of i32)
+    /// from a database table row.
+    #[test]
+    fn test_new_from_sql_i32() -> Result<()> {
+        let db = init_db()?;
+        insert_into_keyparameter(&db, 1, Tag::KEY_SIZE.0, &1024, SecurityLevel::STRONGBOX.0)?;
+        let key_param = query_from_keyparameter(&db)?;
+        assert_eq!(Tag::KEY_SIZE, key_param.get_tag());
+        assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::KeySize(1024));
+        Ok(())
+    }
+
+    /// Test initializing a KeyParameter (with key parameter value which is of i64)
+    /// from a database table row.
+    #[test]
+    fn test_new_from_sql_i64() -> Result<()> {
+        let db = init_db()?;
+        // max value for i64, just to test corner cases
+        insert_into_keyparameter(
+            &db,
+            1,
+            Tag::RSA_PUBLIC_EXPONENT.0,
+            &(i64::MAX),
+            SecurityLevel::STRONGBOX.0,
+        )?;
+        let key_param = query_from_keyparameter(&db)?;
+        assert_eq!(Tag::RSA_PUBLIC_EXPONENT, key_param.get_tag());
+        assert_eq!(
+            *key_param.key_parameter_value(),
+            KeyParameterValue::RSAPublicExponent(i64::MAX)
+        );
+        Ok(())
+    }
+
+    /// Test initializing a KeyParameter (with key parameter value which is of bool)
+    /// from a database table row.
+    #[test]
+    fn test_new_from_sql_bool() -> Result<()> {
+        let db = init_db()?;
+        insert_into_keyparameter(&db, 1, Tag::CALLER_NONCE.0, &Null, SecurityLevel::STRONGBOX.0)?;
+        let key_param = query_from_keyparameter(&db)?;
+        assert_eq!(Tag::CALLER_NONCE, key_param.get_tag());
+        assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::CallerNonce);
+        Ok(())
+    }
+
+    /// Test initializing a KeyParameter (with key parameter value which is of Vec<u8>)
+    /// from a database table row.
+    #[test]
+    fn test_new_from_sql_vec_u8() -> Result<()> {
+        let db = init_db()?;
+        let app_id = String::from("MyAppID");
+        let app_id_bytes = app_id.into_bytes();
+        insert_into_keyparameter(
+            &db,
+            1,
+            Tag::APPLICATION_ID.0,
+            &app_id_bytes,
+            SecurityLevel::STRONGBOX.0,
+        )?;
+        let key_param = query_from_keyparameter(&db)?;
+        assert_eq!(Tag::APPLICATION_ID, key_param.get_tag());
+        assert_eq!(
+            *key_param.key_parameter_value(),
+            KeyParameterValue::ApplicationID(app_id_bytes)
+        );
+        Ok(())
+    }
+
+    /// Test storing a KeyParameter (with key parameter value which corresponds to an enum of i32)
+    /// in the database
+    #[test]
+    fn test_to_sql_enum_i32() -> Result<()> {
+        let db = init_db()?;
+        let kp = KeyParameter::new(
+            KeyParameterValue::Algorithm(Algorithm::RSA),
+            SecurityLevel::STRONGBOX,
+        );
+        store_keyparameter(&db, 1, &kp)?;
+        let key_param = query_from_keyparameter(&db)?;
+        assert_eq!(kp.get_tag(), key_param.get_tag());
+        assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+        assert_eq!(kp.security_level(), key_param.security_level());
+        Ok(())
+    }
+
+    /// Test storing a KeyParameter (with key parameter value which is of i32) in the database
+    #[test]
+    fn test_to_sql_i32() -> Result<()> {
+        let db = init_db()?;
+        let kp = KeyParameter::new(KeyParameterValue::KeySize(1024), SecurityLevel::STRONGBOX);
+        store_keyparameter(&db, 1, &kp)?;
+        let key_param = query_from_keyparameter(&db)?;
+        assert_eq!(kp.get_tag(), key_param.get_tag());
+        assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+        assert_eq!(kp.security_level(), key_param.security_level());
+        Ok(())
+    }
+
+    /// Test storing a KeyParameter (with key parameter value which is of i64) in the database
+    #[test]
+    fn test_to_sql_i64() -> Result<()> {
+        let db = init_db()?;
+        // max value for i64, just to test corner cases
+        let kp = KeyParameter::new(
+            KeyParameterValue::RSAPublicExponent(i64::MAX),
+            SecurityLevel::STRONGBOX,
+        );
+        store_keyparameter(&db, 1, &kp)?;
+        let key_param = query_from_keyparameter(&db)?;
+        assert_eq!(kp.get_tag(), key_param.get_tag());
+        assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+        assert_eq!(kp.security_level(), key_param.security_level());
+        Ok(())
+    }
+
+    /// Test storing a KeyParameter (with key parameter value which is of Vec<u8>) in the database
+    #[test]
+    fn test_to_sql_vec_u8() -> Result<()> {
+        let db = init_db()?;
+        let kp = KeyParameter::new(
+            KeyParameterValue::ApplicationID(String::from("MyAppID").into_bytes()),
+            SecurityLevel::STRONGBOX,
+        );
+        store_keyparameter(&db, 1, &kp)?;
+        let key_param = query_from_keyparameter(&db)?;
+        assert_eq!(kp.get_tag(), key_param.get_tag());
+        assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+        assert_eq!(kp.security_level(), key_param.security_level());
+        Ok(())
+    }
+
+    /// Test storing a KeyParameter (with key parameter value which is of i32) in the database
+    #[test]
+    fn test_to_sql_bool() -> Result<()> {
+        let db = init_db()?;
+        let kp = KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::STRONGBOX);
+        store_keyparameter(&db, 1, &kp)?;
+        let key_param = query_from_keyparameter(&db)?;
+        assert_eq!(kp.get_tag(), key_param.get_tag());
+        assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+        assert_eq!(kp.security_level(), key_param.security_level());
+        Ok(())
+    }
+
+    #[test]
+    /// Test Tag::Invalid
+    fn test_invalid_tag() -> Result<()> {
+        let db = init_db()?;
+        insert_into_keyparameter(&db, 1, 0, &123, 1)?;
+        let key_param = query_from_keyparameter(&db)?;
+        assert_eq!(Tag::INVALID, key_param.get_tag());
+        Ok(())
+    }
+
+    #[test]
+    fn test_non_existing_enum_variant() -> Result<()> {
+        let db = init_db()?;
+        insert_into_keyparameter(&db, 1, 100, &123, 1)?;
+        let key_param = query_from_keyparameter(&db)?;
+        assert_eq!(Tag::INVALID, key_param.get_tag());
+        Ok(())
+    }
+
+    #[test]
+    fn test_invalid_conversion_from_sql() -> Result<()> {
+        let db = init_db()?;
+        insert_into_keyparameter(&db, 1, Tag::ALGORITHM.0, &Null, 1)?;
+        tests::check_result_contains_error_string(
+            query_from_keyparameter(&db),
+            "Failed to read sql data for tag: ALGORITHM.",
+        );
+        Ok(())
+    }
+
+    /// Helper method to init database table for key parameter
+    fn init_db() -> Result<Connection> {
+        let db = Connection::open_in_memory().context("Failed to initialize sqlite connection.")?;
+        db.execute("ATTACH DATABASE ? as 'persistent';", params![""])
+            .context("Failed to attach databases.")?;
+        db.execute(
+            "CREATE TABLE IF NOT EXISTS persistent.keyparameter (
+                                keyentryid INTEGER,
+                                tag INTEGER,
+                                data ANY,
+                                security_level INTEGER);",
+            NO_PARAMS,
+        )
+        .context("Failed to initialize \"keyparameter\" table.")?;
+        Ok(db)
+    }
+
+    /// Helper method to insert an entry into key parameter table, with individual parameters
+    fn insert_into_keyparameter<T: ToSql>(
+        db: &Connection,
+        key_id: i64,
+        tag: i32,
+        value: &T,
+        security_level: i32,
+    ) -> Result<()> {
+        db.execute(
+            "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level)
+                VALUES(?, ?, ?, ?);",
+            params![key_id, tag, *value, security_level],
+        )?;
+        Ok(())
+    }
+
+    /// Helper method to store a key parameter instance.
+    fn store_keyparameter(db: &Connection, key_id: i64, kp: &KeyParameter) -> Result<()> {
+        db.execute(
+            "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level)
+                VALUES(?, ?, ?, ?);",
+            params![key_id, kp.get_tag().0, kp.key_parameter_value(), kp.security_level().0],
+        )?;
+        Ok(())
+    }
+
+    /// Helper method to query a row from keyparameter table
+    fn query_from_keyparameter(db: &Connection) -> Result<KeyParameter> {
+        let mut stmt =
+            db.prepare("SELECT tag, data, security_level FROM persistent.keyparameter")?;
+        let mut rows = stmt.query(NO_PARAMS)?;
+        let row = rows.next()?.unwrap();
+        Ok(KeyParameter::new_from_sql(
+            Tag(row.get(0)?),
+            &SqlField::new(1, row),
+            SecurityLevel(row.get(2)?),
+        )?)
+    }
+}
+
+/// The wire_tests module tests the 'convert_to_wire' and 'convert_from_wire' methods for
+/// KeyParameter, for the four different types used in KmKeyParameter, in addition to Invalid
+/// key parameter.
+/// i) bool
+/// ii) integer
+/// iii) longInteger
+/// iv) blob
+#[cfg(test)]
+mod wire_tests {
+    use crate::key_parameter::*;
+    /// unit tests for to conversions
+    #[test]
+    fn test_convert_to_wire_invalid() {
+        let kp = KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::STRONGBOX);
+        assert_eq!(
+            KmKeyParameter { tag: Tag::INVALID, value: KmKeyParameterValue::Invalid(0) },
+            kp.value.into()
+        );
+    }
+    #[test]
+    fn test_convert_to_wire_bool() {
+        let kp = KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::STRONGBOX);
+        assert_eq!(
+            KmKeyParameter { tag: Tag::CALLER_NONCE, value: KmKeyParameterValue::BoolValue(true) },
+            kp.value.into()
+        );
+    }
+    #[test]
+    fn test_convert_to_wire_integer() {
+        let kp = KeyParameter::new(
+            KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
+            SecurityLevel::STRONGBOX,
+        );
+        assert_eq!(
+            KmKeyParameter {
+                tag: Tag::PURPOSE,
+                value: KmKeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT)
+            },
+            kp.value.into()
+        );
+    }
+    #[test]
+    fn test_convert_to_wire_long_integer() {
+        let kp =
+            KeyParameter::new(KeyParameterValue::UserSecureID(i64::MAX), SecurityLevel::STRONGBOX);
+        assert_eq!(
+            KmKeyParameter {
+                tag: Tag::USER_SECURE_ID,
+                value: KmKeyParameterValue::LongInteger(i64::MAX)
+            },
+            kp.value.into()
+        );
+    }
+    #[test]
+    fn test_convert_to_wire_blob() {
+        let kp = KeyParameter::new(
+            KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
+            SecurityLevel::STRONGBOX,
+        );
+        assert_eq!(
+            KmKeyParameter {
+                tag: Tag::CONFIRMATION_TOKEN,
+                value: KmKeyParameterValue::Blob(String::from("ConfirmationToken").into_bytes())
+            },
+            kp.value.into()
+        );
+    }
+
+    /// unit tests for from conversion
+    #[test]
+    fn test_convert_from_wire_invalid() {
+        let aidl_kp = KmKeyParameter { tag: Tag::INVALID, ..Default::default() };
+        assert_eq!(KeyParameterValue::Invalid, aidl_kp.into());
+    }
+    #[test]
+    fn test_convert_from_wire_bool() {
+        let aidl_kp =
+            KmKeyParameter { tag: Tag::CALLER_NONCE, value: KmKeyParameterValue::BoolValue(true) };
+        assert_eq!(KeyParameterValue::CallerNonce, aidl_kp.into());
+    }
+    #[test]
+    fn test_convert_from_wire_integer() {
+        let aidl_kp = KmKeyParameter {
+            tag: Tag::PURPOSE,
+            value: KmKeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
+        };
+        assert_eq!(KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT), aidl_kp.into());
+    }
+    #[test]
+    fn test_convert_from_wire_long_integer() {
+        let aidl_kp = KmKeyParameter {
+            tag: Tag::USER_SECURE_ID,
+            value: KmKeyParameterValue::LongInteger(i64::MAX),
+        };
+        assert_eq!(KeyParameterValue::UserSecureID(i64::MAX), aidl_kp.into());
+    }
+    #[test]
+    fn test_convert_from_wire_blob() {
+        let aidl_kp = KmKeyParameter {
+            tag: Tag::CONFIRMATION_TOKEN,
+            value: KmKeyParameterValue::Blob(String::from("ConfirmationToken").into_bytes()),
+        };
+        assert_eq!(
+            KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
+            aidl_kp.into()
+        );
+    }
+}
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
new file mode 100644
index 0000000..4d4a718
--- /dev/null
+++ b/keystore2/src/keystore2_main.rs
@@ -0,0 +1,150 @@
+// 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.
+
+//! This crate implements the Keystore 2.0 service entry point.
+
+use keystore2::entropy;
+use keystore2::globals::ENFORCEMENTS;
+use keystore2::maintenance::Maintenance;
+use keystore2::metrics;
+use keystore2::remote_provisioning::RemoteProvisioningService;
+use keystore2::service::KeystoreService;
+use keystore2::{apc::ApcManager, shared_secret_negotiation};
+use keystore2::{authorization::AuthorizationManager, id_rotation::IdRotationState};
+use log::{error, info};
+use std::{panic, path::Path, sync::mpsc::channel};
+use vpnprofilestore::VpnProfileStore;
+
+static KS2_SERVICE_NAME: &str = "android.system.keystore2.IKeystoreService/default";
+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.maintenance";
+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() {
+    // Initialize android logging.
+    android_logger::init_once(
+        android_logger::Config::default().with_tag("keystore2").with_min_level(log::Level::Debug),
+    );
+    // Redirect panic messages to logcat.
+    panic::set_hook(Box::new(|panic_info| {
+        error!("{}", panic_info);
+    }));
+
+    // Saying hi.
+    info!("Keystore2 is starting.");
+
+    // Initialize the per boot database.
+    let _keep_me_alive = keystore2::database::KeystoreDB::keep_perboot_db_alive()
+        .expect("Failed to initialize the perboot database.");
+
+    let mut args = std::env::args();
+    args.next().expect("That's odd. How is there not even a first argument?");
+
+    // Keystore 2.0 cannot change to the database directory (typically /data/misc/keystore) on
+    // startup as Keystore 1.0 did because Keystore 2.0 is intended to run much earlier than
+    // Keystore 1.0. Instead we set a global variable to the database path.
+    // For the ground truth check the service startup rule for init (typically in keystore2.rc).
+    let id_rotation_state = if let Some(dir) = args.next() {
+        let db_path = Path::new(&dir);
+        *keystore2::globals::DB_PATH.lock().expect("Could not lock DB_PATH.") =
+            db_path.to_path_buf();
+        IdRotationState::new(&db_path)
+    } else {
+        panic!("Must specify a database directory.");
+    };
+
+    let (confirmation_token_sender, confirmation_token_receiver) = channel();
+
+    ENFORCEMENTS.install_confirmation_token_receiver(confirmation_token_receiver);
+
+    entropy::register_feeder();
+    shared_secret_negotiation::perform_shared_secret_negotiation();
+
+    info!("Starting thread pool now.");
+    binder::ProcessState::start_thread_pool();
+
+    let ks_service = KeystoreService::new_native_binder(id_rotation_state).unwrap_or_else(|e| {
+        panic!("Failed to create service {} because of {:?}.", KS2_SERVICE_NAME, e);
+    });
+    binder::add_service(KS2_SERVICE_NAME, ks_service.as_binder()).unwrap_or_else(|e| {
+        panic!("Failed to register service {} because of {:?}.", KS2_SERVICE_NAME, e);
+    });
+
+    let apc_service =
+        ApcManager::new_native_binder(confirmation_token_sender).unwrap_or_else(|e| {
+            panic!("Failed to create service {} because of {:?}.", APC_SERVICE_NAME, e);
+        });
+    binder::add_service(APC_SERVICE_NAME, apc_service.as_binder()).unwrap_or_else(|e| {
+        panic!("Failed to register service {} because of {:?}.", APC_SERVICE_NAME, e);
+    });
+
+    let authorization_service = AuthorizationManager::new_native_binder().unwrap_or_else(|e| {
+        panic!("Failed to create service {} because of {:?}.", AUTHORIZATION_SERVICE_NAME, e);
+    });
+    binder::add_service(AUTHORIZATION_SERVICE_NAME, authorization_service.as_binder())
+        .unwrap_or_else(|e| {
+            panic!("Failed to register service {} because of {:?}.", AUTHORIZATION_SERVICE_NAME, e);
+        });
+
+    let maintenance_service = Maintenance::new_native_binder().unwrap_or_else(|e| {
+        panic!("Failed to create service {} because of {:?}.", USER_MANAGER_SERVICE_NAME, e);
+    });
+    binder::add_service(USER_MANAGER_SERVICE_NAME, maintenance_service.as_binder()).unwrap_or_else(
+        |e| {
+            panic!("Failed to register service {} because of {:?}.", USER_MANAGER_SERVICE_NAME, e);
+        },
+    );
+
+    // 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
+            );
+        },
+    );
+
+    std::thread::spawn(|| {
+        match metrics::register_pull_metrics_callbacks() {
+            Err(e) => error!("register_pull_metrics_callbacks failed: {:?}.", e),
+            _ => info!("Pull metrics callbacks successfully registered."),
+        };
+    });
+
+    info!("Successfully registered Keystore 2.0 service.");
+
+    info!("Joining thread pool now.");
+    binder::ProcessState::join_thread_pool();
+}
diff --git a/keystore2/src/km_compat/Android.bp b/keystore2/src/km_compat/Android.bp
new file mode 100644
index 0000000..541788e
--- /dev/null
+++ b/keystore2/src/km_compat/Android.bp
@@ -0,0 +1,126 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+rust_library {
+    name: "libkeystore2_km_compat",
+    crate_name: "keystore2_km_compat",
+    srcs: ["lib.rs"],
+
+    rustlibs: [
+        "android.hardware.security.keymint-V1-rust",
+        "android.security.compat-rust",
+    ],
+    shared_libs: [
+        "libkm_compat_service",
+    ]
+}
+
+rust_test {
+    name: "keystore2_km_compat_test",
+    crate_name: "keystore2",
+    srcs: ["lib.rs"],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    rustlibs: [
+        "android.hardware.security.keymint-V1-rust",
+        "android.security.compat-rust",
+    ],
+    shared_libs: [
+        "libkm_compat_service",
+    ],
+}
+
+cc_library {
+    name: "libkm_compat",
+    srcs: ["km_compat.cpp"],
+    shared_libs: [
+        "android.hardware.keymaster@3.0",
+        "android.hardware.keymaster@4.0",
+        "android.hardware.keymaster@4.1",
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "android.hardware.security.secureclock-V1-ndk_platform",
+        "android.hardware.security.sharedsecret-V1-ndk_platform",
+        "android.security.compat-ndk_platform",
+        "android.system.keystore2-V1-ndk_platform",
+        "libbase",
+        "libbinder_ndk",
+        "libcrypto",
+        "libhidlbase",
+        "libkeymaster4_1support",
+        "libkeymint",
+        "libkeymint_support",
+        "libkeystore2_crypto",
+        "libutils",
+    ],
+}
+
+cc_library {
+    name: "libkm_compat_service",
+    srcs: ["km_compat_service.cpp"],
+    shared_libs: [
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "android.hardware.security.secureclock-V1-ndk_platform",
+        "android.hardware.security.sharedsecret-V1-ndk_platform",
+        "android.security.compat-ndk_platform",
+        "libbinder_ndk",
+        "libcrypto",
+        "libkm_compat",
+        "libkeymaster4_1support",
+        "libkeystore2_crypto",
+    ],
+}
+
+cc_test {
+    name: "keystore2_km_compat_test_cpp",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    srcs: [
+        "certificate_test.cpp",
+        "gtest_main.cpp",
+        "parameter_conversion_test.cpp",
+        "slot_test.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.keymaster@3.0",
+        "android.hardware.keymaster@4.0",
+        "android.hardware.keymaster@4.1",
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "android.hardware.security.secureclock-V1-ndk_platform",
+        "android.hardware.security.sharedsecret-V1-ndk_platform",
+        "android.security.compat-ndk_platform",
+        "android.system.keystore2-V1-ndk_platform",
+        "libbase",
+        "libbinder_ndk",
+        "libcrypto",
+        "libhidlbase",
+        "libkeymaster4_1support",
+        "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
new file mode 100644
index 0000000..06cb0cb
--- /dev/null
+++ b/keystore2/src/km_compat/certificate_test.cpp
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "km_compat.h"
+#include <keymint_support/keymint_tags.h>
+
+#include <aidl/android/hardware/security/keymint/Algorithm.h>
+#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>
+
+#define DEFINE_OPENSSL_OBJECT_POINTER(name) using name##_Ptr = bssl::UniquePtr<name>
+
+DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY);
+DEFINE_OPENSSL_OBJECT_POINTER(X509);
+
+using ::aidl::android::hardware::security::keymint::Algorithm;
+using ::aidl::android::hardware::security::keymint::BlockMode;
+using ::aidl::android::hardware::security::keymint::Certificate;
+using ::aidl::android::hardware::security::keymint::Digest;
+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;
+
+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()) {
+        return status;
+    }
+    return creationResult.certificateChain;
+}
+
+static void ensureCertChainSize(const std::variant<std::vector<Certificate>, ScopedAStatus>& result,
+                                uint32_t size) {
+    ASSERT_TRUE(std::holds_alternative<std::vector<Certificate>>(result));
+    auto certChain = std::get<std::vector<Certificate>>(result);
+    ASSERT_EQ(certChain.size(), size);
+}
+
+static void verify(const Certificate& certificate) {
+    const uint8_t* p = certificate.encodedCertificate.data();
+    X509_Ptr decoded_cert(d2i_X509(nullptr, &p, (long)certificate.encodedCertificate.size()));
+    EVP_PKEY_Ptr decoded_pkey(X509_get_pubkey(decoded_cert.get()));
+    ASSERT_TRUE(X509_verify(decoded_cert.get(), decoded_pkey.get()));
+}
+
+static std::vector<KeyParameter> getRSAKeyParams(const std::vector<KeyParameter>& extraParams) {
+    auto keyParams = 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_CERTIFICATE_NOT_BEFORE, 0),
+        KMV1::makeKeyParameter(KMV1::TAG_CERTIFICATE_NOT_AFTER, 253402300799000),
+    });
+    keyParams.insert(keyParams.end(), extraParams.begin(), extraParams.end());
+    return keyParams;
+}
+
+TEST(CertificateTest, TestRSAKeygen) {
+    auto keyParams = getRSAKeyParams({
+        KMV1::makeKeyParameter(KMV1::TAG_DIGEST, Digest::SHA_2_256),
+        KMV1::makeKeyParameter(KMV1::TAG_PADDING, PaddingMode::RSA_PSS),
+        KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED),
+        KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::SIGN),
+    });
+    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) {
+    auto keyParams = {
+        KMV1::makeKeyParameter(KMV1::TAG_ALGORITHM, Algorithm::AES),
+        KMV1::makeKeyParameter(KMV1::TAG_KEY_SIZE, 128),
+        KMV1::makeKeyParameter(KMV1::TAG_BLOCK_MODE, BlockMode::CBC),
+        KMV1::makeKeyParameter(KMV1::TAG_PADDING, PaddingMode::NONE),
+        KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::ENCRYPT),
+    };
+    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) {
+    auto keyParams = getRSAKeyParams({
+        KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::SIGN),
+        KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_CHALLENGE, 42),
+        KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_APPLICATION_ID, 42),
+    });
+    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) {
+    auto keyParams = getRSAKeyParams({
+        KMV1::makeKeyParameter(KMV1::TAG_DIGEST, Digest::SHA_2_256),
+        KMV1::makeKeyParameter(KMV1::TAG_PADDING, PaddingMode::RSA_PSS),
+        KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED, true),
+        KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::SIGN),
+    });
+    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) {
+    auto keyParams = getRSAKeyParams({
+        KMV1::makeKeyParameter(KMV1::TAG_DIGEST, Digest::SHA_2_256),
+        KMV1::makeKeyParameter(KMV1::TAG_PADDING, PaddingMode::RSA_PSS),
+        KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::SIGN),
+    });
+    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/keystore/binder/android/security/keymaster/OperationResult.aidl b/keystore2/src/km_compat/gtest_main.cpp
similarity index 74%
copy from keystore/binder/android/security/keymaster/OperationResult.aidl
copy to keystore2/src/km_compat/gtest_main.cpp
index db689d4..149cbbc 100644
--- a/keystore/binder/android/security/keymaster/OperationResult.aidl
+++ b/keystore2/src/km_compat/gtest_main.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 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.
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-package android.security.keymaster;
-
-/* @hide */
-parcelable OperationResult cpp_header "keystore/OperationResult.h";
+#include <gtest/gtest.h>
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
new file mode 100644
index 0000000..bdc3f2a
--- /dev/null
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -0,0 +1,1386 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "km_compat.h"
+
+#include "km_compat_type_conversion.h"
+#include <AndroidKeyMintDevice.h>
+#include <aidl/android/hardware/security/keymint/Algorithm.h>
+#include <aidl/android/hardware/security/keymint/Digest.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/keymint/KeyParameterValue.h>
+#include <aidl/android/hardware/security/keymint/PaddingMode.h>
+#include <aidl/android/system/keystore2/ResponseCode.h>
+#include <android-base/logging.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+#include <binder/IServiceManager.h>
+#include <hardware/keymaster_defs.h>
+#include <keymasterV4_1/Keymaster.h>
+#include <keymasterV4_1/Keymaster3.h>
+#include <keymasterV4_1/Keymaster4.h>
+
+#include <chrono>
+
+#include "certificate_utils.h"
+
+using ::aidl::android::hardware::security::keymint::Algorithm;
+using ::aidl::android::hardware::security::keymint::CreateKeyMintDevice;
+using ::aidl::android::hardware::security::keymint::Digest;
+using ::aidl::android::hardware::security::keymint::KeyParameterValue;
+using ::aidl::android::hardware::security::keymint::PaddingMode;
+using ::aidl::android::hardware::security::keymint::Tag;
+using ::aidl::android::system::keystore2::ResponseCode;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::keymaster::V4_0::TagType;
+using ::android::hidl::manager::V1_2::IServiceManager;
+using V4_0_HardwareAuthToken = ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+using V4_0_HmacSharingParameters = ::android::hardware::keymaster::V4_0::HmacSharingParameters;
+using V4_0_KeyCharacteristics = ::android::hardware::keymaster::V4_0::KeyCharacteristics;
+using V4_0_KeyFormat = ::android::hardware::keymaster::V4_0::KeyFormat;
+using V4_0_KeyParameter = ::android::hardware::keymaster::V4_0::KeyParameter;
+using V4_0_VerificationToken = ::android::hardware::keymaster::V4_0::VerificationToken;
+namespace V4_0 = ::android::hardware::keymaster::V4_0;
+namespace V4_1 = ::android::hardware::keymaster::V4_1;
+namespace KMV1 = ::aidl::android::hardware::security::keymint;
+
+using namespace std::chrono_literals;
+using std::chrono::duration_cast;
+
+// Utility functions
+
+// Returns true if this parameter may be passed to attestKey.
+bool isAttestationParameter(const KMV1::KeyParameter& param) {
+    switch (param.tag) {
+    case Tag::APPLICATION_ID:
+    case Tag::APPLICATION_DATA:
+    case Tag::ATTESTATION_CHALLENGE:
+    case Tag::ATTESTATION_APPLICATION_ID:
+    case Tag::ATTESTATION_ID_BRAND:
+    case Tag::ATTESTATION_ID_DEVICE:
+    case Tag::ATTESTATION_ID_PRODUCT:
+    case Tag::ATTESTATION_ID_SERIAL:
+    case Tag::ATTESTATION_ID_IMEI:
+    case Tag::ATTESTATION_ID_MEID:
+    case Tag::ATTESTATION_ID_MANUFACTURER:
+    case Tag::ATTESTATION_ID_MODEL:
+    case Tag::CERTIFICATE_SERIAL:
+    case Tag::CERTIFICATE_SUBJECT:
+    case Tag::CERTIFICATE_NOT_BEFORE:
+    case Tag::CERTIFICATE_NOT_AFTER:
+    case Tag::INCLUDE_UNIQUE_ID:
+    case Tag::DEVICE_UNIQUE_ATTESTATION:
+        return true;
+    default:
+        return false;
+    }
+}
+
+// Returns true if this parameter may be passed to generate/importKey.
+bool isKeyCreationParameter(const KMV1::KeyParameter& param) {
+    switch (param.tag) {
+    case Tag::APPLICATION_ID:
+    case Tag::APPLICATION_DATA:
+    case Tag::CERTIFICATE_SERIAL:
+    case Tag::CERTIFICATE_SUBJECT:
+    case Tag::CERTIFICATE_NOT_BEFORE:
+    case Tag::CERTIFICATE_NOT_AFTER:
+    case Tag::PURPOSE:
+    case Tag::ALGORITHM:
+    case Tag::KEY_SIZE:
+    case Tag::BLOCK_MODE:
+    case Tag::DIGEST:
+    case Tag::PADDING:
+    case Tag::CALLER_NONCE:
+    case Tag::MIN_MAC_LENGTH:
+    case Tag::EC_CURVE:
+    case Tag::RSA_PUBLIC_EXPONENT:
+    case Tag::RSA_OAEP_MGF_DIGEST:
+    case Tag::BOOTLOADER_ONLY:
+    case Tag::ROLLBACK_RESISTANCE:
+    case Tag::EARLY_BOOT_ONLY:
+    case Tag::ACTIVE_DATETIME:
+    case Tag::ORIGINATION_EXPIRE_DATETIME:
+    case Tag::USAGE_EXPIRE_DATETIME:
+    case Tag::MIN_SECONDS_BETWEEN_OPS:
+    case Tag::MAX_USES_PER_BOOT:
+    case Tag::USAGE_COUNT_LIMIT:
+    case Tag::USER_ID:
+    case Tag::USER_SECURE_ID:
+    case Tag::NO_AUTH_REQUIRED:
+    case Tag::USER_AUTH_TYPE:
+    case Tag::AUTH_TIMEOUT:
+    case Tag::ALLOW_WHILE_ON_BODY:
+    case Tag::TRUSTED_USER_PRESENCE_REQUIRED:
+    case Tag::TRUSTED_CONFIRMATION_REQUIRED:
+    case Tag::UNLOCKED_DEVICE_REQUIRED:
+    case Tag::CREATION_DATETIME:
+    case Tag::UNIQUE_ID:
+    case Tag::IDENTITY_CREDENTIAL_KEY:
+    case Tag::STORAGE_KEY:
+    case Tag::MAC_LENGTH:
+        return true;
+    default:
+        return false;
+    }
+}
+
+// Size of prefix for blobs, see keyBlobPrefix().
+//
+const size_t kKeyBlobPrefixSize = 8;
+
+// Magic used in blob prefix, see keyBlobPrefix().
+//
+const uint8_t kKeyBlobMagic[7] = {'p', 'K', 'M', 'b', 'l', 'o', 'b'};
+
+// Prefixes a keyblob returned by e.g. generateKey() with information on whether it
+// originated from the real underlying KeyMaster HAL or from soft-KeyMint.
+//
+// When dealing with a keyblob, use prefixedKeyBlobRemovePrefix() to remove the
+// prefix and prefixedKeyBlobIsSoftKeyMint() to determine its origin.
+//
+// Note how the prefix itself has a magic marker ("pKMblob") which can be used
+// to identify if a blob has a prefix at all (it's assumed that any valid blob
+// from KeyMint or KeyMaster HALs never starts with the magic). This is needed
+// because blobs persisted to disk prior to using this code will not have the
+// prefix and in that case we want prefixedKeyBlobRemovePrefix() to still work.
+//
+std::vector<uint8_t> keyBlobPrefix(const std::vector<uint8_t>& blob, bool isSoftKeyMint) {
+    std::vector<uint8_t> result;
+    result.reserve(blob.size() + kKeyBlobPrefixSize);
+    result.insert(result.begin(), kKeyBlobMagic, kKeyBlobMagic + sizeof kKeyBlobMagic);
+    result.push_back(isSoftKeyMint ? 1 : 0);
+    std::copy(blob.begin(), blob.end(), std::back_inserter(result));
+    return result;
+}
+
+// Helper for prefixedKeyBlobRemovePrefix() and prefixedKeyBlobIsSoftKeyMint().
+//
+// First bool is whether there's a valid prefix. If there is, the second bool is
+// the |isSoftKeyMint| value of the prefix
+//
+std::pair<bool, bool> prefixedKeyBlobParsePrefix(const std::vector<uint8_t>& prefixedBlob) {
+    // Having a unprefixed blob is not that uncommon, for example all devices
+    // upgrading to keystore2 (so e.g. upgrading to Android 12) will have
+    // unprefixed blobs. So don't spew warnings/errors in this case...
+    if (prefixedBlob.size() < kKeyBlobPrefixSize) {
+        return std::make_pair(false, false);
+    }
+    if (std::memcmp(prefixedBlob.data(), kKeyBlobMagic, sizeof kKeyBlobMagic) != 0) {
+        return std::make_pair(false, false);
+    }
+    if (prefixedBlob[kKeyBlobPrefixSize - 1] != 0 && prefixedBlob[kKeyBlobPrefixSize - 1] != 1) {
+        return std::make_pair(false, false);
+    }
+    bool isSoftKeyMint = (prefixedBlob[kKeyBlobPrefixSize - 1] == 1);
+    return std::make_pair(true, isSoftKeyMint);
+}
+
+// Returns the prefix from a blob. If there's no prefix, returns the passed-in blob.
+//
+std::vector<uint8_t> prefixedKeyBlobRemovePrefix(const std::vector<uint8_t>& prefixedBlob) {
+    auto parsed = prefixedKeyBlobParsePrefix(prefixedBlob);
+    if (!parsed.first) {
+        // Not actually prefixed, blob was probably persisted to disk prior to the
+        // prefixing code being introduced.
+        return prefixedBlob;
+    }
+    return std::vector<uint8_t>(prefixedBlob.begin() + kKeyBlobPrefixSize, prefixedBlob.end());
+}
+
+// Returns true if the blob's origin is soft-KeyMint, false otherwise or if there
+// is no prefix on the passed-in blob.
+//
+bool prefixedKeyBlobIsSoftKeyMint(const std::vector<uint8_t>& prefixedBlob) {
+    auto parsed = prefixedKeyBlobParsePrefix(prefixedBlob);
+    return parsed.second;
+}
+
+/*
+ * Returns true if the parameter is not understood by KM 4.1 and older but can be enforced by
+ * Keystore. These parameters need to be included in the returned KeyCharacteristics, but will not
+ * be passed to the legacy backend.
+ */
+bool isNewAndKeystoreEnforceable(const KMV1::KeyParameter& param) {
+    switch (param.tag) {
+    case KMV1::Tag::MAX_BOOT_LEVEL:
+        return true;
+    case KMV1::Tag::USAGE_COUNT_LIMIT:
+        return true;
+    default:
+        return false;
+    }
+}
+
+std::vector<KMV1::KeyParameter>
+extractGenerationParams(const std::vector<KMV1::KeyParameter>& params) {
+    std::vector<KMV1::KeyParameter> result;
+    std::copy_if(params.begin(), params.end(), std::back_inserter(result), isKeyCreationParameter);
+    return result;
+}
+
+std::vector<KMV1::KeyParameter>
+extractAttestationParams(const std::vector<KMV1::KeyParameter>& params) {
+    std::vector<KMV1::KeyParameter> result;
+    std::copy_if(params.begin(), params.end(), std::back_inserter(result), isAttestationParameter);
+    return result;
+}
+
+std::vector<KMV1::KeyParameter>
+extractNewAndKeystoreEnforceableParams(const std::vector<KMV1::KeyParameter>& params) {
+    std::vector<KMV1::KeyParameter> result;
+    std::copy_if(params.begin(), params.end(), std::back_inserter(result),
+                 isNewAndKeystoreEnforceable);
+    return result;
+}
+
+ScopedAStatus convertErrorCode(KMV1::ErrorCode result) {
+    if (result == KMV1::ErrorCode::OK) {
+        return ScopedAStatus::ok();
+    }
+    return ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(result));
+}
+
+// Converts a V4 error code into a ScopedAStatus
+ScopedAStatus convertErrorCode(V4_0_ErrorCode result) {
+    return convertErrorCode(convert(result));
+}
+
+static KMV1::ErrorCode toErrorCode(const ScopedAStatus& status) {
+    if (status.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+        return static_cast<KMV1::ErrorCode>(status.getServiceSpecificError());
+    } else {
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+}
+
+static std::vector<V4_0::KeyParameter>
+convertKeyParametersToLegacy(const std::vector<KeyParameter>& kps) {
+    std::vector<V4_0::KeyParameter> legacyKps;
+    legacyKps.reserve(kps.size());
+    for (const auto& kp : kps) {
+        auto p = convertKeyParameterToLegacy(kp);
+        if (p.tag != V4_0::Tag::INVALID) {
+            legacyKps.push_back(std::move(p));
+        }
+    }
+    return legacyKps;
+}
+
+static std::vector<KeyParameter>
+convertKeyParametersFromLegacy(const std::vector<V4_0_KeyParameter>& legacyKps) {
+    std::vector<KeyParameter> kps(legacyKps.size());
+    std::transform(legacyKps.begin(), legacyKps.end(), kps.begin(), convertKeyParameterFromLegacy);
+    return kps;
+}
+
+static std::vector<KeyCharacteristics>
+processLegacyCharacteristics(KeyMintSecurityLevel securityLevel,
+                             const std::vector<KeyParameter>& genParams,
+                             const V4_0_KeyCharacteristics& legacyKc) {
+
+    KeyCharacteristics keystoreEnforced{KeyMintSecurityLevel::KEYSTORE,
+                                        convertKeyParametersFromLegacy(legacyKc.softwareEnforced)};
+
+    // Add all parameters that we know can be enforced by keystore but not by the legacy backend.
+    auto unsupported_requested = extractNewAndKeystoreEnforceableParams(genParams);
+    std::copy(unsupported_requested.begin(), unsupported_requested.end(),
+              std::back_insert_iterator(keystoreEnforced.authorizations));
+
+    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.
+        if (legacyKc.hardwareEnforced.size() != 0) {
+            LOG(WARNING) << "Unexpected hardware enforced parameters.";
+        }
+        return {keystoreEnforced};
+    }
+
+    KeyCharacteristics hwEnforced{securityLevel,
+                                  convertKeyParametersFromLegacy(legacyKc.hardwareEnforced)};
+    return {hwEnforced, keystoreEnforced};
+}
+
+static V4_0_KeyFormat convertKeyFormatToLegacy(const KeyFormat& kf) {
+    return static_cast<V4_0_KeyFormat>(kf);
+}
+
+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.authenticatorType =
+        static_cast<::android::hardware::keymaster::V4_0::HardwareAuthenticatorType>(
+            at->authenticatorType);
+    legacyAt.timestamp = at->timestamp.milliSeconds;
+    legacyAt.mac = at->mac;
+    return legacyAt;
+}
+
+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;
+    // Legacy verification tokens were always minted by TEE.
+    legacyVt.securityLevel = V4_0::SecurityLevel::TRUSTED_ENVIRONMENT;
+    legacyVt.mac = tst->mac;
+    return legacyVt;
+}
+
+static V4_0_HmacSharingParameters
+convertSharedSecretParameterToLegacy(const SharedSecretParameters& ssp) {
+    V4_0_HmacSharingParameters legacyHsp;
+    legacyHsp.seed = ssp.seed;
+    std::copy(ssp.nonce.begin(), ssp.nonce.end(), legacyHsp.nonce.data());
+    return legacyHsp;
+}
+
+static std::vector<V4_0_HmacSharingParameters>
+convertSharedSecretParametersToLegacy(const std::vector<SharedSecretParameters>& legacySsps) {
+    std::vector<V4_0_HmacSharingParameters> ssps(legacySsps.size());
+    std::transform(legacySsps.begin(), legacySsps.end(), ssps.begin(),
+                   convertSharedSecretParameterToLegacy);
+    return ssps;
+}
+
+void OperationSlots::setNumFreeSlots(uint8_t numFreeSlots) {
+    std::lock_guard<std::mutex> lock(mNumFreeSlotsMutex);
+    mNumFreeSlots = numFreeSlots;
+}
+
+bool OperationSlots::claimSlot() {
+    std::lock_guard<std::mutex> lock(mNumFreeSlotsMutex);
+    if (mNumFreeSlots > 0) {
+        mNumFreeSlots--;
+        return true;
+    }
+    return false;
+}
+
+void OperationSlots::freeSlot() {
+    std::lock_guard<std::mutex> lock(mNumFreeSlotsMutex);
+    mNumFreeSlots++;
+}
+
+void OperationSlot::freeSlot() {
+    if (mIsActive) {
+        mOperationSlots->freeSlot();
+        mIsActive = false;
+    }
+}
+
+// KeyMintDevice implementation
+
+ScopedAStatus KeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* _aidl_return) {
+    auto result = mDevice->halVersion();
+    _aidl_return->versionNumber = result.majorVersion * 10 + result.minorVersion;
+    securityLevel_ = convert(result.securityLevel);
+    _aidl_return->securityLevel = securityLevel_;
+    _aidl_return->keyMintName = result.keymasterName;
+    _aidl_return->keyMintAuthorName = result.authorName;
+    _aidl_return->timestampTokenRequired = securityLevel_ == KMV1::SecurityLevel::STRONGBOX;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus KeyMintDevice::addRngEntropy(const std::vector<uint8_t>& in_data) {
+    auto result = mDevice->addRngEntropy(in_data);
+    if (!result.isOk()) {
+        LOG(ERROR) << __func__ << " transaction failed. " << result.description();
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    }
+    return convertErrorCode(result);
+}
+
+ScopedAStatus KeyMintDevice::generateKey(const std::vector<KeyParameter>& inKeyParams,
+                                         const std::optional<AttestationKey>& in_attestationKey,
+                                         KeyCreationResult* out_creationResult) {
+
+    // Since KeyMaster doesn't support ECDH, route all key creation requests to
+    // soft-KeyMint if and only an ECDH key is requested.
+    //
+    // For this to work we'll need to also route begin() and deleteKey() calls to
+    // soft-KM. In order to do that, we'll prefix all keyblobs with whether it was
+    // created by the real underlying KeyMaster HAL or whether it was created by
+    // soft-KeyMint.
+    //
+    // See keyBlobPrefix() for more discussion.
+    //
+    for (const auto& keyParam : inKeyParams) {
+        if (keyParam.tag == Tag::PURPOSE &&
+            keyParam.value.get<KeyParameterValue::Tag::keyPurpose>() == KeyPurpose::AGREE_KEY) {
+            auto ret =
+                softKeyMintDevice_->generateKey(inKeyParams, in_attestationKey, out_creationResult);
+            if (ret.isOk()) {
+                out_creationResult->keyBlob = keyBlobPrefix(out_creationResult->keyBlob, true);
+            }
+            return ret;
+        }
+    }
+
+    auto legacyKeyGenParams = convertKeyParametersToLegacy(extractGenerationParams(inKeyParams));
+    KMV1::ErrorCode errorCode;
+    auto result = mDevice->generateKey(
+        legacyKeyGenParams, [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
+                                const V4_0_KeyCharacteristics& keyCharacteristics) {
+            errorCode = convert(error);
+            out_creationResult->keyBlob = keyBlobPrefix(keyBlob, false);
+            out_creationResult->keyCharacteristics =
+                processLegacyCharacteristics(securityLevel_, inKeyParams, keyCharacteristics);
+        });
+    if (!result.isOk()) {
+        LOG(ERROR) << __func__ << " transaction failed. " << result.description();
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    }
+    if (errorCode == KMV1::ErrorCode::OK) {
+        auto cert = getCertificate(inKeyParams, out_creationResult->keyBlob);
+        if (std::holds_alternative<KMV1::ErrorCode>(cert)) {
+            auto code = std::get<KMV1::ErrorCode>(cert);
+            // We return OK in successful cases that do not generate a certificate.
+            if (code != KMV1::ErrorCode::OK) {
+                errorCode = code;
+                deleteKey(out_creationResult->keyBlob);
+            }
+        } else {
+            out_creationResult->certificateChain = std::get<std::vector<Certificate>>(cert);
+        }
+    }
+    return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintDevice::importKey(const std::vector<KeyParameter>& inKeyParams,
+                                       KeyFormat in_inKeyFormat,
+                                       const std::vector<uint8_t>& in_inKeyData,
+                                       const std::optional<AttestationKey>& /* in_attestationKey */,
+                                       KeyCreationResult* out_creationResult) {
+    auto legacyKeyGENParams = convertKeyParametersToLegacy(extractGenerationParams(inKeyParams));
+    auto legacyKeyFormat = convertKeyFormatToLegacy(in_inKeyFormat);
+    KMV1::ErrorCode errorCode;
+    auto result = mDevice->importKey(legacyKeyGENParams, legacyKeyFormat, in_inKeyData,
+                                     [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
+                                         const V4_0_KeyCharacteristics& keyCharacteristics) {
+                                         errorCode = convert(error);
+                                         out_creationResult->keyBlob =
+                                             keyBlobPrefix(keyBlob, false);
+                                         out_creationResult->keyCharacteristics =
+                                             processLegacyCharacteristics(
+                                                 securityLevel_, inKeyParams, keyCharacteristics);
+                                     });
+    if (!result.isOk()) {
+        LOG(ERROR) << __func__ << " transaction failed. " << result.description();
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    }
+    if (errorCode == KMV1::ErrorCode::OK) {
+        auto cert = getCertificate(inKeyParams, out_creationResult->keyBlob);
+        if (std::holds_alternative<KMV1::ErrorCode>(cert)) {
+            auto code = std::get<KMV1::ErrorCode>(cert);
+            // We return OK in successful cases that do not generate a certificate.
+            if (code != KMV1::ErrorCode::OK) {
+                errorCode = code;
+                deleteKey(out_creationResult->keyBlob);
+            }
+        } else {
+            out_creationResult->certificateChain = std::get<std::vector<Certificate>>(cert);
+        }
+    }
+    return convertErrorCode(errorCode);
+}
+
+ScopedAStatus
+KeyMintDevice::importWrappedKey(const std::vector<uint8_t>& in_inWrappedKeyData,
+                                const std::vector<uint8_t>& in_inPrefixedWrappingKeyBlob,
+                                const std::vector<uint8_t>& in_inMaskingKey,
+                                const std::vector<KeyParameter>& in_inUnwrappingParams,
+                                int64_t in_inPasswordSid, int64_t in_inBiometricSid,
+                                KeyCreationResult* out_creationResult) {
+    const std::vector<uint8_t>& wrappingKeyBlob =
+        prefixedKeyBlobRemovePrefix(in_inPrefixedWrappingKeyBlob);
+    if (prefixedKeyBlobIsSoftKeyMint(in_inPrefixedWrappingKeyBlob)) {
+        return softKeyMintDevice_->importWrappedKey(
+            in_inWrappedKeyData, wrappingKeyBlob, in_inMaskingKey, in_inUnwrappingParams,
+            in_inPasswordSid, in_inBiometricSid, out_creationResult);
+    }
+
+    auto legacyUnwrappingParams = convertKeyParametersToLegacy(in_inUnwrappingParams);
+    KMV1::ErrorCode errorCode;
+    auto result = mDevice->importWrappedKey(
+        in_inWrappedKeyData, wrappingKeyBlob, in_inMaskingKey, legacyUnwrappingParams,
+        in_inPasswordSid, in_inBiometricSid,
+        [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
+            const V4_0_KeyCharacteristics& keyCharacteristics) {
+            errorCode = convert(error);
+            out_creationResult->keyBlob = keyBlobPrefix(keyBlob, false);
+            out_creationResult->keyCharacteristics =
+                processLegacyCharacteristics(securityLevel_, {}, keyCharacteristics);
+        });
+    if (!result.isOk()) {
+        LOG(ERROR) << __func__ << " transaction failed. " << result.description();
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    }
+    return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintDevice::upgradeKey(const std::vector<uint8_t>& in_inKeyBlobToUpgrade,
+                                        const std::vector<KeyParameter>& in_inUpgradeParams,
+                                        std::vector<uint8_t>* _aidl_return) {
+    auto legacyUpgradeParams = convertKeyParametersToLegacy(in_inUpgradeParams);
+    V4_0_ErrorCode errorCode;
+
+    auto result =
+        mDevice->upgradeKey(prefixedKeyBlobRemovePrefix(in_inKeyBlobToUpgrade), legacyUpgradeParams,
+                            [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& upgradedKeyBlob) {
+                                errorCode = error;
+                                *_aidl_return = keyBlobPrefix(upgradedKeyBlob, false);
+                            });
+    if (!result.isOk()) {
+        LOG(ERROR) << __func__ << " transaction failed. " << result.description();
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    }
+    return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintDevice::deleteKey(const std::vector<uint8_t>& prefixedKeyBlob) {
+    const std::vector<uint8_t>& keyBlob = prefixedKeyBlobRemovePrefix(prefixedKeyBlob);
+    if (prefixedKeyBlobIsSoftKeyMint(prefixedKeyBlob)) {
+        return softKeyMintDevice_->deleteKey(keyBlob);
+    }
+
+    auto result = mDevice->deleteKey(keyBlob);
+    if (!result.isOk()) {
+        LOG(ERROR) << __func__ << " transaction failed. " << result.description();
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    }
+    return convertErrorCode(result);
+}
+
+ScopedAStatus KeyMintDevice::deleteAllKeys() {
+    auto result = mDevice->deleteAllKeys();
+    if (!result.isOk()) {
+        LOG(ERROR) << __func__ << " transaction failed. " << result.description();
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    }
+    return convertErrorCode(result);
+}
+
+// We're not implementing this.
+ScopedAStatus KeyMintDevice::destroyAttestationIds() {
+    return ScopedAStatus::fromServiceSpecificError(
+        static_cast<int32_t>(V4_0_ErrorCode::UNIMPLEMENTED));
+}
+
+ScopedAStatus KeyMintDevice::begin(KeyPurpose in_inPurpose,
+                                   const std::vector<uint8_t>& prefixedKeyBlob,
+                                   const std::vector<KeyParameter>& in_inParams,
+                                   const std::optional<HardwareAuthToken>& in_inAuthToken,
+                                   BeginResult* _aidl_return) {
+    if (!mOperationSlots.claimSlot()) {
+        return convertErrorCode(V4_0_ErrorCode::TOO_MANY_OPERATIONS);
+    }
+
+    const std::vector<uint8_t>& in_inKeyBlob = prefixedKeyBlobRemovePrefix(prefixedKeyBlob);
+    if (prefixedKeyBlobIsSoftKeyMint(prefixedKeyBlob)) {
+        return softKeyMintDevice_->begin(in_inPurpose, in_inKeyBlob, in_inParams, in_inAuthToken,
+                                         _aidl_return);
+    }
+
+    auto legacyPurpose =
+        static_cast<::android::hardware::keymaster::V4_0::KeyPurpose>(in_inPurpose);
+    auto legacyParams = convertKeyParametersToLegacy(in_inParams);
+    auto legacyAuthToken = convertAuthTokenToLegacy(in_inAuthToken);
+    KMV1::ErrorCode errorCode;
+    auto result = mDevice->begin(
+        legacyPurpose, in_inKeyBlob, legacyParams, legacyAuthToken,
+        [&](V4_0_ErrorCode error, const hidl_vec<V4_0_KeyParameter>& outParams,
+            uint64_t operationHandle) {
+            errorCode = convert(error);
+            _aidl_return->challenge = operationHandle;
+            _aidl_return->params = convertKeyParametersFromLegacy(outParams);
+            _aidl_return->operation = ndk::SharedRefBase::make<KeyMintOperation>(
+                mDevice, operationHandle, &mOperationSlots, error == V4_0_ErrorCode::OK);
+        });
+    if (!result.isOk()) {
+        LOG(ERROR) << __func__ << " transaction failed. " << result.description();
+        errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+    if (errorCode != KMV1::ErrorCode::OK) {
+        mOperationSlots.freeSlot();
+    }
+    return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintDevice::deviceLocked(bool passwordOnly,
+                                          const std::optional<TimeStampToken>& timestampToken) {
+    V4_0_VerificationToken token;
+    if (timestampToken.has_value()) {
+        token = convertTimestampTokenToLegacy(timestampToken.value());
+    }
+    auto ret = mDevice->deviceLocked(passwordOnly, token);
+    if (!ret.isOk()) {
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    } else {
+        return convertErrorCode(KMV1::ErrorCode::OK);
+    }
+}
+
+ScopedAStatus KeyMintDevice::earlyBootEnded() {
+    auto ret = mDevice->earlyBootEnded();
+    if (!ret.isOk()) {
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    } else {
+        return convertErrorCode(KMV1::ErrorCode::OK);
+    }
+}
+
+ScopedAStatus
+KeyMintDevice::convertStorageKeyToEphemeral(const std::vector<uint8_t>& prefixedStorageKeyBlob,
+                                            std::vector<uint8_t>* ephemeralKeyBlob) {
+    KMV1::ErrorCode km_error;
+
+    /*
+     * Wrapped storage keys cannot be emulated (and they don't need to, because if a platform
+     * supports wrapped storage keys, then the legacy backend will support it too. So error out
+     * if the wrapped storage key given is a soft keymint key.
+     */
+    if (prefixedKeyBlobIsSoftKeyMint(prefixedStorageKeyBlob)) {
+        return convertErrorCode(KMV1::ErrorCode::UNIMPLEMENTED);
+    }
+
+    const std::vector<uint8_t>& storageKeyBlob =
+        prefixedKeyBlobRemovePrefix(prefixedStorageKeyBlob);
+
+    auto hidlCb = [&](V4_0_ErrorCode ret, const hidl_vec<uint8_t>& exportedKeyBlob) {
+        km_error = convert(ret);
+        if (km_error != KMV1::ErrorCode::OK) return;
+        /*
+         * This must return the blob without the prefix since it will be used directly
+         * as a storage encryption key. But this is alright, since this wrapped ephemeral
+         * key shouldn't/won't ever be used with keymint.
+         */
+        *ephemeralKeyBlob = exportedKeyBlob;
+    };
+
+    auto ret = mDevice->exportKey(V4_0_KeyFormat::RAW, storageKeyBlob, {}, {}, hidlCb);
+    if (!ret.isOk()) {
+        LOG(ERROR) << __func__ << " export_key failed: " << ret.description();
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    }
+    if (km_error != KMV1::ErrorCode::OK)
+        LOG(ERROR) << __func__ << " export_key failed, code " << int32_t(km_error);
+
+    return convertErrorCode(km_error);
+}
+
+ScopedAStatus
+KeyMintDevice::getKeyCharacteristics(const std::vector<uint8_t>& /* storageKeyBlob */,
+                                     const std::vector<uint8_t>& /* appId */,
+                                     const std::vector<uint8_t>& /* appData */,
+                                     std::vector<KeyCharacteristics>* /* keyCharacteristics */) {
+    return convertErrorCode(KMV1::ErrorCode::UNIMPLEMENTED);
+}
+
+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, {V4_0::makeKeyParameter(V4_0::TAG_ASSOCIATED_DATA, 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();
+
+    return convertErrorCode(errorCode);
+}
+
+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_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));
+    }
+
+    KMV1::ErrorCode errorCode;
+    auto result = mDevice->finish(
+        mOperationHandle, inParams, input, signature, authToken, verificationToken,
+        [&](V4_0_ErrorCode error, auto /* outParams */, const hidl_vec<uint8_t>& output) {
+            errorCode = convert(error);
+            *out_output = output;
+        });
+
+    mOperationSlot.freeSlot();
+    if (!result.isOk()) {
+        LOG(ERROR) << __func__ << " transaction failed. " << result.description();
+        errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+    return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintOperation::abort() {
+    auto result = mDevice->abort(mOperationHandle);
+    mOperationSlot.freeSlot();
+    if (!result.isOk()) {
+        LOG(ERROR) << __func__ << " transaction failed. " << result.description();
+        return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
+    }
+    return convertErrorCode(result);
+}
+
+KeyMintOperation::~KeyMintOperation() {
+    if (mOperationSlot.hasSlot()) {
+        auto error = abort();
+        if (!error.isOk()) {
+            LOG(WARNING) << "Error calling abort in ~KeyMintOperation: " << error.getMessage();
+        }
+    }
+}
+
+// SecureClock implementation
+
+ScopedAStatus SecureClock::generateTimeStamp(int64_t in_challenge, TimeStampToken* _aidl_return) {
+    KMV1::ErrorCode errorCode;
+    auto result = mDevice->verifyAuthorization(
+        in_challenge, {}, V4_0_HardwareAuthToken(),
+        [&](V4_0_ErrorCode error, const V4_0_VerificationToken& token) {
+            errorCode = convert(error);
+            _aidl_return->challenge = token.challenge;
+            _aidl_return->timestamp.milliSeconds = token.timestamp;
+            _aidl_return->mac = token.mac;
+        });
+    if (!result.isOk()) {
+        LOG(ERROR) << __func__ << " transaction failed. " << result.description();
+        errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+    return convertErrorCode(errorCode);
+}
+
+// SharedSecret implementation
+
+ScopedAStatus SharedSecret::getSharedSecretParameters(SharedSecretParameters* _aidl_return) {
+    KMV1::ErrorCode errorCode;
+    auto result = mDevice->getHmacSharingParameters(
+        [&](V4_0_ErrorCode error, const V4_0_HmacSharingParameters& params) {
+            errorCode = convert(error);
+            _aidl_return->seed = params.seed;
+            std::copy(params.nonce.data(), params.nonce.data() + params.nonce.elementCount(),
+                      std::back_inserter(_aidl_return->nonce));
+        });
+    if (!result.isOk()) {
+        LOG(ERROR) << __func__ << " transaction failed. " << result.description();
+        errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+    return convertErrorCode(errorCode);
+}
+
+ScopedAStatus
+SharedSecret::computeSharedSecret(const std::vector<SharedSecretParameters>& in_params,
+                                  std::vector<uint8_t>* _aidl_return) {
+    KMV1::ErrorCode errorCode;
+    auto legacyParams = convertSharedSecretParametersToLegacy(in_params);
+    auto result = mDevice->computeSharedHmac(
+        legacyParams, [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& sharingCheck) {
+            errorCode = convert(error);
+            *_aidl_return = sharingCheck;
+        });
+    if (!result.isOk()) {
+        LOG(ERROR) << __func__ << " transaction failed. " << result.description();
+        errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+    return convertErrorCode(errorCode);
+}
+
+// Certificate implementation
+
+template <KMV1::Tag tag, KMV1::TagType type>
+static auto getParam(const std::vector<KeyParameter>& keyParams, KMV1::TypedTag<type, tag> ttag)
+    -> decltype(authorizationValue(ttag, KeyParameter())) {
+    for (const auto& p : keyParams) {
+
+        if (auto v = authorizationValue(ttag, p)) {
+            return v;
+        }
+    }
+    return {};
+}
+
+template <typename T>
+static bool containsParam(const std::vector<KeyParameter>& keyParams, T ttag) {
+    return static_cast<bool>(getParam(keyParams, ttag));
+}
+
+// Prefer the smallest.
+// If no options are found, return the first.
+template <typename T>
+static typename KMV1::TypedTag2ValueType<T>::type
+getMaximum(const std::vector<KeyParameter>& keyParams, T tag,
+           std::vector<typename KMV1::TypedTag2ValueType<T>::type> sortedOptions) {
+    auto bestSoFar = sortedOptions.end();
+    for (const KeyParameter& kp : keyParams) {
+        if (auto value = authorizationValue(tag, kp)) {
+            auto candidate = std::find(sortedOptions.begin(), sortedOptions.end(), *value);
+            // sortedOptions is sorted from best to worst. `std::distance(first, last)` counts the
+            // hops from `first` to `last`. So a better `candidate` yields a positive distance to
+            // `bestSoFar`.
+            if (std::distance(candidate, bestSoFar) > 0) {
+                bestSoFar = candidate;
+            }
+        }
+    }
+    if (bestSoFar == sortedOptions.end()) {
+        return sortedOptions[0];
+    }
+    return *bestSoFar;
+}
+
+static std::variant<keystore::X509_Ptr, KMV1::ErrorCode>
+makeCert(::android::sp<Keymaster> mDevice, const std::vector<KeyParameter>& keyParams,
+         const std::vector<uint8_t>& keyBlob) {
+    // Start generating the certificate.
+    // Get public key for makeCert.
+    KMV1::ErrorCode errorCode;
+    std::vector<uint8_t> key;
+    static std::vector<uint8_t> empty_vector;
+    auto unwrapBlob = [&](auto b) -> const std::vector<uint8_t>& {
+        if (b)
+            return *b;
+        else
+            return empty_vector;
+    };
+    auto result = mDevice->exportKey(
+        V4_0_KeyFormat::X509, keyBlob, unwrapBlob(getParam(keyParams, KMV1::TAG_APPLICATION_ID)),
+        unwrapBlob(getParam(keyParams, KMV1::TAG_APPLICATION_DATA)),
+        [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyMaterial) {
+            errorCode = convert(error);
+            key = keyMaterial;
+        });
+    if (!result.isOk()) {
+        LOG(ERROR) << __func__ << " exportKey transaction failed. " << result.description();
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+    if (errorCode != KMV1::ErrorCode::OK) {
+        return errorCode;
+    }
+    // Get pkey for makeCert.
+    CBS cbs;
+    CBS_init(&cbs, key.data(), key.size());
+    auto pkey = EVP_parse_public_key(&cbs);
+
+    // makeCert
+    std::optional<std::reference_wrapper<const std::vector<uint8_t>>> subject;
+    if (auto blob = getParam(keyParams, KMV1::TAG_CERTIFICATE_SUBJECT)) {
+        subject = *blob;
+    }
+
+    std::optional<std::reference_wrapper<const std::vector<uint8_t>>> serial;
+    if (auto blob = getParam(keyParams, KMV1::TAG_CERTIFICATE_SERIAL)) {
+        serial = *blob;
+    }
+
+    int64_t activation;
+    if (auto date = getParam(keyParams, KMV1::TAG_CERTIFICATE_NOT_BEFORE)) {
+        activation = static_cast<int64_t>(*date);
+    } else {
+        return KMV1::ErrorCode::MISSING_NOT_BEFORE;
+    }
+
+    int64_t expiration;
+    if (auto date = getParam(keyParams, KMV1::TAG_CERTIFICATE_NOT_AFTER)) {
+        expiration = static_cast<int64_t>(*date);
+    } else {
+        return KMV1::ErrorCode::MISSING_NOT_AFTER;
+    }
+
+    auto certOrError = keystore::makeCert(
+        pkey, serial, subject, activation, expiration, false /* intentionally left blank */,
+        std::nullopt /* intentionally left blank */, std::nullopt /* intentionally left blank */);
+    if (std::holds_alternative<keystore::CertUtilsError>(certOrError)) {
+        LOG(ERROR) << __func__ << ": Failed to make certificate";
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+    return std::move(std::get<keystore::X509_Ptr>(certOrError));
+}
+
+static std::variant<keystore::Algo, KMV1::ErrorCode> getKeystoreAlgorithm(Algorithm algorithm) {
+    switch (algorithm) {
+    case Algorithm::RSA:
+        return keystore::Algo::RSA;
+    case Algorithm::EC:
+        return keystore::Algo::ECDSA;
+    default:
+        LOG(ERROR) << __func__ << ": This should not be called with symmetric algorithm.";
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+}
+
+static std::variant<keystore::Padding, KMV1::ErrorCode> getKeystorePadding(PaddingMode padding) {
+    switch (padding) {
+    case PaddingMode::RSA_PKCS1_1_5_SIGN:
+        return keystore::Padding::PKCS1_5;
+    case PaddingMode::RSA_PSS:
+        return keystore::Padding::PSS;
+    default:
+        return keystore::Padding::Ignored;
+    }
+}
+
+static std::variant<keystore::Digest, KMV1::ErrorCode> getKeystoreDigest(Digest digest) {
+    switch (digest) {
+    case Digest::SHA1:
+        return keystore::Digest::SHA1;
+    case Digest::SHA_2_224:
+        return keystore::Digest::SHA224;
+    case Digest::SHA_2_256:
+    case Digest::NONE:
+        return keystore::Digest::SHA256;
+    case Digest::SHA_2_384:
+        return keystore::Digest::SHA384;
+    case Digest::SHA_2_512:
+        return keystore::Digest::SHA512;
+    default:
+        LOG(ERROR) << __func__ << ": Unknown digest.";
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+}
+
+std::optional<KMV1::ErrorCode>
+KeyMintDevice::signCertificate(const std::vector<KeyParameter>& keyParams,
+                               const std::vector<uint8_t>& prefixedKeyBlob, X509* cert) {
+
+    auto algorithm = getParam(keyParams, KMV1::TAG_ALGORITHM);
+    auto algoOrError = getKeystoreAlgorithm(*algorithm);
+    if (std::holds_alternative<KMV1::ErrorCode>(algoOrError)) {
+        return std::get<KMV1::ErrorCode>(algoOrError);
+    }
+    auto algo = std::get<keystore::Algo>(algoOrError);
+    auto origPadding = getMaximum(keyParams, KMV1::TAG_PADDING,
+                                  {PaddingMode::RSA_PSS, PaddingMode::RSA_PKCS1_1_5_SIGN});
+    auto paddingOrError = getKeystorePadding(origPadding);
+    if (std::holds_alternative<KMV1::ErrorCode>(paddingOrError)) {
+        return std::get<KMV1::ErrorCode>(paddingOrError);
+    }
+    auto padding = std::get<keystore::Padding>(paddingOrError);
+    auto origDigest = getMaximum(keyParams, KMV1::TAG_DIGEST,
+                                 {Digest::SHA_2_256, Digest::SHA_2_512, Digest::SHA_2_384,
+                                  Digest::SHA_2_224, Digest::SHA1, Digest::NONE});
+    auto digestOrError = getKeystoreDigest(origDigest);
+    if (std::holds_alternative<KMV1::ErrorCode>(digestOrError)) {
+        return std::get<KMV1::ErrorCode>(digestOrError);
+    }
+    auto digest = std::get<keystore::Digest>(digestOrError);
+
+    KMV1::ErrorCode errorCode = KMV1::ErrorCode::OK;
+    auto error = keystore::signCertWith(
+        &*cert,
+        [&](const uint8_t* data, size_t len) {
+            std::vector<uint8_t> dataVec(data, data + len);
+            std::vector<KeyParameter> kps = {
+                KMV1::makeKeyParameter(KMV1::TAG_DIGEST, origDigest),
+            };
+            if (algorithm == KMV1::Algorithm::RSA) {
+                kps.push_back(KMV1::makeKeyParameter(KMV1::TAG_PADDING, origPadding));
+            }
+            BeginResult beginResult;
+            auto error =
+                begin(KeyPurpose::SIGN, prefixedKeyBlob, kps, HardwareAuthToken(), &beginResult);
+            if (!error.isOk()) {
+                errorCode = toErrorCode(error);
+                return std::vector<uint8_t>();
+            }
+
+            std::vector<uint8_t> result;
+            error = beginResult.operation->finish(dataVec,                     //
+                                                  {} /* signature */,          //
+                                                  {} /* authToken */,          //
+                                                  {} /* timestampToken */,     //
+                                                  {} /* confirmationToken */,  //
+                                                  &result);
+            if (!error.isOk()) {
+                errorCode = toErrorCode(error);
+                return std::vector<uint8_t>();
+            }
+            return result;
+        },
+        algo, padding, digest);
+    if (error) {
+        LOG(ERROR) << __func__
+                   << ": signCertWith failed. (Callback diagnosed: " << toString(errorCode) << ")";
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+    if (errorCode != KMV1::ErrorCode::OK) {
+        return errorCode;
+    }
+    return std::nullopt;
+}
+
+std::variant<std::vector<Certificate>, KMV1::ErrorCode>
+KeyMintDevice::getCertificate(const std::vector<KeyParameter>& keyParams,
+                              const std::vector<uint8_t>& prefixedKeyBlob) {
+    const std::vector<uint8_t>& keyBlob = prefixedKeyBlobRemovePrefix(prefixedKeyBlob);
+
+    // There are no certificates for symmetric keys.
+    auto algorithm = getParam(keyParams, KMV1::TAG_ALGORITHM);
+    if (!algorithm) {
+        LOG(ERROR) << __func__ << ": Unable to determine key algorithm.";
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+    switch (*algorithm) {
+    case Algorithm::RSA:
+    case Algorithm::EC:
+        break;
+    default:
+        return KMV1::ErrorCode::OK;
+    }
+
+    // If attestation was requested, call and use attestKey.
+    if (containsParam(keyParams, KMV1::TAG_ATTESTATION_CHALLENGE)) {
+        auto legacyParams = convertKeyParametersToLegacy(extractAttestationParams(keyParams));
+        std::vector<Certificate> certs;
+        KMV1::ErrorCode errorCode = KMV1::ErrorCode::OK;
+        auto result = mDevice->attestKey(
+            keyBlob, legacyParams,
+            [&](V4_0::ErrorCode error, const hidl_vec<hidl_vec<uint8_t>>& certChain) {
+                errorCode = convert(error);
+                for (const auto& cert : certChain) {
+                    Certificate certificate;
+                    certificate.encodedCertificate = cert;
+                    certs.push_back(certificate);
+                }
+            });
+        if (!result.isOk()) {
+            LOG(ERROR) << __func__ << ": Call to attestKey failed.";
+            return KMV1::ErrorCode::UNKNOWN_ERROR;
+        }
+        if (errorCode != KMV1::ErrorCode::OK) {
+            return errorCode;
+        }
+        return certs;
+    }
+
+    // makeCert
+    auto certOrError = makeCert(mDevice, keyParams, keyBlob);
+    if (std::holds_alternative<KMV1::ErrorCode>(certOrError)) {
+        return std::get<KMV1::ErrorCode>(certOrError);
+    }
+    auto cert = std::move(std::get<keystore::X509_Ptr>(certOrError));
+
+    // setIssuer
+    auto error = keystore::setIssuer(&*cert, &*cert, false);
+    if (error) {
+        LOG(ERROR) << __func__ << ": Set issuer failed.";
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+
+    // Signing
+    auto canSelfSign =
+        std::find_if(keyParams.begin(), keyParams.end(), [&](const KeyParameter& kp) {
+            if (auto v = KMV1::authorizationValue(KMV1::TAG_PURPOSE, kp)) {
+                return *v == KeyPurpose::SIGN;
+            }
+            return false;
+        }) != keyParams.end();
+    auto noAuthRequired = containsParam(keyParams, KMV1::TAG_NO_AUTH_REQUIRED);
+    // If we cannot sign because of purpose or authorization requirement,
+    if (!(canSelfSign && noAuthRequired)
+        // or if self signing fails for any other reason,
+        || signCertificate(keyParams, keyBlob, &*cert).has_value()) {
+        // we sign with ephemeral key.
+        keystore::EVP_PKEY_CTX_Ptr pkey_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL));
+        EVP_PKEY_keygen_init(pkey_ctx.get());
+        EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pkey_ctx.get(), NID_X9_62_prime256v1);
+        EVP_PKEY* pkey_ptr = nullptr;
+        EVP_PKEY_keygen(pkey_ctx.get(), &pkey_ptr);
+        error = keystore::signCert(&*cert, pkey_ptr);
+        if (error) {
+            LOG(ERROR) << __func__ << ": signCert failed.";
+            return KMV1::ErrorCode::UNKNOWN_ERROR;
+        }
+    }
+
+    // encodeCert
+    auto encodedCertOrError = keystore::encodeCert(&*cert);
+    if (std::holds_alternative<keystore::CertUtilsError>(encodedCertOrError)) {
+        LOG(ERROR) << __func__ << ": encodeCert failed.";
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+
+    Certificate certificate{.encodedCertificate =
+                                std::get<std::vector<uint8_t>>(encodedCertOrError)};
+    std::vector certificates = {certificate};
+    return certificates;
+}
+
+// Code to find the Keymaster devices (copied from existing code).
+
+// Copied from system/security/keystore/include/keystore/keymaster_types.h.
+
+// Changing this namespace alias will change the keymaster version.
+namespace keymasterNs = ::android::hardware::keymaster::V4_1;
+
+using keymasterNs::SecurityLevel;
+
+// Copied from system/security/keystore/KeyStore.h.
+
+using ::android::sp;
+using keymasterNs::support::Keymaster;
+
+template <typename T, size_t count> class Devices : public std::array<T, count> {
+  public:
+    T& operator[](SecurityLevel secLevel) {
+        static_assert(uint32_t(SecurityLevel::SOFTWARE) == 0 &&
+                          uint32_t(SecurityLevel::TRUSTED_ENVIRONMENT) == 1 &&
+                          uint32_t(SecurityLevel::STRONGBOX) == 2,
+                      "Numeric values of security levels have changed");
+        return std::array<T, count>::at(static_cast<uint32_t>(secLevel));
+    }
+    T operator[](SecurityLevel secLevel) const {
+        if (static_cast<uint32_t>(secLevel) > static_cast<uint32_t>(SecurityLevel::STRONGBOX)) {
+            LOG(ERROR) << "Invalid security level requested";
+            return {};
+        }
+        return (*const_cast<Devices*>(this))[secLevel];
+    }
+};
+
+using KeymasterDevices = Devices<sp<Keymaster>, 3>;
+
+// Copied from system/security/keystore/keystore_main.cpp.
+
+using ::android::hardware::hidl_string;
+using keymasterNs::support::Keymaster3;
+using keymasterNs::support::Keymaster4;
+
+template <typename Wrapper>
+KeymasterDevices enumerateKeymasterDevices(IServiceManager* serviceManager) {
+    KeymasterDevices result;
+    serviceManager->listManifestByInterface(
+        Wrapper::WrappedIKeymasterDevice::descriptor, [&](const hidl_vec<hidl_string>& names) {
+            auto try_get_device = [&](const auto& name, bool fail_silent) {
+                auto device = Wrapper::WrappedIKeymasterDevice::getService(name);
+                if (fail_silent && !device) return;
+                CHECK(device) << "Failed to get service for \""
+                              << Wrapper::WrappedIKeymasterDevice::descriptor
+                              << "\" with interface name \"" << name << "\"";
+
+                sp<Keymaster> kmDevice(new Wrapper(device, name));
+                auto halVersion = kmDevice->halVersion();
+                SecurityLevel securityLevel = halVersion.securityLevel;
+                LOG(INFO) << "found " << Wrapper::WrappedIKeymasterDevice::descriptor
+                          << " with interface name " << name << " and seclevel "
+                          << toString(securityLevel);
+                CHECK(static_cast<uint32_t>(securityLevel) < result.size())
+                    << "Security level of \"" << Wrapper::WrappedIKeymasterDevice::descriptor
+                    << "\" with interface name \"" << name << "\" out of range";
+                auto& deviceSlot = result[securityLevel];
+                if (deviceSlot) {
+                    if (!fail_silent) {
+                        LOG(WARNING) << "Implementation of \""
+                                     << Wrapper::WrappedIKeymasterDevice::descriptor
+                                     << "\" with interface name \"" << name
+                                     << "\" and security level: " << toString(securityLevel)
+                                     << " Masked by other implementation of Keymaster";
+                    }
+                } else {
+                    deviceSlot = kmDevice;
+                }
+            };
+            bool has_default = false;
+            for (auto& n : names) {
+                try_get_device(n, false);
+                if (n == "default") has_default = true;
+            }
+            // Make sure that we always check the default device. If we enumerate only what is
+            // known to hwservicemanager, we miss a possible passthrough HAL.
+            if (!has_default) {
+                try_get_device("default", true /* fail_silent */);
+            }
+        });
+    return result;
+}
+
+KeymasterDevices initializeKeymasters() {
+    auto serviceManager = IServiceManager::getService();
+    CHECK(serviceManager.get()) << "Failed to get ServiceManager";
+    auto result = enumerateKeymasterDevices<Keymaster4>(serviceManager.get());
+    auto softKeymaster = result[SecurityLevel::SOFTWARE];
+    if (!result[SecurityLevel::TRUSTED_ENVIRONMENT]) {
+        result = enumerateKeymasterDevices<Keymaster3>(serviceManager.get());
+    }
+    if (softKeymaster) result[SecurityLevel::SOFTWARE] = softKeymaster;
+    if (result[SecurityLevel::SOFTWARE] && !result[SecurityLevel::TRUSTED_ENVIRONMENT]) {
+        LOG(WARNING) << "No secure Keymaster implementation found, but device offers insecure"
+                        " Keymaster HAL. Using as default.";
+        result[SecurityLevel::TRUSTED_ENVIRONMENT] = result[SecurityLevel::SOFTWARE];
+        result[SecurityLevel::SOFTWARE] = nullptr;
+    }
+    // The software bit was removed since we do not need it.
+    return result;
+}
+
+void KeyMintDevice::setNumFreeSlots(uint8_t numFreeSlots) {
+    mOperationSlots.setNumFreeSlots(numFreeSlots);
+}
+
+// Constructors and helpers.
+
+KeyMintDevice::KeyMintDevice(sp<Keymaster> device, KeyMintSecurityLevel securityLevel)
+    : mDevice(device), securityLevel_(securityLevel) {
+    if (securityLevel == KeyMintSecurityLevel::STRONGBOX) {
+        setNumFreeSlots(3);
+    } else {
+        setNumFreeSlots(15);
+    }
+
+    softKeyMintDevice_.reset(CreateKeyMintDevice(KeyMintSecurityLevel::SOFTWARE));
+}
+
+sp<Keymaster> getDevice(KeyMintSecurityLevel securityLevel) {
+    static std::mutex mutex;
+    static sp<Keymaster> teeDevice;
+    static sp<Keymaster> sbDevice;
+    std::lock_guard<std::mutex> lock(mutex);
+    if (!teeDevice) {
+        auto devices = initializeKeymasters();
+        teeDevice = devices[V4_0::SecurityLevel::TRUSTED_ENVIRONMENT];
+        sbDevice = devices[V4_0::SecurityLevel::STRONGBOX];
+    }
+    switch (securityLevel) {
+    case KeyMintSecurityLevel::TRUSTED_ENVIRONMENT:
+        return teeDevice;
+    case KeyMintSecurityLevel::STRONGBOX:
+        return sbDevice;
+    default:
+        return {};
+    }
+}
+
+std::shared_ptr<KeyMintDevice>
+KeyMintDevice::createKeyMintDevice(KeyMintSecurityLevel securityLevel) {
+    if (auto dev = getDevice(securityLevel)) {
+        return ndk::SharedRefBase::make<KeyMintDevice>(std::move(dev), securityLevel);
+    }
+    return {};
+}
+
+std::shared_ptr<SharedSecret> SharedSecret::createSharedSecret(KeyMintSecurityLevel securityLevel) {
+    auto device = getDevice(securityLevel);
+    if (!device) {
+        return {};
+    }
+    return ndk::SharedRefBase::make<SharedSecret>(std::move(device));
+}
+
+std::shared_ptr<SecureClock> SecureClock::createSecureClock(KeyMintSecurityLevel securityLevel) {
+    auto device = getDevice(securityLevel);
+    if (!device) {
+        return {};
+    }
+    return ndk::SharedRefBase::make<SecureClock>(std::move(device));
+}
+
+ScopedAStatus
+KeystoreCompatService::getKeyMintDevice(KeyMintSecurityLevel in_securityLevel,
+                                        std::shared_ptr<IKeyMintDevice>* _aidl_return) {
+    auto i = mDeviceCache.find(in_securityLevel);
+    if (i == mDeviceCache.end()) {
+        auto device = KeyMintDevice::createKeyMintDevice(in_securityLevel);
+        if (!device) {
+            return ScopedAStatus::fromStatus(STATUS_NAME_NOT_FOUND);
+        }
+        bool inserted = false;
+        std::tie(i, inserted) = mDeviceCache.insert({in_securityLevel, std::move(device)});
+    }
+    *_aidl_return = i->second;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus KeystoreCompatService::getSharedSecret(KeyMintSecurityLevel in_securityLevel,
+                                                     std::shared_ptr<ISharedSecret>* _aidl_return) {
+    if (!mSharedSecret) {
+        auto secret = SharedSecret::createSharedSecret(in_securityLevel);
+        if (!secret) {
+            return ScopedAStatus::fromStatus(STATUS_NAME_NOT_FOUND);
+        }
+        mSharedSecret = std::move(secret);
+    }
+    *_aidl_return = mSharedSecret;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus KeystoreCompatService::getSecureClock(std::shared_ptr<ISecureClock>* _aidl_return) {
+    if (!mSecureClock) {
+        // The legacy verification service was always provided by the TEE variant.
+        auto clock = SecureClock::createSecureClock(KeyMintSecurityLevel::TRUSTED_ENVIRONMENT);
+        if (!clock) {
+            return ScopedAStatus::fromStatus(STATUS_NAME_NOT_FOUND);
+        }
+        mSecureClock = std::move(clock);
+    }
+    *_aidl_return = mSecureClock;
+    return ScopedAStatus::ok();
+}
diff --git a/keystore2/src/km_compat/km_compat.h b/keystore2/src/km_compat/km_compat.h
new file mode 100644
index 0000000..09c9157
--- /dev/null
+++ b/keystore2/src/km_compat/km_compat.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/security/keymint/BnKeyMintDevice.h>
+#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/secureclock/BnSecureClock.h>
+#include <aidl/android/hardware/security/sharedsecret/BnSharedSecret.h>
+#include <aidl/android/security/compat/BnKeystoreCompatService.h>
+#include <keymasterV4_1/Keymaster4.h>
+#include <unordered_map>
+#include <variant>
+
+#include "certificate_utils.h"
+
+using ::aidl::android::hardware::security::keymint::AttestationKey;
+using ::aidl::android::hardware::security::keymint::BeginResult;
+using ::aidl::android::hardware::security::keymint::Certificate;
+using ::aidl::android::hardware::security::keymint::HardwareAuthToken;
+using ::aidl::android::hardware::security::keymint::KeyCharacteristics;
+using ::aidl::android::hardware::security::keymint::KeyCreationResult;
+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::KeyPurpose;
+using KeyMintSecurityLevel = ::aidl::android::hardware::security::keymint::SecurityLevel;
+using V4_0_ErrorCode = ::android::hardware::keymaster::V4_0::ErrorCode;
+using ::aidl::android::hardware::security::keymint::IKeyMintDevice;
+using KMV1_ErrorCode = ::aidl::android::hardware::security::keymint::ErrorCode;
+using ::aidl::android::hardware::security::secureclock::ISecureClock;
+using ::aidl::android::hardware::security::secureclock::TimeStampToken;
+using ::aidl::android::hardware::security::sharedsecret::ISharedSecret;
+using ::aidl::android::hardware::security::sharedsecret::SharedSecretParameters;
+using ::aidl::android::security::compat::BnKeystoreCompatService;
+using ::android::hardware::keymaster::V4_1::support::Keymaster;
+using ::ndk::ScopedAStatus;
+
+class OperationSlots {
+  private:
+    uint8_t mNumFreeSlots;
+    std::mutex mNumFreeSlotsMutex;
+
+  public:
+    void setNumFreeSlots(uint8_t numFreeSlots);
+    bool claimSlot();
+    void freeSlot();
+};
+
+// An abstraction for a single operation slot.
+// This contains logic to ensure that we do not free the slot multiple times,
+// e.g., if we call abort twice on the same operation.
+class OperationSlot {
+  private:
+    OperationSlots* mOperationSlots;
+    bool mIsActive;
+
+  public:
+    OperationSlot(OperationSlots* slots, bool isActive)
+        : mOperationSlots(slots), mIsActive(isActive) {}
+
+    void freeSlot();
+    bool hasSlot() { return mIsActive; }
+};
+
+class KeyMintDevice : public aidl::android::hardware::security::keymint::BnKeyMintDevice {
+  private:
+    ::android::sp<Keymaster> mDevice;
+    OperationSlots mOperationSlots;
+
+  public:
+    explicit KeyMintDevice(::android::sp<Keymaster>, KeyMintSecurityLevel);
+    static std::shared_ptr<KeyMintDevice> createKeyMintDevice(KeyMintSecurityLevel securityLevel);
+
+    ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* _aidl_return) override;
+    ScopedAStatus addRngEntropy(const std::vector<uint8_t>& in_data) override;
+    ScopedAStatus generateKey(const std::vector<KeyParameter>& in_keyParams,
+                              const std::optional<AttestationKey>& in_attestationKey,
+                              KeyCreationResult* out_creationResult) override;
+    ScopedAStatus importKey(const std::vector<KeyParameter>& in_inKeyParams,
+                            KeyFormat in_inKeyFormat, const std::vector<uint8_t>& in_inKeyData,
+                            const std::optional<AttestationKey>& in_attestationKey,
+                            KeyCreationResult* out_creationResult) override;
+    ScopedAStatus importWrappedKey(const std::vector<uint8_t>& in_inWrappedKeyData,
+                                   const std::vector<uint8_t>& in_inWrappingKeyBlob,
+                                   const std::vector<uint8_t>& in_inMaskingKey,
+                                   const std::vector<KeyParameter>& in_inUnwrappingParams,
+                                   int64_t in_inPasswordSid, int64_t in_inBiometricSid,
+                                   KeyCreationResult* out_creationResult) override;
+    ScopedAStatus upgradeKey(const std::vector<uint8_t>& in_inKeyBlobToUpgrade,
+                             const std::vector<KeyParameter>& in_inUpgradeParams,
+                             std::vector<uint8_t>* _aidl_return) override;
+    ScopedAStatus deleteKey(const std::vector<uint8_t>& in_inKeyBlob) override;
+    ScopedAStatus deleteAllKeys() override;
+    ScopedAStatus destroyAttestationIds() override;
+    ScopedAStatus begin(KeyPurpose in_inPurpose, const std::vector<uint8_t>& in_inKeyBlob,
+                        const std::vector<KeyParameter>& in_inParams,
+                        const std::optional<HardwareAuthToken>& in_inAuthToken,
+                        BeginResult* _aidl_return) override;
+    ScopedAStatus deviceLocked(bool passwordOnly,
+                               const std::optional<TimeStampToken>& timestampToken) override;
+    ScopedAStatus earlyBootEnded() override;
+
+    ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob,
+                                               std::vector<uint8_t>* ephemeralKeyBlob) override;
+
+    ScopedAStatus
+    getKeyCharacteristics(const std::vector<uint8_t>& storageKeyBlob,
+                          const std::vector<uint8_t>& appId, const std::vector<uint8_t>& appData,
+                          std::vector<KeyCharacteristics>* keyCharacteristics) override;
+
+    // These are public to allow testing code to use them directly.
+    // This class should not be used publicly anyway.
+    std::variant<std::vector<Certificate>, KMV1_ErrorCode>
+    getCertificate(const std::vector<KeyParameter>& keyParams, const std::vector<uint8_t>& keyBlob);
+
+    void setNumFreeSlots(uint8_t numFreeSlots);
+
+  private:
+    std::optional<KMV1_ErrorCode> signCertificate(const std::vector<KeyParameter>& keyParams,
+                                                  const std::vector<uint8_t>& keyBlob, X509* cert);
+    KeyMintSecurityLevel securityLevel_;
+
+    // Software-based KeyMint device used to implement ECDH.
+    std::shared_ptr<IKeyMintDevice> softKeyMintDevice_;
+};
+
+class KeyMintOperation : public aidl::android::hardware::security::keymint::BnKeyMintOperation {
+  private:
+    ::android::sp<Keymaster> mDevice;
+    uint64_t mOperationHandle;
+    OperationSlot mOperationSlot;
+
+  public:
+    KeyMintOperation(::android::sp<Keymaster> device, uint64_t operationHandle,
+                     OperationSlots* slots, bool isActive)
+        : mDevice(device), mOperationHandle(operationHandle), mOperationSlot(slots, isActive) {}
+    ~KeyMintOperation();
+
+    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();
+};
+
+class SharedSecret : public aidl::android::hardware::security::sharedsecret::BnSharedSecret {
+  private:
+    ::android::sp<Keymaster> mDevice;
+
+  public:
+    SharedSecret(::android::sp<Keymaster> device) : mDevice(device) {}
+    static std::shared_ptr<SharedSecret> createSharedSecret(KeyMintSecurityLevel securityLevel);
+
+    virtual ScopedAStatus getSharedSecretParameters(SharedSecretParameters* _aidl_return) override;
+    virtual ScopedAStatus computeSharedSecret(const std::vector<SharedSecretParameters>& in_params,
+                                              std::vector<uint8_t>* _aidl_return) override;
+};
+
+class SecureClock : public aidl::android::hardware::security::secureclock::BnSecureClock {
+  private:
+    ::android::sp<Keymaster> mDevice;
+
+  public:
+    SecureClock(::android::sp<Keymaster> device) : mDevice(device) {}
+    static std::shared_ptr<SecureClock> createSecureClock(KeyMintSecurityLevel securityLevel);
+
+    ScopedAStatus generateTimeStamp(int64_t in_challenge, TimeStampToken* _aidl_return) override;
+};
+
+class KeystoreCompatService : public BnKeystoreCompatService {
+  private:
+    std::unordered_map<KeyMintSecurityLevel, std::shared_ptr<IKeyMintDevice>> mDeviceCache;
+    std::shared_ptr<ISharedSecret> mSharedSecret;
+    std::shared_ptr<ISecureClock> mSecureClock;
+
+  public:
+    KeystoreCompatService() {}
+    ScopedAStatus getKeyMintDevice(KeyMintSecurityLevel in_securityLevel,
+                                   std::shared_ptr<IKeyMintDevice>* _aidl_return) override;
+    ScopedAStatus getSharedSecret(KeyMintSecurityLevel in_securityLevel,
+                                  std::shared_ptr<ISharedSecret>* _aidl_return) override;
+    ScopedAStatus getSecureClock(std::shared_ptr<ISecureClock>* _aidl_return) override;
+};
diff --git a/keystore2/src/km_compat/km_compat_service.cpp b/keystore2/src/km_compat/km_compat_service.cpp
new file mode 100644
index 0000000..d2ced49
--- /dev/null
+++ b/keystore2/src/km_compat/km_compat_service.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "km_compat.h"
+#include <android/binder_manager.h>
+
+#include <mutex>
+
+extern "C" {
+
+// Create a KeyMintDevice and add it as a service.
+int32_t addKeyMintDeviceService() {
+    static std::mutex mutex;
+    std::lock_guard<std::mutex> lock(mutex);
+    static std::shared_ptr<KeystoreCompatService> ti;
+    binder_status_t status = STATUS_OK;
+    if (!ti) {
+        ti = ndk::SharedRefBase::make<KeystoreCompatService>();
+        const auto instanceName = "android.security.compat";
+        status = AServiceManager_addService(ti->asBinder().get(), instanceName);
+        if (status != STATUS_OK) {
+            ti.reset();
+        }
+    }
+    return status;
+}
+}
diff --git a/keystore2/src/km_compat/km_compat_type_conversion.h b/keystore2/src/km_compat/km_compat_type_conversion.h
new file mode 100644
index 0000000..de09477
--- /dev/null
+++ b/keystore2/src/km_compat/km_compat_type_conversion.h
@@ -0,0 +1,1067 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <keymasterV4_1/keymaster_tags.h>
+#include <keymint_support/keymint_tags.h>
+
+namespace V4_0 = ::android::hardware::keymaster::V4_0;
+namespace V4_1 = ::android::hardware::keymaster::V4_1;
+namespace KMV1 = ::aidl::android::hardware::security::keymint;
+
+static KMV1::ErrorCode convert(V4_0::ErrorCode error) {
+    switch (error) {
+    case V4_0::ErrorCode::OK:
+        return KMV1::ErrorCode::OK;
+    case V4_0::ErrorCode::ROOT_OF_TRUST_ALREADY_SET:
+        return KMV1::ErrorCode::ROOT_OF_TRUST_ALREADY_SET;
+    case V4_0::ErrorCode::UNSUPPORTED_PURPOSE:
+        return KMV1::ErrorCode::UNSUPPORTED_PURPOSE;
+    case V4_0::ErrorCode::INCOMPATIBLE_PURPOSE:
+        return KMV1::ErrorCode::INCOMPATIBLE_PURPOSE;
+    case V4_0::ErrorCode::UNSUPPORTED_ALGORITHM:
+        return KMV1::ErrorCode::UNSUPPORTED_ALGORITHM;
+    case V4_0::ErrorCode::INCOMPATIBLE_ALGORITHM:
+        return KMV1::ErrorCode::INCOMPATIBLE_ALGORITHM;
+    case V4_0::ErrorCode::UNSUPPORTED_KEY_SIZE:
+        return KMV1::ErrorCode::UNSUPPORTED_KEY_SIZE;
+    case V4_0::ErrorCode::UNSUPPORTED_BLOCK_MODE:
+        return KMV1::ErrorCode::UNSUPPORTED_BLOCK_MODE;
+    case V4_0::ErrorCode::INCOMPATIBLE_BLOCK_MODE:
+        return KMV1::ErrorCode::INCOMPATIBLE_BLOCK_MODE;
+    case V4_0::ErrorCode::UNSUPPORTED_MAC_LENGTH:
+        return KMV1::ErrorCode::UNSUPPORTED_MAC_LENGTH;
+    case V4_0::ErrorCode::UNSUPPORTED_PADDING_MODE:
+        return KMV1::ErrorCode::UNSUPPORTED_PADDING_MODE;
+    case V4_0::ErrorCode::INCOMPATIBLE_PADDING_MODE:
+        return KMV1::ErrorCode::INCOMPATIBLE_PADDING_MODE;
+    case V4_0::ErrorCode::UNSUPPORTED_DIGEST:
+        return KMV1::ErrorCode::UNSUPPORTED_DIGEST;
+    case V4_0::ErrorCode::INCOMPATIBLE_DIGEST:
+        return KMV1::ErrorCode::INCOMPATIBLE_DIGEST;
+    case V4_0::ErrorCode::INVALID_EXPIRATION_TIME:
+        return KMV1::ErrorCode::INVALID_EXPIRATION_TIME;
+    case V4_0::ErrorCode::INVALID_USER_ID:
+        return KMV1::ErrorCode::INVALID_USER_ID;
+    case V4_0::ErrorCode::INVALID_AUTHORIZATION_TIMEOUT:
+        return KMV1::ErrorCode::INVALID_AUTHORIZATION_TIMEOUT;
+    case V4_0::ErrorCode::UNSUPPORTED_KEY_FORMAT:
+        return KMV1::ErrorCode::UNSUPPORTED_KEY_FORMAT;
+    case V4_0::ErrorCode::INCOMPATIBLE_KEY_FORMAT:
+        return KMV1::ErrorCode::INCOMPATIBLE_KEY_FORMAT;
+    case V4_0::ErrorCode::UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM:
+        return KMV1::ErrorCode::UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM;
+    case V4_0::ErrorCode::UNSUPPORTED_KEY_VERIFICATION_ALGORITHM:
+        return KMV1::ErrorCode::UNSUPPORTED_KEY_VERIFICATION_ALGORITHM;
+    case V4_0::ErrorCode::INVALID_INPUT_LENGTH:
+        return KMV1::ErrorCode::INVALID_INPUT_LENGTH;
+    case V4_0::ErrorCode::KEY_EXPORT_OPTIONS_INVALID:
+        return KMV1::ErrorCode::KEY_EXPORT_OPTIONS_INVALID;
+    case V4_0::ErrorCode::DELEGATION_NOT_ALLOWED:
+        return KMV1::ErrorCode::DELEGATION_NOT_ALLOWED;
+    case V4_0::ErrorCode::KEY_NOT_YET_VALID:
+        return KMV1::ErrorCode::KEY_NOT_YET_VALID;
+    case V4_0::ErrorCode::KEY_EXPIRED:
+        return KMV1::ErrorCode::KEY_EXPIRED;
+    case V4_0::ErrorCode::KEY_USER_NOT_AUTHENTICATED:
+        return KMV1::ErrorCode::KEY_USER_NOT_AUTHENTICATED;
+    case V4_0::ErrorCode::OUTPUT_PARAMETER_NULL:
+        return KMV1::ErrorCode::OUTPUT_PARAMETER_NULL;
+    case V4_0::ErrorCode::INVALID_OPERATION_HANDLE:
+        return KMV1::ErrorCode::INVALID_OPERATION_HANDLE;
+    case V4_0::ErrorCode::INSUFFICIENT_BUFFER_SPACE:
+        return KMV1::ErrorCode::INSUFFICIENT_BUFFER_SPACE;
+    case V4_0::ErrorCode::VERIFICATION_FAILED:
+        return KMV1::ErrorCode::VERIFICATION_FAILED;
+    case V4_0::ErrorCode::TOO_MANY_OPERATIONS:
+        return KMV1::ErrorCode::TOO_MANY_OPERATIONS;
+    case V4_0::ErrorCode::UNEXPECTED_NULL_POINTER:
+        return KMV1::ErrorCode::UNEXPECTED_NULL_POINTER;
+    case V4_0::ErrorCode::INVALID_KEY_BLOB:
+        return KMV1::ErrorCode::INVALID_KEY_BLOB;
+    case V4_0::ErrorCode::IMPORTED_KEY_NOT_ENCRYPTED:
+        return KMV1::ErrorCode::IMPORTED_KEY_NOT_ENCRYPTED;
+    case V4_0::ErrorCode::IMPORTED_KEY_DECRYPTION_FAILED:
+        return KMV1::ErrorCode::IMPORTED_KEY_DECRYPTION_FAILED;
+    case V4_0::ErrorCode::IMPORTED_KEY_NOT_SIGNED:
+        return KMV1::ErrorCode::IMPORTED_KEY_NOT_SIGNED;
+    case V4_0::ErrorCode::IMPORTED_KEY_VERIFICATION_FAILED:
+        return KMV1::ErrorCode::IMPORTED_KEY_VERIFICATION_FAILED;
+    case V4_0::ErrorCode::INVALID_ARGUMENT:
+        return KMV1::ErrorCode::INVALID_ARGUMENT;
+    case V4_0::ErrorCode::UNSUPPORTED_TAG:
+        return KMV1::ErrorCode::UNSUPPORTED_TAG;
+    case V4_0::ErrorCode::INVALID_TAG:
+        return KMV1::ErrorCode::INVALID_TAG;
+    case V4_0::ErrorCode::MEMORY_ALLOCATION_FAILED:
+        return KMV1::ErrorCode::MEMORY_ALLOCATION_FAILED;
+    case V4_0::ErrorCode::IMPORT_PARAMETER_MISMATCH:
+        return KMV1::ErrorCode::IMPORT_PARAMETER_MISMATCH;
+    case V4_0::ErrorCode::SECURE_HW_ACCESS_DENIED:
+        return KMV1::ErrorCode::SECURE_HW_ACCESS_DENIED;
+    case V4_0::ErrorCode::OPERATION_CANCELLED:
+        return KMV1::ErrorCode::OPERATION_CANCELLED;
+    case V4_0::ErrorCode::CONCURRENT_ACCESS_CONFLICT:
+        return KMV1::ErrorCode::CONCURRENT_ACCESS_CONFLICT;
+    case V4_0::ErrorCode::SECURE_HW_BUSY:
+        return KMV1::ErrorCode::SECURE_HW_BUSY;
+    case V4_0::ErrorCode::SECURE_HW_COMMUNICATION_FAILED:
+        return KMV1::ErrorCode::SECURE_HW_COMMUNICATION_FAILED;
+    case V4_0::ErrorCode::UNSUPPORTED_EC_FIELD:
+        return KMV1::ErrorCode::UNSUPPORTED_EC_FIELD;
+    case V4_0::ErrorCode::MISSING_NONCE:
+        return KMV1::ErrorCode::MISSING_NONCE;
+    case V4_0::ErrorCode::INVALID_NONCE:
+        return KMV1::ErrorCode::INVALID_NONCE;
+    case V4_0::ErrorCode::MISSING_MAC_LENGTH:
+        return KMV1::ErrorCode::MISSING_MAC_LENGTH;
+    case V4_0::ErrorCode::KEY_RATE_LIMIT_EXCEEDED:
+        return KMV1::ErrorCode::KEY_RATE_LIMIT_EXCEEDED;
+    case V4_0::ErrorCode::CALLER_NONCE_PROHIBITED:
+        return KMV1::ErrorCode::CALLER_NONCE_PROHIBITED;
+    case V4_0::ErrorCode::KEY_MAX_OPS_EXCEEDED:
+        return KMV1::ErrorCode::KEY_MAX_OPS_EXCEEDED;
+    case V4_0::ErrorCode::INVALID_MAC_LENGTH:
+        return KMV1::ErrorCode::INVALID_MAC_LENGTH;
+    case V4_0::ErrorCode::MISSING_MIN_MAC_LENGTH:
+        return KMV1::ErrorCode::MISSING_MIN_MAC_LENGTH;
+    case V4_0::ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH:
+        return KMV1::ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH;
+    case V4_0::ErrorCode::UNSUPPORTED_KDF:
+        return KMV1::ErrorCode::UNSUPPORTED_KDF;
+    case V4_0::ErrorCode::UNSUPPORTED_EC_CURVE:
+        return KMV1::ErrorCode::UNSUPPORTED_EC_CURVE;
+    case V4_0::ErrorCode::KEY_REQUIRES_UPGRADE:
+        return KMV1::ErrorCode::KEY_REQUIRES_UPGRADE;
+    case V4_0::ErrorCode::ATTESTATION_CHALLENGE_MISSING:
+        return KMV1::ErrorCode::ATTESTATION_CHALLENGE_MISSING;
+    case V4_0::ErrorCode::KEYMASTER_NOT_CONFIGURED:
+        return KMV1::ErrorCode::KEYMINT_NOT_CONFIGURED;
+    case V4_0::ErrorCode::ATTESTATION_APPLICATION_ID_MISSING:
+        return KMV1::ErrorCode::ATTESTATION_APPLICATION_ID_MISSING;
+    case V4_0::ErrorCode::CANNOT_ATTEST_IDS:
+        return KMV1::ErrorCode::CANNOT_ATTEST_IDS;
+    case V4_0::ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE:
+        return KMV1::ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE;
+    case V4_0::ErrorCode::HARDWARE_TYPE_UNAVAILABLE:
+        return KMV1::ErrorCode::HARDWARE_TYPE_UNAVAILABLE;
+    case V4_0::ErrorCode::PROOF_OF_PRESENCE_REQUIRED:
+        return KMV1::ErrorCode::PROOF_OF_PRESENCE_REQUIRED;
+    case V4_0::ErrorCode::CONCURRENT_PROOF_OF_PRESENCE_REQUESTED:
+        return KMV1::ErrorCode::CONCURRENT_PROOF_OF_PRESENCE_REQUESTED;
+    case V4_0::ErrorCode::NO_USER_CONFIRMATION:
+        return KMV1::ErrorCode::NO_USER_CONFIRMATION;
+    case V4_0::ErrorCode::DEVICE_LOCKED:
+        return KMV1::ErrorCode::DEVICE_LOCKED;
+    case V4_0::ErrorCode::UNIMPLEMENTED:
+        return KMV1::ErrorCode::UNIMPLEMENTED;
+    case V4_0::ErrorCode::VERSION_MISMATCH:
+        return KMV1::ErrorCode::VERSION_MISMATCH;
+    case V4_0::ErrorCode::UNKNOWN_ERROR:
+        return KMV1::ErrorCode::UNKNOWN_ERROR;
+    }
+}
+
+static std::optional<V4_0::KeyPurpose> convert(KMV1::KeyPurpose p) {
+    switch (p) {
+    case KMV1::KeyPurpose::ENCRYPT:
+        return V4_0::KeyPurpose::ENCRYPT;
+    case KMV1::KeyPurpose::DECRYPT:
+        return V4_0::KeyPurpose::DECRYPT;
+    case KMV1::KeyPurpose::SIGN:
+        return V4_0::KeyPurpose::SIGN;
+    case KMV1::KeyPurpose::VERIFY:
+        return V4_0::KeyPurpose::VERIFY;
+    case KMV1::KeyPurpose::WRAP_KEY:
+        return V4_0::KeyPurpose::WRAP_KEY;
+    default:
+        // Can end up here because KeyMint may have KeyPurpose values not in KM4.
+        return {};
+    }
+}
+
+static KMV1::KeyPurpose convert(V4_0::KeyPurpose p) {
+    switch (p) {
+    case V4_0::KeyPurpose::ENCRYPT:
+        return KMV1::KeyPurpose::ENCRYPT;
+    case V4_0::KeyPurpose::DECRYPT:
+        return KMV1::KeyPurpose::DECRYPT;
+    case V4_0::KeyPurpose::SIGN:
+        return KMV1::KeyPurpose::SIGN;
+    case V4_0::KeyPurpose::VERIFY:
+        return KMV1::KeyPurpose::VERIFY;
+    case V4_0::KeyPurpose::WRAP_KEY:
+        return KMV1::KeyPurpose::WRAP_KEY;
+    }
+}
+
+static V4_0::Algorithm convert(KMV1::Algorithm a) {
+    switch (a) {
+    case KMV1::Algorithm::RSA:
+        return V4_0::Algorithm::RSA;
+    case KMV1::Algorithm::EC:
+        return V4_0::Algorithm::EC;
+    case KMV1::Algorithm::AES:
+        return V4_0::Algorithm::AES;
+    case KMV1::Algorithm::TRIPLE_DES:
+        return V4_0::Algorithm::TRIPLE_DES;
+    case KMV1::Algorithm::HMAC:
+        return V4_0::Algorithm::HMAC;
+    }
+}
+
+static KMV1::Algorithm convert(V4_0::Algorithm a) {
+    switch (a) {
+    case V4_0::Algorithm::RSA:
+        return KMV1::Algorithm::RSA;
+    case V4_0::Algorithm::EC:
+        return KMV1::Algorithm::EC;
+    case V4_0::Algorithm::AES:
+        return KMV1::Algorithm::AES;
+    case V4_0::Algorithm::TRIPLE_DES:
+        return KMV1::Algorithm::TRIPLE_DES;
+    case V4_0::Algorithm::HMAC:
+        return KMV1::Algorithm::HMAC;
+    }
+}
+
+static V4_0::Digest convert(KMV1::Digest d) {
+    switch (d) {
+    case KMV1::Digest::NONE:
+        return V4_0::Digest::NONE;
+    case KMV1::Digest::MD5:
+        return V4_0::Digest::MD5;
+    case KMV1::Digest::SHA1:
+        return V4_0::Digest::SHA1;
+    case KMV1::Digest::SHA_2_224:
+        return V4_0::Digest::SHA_2_224;
+    case KMV1::Digest::SHA_2_256:
+        return V4_0::Digest::SHA_2_256;
+    case KMV1::Digest::SHA_2_384:
+        return V4_0::Digest::SHA_2_384;
+    case KMV1::Digest::SHA_2_512:
+        return V4_0::Digest::SHA_2_512;
+    }
+}
+
+static KMV1::Digest convert(V4_0::Digest d) {
+    switch (d) {
+    case V4_0::Digest::NONE:
+        return KMV1::Digest::NONE;
+    case V4_0::Digest::MD5:
+        return KMV1::Digest::MD5;
+    case V4_0::Digest::SHA1:
+        return KMV1::Digest::SHA1;
+    case V4_0::Digest::SHA_2_224:
+        return KMV1::Digest::SHA_2_224;
+    case V4_0::Digest::SHA_2_256:
+        return KMV1::Digest::SHA_2_256;
+    case V4_0::Digest::SHA_2_384:
+        return KMV1::Digest::SHA_2_384;
+    case V4_0::Digest::SHA_2_512:
+        return KMV1::Digest::SHA_2_512;
+    }
+}
+
+static V4_0::EcCurve convert(KMV1::EcCurve e) {
+    switch (e) {
+    case KMV1::EcCurve::P_224:
+        return V4_0::EcCurve::P_224;
+    case KMV1::EcCurve::P_256:
+        return V4_0::EcCurve::P_256;
+    case KMV1::EcCurve::P_384:
+        return V4_0::EcCurve::P_384;
+    case KMV1::EcCurve::P_521:
+        return V4_0::EcCurve::P_521;
+    }
+}
+
+static KMV1::EcCurve convert(V4_0::EcCurve e) {
+    switch (e) {
+    case V4_0::EcCurve::P_224:
+        return KMV1::EcCurve::P_224;
+    case V4_0::EcCurve::P_256:
+        return KMV1::EcCurve::P_256;
+    case V4_0::EcCurve::P_384:
+        return KMV1::EcCurve::P_384;
+    case V4_0::EcCurve::P_521:
+        return KMV1::EcCurve::P_521;
+    }
+}
+
+static V4_0::BlockMode convert(KMV1::BlockMode b) {
+    switch (b) {
+    case KMV1::BlockMode::ECB:
+        return V4_0::BlockMode::ECB;
+    case KMV1::BlockMode::CBC:
+        return V4_0::BlockMode::CBC;
+    case KMV1::BlockMode::CTR:
+        return V4_0::BlockMode::CTR;
+    case KMV1::BlockMode::GCM:
+        return V4_0::BlockMode::GCM;
+    }
+}
+
+static KMV1::BlockMode convert(V4_0::BlockMode b) {
+    switch (b) {
+    case V4_0::BlockMode::ECB:
+        return KMV1::BlockMode::ECB;
+    case V4_0::BlockMode::CBC:
+        return KMV1::BlockMode::CBC;
+    case V4_0::BlockMode::CTR:
+        return KMV1::BlockMode::CTR;
+    case V4_0::BlockMode::GCM:
+        return KMV1::BlockMode::GCM;
+    }
+}
+
+static V4_0::PaddingMode convert(KMV1::PaddingMode p) {
+    switch (p) {
+    case KMV1::PaddingMode::NONE:
+        return V4_0::PaddingMode::NONE;
+    case KMV1::PaddingMode::RSA_OAEP:
+        return V4_0::PaddingMode::RSA_OAEP;
+    case KMV1::PaddingMode::RSA_PSS:
+        return V4_0::PaddingMode::RSA_PSS;
+    case KMV1::PaddingMode::RSA_PKCS1_1_5_ENCRYPT:
+        return V4_0::PaddingMode::RSA_PKCS1_1_5_ENCRYPT;
+    case KMV1::PaddingMode::RSA_PKCS1_1_5_SIGN:
+        return V4_0::PaddingMode::RSA_PKCS1_1_5_SIGN;
+    case KMV1::PaddingMode::PKCS7:
+        return V4_0::PaddingMode::PKCS7;
+    }
+}
+
+static KMV1::PaddingMode convert(V4_0::PaddingMode p) {
+    switch (p) {
+    case V4_0::PaddingMode::NONE:
+        return KMV1::PaddingMode::NONE;
+    case V4_0::PaddingMode::RSA_OAEP:
+        return KMV1::PaddingMode::RSA_OAEP;
+    case V4_0::PaddingMode::RSA_PSS:
+        return KMV1::PaddingMode::RSA_PSS;
+    case V4_0::PaddingMode::RSA_PKCS1_1_5_ENCRYPT:
+        return KMV1::PaddingMode::RSA_PKCS1_1_5_ENCRYPT;
+    case V4_0::PaddingMode::RSA_PKCS1_1_5_SIGN:
+        return KMV1::PaddingMode::RSA_PKCS1_1_5_SIGN;
+    case V4_0::PaddingMode::PKCS7:
+        return KMV1::PaddingMode::PKCS7;
+    }
+}
+
+static V4_0::HardwareAuthenticatorType convert(KMV1::HardwareAuthenticatorType h) {
+    uint32_t result = 0;
+    uint32_t hat = static_cast<uint32_t>(h);
+    if (hat & static_cast<uint32_t>(KMV1::HardwareAuthenticatorType::PASSWORD)) {
+        result |= static_cast<uint32_t>(V4_0::HardwareAuthenticatorType::PASSWORD);
+    }
+    if (hat & static_cast<uint32_t>(KMV1::HardwareAuthenticatorType::FINGERPRINT)) {
+        result |= static_cast<uint32_t>(V4_0::HardwareAuthenticatorType::FINGERPRINT);
+    }
+    return static_cast<V4_0::HardwareAuthenticatorType>(result);
+}
+
+static KMV1::HardwareAuthenticatorType convert(V4_0::HardwareAuthenticatorType h) {
+    uint32_t result = 0;
+    if ((uint32_t)h & (uint32_t)V4_0::HardwareAuthenticatorType::PASSWORD) {
+        result |= (uint32_t)KMV1::HardwareAuthenticatorType::PASSWORD;
+    }
+    if ((uint32_t)h & (uint32_t)V4_0::HardwareAuthenticatorType::FINGERPRINT) {
+        result |= (uint32_t)KMV1::HardwareAuthenticatorType::FINGERPRINT;
+    }
+    return static_cast<KMV1::HardwareAuthenticatorType>(result);
+}
+
+static V4_0::SecurityLevel convert(KMV1::SecurityLevel s) {
+    switch (s) {
+    case KMV1::SecurityLevel::SOFTWARE:
+        return V4_0::SecurityLevel::SOFTWARE;
+    case KMV1::SecurityLevel::TRUSTED_ENVIRONMENT:
+        return V4_0::SecurityLevel::TRUSTED_ENVIRONMENT;
+    case KMV1::SecurityLevel::STRONGBOX:
+        return V4_0::SecurityLevel::STRONGBOX;
+    case KMV1::SecurityLevel::KEYSTORE:
+        return V4_0::SecurityLevel::SOFTWARE;
+    }
+}
+
+static KMV1::SecurityLevel convert(V4_0::SecurityLevel s) {
+    switch (s) {
+    case V4_0::SecurityLevel::SOFTWARE:
+        return KMV1::SecurityLevel::SOFTWARE;
+    case V4_0::SecurityLevel::TRUSTED_ENVIRONMENT:
+        return KMV1::SecurityLevel::TRUSTED_ENVIRONMENT;
+    case V4_0::SecurityLevel::STRONGBOX:
+        return KMV1::SecurityLevel::STRONGBOX;
+    }
+}
+
+static V4_0::KeyOrigin convert(KMV1::KeyOrigin o) {
+    switch (o) {
+    case KMV1::KeyOrigin::GENERATED:
+        return V4_0::KeyOrigin::GENERATED;
+    case KMV1::KeyOrigin::DERIVED:
+        return V4_0::KeyOrigin::DERIVED;
+    case KMV1::KeyOrigin::IMPORTED:
+        return V4_0::KeyOrigin::IMPORTED;
+    case KMV1::KeyOrigin::RESERVED:
+        return V4_0::KeyOrigin::UNKNOWN;
+    case KMV1::KeyOrigin::SECURELY_IMPORTED:
+        return V4_0::KeyOrigin::SECURELY_IMPORTED;
+    }
+}
+
+static KMV1::KeyOrigin convert(V4_0::KeyOrigin o) {
+    switch (o) {
+    case V4_0::KeyOrigin::GENERATED:
+        return KMV1::KeyOrigin::GENERATED;
+    case V4_0::KeyOrigin::DERIVED:
+        return KMV1::KeyOrigin::DERIVED;
+    case V4_0::KeyOrigin::IMPORTED:
+        return KMV1::KeyOrigin::IMPORTED;
+    case V4_0::KeyOrigin::UNKNOWN:
+        return KMV1::KeyOrigin::RESERVED;
+    case V4_0::KeyOrigin::SECURELY_IMPORTED:
+        return KMV1::KeyOrigin::SECURELY_IMPORTED;
+    }
+}
+
+static V4_0::KeyParameter convertKeyParameterToLegacy(const KMV1::KeyParameter& kp) {
+    switch (kp.tag) {
+    case KMV1::Tag::INVALID:
+        break;
+    case KMV1::Tag::PURPOSE:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_PURPOSE, kp)) {
+            std::optional<V4_0::KeyPurpose> purpose = convert(v->get());
+            if (purpose) {
+                return V4_0::makeKeyParameter(V4_0::TAG_PURPOSE, purpose.value());
+            }
+        }
+        break;
+    case KMV1::Tag::ALGORITHM:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ALGORITHM, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ALGORITHM, convert(v->get()));
+        }
+        break;
+    case KMV1::Tag::KEY_SIZE:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_KEY_SIZE, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_KEY_SIZE, v->get());
+        }
+        break;
+    case KMV1::Tag::BLOCK_MODE:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_BLOCK_MODE, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_BLOCK_MODE, convert(v->get()));
+        }
+        break;
+    case KMV1::Tag::DIGEST:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_DIGEST, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_DIGEST, convert(v->get()));
+        }
+        break;
+    case KMV1::Tag::PADDING:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_PADDING, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_PADDING, convert(v->get()));
+        }
+        break;
+    case KMV1::Tag::CALLER_NONCE:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_CALLER_NONCE, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_CALLER_NONCE, v->get());
+        }
+        break;
+    case KMV1::Tag::MIN_MAC_LENGTH:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_MIN_MAC_LENGTH, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_MIN_MAC_LENGTH, v->get());
+        }
+        break;
+    case KMV1::Tag::EC_CURVE:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_EC_CURVE, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_EC_CURVE, convert(v->get()));
+        }
+        break;
+    case KMV1::Tag::RSA_PUBLIC_EXPONENT:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_RSA_PUBLIC_EXPONENT, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_RSA_PUBLIC_EXPONENT, v->get());
+        }
+        break;
+    case KMV1::Tag::INCLUDE_UNIQUE_ID:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_INCLUDE_UNIQUE_ID, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_INCLUDE_UNIQUE_ID, v->get());
+        }
+        break;
+    case KMV1::Tag::BOOTLOADER_ONLY:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_BOOTLOADER_ONLY, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_BOOTLOADER_ONLY, v->get());
+        }
+        break;
+    case KMV1::Tag::ROLLBACK_RESISTANCE:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ROLLBACK_RESISTANCE, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ROLLBACK_RESISTANCE, v->get());
+        }
+        break;
+    case KMV1::Tag::HARDWARE_TYPE:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_HARDWARE_TYPE, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_HARDWARE_TYPE, convert(v->get()));
+        }
+        break;
+    case KMV1::Tag::EARLY_BOOT_ONLY:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_EARLY_BOOT_ONLY, kp)) {
+            return V4_0::makeKeyParameter(V4_1::TAG_EARLY_BOOT_ONLY, v->get());
+        }
+        break;
+    case KMV1::Tag::ACTIVE_DATETIME:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ACTIVE_DATETIME, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ACTIVE_DATETIME, v->get());
+        }
+        break;
+    case KMV1::Tag::ORIGINATION_EXPIRE_DATETIME:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ORIGINATION_EXPIRE_DATETIME, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ORIGINATION_EXPIRE_DATETIME, v->get());
+        }
+        break;
+    case KMV1::Tag::USAGE_EXPIRE_DATETIME:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_USAGE_EXPIRE_DATETIME, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_USAGE_EXPIRE_DATETIME, v->get());
+        }
+        break;
+    case KMV1::Tag::MIN_SECONDS_BETWEEN_OPS:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_MIN_SECONDS_BETWEEN_OPS, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_MIN_SECONDS_BETWEEN_OPS, v->get());
+        }
+        break;
+    case KMV1::Tag::MAX_USES_PER_BOOT:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_MAX_USES_PER_BOOT, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_MAX_USES_PER_BOOT, v->get());
+        }
+        break;
+    case KMV1::Tag::USAGE_COUNT_LIMIT:
+        // Does not exist in KM < KeyMint 1.0.
+        break;
+    case KMV1::Tag::USER_ID:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_USER_ID, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_USER_ID, v->get());
+        }
+        break;
+    case KMV1::Tag::USER_SECURE_ID:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_USER_SECURE_ID, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_USER_SECURE_ID, v->get());
+        }
+        break;
+    case KMV1::Tag::NO_AUTH_REQUIRED:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_NO_AUTH_REQUIRED, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_NO_AUTH_REQUIRED, v->get());
+        }
+        break;
+    case KMV1::Tag::USER_AUTH_TYPE:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_USER_AUTH_TYPE, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_USER_AUTH_TYPE, convert(v->get()));
+        }
+        break;
+    case KMV1::Tag::AUTH_TIMEOUT:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_AUTH_TIMEOUT, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_AUTH_TIMEOUT, v->get());
+        }
+        break;
+    case KMV1::Tag::ALLOW_WHILE_ON_BODY:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ALLOW_WHILE_ON_BODY, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ALLOW_WHILE_ON_BODY, v->get());
+        }
+        break;
+    case KMV1::Tag::TRUSTED_USER_PRESENCE_REQUIRED:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_TRUSTED_USER_PRESENCE_REQUIRED, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_TRUSTED_USER_PRESENCE_REQUIRED, v->get());
+        }
+        break;
+    case KMV1::Tag::TRUSTED_CONFIRMATION_REQUIRED:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_TRUSTED_CONFIRMATION_REQUIRED, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_TRUSTED_CONFIRMATION_REQUIRED, v->get());
+        }
+        break;
+    case KMV1::Tag::UNLOCKED_DEVICE_REQUIRED:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_UNLOCKED_DEVICE_REQUIRED, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_UNLOCKED_DEVICE_REQUIRED, v->get());
+        }
+        break;
+    case KMV1::Tag::APPLICATION_ID:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_APPLICATION_ID, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_APPLICATION_ID, v->get());
+        }
+        break;
+    case KMV1::Tag::APPLICATION_DATA:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_APPLICATION_DATA, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_APPLICATION_DATA, v->get());
+        }
+        break;
+    case KMV1::Tag::CREATION_DATETIME:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_CREATION_DATETIME, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_CREATION_DATETIME, v->get());
+        }
+        break;
+    case KMV1::Tag::ORIGIN:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ORIGIN, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ORIGIN, convert(v->get()));
+        }
+        break;
+    case KMV1::Tag::ROOT_OF_TRUST:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ROOT_OF_TRUST, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ROOT_OF_TRUST, v->get());
+        }
+        break;
+    case KMV1::Tag::OS_VERSION:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_OS_VERSION, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_OS_VERSION, v->get());
+        }
+        break;
+    case KMV1::Tag::OS_PATCHLEVEL:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_OS_PATCHLEVEL, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_OS_PATCHLEVEL, v->get());
+        }
+        break;
+    case KMV1::Tag::UNIQUE_ID:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_UNIQUE_ID, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_UNIQUE_ID, v->get());
+        }
+        break;
+    case KMV1::Tag::ATTESTATION_CHALLENGE:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_CHALLENGE, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_CHALLENGE, v->get());
+        }
+        break;
+    case KMV1::Tag::ATTESTATION_APPLICATION_ID:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_APPLICATION_ID, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_APPLICATION_ID, v->get());
+        }
+        break;
+    case KMV1::Tag::ATTESTATION_ID_BRAND:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_BRAND, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_ID_BRAND, v->get());
+        }
+        break;
+    case KMV1::Tag::ATTESTATION_ID_DEVICE:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_DEVICE, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_ID_DEVICE, v->get());
+        }
+        break;
+    case KMV1::Tag::ATTESTATION_ID_PRODUCT:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_PRODUCT, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_ID_PRODUCT, v->get());
+        }
+        break;
+    case KMV1::Tag::ATTESTATION_ID_SERIAL:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_SERIAL, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_ID_SERIAL, v->get());
+        }
+        break;
+    case KMV1::Tag::ATTESTATION_ID_IMEI:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_IMEI, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_ID_IMEI, v->get());
+        }
+        break;
+    case KMV1::Tag::ATTESTATION_ID_MEID:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_MEID, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_ID_MEID, v->get());
+        }
+        break;
+    case KMV1::Tag::ATTESTATION_ID_MANUFACTURER:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_MANUFACTURER, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_ID_MANUFACTURER, v->get());
+        }
+        break;
+    case KMV1::Tag::ATTESTATION_ID_MODEL:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ATTESTATION_ID_MODEL, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ATTESTATION_ID_MODEL, v->get());
+        }
+        break;
+    case KMV1::Tag::VENDOR_PATCHLEVEL:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_VENDOR_PATCHLEVEL, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_VENDOR_PATCHLEVEL, v->get());
+        }
+        break;
+    case KMV1::Tag::BOOT_PATCHLEVEL:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_BOOT_PATCHLEVEL, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_BOOT_PATCHLEVEL, v->get());
+        }
+        break;
+    case KMV1::Tag::DEVICE_UNIQUE_ATTESTATION:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_DEVICE_UNIQUE_ATTESTATION, kp)) {
+            return V4_0::makeKeyParameter(V4_1::TAG_DEVICE_UNIQUE_ATTESTATION, v->get());
+        }
+        break;
+    case KMV1::Tag::IDENTITY_CREDENTIAL_KEY:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_IDENTITY_CREDENTIAL_KEY, kp)) {
+            return V4_0::makeKeyParameter(V4_1::TAG_IDENTITY_CREDENTIAL_KEY, v->get());
+        }
+        break;
+    case KMV1::Tag::STORAGE_KEY:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_STORAGE_KEY, kp)) {
+            return V4_0::makeKeyParameter(V4_1::TAG_STORAGE_KEY, v->get());
+        }
+        break;
+    case KMV1::Tag::ASSOCIATED_DATA:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_ASSOCIATED_DATA, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_ASSOCIATED_DATA, v->get());
+        }
+        break;
+    case KMV1::Tag::NONCE:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_NONCE, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_NONCE, v->get());
+        }
+        break;
+    case KMV1::Tag::MAC_LENGTH:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_MAC_LENGTH, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_MAC_LENGTH, v->get());
+        }
+        break;
+    case KMV1::Tag::RESET_SINCE_ID_ROTATION:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_RESET_SINCE_ID_ROTATION, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_RESET_SINCE_ID_ROTATION, v->get());
+        }
+        break;
+    case KMV1::Tag::CONFIRMATION_TOKEN:
+        if (auto v = KMV1::authorizationValue(KMV1::TAG_CONFIRMATION_TOKEN, kp)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_CONFIRMATION_TOKEN, v->get());
+        }
+        break;
+    case KMV1::Tag::RSA_OAEP_MGF_DIGEST:
+    case KMV1::Tag::CERTIFICATE_SERIAL:
+    case KMV1::Tag::CERTIFICATE_SUBJECT:
+    case KMV1::Tag::CERTIFICATE_NOT_BEFORE:
+    case KMV1::Tag::CERTIFICATE_NOT_AFTER:
+        // These tags do not exist in KM < KeyMint 1.0.
+        break;
+    case KMV1::Tag::MAX_BOOT_LEVEL:
+        // Does not exist in API level 30 or below.
+        break;
+    }
+    return V4_0::KeyParameter{.tag = V4_0::Tag::INVALID};
+}
+
+static KMV1::KeyParameter convertKeyParameterFromLegacy(const V4_0::KeyParameter& kp) {
+    auto unwrapper = [](auto v) -> auto {
+        if (v.isOk()) {
+            return std::optional(std::reference_wrapper(v.value()));
+        } else {
+            return std::optional<decltype(std::reference_wrapper(v.value()))>{};
+        }
+    };
+    switch (kp.tag) {
+    case V4_0::Tag::INVALID:
+        break;
+    case V4_0::Tag::PURPOSE:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_PURPOSE, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, convert(v->get()));
+        }
+        break;
+    case V4_0::Tag::ALGORITHM:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ALGORITHM, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ALGORITHM, convert(v->get()));
+        }
+        break;
+    case V4_0::Tag::KEY_SIZE:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_KEY_SIZE, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_KEY_SIZE, v->get());
+        }
+        break;
+    case V4_0::Tag::BLOCK_MODE:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_BLOCK_MODE, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_BLOCK_MODE, convert(v->get()));
+        }
+        break;
+    case V4_0::Tag::DIGEST:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_DIGEST, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_DIGEST, convert(v->get()));
+        }
+        break;
+    case V4_0::Tag::PADDING:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_PADDING, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_PADDING, convert(v->get()));
+        }
+        break;
+    case V4_0::Tag::CALLER_NONCE:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_CALLER_NONCE, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_CALLER_NONCE, v->get());
+        }
+        break;
+    case V4_0::Tag::MIN_MAC_LENGTH:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_MIN_MAC_LENGTH, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_MIN_MAC_LENGTH, v->get());
+        }
+        break;
+    case V4_0::Tag::EC_CURVE:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_EC_CURVE, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_EC_CURVE, convert(v->get()));
+        }
+        break;
+    case V4_0::Tag::RSA_PUBLIC_EXPONENT:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_RSA_PUBLIC_EXPONENT, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_RSA_PUBLIC_EXPONENT, v->get());
+        }
+        break;
+    case V4_0::Tag::INCLUDE_UNIQUE_ID:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_INCLUDE_UNIQUE_ID, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_INCLUDE_UNIQUE_ID, v->get());
+        }
+        break;
+    case V4_0::Tag::BLOB_USAGE_REQUIREMENTS:
+        // This tag has been removed. Mapped on invalid.
+        break;
+    case V4_0::Tag::BOOTLOADER_ONLY:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_BOOTLOADER_ONLY, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_BOOTLOADER_ONLY, v->get());
+        }
+        break;
+    case V4_0::Tag::ROLLBACK_RESISTANCE:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ROLLBACK_RESISTANCE, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ROLLBACK_RESISTANCE, v->get());
+        }
+        break;
+    case V4_0::Tag::HARDWARE_TYPE:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_HARDWARE_TYPE, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_HARDWARE_TYPE, convert(v->get()));
+        }
+        break;
+    case V4_0::Tag::ACTIVE_DATETIME:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ACTIVE_DATETIME, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ACTIVE_DATETIME, v->get());
+        }
+        break;
+    case V4_0::Tag::ORIGINATION_EXPIRE_DATETIME:
+        if (auto v =
+                unwrapper(V4_0::authorizationValue(V4_0::TAG_ORIGINATION_EXPIRE_DATETIME, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ORIGINATION_EXPIRE_DATETIME, v->get());
+        }
+        break;
+    case V4_0::Tag::USAGE_EXPIRE_DATETIME:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_USAGE_EXPIRE_DATETIME, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_USAGE_EXPIRE_DATETIME, v->get());
+        }
+        break;
+    case V4_0::Tag::MIN_SECONDS_BETWEEN_OPS:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_MIN_SECONDS_BETWEEN_OPS, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_MIN_SECONDS_BETWEEN_OPS, v->get());
+        }
+        break;
+    case V4_0::Tag::MAX_USES_PER_BOOT:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_MAX_USES_PER_BOOT, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_MAX_USES_PER_BOOT, v->get());
+        }
+        break;
+    case V4_0::Tag::USER_ID:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_USER_ID, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_USER_ID, v->get());
+        }
+        break;
+    case V4_0::Tag::USER_SECURE_ID:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_USER_SECURE_ID, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_USER_SECURE_ID, v->get());
+        }
+        break;
+    case V4_0::Tag::NO_AUTH_REQUIRED:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_NO_AUTH_REQUIRED, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED, v->get());
+        }
+        break;
+    case V4_0::Tag::USER_AUTH_TYPE:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_USER_AUTH_TYPE, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_USER_AUTH_TYPE, convert(v->get()));
+        }
+        break;
+    case V4_0::Tag::AUTH_TIMEOUT:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_AUTH_TIMEOUT, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_AUTH_TIMEOUT, v->get());
+        }
+        break;
+    case V4_0::Tag::ALLOW_WHILE_ON_BODY:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ALLOW_WHILE_ON_BODY, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ALLOW_WHILE_ON_BODY, v->get());
+        }
+        break;
+    case V4_0::Tag::TRUSTED_USER_PRESENCE_REQUIRED:
+        if (auto v =
+                unwrapper(V4_0::authorizationValue(V4_0::TAG_TRUSTED_USER_PRESENCE_REQUIRED, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_TRUSTED_USER_PRESENCE_REQUIRED, v->get());
+        }
+        break;
+    case V4_0::Tag::TRUSTED_CONFIRMATION_REQUIRED:
+        if (auto v =
+                unwrapper(V4_0::authorizationValue(V4_0::TAG_TRUSTED_CONFIRMATION_REQUIRED, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_TRUSTED_CONFIRMATION_REQUIRED, v->get());
+        }
+        break;
+    case V4_0::Tag::UNLOCKED_DEVICE_REQUIRED:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_UNLOCKED_DEVICE_REQUIRED, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_UNLOCKED_DEVICE_REQUIRED, v->get());
+        }
+        break;
+    case V4_0::Tag::APPLICATION_ID:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_APPLICATION_ID, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_APPLICATION_ID, v->get());
+        }
+        break;
+    case V4_0::Tag::APPLICATION_DATA:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_APPLICATION_DATA, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_APPLICATION_DATA, v->get());
+        }
+        break;
+    case V4_0::Tag::CREATION_DATETIME:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_CREATION_DATETIME, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_CREATION_DATETIME, v->get());
+        }
+        break;
+    case V4_0::Tag::ORIGIN:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ORIGIN, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ORIGIN, convert(v->get()));
+        }
+        break;
+    case V4_0::Tag::ROOT_OF_TRUST:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ROOT_OF_TRUST, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ROOT_OF_TRUST, v->get());
+        }
+        break;
+    case V4_0::Tag::OS_VERSION:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_OS_VERSION, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_OS_VERSION, v->get());
+        }
+        break;
+    case V4_0::Tag::OS_PATCHLEVEL:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_OS_PATCHLEVEL, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_OS_PATCHLEVEL, v->get());
+        }
+        break;
+    case V4_0::Tag::UNIQUE_ID:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_UNIQUE_ID, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_UNIQUE_ID, v->get());
+        }
+        break;
+    case V4_0::Tag::ATTESTATION_CHALLENGE:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_CHALLENGE, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_CHALLENGE, v->get());
+        }
+        break;
+    case V4_0::Tag::ATTESTATION_APPLICATION_ID:
+        if (auto v =
+                unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_APPLICATION_ID, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_APPLICATION_ID, v->get());
+        }
+        break;
+    case V4_0::Tag::ATTESTATION_ID_BRAND:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_ID_BRAND, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_ID_BRAND, v->get());
+        }
+        break;
+    case V4_0::Tag::ATTESTATION_ID_DEVICE:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_ID_DEVICE, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_ID_DEVICE, v->get());
+        }
+        break;
+    case V4_0::Tag::ATTESTATION_ID_PRODUCT:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_ID_PRODUCT, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_ID_PRODUCT, v->get());
+        }
+        break;
+    case V4_0::Tag::ATTESTATION_ID_SERIAL:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_ID_SERIAL, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_ID_SERIAL, v->get());
+        }
+        break;
+    case V4_0::Tag::ATTESTATION_ID_IMEI:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_ID_IMEI, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_ID_IMEI, v->get());
+        }
+        break;
+    case V4_0::Tag::ATTESTATION_ID_MEID:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_ID_MEID, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_ID_MEID, v->get());
+        }
+        break;
+    case V4_0::Tag::ATTESTATION_ID_MANUFACTURER:
+        if (auto v =
+                unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_ID_MANUFACTURER, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_ID_MANUFACTURER, v->get());
+        }
+        break;
+    case V4_0::Tag::ATTESTATION_ID_MODEL:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ATTESTATION_ID_MODEL, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ATTESTATION_ID_MODEL, v->get());
+        }
+        break;
+    case V4_0::Tag::VENDOR_PATCHLEVEL:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_VENDOR_PATCHLEVEL, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_VENDOR_PATCHLEVEL, v->get());
+        }
+        break;
+    case V4_0::Tag::BOOT_PATCHLEVEL:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_BOOT_PATCHLEVEL, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_BOOT_PATCHLEVEL, v->get());
+        }
+        break;
+    case V4_0::Tag::ASSOCIATED_DATA:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_ASSOCIATED_DATA, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_ASSOCIATED_DATA, v->get());
+        }
+        break;
+    case V4_0::Tag::NONCE:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_NONCE, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_NONCE, v->get());
+        }
+        break;
+    case V4_0::Tag::MAC_LENGTH:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_MAC_LENGTH, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_MAC_LENGTH, v->get());
+        }
+        break;
+    case V4_0::Tag::RESET_SINCE_ID_ROTATION:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_RESET_SINCE_ID_ROTATION, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_RESET_SINCE_ID_ROTATION, v->get());
+        }
+        break;
+    case V4_0::Tag::CONFIRMATION_TOKEN:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_0::TAG_CONFIRMATION_TOKEN, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_CONFIRMATION_TOKEN, v->get());
+        }
+        break;
+    default:
+        break;
+    }
+
+    switch (static_cast<V4_1::Tag>(kp.tag)) {
+    case V4_1::Tag::EARLY_BOOT_ONLY:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_1::TAG_EARLY_BOOT_ONLY, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_EARLY_BOOT_ONLY, v->get());
+        }
+        break;
+    case V4_1::Tag::DEVICE_UNIQUE_ATTESTATION:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_1::TAG_DEVICE_UNIQUE_ATTESTATION, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_DEVICE_UNIQUE_ATTESTATION, v->get());
+        }
+        break;
+    case V4_1::Tag::IDENTITY_CREDENTIAL_KEY:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_1::TAG_IDENTITY_CREDENTIAL_KEY, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_IDENTITY_CREDENTIAL_KEY, v->get());
+        }
+        break;
+    case V4_1::Tag::STORAGE_KEY:
+        if (auto v = unwrapper(V4_0::authorizationValue(V4_1::TAG_STORAGE_KEY, kp))) {
+            return KMV1::makeKeyParameter(KMV1::TAG_STORAGE_KEY, v->get());
+        }
+        break;
+    default:
+        break;
+    }
+
+    return KMV1::makeKeyParameter(KMV1::TAG_INVALID);
+}
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
new file mode 100644
index 0000000..eddd684
--- /dev/null
+++ b/keystore2/src/km_compat/lib.rs
@@ -0,0 +1,378 @@
+// 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.
+
+//! Export into Rust a function to create a KeyMintDevice and add it as a service.
+
+#[allow(missing_docs)] // TODO remove this
+extern "C" {
+    fn addKeyMintDeviceService() -> i32;
+}
+
+#[allow(missing_docs)] // TODO remove this
+pub fn add_keymint_device_service() -> i32 {
+    unsafe { addKeyMintDeviceService() }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+    use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+        Algorithm::Algorithm, BeginResult::BeginResult, BlockMode::BlockMode, Digest::Digest,
+        ErrorCode::ErrorCode, IKeyMintDevice::IKeyMintDevice, KeyCreationResult::KeyCreationResult,
+        KeyFormat::KeyFormat, KeyParameter::KeyParameter, 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;
+
+    static COMPAT_NAME: &str = "android.security.compat";
+
+    fn get_device() -> Option<Strong<dyn IKeyMintDevice>> {
+        add_keymint_device_service();
+        let compat_service: Strong<dyn IKeystoreCompatService> =
+            binder::get_interface(COMPAT_NAME).ok()?;
+        compat_service.getKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT).ok()
+    }
+
+    macro_rules! get_device_or_skip_test {
+        () => {
+            match get_device() {
+                Some(dev) => dev,
+                None => return,
+            }
+        };
+    }
+
+    #[test]
+    fn test_get_hardware_info() {
+        let legacy = get_device_or_skip_test!();
+        let hinfo = legacy.getHardwareInfo();
+        assert!(hinfo.is_ok());
+    }
+
+    #[test]
+    fn test_add_rng_entropy() {
+        let legacy = get_device_or_skip_test!();
+        let result = legacy.addRngEntropy(&[42; 16]);
+        assert!(result.is_ok(), "{:?}", result);
+    }
+
+    // TODO: If I only need the key itself, don't return the other things.
+    fn generate_key(legacy: &dyn IKeyMintDevice, kps: Vec<KeyParameter>) -> KeyCreationResult {
+        let creation_result =
+            legacy.generateKey(&kps, None /* attest_key */).expect("Failed to generate key");
+        assert_ne!(creation_result.keyBlob.len(), 0);
+        creation_result
+    }
+
+    // Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to GeneralizedTime
+    // 999912312359559, which is 253402300799000 ms from Jan 1, 1970.
+    const UNDEFINED_NOT_AFTER: i64 = 253402300799000i64;
+
+    fn generate_rsa_key(legacy: &dyn IKeyMintDevice, encrypt: bool, attest: bool) -> Vec<u8> {
+        let mut kps = vec![
+            KeyParameter {
+                tag: Tag::ALGORITHM,
+                value: KeyParameterValue::Algorithm(Algorithm::RSA),
+            },
+            KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(2048) },
+            KeyParameter {
+                tag: Tag::RSA_PUBLIC_EXPONENT,
+                value: KeyParameterValue::LongInteger(65537),
+            },
+            KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) },
+            KeyParameter {
+                tag: Tag::PADDING,
+                value: KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS),
+            },
+            KeyParameter { tag: Tag::NO_AUTH_REQUIRED, value: KeyParameterValue::BoolValue(true) },
+            KeyParameter {
+                tag: Tag::PURPOSE,
+                value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
+            },
+            KeyParameter {
+                tag: Tag::CERTIFICATE_NOT_BEFORE,
+                value: KeyParameterValue::DateTime(0),
+            },
+            KeyParameter {
+                tag: Tag::CERTIFICATE_NOT_AFTER,
+                value: KeyParameterValue::DateTime(UNDEFINED_NOT_AFTER),
+            },
+        ];
+        if encrypt {
+            kps.push(KeyParameter {
+                tag: Tag::PURPOSE,
+                value: KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
+            });
+        }
+        if attest {
+            kps.push(KeyParameter {
+                tag: Tag::ATTESTATION_CHALLENGE,
+                value: KeyParameterValue::Blob(vec![42; 8]),
+            });
+            kps.push(KeyParameter {
+                tag: Tag::ATTESTATION_APPLICATION_ID,
+                value: KeyParameterValue::Blob(vec![42; 8]),
+            });
+        }
+        let creation_result = generate_key(legacy, kps);
+        if attest {
+            // TODO: Will this always be greater than 1?
+            assert!(creation_result.certificateChain.len() > 1);
+        } else {
+            assert_eq!(creation_result.certificateChain.len(), 1);
+        }
+        creation_result.keyBlob
+    }
+
+    #[test]
+    fn test_generate_key_no_encrypt() {
+        let legacy = get_device_or_skip_test!();
+        generate_rsa_key(legacy.as_ref(), false, false);
+    }
+
+    #[test]
+    fn test_generate_key_encrypt() {
+        let legacy = get_device_or_skip_test!();
+        generate_rsa_key(legacy.as_ref(), true, false);
+    }
+
+    #[test]
+    fn test_generate_key_attested() {
+        let legacy = get_device_or_skip_test!();
+        generate_rsa_key(legacy.as_ref(), false, true);
+    }
+
+    #[test]
+    fn test_import_key() {
+        let legacy = get_device_or_skip_test!();
+        let kps = [KeyParameter {
+            tag: Tag::ALGORITHM,
+            value: KeyParameterValue::Algorithm(Algorithm::AES),
+        }];
+        let kf = KeyFormat::RAW;
+        let kd = [0; 16];
+        let creation_result =
+            legacy.importKey(&kps, kf, &kd, None /* attest_key */).expect("Failed to import key");
+        assert_ne!(creation_result.keyBlob.len(), 0);
+        assert_eq!(creation_result.certificateChain.len(), 0);
+    }
+
+    #[test]
+    fn test_import_wrapped_key() {
+        let legacy = get_device_or_skip_test!();
+        let result = legacy.importWrappedKey(&[], &[], &[], &[], 0, 0);
+        // For this test we only care that there was no crash.
+        assert!(result.is_ok() || result.is_err());
+    }
+
+    #[test]
+    fn test_upgrade_key() {
+        let legacy = get_device_or_skip_test!();
+        let blob = generate_rsa_key(legacy.as_ref(), false, false);
+        let result = legacy.upgradeKey(&blob, &[]);
+        // For this test we only care that there was no crash.
+        assert!(result.is_ok() || result.is_err());
+    }
+
+    #[test]
+    fn test_delete_key() {
+        let legacy = get_device_or_skip_test!();
+        let blob = generate_rsa_key(legacy.as_ref(), false, false);
+        let result = legacy.deleteKey(&blob);
+        assert!(result.is_ok(), "{:?}", result);
+    }
+
+    #[test]
+    fn test_delete_all_keys() {
+        let legacy = get_device_or_skip_test!();
+        let result = legacy.deleteAllKeys();
+        assert!(result.is_ok(), "{:?}", result);
+    }
+
+    #[test]
+    fn test_destroy_attestation_ids() {
+        let legacy = get_device_or_skip_test!();
+        let result = legacy.destroyAttestationIds();
+        assert!(result.is_err());
+        assert_eq!(result.unwrap_err().service_specific_error(), ErrorCode::UNIMPLEMENTED.0,);
+    }
+
+    fn generate_aes_key(legacy: &dyn IKeyMintDevice) -> Vec<u8> {
+        let kps = vec![
+            KeyParameter {
+                tag: Tag::ALGORITHM,
+                value: KeyParameterValue::Algorithm(Algorithm::AES),
+            },
+            KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(128) },
+            KeyParameter {
+                tag: Tag::BLOCK_MODE,
+                value: KeyParameterValue::BlockMode(BlockMode::CBC),
+            },
+            KeyParameter {
+                tag: Tag::PADDING,
+                value: KeyParameterValue::PaddingMode(PaddingMode::NONE),
+            },
+            KeyParameter { tag: Tag::NO_AUTH_REQUIRED, value: KeyParameterValue::BoolValue(true) },
+            KeyParameter {
+                tag: Tag::PURPOSE,
+                value: KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
+            },
+            KeyParameter {
+                tag: Tag::PURPOSE,
+                value: KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT),
+            },
+        ];
+        let creation_result = generate_key(legacy, kps);
+        assert_eq!(creation_result.certificateChain.len(), 0);
+        creation_result.keyBlob
+    }
+
+    fn begin(
+        legacy: &dyn IKeyMintDevice,
+        blob: &[u8],
+        purpose: KeyPurpose,
+        extra_params: Option<Vec<KeyParameter>>,
+    ) -> BeginResult {
+        let mut kps = vec![
+            KeyParameter {
+                tag: Tag::BLOCK_MODE,
+                value: KeyParameterValue::BlockMode(BlockMode::CBC),
+            },
+            KeyParameter {
+                tag: Tag::PADDING,
+                value: KeyParameterValue::PaddingMode(PaddingMode::NONE),
+            },
+        ];
+        if let Some(mut extras) = extra_params {
+            kps.append(&mut extras);
+        }
+        let result = legacy.begin(purpose, &blob, &kps, None);
+        assert!(result.is_ok(), "{:?}", result);
+        result.unwrap()
+    }
+
+    #[test]
+    fn test_begin_abort() {
+        let legacy = get_device_or_skip_test!();
+        let blob = generate_aes_key(legacy.as_ref());
+        let begin_result = begin(legacy.as_ref(), &blob, KeyPurpose::ENCRYPT, None);
+        let operation = begin_result.operation.unwrap();
+        let result = operation.abort();
+        assert!(result.is_ok(), "{:?}", result);
+        let result = operation.abort();
+        assert!(result.is_err());
+    }
+
+    #[test]
+    fn test_begin_update_finish() {
+        let legacy = get_device_or_skip_test!();
+        let blob = generate_aes_key(legacy.as_ref());
+
+        let begin_result = begin(legacy.as_ref(), &blob, KeyPurpose::ENCRYPT, None);
+        let operation = begin_result.operation.unwrap();
+
+        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 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());
+
+        let begin_result =
+            begin(legacy.as_ref(), &blob, KeyPurpose::DECRYPT, Some(begin_result.params));
+
+        let operation = begin_result.operation.unwrap();
+
+        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(
+            &ciphertext,
+            None, /* authToken */
+            None, /* timestampToken */
+        );
+        assert!(result.is_ok(), "{:?}", result);
+        assert_eq!(result.unwrap(), message);
+        let result = operation.finish(
+            None, /* input */
+            None, /* signature */
+            None, /* authToken */
+            None, /* timestampToken */
+            None, /* confirmationToken */
+        );
+        assert!(result.is_ok(), "{:?}", result);
+    }
+
+    #[test]
+    fn test_secure_clock() {
+        add_keymint_device_service();
+        let compat_service: binder::Strong<dyn IKeystoreCompatService> =
+            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);
+        assert!(result.is_ok(), "{:?}", result);
+        let result = result.unwrap();
+        assert_eq!(result.challenge, challenge);
+        assert_eq!(result.mac.len(), 32);
+    }
+
+    #[test]
+    fn test_shared_secret() {
+        add_keymint_device_service();
+        let compat_service: binder::Strong<dyn IKeystoreCompatService> =
+            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);
+        let params = result.unwrap();
+
+        let result = shared_secret.computeSharedSecret(&[params]);
+        assert!(result.is_ok(), "{:?}", result);
+        assert_ne!(result.unwrap().len(), 0);
+    }
+}
diff --git a/keystore2/src/km_compat/parameter_conversion_test.cpp b/keystore2/src/km_compat/parameter_conversion_test.cpp
new file mode 100644
index 0000000..48af20c
--- /dev/null
+++ b/keystore2/src/km_compat/parameter_conversion_test.cpp
@@ -0,0 +1,233 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "km_compat_type_conversion.h"
+
+#define TEST_ENUM_CONVERSION(type, variant)                                                        \
+    ASSERT_EQ(KMV1::type::variant, convert(V4_0::type::variant));                                  \
+    ASSERT_EQ(V4_0::type::variant, convert(KMV1::type::variant))
+
+TEST(KmCompatTypeConversionTest, testEnumCoversion) {
+    TEST_ENUM_CONVERSION(KeyPurpose, ENCRYPT);
+    TEST_ENUM_CONVERSION(KeyPurpose, DECRYPT);
+    TEST_ENUM_CONVERSION(KeyPurpose, SIGN);
+    TEST_ENUM_CONVERSION(KeyPurpose, VERIFY);
+    TEST_ENUM_CONVERSION(KeyPurpose, WRAP_KEY);
+    TEST_ENUM_CONVERSION(Algorithm, RSA);
+    TEST_ENUM_CONVERSION(Algorithm, EC);
+    TEST_ENUM_CONVERSION(Algorithm, AES);
+    TEST_ENUM_CONVERSION(Algorithm, TRIPLE_DES);
+    TEST_ENUM_CONVERSION(Algorithm, HMAC);
+    TEST_ENUM_CONVERSION(Digest, NONE);
+    TEST_ENUM_CONVERSION(Digest, MD5);
+    TEST_ENUM_CONVERSION(Digest, SHA1);
+    TEST_ENUM_CONVERSION(Digest, SHA_2_224);
+    TEST_ENUM_CONVERSION(Digest, SHA_2_256);
+    TEST_ENUM_CONVERSION(Digest, SHA_2_384);
+    TEST_ENUM_CONVERSION(Digest, SHA_2_512);
+    TEST_ENUM_CONVERSION(EcCurve, P_224);
+    TEST_ENUM_CONVERSION(EcCurve, P_256);
+    TEST_ENUM_CONVERSION(EcCurve, P_384);
+    TEST_ENUM_CONVERSION(EcCurve, P_521);
+    TEST_ENUM_CONVERSION(BlockMode, ECB);
+    TEST_ENUM_CONVERSION(BlockMode, CBC);
+    TEST_ENUM_CONVERSION(BlockMode, CTR);
+    TEST_ENUM_CONVERSION(BlockMode, GCM);
+    TEST_ENUM_CONVERSION(PaddingMode, NONE);
+    TEST_ENUM_CONVERSION(PaddingMode, RSA_OAEP);
+    TEST_ENUM_CONVERSION(PaddingMode, RSA_PSS);
+    TEST_ENUM_CONVERSION(PaddingMode, RSA_PKCS1_1_5_ENCRYPT);
+    TEST_ENUM_CONVERSION(PaddingMode, RSA_PKCS1_1_5_SIGN);
+    TEST_ENUM_CONVERSION(PaddingMode, PKCS7);
+    TEST_ENUM_CONVERSION(HardwareAuthenticatorType, PASSWORD);
+    TEST_ENUM_CONVERSION(HardwareAuthenticatorType, FINGERPRINT);
+    TEST_ENUM_CONVERSION(SecurityLevel, SOFTWARE);
+    TEST_ENUM_CONVERSION(SecurityLevel, TRUSTED_ENVIRONMENT);
+    TEST_ENUM_CONVERSION(SecurityLevel, STRONGBOX);
+    TEST_ENUM_CONVERSION(KeyOrigin, GENERATED);
+    TEST_ENUM_CONVERSION(KeyOrigin, DERIVED);
+    TEST_ENUM_CONVERSION(KeyOrigin, IMPORTED);
+    TEST_ENUM_CONVERSION(KeyOrigin, GENERATED);
+    TEST_ENUM_CONVERSION(KeyOrigin, SECURELY_IMPORTED);
+
+    // RESERVED and UNKNOWN correspond but changed their names.
+    ASSERT_EQ(KMV1::KeyOrigin::RESERVED, convert(V4_0::KeyOrigin::UNKNOWN));
+    ASSERT_EQ(V4_0::KeyOrigin::UNKNOWN, convert(KMV1::KeyOrigin::RESERVED));
+}
+
+#define TEST_KEY_PARAMETER_CONVERSION_V4_0(tag)                                                    \
+    do {                                                                                           \
+        auto kmv1_param = KMV1::makeKeyParameter(                                                  \
+            KMV1::tag, KMV1::TypedTag2ValueType<decltype(KMV1::tag)>::type{});                     \
+        auto legacy_param = V4_0::makeKeyParameter(                                                \
+            V4_0::tag, V4_0::TypedTag2ValueType<decltype(V4_0::tag)>::type{});                     \
+        ASSERT_EQ(legacy_param, convertKeyParameterToLegacy(kmv1_param));                          \
+        ASSERT_EQ(kmv1_param, convertKeyParameterFromLegacy(legacy_param));                        \
+    } while (false)
+
+#define TEST_KEY_PARAMETER_CONVERSION_V4_1(tag)                                                    \
+    do {                                                                                           \
+        auto kmv1_param = KMV1::makeKeyParameter(                                                  \
+            KMV1::tag, KMV1::TypedTag2ValueType<decltype(KMV1::tag)>::type{});                     \
+        auto legacy_param = V4_0::makeKeyParameter(                                                \
+            V4_1::tag, V4_0::TypedTag2ValueType<decltype(V4_1::tag)>::type{});                     \
+        ASSERT_EQ(legacy_param, convertKeyParameterToLegacy(kmv1_param));                          \
+        ASSERT_EQ(kmv1_param, convertKeyParameterFromLegacy(legacy_param));                        \
+    } while (false)
+
+TEST(KmCompatTypeConversionTest, testKeyParameterConversion) {
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ACTIVE_DATETIME);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ALGORITHM);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ALLOW_WHILE_ON_BODY);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_APPLICATION_DATA);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_APPLICATION_ID);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ASSOCIATED_DATA);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ATTESTATION_APPLICATION_ID);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ATTESTATION_CHALLENGE);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ATTESTATION_ID_BRAND);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ATTESTATION_ID_DEVICE);
+    //    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ATTESTATION_ID_IMEI);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ATTESTATION_ID_MANUFACTURER);
+    //    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ATTESTATION_ID_MEID);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ATTESTATION_ID_PRODUCT);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ATTESTATION_ID_MODEL);
+    //    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ATTESTATION_ID_SERIAL);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_AUTH_TIMEOUT);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_BLOCK_MODE);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_BOOTLOADER_ONLY);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_BOOT_PATCHLEVEL);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_CALLER_NONCE);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_CONFIRMATION_TOKEN);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_CREATION_DATETIME);
+    TEST_KEY_PARAMETER_CONVERSION_V4_1(TAG_DEVICE_UNIQUE_ATTESTATION);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_DIGEST);
+    TEST_KEY_PARAMETER_CONVERSION_V4_1(TAG_EARLY_BOOT_ONLY);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_EC_CURVE);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_HARDWARE_TYPE);
+    TEST_KEY_PARAMETER_CONVERSION_V4_1(TAG_IDENTITY_CREDENTIAL_KEY);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_INCLUDE_UNIQUE_ID);
+    //    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_INVALID);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_KEY_SIZE);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_MAC_LENGTH);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_MAX_USES_PER_BOOT);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_MIN_MAC_LENGTH);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_MIN_SECONDS_BETWEEN_OPS);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_NONCE);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_NO_AUTH_REQUIRED);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ORIGIN);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ORIGINATION_EXPIRE_DATETIME);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_OS_PATCHLEVEL);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_OS_VERSION);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_PADDING);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_PURPOSE);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_RESET_SINCE_ID_ROTATION);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ROLLBACK_RESISTANCE);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_ROOT_OF_TRUST);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_RSA_PUBLIC_EXPONENT);
+    TEST_KEY_PARAMETER_CONVERSION_V4_1(TAG_STORAGE_KEY);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_TRUSTED_CONFIRMATION_REQUIRED);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_TRUSTED_USER_PRESENCE_REQUIRED);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_UNIQUE_ID);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_UNLOCKED_DEVICE_REQUIRED);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_USAGE_EXPIRE_DATETIME);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_USER_AUTH_TYPE);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_USER_ID);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_USER_SECURE_ID);
+    TEST_KEY_PARAMETER_CONVERSION_V4_0(TAG_VENDOR_PATCHLEVEL);
+}
+
+#define TEST_ERROR_CODE_CONVERSION(variant)                                                        \
+    ASSERT_EQ(KMV1::ErrorCode::variant, convert(V4_0::ErrorCode::variant))
+
+TEST(KmCompatTypeConversionTest, testErrorCodeConversion) {
+    TEST_ERROR_CODE_CONVERSION(OK);
+    TEST_ERROR_CODE_CONVERSION(ROOT_OF_TRUST_ALREADY_SET);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_PURPOSE);
+    TEST_ERROR_CODE_CONVERSION(INCOMPATIBLE_PURPOSE);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_ALGORITHM);
+    TEST_ERROR_CODE_CONVERSION(INCOMPATIBLE_ALGORITHM);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_KEY_SIZE);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_BLOCK_MODE);
+    TEST_ERROR_CODE_CONVERSION(INCOMPATIBLE_BLOCK_MODE);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_MAC_LENGTH);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_PADDING_MODE);
+    TEST_ERROR_CODE_CONVERSION(INCOMPATIBLE_PADDING_MODE);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_DIGEST);
+    TEST_ERROR_CODE_CONVERSION(INCOMPATIBLE_DIGEST);
+    TEST_ERROR_CODE_CONVERSION(INVALID_EXPIRATION_TIME);
+    TEST_ERROR_CODE_CONVERSION(INVALID_USER_ID);
+    TEST_ERROR_CODE_CONVERSION(INVALID_AUTHORIZATION_TIMEOUT);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_KEY_FORMAT);
+    TEST_ERROR_CODE_CONVERSION(INCOMPATIBLE_KEY_FORMAT);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_KEY_VERIFICATION_ALGORITHM);
+    TEST_ERROR_CODE_CONVERSION(INVALID_INPUT_LENGTH);
+    TEST_ERROR_CODE_CONVERSION(KEY_EXPORT_OPTIONS_INVALID);
+    TEST_ERROR_CODE_CONVERSION(DELEGATION_NOT_ALLOWED);
+    TEST_ERROR_CODE_CONVERSION(KEY_NOT_YET_VALID);
+    TEST_ERROR_CODE_CONVERSION(KEY_EXPIRED);
+    TEST_ERROR_CODE_CONVERSION(KEY_USER_NOT_AUTHENTICATED);
+    TEST_ERROR_CODE_CONVERSION(OUTPUT_PARAMETER_NULL);
+    TEST_ERROR_CODE_CONVERSION(INVALID_OPERATION_HANDLE);
+    TEST_ERROR_CODE_CONVERSION(INSUFFICIENT_BUFFER_SPACE);
+    TEST_ERROR_CODE_CONVERSION(VERIFICATION_FAILED);
+    TEST_ERROR_CODE_CONVERSION(TOO_MANY_OPERATIONS);
+    TEST_ERROR_CODE_CONVERSION(UNEXPECTED_NULL_POINTER);
+    TEST_ERROR_CODE_CONVERSION(INVALID_KEY_BLOB);
+    TEST_ERROR_CODE_CONVERSION(IMPORTED_KEY_NOT_ENCRYPTED);
+    TEST_ERROR_CODE_CONVERSION(IMPORTED_KEY_DECRYPTION_FAILED);
+    TEST_ERROR_CODE_CONVERSION(IMPORTED_KEY_NOT_SIGNED);
+    TEST_ERROR_CODE_CONVERSION(IMPORTED_KEY_VERIFICATION_FAILED);
+    TEST_ERROR_CODE_CONVERSION(INVALID_ARGUMENT);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_TAG);
+    TEST_ERROR_CODE_CONVERSION(INVALID_TAG);
+    TEST_ERROR_CODE_CONVERSION(MEMORY_ALLOCATION_FAILED);
+    TEST_ERROR_CODE_CONVERSION(IMPORT_PARAMETER_MISMATCH);
+    TEST_ERROR_CODE_CONVERSION(SECURE_HW_ACCESS_DENIED);
+    TEST_ERROR_CODE_CONVERSION(OPERATION_CANCELLED);
+    TEST_ERROR_CODE_CONVERSION(CONCURRENT_ACCESS_CONFLICT);
+    TEST_ERROR_CODE_CONVERSION(SECURE_HW_BUSY);
+    TEST_ERROR_CODE_CONVERSION(SECURE_HW_COMMUNICATION_FAILED);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_EC_FIELD);
+    TEST_ERROR_CODE_CONVERSION(MISSING_NONCE);
+    TEST_ERROR_CODE_CONVERSION(INVALID_NONCE);
+    TEST_ERROR_CODE_CONVERSION(MISSING_MAC_LENGTH);
+    TEST_ERROR_CODE_CONVERSION(KEY_RATE_LIMIT_EXCEEDED);
+    TEST_ERROR_CODE_CONVERSION(CALLER_NONCE_PROHIBITED);
+    TEST_ERROR_CODE_CONVERSION(KEY_MAX_OPS_EXCEEDED);
+    TEST_ERROR_CODE_CONVERSION(INVALID_MAC_LENGTH);
+    TEST_ERROR_CODE_CONVERSION(MISSING_MIN_MAC_LENGTH);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_MIN_MAC_LENGTH);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_KDF);
+    TEST_ERROR_CODE_CONVERSION(UNSUPPORTED_EC_CURVE);
+    TEST_ERROR_CODE_CONVERSION(KEY_REQUIRES_UPGRADE);
+    TEST_ERROR_CODE_CONVERSION(ATTESTATION_CHALLENGE_MISSING);
+    ASSERT_EQ(KMV1::ErrorCode::KEYMINT_NOT_CONFIGURED,
+              convert(V4_0::ErrorCode::KEYMASTER_NOT_CONFIGURED));
+    TEST_ERROR_CODE_CONVERSION(ATTESTATION_APPLICATION_ID_MISSING);
+    TEST_ERROR_CODE_CONVERSION(CANNOT_ATTEST_IDS);
+    TEST_ERROR_CODE_CONVERSION(ROLLBACK_RESISTANCE_UNAVAILABLE);
+    TEST_ERROR_CODE_CONVERSION(HARDWARE_TYPE_UNAVAILABLE);
+    TEST_ERROR_CODE_CONVERSION(PROOF_OF_PRESENCE_REQUIRED);
+    TEST_ERROR_CODE_CONVERSION(CONCURRENT_PROOF_OF_PRESENCE_REQUESTED);
+    TEST_ERROR_CODE_CONVERSION(NO_USER_CONFIRMATION);
+    TEST_ERROR_CODE_CONVERSION(DEVICE_LOCKED);
+    TEST_ERROR_CODE_CONVERSION(UNIMPLEMENTED);
+    TEST_ERROR_CODE_CONVERSION(VERSION_MISMATCH);
+    TEST_ERROR_CODE_CONVERSION(UNKNOWN_ERROR);
+}
diff --git a/keystore2/src/km_compat/slot_test.cpp b/keystore2/src/km_compat/slot_test.cpp
new file mode 100644
index 0000000..43f3bc6
--- /dev/null
+++ b/keystore2/src/km_compat/slot_test.cpp
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "km_compat.h"
+#include <keymint_support/keymint_tags.h>
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/keymint/IKeyMintOperation.h>
+
+using ::aidl::android::hardware::security::keymint::Algorithm;
+using ::aidl::android::hardware::security::keymint::BlockMode;
+using ::aidl::android::hardware::security::keymint::Certificate;
+using ::aidl::android::hardware::security::keymint::Digest;
+using ::aidl::android::hardware::security::keymint::ErrorCode;
+using ::aidl::android::hardware::security::keymint::IKeyMintOperation;
+using ::aidl::android::hardware::security::keymint::KeyCharacteristics;
+using ::aidl::android::hardware::security::keymint::KeyPurpose;
+using ::aidl::android::hardware::security::keymint::PaddingMode;
+using ::aidl::android::hardware::security::keymint::SecurityLevel;
+
+namespace KMV1 = ::aidl::android::hardware::security::keymint;
+
+static std::vector<uint8_t> generateAESKey(std::shared_ptr<KeyMintDevice> device) {
+    auto keyParams = std::vector<KeyParameter>({
+        KMV1::makeKeyParameter(KMV1::TAG_ALGORITHM, Algorithm::AES),
+        KMV1::makeKeyParameter(KMV1::TAG_KEY_SIZE, 128),
+        KMV1::makeKeyParameter(KMV1::TAG_BLOCK_MODE, BlockMode::CBC),
+        KMV1::makeKeyParameter(KMV1::TAG_PADDING, PaddingMode::NONE),
+        KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED, true),
+        KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::ENCRYPT),
+        KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::DECRYPT),
+    });
+    KeyCreationResult creationResult;
+    auto status = device->generateKey(keyParams, std::nullopt /* attest_key */, &creationResult);
+    if (!status.isOk()) {
+        return {};
+    }
+    return creationResult.keyBlob;
+}
+
+static std::variant<BeginResult, ScopedAStatus> begin(std::shared_ptr<KeyMintDevice> device,
+                                                      bool valid) {
+    auto blob = generateAESKey(device);
+    std::vector<KeyParameter> kps;
+    if (valid) {
+        kps.push_back(KMV1::makeKeyParameter(KMV1::TAG_BLOCK_MODE, BlockMode::CBC));
+        kps.push_back(KMV1::makeKeyParameter(KMV1::TAG_PADDING, PaddingMode::NONE));
+    }
+    BeginResult beginResult;
+    auto status = device->begin(KeyPurpose::ENCRYPT, blob, kps, HardwareAuthToken(), &beginResult);
+    if (!status.isOk()) {
+        return status;
+    }
+    return beginResult;
+}
+
+static const int NUM_SLOTS = 2;
+
+TEST(SlotTest, TestSlots) {
+    static std::shared_ptr<KeyMintDevice> device =
+        KeyMintDevice::createKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
+    device->setNumFreeSlots(NUM_SLOTS);
+
+    // A begin() that returns a failure should not use a slot.
+    auto result = begin(device, false);
+    ASSERT_TRUE(std::holds_alternative<ScopedAStatus>(result));
+
+    // Fill up all the slots.
+    std::vector<std::shared_ptr<IKeyMintOperation>> operations;
+    for (int i = 0; i < NUM_SLOTS; i++) {
+        auto result = begin(device, true);
+        ASSERT_TRUE(std::holds_alternative<BeginResult>(result));
+        operations.push_back(std::get<BeginResult>(result).operation);
+    }
+
+    // We should not be able to create a new operation.
+    result = begin(device, true);
+    ASSERT_TRUE(std::holds_alternative<ScopedAStatus>(result));
+    ASSERT_EQ(std::get<ScopedAStatus>(result).getServiceSpecificError(),
+              static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
+
+    // TODO: I'm not sure how to generate a failing update call to test that.
+
+    // Calling finish should free up a slot.
+    auto last = operations.back();
+    operations.pop_back();
+    std::vector<uint8_t> 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 /* 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());
+    result = begin(device, true);
+    ASSERT_TRUE(std::holds_alternative<ScopedAStatus>(result));
+    ASSERT_EQ(std::get<ScopedAStatus>(result).getServiceSpecificError(),
+              static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
+
+    // Calling abort should free up a slot.
+    last = operations.back();
+    operations.pop_back();
+    status = last->abort();
+    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-aborted operation should not free up another slot.
+    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());
+    result = begin(device, true);
+    ASSERT_TRUE(std::holds_alternative<ScopedAStatus>(result));
+    ASSERT_EQ(std::get<ScopedAStatus>(result).getServiceSpecificError(),
+              static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
+
+    // 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());
+    // But generating a certificate with signCert does not use a slot.
+    kps.pop_back();
+    status = device->generateKey(kps, std::nullopt /* attest_key */, &creationResult);
+    ASSERT_TRUE(status.isOk());
+
+    // Destructing operations should free up their slots.
+    operations.clear();
+    result = begin(device, true);
+    ASSERT_TRUE(std::holds_alternative<BeginResult>(result));
+}
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
new file mode 100644
index 0000000..29d46ad
--- /dev/null
+++ b/keystore2/src/legacy_blob.rs
@@ -0,0 +1,1391 @@
+// 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.
+
+//! This module implements methods to load legacy keystore key blob files.
+
+#![allow(clippy::redundant_slicing)]
+
+use crate::{
+    error::{Error as KsError, ResponseCode},
+    key_parameter::{KeyParameter, KeyParameterValue},
+    super_key::SuperKeyManager,
+    utils::uid_to_android_user,
+};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    SecurityLevel::SecurityLevel, Tag::Tag, TagType::TagType,
+};
+use anyhow::{Context, Result};
+use keystore2_crypto::{aes_gcm_decrypt, Password, ZVec};
+use std::collections::{HashMap, HashSet};
+use std::{convert::TryInto, fs::File, path::Path, path::PathBuf};
+use std::{
+    fs,
+    io::{ErrorKind, Read, Result as IoResult},
+};
+
+const SUPPORTED_LEGACY_BLOB_VERSION: u8 = 3;
+
+mod flags {
+    /// This flag is deprecated. It is here to support keys that have been written with this flag
+    /// set, but we don't create any new keys with this flag.
+    pub const ENCRYPTED: u8 = 1 << 0;
+    /// This flag is deprecated. It indicates that the blob was generated and thus owned by a
+    /// software fallback Keymaster implementation. Keymaster 1.0 was the last Keymaster version
+    /// that could be accompanied by a software fallback. With the removal of Keymaster 1.0
+    /// support, this flag is obsolete.
+    pub const FALLBACK: u8 = 1 << 1;
+    /// KEYSTORE_FLAG_SUPER_ENCRYPTED is for blobs that are already encrypted by KM but have
+    /// an additional layer of password-based encryption applied. The same encryption scheme is used
+    /// as KEYSTORE_FLAG_ENCRYPTED. The latter is deprecated.
+    pub const SUPER_ENCRYPTED: u8 = 1 << 2;
+    /// KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION is for blobs that are part of device encryption
+    /// flow so it receives special treatment from keystore. For example this blob will not be super
+    /// encrypted, and it will be stored separately under a unique UID instead. This flag should
+    /// only be available to system uid.
+    pub const CRITICAL_TO_DEVICE_ENCRYPTION: u8 = 1 << 3;
+    /// The blob is associated with the security level Strongbox as opposed to TEE.
+    pub const STRONGBOX: u8 = 1 << 4;
+}
+
+/// Lagacy key blob types.
+mod blob_types {
+    /// A generic blob used for non sensitive unstructured blobs.
+    pub const GENERIC: u8 = 1;
+    /// This key is a super encryption key encrypted with AES128
+    /// and a password derived key.
+    pub const SUPER_KEY: u8 = 2;
+    // Used to be the KEY_PAIR type.
+    const _RESERVED: u8 = 3;
+    /// A KM key blob.
+    pub const KM_BLOB: u8 = 4;
+    /// A legacy key characteristics file. This has only a single list of Authorizations.
+    pub const KEY_CHARACTERISTICS: u8 = 5;
+    /// A key characteristics cache has both a hardware enforced and a software enforced list
+    /// of authorizations.
+    pub const KEY_CHARACTERISTICS_CACHE: u8 = 6;
+    /// Like SUPER_KEY but encrypted with AES256.
+    pub const SUPER_KEY_AES256: u8 = 7;
+}
+
+/// Error codes specific to the legacy blob module.
+#[derive(thiserror::Error, Debug, Eq, PartialEq)]
+pub enum Error {
+    /// Returned by the legacy blob module functions if an input stream
+    /// did not have enough bytes to read.
+    #[error("Input stream had insufficient bytes to read.")]
+    BadLen,
+    /// This error code is returned by `Blob::decode_alias` if it encounters
+    /// an invalid alias filename encoding.
+    #[error("Invalid alias filename encoding.")]
+    BadEncoding,
+}
+
+/// The blob payload, optionally with all information required to decrypt it.
+#[derive(Debug, Eq, PartialEq)]
+pub enum BlobValue {
+    /// A generic blob used for non sensitive unstructured blobs.
+    Generic(Vec<u8>),
+    /// A legacy key characteristics file. This has only a single list of Authorizations.
+    Characteristics(Vec<u8>),
+    /// A key characteristics cache has both a hardware enforced and a software enforced list
+    /// of authorizations.
+    CharacteristicsCache(Vec<u8>),
+    /// A password encrypted blob. Includes the initialization vector, the aead tag, the
+    /// ciphertext data, a salt, and a key size. The latter two are used for key derivation.
+    PwEncrypted {
+        /// Initialization vector.
+        iv: Vec<u8>,
+        /// Aead tag for integrity verification.
+        tag: Vec<u8>,
+        /// Ciphertext.
+        data: Vec<u8>,
+        /// Salt for key derivation.
+        salt: Vec<u8>,
+        /// Key sise for key derivation. This selects between AES128 GCM and AES256 GCM.
+        key_size: usize,
+    },
+    /// An encrypted blob. Includes the initialization vector, the aead tag, and the
+    /// ciphertext data. The key can be selected from context, i.e., the owner of the key
+    /// blob.
+    Encrypted {
+        /// Initialization vector.
+        iv: Vec<u8>,
+        /// Aead tag for integrity verification.
+        tag: Vec<u8>,
+        /// Ciphertext.
+        data: Vec<u8>,
+    },
+    /// Holds the plaintext key blob either after unwrapping an encrypted blob or when the
+    /// blob was stored in "plaintext" on disk. The "plaintext" of a key blob is not actual
+    /// plaintext because all KeyMint blobs are encrypted with a device bound key. The key
+    /// blob in this Variant is decrypted only with respect to any extra layer of encryption
+    /// that Keystore added.
+    Decrypted(ZVec),
+}
+
+/// Represents a loaded legacy key blob file.
+#[derive(Debug, Eq, PartialEq)]
+pub struct Blob {
+    flags: u8,
+    value: BlobValue,
+}
+
+/// This object represents a path that holds a legacy Keystore blob database.
+pub struct LegacyBlobLoader {
+    path: PathBuf,
+}
+
+fn read_bool(stream: &mut dyn Read) -> Result<bool> {
+    const SIZE: usize = std::mem::size_of::<bool>();
+    let mut buffer: [u8; SIZE] = [0; SIZE];
+    stream.read_exact(&mut buffer).map(|_| buffer[0] != 0).context("In read_ne_bool.")
+}
+
+fn read_ne_u32(stream: &mut dyn Read) -> Result<u32> {
+    const SIZE: usize = std::mem::size_of::<u32>();
+    let mut buffer: [u8; SIZE] = [0; SIZE];
+    stream.read_exact(&mut buffer).map(|_| u32::from_ne_bytes(buffer)).context("In read_ne_u32.")
+}
+
+fn read_ne_i32(stream: &mut dyn Read) -> Result<i32> {
+    const SIZE: usize = std::mem::size_of::<i32>();
+    let mut buffer: [u8; SIZE] = [0; SIZE];
+    stream.read_exact(&mut buffer).map(|_| i32::from_ne_bytes(buffer)).context("In read_ne_i32.")
+}
+
+fn read_ne_i64(stream: &mut dyn Read) -> Result<i64> {
+    const SIZE: usize = std::mem::size_of::<i64>();
+    let mut buffer: [u8; SIZE] = [0; SIZE];
+    stream.read_exact(&mut buffer).map(|_| i64::from_ne_bytes(buffer)).context("In read_ne_i64.")
+}
+
+impl Blob {
+    /// This blob was generated with a fallback software KM device.
+    pub fn is_fallback(&self) -> bool {
+        self.flags & flags::FALLBACK != 0
+    }
+
+    /// This blob is encrypted and needs to be decrypted with the user specific master key
+    /// before use.
+    pub fn is_encrypted(&self) -> bool {
+        self.flags & (flags::SUPER_ENCRYPTED | flags::ENCRYPTED) != 0
+    }
+
+    /// This blob is critical to device encryption. It cannot be encrypted with the super key
+    /// because it is itself part of the key derivation process for the key encrypting the
+    /// super key.
+    pub fn is_critical_to_device_encryption(&self) -> bool {
+        self.flags & flags::CRITICAL_TO_DEVICE_ENCRYPTION != 0
+    }
+
+    /// This blob is associated with the Strongbox security level.
+    pub fn is_strongbox(&self) -> bool {
+        self.flags & flags::STRONGBOX != 0
+    }
+
+    /// Returns the payload data of this blob file.
+    pub fn value(&self) -> &BlobValue {
+        &self.value
+    }
+
+    /// Consume this blob structure and extract the payload.
+    pub fn take_value(self) -> BlobValue {
+        self.value
+    }
+}
+
+impl LegacyBlobLoader {
+    const IV_SIZE: usize = keystore2_crypto::LEGACY_IV_LENGTH;
+    const GCM_TAG_LENGTH: usize = keystore2_crypto::TAG_LENGTH;
+    const SALT_SIZE: usize = keystore2_crypto::SALT_LENGTH;
+
+    // The common header has the following structure:
+    // version (1 Byte)
+    // blob_type (1 Byte)
+    // flags (1 Byte)
+    // info (1 Byte)
+    // initialization_vector (16 Bytes)
+    // integrity (MD5 digest or gcm tag) (16 Bytes)
+    // length (4 Bytes)
+    const COMMON_HEADER_SIZE: usize = 4 + Self::IV_SIZE + Self::GCM_TAG_LENGTH + 4;
+
+    const VERSION_OFFSET: usize = 0;
+    const TYPE_OFFSET: usize = 1;
+    const FLAGS_OFFSET: usize = 2;
+    const SALT_SIZE_OFFSET: usize = 3;
+    const LENGTH_OFFSET: usize = 4 + Self::IV_SIZE + Self::GCM_TAG_LENGTH;
+    const IV_OFFSET: usize = 4;
+    const AEAD_TAG_OFFSET: usize = Self::IV_OFFSET + Self::IV_SIZE;
+    const _DIGEST_OFFSET: usize = Self::IV_OFFSET + Self::IV_SIZE;
+
+    /// Construct a new LegacyBlobLoader with a root path of `path` relative to which it will
+    /// expect legacy key blob files.
+    pub fn new(path: &Path) -> Self {
+        Self { path: path.to_owned() }
+    }
+
+    /// Encodes an alias string as ascii character sequence in the range
+    /// ['+' .. '.'] and ['0' .. '~'].
+    /// Bytes with values in the range ['0' .. '~'] are represented as they are.
+    /// All other bytes are split into two characters as follows:
+    ///
+    ///      msb a a | b b b b b b
+    ///
+    /// The most significant bits (a) are encoded:
+    ///   a a  character
+    ///   0 0     '+'
+    ///   0 1     ','
+    ///   1 0     '-'
+    ///   1 1     '.'
+    ///
+    /// The 6 lower bits are represented with the range ['0' .. 'o']:
+    ///   b(hex)  character
+    ///   0x00     '0'
+    ///       ...
+    ///   0x3F     'o'
+    ///
+    /// The function cannot fail because we have a representation for each
+    /// of the 256 possible values of each byte.
+    pub fn encode_alias(name: &str) -> String {
+        let mut acc = String::new();
+        for c in name.bytes() {
+            match c {
+                b'0'..=b'~' => {
+                    acc.push(c as char);
+                }
+                c => {
+                    acc.push((b'+' + (c as u8 >> 6)) as char);
+                    acc.push((b'0' + (c & 0x3F)) as char);
+                }
+            };
+        }
+        acc
+    }
+
+    /// This function reverses the encoding described in `encode_alias`.
+    /// This function can fail, because not all possible character
+    /// sequences are valid code points. And even if the encoding is valid,
+    /// the result may not be a valid UTF-8 sequence.
+    pub fn decode_alias(name: &str) -> Result<String> {
+        let mut multi: Option<u8> = None;
+        let mut s = Vec::<u8>::new();
+        for c in name.bytes() {
+            multi = match (c, multi) {
+                // m is set, we are processing the second part of a multi byte sequence
+                (b'0'..=b'o', Some(m)) => {
+                    s.push(m | (c - b'0'));
+                    None
+                }
+                (b'+'..=b'.', None) => Some((c - b'+') << 6),
+                (b'0'..=b'~', None) => {
+                    s.push(c);
+                    None
+                }
+                _ => {
+                    return Err(Error::BadEncoding)
+                        .context("In decode_alias: could not decode filename.")
+                }
+            };
+        }
+        if multi.is_some() {
+            return Err(Error::BadEncoding).context("In decode_alias: could not decode filename.");
+        }
+
+        String::from_utf8(s).context("In decode_alias: encoded alias was not valid UTF-8.")
+    }
+
+    fn new_from_stream(stream: &mut dyn Read) -> Result<Blob> {
+        let mut buffer = Vec::new();
+        stream.read_to_end(&mut buffer).context("In new_from_stream.")?;
+
+        if buffer.len() < Self::COMMON_HEADER_SIZE {
+            return Err(Error::BadLen).context("In new_from_stream.")?;
+        }
+
+        let version: u8 = buffer[Self::VERSION_OFFSET];
+
+        let flags: u8 = buffer[Self::FLAGS_OFFSET];
+        let blob_type: u8 = buffer[Self::TYPE_OFFSET];
+        let is_encrypted = flags & (flags::ENCRYPTED | flags::SUPER_ENCRYPTED) != 0;
+        let salt = match buffer[Self::SALT_SIZE_OFFSET] as usize {
+            Self::SALT_SIZE => Some(&buffer[buffer.len() - Self::SALT_SIZE..buffer.len()]),
+            _ => None,
+        };
+
+        if version != SUPPORTED_LEGACY_BLOB_VERSION {
+            return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
+                .context(format!("In new_from_stream: Unknown blob version: {}.", version));
+        }
+
+        let length = u32::from_be_bytes(
+            buffer[Self::LENGTH_OFFSET..Self::LENGTH_OFFSET + 4].try_into().unwrap(),
+        ) as usize;
+        if buffer.len() < Self::COMMON_HEADER_SIZE + length {
+            return Err(Error::BadLen).context(format!(
+                "In new_from_stream. Expected: {} got: {}.",
+                Self::COMMON_HEADER_SIZE + length,
+                buffer.len()
+            ));
+        }
+        let value = &buffer[Self::COMMON_HEADER_SIZE..Self::COMMON_HEADER_SIZE + length];
+        let iv = &buffer[Self::IV_OFFSET..Self::IV_OFFSET + Self::IV_SIZE];
+        let tag = &buffer[Self::AEAD_TAG_OFFSET..Self::AEAD_TAG_OFFSET + Self::GCM_TAG_LENGTH];
+
+        match (blob_type, is_encrypted, salt) {
+            (blob_types::GENERIC, _, _) => {
+                Ok(Blob { flags, value: BlobValue::Generic(value.to_vec()) })
+            }
+            (blob_types::KEY_CHARACTERISTICS, _, _) => {
+                Ok(Blob { flags, value: BlobValue::Characteristics(value.to_vec()) })
+            }
+            (blob_types::KEY_CHARACTERISTICS_CACHE, _, _) => {
+                Ok(Blob { flags, value: BlobValue::CharacteristicsCache(value.to_vec()) })
+            }
+            (blob_types::SUPER_KEY, _, Some(salt)) => Ok(Blob {
+                flags,
+                value: BlobValue::PwEncrypted {
+                    iv: iv.to_vec(),
+                    tag: tag.to_vec(),
+                    data: value.to_vec(),
+                    key_size: keystore2_crypto::AES_128_KEY_LENGTH,
+                    salt: salt.to_vec(),
+                },
+            }),
+            (blob_types::SUPER_KEY_AES256, _, Some(salt)) => Ok(Blob {
+                flags,
+                value: BlobValue::PwEncrypted {
+                    iv: iv.to_vec(),
+                    tag: tag.to_vec(),
+                    data: value.to_vec(),
+                    key_size: keystore2_crypto::AES_256_KEY_LENGTH,
+                    salt: salt.to_vec(),
+                },
+            }),
+            (blob_types::KM_BLOB, true, _) => Ok(Blob {
+                flags,
+                value: BlobValue::Encrypted {
+                    iv: iv.to_vec(),
+                    tag: tag.to_vec(),
+                    data: value.to_vec(),
+                },
+            }),
+            (blob_types::KM_BLOB, false, _) => Ok(Blob {
+                flags,
+                value: BlobValue::Decrypted(value.try_into().context("In new_from_stream.")?),
+            }),
+            (blob_types::SUPER_KEY, _, None) | (blob_types::SUPER_KEY_AES256, _, None) => {
+                Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
+                    .context("In new_from_stream: Super key without salt for key derivation.")
+            }
+            _ => Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
+                "In new_from_stream: Unknown blob type. {} {}",
+                blob_type, is_encrypted
+            )),
+        }
+    }
+
+    /// Parses a legacy key blob file read from `stream`. A `decrypt` closure
+    /// must be supplied, that is primed with the appropriate key.
+    /// The callback takes the following arguments:
+    ///  * ciphertext: &[u8] - The to-be-deciphered message.
+    ///  * iv: &[u8] - The initialization vector.
+    ///  * tag: Option<&[u8]> - AEAD tag if AES GCM is selected.
+    ///  * salt: Option<&[u8]> - An optional salt. Used for password key derivation.
+    ///  * key_size: Option<usize> - An optional key size. Used for pw key derivation.
+    ///
+    /// If no super key is available, the callback must return
+    /// `Err(KsError::Rc(ResponseCode::LOCKED))`. The callback is only called
+    /// if the to-be-read blob is encrypted.
+    pub fn new_from_stream_decrypt_with<F>(mut stream: impl Read, decrypt: F) -> Result<Blob>
+    where
+        F: FnOnce(&[u8], &[u8], &[u8], Option<&[u8]>, Option<usize>) -> Result<ZVec>,
+    {
+        let blob =
+            Self::new_from_stream(&mut stream).context("In new_from_stream_decrypt_with.")?;
+
+        match blob.value() {
+            BlobValue::Encrypted { iv, tag, data } => Ok(Blob {
+                flags: blob.flags,
+                value: BlobValue::Decrypted(
+                    decrypt(&data, &iv, &tag, None, None)
+                        .context("In new_from_stream_decrypt_with.")?,
+                ),
+            }),
+            BlobValue::PwEncrypted { iv, tag, data, salt, key_size } => Ok(Blob {
+                flags: blob.flags,
+                value: BlobValue::Decrypted(
+                    decrypt(&data, &iv, &tag, Some(salt), Some(*key_size))
+                        .context("In new_from_stream_decrypt_with.")?,
+                ),
+            }),
+            _ => Ok(blob),
+        }
+    }
+
+    fn tag_type(tag: Tag) -> TagType {
+        TagType((tag.0 as u32 & 0xFF000000u32) as i32)
+    }
+
+    /// Read legacy key parameter file content.
+    /// Depending on the file type a key characteristics file stores one (TYPE_KEY_CHARACTERISTICS)
+    /// or two (TYPE_KEY_CHARACTERISTICS_CACHE) key parameter lists. The format of the list is as
+    /// follows:
+    ///
+    /// +------------------------------+
+    /// | 32 bit indirect_size         |
+    /// +------------------------------+
+    /// | indirect_size bytes of data  |     This is where the blob data is stored
+    /// +------------------------------+
+    /// | 32 bit element_count         |     Number of key parameter entries.
+    /// | 32 bit elements_size         |     Total bytes used by entries.
+    /// +------------------------------+
+    /// | elements_size bytes of data  |     This is where the elements are stored.
+    /// +------------------------------+
+    ///
+    /// Elements have a 32 bit header holding the tag with a tag type encoded in the
+    /// four most significant bits (see android/hardware/secruity/keymint/TagType.aidl).
+    /// The header is immediately followed by the payload. The payload size depends on
+    /// the encoded tag type in the header:
+    ///      BOOLEAN                          :    1 byte
+    ///      ENUM, ENUM_REP, UINT, UINT_REP   :    4 bytes
+    ///      ULONG, ULONG_REP, DATETIME       :    8 bytes
+    ///      BLOB, BIGNUM                     :    8 bytes see below.
+    ///
+    /// Bignum and blob payload format:
+    /// +------------------------+
+    /// | 32 bit blob_length     |    Length of the indirect payload in bytes.
+    /// | 32 bit indirect_offset |    Offset from the beginning of the indirect section.
+    /// +------------------------+
+    pub fn read_key_parameters(stream: &mut &[u8]) -> Result<Vec<KeyParameterValue>> {
+        let indirect_size =
+            read_ne_u32(stream).context("In read_key_parameters: While reading indirect size.")?;
+
+        let indirect_buffer = stream
+            .get(0..indirect_size as usize)
+            .ok_or(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
+            .context("In read_key_parameters: While reading indirect buffer.")?;
+
+        // update the stream position.
+        *stream = &stream[indirect_size as usize..];
+
+        let element_count =
+            read_ne_u32(stream).context("In read_key_parameters: While reading element count.")?;
+        let element_size =
+            read_ne_u32(stream).context("In read_key_parameters: While reading element size.")?;
+
+        let elements_buffer = stream
+            .get(0..element_size as usize)
+            .ok_or(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
+            .context("In read_key_parameters: While reading elements buffer.")?;
+
+        // update the stream position.
+        *stream = &stream[element_size as usize..];
+
+        let mut element_stream = &elements_buffer[..];
+
+        let mut params: Vec<KeyParameterValue> = Vec::new();
+        for _ in 0..element_count {
+            let tag = Tag(read_ne_i32(&mut element_stream).context("In read_key_parameters.")?);
+            let param = match Self::tag_type(tag) {
+                TagType::ENUM | TagType::ENUM_REP | TagType::UINT | TagType::UINT_REP => {
+                    KeyParameterValue::new_from_tag_primitive_pair(
+                        tag,
+                        read_ne_i32(&mut element_stream).context("While reading integer.")?,
+                    )
+                    .context("Trying to construct integer/enum KeyParameterValue.")
+                }
+                TagType::ULONG | TagType::ULONG_REP | TagType::DATE => {
+                    KeyParameterValue::new_from_tag_primitive_pair(
+                        tag,
+                        read_ne_i64(&mut element_stream).context("While reading long integer.")?,
+                    )
+                    .context("Trying to construct long KeyParameterValue.")
+                }
+                TagType::BOOL => {
+                    if read_bool(&mut element_stream).context("While reading long integer.")? {
+                        KeyParameterValue::new_from_tag_primitive_pair(tag, 1)
+                            .context("Trying to construct boolean KeyParameterValue.")
+                    } else {
+                        Err(anyhow::anyhow!("Invalid."))
+                    }
+                }
+                TagType::BYTES | TagType::BIGNUM => {
+                    let blob_size = read_ne_u32(&mut element_stream)
+                        .context("While reading blob size.")?
+                        as usize;
+                    let indirect_offset = read_ne_u32(&mut element_stream)
+                        .context("While reading indirect offset.")?
+                        as usize;
+                    KeyParameterValue::new_from_tag_primitive_pair(
+                        tag,
+                        indirect_buffer
+                            .get(indirect_offset..indirect_offset + blob_size)
+                            .context("While reading blob value.")?
+                            .to_vec(),
+                    )
+                    .context("Trying to construct blob KeyParameterValue.")
+                }
+                TagType::INVALID => Err(anyhow::anyhow!("Invalid.")),
+                _ => {
+                    return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
+                        .context("In read_key_parameters: Encountered bogus tag type.");
+                }
+            };
+            if let Ok(p) = param {
+                params.push(p);
+            }
+        }
+
+        Ok(params)
+    }
+
+    fn read_characteristics_file(
+        &self,
+        uid: u32,
+        prefix: &str,
+        alias: &str,
+        hw_sec_level: SecurityLevel,
+    ) -> Result<Vec<KeyParameter>> {
+        let blob = Self::read_generic_blob(&self.make_chr_filename(uid, alias, prefix))
+            .context("In read_characteristics_file")?;
+
+        let blob = match blob {
+            None => return Ok(Vec::new()),
+            Some(blob) => blob,
+        };
+
+        let mut stream = match blob.value() {
+            BlobValue::Characteristics(data) => &data[..],
+            BlobValue::CharacteristicsCache(data) => &data[..],
+            _ => {
+                return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(concat!(
+                    "In read_characteristics_file: ",
+                    "Characteristics file does not hold key characteristics."
+                ))
+            }
+        };
+
+        let hw_list = match blob.value() {
+            // The characteristics cache file has two lists and the first is
+            // the hardware enforced list.
+            BlobValue::CharacteristicsCache(_) => Some(
+                Self::read_key_parameters(&mut stream)
+                    .context("In read_characteristics_file.")?
+                    .into_iter()
+                    .map(|value| KeyParameter::new(value, hw_sec_level)),
+            ),
+            _ => None,
+        };
+
+        let sw_list = Self::read_key_parameters(&mut stream)
+            .context("In read_characteristics_file.")?
+            .into_iter()
+            .map(|value| KeyParameter::new(value, SecurityLevel::KEYSTORE));
+
+        Ok(hw_list.into_iter().flatten().chain(sw_list).collect())
+    }
+
+    // This is a list of known prefixes that the Keystore 1.0 SPI used to use.
+    //  * USRPKEY was used for private and secret key material, i.e., KM blobs.
+    //  * USRSKEY was used for secret key material, i.e., KM blobs, before Android P.
+    //  * CACERT  was used for key chains or free standing public certificates.
+    //  * USRCERT was used for public certificates of USRPKEY entries. But KeyChain also
+    //            used this for user installed certificates without private key material.
+
+    fn read_km_blob_file(&self, uid: u32, alias: &str) -> Result<Option<(Blob, String)>> {
+        let mut iter = ["USRPKEY", "USRSKEY"].iter();
+
+        let (blob, prefix) = loop {
+            if let Some(prefix) = iter.next() {
+                if let Some(blob) =
+                    Self::read_generic_blob(&self.make_blob_filename(uid, alias, prefix))
+                        .context("In read_km_blob_file.")?
+                {
+                    break (blob, prefix);
+                }
+            } else {
+                return Ok(None);
+            }
+        };
+
+        Ok(Some((blob, prefix.to_string())))
+    }
+
+    fn read_generic_blob(path: &Path) -> Result<Option<Blob>> {
+        let mut file = match Self::with_retry_interrupted(|| File::open(path)) {
+            Ok(file) => file,
+            Err(e) => match e.kind() {
+                ErrorKind::NotFound => return Ok(None),
+                _ => return Err(e).context("In read_generic_blob."),
+            },
+        };
+
+        Ok(Some(Self::new_from_stream(&mut file).context("In read_generic_blob.")?))
+    }
+
+    /// 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 = match Self::with_retry_interrupted(|| fs::read_dir(path.as_path())) {
+            Ok(dir) => dir,
+            Err(e) => match e.kind() {
+                ErrorKind::NotFound => return Ok(Default::default()),
+                _ => {
+                    return Err(e).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));
+        let mut path = self.make_user_path_name(user_id);
+        path.push(format!("{}_{}", uid, encoded_alias));
+        path
+    }
+
+    /// This function constructs the characteristics file name which has the form:
+    /// user_<android user id>/.<uid>_chr_<prefix>_<alias>.
+    fn make_chr_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));
+        let mut path = self.make_user_path_name(user_id);
+        path.push(format!(".{}_chr_{}", uid, encoded_alias));
+        path
+    }
+
+    fn make_super_key_filename(&self, user_id: u32) -> PathBuf {
+        let mut path = self.make_user_path_name(user_id);
+        path.push(".masterkey");
+        path
+    }
+
+    fn make_user_path_name(&self, user_id: u32) -> PathBuf {
+        let mut path = self.path.clone();
+        path.push(&format!("user_{}", user_id));
+        path
+    }
+
+    /// Returns if the legacy blob database is empty, i.e., there are no entries matching "user_*"
+    /// in the database dir.
+    pub fn is_empty(&self) -> Result<bool> {
+        let dir = Self::with_retry_interrupted(|| fs::read_dir(self.path.as_path()))
+            .context("In is_empty: Failed to open legacy blob database.")?;
+        for entry in dir {
+            if (*entry.context("In is_empty: Trying to access dir entry")?.file_name())
+                .to_str()
+                .map_or(false, |f| f.starts_with("user_"))
+            {
+                return Ok(false);
+            }
+        }
+        Ok(true)
+    }
+
+    /// Returns if the legacy blob database is empty for a given user, i.e., there are no entries
+    /// matching "user_*" in the database dir.
+    pub fn is_empty_user(&self, user_id: u32) -> Result<bool> {
+        let mut user_path = self.path.clone();
+        user_path.push(format!("user_{}", user_id));
+        if !user_path.as_path().is_dir() {
+            return Ok(true);
+        }
+        Ok(Self::with_retry_interrupted(|| user_path.read_dir())
+            .context("In is_empty_user: Failed to open legacy user dir.")?
+            .next()
+            .is_none())
+    }
+
+    fn extract_alias(encoded_alias: &str) -> Option<String> {
+        // We can check the encoded alias because the prefixes we are interested
+        // in are all in the printable range that don't get mangled.
+        for prefix in &["USRPKEY_", "USRSKEY_", "USRCERT_", "CACERT_"] {
+            if let Some(alias) = encoded_alias.strip_prefix(prefix) {
+                return Self::decode_alias(&alias).ok();
+            }
+        }
+        None
+    }
+
+    /// List all entries for a given user. The strings are unchanged file names, i.e.,
+    /// encoded with UID prefix.
+    fn list_user(&self, user_id: u32) -> Result<Vec<String>> {
+        let path = self.make_user_path_name(user_id);
+        let dir = match Self::with_retry_interrupted(|| fs::read_dir(path.as_path())) {
+            Ok(dir) => dir,
+            Err(e) => match e.kind() {
+                ErrorKind::NotFound => return Ok(Default::default()),
+                _ => {
+                    return Err(e).context(format!(
+                        "In list_user: 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_user: Trying to access dir entry")?.file_name();
+            if let Some(f) = file_name.to_str() {
+                result.push(f.to_string())
+            }
+        }
+        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);
+
+        let user_entries = self
+            .list_user(user_id)
+            .context("In list_keystore_entries_for_uid: Trying to list user.")?;
+
+        let uid_str = format!("{}_", uid);
+
+        let mut result: Vec<String> = user_entries
+            .into_iter()
+            .filter_map(|v| {
+                if !v.starts_with(&uid_str) {
+                    return None;
+                }
+                let encoded_alias = &v[uid_str.len()..];
+                Self::extract_alias(encoded_alias)
+            })
+            .collect();
+
+        result.sort_unstable();
+        result.dedup();
+        Ok(result)
+    }
+
+    fn with_retry_interrupted<F, T>(f: F) -> IoResult<T>
+    where
+        F: Fn() -> IoResult<T>,
+    {
+        loop {
+            match f() {
+                Ok(v) => return Ok(v),
+                Err(e) => match e.kind() {
+                    ErrorKind::Interrupted => continue,
+                    _ => return Err(e),
+                },
+            }
+        }
+    }
+
+    /// Deletes a keystore entry. Also removes the user_<uid> directory on the
+    /// last migration.
+    pub fn remove_keystore_entry(&self, uid: u32, alias: &str) -> Result<bool> {
+        let mut something_was_deleted = false;
+        let prefixes = ["USRPKEY", "USRSKEY"];
+        for prefix in &prefixes {
+            let path = self.make_blob_filename(uid, alias, prefix);
+            if let Err(e) = Self::with_retry_interrupted(|| fs::remove_file(path.as_path())) {
+                match e.kind() {
+                    // Only a subset of keys are expected.
+                    ErrorKind::NotFound => continue,
+                    // Log error but ignore.
+                    _ => log::error!("Error while deleting key blob entries. {:?}", e),
+                }
+            }
+            let path = self.make_chr_filename(uid, alias, prefix);
+            if let Err(e) = Self::with_retry_interrupted(|| fs::remove_file(path.as_path())) {
+                match e.kind() {
+                    ErrorKind::NotFound => {
+                        log::info!("No characteristics file found for legacy key blob.")
+                    }
+                    // Log error but ignore.
+                    _ => log::error!("Error while deleting key blob entries. {:?}", e),
+                }
+            }
+            something_was_deleted = true;
+            // Only one of USRPKEY and USRSKEY can be present. So we can end the loop
+            // if we reach this point.
+            break;
+        }
+
+        let prefixes = ["USRCERT", "CACERT"];
+        for prefix in &prefixes {
+            let path = self.make_blob_filename(uid, alias, prefix);
+            if let Err(e) = Self::with_retry_interrupted(|| fs::remove_file(path.as_path())) {
+                match e.kind() {
+                    // USRCERT and CACERT are optional either or both may or may not be present.
+                    ErrorKind::NotFound => continue,
+                    // Log error but ignore.
+                    _ => log::error!("Error while deleting key blob entries. {:?}", e),
+                }
+                something_was_deleted = true;
+            }
+        }
+
+        if something_was_deleted {
+            let user_id = uid_to_android_user(uid);
+            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,
+        uid: u32,
+        alias: &str,
+        key_manager: Option<&SuperKeyManager>,
+    ) -> Result<(Option<(Blob, Vec<KeyParameter>)>, Option<Vec<u8>>, Option<Vec<u8>>)> {
+        let km_blob = self.read_km_blob_file(uid, alias).context("In load_by_uid_alias.")?;
+
+        let km_blob = match km_blob {
+            Some((km_blob, prefix)) => {
+                let km_blob = match km_blob {
+                    Blob { flags: _, value: BlobValue::Decrypted(_) } => km_blob,
+                    // Unwrap the key blob if required and if we have key_manager.
+                    Blob { flags, value: BlobValue::Encrypted { ref iv, ref tag, ref data } } => {
+                        if let Some(key_manager) = key_manager {
+                            let decrypted = match key_manager
+                                .get_per_boot_key_by_user_id(uid_to_android_user(uid))
+                            {
+                                Some(key) => key.aes_gcm_decrypt(data, iv, tag).context(
+                                    "In load_by_uid_alias: while trying to decrypt legacy blob.",
+                                )?,
+                                None => {
+                                    return Err(KsError::Rc(ResponseCode::LOCKED)).context(format!(
+                                        concat!(
+                                            "In load_by_uid_alias: ",
+                                            "User {} has not unlocked the keystore yet.",
+                                        ),
+                                        uid_to_android_user(uid)
+                                    ))
+                                }
+                            };
+                            Blob { flags, value: BlobValue::Decrypted(decrypted) }
+                        } else {
+                            km_blob
+                        }
+                    }
+                    _ => {
+                        return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(
+                            "In load_by_uid_alias: Found wrong blob type in legacy key blob file.",
+                        )
+                    }
+                };
+
+                let hw_sec_level = match km_blob.is_strongbox() {
+                    true => SecurityLevel::STRONGBOX,
+                    false => SecurityLevel::TRUSTED_ENVIRONMENT,
+                };
+                let key_parameters = self
+                    .read_characteristics_file(uid, &prefix, alias, hw_sec_level)
+                    .context("In load_by_uid_alias.")?;
+                Some((km_blob, key_parameters))
+            }
+            None => None,
+        };
+
+        let user_cert =
+            match Self::read_generic_blob(&self.make_blob_filename(uid, alias, "USRCERT"))
+                .context("In load_by_uid_alias: While loading user cert.")?
+            {
+                Some(Blob { value: BlobValue::Generic(data), .. }) => Some(data),
+                None => None,
+                _ => {
+                    return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(
+                        "In load_by_uid_alias: Found unexpected blob type in USRCERT file",
+                    )
+                }
+            };
+
+        let ca_cert = match Self::read_generic_blob(&self.make_blob_filename(uid, alias, "CACERT"))
+            .context("In load_by_uid_alias: While loading ca cert.")?
+        {
+            Some(Blob { value: BlobValue::Generic(data), .. }) => Some(data),
+            None => None,
+            _ => {
+                return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED))
+                    .context("In load_by_uid_alias: Found unexpected blob type in CACERT file")
+            }
+        };
+
+        Ok((km_blob, user_cert, ca_cert))
+    }
+
+    /// Returns true if the given user has a super key.
+    pub fn has_super_key(&self, user_id: u32) -> bool {
+        self.make_super_key_filename(user_id).is_file()
+    }
+
+    /// Load and decrypt legacy super key blob.
+    pub fn load_super_key(&self, user_id: u32, pw: &Password) -> Result<Option<ZVec>> {
+        let path = self.make_super_key_filename(user_id);
+        let blob = Self::read_generic_blob(&path)
+            .context("In load_super_key: While loading super key.")?;
+
+        let blob = match blob {
+            Some(blob) => match blob {
+                Blob { flags, value: BlobValue::PwEncrypted { iv, tag, data, salt, key_size } } => {
+                    if (flags & flags::ENCRYPTED) != 0 {
+                        let key = pw
+                            .derive_key(Some(&salt), key_size)
+                            .context("In load_super_key: Failed to derive key from password.")?;
+                        let blob = aes_gcm_decrypt(&data, &iv, &tag, &key).context(
+                            "In load_super_key: while trying to decrypt legacy super key blob.",
+                        )?;
+                        Some(blob)
+                    } else {
+                        // In 2019 we had some unencrypted super keys due to b/141955555.
+                        Some(
+                            data.try_into()
+                                .context("In load_super_key: Trying to convert key into ZVec")?,
+                        )
+                    }
+                }
+                _ => {
+                    return Err(KsError::Rc(ResponseCode::VALUE_CORRUPTED)).context(
+                        "In load_super_key: Found wrong blob type in legacy super key blob file.",
+                    )
+                }
+            },
+            None => None,
+        };
+
+        Ok(blob)
+    }
+
+    /// Removes the super key for the given user from the legacy database.
+    /// If this was the last entry in the user's database, this function removes
+    /// the user_<uid> directory as well.
+    pub fn remove_super_key(&self, user_id: u32) {
+        let path = self.make_super_key_filename(user_id);
+        Self::with_retry_interrupted(|| fs::remove_file(path.as_path())).ok();
+        if self.is_empty_user(user_id).ok().unwrap_or(false) {
+            let path = self.make_user_path_name(user_id);
+            Self::with_retry_interrupted(|| fs::remove_dir(path.as_path())).ok();
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use anyhow::anyhow;
+    use keystore2_crypto::aes_gcm_decrypt;
+    use rand::Rng;
+    use std::string::FromUtf8Error;
+    mod legacy_blob_test_vectors;
+    use crate::error;
+    use crate::legacy_blob::test::legacy_blob_test_vectors::*;
+    use keystore2_test_utils::TempDir;
+
+    #[test]
+    fn decode_encode_alias_test() {
+        static ALIAS: &str = "#({}test[])😗";
+        static ENCODED_ALIAS: &str = "+S+X{}test[]+Y.`-O-H-G";
+        // Second multi byte out of range ------v
+        static ENCODED_ALIAS_ERROR1: &str = "+S+{}test[]+Y";
+        // Incomplete multi byte ------------------------v
+        static ENCODED_ALIAS_ERROR2: &str = "+S+X{}test[]+";
+        // Our encoding: ".`-O-H-G"
+        // is UTF-8: 0xF0 0x9F 0x98 0x97
+        // is UNICODE: U+1F617
+        // is 😗
+        // But +H below is a valid encoding for 0x18 making this sequence invalid UTF-8.
+        static ENCODED_ALIAS_ERROR_UTF8: &str = ".`-O+H-G";
+
+        assert_eq!(ENCODED_ALIAS, &LegacyBlobLoader::encode_alias(ALIAS));
+        assert_eq!(ALIAS, &LegacyBlobLoader::decode_alias(ENCODED_ALIAS).unwrap());
+        assert_eq!(
+            Some(&Error::BadEncoding),
+            LegacyBlobLoader::decode_alias(ENCODED_ALIAS_ERROR1)
+                .unwrap_err()
+                .root_cause()
+                .downcast_ref::<Error>()
+        );
+        assert_eq!(
+            Some(&Error::BadEncoding),
+            LegacyBlobLoader::decode_alias(ENCODED_ALIAS_ERROR2)
+                .unwrap_err()
+                .root_cause()
+                .downcast_ref::<Error>()
+        );
+        assert!(LegacyBlobLoader::decode_alias(ENCODED_ALIAS_ERROR_UTF8)
+            .unwrap_err()
+            .root_cause()
+            .downcast_ref::<FromUtf8Error>()
+            .is_some());
+
+        for _i in 0..100 {
+            // Any valid UTF-8 string should be en- and decoded without loss.
+            let alias_str = rand::thread_rng().gen::<[char; 20]>().iter().collect::<String>();
+            let random_alias = alias_str.as_bytes();
+            let encoded = LegacyBlobLoader::encode_alias(&alias_str);
+            let decoded = match LegacyBlobLoader::decode_alias(&encoded) {
+                Ok(d) => d,
+                Err(_) => panic!("random_alias: {:x?}\nencoded {}", random_alias, encoded),
+            };
+            assert_eq!(random_alias.to_vec(), decoded.bytes().collect::<Vec<u8>>());
+        }
+    }
+
+    #[test]
+    fn read_golden_key_blob_test() -> anyhow::Result<()> {
+        let blob = LegacyBlobLoader::new_from_stream_decrypt_with(&mut &*BLOB, |_, _, _, _, _| {
+            Err(anyhow!("should not be called"))
+        })?;
+        assert!(!blob.is_encrypted());
+        assert!(!blob.is_fallback());
+        assert!(!blob.is_strongbox());
+        assert!(!blob.is_critical_to_device_encryption());
+        assert_eq!(blob.value(), &BlobValue::Generic([0xde, 0xed, 0xbe, 0xef].to_vec()));
+
+        let blob = LegacyBlobLoader::new_from_stream_decrypt_with(
+            &mut &*REAL_LEGACY_BLOB,
+            |_, _, _, _, _| Err(anyhow!("should not be called")),
+        )?;
+        assert!(!blob.is_encrypted());
+        assert!(!blob.is_fallback());
+        assert!(!blob.is_strongbox());
+        assert!(!blob.is_critical_to_device_encryption());
+        assert_eq!(
+            blob.value(),
+            &BlobValue::Decrypted(REAL_LEGACY_BLOB_PAYLOAD.try_into().unwrap())
+        );
+        Ok(())
+    }
+
+    #[test]
+    fn read_aes_gcm_encrypted_key_blob_test() {
+        let blob = LegacyBlobLoader::new_from_stream_decrypt_with(
+            &mut &*AES_GCM_ENCRYPTED_BLOB,
+            |d, iv, tag, salt, key_size| {
+                assert_eq!(salt, None);
+                assert_eq!(key_size, None);
+                assert_eq!(
+                    iv,
+                    &[
+                        0xbd, 0xdb, 0x8d, 0x69, 0x72, 0x56, 0xf0, 0xf5, 0xa4, 0x02, 0x88, 0x7f,
+                        0x00, 0x00, 0x00, 0x00,
+                    ]
+                );
+                assert_eq!(
+                    tag,
+                    &[
+                        0x50, 0xd9, 0x97, 0x95, 0x37, 0x6e, 0x28, 0x6a, 0x28, 0x9d, 0x51, 0xb9,
+                        0xb9, 0xe0, 0x0b, 0xc3
+                    ][..]
+                );
+                aes_gcm_decrypt(d, iv, tag, AES_KEY).context("Trying to decrypt blob.")
+            },
+        )
+        .unwrap();
+        assert!(blob.is_encrypted());
+        assert!(!blob.is_fallback());
+        assert!(!blob.is_strongbox());
+        assert!(!blob.is_critical_to_device_encryption());
+
+        assert_eq!(blob.value(), &BlobValue::Decrypted(DECRYPTED_PAYLOAD.try_into().unwrap()));
+    }
+
+    #[test]
+    fn read_golden_key_blob_too_short_test() {
+        let error =
+            LegacyBlobLoader::new_from_stream_decrypt_with(&mut &BLOB[0..15], |_, _, _, _, _| {
+                Err(anyhow!("should not be called"))
+            })
+            .unwrap_err();
+        assert_eq!(Some(&Error::BadLen), error.root_cause().downcast_ref::<Error>());
+    }
+
+    #[test]
+    fn test_is_empty() {
+        let temp_dir = TempDir::new("test_is_empty").expect("Failed to create temp dir.");
+        let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+        assert!(legacy_blob_loader.is_empty().expect("Should succeed and be empty."));
+
+        let _db = crate::database::KeystoreDB::new(temp_dir.path(), None)
+            .expect("Failed to open database.");
+
+        assert!(legacy_blob_loader.is_empty().expect("Should succeed and still be empty."));
+
+        std::fs::create_dir(&*temp_dir.build().push("user_0")).expect("Failed to create user_0.");
+
+        assert!(!legacy_blob_loader.is_empty().expect("Should succeed but not be empty."));
+
+        std::fs::create_dir(&*temp_dir.build().push("user_10")).expect("Failed to create user_10.");
+
+        assert!(!legacy_blob_loader.is_empty().expect("Should succeed but still not be empty."));
+
+        std::fs::remove_dir_all(&*temp_dir.build().push("user_0"))
+            .expect("Failed to remove user_0.");
+
+        assert!(!legacy_blob_loader.is_empty().expect("Should succeed but still not be empty."));
+
+        std::fs::remove_dir_all(&*temp_dir.build().push("user_10"))
+            .expect("Failed to remove user_10.");
+
+        assert!(legacy_blob_loader.is_empty().expect("Should succeed and be empty again."));
+    }
+
+    #[test]
+    fn test_legacy_blobs() -> anyhow::Result<()> {
+        let temp_dir = TempDir::new("legacy_blob_test")?;
+        std::fs::create_dir(&*temp_dir.build().push("user_0"))?;
+
+        std::fs::write(&*temp_dir.build().push("user_0").push(".masterkey"), SUPERKEY)?;
+
+        std::fs::write(
+            &*temp_dir.build().push("user_0").push("10223_USRPKEY_authbound"),
+            USRPKEY_AUTHBOUND,
+        )?;
+        std::fs::write(
+            &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_authbound"),
+            USRPKEY_AUTHBOUND_CHR,
+        )?;
+        std::fs::write(
+            &*temp_dir.build().push("user_0").push("10223_USRCERT_authbound"),
+            USRCERT_AUTHBOUND,
+        )?;
+        std::fs::write(
+            &*temp_dir.build().push("user_0").push("10223_CACERT_authbound"),
+            CACERT_AUTHBOUND,
+        )?;
+
+        std::fs::write(
+            &*temp_dir.build().push("user_0").push("10223_USRPKEY_non_authbound"),
+            USRPKEY_NON_AUTHBOUND,
+        )?;
+        std::fs::write(
+            &*temp_dir.build().push("user_0").push(".10223_chr_USRPKEY_non_authbound"),
+            USRPKEY_NON_AUTHBOUND_CHR,
+        )?;
+        std::fs::write(
+            &*temp_dir.build().push("user_0").push("10223_USRCERT_non_authbound"),
+            USRCERT_NON_AUTHBOUND,
+        )?;
+        std::fs::write(
+            &*temp_dir.build().push("user_0").push("10223_CACERT_non_authbound"),
+            CACERT_NON_AUTHBOUND,
+        )?;
+
+        let key_manager: SuperKeyManager = Default::default();
+        let mut db = crate::database::KeystoreDB::new(temp_dir.path(), None)?;
+        let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+        assert_eq!(
+            legacy_blob_loader
+                .load_by_uid_alias(10223, "authbound", Some(&key_manager))
+                .unwrap_err()
+                .root_cause()
+                .downcast_ref::<error::Error>(),
+            Some(&error::Error::Rc(ResponseCode::LOCKED))
+        );
+
+        key_manager.unlock_user_key(&mut db, 0, &(PASSWORD.into()), &legacy_blob_loader)?;
+
+        if let (Some((Blob { flags, value: _ }, _params)), Some(cert), Some(chain)) =
+            legacy_blob_loader.load_by_uid_alias(10223, "authbound", Some(&key_manager))?
+        {
+            assert_eq!(flags, 4);
+            //assert_eq!(value, BlobValue::Encrypted(..));
+            assert_eq!(&cert[..], LOADED_CERT_AUTHBOUND);
+            assert_eq!(&chain[..], LOADED_CACERT_AUTHBOUND);
+        } else {
+            panic!("");
+        }
+        if let (Some((Blob { flags, value }, _params)), Some(cert), Some(chain)) =
+            legacy_blob_loader.load_by_uid_alias(10223, "non_authbound", Some(&key_manager))?
+        {
+            assert_eq!(flags, 0);
+            assert_eq!(value, BlobValue::Decrypted(LOADED_USRPKEY_NON_AUTHBOUND.try_into()?));
+            assert_eq!(&cert[..], LOADED_CERT_NON_AUTHBOUND);
+            assert_eq!(&chain[..], LOADED_CACERT_NON_AUTHBOUND);
+        } else {
+            panic!("");
+        }
+
+        legacy_blob_loader.remove_keystore_entry(10223, "authbound").expect("This should succeed.");
+        legacy_blob_loader
+            .remove_keystore_entry(10223, "non_authbound")
+            .expect("This should succeed.");
+
+        assert_eq!(
+            (None, None, None),
+            legacy_blob_loader.load_by_uid_alias(10223, "authbound", Some(&key_manager))?
+        );
+        assert_eq!(
+            (None, None, None),
+            legacy_blob_loader.load_by_uid_alias(10223, "non_authbound", Some(&key_manager))?
+        );
+
+        // The database should not be empty due to the super key.
+        assert!(!legacy_blob_loader.is_empty()?);
+        assert!(!legacy_blob_loader.is_empty_user(0)?);
+
+        // The database should be considered empty for user 1.
+        assert!(legacy_blob_loader.is_empty_user(1)?);
+
+        legacy_blob_loader.remove_super_key(0);
+
+        // Now it should be empty.
+        assert!(legacy_blob_loader.is_empty_user(0)?);
+        assert!(legacy_blob_loader.is_empty()?);
+
+        Ok(())
+    }
+
+    #[test]
+    fn list_non_existing_user() -> Result<()> {
+        let temp_dir = TempDir::new("list_non_existing_user")?;
+        let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+        assert!(legacy_blob_loader.list_user(20)?.is_empty());
+
+        Ok(())
+    }
+
+    #[test]
+    fn list_vpn_profiles_on_non_existing_user() -> Result<()> {
+        let temp_dir = TempDir::new("list_vpn_profiles_on_non_existing_user")?;
+        let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+        assert!(legacy_blob_loader.list_vpn_profiles(20)?.is_empty());
+
+        Ok(())
+    }
+}
diff --git a/keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs b/keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs
new file mode 100644
index 0000000..14bd40c
--- /dev/null
+++ b/keystore2/src/legacy_blob/test/legacy_blob_test_vectors.rs
@@ -0,0 +1,1277 @@
+// 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.
+
+pub static BLOB: &[u8] = &[
+    3, // version
+    1, // type
+    0, // flags
+    0, // info
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // IV
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // aead tag
+    0, 0, 0, 4, // length in big endian
+    0xde, 0xed, 0xbe, 0xef, // payload
+];
+pub static REAL_LEGACY_BLOB: &[u8] = &[
+    0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x94, 0x6c, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x25,
+    0x00, 0x0b, 0x00, 0x06, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x80, 0x00, 0x43, 0x00, 0x20,
+    0x85, 0x42, 0x9e, 0xe9, 0x34, 0x85, 0x2a, 0x00, 0xae, 0xbb, 0x53, 0xb4, 0x77, 0x1e, 0x59, 0x06,
+    0x70, 0x14, 0x10, 0xe3, 0xa4, 0x6e, 0xa7, 0xfa, 0x68, 0xf6, 0xa9, 0x29, 0x86, 0x08, 0x61, 0xef,
+    0x00, 0x8e, 0x00, 0x20, 0x4c, 0x3b, 0x31, 0x6a, 0x5e, 0xc2, 0xbc, 0xfc, 0x6f, 0x35, 0x14, 0x38,
+    0x0b, 0x8c, 0xa1, 0x9a, 0x2e, 0xf0, 0x40, 0xa8, 0x48, 0x4f, 0xe5, 0xe3, 0x56, 0xfb, 0x8d, 0x98,
+    0x98, 0x04, 0x73, 0x88, 0x00, 0x10, 0x8d, 0xff, 0x36, 0xf6, 0x5c, 0x30, 0x3c, 0x36, 0x96, 0xa1,
+    0x72, 0x63, 0x0b, 0xa5, 0xd7, 0x5f, 0xaf, 0x49, 0x34, 0xd0, 0xa9, 0xc3, 0x0b, 0x3c, 0xa4, 0x81,
+    0xb9, 0xb3, 0x6d, 0xfa, 0xa2, 0x45, 0x7d, 0x02, 0x34, 0x93, 0x1d, 0x89, 0x80, 0x40, 0xa2, 0xe8,
+    0xb2, 0xab, 0x61, 0x75, 0x9f, 0x0c, 0x39, 0x97, 0x28, 0x53, 0xad, 0x56, 0x19, 0x01, 0xc6, 0x66,
+    0xa5, 0xed, 0x4f, 0xe0, 0xa1, 0x6d, 0x26, 0x3b, 0xa3, 0x88, 0x22, 0x38, 0x3f, 0x53, 0x5e, 0xa5,
+    0x68, 0x85, 0x91, 0x22, 0xc3, 0x4c, 0x40, 0x64, 0x38, 0xa8, 0x49, 0x68, 0x22, 0x3c, 0xc1, 0xdc,
+    0x5d, 0x4f, 0x56, 0x12, 0xac, 0x20, 0x6b, 0xb7, 0x03, 0x6c, 0x27, 0xfc, 0x3f, 0x23, 0x2c, 0x4e,
+    0x10, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x65, 0xf5, 0x71, 0xc8, 0x6a, 0xbc, 0xd1, 0xa0,
+    0x4d, 0xa1, 0x69, 0xbf, 0xfe, 0x92, 0xae, 0x09, 0xb4, 0x26, 0xe9, 0xd7, 0x2b, 0xfe, 0x66, 0x40,
+    0x76, 0xb8, 0xc4, 0x72, 0x51, 0x80, 0xa4, 0xbd, 0x60, 0x52, 0x25, 0xaf, 0xaf, 0x0c, 0x9d, 0xe9,
+    0x91, 0xbf, 0xbe, 0xc1, 0xf8, 0x86, 0x74, 0xda, 0xf2, 0x0d, 0xa3, 0x72, 0x08, 0x29, 0x81, 0xec,
+    0x9c, 0x69, 0xea, 0x1f, 0x80, 0x0c, 0x97, 0x80, 0x6c, 0xcb, 0x61, 0x5d, 0x51, 0x35, 0x4d, 0xd1,
+    0x88, 0xe5, 0x47, 0xc3, 0x6f, 0x15, 0xee, 0xa1, 0x79, 0x02, 0x21, 0x0d, 0x1b, 0xb9, 0x87, 0x5f,
+    0x28, 0xd4, 0x70, 0x2e, 0x08, 0xa5, 0x73, 0x36, 0x81, 0xa3, 0x6a, 0x5f, 0xad, 0x61, 0x9a, 0xc5,
+    0x60, 0x76, 0x38, 0x8a, 0x2e, 0x42, 0x3e, 0x3d, 0x7f, 0x42, 0xc8, 0x00, 0xab, 0xac, 0x7f, 0xec,
+    0xba, 0xc7, 0x45, 0x08, 0xbb, 0x60, 0x58, 0xef, 0xb0, 0xc2, 0xf4, 0xe5, 0x9e, 0xb2, 0x34, 0xc6,
+    0x9f, 0x2d, 0x9b, 0x08, 0xec, 0x3c, 0xdc, 0xc2, 0xaa, 0x55, 0xf5, 0xca, 0x62, 0xbd, 0x41, 0x91,
+    0x42, 0xa8, 0xa4, 0x7e, 0xac, 0x7d, 0x78, 0xc3, 0x20, 0x00, 0x00, 0x00, 0x9f, 0xc9, 0x4a, 0x8b,
+    0xe1, 0xc0, 0x48, 0xe0, 0xd7, 0x76, 0xe7, 0x83, 0x20, 0x06, 0x24, 0x6c, 0x80, 0xf7, 0xaf, 0x7f,
+    0xda, 0x40, 0x2b, 0x75, 0xd0, 0xd2, 0x81, 0x7f, 0xe2, 0x2b, 0xef, 0x64,
+];
+
+pub static REAL_LEGACY_BLOB_PAYLOAD: &[u8] = &[
+    0x6c, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x25, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x72, 0x00, 0x00,
+    0x00, 0x06, 0x00, 0x80, 0x00, 0x43, 0x00, 0x20, 0x85, 0x42, 0x9e, 0xe9, 0x34, 0x85, 0x2a, 0x00,
+    0xae, 0xbb, 0x53, 0xb4, 0x77, 0x1e, 0x59, 0x06, 0x70, 0x14, 0x10, 0xe3, 0xa4, 0x6e, 0xa7, 0xfa,
+    0x68, 0xf6, 0xa9, 0x29, 0x86, 0x08, 0x61, 0xef, 0x00, 0x8e, 0x00, 0x20, 0x4c, 0x3b, 0x31, 0x6a,
+    0x5e, 0xc2, 0xbc, 0xfc, 0x6f, 0x35, 0x14, 0x38, 0x0b, 0x8c, 0xa1, 0x9a, 0x2e, 0xf0, 0x40, 0xa8,
+    0x48, 0x4f, 0xe5, 0xe3, 0x56, 0xfb, 0x8d, 0x98, 0x98, 0x04, 0x73, 0x88, 0x00, 0x10, 0x8d, 0xff,
+    0x36, 0xf6, 0x5c, 0x30, 0x3c, 0x36, 0x96, 0xa1, 0x72, 0x63, 0x0b, 0xa5, 0xd7, 0x5f, 0xaf, 0x49,
+    0x34, 0xd0, 0xa9, 0xc3, 0x0b, 0x3c, 0xa4, 0x81, 0xb9, 0xb3, 0x6d, 0xfa, 0xa2, 0x45, 0x7d, 0x02,
+    0x34, 0x93, 0x1d, 0x89, 0x80, 0x40, 0xa2, 0xe8, 0xb2, 0xab, 0x61, 0x75, 0x9f, 0x0c, 0x39, 0x97,
+    0x28, 0x53, 0xad, 0x56, 0x19, 0x01, 0xc6, 0x66, 0xa5, 0xed, 0x4f, 0xe0, 0xa1, 0x6d, 0x26, 0x3b,
+    0xa3, 0x88, 0x22, 0x38, 0x3f, 0x53, 0x5e, 0xa5, 0x68, 0x85, 0x91, 0x22, 0xc3, 0x4c, 0x40, 0x64,
+    0x38, 0xa8, 0x49, 0x68, 0x22, 0x3c, 0xc1, 0xdc, 0x5d, 0x4f, 0x56, 0x12, 0xac, 0x20, 0x6b, 0xb7,
+    0x03, 0x6c, 0x27, 0xfc, 0x3f, 0x23, 0x2c, 0x4e, 0x10, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00,
+    0x65, 0xf5, 0x71, 0xc8, 0x6a, 0xbc, 0xd1, 0xa0, 0x4d, 0xa1, 0x69, 0xbf, 0xfe, 0x92, 0xae, 0x09,
+    0xb4, 0x26, 0xe9, 0xd7, 0x2b, 0xfe, 0x66, 0x40, 0x76, 0xb8, 0xc4, 0x72, 0x51, 0x80, 0xa4, 0xbd,
+    0x60, 0x52, 0x25, 0xaf, 0xaf, 0x0c, 0x9d, 0xe9, 0x91, 0xbf, 0xbe, 0xc1, 0xf8, 0x86, 0x74, 0xda,
+    0xf2, 0x0d, 0xa3, 0x72, 0x08, 0x29, 0x81, 0xec, 0x9c, 0x69, 0xea, 0x1f, 0x80, 0x0c, 0x97, 0x80,
+    0x6c, 0xcb, 0x61, 0x5d, 0x51, 0x35, 0x4d, 0xd1, 0x88, 0xe5, 0x47, 0xc3, 0x6f, 0x15, 0xee, 0xa1,
+    0x79, 0x02, 0x21, 0x0d, 0x1b, 0xb9, 0x87, 0x5f, 0x28, 0xd4, 0x70, 0x2e, 0x08, 0xa5, 0x73, 0x36,
+    0x81, 0xa3, 0x6a, 0x5f, 0xad, 0x61, 0x9a, 0xc5, 0x60, 0x76, 0x38, 0x8a, 0x2e, 0x42, 0x3e, 0x3d,
+    0x7f, 0x42, 0xc8, 0x00, 0xab, 0xac, 0x7f, 0xec, 0xba, 0xc7, 0x45, 0x08, 0xbb, 0x60, 0x58, 0xef,
+    0xb0, 0xc2, 0xf4, 0xe5, 0x9e, 0xb2, 0x34, 0xc6, 0x9f, 0x2d, 0x9b, 0x08, 0xec, 0x3c, 0xdc, 0xc2,
+    0xaa, 0x55, 0xf5, 0xca, 0x62, 0xbd, 0x41, 0x91, 0x42, 0xa8, 0xa4, 0x7e, 0xac, 0x7d, 0x78, 0xc3,
+    0x20, 0x00, 0x00, 0x00, 0x9f, 0xc9, 0x4a, 0x8b, 0xe1, 0xc0, 0x48, 0xe0, 0xd7, 0x76, 0xe7, 0x83,
+    0x20, 0x06, 0x24, 0x6c, 0x80, 0xf7, 0xaf, 0x7f, 0xda, 0x40, 0x2b, 0x75, 0xd0, 0xd2, 0x81, 0x7f,
+    0xe2, 0x2b, 0xef, 0x64,
+];
+
+pub static AES_KEY: &[u8] = &[
+    0x48, 0xe4, 0xb5, 0xff, 0xcd, 0x9c, 0x41, 0x1e, 0x20, 0x41, 0xf2, 0x65, 0xa0, 0x4f, 0xf6, 0x57,
+    0xc6, 0x58, 0xca, 0xbf, 0x28, 0xa3, 0x01, 0x98, 0x01, 0x76, 0x10, 0xc0, 0x30, 0x4e, 0x35, 0x6e,
+];
+
+pub static AES_GCM_ENCRYPTED_BLOB: &[u8] = &[
+    0x03, 0x04, 0x04, 0x00, 0xbd, 0xdb, 0x8d, 0x69, 0x72, 0x56, 0xf0, 0xf5, 0xa4, 0x02, 0x88, 0x7f,
+    0x00, 0x00, 0x00, 0x00, 0x50, 0xd9, 0x97, 0x95, 0x37, 0x6e, 0x28, 0x6a, 0x28, 0x9d, 0x51, 0xb9,
+    0xb9, 0xe0, 0x0b, 0xc3, 0x00, 0x00, 0x01, 0xa4, 0x47, 0xf5, 0xf8, 0xb0, 0x77, 0x91, 0x6b, 0xb1,
+    0x33, 0xba, 0xd7, 0xaa, 0x81, 0x20, 0x4d, 0x2f, 0xda, 0x9e, 0x03, 0xe1, 0x41, 0xc9, 0x5a, 0x81,
+    0x30, 0xf6, 0x77, 0x71, 0x53, 0x8a, 0xf1, 0xf2, 0xa4, 0x28, 0xe4, 0x46, 0xc3, 0x7e, 0xbb, 0xb6,
+    0x01, 0x38, 0xa4, 0x0d, 0x78, 0x41, 0xbd, 0x63, 0x37, 0x15, 0x1d, 0x41, 0xc4, 0x55, 0xda, 0xe3,
+    0x23, 0xbf, 0x50, 0xb0, 0x7f, 0xb6, 0x1f, 0x45, 0x7b, 0x7b, 0x51, 0xd8, 0xc1, 0xa2, 0x7e, 0x1d,
+    0x81, 0xc6, 0x67, 0xde, 0x53, 0x55, 0x08, 0x97, 0x0d, 0x56, 0x30, 0x40, 0x02, 0x61, 0x85, 0x63,
+    0x1b, 0xe3, 0x4a, 0x13, 0xff, 0xf5, 0xaf, 0x2a, 0xa6, 0x8c, 0x64, 0xbf, 0x45, 0xd6, 0x63, 0x0c,
+    0x4f, 0xee, 0xac, 0x2b, 0x5c, 0x3a, 0x55, 0x1b, 0x02, 0x3b, 0x7c, 0xe6, 0xfb, 0x46, 0x85, 0x3f,
+    0x7a, 0x64, 0xb4, 0xf9, 0x30, 0xc9, 0x92, 0x8f, 0x76, 0x36, 0x11, 0xff, 0xcd, 0x12, 0x4f, 0xbe,
+    0x8b, 0x36, 0x6a, 0x65, 0x38, 0xed, 0x20, 0x2e, 0x6a, 0xe3, 0x81, 0x0c, 0x17, 0xd8, 0x65, 0xf3,
+    0xe7, 0xd2, 0x3e, 0x3b, 0x08, 0x9c, 0x52, 0x47, 0x00, 0x42, 0x4d, 0xba, 0x02, 0x2e, 0x51, 0x8c,
+    0x7f, 0x8d, 0xc3, 0x91, 0x39, 0x24, 0x69, 0x12, 0xcd, 0x3b, 0xa6, 0x9d, 0x23, 0x21, 0x94, 0xdd,
+    0xc2, 0x0a, 0x22, 0x07, 0x81, 0x8b, 0x18, 0x8c, 0x74, 0x9e, 0x47, 0x0b, 0x0b, 0x9f, 0xe2, 0xa3,
+    0x9e, 0x3f, 0x02, 0x4c, 0x8c, 0x13, 0xd3, 0xa2, 0xbb, 0x79, 0x8b, 0x1f, 0x0d, 0x61, 0xd2, 0x0d,
+    0x85, 0xf8, 0x85, 0xd9, 0x5a, 0x68, 0x50, 0x68, 0xef, 0x84, 0xfe, 0xf9, 0x63, 0x54, 0x63, 0x26,
+    0x55, 0x6c, 0x93, 0xeb, 0xa9, 0x4f, 0x13, 0x7e, 0x62, 0x34, 0xfe, 0x4d, 0x4e, 0x7b, 0x53, 0x75,
+    0x06, 0x9f, 0x2d, 0x83, 0xaf, 0xc7, 0x73, 0xab, 0x99, 0x9d, 0xda, 0x47, 0x30, 0x2a, 0x1a, 0x9f,
+    0x27, 0x6a, 0xc5, 0x19, 0xfa, 0xf2, 0x10, 0xb4, 0xf5, 0x82, 0xaa, 0x31, 0x7a, 0xe4, 0x5b, 0x3e,
+    0xe9, 0x4c, 0x92, 0x63, 0xe5, 0x28, 0x91, 0x0a, 0x0f, 0xb9, 0xe7, 0xbe, 0xad, 0x37, 0x29, 0x5e,
+    0x6b, 0x3a, 0x3b, 0xf9, 0xec, 0xab, 0xa5, 0x31, 0x44, 0xa7, 0x1a, 0x29, 0x4f, 0x20, 0x13, 0xb6,
+    0xa3, 0xe5, 0x02, 0xb9, 0x42, 0xc2, 0x1e, 0x80, 0xb6, 0x43, 0xec, 0xb7, 0x4e, 0x7c, 0x37, 0x6c,
+    0x60, 0xbd, 0xdc, 0x91, 0x13, 0x3a, 0xa0, 0x16, 0x33, 0x08, 0x51, 0xde, 0x4e, 0x1d, 0x72, 0x4a,
+    0x03, 0x7c, 0x92, 0x43, 0xe5, 0x01, 0x25, 0x71, 0x67, 0xbe, 0xf7, 0x93, 0x00, 0x38, 0x9d, 0xaf,
+    0xe7, 0x2f, 0x6a, 0xaf, 0x6c, 0xe9, 0x0b, 0x28, 0x2f, 0x11, 0xa5, 0xaf, 0x62, 0xe9, 0x0e, 0x39,
+    0xd0, 0x88, 0x50, 0x06, 0xa5, 0xa8, 0x1c, 0xce, 0x5e, 0x1e, 0x8a, 0x5b, 0x3c, 0x28, 0xf2, 0xaf,
+    0xef, 0x54, 0x86, 0xaf, 0x4d, 0x98, 0xcb, 0x71, 0xac, 0xaa, 0x93, 0x1e, 0xd2, 0xdd, 0xdf, 0x1a,
+    0x2e, 0x0c, 0xc7, 0xbf, 0x29, 0x1e, 0x31, 0xdc, 0x0e, 0x85, 0x96, 0x7b,
+];
+
+pub static DECRYPTED_PAYLOAD: &[u8] = &[
+    0x7c, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x25, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x72, 0x00, 0x00,
+    0x00, 0x06, 0x00, 0x80, 0x00, 0x43, 0x00, 0x20, 0xa4, 0xee, 0xdc, 0x1f, 0x9e, 0xba, 0x42, 0xd6,
+    0xc8, 0x7c, 0xdd, 0x1c, 0xba, 0x12, 0x67, 0x83, 0xd1, 0x7a, 0xb9, 0x91, 0x5b, 0xd3, 0x4d, 0xc7,
+    0xfe, 0xdd, 0x5b, 0x1c, 0x3e, 0x01, 0x19, 0x01, 0x00, 0x8e, 0x00, 0x20, 0xd1, 0x73, 0xca, 0x4b,
+    0xbf, 0xf0, 0xaa, 0x6c, 0xff, 0x5c, 0x3a, 0x2b, 0xf3, 0xb1, 0x6f, 0xdb, 0xff, 0x0e, 0x63, 0x43,
+    0xe3, 0x6a, 0x78, 0x1f, 0x24, 0xf2, 0x89, 0x99, 0x50, 0x66, 0xd0, 0x30, 0x00, 0x10, 0x84, 0x02,
+    0x9c, 0xf6, 0x58, 0x77, 0xe2, 0x69, 0x19, 0xb8, 0x8a, 0x49, 0x10, 0xdc, 0xb8, 0x27, 0xf8, 0xcd,
+    0x59, 0x91, 0xbb, 0x72, 0x84, 0x65, 0xee, 0x57, 0x15, 0x5b, 0x11, 0xc0, 0x8f, 0xfe, 0x16, 0x2f,
+    0x9b, 0x41, 0x80, 0x94, 0x6b, 0xf6, 0xca, 0x6e, 0x1d, 0xf4, 0x89, 0x9b, 0xb6, 0x22, 0x6d, 0x03,
+    0xbd, 0xfb, 0xec, 0x4a, 0x10, 0x9f, 0xc2, 0x67, 0xef, 0x9b, 0x25, 0x12, 0xa0, 0x6b, 0x2e, 0xa8,
+    0x63, 0x45, 0xdc, 0xb9, 0x02, 0x20, 0xcb, 0x26, 0x96, 0xec, 0xa2, 0xfb, 0x6d, 0x23, 0xc4, 0xa4,
+    0x29, 0x20, 0xa5, 0xbe, 0xb5, 0x5a, 0xa6, 0x13, 0xb6, 0x76, 0x17, 0xf3, 0xa1, 0x7f, 0x3e, 0x6c,
+    0x5a, 0xa7, 0xdb, 0x85, 0x30, 0xc7, 0x9f, 0x0c, 0x10, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00,
+    0xe9, 0x32, 0x4a, 0x5a, 0x82, 0x79, 0xd4, 0xdc, 0x64, 0xd9, 0x58, 0xbf, 0xf5, 0x24, 0xe0, 0xb0,
+    0xf1, 0x57, 0xc0, 0x52, 0x32, 0x1f, 0x9b, 0x2e, 0xd9, 0x9b, 0x04, 0x6c, 0x1f, 0xca, 0x1b, 0x0e,
+    0x08, 0x84, 0x83, 0xe6, 0x38, 0x61, 0xcb, 0x04, 0xfe, 0x6f, 0x9d, 0xe9, 0x57, 0xb2, 0xd9, 0x9f,
+    0x9d, 0xe0, 0xe4, 0xc5, 0x53, 0x21, 0x61, 0x27, 0xeb, 0x82, 0x42, 0x79, 0xdb, 0xda, 0x58, 0xea,
+    0x35, 0x01, 0x28, 0x6a, 0xaf, 0xd7, 0x7b, 0x30, 0xd4, 0xe9, 0x17, 0x18, 0xb9, 0xc1, 0xd8, 0x83,
+    0xc3, 0x04, 0xb8, 0xff, 0xd2, 0x25, 0x91, 0x7a, 0xb2, 0x42, 0xe0, 0xb8, 0x93, 0x07, 0x0a, 0x21,
+    0x79, 0xcc, 0xb4, 0x86, 0xee, 0xb8, 0x87, 0x83, 0x44, 0x47, 0x53, 0x06, 0xf2, 0xd1, 0xe5, 0xdd,
+    0xef, 0xd2, 0xe1, 0x0b, 0x05, 0x2b, 0x34, 0x84, 0x64, 0x67, 0xf6, 0xa0, 0x79, 0xc9, 0xfe, 0x60,
+    0xf9, 0x17, 0x69, 0xe1, 0xec, 0x3f, 0x20, 0xa1, 0x23, 0x86, 0x8a, 0x73, 0x6b, 0x20, 0x5f, 0xa3,
+    0x74, 0x9a, 0xbe, 0xf4, 0x38, 0x05, 0x6a, 0xc9, 0x5e, 0xb7, 0xe4, 0x70, 0x57, 0x55, 0x28, 0x18,
+    0x97, 0xb2, 0x92, 0x93, 0x21, 0xc0, 0xe1, 0x6c, 0xd8, 0x61, 0xec, 0xce, 0x46, 0x83, 0xf1, 0x12,
+    0x20, 0x00, 0x00, 0x00, 0x99, 0x6d, 0x9a, 0x9d, 0x13, 0xf3, 0x43, 0x06, 0x35, 0xf5, 0x89, 0x01,
+    0x90, 0x00, 0x09, 0xc9, 0x3f, 0xee, 0x79, 0x27, 0x26, 0xd9, 0x03, 0x9b, 0x57, 0xb1, 0x61, 0x6b,
+    0xf6, 0x0b, 0x81, 0x07,
+];
+
+pub static PASSWORD: &[u8] = &[
+    0x42, 0x39, 0x30, 0x37, 0x44, 0x37, 0x32, 0x37, 0x39, 0x39, 0x43, 0x42, 0x39, 0x41, 0x42, 0x30,
+    0x34, 0x31, 0x30, 0x38, 0x46, 0x44, 0x33, 0x45, 0x39, 0x42, 0x32, 0x38, 0x36, 0x35, 0x41, 0x36,
+    0x33, 0x44, 0x42, 0x42, 0x43, 0x36, 0x33, 0x42, 0x34, 0x39, 0x37, 0x33, 0x35, 0x45, 0x41, 0x41,
+    0x32, 0x45, 0x31, 0x35, 0x43, 0x43, 0x46, 0x32, 0x39, 0x36, 0x33, 0x34, 0x31, 0x32, 0x41, 0x39,
+];
+
+pub static SUPERKEY: &[u8] = &[
+    0x03, 0x07, 0x01, 0x10, 0x9a, 0x81, 0x56, 0x7d, 0xf5, 0x86, 0x7c, 0x62, 0xd7, 0xf9, 0x26, 0x06,
+    0x00, 0x00, 0x00, 0x00, 0xde, 0x2a, 0xcb, 0xac, 0x98, 0x57, 0x2b, 0xe5, 0x57, 0x18, 0x78, 0x57,
+    0x6e, 0x10, 0x09, 0x84, 0x00, 0x00, 0x00, 0x20, 0xac, 0x6d, 0x13, 0xe6, 0xad, 0x2c, 0x89, 0x53,
+    0x1a, 0x99, 0xa5, 0x6c, 0x88, 0xe9, 0xeb, 0x5c, 0xef, 0x68, 0x5e, 0x5b, 0x53, 0xa8, 0xe7, 0xa2,
+    0x76, 0x04, 0x2a, 0x48, 0xd1, 0xa7, 0x59, 0xd1, 0x04, 0x5b, 0xb4, 0x8a, 0x09, 0x22, 0x13, 0x0c,
+    0x94, 0xb6, 0x67, 0x7b, 0x39, 0x85, 0x28, 0x11,
+];
+pub static USRPKEY_AUTHBOUND: &[u8] = &[
+    0x03, 0x04, 0x04, 0x00, 0x1c, 0x34, 0x87, 0x6f, 0xc8, 0x35, 0x0d, 0x34, 0x88, 0x59, 0xbc, 0xf5,
+    0x00, 0x00, 0x00, 0x00, 0x62, 0xe3, 0x38, 0x2d, 0xd0, 0x58, 0x40, 0xc1, 0xb0, 0xf2, 0x4a, 0xdd,
+    0xf7, 0x81, 0x67, 0x0b, 0x00, 0x00, 0x02, 0x1d, 0x05, 0xb2, 0x5a, 0x1d, 0x1b, 0x25, 0x19, 0x48,
+    0xbf, 0x76, 0x0b, 0x37, 0x8c, 0x60, 0x52, 0xea, 0x30, 0x2a, 0x2c, 0x89, 0x99, 0x95, 0x57, 0x5c,
+    0xec, 0x62, 0x3c, 0x08, 0x1a, 0xc6, 0x65, 0xf9, 0xad, 0x24, 0x99, 0xf0, 0x5c, 0x44, 0xa0, 0xea,
+    0x9a, 0x60, 0xa2, 0xef, 0xf5, 0x27, 0x50, 0xba, 0x9c, 0xef, 0xa6, 0x08, 0x88, 0x4b, 0x0f, 0xfe,
+    0x5d, 0x41, 0xac, 0xba, 0xef, 0x9d, 0xa4, 0xb7, 0x72, 0xd3, 0xc8, 0x11, 0x92, 0x06, 0xf6, 0x26,
+    0xdf, 0x90, 0xe2, 0x66, 0x89, 0xf3, 0x85, 0x16, 0x4a, 0xdf, 0x7f, 0xac, 0x94, 0x4a, 0x1c, 0xce,
+    0x18, 0xee, 0xf4, 0x1f, 0x8e, 0xd6, 0xaf, 0xfd, 0x1d, 0xe5, 0x80, 0x4a, 0x6b, 0xbf, 0x91, 0xe2,
+    0x36, 0x1d, 0xb3, 0x53, 0x12, 0xfd, 0xc9, 0x0b, 0xa6, 0x69, 0x00, 0x45, 0xcb, 0x4c, 0x40, 0x6b,
+    0x70, 0xcb, 0xd2, 0xa0, 0x44, 0x0b, 0x4b, 0xec, 0xd6, 0x4f, 0x6f, 0x64, 0x37, 0xa7, 0xc7, 0x25,
+    0x54, 0xf4, 0xac, 0x6b, 0x34, 0x53, 0xea, 0x4e, 0x56, 0x49, 0xba, 0xf4, 0x1e, 0xc6, 0x52, 0x8f,
+    0xf4, 0x85, 0xe7, 0xb5, 0xaf, 0x49, 0x68, 0xb3, 0xb8, 0x7d, 0x63, 0xfc, 0x6e, 0x83, 0xa0, 0xf3,
+    0x91, 0x04, 0x80, 0xfd, 0xc5, 0x54, 0x7e, 0x92, 0x1a, 0x87, 0x2c, 0x6e, 0xa6, 0x29, 0xb9, 0x1e,
+    0x3f, 0xef, 0x30, 0x12, 0x7b, 0x2f, 0xa2, 0x16, 0x61, 0x8a, 0xcf, 0x14, 0x2d, 0x62, 0x98, 0x15,
+    0xae, 0x3b, 0xe6, 0x08, 0x1e, 0xb1, 0xf1, 0x21, 0xb0, 0x50, 0xc0, 0x4b, 0x81, 0x71, 0x29, 0xe7,
+    0x86, 0xbf, 0x29, 0xe1, 0xeb, 0xfe, 0xbc, 0x11, 0x3c, 0xc6, 0x15, 0x47, 0x9b, 0x41, 0x84, 0x61,
+    0x33, 0xbf, 0xca, 0xfe, 0x24, 0x92, 0x9e, 0x70, 0x26, 0x36, 0x46, 0xca, 0xfe, 0xd3, 0x5a, 0x1d,
+    0x9e, 0x30, 0x19, 0xbd, 0x26, 0x49, 0xb4, 0x90, 0x0c, 0x8d, 0xa2, 0x28, 0xa6, 0x24, 0x62, 0x6b,
+    0xe2, 0xfa, 0xe0, 0x53, 0xaa, 0x01, 0xeb, 0xaa, 0x41, 0x2b, 0xcb, 0xb1, 0x08, 0x66, 0x9d, 0x21,
+    0x2d, 0x2a, 0x47, 0x44, 0xee, 0xd5, 0x06, 0xe3, 0x4a, 0xb9, 0x3f, 0xcd, 0x78, 0x67, 0x89, 0x5b,
+    0xf7, 0x51, 0xc0, 0xc4, 0xa9, 0x68, 0xee, 0x44, 0x9c, 0x47, 0xa4, 0xbd, 0x6f, 0x7b, 0xdd, 0x64,
+    0xa8, 0xc7, 0x1e, 0x77, 0x1d, 0x68, 0x87, 0xaa, 0xae, 0x3c, 0xfc, 0x58, 0xb6, 0x3c, 0xcf, 0x58,
+    0xd0, 0x10, 0xaa, 0xef, 0xf0, 0x98, 0x67, 0x14, 0x29, 0x4d, 0x40, 0x8b, 0xe5, 0xb1, 0xdf, 0x7f,
+    0x40, 0xb1, 0xd8, 0xea, 0x6c, 0xa8, 0xf7, 0x64, 0xed, 0x02, 0x8d, 0xe7, 0x93, 0xfe, 0x79, 0x9a,
+    0x88, 0x62, 0x4f, 0xd0, 0x8a, 0x80, 0x36, 0x42, 0x0a, 0xf1, 0xa2, 0x0e, 0x30, 0x39, 0xbd, 0x26,
+    0x1d, 0xd4, 0xf1, 0xc8, 0x6e, 0xdd, 0xc5, 0x41, 0x29, 0xd8, 0xc1, 0x9e, 0x24, 0xf0, 0x25, 0x07,
+    0x05, 0x06, 0xc5, 0x08, 0xe3, 0x02, 0x2b, 0xe1, 0x40, 0xc5, 0x67, 0xd2, 0x82, 0x96, 0x20, 0x80,
+    0xcf, 0x87, 0x3a, 0xc6, 0xb0, 0xbe, 0xcc, 0xbb, 0x5a, 0x01, 0xab, 0xdd, 0x00, 0xc7, 0x0e, 0x7b,
+    0x02, 0x35, 0x27, 0xf4, 0x70, 0xfe, 0xd1, 0x19, 0x6a, 0x64, 0x23, 0x9d, 0xba, 0xe9, 0x1d, 0x76,
+    0x90, 0xfe, 0x7f, 0xd6, 0xb5, 0xa0, 0xe7, 0xb9, 0xf3, 0x56, 0x82, 0x8e, 0x57, 0x35, 0xf2, 0x69,
+    0xce, 0x52, 0xac, 0xc2, 0xf6, 0x5e, 0xb6, 0x54, 0x95, 0x83, 0x3b, 0x9f, 0x48, 0xbb, 0x04, 0x06,
+    0xac, 0x55, 0xa9, 0xb9, 0xa3, 0xe7, 0x89, 0x6e, 0x5c, 0x3a, 0x08, 0x67, 0x00, 0x8f, 0x1e, 0x26,
+    0x1b, 0x4d, 0x8a, 0xa6, 0x17, 0xa0, 0xa6, 0x18, 0xe6, 0x31, 0x43, 0x15, 0xb8, 0x7f, 0x9e, 0xf5,
+    0x78, 0x58, 0x98, 0xb1, 0x8c, 0xf5, 0x22, 0x42, 0x33, 0xc0, 0x42, 0x72, 0x4f, 0xce, 0x9f, 0x31,
+    0xaf, 0x17, 0x2f, 0x21, 0x07, 0xea, 0x61, 0xff, 0x73, 0x08, 0x50, 0xb2, 0x19, 0xe8, 0x23, 0x1b,
+    0x83, 0x42, 0xdd, 0x4e, 0x6d,
+];
+pub static USRPKEY_AUTHBOUND_CHR: &[u8] = &[
+    0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+    0x7c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x20,
+    0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0xf6, 0x01, 0x00, 0xa0,
+    0xf0, 0x7e, 0x7d, 0xb4, 0xc6, 0xd7, 0x25, 0x1d, 0x02, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x00,
+    0x03, 0x00, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00,
+    0x2d, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00,
+    0xbe, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x02, 0x00, 0x30, 0xb0, 0xad, 0x01, 0x00,
+    0xc2, 0x02, 0x00, 0x30, 0x75, 0x15, 0x03, 0x00, 0xcf, 0x02, 0x00, 0x30, 0xb9, 0x61, 0x34, 0x01,
+    0xce, 0x02, 0x00, 0x30, 0xb9, 0x61, 0x34, 0x01, 0x30, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xbd, 0x02, 0x00, 0x60,
+    0x10, 0x9d, 0x8b, 0x31, 0x76, 0x01, 0x00, 0x00, 0xf5, 0x01, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00,
+];
+pub static USRCERT_AUTHBOUND: &[u8] = &[
+    0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x97, 0x30, 0x82, 0x02, 0x93, 0x30, 0x82, 0x02, 0x3a,
+    0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
+    0x3d, 0x04, 0x03, 0x02, 0x30, 0x29, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13,
+    0x10, 0x34, 0x34, 0x61, 0x38, 0x31, 0x65, 0x61, 0x65, 0x63, 0x35, 0x31, 0x64, 0x62, 0x30, 0x62,
+    0x31, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0c, 0x0c, 0x03, 0x54, 0x45, 0x45, 0x30,
+    0x20, 0x17, 0x0d, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a,
+    0x18, 0x0f, 0x32, 0x31, 0x30, 0x36, 0x30, 0x32, 0x30, 0x37, 0x30, 0x36, 0x32, 0x38, 0x31, 0x35,
+    0x5a, 0x30, 0x1f, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x14, 0x41, 0x6e,
+    0x64, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x4b,
+    0x65, 0x79, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
+    0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x20, 0x16, 0x85,
+    0xe6, 0x7f, 0xf1, 0x0e, 0x99, 0x1b, 0x3a, 0xc6, 0xc2, 0x83, 0x0a, 0x1d, 0xa4, 0xf1, 0x92, 0x76,
+    0x88, 0x4b, 0x6a, 0xcd, 0xb2, 0x8e, 0xf1, 0x50, 0x58, 0xd2, 0x69, 0xde, 0x57, 0x9c, 0x9c, 0x29,
+    0x04, 0x03, 0xf2, 0x4d, 0x12, 0x77, 0x9c, 0x62, 0xbc, 0x75, 0xb4, 0xab, 0x7a, 0xbc, 0xa0, 0x8f,
+    0x60, 0x5e, 0xcd, 0xce, 0x3a, 0xd8, 0x09, 0xeb, 0x9d, 0x40, 0xdb, 0x58, 0x53, 0xa3, 0x82, 0x01,
+    0x59, 0x30, 0x82, 0x01, 0x55, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+    0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x82, 0x01, 0x41, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01,
+    0xd6, 0x79, 0x02, 0x01, 0x11, 0x04, 0x82, 0x01, 0x31, 0x30, 0x82, 0x01, 0x2d, 0x02, 0x01, 0x03,
+    0x0a, 0x01, 0x01, 0x02, 0x01, 0x04, 0x0a, 0x01, 0x01, 0x04, 0x08, 0x61, 0x73, 0x64, 0x66, 0x6a,
+    0x6b, 0x6c, 0x3b, 0x04, 0x00, 0x30, 0x6b, 0xbf, 0x85, 0x3d, 0x08, 0x02, 0x06, 0x01, 0x76, 0x31,
+    0x8b, 0x9d, 0x10, 0xbf, 0x85, 0x45, 0x5b, 0x04, 0x59, 0x30, 0x57, 0x31, 0x31, 0x30, 0x2f, 0x04,
+    0x2a, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65,
+    0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x6a, 0x64, 0x61, 0x6e, 0x69, 0x73, 0x2e, 0x6b,
+    0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x74, 0x6f, 0x6f, 0x6c, 0x02, 0x01, 0x01, 0x31, 0x22,
+    0x04, 0x20, 0x30, 0xe0, 0x78, 0x45, 0xab, 0xd7, 0xc1, 0x74, 0x49, 0x01, 0x0f, 0xa7, 0x7f, 0x89,
+    0xde, 0x11, 0xa3, 0x8b, 0x3e, 0x31, 0x6b, 0xf1, 0x18, 0xb4, 0x58, 0x1b, 0xd7, 0xb3, 0x58, 0xa9,
+    0xc2, 0x81, 0x30, 0x81, 0xa5, 0xa1, 0x08, 0x31, 0x06, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03, 0xa2,
+    0x03, 0x02, 0x01, 0x03, 0xa3, 0x04, 0x02, 0x02, 0x01, 0x00, 0xa5, 0x05, 0x31, 0x03, 0x02, 0x01,
+    0x04, 0xaa, 0x03, 0x02, 0x01, 0x01, 0xbf, 0x83, 0x78, 0x03, 0x02, 0x01, 0x02, 0xbf, 0x85, 0x3e,
+    0x03, 0x02, 0x01, 0x00, 0xbf, 0x85, 0x40, 0x4c, 0x30, 0x4a, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x0a,
+    0x01, 0x02, 0x04, 0x20, 0xe7, 0xad, 0x3c, 0x13, 0xc2, 0x73, 0x41, 0x60, 0xd7, 0x1a, 0x7c, 0x00,
+    0x5e, 0x14, 0xd8, 0xae, 0x06, 0x5d, 0x22, 0xd0, 0xb5, 0xf5, 0x6a, 0xba, 0x1f, 0x82, 0xa7, 0x8c,
+    0x17, 0x2c, 0xfd, 0x0f, 0xbf, 0x85, 0x41, 0x05, 0x02, 0x03, 0x01, 0xad, 0xb0, 0xbf, 0x85, 0x42,
+    0x05, 0x02, 0x03, 0x03, 0x15, 0x75, 0xbf, 0x85, 0x4e, 0x06, 0x02, 0x04, 0x01, 0x34, 0x61, 0xb9,
+    0xbf, 0x85, 0x4f, 0x06, 0x02, 0x04, 0x01, 0x34, 0x61, 0xb9, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86,
+    0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x4b, 0xdc, 0x8e,
+    0x91, 0xe6, 0xaa, 0x4a, 0x81, 0x6d, 0xa2, 0xd7, 0x13, 0x9e, 0x70, 0x12, 0x79, 0xb7, 0x85, 0x05,
+    0xad, 0x6e, 0x5e, 0x0b, 0x43, 0x3b, 0xaf, 0x9a, 0xa9, 0x29, 0x40, 0xd7, 0x92, 0x02, 0x20, 0x2f,
+    0x39, 0x58, 0xe9, 0x89, 0x1a, 0x14, 0x41, 0x8d, 0xe0, 0xdc, 0x3d, 0x88, 0xf4, 0x2c, 0x7c, 0xda,
+    0xa1, 0x84, 0xfa, 0x7f, 0xf9, 0x07, 0x97, 0xfb, 0xb5, 0xb7, 0x28, 0x28, 0x00, 0x7c, 0xa7,
+];
+pub static CACERT_AUTHBOUND: &[u8] = &[
+    0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x63, 0x30, 0x82, 0x02, 0x26, 0x30, 0x82, 0x01, 0xab,
+    0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0a, 0x05, 0x84, 0x20, 0x26, 0x90, 0x76, 0x23, 0x58, 0x71,
+    0x77, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x29, 0x31,
+    0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x34, 0x64, 0x37, 0x34, 0x61, 0x30,
+    0x65, 0x30, 0x31, 0x61, 0x61, 0x66, 0x33, 0x64, 0x64, 0x66, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03,
+    0x55, 0x04, 0x0c, 0x0c, 0x03, 0x54, 0x45, 0x45, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x33,
+    0x32, 0x31, 0x32, 0x31, 0x32, 0x35, 0x31, 0x33, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x30, 0x33, 0x31,
+    0x38, 0x32, 0x31, 0x32, 0x35, 0x31, 0x33, 0x5a, 0x30, 0x29, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03,
+    0x55, 0x04, 0x05, 0x13, 0x10, 0x34, 0x34, 0x61, 0x38, 0x31, 0x65, 0x61, 0x65, 0x63, 0x35, 0x31,
+    0x64, 0x62, 0x30, 0x62, 0x31, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0c, 0x0c, 0x03,
+    0x54, 0x45, 0x45, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
+    0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xfa, 0x35,
+    0x8f, 0xb0, 0x31, 0xd6, 0x30, 0x88, 0xde, 0xb0, 0x29, 0xcd, 0x6c, 0x7d, 0x4e, 0xa9, 0xce, 0x6e,
+    0x9d, 0x7a, 0xac, 0x97, 0x92, 0xc2, 0x45, 0xb5, 0xe2, 0xd0, 0xc1, 0x52, 0xa8, 0x50, 0x25, 0xd7,
+    0x89, 0x58, 0x7b, 0x04, 0xb6, 0x66, 0x93, 0x2a, 0x26, 0x5d, 0x3a, 0xb1, 0x5b, 0x77, 0x30, 0xbf,
+    0x95, 0xaa, 0x8b, 0x43, 0xc3, 0xbf, 0x43, 0xb7, 0xee, 0xac, 0x73, 0xdc, 0x03, 0x6a, 0xa3, 0x81,
+    0xba, 0x30, 0x81, 0xb7, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x19,
+    0x9f, 0x87, 0x8b, 0x56, 0xf4, 0x99, 0x3a, 0x69, 0x96, 0x9b, 0x8d, 0x9e, 0x64, 0xaa, 0x56, 0xb4,
+    0x7f, 0x8b, 0x4d, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+    0xa9, 0xb5, 0xf4, 0x29, 0xc9, 0x1a, 0x58, 0xbd, 0x2f, 0x98, 0x2d, 0x67, 0x73, 0x31, 0x06, 0x87,
+    0xe0, 0xdf, 0xcd, 0x62, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05,
+    0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+    0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x54, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x4d, 0x30, 0x4b,
+    0x30, 0x49, 0xa0, 0x47, 0xa0, 0x45, 0x86, 0x43, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
+    0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70,
+    0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69,
+    0x6f, 0x6e, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x30, 0x35, 0x38, 0x34, 0x32, 0x30, 0x32, 0x36, 0x39,
+    0x30, 0x37, 0x36, 0x32, 0x33, 0x35, 0x38, 0x37, 0x31, 0x37, 0x37, 0x30, 0x0a, 0x06, 0x08, 0x2a,
+    0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x69, 0x00, 0x30, 0x66, 0x02, 0x31, 0x00, 0xe3,
+    0x35, 0xc6, 0xa8, 0xb2, 0x75, 0x9c, 0x56, 0x7b, 0x6e, 0x61, 0x80, 0x65, 0x2c, 0x06, 0x88, 0xdd,
+    0xb9, 0x68, 0x4d, 0x3c, 0x68, 0x49, 0x66, 0x01, 0x4e, 0x30, 0x1d, 0xf3, 0xec, 0xa5, 0x51, 0x5c,
+    0xbf, 0xe7, 0x83, 0x33, 0xbd, 0x14, 0xee, 0x23, 0xf0, 0xcf, 0xb1, 0x37, 0x1c, 0x27, 0x78, 0x02,
+    0x31, 0x00, 0x94, 0xcb, 0x08, 0x3d, 0x2d, 0x3e, 0x69, 0x54, 0x5f, 0x63, 0xe3, 0xe4, 0x74, 0x72,
+    0xe2, 0xff, 0x8b, 0x26, 0xd2, 0x86, 0xc0, 0x97, 0x32, 0x40, 0xdd, 0x7c, 0x1f, 0x50, 0x60, 0x57,
+    0xcf, 0x2e, 0x23, 0xf3, 0x33, 0xe4, 0xfb, 0x6f, 0x5b, 0x7c, 0xc6, 0x31, 0x85, 0xae, 0xe0, 0x4e,
+    0x44, 0xa9, 0x30, 0x82, 0x03, 0xd1, 0x30, 0x82, 0x01, 0xb9, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02,
+    0x0a, 0x03, 0x88, 0x26, 0x67, 0x60, 0x65, 0x89, 0x96, 0x85, 0x7f, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+    0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x1b, 0x31, 0x19, 0x30, 0x17,
+    0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x66, 0x39, 0x32, 0x30, 0x30, 0x39, 0x65, 0x38, 0x35,
+    0x33, 0x62, 0x36, 0x62, 0x30, 0x34, 0x35, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x33, 0x32,
+    0x31, 0x32, 0x31, 0x31, 0x34, 0x31, 0x34, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x30, 0x33, 0x31, 0x38,
+    0x32, 0x31, 0x31, 0x34, 0x31, 0x34, 0x5a, 0x30, 0x29, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55,
+    0x04, 0x05, 0x13, 0x10, 0x34, 0x64, 0x37, 0x34, 0x61, 0x30, 0x65, 0x30, 0x31, 0x61, 0x61, 0x66,
+    0x33, 0x64, 0x64, 0x66, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0c, 0x0c, 0x03, 0x54,
+    0x45, 0x45, 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
+    0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04, 0xd5, 0xf5, 0x0e, 0xe2, 0x8d, 0xf3,
+    0x33, 0x4a, 0x6a, 0x77, 0x90, 0x9c, 0xc2, 0x25, 0xc8, 0x8a, 0x32, 0xae, 0x3b, 0xb4, 0x9c, 0x4a,
+    0x95, 0x22, 0x0c, 0xba, 0x0a, 0x76, 0xca, 0xcb, 0x24, 0x0c, 0x84, 0x3a, 0x83, 0x76, 0x04, 0x23,
+    0x31, 0x3a, 0xa0, 0x82, 0x80, 0x26, 0x65, 0xfd, 0x2f, 0x44, 0xf4, 0x96, 0xd8, 0xb7, 0xdc, 0xac,
+    0x55, 0x34, 0x74, 0x41, 0x0d, 0x0d, 0x7f, 0xbd, 0xe3, 0xf4, 0x28, 0xdf, 0x74, 0x4a, 0x17, 0x4d,
+    0xe7, 0xb2, 0x9b, 0x2b, 0x24, 0xc0, 0x9e, 0x56, 0x00, 0x52, 0xbb, 0x75, 0xb0, 0xd5, 0x6a, 0x41,
+    0x16, 0x08, 0xce, 0x32, 0xdb, 0x8f, 0x8b, 0x20, 0x73, 0x72, 0xa3, 0x81, 0xb6, 0x30, 0x81, 0xb3,
+    0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xa9, 0xb5, 0xf4, 0x29, 0xc9,
+    0x1a, 0x58, 0xbd, 0x2f, 0x98, 0x2d, 0x67, 0x73, 0x31, 0x06, 0x87, 0xe0, 0xdf, 0xcd, 0x62, 0x30,
+    0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x36, 0x61, 0xe1, 0x00,
+    0x7c, 0x88, 0x05, 0x09, 0x51, 0x8b, 0x44, 0x6c, 0x47, 0xff, 0x1a, 0x4c, 0xc9, 0xea, 0x4f, 0x12,
+    0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01,
+    0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02,
+    0x04, 0x30, 0x50, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x49, 0x30, 0x47, 0x30, 0x45, 0xa0, 0x43,
+    0xa0, 0x41, 0x86, 0x3f, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x61, 0x6e, 0x64, 0x72,
+    0x6f, 0x69, 0x64, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x63,
+    0x6f, 0x6d, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x63,
+    0x72, 0x6c, 0x2f, 0x45, 0x38, 0x46, 0x41, 0x31, 0x39, 0x36, 0x33, 0x31, 0x34, 0x44, 0x32, 0x46,
+    0x41, 0x31, 0x38, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+    0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x32, 0xce, 0x04, 0xcc, 0x4d, 0x82, 0xad, 0x1d, 0xde,
+    0xa5, 0xcf, 0xe2, 0x1a, 0xa3, 0x79, 0xf7, 0xed, 0x88, 0x1e, 0x0e, 0x67, 0x8e, 0xfc, 0xbe, 0x7b,
+    0x04, 0xb7, 0x26, 0x59, 0xca, 0x95, 0x47, 0x8a, 0x10, 0x3f, 0xe5, 0x14, 0x19, 0xec, 0xd4, 0xdb,
+    0x33, 0xc3, 0xa1, 0x51, 0xf5, 0x06, 0x5e, 0x30, 0x66, 0x1f, 0xd2, 0x58, 0x2f, 0x14, 0x03, 0x7b,
+    0x35, 0x83, 0x86, 0x46, 0xdc, 0xee, 0x04, 0x30, 0xa1, 0x0f, 0xc4, 0x16, 0xc9, 0x8e, 0x63, 0xd0,
+    0xda, 0x5c, 0xb0, 0xf7, 0x3e, 0x21, 0xb6, 0xa5, 0x04, 0x07, 0x5a, 0x01, 0x8c, 0x31, 0x1f, 0x3e,
+    0x3a, 0xaf, 0x8d, 0x31, 0x3e, 0xb6, 0x12, 0x14, 0xf0, 0x0d, 0x2c, 0xcc, 0x6c, 0xb8, 0x7a, 0xbf,
+    0xd2, 0x6b, 0x5f, 0x27, 0xb0, 0xff, 0xc0, 0xaa, 0xde, 0xde, 0xf6, 0x31, 0x6d, 0xf3, 0x95, 0xc2,
+    0xd4, 0x90, 0xdc, 0x82, 0x4f, 0x24, 0x0d, 0x85, 0xf2, 0xbb, 0xc4, 0x58, 0xc9, 0xfa, 0xdd, 0x96,
+    0x41, 0x2b, 0x1f, 0x4c, 0x10, 0x1c, 0x9a, 0x57, 0x55, 0x0f, 0x62, 0xfc, 0x8d, 0xa2, 0xca, 0x84,
+    0x7b, 0x16, 0x60, 0xe8, 0x62, 0xce, 0x92, 0x85, 0x13, 0xf0, 0x63, 0x83, 0xd8, 0x5b, 0xa8, 0x74,
+    0x78, 0xb5, 0x28, 0xdb, 0x6c, 0xc9, 0x6e, 0x85, 0x85, 0x52, 0x3f, 0xd8, 0x67, 0xae, 0xf4, 0x09,
+    0xbe, 0xcf, 0x8c, 0x7f, 0x72, 0xb2, 0xc8, 0x93, 0xc6, 0xd2, 0xf3, 0x38, 0x74, 0x71, 0x22, 0xd6,
+    0x92, 0x76, 0xb1, 0xae, 0x14, 0x5a, 0x09, 0xd8, 0xaf, 0x1d, 0xaf, 0x48, 0x22, 0x5c, 0x30, 0x85,
+    0x8e, 0xc2, 0xfe, 0x61, 0xaf, 0xc3, 0xd2, 0x4c, 0x92, 0x53, 0xa4, 0x75, 0x1f, 0x78, 0xea, 0xfc,
+    0xfa, 0xc4, 0xca, 0x4e, 0x67, 0x68, 0x1f, 0x7d, 0xb2, 0x5e, 0xea, 0x8a, 0xb1, 0xcc, 0xb6, 0x92,
+    0x64, 0xf8, 0x82, 0xc0, 0x8b, 0xdc, 0x24, 0xe8, 0x57, 0x20, 0x33, 0x6d, 0x17, 0x33, 0x0d, 0xcb,
+    0x70, 0x02, 0x8b, 0xe5, 0xe3, 0x7d, 0x2c, 0x98, 0x32, 0x00, 0x20, 0xb4, 0xbd, 0xee, 0x89, 0xaa,
+    0x66, 0x13, 0x34, 0x9d, 0x9c, 0x8f, 0xde, 0x16, 0x09, 0x91, 0x49, 0x80, 0x50, 0x57, 0x39, 0xae,
+    0x35, 0x01, 0xe2, 0x25, 0x8e, 0x17, 0x08, 0xe0, 0xf0, 0x77, 0x98, 0x9d, 0x0a, 0x4f, 0xd2, 0x76,
+    0xda, 0xc4, 0x51, 0x45, 0x32, 0x8b, 0xe1, 0xab, 0xee, 0x10, 0x16, 0xf6, 0x95, 0x7d, 0x32, 0x76,
+    0xb2, 0xb5, 0x19, 0x67, 0x73, 0xfe, 0xc0, 0xc6, 0xa9, 0xd2, 0xa9, 0x23, 0xf0, 0x2b, 0xfc, 0xb1,
+    0xb6, 0xec, 0x3e, 0x11, 0x60, 0xa4, 0x22, 0xc7, 0xff, 0x25, 0xc3, 0xed, 0x6c, 0x6b, 0x79, 0x02,
+    0x3d, 0x5d, 0x62, 0x36, 0xd9, 0x32, 0xe4, 0x6e, 0x47, 0x67, 0x85, 0x8b, 0x23, 0x0a, 0xd5, 0x1e,
+    0xd0, 0xf4, 0x17, 0x1d, 0xcc, 0x3f, 0x5f, 0xda, 0x12, 0xe2, 0x35, 0x25, 0x52, 0xc2, 0xd6, 0x94,
+    0x3e, 0x83, 0x60, 0x55, 0xf8, 0x8d, 0x54, 0xf5, 0x47, 0x6f, 0x38, 0x03, 0x3b, 0xd7, 0x9a, 0x94,
+    0x8a, 0x3b, 0x9f, 0x92, 0x69, 0x0f, 0xcd, 0xb8, 0xf4, 0x62, 0x78, 0x22, 0x47, 0xe0, 0xae, 0xed,
+    0xfd, 0xf6, 0xe4, 0xc5, 0x8c, 0x0e, 0xb5, 0x18, 0xb1, 0x46, 0x3a, 0x6f, 0xbd, 0xde, 0x50, 0x3f,
+    0x1c, 0x35, 0x28, 0xf9, 0xed, 0x1e, 0xe8, 0x15, 0x31, 0xa9, 0xf7, 0xb1, 0x9d, 0xe1, 0x34, 0x81,
+    0x20, 0x1f, 0x22, 0xd4, 0xb7, 0xc6, 0x59, 0x8b, 0x90, 0x98, 0xdf, 0xa6, 0xb9, 0xa8, 0x8e, 0x6c,
+    0x15, 0x55, 0x5c, 0x41, 0x96, 0x82, 0x0d, 0xa9, 0x5f, 0xa9, 0xf3, 0x77, 0x1d, 0xee, 0x6b, 0x4c,
+    0x94, 0xc6, 0xc6, 0x9b, 0x78, 0x5b, 0x03, 0xbd, 0xa9, 0x87, 0xdd, 0x24, 0x04, 0x70, 0xce, 0x6c,
+    0x52, 0xe6, 0x21, 0x63, 0x6d, 0x28, 0x6c, 0x30, 0x82, 0x05, 0x60, 0x30, 0x82, 0x03, 0x48, 0xa0,
+    0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0xe8, 0xfa, 0x19, 0x63, 0x14, 0xd2, 0xfa, 0x18, 0x30,
+    0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x1b,
+    0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x66, 0x39, 0x32, 0x30, 0x30,
+    0x39, 0x65, 0x38, 0x35, 0x33, 0x62, 0x36, 0x62, 0x30, 0x34, 0x35, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+    0x36, 0x30, 0x35, 0x32, 0x36, 0x31, 0x36, 0x32, 0x38, 0x35, 0x32, 0x5a, 0x17, 0x0d, 0x32, 0x36,
+    0x30, 0x35, 0x32, 0x34, 0x31, 0x36, 0x32, 0x38, 0x35, 0x32, 0x5a, 0x30, 0x1b, 0x31, 0x19, 0x30,
+    0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x66, 0x39, 0x32, 0x30, 0x30, 0x39, 0x65, 0x38,
+    0x35, 0x33, 0x62, 0x36, 0x62, 0x30, 0x34, 0x35, 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09,
+    0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00,
+    0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xaf, 0xb6, 0xc7, 0x82, 0x2b, 0xb1, 0xa7,
+    0x01, 0xec, 0x2b, 0xb4, 0x2e, 0x8b, 0xcc, 0x54, 0x16, 0x63, 0xab, 0xef, 0x98, 0x2f, 0x32, 0xc7,
+    0x7f, 0x75, 0x31, 0x03, 0x0c, 0x97, 0x52, 0x4b, 0x1b, 0x5f, 0xe8, 0x09, 0xfb, 0xc7, 0x2a, 0xa9,
+    0x45, 0x1f, 0x74, 0x3c, 0xbd, 0x9a, 0x6f, 0x13, 0x35, 0x74, 0x4a, 0xa5, 0x5e, 0x77, 0xf6, 0xb6,
+    0xac, 0x35, 0x35, 0xee, 0x17, 0xc2, 0x5e, 0x63, 0x95, 0x17, 0xdd, 0x9c, 0x92, 0xe6, 0x37, 0x4a,
+    0x53, 0xcb, 0xfe, 0x25, 0x8f, 0x8f, 0xfb, 0xb6, 0xfd, 0x12, 0x93, 0x78, 0xa2, 0x2a, 0x4c, 0xa9,
+    0x9c, 0x45, 0x2d, 0x47, 0xa5, 0x9f, 0x32, 0x01, 0xf4, 0x41, 0x97, 0xca, 0x1c, 0xcd, 0x7e, 0x76,
+    0x2f, 0xb2, 0xf5, 0x31, 0x51, 0xb6, 0xfe, 0xb2, 0xff, 0xfd, 0x2b, 0x6f, 0xe4, 0xfe, 0x5b, 0xc6,
+    0xbd, 0x9e, 0xc3, 0x4b, 0xfe, 0x08, 0x23, 0x9d, 0xaa, 0xfc, 0xeb, 0x8e, 0xb5, 0xa8, 0xed, 0x2b,
+    0x3a, 0xcd, 0x9c, 0x5e, 0x3a, 0x77, 0x90, 0xe1, 0xb5, 0x14, 0x42, 0x79, 0x31, 0x59, 0x85, 0x98,
+    0x11, 0xad, 0x9e, 0xb2, 0xa9, 0x6b, 0xbd, 0xd7, 0xa5, 0x7c, 0x93, 0xa9, 0x1c, 0x41, 0xfc, 0xcd,
+    0x27, 0xd6, 0x7f, 0xd6, 0xf6, 0x71, 0xaa, 0x0b, 0x81, 0x52, 0x61, 0xad, 0x38, 0x4f, 0xa3, 0x79,
+    0x44, 0x86, 0x46, 0x04, 0xdd, 0xb3, 0xd8, 0xc4, 0xf9, 0x20, 0xa1, 0x9b, 0x16, 0x56, 0xc2, 0xf1,
+    0x4a, 0xd6, 0xd0, 0x3c, 0x56, 0xec, 0x06, 0x08, 0x99, 0x04, 0x1c, 0x1e, 0xd1, 0xa5, 0xfe, 0x6d,
+    0x34, 0x40, 0xb5, 0x56, 0xba, 0xd1, 0xd0, 0xa1, 0x52, 0x58, 0x9c, 0x53, 0xe5, 0x5d, 0x37, 0x07,
+    0x62, 0xf0, 0x12, 0x2e, 0xef, 0x91, 0x86, 0x1b, 0x1b, 0x0e, 0x6c, 0x4c, 0x80, 0x92, 0x74, 0x99,
+    0xc0, 0xe9, 0xbe, 0xc0, 0xb8, 0x3e, 0x3b, 0xc1, 0xf9, 0x3c, 0x72, 0xc0, 0x49, 0x60, 0x4b, 0xbd,
+    0x2f, 0x13, 0x45, 0xe6, 0x2c, 0x3f, 0x8e, 0x26, 0xdb, 0xec, 0x06, 0xc9, 0x47, 0x66, 0xf3, 0xc1,
+    0x28, 0x23, 0x9d, 0x4f, 0x43, 0x12, 0xfa, 0xd8, 0x12, 0x38, 0x87, 0xe0, 0x6b, 0xec, 0xf5, 0x67,
+    0x58, 0x3b, 0xf8, 0x35, 0x5a, 0x81, 0xfe, 0xea, 0xba, 0xf9, 0x9a, 0x83, 0xc8, 0xdf, 0x3e, 0x2a,
+    0x32, 0x2a, 0xfc, 0x67, 0x2b, 0xf1, 0x20, 0xb1, 0x35, 0x15, 0x8b, 0x68, 0x21, 0xce, 0xaf, 0x30,
+    0x9b, 0x6e, 0xee, 0x77, 0xf9, 0x88, 0x33, 0xb0, 0x18, 0xda, 0xa1, 0x0e, 0x45, 0x1f, 0x06, 0xa3,
+    0x74, 0xd5, 0x07, 0x81, 0xf3, 0x59, 0x08, 0x29, 0x66, 0xbb, 0x77, 0x8b, 0x93, 0x08, 0x94, 0x26,
+    0x98, 0xe7, 0x4e, 0x0b, 0xcd, 0x24, 0x62, 0x8a, 0x01, 0xc2, 0xcc, 0x03, 0xe5, 0x1f, 0x0b, 0x3e,
+    0x5b, 0x4a, 0xc1, 0xe4, 0xdf, 0x9e, 0xaf, 0x9f, 0xf6, 0xa4, 0x92, 0xa7, 0x7c, 0x14, 0x83, 0x88,
+    0x28, 0x85, 0x01, 0x5b, 0x42, 0x2c, 0xe6, 0x7b, 0x80, 0xb8, 0x8c, 0x9b, 0x48, 0xe1, 0x3b, 0x60,
+    0x7a, 0xb5, 0x45, 0xc7, 0x23, 0xff, 0x8c, 0x44, 0xf8, 0xf2, 0xd3, 0x68, 0xb9, 0xf6, 0x52, 0x0d,
+    0x31, 0x14, 0x5e, 0xbf, 0x9e, 0x86, 0x2a, 0xd7, 0x1d, 0xf6, 0xa3, 0xbf, 0xd2, 0x45, 0x09, 0x59,
+    0xd6, 0x53, 0x74, 0x0d, 0x97, 0xa1, 0x2f, 0x36, 0x8b, 0x13, 0xef, 0x66, 0xd5, 0xd0, 0xa5, 0x4a,
+    0x6e, 0x2f, 0x5d, 0x9a, 0x6f, 0xef, 0x44, 0x68, 0x32, 0xbc, 0x67, 0x84, 0x47, 0x25, 0x86, 0x1f,
+    0x09, 0x3d, 0xd0, 0xe6, 0xf3, 0x40, 0x5d, 0xa8, 0x96, 0x43, 0xef, 0x0f, 0x4d, 0x69, 0xb6, 0x42,
+    0x00, 0x51, 0xfd, 0xb9, 0x30, 0x49, 0x67, 0x3e, 0x36, 0x95, 0x05, 0x80, 0xd3, 0xcd, 0xf4, 0xfb,
+    0xd0, 0x8b, 0xc5, 0x84, 0x83, 0x95, 0x26, 0x00, 0x63, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81,
+    0xa6, 0x30, 0x81, 0xa3, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x36,
+    0x61, 0xe1, 0x00, 0x7c, 0x88, 0x05, 0x09, 0x51, 0x8b, 0x44, 0x6c, 0x47, 0xff, 0x1a, 0x4c, 0xc9,
+    0xea, 0x4f, 0x12, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+    0x36, 0x61, 0xe1, 0x00, 0x7c, 0x88, 0x05, 0x09, 0x51, 0x8b, 0x44, 0x6c, 0x47, 0xff, 0x1a, 0x4c,
+    0xc9, 0xea, 0x4f, 0x12, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05,
+    0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+    0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x40, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x39, 0x30, 0x37,
+    0x30, 0x35, 0xa0, 0x33, 0xa0, 0x31, 0x86, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
+    0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70,
+    0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69,
+    0x6f, 0x6e, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+    0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x20, 0xc8, 0xc3, 0x8d, 0x4b,
+    0xdc, 0xa9, 0x57, 0x1b, 0x46, 0x8c, 0x89, 0x2f, 0xff, 0x72, 0xaa, 0xc6, 0xf8, 0x44, 0xa1, 0x1d,
+    0x41, 0xa8, 0xf0, 0x73, 0x6c, 0xc3, 0x7d, 0x16, 0xd6, 0x42, 0x6d, 0x8e, 0x7e, 0x94, 0x07, 0x04,
+    0x4c, 0xea, 0x39, 0xe6, 0x8b, 0x07, 0xc1, 0x3d, 0xbf, 0x15, 0x03, 0xdd, 0x5c, 0x85, 0xbd, 0xaf,
+    0xb2, 0xc0, 0x2d, 0x5f, 0x6c, 0xdb, 0x4e, 0xfa, 0x81, 0x27, 0xdf, 0x8b, 0x04, 0xf1, 0x82, 0x77,
+    0x0f, 0xc4, 0xe7, 0x74, 0x5b, 0x7f, 0xce, 0xaa, 0x87, 0x12, 0x9a, 0x88, 0x01, 0xce, 0x8e, 0x9b,
+    0xc0, 0xcb, 0x96, 0x37, 0x9b, 0x4d, 0x26, 0xa8, 0x2d, 0x30, 0xfd, 0x9c, 0x2f, 0x8e, 0xed, 0x6d,
+    0xc1, 0xbe, 0x2f, 0x84, 0xb6, 0x89, 0xe4, 0xd9, 0x14, 0x25, 0x8b, 0x14, 0x4b, 0xba, 0xe6, 0x24,
+    0xa1, 0xc7, 0x06, 0x71, 0x13, 0x2e, 0x2f, 0x06, 0x16, 0xa8, 0x84, 0xb2, 0xa4, 0xd6, 0xa4, 0x6f,
+    0xfa, 0x89, 0xb6, 0x02, 0xbf, 0xba, 0xd8, 0x0c, 0x12, 0x43, 0x71, 0x1f, 0x56, 0xeb, 0x60, 0x56,
+    0xf6, 0x37, 0xc8, 0xa0, 0x14, 0x1c, 0xc5, 0x40, 0x94, 0x26, 0x8b, 0x8c, 0x3c, 0x7d, 0xb9, 0x94,
+    0xb3, 0x5c, 0x0d, 0xcd, 0x6c, 0xb2, 0xab, 0xc2, 0xda, 0xfe, 0xe2, 0x52, 0x02, 0x3d, 0x2d, 0xea,
+    0x0c, 0xd6, 0xc3, 0x68, 0xbe, 0xa3, 0xe6, 0x41, 0x48, 0x86, 0xf6, 0xb1, 0xe5, 0x8b, 0x5b, 0xd7,
+    0xc7, 0x30, 0xb2, 0x68, 0xc4, 0xe3, 0xc1, 0xfb, 0x64, 0x24, 0xb9, 0x1f, 0xeb, 0xbd, 0xb8, 0x0c,
+    0x58, 0x6e, 0x2a, 0xe8, 0x36, 0x8c, 0x84, 0xd5, 0xd1, 0x09, 0x17, 0xbd, 0xa2, 0x56, 0x17, 0x89,
+    0xd4, 0x68, 0x73, 0x93, 0x34, 0x0e, 0x2e, 0x25, 0x4f, 0x56, 0x0e, 0xf6, 0x4b, 0x23, 0x58, 0xfc,
+    0xdc, 0x0f, 0xbf, 0xc6, 0x70, 0x09, 0x52, 0xe7, 0x08, 0xbf, 0xfc, 0xc6, 0x27, 0x50, 0x0c, 0x1f,
+    0x66, 0xe8, 0x1e, 0xa1, 0x7c, 0x09, 0x8d, 0x7a, 0x2e, 0x9b, 0x18, 0x80, 0x1b, 0x7a, 0xb4, 0xac,
+    0x71, 0x58, 0x7d, 0x34, 0x5d, 0xcc, 0x83, 0x09, 0xd5, 0xb6, 0x2a, 0x50, 0x42, 0x7a, 0xa6, 0xd0,
+    0x3d, 0xcb, 0x05, 0x99, 0x6c, 0x96, 0xba, 0x0c, 0x5d, 0x71, 0xe9, 0x21, 0x62, 0xc0, 0x16, 0xca,
+    0x84, 0x9f, 0xf3, 0x5f, 0x0d, 0x52, 0xc6, 0x5d, 0x05, 0x60, 0x5a, 0x47, 0xf3, 0xae, 0x91, 0x7a,
+    0xcd, 0x2d, 0xf9, 0x10, 0xef, 0xd2, 0x32, 0x66, 0x88, 0x59, 0x6e, 0xf6, 0x9b, 0x3b, 0xf5, 0xfe,
+    0x31, 0x54, 0xf7, 0xae, 0xb8, 0x80, 0xa0, 0xa7, 0x3c, 0xa0, 0x4d, 0x94, 0xc2, 0xce, 0x83, 0x17,
+    0xee, 0xb4, 0x3d, 0x5e, 0xff, 0x58, 0x83, 0xe3, 0x36, 0xf5, 0xf2, 0x49, 0xda, 0xac, 0xa4, 0x89,
+    0x92, 0x37, 0xbf, 0x26, 0x7e, 0x5c, 0x43, 0xab, 0x02, 0xea, 0x44, 0x16, 0x24, 0x03, 0x72, 0x3b,
+    0xe6, 0xaa, 0x69, 0x2c, 0x61, 0xbd, 0xae, 0x9e, 0xd4, 0x09, 0xd4, 0x63, 0xc4, 0xc9, 0x7c, 0x64,
+    0x30, 0x65, 0x77, 0xee, 0xf2, 0xbc, 0x75, 0x60, 0xb7, 0x57, 0x15, 0xcc, 0x9c, 0x7d, 0xc6, 0x7c,
+    0x86, 0x08, 0x2d, 0xb7, 0x51, 0xa8, 0x9c, 0x30, 0x34, 0x97, 0x62, 0xb0, 0x78, 0x23, 0x85, 0x87,
+    0x5c, 0xf1, 0xa3, 0xc6, 0x16, 0x6e, 0x0a, 0xe3, 0xc1, 0x2d, 0x37, 0x4e, 0x2d, 0x4f, 0x18, 0x46,
+    0xf3, 0x18, 0x74, 0x4b, 0xd8, 0x79, 0xb5, 0x87, 0x32, 0x9b, 0xf0, 0x18, 0x21, 0x7a, 0x6c, 0x0c,
+    0x77, 0x24, 0x1a, 0x48, 0x78, 0xe4, 0x35, 0xc0, 0x30, 0x79, 0xcb, 0x45, 0x12, 0x89, 0xc5, 0x77,
+    0x62, 0x06, 0x06, 0x9a, 0x2f, 0x8d, 0x65, 0xf8, 0x40, 0xe1, 0x44, 0x52, 0x87, 0xbe, 0xd8, 0x77,
+    0xab, 0xae, 0x24, 0xe2, 0x44, 0x35, 0x16, 0x8d, 0x55, 0x3c, 0xe4,
+];
+
+pub static USRPKEY_NON_AUTHBOUND: &[u8] = &[
+    0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1d, 0x44, 0x4b, 0x4d, 0x4b, 0x00, 0x00, 0x00, 0x00,
+    0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8a, 0xc1, 0x08, 0x13, 0x7c, 0x47, 0xba, 0x09,
+    0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00,
+    0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb0, 0xad, 0x01, 0x00, 0x01,
+    0x75, 0x15, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x60,
+    0x60, 0x8c, 0x31, 0x76, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x61, 0x34,
+    0x01, 0x01, 0xb9, 0x61, 0x34, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0xa1, 0x01, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0xc9, 0xcd, 0xcb, 0xca, 0xfa, 0x37, 0xe2,
+    0xc7, 0x56, 0x8c, 0x23, 0xf6, 0x7f, 0xd1, 0x8c, 0x01, 0xc1, 0x4f, 0x65, 0xd7, 0x1b, 0x10, 0xc5,
+    0x0a, 0x77, 0x13, 0xf2, 0x82, 0xde, 0x63, 0x68, 0x5f, 0xec, 0x2f, 0x95, 0x34, 0x65, 0x5d, 0x2f,
+    0x99, 0xfc, 0xed, 0x0d, 0x1b, 0xe9, 0xf4, 0x83, 0x38, 0x71, 0x83, 0x82, 0x64, 0x51, 0xab, 0x53,
+    0xb1, 0xfa, 0x73, 0x00, 0x20, 0x24, 0xdd, 0x1c, 0x13, 0x00, 0x01, 0x00, 0x00, 0x1c, 0x00, 0x00,
+    0x00, 0x20, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00,
+    0x00, 0x20, 0x00, 0x00, 0x00, 0xe5, 0xa5, 0x27, 0xb5, 0x66, 0x76, 0x6c, 0x74, 0x36, 0xd7, 0x2d,
+    0xad, 0x32, 0x49, 0xd4, 0xa5, 0xed, 0xb2, 0x9c, 0x4b, 0xbd, 0xb8, 0xe1, 0x79, 0x9f, 0x8a, 0x72,
+    0xc3, 0xdf, 0x8b, 0x99, 0x49, 0xa8, 0x5e, 0x10, 0x00, 0xd6, 0xa6, 0x58, 0x49, 0x5a, 0xa2, 0x71,
+    0xb3, 0x54, 0xd3, 0x69, 0xb7, 0xfe, 0x51, 0xc5, 0xe4, 0x94, 0xff, 0x10, 0xd7, 0x46, 0x01, 0x78,
+    0x43, 0x8c, 0x9c, 0xbe, 0x2f, 0x9a, 0x4b, 0x5b, 0x72, 0x07, 0x4d, 0x8f, 0x25, 0x50, 0x1e, 0xb2,
+    0x46, 0xf0, 0xee, 0x50, 0x73, 0x6a, 0x7b, 0xa3, 0xe9, 0xb1, 0x08, 0x81, 0x00, 0xdf, 0x0e, 0xc9,
+    0xc3, 0x2c, 0x13, 0x64, 0xa1,
+];
+pub static USRPKEY_NON_AUTHBOUND_CHR: &[u8] = &[
+    0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+    0x6d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x20,
+    0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10,
+    0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x10,
+    0x01, 0x00, 0x00, 0x00, 0x2d, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0xf7, 0x01, 0x00, 0x70,
+    0x01, 0xbe, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x02, 0x00, 0x30, 0xb0, 0xad, 0x01,
+    0x00, 0xc2, 0x02, 0x00, 0x30, 0x75, 0x15, 0x03, 0x00, 0xcf, 0x02, 0x00, 0x30, 0xb9, 0x61, 0x34,
+    0x01, 0xce, 0x02, 0x00, 0x30, 0xb9, 0x61, 0x34, 0x01, 0x30, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xbd, 0x02, 0x00,
+    0x60, 0x60, 0x60, 0x8c, 0x31, 0x76, 0x01, 0x00, 0x00, 0xf5, 0x01, 0x00, 0x30, 0x00, 0x00, 0x00,
+    0x00,
+];
+pub static USRCERT_NON_AUTHBOUND: &[u8] = &[
+    0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x97, 0x30, 0x82, 0x02, 0x93, 0x30, 0x82, 0x02, 0x39,
+    0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
+    0x3d, 0x04, 0x03, 0x02, 0x30, 0x29, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13,
+    0x10, 0x34, 0x34, 0x61, 0x38, 0x31, 0x65, 0x61, 0x65, 0x63, 0x35, 0x31, 0x64, 0x62, 0x30, 0x62,
+    0x31, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0c, 0x0c, 0x03, 0x54, 0x45, 0x45, 0x30,
+    0x20, 0x17, 0x0d, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a,
+    0x18, 0x0f, 0x32, 0x31, 0x30, 0x36, 0x30, 0x32, 0x30, 0x37, 0x30, 0x36, 0x32, 0x38, 0x31, 0x35,
+    0x5a, 0x30, 0x1f, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x14, 0x41, 0x6e,
+    0x64, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x4b,
+    0x65, 0x79, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
+    0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xa8, 0x5e, 0x10,
+    0x00, 0xd6, 0xa6, 0x58, 0x49, 0x5a, 0xa2, 0x71, 0xb3, 0x54, 0xd3, 0x69, 0xb7, 0xfe, 0x51, 0xc5,
+    0xe4, 0x94, 0xff, 0x10, 0xd7, 0x46, 0x01, 0x78, 0x43, 0x8c, 0x9c, 0xbe, 0x2f, 0x9a, 0x4b, 0x5b,
+    0x72, 0x07, 0x4d, 0x8f, 0x25, 0x50, 0x1e, 0xb2, 0x46, 0xf0, 0xee, 0x50, 0x73, 0x6a, 0x7b, 0xa3,
+    0xe9, 0xb1, 0x08, 0x81, 0x00, 0xdf, 0x0e, 0xc9, 0xc3, 0x2c, 0x13, 0x64, 0xa1, 0xa3, 0x82, 0x01,
+    0x58, 0x30, 0x82, 0x01, 0x54, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+    0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x82, 0x01, 0x40, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01,
+    0xd6, 0x79, 0x02, 0x01, 0x11, 0x04, 0x82, 0x01, 0x30, 0x30, 0x82, 0x01, 0x2c, 0x02, 0x01, 0x03,
+    0x0a, 0x01, 0x01, 0x02, 0x01, 0x04, 0x0a, 0x01, 0x01, 0x04, 0x08, 0x61, 0x73, 0x64, 0x66, 0x6a,
+    0x6b, 0x6c, 0x3b, 0x04, 0x00, 0x30, 0x6b, 0xbf, 0x85, 0x3d, 0x08, 0x02, 0x06, 0x01, 0x76, 0x31,
+    0x8c, 0x60, 0x60, 0xbf, 0x85, 0x45, 0x5b, 0x04, 0x59, 0x30, 0x57, 0x31, 0x31, 0x30, 0x2f, 0x04,
+    0x2a, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65,
+    0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x6a, 0x64, 0x61, 0x6e, 0x69, 0x73, 0x2e, 0x6b,
+    0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x74, 0x6f, 0x6f, 0x6c, 0x02, 0x01, 0x01, 0x31, 0x22,
+    0x04, 0x20, 0x30, 0xe0, 0x78, 0x45, 0xab, 0xd7, 0xc1, 0x74, 0x49, 0x01, 0x0f, 0xa7, 0x7f, 0x89,
+    0xde, 0x11, 0xa3, 0x8b, 0x3e, 0x31, 0x6b, 0xf1, 0x18, 0xb4, 0x58, 0x1b, 0xd7, 0xb3, 0x58, 0xa9,
+    0xc2, 0x81, 0x30, 0x81, 0xa4, 0xa1, 0x08, 0x31, 0x06, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03, 0xa2,
+    0x03, 0x02, 0x01, 0x03, 0xa3, 0x04, 0x02, 0x02, 0x01, 0x00, 0xa5, 0x05, 0x31, 0x03, 0x02, 0x01,
+    0x04, 0xaa, 0x03, 0x02, 0x01, 0x01, 0xbf, 0x83, 0x77, 0x02, 0x05, 0x00, 0xbf, 0x85, 0x3e, 0x03,
+    0x02, 0x01, 0x00, 0xbf, 0x85, 0x40, 0x4c, 0x30, 0x4a, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x0a, 0x01,
+    0x02, 0x04, 0x20, 0xe7, 0xad, 0x3c, 0x13, 0xc2, 0x73, 0x41, 0x60, 0xd7, 0x1a, 0x7c, 0x00, 0x5e,
+    0x14, 0xd8, 0xae, 0x06, 0x5d, 0x22, 0xd0, 0xb5, 0xf5, 0x6a, 0xba, 0x1f, 0x82, 0xa7, 0x8c, 0x17,
+    0x2c, 0xfd, 0x0f, 0xbf, 0x85, 0x41, 0x05, 0x02, 0x03, 0x01, 0xad, 0xb0, 0xbf, 0x85, 0x42, 0x05,
+    0x02, 0x03, 0x03, 0x15, 0x75, 0xbf, 0x85, 0x4e, 0x06, 0x02, 0x04, 0x01, 0x34, 0x61, 0xb9, 0xbf,
+    0x85, 0x4f, 0x06, 0x02, 0x04, 0x01, 0x34, 0x61, 0xb9, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48,
+    0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x20, 0x3f, 0x12, 0x76, 0x4c,
+    0x85, 0xfd, 0xc9, 0x68, 0x0d, 0x66, 0x0b, 0x60, 0x3d, 0xff, 0x7c, 0x8b, 0x11, 0x9c, 0x26, 0xef,
+    0xdb, 0x4a, 0xc3, 0x37, 0x40, 0x06, 0xa9, 0x16, 0xc7, 0x99, 0x85, 0x89, 0x02, 0x21, 0x00, 0xc7,
+    0x02, 0xf3, 0x21, 0x60, 0x17, 0x05, 0x7e, 0x36, 0x33, 0x21, 0x0c, 0x1d, 0x27, 0xc3, 0x8f, 0xd6,
+    0xd8, 0xd5, 0xd1, 0x64, 0x4c, 0x05, 0xdd, 0x13, 0x0e, 0xa4, 0xf3, 0x38, 0xbf, 0x18, 0xd5,
+];
+
+pub static CACERT_NON_AUTHBOUND: &[u8] = &[
+    0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x63, 0x30, 0x82, 0x02, 0x26, 0x30, 0x82, 0x01, 0xab,
+    0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0a, 0x05, 0x84, 0x20, 0x26, 0x90, 0x76, 0x23, 0x58, 0x71,
+    0x77, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x29, 0x31,
+    0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x34, 0x64, 0x37, 0x34, 0x61, 0x30,
+    0x65, 0x30, 0x31, 0x61, 0x61, 0x66, 0x33, 0x64, 0x64, 0x66, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03,
+    0x55, 0x04, 0x0c, 0x0c, 0x03, 0x54, 0x45, 0x45, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x33,
+    0x32, 0x31, 0x32, 0x31, 0x32, 0x35, 0x31, 0x33, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x30, 0x33, 0x31,
+    0x38, 0x32, 0x31, 0x32, 0x35, 0x31, 0x33, 0x5a, 0x30, 0x29, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03,
+    0x55, 0x04, 0x05, 0x13, 0x10, 0x34, 0x34, 0x61, 0x38, 0x31, 0x65, 0x61, 0x65, 0x63, 0x35, 0x31,
+    0x64, 0x62, 0x30, 0x62, 0x31, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0c, 0x0c, 0x03,
+    0x54, 0x45, 0x45, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
+    0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xfa, 0x35,
+    0x8f, 0xb0, 0x31, 0xd6, 0x30, 0x88, 0xde, 0xb0, 0x29, 0xcd, 0x6c, 0x7d, 0x4e, 0xa9, 0xce, 0x6e,
+    0x9d, 0x7a, 0xac, 0x97, 0x92, 0xc2, 0x45, 0xb5, 0xe2, 0xd0, 0xc1, 0x52, 0xa8, 0x50, 0x25, 0xd7,
+    0x89, 0x58, 0x7b, 0x04, 0xb6, 0x66, 0x93, 0x2a, 0x26, 0x5d, 0x3a, 0xb1, 0x5b, 0x77, 0x30, 0xbf,
+    0x95, 0xaa, 0x8b, 0x43, 0xc3, 0xbf, 0x43, 0xb7, 0xee, 0xac, 0x73, 0xdc, 0x03, 0x6a, 0xa3, 0x81,
+    0xba, 0x30, 0x81, 0xb7, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x19,
+    0x9f, 0x87, 0x8b, 0x56, 0xf4, 0x99, 0x3a, 0x69, 0x96, 0x9b, 0x8d, 0x9e, 0x64, 0xaa, 0x56, 0xb4,
+    0x7f, 0x8b, 0x4d, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+    0xa9, 0xb5, 0xf4, 0x29, 0xc9, 0x1a, 0x58, 0xbd, 0x2f, 0x98, 0x2d, 0x67, 0x73, 0x31, 0x06, 0x87,
+    0xe0, 0xdf, 0xcd, 0x62, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05,
+    0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+    0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x54, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x4d, 0x30, 0x4b,
+    0x30, 0x49, 0xa0, 0x47, 0xa0, 0x45, 0x86, 0x43, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
+    0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70,
+    0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69,
+    0x6f, 0x6e, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x30, 0x35, 0x38, 0x34, 0x32, 0x30, 0x32, 0x36, 0x39,
+    0x30, 0x37, 0x36, 0x32, 0x33, 0x35, 0x38, 0x37, 0x31, 0x37, 0x37, 0x30, 0x0a, 0x06, 0x08, 0x2a,
+    0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x69, 0x00, 0x30, 0x66, 0x02, 0x31, 0x00, 0xe3,
+    0x35, 0xc6, 0xa8, 0xb2, 0x75, 0x9c, 0x56, 0x7b, 0x6e, 0x61, 0x80, 0x65, 0x2c, 0x06, 0x88, 0xdd,
+    0xb9, 0x68, 0x4d, 0x3c, 0x68, 0x49, 0x66, 0x01, 0x4e, 0x30, 0x1d, 0xf3, 0xec, 0xa5, 0x51, 0x5c,
+    0xbf, 0xe7, 0x83, 0x33, 0xbd, 0x14, 0xee, 0x23, 0xf0, 0xcf, 0xb1, 0x37, 0x1c, 0x27, 0x78, 0x02,
+    0x31, 0x00, 0x94, 0xcb, 0x08, 0x3d, 0x2d, 0x3e, 0x69, 0x54, 0x5f, 0x63, 0xe3, 0xe4, 0x74, 0x72,
+    0xe2, 0xff, 0x8b, 0x26, 0xd2, 0x86, 0xc0, 0x97, 0x32, 0x40, 0xdd, 0x7c, 0x1f, 0x50, 0x60, 0x57,
+    0xcf, 0x2e, 0x23, 0xf3, 0x33, 0xe4, 0xfb, 0x6f, 0x5b, 0x7c, 0xc6, 0x31, 0x85, 0xae, 0xe0, 0x4e,
+    0x44, 0xa9, 0x30, 0x82, 0x03, 0xd1, 0x30, 0x82, 0x01, 0xb9, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02,
+    0x0a, 0x03, 0x88, 0x26, 0x67, 0x60, 0x65, 0x89, 0x96, 0x85, 0x7f, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+    0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x1b, 0x31, 0x19, 0x30, 0x17,
+    0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x66, 0x39, 0x32, 0x30, 0x30, 0x39, 0x65, 0x38, 0x35,
+    0x33, 0x62, 0x36, 0x62, 0x30, 0x34, 0x35, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x33, 0x32,
+    0x31, 0x32, 0x31, 0x31, 0x34, 0x31, 0x34, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x30, 0x33, 0x31, 0x38,
+    0x32, 0x31, 0x31, 0x34, 0x31, 0x34, 0x5a, 0x30, 0x29, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55,
+    0x04, 0x05, 0x13, 0x10, 0x34, 0x64, 0x37, 0x34, 0x61, 0x30, 0x65, 0x30, 0x31, 0x61, 0x61, 0x66,
+    0x33, 0x64, 0x64, 0x66, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0c, 0x0c, 0x03, 0x54,
+    0x45, 0x45, 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
+    0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04, 0xd5, 0xf5, 0x0e, 0xe2, 0x8d, 0xf3,
+    0x33, 0x4a, 0x6a, 0x77, 0x90, 0x9c, 0xc2, 0x25, 0xc8, 0x8a, 0x32, 0xae, 0x3b, 0xb4, 0x9c, 0x4a,
+    0x95, 0x22, 0x0c, 0xba, 0x0a, 0x76, 0xca, 0xcb, 0x24, 0x0c, 0x84, 0x3a, 0x83, 0x76, 0x04, 0x23,
+    0x31, 0x3a, 0xa0, 0x82, 0x80, 0x26, 0x65, 0xfd, 0x2f, 0x44, 0xf4, 0x96, 0xd8, 0xb7, 0xdc, 0xac,
+    0x55, 0x34, 0x74, 0x41, 0x0d, 0x0d, 0x7f, 0xbd, 0xe3, 0xf4, 0x28, 0xdf, 0x74, 0x4a, 0x17, 0x4d,
+    0xe7, 0xb2, 0x9b, 0x2b, 0x24, 0xc0, 0x9e, 0x56, 0x00, 0x52, 0xbb, 0x75, 0xb0, 0xd5, 0x6a, 0x41,
+    0x16, 0x08, 0xce, 0x32, 0xdb, 0x8f, 0x8b, 0x20, 0x73, 0x72, 0xa3, 0x81, 0xb6, 0x30, 0x81, 0xb3,
+    0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xa9, 0xb5, 0xf4, 0x29, 0xc9,
+    0x1a, 0x58, 0xbd, 0x2f, 0x98, 0x2d, 0x67, 0x73, 0x31, 0x06, 0x87, 0xe0, 0xdf, 0xcd, 0x62, 0x30,
+    0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x36, 0x61, 0xe1, 0x00,
+    0x7c, 0x88, 0x05, 0x09, 0x51, 0x8b, 0x44, 0x6c, 0x47, 0xff, 0x1a, 0x4c, 0xc9, 0xea, 0x4f, 0x12,
+    0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01,
+    0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02,
+    0x04, 0x30, 0x50, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x49, 0x30, 0x47, 0x30, 0x45, 0xa0, 0x43,
+    0xa0, 0x41, 0x86, 0x3f, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x61, 0x6e, 0x64, 0x72,
+    0x6f, 0x69, 0x64, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x63,
+    0x6f, 0x6d, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x63,
+    0x72, 0x6c, 0x2f, 0x45, 0x38, 0x46, 0x41, 0x31, 0x39, 0x36, 0x33, 0x31, 0x34, 0x44, 0x32, 0x46,
+    0x41, 0x31, 0x38, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+    0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x32, 0xce, 0x04, 0xcc, 0x4d, 0x82, 0xad, 0x1d, 0xde,
+    0xa5, 0xcf, 0xe2, 0x1a, 0xa3, 0x79, 0xf7, 0xed, 0x88, 0x1e, 0x0e, 0x67, 0x8e, 0xfc, 0xbe, 0x7b,
+    0x04, 0xb7, 0x26, 0x59, 0xca, 0x95, 0x47, 0x8a, 0x10, 0x3f, 0xe5, 0x14, 0x19, 0xec, 0xd4, 0xdb,
+    0x33, 0xc3, 0xa1, 0x51, 0xf5, 0x06, 0x5e, 0x30, 0x66, 0x1f, 0xd2, 0x58, 0x2f, 0x14, 0x03, 0x7b,
+    0x35, 0x83, 0x86, 0x46, 0xdc, 0xee, 0x04, 0x30, 0xa1, 0x0f, 0xc4, 0x16, 0xc9, 0x8e, 0x63, 0xd0,
+    0xda, 0x5c, 0xb0, 0xf7, 0x3e, 0x21, 0xb6, 0xa5, 0x04, 0x07, 0x5a, 0x01, 0x8c, 0x31, 0x1f, 0x3e,
+    0x3a, 0xaf, 0x8d, 0x31, 0x3e, 0xb6, 0x12, 0x14, 0xf0, 0x0d, 0x2c, 0xcc, 0x6c, 0xb8, 0x7a, 0xbf,
+    0xd2, 0x6b, 0x5f, 0x27, 0xb0, 0xff, 0xc0, 0xaa, 0xde, 0xde, 0xf6, 0x31, 0x6d, 0xf3, 0x95, 0xc2,
+    0xd4, 0x90, 0xdc, 0x82, 0x4f, 0x24, 0x0d, 0x85, 0xf2, 0xbb, 0xc4, 0x58, 0xc9, 0xfa, 0xdd, 0x96,
+    0x41, 0x2b, 0x1f, 0x4c, 0x10, 0x1c, 0x9a, 0x57, 0x55, 0x0f, 0x62, 0xfc, 0x8d, 0xa2, 0xca, 0x84,
+    0x7b, 0x16, 0x60, 0xe8, 0x62, 0xce, 0x92, 0x85, 0x13, 0xf0, 0x63, 0x83, 0xd8, 0x5b, 0xa8, 0x74,
+    0x78, 0xb5, 0x28, 0xdb, 0x6c, 0xc9, 0x6e, 0x85, 0x85, 0x52, 0x3f, 0xd8, 0x67, 0xae, 0xf4, 0x09,
+    0xbe, 0xcf, 0x8c, 0x7f, 0x72, 0xb2, 0xc8, 0x93, 0xc6, 0xd2, 0xf3, 0x38, 0x74, 0x71, 0x22, 0xd6,
+    0x92, 0x76, 0xb1, 0xae, 0x14, 0x5a, 0x09, 0xd8, 0xaf, 0x1d, 0xaf, 0x48, 0x22, 0x5c, 0x30, 0x85,
+    0x8e, 0xc2, 0xfe, 0x61, 0xaf, 0xc3, 0xd2, 0x4c, 0x92, 0x53, 0xa4, 0x75, 0x1f, 0x78, 0xea, 0xfc,
+    0xfa, 0xc4, 0xca, 0x4e, 0x67, 0x68, 0x1f, 0x7d, 0xb2, 0x5e, 0xea, 0x8a, 0xb1, 0xcc, 0xb6, 0x92,
+    0x64, 0xf8, 0x82, 0xc0, 0x8b, 0xdc, 0x24, 0xe8, 0x57, 0x20, 0x33, 0x6d, 0x17, 0x33, 0x0d, 0xcb,
+    0x70, 0x02, 0x8b, 0xe5, 0xe3, 0x7d, 0x2c, 0x98, 0x32, 0x00, 0x20, 0xb4, 0xbd, 0xee, 0x89, 0xaa,
+    0x66, 0x13, 0x34, 0x9d, 0x9c, 0x8f, 0xde, 0x16, 0x09, 0x91, 0x49, 0x80, 0x50, 0x57, 0x39, 0xae,
+    0x35, 0x01, 0xe2, 0x25, 0x8e, 0x17, 0x08, 0xe0, 0xf0, 0x77, 0x98, 0x9d, 0x0a, 0x4f, 0xd2, 0x76,
+    0xda, 0xc4, 0x51, 0x45, 0x32, 0x8b, 0xe1, 0xab, 0xee, 0x10, 0x16, 0xf6, 0x95, 0x7d, 0x32, 0x76,
+    0xb2, 0xb5, 0x19, 0x67, 0x73, 0xfe, 0xc0, 0xc6, 0xa9, 0xd2, 0xa9, 0x23, 0xf0, 0x2b, 0xfc, 0xb1,
+    0xb6, 0xec, 0x3e, 0x11, 0x60, 0xa4, 0x22, 0xc7, 0xff, 0x25, 0xc3, 0xed, 0x6c, 0x6b, 0x79, 0x02,
+    0x3d, 0x5d, 0x62, 0x36, 0xd9, 0x32, 0xe4, 0x6e, 0x47, 0x67, 0x85, 0x8b, 0x23, 0x0a, 0xd5, 0x1e,
+    0xd0, 0xf4, 0x17, 0x1d, 0xcc, 0x3f, 0x5f, 0xda, 0x12, 0xe2, 0x35, 0x25, 0x52, 0xc2, 0xd6, 0x94,
+    0x3e, 0x83, 0x60, 0x55, 0xf8, 0x8d, 0x54, 0xf5, 0x47, 0x6f, 0x38, 0x03, 0x3b, 0xd7, 0x9a, 0x94,
+    0x8a, 0x3b, 0x9f, 0x92, 0x69, 0x0f, 0xcd, 0xb8, 0xf4, 0x62, 0x78, 0x22, 0x47, 0xe0, 0xae, 0xed,
+    0xfd, 0xf6, 0xe4, 0xc5, 0x8c, 0x0e, 0xb5, 0x18, 0xb1, 0x46, 0x3a, 0x6f, 0xbd, 0xde, 0x50, 0x3f,
+    0x1c, 0x35, 0x28, 0xf9, 0xed, 0x1e, 0xe8, 0x15, 0x31, 0xa9, 0xf7, 0xb1, 0x9d, 0xe1, 0x34, 0x81,
+    0x20, 0x1f, 0x22, 0xd4, 0xb7, 0xc6, 0x59, 0x8b, 0x90, 0x98, 0xdf, 0xa6, 0xb9, 0xa8, 0x8e, 0x6c,
+    0x15, 0x55, 0x5c, 0x41, 0x96, 0x82, 0x0d, 0xa9, 0x5f, 0xa9, 0xf3, 0x77, 0x1d, 0xee, 0x6b, 0x4c,
+    0x94, 0xc6, 0xc6, 0x9b, 0x78, 0x5b, 0x03, 0xbd, 0xa9, 0x87, 0xdd, 0x24, 0x04, 0x70, 0xce, 0x6c,
+    0x52, 0xe6, 0x21, 0x63, 0x6d, 0x28, 0x6c, 0x30, 0x82, 0x05, 0x60, 0x30, 0x82, 0x03, 0x48, 0xa0,
+    0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0xe8, 0xfa, 0x19, 0x63, 0x14, 0xd2, 0xfa, 0x18, 0x30,
+    0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x1b,
+    0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x66, 0x39, 0x32, 0x30, 0x30,
+    0x39, 0x65, 0x38, 0x35, 0x33, 0x62, 0x36, 0x62, 0x30, 0x34, 0x35, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+    0x36, 0x30, 0x35, 0x32, 0x36, 0x31, 0x36, 0x32, 0x38, 0x35, 0x32, 0x5a, 0x17, 0x0d, 0x32, 0x36,
+    0x30, 0x35, 0x32, 0x34, 0x31, 0x36, 0x32, 0x38, 0x35, 0x32, 0x5a, 0x30, 0x1b, 0x31, 0x19, 0x30,
+    0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x66, 0x39, 0x32, 0x30, 0x30, 0x39, 0x65, 0x38,
+    0x35, 0x33, 0x62, 0x36, 0x62, 0x30, 0x34, 0x35, 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09,
+    0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00,
+    0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xaf, 0xb6, 0xc7, 0x82, 0x2b, 0xb1, 0xa7,
+    0x01, 0xec, 0x2b, 0xb4, 0x2e, 0x8b, 0xcc, 0x54, 0x16, 0x63, 0xab, 0xef, 0x98, 0x2f, 0x32, 0xc7,
+    0x7f, 0x75, 0x31, 0x03, 0x0c, 0x97, 0x52, 0x4b, 0x1b, 0x5f, 0xe8, 0x09, 0xfb, 0xc7, 0x2a, 0xa9,
+    0x45, 0x1f, 0x74, 0x3c, 0xbd, 0x9a, 0x6f, 0x13, 0x35, 0x74, 0x4a, 0xa5, 0x5e, 0x77, 0xf6, 0xb6,
+    0xac, 0x35, 0x35, 0xee, 0x17, 0xc2, 0x5e, 0x63, 0x95, 0x17, 0xdd, 0x9c, 0x92, 0xe6, 0x37, 0x4a,
+    0x53, 0xcb, 0xfe, 0x25, 0x8f, 0x8f, 0xfb, 0xb6, 0xfd, 0x12, 0x93, 0x78, 0xa2, 0x2a, 0x4c, 0xa9,
+    0x9c, 0x45, 0x2d, 0x47, 0xa5, 0x9f, 0x32, 0x01, 0xf4, 0x41, 0x97, 0xca, 0x1c, 0xcd, 0x7e, 0x76,
+    0x2f, 0xb2, 0xf5, 0x31, 0x51, 0xb6, 0xfe, 0xb2, 0xff, 0xfd, 0x2b, 0x6f, 0xe4, 0xfe, 0x5b, 0xc6,
+    0xbd, 0x9e, 0xc3, 0x4b, 0xfe, 0x08, 0x23, 0x9d, 0xaa, 0xfc, 0xeb, 0x8e, 0xb5, 0xa8, 0xed, 0x2b,
+    0x3a, 0xcd, 0x9c, 0x5e, 0x3a, 0x77, 0x90, 0xe1, 0xb5, 0x14, 0x42, 0x79, 0x31, 0x59, 0x85, 0x98,
+    0x11, 0xad, 0x9e, 0xb2, 0xa9, 0x6b, 0xbd, 0xd7, 0xa5, 0x7c, 0x93, 0xa9, 0x1c, 0x41, 0xfc, 0xcd,
+    0x27, 0xd6, 0x7f, 0xd6, 0xf6, 0x71, 0xaa, 0x0b, 0x81, 0x52, 0x61, 0xad, 0x38, 0x4f, 0xa3, 0x79,
+    0x44, 0x86, 0x46, 0x04, 0xdd, 0xb3, 0xd8, 0xc4, 0xf9, 0x20, 0xa1, 0x9b, 0x16, 0x56, 0xc2, 0xf1,
+    0x4a, 0xd6, 0xd0, 0x3c, 0x56, 0xec, 0x06, 0x08, 0x99, 0x04, 0x1c, 0x1e, 0xd1, 0xa5, 0xfe, 0x6d,
+    0x34, 0x40, 0xb5, 0x56, 0xba, 0xd1, 0xd0, 0xa1, 0x52, 0x58, 0x9c, 0x53, 0xe5, 0x5d, 0x37, 0x07,
+    0x62, 0xf0, 0x12, 0x2e, 0xef, 0x91, 0x86, 0x1b, 0x1b, 0x0e, 0x6c, 0x4c, 0x80, 0x92, 0x74, 0x99,
+    0xc0, 0xe9, 0xbe, 0xc0, 0xb8, 0x3e, 0x3b, 0xc1, 0xf9, 0x3c, 0x72, 0xc0, 0x49, 0x60, 0x4b, 0xbd,
+    0x2f, 0x13, 0x45, 0xe6, 0x2c, 0x3f, 0x8e, 0x26, 0xdb, 0xec, 0x06, 0xc9, 0x47, 0x66, 0xf3, 0xc1,
+    0x28, 0x23, 0x9d, 0x4f, 0x43, 0x12, 0xfa, 0xd8, 0x12, 0x38, 0x87, 0xe0, 0x6b, 0xec, 0xf5, 0x67,
+    0x58, 0x3b, 0xf8, 0x35, 0x5a, 0x81, 0xfe, 0xea, 0xba, 0xf9, 0x9a, 0x83, 0xc8, 0xdf, 0x3e, 0x2a,
+    0x32, 0x2a, 0xfc, 0x67, 0x2b, 0xf1, 0x20, 0xb1, 0x35, 0x15, 0x8b, 0x68, 0x21, 0xce, 0xaf, 0x30,
+    0x9b, 0x6e, 0xee, 0x77, 0xf9, 0x88, 0x33, 0xb0, 0x18, 0xda, 0xa1, 0x0e, 0x45, 0x1f, 0x06, 0xa3,
+    0x74, 0xd5, 0x07, 0x81, 0xf3, 0x59, 0x08, 0x29, 0x66, 0xbb, 0x77, 0x8b, 0x93, 0x08, 0x94, 0x26,
+    0x98, 0xe7, 0x4e, 0x0b, 0xcd, 0x24, 0x62, 0x8a, 0x01, 0xc2, 0xcc, 0x03, 0xe5, 0x1f, 0x0b, 0x3e,
+    0x5b, 0x4a, 0xc1, 0xe4, 0xdf, 0x9e, 0xaf, 0x9f, 0xf6, 0xa4, 0x92, 0xa7, 0x7c, 0x14, 0x83, 0x88,
+    0x28, 0x85, 0x01, 0x5b, 0x42, 0x2c, 0xe6, 0x7b, 0x80, 0xb8, 0x8c, 0x9b, 0x48, 0xe1, 0x3b, 0x60,
+    0x7a, 0xb5, 0x45, 0xc7, 0x23, 0xff, 0x8c, 0x44, 0xf8, 0xf2, 0xd3, 0x68, 0xb9, 0xf6, 0x52, 0x0d,
+    0x31, 0x14, 0x5e, 0xbf, 0x9e, 0x86, 0x2a, 0xd7, 0x1d, 0xf6, 0xa3, 0xbf, 0xd2, 0x45, 0x09, 0x59,
+    0xd6, 0x53, 0x74, 0x0d, 0x97, 0xa1, 0x2f, 0x36, 0x8b, 0x13, 0xef, 0x66, 0xd5, 0xd0, 0xa5, 0x4a,
+    0x6e, 0x2f, 0x5d, 0x9a, 0x6f, 0xef, 0x44, 0x68, 0x32, 0xbc, 0x67, 0x84, 0x47, 0x25, 0x86, 0x1f,
+    0x09, 0x3d, 0xd0, 0xe6, 0xf3, 0x40, 0x5d, 0xa8, 0x96, 0x43, 0xef, 0x0f, 0x4d, 0x69, 0xb6, 0x42,
+    0x00, 0x51, 0xfd, 0xb9, 0x30, 0x49, 0x67, 0x3e, 0x36, 0x95, 0x05, 0x80, 0xd3, 0xcd, 0xf4, 0xfb,
+    0xd0, 0x8b, 0xc5, 0x84, 0x83, 0x95, 0x26, 0x00, 0x63, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81,
+    0xa6, 0x30, 0x81, 0xa3, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x36,
+    0x61, 0xe1, 0x00, 0x7c, 0x88, 0x05, 0x09, 0x51, 0x8b, 0x44, 0x6c, 0x47, 0xff, 0x1a, 0x4c, 0xc9,
+    0xea, 0x4f, 0x12, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+    0x36, 0x61, 0xe1, 0x00, 0x7c, 0x88, 0x05, 0x09, 0x51, 0x8b, 0x44, 0x6c, 0x47, 0xff, 0x1a, 0x4c,
+    0xc9, 0xea, 0x4f, 0x12, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05,
+    0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+    0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x40, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x39, 0x30, 0x37,
+    0x30, 0x35, 0xa0, 0x33, 0xa0, 0x31, 0x86, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
+    0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70,
+    0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69,
+    0x6f, 0x6e, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+    0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x20, 0xc8, 0xc3, 0x8d, 0x4b,
+    0xdc, 0xa9, 0x57, 0x1b, 0x46, 0x8c, 0x89, 0x2f, 0xff, 0x72, 0xaa, 0xc6, 0xf8, 0x44, 0xa1, 0x1d,
+    0x41, 0xa8, 0xf0, 0x73, 0x6c, 0xc3, 0x7d, 0x16, 0xd6, 0x42, 0x6d, 0x8e, 0x7e, 0x94, 0x07, 0x04,
+    0x4c, 0xea, 0x39, 0xe6, 0x8b, 0x07, 0xc1, 0x3d, 0xbf, 0x15, 0x03, 0xdd, 0x5c, 0x85, 0xbd, 0xaf,
+    0xb2, 0xc0, 0x2d, 0x5f, 0x6c, 0xdb, 0x4e, 0xfa, 0x81, 0x27, 0xdf, 0x8b, 0x04, 0xf1, 0x82, 0x77,
+    0x0f, 0xc4, 0xe7, 0x74, 0x5b, 0x7f, 0xce, 0xaa, 0x87, 0x12, 0x9a, 0x88, 0x01, 0xce, 0x8e, 0x9b,
+    0xc0, 0xcb, 0x96, 0x37, 0x9b, 0x4d, 0x26, 0xa8, 0x2d, 0x30, 0xfd, 0x9c, 0x2f, 0x8e, 0xed, 0x6d,
+    0xc1, 0xbe, 0x2f, 0x84, 0xb6, 0x89, 0xe4, 0xd9, 0x14, 0x25, 0x8b, 0x14, 0x4b, 0xba, 0xe6, 0x24,
+    0xa1, 0xc7, 0x06, 0x71, 0x13, 0x2e, 0x2f, 0x06, 0x16, 0xa8, 0x84, 0xb2, 0xa4, 0xd6, 0xa4, 0x6f,
+    0xfa, 0x89, 0xb6, 0x02, 0xbf, 0xba, 0xd8, 0x0c, 0x12, 0x43, 0x71, 0x1f, 0x56, 0xeb, 0x60, 0x56,
+    0xf6, 0x37, 0xc8, 0xa0, 0x14, 0x1c, 0xc5, 0x40, 0x94, 0x26, 0x8b, 0x8c, 0x3c, 0x7d, 0xb9, 0x94,
+    0xb3, 0x5c, 0x0d, 0xcd, 0x6c, 0xb2, 0xab, 0xc2, 0xda, 0xfe, 0xe2, 0x52, 0x02, 0x3d, 0x2d, 0xea,
+    0x0c, 0xd6, 0xc3, 0x68, 0xbe, 0xa3, 0xe6, 0x41, 0x48, 0x86, 0xf6, 0xb1, 0xe5, 0x8b, 0x5b, 0xd7,
+    0xc7, 0x30, 0xb2, 0x68, 0xc4, 0xe3, 0xc1, 0xfb, 0x64, 0x24, 0xb9, 0x1f, 0xeb, 0xbd, 0xb8, 0x0c,
+    0x58, 0x6e, 0x2a, 0xe8, 0x36, 0x8c, 0x84, 0xd5, 0xd1, 0x09, 0x17, 0xbd, 0xa2, 0x56, 0x17, 0x89,
+    0xd4, 0x68, 0x73, 0x93, 0x34, 0x0e, 0x2e, 0x25, 0x4f, 0x56, 0x0e, 0xf6, 0x4b, 0x23, 0x58, 0xfc,
+    0xdc, 0x0f, 0xbf, 0xc6, 0x70, 0x09, 0x52, 0xe7, 0x08, 0xbf, 0xfc, 0xc6, 0x27, 0x50, 0x0c, 0x1f,
+    0x66, 0xe8, 0x1e, 0xa1, 0x7c, 0x09, 0x8d, 0x7a, 0x2e, 0x9b, 0x18, 0x80, 0x1b, 0x7a, 0xb4, 0xac,
+    0x71, 0x58, 0x7d, 0x34, 0x5d, 0xcc, 0x83, 0x09, 0xd5, 0xb6, 0x2a, 0x50, 0x42, 0x7a, 0xa6, 0xd0,
+    0x3d, 0xcb, 0x05, 0x99, 0x6c, 0x96, 0xba, 0x0c, 0x5d, 0x71, 0xe9, 0x21, 0x62, 0xc0, 0x16, 0xca,
+    0x84, 0x9f, 0xf3, 0x5f, 0x0d, 0x52, 0xc6, 0x5d, 0x05, 0x60, 0x5a, 0x47, 0xf3, 0xae, 0x91, 0x7a,
+    0xcd, 0x2d, 0xf9, 0x10, 0xef, 0xd2, 0x32, 0x66, 0x88, 0x59, 0x6e, 0xf6, 0x9b, 0x3b, 0xf5, 0xfe,
+    0x31, 0x54, 0xf7, 0xae, 0xb8, 0x80, 0xa0, 0xa7, 0x3c, 0xa0, 0x4d, 0x94, 0xc2, 0xce, 0x83, 0x17,
+    0xee, 0xb4, 0x3d, 0x5e, 0xff, 0x58, 0x83, 0xe3, 0x36, 0xf5, 0xf2, 0x49, 0xda, 0xac, 0xa4, 0x89,
+    0x92, 0x37, 0xbf, 0x26, 0x7e, 0x5c, 0x43, 0xab, 0x02, 0xea, 0x44, 0x16, 0x24, 0x03, 0x72, 0x3b,
+    0xe6, 0xaa, 0x69, 0x2c, 0x61, 0xbd, 0xae, 0x9e, 0xd4, 0x09, 0xd4, 0x63, 0xc4, 0xc9, 0x7c, 0x64,
+    0x30, 0x65, 0x77, 0xee, 0xf2, 0xbc, 0x75, 0x60, 0xb7, 0x57, 0x15, 0xcc, 0x9c, 0x7d, 0xc6, 0x7c,
+    0x86, 0x08, 0x2d, 0xb7, 0x51, 0xa8, 0x9c, 0x30, 0x34, 0x97, 0x62, 0xb0, 0x78, 0x23, 0x85, 0x87,
+    0x5c, 0xf1, 0xa3, 0xc6, 0x16, 0x6e, 0x0a, 0xe3, 0xc1, 0x2d, 0x37, 0x4e, 0x2d, 0x4f, 0x18, 0x46,
+    0xf3, 0x18, 0x74, 0x4b, 0xd8, 0x79, 0xb5, 0x87, 0x32, 0x9b, 0xf0, 0x18, 0x21, 0x7a, 0x6c, 0x0c,
+    0x77, 0x24, 0x1a, 0x48, 0x78, 0xe4, 0x35, 0xc0, 0x30, 0x79, 0xcb, 0x45, 0x12, 0x89, 0xc5, 0x77,
+    0x62, 0x06, 0x06, 0x9a, 0x2f, 0x8d, 0x65, 0xf8, 0x40, 0xe1, 0x44, 0x52, 0x87, 0xbe, 0xd8, 0x77,
+    0xab, 0xae, 0x24, 0xe2, 0x44, 0x35, 0x16, 0x8d, 0x55, 0x3c, 0xe4,
+];
+
+pub static _DECRYPTED_USRPKEY_AUTHBOUND: &[u8] = &[
+    0x44, 0x4b, 0x4d, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+    0xc6, 0x15, 0x3a, 0x08, 0x1e, 0x43, 0xba, 0x7a, 0x0f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+    0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x02, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0xf0, 0x7e, 0x7d, 0xb4, 0xc6, 0xd7, 0x25, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x01, 0xb0, 0xad, 0x01, 0x00, 0x01, 0x75, 0x15, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x9d, 0x8b, 0x31, 0x76, 0x01, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x61, 0x34, 0x01, 0x01, 0xb9, 0x61, 0x34, 0x01, 0x01, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa1, 0x01, 0x00, 0x00, 0x7c, 0x00, 0x00,
+    0x00, 0x37, 0x91, 0x69, 0xeb, 0x25, 0x1e, 0xcf, 0x6c, 0xab, 0xe1, 0x91, 0xdd, 0x2e, 0xf8, 0x86,
+    0x20, 0x97, 0x54, 0x23, 0x0b, 0x0a, 0x0c, 0x35, 0xcb, 0xcd, 0x9c, 0x60, 0x44, 0x29, 0xb3, 0xe9,
+    0x84, 0xa9, 0x91, 0xd9, 0x71, 0x62, 0x45, 0x7c, 0x2b, 0x73, 0xf5, 0x10, 0x6b, 0xc5, 0x35, 0xa7,
+    0x36, 0xcb, 0x65, 0x0d, 0x0d, 0xa9, 0x3a, 0x17, 0xd1, 0x83, 0x08, 0x22, 0xe4, 0x3a, 0xa1, 0x11,
+    0xac, 0x00, 0x01, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00,
+    0x00, 0x20, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x4d, 0xac, 0x97,
+    0x2a, 0xc9, 0xfc, 0x59, 0x46, 0x9a, 0x5d, 0x9f, 0x55, 0x20, 0x91, 0xa3, 0x15, 0x37, 0xbb, 0x86,
+    0xb4, 0x03, 0xcb, 0x78, 0x48, 0x08, 0x0a, 0xad, 0x44, 0x35, 0x14, 0x0d, 0x6c, 0x20, 0x16, 0x85,
+    0xe6, 0x7f, 0xf1, 0x0e, 0x99, 0x1b, 0x3a, 0xc6, 0xc2, 0x83, 0x0a, 0x1d, 0xa4, 0xf1, 0x92, 0x76,
+    0x88, 0x4b, 0x6a, 0xcd, 0xb2, 0x8e, 0xf1, 0x50, 0x58, 0xd2, 0x69, 0xde, 0x57, 0x9c, 0x9c, 0x29,
+    0x04, 0x03, 0xf2, 0x4d, 0x12, 0x77, 0x9c, 0x62, 0xbc, 0x75, 0xb4, 0xab, 0x7a, 0xbc, 0xa0, 0x8f,
+    0x60, 0x5e, 0xcd, 0xce, 0x3a, 0xd8, 0x09, 0xeb, 0x9d, 0x40, 0xdb, 0x58, 0x53,
+];
+
+pub static LOADED_CERT_AUTHBOUND: &[u8] = &[
+    0x30, 0x82, 0x02, 0x93, 0x30, 0x82, 0x02, 0x3A, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01,
+    0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x30, 0x29, 0x31, 0x19,
+    0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x34, 0x34, 0x61, 0x38, 0x31, 0x65, 0x61,
+    0x65, 0x63, 0x35, 0x31, 0x64, 0x62, 0x30, 0x62, 0x31, 0x31, 0x0C, 0x30, 0x0A, 0x06, 0x03, 0x55,
+    0x04, 0x0C, 0x0C, 0x03, 0x54, 0x45, 0x45, 0x30, 0x20, 0x17, 0x0D, 0x37, 0x30, 0x30, 0x31, 0x30,
+    0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x18, 0x0F, 0x32, 0x31, 0x30, 0x36, 0x30, 0x32,
+    0x30, 0x37, 0x30, 0x36, 0x32, 0x38, 0x31, 0x35, 0x5A, 0x30, 0x1F, 0x31, 0x1D, 0x30, 0x1B, 0x06,
+    0x03, 0x55, 0x04, 0x03, 0x0C, 0x14, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x20, 0x4B, 0x65,
+    0x79, 0x73, 0x74, 0x6F, 0x72, 0x65, 0x20, 0x4B, 0x65, 0x79, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
+    0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01,
+    0x07, 0x03, 0x42, 0x00, 0x04, 0x20, 0x16, 0x85, 0xE6, 0x7F, 0xF1, 0x0E, 0x99, 0x1B, 0x3A, 0xC6,
+    0xC2, 0x83, 0x0A, 0x1D, 0xA4, 0xF1, 0x92, 0x76, 0x88, 0x4B, 0x6A, 0xCD, 0xB2, 0x8E, 0xF1, 0x50,
+    0x58, 0xD2, 0x69, 0xDE, 0x57, 0x9C, 0x9C, 0x29, 0x04, 0x03, 0xF2, 0x4D, 0x12, 0x77, 0x9C, 0x62,
+    0xBC, 0x75, 0xB4, 0xAB, 0x7A, 0xBC, 0xA0, 0x8F, 0x60, 0x5E, 0xCD, 0xCE, 0x3A, 0xD8, 0x09, 0xEB,
+    0x9D, 0x40, 0xDB, 0x58, 0x53, 0xA3, 0x82, 0x01, 0x59, 0x30, 0x82, 0x01, 0x55, 0x30, 0x0E, 0x06,
+    0x03, 0x55, 0x1D, 0x0F, 0x01, 0x01, 0xFF, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x82, 0x01,
+    0x41, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79, 0x02, 0x01, 0x11, 0x04, 0x82, 0x01,
+    0x31, 0x30, 0x82, 0x01, 0x2D, 0x02, 0x01, 0x03, 0x0A, 0x01, 0x01, 0x02, 0x01, 0x04, 0x0A, 0x01,
+    0x01, 0x04, 0x08, 0x61, 0x73, 0x64, 0x66, 0x6A, 0x6B, 0x6C, 0x3B, 0x04, 0x00, 0x30, 0x6B, 0xBF,
+    0x85, 0x3D, 0x08, 0x02, 0x06, 0x01, 0x76, 0x31, 0x8B, 0x9D, 0x10, 0xBF, 0x85, 0x45, 0x5B, 0x04,
+    0x59, 0x30, 0x57, 0x31, 0x31, 0x30, 0x2F, 0x04, 0x2A, 0x63, 0x6F, 0x6D, 0x2E, 0x67, 0x6F, 0x6F,
+    0x67, 0x6C, 0x65, 0x2E, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x2E,
+    0x6A, 0x64, 0x61, 0x6E, 0x69, 0x73, 0x2E, 0x6B, 0x65, 0x79, 0x73, 0x74, 0x6F, 0x72, 0x65, 0x74,
+    0x6F, 0x6F, 0x6C, 0x02, 0x01, 0x01, 0x31, 0x22, 0x04, 0x20, 0x30, 0xE0, 0x78, 0x45, 0xAB, 0xD7,
+    0xC1, 0x74, 0x49, 0x01, 0x0F, 0xA7, 0x7F, 0x89, 0xDE, 0x11, 0xA3, 0x8B, 0x3E, 0x31, 0x6B, 0xF1,
+    0x18, 0xB4, 0x58, 0x1B, 0xD7, 0xB3, 0x58, 0xA9, 0xC2, 0x81, 0x30, 0x81, 0xA5, 0xA1, 0x08, 0x31,
+    0x06, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03, 0xA2, 0x03, 0x02, 0x01, 0x03, 0xA3, 0x04, 0x02, 0x02,
+    0x01, 0x00, 0xA5, 0x05, 0x31, 0x03, 0x02, 0x01, 0x04, 0xAA, 0x03, 0x02, 0x01, 0x01, 0xBF, 0x83,
+    0x78, 0x03, 0x02, 0x01, 0x02, 0xBF, 0x85, 0x3E, 0x03, 0x02, 0x01, 0x00, 0xBF, 0x85, 0x40, 0x4C,
+    0x30, 0x4A, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x0A, 0x01, 0x02, 0x04, 0x20, 0xE7, 0xAD, 0x3C, 0x13,
+    0xC2, 0x73, 0x41, 0x60, 0xD7, 0x1A, 0x7C, 0x00, 0x5E, 0x14, 0xD8, 0xAE, 0x06, 0x5D, 0x22, 0xD0,
+    0xB5, 0xF5, 0x6A, 0xBA, 0x1F, 0x82, 0xA7, 0x8C, 0x17, 0x2C, 0xFD, 0x0F, 0xBF, 0x85, 0x41, 0x05,
+    0x02, 0x03, 0x01, 0xAD, 0xB0, 0xBF, 0x85, 0x42, 0x05, 0x02, 0x03, 0x03, 0x15, 0x75, 0xBF, 0x85,
+    0x4E, 0x06, 0x02, 0x04, 0x01, 0x34, 0x61, 0xB9, 0xBF, 0x85, 0x4F, 0x06, 0x02, 0x04, 0x01, 0x34,
+    0x61, 0xB9, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x03, 0x47,
+    0x00, 0x30, 0x44, 0x02, 0x20, 0x4B, 0xDC, 0x8E, 0x91, 0xE6, 0xAA, 0x4A, 0x81, 0x6D, 0xA2, 0xD7,
+    0x13, 0x9E, 0x70, 0x12, 0x79, 0xB7, 0x85, 0x05, 0xAD, 0x6E, 0x5E, 0x0B, 0x43, 0x3B, 0xAF, 0x9A,
+    0xA9, 0x29, 0x40, 0xD7, 0x92, 0x02, 0x20, 0x2F, 0x39, 0x58, 0xE9, 0x89, 0x1A, 0x14, 0x41, 0x8D,
+    0xE0, 0xDC, 0x3D, 0x88, 0xF4, 0x2C, 0x7C, 0xDA, 0xA1, 0x84, 0xFA, 0x7F, 0xF9, 0x07, 0x97, 0xFB,
+    0xB5, 0xB7, 0x28, 0x28, 0x00, 0x7C, 0xA7,
+];
+pub static LOADED_CACERT_AUTHBOUND: &[u8] = &[
+    0x30, 0x82, 0x02, 0x26, 0x30, 0x82, 0x01, 0xAB, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0A, 0x05,
+    0x84, 0x20, 0x26, 0x90, 0x76, 0x23, 0x58, 0x71, 0x77, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48,
+    0xCE, 0x3D, 0x04, 0x03, 0x02, 0x30, 0x29, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05,
+    0x13, 0x10, 0x34, 0x64, 0x37, 0x34, 0x61, 0x30, 0x65, 0x30, 0x31, 0x61, 0x61, 0x66, 0x33, 0x64,
+    0x64, 0x66, 0x31, 0x0C, 0x30, 0x0A, 0x06, 0x03, 0x55, 0x04, 0x0C, 0x0C, 0x03, 0x54, 0x45, 0x45,
+    0x30, 0x1E, 0x17, 0x0D, 0x31, 0x38, 0x30, 0x33, 0x32, 0x31, 0x32, 0x31, 0x32, 0x35, 0x31, 0x33,
+    0x5A, 0x17, 0x0D, 0x32, 0x38, 0x30, 0x33, 0x31, 0x38, 0x32, 0x31, 0x32, 0x35, 0x31, 0x33, 0x5A,
+    0x30, 0x29, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x34, 0x34, 0x61,
+    0x38, 0x31, 0x65, 0x61, 0x65, 0x63, 0x35, 0x31, 0x64, 0x62, 0x30, 0x62, 0x31, 0x31, 0x0C, 0x30,
+    0x0A, 0x06, 0x03, 0x55, 0x04, 0x0C, 0x0C, 0x03, 0x54, 0x45, 0x45, 0x30, 0x59, 0x30, 0x13, 0x06,
+    0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03,
+    0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xFA, 0x35, 0x8F, 0xB0, 0x31, 0xD6, 0x30, 0x88, 0xDE, 0xB0,
+    0x29, 0xCD, 0x6C, 0x7D, 0x4E, 0xA9, 0xCE, 0x6E, 0x9D, 0x7A, 0xAC, 0x97, 0x92, 0xC2, 0x45, 0xB5,
+    0xE2, 0xD0, 0xC1, 0x52, 0xA8, 0x50, 0x25, 0xD7, 0x89, 0x58, 0x7B, 0x04, 0xB6, 0x66, 0x93, 0x2A,
+    0x26, 0x5D, 0x3A, 0xB1, 0x5B, 0x77, 0x30, 0xBF, 0x95, 0xAA, 0x8B, 0x43, 0xC3, 0xBF, 0x43, 0xB7,
+    0xEE, 0xAC, 0x73, 0xDC, 0x03, 0x6A, 0xA3, 0x81, 0xBA, 0x30, 0x81, 0xB7, 0x30, 0x1D, 0x06, 0x03,
+    0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0x19, 0x9F, 0x87, 0x8B, 0x56, 0xF4, 0x99, 0x3A, 0x69,
+    0x96, 0x9B, 0x8D, 0x9E, 0x64, 0xAA, 0x56, 0xB4, 0x7F, 0x8B, 0x4D, 0x30, 0x1F, 0x06, 0x03, 0x55,
+    0x1D, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xA9, 0xB5, 0xF4, 0x29, 0xC9, 0x1A, 0x58, 0xBD,
+    0x2F, 0x98, 0x2D, 0x67, 0x73, 0x31, 0x06, 0x87, 0xE0, 0xDF, 0xCD, 0x62, 0x30, 0x0F, 0x06, 0x03,
+    0x55, 0x1D, 0x13, 0x01, 0x01, 0xFF, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xFF, 0x30, 0x0E, 0x06,
+    0x03, 0x55, 0x1D, 0x0F, 0x01, 0x01, 0xFF, 0x04, 0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x54, 0x06,
+    0x03, 0x55, 0x1D, 0x1F, 0x04, 0x4D, 0x30, 0x4B, 0x30, 0x49, 0xA0, 0x47, 0xA0, 0x45, 0x86, 0x43,
+    0x68, 0x74, 0x74, 0x70, 0x73, 0x3A, 0x2F, 0x2F, 0x61, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x2E,
+    0x67, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x61,
+    0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x63, 0x72, 0x6C, 0x2F, 0x30,
+    0x35, 0x38, 0x34, 0x32, 0x30, 0x32, 0x36, 0x39, 0x30, 0x37, 0x36, 0x32, 0x33, 0x35, 0x38, 0x37,
+    0x31, 0x37, 0x37, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x03,
+    0x69, 0x00, 0x30, 0x66, 0x02, 0x31, 0x00, 0xE3, 0x35, 0xC6, 0xA8, 0xB2, 0x75, 0x9C, 0x56, 0x7B,
+    0x6E, 0x61, 0x80, 0x65, 0x2C, 0x06, 0x88, 0xDD, 0xB9, 0x68, 0x4D, 0x3C, 0x68, 0x49, 0x66, 0x01,
+    0x4E, 0x30, 0x1D, 0xF3, 0xEC, 0xA5, 0x51, 0x5C, 0xBF, 0xE7, 0x83, 0x33, 0xBD, 0x14, 0xEE, 0x23,
+    0xF0, 0xCF, 0xB1, 0x37, 0x1C, 0x27, 0x78, 0x02, 0x31, 0x00, 0x94, 0xCB, 0x08, 0x3D, 0x2D, 0x3E,
+    0x69, 0x54, 0x5F, 0x63, 0xE3, 0xE4, 0x74, 0x72, 0xE2, 0xFF, 0x8B, 0x26, 0xD2, 0x86, 0xC0, 0x97,
+    0x32, 0x40, 0xDD, 0x7C, 0x1F, 0x50, 0x60, 0x57, 0xCF, 0x2E, 0x23, 0xF3, 0x33, 0xE4, 0xFB, 0x6F,
+    0x5B, 0x7C, 0xC6, 0x31, 0x85, 0xAE, 0xE0, 0x4E, 0x44, 0xA9, 0x30, 0x82, 0x03, 0xD1, 0x30, 0x82,
+    0x01, 0xB9, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0A, 0x03, 0x88, 0x26, 0x67, 0x60, 0x65, 0x89,
+    0x96, 0x85, 0x7F, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B,
+    0x05, 0x00, 0x30, 0x1B, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x66,
+    0x39, 0x32, 0x30, 0x30, 0x39, 0x65, 0x38, 0x35, 0x33, 0x62, 0x36, 0x62, 0x30, 0x34, 0x35, 0x30,
+    0x1E, 0x17, 0x0D, 0x31, 0x38, 0x30, 0x33, 0x32, 0x31, 0x32, 0x31, 0x31, 0x34, 0x31, 0x34, 0x5A,
+    0x17, 0x0D, 0x32, 0x38, 0x30, 0x33, 0x31, 0x38, 0x32, 0x31, 0x31, 0x34, 0x31, 0x34, 0x5A, 0x30,
+    0x29, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x34, 0x64, 0x37, 0x34,
+    0x61, 0x30, 0x65, 0x30, 0x31, 0x61, 0x61, 0x66, 0x33, 0x64, 0x64, 0x66, 0x31, 0x0C, 0x30, 0x0A,
+    0x06, 0x03, 0x55, 0x04, 0x0C, 0x0C, 0x03, 0x54, 0x45, 0x45, 0x30, 0x76, 0x30, 0x10, 0x06, 0x07,
+    0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62,
+    0x00, 0x04, 0xD5, 0xF5, 0x0E, 0xE2, 0x8D, 0xF3, 0x33, 0x4A, 0x6A, 0x77, 0x90, 0x9C, 0xC2, 0x25,
+    0xC8, 0x8A, 0x32, 0xAE, 0x3B, 0xB4, 0x9C, 0x4A, 0x95, 0x22, 0x0C, 0xBA, 0x0A, 0x76, 0xCA, 0xCB,
+    0x24, 0x0C, 0x84, 0x3A, 0x83, 0x76, 0x04, 0x23, 0x31, 0x3A, 0xA0, 0x82, 0x80, 0x26, 0x65, 0xFD,
+    0x2F, 0x44, 0xF4, 0x96, 0xD8, 0xB7, 0xDC, 0xAC, 0x55, 0x34, 0x74, 0x41, 0x0D, 0x0D, 0x7F, 0xBD,
+    0xE3, 0xF4, 0x28, 0xDF, 0x74, 0x4A, 0x17, 0x4D, 0xE7, 0xB2, 0x9B, 0x2B, 0x24, 0xC0, 0x9E, 0x56,
+    0x00, 0x52, 0xBB, 0x75, 0xB0, 0xD5, 0x6A, 0x41, 0x16, 0x08, 0xCE, 0x32, 0xDB, 0x8F, 0x8B, 0x20,
+    0x73, 0x72, 0xA3, 0x81, 0xB6, 0x30, 0x81, 0xB3, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04,
+    0x16, 0x04, 0x14, 0xA9, 0xB5, 0xF4, 0x29, 0xC9, 0x1A, 0x58, 0xBD, 0x2F, 0x98, 0x2D, 0x67, 0x73,
+    0x31, 0x06, 0x87, 0xE0, 0xDF, 0xCD, 0x62, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x1D, 0x23, 0x04, 0x18,
+    0x30, 0x16, 0x80, 0x14, 0x36, 0x61, 0xE1, 0x00, 0x7C, 0x88, 0x05, 0x09, 0x51, 0x8B, 0x44, 0x6C,
+    0x47, 0xFF, 0x1A, 0x4C, 0xC9, 0xEA, 0x4F, 0x12, 0x30, 0x0F, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01,
+    0x01, 0xFF, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xFF, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x1D, 0x0F,
+    0x01, 0x01, 0xFF, 0x04, 0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x50, 0x06, 0x03, 0x55, 0x1D, 0x1F,
+    0x04, 0x49, 0x30, 0x47, 0x30, 0x45, 0xA0, 0x43, 0xA0, 0x41, 0x86, 0x3F, 0x68, 0x74, 0x74, 0x70,
+    0x73, 0x3A, 0x2F, 0x2F, 0x61, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x2E, 0x67, 0x6F, 0x6F, 0x67,
+    0x6C, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x61, 0x74, 0x74, 0x65, 0x73,
+    0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x63, 0x72, 0x6C, 0x2F, 0x45, 0x38, 0x46, 0x41, 0x31,
+    0x39, 0x36, 0x33, 0x31, 0x34, 0x44, 0x32, 0x46, 0x41, 0x31, 0x38, 0x30, 0x0D, 0x06, 0x09, 0x2A,
+    0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x32,
+    0xCE, 0x04, 0xCC, 0x4D, 0x82, 0xAD, 0x1D, 0xDE, 0xA5, 0xCF, 0xE2, 0x1A, 0xA3, 0x79, 0xF7, 0xED,
+    0x88, 0x1E, 0x0E, 0x67, 0x8E, 0xFC, 0xBE, 0x7B, 0x04, 0xB7, 0x26, 0x59, 0xCA, 0x95, 0x47, 0x8A,
+    0x10, 0x3F, 0xE5, 0x14, 0x19, 0xEC, 0xD4, 0xDB, 0x33, 0xC3, 0xA1, 0x51, 0xF5, 0x06, 0x5E, 0x30,
+    0x66, 0x1F, 0xD2, 0x58, 0x2F, 0x14, 0x03, 0x7B, 0x35, 0x83, 0x86, 0x46, 0xDC, 0xEE, 0x04, 0x30,
+    0xA1, 0x0F, 0xC4, 0x16, 0xC9, 0x8E, 0x63, 0xD0, 0xDA, 0x5C, 0xB0, 0xF7, 0x3E, 0x21, 0xB6, 0xA5,
+    0x04, 0x07, 0x5A, 0x01, 0x8C, 0x31, 0x1F, 0x3E, 0x3A, 0xAF, 0x8D, 0x31, 0x3E, 0xB6, 0x12, 0x14,
+    0xF0, 0x0D, 0x2C, 0xCC, 0x6C, 0xB8, 0x7A, 0xBF, 0xD2, 0x6B, 0x5F, 0x27, 0xB0, 0xFF, 0xC0, 0xAA,
+    0xDE, 0xDE, 0xF6, 0x31, 0x6D, 0xF3, 0x95, 0xC2, 0xD4, 0x90, 0xDC, 0x82, 0x4F, 0x24, 0x0D, 0x85,
+    0xF2, 0xBB, 0xC4, 0x58, 0xC9, 0xFA, 0xDD, 0x96, 0x41, 0x2B, 0x1F, 0x4C, 0x10, 0x1C, 0x9A, 0x57,
+    0x55, 0x0F, 0x62, 0xFC, 0x8D, 0xA2, 0xCA, 0x84, 0x7B, 0x16, 0x60, 0xE8, 0x62, 0xCE, 0x92, 0x85,
+    0x13, 0xF0, 0x63, 0x83, 0xD8, 0x5B, 0xA8, 0x74, 0x78, 0xB5, 0x28, 0xDB, 0x6C, 0xC9, 0x6E, 0x85,
+    0x85, 0x52, 0x3F, 0xD8, 0x67, 0xAE, 0xF4, 0x09, 0xBE, 0xCF, 0x8C, 0x7F, 0x72, 0xB2, 0xC8, 0x93,
+    0xC6, 0xD2, 0xF3, 0x38, 0x74, 0x71, 0x22, 0xD6, 0x92, 0x76, 0xB1, 0xAE, 0x14, 0x5A, 0x09, 0xD8,
+    0xAF, 0x1D, 0xAF, 0x48, 0x22, 0x5C, 0x30, 0x85, 0x8E, 0xC2, 0xFE, 0x61, 0xAF, 0xC3, 0xD2, 0x4C,
+    0x92, 0x53, 0xA4, 0x75, 0x1F, 0x78, 0xEA, 0xFC, 0xFA, 0xC4, 0xCA, 0x4E, 0x67, 0x68, 0x1F, 0x7D,
+    0xB2, 0x5E, 0xEA, 0x8A, 0xB1, 0xCC, 0xB6, 0x92, 0x64, 0xF8, 0x82, 0xC0, 0x8B, 0xDC, 0x24, 0xE8,
+    0x57, 0x20, 0x33, 0x6D, 0x17, 0x33, 0x0D, 0xCB, 0x70, 0x02, 0x8B, 0xE5, 0xE3, 0x7D, 0x2C, 0x98,
+    0x32, 0x00, 0x20, 0xB4, 0xBD, 0xEE, 0x89, 0xAA, 0x66, 0x13, 0x34, 0x9D, 0x9C, 0x8F, 0xDE, 0x16,
+    0x09, 0x91, 0x49, 0x80, 0x50, 0x57, 0x39, 0xAE, 0x35, 0x01, 0xE2, 0x25, 0x8E, 0x17, 0x08, 0xE0,
+    0xF0, 0x77, 0x98, 0x9D, 0x0A, 0x4F, 0xD2, 0x76, 0xDA, 0xC4, 0x51, 0x45, 0x32, 0x8B, 0xE1, 0xAB,
+    0xEE, 0x10, 0x16, 0xF6, 0x95, 0x7D, 0x32, 0x76, 0xB2, 0xB5, 0x19, 0x67, 0x73, 0xFE, 0xC0, 0xC6,
+    0xA9, 0xD2, 0xA9, 0x23, 0xF0, 0x2B, 0xFC, 0xB1, 0xB6, 0xEC, 0x3E, 0x11, 0x60, 0xA4, 0x22, 0xC7,
+    0xFF, 0x25, 0xC3, 0xED, 0x6C, 0x6B, 0x79, 0x02, 0x3D, 0x5D, 0x62, 0x36, 0xD9, 0x32, 0xE4, 0x6E,
+    0x47, 0x67, 0x85, 0x8B, 0x23, 0x0A, 0xD5, 0x1E, 0xD0, 0xF4, 0x17, 0x1D, 0xCC, 0x3F, 0x5F, 0xDA,
+    0x12, 0xE2, 0x35, 0x25, 0x52, 0xC2, 0xD6, 0x94, 0x3E, 0x83, 0x60, 0x55, 0xF8, 0x8D, 0x54, 0xF5,
+    0x47, 0x6F, 0x38, 0x03, 0x3B, 0xD7, 0x9A, 0x94, 0x8A, 0x3B, 0x9F, 0x92, 0x69, 0x0F, 0xCD, 0xB8,
+    0xF4, 0x62, 0x78, 0x22, 0x47, 0xE0, 0xAE, 0xED, 0xFD, 0xF6, 0xE4, 0xC5, 0x8C, 0x0E, 0xB5, 0x18,
+    0xB1, 0x46, 0x3A, 0x6F, 0xBD, 0xDE, 0x50, 0x3F, 0x1C, 0x35, 0x28, 0xF9, 0xED, 0x1E, 0xE8, 0x15,
+    0x31, 0xA9, 0xF7, 0xB1, 0x9D, 0xE1, 0x34, 0x81, 0x20, 0x1F, 0x22, 0xD4, 0xB7, 0xC6, 0x59, 0x8B,
+    0x90, 0x98, 0xDF, 0xA6, 0xB9, 0xA8, 0x8E, 0x6C, 0x15, 0x55, 0x5C, 0x41, 0x96, 0x82, 0x0D, 0xA9,
+    0x5F, 0xA9, 0xF3, 0x77, 0x1D, 0xEE, 0x6B, 0x4C, 0x94, 0xC6, 0xC6, 0x9B, 0x78, 0x5B, 0x03, 0xBD,
+    0xA9, 0x87, 0xDD, 0x24, 0x04, 0x70, 0xCE, 0x6C, 0x52, 0xE6, 0x21, 0x63, 0x6D, 0x28, 0x6C, 0x30,
+    0x82, 0x05, 0x60, 0x30, 0x82, 0x03, 0x48, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0xE8,
+    0xFA, 0x19, 0x63, 0x14, 0xD2, 0xFA, 0x18, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
+    0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x1B, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04,
+    0x05, 0x13, 0x10, 0x66, 0x39, 0x32, 0x30, 0x30, 0x39, 0x65, 0x38, 0x35, 0x33, 0x62, 0x36, 0x62,
+    0x30, 0x34, 0x35, 0x30, 0x1E, 0x17, 0x0D, 0x31, 0x36, 0x30, 0x35, 0x32, 0x36, 0x31, 0x36, 0x32,
+    0x38, 0x35, 0x32, 0x5A, 0x17, 0x0D, 0x32, 0x36, 0x30, 0x35, 0x32, 0x34, 0x31, 0x36, 0x32, 0x38,
+    0x35, 0x32, 0x5A, 0x30, 0x1B, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10,
+    0x66, 0x39, 0x32, 0x30, 0x30, 0x39, 0x65, 0x38, 0x35, 0x33, 0x62, 0x36, 0x62, 0x30, 0x34, 0x35,
+    0x30, 0x82, 0x02, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01,
+    0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0F, 0x00, 0x30, 0x82, 0x02, 0x0A, 0x02, 0x82, 0x02, 0x01,
+    0x00, 0xAF, 0xB6, 0xC7, 0x82, 0x2B, 0xB1, 0xA7, 0x01, 0xEC, 0x2B, 0xB4, 0x2E, 0x8B, 0xCC, 0x54,
+    0x16, 0x63, 0xAB, 0xEF, 0x98, 0x2F, 0x32, 0xC7, 0x7F, 0x75, 0x31, 0x03, 0x0C, 0x97, 0x52, 0x4B,
+    0x1B, 0x5F, 0xE8, 0x09, 0xFB, 0xC7, 0x2A, 0xA9, 0x45, 0x1F, 0x74, 0x3C, 0xBD, 0x9A, 0x6F, 0x13,
+    0x35, 0x74, 0x4A, 0xA5, 0x5E, 0x77, 0xF6, 0xB6, 0xAC, 0x35, 0x35, 0xEE, 0x17, 0xC2, 0x5E, 0x63,
+    0x95, 0x17, 0xDD, 0x9C, 0x92, 0xE6, 0x37, 0x4A, 0x53, 0xCB, 0xFE, 0x25, 0x8F, 0x8F, 0xFB, 0xB6,
+    0xFD, 0x12, 0x93, 0x78, 0xA2, 0x2A, 0x4C, 0xA9, 0x9C, 0x45, 0x2D, 0x47, 0xA5, 0x9F, 0x32, 0x01,
+    0xF4, 0x41, 0x97, 0xCA, 0x1C, 0xCD, 0x7E, 0x76, 0x2F, 0xB2, 0xF5, 0x31, 0x51, 0xB6, 0xFE, 0xB2,
+    0xFF, 0xFD, 0x2B, 0x6F, 0xE4, 0xFE, 0x5B, 0xC6, 0xBD, 0x9E, 0xC3, 0x4B, 0xFE, 0x08, 0x23, 0x9D,
+    0xAA, 0xFC, 0xEB, 0x8E, 0xB5, 0xA8, 0xED, 0x2B, 0x3A, 0xCD, 0x9C, 0x5E, 0x3A, 0x77, 0x90, 0xE1,
+    0xB5, 0x14, 0x42, 0x79, 0x31, 0x59, 0x85, 0x98, 0x11, 0xAD, 0x9E, 0xB2, 0xA9, 0x6B, 0xBD, 0xD7,
+    0xA5, 0x7C, 0x93, 0xA9, 0x1C, 0x41, 0xFC, 0xCD, 0x27, 0xD6, 0x7F, 0xD6, 0xF6, 0x71, 0xAA, 0x0B,
+    0x81, 0x52, 0x61, 0xAD, 0x38, 0x4F, 0xA3, 0x79, 0x44, 0x86, 0x46, 0x04, 0xDD, 0xB3, 0xD8, 0xC4,
+    0xF9, 0x20, 0xA1, 0x9B, 0x16, 0x56, 0xC2, 0xF1, 0x4A, 0xD6, 0xD0, 0x3C, 0x56, 0xEC, 0x06, 0x08,
+    0x99, 0x04, 0x1C, 0x1E, 0xD1, 0xA5, 0xFE, 0x6D, 0x34, 0x40, 0xB5, 0x56, 0xBA, 0xD1, 0xD0, 0xA1,
+    0x52, 0x58, 0x9C, 0x53, 0xE5, 0x5D, 0x37, 0x07, 0x62, 0xF0, 0x12, 0x2E, 0xEF, 0x91, 0x86, 0x1B,
+    0x1B, 0x0E, 0x6C, 0x4C, 0x80, 0x92, 0x74, 0x99, 0xC0, 0xE9, 0xBE, 0xC0, 0xB8, 0x3E, 0x3B, 0xC1,
+    0xF9, 0x3C, 0x72, 0xC0, 0x49, 0x60, 0x4B, 0xBD, 0x2F, 0x13, 0x45, 0xE6, 0x2C, 0x3F, 0x8E, 0x26,
+    0xDB, 0xEC, 0x06, 0xC9, 0x47, 0x66, 0xF3, 0xC1, 0x28, 0x23, 0x9D, 0x4F, 0x43, 0x12, 0xFA, 0xD8,
+    0x12, 0x38, 0x87, 0xE0, 0x6B, 0xEC, 0xF5, 0x67, 0x58, 0x3B, 0xF8, 0x35, 0x5A, 0x81, 0xFE, 0xEA,
+    0xBA, 0xF9, 0x9A, 0x83, 0xC8, 0xDF, 0x3E, 0x2A, 0x32, 0x2A, 0xFC, 0x67, 0x2B, 0xF1, 0x20, 0xB1,
+    0x35, 0x15, 0x8B, 0x68, 0x21, 0xCE, 0xAF, 0x30, 0x9B, 0x6E, 0xEE, 0x77, 0xF9, 0x88, 0x33, 0xB0,
+    0x18, 0xDA, 0xA1, 0x0E, 0x45, 0x1F, 0x06, 0xA3, 0x74, 0xD5, 0x07, 0x81, 0xF3, 0x59, 0x08, 0x29,
+    0x66, 0xBB, 0x77, 0x8B, 0x93, 0x08, 0x94, 0x26, 0x98, 0xE7, 0x4E, 0x0B, 0xCD, 0x24, 0x62, 0x8A,
+    0x01, 0xC2, 0xCC, 0x03, 0xE5, 0x1F, 0x0B, 0x3E, 0x5B, 0x4A, 0xC1, 0xE4, 0xDF, 0x9E, 0xAF, 0x9F,
+    0xF6, 0xA4, 0x92, 0xA7, 0x7C, 0x14, 0x83, 0x88, 0x28, 0x85, 0x01, 0x5B, 0x42, 0x2C, 0xE6, 0x7B,
+    0x80, 0xB8, 0x8C, 0x9B, 0x48, 0xE1, 0x3B, 0x60, 0x7A, 0xB5, 0x45, 0xC7, 0x23, 0xFF, 0x8C, 0x44,
+    0xF8, 0xF2, 0xD3, 0x68, 0xB9, 0xF6, 0x52, 0x0D, 0x31, 0x14, 0x5E, 0xBF, 0x9E, 0x86, 0x2A, 0xD7,
+    0x1D, 0xF6, 0xA3, 0xBF, 0xD2, 0x45, 0x09, 0x59, 0xD6, 0x53, 0x74, 0x0D, 0x97, 0xA1, 0x2F, 0x36,
+    0x8B, 0x13, 0xEF, 0x66, 0xD5, 0xD0, 0xA5, 0x4A, 0x6E, 0x2F, 0x5D, 0x9A, 0x6F, 0xEF, 0x44, 0x68,
+    0x32, 0xBC, 0x67, 0x84, 0x47, 0x25, 0x86, 0x1F, 0x09, 0x3D, 0xD0, 0xE6, 0xF3, 0x40, 0x5D, 0xA8,
+    0x96, 0x43, 0xEF, 0x0F, 0x4D, 0x69, 0xB6, 0x42, 0x00, 0x51, 0xFD, 0xB9, 0x30, 0x49, 0x67, 0x3E,
+    0x36, 0x95, 0x05, 0x80, 0xD3, 0xCD, 0xF4, 0xFB, 0xD0, 0x8B, 0xC5, 0x84, 0x83, 0x95, 0x26, 0x00,
+    0x63, 0x02, 0x03, 0x01, 0x00, 0x01, 0xA3, 0x81, 0xA6, 0x30, 0x81, 0xA3, 0x30, 0x1D, 0x06, 0x03,
+    0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0x36, 0x61, 0xE1, 0x00, 0x7C, 0x88, 0x05, 0x09, 0x51,
+    0x8B, 0x44, 0x6C, 0x47, 0xFF, 0x1A, 0x4C, 0xC9, 0xEA, 0x4F, 0x12, 0x30, 0x1F, 0x06, 0x03, 0x55,
+    0x1D, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x36, 0x61, 0xE1, 0x00, 0x7C, 0x88, 0x05, 0x09,
+    0x51, 0x8B, 0x44, 0x6C, 0x47, 0xFF, 0x1A, 0x4C, 0xC9, 0xEA, 0x4F, 0x12, 0x30, 0x0F, 0x06, 0x03,
+    0x55, 0x1D, 0x13, 0x01, 0x01, 0xFF, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xFF, 0x30, 0x0E, 0x06,
+    0x03, 0x55, 0x1D, 0x0F, 0x01, 0x01, 0xFF, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x40, 0x06,
+    0x03, 0x55, 0x1D, 0x1F, 0x04, 0x39, 0x30, 0x37, 0x30, 0x35, 0xA0, 0x33, 0xA0, 0x31, 0x86, 0x2F,
+    0x68, 0x74, 0x74, 0x70, 0x73, 0x3A, 0x2F, 0x2F, 0x61, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x2E,
+    0x67, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x61,
+    0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x63, 0x72, 0x6C, 0x2F, 0x30,
+    0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82,
+    0x02, 0x01, 0x00, 0x20, 0xC8, 0xC3, 0x8D, 0x4B, 0xDC, 0xA9, 0x57, 0x1B, 0x46, 0x8C, 0x89, 0x2F,
+    0xFF, 0x72, 0xAA, 0xC6, 0xF8, 0x44, 0xA1, 0x1D, 0x41, 0xA8, 0xF0, 0x73, 0x6C, 0xC3, 0x7D, 0x16,
+    0xD6, 0x42, 0x6D, 0x8E, 0x7E, 0x94, 0x07, 0x04, 0x4C, 0xEA, 0x39, 0xE6, 0x8B, 0x07, 0xC1, 0x3D,
+    0xBF, 0x15, 0x03, 0xDD, 0x5C, 0x85, 0xBD, 0xAF, 0xB2, 0xC0, 0x2D, 0x5F, 0x6C, 0xDB, 0x4E, 0xFA,
+    0x81, 0x27, 0xDF, 0x8B, 0x04, 0xF1, 0x82, 0x77, 0x0F, 0xC4, 0xE7, 0x74, 0x5B, 0x7F, 0xCE, 0xAA,
+    0x87, 0x12, 0x9A, 0x88, 0x01, 0xCE, 0x8E, 0x9B, 0xC0, 0xCB, 0x96, 0x37, 0x9B, 0x4D, 0x26, 0xA8,
+    0x2D, 0x30, 0xFD, 0x9C, 0x2F, 0x8E, 0xED, 0x6D, 0xC1, 0xBE, 0x2F, 0x84, 0xB6, 0x89, 0xE4, 0xD9,
+    0x14, 0x25, 0x8B, 0x14, 0x4B, 0xBA, 0xE6, 0x24, 0xA1, 0xC7, 0x06, 0x71, 0x13, 0x2E, 0x2F, 0x06,
+    0x16, 0xA8, 0x84, 0xB2, 0xA4, 0xD6, 0xA4, 0x6F, 0xFA, 0x89, 0xB6, 0x02, 0xBF, 0xBA, 0xD8, 0x0C,
+    0x12, 0x43, 0x71, 0x1F, 0x56, 0xEB, 0x60, 0x56, 0xF6, 0x37, 0xC8, 0xA0, 0x14, 0x1C, 0xC5, 0x40,
+    0x94, 0x26, 0x8B, 0x8C, 0x3C, 0x7D, 0xB9, 0x94, 0xB3, 0x5C, 0x0D, 0xCD, 0x6C, 0xB2, 0xAB, 0xC2,
+    0xDA, 0xFE, 0xE2, 0x52, 0x02, 0x3D, 0x2D, 0xEA, 0x0C, 0xD6, 0xC3, 0x68, 0xBE, 0xA3, 0xE6, 0x41,
+    0x48, 0x86, 0xF6, 0xB1, 0xE5, 0x8B, 0x5B, 0xD7, 0xC7, 0x30, 0xB2, 0x68, 0xC4, 0xE3, 0xC1, 0xFB,
+    0x64, 0x24, 0xB9, 0x1F, 0xEB, 0xBD, 0xB8, 0x0C, 0x58, 0x6E, 0x2A, 0xE8, 0x36, 0x8C, 0x84, 0xD5,
+    0xD1, 0x09, 0x17, 0xBD, 0xA2, 0x56, 0x17, 0x89, 0xD4, 0x68, 0x73, 0x93, 0x34, 0x0E, 0x2E, 0x25,
+    0x4F, 0x56, 0x0E, 0xF6, 0x4B, 0x23, 0x58, 0xFC, 0xDC, 0x0F, 0xBF, 0xC6, 0x70, 0x09, 0x52, 0xE7,
+    0x08, 0xBF, 0xFC, 0xC6, 0x27, 0x50, 0x0C, 0x1F, 0x66, 0xE8, 0x1E, 0xA1, 0x7C, 0x09, 0x8D, 0x7A,
+    0x2E, 0x9B, 0x18, 0x80, 0x1B, 0x7A, 0xB4, 0xAC, 0x71, 0x58, 0x7D, 0x34, 0x5D, 0xCC, 0x83, 0x09,
+    0xD5, 0xB6, 0x2A, 0x50, 0x42, 0x7A, 0xA6, 0xD0, 0x3D, 0xCB, 0x05, 0x99, 0x6C, 0x96, 0xBA, 0x0C,
+    0x5D, 0x71, 0xE9, 0x21, 0x62, 0xC0, 0x16, 0xCA, 0x84, 0x9F, 0xF3, 0x5F, 0x0D, 0x52, 0xC6, 0x5D,
+    0x05, 0x60, 0x5A, 0x47, 0xF3, 0xAE, 0x91, 0x7A, 0xCD, 0x2D, 0xF9, 0x10, 0xEF, 0xD2, 0x32, 0x66,
+    0x88, 0x59, 0x6E, 0xF6, 0x9B, 0x3B, 0xF5, 0xFE, 0x31, 0x54, 0xF7, 0xAE, 0xB8, 0x80, 0xA0, 0xA7,
+    0x3C, 0xA0, 0x4D, 0x94, 0xC2, 0xCE, 0x83, 0x17, 0xEE, 0xB4, 0x3D, 0x5E, 0xFF, 0x58, 0x83, 0xE3,
+    0x36, 0xF5, 0xF2, 0x49, 0xDA, 0xAC, 0xA4, 0x89, 0x92, 0x37, 0xBF, 0x26, 0x7E, 0x5C, 0x43, 0xAB,
+    0x02, 0xEA, 0x44, 0x16, 0x24, 0x03, 0x72, 0x3B, 0xE6, 0xAA, 0x69, 0x2C, 0x61, 0xBD, 0xAE, 0x9E,
+    0xD4, 0x09, 0xD4, 0x63, 0xC4, 0xC9, 0x7C, 0x64, 0x30, 0x65, 0x77, 0xEE, 0xF2, 0xBC, 0x75, 0x60,
+    0xB7, 0x57, 0x15, 0xCC, 0x9C, 0x7D, 0xC6, 0x7C, 0x86, 0x08, 0x2D, 0xB7, 0x51, 0xA8, 0x9C, 0x30,
+    0x34, 0x97, 0x62, 0xB0, 0x78, 0x23, 0x85, 0x87, 0x5C, 0xF1, 0xA3, 0xC6, 0x16, 0x6E, 0x0A, 0xE3,
+    0xC1, 0x2D, 0x37, 0x4E, 0x2D, 0x4F, 0x18, 0x46, 0xF3, 0x18, 0x74, 0x4B, 0xD8, 0x79, 0xB5, 0x87,
+    0x32, 0x9B, 0xF0, 0x18, 0x21, 0x7A, 0x6C, 0x0C, 0x77, 0x24, 0x1A, 0x48, 0x78, 0xE4, 0x35, 0xC0,
+    0x30, 0x79, 0xCB, 0x45, 0x12, 0x89, 0xC5, 0x77, 0x62, 0x06, 0x06, 0x9A, 0x2F, 0x8D, 0x65, 0xF8,
+    0x40, 0xE1, 0x44, 0x52, 0x87, 0xBE, 0xD8, 0x77, 0xAB, 0xAE, 0x24, 0xE2, 0x44, 0x35, 0x16, 0x8D,
+    0x55, 0x3C, 0xE4,
+];
+
+pub static LOADED_USRPKEY_NON_AUTHBOUND: &[u8] = &[
+    0x44, 0x4b, 0x4d, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+    0x8a, 0xc1, 0x08, 0x13, 0x7c, 0x47, 0xba, 0x09, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+    0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x02, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x01, 0xb0, 0xad, 0x01, 0x00, 0x01, 0x75, 0x15, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x60, 0x60, 0x8c, 0x31, 0x76, 0x01, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x61, 0x34, 0x01, 0x01, 0xb9, 0x61, 0x34, 0x01, 0x01, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa1, 0x01, 0x00, 0x00, 0x7c, 0x00, 0x00,
+    0x00, 0xc9, 0xcd, 0xcb, 0xca, 0xfa, 0x37, 0xe2, 0xc7, 0x56, 0x8c, 0x23, 0xf6, 0x7f, 0xd1, 0x8c,
+    0x01, 0xc1, 0x4f, 0x65, 0xd7, 0x1b, 0x10, 0xc5, 0x0a, 0x77, 0x13, 0xf2, 0x82, 0xde, 0x63, 0x68,
+    0x5f, 0xec, 0x2f, 0x95, 0x34, 0x65, 0x5d, 0x2f, 0x99, 0xfc, 0xed, 0x0d, 0x1b, 0xe9, 0xf4, 0x83,
+    0x38, 0x71, 0x83, 0x82, 0x64, 0x51, 0xab, 0x53, 0xb1, 0xfa, 0x73, 0x00, 0x20, 0x24, 0xdd, 0x1c,
+    0x13, 0x00, 0x01, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00,
+    0x00, 0x20, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xe5, 0xa5, 0x27,
+    0xb5, 0x66, 0x76, 0x6c, 0x74, 0x36, 0xd7, 0x2d, 0xad, 0x32, 0x49, 0xd4, 0xa5, 0xed, 0xb2, 0x9c,
+    0x4b, 0xbd, 0xb8, 0xe1, 0x79, 0x9f, 0x8a, 0x72, 0xc3, 0xdf, 0x8b, 0x99, 0x49, 0xa8, 0x5e, 0x10,
+    0x00, 0xd6, 0xa6, 0x58, 0x49, 0x5a, 0xa2, 0x71, 0xb3, 0x54, 0xd3, 0x69, 0xb7, 0xfe, 0x51, 0xc5,
+    0xe4, 0x94, 0xff, 0x10, 0xd7, 0x46, 0x01, 0x78, 0x43, 0x8c, 0x9c, 0xbe, 0x2f, 0x9a, 0x4b, 0x5b,
+    0x72, 0x07, 0x4d, 0x8f, 0x25, 0x50, 0x1e, 0xb2, 0x46, 0xf0, 0xee, 0x50, 0x73, 0x6a, 0x7b, 0xa3,
+    0xe9, 0xb1, 0x08, 0x81, 0x00, 0xdf, 0x0e, 0xc9, 0xc3, 0x2c, 0x13, 0x64, 0xa1,
+];
+
+pub static LOADED_CERT_NON_AUTHBOUND: &[u8] = &[
+    0x30, 0x82, 0x02, 0x93, 0x30, 0x82, 0x02, 0x39, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01,
+    0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x29, 0x31, 0x19,
+    0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x34, 0x34, 0x61, 0x38, 0x31, 0x65, 0x61,
+    0x65, 0x63, 0x35, 0x31, 0x64, 0x62, 0x30, 0x62, 0x31, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55,
+    0x04, 0x0c, 0x0c, 0x03, 0x54, 0x45, 0x45, 0x30, 0x20, 0x17, 0x0d, 0x37, 0x30, 0x30, 0x31, 0x30,
+    0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x30, 0x36, 0x30, 0x32,
+    0x30, 0x37, 0x30, 0x36, 0x32, 0x38, 0x31, 0x35, 0x5a, 0x30, 0x1f, 0x31, 0x1d, 0x30, 0x1b, 0x06,
+    0x03, 0x55, 0x04, 0x03, 0x0c, 0x14, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4b, 0x65,
+    0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
+    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01,
+    0x07, 0x03, 0x42, 0x00, 0x04, 0xa8, 0x5e, 0x10, 0x00, 0xd6, 0xa6, 0x58, 0x49, 0x5a, 0xa2, 0x71,
+    0xb3, 0x54, 0xd3, 0x69, 0xb7, 0xfe, 0x51, 0xc5, 0xe4, 0x94, 0xff, 0x10, 0xd7, 0x46, 0x01, 0x78,
+    0x43, 0x8c, 0x9c, 0xbe, 0x2f, 0x9a, 0x4b, 0x5b, 0x72, 0x07, 0x4d, 0x8f, 0x25, 0x50, 0x1e, 0xb2,
+    0x46, 0xf0, 0xee, 0x50, 0x73, 0x6a, 0x7b, 0xa3, 0xe9, 0xb1, 0x08, 0x81, 0x00, 0xdf, 0x0e, 0xc9,
+    0xc3, 0x2c, 0x13, 0x64, 0xa1, 0xa3, 0x82, 0x01, 0x58, 0x30, 0x82, 0x01, 0x54, 0x30, 0x0e, 0x06,
+    0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x82, 0x01,
+    0x40, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x11, 0x04, 0x82, 0x01,
+    0x30, 0x30, 0x82, 0x01, 0x2c, 0x02, 0x01, 0x03, 0x0a, 0x01, 0x01, 0x02, 0x01, 0x04, 0x0a, 0x01,
+    0x01, 0x04, 0x08, 0x61, 0x73, 0x64, 0x66, 0x6a, 0x6b, 0x6c, 0x3b, 0x04, 0x00, 0x30, 0x6b, 0xbf,
+    0x85, 0x3d, 0x08, 0x02, 0x06, 0x01, 0x76, 0x31, 0x8c, 0x60, 0x60, 0xbf, 0x85, 0x45, 0x5b, 0x04,
+    0x59, 0x30, 0x57, 0x31, 0x31, 0x30, 0x2f, 0x04, 0x2a, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f,
+    0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e,
+    0x6a, 0x64, 0x61, 0x6e, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x74,
+    0x6f, 0x6f, 0x6c, 0x02, 0x01, 0x01, 0x31, 0x22, 0x04, 0x20, 0x30, 0xe0, 0x78, 0x45, 0xab, 0xd7,
+    0xc1, 0x74, 0x49, 0x01, 0x0f, 0xa7, 0x7f, 0x89, 0xde, 0x11, 0xa3, 0x8b, 0x3e, 0x31, 0x6b, 0xf1,
+    0x18, 0xb4, 0x58, 0x1b, 0xd7, 0xb3, 0x58, 0xa9, 0xc2, 0x81, 0x30, 0x81, 0xa4, 0xa1, 0x08, 0x31,
+    0x06, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03, 0xa2, 0x03, 0x02, 0x01, 0x03, 0xa3, 0x04, 0x02, 0x02,
+    0x01, 0x00, 0xa5, 0x05, 0x31, 0x03, 0x02, 0x01, 0x04, 0xaa, 0x03, 0x02, 0x01, 0x01, 0xbf, 0x83,
+    0x77, 0x02, 0x05, 0x00, 0xbf, 0x85, 0x3e, 0x03, 0x02, 0x01, 0x00, 0xbf, 0x85, 0x40, 0x4c, 0x30,
+    0x4a, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x0a, 0x01, 0x02, 0x04, 0x20, 0xe7, 0xad, 0x3c, 0x13, 0xc2,
+    0x73, 0x41, 0x60, 0xd7, 0x1a, 0x7c, 0x00, 0x5e, 0x14, 0xd8, 0xae, 0x06, 0x5d, 0x22, 0xd0, 0xb5,
+    0xf5, 0x6a, 0xba, 0x1f, 0x82, 0xa7, 0x8c, 0x17, 0x2c, 0xfd, 0x0f, 0xbf, 0x85, 0x41, 0x05, 0x02,
+    0x03, 0x01, 0xad, 0xb0, 0xbf, 0x85, 0x42, 0x05, 0x02, 0x03, 0x03, 0x15, 0x75, 0xbf, 0x85, 0x4e,
+    0x06, 0x02, 0x04, 0x01, 0x34, 0x61, 0xb9, 0xbf, 0x85, 0x4f, 0x06, 0x02, 0x04, 0x01, 0x34, 0x61,
+    0xb9, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00,
+    0x30, 0x45, 0x02, 0x20, 0x3f, 0x12, 0x76, 0x4c, 0x85, 0xfd, 0xc9, 0x68, 0x0d, 0x66, 0x0b, 0x60,
+    0x3d, 0xff, 0x7c, 0x8b, 0x11, 0x9c, 0x26, 0xef, 0xdb, 0x4a, 0xc3, 0x37, 0x40, 0x06, 0xa9, 0x16,
+    0xc7, 0x99, 0x85, 0x89, 0x02, 0x21, 0x00, 0xc7, 0x02, 0xf3, 0x21, 0x60, 0x17, 0x05, 0x7e, 0x36,
+    0x33, 0x21, 0x0c, 0x1d, 0x27, 0xc3, 0x8f, 0xd6, 0xd8, 0xd5, 0xd1, 0x64, 0x4c, 0x05, 0xdd, 0x13,
+    0x0e, 0xa4, 0xf3, 0x38, 0xbf, 0x18, 0xd5,
+];
+
+pub static LOADED_CACERT_NON_AUTHBOUND: &[u8] = &[
+    0x30, 0x82, 0x02, 0x26, 0x30, 0x82, 0x01, 0xab, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0a, 0x05,
+    0x84, 0x20, 0x26, 0x90, 0x76, 0x23, 0x58, 0x71, 0x77, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48,
+    0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x29, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05,
+    0x13, 0x10, 0x34, 0x64, 0x37, 0x34, 0x61, 0x30, 0x65, 0x30, 0x31, 0x61, 0x61, 0x66, 0x33, 0x64,
+    0x64, 0x66, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0c, 0x0c, 0x03, 0x54, 0x45, 0x45,
+    0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x33, 0x32, 0x31, 0x32, 0x31, 0x32, 0x35, 0x31, 0x33,
+    0x5a, 0x17, 0x0d, 0x32, 0x38, 0x30, 0x33, 0x31, 0x38, 0x32, 0x31, 0x32, 0x35, 0x31, 0x33, 0x5a,
+    0x30, 0x29, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x34, 0x34, 0x61,
+    0x38, 0x31, 0x65, 0x61, 0x65, 0x63, 0x35, 0x31, 0x64, 0x62, 0x30, 0x62, 0x31, 0x31, 0x0c, 0x30,
+    0x0a, 0x06, 0x03, 0x55, 0x04, 0x0c, 0x0c, 0x03, 0x54, 0x45, 0x45, 0x30, 0x59, 0x30, 0x13, 0x06,
+    0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03,
+    0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xfa, 0x35, 0x8f, 0xb0, 0x31, 0xd6, 0x30, 0x88, 0xde, 0xb0,
+    0x29, 0xcd, 0x6c, 0x7d, 0x4e, 0xa9, 0xce, 0x6e, 0x9d, 0x7a, 0xac, 0x97, 0x92, 0xc2, 0x45, 0xb5,
+    0xe2, 0xd0, 0xc1, 0x52, 0xa8, 0x50, 0x25, 0xd7, 0x89, 0x58, 0x7b, 0x04, 0xb6, 0x66, 0x93, 0x2a,
+    0x26, 0x5d, 0x3a, 0xb1, 0x5b, 0x77, 0x30, 0xbf, 0x95, 0xaa, 0x8b, 0x43, 0xc3, 0xbf, 0x43, 0xb7,
+    0xee, 0xac, 0x73, 0xdc, 0x03, 0x6a, 0xa3, 0x81, 0xba, 0x30, 0x81, 0xb7, 0x30, 0x1d, 0x06, 0x03,
+    0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x19, 0x9f, 0x87, 0x8b, 0x56, 0xf4, 0x99, 0x3a, 0x69,
+    0x96, 0x9b, 0x8d, 0x9e, 0x64, 0xaa, 0x56, 0xb4, 0x7f, 0x8b, 0x4d, 0x30, 0x1f, 0x06, 0x03, 0x55,
+    0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xa9, 0xb5, 0xf4, 0x29, 0xc9, 0x1a, 0x58, 0xbd,
+    0x2f, 0x98, 0x2d, 0x67, 0x73, 0x31, 0x06, 0x87, 0xe0, 0xdf, 0xcd, 0x62, 0x30, 0x0f, 0x06, 0x03,
+    0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06,
+    0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x54, 0x06,
+    0x03, 0x55, 0x1d, 0x1f, 0x04, 0x4d, 0x30, 0x4b, 0x30, 0x49, 0xa0, 0x47, 0xa0, 0x45, 0x86, 0x43,
+    0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+    0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61,
+    0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x30,
+    0x35, 0x38, 0x34, 0x32, 0x30, 0x32, 0x36, 0x39, 0x30, 0x37, 0x36, 0x32, 0x33, 0x35, 0x38, 0x37,
+    0x31, 0x37, 0x37, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03,
+    0x69, 0x00, 0x30, 0x66, 0x02, 0x31, 0x00, 0xe3, 0x35, 0xc6, 0xa8, 0xb2, 0x75, 0x9c, 0x56, 0x7b,
+    0x6e, 0x61, 0x80, 0x65, 0x2c, 0x06, 0x88, 0xdd, 0xb9, 0x68, 0x4d, 0x3c, 0x68, 0x49, 0x66, 0x01,
+    0x4e, 0x30, 0x1d, 0xf3, 0xec, 0xa5, 0x51, 0x5c, 0xbf, 0xe7, 0x83, 0x33, 0xbd, 0x14, 0xee, 0x23,
+    0xf0, 0xcf, 0xb1, 0x37, 0x1c, 0x27, 0x78, 0x02, 0x31, 0x00, 0x94, 0xcb, 0x08, 0x3d, 0x2d, 0x3e,
+    0x69, 0x54, 0x5f, 0x63, 0xe3, 0xe4, 0x74, 0x72, 0xe2, 0xff, 0x8b, 0x26, 0xd2, 0x86, 0xc0, 0x97,
+    0x32, 0x40, 0xdd, 0x7c, 0x1f, 0x50, 0x60, 0x57, 0xcf, 0x2e, 0x23, 0xf3, 0x33, 0xe4, 0xfb, 0x6f,
+    0x5b, 0x7c, 0xc6, 0x31, 0x85, 0xae, 0xe0, 0x4e, 0x44, 0xa9, 0x30, 0x82, 0x03, 0xd1, 0x30, 0x82,
+    0x01, 0xb9, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x0a, 0x03, 0x88, 0x26, 0x67, 0x60, 0x65, 0x89,
+    0x96, 0x85, 0x7f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+    0x05, 0x00, 0x30, 0x1b, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x66,
+    0x39, 0x32, 0x30, 0x30, 0x39, 0x65, 0x38, 0x35, 0x33, 0x62, 0x36, 0x62, 0x30, 0x34, 0x35, 0x30,
+    0x1e, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x33, 0x32, 0x31, 0x32, 0x31, 0x31, 0x34, 0x31, 0x34, 0x5a,
+    0x17, 0x0d, 0x32, 0x38, 0x30, 0x33, 0x31, 0x38, 0x32, 0x31, 0x31, 0x34, 0x31, 0x34, 0x5a, 0x30,
+    0x29, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10, 0x34, 0x64, 0x37, 0x34,
+    0x61, 0x30, 0x65, 0x30, 0x31, 0x61, 0x61, 0x66, 0x33, 0x64, 0x64, 0x66, 0x31, 0x0c, 0x30, 0x0a,
+    0x06, 0x03, 0x55, 0x04, 0x0c, 0x0c, 0x03, 0x54, 0x45, 0x45, 0x30, 0x76, 0x30, 0x10, 0x06, 0x07,
+    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62,
+    0x00, 0x04, 0xd5, 0xf5, 0x0e, 0xe2, 0x8d, 0xf3, 0x33, 0x4a, 0x6a, 0x77, 0x90, 0x9c, 0xc2, 0x25,
+    0xc8, 0x8a, 0x32, 0xae, 0x3b, 0xb4, 0x9c, 0x4a, 0x95, 0x22, 0x0c, 0xba, 0x0a, 0x76, 0xca, 0xcb,
+    0x24, 0x0c, 0x84, 0x3a, 0x83, 0x76, 0x04, 0x23, 0x31, 0x3a, 0xa0, 0x82, 0x80, 0x26, 0x65, 0xfd,
+    0x2f, 0x44, 0xf4, 0x96, 0xd8, 0xb7, 0xdc, 0xac, 0x55, 0x34, 0x74, 0x41, 0x0d, 0x0d, 0x7f, 0xbd,
+    0xe3, 0xf4, 0x28, 0xdf, 0x74, 0x4a, 0x17, 0x4d, 0xe7, 0xb2, 0x9b, 0x2b, 0x24, 0xc0, 0x9e, 0x56,
+    0x00, 0x52, 0xbb, 0x75, 0xb0, 0xd5, 0x6a, 0x41, 0x16, 0x08, 0xce, 0x32, 0xdb, 0x8f, 0x8b, 0x20,
+    0x73, 0x72, 0xa3, 0x81, 0xb6, 0x30, 0x81, 0xb3, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+    0x16, 0x04, 0x14, 0xa9, 0xb5, 0xf4, 0x29, 0xc9, 0x1a, 0x58, 0xbd, 0x2f, 0x98, 0x2d, 0x67, 0x73,
+    0x31, 0x06, 0x87, 0xe0, 0xdf, 0xcd, 0x62, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
+    0x30, 0x16, 0x80, 0x14, 0x36, 0x61, 0xe1, 0x00, 0x7c, 0x88, 0x05, 0x09, 0x51, 0x8b, 0x44, 0x6c,
+    0x47, 0xff, 0x1a, 0x4c, 0xc9, 0xea, 0x4f, 0x12, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+    0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
+    0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x50, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+    0x04, 0x49, 0x30, 0x47, 0x30, 0x45, 0xa0, 0x43, 0xa0, 0x41, 0x86, 0x3f, 0x68, 0x74, 0x74, 0x70,
+    0x73, 0x3a, 0x2f, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+    0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73,
+    0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x45, 0x38, 0x46, 0x41, 0x31,
+    0x39, 0x36, 0x33, 0x31, 0x34, 0x44, 0x32, 0x46, 0x41, 0x31, 0x38, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+    0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x32,
+    0xce, 0x04, 0xcc, 0x4d, 0x82, 0xad, 0x1d, 0xde, 0xa5, 0xcf, 0xe2, 0x1a, 0xa3, 0x79, 0xf7, 0xed,
+    0x88, 0x1e, 0x0e, 0x67, 0x8e, 0xfc, 0xbe, 0x7b, 0x04, 0xb7, 0x26, 0x59, 0xca, 0x95, 0x47, 0x8a,
+    0x10, 0x3f, 0xe5, 0x14, 0x19, 0xec, 0xd4, 0xdb, 0x33, 0xc3, 0xa1, 0x51, 0xf5, 0x06, 0x5e, 0x30,
+    0x66, 0x1f, 0xd2, 0x58, 0x2f, 0x14, 0x03, 0x7b, 0x35, 0x83, 0x86, 0x46, 0xdc, 0xee, 0x04, 0x30,
+    0xa1, 0x0f, 0xc4, 0x16, 0xc9, 0x8e, 0x63, 0xd0, 0xda, 0x5c, 0xb0, 0xf7, 0x3e, 0x21, 0xb6, 0xa5,
+    0x04, 0x07, 0x5a, 0x01, 0x8c, 0x31, 0x1f, 0x3e, 0x3a, 0xaf, 0x8d, 0x31, 0x3e, 0xb6, 0x12, 0x14,
+    0xf0, 0x0d, 0x2c, 0xcc, 0x6c, 0xb8, 0x7a, 0xbf, 0xd2, 0x6b, 0x5f, 0x27, 0xb0, 0xff, 0xc0, 0xaa,
+    0xde, 0xde, 0xf6, 0x31, 0x6d, 0xf3, 0x95, 0xc2, 0xd4, 0x90, 0xdc, 0x82, 0x4f, 0x24, 0x0d, 0x85,
+    0xf2, 0xbb, 0xc4, 0x58, 0xc9, 0xfa, 0xdd, 0x96, 0x41, 0x2b, 0x1f, 0x4c, 0x10, 0x1c, 0x9a, 0x57,
+    0x55, 0x0f, 0x62, 0xfc, 0x8d, 0xa2, 0xca, 0x84, 0x7b, 0x16, 0x60, 0xe8, 0x62, 0xce, 0x92, 0x85,
+    0x13, 0xf0, 0x63, 0x83, 0xd8, 0x5b, 0xa8, 0x74, 0x78, 0xb5, 0x28, 0xdb, 0x6c, 0xc9, 0x6e, 0x85,
+    0x85, 0x52, 0x3f, 0xd8, 0x67, 0xae, 0xf4, 0x09, 0xbe, 0xcf, 0x8c, 0x7f, 0x72, 0xb2, 0xc8, 0x93,
+    0xc6, 0xd2, 0xf3, 0x38, 0x74, 0x71, 0x22, 0xd6, 0x92, 0x76, 0xb1, 0xae, 0x14, 0x5a, 0x09, 0xd8,
+    0xaf, 0x1d, 0xaf, 0x48, 0x22, 0x5c, 0x30, 0x85, 0x8e, 0xc2, 0xfe, 0x61, 0xaf, 0xc3, 0xd2, 0x4c,
+    0x92, 0x53, 0xa4, 0x75, 0x1f, 0x78, 0xea, 0xfc, 0xfa, 0xc4, 0xca, 0x4e, 0x67, 0x68, 0x1f, 0x7d,
+    0xb2, 0x5e, 0xea, 0x8a, 0xb1, 0xcc, 0xb6, 0x92, 0x64, 0xf8, 0x82, 0xc0, 0x8b, 0xdc, 0x24, 0xe8,
+    0x57, 0x20, 0x33, 0x6d, 0x17, 0x33, 0x0d, 0xcb, 0x70, 0x02, 0x8b, 0xe5, 0xe3, 0x7d, 0x2c, 0x98,
+    0x32, 0x00, 0x20, 0xb4, 0xbd, 0xee, 0x89, 0xaa, 0x66, 0x13, 0x34, 0x9d, 0x9c, 0x8f, 0xde, 0x16,
+    0x09, 0x91, 0x49, 0x80, 0x50, 0x57, 0x39, 0xae, 0x35, 0x01, 0xe2, 0x25, 0x8e, 0x17, 0x08, 0xe0,
+    0xf0, 0x77, 0x98, 0x9d, 0x0a, 0x4f, 0xd2, 0x76, 0xda, 0xc4, 0x51, 0x45, 0x32, 0x8b, 0xe1, 0xab,
+    0xee, 0x10, 0x16, 0xf6, 0x95, 0x7d, 0x32, 0x76, 0xb2, 0xb5, 0x19, 0x67, 0x73, 0xfe, 0xc0, 0xc6,
+    0xa9, 0xd2, 0xa9, 0x23, 0xf0, 0x2b, 0xfc, 0xb1, 0xb6, 0xec, 0x3e, 0x11, 0x60, 0xa4, 0x22, 0xc7,
+    0xff, 0x25, 0xc3, 0xed, 0x6c, 0x6b, 0x79, 0x02, 0x3d, 0x5d, 0x62, 0x36, 0xd9, 0x32, 0xe4, 0x6e,
+    0x47, 0x67, 0x85, 0x8b, 0x23, 0x0a, 0xd5, 0x1e, 0xd0, 0xf4, 0x17, 0x1d, 0xcc, 0x3f, 0x5f, 0xda,
+    0x12, 0xe2, 0x35, 0x25, 0x52, 0xc2, 0xd6, 0x94, 0x3e, 0x83, 0x60, 0x55, 0xf8, 0x8d, 0x54, 0xf5,
+    0x47, 0x6f, 0x38, 0x03, 0x3b, 0xd7, 0x9a, 0x94, 0x8a, 0x3b, 0x9f, 0x92, 0x69, 0x0f, 0xcd, 0xb8,
+    0xf4, 0x62, 0x78, 0x22, 0x47, 0xe0, 0xae, 0xed, 0xfd, 0xf6, 0xe4, 0xc5, 0x8c, 0x0e, 0xb5, 0x18,
+    0xb1, 0x46, 0x3a, 0x6f, 0xbd, 0xde, 0x50, 0x3f, 0x1c, 0x35, 0x28, 0xf9, 0xed, 0x1e, 0xe8, 0x15,
+    0x31, 0xa9, 0xf7, 0xb1, 0x9d, 0xe1, 0x34, 0x81, 0x20, 0x1f, 0x22, 0xd4, 0xb7, 0xc6, 0x59, 0x8b,
+    0x90, 0x98, 0xdf, 0xa6, 0xb9, 0xa8, 0x8e, 0x6c, 0x15, 0x55, 0x5c, 0x41, 0x96, 0x82, 0x0d, 0xa9,
+    0x5f, 0xa9, 0xf3, 0x77, 0x1d, 0xee, 0x6b, 0x4c, 0x94, 0xc6, 0xc6, 0x9b, 0x78, 0x5b, 0x03, 0xbd,
+    0xa9, 0x87, 0xdd, 0x24, 0x04, 0x70, 0xce, 0x6c, 0x52, 0xe6, 0x21, 0x63, 0x6d, 0x28, 0x6c, 0x30,
+    0x82, 0x05, 0x60, 0x30, 0x82, 0x03, 0x48, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0xe8,
+    0xfa, 0x19, 0x63, 0x14, 0xd2, 0xfa, 0x18, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+    0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x1b, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04,
+    0x05, 0x13, 0x10, 0x66, 0x39, 0x32, 0x30, 0x30, 0x39, 0x65, 0x38, 0x35, 0x33, 0x62, 0x36, 0x62,
+    0x30, 0x34, 0x35, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x36, 0x30, 0x35, 0x32, 0x36, 0x31, 0x36, 0x32,
+    0x38, 0x35, 0x32, 0x5a, 0x17, 0x0d, 0x32, 0x36, 0x30, 0x35, 0x32, 0x34, 0x31, 0x36, 0x32, 0x38,
+    0x35, 0x32, 0x5a, 0x30, 0x1b, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x10,
+    0x66, 0x39, 0x32, 0x30, 0x30, 0x39, 0x65, 0x38, 0x35, 0x33, 0x62, 0x36, 0x62, 0x30, 0x34, 0x35,
+    0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+    0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01,
+    0x00, 0xaf, 0xb6, 0xc7, 0x82, 0x2b, 0xb1, 0xa7, 0x01, 0xec, 0x2b, 0xb4, 0x2e, 0x8b, 0xcc, 0x54,
+    0x16, 0x63, 0xab, 0xef, 0x98, 0x2f, 0x32, 0xc7, 0x7f, 0x75, 0x31, 0x03, 0x0c, 0x97, 0x52, 0x4b,
+    0x1b, 0x5f, 0xe8, 0x09, 0xfb, 0xc7, 0x2a, 0xa9, 0x45, 0x1f, 0x74, 0x3c, 0xbd, 0x9a, 0x6f, 0x13,
+    0x35, 0x74, 0x4a, 0xa5, 0x5e, 0x77, 0xf6, 0xb6, 0xac, 0x35, 0x35, 0xee, 0x17, 0xc2, 0x5e, 0x63,
+    0x95, 0x17, 0xdd, 0x9c, 0x92, 0xe6, 0x37, 0x4a, 0x53, 0xcb, 0xfe, 0x25, 0x8f, 0x8f, 0xfb, 0xb6,
+    0xfd, 0x12, 0x93, 0x78, 0xa2, 0x2a, 0x4c, 0xa9, 0x9c, 0x45, 0x2d, 0x47, 0xa5, 0x9f, 0x32, 0x01,
+    0xf4, 0x41, 0x97, 0xca, 0x1c, 0xcd, 0x7e, 0x76, 0x2f, 0xb2, 0xf5, 0x31, 0x51, 0xb6, 0xfe, 0xb2,
+    0xff, 0xfd, 0x2b, 0x6f, 0xe4, 0xfe, 0x5b, 0xc6, 0xbd, 0x9e, 0xc3, 0x4b, 0xfe, 0x08, 0x23, 0x9d,
+    0xaa, 0xfc, 0xeb, 0x8e, 0xb5, 0xa8, 0xed, 0x2b, 0x3a, 0xcd, 0x9c, 0x5e, 0x3a, 0x77, 0x90, 0xe1,
+    0xb5, 0x14, 0x42, 0x79, 0x31, 0x59, 0x85, 0x98, 0x11, 0xad, 0x9e, 0xb2, 0xa9, 0x6b, 0xbd, 0xd7,
+    0xa5, 0x7c, 0x93, 0xa9, 0x1c, 0x41, 0xfc, 0xcd, 0x27, 0xd6, 0x7f, 0xd6, 0xf6, 0x71, 0xaa, 0x0b,
+    0x81, 0x52, 0x61, 0xad, 0x38, 0x4f, 0xa3, 0x79, 0x44, 0x86, 0x46, 0x04, 0xdd, 0xb3, 0xd8, 0xc4,
+    0xf9, 0x20, 0xa1, 0x9b, 0x16, 0x56, 0xc2, 0xf1, 0x4a, 0xd6, 0xd0, 0x3c, 0x56, 0xec, 0x06, 0x08,
+    0x99, 0x04, 0x1c, 0x1e, 0xd1, 0xa5, 0xfe, 0x6d, 0x34, 0x40, 0xb5, 0x56, 0xba, 0xd1, 0xd0, 0xa1,
+    0x52, 0x58, 0x9c, 0x53, 0xe5, 0x5d, 0x37, 0x07, 0x62, 0xf0, 0x12, 0x2e, 0xef, 0x91, 0x86, 0x1b,
+    0x1b, 0x0e, 0x6c, 0x4c, 0x80, 0x92, 0x74, 0x99, 0xc0, 0xe9, 0xbe, 0xc0, 0xb8, 0x3e, 0x3b, 0xc1,
+    0xf9, 0x3c, 0x72, 0xc0, 0x49, 0x60, 0x4b, 0xbd, 0x2f, 0x13, 0x45, 0xe6, 0x2c, 0x3f, 0x8e, 0x26,
+    0xdb, 0xec, 0x06, 0xc9, 0x47, 0x66, 0xf3, 0xc1, 0x28, 0x23, 0x9d, 0x4f, 0x43, 0x12, 0xfa, 0xd8,
+    0x12, 0x38, 0x87, 0xe0, 0x6b, 0xec, 0xf5, 0x67, 0x58, 0x3b, 0xf8, 0x35, 0x5a, 0x81, 0xfe, 0xea,
+    0xba, 0xf9, 0x9a, 0x83, 0xc8, 0xdf, 0x3e, 0x2a, 0x32, 0x2a, 0xfc, 0x67, 0x2b, 0xf1, 0x20, 0xb1,
+    0x35, 0x15, 0x8b, 0x68, 0x21, 0xce, 0xaf, 0x30, 0x9b, 0x6e, 0xee, 0x77, 0xf9, 0x88, 0x33, 0xb0,
+    0x18, 0xda, 0xa1, 0x0e, 0x45, 0x1f, 0x06, 0xa3, 0x74, 0xd5, 0x07, 0x81, 0xf3, 0x59, 0x08, 0x29,
+    0x66, 0xbb, 0x77, 0x8b, 0x93, 0x08, 0x94, 0x26, 0x98, 0xe7, 0x4e, 0x0b, 0xcd, 0x24, 0x62, 0x8a,
+    0x01, 0xc2, 0xcc, 0x03, 0xe5, 0x1f, 0x0b, 0x3e, 0x5b, 0x4a, 0xc1, 0xe4, 0xdf, 0x9e, 0xaf, 0x9f,
+    0xf6, 0xa4, 0x92, 0xa7, 0x7c, 0x14, 0x83, 0x88, 0x28, 0x85, 0x01, 0x5b, 0x42, 0x2c, 0xe6, 0x7b,
+    0x80, 0xb8, 0x8c, 0x9b, 0x48, 0xe1, 0x3b, 0x60, 0x7a, 0xb5, 0x45, 0xc7, 0x23, 0xff, 0x8c, 0x44,
+    0xf8, 0xf2, 0xd3, 0x68, 0xb9, 0xf6, 0x52, 0x0d, 0x31, 0x14, 0x5e, 0xbf, 0x9e, 0x86, 0x2a, 0xd7,
+    0x1d, 0xf6, 0xa3, 0xbf, 0xd2, 0x45, 0x09, 0x59, 0xd6, 0x53, 0x74, 0x0d, 0x97, 0xa1, 0x2f, 0x36,
+    0x8b, 0x13, 0xef, 0x66, 0xd5, 0xd0, 0xa5, 0x4a, 0x6e, 0x2f, 0x5d, 0x9a, 0x6f, 0xef, 0x44, 0x68,
+    0x32, 0xbc, 0x67, 0x84, 0x47, 0x25, 0x86, 0x1f, 0x09, 0x3d, 0xd0, 0xe6, 0xf3, 0x40, 0x5d, 0xa8,
+    0x96, 0x43, 0xef, 0x0f, 0x4d, 0x69, 0xb6, 0x42, 0x00, 0x51, 0xfd, 0xb9, 0x30, 0x49, 0x67, 0x3e,
+    0x36, 0x95, 0x05, 0x80, 0xd3, 0xcd, 0xf4, 0xfb, 0xd0, 0x8b, 0xc5, 0x84, 0x83, 0x95, 0x26, 0x00,
+    0x63, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa6, 0x30, 0x81, 0xa3, 0x30, 0x1d, 0x06, 0x03,
+    0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x36, 0x61, 0xe1, 0x00, 0x7c, 0x88, 0x05, 0x09, 0x51,
+    0x8b, 0x44, 0x6c, 0x47, 0xff, 0x1a, 0x4c, 0xc9, 0xea, 0x4f, 0x12, 0x30, 0x1f, 0x06, 0x03, 0x55,
+    0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x36, 0x61, 0xe1, 0x00, 0x7c, 0x88, 0x05, 0x09,
+    0x51, 0x8b, 0x44, 0x6c, 0x47, 0xff, 0x1a, 0x4c, 0xc9, 0xea, 0x4f, 0x12, 0x30, 0x0f, 0x06, 0x03,
+    0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06,
+    0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x40, 0x06,
+    0x03, 0x55, 0x1d, 0x1f, 0x04, 0x39, 0x30, 0x37, 0x30, 0x35, 0xa0, 0x33, 0xa0, 0x31, 0x86, 0x2f,
+    0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+    0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61,
+    0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x30,
+    0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82,
+    0x02, 0x01, 0x00, 0x20, 0xc8, 0xc3, 0x8d, 0x4b, 0xdc, 0xa9, 0x57, 0x1b, 0x46, 0x8c, 0x89, 0x2f,
+    0xff, 0x72, 0xaa, 0xc6, 0xf8, 0x44, 0xa1, 0x1d, 0x41, 0xa8, 0xf0, 0x73, 0x6c, 0xc3, 0x7d, 0x16,
+    0xd6, 0x42, 0x6d, 0x8e, 0x7e, 0x94, 0x07, 0x04, 0x4c, 0xea, 0x39, 0xe6, 0x8b, 0x07, 0xc1, 0x3d,
+    0xbf, 0x15, 0x03, 0xdd, 0x5c, 0x85, 0xbd, 0xaf, 0xb2, 0xc0, 0x2d, 0x5f, 0x6c, 0xdb, 0x4e, 0xfa,
+    0x81, 0x27, 0xdf, 0x8b, 0x04, 0xf1, 0x82, 0x77, 0x0f, 0xc4, 0xe7, 0x74, 0x5b, 0x7f, 0xce, 0xaa,
+    0x87, 0x12, 0x9a, 0x88, 0x01, 0xce, 0x8e, 0x9b, 0xc0, 0xcb, 0x96, 0x37, 0x9b, 0x4d, 0x26, 0xa8,
+    0x2d, 0x30, 0xfd, 0x9c, 0x2f, 0x8e, 0xed, 0x6d, 0xc1, 0xbe, 0x2f, 0x84, 0xb6, 0x89, 0xe4, 0xd9,
+    0x14, 0x25, 0x8b, 0x14, 0x4b, 0xba, 0xe6, 0x24, 0xa1, 0xc7, 0x06, 0x71, 0x13, 0x2e, 0x2f, 0x06,
+    0x16, 0xa8, 0x84, 0xb2, 0xa4, 0xd6, 0xa4, 0x6f, 0xfa, 0x89, 0xb6, 0x02, 0xbf, 0xba, 0xd8, 0x0c,
+    0x12, 0x43, 0x71, 0x1f, 0x56, 0xeb, 0x60, 0x56, 0xf6, 0x37, 0xc8, 0xa0, 0x14, 0x1c, 0xc5, 0x40,
+    0x94, 0x26, 0x8b, 0x8c, 0x3c, 0x7d, 0xb9, 0x94, 0xb3, 0x5c, 0x0d, 0xcd, 0x6c, 0xb2, 0xab, 0xc2,
+    0xda, 0xfe, 0xe2, 0x52, 0x02, 0x3d, 0x2d, 0xea, 0x0c, 0xd6, 0xc3, 0x68, 0xbe, 0xa3, 0xe6, 0x41,
+    0x48, 0x86, 0xf6, 0xb1, 0xe5, 0x8b, 0x5b, 0xd7, 0xc7, 0x30, 0xb2, 0x68, 0xc4, 0xe3, 0xc1, 0xfb,
+    0x64, 0x24, 0xb9, 0x1f, 0xeb, 0xbd, 0xb8, 0x0c, 0x58, 0x6e, 0x2a, 0xe8, 0x36, 0x8c, 0x84, 0xd5,
+    0xd1, 0x09, 0x17, 0xbd, 0xa2, 0x56, 0x17, 0x89, 0xd4, 0x68, 0x73, 0x93, 0x34, 0x0e, 0x2e, 0x25,
+    0x4f, 0x56, 0x0e, 0xf6, 0x4b, 0x23, 0x58, 0xfc, 0xdc, 0x0f, 0xbf, 0xc6, 0x70, 0x09, 0x52, 0xe7,
+    0x08, 0xbf, 0xfc, 0xc6, 0x27, 0x50, 0x0c, 0x1f, 0x66, 0xe8, 0x1e, 0xa1, 0x7c, 0x09, 0x8d, 0x7a,
+    0x2e, 0x9b, 0x18, 0x80, 0x1b, 0x7a, 0xb4, 0xac, 0x71, 0x58, 0x7d, 0x34, 0x5d, 0xcc, 0x83, 0x09,
+    0xd5, 0xb6, 0x2a, 0x50, 0x42, 0x7a, 0xa6, 0xd0, 0x3d, 0xcb, 0x05, 0x99, 0x6c, 0x96, 0xba, 0x0c,
+    0x5d, 0x71, 0xe9, 0x21, 0x62, 0xc0, 0x16, 0xca, 0x84, 0x9f, 0xf3, 0x5f, 0x0d, 0x52, 0xc6, 0x5d,
+    0x05, 0x60, 0x5a, 0x47, 0xf3, 0xae, 0x91, 0x7a, 0xcd, 0x2d, 0xf9, 0x10, 0xef, 0xd2, 0x32, 0x66,
+    0x88, 0x59, 0x6e, 0xf6, 0x9b, 0x3b, 0xf5, 0xfe, 0x31, 0x54, 0xf7, 0xae, 0xb8, 0x80, 0xa0, 0xa7,
+    0x3c, 0xa0, 0x4d, 0x94, 0xc2, 0xce, 0x83, 0x17, 0xee, 0xb4, 0x3d, 0x5e, 0xff, 0x58, 0x83, 0xe3,
+    0x36, 0xf5, 0xf2, 0x49, 0xda, 0xac, 0xa4, 0x89, 0x92, 0x37, 0xbf, 0x26, 0x7e, 0x5c, 0x43, 0xab,
+    0x02, 0xea, 0x44, 0x16, 0x24, 0x03, 0x72, 0x3b, 0xe6, 0xaa, 0x69, 0x2c, 0x61, 0xbd, 0xae, 0x9e,
+    0xd4, 0x09, 0xd4, 0x63, 0xc4, 0xc9, 0x7c, 0x64, 0x30, 0x65, 0x77, 0xee, 0xf2, 0xbc, 0x75, 0x60,
+    0xb7, 0x57, 0x15, 0xcc, 0x9c, 0x7d, 0xc6, 0x7c, 0x86, 0x08, 0x2d, 0xb7, 0x51, 0xa8, 0x9c, 0x30,
+    0x34, 0x97, 0x62, 0xb0, 0x78, 0x23, 0x85, 0x87, 0x5c, 0xf1, 0xa3, 0xc6, 0x16, 0x6e, 0x0a, 0xe3,
+    0xc1, 0x2d, 0x37, 0x4e, 0x2d, 0x4f, 0x18, 0x46, 0xf3, 0x18, 0x74, 0x4b, 0xd8, 0x79, 0xb5, 0x87,
+    0x32, 0x9b, 0xf0, 0x18, 0x21, 0x7a, 0x6c, 0x0c, 0x77, 0x24, 0x1a, 0x48, 0x78, 0xe4, 0x35, 0xc0,
+    0x30, 0x79, 0xcb, 0x45, 0x12, 0x89, 0xc5, 0x77, 0x62, 0x06, 0x06, 0x9a, 0x2f, 0x8d, 0x65, 0xf8,
+    0x40, 0xe1, 0x44, 0x52, 0x87, 0xbe, 0xd8, 0x77, 0xab, 0xae, 0x24, 0xe2, 0x44, 0x35, 0x16, 0x8d,
+    0x55, 0x3c, 0xe4,
+];
diff --git a/keystore2/src/legacy_migrator.rs b/keystore2/src/legacy_migrator.rs
new file mode 100644
index 0000000..d5647cd
--- /dev/null
+++ b/keystore2/src/legacy_migrator.rs
@@ -0,0 +1,730 @@
+// 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.
+
+//! This module acts as a bridge between the legacy key database and the keystore2 database.
+
+use crate::error::Error;
+use crate::key_parameter::KeyParameterValue;
+use crate::legacy_blob::BlobValue;
+use crate::utils::{uid_to_android_user, watchdog as wd};
+use crate::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader};
+use crate::{
+    database::{
+        BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, EncryptedBy, KeyMetaData,
+        KeyMetaEntry, KeystoreDB, Uuid, KEYSTORE_UUID,
+    },
+    super_key::USER_SUPER_KEY,
+};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+};
+use anyhow::{Context, Result};
+use core::ops::Deref;
+use keystore2_crypto::{Password, ZVec};
+use std::collections::{HashMap, HashSet};
+use std::sync::atomic::{AtomicU8, Ordering};
+use std::sync::mpsc::channel;
+use std::sync::{Arc, Mutex};
+
+/// Represents LegacyMigrator.
+pub struct LegacyMigrator {
+    async_task: Arc<AsyncTask>,
+    initializer: Mutex<
+        Option<
+            Box<
+                dyn FnOnce() -> (KeystoreDB, HashMap<SecurityLevel, Uuid>, Arc<LegacyBlobLoader>)
+                    + Send
+                    + 'static,
+            >,
+        >,
+    >,
+    /// This atomic is used for cheap interior mutability. It is intended to prevent
+    /// expensive calls into the legacy migrator when the legacy database is empty.
+    /// When transitioning from READY to EMPTY, spurious calls may occur for a brief period
+    /// of time. This is tolerable in favor of the common case.
+    state: AtomicU8,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct RecentMigration {
+    uid: u32,
+    alias: String,
+}
+
+impl RecentMigration {
+    fn new(uid: u32, alias: String) -> Self {
+        Self { uid, alias }
+    }
+}
+
+enum BulkDeleteRequest {
+    Uid(u32),
+    User(u32),
+}
+
+struct LegacyMigratorState {
+    recently_migrated: HashSet<RecentMigration>,
+    recently_migrated_super_key: HashSet<u32>,
+    legacy_loader: Arc<LegacyBlobLoader>,
+    sec_level_to_km_uuid: HashMap<SecurityLevel, Uuid>,
+    db: KeystoreDB,
+}
+
+impl LegacyMigrator {
+    const WIFI_NAMESPACE: i64 = 102;
+    const AID_WIFI: u32 = 1010;
+
+    const STATE_UNINITIALIZED: u8 = 0;
+    const STATE_READY: u8 = 1;
+    const STATE_EMPTY: u8 = 2;
+
+    /// Constructs a new LegacyMigrator using the given AsyncTask object as migration
+    /// worker.
+    pub fn new(async_task: Arc<AsyncTask>) -> Self {
+        Self {
+            async_task,
+            initializer: Default::default(),
+            state: AtomicU8::new(Self::STATE_UNINITIALIZED),
+        }
+    }
+
+    /// The legacy migrator must be initialized deferred, because keystore starts very early.
+    /// At this time the data partition may not be mounted. So we cannot open database connections
+    /// until we get actual key load requests. This sets the function that the legacy loader
+    /// uses to connect to the database.
+    pub fn set_init<F>(&self, f_init: F) -> Result<()>
+    where
+        F: FnOnce() -> (KeystoreDB, HashMap<SecurityLevel, Uuid>, Arc<LegacyBlobLoader>)
+            + Send
+            + 'static,
+    {
+        let mut initializer = self.initializer.lock().expect("Failed to lock initializer.");
+
+        // If we are not uninitialized we have no business setting the initializer.
+        if self.state.load(Ordering::Relaxed) != Self::STATE_UNINITIALIZED {
+            return Ok(());
+        }
+
+        // Only set the initializer if it hasn't been set before.
+        if initializer.is_none() {
+            *initializer = Some(Box::new(f_init))
+        }
+
+        Ok(())
+    }
+
+    /// This function is called by the migration requestor to check if it is worth
+    /// making a migration request. It also transitions the state from UNINITIALIZED
+    /// to READY or EMPTY on first use. The deferred initialization is necessary, because
+    /// Keystore 2.0 runs early during boot, where data may not yet be mounted.
+    /// Returns Ok(STATE_READY) if a migration request is worth undertaking and
+    /// Ok(STATE_EMPTY) if the database is empty. An error is returned if the loader
+    /// was not initialized and cannot be initialized.
+    fn check_state(&self) -> Result<u8> {
+        let mut first_try = true;
+        loop {
+            match (self.state.load(Ordering::Relaxed), first_try) {
+                (Self::STATE_EMPTY, _) => {
+                    return Ok(Self::STATE_EMPTY);
+                }
+                (Self::STATE_UNINITIALIZED, true) => {
+                    // If we find the legacy loader uninitialized, we grab the initializer lock,
+                    // check if the legacy database is empty, and if not, schedule an initialization
+                    // request. Coming out of the initializer lock, the state is either EMPTY or
+                    // READY.
+                    let mut initializer = self.initializer.lock().unwrap();
+
+                    if let Some(initializer) = initializer.take() {
+                        let (db, sec_level_to_km_uuid, legacy_loader) = (initializer)();
+
+                        if legacy_loader.is_empty().context(
+                            "In check_state: Trying to check if the legacy database is empty.",
+                        )? {
+                            self.state.store(Self::STATE_EMPTY, Ordering::Relaxed);
+                            return Ok(Self::STATE_EMPTY);
+                        }
+
+                        self.async_task.queue_hi(move |shelf| {
+                            shelf.get_or_put_with(|| LegacyMigratorState {
+                                recently_migrated: Default::default(),
+                                recently_migrated_super_key: Default::default(),
+                                legacy_loader,
+                                sec_level_to_km_uuid,
+                                db,
+                            });
+                        });
+
+                        // It is safe to set this here even though the async task may not yet have
+                        // run because any thread observing this will not be able to schedule a
+                        // task that can run before the initialization.
+                        // Also we can only transition out of this state while having the
+                        // initializer lock and having found an initializer.
+                        self.state.store(Self::STATE_READY, Ordering::Relaxed);
+                        return Ok(Self::STATE_READY);
+                    } else {
+                        // There is a chance that we just lost the race from state.load() to
+                        // grabbing the initializer mutex. If that is the case the state must
+                        // be EMPTY or READY after coming out of the lock. So we can give it
+                        // one more try.
+                        first_try = false;
+                        continue;
+                    }
+                }
+                (Self::STATE_UNINITIALIZED, false) => {
+                    // Okay, tough luck. The legacy loader was really completely uninitialized.
+                    return Err(Error::sys()).context(
+                        "In check_state: Legacy loader should not be called uninitialized.",
+                    );
+                }
+                (Self::STATE_READY, _) => return Ok(Self::STATE_READY),
+                (s, _) => panic!("Unknown legacy migrator state. {} ", s),
+            }
+        }
+    }
+
+    /// List all aliases for uid in the legacy database.
+    pub fn list_uid(&self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
+        let _wp = wd::watch_millis("LegacyMigrator::list_uid", 500);
+
+        let uid = match (domain, namespace) {
+            (Domain::APP, namespace) => namespace as u32,
+            (Domain::SELINUX, Self::WIFI_NAMESPACE) => Self::AID_WIFI,
+            _ => return Ok(Vec::new()),
+        };
+        self.do_serialized(move |state| state.list_uid(uid)).unwrap_or_else(|| Ok(Vec::new())).map(
+            |v| {
+                v.into_iter()
+                    .map(|alias| KeyDescriptor {
+                        domain,
+                        nspace: namespace,
+                        alias: Some(alias),
+                        blob: None,
+                    })
+                    .collect()
+            },
+        )
+    }
+
+    /// Sends the given closure to the migrator thread for execution after calling check_state.
+    /// Returns None if the database was empty and the request was not executed.
+    /// Otherwise returns Some with the result produced by the migration request.
+    /// The loader state may transition to STATE_EMPTY during the execution of this function.
+    fn do_serialized<F, T: Send + 'static>(&self, f: F) -> Option<Result<T>>
+    where
+        F: FnOnce(&mut LegacyMigratorState) -> Result<T> + Send + 'static,
+    {
+        // Short circuit if the database is empty or not initialized (error case).
+        match self.check_state().context("In do_serialized: Checking state.") {
+            Ok(LegacyMigrator::STATE_EMPTY) => return None,
+            Ok(LegacyMigrator::STATE_READY) => {}
+            Err(e) => return Some(Err(e)),
+            Ok(s) => panic!("Unknown legacy migrator state. {} ", s),
+        }
+
+        // We have established that there may be a key in the legacy database.
+        // Now we schedule a migration request.
+        let (sender, receiver) = channel();
+        self.async_task.queue_hi(move |shelf| {
+            // Get the migrator state from the shelf.
+            // There may not be a state. This can happen if this migration request was scheduled
+            // before a previous request established that the legacy database was empty
+            // and removed the state from the shelf. Since we know now that the database
+            // is empty, we can return None here.
+            let (new_state, result) = if let Some(legacy_migrator_state) =
+                shelf.get_downcast_mut::<LegacyMigratorState>()
+            {
+                let result = f(legacy_migrator_state);
+                (legacy_migrator_state.check_empty(), Some(result))
+            } else {
+                (Self::STATE_EMPTY, None)
+            };
+
+            // If the migration request determined that the database is now empty, we discard
+            // the state from the shelf to free up the resources we won't need any longer.
+            if result.is_some() && new_state == Self::STATE_EMPTY {
+                shelf.remove_downcast_ref::<LegacyMigratorState>();
+            }
+
+            // Send the result to the requester.
+            if let Err(e) = sender.send((new_state, result)) {
+                log::error!("In do_serialized. Error in sending the result. {:?}", e);
+            }
+        });
+
+        let (new_state, result) = match receiver.recv() {
+            Err(e) => {
+                return Some(Err(e).context("In do_serialized. Failed to receive from the sender."))
+            }
+            Ok(r) => r,
+        };
+
+        // We can only transition to EMPTY but never back.
+        // The migrator never creates any legacy blobs.
+        if new_state == Self::STATE_EMPTY {
+            self.state.store(Self::STATE_EMPTY, Ordering::Relaxed)
+        }
+
+        result
+    }
+
+    /// Runs the key_accessor function and returns its result. If it returns an error and the
+    /// root cause was KEY_NOT_FOUND, tries to migrate a key with the given parameters from
+    /// the legacy database to the new database and runs the key_accessor function again if
+    /// the migration request was successful.
+    pub fn with_try_migrate<F, T>(
+        &self,
+        key: &KeyDescriptor,
+        caller_uid: u32,
+        key_accessor: F,
+    ) -> Result<T>
+    where
+        F: Fn() -> Result<T>,
+    {
+        let _wp = wd::watch_millis("LegacyMigrator::with_try_migrate", 500);
+
+        // Access the key and return on success.
+        match key_accessor() {
+            Ok(result) => return Ok(result),
+            Err(e) => match e.root_cause().downcast_ref::<Error>() {
+                Some(&Error::Rc(ResponseCode::KEY_NOT_FOUND)) => {}
+                _ => return Err(e),
+            },
+        }
+
+        // Filter inputs. We can only load legacy app domain keys and some special rules due
+        // to which we migrate keys transparently to an SELINUX domain.
+        let uid = match key {
+            KeyDescriptor { domain: Domain::APP, alias: Some(_), .. } => caller_uid,
+            KeyDescriptor { domain: Domain::SELINUX, nspace, alias: Some(_), .. } => {
+                match *nspace {
+                    Self::WIFI_NAMESPACE => Self::AID_WIFI,
+                    _ => {
+                        return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+                            .context(format!("No legacy keys for namespace {}", nspace))
+                    }
+                }
+            }
+            _ => {
+                return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+                    .context("No legacy keys for key descriptor.")
+            }
+        };
+
+        let key_clone = key.clone();
+        let result = self
+            .do_serialized(move |migrator_state| migrator_state.check_and_migrate(uid, key_clone));
+
+        if let Some(result) = result {
+            result?;
+            // After successful migration try again.
+            key_accessor()
+        } else {
+            Err(Error::Rc(ResponseCode::KEY_NOT_FOUND)).context("Legacy database is empty.")
+        }
+    }
+
+    /// Calls key_accessor and returns the result on success. In the case of a KEY_NOT_FOUND error
+    /// this function makes a migration request and on success retries the key_accessor.
+    pub fn with_try_migrate_super_key<F, T>(
+        &self,
+        user_id: u32,
+        pw: &Password,
+        mut key_accessor: F,
+    ) -> Result<Option<T>>
+    where
+        F: FnMut() -> Result<Option<T>>,
+    {
+        let _wp = wd::watch_millis("LegacyMigrator::with_try_migrate_super_key", 500);
+
+        match key_accessor() {
+            Ok(Some(result)) => return Ok(Some(result)),
+            Ok(None) => {}
+            Err(e) => return Err(e),
+        }
+        let pw = pw.try_clone().context("In with_try_migrate_super_key: Cloning password.")?;
+        let result = self.do_serialized(move |migrator_state| {
+            migrator_state.check_and_migrate_super_key(user_id, &pw)
+        });
+
+        if let Some(result) = result {
+            result?;
+            // After successful migration try again.
+            key_accessor()
+        } else {
+            Ok(None)
+        }
+    }
+
+    /// 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 _wp = wd::watch_millis("LegacyMigrator::bulk_delete_uid", 500);
+
+        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 _wp = wd::watch_millis("LegacyMigrator::bulk_delete_user", 500);
+
+        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 =
+            self.do_serialized(move |migrator_state| migrator_state.has_super_key(user_id));
+        result.unwrap_or(Ok(false))
+    }
+}
+
+impl LegacyMigratorState {
+    fn get_km_uuid(&self, is_strongbox: bool) -> Result<Uuid> {
+        let sec_level = if is_strongbox {
+            SecurityLevel::STRONGBOX
+        } else {
+            SecurityLevel::TRUSTED_ENVIRONMENT
+        };
+
+        self.sec_level_to_km_uuid.get(&sec_level).copied().ok_or_else(|| {
+            anyhow::anyhow!(Error::sys()).context("In get_km_uuid: No KM instance for blob.")
+        })
+    }
+
+    fn list_uid(&mut self, uid: u32) -> Result<Vec<String>> {
+        self.legacy_loader
+            .list_keystore_entries_for_uid(uid)
+            .context("In list_uid: Trying to list legacy entries.")
+    }
+
+    /// This is a key migration request that must run in the migrator thread. This must
+    /// be passed to do_serialized.
+    fn check_and_migrate(&mut self, uid: u32, mut key: KeyDescriptor) -> Result<()> {
+        let alias = key.alias.clone().ok_or_else(|| {
+            anyhow::anyhow!(Error::sys()).context(concat!(
+                "In check_and_migrate: Must be Some because ",
+                "our caller must not have called us otherwise."
+            ))
+        })?;
+
+        if self.recently_migrated.contains(&RecentMigration::new(uid, alias.clone())) {
+            return Ok(());
+        }
+
+        if key.domain == Domain::APP {
+            key.nspace = uid as i64;
+        }
+
+        // If the key is not found in the cache, try to load from the legacy database.
+        let (km_blob_params, user_cert, ca_cert) = self
+            .legacy_loader
+            .load_by_uid_alias(uid, &alias, None)
+            .context("In check_and_migrate: Trying to load legacy blob.")?;
+        let result = match km_blob_params {
+            Some((km_blob, params)) => {
+                let is_strongbox = km_blob.is_strongbox();
+                let (blob, mut blob_metadata) = match km_blob.take_value() {
+                    BlobValue::Encrypted { iv, tag, data } => {
+                        // Get super key id for user id.
+                        let user_id = uid_to_android_user(uid as u32);
+
+                        let super_key_id = match self
+                            .db
+                            .load_super_key(&USER_SUPER_KEY, user_id)
+                            .context("In check_and_migrate: Failed to load super key")?
+                        {
+                            Some((_, entry)) => entry.id(),
+                            None => {
+                                // This might be the first time we access the super key,
+                                // and it may not have been migrated. We cannot import
+                                // the legacy super_key key now, because we need to reencrypt
+                                // it which we cannot do if we are not unlocked, which we are
+                                // not because otherwise the key would have been migrated.
+                                // We can check though if the key exists. If it does,
+                                // we can return Locked. Otherwise, we can delete the
+                                // key and return NotFound, because the key will never
+                                // be unlocked again.
+                                if self.legacy_loader.has_super_key(user_id) {
+                                    return Err(Error::Rc(ResponseCode::LOCKED)).context(concat!(
+                                        "In check_and_migrate: Cannot migrate super key of this ",
+                                        "key while user is locked."
+                                    ));
+                                } else {
+                                    self.legacy_loader.remove_keystore_entry(uid, &alias).context(
+                                        concat!(
+                                            "In check_and_migrate: ",
+                                            "Trying to remove obsolete key."
+                                        ),
+                                    )?;
+                                    return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+                                        .context("In check_and_migrate: Obsolete key.");
+                                }
+                            }
+                        };
+
+                        let mut blob_metadata = BlobMetaData::new();
+                        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)));
+                        (LegacyBlob::Vec(data), blob_metadata)
+                    }
+                    BlobValue::Decrypted(data) => (LegacyBlob::ZVec(data), BlobMetaData::new()),
+                    _ => {
+                        return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+                            .context("In check_and_migrate: Legacy key has unexpected type.")
+                    }
+                };
+
+                let km_uuid = self
+                    .get_km_uuid(is_strongbox)
+                    .context("In check_and_migrate: Trying to get KM UUID")?;
+                blob_metadata.add(BlobMetaEntry::KmUuid(km_uuid));
+
+                let mut metadata = KeyMetaData::new();
+                let creation_date = DateTime::now()
+                    .context("In check_and_migrate: Trying to make creation time.")?;
+                metadata.add(KeyMetaEntry::CreationDate(creation_date));
+
+                // Store legacy key in the database.
+                self.db
+                    .store_new_key(
+                        &key,
+                        &params,
+                        &(&blob, &blob_metadata),
+                        &CertificateInfo::new(user_cert, ca_cert),
+                        &metadata,
+                        &km_uuid,
+                    )
+                    .context("In check_and_migrate.")?;
+                Ok(())
+            }
+            None => {
+                if let Some(ca_cert) = ca_cert {
+                    self.db
+                        .store_new_certificate(&key, &ca_cert, &KEYSTORE_UUID)
+                        .context("In check_and_migrate: Failed to insert new certificate.")?;
+                    Ok(())
+                } else {
+                    Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+                        .context("In check_and_migrate: Legacy key not found.")
+                }
+            }
+        };
+
+        match result {
+            Ok(()) => {
+                // Add the key to the migrated_keys list.
+                self.recently_migrated.insert(RecentMigration::new(uid, alias.clone()));
+                // Delete legacy key from the file system
+                self.legacy_loader
+                    .remove_keystore_entry(uid, &alias)
+                    .context("In check_and_migrate: Trying to remove migrated key.")?;
+                Ok(())
+            }
+            Err(e) => Err(e),
+        }
+    }
+
+    fn check_and_migrate_super_key(&mut self, user_id: u32, pw: &Password) -> Result<()> {
+        if self.recently_migrated_super_key.contains(&user_id) {
+            return Ok(());
+        }
+
+        if let Some(super_key) = self
+            .legacy_loader
+            .load_super_key(user_id, &pw)
+            .context("In check_and_migrate_super_key: Trying to load legacy super key.")?
+        {
+            let (blob, blob_metadata) =
+                crate::super_key::SuperKeyManager::encrypt_with_password(&super_key, pw)
+                    .context("In check_and_migrate_super_key: Trying to encrypt super key.")?;
+
+            self.db
+                .store_super_key(
+                    user_id,
+                    &USER_SUPER_KEY,
+                    &blob,
+                    &blob_metadata,
+                    &KeyMetaData::new(),
+                )
+                .context(concat!(
+                    "In check_and_migrate_super_key: ",
+                    "Trying to insert legacy super_key into the database."
+                ))?;
+            self.legacy_loader.remove_super_key(user_id);
+            self.recently_migrated_super_key.insert(user_id);
+            Ok(())
+        } else {
+            Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
+                .context("In check_and_migrate_super_key: No key found do migrate.")
+        }
+    }
+
+    /// 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_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))
+    }
+
+    fn check_empty(&self) -> u8 {
+        if self.legacy_loader.is_empty().unwrap_or(false) {
+            LegacyMigrator::STATE_EMPTY
+        } else {
+            LegacyMigrator::STATE_READY
+        }
+    }
+}
+
+enum LegacyBlob {
+    Vec(Vec<u8>),
+    ZVec(ZVec),
+}
+
+impl Deref for LegacyBlob {
+    type Target = [u8];
+
+    fn deref(&self) -> &Self::Target {
+        match self {
+            Self::Vec(v) => &v,
+            Self::ZVec(v) => &v,
+        }
+    }
+}
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
new file mode 100644
index 0000000..c04c4b0
--- /dev/null
+++ b/keystore2/src/lib.rs
@@ -0,0 +1,52 @@
+// 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.
+
+//! This crate implements the Android Keystore 2.0 service.
+#![recursion_limit = "256"]
+
+pub mod apc;
+pub mod async_task;
+pub mod authorization;
+pub mod boot_level_keys;
+pub mod database;
+pub mod ec_crypto;
+pub mod enforcements;
+pub mod entropy;
+pub mod error;
+pub mod globals;
+pub mod id_rotation;
+/// Internal Representation of Key Parameter and convenience functions.
+pub mod key_parameter;
+pub mod legacy_blob;
+pub mod legacy_migrator;
+pub mod maintenance;
+pub mod metrics;
+pub mod operation;
+pub mod permission;
+pub mod raw_device;
+pub mod remote_provisioning;
+pub mod security_level;
+pub mod service;
+pub mod shared_secret_negotiation;
+pub mod try_insert;
+pub mod utils;
+
+mod attestation_key_utils;
+mod audit_log;
+mod db_utils;
+mod gc;
+mod super_key;
+
+#[cfg(feature = "watchdog")]
+mod watchdog;
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
new file mode 100644
index 0000000..a099d18
--- /dev/null
+++ b/keystore2/src/maintenance.rs
@@ -0,0 +1,270 @@
+// 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.
+
+//! This module implements IKeystoreMaintenance AIDL interface.
+
+use crate::database::{KeyEntryLoadBits, KeyType, MonotonicRawTime};
+use crate::error::map_km_error;
+use crate::error::map_or_log_err;
+use crate::error::Error;
+use crate::globals::get_keymint_device;
+use crate::globals::{DB, LEGACY_MIGRATOR, SUPER_KEY};
+use crate::permission::{KeyPerm, KeystorePerm};
+use crate::super_key::UserState;
+use crate::utils::{check_key_permission, check_keystore_permission, watchdog as wd};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::IKeyMintDevice::IKeyMintDevice;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use android_security_maintenance::aidl::android::security::maintenance::{
+    IKeystoreMaintenance::{BnKeystoreMaintenance, IKeystoreMaintenance},
+    UserState::UserState as AidlUserState,
+};
+use android_security_maintenance::binder::{
+    BinderFeatures, Interface, Result as BinderResult, Strong, ThreadState,
+};
+use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
+use anyhow::{Context, Result};
+use keystore2_crypto::Password;
+
+/// This struct is defined to implement the aforementioned AIDL interface.
+/// As of now, it is an empty struct.
+pub struct Maintenance;
+
+impl Maintenance {
+    /// Create a new instance of Keystore User Manager service.
+    pub fn new_native_binder() -> Result<Strong<dyn IKeystoreMaintenance>> {
+        Ok(BnKeystoreMaintenance::new_binder(
+            Self,
+            BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+        ))
+    }
+
+    fn on_user_password_changed(user_id: i32, password: Option<Password>) -> Result<()> {
+        //Check permission. Function should return if this failed. Therefore having '?' at the end
+        //is very important.
+        check_keystore_permission(KeystorePerm::change_password())
+            .context("In on_user_password_changed.")?;
+
+        if let Some(pw) = password.as_ref() {
+            DB.with(|db| {
+                SUPER_KEY.unlock_screen_lock_bound_key(&mut db.borrow_mut(), user_id as u32, pw)
+            })
+            .context("In on_user_password_changed: unlock_screen_lock_bound_key failed")?;
+        }
+
+        match DB
+            .with(|db| {
+                UserState::get_with_password_changed(
+                    &mut db.borrow_mut(),
+                    &LEGACY_MIGRATOR,
+                    &SUPER_KEY,
+                    user_id as u32,
+                    password.as_ref(),
+                )
+            })
+            .context("In on_user_password_changed.")?
+        {
+            UserState::LskfLocked => {
+                // Error - password can not be changed when the device is locked
+                Err(Error::Rc(ResponseCode::LOCKED))
+                    .context("In on_user_password_changed. Device is locked.")
+            }
+            _ => {
+                // LskfLocked is the only error case for password change
+                Ok(())
+            }
+        }
+    }
+
+    fn add_or_remove_user(user_id: i32) -> Result<()> {
+        // 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,
+                &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.")
+    }
+
+    fn get_state(user_id: i32) -> Result<AidlUserState> {
+        // Check permission. Function should return if this failed. Therefore having '?' at the end
+        // is very important.
+        check_keystore_permission(KeystorePerm::get_state()).context("In get_state.")?;
+        let state = DB
+            .with(|db| {
+                UserState::get(&mut db.borrow_mut(), &LEGACY_MIGRATOR, &SUPER_KEY, user_id as u32)
+            })
+            .context("In get_state. Trying to get UserState.")?;
+
+        match state {
+            UserState::Uninitialized => Ok(AidlUserState::UNINITIALIZED),
+            UserState::LskfUnlocked(_) => Ok(AidlUserState::LSKF_UNLOCKED),
+            UserState::LskfLocked => Ok(AidlUserState::LSKF_LOCKED),
+        }
+    }
+
+    fn early_boot_ended_help(sec_level: SecurityLevel) -> Result<()> {
+        let (dev, _, _) = get_keymint_device(&sec_level)
+            .context("In early_boot_ended: getting keymint device")?;
+        let km_dev: Strong<dyn IKeyMintDevice> =
+            dev.get_interface().context("In early_boot_ended: getting keymint device interface")?;
+
+        let _wp = wd::watch_millis_with(
+            "In early_boot_ended_help: calling earlyBootEnded()",
+            500,
+            move || format!("Seclevel: {:?}", sec_level),
+        );
+        map_km_error(km_dev.earlyBootEnded())
+            .context("In keymint device: calling earlyBootEnded")?;
+        Ok(())
+    }
+
+    fn early_boot_ended() -> Result<()> {
+        check_keystore_permission(KeystorePerm::early_boot_ended())
+            .context("In early_boot_ended. Checking permission")?;
+        log::info!("In early_boot_ended.");
+
+        if let Err(e) = DB.with(|db| SUPER_KEY.set_up_boot_level_cache(&mut db.borrow_mut())) {
+            log::error!("SUPER_KEY.set_up_boot_level_cache failed:\n{:?}\n:(", e);
+        }
+
+        let sec_levels = [
+            (SecurityLevel::TRUSTED_ENVIRONMENT, "TRUSTED_ENVIRONMENT"),
+            (SecurityLevel::STRONGBOX, "STRONGBOX"),
+        ];
+        sec_levels.iter().fold(Ok(()), |result, (sec_level, sec_level_string)| {
+            let curr_result = Maintenance::early_boot_ended_help(*sec_level);
+            if curr_result.is_err() {
+                log::error!(
+                    "Call to earlyBootEnded failed for security level {}.",
+                    &sec_level_string
+                );
+            }
+            result.and(curr_result)
+        })
+    }
+
+    fn on_device_off_body() -> Result<()> {
+        // Security critical permission check. This statement must return on fail.
+        check_keystore_permission(KeystorePerm::report_off_body())
+            .context("In on_device_off_body.")?;
+
+        DB.with(|db| db.borrow_mut().update_last_off_body(MonotonicRawTime::now()))
+            .context("In on_device_off_body: Trying to update last off body time.")
+    }
+
+    fn migrate_key_namespace(source: &KeyDescriptor, destination: &KeyDescriptor) -> Result<()> {
+        let caller_uid = ThreadState::get_calling_uid();
+
+        DB.with(|db| {
+            let key_id_guard = match source.domain {
+                Domain::APP | Domain::SELINUX | Domain::KEY_ID => {
+                    let (key_id_guard, _) = LEGACY_MIGRATOR
+                        .with_try_migrate(&source, caller_uid, || {
+                            db.borrow_mut().load_key_entry(
+                                &source,
+                                KeyType::Client,
+                                KeyEntryLoadBits::NONE,
+                                caller_uid,
+                                |k, av| {
+                                    check_key_permission(KeyPerm::use_(), k, &av)?;
+                                    check_key_permission(KeyPerm::delete(), k, &av)?;
+                                    check_key_permission(KeyPerm::grant(), k, &av)
+                                },
+                            )
+                        })
+                        .context("In migrate_key_namespace: Failed to load key blob.")?;
+                    key_id_guard
+                }
+                _ => {
+                    return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(concat!(
+                        "In migrate_key_namespace: ",
+                        "Source domain must be one of APP, SELINUX, or KEY_ID."
+                    ))
+                }
+            };
+
+            db.borrow_mut().migrate_key_namespace(key_id_guard, destination, caller_uid, |k| {
+                check_key_permission(KeyPerm::rebind(), k, &None)
+            })
+        })
+    }
+}
+
+impl Interface for Maintenance {}
+
+impl IKeystoreMaintenance for Maintenance {
+    fn onUserPasswordChanged(&self, user_id: i32, password: Option<&[u8]>) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::onUserPasswordChanged", 500);
+        map_or_log_err(Self::on_user_password_changed(user_id, password.map(|pw| pw.into())), Ok)
+    }
+
+    fn onUserAdded(&self, user_id: i32) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::onUserAdded", 500);
+        map_or_log_err(Self::add_or_remove_user(user_id), Ok)
+    }
+
+    fn onUserRemoved(&self, user_id: i32) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::onUserRemoved", 500);
+        map_or_log_err(Self::add_or_remove_user(user_id), Ok)
+    }
+
+    fn clearNamespace(&self, domain: Domain, nspace: i64) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::clearNamespace", 500);
+        map_or_log_err(Self::clear_namespace(domain, nspace), Ok)
+    }
+
+    fn getState(&self, user_id: i32) -> BinderResult<AidlUserState> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::getState", 500);
+        map_or_log_err(Self::get_state(user_id), Ok)
+    }
+
+    fn earlyBootEnded(&self) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::earlyBootEnded", 500);
+        map_or_log_err(Self::early_boot_ended(), Ok)
+    }
+
+    fn onDeviceOffBody(&self) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::onDeviceOffBody", 500);
+        map_or_log_err(Self::on_device_off_body(), Ok)
+    }
+
+    fn migrateKeyNamespace(
+        &self,
+        source: &KeyDescriptor,
+        destination: &KeyDescriptor,
+    ) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IKeystoreMaintenance::migrateKeyNamespace", 500);
+        map_or_log_err(Self::migrate_key_namespace(source, destination), Ok)
+    }
+}
diff --git a/keystore2/src/metrics.rs b/keystore2/src/metrics.rs
new file mode 100644
index 0000000..07c3d64
--- /dev/null
+++ b/keystore2/src/metrics.rs
@@ -0,0 +1,507 @@
+// 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.
+
+//! This module provides convenience functions for keystore2 logging.
+use crate::error::get_error_code;
+use crate::globals::{DB, LOGS_HANDLER};
+use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
+use crate::operation::Outcome;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+    HardwareAuthenticatorType::HardwareAuthenticatorType, KeyOrigin::KeyOrigin,
+    KeyParameter::KeyParameter, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+    SecurityLevel::SecurityLevel,
+};
+use anyhow::Result;
+use keystore2_system_property::PropertyWatcher;
+use statslog_rust::{
+    keystore2_key_creation_event_reported::{
+        Algorithm as StatsdAlgorithm, EcCurve as StatsdEcCurve, KeyOrigin as StatsdKeyOrigin,
+        Keystore2KeyCreationEventReported, SecurityLevel as StatsdKeyCreationSecurityLevel,
+        UserAuthType as StatsdUserAuthType,
+    },
+    keystore2_key_operation_event_reported::{
+        Keystore2KeyOperationEventReported, Outcome as StatsdOutcome, Purpose as StatsdKeyPurpose,
+        SecurityLevel as StatsdKeyOperationSecurityLevel,
+    },
+    keystore2_storage_stats::StorageType as StatsdStorageType,
+};
+use statslog_rust_header::Atoms;
+use statspull_rust::{set_pull_atom_callback, StatsPullResult};
+
+fn create_default_key_creation_atom() -> Keystore2KeyCreationEventReported {
+    // If a value is not present, fields represented by bitmaps and i32 fields
+    // will take 0, except error_code which defaults to 1 indicating NO_ERROR and key_size,
+    // and auth_time_out which default to -1.
+    // The boolean fields are set to false by default.
+    // Some keymint enums do have 0 as an enum variant value. In such cases, the corresponding
+    // enum variant value in atoms.proto is incremented by 1, in order to have 0 as the reserved
+    // value for unspecified fields.
+    Keystore2KeyCreationEventReported {
+        algorithm: StatsdAlgorithm::AlgorithmUnspecified,
+        key_size: -1,
+        key_origin: StatsdKeyOrigin::OriginUnspecified,
+        user_auth_type: StatsdUserAuthType::AuthTypeUnspecified,
+        user_auth_key_timeout_seconds: -1,
+        padding_mode_bitmap: 0,
+        digest_bitmap: 0,
+        block_mode_bitmap: 0,
+        purpose_bitmap: 0,
+        ec_curve: StatsdEcCurve::EcCurveUnspecified,
+        // as per keystore2/ResponseCode.aidl, 1 is reserved for NO_ERROR
+        error_code: 1,
+        attestation_requested: false,
+        security_level: StatsdKeyCreationSecurityLevel::SecurityLevelUnspecified,
+    }
+}
+
+fn create_default_key_operation_atom() -> Keystore2KeyOperationEventReported {
+    Keystore2KeyOperationEventReported {
+        purpose: StatsdKeyPurpose::KeyPurposeUnspecified,
+        padding_mode_bitmap: 0,
+        digest_bitmap: 0,
+        block_mode_bitmap: 0,
+        outcome: StatsdOutcome::OutcomeUnspecified,
+        error_code: 1,
+        key_upgraded: false,
+        security_level: StatsdKeyOperationSecurityLevel::SecurityLevelUnspecified,
+    }
+}
+
+/// Log key creation events via statsd API.
+pub fn log_key_creation_event_stats<U>(
+    sec_level: SecurityLevel,
+    key_params: &[KeyParameter],
+    result: &Result<U>,
+) {
+    let key_creation_event_stats =
+        construct_key_creation_event_stats(sec_level, key_params, result);
+
+    LOGS_HANDLER.queue_lo(move |_| {
+        let logging_result = key_creation_event_stats.stats_write();
+
+        if let Err(e) = logging_result {
+            log::error!("Error in logging key creation event in the async task. {:?}", e);
+        }
+    });
+}
+
+/// Log key operation events via statsd API.
+pub fn log_key_operation_event_stats(
+    sec_level: SecurityLevel,
+    key_purpose: KeyPurpose,
+    op_params: &[KeyParameter],
+    op_outcome: &Outcome,
+    key_upgraded: bool,
+) {
+    let key_operation_event_stats = construct_key_operation_event_stats(
+        sec_level,
+        key_purpose,
+        op_params,
+        op_outcome,
+        key_upgraded,
+    );
+
+    LOGS_HANDLER.queue_lo(move |_| {
+        let logging_result = key_operation_event_stats.stats_write();
+
+        if let Err(e) = logging_result {
+            log::error!("Error in logging key operation event in the async task. {:?}", e);
+        }
+    });
+}
+
+fn construct_key_creation_event_stats<U>(
+    sec_level: SecurityLevel,
+    key_params: &[KeyParameter],
+    result: &Result<U>,
+) -> Keystore2KeyCreationEventReported {
+    let mut key_creation_event_atom = create_default_key_creation_atom();
+
+    if let Err(ref e) = result {
+        key_creation_event_atom.error_code = get_error_code(e);
+    }
+
+    key_creation_event_atom.security_level = match sec_level {
+        SecurityLevel::SOFTWARE => StatsdKeyCreationSecurityLevel::SecurityLevelSoftware,
+        SecurityLevel::TRUSTED_ENVIRONMENT => {
+            StatsdKeyCreationSecurityLevel::SecurityLevelTrustedEnvironment
+        }
+        SecurityLevel::STRONGBOX => StatsdKeyCreationSecurityLevel::SecurityLevelStrongbox,
+        //KEYSTORE is not a valid variant here
+        _ => StatsdKeyCreationSecurityLevel::SecurityLevelUnspecified,
+    };
+
+    for key_param in key_params.iter().map(KsKeyParamValue::from) {
+        match key_param {
+            KsKeyParamValue::Algorithm(a) => {
+                key_creation_event_atom.algorithm = match a {
+                    Algorithm::RSA => StatsdAlgorithm::Rsa,
+                    Algorithm::EC => StatsdAlgorithm::Ec,
+                    Algorithm::AES => StatsdAlgorithm::Aes,
+                    Algorithm::TRIPLE_DES => StatsdAlgorithm::TripleDes,
+                    Algorithm::HMAC => StatsdAlgorithm::Hmac,
+                    _ => StatsdAlgorithm::AlgorithmUnspecified,
+                }
+            }
+            KsKeyParamValue::KeySize(s) => {
+                key_creation_event_atom.key_size = s;
+            }
+            KsKeyParamValue::KeyOrigin(o) => {
+                key_creation_event_atom.key_origin = match o {
+                    KeyOrigin::GENERATED => StatsdKeyOrigin::Generated,
+                    KeyOrigin::DERIVED => StatsdKeyOrigin::Derived,
+                    KeyOrigin::IMPORTED => StatsdKeyOrigin::Imported,
+                    KeyOrigin::RESERVED => StatsdKeyOrigin::Reserved,
+                    KeyOrigin::SECURELY_IMPORTED => StatsdKeyOrigin::SecurelyImported,
+                    _ => StatsdKeyOrigin::OriginUnspecified,
+                }
+            }
+            KsKeyParamValue::HardwareAuthenticatorType(a) => {
+                key_creation_event_atom.user_auth_type = match a {
+                    HardwareAuthenticatorType::NONE => StatsdUserAuthType::None,
+                    HardwareAuthenticatorType::PASSWORD => StatsdUserAuthType::Password,
+                    HardwareAuthenticatorType::FINGERPRINT => StatsdUserAuthType::Fingerprint,
+                    HardwareAuthenticatorType::ANY => StatsdUserAuthType::Any,
+                    _ => StatsdUserAuthType::AuthTypeUnspecified,
+                }
+            }
+            KsKeyParamValue::AuthTimeout(t) => {
+                key_creation_event_atom.user_auth_key_timeout_seconds = t;
+            }
+            KsKeyParamValue::PaddingMode(p) => {
+                key_creation_event_atom.padding_mode_bitmap =
+                    compute_padding_mode_bitmap(&key_creation_event_atom.padding_mode_bitmap, p);
+            }
+            KsKeyParamValue::Digest(d) => {
+                key_creation_event_atom.digest_bitmap =
+                    compute_digest_bitmap(&key_creation_event_atom.digest_bitmap, d);
+            }
+            KsKeyParamValue::BlockMode(b) => {
+                key_creation_event_atom.block_mode_bitmap =
+                    compute_block_mode_bitmap(&key_creation_event_atom.block_mode_bitmap, b);
+            }
+            KsKeyParamValue::KeyPurpose(k) => {
+                key_creation_event_atom.purpose_bitmap =
+                    compute_purpose_bitmap(&key_creation_event_atom.purpose_bitmap, k);
+            }
+            KsKeyParamValue::EcCurve(e) => {
+                key_creation_event_atom.ec_curve = match e {
+                    EcCurve::P_224 => StatsdEcCurve::P224,
+                    EcCurve::P_256 => StatsdEcCurve::P256,
+                    EcCurve::P_384 => StatsdEcCurve::P384,
+                    EcCurve::P_521 => StatsdEcCurve::P521,
+                    _ => StatsdEcCurve::EcCurveUnspecified,
+                }
+            }
+            KsKeyParamValue::AttestationChallenge(_) => {
+                key_creation_event_atom.attestation_requested = true;
+            }
+            _ => {}
+        }
+    }
+    key_creation_event_atom
+}
+
+fn construct_key_operation_event_stats(
+    sec_level: SecurityLevel,
+    key_purpose: KeyPurpose,
+    op_params: &[KeyParameter],
+    op_outcome: &Outcome,
+    key_upgraded: bool,
+) -> Keystore2KeyOperationEventReported {
+    let mut key_operation_event_atom = create_default_key_operation_atom();
+
+    key_operation_event_atom.security_level = match sec_level {
+        SecurityLevel::SOFTWARE => StatsdKeyOperationSecurityLevel::SecurityLevelSoftware,
+        SecurityLevel::TRUSTED_ENVIRONMENT => {
+            StatsdKeyOperationSecurityLevel::SecurityLevelTrustedEnvironment
+        }
+        SecurityLevel::STRONGBOX => StatsdKeyOperationSecurityLevel::SecurityLevelStrongbox,
+        //KEYSTORE is not a valid variant here
+        _ => StatsdKeyOperationSecurityLevel::SecurityLevelUnspecified,
+    };
+
+    key_operation_event_atom.key_upgraded = key_upgraded;
+
+    key_operation_event_atom.purpose = match key_purpose {
+        KeyPurpose::ENCRYPT => StatsdKeyPurpose::Encrypt,
+        KeyPurpose::DECRYPT => StatsdKeyPurpose::Decrypt,
+        KeyPurpose::SIGN => StatsdKeyPurpose::Sign,
+        KeyPurpose::VERIFY => StatsdKeyPurpose::Verify,
+        KeyPurpose::WRAP_KEY => StatsdKeyPurpose::WrapKey,
+        KeyPurpose::AGREE_KEY => StatsdKeyPurpose::AgreeKey,
+        KeyPurpose::ATTEST_KEY => StatsdKeyPurpose::AttestKey,
+        _ => StatsdKeyPurpose::KeyPurposeUnspecified,
+    };
+
+    key_operation_event_atom.outcome = match op_outcome {
+        Outcome::Unknown | Outcome::Dropped => StatsdOutcome::Dropped,
+        Outcome::Success => StatsdOutcome::Success,
+        Outcome::Abort => StatsdOutcome::Abort,
+        Outcome::Pruned => StatsdOutcome::Pruned,
+        Outcome::ErrorCode(e) => {
+            key_operation_event_atom.error_code = e.0;
+            StatsdOutcome::Error
+        }
+    };
+
+    for key_param in op_params.iter().map(KsKeyParamValue::from) {
+        match key_param {
+            KsKeyParamValue::PaddingMode(p) => {
+                key_operation_event_atom.padding_mode_bitmap =
+                    compute_padding_mode_bitmap(&key_operation_event_atom.padding_mode_bitmap, p);
+            }
+            KsKeyParamValue::Digest(d) => {
+                key_operation_event_atom.digest_bitmap =
+                    compute_digest_bitmap(&key_operation_event_atom.digest_bitmap, d);
+            }
+            KsKeyParamValue::BlockMode(b) => {
+                key_operation_event_atom.block_mode_bitmap =
+                    compute_block_mode_bitmap(&key_operation_event_atom.block_mode_bitmap, b);
+            }
+            _ => {}
+        }
+    }
+
+    key_operation_event_atom
+}
+
+fn compute_purpose_bitmap(purpose_bitmap: &i32, purpose: KeyPurpose) -> i32 {
+    let mut bitmap = *purpose_bitmap;
+    match purpose {
+        KeyPurpose::ENCRYPT => {
+            bitmap |= 1 << KeyPurposeBitPosition::ENCRYPT_BIT_POS as i32;
+        }
+        KeyPurpose::DECRYPT => {
+            bitmap |= 1 << KeyPurposeBitPosition::DECRYPT_BIT_POS as i32;
+        }
+        KeyPurpose::SIGN => {
+            bitmap |= 1 << KeyPurposeBitPosition::SIGN_BIT_POS as i32;
+        }
+        KeyPurpose::VERIFY => {
+            bitmap |= 1 << KeyPurposeBitPosition::VERIFY_BIT_POS as i32;
+        }
+        KeyPurpose::WRAP_KEY => {
+            bitmap |= 1 << KeyPurposeBitPosition::WRAP_KEY_BIT_POS as i32;
+        }
+        KeyPurpose::AGREE_KEY => {
+            bitmap |= 1 << KeyPurposeBitPosition::AGREE_KEY_BIT_POS as i32;
+        }
+        KeyPurpose::ATTEST_KEY => {
+            bitmap |= 1 << KeyPurposeBitPosition::ATTEST_KEY_BIT_POS as i32;
+        }
+        _ => {}
+    }
+    bitmap
+}
+
+fn compute_padding_mode_bitmap(padding_mode_bitmap: &i32, padding_mode: PaddingMode) -> i32 {
+    let mut bitmap = *padding_mode_bitmap;
+    match padding_mode {
+        PaddingMode::NONE => {
+            bitmap |= 1 << PaddingModeBitPosition::NONE_BIT_POSITION as i32;
+        }
+        PaddingMode::RSA_OAEP => {
+            bitmap |= 1 << PaddingModeBitPosition::RSA_OAEP_BIT_POS as i32;
+        }
+        PaddingMode::RSA_PSS => {
+            bitmap |= 1 << PaddingModeBitPosition::RSA_PSS_BIT_POS as i32;
+        }
+        PaddingMode::RSA_PKCS1_1_5_ENCRYPT => {
+            bitmap |= 1 << PaddingModeBitPosition::RSA_PKCS1_1_5_ENCRYPT_BIT_POS as i32;
+        }
+        PaddingMode::RSA_PKCS1_1_5_SIGN => {
+            bitmap |= 1 << PaddingModeBitPosition::RSA_PKCS1_1_5_SIGN_BIT_POS as i32;
+        }
+        PaddingMode::PKCS7 => {
+            bitmap |= 1 << PaddingModeBitPosition::PKCS7_BIT_POS as i32;
+        }
+        _ => {}
+    }
+    bitmap
+}
+
+fn compute_digest_bitmap(digest_bitmap: &i32, digest: Digest) -> i32 {
+    let mut bitmap = *digest_bitmap;
+    match digest {
+        Digest::NONE => {
+            bitmap |= 1 << DigestBitPosition::NONE_BIT_POSITION as i32;
+        }
+        Digest::MD5 => {
+            bitmap |= 1 << DigestBitPosition::MD5_BIT_POS as i32;
+        }
+        Digest::SHA1 => {
+            bitmap |= 1 << DigestBitPosition::SHA_1_BIT_POS as i32;
+        }
+        Digest::SHA_2_224 => {
+            bitmap |= 1 << DigestBitPosition::SHA_2_224_BIT_POS as i32;
+        }
+        Digest::SHA_2_256 => {
+            bitmap |= 1 << DigestBitPosition::SHA_2_256_BIT_POS as i32;
+        }
+        Digest::SHA_2_384 => {
+            bitmap |= 1 << DigestBitPosition::SHA_2_384_BIT_POS as i32;
+        }
+        Digest::SHA_2_512 => {
+            bitmap |= 1 << DigestBitPosition::SHA_2_512_BIT_POS as i32;
+        }
+        _ => {}
+    }
+    bitmap
+}
+
+fn compute_block_mode_bitmap(block_mode_bitmap: &i32, block_mode: BlockMode) -> i32 {
+    let mut bitmap = *block_mode_bitmap;
+    match block_mode {
+        BlockMode::ECB => {
+            bitmap |= 1 << BlockModeBitPosition::ECB_BIT_POS as i32;
+        }
+        BlockMode::CBC => {
+            bitmap |= 1 << BlockModeBitPosition::CBC_BIT_POS as i32;
+        }
+        BlockMode::CTR => {
+            bitmap |= 1 << BlockModeBitPosition::CTR_BIT_POS as i32;
+        }
+        BlockMode::GCM => {
+            bitmap |= 1 << BlockModeBitPosition::GCM_BIT_POS as i32;
+        }
+        _ => {}
+    }
+    bitmap
+}
+
+/// Registers pull metrics callbacks
+pub fn register_pull_metrics_callbacks() -> Result<()> {
+    // Before registering the callbacks with statsd, we have to wait for the system to finish
+    // booting up. This avoids possible races that may occur at startup. For example, statsd
+    // depends on a companion service, and if registration happens too soon it will fail since
+    // the companion service isn't up yet.
+    let mut watcher = PropertyWatcher::new("sys.boot_completed")?;
+    loop {
+        watcher.wait()?;
+        let value = watcher.read(|_name, value| Ok(value.trim().to_string()));
+        if value? == "1" {
+            set_pull_atom_callback(Atoms::Keystore2StorageStats, None, pull_metrics_callback);
+            break;
+        }
+    }
+    Ok(())
+}
+
+fn pull_metrics_callback() -> StatsPullResult {
+    let mut result = StatsPullResult::new();
+    let mut append = |stat| {
+        match stat {
+            Ok(s) => result.push(Box::new(s)),
+            Err(error) => {
+                log::error!("pull_metrics_callback: Error getting storage stat: {}", error)
+            }
+        };
+    };
+    DB.with(|db| {
+        let mut db = db.borrow_mut();
+        append(db.get_storage_stat(StatsdStorageType::Database));
+        append(db.get_storage_stat(StatsdStorageType::KeyEntry));
+        append(db.get_storage_stat(StatsdStorageType::KeyEntryIdIndex));
+        append(db.get_storage_stat(StatsdStorageType::KeyEntryDomainNamespaceIndex));
+        append(db.get_storage_stat(StatsdStorageType::BlobEntry));
+        append(db.get_storage_stat(StatsdStorageType::BlobEntryKeyEntryIdIndex));
+        append(db.get_storage_stat(StatsdStorageType::KeyParameter));
+        append(db.get_storage_stat(StatsdStorageType::KeyParameterKeyEntryIdIndex));
+        append(db.get_storage_stat(StatsdStorageType::KeyMetadata));
+        append(db.get_storage_stat(StatsdStorageType::KeyMetadataKeyEntryIdIndex));
+        append(db.get_storage_stat(StatsdStorageType::Grant));
+        append(db.get_storage_stat(StatsdStorageType::AuthToken));
+        append(db.get_storage_stat(StatsdStorageType::BlobMetadata));
+        append(db.get_storage_stat(StatsdStorageType::BlobMetadataBlobEntryIdIndex));
+    });
+    result
+}
+
+/// Enum defining the bit position for each padding mode. Since padding mode can be repeatable, it
+/// is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+pub enum PaddingModeBitPosition {
+    ///Bit position in the PaddingMode bitmap for NONE.
+    NONE_BIT_POSITION = 0,
+    ///Bit position in the PaddingMode bitmap for RSA_OAEP.
+    RSA_OAEP_BIT_POS = 1,
+    ///Bit position in the PaddingMode bitmap for RSA_PSS.
+    RSA_PSS_BIT_POS = 2,
+    ///Bit position in the PaddingMode bitmap for RSA_PKCS1_1_5_ENCRYPT.
+    RSA_PKCS1_1_5_ENCRYPT_BIT_POS = 3,
+    ///Bit position in the PaddingMode bitmap for RSA_PKCS1_1_5_SIGN.
+    RSA_PKCS1_1_5_SIGN_BIT_POS = 4,
+    ///Bit position in the PaddingMode bitmap for RSA_PKCS7.
+    PKCS7_BIT_POS = 5,
+}
+
+/// Enum defining the bit position for each digest type. Since digest can be repeatable in
+/// key parameters, it is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+pub enum DigestBitPosition {
+    ///Bit position in the Digest bitmap for NONE.
+    NONE_BIT_POSITION = 0,
+    ///Bit position in the Digest bitmap for MD5.
+    MD5_BIT_POS = 1,
+    ///Bit position in the Digest bitmap for SHA1.
+    SHA_1_BIT_POS = 2,
+    ///Bit position in the Digest bitmap for SHA_2_224.
+    SHA_2_224_BIT_POS = 3,
+    ///Bit position in the Digest bitmap for SHA_2_256.
+    SHA_2_256_BIT_POS = 4,
+    ///Bit position in the Digest bitmap for SHA_2_384.
+    SHA_2_384_BIT_POS = 5,
+    ///Bit position in the Digest bitmap for SHA_2_512.
+    SHA_2_512_BIT_POS = 6,
+}
+
+/// Enum defining the bit position for each block mode type. Since block mode can be repeatable in
+/// key parameters, it is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+enum BlockModeBitPosition {
+    ///Bit position in the BlockMode bitmap for ECB.
+    ECB_BIT_POS = 1,
+    ///Bit position in the BlockMode bitmap for CBC.
+    CBC_BIT_POS = 2,
+    ///Bit position in the BlockMode bitmap for CTR.
+    CTR_BIT_POS = 3,
+    ///Bit position in the BlockMode bitmap for GCM.
+    GCM_BIT_POS = 4,
+}
+
+/// Enum defining the bit position for each key purpose. Since key purpose can be repeatable in
+/// key parameters, it is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+enum KeyPurposeBitPosition {
+    ///Bit position in the KeyPurpose bitmap for Encrypt.
+    ENCRYPT_BIT_POS = 1,
+    ///Bit position in the KeyPurpose bitmap for Decrypt.
+    DECRYPT_BIT_POS = 2,
+    ///Bit position in the KeyPurpose bitmap for Sign.
+    SIGN_BIT_POS = 3,
+    ///Bit position in the KeyPurpose bitmap for Verify.
+    VERIFY_BIT_POS = 4,
+    ///Bit position in the KeyPurpose bitmap for Wrap Key.
+    WRAP_KEY_BIT_POS = 5,
+    ///Bit position in the KeyPurpose bitmap for Agree Key.
+    AGREE_KEY_BIT_POS = 6,
+    ///Bit position in the KeyPurpose bitmap for Attest Key.
+    ATTEST_KEY_BIT_POS = 7,
+}
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
new file mode 100644
index 0000000..8d7ad0a
--- /dev/null
+++ b/keystore2/src/operation.rs
@@ -0,0 +1,900 @@
+// 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.
+
+//! This crate implements the `IKeystoreOperation` AIDL interface, which represents
+//! an ongoing key operation, as well as the operation database, which is mainly
+//! required for tracking operations for the purpose of pruning.
+//! This crate also implements an operation pruning strategy.
+//!
+//! Operations implement the API calls update, finish, and abort.
+//! Additionally, an operation can be dropped and pruned. The former
+//! happens if the client deletes a binder to the operation object.
+//! An existing operation may get pruned when running out of operation
+//! slots and a new operation takes precedence.
+//!
+//! ## Operation Lifecycle
+//! An operation gets created when the client calls `IKeystoreSecurityLevel::create`.
+//! It may receive zero or more update request. The lifecycle ends when:
+//!  * `update` yields an error.
+//!  * `finish` is called.
+//!  * `abort` is called.
+//!  * The operation gets dropped.
+//!  * The operation gets pruned.
+//! `Operation` has an `Outcome` member. While the outcome is `Outcome::Unknown`,
+//! the operation is active and in a good state. Any of the above conditions may
+//! change the outcome to one of the defined outcomes Success, Abort, Dropped,
+//! Pruned, or ErrorCode. The latter is chosen in the case of an unexpected error, during
+//! `update` or `finish`. `Success` is chosen iff `finish` completes without error.
+//! Note that all operations get dropped eventually in the sense that they lose
+//! their last reference and get destroyed. At that point, the fate of the operation
+//! gets logged. However, an operation will transition to `Outcome::Dropped` iff
+//! the operation was still active (`Outcome::Unknown`) at that time.
+//!
+//! ## Operation Dropping
+//! To observe the dropping of an operation, we have to make sure that there
+//! are no strong references to the IBinder representing this operation.
+//! This would be simple enough if the operation object would need to be accessed
+//! only by transactions. But to perform pruning, we have to retain a reference to the
+//! original operation object.
+//!
+//! ## Operation Pruning
+//! Pruning an operation happens during the creation of a new operation.
+//! We have to iterate through the operation database to find a suitable
+//! candidate. Then we abort and finalize this operation setting its outcome to
+//! `Outcome::Pruned`. The corresponding KeyMint operation slot will have been freed
+//! up at this point, but the `Operation` object lingers. When the client
+//! attempts to use the operation again they will receive
+//! ErrorCode::INVALID_OPERATION_HANDLE indicating that the operation no longer
+//! exits. This should be the cue for the client to destroy its binder.
+//! At that point the operation gets dropped.
+//!
+//! ## Architecture
+//! The `IKeystoreOperation` trait is implemented by `KeystoreOperation`.
+//! This acts as a proxy object holding a strong reference to actual operation
+//! implementation `Operation`.
+//!
+//! ```
+//! struct KeystoreOperation {
+//!     operation: Mutex<Option<Arc<Operation>>>,
+//! }
+//! ```
+//!
+//! The `Mutex` serves two purposes. It provides interior mutability allowing
+//! us to set the Option to None. We do this when the life cycle ends during
+//! a call to `update`, `finish`, or `abort`. As a result most of the Operation
+//! related resources are freed. The `KeystoreOperation` proxy object still
+//! lingers until dropped by the client.
+//! The second purpose is to protect operations against concurrent usage.
+//! Failing to lock this mutex yields `ResponseCode::OPERATION_BUSY` and indicates
+//! a programming error in the client.
+//!
+//! Note that the Mutex only protects the operation against concurrent client calls.
+//! We still retain weak references to the operation in the operation database:
+//!
+//! ```
+//! struct OperationDb {
+//!     operations: Mutex<Vec<Weak<Operation>>>
+//! }
+//! ```
+//!
+//! This allows us to access the operations for the purpose of pruning.
+//! We do this in three phases.
+//!  1. We gather the pruning information. Besides non mutable information,
+//!     we access `last_usage` which is protected by a mutex.
+//!     We only lock this mutex for single statements at a time. During
+//!     this phase we hold the operation db lock.
+//!  2. We choose a pruning candidate by computing the pruning resistance
+//!     of each operation. We do this entirely with information we now
+//!     have on the stack without holding any locks.
+//!     (See `OperationDb::prune` for more details on the pruning strategy.)
+//!  3. During pruning we briefly lock the operation database again to get the
+//!     the pruning candidate by index. We then attempt to abort the candidate.
+//!     If the candidate was touched in the meantime or is currently fulfilling
+//!     a request (i.e., the client calls update, finish, or abort),
+//!     we go back to 1 and try again.
+//!
+//! So the outer Mutex in `KeystoreOperation::operation` only protects
+//! operations against concurrent client calls but not against concurrent
+//! pruning attempts. This is what the `Operation::outcome` mutex is used for.
+//!
+//! ```
+//! struct Operation {
+//!     ...
+//!     outcome: Mutex<Outcome>,
+//!     ...
+//! }
+//! ```
+//!
+//! Any request that can change the outcome, i.e., `update`, `finish`, `abort`,
+//! `drop`, and `prune` has to take the outcome lock and check if the outcome
+//! is still `Outcome::Unknown` before entering. `prune` is special in that
+//! it will `try_lock`, because we don't want to be blocked on a potentially
+//! long running request at another operation. If it fails to get the lock
+//! the operation is either being touched, which changes its pruning resistance,
+//! or it transitions to its end-of-life, which means we may get a free slot.
+//! Either way, we have to revaluate the pruning scores.
+
+use crate::enforcements::AuthInfo;
+use crate::error::{map_err_with, map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
+use crate::metrics::log_key_operation_event_stats;
+use crate::utils::{watchdog as wd, Asp};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    IKeyMintOperation::IKeyMintOperation, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
+    SecurityLevel::SecurityLevel,
+};
+use android_hardware_security_keymint::binder::BinderFeatures;
+use android_system_keystore2::aidl::android::system::keystore2::{
+    IKeystoreOperation::BnKeystoreOperation, IKeystoreOperation::IKeystoreOperation,
+};
+use anyhow::{anyhow, Context, Result};
+use std::{
+    collections::HashMap,
+    sync::{Arc, Mutex, MutexGuard, Weak},
+    time::Duration,
+    time::Instant,
+};
+
+/// Operations have `Outcome::Unknown` as long as they are active. They transition
+/// to one of the other variants exactly once. The distinction in outcome is mainly
+/// for the statistic.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub enum Outcome {
+    /// Operations have `Outcome::Unknown` as long as they are active.
+    Unknown,
+    /// Operation is successful.
+    Success,
+    /// Operation is aborted.
+    Abort,
+    /// Operation is dropped.
+    Dropped,
+    /// Operation is pruned.
+    Pruned,
+    /// Operation is failed with the error code.
+    ErrorCode(ErrorCode),
+}
+
+/// Operation bundles all of the operation related resources and tracks the operation's
+/// outcome.
+#[derive(Debug)]
+pub struct Operation {
+    // The index of this operation in the OperationDb.
+    index: usize,
+    km_op: Asp,
+    last_usage: Mutex<Instant>,
+    outcome: Mutex<Outcome>,
+    owner: u32, // Uid of the operation's owner.
+    auth_info: Mutex<AuthInfo>,
+    forced: bool,
+    logging_info: LoggingInfo,
+}
+
+/// Keeps track of the information required for logging operations.
+#[derive(Debug)]
+pub struct LoggingInfo {
+    sec_level: SecurityLevel,
+    purpose: KeyPurpose,
+    op_params: Vec<KeyParameter>,
+    key_upgraded: bool,
+}
+
+impl LoggingInfo {
+    /// Constructor
+    pub fn new(
+        sec_level: SecurityLevel,
+        purpose: KeyPurpose,
+        op_params: Vec<KeyParameter>,
+        key_upgraded: bool,
+    ) -> LoggingInfo {
+        Self { sec_level, purpose, op_params, key_upgraded }
+    }
+}
+
+struct PruningInfo {
+    last_usage: Instant,
+    owner: u32,
+    index: usize,
+    forced: bool,
+}
+
+// We don't except more than 32KiB of data in `update`, `updateAad`, and `finish`.
+const MAX_RECEIVE_DATA: usize = 0x8000;
+
+impl Operation {
+    /// Constructor
+    pub fn new(
+        index: usize,
+        km_op: binder::Strong<dyn IKeyMintOperation>,
+        owner: u32,
+        auth_info: AuthInfo,
+        forced: bool,
+        logging_info: LoggingInfo,
+    ) -> Self {
+        Self {
+            index,
+            km_op: Asp::new(km_op.as_binder()),
+            last_usage: Mutex::new(Instant::now()),
+            outcome: Mutex::new(Outcome::Unknown),
+            owner,
+            auth_info: Mutex::new(auth_info),
+            forced,
+            logging_info,
+        }
+    }
+
+    fn get_pruning_info(&self) -> Option<PruningInfo> {
+        // An operation may be finalized.
+        if let Ok(guard) = self.outcome.try_lock() {
+            match *guard {
+                Outcome::Unknown => {}
+                // If the outcome is any other than unknown, it has been finalized,
+                // and we can no longer consider it for pruning.
+                _ => return None,
+            }
+        }
+        // Else: If we could not grab the lock, this means that the operation is currently
+        //       being used and it may be transitioning to finalized or it was simply updated.
+        //       In any case it is fair game to consider it for pruning. If the operation
+        //       transitioned to a final state, we will notice when we attempt to prune, and
+        //       a subsequent attempt to create a new operation will succeed.
+        Some(PruningInfo {
+            // Expect safety:
+            // `last_usage` is locked only for primitive single line statements.
+            // There is no chance to panic and poison the mutex.
+            last_usage: *self.last_usage.lock().expect("In get_pruning_info."),
+            owner: self.owner,
+            index: self.index,
+            forced: self.forced,
+        })
+    }
+
+    fn prune(&self, last_usage: Instant) -> Result<(), Error> {
+        let mut locked_outcome = match self.outcome.try_lock() {
+            Ok(guard) => match *guard {
+                Outcome::Unknown => guard,
+                _ => return Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)),
+            },
+            Err(_) => return Err(Error::Rc(ResponseCode::OPERATION_BUSY)),
+        };
+
+        // In `OperationDb::prune`, which is our caller, we first gather the pruning
+        // information including the last usage. When we select a candidate
+        // we call `prune` on that candidate passing the last_usage
+        // that we gathered earlier. If the actual last usage
+        // has changed since than, it means the operation was busy in the
+        // meantime, which means that we have to reevaluate the pruning score.
+        //
+        // Expect safety:
+        // `last_usage` is locked only for primitive single line statements.
+        // There is no chance to panic and poison the mutex.
+        if *self.last_usage.lock().expect("In Operation::prune()") != last_usage {
+            return Err(Error::Rc(ResponseCode::OPERATION_BUSY));
+        }
+        *locked_outcome = Outcome::Pruned;
+
+        let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
+            match self.km_op.get_interface() {
+                Ok(km_op) => km_op,
+                Err(e) => {
+                    log::error!("In prune: Failed to get KeyMintOperation interface.\n    {:?}", e);
+                    return Err(Error::sys());
+                }
+            };
+
+        let _wp = wd::watch_millis("In Operation::prune: calling abort()", 500);
+
+        // We abort the operation. If there was an error we log it but ignore it.
+        if let Err(e) = map_km_error(km_op.abort()) {
+            log::error!("In prune: KeyMint::abort failed with {:?}.", e);
+        }
+
+        Ok(())
+    }
+
+    // This function takes a Result from a KeyMint call and inspects it for errors.
+    // If an error was found it updates the given `locked_outcome` accordingly.
+    // It forwards the Result unmodified.
+    // The precondition to this call must be *locked_outcome == Outcome::Unknown.
+    // Ideally the `locked_outcome` came from a successful call to `check_active`
+    // see below.
+    fn update_outcome<T>(
+        &self,
+        locked_outcome: &mut Outcome,
+        err: Result<T, Error>,
+    ) -> Result<T, Error> {
+        match &err {
+            Err(Error::Km(e)) => *locked_outcome = Outcome::ErrorCode(*e),
+            Err(_) => *locked_outcome = Outcome::ErrorCode(ErrorCode::UNKNOWN_ERROR),
+            Ok(_) => (),
+        }
+        err
+    }
+
+    // This function grabs the outcome lock and checks the current outcome state.
+    // If the outcome is still `Outcome::Unknown`, this function returns
+    // the locked outcome for further updates. In any other case it returns
+    // ErrorCode::INVALID_OPERATION_HANDLE indicating that this operation has
+    // been finalized and is no longer active.
+    fn check_active(&self) -> Result<MutexGuard<Outcome>> {
+        let guard = self.outcome.lock().expect("In check_active.");
+        match *guard {
+            Outcome::Unknown => Ok(guard),
+            _ => Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)).context(format!(
+                "In check_active: Call on finalized operation with outcome: {:?}.",
+                *guard
+            )),
+        }
+    }
+
+    // This function checks the amount of input data sent to us. We reject any buffer
+    // exceeding MAX_RECEIVE_DATA bytes as input to `update`, `update_aad`, and `finish`
+    // in order to force clients into using reasonable limits.
+    fn check_input_length(data: &[u8]) -> Result<()> {
+        if data.len() > MAX_RECEIVE_DATA {
+            // This error code is unique, no context required here.
+            return Err(anyhow!(Error::Rc(ResponseCode::TOO_MUCH_DATA)));
+        }
+        Ok(())
+    }
+
+    // Update the last usage to now.
+    fn touch(&self) {
+        // Expect safety:
+        // `last_usage` is locked only for primitive single line statements.
+        // There is no chance to panic and poison the mutex.
+        *self.last_usage.lock().expect("In touch.") = Instant::now();
+    }
+
+    /// Implementation of `IKeystoreOperation::updateAad`.
+    /// Refer to the AIDL spec at system/hardware/interfaces/keystore2 for details.
+    fn update_aad(&self, aad_input: &[u8]) -> Result<()> {
+        let mut outcome = self.check_active().context("In update_aad")?;
+        Self::check_input_length(aad_input).context("In update_aad")?;
+        self.touch();
+
+        let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
+            self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
+
+        let (hat, tst) = self
+            .auth_info
+            .lock()
+            .unwrap()
+            .before_update()
+            .context("In update_aad: Trying to get auth tokens.")?;
+
+        self.update_outcome(&mut *outcome, {
+            let _wp = wd::watch_millis("Operation::update_aad: calling updateAad", 500);
+            map_km_error(km_op.updateAad(aad_input, hat.as_ref(), tst.as_ref()))
+        })
+        .context("In update_aad: KeyMint::update failed.")?;
+
+        Ok(())
+    }
+
+    /// Implementation of `IKeystoreOperation::update`.
+    /// Refer to the AIDL spec at system/hardware/interfaces/keystore2 for details.
+    fn update(&self, input: &[u8]) -> Result<Option<Vec<u8>>> {
+        let mut outcome = self.check_active().context("In update")?;
+        Self::check_input_length(input).context("In update")?;
+        self.touch();
+
+        let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
+            self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
+
+        let (hat, tst) = self
+            .auth_info
+            .lock()
+            .unwrap()
+            .before_update()
+            .context("In update: Trying to get auth tokens.")?;
+
+        let output = self
+            .update_outcome(&mut *outcome, {
+                let _wp = wd::watch_millis("Operation::update: calling update", 500);
+                map_km_error(km_op.update(input, hat.as_ref(), tst.as_ref()))
+            })
+            .context("In update: KeyMint::update failed.")?;
+
+        if output.is_empty() {
+            Ok(None)
+        } else {
+            Ok(Some(output))
+        }
+    }
+
+    /// Implementation of `IKeystoreOperation::finish`.
+    /// Refer to the AIDL spec at system/hardware/interfaces/keystore2 for details.
+    fn finish(&self, input: Option<&[u8]>, signature: Option<&[u8]>) -> Result<Option<Vec<u8>>> {
+        let mut outcome = self.check_active().context("In finish")?;
+        if let Some(input) = input {
+            Self::check_input_length(input).context("In finish")?;
+        }
+        self.touch();
+
+        let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
+            self.km_op.get_interface().context("In finish: Failed to get KeyMintOperation.")?;
+
+        let (hat, tst, confirmation_token) = self
+            .auth_info
+            .lock()
+            .unwrap()
+            .before_finish()
+            .context("In finish: Trying to get auth tokens.")?;
+
+        let output = self
+            .update_outcome(&mut *outcome, {
+                let _wp = wd::watch_millis("Operation::finish: calling finish", 500);
+                map_km_error(km_op.finish(
+                    input,
+                    signature,
+                    hat.as_ref(),
+                    tst.as_ref(),
+                    confirmation_token.as_deref(),
+                ))
+            })
+            .context("In finish: KeyMint::finish failed.")?;
+
+        self.auth_info.lock().unwrap().after_finish().context("In finish.")?;
+
+        // At this point the operation concluded successfully.
+        *outcome = Outcome::Success;
+
+        if output.is_empty() {
+            Ok(None)
+        } else {
+            Ok(Some(output))
+        }
+    }
+
+    /// Aborts the operation if it is active. IFF the operation is aborted the outcome is
+    /// set to `outcome`. `outcome` must reflect the reason for the abort. Since the operation
+    /// gets aborted `outcome` must not be `Operation::Success` or `Operation::Unknown`.
+    fn abort(&self, outcome: Outcome) -> Result<()> {
+        let mut locked_outcome = self.check_active().context("In abort")?;
+        *locked_outcome = outcome;
+        let km_op: binder::public_api::Strong<dyn IKeyMintOperation> =
+            self.km_op.get_interface().context("In abort: Failed to get KeyMintOperation.")?;
+
+        {
+            let _wp = wd::watch_millis("Operation::abort: calling abort", 500);
+            map_km_error(km_op.abort()).context("In abort: KeyMint::abort failed.")
+        }
+    }
+}
+
+impl Drop for Operation {
+    fn drop(&mut self) {
+        let guard = self.outcome.lock().expect("In drop.");
+        log_key_operation_event_stats(
+            self.logging_info.sec_level,
+            self.logging_info.purpose,
+            &(self.logging_info.op_params),
+            &guard,
+            self.logging_info.key_upgraded,
+        );
+        if let Outcome::Unknown = *guard {
+            drop(guard);
+            // If the operation was still active we call abort, setting
+            // the outcome to `Outcome::Dropped`
+            if let Err(e) = self.abort(Outcome::Dropped) {
+                log::error!("While dropping Operation: abort failed:\n    {:?}", e);
+            }
+        }
+    }
+}
+
+/// The OperationDb holds weak references to all ongoing operations.
+/// Its main purpose is to facilitate operation pruning.
+#[derive(Debug, Default)]
+pub struct OperationDb {
+    // TODO replace Vec with WeakTable when the weak_table crate becomes
+    // available.
+    operations: Mutex<Vec<Weak<Operation>>>,
+}
+
+impl OperationDb {
+    /// Creates a new OperationDb.
+    pub fn new() -> Self {
+        Self { operations: Mutex::new(Vec::new()) }
+    }
+
+    /// Creates a new operation.
+    /// This function takes a KeyMint operation and an associated
+    /// owner uid and returns a new Operation wrapped in a `std::sync::Arc`.
+    pub fn create_operation(
+        &self,
+        km_op: binder::public_api::Strong<dyn IKeyMintOperation>,
+        owner: u32,
+        auth_info: AuthInfo,
+        forced: bool,
+        logging_info: LoggingInfo,
+    ) -> Arc<Operation> {
+        // We use unwrap because we don't allow code that can panic while locked.
+        let mut operations = self.operations.lock().expect("In create_operation.");
+
+        let mut index: usize = 0;
+        // First we iterate through the operation slots to try and find an unused
+        // slot. If we don't find one, we append the new entry instead.
+        match (*operations).iter_mut().find(|s| {
+            index += 1;
+            s.upgrade().is_none()
+        }) {
+            Some(free_slot) => {
+                let new_op = Arc::new(Operation::new(
+                    index - 1,
+                    km_op,
+                    owner,
+                    auth_info,
+                    forced,
+                    logging_info,
+                ));
+                *free_slot = Arc::downgrade(&new_op);
+                new_op
+            }
+            None => {
+                let new_op = Arc::new(Operation::new(
+                    operations.len(),
+                    km_op,
+                    owner,
+                    auth_info,
+                    forced,
+                    logging_info,
+                ));
+                operations.push(Arc::downgrade(&new_op));
+                new_op
+            }
+        }
+    }
+
+    fn get(&self, index: usize) -> Option<Arc<Operation>> {
+        self.operations.lock().expect("In OperationDb::get.").get(index).and_then(|op| op.upgrade())
+    }
+
+    /// Attempts to prune an operation.
+    ///
+    /// This function is used during operation creation, i.e., by
+    /// `KeystoreSecurityLevel::create_operation`, to try and free up an operation slot
+    /// if it got `ErrorCode::TOO_MANY_OPERATIONS` from the KeyMint backend. It is not
+    /// guaranteed that an operation slot is available after this call successfully
+    /// returned for various reasons. E.g., another thread may have snatched up the newly
+    /// available slot. Callers may have to call prune multiple times before they get a
+    /// free operation slot. Prune may also return `Err(Error::Rc(ResponseCode::BACKEND_BUSY))`
+    /// which indicates that no prunable operation was found.
+    ///
+    /// To find a suitable candidate we compute the malus for the caller and each existing
+    /// operation. The malus is the inverse of the pruning power (caller) or pruning
+    /// resistance (existing operation).
+    ///
+    /// The malus is based on the number of sibling operations and age. Sibling
+    /// operations are operations that have the same owner (UID).
+    ///
+    /// Every operation, existing or new, starts with a malus of 1. Every sibling
+    /// increases the malus by one. The age is the time since an operation was last touched.
+    /// It increases the malus by log6(<age in seconds> + 1) rounded down to the next
+    /// integer. So the malus increases stepwise after 5s, 35s, 215s, ...
+    /// Of two operations with the same malus the least recently used one is considered
+    /// weaker.
+    ///
+    /// For the caller to be able to prune an operation it must find an operation
+    /// with a malus higher than its own.
+    ///
+    /// The malus can be expressed as
+    /// ```
+    /// malus = 1 + no_of_siblings + floor(log6(age_in_seconds + 1))
+    /// ```
+    /// where the constant `1` accounts for the operation under consideration.
+    /// In reality we compute it as
+    /// ```
+    /// caller_malus = 1 + running_siblings
+    /// ```
+    /// because the new operation has no age and is not included in the `running_siblings`,
+    /// and
+    /// ```
+    /// running_malus = running_siblings + floor(log6(age_in_seconds + 1))
+    /// ```
+    /// because a running operation is included in the `running_siblings` and it has
+    /// an age.
+    ///
+    /// ## Example
+    /// A caller with no running operations has a malus of 1. Young (age < 5s) operations
+    /// also with no siblings have a malus of one and cannot be pruned by the caller.
+    /// We have to find an operation that has at least one sibling or is older than 5s.
+    ///
+    /// A caller with one running operation has a malus of 2. Now even young siblings
+    /// or single child aging (5s <= age < 35s) operations are off limit. An aging
+    /// sibling of two, however, would have a malus of 3 and would be fair game.
+    ///
+    /// ## Rationale
+    /// Due to the limitation of KeyMint operation slots, we cannot get around pruning or
+    /// a single app could easily DoS KeyMint.
+    /// Keystore 1.0 used to always prune the least recently used operation. This at least
+    /// guaranteed that new operations can always be started. With the increased usage
+    /// of Keystore we saw increased pruning activity which can lead to a livelock
+    /// situation in the worst case.
+    ///
+    /// With the new pruning strategy we want to provide well behaved clients with
+    /// progress assurances while punishing DoS attempts. As a result of this
+    /// strategy we can be in the situation where no operation can be pruned and the
+    /// creation of a new operation fails. This allows single child operations which
+    /// are frequently updated to complete, thereby breaking up livelock situations
+    /// and facilitating system wide progress.
+    ///
+    /// ## Update
+    /// We also allow callers to cannibalize their own sibling operations if no other
+    /// slot can be found. In this case the least recently used sibling is pruned.
+    pub fn prune(&self, caller: u32, forced: bool) -> Result<(), Error> {
+        loop {
+            // Maps the uid of the owner to the number of operations that owner has
+            // (running_siblings). More operations per owner lowers the pruning
+            // resistance of the operations of that owner. Whereas the number of
+            // ongoing operations of the caller lowers the pruning power of the caller.
+            let mut owners: HashMap<u32, u64> = HashMap::new();
+            let mut pruning_info: Vec<PruningInfo> = Vec::new();
+
+            let now = Instant::now();
+            self.operations
+                .lock()
+                .expect("In OperationDb::prune: Trying to lock self.operations.")
+                .iter()
+                .for_each(|op| {
+                    if let Some(op) = op.upgrade() {
+                        if let Some(p_info) = op.get_pruning_info() {
+                            let owner = p_info.owner;
+                            pruning_info.push(p_info);
+                            // Count operations per owner.
+                            *owners.entry(owner).or_insert(0) += 1;
+                        }
+                    }
+                });
+
+            // If the operation is forced, the caller has a malus of 0.
+            let caller_malus = if forced { 0 } else { 1u64 + *owners.entry(caller).or_default() };
+
+            // We iterate through all operations computing the malus and finding
+            // the candidate with the highest malus which must also be higher
+            // than the caller_malus.
+            struct CandidateInfo {
+                index: usize,
+                malus: u64,
+                last_usage: Instant,
+                age: Duration,
+            }
+            let mut oldest_caller_op: Option<CandidateInfo> = None;
+            let candidate = pruning_info.iter().fold(
+                None,
+                |acc: Option<CandidateInfo>, &PruningInfo { last_usage, owner, index, forced }| {
+                    // Compute the age of the current operation.
+                    let age = now
+                        .checked_duration_since(last_usage)
+                        .unwrap_or_else(|| Duration::new(0, 0));
+
+                    // Find the least recently used sibling as an alternative pruning candidate.
+                    if owner == caller {
+                        if let Some(CandidateInfo { age: a, .. }) = oldest_caller_op {
+                            if age > a {
+                                oldest_caller_op =
+                                    Some(CandidateInfo { index, malus: 0, last_usage, age });
+                            }
+                        } else {
+                            oldest_caller_op =
+                                Some(CandidateInfo { index, malus: 0, last_usage, age });
+                        }
+                    }
+
+                    // Compute the malus of the current operation.
+                    let malus = if forced {
+                        // Forced operations have a malus of 0. And cannot even be pruned
+                        // by other forced operations.
+                        0
+                    } else {
+                        // Expect safety: Every owner in pruning_info was counted in
+                        // the owners map. So this unwrap cannot panic.
+                        *owners.get(&owner).expect(
+                            "This is odd. We should have counted every owner in pruning_info.",
+                        ) + ((age.as_secs() + 1) as f64).log(6.0).floor() as u64
+                    };
+
+                    // Now check if the current operation is a viable/better candidate
+                    // the one currently stored in the accumulator.
+                    match acc {
+                        // First we have to find any operation that is prunable by the caller.
+                        None => {
+                            if caller_malus < malus {
+                                Some(CandidateInfo { index, malus, last_usage, age })
+                            } else {
+                                None
+                            }
+                        }
+                        // If we have found one we look for the operation with the worst score.
+                        // If there is a tie, the older operation is considered weaker.
+                        Some(CandidateInfo { index: i, malus: m, last_usage: l, age: a }) => {
+                            if malus > m || (malus == m && age > a) {
+                                Some(CandidateInfo { index, malus, last_usage, age })
+                            } else {
+                                Some(CandidateInfo { index: i, malus: m, last_usage: l, age: a })
+                            }
+                        }
+                    }
+                },
+            );
+
+            // If we did not find a suitable candidate we may cannibalize our oldest sibling.
+            let candidate = candidate.or(oldest_caller_op);
+
+            match candidate {
+                Some(CandidateInfo { index, malus: _, last_usage, age: _ }) => {
+                    match self.get(index) {
+                        Some(op) => {
+                            match op.prune(last_usage) {
+                                // We successfully freed up a slot.
+                                Ok(()) => break Ok(()),
+                                // This means the operation we tried to prune was on its way
+                                // out. It also means that the slot it had occupied was freed up.
+                                Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)) => break Ok(()),
+                                // This means the operation we tried to prune was currently
+                                // servicing a request. There are two options.
+                                // * Assume that it was touched, which means that its
+                                //   pruning resistance increased. In that case we have
+                                //   to start over and find another candidate.
+                                // * Assume that the operation is transitioning to end-of-life.
+                                //   which means that we got a free slot for free.
+                                // If we assume the first but the second is true, we prune
+                                // a good operation without need (aggressive approach).
+                                // If we assume the second but the first is true, our
+                                // caller will attempt to create a new KeyMint operation,
+                                // fail with `ErrorCode::TOO_MANY_OPERATIONS`, and call
+                                // us again (conservative approach).
+                                Err(Error::Rc(ResponseCode::OPERATION_BUSY)) => {
+                                    // We choose the conservative approach, because
+                                    // every needlessly pruned operation can impact
+                                    // the user experience.
+                                    // To switch to the aggressive approach replace
+                                    // the following line with `continue`.
+                                    break Ok(());
+                                }
+
+                                // The candidate may have been touched so the score
+                                // has changed since our evaluation.
+                                _ => continue,
+                            }
+                        }
+                        // This index does not exist any more. The operation
+                        // in this slot was dropped. Good news, a slot
+                        // has freed up.
+                        None => break Ok(()),
+                    }
+                }
+                // We did not get a pruning candidate.
+                None => break Err(Error::Rc(ResponseCode::BACKEND_BUSY)),
+            }
+        }
+    }
+}
+
+/// Implementation of IKeystoreOperation.
+pub struct KeystoreOperation {
+    operation: Mutex<Option<Arc<Operation>>>,
+}
+
+impl KeystoreOperation {
+    /// Creates a new operation instance wrapped in a
+    /// BnKeystoreOperation proxy object. It also enables
+    /// `BinderFeatures::set_requesting_sid` on the new interface, because
+    /// we need it for checking Keystore permissions.
+    pub fn new_native_binder(
+        operation: Arc<Operation>,
+    ) -> binder::public_api::Strong<dyn IKeystoreOperation> {
+        BnKeystoreOperation::new_binder(
+            Self { operation: Mutex::new(Some(operation)) },
+            BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+        )
+    }
+
+    /// Grabs the outer operation mutex and calls `f` on the locked operation.
+    /// The function also deletes the operation if it returns with an error or if
+    /// `delete_op` is true.
+    fn with_locked_operation<T, F>(&self, f: F, delete_op: bool) -> Result<T>
+    where
+        for<'a> F: FnOnce(&'a Operation) -> Result<T>,
+    {
+        let mut delete_op: bool = delete_op;
+        match self.operation.try_lock() {
+            Ok(mut mutex_guard) => {
+                let result = match &*mutex_guard {
+                    Some(op) => {
+                        let result = f(&*op);
+                        // Any error here means we can discard the operation.
+                        if result.is_err() {
+                            delete_op = true;
+                        }
+                        result
+                    }
+                    None => Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE))
+                        .context("In KeystoreOperation::with_locked_operation"),
+                };
+
+                if delete_op {
+                    // We give up our reference to the Operation, thereby freeing up our
+                    // internal resources and ending the wrapped KeyMint operation.
+                    // This KeystoreOperation object will still be owned by an SpIBinder
+                    // until the client drops its remote reference.
+                    *mutex_guard = None;
+                }
+                result
+            }
+            Err(_) => Err(Error::Rc(ResponseCode::OPERATION_BUSY))
+                .context("In KeystoreOperation::with_locked_operation"),
+        }
+    }
+}
+
+impl binder::Interface for KeystoreOperation {}
+
+impl IKeystoreOperation for KeystoreOperation {
+    fn updateAad(&self, aad_input: &[u8]) -> binder::public_api::Result<()> {
+        let _wp = wd::watch_millis("IKeystoreOperation::updateAad", 500);
+        map_or_log_err(
+            self.with_locked_operation(
+                |op| op.update_aad(aad_input).context("In KeystoreOperation::updateAad"),
+                false,
+            ),
+            Ok,
+        )
+    }
+
+    fn update(&self, input: &[u8]) -> binder::public_api::Result<Option<Vec<u8>>> {
+        let _wp = wd::watch_millis("IKeystoreOperation::update", 500);
+        map_or_log_err(
+            self.with_locked_operation(
+                |op| op.update(input).context("In KeystoreOperation::update"),
+                false,
+            ),
+            Ok,
+        )
+    }
+    fn finish(
+        &self,
+        input: Option<&[u8]>,
+        signature: Option<&[u8]>,
+    ) -> binder::public_api::Result<Option<Vec<u8>>> {
+        let _wp = wd::watch_millis("IKeystoreOperation::finish", 500);
+        map_or_log_err(
+            self.with_locked_operation(
+                |op| op.finish(input, signature).context("In KeystoreOperation::finish"),
+                true,
+            ),
+            Ok,
+        )
+    }
+
+    fn abort(&self) -> binder::public_api::Result<()> {
+        let _wp = wd::watch_millis("IKeystoreOperation::abort", 500);
+        map_err_with(
+            self.with_locked_operation(
+                |op| op.abort(Outcome::Abort).context("In KeystoreOperation::abort"),
+                true,
+            ),
+            |e| {
+                match e.root_cause().downcast_ref::<Error>() {
+                    // Calling abort on expired operations is something very common.
+                    // There is no reason to clutter the log with it. It is never the cause
+                    // for a true problem.
+                    Some(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)) => {}
+                    _ => log::error!("{:?}", e),
+                };
+                e
+            },
+            Ok,
+        )
+    }
+}
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
new file mode 100644
index 0000000..726c2ec
--- /dev/null
+++ b/keystore2/src/permission.rs
@@ -0,0 +1,1064 @@
+// 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.
+
+//! This crate provides access control primitives for Keystore 2.0.
+//! It provides high level functions for checking permissions in the keystore2 and keystore2_key
+//! SELinux classes based on the keystore2_selinux backend.
+//! It also provides KeystorePerm and KeyPerm as convenience wrappers for the SELinux permission
+//! defined by keystore2 and keystore2_key respectively.
+
+#![allow(clippy::from_over_into)]
+
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
+};
+
+use std::cmp::PartialEq;
+use std::convert::From;
+use std::ffi::CStr;
+
+use crate::error::Error as KsError;
+use keystore2_selinux as selinux;
+
+use anyhow::Context as AnyhowContext;
+
+use selinux::Backend;
+
+use lazy_static::lazy_static;
+
+// Replace getcon with a mock in the test situation
+#[cfg(not(test))]
+use selinux::getcon;
+#[cfg(test)]
+use tests::test_getcon as getcon;
+
+lazy_static! {
+    // Panicking here is allowed because keystore cannot function without this backend
+    // and it would happen early and indicate a gross misconfiguration of the device.
+    static ref KEYSTORE2_KEY_LABEL_BACKEND: selinux::KeystoreKeyBackend =
+            selinux::KeystoreKeyBackend::new().unwrap();
+}
+
+fn lookup_keystore2_key_context(namespace: i64) -> anyhow::Result<selinux::Context> {
+    KEYSTORE2_KEY_LABEL_BACKEND.lookup(&namespace.to_string())
+}
+
+/// ## Background
+///
+/// AIDL enums are represented as constants of the form:
+/// ```
+/// mod EnumName {
+///     pub type EnumName = i32;
+///     pub const Variant1: EnumName = <value1>;
+///     pub const Variant2: EnumName = <value2>;
+///     ...
+/// }
+///```
+/// This macro wraps the enum in a new type, e.g., `MyPerm` and maps each variant to an SELinux
+/// permission while providing the following interface:
+///  * From<EnumName> and Into<EnumName> are implemented. Where the implementation of From maps
+///    any variant not specified to the default.
+///  * Every variant has a constructor with a name corresponding to its lower case SELinux string
+///    representation.
+///  * `MyPerm.to_selinux(&self)` returns the SELinux string representation of the
+///    represented permission.
+///
+/// ## Special behavior
+/// If the keyword `use` appears as an selinux name `use_` is used as identifier for the
+/// constructor function (e.g. `MePerm::use_()`) but the string returned by `to_selinux` will
+/// still be `"use"`.
+///
+/// ## Example
+/// ```
+///
+/// implement_permission!(
+///     /// MyPerm documentation.
+///     #[derive(Clone, Copy, Debug, PartialEq)]
+///     MyPerm from EnumName with default (None, none) {}
+///         Variant1,    selinux name: variant1;
+///         Variant2,    selinux name: variant1;
+///     }
+/// );
+/// ```
+macro_rules! implement_permission_aidl {
+    // This rule provides the public interface of the macro. And starts the preprocessing
+    // recursion (see below).
+    ($(#[$m:meta])* $name:ident from $aidl_name:ident with default ($($def:tt)*)
+        { $($element:tt)* })
+    => {
+        implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*), [],
+            $($element)*);
+    };
+
+    // The following three rules recurse through the elements of the form
+    // `<enum variant>, selinux name: <selinux_name>;`
+    // preprocessing the input.
+
+    // The first rule terminates the recursion and passes the processed arguments to the final
+    // rule that spills out the implementation.
+    (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*], ) => {
+        implement_permission_aidl!(@end $($m)*, $name, $aidl_name, ($($def)*) { $($out)* } );
+    };
+
+    // The second rule is triggered if the selinux name of an element is literally `use`.
+    // It produces the tuple `<enum variant>, use_, use;`
+    // and appends it to the out list.
+    (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*],
+        $e_name:ident, selinux name: use; $($element:tt)*)
+    => {
+        implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*),
+                              [$($out)* $e_name, use_, use;], $($element)*);
+    };
+
+    // The third rule is the default rule which replaces every input tuple with
+    // `<enum variant>, <selinux_name>, <selinux_name>;`
+    // and appends the result to the out list.
+    (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*],
+        $e_name:ident, selinux name: $e_str:ident; $($element:tt)*)
+    => {
+        implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*),
+                              [$($out)* $e_name, $e_str, $e_str;], $($element)*);
+    };
+
+    (@end $($m:meta)*, $name:ident, $aidl_name:ident,
+        ($def_name:ident, $def_selinux_name:ident) {
+            $($element_name:ident, $element_identifier:ident,
+                $selinux_name:ident;)*
+        })
+    =>
+    {
+        $(#[$m])*
+        pub struct $name(pub $aidl_name);
+
+        impl From<$aidl_name> for $name {
+            fn from (p: $aidl_name) -> Self {
+                match p {
+                    $aidl_name::$def_name => Self($aidl_name::$def_name),
+                    $($aidl_name::$element_name => Self($aidl_name::$element_name),)*
+                    _ => Self($aidl_name::$def_name),
+                }
+            }
+        }
+
+        impl Into<$aidl_name> for $name {
+            fn into(self) -> $aidl_name {
+                self.0
+            }
+        }
+
+        impl $name {
+            /// Returns a string representation of the permission as required by
+            /// `selinux::check_access`.
+            pub fn to_selinux(&self) -> &'static str {
+                match self {
+                    Self($aidl_name::$def_name) => stringify!($def_selinux_name),
+                    $(Self($aidl_name::$element_name) => stringify!($selinux_name),)*
+                    _ => stringify!($def_selinux_name),
+                }
+            }
+
+            /// Creates an instance representing a permission with the same name.
+            pub const fn $def_selinux_name() -> Self { Self($aidl_name::$def_name) }
+            $(
+                /// Creates an instance representing a permission with the same name.
+                pub const fn $element_identifier() -> Self { Self($aidl_name::$element_name) }
+            )*
+        }
+    };
+}
+
+implement_permission_aidl!(
+    /// KeyPerm provides a convenient abstraction from the SELinux class `keystore2_key`.
+    /// At the same time it maps `KeyPermissions` from the Keystore 2.0 AIDL Grant interface to
+    /// the SELinux permissions. With the implement_permission macro, we conveniently
+    /// provide mappings between the wire type bit field values, the rust enum and the SELinux
+    /// string representation.
+    ///
+    /// ## Example
+    ///
+    /// In this access check `KeyPerm::get_info().to_selinux()` would return the SELinux representation
+    /// "info".
+    /// ```
+    /// selinux::check_access(source_context, target_context, "keystore2_key",
+    ///                       KeyPerm::get_info().to_selinux());
+    /// ```
+    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+    KeyPerm from KeyPermission with default (NONE, none) {
+        CONVERT_STORAGE_KEY_TO_EPHEMERAL,   selinux name: convert_storage_key_to_ephemeral;
+        DELETE,         selinux name: delete;
+        GEN_UNIQUE_ID,  selinux name: gen_unique_id;
+        GET_INFO,       selinux name: get_info;
+        GRANT,          selinux name: grant;
+        MANAGE_BLOB,    selinux name: manage_blob;
+        REBIND,         selinux name: rebind;
+        REQ_FORCED_OP,  selinux name: req_forced_op;
+        UPDATE,         selinux name: update;
+        USE,            selinux name: use;
+        USE_DEV_ID,     selinux name: use_dev_id;
+    }
+);
+
+/// This macro implements an enum with values mapped to SELinux permission names.
+/// The below example wraps the enum MyPermission in the tuple struct `MyPerm` and implements
+///  * From<i32> and Into<i32> are implemented. Where the implementation of From maps
+///    any variant not specified to the default.
+///  * Every variant has a constructor with a name corresponding to its lower case SELinux string
+///    representation.
+///  * `MyPerm.to_selinux(&self)` returns the SELinux string representation of the
+///    represented permission.
+///
+/// ## Example
+/// ```
+/// implement_permission!(
+///     /// MyPerm documentation.
+///     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+///     MyPerm with default (None = 0, none) {
+///         Foo = 1,           selinux name: foo;
+///         Bar = 2,           selinux name: bar;
+///     }
+/// );
+/// ```
+macro_rules! implement_permission {
+    // This rule provides the public interface of the macro. And starts the preprocessing
+    // recursion (see below).
+    ($(#[$m:meta])* $name:ident with default
+        ($def_name:ident = $def_val:expr, $def_selinux_name:ident)
+        {
+            $($(#[$element_meta:meta])*
+            $element_name:ident = $element_val:expr, selinux name: $selinux_name:ident;)*
+        })
+    => {
+        $(#[$m])*
+        pub enum $name {
+            /// The default variant of an enum.
+            $def_name = $def_val,
+            $(
+                $(#[$element_meta])*
+                $element_name = $element_val,
+            )*
+        }
+
+        impl From<i32> for $name {
+            fn from (p: i32) -> Self {
+                match p {
+                    $def_val => Self::$def_name,
+                    $($element_val => Self::$element_name,)*
+                    _ => Self::$def_name,
+                }
+            }
+        }
+
+        impl Into<i32> for $name {
+            fn into(self) -> i32 {
+                self as i32
+            }
+        }
+
+        impl $name {
+            /// Returns a string representation of the permission as required by
+            /// `selinux::check_access`.
+            pub fn to_selinux(&self) -> &'static str {
+                match self {
+                    Self::$def_name => stringify!($def_selinux_name),
+                    $(Self::$element_name => stringify!($selinux_name),)*
+                }
+            }
+
+            /// Creates an instance representing a permission with the same name.
+            pub const fn $def_selinux_name() -> Self { Self::$def_name }
+            $(
+                /// Creates an instance representing a permission with the same name.
+                pub const fn $selinux_name() -> Self { Self::$element_name }
+            )*
+        }
+    };
+}
+
+implement_permission!(
+    /// KeystorePerm provides a convenient abstraction from the SELinux class `keystore2`.
+    /// Using the implement_permission macro we get the same features as `KeyPerm`.
+    #[derive(Clone, Copy, Debug, PartialEq)]
+    KeystorePerm with default (None = 0, none) {
+        /// Checked when a new auth token is installed.
+        AddAuth = 1,    selinux name: add_auth;
+        /// Checked when an app is uninstalled or wiped.
+        ClearNs = 2,    selinux name: clear_ns;
+        /// Checked when the user state is queried from Keystore 2.0.
+        GetState = 4,   selinux name: get_state;
+        /// Checked when Keystore 2.0 is asked to list a namespace that the caller
+        /// does not have the get_info permission for.
+        List = 8,       selinux name: list;
+        /// Checked when Keystore 2.0 gets locked.
+        Lock = 0x10,       selinux name: lock;
+        /// Checked when Keystore 2.0 shall be reset.
+        Reset = 0x20,    selinux name: reset;
+        /// Checked when Keystore 2.0 shall be unlocked.
+        Unlock = 0x40,    selinux name: unlock;
+        /// Checked when user is added or removed.
+        ChangeUser = 0x80,    selinux name: change_user;
+        /// Checked when password of the user is changed.
+        ChangePassword = 0x100,    selinux name: change_password;
+        /// Checked when a UID is cleared.
+        ClearUID = 0x200,    selinux name: clear_uid;
+        /// Checked when Credstore calls IKeystoreAuthorization to obtain auth tokens.
+        GetAuthToken = 0x400,  selinux name: get_auth_token;
+        /// Checked when earlyBootEnded() is called.
+        EarlyBootEnded = 0x800,   selinux name: early_boot_ended;
+        /// Checked when IKeystoreMaintenance::onDeviceOffBody is called.
+        ReportOffBody = 0x1000, selinux name: report_off_body;
+    }
+);
+
+/// Represents a set of `KeyPerm` permissions.
+/// `IntoIterator` is implemented for this struct allowing the iteration through all the
+/// permissions in the set.
+/// It also implements a function `includes(self, other)` that checks if the permissions
+/// in `other` are included in `self`.
+///
+/// KeyPermSet can be created with the macro `key_perm_set![]`.
+///
+/// ## Example
+/// ```
+/// let perms1 = key_perm_set![KeyPerm::use_(), KeyPerm::manage_blob(), KeyPerm::grant()];
+/// let perms2 = key_perm_set![KeyPerm::use_(), KeyPerm::manage_blob()];
+///
+/// assert!(perms1.includes(perms2))
+/// assert!(!perms2.includes(perms1))
+///
+/// let i = perms1.into_iter();
+/// // iteration in ascending order of the permission's numeric representation.
+/// assert_eq(Some(KeyPerm::manage_blob()), i.next());
+/// assert_eq(Some(KeyPerm::grant()), i.next());
+/// assert_eq(Some(KeyPerm::use_()), i.next());
+/// assert_eq(None, i.next());
+/// ```
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub struct KeyPermSet(pub i32);
+
+mod perm {
+    use super::*;
+
+    pub struct IntoIter {
+        vec: KeyPermSet,
+        pos: u8,
+    }
+
+    impl IntoIter {
+        pub fn new(v: KeyPermSet) -> Self {
+            Self { vec: v, pos: 0 }
+        }
+    }
+
+    impl std::iter::Iterator for IntoIter {
+        type Item = KeyPerm;
+
+        fn next(&mut self) -> Option<Self::Item> {
+            loop {
+                if self.pos == 32 {
+                    return None;
+                }
+                let p = self.vec.0 & (1 << self.pos);
+                self.pos += 1;
+                if p != 0 {
+                    return Some(KeyPerm::from(KeyPermission(p)));
+                }
+            }
+        }
+    }
+}
+
+impl From<KeyPerm> for KeyPermSet {
+    fn from(p: KeyPerm) -> Self {
+        Self((p.0).0 as i32)
+    }
+}
+
+/// allow conversion from the AIDL wire type i32 to a permission set.
+impl From<i32> for KeyPermSet {
+    fn from(p: i32) -> Self {
+        Self(p)
+    }
+}
+
+impl From<KeyPermSet> for i32 {
+    fn from(p: KeyPermSet) -> i32 {
+        p.0
+    }
+}
+
+impl KeyPermSet {
+    /// Returns true iff this permission set has all of the permissions that are in `other`.
+    pub fn includes<T: Into<KeyPermSet>>(&self, other: T) -> bool {
+        let o: KeyPermSet = other.into();
+        (self.0 & o.0) == o.0
+    }
+}
+
+/// This macro can be used to create a `KeyPermSet` from a list of `KeyPerm` values.
+///
+/// ## Example
+/// ```
+/// let v = key_perm_set![Perm::delete(), Perm::manage_blob()];
+/// ```
+#[macro_export]
+macro_rules! key_perm_set {
+    () => { KeyPermSet(0) };
+    ($head:expr $(, $tail:expr)* $(,)?) => {
+        KeyPermSet(($head.0).0 $(| ($tail.0).0)*)
+    };
+}
+
+impl IntoIterator for KeyPermSet {
+    type Item = KeyPerm;
+    type IntoIter = perm::IntoIter;
+
+    fn into_iter(self) -> Self::IntoIter {
+        Self::IntoIter::new(self)
+    }
+}
+
+/// Uses `selinux::check_access` to check if the given caller context `caller_cxt` may access
+/// the given permision `perm` of the `keystore2` security class.
+pub fn check_keystore_permission(caller_ctx: &CStr, perm: KeystorePerm) -> anyhow::Result<()> {
+    let target_context = getcon().context("check_keystore_permission: getcon failed.")?;
+    selinux::check_access(caller_ctx, &target_context, "keystore2", perm.to_selinux())
+}
+
+/// Uses `selinux::check_access` to check if the given caller context `caller_cxt` has
+/// all the permissions indicated in `access_vec` for the target domain indicated by the key
+/// descriptor `key` in the security class `keystore2_key`.
+///
+/// Also checks if the caller has the grant permission for the given target domain.
+///
+/// Attempts to grant the grant permission are always denied.
+///
+/// The only viable target domains are
+///  * `Domain::APP` in which case u:r:keystore:s0 is used as target context and
+///  * `Domain::SELINUX` in which case the `key.nspace` parameter is looked up in
+///                      SELinux keystore key backend, and the result is used
+///                      as target context.
+pub fn check_grant_permission(
+    caller_ctx: &CStr,
+    access_vec: KeyPermSet,
+    key: &KeyDescriptor,
+) -> anyhow::Result<()> {
+    let target_context = match key.domain {
+        Domain::APP => getcon().context("check_grant_permission: getcon failed.")?,
+        Domain::SELINUX => lookup_keystore2_key_context(key.nspace)
+            .context("check_grant_permission: Domain::SELINUX: Failed to lookup namespace.")?,
+        _ => return Err(KsError::sys()).context(format!("Cannot grant {:?}.", key.domain)),
+    };
+
+    selinux::check_access(caller_ctx, &target_context, "keystore2_key", "grant")
+        .context("Grant permission is required when granting.")?;
+
+    if access_vec.includes(KeyPerm::grant()) {
+        return Err(selinux::Error::perm()).context("Grant permission cannot be granted.");
+    }
+
+    for p in access_vec.into_iter() {
+        selinux::check_access(caller_ctx, &target_context, "keystore2_key", p.to_selinux())
+            .context(format!(
+                concat!(
+                    "check_grant_permission: check_access failed. ",
+                    "The caller may have tried to grant a permission that they don't possess. {:?}"
+                ),
+                p
+            ))?
+    }
+    Ok(())
+}
+
+/// Uses `selinux::check_access` to check if the given caller context `caller_cxt`
+/// has the permissions indicated by `perm` for the target domain indicated by the key
+/// descriptor `key` in the security class `keystore2_key`.
+///
+/// The behavior differs slightly depending on the selected target domain:
+///  * `Domain::APP` u:r:keystore:s0 is used as target context.
+///  * `Domain::SELINUX` `key.nspace` parameter is looked up in the SELinux keystore key
+///                      backend, and the result is used as target context.
+///  * `Domain::BLOB` Same as SELinux but the "manage_blob" permission is always checked additionally
+///                   to the one supplied in `perm`.
+///  * `Domain::GRANT` Does not use selinux::check_access. Instead the `access_vector`
+///                    parameter is queried for permission, which must be supplied in this case.
+///
+/// ## Return values.
+///  * Ok(()) If the requested permissions were granted.
+///  * Err(selinux::Error::perm()) If the requested permissions were denied.
+///  * Err(KsError::sys()) This error is produced if `Domain::GRANT` is selected but no `access_vec`
+///                      was supplied. It is also produced if `Domain::KEY_ID` was selected, and
+///                      on various unexpected backend failures.
+pub fn check_key_permission(
+    caller_uid: u32,
+    caller_ctx: &CStr,
+    perm: KeyPerm,
+    key: &KeyDescriptor,
+    access_vector: &Option<KeyPermSet>,
+) -> anyhow::Result<()> {
+    // If an access vector was supplied, the key is either accessed by GRANT or by KEY_ID.
+    // In the former case, key.domain was set to GRANT and we check the failure cases
+    // further below. If the access is requested by KEY_ID, key.domain would have been
+    // resolved to APP or SELINUX depending on where the key actually resides.
+    // Either way we can return here immediately if the access vector covers the requested
+    // permission. If it does not, we can still check if the caller has access by means of
+    // ownership.
+    if let Some(access_vector) = access_vector {
+        if access_vector.includes(perm) {
+            return Ok(());
+        }
+    }
+
+    let target_context = match key.domain {
+        // apps get the default keystore context
+        Domain::APP => {
+            if caller_uid as i64 != key.nspace {
+                return Err(selinux::Error::perm())
+                    .context("Trying to access key without ownership.");
+            }
+            getcon().context("check_key_permission: getcon failed.")?
+        }
+        Domain::SELINUX => lookup_keystore2_key_context(key.nspace)
+            .context("check_key_permission: Domain::SELINUX: Failed to lookup namespace.")?,
+        Domain::GRANT => {
+            match access_vector {
+                Some(_) => {
+                    return Err(selinux::Error::perm())
+                        .context(format!("\"{}\" not granted", perm.to_selinux()));
+                }
+                None => {
+                    // If DOMAIN_GRANT was selected an access vector must be supplied.
+                    return Err(KsError::sys()).context(
+                        "Cannot check permission for Domain::GRANT without access vector.",
+                    );
+                }
+            }
+        }
+        Domain::KEY_ID => {
+            // We should never be called with `Domain::KEY_ID. The database
+            // lookup should have converted this into one of `Domain::APP`
+            // or `Domain::SELINUX`.
+            return Err(KsError::sys()).context("Cannot check permission for Domain::KEY_ID.");
+        }
+        Domain::BLOB => {
+            let tctx = lookup_keystore2_key_context(key.nspace)
+                .context("Domain::BLOB: Failed to lookup namespace.")?;
+            // If DOMAIN_KEY_BLOB was specified, we check for the "manage_blob"
+            // permission in addition to the requested permission.
+            selinux::check_access(
+                caller_ctx,
+                &tctx,
+                "keystore2_key",
+                KeyPerm::manage_blob().to_selinux(),
+            )?;
+
+            tctx
+        }
+        _ => {
+            return Err(KsError::sys())
+                .context(format!("Unknown domain value: \"{:?}\".", key.domain))
+        }
+    };
+
+    selinux::check_access(caller_ctx, &target_context, "keystore2_key", perm.to_selinux())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use anyhow::anyhow;
+    use anyhow::Result;
+    use keystore2_selinux::*;
+
+    const ALL_PERMS: KeyPermSet = key_perm_set![
+        KeyPerm::manage_blob(),
+        KeyPerm::delete(),
+        KeyPerm::use_dev_id(),
+        KeyPerm::req_forced_op(),
+        KeyPerm::gen_unique_id(),
+        KeyPerm::grant(),
+        KeyPerm::get_info(),
+        KeyPerm::rebind(),
+        KeyPerm::update(),
+        KeyPerm::use_(),
+        KeyPerm::convert_storage_key_to_ephemeral(),
+    ];
+
+    const SYSTEM_SERVER_PERMISSIONS_NO_GRANT: KeyPermSet = key_perm_set![
+        KeyPerm::delete(),
+        KeyPerm::use_dev_id(),
+        // No KeyPerm::grant()
+        KeyPerm::get_info(),
+        KeyPerm::rebind(),
+        KeyPerm::update(),
+        KeyPerm::use_(),
+    ];
+
+    const NOT_GRANT_PERMS: KeyPermSet = key_perm_set![
+        KeyPerm::manage_blob(),
+        KeyPerm::delete(),
+        KeyPerm::use_dev_id(),
+        KeyPerm::req_forced_op(),
+        KeyPerm::gen_unique_id(),
+        // No KeyPerm::grant()
+        KeyPerm::get_info(),
+        KeyPerm::rebind(),
+        KeyPerm::update(),
+        KeyPerm::use_(),
+        KeyPerm::convert_storage_key_to_ephemeral(),
+    ];
+
+    const UNPRIV_PERMS: KeyPermSet = key_perm_set![
+        KeyPerm::delete(),
+        KeyPerm::get_info(),
+        KeyPerm::rebind(),
+        KeyPerm::update(),
+        KeyPerm::use_(),
+    ];
+
+    /// The su_key namespace as defined in su.te and keystore_key_contexts of the
+    /// SePolicy (system/sepolicy).
+    const SU_KEY_NAMESPACE: i32 = 0;
+    /// The shell_key namespace as defined in shell.te and keystore_key_contexts of the
+    /// SePolicy (system/sepolicy).
+    const SHELL_KEY_NAMESPACE: i32 = 1;
+
+    pub fn test_getcon() -> Result<Context> {
+        Context::new("u:object_r:keystore:s0")
+    }
+
+    // This macro evaluates the given expression and checks that
+    // a) evaluated to Result::Err() and that
+    // b) the wrapped error is selinux::Error::perm() (permission denied).
+    // We use a macro here because a function would mask which invocation caused the failure.
+    //
+    // TODO b/164121720 Replace this macro with a function when `track_caller` is available.
+    macro_rules! assert_perm_failed {
+        ($test_function:expr) => {
+            let result = $test_function;
+            assert!(result.is_err(), "Permission check should have failed.");
+            assert_eq!(
+                Some(&selinux::Error::perm()),
+                result.err().unwrap().root_cause().downcast_ref::<selinux::Error>()
+            );
+        };
+    }
+
+    fn check_context() -> Result<(selinux::Context, i32, bool)> {
+        // Calling the non mocked selinux::getcon here intended.
+        let context = selinux::getcon()?;
+        match context.to_str().unwrap() {
+            "u:r:su:s0" => Ok((context, SU_KEY_NAMESPACE, true)),
+            "u:r:shell:s0" => Ok((context, SHELL_KEY_NAMESPACE, false)),
+            c => Err(anyhow!(format!(
+                "This test must be run as \"su\" or \"shell\". Current context: \"{}\"",
+                c
+            ))),
+        }
+    }
+
+    #[test]
+    fn check_keystore_permission_test() -> Result<()> {
+        let system_server_ctx = Context::new("u:r:system_server:s0")?;
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::add_auth()).is_ok());
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::clear_ns()).is_ok());
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::get_state()).is_ok());
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::lock()).is_ok());
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::reset()).is_ok());
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::unlock()).is_ok());
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::change_user()).is_ok());
+        assert!(
+            check_keystore_permission(&system_server_ctx, KeystorePerm::change_password()).is_ok()
+        );
+        assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::clear_uid()).is_ok());
+        let shell_ctx = Context::new("u:r:shell:s0")?;
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::add_auth()));
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::clear_ns()));
+        assert!(check_keystore_permission(&shell_ctx, KeystorePerm::get_state()).is_ok());
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::list()));
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::lock()));
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::reset()));
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::unlock()));
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::change_user()));
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::change_password()));
+        assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::clear_uid()));
+        Ok(())
+    }
+
+    #[test]
+    fn check_grant_permission_app() -> Result<()> {
+        let system_server_ctx = Context::new("u:r:system_server:s0")?;
+        let shell_ctx = Context::new("u:r:shell:s0")?;
+        let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
+        check_grant_permission(&system_server_ctx, SYSTEM_SERVER_PERMISSIONS_NO_GRANT, &key)
+            .expect("Grant permission check failed.");
+
+        // attempts to grant the grant permission must always fail even when privileged.
+        assert_perm_failed!(check_grant_permission(
+            &system_server_ctx,
+            KeyPerm::grant().into(),
+            &key
+        ));
+        // unprivileged grant attempts always fail. shell does not have the grant permission.
+        assert_perm_failed!(check_grant_permission(&shell_ctx, UNPRIV_PERMS, &key));
+        Ok(())
+    }
+
+    #[test]
+    fn check_grant_permission_selinux() -> Result<()> {
+        let (sctx, namespace, is_su) = check_context()?;
+        let key = KeyDescriptor {
+            domain: Domain::SELINUX,
+            nspace: namespace as i64,
+            alias: None,
+            blob: None,
+        };
+        if is_su {
+            assert!(check_grant_permission(&sctx, NOT_GRANT_PERMS, &key).is_ok());
+            // attempts to grant the grant permission must always fail even when privileged.
+            assert_perm_failed!(check_grant_permission(&sctx, KeyPerm::grant().into(), &key));
+        } else {
+            // unprivileged grant attempts always fail. shell does not have the grant permission.
+            assert_perm_failed!(check_grant_permission(&sctx, UNPRIV_PERMS, &key));
+        }
+        Ok(())
+    }
+
+    #[test]
+    fn check_key_permission_domain_grant() -> Result<()> {
+        let key = KeyDescriptor { domain: Domain::GRANT, nspace: 0, alias: None, blob: None };
+
+        assert_perm_failed!(check_key_permission(
+            0,
+            &selinux::Context::new("ignored").unwrap(),
+            KeyPerm::grant(),
+            &key,
+            &Some(UNPRIV_PERMS)
+        ));
+
+        check_key_permission(
+            0,
+            &selinux::Context::new("ignored").unwrap(),
+            KeyPerm::use_(),
+            &key,
+            &Some(ALL_PERMS),
+        )
+    }
+
+    #[test]
+    fn check_key_permission_domain_app() -> Result<()> {
+        let system_server_ctx = Context::new("u:r:system_server:s0")?;
+        let shell_ctx = Context::new("u:r:shell:s0")?;
+        let gmscore_app = Context::new("u:r:gmscore_app:s0")?;
+
+        let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
+
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::use_(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::delete(), &key, &None).is_ok());
+        assert!(
+            check_key_permission(0, &system_server_ctx, KeyPerm::get_info(), &key, &None).is_ok()
+        );
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::rebind(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::update(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &system_server_ctx, KeyPerm::grant(), &key, &None).is_ok());
+        assert!(
+            check_key_permission(0, &system_server_ctx, KeyPerm::use_dev_id(), &key, &None).is_ok()
+        );
+        assert!(
+            check_key_permission(0, &gmscore_app, KeyPerm::gen_unique_id(), &key, &None).is_ok()
+        );
+
+        assert!(check_key_permission(0, &shell_ctx, KeyPerm::use_(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &shell_ctx, KeyPerm::delete(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &shell_ctx, KeyPerm::get_info(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &shell_ctx, KeyPerm::rebind(), &key, &None).is_ok());
+        assert!(check_key_permission(0, &shell_ctx, KeyPerm::update(), &key, &None).is_ok());
+        assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::grant(), &key, &None));
+        assert_perm_failed!(check_key_permission(
+            0,
+            &shell_ctx,
+            KeyPerm::req_forced_op(),
+            &key,
+            &None
+        ));
+        assert_perm_failed!(check_key_permission(
+            0,
+            &shell_ctx,
+            KeyPerm::manage_blob(),
+            &key,
+            &None
+        ));
+        assert_perm_failed!(check_key_permission(
+            0,
+            &shell_ctx,
+            KeyPerm::use_dev_id(),
+            &key,
+            &None
+        ));
+        assert_perm_failed!(check_key_permission(
+            0,
+            &shell_ctx,
+            KeyPerm::gen_unique_id(),
+            &key,
+            &None
+        ));
+
+        // Also make sure that the permission fails if the caller is not the owner.
+        assert_perm_failed!(check_key_permission(
+            1, // the owner is 0
+            &system_server_ctx,
+            KeyPerm::use_(),
+            &key,
+            &None
+        ));
+        // Unless there was a grant.
+        assert!(check_key_permission(
+            1,
+            &system_server_ctx,
+            KeyPerm::use_(),
+            &key,
+            &Some(key_perm_set![KeyPerm::use_()])
+        )
+        .is_ok());
+        // But fail if the grant did not cover the requested permission.
+        assert_perm_failed!(check_key_permission(
+            1,
+            &system_server_ctx,
+            KeyPerm::use_(),
+            &key,
+            &Some(key_perm_set![KeyPerm::get_info()])
+        ));
+
+        Ok(())
+    }
+
+    #[test]
+    fn check_key_permission_domain_selinux() -> Result<()> {
+        let (sctx, namespace, is_su) = check_context()?;
+        let key = KeyDescriptor {
+            domain: Domain::SELINUX,
+            nspace: namespace as i64,
+            alias: None,
+            blob: None,
+        };
+
+        if is_su {
+            assert!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::delete(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::get_info(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::rebind(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::update(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::grant(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::manage_blob(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::use_dev_id(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::gen_unique_id(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::req_forced_op(), &key, &None).is_ok());
+        } else {
+            assert!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::delete(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::get_info(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::rebind(), &key, &None).is_ok());
+            assert!(check_key_permission(0, &sctx, KeyPerm::update(), &key, &None).is_ok());
+            assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::grant(), &key, &None));
+            assert_perm_failed!(check_key_permission(
+                0,
+                &sctx,
+                KeyPerm::req_forced_op(),
+                &key,
+                &None
+            ));
+            assert_perm_failed!(check_key_permission(
+                0,
+                &sctx,
+                KeyPerm::manage_blob(),
+                &key,
+                &None
+            ));
+            assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::use_dev_id(), &key, &None));
+            assert_perm_failed!(check_key_permission(
+                0,
+                &sctx,
+                KeyPerm::gen_unique_id(),
+                &key,
+                &None
+            ));
+        }
+        Ok(())
+    }
+
+    #[test]
+    fn check_key_permission_domain_blob() -> Result<()> {
+        let (sctx, namespace, is_su) = check_context()?;
+        let key = KeyDescriptor {
+            domain: Domain::BLOB,
+            nspace: namespace as i64,
+            alias: None,
+            blob: None,
+        };
+
+        if is_su {
+            check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None)
+        } else {
+            assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None));
+            Ok(())
+        }
+    }
+
+    #[test]
+    fn check_key_permission_domain_key_id() -> Result<()> {
+        let key = KeyDescriptor { domain: Domain::KEY_ID, nspace: 0, alias: None, blob: None };
+
+        assert_eq!(
+            Some(&KsError::sys()),
+            check_key_permission(
+                0,
+                &selinux::Context::new("ignored").unwrap(),
+                KeyPerm::use_(),
+                &key,
+                &None
+            )
+            .err()
+            .unwrap()
+            .root_cause()
+            .downcast_ref::<KsError>()
+        );
+        Ok(())
+    }
+
+    #[test]
+    fn key_perm_set_all_test() {
+        let v = key_perm_set![
+            KeyPerm::manage_blob(),
+            KeyPerm::delete(),
+            KeyPerm::use_dev_id(),
+            KeyPerm::req_forced_op(),
+            KeyPerm::gen_unique_id(),
+            KeyPerm::grant(),
+            KeyPerm::get_info(),
+            KeyPerm::rebind(),
+            KeyPerm::update(),
+            KeyPerm::use_() // Test if the macro accepts missing comma at the end of the list.
+        ];
+        let mut i = v.into_iter();
+        assert_eq!(i.next().unwrap().to_selinux(), "delete");
+        assert_eq!(i.next().unwrap().to_selinux(), "gen_unique_id");
+        assert_eq!(i.next().unwrap().to_selinux(), "get_info");
+        assert_eq!(i.next().unwrap().to_selinux(), "grant");
+        assert_eq!(i.next().unwrap().to_selinux(), "manage_blob");
+        assert_eq!(i.next().unwrap().to_selinux(), "rebind");
+        assert_eq!(i.next().unwrap().to_selinux(), "req_forced_op");
+        assert_eq!(i.next().unwrap().to_selinux(), "update");
+        assert_eq!(i.next().unwrap().to_selinux(), "use");
+        assert_eq!(i.next().unwrap().to_selinux(), "use_dev_id");
+        assert_eq!(None, i.next());
+    }
+    #[test]
+    fn key_perm_set_sparse_test() {
+        let v = key_perm_set![
+            KeyPerm::manage_blob(),
+            KeyPerm::req_forced_op(),
+            KeyPerm::gen_unique_id(),
+            KeyPerm::update(),
+            KeyPerm::use_(), // Test if macro accepts the comma at the end of the list.
+        ];
+        let mut i = v.into_iter();
+        assert_eq!(i.next().unwrap().to_selinux(), "gen_unique_id");
+        assert_eq!(i.next().unwrap().to_selinux(), "manage_blob");
+        assert_eq!(i.next().unwrap().to_selinux(), "req_forced_op");
+        assert_eq!(i.next().unwrap().to_selinux(), "update");
+        assert_eq!(i.next().unwrap().to_selinux(), "use");
+        assert_eq!(None, i.next());
+    }
+    #[test]
+    fn key_perm_set_empty_test() {
+        let v = key_perm_set![];
+        let mut i = v.into_iter();
+        assert_eq!(None, i.next());
+    }
+    #[test]
+    fn key_perm_set_include_subset_test() {
+        let v1 = key_perm_set![
+            KeyPerm::manage_blob(),
+            KeyPerm::delete(),
+            KeyPerm::use_dev_id(),
+            KeyPerm::req_forced_op(),
+            KeyPerm::gen_unique_id(),
+            KeyPerm::grant(),
+            KeyPerm::get_info(),
+            KeyPerm::rebind(),
+            KeyPerm::update(),
+            KeyPerm::use_(),
+        ];
+        let v2 = key_perm_set![
+            KeyPerm::manage_blob(),
+            KeyPerm::delete(),
+            KeyPerm::rebind(),
+            KeyPerm::update(),
+            KeyPerm::use_(),
+        ];
+        assert!(v1.includes(v2));
+        assert!(!v2.includes(v1));
+    }
+    #[test]
+    fn key_perm_set_include_equal_test() {
+        let v1 = key_perm_set![
+            KeyPerm::manage_blob(),
+            KeyPerm::delete(),
+            KeyPerm::rebind(),
+            KeyPerm::update(),
+            KeyPerm::use_(),
+        ];
+        let v2 = key_perm_set![
+            KeyPerm::manage_blob(),
+            KeyPerm::delete(),
+            KeyPerm::rebind(),
+            KeyPerm::update(),
+            KeyPerm::use_(),
+        ];
+        assert!(v1.includes(v2));
+        assert!(v2.includes(v1));
+    }
+    #[test]
+    fn key_perm_set_include_overlap_test() {
+        let v1 = key_perm_set![
+            KeyPerm::manage_blob(),
+            KeyPerm::delete(),
+            KeyPerm::grant(), // only in v1
+            KeyPerm::rebind(),
+            KeyPerm::update(),
+            KeyPerm::use_(),
+        ];
+        let v2 = key_perm_set![
+            KeyPerm::manage_blob(),
+            KeyPerm::delete(),
+            KeyPerm::req_forced_op(), // only in v2
+            KeyPerm::rebind(),
+            KeyPerm::update(),
+            KeyPerm::use_(),
+        ];
+        assert!(!v1.includes(v2));
+        assert!(!v2.includes(v1));
+    }
+    #[test]
+    fn key_perm_set_include_no_overlap_test() {
+        let v1 = key_perm_set![KeyPerm::manage_blob(), KeyPerm::delete(), KeyPerm::grant(),];
+        let v2 = key_perm_set![
+            KeyPerm::req_forced_op(),
+            KeyPerm::rebind(),
+            KeyPerm::update(),
+            KeyPerm::use_(),
+        ];
+        assert!(!v1.includes(v2));
+        assert!(!v2.includes(v1));
+    }
+}
diff --git a/keystore2/src/raw_device.rs b/keystore2/src/raw_device.rs
new file mode 100644
index 0000000..9e6ef41
--- /dev/null
+++ b/keystore2/src/raw_device.rs
@@ -0,0 +1,248 @@
+// 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.
+
+//! Provide the [`KeyMintDevice`] wrapper for operating directly on a KeyMint device.
+
+use crate::{
+    database::{
+        BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, KeyEntry, KeyEntryLoadBits,
+        KeyIdGuard, KeyMetaData, KeyMetaEntry, KeyType, KeystoreDB, SubComponentType, Uuid,
+    },
+    error::{map_km_error, Error},
+    globals::get_keymint_device,
+    super_key::KeyBlob,
+    utils::{key_characteristics_to_internal, watchdog as wd, Asp, AID_KEYSTORE},
+};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    BeginResult::BeginResult, ErrorCode::ErrorCode, HardwareAuthToken::HardwareAuthToken,
+    IKeyMintDevice::IKeyMintDevice, IKeyMintOperation::IKeyMintOperation,
+    KeyCreationResult::KeyCreationResult, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
+    SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+};
+use anyhow::{Context, Result};
+use binder::Strong;
+
+/// Wrapper for operating directly on a KeyMint device.
+/// These methods often mirror methods in [`crate::security_level`]. However
+/// the functions in [`crate::security_level`] make assumptions that hold, and has side effects
+/// that make sense, only if called by an external client through binder.
+/// In addition we are trying to maintain a separation between interface services
+/// so that the architecture is compatible with a future move to multiple thread pools.
+/// So the simplest approach today is to write new implementations of them for internal use.
+/// Because these methods run very early, we don't even try to cooperate with
+/// the operation slot database; we assume there will be plenty of slots.
+pub struct KeyMintDevice {
+    asp: Asp,
+    km_uuid: Uuid,
+}
+
+impl KeyMintDevice {
+    /// Get a [`KeyMintDevice`] for the given [`SecurityLevel`]
+    pub fn get(security_level: SecurityLevel) -> Result<KeyMintDevice> {
+        let (asp, _hw_info, km_uuid) = get_keymint_device(&security_level)
+            .context("In KeyMintDevice::get: get_keymint_device failed")?;
+        Ok(KeyMintDevice { asp, km_uuid })
+    }
+
+    /// Create a KM key and store in the database.
+    pub fn create_and_store_key<F>(
+        &self,
+        db: &mut KeystoreDB,
+        key_desc: &KeyDescriptor,
+        creator: F,
+    ) -> Result<()>
+    where
+        F: FnOnce(Strong<dyn IKeyMintDevice>) -> Result<KeyCreationResult, binder::Status>,
+    {
+        let km_dev: Strong<dyn IKeyMintDevice> = self
+            .asp
+            .get_interface()
+            .context("In create_and_store_key: Failed to get KeyMint device")?;
+        let creation_result =
+            map_km_error(creator(km_dev)).context("In create_and_store_key: creator failed")?;
+        let key_parameters = key_characteristics_to_internal(creation_result.keyCharacteristics);
+
+        let creation_date =
+            DateTime::now().context("In create_and_store_key: DateTime::now() failed")?;
+
+        let mut key_metadata = KeyMetaData::new();
+        key_metadata.add(KeyMetaEntry::CreationDate(creation_date));
+        let mut blob_metadata = BlobMetaData::new();
+        blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
+
+        db.store_new_key(
+            &key_desc,
+            &key_parameters,
+            &(&creation_result.keyBlob, &blob_metadata),
+            &CertificateInfo::new(None, None),
+            &key_metadata,
+            &self.km_uuid,
+        )
+        .context("In create_and_store_key: store_new_key failed")?;
+        Ok(())
+    }
+
+    /// Generate a KeyDescriptor for internal-use keys.
+    pub fn internal_descriptor(alias: String) -> KeyDescriptor {
+        KeyDescriptor {
+            domain: Domain::APP,
+            nspace: AID_KEYSTORE as i64,
+            alias: Some(alias),
+            blob: None,
+        }
+    }
+
+    /// Look up an internal-use key in the database given a key descriptor.
+    pub fn lookup_from_desc(
+        db: &mut KeystoreDB,
+        key_desc: &KeyDescriptor,
+    ) -> Result<(KeyIdGuard, KeyEntry)> {
+        db.load_key_entry(&key_desc, KeyType::Client, KeyEntryLoadBits::KM, AID_KEYSTORE, |_, _| {
+            Ok(())
+        })
+        .context("In lookup_from_desc: load_key_entry failed")
+    }
+
+    /// Look up the key in the database, and return None if it is absent.
+    pub fn not_found_is_none(
+        lookup: Result<(KeyIdGuard, KeyEntry)>,
+    ) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
+        match lookup {
+            Ok(result) => Ok(Some(result)),
+            Err(e) => match e.root_cause().downcast_ref::<Error>() {
+                Some(&Error::Rc(ResponseCode::KEY_NOT_FOUND)) => Ok(None),
+                _ => Err(e),
+            },
+        }
+    }
+
+    /// This does the lookup and store in separate transactions; caller must
+    /// hold a lock before calling.
+    pub fn lookup_or_generate_key(
+        &self,
+        db: &mut KeystoreDB,
+        key_desc: &KeyDescriptor,
+        params: &[KeyParameter],
+    ) -> Result<(KeyIdGuard, KeyEntry)> {
+        // We use a separate transaction for the lookup than for the store
+        // - to keep the code simple
+        // - because the caller needs to hold a lock in any case
+        // - because it avoids holding database locks during slow
+        //   KeyMint operations
+        let lookup = Self::not_found_is_none(Self::lookup_from_desc(db, key_desc))
+            .context("In lookup_or_generate_key: first lookup failed")?;
+        if let Some(result) = lookup {
+            Ok(result)
+        } else {
+            self.create_and_store_key(db, &key_desc, |km_dev| km_dev.generateKey(&params, None))
+                .context("In lookup_or_generate_key: generate_and_store_key failed")?;
+            Self::lookup_from_desc(db, key_desc)
+                .context("In lookup_or_generate_key: second lookup failed")
+        }
+    }
+
+    /// Call the passed closure; if it returns `KEY_REQUIRES_UPGRADE`, call upgradeKey, and
+    /// write the upgraded key to the database.
+    fn upgrade_keyblob_if_required_with<T, F>(
+        &self,
+        db: &mut KeystoreDB,
+        km_dev: &Strong<dyn IKeyMintDevice>,
+        key_id_guard: &KeyIdGuard,
+        key_blob: &KeyBlob,
+        f: F,
+    ) -> Result<T>
+    where
+        F: Fn(&[u8]) -> Result<T, Error>,
+    {
+        match f(key_blob) {
+            Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
+                let upgraded_blob = map_km_error({
+                    let _wp = wd::watch_millis(
+                        "In KeyMintDevice::upgrade_keyblob_if_required_with: calling upgradeKey.",
+                        500,
+                    );
+                    km_dev.upgradeKey(key_blob, &[])
+                })
+                .context("In upgrade_keyblob_if_required_with: Upgrade failed")?;
+
+                let mut new_blob_metadata = BlobMetaData::new();
+                new_blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
+
+                db.set_blob(
+                    key_id_guard,
+                    SubComponentType::KEY_BLOB,
+                    Some(&upgraded_blob),
+                    Some(&new_blob_metadata),
+                )
+                .context(concat!(
+                    "In upgrade_keyblob_if_required_with: ",
+                    "Failed to insert upgraded blob into the database"
+                ))?;
+
+                Ok(f(&upgraded_blob).context(concat!(
+                    "In upgrade_keyblob_if_required_with: ",
+                    "Closure failed after upgrade"
+                ))?)
+            }
+            result => Ok(result.context("In upgrade_keyblob_if_required_with: Closure failed")?),
+        }
+    }
+
+    /// Use the created key in an operation that can be done with
+    /// a call to begin followed by a call to finish.
+    #[allow(clippy::too_many_arguments)]
+    pub fn use_key_in_one_step(
+        &self,
+        db: &mut KeystoreDB,
+        key_id_guard: &KeyIdGuard,
+        key_entry: &KeyEntry,
+        purpose: KeyPurpose,
+        operation_parameters: &[KeyParameter],
+        auth_token: Option<&HardwareAuthToken>,
+        input: &[u8],
+    ) -> Result<Vec<u8>> {
+        let km_dev: Strong<dyn IKeyMintDevice> = self
+            .asp
+            .get_interface()
+            .context("In use_key_in_one_step: Failed to get KeyMint device")?;
+
+        let (key_blob, _blob_metadata) = key_entry
+            .key_blob_info()
+            .as_ref()
+            .ok_or_else(Error::sys)
+            .context("use_key_in_one_step: Keyblob missing")?;
+        let key_blob = KeyBlob::Ref(&key_blob);
+
+        let begin_result: BeginResult = self
+            .upgrade_keyblob_if_required_with(db, &km_dev, key_id_guard, &key_blob, |blob| {
+                map_km_error({
+                    let _wp = wd::watch_millis("In use_key_in_one_step: calling: begin", 500);
+                    km_dev.begin(purpose, blob, operation_parameters, auth_token)
+                })
+            })
+            .context("In use_key_in_one_step: Failed to begin operation.")?;
+        let operation: Strong<dyn IKeyMintOperation> = begin_result
+            .operation
+            .ok_or_else(Error::sys)
+            .context("In use_key_in_one_step: Operation missing")?;
+        map_km_error({
+            let _wp = wd::watch_millis("In use_key_in_one_step: calling: finish", 500);
+            operation.finish(Some(input), None, None, None, None)
+        })
+        .context("In use_key_in_one_step: Failed to finish operation.")
+    }
+}
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
new file mode 100644
index 0000000..fc1a6ad
--- /dev/null
+++ b/keystore2/src/remote_provisioning.rs
@@ -0,0 +1,457 @@
+// 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.
+
+//! This is the implementation for the remote provisioning AIDL interface between
+//! the network providers for remote provisioning and the system. This interface
+//! allows the caller to prompt the Remote Provisioning HAL to generate keys and
+//! CBOR blobs that can be ferried to a provisioning server that will return
+//! certificate chains signed by some root authority and stored in a keystore SQLite
+//! DB.
+
+#![allow(clippy::from_over_into, clippy::needless_question_mark, clippy::vec_init_then_push)]
+
+use std::collections::HashMap;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, AttestationKey::AttestationKey, Certificate::Certificate,
+    DeviceInfo::DeviceInfo, IRemotelyProvisionedComponent::IRemotelyProvisionedComponent,
+    KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue,
+    MacedPublicKey::MacedPublicKey, ProtectedData::ProtectedData, SecurityLevel::SecurityLevel,
+    Tag::Tag,
+};
+use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
+    AttestationPoolStatus::AttestationPoolStatus, IRemoteProvisioning::BnRemoteProvisioning,
+    IRemoteProvisioning::IRemoteProvisioning,
+};
+use android_security_remoteprovisioning::binder::{BinderFeatures, Strong};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
+use anyhow::{Context, Result};
+use keystore2_crypto::parse_subject_from_certificate;
+use std::sync::atomic::{AtomicBool, Ordering};
+
+use crate::database::{CertificateChain, KeystoreDB, Uuid};
+use crate::error::{self, map_or_log_err, map_rem_prov_error, Error};
+use crate::globals::{get_keymint_device, get_remotely_provisioned_component, DB};
+use crate::utils::{watchdog as wd, Asp};
+
+/// Contains helper functions to check if remote provisioning is enabled on the system and, if so,
+/// to assign and retrieve attestation keys and certificate chains.
+#[derive(Default)]
+pub struct RemProvState {
+    security_level: SecurityLevel,
+    km_uuid: Uuid,
+    is_hal_present: AtomicBool,
+}
+
+impl RemProvState {
+    /// Creates a RemProvState struct.
+    pub fn new(security_level: SecurityLevel, km_uuid: Uuid) -> Self {
+        Self { security_level, km_uuid, is_hal_present: AtomicBool::new(true) }
+    }
+
+    /// Checks if remote provisioning is enabled and partially caches the result. On a hybrid system
+    /// remote provisioning can flip from being disabled to enabled depending on responses from the
+    /// server, so unfortunately caching the presence or absence of the HAL is not enough to fully
+    /// make decisions about the state of remote provisioning during runtime.
+    fn check_rem_prov_enabled(&self, db: &mut KeystoreDB) -> Result<bool> {
+        if !self.is_hal_present.load(Ordering::Relaxed)
+            || get_remotely_provisioned_component(&self.security_level).is_err()
+        {
+            self.is_hal_present.store(false, Ordering::Relaxed);
+            return Ok(false);
+        }
+        // To check if remote provisioning is enabled on a system that supports both remote
+        // provisioning and factory provisioned keys, we only need to check if there are any
+        // keys at all generated to indicate if the app has gotten the signal to begin filling
+        // the key pool from the server.
+        let pool_status = db
+            .get_attestation_pool_status(0 /* date */, &self.km_uuid)
+            .context("In check_rem_prov_enabled: failed to get attestation pool status.")?;
+        Ok(pool_status.total != 0)
+    }
+
+    /// Fetches a remote provisioning attestation key and certificate chain inside of the
+    /// returned `CertificateChain` struct if one exists for the given caller_uid. If one has not
+    /// been assigned, this function will assign it. If there are no signed attestation keys
+    /// available to be assigned, it will return the ResponseCode `OUT_OF_KEYS`
+    fn get_rem_prov_attest_key(
+        &self,
+        key: &KeyDescriptor,
+        caller_uid: u32,
+        db: &mut KeystoreDB,
+    ) -> Result<Option<CertificateChain>> {
+        match key.domain {
+            Domain::APP => {
+                // Attempt to get an Attestation Key once. If it fails, then the app doesn't
+                // have a valid chain assigned to it. The helper function will return None after
+                // attempting to assign a key. An error will be thrown if the pool is simply out
+                // of usable keys. Then another attempt to fetch the just-assigned key will be
+                // made. If this fails too, something is very wrong.
+                self.get_rem_prov_attest_key_helper(key, caller_uid, db)
+                    .context("In get_rem_prov_attest_key: Failed to get a key")?
+                    .map_or_else(
+                        || self.get_rem_prov_attest_key_helper(key, caller_uid, db),
+                        |v| Ok(Some(v)),
+                    )
+                    .context(concat!(
+                        "In get_rem_prov_attest_key: Failed to get a key after",
+                        "attempting to assign one."
+                    ))?
+                    .map_or_else(
+                        || {
+                            Err(Error::sys()).context(concat!(
+                                "In get_rem_prov_attest_key: Attempted to assign a ",
+                                "key and failed silently. Something is very wrong."
+                            ))
+                        },
+                        |cert_chain| Ok(Some(cert_chain)),
+                    )
+            }
+            _ => Ok(None),
+        }
+    }
+
+    /// Returns None if an AttestationKey fails to be assigned. Errors if no keys are available.
+    fn get_rem_prov_attest_key_helper(
+        &self,
+        key: &KeyDescriptor,
+        caller_uid: u32,
+        db: &mut KeystoreDB,
+    ) -> Result<Option<CertificateChain>> {
+        let cert_chain = db
+            .retrieve_attestation_key_and_cert_chain(key.domain, caller_uid as i64, &self.km_uuid)
+            .context("In get_rem_prov_attest_key_helper: Failed to retrieve a key + cert chain")?;
+        match cert_chain {
+            Some(cert_chain) => Ok(Some(cert_chain)),
+            // Either this app needs to be assigned a key, or the pool is empty. An error will
+            // be thrown if there is no key available to assign. This will indicate that the app
+            // should be nudged to provision more keys so keystore can retry.
+            None => {
+                db.assign_attestation_key(key.domain, caller_uid as i64, &self.km_uuid)
+                    .context("In get_rem_prov_attest_key_helper: Failed to assign a key")?;
+                Ok(None)
+            }
+        }
+    }
+
+    fn is_asymmetric_key(&self, params: &[KeyParameter]) -> bool {
+        params.iter().any(|kp| {
+            matches!(
+                kp,
+                KeyParameter {
+                    tag: Tag::ALGORITHM,
+                    value: KeyParameterValue::Algorithm(Algorithm::RSA)
+                } | KeyParameter {
+                    tag: Tag::ALGORITHM,
+                    value: KeyParameterValue::Algorithm(Algorithm::EC)
+                }
+            )
+        })
+    }
+
+    /// Checks to see (1) if the key in question should be attested to based on the algorithm and
+    /// (2) if remote provisioning is present and enabled on the system. If these conditions are
+    /// met, it makes an attempt to fetch the attestation key assigned to the `caller_uid`.
+    ///
+    /// It returns the ResponseCode `OUT_OF_KEYS` if there is not one key currently assigned to the
+    /// `caller_uid` and there are none available to assign.
+    pub fn get_remotely_provisioned_attestation_key_and_certs(
+        &self,
+        key: &KeyDescriptor,
+        caller_uid: u32,
+        params: &[KeyParameter],
+        db: &mut KeystoreDB,
+    ) -> Result<Option<(AttestationKey, Certificate)>> {
+        if !self.is_asymmetric_key(params) || !self.check_rem_prov_enabled(db)? {
+            // There is no remote provisioning component for this security level on the
+            // device. Return None so the underlying KM instance knows to use its
+            // factory provisioned key instead. Alternatively, it's not an asymmetric key
+            // and therefore will not be attested.
+            Ok(None)
+        } else {
+            match self.get_rem_prov_attest_key(&key, caller_uid, db).context(concat!(
+                "In get_remote_provisioning_key_and_certs: Failed to get ",
+                "attestation key"
+            ))? {
+                Some(cert_chain) => Ok(Some((
+                    AttestationKey {
+                        keyBlob: cert_chain.private_key.to_vec(),
+                        attestKeyParams: vec![],
+                        issuerSubjectName: parse_subject_from_certificate(&cert_chain.batch_cert)
+                            .context(concat!(
+                            "In get_remote_provisioning_key_and_certs: Failed to ",
+                            "parse subject."
+                        ))?,
+                    },
+                    Certificate { encodedCertificate: cert_chain.cert_chain },
+                ))),
+                None => Ok(None),
+            }
+        }
+    }
+}
+/// Implementation of the IRemoteProvisioning service.
+#[derive(Default)]
+pub struct RemoteProvisioningService {
+    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 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, BinderFeatures::default()))
+    }
+
+    /// Populates the AttestationPoolStatus parcelable with information about how many
+    /// certs will be expiring by the date provided in `expired_by` along with how many
+    /// keys have not yet been assigned.
+    pub fn get_pool_status(
+        &self,
+        expired_by: i64,
+        sec_level: SecurityLevel,
+    ) -> Result<AttestationPoolStatus> {
+        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)?)
+        })
+    }
+
+    /// Generates a CBOR blob which will be assembled by the calling code into a larger
+    /// CBOR blob intended for delivery to a provisioning serever. This blob will contain
+    /// `num_csr` certificate signing requests for attestation keys generated in the TEE,
+    /// along with a server provided `eek` and `challenge`. The endpoint encryption key will
+    /// be used to encrypt the sensitive contents being transmitted to the server, and the
+    /// challenge will ensure freshness. A `test_mode` flag will instruct the remote provisioning
+    /// HAL if it is okay to accept EEKs that aren't signed by something that chains back to the
+    /// baked in root of trust in the underlying IRemotelyProvisionedComponent instance.
+    #[allow(clippy::too_many_arguments)]
+    pub fn generate_csr(
+        &self,
+        test_mode: bool,
+        num_csr: i32,
+        eek: &[u8],
+        challenge: &[u8],
+        sec_level: SecurityLevel,
+        protected_data: &mut ProtectedData,
+        device_info: &mut DeviceInfo,
+    ) -> Result<Vec<u8>> {
+        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 = map_rem_prov_error(dev.generateCertificateRequest(
+            test_mode,
+            &keys_to_sign,
+            eek,
+            challenge,
+            device_info,
+            protected_data,
+        ))
+        .context("In generate_csr: Failed to generate csr")?;
+        let mut cose_mac_0 = Vec::<u8>::new();
+        // TODO(b/180392379): Replace this manual CBOR generation with the cbor-serde crate as well.
+        //                    This generates an array consisting of the mac and the public key Maps.
+        //                    Just generate the actual MacedPublicKeys structure when the crate is
+        //                    available.
+        cose_mac_0.push((0b100_00000 | (keys_to_sign.len() + 1)) as u8);
+        cose_mac_0.push(0b010_11000); //push mac
+        cose_mac_0.push(mac.len() as u8);
+        cose_mac_0.append(&mut mac);
+        for maced_public_key in keys_to_sign {
+            if maced_public_key.macedKey.len() > 83 + 8 {
+                cose_mac_0.extend_from_slice(&maced_public_key.macedKey[8..83 + 8]);
+            }
+        }
+        Ok(cose_mac_0)
+    }
+
+    /// Provisions a certificate chain for a key whose CSR was included in generate_csr. The
+    /// `public_key` is used to index into the SQL database in order to insert the `certs` blob
+    /// which represents a PEM encoded X.509 certificate chain. The `expiration_date` is provided
+    /// as a convenience from the caller to avoid having to parse the certificates semantically
+    /// here.
+    pub fn provision_cert_chain(
+        &self,
+        public_key: &[u8],
+        batch_cert: &[u8],
+        certs: &[u8],
+        expiration_date: i64,
+        sec_level: SecurityLevel,
+    ) -> Result<()> {
+        DB.with::<_, Result<()>>(|db| {
+            let mut db = db.borrow_mut();
+            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,
+            )?)
+        })
+    }
+
+    /// Submits a request to the Remote Provisioner HAL to generate a signing key pair.
+    /// `is_test_mode` indicates whether or not the returned public key should be marked as being
+    /// for testing in order to differentiate them from private keys. If the call is successful,
+    /// the key pair is then added to the database.
+    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())
+    }
+
+    /// Deletes all attestation keys generated by the IRemotelyProvisionedComponent from the device,
+    /// regardless of what state of the attestation key lifecycle they were in.
+    pub fn delete_all_keys(&self) -> Result<i64> {
+        DB.with::<_, Result<i64>>(|db| {
+            let mut db = db.borrow_mut();
+            Ok(db.delete_all_attestation_keys()?)
+        })
+    }
+}
+
+impl binder::Interface for RemoteProvisioningService {}
+
+// Implementation of IRemoteProvisioning. See AIDL spec at
+// :aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
+impl IRemoteProvisioning for RemoteProvisioningService {
+    fn getPoolStatus(
+        &self,
+        expired_by: i64,
+        sec_level: SecurityLevel,
+    ) -> binder::public_api::Result<AttestationPoolStatus> {
+        let _wp = wd::watch_millis("IRemoteProvisioning::getPoolStatus", 500);
+        map_or_log_err(self.get_pool_status(expired_by, sec_level), Ok)
+    }
+
+    fn generateCsr(
+        &self,
+        test_mode: bool,
+        num_csr: i32,
+        eek: &[u8],
+        challenge: &[u8],
+        sec_level: SecurityLevel,
+        protected_data: &mut ProtectedData,
+        device_info: &mut DeviceInfo,
+    ) -> binder::public_api::Result<Vec<u8>> {
+        let _wp = wd::watch_millis("IRemoteProvisioning::generateCsr", 500);
+        map_or_log_err(
+            self.generate_csr(
+                test_mode,
+                num_csr,
+                eek,
+                challenge,
+                sec_level,
+                protected_data,
+                device_info,
+            ),
+            Ok,
+        )
+    }
+
+    fn provisionCertChain(
+        &self,
+        public_key: &[u8],
+        batch_cert: &[u8],
+        certs: &[u8],
+        expiration_date: i64,
+        sec_level: SecurityLevel,
+    ) -> binder::public_api::Result<()> {
+        let _wp = wd::watch_millis("IRemoteProvisioning::provisionCertChain", 500);
+        map_or_log_err(
+            self.provision_cert_chain(public_key, batch_cert, certs, expiration_date, sec_level),
+            Ok,
+        )
+    }
+
+    fn generateKeyPair(
+        &self,
+        is_test_mode: bool,
+        sec_level: SecurityLevel,
+    ) -> binder::public_api::Result<()> {
+        let _wp = wd::watch_millis("IRemoteProvisioning::generateKeyPair", 500);
+        map_or_log_err(self.generate_key_pair(is_test_mode, sec_level), Ok)
+    }
+
+    fn getSecurityLevels(&self) -> binder::public_api::Result<Vec<SecurityLevel>> {
+        let _wp = wd::watch_millis("IRemoteProvisioning::getSecurityLevels", 500);
+        map_or_log_err(self.get_security_levels(), Ok)
+    }
+
+    fn deleteAllKeys(&self) -> binder::public_api::Result<i64> {
+        let _wp = wd::watch_millis("IRemoteProvisioning::deleteAllKeys", 500);
+        map_or_log_err(self.delete_all_keys(), Ok)
+    }
+}
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
new file mode 100644
index 0000000..d10aba0
--- /dev/null
+++ b/keystore2/src/security_level.rs
@@ -0,0 +1,1015 @@
+// 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.
+
+//! This crate implements the IKeystoreSecurityLevel interface.
+
+use crate::attestation_key_utils::{get_attest_key_info, AttestationKeyInfo};
+use crate::audit_log::{log_key_deleted, log_key_generated, log_key_imported};
+use crate::database::{CertificateInfo, KeyIdGuard};
+use crate::error::{self, map_km_error, map_or_log_err, Error, ErrorCode};
+use crate::globals::{DB, ENFORCEMENTS, LEGACY_MIGRATOR, SUPER_KEY};
+use crate::key_parameter::KeyParameter as KsKeyParam;
+use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
+use crate::metrics::log_key_creation_event_stats;
+use crate::remote_provisioning::RemProvState;
+use crate::super_key::{KeyBlob, SuperKeyManager};
+use crate::utils::{
+    check_device_attestation_permissions, check_key_permission, is_device_id_attestation_tag,
+    key_characteristics_to_internal, uid_to_android_user, watchdog as wd, Asp,
+};
+use crate::{
+    database::{
+        BlobMetaData, BlobMetaEntry, DateTime, KeyEntry, KeyEntryLoadBits, KeyMetaData,
+        KeyMetaEntry, KeyType, SubComponentType, Uuid,
+    },
+    operation::KeystoreOperation,
+    operation::LoggingInfo,
+    operation::OperationDb,
+    permission::KeyPerm,
+};
+use crate::{globals::get_keymint_device, id_rotation::IdRotationState};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, AttestationKey::AttestationKey,
+    HardwareAuthenticatorType::HardwareAuthenticatorType, IKeyMintDevice::IKeyMintDevice,
+    KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat,
+    KeyMintHardwareInfo::KeyMintHardwareInfo, KeyParameter::KeyParameter,
+    KeyParameterValue::KeyParameterValue, SecurityLevel::SecurityLevel, Tag::Tag,
+};
+use android_hardware_security_keymint::binder::{BinderFeatures, Strong, ThreadState};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    AuthenticatorSpec::AuthenticatorSpec, CreateOperationResponse::CreateOperationResponse,
+    Domain::Domain, EphemeralStorageKeyResponse::EphemeralStorageKeyResponse,
+    IKeystoreOperation::IKeystoreOperation, IKeystoreSecurityLevel::BnKeystoreSecurityLevel,
+    IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+    KeyMetadata::KeyMetadata, KeyParameters::KeyParameters,
+};
+use anyhow::{anyhow, Context, Result};
+
+/// Implementation of the IKeystoreSecurityLevel Interface.
+pub struct KeystoreSecurityLevel {
+    security_level: SecurityLevel,
+    keymint: Asp,
+    hw_info: KeyMintHardwareInfo,
+    km_uuid: Uuid,
+    operation_db: OperationDb,
+    rem_prov_state: RemProvState,
+    id_rotation_state: IdRotationState,
+}
+
+// Blob of 32 zeroes used as empty masking key.
+static ZERO_BLOB_32: &[u8] = &[0; 32];
+
+// Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to GeneralizedTime
+// 999912312359559, which is 253402300799000 ms from Jan 1, 1970.
+const UNDEFINED_NOT_AFTER: i64 = 253402300799000i64;
+
+impl KeystoreSecurityLevel {
+    /// Creates a new security level instance wrapped in a
+    /// BnKeystoreSecurityLevel proxy object. It also enables
+    /// `BinderFeatures::set_requesting_sid` on the new interface, because
+    /// we need it for checking keystore permissions.
+    pub fn new_native_binder(
+        security_level: SecurityLevel,
+        id_rotation_state: IdRotationState,
+    ) -> Result<(Strong<dyn IKeystoreSecurityLevel>, Uuid)> {
+        let (dev, hw_info, km_uuid) = get_keymint_device(&security_level)
+            .context("In KeystoreSecurityLevel::new_native_binder.")?;
+        let result = BnKeystoreSecurityLevel::new_binder(
+            Self {
+                security_level,
+                keymint: dev,
+                hw_info,
+                km_uuid,
+                operation_db: OperationDb::new(),
+                rem_prov_state: RemProvState::new(security_level, km_uuid),
+                id_rotation_state,
+            },
+            BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+        );
+        Ok((result, km_uuid))
+    }
+
+    fn watch_millis(&self, id: &'static str, millis: u64) -> Option<wd::WatchPoint> {
+        let sec_level = self.security_level;
+        wd::watch_millis_with(id, millis, move || format!("SecurityLevel {:?}", sec_level))
+    }
+
+    fn store_new_key(
+        &self,
+        key: KeyDescriptor,
+        creation_result: KeyCreationResult,
+        user_id: u32,
+        flags: Option<i32>,
+    ) -> Result<KeyMetadata> {
+        let KeyCreationResult {
+            keyBlob: key_blob,
+            keyCharacteristics: key_characteristics,
+            certificateChain: mut certificate_chain,
+        } = creation_result;
+
+        let mut cert_info: CertificateInfo = CertificateInfo::new(
+            match certificate_chain.len() {
+                0 => None,
+                _ => Some(certificate_chain.remove(0).encodedCertificate),
+            },
+            match certificate_chain.len() {
+                0 => None,
+                _ => Some(
+                    certificate_chain
+                        .iter()
+                        .map(|c| c.encodedCertificate.iter())
+                        .flatten()
+                        .copied()
+                        .collect(),
+                ),
+            },
+        );
+
+        let mut key_parameters = key_characteristics_to_internal(key_characteristics);
+
+        key_parameters.push(KsKeyParam::new(
+            KsKeyParamValue::UserID(user_id as i32),
+            SecurityLevel::SOFTWARE,
+        ));
+
+        let creation_date = DateTime::now().context("Trying to make creation time.")?;
+
+        let key = match key.domain {
+            Domain::BLOB => KeyDescriptor {
+                domain: Domain::BLOB,
+                blob: Some(key_blob.to_vec()),
+                ..Default::default()
+            },
+            _ => DB
+                .with::<_, Result<KeyDescriptor>>(|db| {
+                    let mut db = db.borrow_mut();
+
+                    let (key_blob, mut blob_metadata) = SUPER_KEY
+                        .handle_super_encryption_on_key_init(
+                            &mut db,
+                            &LEGACY_MIGRATOR,
+                            &(key.domain),
+                            &key_parameters,
+                            flags,
+                            user_id,
+                            &key_blob,
+                        )
+                        .context("In store_new_key. Failed to handle super encryption.")?;
+
+                    let mut key_metadata = KeyMetaData::new();
+                    key_metadata.add(KeyMetaEntry::CreationDate(creation_date));
+                    blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
+
+                    let key_id = db
+                        .store_new_key(
+                            &key,
+                            &key_parameters,
+                            &(&key_blob, &blob_metadata),
+                            &cert_info,
+                            &key_metadata,
+                            &self.km_uuid,
+                        )
+                        .context("In store_new_key.")?;
+                    Ok(KeyDescriptor {
+                        domain: Domain::KEY_ID,
+                        nspace: key_id.id(),
+                        ..Default::default()
+                    })
+                })
+                .context("In store_new_key.")?,
+        };
+
+        Ok(KeyMetadata {
+            key,
+            keySecurityLevel: self.security_level,
+            certificate: cert_info.take_cert(),
+            certificateChain: cert_info.take_cert_chain(),
+            authorizations: crate::utils::key_parameters_to_authorizations(key_parameters),
+            modificationTimeMs: creation_date.to_millis_epoch(),
+        })
+    }
+
+    fn create_operation(
+        &self,
+        key: &KeyDescriptor,
+        operation_parameters: &[KeyParameter],
+        forced: bool,
+    ) -> Result<CreateOperationResponse> {
+        let caller_uid = ThreadState::get_calling_uid();
+        // We use `scoping_blob` to extend the life cycle of the blob loaded from the database,
+        // so that we can use it by reference like the blob provided by the key descriptor.
+        // Otherwise, we would have to clone the blob from the key descriptor.
+        let scoping_blob: Vec<u8>;
+        let (km_blob, key_properties, key_id_guard, blob_metadata) = match key.domain {
+            Domain::BLOB => {
+                check_key_permission(KeyPerm::use_(), key, &None)
+                    .context("In create_operation: checking use permission for Domain::BLOB.")?;
+                if forced {
+                    check_key_permission(KeyPerm::req_forced_op(), key, &None).context(
+                        "In create_operation: checking forced permission for Domain::BLOB.",
+                    )?;
+                }
+                (
+                    match &key.blob {
+                        Some(blob) => blob,
+                        None => {
+                            return Err(Error::sys()).context(concat!(
+                                "In create_operation: Key blob must be specified when",
+                                " using Domain::BLOB."
+                            ))
+                        }
+                    },
+                    None,
+                    None,
+                    BlobMetaData::new(),
+                )
+            }
+            _ => {
+                let (key_id_guard, mut key_entry) = DB
+                    .with::<_, Result<(KeyIdGuard, KeyEntry)>>(|db| {
+                        LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
+                            db.borrow_mut().load_key_entry(
+                                &key,
+                                KeyType::Client,
+                                KeyEntryLoadBits::KM,
+                                caller_uid,
+                                |k, av| {
+                                    check_key_permission(KeyPerm::use_(), k, &av)?;
+                                    if forced {
+                                        check_key_permission(KeyPerm::req_forced_op(), k, &av)?;
+                                    }
+                                    Ok(())
+                                },
+                            )
+                        })
+                    })
+                    .context("In create_operation: Failed to load key blob.")?;
+
+                let (blob, blob_metadata) =
+                    key_entry.take_key_blob_info().ok_or_else(Error::sys).context(concat!(
+                        "In create_operation: Successfully loaded key entry, ",
+                        "but KM blob was missing."
+                    ))?;
+                scoping_blob = blob;
+
+                (
+                    &scoping_blob,
+                    Some((key_id_guard.id(), key_entry.into_key_parameters())),
+                    Some(key_id_guard),
+                    blob_metadata,
+                )
+            }
+        };
+
+        let purpose = operation_parameters.iter().find(|p| p.tag == Tag::PURPOSE).map_or(
+            Err(Error::Km(ErrorCode::INVALID_ARGUMENT))
+                .context("In create_operation: No operation purpose specified."),
+            |kp| match kp.value {
+                KeyParameterValue::KeyPurpose(p) => Ok(p),
+                _ => Err(Error::Km(ErrorCode::INVALID_ARGUMENT))
+                    .context("In create_operation: Malformed KeyParameter."),
+            },
+        )?;
+
+        // Remove Tag::PURPOSE from the operation_parameters, since some keymaster devices return
+        // an error on begin() if Tag::PURPOSE is in the operation_parameters.
+        let op_params: Vec<KeyParameter> =
+            operation_parameters.iter().filter(|p| p.tag != Tag::PURPOSE).cloned().collect();
+        let operation_parameters = op_params.as_slice();
+
+        let (immediate_hat, mut auth_info) = ENFORCEMENTS
+            .authorize_create(
+                purpose,
+                key_properties.as_ref(),
+                operation_parameters.as_ref(),
+                self.hw_info.timestampTokenRequired,
+            )
+            .context("In create_operation.")?;
+
+        let km_blob = SUPER_KEY
+            .unwrap_key_if_required(&blob_metadata, km_blob)
+            .context("In create_operation. Failed to handle super encryption.")?;
+
+        let km_dev: Strong<dyn IKeyMintDevice> = self
+            .keymint
+            .get_interface()
+            .context("In create_operation: Failed to get KeyMint device")?;
+
+        let (begin_result, upgraded_blob) = self
+            .upgrade_keyblob_if_required_with(
+                &*km_dev,
+                key_id_guard,
+                &km_blob,
+                &blob_metadata,
+                &operation_parameters,
+                |blob| loop {
+                    match map_km_error({
+                        let _wp = self.watch_millis(
+                            "In KeystoreSecurityLevel::create_operation: calling begin",
+                            500,
+                        );
+                        km_dev.begin(purpose, blob, &operation_parameters, immediate_hat.as_ref())
+                    }) {
+                        Err(Error::Km(ErrorCode::TOO_MANY_OPERATIONS)) => {
+                            self.operation_db.prune(caller_uid, forced)?;
+                            continue;
+                        }
+                        v => return v,
+                    }
+                },
+            )
+            .context("In create_operation: Failed to begin operation.")?;
+
+        let operation_challenge = auth_info.finalize_create_authorization(begin_result.challenge);
+
+        let op_params: Vec<KeyParameter> = operation_parameters.to_vec();
+
+        let operation = match begin_result.operation {
+            Some(km_op) => self.operation_db.create_operation(
+                km_op,
+                caller_uid,
+                auth_info,
+                forced,
+                LoggingInfo::new(self.security_level, purpose, op_params, upgraded_blob.is_some()),
+            ),
+            None => {
+                return Err(Error::sys()).context(concat!(
+                    "In create_operation: Begin operation returned successfully, ",
+                    "but did not return a valid operation."
+                ))
+            }
+        };
+
+        let op_binder: binder::public_api::Strong<dyn IKeystoreOperation> =
+            KeystoreOperation::new_native_binder(operation)
+                .as_binder()
+                .into_interface()
+                .context("In create_operation: Failed to create IKeystoreOperation.")?;
+
+        Ok(CreateOperationResponse {
+            iOperation: Some(op_binder),
+            operationChallenge: operation_challenge,
+            parameters: match begin_result.params.len() {
+                0 => None,
+                _ => Some(KeyParameters { keyParameter: begin_result.params }),
+            },
+            // An upgraded blob should only be returned if the caller has permission
+            // to use Domain::BLOB keys. If we got to this point, we already checked
+            // that the caller had that permission.
+            upgradedBlob: if key.domain == Domain::BLOB { upgraded_blob } else { None },
+        })
+    }
+
+    fn add_certificate_parameters(
+        &self,
+        uid: u32,
+        params: &[KeyParameter],
+        key: &KeyDescriptor,
+    ) -> Result<Vec<KeyParameter>> {
+        let mut result = params.to_vec();
+        // If there is an attestation challenge we need to get an application id.
+        if params.iter().any(|kp| kp.tag == Tag::ATTESTATION_CHALLENGE) {
+            let aaid = {
+                let _wp = self.watch_millis(
+                    "In KeystoreSecurityLevel::add_certificate_parameters calling: get_aaid",
+                    500,
+                );
+                keystore2_aaid::get_aaid(uid).map_err(|e| {
+                    anyhow!(format!(
+                        "In add_certificate_parameters: get_aaid returned status {}.",
+                        e
+                    ))
+                })
+            }?;
+
+            result.push(KeyParameter {
+                tag: Tag::ATTESTATION_APPLICATION_ID,
+                value: KeyParameterValue::Blob(aaid),
+            });
+        }
+
+        if params.iter().any(|kp| kp.tag == Tag::INCLUDE_UNIQUE_ID) {
+            check_key_permission(KeyPerm::gen_unique_id(), key, &None).context(concat!(
+                "In add_certificate_parameters: ",
+                "Caller does not have the permission to generate a unique ID"
+            ))?;
+            if self.id_rotation_state.had_factory_reset_since_id_rotation().context(
+                "In add_certificate_parameters: Call to had_factory_reset_since_id_rotation failed."
+            )? {
+                result.push(KeyParameter{
+                    tag: Tag::RESET_SINCE_ID_ROTATION,
+                    value: KeyParameterValue::BoolValue(true),
+                })
+            }
+        }
+
+        // If the caller requests any device identifier attestation tag, check that they hold the
+        // correct Android permission.
+        if params.iter().any(|kp| is_device_id_attestation_tag(kp.tag)) {
+            check_device_attestation_permissions().context(concat!(
+                "In add_certificate_parameters: ",
+                "Caller does not have the permission to attest device identifiers."
+            ))?;
+        }
+
+        // If we are generating/importing an asymmetric key, we need to make sure
+        // that NOT_BEFORE and NOT_AFTER are present.
+        match params.iter().find(|kp| kp.tag == Tag::ALGORITHM) {
+            Some(KeyParameter { tag: _, value: KeyParameterValue::Algorithm(Algorithm::RSA) })
+            | Some(KeyParameter { tag: _, value: KeyParameterValue::Algorithm(Algorithm::EC) }) => {
+                if !params.iter().any(|kp| kp.tag == Tag::CERTIFICATE_NOT_BEFORE) {
+                    result.push(KeyParameter {
+                        tag: Tag::CERTIFICATE_NOT_BEFORE,
+                        value: KeyParameterValue::DateTime(0),
+                    })
+                }
+                if !params.iter().any(|kp| kp.tag == Tag::CERTIFICATE_NOT_AFTER) {
+                    result.push(KeyParameter {
+                        tag: Tag::CERTIFICATE_NOT_AFTER,
+                        value: KeyParameterValue::DateTime(UNDEFINED_NOT_AFTER),
+                    })
+                }
+            }
+            _ => {}
+        }
+        Ok(result)
+    }
+
+    fn generate_key(
+        &self,
+        key: &KeyDescriptor,
+        attest_key_descriptor: Option<&KeyDescriptor>,
+        params: &[KeyParameter],
+        flags: i32,
+        _entropy: &[u8],
+    ) -> Result<KeyMetadata> {
+        if key.domain != Domain::BLOB && key.alias.is_none() {
+            return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+                .context("In generate_key: Alias must be specified");
+        }
+        let caller_uid = ThreadState::get_calling_uid();
+
+        let key = match key.domain {
+            Domain::APP => KeyDescriptor {
+                domain: key.domain,
+                nspace: caller_uid as i64,
+                alias: key.alias.clone(),
+                blob: None,
+            },
+            _ => key.clone(),
+        };
+
+        // generate_key requires the rebind permission.
+        // Must return on error for security reasons.
+        check_key_permission(KeyPerm::rebind(), &key, &None).context("In generate_key.")?;
+
+        let attestation_key_info = match (key.domain, attest_key_descriptor) {
+            (Domain::BLOB, _) => None,
+            _ => DB
+                .with(|db| {
+                    get_attest_key_info(
+                        &key,
+                        caller_uid,
+                        attest_key_descriptor,
+                        params,
+                        &self.rem_prov_state,
+                        &mut db.borrow_mut(),
+                    )
+                })
+                .context("In generate_key: Trying to get an attestation key")?,
+        };
+        let params = self
+            .add_certificate_parameters(caller_uid, params, &key)
+            .context("In generate_key: Trying to get aaid.")?;
+
+        let km_dev: Strong<dyn IKeyMintDevice> = self.keymint.get_interface()?;
+
+        let creation_result = match attestation_key_info {
+            Some(AttestationKeyInfo::UserGenerated {
+                key_id_guard,
+                blob,
+                blob_metadata,
+                issuer_subject,
+            }) => self
+                .upgrade_keyblob_if_required_with(
+                    &*km_dev,
+                    Some(key_id_guard),
+                    &KeyBlob::Ref(&blob),
+                    &blob_metadata,
+                    &params,
+                    |blob| {
+                        let attest_key = Some(AttestationKey {
+                            keyBlob: blob.to_vec(),
+                            attestKeyParams: vec![],
+                            issuerSubjectName: issuer_subject.clone(),
+                        });
+                        map_km_error({
+                            let _wp = self.watch_millis(
+                                concat!(
+                                    "In KeystoreSecurityLevel::generate_key (UserGenerated): ",
+                                    "calling generate_key."
+                                ),
+                                5000, // Generate can take a little longer.
+                            );
+                            km_dev.generateKey(&params, attest_key.as_ref())
+                        })
+                    },
+                )
+                .context("In generate_key: Using user generated attestation key.")
+                .map(|(result, _)| result),
+            Some(AttestationKeyInfo::RemoteProvisioned { attestation_key, attestation_certs }) => {
+                map_km_error({
+                    let _wp = self.watch_millis(
+                        concat!(
+                            "In KeystoreSecurityLevel::generate_key (RemoteProvisioned): ",
+                            "calling generate_key.",
+                        ),
+                        5000, // Generate can take a little longer.
+                    );
+                    km_dev.generateKey(&params, Some(&attestation_key))
+                })
+                .context("While generating Key with remote provisioned attestation key.")
+                .map(|mut creation_result| {
+                    creation_result.certificateChain.push(attestation_certs);
+                    creation_result
+                })
+            }
+            None => map_km_error({
+                let _wp = self.watch_millis(
+                    concat!(
+                        "In KeystoreSecurityLevel::generate_key (No attestation): ",
+                        "calling generate_key.",
+                    ),
+                    5000, // Generate can take a little longer.
+                );
+                km_dev.generateKey(&params, None)
+            })
+            .context("While generating Key without explicit attestation key."),
+        }
+        .context("In generate_key.")?;
+
+        let user_id = uid_to_android_user(caller_uid);
+        self.store_new_key(key, creation_result, user_id, Some(flags)).context("In generate_key.")
+    }
+
+    fn import_key(
+        &self,
+        key: &KeyDescriptor,
+        _attestation_key: Option<&KeyDescriptor>,
+        params: &[KeyParameter],
+        flags: i32,
+        key_data: &[u8],
+    ) -> Result<KeyMetadata> {
+        if key.domain != Domain::BLOB && key.alias.is_none() {
+            return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+                .context("In import_key: Alias must be specified");
+        }
+        let caller_uid = ThreadState::get_calling_uid();
+
+        let key = match key.domain {
+            Domain::APP => KeyDescriptor {
+                domain: key.domain,
+                nspace: caller_uid as i64,
+                alias: key.alias.clone(),
+                blob: None,
+            },
+            _ => key.clone(),
+        };
+
+        // import_key requires the rebind permission.
+        check_key_permission(KeyPerm::rebind(), &key, &None).context("In import_key.")?;
+
+        let params = self
+            .add_certificate_parameters(caller_uid, params, &key)
+            .context("In import_key: Trying to get aaid.")?;
+
+        let format = params
+            .iter()
+            .find(|p| p.tag == Tag::ALGORITHM)
+            .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+            .context("No KeyParameter 'Algorithm'.")
+            .and_then(|p| match &p.value {
+                KeyParameterValue::Algorithm(Algorithm::AES)
+                | KeyParameterValue::Algorithm(Algorithm::HMAC)
+                | KeyParameterValue::Algorithm(Algorithm::TRIPLE_DES) => Ok(KeyFormat::RAW),
+                KeyParameterValue::Algorithm(Algorithm::RSA)
+                | KeyParameterValue::Algorithm(Algorithm::EC) => Ok(KeyFormat::PKCS8),
+                v => Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+                    .context(format!("Unknown Algorithm {:?}.", v)),
+            })
+            .context("In import_key.")?;
+
+        let km_dev: Strong<dyn IKeyMintDevice> =
+            self.keymint.get_interface().context("In import_key: Trying to get the KM device")?;
+        let creation_result = map_km_error({
+            let _wp =
+                self.watch_millis("In KeystoreSecurityLevel::import_key: calling importKey.", 500);
+            km_dev.importKey(&params, format, key_data, None /* attestKey */)
+        })
+        .context("In import_key: Trying to call importKey")?;
+
+        let user_id = uid_to_android_user(caller_uid);
+        self.store_new_key(key, creation_result, user_id, Some(flags)).context("In import_key.")
+    }
+
+    fn import_wrapped_key(
+        &self,
+        key: &KeyDescriptor,
+        wrapping_key: &KeyDescriptor,
+        masking_key: Option<&[u8]>,
+        params: &[KeyParameter],
+        authenticators: &[AuthenticatorSpec],
+    ) -> Result<KeyMetadata> {
+        let wrapped_data: &[u8] = match key {
+            KeyDescriptor { domain: Domain::APP, blob: Some(ref blob), alias: Some(_), .. }
+            | KeyDescriptor {
+                domain: Domain::SELINUX, blob: Some(ref blob), alias: Some(_), ..
+            } => blob,
+            _ => {
+                return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context(format!(
+                    concat!(
+                        "In import_wrapped_key: Alias and blob must be specified ",
+                        "and domain must be APP or SELINUX. {:?}"
+                    ),
+                    key
+                ))
+            }
+        };
+
+        if wrapping_key.domain == Domain::BLOB {
+            return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context(
+                "In import_wrapped_key: Import wrapped key not supported for self managed blobs.",
+            );
+        }
+
+        let caller_uid = ThreadState::get_calling_uid();
+        let user_id = uid_to_android_user(caller_uid);
+
+        let key = match key.domain {
+            Domain::APP => KeyDescriptor {
+                domain: key.domain,
+                nspace: caller_uid as i64,
+                alias: key.alias.clone(),
+                blob: None,
+            },
+            Domain::SELINUX => KeyDescriptor {
+                domain: Domain::SELINUX,
+                nspace: key.nspace,
+                alias: key.alias.clone(),
+                blob: None,
+            },
+            _ => panic!("Unreachable."),
+        };
+
+        // Import_wrapped_key requires the rebind permission for the new key.
+        check_key_permission(KeyPerm::rebind(), &key, &None).context("In import_wrapped_key.")?;
+
+        let (wrapping_key_id_guard, mut wrapping_key_entry) = DB
+            .with(|db| {
+                LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
+                    db.borrow_mut().load_key_entry(
+                        &wrapping_key,
+                        KeyType::Client,
+                        KeyEntryLoadBits::KM,
+                        caller_uid,
+                        |k, av| check_key_permission(KeyPerm::use_(), k, &av),
+                    )
+                })
+            })
+            .context("Failed to load wrapping key.")?;
+
+        let (wrapping_key_blob, wrapping_blob_metadata) = wrapping_key_entry
+            .take_key_blob_info()
+            .ok_or_else(error::Error::sys)
+            .context("No km_blob after successfully loading key. This should never happen.")?;
+
+        let wrapping_key_blob =
+            SUPER_KEY.unwrap_key_if_required(&wrapping_blob_metadata, &wrapping_key_blob).context(
+                "In import_wrapped_key. Failed to handle super encryption for wrapping key.",
+            )?;
+
+        // km_dev.importWrappedKey does not return a certificate chain.
+        // TODO Do we assume that all wrapped keys are symmetric?
+        // let certificate_chain: Vec<KmCertificate> = Default::default();
+
+        let pw_sid = authenticators
+            .iter()
+            .find_map(|a| match a.authenticatorType {
+                HardwareAuthenticatorType::PASSWORD => Some(a.authenticatorId),
+                _ => None,
+            })
+            .unwrap_or(-1);
+
+        let fp_sid = authenticators
+            .iter()
+            .find_map(|a| match a.authenticatorType {
+                HardwareAuthenticatorType::FINGERPRINT => Some(a.authenticatorId),
+                _ => None,
+            })
+            .unwrap_or(-1);
+
+        let masking_key = masking_key.unwrap_or(ZERO_BLOB_32);
+
+        let km_dev: Strong<dyn IKeyMintDevice> = self.keymint.get_interface()?;
+        let (creation_result, _) = self
+            .upgrade_keyblob_if_required_with(
+                &*km_dev,
+                Some(wrapping_key_id_guard),
+                &wrapping_key_blob,
+                &wrapping_blob_metadata,
+                &[],
+                |wrapping_blob| {
+                    let _wp = self.watch_millis(
+                        "In KeystoreSecurityLevel::import_wrapped_key: calling importWrappedKey.",
+                        500,
+                    );
+                    let creation_result = map_km_error(km_dev.importWrappedKey(
+                        wrapped_data,
+                        wrapping_blob,
+                        masking_key,
+                        &params,
+                        pw_sid,
+                        fp_sid,
+                    ))?;
+                    Ok(creation_result)
+                },
+            )
+            .context("In import_wrapped_key.")?;
+
+        self.store_new_key(key, creation_result, user_id, None)
+            .context("In import_wrapped_key: Trying to store the new key.")
+    }
+
+    fn store_upgraded_keyblob(
+        key_id_guard: KeyIdGuard,
+        km_uuid: Option<&Uuid>,
+        key_blob: &KeyBlob,
+        upgraded_blob: &[u8],
+    ) -> Result<()> {
+        let (upgraded_blob_to_be_stored, new_blob_metadata) =
+            SuperKeyManager::reencrypt_if_required(key_blob, &upgraded_blob)
+                .context("In store_upgraded_keyblob: Failed to handle super encryption.")?;
+
+        let mut new_blob_metadata = new_blob_metadata.unwrap_or_default();
+        if let Some(uuid) = km_uuid {
+            new_blob_metadata.add(BlobMetaEntry::KmUuid(*uuid));
+        }
+
+        DB.with(|db| {
+            let mut db = db.borrow_mut();
+            db.set_blob(
+                &key_id_guard,
+                SubComponentType::KEY_BLOB,
+                Some(&upgraded_blob_to_be_stored),
+                Some(&new_blob_metadata),
+            )
+        })
+        .context("In store_upgraded_keyblob: Failed to insert upgraded blob into the database.")
+    }
+
+    fn upgrade_keyblob_if_required_with<T, F>(
+        &self,
+        km_dev: &dyn IKeyMintDevice,
+        key_id_guard: Option<KeyIdGuard>,
+        key_blob: &KeyBlob,
+        blob_metadata: &BlobMetaData,
+        params: &[KeyParameter],
+        f: F,
+    ) -> Result<(T, Option<Vec<u8>>)>
+    where
+        F: Fn(&[u8]) -> Result<T, Error>,
+    {
+        match f(key_blob) {
+            Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
+                let upgraded_blob = {
+                    let _wp = self.watch_millis(
+                        concat!(
+                            "In KeystoreSecurityLevel::upgrade_keyblob_if_required_with: ",
+                            "calling upgradeKey."
+                        ),
+                        500,
+                    );
+                    map_km_error(km_dev.upgradeKey(key_blob, params))
+                }
+                .context("In upgrade_keyblob_if_required_with: Upgrade failed.")?;
+
+                if let Some(kid) = key_id_guard {
+                    Self::store_upgraded_keyblob(
+                        kid,
+                        blob_metadata.km_uuid(),
+                        key_blob,
+                        &upgraded_blob,
+                    )
+                    .context(
+                        "In upgrade_keyblob_if_required_with: store_upgraded_keyblob failed",
+                    )?;
+                }
+
+                match f(&upgraded_blob) {
+                    Ok(v) => Ok((v, Some(upgraded_blob))),
+                    Err(e) => Err(e).context(concat!(
+                        "In upgrade_keyblob_if_required_with: ",
+                        "Failed to perform operation on second try."
+                    )),
+                }
+            }
+            result => {
+                if let Some(kid) = key_id_guard {
+                    if key_blob.force_reencrypt() {
+                        Self::store_upgraded_keyblob(
+                            kid,
+                            blob_metadata.km_uuid(),
+                            key_blob,
+                            key_blob,
+                        )
+                        .context(concat!(
+                            "In upgrade_keyblob_if_required_with: ",
+                            "store_upgraded_keyblob failed in forced reencrypt"
+                        ))?;
+                    }
+                }
+                result
+                    .map(|v| (v, None))
+                    .context("In upgrade_keyblob_if_required_with: Called closure failed.")
+            }
+        }
+    }
+
+    fn convert_storage_key_to_ephemeral(
+        &self,
+        storage_key: &KeyDescriptor,
+    ) -> Result<EphemeralStorageKeyResponse> {
+        if storage_key.domain != Domain::BLOB {
+            return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context(concat!(
+                "In IKeystoreSecurityLevel convert_storage_key_to_ephemeral: ",
+                "Key must be of Domain::BLOB"
+            ));
+        }
+        let key_blob = storage_key
+            .blob
+            .as_ref()
+            .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+            .context(
+                "In IKeystoreSecurityLevel convert_storage_key_to_ephemeral: No key blob specified",
+            )?;
+
+        // convert_storage_key_to_ephemeral requires the associated permission
+        check_key_permission(KeyPerm::convert_storage_key_to_ephemeral(), storage_key, &None)
+            .context("In convert_storage_key_to_ephemeral: Check permission")?;
+
+        let km_dev: Strong<dyn IKeyMintDevice> = self.keymint.get_interface().context(concat!(
+            "In IKeystoreSecurityLevel convert_storage_key_to_ephemeral: ",
+            "Getting keymint device interface"
+        ))?;
+        match {
+            let _wp = self.watch_millis(
+                concat!(
+                    "In IKeystoreSecurityLevel::convert_storage_key_to_ephemeral: ",
+                    "calling convertStorageKeyToEphemeral (1)"
+                ),
+                500,
+            );
+            map_km_error(km_dev.convertStorageKeyToEphemeral(key_blob))
+        } {
+            Ok(result) => {
+                Ok(EphemeralStorageKeyResponse { ephemeralKey: result, upgradedBlob: None })
+            }
+            Err(error::Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
+                let upgraded_blob = {
+                    let _wp = self.watch_millis(
+                        "In convert_storage_key_to_ephemeral: calling upgradeKey",
+                        500,
+                    );
+                    map_km_error(km_dev.upgradeKey(key_blob, &[]))
+                }
+                .context("In convert_storage_key_to_ephemeral: Failed to upgrade key blob.")?;
+                let ephemeral_key = {
+                    let _wp = self.watch_millis(
+                        "In convert_storage_key_to_ephemeral: calling convertStorageKeyToEphemeral (2)",
+                        500,
+                    );
+                    map_km_error(km_dev.convertStorageKeyToEphemeral(key_blob))
+                }
+                    .context(concat!(
+                        "In convert_storage_key_to_ephemeral: ",
+                        "Failed to retrieve ephemeral key (after upgrade)."
+                    ))?;
+                Ok(EphemeralStorageKeyResponse {
+                    ephemeralKey: ephemeral_key,
+                    upgradedBlob: Some(upgraded_blob),
+                })
+            }
+            Err(e) => Err(e)
+                .context("In convert_storage_key_to_ephemeral: Failed to retrieve ephemeral key."),
+        }
+    }
+
+    fn delete_key(&self, key: &KeyDescriptor) -> Result<()> {
+        if key.domain != Domain::BLOB {
+            return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+                .context("In IKeystoreSecurityLevel delete_key: Key must be of Domain::BLOB");
+        }
+
+        let key_blob = key
+            .blob
+            .as_ref()
+            .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+            .context("In IKeystoreSecurityLevel delete_key: No key blob specified")?;
+
+        check_key_permission(KeyPerm::delete(), key, &None)
+            .context("In IKeystoreSecurityLevel delete_key: Checking delete permissions")?;
+
+        let km_dev: Strong<dyn IKeyMintDevice> = self
+            .keymint
+            .get_interface()
+            .context("In IKeystoreSecurityLevel delete_key: Getting keymint device interface")?;
+        {
+            let _wp =
+                self.watch_millis("In KeystoreSecuritylevel::delete_key: calling deleteKey", 500);
+            map_km_error(km_dev.deleteKey(&key_blob)).context("In keymint device deleteKey")
+        }
+    }
+}
+
+impl binder::Interface for KeystoreSecurityLevel {}
+
+impl IKeystoreSecurityLevel for KeystoreSecurityLevel {
+    fn createOperation(
+        &self,
+        key: &KeyDescriptor,
+        operation_parameters: &[KeyParameter],
+        forced: bool,
+    ) -> binder::public_api::Result<CreateOperationResponse> {
+        let _wp = self.watch_millis("IKeystoreSecurityLevel::createOperation", 500);
+        map_or_log_err(self.create_operation(key, operation_parameters, forced), Ok)
+    }
+    fn generateKey(
+        &self,
+        key: &KeyDescriptor,
+        attestation_key: Option<&KeyDescriptor>,
+        params: &[KeyParameter],
+        flags: i32,
+        entropy: &[u8],
+    ) -> binder::public_api::Result<KeyMetadata> {
+        // Duration is set to 5 seconds, because generateKey - especially for RSA keys, takes more
+        // time than other operations
+        let _wp = self.watch_millis("IKeystoreSecurityLevel::generateKey", 5000);
+        let result = self.generate_key(key, attestation_key, params, flags, entropy);
+        log_key_creation_event_stats(self.security_level, params, &result);
+        log_key_generated(key, ThreadState::get_calling_uid(), result.is_ok());
+        map_or_log_err(result, Ok)
+    }
+    fn importKey(
+        &self,
+        key: &KeyDescriptor,
+        attestation_key: Option<&KeyDescriptor>,
+        params: &[KeyParameter],
+        flags: i32,
+        key_data: &[u8],
+    ) -> binder::public_api::Result<KeyMetadata> {
+        let _wp = self.watch_millis("IKeystoreSecurityLevel::importKey", 500);
+        let result = self.import_key(key, attestation_key, params, flags, key_data);
+        log_key_creation_event_stats(self.security_level, params, &result);
+        log_key_imported(key, ThreadState::get_calling_uid(), result.is_ok());
+        map_or_log_err(result, Ok)
+    }
+    fn importWrappedKey(
+        &self,
+        key: &KeyDescriptor,
+        wrapping_key: &KeyDescriptor,
+        masking_key: Option<&[u8]>,
+        params: &[KeyParameter],
+        authenticators: &[AuthenticatorSpec],
+    ) -> binder::public_api::Result<KeyMetadata> {
+        let _wp = self.watch_millis("IKeystoreSecurityLevel::importWrappedKey", 500);
+        let result =
+            self.import_wrapped_key(key, wrapping_key, masking_key, params, authenticators);
+        log_key_creation_event_stats(self.security_level, params, &result);
+        log_key_imported(key, ThreadState::get_calling_uid(), result.is_ok());
+        map_or_log_err(result, Ok)
+    }
+    fn convertStorageKeyToEphemeral(
+        &self,
+        storage_key: &KeyDescriptor,
+    ) -> binder::public_api::Result<EphemeralStorageKeyResponse> {
+        let _wp = self.watch_millis("IKeystoreSecurityLevel::convertStorageKeyToEphemeral", 500);
+        map_or_log_err(self.convert_storage_key_to_ephemeral(storage_key), Ok)
+    }
+    fn deleteKey(&self, key: &KeyDescriptor) -> binder::public_api::Result<()> {
+        let _wp = self.watch_millis("IKeystoreSecurityLevel::deleteKey", 500);
+        let result = self.delete_key(key);
+        log_key_deleted(key, ThreadState::get_calling_uid(), result.is_ok());
+        map_or_log_err(result, Ok)
+    }
+}
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
new file mode 100644
index 0000000..3ce0550
--- /dev/null
+++ b/keystore2/src/service.rs
@@ -0,0 +1,402 @@
+// 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.
+
+//! This crate implement the core Keystore 2.0 service API as defined by the Keystore 2.0
+//! AIDL spec.
+
+use std::collections::HashMap;
+
+use crate::audit_log::log_key_deleted;
+use crate::permission::{KeyPerm, KeystorePerm};
+use crate::security_level::KeystoreSecurityLevel;
+use crate::utils::{
+    check_grant_permission, check_key_permission, check_keystore_permission,
+    key_parameters_to_authorizations, watchdog as wd, Asp,
+};
+use crate::{
+    database::Uuid,
+    globals::{create_thread_local_db, DB, LEGACY_BLOB_LOADER, LEGACY_MIGRATOR},
+};
+use crate::{database::KEYSTORE_UUID, permission};
+use crate::{
+    database::{KeyEntryLoadBits, KeyType, SubComponentType},
+    error::ResponseCode,
+};
+use crate::{
+    error::{self, map_or_log_err, ErrorCode},
+    id_rotation::IdRotationState,
+};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use android_hardware_security_keymint::binder::{BinderFeatures, Strong, ThreadState};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
+    IKeystoreService::BnKeystoreService, IKeystoreService::IKeystoreService,
+    KeyDescriptor::KeyDescriptor, KeyEntryResponse::KeyEntryResponse, KeyMetadata::KeyMetadata,
+};
+use anyhow::{Context, Result};
+use error::Error;
+use keystore2_selinux as selinux;
+
+/// Implementation of the IKeystoreService.
+#[derive(Default)]
+pub struct KeystoreService {
+    i_sec_level_by_uuid: HashMap<Uuid, Asp>,
+    uuid_by_sec_level: HashMap<SecurityLevel, Uuid>,
+}
+
+impl KeystoreService {
+    /// Create a new instance of the Keystore 2.0 service.
+    pub fn new_native_binder(
+        id_rotation_state: IdRotationState,
+    ) -> Result<Strong<dyn IKeystoreService>> {
+        let mut result: Self = Default::default();
+        let (dev, uuid) = KeystoreSecurityLevel::new_native_binder(
+            SecurityLevel::TRUSTED_ENVIRONMENT,
+            id_rotation_state.clone(),
+        )
+        .context(concat!(
+            "In KeystoreService::new_native_binder: ",
+            "Trying to construct mandatory security level TEE."
+        ))
+        .map(|(dev, uuid)| (Asp::new(dev.as_binder()), uuid))?;
+        result.i_sec_level_by_uuid.insert(uuid, dev);
+        result.uuid_by_sec_level.insert(SecurityLevel::TRUSTED_ENVIRONMENT, uuid);
+
+        // Strongbox is optional, so we ignore errors and turn the result into an Option.
+        if let Ok((dev, uuid)) =
+            KeystoreSecurityLevel::new_native_binder(SecurityLevel::STRONGBOX, id_rotation_state)
+                .map(|(dev, uuid)| (Asp::new(dev.as_binder()), uuid))
+        {
+            result.i_sec_level_by_uuid.insert(uuid, dev);
+            result.uuid_by_sec_level.insert(SecurityLevel::STRONGBOX, uuid);
+        }
+
+        let uuid_by_sec_level = result.uuid_by_sec_level.clone();
+        LEGACY_MIGRATOR
+            .set_init(move || {
+                (create_thread_local_db(), uuid_by_sec_level, LEGACY_BLOB_LOADER.clone())
+            })
+            .context(
+                "In KeystoreService::new_native_binder: Trying to initialize the legacy migrator.",
+            )?;
+
+        Ok(BnKeystoreService::new_binder(
+            result,
+            BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+        ))
+    }
+
+    fn uuid_to_sec_level(&self, uuid: &Uuid) -> SecurityLevel {
+        self.uuid_by_sec_level
+            .iter()
+            .find(|(_, v)| **v == *uuid)
+            .map(|(s, _)| *s)
+            .unwrap_or(SecurityLevel::SOFTWARE)
+    }
+
+    fn get_i_sec_level_by_uuid(&self, uuid: &Uuid) -> Result<Strong<dyn IKeystoreSecurityLevel>> {
+        if let Some(dev) = self.i_sec_level_by_uuid.get(uuid) {
+            dev.get_interface().context("In get_i_sec_level_by_uuid.")
+        } else {
+            Err(error::Error::sys())
+                .context("In get_i_sec_level_by_uuid: KeyMint instance for key not found.")
+        }
+    }
+
+    fn get_security_level(
+        &self,
+        sec_level: SecurityLevel,
+    ) -> Result<Strong<dyn IKeystoreSecurityLevel>> {
+        if let Some(dev) = self
+            .uuid_by_sec_level
+            .get(&sec_level)
+            .and_then(|uuid| self.i_sec_level_by_uuid.get(uuid))
+        {
+            dev.get_interface().context("In get_security_level.")
+        } else {
+            Err(error::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
+                .context("In get_security_level: No such security level.")
+        }
+    }
+
+    fn get_key_entry(&self, key: &KeyDescriptor) -> Result<KeyEntryResponse> {
+        let caller_uid = ThreadState::get_calling_uid();
+        let (key_id_guard, mut key_entry) = DB
+            .with(|db| {
+                LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
+                    db.borrow_mut().load_key_entry(
+                        &key,
+                        KeyType::Client,
+                        KeyEntryLoadBits::PUBLIC,
+                        caller_uid,
+                        |k, av| check_key_permission(KeyPerm::get_info(), k, &av),
+                    )
+                })
+            })
+            .context("In get_key_entry, while trying to load key info.")?;
+
+        let i_sec_level = if !key_entry.pure_cert() {
+            Some(
+                self.get_i_sec_level_by_uuid(key_entry.km_uuid())
+                    .context("In get_key_entry: Trying to get security level proxy.")?,
+            )
+        } else {
+            None
+        };
+
+        Ok(KeyEntryResponse {
+            iSecurityLevel: i_sec_level,
+            metadata: KeyMetadata {
+                key: KeyDescriptor {
+                    domain: Domain::KEY_ID,
+                    nspace: key_id_guard.id(),
+                    ..Default::default()
+                },
+                keySecurityLevel: self.uuid_to_sec_level(key_entry.km_uuid()),
+                certificate: key_entry.take_cert(),
+                certificateChain: key_entry.take_cert_chain(),
+                modificationTimeMs: key_entry
+                    .metadata()
+                    .creation_date()
+                    .map(|d| d.to_millis_epoch())
+                    .ok_or(Error::Rc(ResponseCode::VALUE_CORRUPTED))
+                    .context("In get_key_entry: Trying to get creation date.")?,
+                authorizations: key_parameters_to_authorizations(key_entry.into_key_parameters()),
+            },
+        })
+    }
+
+    fn update_subcomponent(
+        &self,
+        key: &KeyDescriptor,
+        public_cert: Option<&[u8]>,
+        certificate_chain: Option<&[u8]>,
+    ) -> Result<()> {
+        let caller_uid = ThreadState::get_calling_uid();
+        DB.with::<_, Result<()>>(|db| {
+            let entry = match LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
+                db.borrow_mut().load_key_entry(
+                    &key,
+                    KeyType::Client,
+                    KeyEntryLoadBits::NONE,
+                    caller_uid,
+                    |k, av| {
+                        check_key_permission(KeyPerm::update(), k, &av)
+                            .context("In update_subcomponent.")
+                    },
+                )
+            }) {
+                Err(e) => match e.root_cause().downcast_ref::<Error>() {
+                    Some(Error::Rc(ResponseCode::KEY_NOT_FOUND)) => Ok(None),
+                    _ => Err(e),
+                },
+                Ok(v) => Ok(Some(v)),
+            }
+            .context("Failed to load key entry.")?;
+
+            let mut db = db.borrow_mut();
+            if let Some((key_id_guard, _key_entry)) = entry {
+                db.set_blob(&key_id_guard, SubComponentType::CERT, public_cert, None)
+                    .context("Failed to update cert subcomponent.")?;
+
+                db.set_blob(&key_id_guard, SubComponentType::CERT_CHAIN, certificate_chain, None)
+                    .context("Failed to update cert chain subcomponent.")?;
+                return Ok(());
+            }
+
+            // If we reach this point we have to check the special condition where a certificate
+            // entry may be made.
+            if !(public_cert.is_none() && certificate_chain.is_some()) {
+                return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND)).context("No key to update.");
+            }
+
+            // So we know that we have a certificate chain and no public cert.
+            // Now check that we have everything we need to make a new certificate entry.
+            let key = match (key.domain, &key.alias) {
+                (Domain::APP, Some(ref alias)) => KeyDescriptor {
+                    domain: Domain::APP,
+                    nspace: ThreadState::get_calling_uid() as i64,
+                    alias: Some(alias.clone()),
+                    blob: None,
+                },
+                (Domain::SELINUX, Some(_)) => key.clone(),
+                _ => {
+                    return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT))
+                        .context("Domain must be APP or SELINUX to insert a certificate.")
+                }
+            };
+
+            // Security critical: This must return on failure. Do not remove the `?`;
+            check_key_permission(KeyPerm::rebind(), &key, &None)
+                .context("Caller does not have permission to insert this certificate.")?;
+
+            db.store_new_certificate(&key, certificate_chain.unwrap(), &KEYSTORE_UUID)
+                .context("Failed to insert new certificate.")?;
+            Ok(())
+        })
+        .context("In update_subcomponent.")
+    }
+
+    fn list_entries(&self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
+        let mut k = match domain {
+            Domain::APP => KeyDescriptor {
+                domain,
+                nspace: ThreadState::get_calling_uid() as u64 as i64,
+                ..Default::default()
+            },
+            Domain::SELINUX => KeyDescriptor{domain, nspace: namespace, ..Default::default()},
+            _ => return Err(Error::perm()).context(
+                "In list_entries: List entries is only supported for Domain::APP and Domain::SELINUX."
+            ),
+        };
+
+        // First we check if the caller has the info permission for the selected domain/namespace.
+        // By default we use the calling uid as namespace if domain is Domain::APP.
+        // If the first check fails we check if the caller has the list permission allowing to list
+        // any namespace. In that case we also adjust the queried namespace if a specific uid was
+        // selected.
+        match check_key_permission(KeyPerm::get_info(), &k, &None) {
+            Err(e) => {
+                if let Some(selinux::Error::PermissionDenied) =
+                    e.root_cause().downcast_ref::<selinux::Error>()
+                {
+                    check_keystore_permission(KeystorePerm::list())
+                        .context("In list_entries: While checking keystore permission.")?;
+                    if namespace != -1 {
+                        k.nspace = namespace;
+                    }
+                } else {
+                    return Err(e).context("In list_entries: While checking key permission.")?;
+                }
+            }
+            Ok(()) => {}
+        };
+
+        let mut result = LEGACY_MIGRATOR
+            .list_uid(k.domain, k.nspace)
+            .context("In list_entries: Trying to list legacy keys.")?;
+
+        result.append(
+            &mut DB
+                .with(|db| {
+                    let mut db = db.borrow_mut();
+                    db.list(k.domain, k.nspace)
+                })
+                .context("In list_entries: Trying to list keystore database.")?,
+        );
+
+        result.sort_unstable();
+        result.dedup();
+        Ok(result)
+    }
+
+    fn delete_key(&self, key: &KeyDescriptor) -> Result<()> {
+        let caller_uid = ThreadState::get_calling_uid();
+        DB.with(|db| {
+            LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
+                db.borrow_mut().unbind_key(&key, KeyType::Client, caller_uid, |k, av| {
+                    check_key_permission(KeyPerm::delete(), k, &av).context("During delete_key.")
+                })
+            })
+        })
+        .context("In delete_key: Trying to unbind the key.")?;
+        Ok(())
+    }
+
+    fn grant(
+        &self,
+        key: &KeyDescriptor,
+        grantee_uid: i32,
+        access_vector: permission::KeyPermSet,
+    ) -> Result<KeyDescriptor> {
+        let caller_uid = ThreadState::get_calling_uid();
+        DB.with(|db| {
+            LEGACY_MIGRATOR.with_try_migrate(&key, caller_uid, || {
+                db.borrow_mut().grant(
+                    &key,
+                    caller_uid,
+                    grantee_uid as u32,
+                    access_vector,
+                    |k, av| check_grant_permission(*av, k).context("During grant."),
+                )
+            })
+        })
+        .context("In KeystoreService::grant.")
+    }
+
+    fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> Result<()> {
+        DB.with(|db| {
+            db.borrow_mut().ungrant(&key, ThreadState::get_calling_uid(), grantee_uid as u32, |k| {
+                check_key_permission(KeyPerm::grant(), k, &None)
+            })
+        })
+        .context("In KeystoreService::ungrant.")
+    }
+}
+
+impl binder::Interface for KeystoreService {}
+
+// Implementation of IKeystoreService. See AIDL spec at
+// system/security/keystore2/binder/android/security/keystore2/IKeystoreService.aidl
+impl IKeystoreService for KeystoreService {
+    fn getSecurityLevel(
+        &self,
+        security_level: SecurityLevel,
+    ) -> binder::public_api::Result<Strong<dyn IKeystoreSecurityLevel>> {
+        let _wp = wd::watch_millis_with("IKeystoreService::getSecurityLevel", 500, move || {
+            format!("security_level: {}", security_level.0)
+        });
+        map_or_log_err(self.get_security_level(security_level), Ok)
+    }
+    fn getKeyEntry(&self, key: &KeyDescriptor) -> binder::public_api::Result<KeyEntryResponse> {
+        let _wp = wd::watch_millis("IKeystoreService::get_key_entry", 500);
+        map_or_log_err(self.get_key_entry(key), Ok)
+    }
+    fn updateSubcomponent(
+        &self,
+        key: &KeyDescriptor,
+        public_cert: Option<&[u8]>,
+        certificate_chain: Option<&[u8]>,
+    ) -> binder::public_api::Result<()> {
+        let _wp = wd::watch_millis("IKeystoreService::updateSubcomponent", 500);
+        map_or_log_err(self.update_subcomponent(key, public_cert, certificate_chain), Ok)
+    }
+    fn listEntries(
+        &self,
+        domain: Domain,
+        namespace: i64,
+    ) -> binder::public_api::Result<Vec<KeyDescriptor>> {
+        let _wp = wd::watch_millis("IKeystoreService::listEntries", 500);
+        map_or_log_err(self.list_entries(domain, namespace), Ok)
+    }
+    fn deleteKey(&self, key: &KeyDescriptor) -> binder::public_api::Result<()> {
+        let _wp = wd::watch_millis("IKeystoreService::deleteKey", 500);
+        let result = self.delete_key(key);
+        log_key_deleted(key, ThreadState::get_calling_uid(), result.is_ok());
+        map_or_log_err(result, Ok)
+    }
+    fn grant(
+        &self,
+        key: &KeyDescriptor,
+        grantee_uid: i32,
+        access_vector: i32,
+    ) -> binder::public_api::Result<KeyDescriptor> {
+        let _wp = wd::watch_millis("IKeystoreService::grant", 500);
+        map_or_log_err(self.grant(key, grantee_uid, access_vector.into()), Ok)
+    }
+    fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> binder::public_api::Result<()> {
+        let _wp = wd::watch_millis("IKeystoreService::ungrant", 500);
+        map_or_log_err(self.ungrant(key, grantee_uid), Ok)
+    }
+}
diff --git a/keystore2/src/shared_secret_negotiation.rs b/keystore2/src/shared_secret_negotiation.rs
new file mode 100644
index 0000000..fb55f33
--- /dev/null
+++ b/keystore2/src/shared_secret_negotiation.rs
@@ -0,0 +1,265 @@
+// 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.
+
+//! This module implements the shared secret negotiation.
+
+use crate::error::{map_binder_status, map_binder_status_code, Error};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use android_hardware_security_keymint::binder::Strong;
+use android_hardware_security_sharedsecret::aidl::android::hardware::security::sharedsecret::{
+    ISharedSecret::ISharedSecret, SharedSecretParameters::SharedSecretParameters,
+};
+use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
+use anyhow::{Context, Result};
+use keystore2_vintf::{get_aidl_instances, get_hidl_instances};
+use std::fmt::{self, Display, Formatter};
+
+/// This function initiates the shared secret negotiation. It starts a thread and then returns
+/// immediately. The thread consults the vintf manifest to enumerate expected negotiation
+/// participants. It then attempts to connect to all of these participants. If any connection
+/// fails the thread will retry once per second to connect to the failed instance(s) until all of
+/// the instances are connected. It then performs the negotiation.
+///
+/// During the first phase of the negotiation it will again try every second until
+/// all instances have responded successfully to account for instances that register early but
+/// are not fully functioning at this time due to hardware delays or boot order dependency issues.
+/// An error during the second phase or a checksum mismatch leads to a panic.
+pub fn perform_shared_secret_negotiation() {
+    std::thread::spawn(|| {
+        let participants = list_participants()
+            .expect("In perform_shared_secret_negotiation: Trying to list participants.");
+        let connected = connect_participants(participants);
+        negotiate_shared_secret(connected);
+        log::info!("Shared secret negotiation concluded successfully.");
+    });
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+enum SharedSecretParticipant {
+    /// Represents an instance of android.hardware.security.sharedsecret.ISharedSecret.
+    Aidl(String),
+    /// In the legacy case there can be at most one TEE and one Strongbox hal.
+    Hidl { is_strongbox: bool, version: (usize, usize) },
+}
+
+impl Display for SharedSecretParticipant {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        match self {
+            Self::Aidl(instance) => write!(
+                f,
+                "{}.{}/{}",
+                SHARED_SECRET_PACKAGE_NAME, SHARED_SECRET_INTERFACE_NAME, instance
+            ),
+            Self::Hidl { is_strongbox, version: (ma, mi) } => write!(
+                f,
+                "{}@V{}.{}::{}/{}",
+                KEYMASTER_PACKAGE_NAME,
+                ma,
+                mi,
+                KEYMASTER_INTERFACE_NAME,
+                if *is_strongbox { "strongbox" } else { "default" }
+            ),
+        }
+    }
+}
+
+#[derive(thiserror::Error, Debug)]
+enum SharedSecretError {
+    #[error("Shared parameter retrieval failed on instance {p} with error {e:?}.")]
+    ParameterRetrieval { e: Error, p: SharedSecretParticipant },
+    #[error("Shared secret computation failed on instance {p} with error {e:?}.")]
+    Computation { e: Error, p: SharedSecretParticipant },
+    #[error("Checksum comparison failed on instance {0}.")]
+    Checksum(SharedSecretParticipant),
+}
+
+fn filter_map_legacy_km_instances(
+    name: String,
+    version: (usize, usize),
+) -> Option<SharedSecretParticipant> {
+    match name.as_str() {
+        "default" => Some(SharedSecretParticipant::Hidl { is_strongbox: false, version }),
+        "strongbox" => Some(SharedSecretParticipant::Hidl { is_strongbox: true, version }),
+        _ => {
+            log::warn!("Found unexpected keymaster instance: \"{}\"", name);
+            log::warn!("Device is misconfigured. Allowed instances are:");
+            log::warn!("   * default");
+            log::warn!("   * strongbox");
+            None
+        }
+    }
+}
+
+static KEYMASTER_PACKAGE_NAME: &str = "android.hardware.keymaster";
+static KEYMASTER_INTERFACE_NAME: &str = "IKeymasterDevice";
+static SHARED_SECRET_PACKAGE_NAME: &str = "android.hardware.security.sharedsecret";
+static SHARED_SECRET_INTERFACE_NAME: &str = "ISharedSecret";
+static COMPAT_PACKAGE_NAME: &str = "android.security.compat";
+
+/// Lists participants.
+fn list_participants() -> Result<Vec<SharedSecretParticipant>> {
+    Ok([(4, 0), (4, 1)]
+        .iter()
+        .map(|(ma, mi)| {
+            get_hidl_instances(KEYMASTER_PACKAGE_NAME, *ma, *mi, KEYMASTER_INTERFACE_NAME)
+                .as_vec()
+                .with_context(|| format!("Trying to convert KM{}.{} names to vector.", *ma, *mi))
+                .map(|instances| {
+                    instances
+                        .into_iter()
+                        .filter_map(|name| {
+                            filter_map_legacy_km_instances(name.to_string(), (*ma, *mi))
+                        })
+                        .collect::<Vec<SharedSecretParticipant>>()
+                })
+        })
+        .collect::<Result<Vec<_>>>()
+        .map(|v| v.into_iter().flatten())
+        .and_then(|i| {
+            let participants_aidl: Vec<SharedSecretParticipant> =
+                get_aidl_instances(SHARED_SECRET_PACKAGE_NAME, 1, SHARED_SECRET_INTERFACE_NAME)
+                    .as_vec()
+                    .context("In list_participants: Trying to convert KM1.0 names to vector.")?
+                    .into_iter()
+                    .map(|name| SharedSecretParticipant::Aidl(name.to_string()))
+                    .collect();
+            Ok(i.chain(participants_aidl.into_iter()))
+        })
+        .context("In list_participants.")?
+        .collect())
+}
+
+fn connect_participants(
+    mut participants: Vec<SharedSecretParticipant>,
+) -> Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> {
+    let mut connected_participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)> =
+        vec![];
+    loop {
+        let (connected, not_connected) = participants.into_iter().fold(
+            (connected_participants, vec![]),
+            |(mut connected, mut failed), e| {
+                match e {
+                    SharedSecretParticipant::Aidl(instance_name) => {
+                        let service_name = format!(
+                            "{}.{}/{}",
+                            SHARED_SECRET_PACKAGE_NAME, SHARED_SECRET_INTERFACE_NAME, instance_name
+                        );
+                        match map_binder_status_code(binder::get_interface(&service_name)) {
+                            Err(e) => {
+                                log::warn!(
+                                    "Unable to connect \"{}\" with error:\n{:?}\nRetrying later.",
+                                    service_name,
+                                    e
+                                );
+                                failed.push(SharedSecretParticipant::Aidl(instance_name));
+                            }
+                            Ok(service) => connected
+                                .push((service, SharedSecretParticipant::Aidl(instance_name))),
+                        }
+                    }
+                    SharedSecretParticipant::Hidl { is_strongbox, version } => {
+                        // This is a no-op if it was called before.
+                        keystore2_km_compat::add_keymint_device_service();
+
+                        // If we cannot connect to the compatibility service there is no way to
+                        // recover.
+                        // PANIC! - Unless you brought your towel.
+                        let keystore_compat_service: Strong<dyn IKeystoreCompatService> =
+                            map_binder_status_code(binder::get_interface(COMPAT_PACKAGE_NAME))
+                                .expect(
+                                    "In connect_participants: Trying to connect to compat service.",
+                                );
+
+                        match map_binder_status(keystore_compat_service.getSharedSecret(
+                            if is_strongbox {
+                                SecurityLevel::STRONGBOX
+                            } else {
+                                SecurityLevel::TRUSTED_ENVIRONMENT
+                            },
+                        )) {
+                            Err(e) => {
+                                log::warn!(
+                                    concat!(
+                                        "Unable to connect keymaster device \"{}\" ",
+                                        "with error:\n{:?}\nRetrying later."
+                                    ),
+                                    if is_strongbox { "strongbox" } else { "TEE" },
+                                    e
+                                );
+                                failed
+                                    .push(SharedSecretParticipant::Hidl { is_strongbox, version });
+                            }
+                            Ok(service) => connected.push((
+                                service,
+                                SharedSecretParticipant::Hidl { is_strongbox, version },
+                            )),
+                        }
+                    }
+                }
+                (connected, failed)
+            },
+        );
+        participants = not_connected;
+        connected_participants = connected;
+        if participants.is_empty() {
+            break;
+        }
+        std::thread::sleep(std::time::Duration::from_millis(1000));
+    }
+    connected_participants
+}
+
+fn negotiate_shared_secret(
+    participants: Vec<(Strong<dyn ISharedSecret>, SharedSecretParticipant)>,
+) {
+    // Phase 1: Get the sharing parameters from all participants.
+    let mut params = loop {
+        let result: Result<Vec<SharedSecretParameters>, SharedSecretError> = participants
+            .iter()
+            .map(|(s, p)| {
+                map_binder_status(s.getSharedSecretParameters())
+                    .map_err(|e| SharedSecretError::ParameterRetrieval { e, p: (*p).clone() })
+            })
+            .collect();
+
+        match result {
+            Err(e) => {
+                log::warn!("{:?}", e);
+                log::warn!("Retrying in one second.");
+                std::thread::sleep(std::time::Duration::from_millis(1000));
+            }
+            Ok(params) => break params,
+        }
+    };
+
+    params.sort_unstable();
+
+    // Phase 2: Send the sorted sharing parameters to all participants.
+    participants
+        .into_iter()
+        .try_fold(None, |acc, (s, p)| {
+            match (acc, map_binder_status(s.computeSharedSecret(&params))) {
+                (None, Ok(new_sum)) => Ok(Some(new_sum)),
+                (Some(old_sum), Ok(new_sum)) => {
+                    if old_sum == new_sum {
+                        Ok(Some(old_sum))
+                    } else {
+                        Err(SharedSecretError::Checksum(p))
+                    }
+                }
+                (_, Err(e)) => Err(SharedSecretError::Computation { e, p }),
+            }
+        })
+        .expect("Fatal: Shared secret computation failed.");
+}
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
new file mode 100644
index 0000000..848707c
--- /dev/null
+++ b/keystore2/src/super_key.rs
@@ -0,0 +1,1199 @@
+// 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.
+
+use crate::{
+    boot_level_keys::{get_level_zero_key, BootLevelKeyCache},
+    database::BlobMetaData,
+    database::BlobMetaEntry,
+    database::EncryptedBy,
+    database::KeyEntry,
+    database::KeyType,
+    database::{KeyIdGuard, KeyMetaData, KeyMetaEntry, KeystoreDB},
+    ec_crypto::ECDHPrivateKey,
+    enforcements::Enforcements,
+    error::Error,
+    error::ResponseCode,
+    key_parameter::{KeyParameter, KeyParameterValue},
+    legacy_blob::LegacyBlobLoader,
+    legacy_migrator::LegacyMigrator,
+    raw_device::KeyMintDevice,
+    try_insert::TryInsert,
+    utils::watchdog as wd,
+};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    Algorithm::Algorithm, BlockMode::BlockMode, HardwareAuthToken::HardwareAuthToken,
+    HardwareAuthenticatorType::HardwareAuthenticatorType, KeyFormat::KeyFormat,
+    KeyParameter::KeyParameter as KmKeyParameter, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+    SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
+use anyhow::{Context, Result};
+use keystore2_crypto::{
+    aes_gcm_decrypt, aes_gcm_encrypt, generate_aes256_key, generate_salt, Password, ZVec,
+    AES_256_KEY_LENGTH,
+};
+use keystore2_system_property::PropertyWatcher;
+use std::{
+    collections::HashMap,
+    sync::Arc,
+    sync::{Mutex, Weak},
+};
+use std::{convert::TryFrom, ops::Deref};
+
+const MAX_MAX_BOOT_LEVEL: usize = 1_000_000_000;
+/// Allow up to 15 seconds between the user unlocking using a biometric, and the auth
+/// token being used to unlock in [`SuperKeyManager::try_unlock_user_with_biometric`].
+/// This seems short enough for security purposes, while long enough that even the
+/// very slowest device will present the auth token in time.
+const BIOMETRIC_AUTH_TIMEOUT_S: i32 = 15; // seconds
+
+type UserId = u32;
+
+/// Encryption algorithm used by a particular type of superencryption key
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum SuperEncryptionAlgorithm {
+    /// Symmetric encryption with AES-256-GCM
+    Aes256Gcm,
+    /// Public-key encryption with ECDH P-256
+    EcdhP256,
+}
+
+/// A particular user may have several superencryption keys in the database, each for a
+/// different purpose, distinguished by alias. Each is associated with a static
+/// constant of this type.
+pub struct SuperKeyType {
+    /// Alias used to look the key up in the `persistent.keyentry` table.
+    pub alias: &'static str,
+    /// Encryption algorithm
+    pub algorithm: SuperEncryptionAlgorithm,
+}
+
+/// Key used for LskfLocked keys; the corresponding superencryption key is loaded in memory
+/// when the user first unlocks, and remains in memory until the device reboots.
+pub const USER_SUPER_KEY: SuperKeyType =
+    SuperKeyType { alias: "USER_SUPER_KEY", algorithm: SuperEncryptionAlgorithm::Aes256Gcm };
+/// Key used for ScreenLockBound keys; the corresponding superencryption key is loaded in memory
+/// each time the user enters their LSKF, and cleared from memory each time the device is locked.
+/// Symmetric.
+pub const USER_SCREEN_LOCK_BOUND_KEY: SuperKeyType = SuperKeyType {
+    alias: "USER_SCREEN_LOCK_BOUND_KEY",
+    algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+};
+/// Key used for ScreenLockBound keys; the corresponding superencryption key is loaded in memory
+/// each time the user enters their LSKF, and cleared from memory each time the device is locked.
+/// Asymmetric, so keys can be encrypted when the device is locked.
+pub const USER_SCREEN_LOCK_BOUND_ECDH_KEY: SuperKeyType = SuperKeyType {
+    alias: "USER_SCREEN_LOCK_BOUND_ECDH_KEY",
+    algorithm: SuperEncryptionAlgorithm::EcdhP256,
+};
+
+/// Superencryption to apply to a new key.
+#[derive(Debug, Clone, Copy)]
+pub enum SuperEncryptionType {
+    /// Do not superencrypt this key.
+    None,
+    /// Superencrypt with a key that remains in memory from first unlock to reboot.
+    LskfBound,
+    /// Superencrypt with a key cleared from memory when the device is locked.
+    ScreenLockBound,
+    /// Superencrypt with a key based on the desired boot level
+    BootLevel(i32),
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum SuperKeyIdentifier {
+    /// id of the super key in the database.
+    DatabaseId(i64),
+    /// Boot level of the encrypting boot level key
+    BootLevel(i32),
+}
+
+impl SuperKeyIdentifier {
+    fn from_metadata(metadata: &BlobMetaData) -> Option<Self> {
+        if let Some(EncryptedBy::KeyId(key_id)) = metadata.encrypted_by() {
+            Some(SuperKeyIdentifier::DatabaseId(*key_id))
+        } else if let Some(boot_level) = metadata.max_boot_level() {
+            Some(SuperKeyIdentifier::BootLevel(*boot_level))
+        } else {
+            None
+        }
+    }
+
+    fn add_to_metadata(&self, metadata: &mut BlobMetaData) {
+        match self {
+            SuperKeyIdentifier::DatabaseId(id) => {
+                metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(*id)));
+            }
+            SuperKeyIdentifier::BootLevel(level) => {
+                metadata.add(BlobMetaEntry::MaxBootLevel(*level));
+            }
+        }
+    }
+}
+
+pub struct SuperKey {
+    algorithm: SuperEncryptionAlgorithm,
+    key: ZVec,
+    /// Identifier of the encrypting key, used to write an encrypted blob
+    /// back to the database after re-encryption eg on a key update.
+    id: SuperKeyIdentifier,
+    /// ECDH is more expensive than AES. So on ECDH private keys we set the
+    /// reencrypt_with field to point at the corresponding AES key, and the
+    /// keys will be re-encrypted with AES on first use.
+    reencrypt_with: Option<Arc<SuperKey>>,
+}
+
+impl SuperKey {
+    /// For most purposes `unwrap_key` handles decryption,
+    /// but legacy handling and some tests need to assume AES and decrypt directly.
+    pub fn aes_gcm_decrypt(&self, data: &[u8], iv: &[u8], tag: &[u8]) -> Result<ZVec> {
+        if self.algorithm == SuperEncryptionAlgorithm::Aes256Gcm {
+            aes_gcm_decrypt(data, iv, tag, &self.key)
+                .context("In aes_gcm_decrypt: decryption failed")
+        } else {
+            Err(Error::sys()).context("In aes_gcm_decrypt: Key is not an AES key")
+        }
+    }
+}
+
+/// A SuperKey that has been encrypted with an AES-GCM key. For
+/// encryption the key is in memory, and for decryption it is in KM.
+struct LockedKey {
+    algorithm: SuperEncryptionAlgorithm,
+    id: SuperKeyIdentifier,
+    nonce: Vec<u8>,
+    ciphertext: Vec<u8>, // with tag appended
+}
+
+impl LockedKey {
+    fn new(key: &[u8], to_encrypt: &Arc<SuperKey>) -> Result<Self> {
+        let (mut ciphertext, nonce, mut tag) = aes_gcm_encrypt(&to_encrypt.key, key)?;
+        ciphertext.append(&mut tag);
+        Ok(LockedKey { algorithm: to_encrypt.algorithm, id: to_encrypt.id, nonce, ciphertext })
+    }
+
+    fn decrypt(
+        &self,
+        db: &mut KeystoreDB,
+        km_dev: &KeyMintDevice,
+        key_id_guard: &KeyIdGuard,
+        key_entry: &KeyEntry,
+        auth_token: &HardwareAuthToken,
+        reencrypt_with: Option<Arc<SuperKey>>,
+    ) -> Result<Arc<SuperKey>> {
+        let key_params = vec![
+            KeyParameterValue::Algorithm(Algorithm::AES),
+            KeyParameterValue::KeySize(256),
+            KeyParameterValue::BlockMode(BlockMode::GCM),
+            KeyParameterValue::PaddingMode(PaddingMode::NONE),
+            KeyParameterValue::Nonce(self.nonce.clone()),
+            KeyParameterValue::MacLength(128),
+        ];
+        let key_params: Vec<KmKeyParameter> = key_params.into_iter().map(|x| x.into()).collect();
+        let key = ZVec::try_from(km_dev.use_key_in_one_step(
+            db,
+            key_id_guard,
+            key_entry,
+            KeyPurpose::DECRYPT,
+            &key_params,
+            Some(auth_token),
+            &self.ciphertext,
+        )?)?;
+        Ok(Arc::new(SuperKey { algorithm: self.algorithm, key, id: self.id, reencrypt_with }))
+    }
+}
+
+/// Keys for unlocking UNLOCKED_DEVICE_REQUIRED keys, as LockedKeys, complete with
+/// a database descriptor for the encrypting key and the sids for the auth tokens
+/// that can be used to decrypt it.
+struct BiometricUnlock {
+    /// List of auth token SIDs that can be used to unlock these keys.
+    sids: Vec<i64>,
+    /// Database descriptor of key to use to unlock.
+    key_desc: KeyDescriptor,
+    /// Locked versions of the matching UserSuperKeys fields
+    screen_lock_bound: LockedKey,
+    screen_lock_bound_private: LockedKey,
+}
+
+#[derive(Default)]
+struct UserSuperKeys {
+    /// The per boot key is used for LSKF binding of authentication bound keys. There is one
+    /// key per android user. The key is stored on flash encrypted with a key derived from a
+    /// secret, that is itself derived from the user's lock screen knowledge factor (LSKF).
+    /// When the user unlocks the device for the first time, this key is unlocked, i.e., decrypted,
+    /// and stays memory resident until the device reboots.
+    per_boot: Option<Arc<SuperKey>>,
+    /// The screen lock key works like the per boot key with the distinction that it is cleared
+    /// from memory when the screen lock is engaged.
+    screen_lock_bound: Option<Arc<SuperKey>>,
+    /// When the device is locked, screen-lock-bound keys can still be encrypted, using
+    /// ECDH public-key encryption. This field holds the decryption private key.
+    screen_lock_bound_private: Option<Arc<SuperKey>>,
+    /// Versions of the above two keys, locked behind a biometric.
+    biometric_unlock: Option<BiometricUnlock>,
+}
+
+#[derive(Default)]
+struct SkmState {
+    user_keys: HashMap<UserId, UserSuperKeys>,
+    key_index: HashMap<i64, Weak<SuperKey>>,
+    boot_level_key_cache: Option<BootLevelKeyCache>,
+}
+
+impl SkmState {
+    fn add_key_to_key_index(&mut self, super_key: &Arc<SuperKey>) -> Result<()> {
+        if let SuperKeyIdentifier::DatabaseId(id) = super_key.id {
+            self.key_index.insert(id, Arc::downgrade(super_key));
+            Ok(())
+        } else {
+            Err(Error::sys()).context(format!(
+                "In add_key_to_key_index: cannot add key with ID {:?}",
+                super_key.id
+            ))
+        }
+    }
+}
+
+#[derive(Default)]
+pub struct SuperKeyManager {
+    data: Mutex<SkmState>,
+}
+
+impl SuperKeyManager {
+    pub fn set_up_boot_level_cache(self: &Arc<Self>, db: &mut KeystoreDB) -> Result<()> {
+        let mut data = self.data.lock().unwrap();
+        if data.boot_level_key_cache.is_some() {
+            log::info!("In set_up_boot_level_cache: called for a second time");
+            return Ok(());
+        }
+        let level_zero_key = get_level_zero_key(db)
+            .context("In set_up_boot_level_cache: get_level_zero_key failed")?;
+        data.boot_level_key_cache = Some(BootLevelKeyCache::new(level_zero_key));
+        log::info!("Starting boot level watcher.");
+        let clone = self.clone();
+        std::thread::spawn(move || {
+            clone
+                .watch_boot_level()
+                .unwrap_or_else(|e| log::error!("watch_boot_level failed:\n{:?}", e));
+        });
+        Ok(())
+    }
+
+    /// Watch the `keystore.boot_level` system property, and keep boot level up to date.
+    /// Blocks waiting for system property changes, so must be run in its own thread.
+    fn watch_boot_level(&self) -> Result<()> {
+        let mut w = PropertyWatcher::new("keystore.boot_level")
+            .context("In watch_boot_level: PropertyWatcher::new failed")?;
+        loop {
+            let level = w
+                .read(|_n, v| v.parse::<usize>().map_err(std::convert::Into::into))
+                .context("In watch_boot_level: read of property failed")?;
+            // watch_boot_level should only be called once data.boot_level_key_cache is Some,
+            // so it's safe to unwrap in the branches below.
+            if level < MAX_MAX_BOOT_LEVEL {
+                log::info!("Read keystore.boot_level value {}", level);
+                let mut data = self.data.lock().unwrap();
+                data.boot_level_key_cache
+                    .as_mut()
+                    .unwrap()
+                    .advance_boot_level(level)
+                    .context("In watch_boot_level: advance_boot_level failed")?;
+            } else {
+                log::info!(
+                    "keystore.boot_level {} hits maximum {}, finishing.",
+                    level,
+                    MAX_MAX_BOOT_LEVEL
+                );
+                let mut data = self.data.lock().unwrap();
+                data.boot_level_key_cache.as_mut().unwrap().finish();
+                break;
+            }
+            w.wait().context("In watch_boot_level: property wait failed")?;
+        }
+        Ok(())
+    }
+
+    pub fn level_accessible(&self, boot_level: i32) -> bool {
+        self.data
+            .lock()
+            .unwrap()
+            .boot_level_key_cache
+            .as_ref()
+            .map_or(false, |c| c.level_accessible(boot_level as usize))
+    }
+
+    pub fn forget_all_keys_for_user(&self, user: UserId) {
+        let mut data = self.data.lock().unwrap();
+        data.user_keys.remove(&user);
+    }
+
+    fn install_per_boot_key_for_user(&self, user: UserId, super_key: Arc<SuperKey>) -> Result<()> {
+        let mut data = self.data.lock().unwrap();
+        data.add_key_to_key_index(&super_key)
+            .context("In install_per_boot_key_for_user: add_key_to_key_index failed")?;
+        data.user_keys.entry(user).or_default().per_boot = Some(super_key);
+        Ok(())
+    }
+
+    fn lookup_key(&self, key_id: &SuperKeyIdentifier) -> Result<Option<Arc<SuperKey>>> {
+        let mut data = self.data.lock().unwrap();
+        Ok(match key_id {
+            SuperKeyIdentifier::DatabaseId(id) => data.key_index.get(id).and_then(|k| k.upgrade()),
+            SuperKeyIdentifier::BootLevel(level) => data
+                .boot_level_key_cache
+                .as_mut()
+                .map(|b| b.aes_key(*level as usize))
+                .transpose()
+                .context("In lookup_key: aes_key failed")?
+                .flatten()
+                .map(|key| {
+                    Arc::new(SuperKey {
+                        algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+                        key,
+                        id: *key_id,
+                        reencrypt_with: None,
+                    })
+                }),
+        })
+    }
+
+    pub fn get_per_boot_key_by_user_id(&self, user_id: UserId) -> Option<Arc<SuperKey>> {
+        let data = self.data.lock().unwrap();
+        data.user_keys.get(&user_id).and_then(|e| e.per_boot.as_ref().cloned())
+    }
+
+    /// This function unlocks the super keys for a given user.
+    /// This means the key is loaded from the database, decrypted and placed in the
+    /// super key cache. If there is no such key a new key is created, encrypted with
+    /// a key derived from the given password and stored in the database.
+    pub fn unlock_user_key(
+        &self,
+        db: &mut KeystoreDB,
+        user: UserId,
+        pw: &Password,
+        legacy_blob_loader: &LegacyBlobLoader,
+    ) -> Result<()> {
+        let (_, entry) = db
+            .get_or_create_key_with(
+                Domain::APP,
+                user as u64 as i64,
+                &USER_SUPER_KEY.alias,
+                crate::database::KEYSTORE_UUID,
+                || {
+                    // For backward compatibility we need to check if there is a super key present.
+                    let super_key = legacy_blob_loader
+                        .load_super_key(user, pw)
+                        .context("In create_new_key: Failed to load legacy key blob.")?;
+                    let super_key = match super_key {
+                        None => {
+                            // No legacy file was found. So we generate a new key.
+                            generate_aes256_key()
+                                .context("In create_new_key: Failed to generate AES 256 key.")?
+                        }
+                        Some(key) => key,
+                    };
+                    // Regardless of whether we loaded an old AES128 key or generated a new AES256
+                    // key as the super key, we derive a AES256 key from the password and re-encrypt
+                    // the super key before we insert it in the database. The length of the key is
+                    // preserved by the encryption so we don't need any extra flags to inform us
+                    // which algorithm to use it with.
+                    Self::encrypt_with_password(&super_key, pw).context("In create_new_key.")
+                },
+            )
+            .context("In unlock_user_key: Failed to get key id.")?;
+
+        self.populate_cache_from_super_key_blob(user, USER_SUPER_KEY.algorithm, entry, pw)
+            .context("In unlock_user_key.")?;
+        Ok(())
+    }
+
+    /// Check if a given key is super-encrypted, from its metadata. If so, unwrap the key using
+    /// the relevant super key.
+    pub fn unwrap_key_if_required<'a>(
+        &self,
+        metadata: &BlobMetaData,
+        blob: &'a [u8],
+    ) -> Result<KeyBlob<'a>> {
+        Ok(if let Some(key_id) = SuperKeyIdentifier::from_metadata(metadata) {
+            let super_key = self
+                .lookup_key(&key_id)
+                .context("In unwrap_key: lookup_key failed")?
+                .ok_or(Error::Rc(ResponseCode::LOCKED))
+                .context("In unwrap_key: Required super decryption key is not in memory.")?;
+            KeyBlob::Sensitive {
+                key: Self::unwrap_key_with_key(blob, metadata, &super_key)
+                    .context("In unwrap_key: unwrap_key_with_key failed")?,
+                reencrypt_with: super_key.reencrypt_with.as_ref().unwrap_or(&super_key).clone(),
+                force_reencrypt: super_key.reencrypt_with.is_some(),
+            }
+        } else {
+            KeyBlob::Ref(blob)
+        })
+    }
+
+    /// Unwraps an encrypted key blob given an encryption key.
+    fn unwrap_key_with_key(blob: &[u8], metadata: &BlobMetaData, key: &SuperKey) -> Result<ZVec> {
+        match key.algorithm {
+            SuperEncryptionAlgorithm::Aes256Gcm => match (metadata.iv(), metadata.aead_tag()) {
+                (Some(iv), Some(tag)) => key
+                    .aes_gcm_decrypt(blob, iv, tag)
+                    .context("In unwrap_key_with_key: Failed to decrypt the key blob."),
+                (iv, tag) => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
+                    concat!(
+                        "In unwrap_key_with_key: Key has incomplete metadata.",
+                        "Present: iv: {}, aead_tag: {}."
+                    ),
+                    iv.is_some(),
+                    tag.is_some(),
+                )),
+            },
+            SuperEncryptionAlgorithm::EcdhP256 => {
+                match (metadata.public_key(), metadata.salt(), metadata.iv(), metadata.aead_tag()) {
+                    (Some(public_key), Some(salt), Some(iv), Some(aead_tag)) => {
+                        ECDHPrivateKey::from_private_key(&key.key)
+                            .and_then(|k| k.decrypt_message(public_key, salt, iv, blob, aead_tag))
+                            .context(
+                                "In unwrap_key_with_key: Failed to decrypt the key blob with ECDH.",
+                            )
+                    }
+                    (public_key, salt, iv, aead_tag) => {
+                        Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
+                            concat!(
+                                "In unwrap_key_with_key: Key has incomplete metadata.",
+                                "Present: public_key: {}, salt: {}, iv: {}, aead_tag: {}."
+                            ),
+                            public_key.is_some(),
+                            salt.is_some(),
+                            iv.is_some(),
+                            aead_tag.is_some(),
+                        ))
+                    }
+                }
+            }
+        }
+    }
+
+    /// Checks if user has setup LSKF, even when super key cache is empty for the user.
+    pub fn super_key_exists_in_db_for_user(
+        db: &mut KeystoreDB,
+        legacy_migrator: &LegacyMigrator,
+        user_id: UserId,
+    ) -> Result<bool> {
+        let key_in_db = db
+            .key_exists(Domain::APP, user_id as u64 as i64, &USER_SUPER_KEY.alias, KeyType::Super)
+            .context("In super_key_exists_in_db_for_user.")?;
+
+        if key_in_db {
+            Ok(key_in_db)
+        } else {
+            legacy_migrator
+                .has_super_key(user_id)
+                .context("In super_key_exists_in_db_for_user: Trying to query legacy db.")
+        }
+    }
+
+    /// Checks if user has already setup LSKF (i.e. a super key is persisted in the database or the
+    /// legacy database). If not, return Uninitialized state.
+    /// Otherwise, decrypt the super key from the password and return LskfUnlocked state.
+    pub fn check_and_unlock_super_key(
+        &self,
+        db: &mut KeystoreDB,
+        legacy_migrator: &LegacyMigrator,
+        user_id: UserId,
+        pw: &Password,
+    ) -> Result<UserState> {
+        let alias = &USER_SUPER_KEY;
+        let result = legacy_migrator
+            .with_try_migrate_super_key(user_id, pw, || db.load_super_key(alias, user_id))
+            .context("In check_and_unlock_super_key. Failed to load super key")?;
+
+        match result {
+            Some((_, entry)) => {
+                let super_key = self
+                    .populate_cache_from_super_key_blob(user_id, alias.algorithm, entry, pw)
+                    .context("In check_and_unlock_super_key.")?;
+                Ok(UserState::LskfUnlocked(super_key))
+            }
+            None => Ok(UserState::Uninitialized),
+        }
+    }
+
+    /// Checks if user has already setup LSKF (i.e. a super key is persisted in the database or the
+    /// legacy database). If so, return LskfLocked state.
+    /// If the password is provided, generate a new super key, encrypt with the password,
+    /// store in the database and populate the super key cache for the new user
+    /// and return LskfUnlocked state.
+    /// If the password is not provided, return Uninitialized state.
+    pub fn check_and_initialize_super_key(
+        &self,
+        db: &mut KeystoreDB,
+        legacy_migrator: &LegacyMigrator,
+        user_id: UserId,
+        pw: Option<&Password>,
+    ) -> Result<UserState> {
+        let super_key_exists_in_db =
+            Self::super_key_exists_in_db_for_user(db, legacy_migrator, user_id).context(
+                "In check_and_initialize_super_key. Failed to check if super key exists.",
+            )?;
+        if super_key_exists_in_db {
+            Ok(UserState::LskfLocked)
+        } else if let Some(pw) = pw {
+            //generate a new super key.
+            let super_key = generate_aes256_key()
+                .context("In check_and_initialize_super_key: Failed to generate AES 256 key.")?;
+            //derive an AES256 key from the password and re-encrypt the super key
+            //before we insert it in the database.
+            let (encrypted_super_key, blob_metadata) = Self::encrypt_with_password(&super_key, pw)
+                .context("In check_and_initialize_super_key.")?;
+
+            let key_entry = db
+                .store_super_key(
+                    user_id,
+                    &USER_SUPER_KEY,
+                    &encrypted_super_key,
+                    &blob_metadata,
+                    &KeyMetaData::new(),
+                )
+                .context("In check_and_initialize_super_key. Failed to store super key.")?;
+
+            let super_key = self
+                .populate_cache_from_super_key_blob(
+                    user_id,
+                    USER_SUPER_KEY.algorithm,
+                    key_entry,
+                    pw,
+                )
+                .context("In check_and_initialize_super_key.")?;
+            Ok(UserState::LskfUnlocked(super_key))
+        } else {
+            Ok(UserState::Uninitialized)
+        }
+    }
+
+    //helper function to populate super key cache from the super key blob loaded from the database
+    fn populate_cache_from_super_key_blob(
+        &self,
+        user_id: UserId,
+        algorithm: SuperEncryptionAlgorithm,
+        entry: KeyEntry,
+        pw: &Password,
+    ) -> Result<Arc<SuperKey>> {
+        let super_key = Self::extract_super_key_from_key_entry(algorithm, entry, pw, None)
+            .context(
+                "In populate_cache_from_super_key_blob. Failed to extract super key from key entry",
+            )?;
+        self.install_per_boot_key_for_user(user_id, super_key.clone())?;
+        Ok(super_key)
+    }
+
+    /// Extracts super key from the entry loaded from the database
+    pub fn extract_super_key_from_key_entry(
+        algorithm: SuperEncryptionAlgorithm,
+        entry: KeyEntry,
+        pw: &Password,
+        reencrypt_with: Option<Arc<SuperKey>>,
+    ) -> Result<Arc<SuperKey>> {
+        if let Some((blob, metadata)) = entry.key_blob_info() {
+            let key = match (
+                metadata.encrypted_by(),
+                metadata.salt(),
+                metadata.iv(),
+                metadata.aead_tag(),
+            ) {
+                (Some(&EncryptedBy::Password), Some(salt), Some(iv), Some(tag)) => {
+                    // Note that password encryption is AES no matter the value of algorithm
+                    let key = pw.derive_key(Some(salt), AES_256_KEY_LENGTH).context(
+                        "In extract_super_key_from_key_entry: Failed to generate key from password.",
+                    )?;
+
+                    aes_gcm_decrypt(blob, iv, tag, &key).context(
+                        "In extract_super_key_from_key_entry: Failed to decrypt key blob.",
+                    )?
+                }
+                (enc_by, salt, iv, tag) => {
+                    return Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
+                        concat!(
+                        "In extract_super_key_from_key_entry: Super key has incomplete metadata.",
+                        "encrypted_by: {:?}; Present: salt: {}, iv: {}, aead_tag: {}."
+                    ),
+                        enc_by,
+                        salt.is_some(),
+                        iv.is_some(),
+                        tag.is_some()
+                    ));
+                }
+            };
+            Ok(Arc::new(SuperKey {
+                algorithm,
+                key,
+                id: SuperKeyIdentifier::DatabaseId(entry.id()),
+                reencrypt_with,
+            }))
+        } else {
+            Err(Error::Rc(ResponseCode::VALUE_CORRUPTED))
+                .context("In extract_super_key_from_key_entry: No key blob info.")
+        }
+    }
+
+    /// Encrypts the super key from a key derived from the password, before storing in the database.
+    pub fn encrypt_with_password(
+        super_key: &[u8],
+        pw: &Password,
+    ) -> Result<(Vec<u8>, BlobMetaData)> {
+        let salt = generate_salt().context("In encrypt_with_password: Failed to generate salt.")?;
+        let derived_key = pw
+            .derive_key(Some(&salt), AES_256_KEY_LENGTH)
+            .context("In encrypt_with_password: Failed to derive password.")?;
+        let mut metadata = BlobMetaData::new();
+        metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
+        metadata.add(BlobMetaEntry::Salt(salt));
+        let (encrypted_key, iv, tag) = aes_gcm_encrypt(super_key, &derived_key)
+            .context("In encrypt_with_password: Failed to encrypt new super key.")?;
+        metadata.add(BlobMetaEntry::Iv(iv));
+        metadata.add(BlobMetaEntry::AeadTag(tag));
+        Ok((encrypted_key, metadata))
+    }
+
+    // Encrypt the given key blob with the user's super key, if the super key exists and the device
+    // is unlocked. If the super key exists and the device is locked, or LSKF is not setup,
+    // return error. Note that it is out of the scope of this function to check if super encryption
+    // is required. Such check should be performed before calling this function.
+    fn super_encrypt_on_key_init(
+        &self,
+        db: &mut KeystoreDB,
+        legacy_migrator: &LegacyMigrator,
+        user_id: UserId,
+        key_blob: &[u8],
+    ) -> Result<(Vec<u8>, BlobMetaData)> {
+        match UserState::get(db, legacy_migrator, self, user_id)
+            .context("In super_encrypt. Failed to get user state.")?
+        {
+            UserState::LskfUnlocked(super_key) => {
+                Self::encrypt_with_aes_super_key(key_blob, &super_key)
+                    .context("In super_encrypt_on_key_init. Failed to encrypt the key.")
+            }
+            UserState::LskfLocked => {
+                Err(Error::Rc(ResponseCode::LOCKED)).context("In super_encrypt. Device is locked.")
+            }
+            UserState::Uninitialized => Err(Error::Rc(ResponseCode::UNINITIALIZED))
+                .context("In super_encrypt. LSKF is not setup for the user."),
+        }
+    }
+
+    //Helper function to encrypt a key with the given super key. Callers should select which super
+    //key to be used. This is called when a key is super encrypted at its creation as well as at its
+    //upgrade.
+    fn encrypt_with_aes_super_key(
+        key_blob: &[u8],
+        super_key: &SuperKey,
+    ) -> Result<(Vec<u8>, BlobMetaData)> {
+        if super_key.algorithm != SuperEncryptionAlgorithm::Aes256Gcm {
+            return Err(Error::sys())
+                .context("In encrypt_with_aes_super_key: unexpected algorithm");
+        }
+        let mut metadata = BlobMetaData::new();
+        let (encrypted_key, iv, tag) = aes_gcm_encrypt(key_blob, &(super_key.key))
+            .context("In encrypt_with_aes_super_key: Failed to encrypt new super key.")?;
+        metadata.add(BlobMetaEntry::Iv(iv));
+        metadata.add(BlobMetaEntry::AeadTag(tag));
+        super_key.id.add_to_metadata(&mut metadata);
+        Ok((encrypted_key, metadata))
+    }
+
+    /// Check if super encryption is required and if so, super-encrypt the key to be stored in
+    /// the database.
+    #[allow(clippy::too_many_arguments)]
+    pub fn handle_super_encryption_on_key_init(
+        &self,
+        db: &mut KeystoreDB,
+        legacy_migrator: &LegacyMigrator,
+        domain: &Domain,
+        key_parameters: &[KeyParameter],
+        flags: Option<i32>,
+        user_id: UserId,
+        key_blob: &[u8],
+    ) -> Result<(Vec<u8>, BlobMetaData)> {
+        match Enforcements::super_encryption_required(domain, key_parameters, flags) {
+            SuperEncryptionType::None => Ok((key_blob.to_vec(), BlobMetaData::new())),
+            SuperEncryptionType::LskfBound => self
+                .super_encrypt_on_key_init(db, legacy_migrator, user_id, &key_blob)
+                .context(concat!(
+                    "In handle_super_encryption_on_key_init. ",
+                    "Failed to super encrypt with LskfBound key."
+                )),
+            SuperEncryptionType::ScreenLockBound => {
+                let mut data = self.data.lock().unwrap();
+                let entry = data.user_keys.entry(user_id).or_default();
+                if let Some(super_key) = entry.screen_lock_bound.as_ref() {
+                    Self::encrypt_with_aes_super_key(key_blob, &super_key).context(concat!(
+                        "In handle_super_encryption_on_key_init. ",
+                        "Failed to encrypt with ScreenLockBound key."
+                    ))
+                } else {
+                    // Symmetric key is not available, use public key encryption
+                    let loaded =
+                        db.load_super_key(&USER_SCREEN_LOCK_BOUND_ECDH_KEY, user_id).context(
+                            "In handle_super_encryption_on_key_init: load_super_key failed.",
+                        )?;
+                    let (key_id_guard, key_entry) = loaded.ok_or_else(Error::sys).context(
+                        "In handle_super_encryption_on_key_init: User ECDH key missing.",
+                    )?;
+                    let public_key =
+                        key_entry.metadata().sec1_public_key().ok_or_else(Error::sys).context(
+                            "In handle_super_encryption_on_key_init: sec1_public_key missing.",
+                        )?;
+                    let mut metadata = BlobMetaData::new();
+                    let (ephem_key, salt, iv, encrypted_key, aead_tag) =
+                        ECDHPrivateKey::encrypt_message(public_key, key_blob).context(concat!(
+                            "In handle_super_encryption_on_key_init: ",
+                            "ECDHPrivateKey::encrypt_message failed."
+                        ))?;
+                    metadata.add(BlobMetaEntry::PublicKey(ephem_key));
+                    metadata.add(BlobMetaEntry::Salt(salt));
+                    metadata.add(BlobMetaEntry::Iv(iv));
+                    metadata.add(BlobMetaEntry::AeadTag(aead_tag));
+                    SuperKeyIdentifier::DatabaseId(key_id_guard.id())
+                        .add_to_metadata(&mut metadata);
+                    Ok((encrypted_key, metadata))
+                }
+            }
+            SuperEncryptionType::BootLevel(level) => {
+                let key_id = SuperKeyIdentifier::BootLevel(level);
+                let super_key = self
+                    .lookup_key(&key_id)
+                    .context("In handle_super_encryption_on_key_init: lookup_key failed")?
+                    .ok_or(Error::Rc(ResponseCode::LOCKED))
+                    .context("In handle_super_encryption_on_key_init: Boot stage key absent")?;
+                Self::encrypt_with_aes_super_key(key_blob, &super_key).context(concat!(
+                    "In handle_super_encryption_on_key_init: ",
+                    "Failed to encrypt with BootLevel key."
+                ))
+            }
+        }
+    }
+
+    /// Check if a given key needs re-super-encryption, from its KeyBlob type.
+    /// If so, re-super-encrypt the key and return a new set of metadata,
+    /// containing the new super encryption information.
+    pub fn reencrypt_if_required<'a>(
+        key_blob_before_upgrade: &KeyBlob,
+        key_after_upgrade: &'a [u8],
+    ) -> Result<(KeyBlob<'a>, Option<BlobMetaData>)> {
+        match key_blob_before_upgrade {
+            KeyBlob::Sensitive { reencrypt_with: super_key, .. } => {
+                let (key, metadata) =
+                    Self::encrypt_with_aes_super_key(key_after_upgrade, super_key)
+                        .context("In reencrypt_if_required: Failed to re-super-encrypt key.")?;
+                Ok((KeyBlob::NonSensitive(key), Some(metadata)))
+            }
+            _ => Ok((KeyBlob::Ref(key_after_upgrade), None)),
+        }
+    }
+
+    /// Fetch a superencryption key from the database, or create it if it doesn't already exist.
+    /// When this is called, the caller must hold the lock on the SuperKeyManager.
+    /// So it's OK that the check and creation are different DB transactions.
+    fn get_or_create_super_key(
+        db: &mut KeystoreDB,
+        user_id: UserId,
+        key_type: &SuperKeyType,
+        password: &Password,
+        reencrypt_with: Option<Arc<SuperKey>>,
+    ) -> Result<Arc<SuperKey>> {
+        let loaded_key = db.load_super_key(key_type, user_id)?;
+        if let Some((_, key_entry)) = loaded_key {
+            Ok(Self::extract_super_key_from_key_entry(
+                key_type.algorithm,
+                key_entry,
+                password,
+                reencrypt_with,
+            )?)
+        } else {
+            let (super_key, public_key) = match key_type.algorithm {
+                SuperEncryptionAlgorithm::Aes256Gcm => (
+                    generate_aes256_key()
+                        .context("In get_or_create_super_key: Failed to generate AES 256 key.")?,
+                    None,
+                ),
+                SuperEncryptionAlgorithm::EcdhP256 => {
+                    let key = ECDHPrivateKey::generate()
+                        .context("In get_or_create_super_key: Failed to generate ECDH key")?;
+                    (
+                        key.private_key()
+                            .context("In get_or_create_super_key: private_key failed")?,
+                        Some(
+                            key.public_key()
+                                .context("In get_or_create_super_key: public_key failed")?,
+                        ),
+                    )
+                }
+            };
+            //derive an AES256 key from the password and re-encrypt the super key
+            //before we insert it in the database.
+            let (encrypted_super_key, blob_metadata) =
+                Self::encrypt_with_password(&super_key, password)
+                    .context("In get_or_create_super_key.")?;
+            let mut key_metadata = KeyMetaData::new();
+            if let Some(pk) = public_key {
+                key_metadata.add(KeyMetaEntry::Sec1PublicKey(pk));
+            }
+            let key_entry = db
+                .store_super_key(
+                    user_id,
+                    key_type,
+                    &encrypted_super_key,
+                    &blob_metadata,
+                    &key_metadata,
+                )
+                .context("In get_or_create_super_key. Failed to store super key.")?;
+            Ok(Arc::new(SuperKey {
+                algorithm: key_type.algorithm,
+                key: super_key,
+                id: SuperKeyIdentifier::DatabaseId(key_entry.id()),
+                reencrypt_with,
+            }))
+        }
+    }
+
+    /// Decrypt the screen-lock bound keys for this user using the password and store in memory.
+    pub fn unlock_screen_lock_bound_key(
+        &self,
+        db: &mut KeystoreDB,
+        user_id: UserId,
+        password: &Password,
+    ) -> Result<()> {
+        let mut data = self.data.lock().unwrap();
+        let entry = data.user_keys.entry(user_id).or_default();
+        let aes = entry
+            .screen_lock_bound
+            .get_or_try_to_insert_with(|| {
+                Self::get_or_create_super_key(
+                    db,
+                    user_id,
+                    &USER_SCREEN_LOCK_BOUND_KEY,
+                    password,
+                    None,
+                )
+            })?
+            .clone();
+        let ecdh = entry
+            .screen_lock_bound_private
+            .get_or_try_to_insert_with(|| {
+                Self::get_or_create_super_key(
+                    db,
+                    user_id,
+                    &USER_SCREEN_LOCK_BOUND_ECDH_KEY,
+                    password,
+                    Some(aes.clone()),
+                )
+            })?
+            .clone();
+        data.add_key_to_key_index(&aes)?;
+        data.add_key_to_key_index(&ecdh)?;
+        Ok(())
+    }
+
+    /// Wipe the screen-lock bound keys for this user from memory.
+    pub fn lock_screen_lock_bound_key(
+        &self,
+        db: &mut KeystoreDB,
+        user_id: UserId,
+        unlocking_sids: &[i64],
+    ) {
+        log::info!("Locking screen bound for user {} sids {:?}", user_id, unlocking_sids);
+        let mut data = self.data.lock().unwrap();
+        let mut entry = data.user_keys.entry(user_id).or_default();
+        if !unlocking_sids.is_empty() {
+            if let (Some(aes), Some(ecdh)) = (
+                entry.screen_lock_bound.as_ref().cloned(),
+                entry.screen_lock_bound_private.as_ref().cloned(),
+            ) {
+                let res = (|| -> Result<()> {
+                    let key_desc = KeyMintDevice::internal_descriptor(format!(
+                        "biometric_unlock_key_{}",
+                        user_id
+                    ));
+                    let encrypting_key = generate_aes256_key()?;
+                    let km_dev: KeyMintDevice =
+                        KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
+                            .context("In lock_screen_lock_bound_key: KeyMintDevice::get failed")?;
+                    let mut key_params = vec![
+                        KeyParameterValue::Algorithm(Algorithm::AES),
+                        KeyParameterValue::KeySize(256),
+                        KeyParameterValue::BlockMode(BlockMode::GCM),
+                        KeyParameterValue::PaddingMode(PaddingMode::NONE),
+                        KeyParameterValue::CallerNonce,
+                        KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT),
+                        KeyParameterValue::MinMacLength(128),
+                        KeyParameterValue::AuthTimeout(BIOMETRIC_AUTH_TIMEOUT_S),
+                        KeyParameterValue::HardwareAuthenticatorType(
+                            HardwareAuthenticatorType::FINGERPRINT,
+                        ),
+                    ];
+                    for sid in unlocking_sids {
+                        key_params.push(KeyParameterValue::UserSecureID(*sid));
+                    }
+                    let key_params: Vec<KmKeyParameter> =
+                        key_params.into_iter().map(|x| x.into()).collect();
+                    km_dev.create_and_store_key(db, &key_desc, |dev| {
+                        let _wp = wd::watch_millis(
+                            "In lock_screen_lock_bound_key: calling importKey.",
+                            500,
+                        );
+                        dev.importKey(key_params.as_slice(), KeyFormat::RAW, &encrypting_key, None)
+                    })?;
+                    entry.biometric_unlock = Some(BiometricUnlock {
+                        sids: unlocking_sids.into(),
+                        key_desc,
+                        screen_lock_bound: LockedKey::new(&encrypting_key, &aes)?,
+                        screen_lock_bound_private: LockedKey::new(&encrypting_key, &ecdh)?,
+                    });
+                    Ok(())
+                })();
+                // There is no reason to propagate an error here upwards. We must discard
+                // entry.screen_lock_bound* in any case.
+                if let Err(e) = res {
+                    log::error!("Error setting up biometric unlock: {:#?}", e);
+                }
+            }
+        }
+        entry.screen_lock_bound = None;
+        entry.screen_lock_bound_private = None;
+    }
+
+    /// User has unlocked, not using a password. See if any of our stored auth tokens can be used
+    /// to unlock the keys protecting UNLOCKED_DEVICE_REQUIRED keys.
+    pub fn try_unlock_user_with_biometric(
+        &self,
+        db: &mut KeystoreDB,
+        user_id: UserId,
+    ) -> Result<()> {
+        let mut data = self.data.lock().unwrap();
+        let mut entry = data.user_keys.entry(user_id).or_default();
+        if let Some(biometric) = entry.biometric_unlock.as_ref() {
+            let (key_id_guard, key_entry) =
+                KeyMintDevice::lookup_from_desc(db, &biometric.key_desc)?;
+            let km_dev: KeyMintDevice = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
+                .context("In try_unlock_user_with_biometric: KeyMintDevice::get failed")?;
+            for sid in &biometric.sids {
+                if let Some((auth_token_entry, _)) = db.find_auth_token_entry(|entry| {
+                    entry.auth_token().userId == *sid || entry.auth_token().authenticatorId == *sid
+                })? {
+                    let res: Result<(Arc<SuperKey>, Arc<SuperKey>)> = (|| {
+                        let slb = biometric.screen_lock_bound.decrypt(
+                            db,
+                            &km_dev,
+                            &key_id_guard,
+                            &key_entry,
+                            auth_token_entry.auth_token(),
+                            None,
+                        )?;
+                        let slbp = biometric.screen_lock_bound_private.decrypt(
+                            db,
+                            &km_dev,
+                            &key_id_guard,
+                            &key_entry,
+                            auth_token_entry.auth_token(),
+                            Some(slb.clone()),
+                        )?;
+                        Ok((slb, slbp))
+                    })();
+                    match res {
+                        Ok((slb, slbp)) => {
+                            entry.screen_lock_bound = Some(slb.clone());
+                            entry.screen_lock_bound_private = Some(slbp.clone());
+                            data.add_key_to_key_index(&slb)?;
+                            data.add_key_to_key_index(&slbp)?;
+                            log::info!(concat!(
+                                "In try_unlock_user_with_biometric: ",
+                                "Successfully unlocked with biometric"
+                            ));
+                            return Ok(());
+                        }
+                        Err(e) => {
+                            log::warn!("In try_unlock_user_with_biometric: attempt failed: {:?}", e)
+                        }
+                    }
+                }
+            }
+        }
+        Ok(())
+    }
+}
+
+/// This enum represents different states of the user's life cycle in the device.
+/// For now, only three states are defined. More states may be added later.
+pub enum UserState {
+    // The user has registered LSKF and has unlocked the device by entering PIN/Password,
+    // and hence the per-boot super key is available in the cache.
+    LskfUnlocked(Arc<SuperKey>),
+    // The user has registered LSKF, but has not unlocked the device using password, after reboot.
+    // Hence the per-boot super-key(s) is not available in the cache.
+    // However, the encrypted super key is available in the database.
+    LskfLocked,
+    // There's no user in the device for the given user id, or the user with the user id has not
+    // setup LSKF.
+    Uninitialized,
+}
+
+impl UserState {
+    pub fn get(
+        db: &mut KeystoreDB,
+        legacy_migrator: &LegacyMigrator,
+        skm: &SuperKeyManager,
+        user_id: UserId,
+    ) -> Result<UserState> {
+        match skm.get_per_boot_key_by_user_id(user_id) {
+            Some(super_key) => Ok(UserState::LskfUnlocked(super_key)),
+            None => {
+                //Check if a super key exists in the database or legacy database.
+                //If so, return locked user state.
+                if SuperKeyManager::super_key_exists_in_db_for_user(db, legacy_migrator, user_id)
+                    .context("In get.")?
+                {
+                    Ok(UserState::LskfLocked)
+                } else {
+                    Ok(UserState::Uninitialized)
+                }
+            }
+        }
+    }
+
+    /// Queries user state when serving password change requests.
+    pub fn get_with_password_changed(
+        db: &mut KeystoreDB,
+        legacy_migrator: &LegacyMigrator,
+        skm: &SuperKeyManager,
+        user_id: UserId,
+        password: Option<&Password>,
+    ) -> Result<UserState> {
+        match skm.get_per_boot_key_by_user_id(user_id) {
+            Some(super_key) => {
+                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, 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 {
+                    //Keystore won't be notified when changing to a new password when LSKF is
+                    //already setup. Therefore, ideally this path wouldn't be reached.
+                    Ok(UserState::LskfUnlocked(super_key))
+                }
+            }
+            None => {
+                //Check if a super key exists in the database or legacy database.
+                //If so, return LskfLocked state.
+                //Otherwise, i) if the password is provided, initialize the super key and return
+                //LskfUnlocked state ii) if password is not provided, return Uninitialized state.
+                skm.check_and_initialize_super_key(db, legacy_migrator, user_id, password)
+            }
+        }
+    }
+
+    /// Queries user state when serving password unlock requests.
+    pub fn get_with_password_unlock(
+        db: &mut KeystoreDB,
+        legacy_migrator: &LegacyMigrator,
+        skm: &SuperKeyManager,
+        user_id: UserId,
+        password: &Password,
+    ) -> Result<UserState> {
+        match skm.get_per_boot_key_by_user_id(user_id) {
+            Some(super_key) => {
+                log::info!("In get_with_password_unlock. Trying to unlock when already unlocked.");
+                Ok(UserState::LskfUnlocked(super_key))
+            }
+            None => {
+                //Check if a super key exists in the database or legacy database.
+                //If not, return Uninitialized state.
+                //Otherwise, try to unlock the super key and if successful,
+                //return LskfUnlocked state
+                skm.check_and_unlock_super_key(db, legacy_migrator, user_id, password)
+                    .context("In get_with_password_unlock. Failed to unlock super key.")
+            }
+        }
+    }
+
+    /// Delete all the keys created on behalf of the user.
+    /// If 'keep_non_super_encrypted_keys' is set to true, delete only the super key and super
+    /// encrypted keys.
+    pub fn reset_user(
+        db: &mut KeystoreDB,
+        skm: &SuperKeyManager,
+        legacy_migrator: &LegacyMigrator,
+        user_id: UserId,
+        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, keep_non_super_encrypted_keys)
+            .context("In reset user. Error in unbinding keys.")?;
+
+        //delete super key in cache, if exists
+        skm.forget_all_keys_for_user(user_id);
+        Ok(())
+    }
+}
+
+/// 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 {
+        key: ZVec,
+        /// If KeyMint reports that the key must be upgraded, we must
+        /// re-encrypt the key before writing to the database; we use
+        /// this key.
+        reencrypt_with: Arc<SuperKey>,
+        /// If this key was decrypted with an ECDH key, we want to
+        /// re-encrypt it on first use whether it was upgraded or not;
+        /// this field indicates that that's necessary.
+        force_reencrypt: bool,
+    },
+    NonSensitive(Vec<u8>),
+    Ref(&'a [u8]),
+}
+
+impl<'a> KeyBlob<'a> {
+    pub fn force_reencrypt(&self) -> bool {
+        if let KeyBlob::Sensitive { force_reencrypt, .. } = self {
+            *force_reencrypt
+        } else {
+            false
+        }
+    }
+}
+
+/// Deref returns a reference to the key material in any variant.
+impl<'a> Deref for KeyBlob<'a> {
+    type Target = [u8];
+
+    fn deref(&self) -> &Self::Target {
+        match self {
+            Self::Sensitive { key, .. } => &key,
+            Self::NonSensitive(key) => &key,
+            Self::Ref(key) => key,
+        }
+    }
+}
diff --git a/keystore2/src/try_insert.rs b/keystore2/src/try_insert.rs
new file mode 100644
index 0000000..6dd3962
--- /dev/null
+++ b/keystore2/src/try_insert.rs
@@ -0,0 +1,100 @@
+// 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.
+
+//! The TryInsert trait adds to Option<T> the method
+//! get_or_try_to_insert_with, which is analogous to
+//! get_or_insert_with, but allows the called function to fail and propagates the failure.
+
+/// The TryInsert trait adds to Option<T> the method
+/// get_or_try_to_insert_with, which is analogous to
+/// get_or_insert_with, but allows the called function to fail and propagates the failure.
+pub trait TryInsert {
+    /// Type of the Ok branch of the Result
+    type Item;
+    /// Inserts a value computed from `f` into the option if it is [`None`],
+    /// then returns a mutable reference to the contained value. If `f`
+    /// returns Err, the Option is unchanged.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// let mut x = None;
+    /// assert_eq!(x.get_or_try_to_insert_with(Err("oops".to_string())), Err("oops".to_string()))
+    /// {
+    ///     let y: &mut u32 = x.get_or_try_to_insert_with(|| Ok(5))?;
+    ///     assert_eq!(y, &5);
+    ///
+    ///     *y = 7;
+    /// }
+    ///
+    /// assert_eq!(x, Some(7));
+    /// ```
+    fn get_or_try_to_insert_with<E, F: FnOnce() -> Result<Self::Item, E>>(
+        &mut self,
+        f: F,
+    ) -> Result<&mut Self::Item, E>;
+}
+
+impl<T> TryInsert for Option<T> {
+    type Item = T;
+    fn get_or_try_to_insert_with<E, F: FnOnce() -> Result<Self::Item, E>>(
+        &mut self,
+        f: F,
+    ) -> Result<&mut Self::Item, E> {
+        if self.is_none() {
+            *self = Some(f()?);
+        }
+
+        match self {
+            Some(v) => Ok(v),
+            // SAFETY: a `None` variant for `self` would have been replaced by a `Some`
+            // variant in the code above.
+            None => unsafe { std::hint::unreachable_unchecked() },
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    fn fails() -> Result<i32, String> {
+        Err("fail".to_string())
+    }
+
+    fn succeeds() -> Result<i32, String> {
+        Ok(99)
+    }
+
+    #[test]
+    fn test() {
+        let mut x = None;
+        assert_eq!(x.get_or_try_to_insert_with(fails), Err("fail".to_string()));
+        assert_eq!(x, None);
+        assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 99);
+        assert_eq!(x, Some(99));
+        x = Some(42);
+        assert_eq!(*x.get_or_try_to_insert_with(fails).unwrap(), 42);
+        assert_eq!(x, Some(42));
+        assert_eq!(*x.get_or_try_to_insert_with(succeeds).unwrap(), 42);
+        assert_eq!(x, Some(42));
+        *x.get_or_try_to_insert_with(fails).unwrap() = 2;
+        assert_eq!(x, Some(2));
+        *x.get_or_try_to_insert_with(succeeds).unwrap() = 3;
+        assert_eq!(x, Some(3));
+        x = None;
+        *x.get_or_try_to_insert_with(succeeds).unwrap() = 5;
+        assert_eq!(x, Some(5));
+    }
+}
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
new file mode 100644
index 0000000..9852aad
--- /dev/null
+++ b/keystore2/src/utils.rs
@@ -0,0 +1,307 @@
+// 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.
+
+//! This module implements utility functions used by the Keystore 2.0 service
+//! implementation.
+
+use crate::error::{map_binder_status, Error, ErrorCode};
+use crate::permission;
+use crate::permission::{KeyPerm, KeyPermSet, KeystorePerm};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+    KeyCharacteristics::KeyCharacteristics, Tag::Tag,
+};
+use android_os_permissions_aidl::aidl::android::os::IPermissionController;
+use android_security_apc::aidl::android::security::apc::{
+    IProtectedConfirmation::{FLAG_UI_OPTION_INVERTED, FLAG_UI_OPTION_MAGNIFIED},
+    ResponseCode::ResponseCode as ApcResponseCode,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+    Authorization::Authorization, KeyDescriptor::KeyDescriptor,
+};
+use anyhow::{anyhow, Context};
+use binder::{FromIBinder, SpIBinder, ThreadState};
+use keystore2_apc_compat::{
+    ApcCompatUiOptions, APC_COMPAT_ERROR_ABORTED, APC_COMPAT_ERROR_CANCELLED,
+    APC_COMPAT_ERROR_IGNORED, APC_COMPAT_ERROR_OK, APC_COMPAT_ERROR_OPERATION_PENDING,
+    APC_COMPAT_ERROR_SYSTEM_ERROR,
+};
+use std::convert::TryFrom;
+use std::sync::Mutex;
+
+/// This function uses its namesake in the permission module and in
+/// combination with with_calling_sid from the binder crate to check
+/// if the caller has the given keystore permission.
+pub fn check_keystore_permission(perm: KeystorePerm) -> anyhow::Result<()> {
+    ThreadState::with_calling_sid(|calling_sid| {
+        permission::check_keystore_permission(
+            &calling_sid.ok_or_else(Error::sys).context(
+                "In check_keystore_permission: Cannot check permission without calling_sid.",
+            )?,
+            perm,
+        )
+    })
+}
+
+/// This function uses its namesake in the permission module and in
+/// combination with with_calling_sid from the binder crate to check
+/// if the caller has the given grant permission.
+pub fn check_grant_permission(access_vec: KeyPermSet, key: &KeyDescriptor) -> anyhow::Result<()> {
+    ThreadState::with_calling_sid(|calling_sid| {
+        permission::check_grant_permission(
+            &calling_sid.ok_or_else(Error::sys).context(
+                "In check_grant_permission: Cannot check permission without calling_sid.",
+            )?,
+            access_vec,
+            key,
+        )
+    })
+}
+
+/// This function uses its namesake in the permission module and in
+/// combination with with_calling_sid from the binder crate to check
+/// if the caller has the given key permission.
+pub fn check_key_permission(
+    perm: KeyPerm,
+    key: &KeyDescriptor,
+    access_vector: &Option<KeyPermSet>,
+) -> anyhow::Result<()> {
+    ThreadState::with_calling_sid(|calling_sid| {
+        permission::check_key_permission(
+            ThreadState::get_calling_uid(),
+            &calling_sid
+                .ok_or_else(Error::sys)
+                .context("In check_key_permission: Cannot check permission without calling_sid.")?,
+            perm,
+            key,
+            access_vector,
+        )
+    })
+}
+
+/// This function checks whether a given tag corresponds to the access of device identifiers.
+pub fn is_device_id_attestation_tag(tag: Tag) -> bool {
+    matches!(
+        tag,
+        Tag::ATTESTATION_ID_IMEI
+            | Tag::ATTESTATION_ID_MEID
+            | Tag::ATTESTATION_ID_SERIAL
+            | Tag::DEVICE_UNIQUE_ATTESTATION
+    )
+}
+
+/// This function checks whether the calling app has the Android permissions needed to attest device
+/// identifiers. It throws an error if the permissions cannot be verified, or if the caller doesn't
+/// have the right permissions, and returns silently otherwise.
+pub fn check_device_attestation_permissions() -> anyhow::Result<()> {
+    let permission_controller: binder::Strong<dyn IPermissionController::IPermissionController> =
+        binder::get_interface("permission")?;
+
+    let binder_result = {
+        let _wp = watchdog::watch_millis(
+            "In check_device_attestation_permissions: calling checkPermission.",
+            500,
+        );
+        permission_controller.checkPermission(
+            "android.permission.READ_PRIVILEGED_PHONE_STATE",
+            ThreadState::get_calling_pid(),
+            ThreadState::get_calling_uid() as i32,
+        )
+    };
+    let has_permissions = map_binder_status(binder_result)
+        .context("In check_device_attestation_permissions: checkPermission failed")?;
+    match has_permissions {
+        true => Ok(()),
+        false => Err(Error::Km(ErrorCode::CANNOT_ATTEST_IDS)).context(concat!(
+            "In check_device_attestation_permissions: ",
+            "caller does not have the permission to attest device IDs"
+        )),
+    }
+}
+
+/// Thread safe wrapper around SpIBinder. It is safe to have SpIBinder smart pointers to the
+/// same object in multiple threads, but cloning a SpIBinder is not thread safe.
+/// Keystore frequently hands out binder tokens to the security level interface. If this
+/// is to happen from a multi threaded thread pool, the SpIBinder needs to be protected by a
+/// Mutex.
+#[derive(Debug)]
+pub struct Asp(Mutex<SpIBinder>);
+
+impl Asp {
+    /// Creates a new instance owning a SpIBinder wrapped in a Mutex.
+    pub fn new(i: SpIBinder) -> Self {
+        Self(Mutex::new(i))
+    }
+
+    /// Clones the owned SpIBinder and attempts to convert it into the requested interface.
+    pub fn get_interface<T: FromIBinder + ?Sized>(&self) -> anyhow::Result<binder::Strong<T>> {
+        // We can use unwrap here because we never panic when locked, so the mutex
+        // can never be poisoned.
+        let lock = self.0.lock().unwrap();
+        (*lock)
+            .clone()
+            .into_interface()
+            .map_err(|e| anyhow!(format!("get_interface failed with error code {:?}", e)))
+    }
+}
+
+impl Clone for Asp {
+    fn clone(&self) -> Self {
+        let lock = self.0.lock().unwrap();
+        Self(Mutex::new((*lock).clone()))
+    }
+}
+
+/// Converts a set of key characteristics as returned from KeyMint into the internal
+/// representation of the keystore service.
+pub fn key_characteristics_to_internal(
+    key_characteristics: Vec<KeyCharacteristics>,
+) -> Vec<crate::key_parameter::KeyParameter> {
+    key_characteristics
+        .into_iter()
+        .flat_map(|aidl_key_char| {
+            let sec_level = aidl_key_char.securityLevel;
+            aidl_key_char.authorizations.into_iter().map(move |aidl_kp| {
+                crate::key_parameter::KeyParameter::new(aidl_kp.into(), sec_level)
+            })
+        })
+        .collect()
+}
+
+/// Converts a set of key characteristics from the internal representation into a set of
+/// Authorizations as they are used to convey key characteristics to the clients of keystore.
+pub fn key_parameters_to_authorizations(
+    parameters: Vec<crate::key_parameter::KeyParameter>,
+) -> Vec<Authorization> {
+    parameters.into_iter().map(|p| p.into_authorization()).collect()
+}
+
+/// This returns the current time (in seconds) as an instance of a monotonic clock, by invoking the
+/// system call since Rust does not support getting monotonic time instance as an integer.
+pub fn get_current_time_in_seconds() -> i64 {
+    let mut current_time = libc::timespec { tv_sec: 0, tv_nsec: 0 };
+    // Following unsafe block includes one system call to get monotonic time.
+    // Therefore, it is not considered harmful.
+    unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_RAW, &mut current_time) };
+    // It is safe to unwrap here because try_from() returns std::convert::Infallible, which is
+    // defined to be an error that can never happen (i.e. the result is always ok).
+    // This suppresses the compiler's complaint about converting tv_sec to i64 in method
+    // get_current_time_in_seconds.
+    #[allow(clippy::useless_conversion)]
+    i64::try_from(current_time.tv_sec).unwrap()
+}
+
+/// Converts a response code as returned by the Android Protected Confirmation HIDL compatibility
+/// module (keystore2_apc_compat) into a ResponseCode as defined by the APC AIDL
+/// (android.security.apc) spec.
+pub fn compat_2_response_code(rc: u32) -> ApcResponseCode {
+    match rc {
+        APC_COMPAT_ERROR_OK => ApcResponseCode::OK,
+        APC_COMPAT_ERROR_CANCELLED => ApcResponseCode::CANCELLED,
+        APC_COMPAT_ERROR_ABORTED => ApcResponseCode::ABORTED,
+        APC_COMPAT_ERROR_OPERATION_PENDING => ApcResponseCode::OPERATION_PENDING,
+        APC_COMPAT_ERROR_IGNORED => ApcResponseCode::IGNORED,
+        APC_COMPAT_ERROR_SYSTEM_ERROR => ApcResponseCode::SYSTEM_ERROR,
+        _ => ApcResponseCode::SYSTEM_ERROR,
+    }
+}
+
+/// Converts the UI Options flags as defined by the APC AIDL (android.security.apc) spec into
+/// UI Options flags as defined by the Android Protected Confirmation HIDL compatibility
+/// module (keystore2_apc_compat).
+pub fn ui_opts_2_compat(opt: i32) -> ApcCompatUiOptions {
+    ApcCompatUiOptions {
+        inverted: (opt & FLAG_UI_OPTION_INVERTED) != 0,
+        magnified: (opt & FLAG_UI_OPTION_MAGNIFIED) != 0,
+    }
+}
+
+/// AID offset for uid space partitioning.
+pub const AID_USER_OFFSET: u32 = cutils_bindgen::AID_USER_OFFSET;
+
+/// AID of the keystore process itself, used for keys that
+/// keystore generates for its own use.
+pub const AID_KEYSTORE: u32 = cutils_bindgen::AID_KEYSTORE;
+
+/// Extracts the android user from the given uid.
+pub fn uid_to_android_user(uid: u32) -> u32 {
+    // Safety: No memory access
+    unsafe { cutils_bindgen::multiuser_get_user_id(uid) }
+}
+
+/// This module provides helpers for simplified use of the watchdog module.
+#[cfg(feature = "watchdog")]
+pub mod watchdog {
+    pub use crate::watchdog::WatchPoint;
+    use crate::watchdog::Watchdog;
+    use lazy_static::lazy_static;
+    use std::sync::Arc;
+    use std::time::Duration;
+
+    lazy_static! {
+        /// A Watchdog thread, that can be used to create watch points.
+        static ref WD: Arc<Watchdog> = Watchdog::new(Duration::from_secs(10));
+    }
+
+    /// Sets a watch point with `id` and a timeout of `millis` milliseconds.
+    pub fn watch_millis(id: &'static str, millis: u64) -> Option<WatchPoint> {
+        Watchdog::watch(&WD, id, Duration::from_millis(millis))
+    }
+
+    /// Like `watch_millis` but with a callback that is called every time a report
+    /// is printed about this watch point.
+    pub fn watch_millis_with(
+        id: &'static str,
+        millis: u64,
+        callback: impl Fn() -> String + Send + 'static,
+    ) -> Option<WatchPoint> {
+        Watchdog::watch_with(&WD, id, Duration::from_millis(millis), callback)
+    }
+}
+
+/// This module provides empty/noop implementations of the watch dog utility functions.
+#[cfg(not(feature = "watchdog"))]
+pub mod watchdog {
+    /// Noop watch point.
+    pub struct WatchPoint();
+    /// Sets a Noop watch point.
+    fn watch_millis(_: &'static str, _: u64) -> Option<WatchPoint> {
+        None
+    }
+
+    pub fn watch_millis_with(
+        _: &'static str,
+        _: u64,
+        _: impl Fn() -> String + Send + 'static,
+    ) -> Option<WatchPoint> {
+        None
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use anyhow::Result;
+
+    #[test]
+    fn check_device_attestation_permissions_test() -> Result<()> {
+        check_device_attestation_permissions().or_else(|error| {
+            match error.root_cause().downcast_ref::<Error>() {
+                // Expected: the context for this test might not be allowed to attest device IDs.
+                Some(Error::Km(ErrorCode::CANNOT_ATTEST_IDS)) => Ok(()),
+                // Other errors are unexpected
+                _ => Err(error),
+            }
+        })
+    }
+}
diff --git a/keystore2/src/vintf/Android.bp b/keystore2/src/vintf/Android.bp
new file mode 100644
index 0000000..3ab0ec5
--- /dev/null
+++ b/keystore2/src/vintf/Android.bp
@@ -0,0 +1,80 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+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",
+        "--allowlist-function", "getHalNames",
+        "--allowlist-function", "getHalNamesAndVersions",
+        "--allowlist-function", "getHidlInstances",
+        "--allowlist-function", "getAidlInstances",
+        "--allowlist-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..8730a3e
--- /dev/null
+++ b/keystore2/src/vintf/lib.rs
@@ -0,0 +1,127 @@
+// 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, getHidlInstances,
+};
+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_hidl_instances(
+    package: &str,
+    major_version: usize,
+    minor_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 {
+        getHidlInstances(
+            &mut len,
+            packages.as_ptr(),
+            major_version,
+            minor_version,
+            interface_name.as_ptr(),
+        )
+    };
+    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..e407efa
--- /dev/null
+++ b/keystore2/src/vintf/vintf.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "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** getHidlInstances(size_t* len, const char* package, size_t major_version,
+                        size_t minor_version, const char* interfaceName) {
+    android::vintf::Version version(major_version, minor_version);
+    auto manifest = android::vintf::VintfObject::GetDeviceHalManifest();
+    const auto names = manifest->getHidlInstances(package, version, interfaceName);
+    *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..091e8e8
--- /dev/null
+++ b/keystore2/src/vintf/vintf.hpp
@@ -0,0 +1,33 @@
+/*
+ * 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** getHidlInstances(size_t* len, const char* package, size_t major_version,
+                        size_t minor_version, const char* interfaceName);
+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/src/watchdog.rs b/keystore2/src/watchdog.rs
new file mode 100644
index 0000000..9cca171
--- /dev/null
+++ b/keystore2/src/watchdog.rs
@@ -0,0 +1,326 @@
+// 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.
+
+// Can be removed when instrumentations are added to keystore.
+#![allow(dead_code)]
+
+//! This module implements a watchdog thread.
+
+use std::{
+    cmp::min,
+    collections::HashMap,
+    sync::Arc,
+    sync::{Condvar, Mutex, MutexGuard},
+    thread,
+};
+use std::{
+    marker::PhantomData,
+    time::{Duration, Instant},
+};
+
+/// Represents a Watchdog record. It can be created with `Watchdog::watch` or
+/// `Watchdog::watch_with`. It disarms the record when dropped.
+pub struct WatchPoint {
+    id: &'static str,
+    wd: Arc<Watchdog>,
+    not_send: PhantomData<*mut ()>, // WatchPoint must not be Send.
+}
+
+impl Drop for WatchPoint {
+    fn drop(&mut self) {
+        self.wd.disarm(self.id)
+    }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+enum State {
+    NotRunning,
+    Running,
+}
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+struct Index {
+    tid: thread::ThreadId,
+    id: &'static str,
+}
+
+struct Record {
+    started: Instant,
+    deadline: Instant,
+    callback: Option<Box<dyn Fn() -> String + Send + 'static>>,
+}
+
+struct WatchdogState {
+    state: State,
+    thread: Option<thread::JoinHandle<()>>,
+    timeout: Duration,
+    records: HashMap<Index, Record>,
+    last_report: Instant,
+    has_overdue: bool,
+}
+
+impl WatchdogState {
+    fn update_overdue_and_find_next_timeout(&mut self) -> (bool, Option<Duration>) {
+        let now = Instant::now();
+        let mut next_timeout: Option<Duration> = None;
+        let mut has_overdue = false;
+        for (_, r) in self.records.iter() {
+            let timeout = r.deadline.saturating_duration_since(now);
+            if timeout == Duration::new(0, 0) {
+                has_overdue = true;
+                continue;
+            }
+            next_timeout = match next_timeout {
+                Some(nt) => {
+                    if timeout < nt {
+                        Some(timeout)
+                    } else {
+                        Some(nt)
+                    }
+                }
+                None => Some(timeout),
+            };
+        }
+        (has_overdue, next_timeout)
+    }
+
+    fn log_report(&mut self, has_overdue: bool) -> bool {
+        match (self.has_overdue, has_overdue) {
+            (true, true) => {
+                if self.last_report.elapsed() < Watchdog::NOISY_REPORT_TIMEOUT {
+                    self.has_overdue = false;
+                    return false;
+                }
+            }
+            (_, false) => {
+                self.has_overdue = false;
+                return false;
+            }
+            (false, true) => {}
+        }
+        self.last_report = Instant::now();
+        self.has_overdue = has_overdue;
+        log::warn!("Keystore Watchdog report:");
+        log::warn!("Overdue records:");
+        let now = Instant::now();
+        for (i, r) in self.records.iter() {
+            if r.deadline.saturating_duration_since(now) == Duration::new(0, 0) {
+                match &r.callback {
+                    Some(cb) => {
+                        log::warn!(
+                            "{:?} {} Pending: {:?} Overdue {:?}: {}",
+                            i.tid,
+                            i.id,
+                            r.started.elapsed(),
+                            r.deadline.elapsed(),
+                            (cb)()
+                        );
+                    }
+                    None => {
+                        log::warn!(
+                            "{:?} {} Pending: {:?} Overdue {:?}",
+                            i.tid,
+                            i.id,
+                            r.started.elapsed(),
+                            r.deadline.elapsed()
+                        );
+                    }
+                }
+            }
+        }
+        true
+    }
+
+    fn disarm(&mut self, index: Index) {
+        self.records.remove(&index);
+    }
+
+    fn arm(&mut self, index: Index, record: Record) {
+        if self.records.insert(index.clone(), record).is_some() {
+            log::warn!("Recursive watchdog record at \"{:?}\" replaces previous record.", index);
+        }
+    }
+}
+
+/// Watchdog spawns a thread that logs records of all overdue watch points when a deadline
+/// is missed and at least every second as long as overdue watch points exist.
+/// The thread terminates when idle for a given period of time.
+pub struct Watchdog {
+    state: Arc<(Condvar, Mutex<WatchdogState>)>,
+}
+
+impl Watchdog {
+    /// If we have overdue records, we want to be noisy about it and log a report
+    /// at least every `NOISY_REPORT_TIMEOUT` interval.
+    const NOISY_REPORT_TIMEOUT: Duration = Duration::from_secs(1);
+
+    /// Construct a [`Watchdog`]. When `timeout` has elapsed since the watchdog thread became
+    /// idle, i.e., there are no more active or overdue watch points, the watchdog thread
+    /// terminates.
+    pub fn new(timeout: Duration) -> Arc<Self> {
+        Arc::new(Self {
+            state: Arc::new((
+                Condvar::new(),
+                Mutex::new(WatchdogState {
+                    state: State::NotRunning,
+                    thread: None,
+                    timeout,
+                    records: HashMap::new(),
+                    last_report: Instant::now(),
+                    has_overdue: false,
+                }),
+            )),
+        })
+    }
+
+    fn watch_with_optional(
+        wd: &Arc<Self>,
+        callback: Option<Box<dyn Fn() -> String + Send + 'static>>,
+        id: &'static str,
+        timeout: Duration,
+    ) -> Option<WatchPoint> {
+        let deadline = Instant::now().checked_add(timeout);
+        if deadline.is_none() {
+            log::warn!("Deadline computation failed for WatchPoint \"{}\"", id);
+            log::warn!("WatchPoint not armed.");
+            return None;
+        }
+        wd.arm(callback, id, deadline.unwrap());
+        Some(WatchPoint { id, wd: wd.clone(), not_send: Default::default() })
+    }
+
+    /// Create a new watch point. If the WatchPoint is not dropped before the timeout
+    /// expires, a report is logged at least every second, which includes the id string
+    /// and whatever string the callback returns.
+    pub fn watch_with(
+        wd: &Arc<Self>,
+        id: &'static str,
+        timeout: Duration,
+        callback: impl Fn() -> String + Send + 'static,
+    ) -> Option<WatchPoint> {
+        Self::watch_with_optional(wd, Some(Box::new(callback)), id, timeout)
+    }
+
+    /// Like `watch_with`, but without a callback.
+    pub fn watch(wd: &Arc<Self>, id: &'static str, timeout: Duration) -> Option<WatchPoint> {
+        Self::watch_with_optional(wd, None, id, timeout)
+    }
+
+    fn arm(
+        &self,
+        callback: Option<Box<dyn Fn() -> String + Send + 'static>>,
+        id: &'static str,
+        deadline: Instant,
+    ) {
+        let tid = thread::current().id();
+        let index = Index { tid, id };
+        let record = Record { started: Instant::now(), deadline, callback };
+
+        let (ref condvar, ref state) = *self.state;
+
+        let mut state = state.lock().unwrap();
+        state.arm(index, record);
+
+        if state.state != State::Running {
+            self.spawn_thread(&mut state);
+        }
+        drop(state);
+        condvar.notify_all();
+    }
+
+    fn disarm(&self, id: &'static str) {
+        let tid = thread::current().id();
+        let index = Index { tid, id };
+        let (_, ref state) = *self.state;
+
+        let mut state = state.lock().unwrap();
+        state.disarm(index);
+        // There is no need to notify condvar. There is no action required for the
+        // watchdog thread before the next deadline.
+    }
+
+    fn spawn_thread(&self, state: &mut MutexGuard<WatchdogState>) {
+        if let Some(t) = state.thread.take() {
+            t.join().expect("Watchdog thread panicked.");
+        }
+
+        let cloned_state = self.state.clone();
+
+        state.thread = Some(thread::spawn(move || {
+            let (ref condvar, ref state) = *cloned_state;
+
+            let mut state = state.lock().unwrap();
+
+            loop {
+                let (has_overdue, next_timeout) = state.update_overdue_and_find_next_timeout();
+                state.log_report(has_overdue);
+                let (next_timeout, idle) = match (has_overdue, next_timeout) {
+                    (true, Some(next_timeout)) => {
+                        (min(next_timeout, Self::NOISY_REPORT_TIMEOUT), false)
+                    }
+                    (false, Some(next_timeout)) => (next_timeout, false),
+                    (true, None) => (Self::NOISY_REPORT_TIMEOUT, false),
+                    (false, None) => (state.timeout, true),
+                };
+
+                let (s, timeout) = condvar.wait_timeout(state, next_timeout).unwrap();
+                state = s;
+
+                if idle && timeout.timed_out() && state.records.is_empty() {
+                    state.state = State::NotRunning;
+                    break;
+                }
+            }
+            log::info!("Watchdog thread idle -> terminating. Have a great day.");
+        }));
+        state.state = State::Running;
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+    use std::sync::atomic;
+    use std::thread;
+    use std::time::Duration;
+
+    #[test]
+    fn test_watchdog() {
+        android_logger::init_once(
+            android_logger::Config::default()
+                .with_tag("keystore2_watchdog_tests")
+                .with_min_level(log::Level::Debug),
+        );
+
+        let wd = Watchdog::new(Watchdog::NOISY_REPORT_TIMEOUT.checked_mul(3).unwrap());
+        let hit_count = Arc::new(atomic::AtomicU8::new(0));
+        let hit_count_clone = hit_count.clone();
+        let wp =
+            Watchdog::watch_with(&wd, "test_watchdog", Duration::from_millis(100), move || {
+                format!("hit_count: {}", hit_count_clone.fetch_add(1, atomic::Ordering::Relaxed))
+            });
+        assert_eq!(0, hit_count.load(atomic::Ordering::Relaxed));
+        thread::sleep(Duration::from_millis(500));
+        assert_eq!(1, hit_count.load(atomic::Ordering::Relaxed));
+        thread::sleep(Watchdog::NOISY_REPORT_TIMEOUT);
+        assert_eq!(2, hit_count.load(atomic::Ordering::Relaxed));
+        drop(wp);
+        thread::sleep(Watchdog::NOISY_REPORT_TIMEOUT.checked_mul(4).unwrap());
+        assert_eq!(2, hit_count.load(atomic::Ordering::Relaxed));
+        let (_, ref state) = *wd.state;
+        let state = state.lock().unwrap();
+        assert_eq!(state.state, State::NotRunning);
+    }
+}
diff --git a/keystore2/system_property/Android.bp b/keystore2/system_property/Android.bp
new file mode 100644
index 0000000..9e7b056
--- /dev/null
+++ b/keystore2/system_property/Android.bp
@@ -0,0 +1,52 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+rust_bindgen {
+    name: "libkeystore2_system_property_bindgen",
+    wrapper_src: "system_property_bindgen.hpp",
+    crate_name: "keystore2_system_property_bindgen",
+    source_stem: "bindings",
+
+    bindgen_flags: [
+        "--size_t-is-usize",
+        "--allowlist-function=__system_property_find",
+        "--allowlist-function=__system_property_read_callback",
+        "--allowlist-function=__system_property_wait",
+    ],
+}
+
+rust_library {
+    name: "libkeystore2_system_property-rust",
+    crate_name: "keystore2_system_property",
+    srcs: [
+        "lib.rs",
+    ],
+    rustlibs: [
+        "libanyhow",
+        "libkeystore2_system_property_bindgen",
+        "libthiserror",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+}
diff --git a/keystore2/system_property/lib.rs b/keystore2/system_property/lib.rs
new file mode 100644
index 0000000..be13c88
--- /dev/null
+++ b/keystore2/system_property/lib.rs
@@ -0,0 +1,191 @@
+// 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.
+
+//! This crate provides the PropertyWatcher type, which watches for changes
+//! in Android system properties.
+
+use keystore2_system_property_bindgen::prop_info as PropInfo;
+use std::os::raw::c_char;
+use std::ptr::null;
+use std::{
+    ffi::{c_void, CStr, CString},
+    str::Utf8Error,
+};
+use thiserror::Error;
+
+/// Errors this crate can generate
+#[derive(Error, Debug)]
+pub enum PropertyWatcherError {
+    /// We can't watch for a property whose name contains a NUL character.
+    #[error("Cannot convert name to C string")]
+    BadNameError(#[from] std::ffi::NulError),
+    /// We can only watch for properties that exist when the watcher is created.
+    #[error("System property is absent")]
+    SystemPropertyAbsent,
+    /// __system_property_wait timed out despite being given no timeout.
+    #[error("Wait failed")]
+    WaitFailed,
+    /// read callback was not called
+    #[error("__system_property_read_callback did not call callback")]
+    ReadCallbackNotCalled,
+    /// read callback gave us a NULL pointer
+    #[error("__system_property_read_callback gave us a NULL pointer instead of a string")]
+    MissingCString,
+    /// read callback gave us a bad C string
+    #[error("__system_property_read_callback gave us a non-UTF8 C string")]
+    BadCString(#[from] Utf8Error),
+    /// read callback returned an error
+    #[error("Callback failed")]
+    CallbackError(#[from] anyhow::Error),
+}
+
+/// Result type specific for this crate.
+pub type Result<T> = std::result::Result<T, PropertyWatcherError>;
+
+/// PropertyWatcher takes the name of an Android system property such
+/// as `keystore.boot_level`; it can report the current value of this
+/// property, or wait for it to change.
+pub struct PropertyWatcher {
+    prop_name: CString,
+    prop_info: *const PropInfo,
+    serial: keystore2_system_property_bindgen::__uint32_t,
+}
+
+impl PropertyWatcher {
+    /// Create a PropertyWatcher for the named system property.
+    pub fn new(name: &str) -> Result<Self> {
+        Ok(Self { prop_name: CString::new(name)?, prop_info: null(), serial: 0 })
+    }
+
+    // Lazy-initializing accessor for self.prop_info.
+    fn get_prop_info(&mut self) -> Option<*const PropInfo> {
+        if self.prop_info.is_null() {
+            // Unsafe required for FFI call. Input and output are both const.
+            // The returned pointer is valid for the lifetime of the program.
+            self.prop_info = unsafe {
+                keystore2_system_property_bindgen::__system_property_find(self.prop_name.as_ptr())
+            };
+        }
+        if self.prop_info.is_null() {
+            None
+        } else {
+            Some(self.prop_info)
+        }
+    }
+
+    fn read_raw(prop_info: *const PropInfo, mut f: impl FnOnce(Option<&CStr>, Option<&CStr>)) {
+        // Unsafe function converts values passed to us by
+        // __system_property_read_callback to Rust form
+        // and pass them to inner callback.
+        unsafe extern "C" fn callback(
+            res_p: *mut c_void,
+            name: *const c_char,
+            value: *const c_char,
+            _: keystore2_system_property_bindgen::__uint32_t,
+        ) {
+            let name = if name.is_null() { None } else { Some(CStr::from_ptr(name)) };
+            let value = if value.is_null() { None } else { Some(CStr::from_ptr(value)) };
+            let f = &mut *res_p.cast::<&mut dyn FnMut(Option<&CStr>, Option<&CStr>)>();
+            f(name, value);
+        }
+
+        let mut f: &mut dyn FnOnce(Option<&CStr>, Option<&CStr>) = &mut f;
+
+        // Unsafe block for FFI call. We convert the FnOnce
+        // to a void pointer, and unwrap it in our callback.
+        unsafe {
+            keystore2_system_property_bindgen::__system_property_read_callback(
+                prop_info,
+                Some(callback),
+                &mut f as *mut _ as *mut c_void,
+            )
+        }
+    }
+
+    /// Call the passed function, passing it the name and current value
+    /// of this system property. See documentation for
+    /// `__system_property_read_callback` for details.
+    /// Returns an error if the property is empty or doesn't exist.
+    pub fn read<T, F>(&mut self, f: F) -> Result<T>
+    where
+        F: FnOnce(&str, &str) -> anyhow::Result<T>,
+    {
+        let prop_info = self.get_prop_info().ok_or(PropertyWatcherError::SystemPropertyAbsent)?;
+        let mut result = Err(PropertyWatcherError::ReadCallbackNotCalled);
+        Self::read_raw(prop_info, |name, value| {
+            // use a wrapping closure as an erzatz try block.
+            result = (|| {
+                let name = name.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
+                let value = value.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
+                f(name, value).map_err(PropertyWatcherError::CallbackError)
+            })()
+        });
+        result
+    }
+
+    // Waits for the property that self is watching to be created. Returns immediately if the
+    // property already exists.
+    fn wait_for_property_creation(&mut self) -> Result<()> {
+        let mut global_serial = 0;
+        loop {
+            match self.get_prop_info() {
+                Some(_) => return Ok(()),
+                None => {
+                    // Unsafe call for FFI. The function modifies only global_serial, and has
+                    // no side-effects.
+                    if !unsafe {
+                        // Wait for a global serial number change, then try again. On success,
+                        // the function will update global_serial with the last version seen.
+                        keystore2_system_property_bindgen::__system_property_wait(
+                            null(),
+                            global_serial,
+                            &mut global_serial,
+                            null(),
+                        )
+                    } {
+                        return Err(PropertyWatcherError::WaitFailed);
+                    }
+                }
+            }
+        }
+    }
+
+    /// Wait for the system property to change. This
+    /// records the serial number of the last change, so
+    /// race conditions are avoided.
+    pub fn wait(&mut self) -> Result<()> {
+        // If the property is null, then wait for it to be created. Subsequent waits will
+        // skip this step and wait for our specific property to change.
+        if self.prop_info.is_null() {
+            return self.wait_for_property_creation();
+        }
+
+        let mut new_serial = self.serial;
+        // Unsafe block to call __system_property_wait.
+        // All arguments are private to PropertyWatcher so we
+        // can be confident they are valid.
+        if !unsafe {
+            keystore2_system_property_bindgen::__system_property_wait(
+                self.prop_info,
+                self.serial,
+                &mut new_serial,
+                null(),
+            )
+        } {
+            return Err(PropertyWatcherError::WaitFailed);
+        }
+        self.serial = new_serial;
+        Ok(())
+    }
+}
diff --git a/keystore/binder/android/security/keymaster/OperationResult.aidl b/keystore2/system_property/system_property_bindgen.hpp
similarity index 76%
rename from keystore/binder/android/security/keymaster/OperationResult.aidl
rename to keystore2/system_property/system_property_bindgen.hpp
index db689d4..e3c1ade 100644
--- a/keystore/binder/android/security/keymaster/OperationResult.aidl
+++ b/keystore2/system_property/system_property_bindgen.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -13,8 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#pragma once
 
-package android.security.keymaster;
-
-/* @hide */
-parcelable OperationResult cpp_header "keystore/OperationResult.h";
+#include "sys/system_properties.h"
diff --git a/keystore2/test_utils/lib.rs b/keystore2/test_utils/lib.rs
new file mode 100644
index 0000000..627af20
--- /dev/null
+++ b/keystore2/test_utils/lib.rs
@@ -0,0 +1,104 @@
+// 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 TempDir which aids in creating an cleaning up temporary directories for testing.
+
+use std::fs::{create_dir, remove_dir_all};
+use std::io::ErrorKind;
+use std::path::{Path, PathBuf};
+use std::{env::temp_dir, ops::Deref};
+
+/// Represents the lifecycle of a temporary directory for testing.
+#[derive(Debug)]
+pub struct TempDir {
+    path: std::path::PathBuf,
+    do_drop: bool,
+}
+
+impl TempDir {
+    /// Creates a temporary directory with a name of the form <prefix>_NNNNN where NNNNN is a zero
+    /// padded random number with 5 figures. The prefix must not contain file system separators.
+    /// The location of the directory cannot be chosen.
+    /// The directory with all of its content is removed from the file system when the resulting
+    /// object gets dropped.
+    pub fn new(prefix: &str) -> std::io::Result<Self> {
+        let tmp = loop {
+            let mut tmp = temp_dir();
+            let number: u16 = rand::random();
+            tmp.push(format!("{}_{:05}", prefix, number));
+            match create_dir(&tmp) {
+                Err(e) => match e.kind() {
+                    ErrorKind::AlreadyExists => continue,
+                    _ => return Err(e),
+                },
+                Ok(()) => break tmp,
+            }
+        };
+        Ok(Self { path: tmp, do_drop: true })
+    }
+
+    /// Returns the absolute path of the temporary directory.
+    pub fn path(&self) -> &Path {
+        &self.path
+    }
+
+    /// Returns a path builder for convenient extension of the path.
+    ///
+    /// ## Example:
+    ///
+    /// ```
+    /// let tdir = TempDir::new("my_test")?;
+    /// let temp_foo_bar = tdir.build().push("foo").push("bar");
+    /// ```
+    /// `temp_foo_bar` derefs to a Path that represents "<tdir.path()>/foo/bar"
+    pub fn build(&self) -> PathBuilder {
+        PathBuilder(self.path.clone())
+    }
+
+    /// When a test is failing you can set this to false in order to inspect
+    /// the directory structure after the test failed.
+    #[allow(dead_code)]
+    pub fn do_not_drop(&mut self) {
+        println!("Disabled automatic cleanup for: {:?}", self.path);
+        log::info!("Disabled automatic cleanup for: {:?}", self.path);
+        self.do_drop = false;
+    }
+}
+
+impl Drop for TempDir {
+    fn drop(&mut self) {
+        if self.do_drop {
+            remove_dir_all(&self.path).expect("Cannot delete temporary dir.");
+        }
+    }
+}
+
+/// Allows for convenient building of paths from a TempDir. See TempDir.build() for more details.
+pub struct PathBuilder(PathBuf);
+
+impl PathBuilder {
+    /// Adds another segment to the end of the path. Consumes, modifies and returns self.
+    pub fn push(mut self, segment: &str) -> Self {
+        self.0.push(segment);
+        self
+    }
+}
+
+impl Deref for PathBuilder {
+    type Target = Path;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
diff --git a/keystore2/vpnprofilestore/Android.bp b/keystore2/vpnprofilestore/Android.bp
new file mode 100644
index 0000000..7ddf0d6
--- /dev/null
+++ b/keystore2/vpnprofilestore/Android.bp
@@ -0,0 +1,57 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+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..8b3bc2b
--- /dev/null
+++ b/keystore2/vpnprofilestore/lib.rs
@@ -0,0 +1,558 @@
+// 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::{
+    BinderFeatures, ExceptionCode, Result as BinderResult, Status as BinderStatus, Strong,
+    ThreadState,
+};
+use anyhow::{Context, Result};
+use keystore2::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader, utils::watchdog as wd};
+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.")?,
+        };
+
+        // On busy fail Immediately. It is unlikely to succeed given a bug in sqlite.
+        db.conn.busy_handler(None).context("Failed to set busy handler.")?;
+
+        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, except for ERROR_PROFILE_NOT_FOUND error.
+///
+/// `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| {
+            let root_cause = e.root_cause();
+            let (rc, log_error) = match root_cause.downcast_ref::<Error>() {
+                // Make the profile not found errors silent.
+                Some(Error::Error(ERROR_PROFILE_NOT_FOUND)) => (ERROR_PROFILE_NOT_FOUND, false),
+                Some(Error::Error(e)) => (*e, true),
+                Some(Error::Binder(_, _)) | None => (ERROR_SYSTEM_ERROR, true),
+            };
+            if log_error {
+                log::error!("{:?}", e);
+            }
+            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, BinderFeatures::default())
+    }
+
+    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>> {
+        let _wp = wd::watch_millis("IVpnProfileStore::get", 500);
+        map_or_log_err(self.get(alias), Ok)
+    }
+    fn put(&self, alias: &str, profile: &[u8]) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IVpnProfileStore::put", 500);
+        map_or_log_err(self.put(alias, profile), Ok)
+    }
+    fn remove(&self, alias: &str) -> BinderResult<()> {
+        let _wp = wd::watch_millis("IVpnProfileStore::remove", 500);
+        map_or_log_err(self.remove(alias), Ok)
+    }
+    fn list(&self, prefix: &str) -> BinderResult<Vec<String>> {
+        let _wp = wd::watch_millis("IVpnProfileStore::list", 500);
+        map_or_log_err(self.list(prefix), Ok)
+    }
+}
+
+#[cfg(test)]
+mod db_test {
+    use super::*;
+    use keystore2_test_utils::TempDir;
+    use std::sync::Arc;
+    use std::thread;
+    use std::time::Duration;
+    use std::time::Instant;
+
+    static TEST_ALIAS: &str = &"test_alias";
+    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()
+        );
+    }
+
+    #[test]
+    fn concurrent_vpn_profile_test() -> Result<()> {
+        let temp_dir = Arc::new(
+            TempDir::new("concurrent_vpn_profile_test_").expect("Failed to create temp dir."),
+        );
+
+        let db_path = temp_dir.build().push("vpnprofile.sqlite").to_owned();
+
+        let test_begin = Instant::now();
+
+        let mut db = DB::new(&db_path).expect("Failed to open database.");
+        const PROFILE_COUNT: u32 = 5000u32;
+        const PROFILE_DB_COUNT: u32 = 5000u32;
+
+        let mut actual_profile_count = PROFILE_COUNT;
+        // First insert PROFILE_COUNT profiles.
+        for count in 0..PROFILE_COUNT {
+            if Instant::now().duration_since(test_begin) >= Duration::from_secs(15) {
+                actual_profile_count = count;
+                break;
+            }
+            let alias = format!("test_alias_{}", count);
+            db.put(1, &alias, TEST_BLOB1).expect("Failed to add profile (1).");
+        }
+
+        // Insert more keys from a different thread and into a different namespace.
+        let db_path1 = db_path.clone();
+        let handle1 = thread::spawn(move || {
+            let mut db = DB::new(&db_path1).expect("Failed to open database.");
+
+            for count in 0..actual_profile_count {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let alias = format!("test_alias_{}", count);
+                db.put(2, &alias, TEST_BLOB2).expect("Failed to add profile (2).");
+            }
+
+            // Then delete them again.
+            for count in 0..actual_profile_count {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let alias = format!("test_alias_{}", count);
+                db.remove(2, &alias).expect("Remove Failed (2).");
+            }
+        });
+
+        // And start deleting the first set of profiles.
+        let db_path2 = db_path.clone();
+        let handle2 = thread::spawn(move || {
+            let mut db = DB::new(&db_path2).expect("Failed to open database.");
+
+            for count in 0..actual_profile_count {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let alias = format!("test_alias_{}", count);
+                db.remove(1, &alias).expect("Remove Failed (1)).");
+            }
+        });
+
+        // While a lot of inserting and deleting is going on we have to open database connections
+        // successfully and then insert and delete a specific profile.
+        let db_path3 = db_path.clone();
+        let handle3 = thread::spawn(move || {
+            for _count in 0..PROFILE_DB_COUNT {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let mut db = DB::new(&db_path3).expect("Failed to open database.");
+
+                db.put(3, &TEST_ALIAS, TEST_BLOB3).expect("Failed to add profile (3).");
+
+                db.remove(3, &TEST_ALIAS).expect("Remove failed (3).");
+            }
+        });
+
+        // While thread 3 is inserting and deleting TEST_ALIAS, we try to get the alias.
+        // This may yield an entry or none, but it must not fail.
+        let handle4 = thread::spawn(move || {
+            for _count in 0..PROFILE_DB_COUNT {
+                if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
+                    return;
+                }
+                let mut db = DB::new(&db_path).expect("Failed to open database.");
+
+                // This may return Some or None but it must not fail.
+                db.get(3, &TEST_ALIAS).expect("Failed to get profile (4).");
+            }
+        });
+
+        handle1.join().expect("Thread 1 panicked.");
+        handle2.join().expect("Thread 2 panicked.");
+        handle3.join().expect("Thread 3 panicked.");
+        handle4.join().expect("Thread 4 panicked.");
+
+        Ok(())
+    }
+}
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
new file mode 100644
index 0000000..2e5e02e
--- /dev/null
+++ b/ondevice-signing/Android.bp
@@ -0,0 +1,118 @@
+// Copyright (C) 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.
+// List of clang-tidy checks that are reported as errors.
+// Please keep this list ordered lexicographically.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+tidy_errors = [
+  "cert-err34-c",
+  "google-default-arguments",
+  "google-runtime-int",
+  "google-runtime-member-string-references",
+  "misc-move-const-arg",
+  "misc-move-forwarding-reference",
+  "misc-unused-parameters",
+  "misc-unused-using-decls",
+  "misc-use-after-move",
+  "modernize-pass-by-value",
+  "performance-faster-string-find",
+  "performance-for-range-copy",
+  "performance-implicit-conversion-in-loop",
+  "performance-inefficient-vector-operation",
+  "performance-move-const-arg",
+  "performance-move-constructor-init",
+  "performance-noexcept-move-constructor",
+  "performance-unnecessary-value-param",
+]
+
+cc_defaults {
+  cpp_std: "experimental",
+  name: "odsign_flags_defaults",
+  cflags: [
+    "-Wall",
+    "-Wextra",
+    "-Werror",
+    "-Wno-unused-parameter",
+
+    // Some extra flags.
+    "-fstrict-aliasing",
+    "-Wredundant-decls",
+    "-Wshadow",
+    "-Wstrict-aliasing",
+    "-Wthread-safety",
+    "-Wthread-safety-negative",
+    "-Wunreachable-code",
+    "-Wunreachable-code-break",
+    "-Wunreachable-code-return",
+    "-Wunused",
+    "-Wused-but-marked-unused",
+  ],
+  tidy: true,
+  tidy_checks: tidy_errors,
+  tidy_checks_as_errors: tidy_errors,
+  tidy_flags: [
+    "-format-style=file",
+  ],
+}
+
+cc_binary {
+  name: "odsign",
+  defaults: [
+    "odsign_flags_defaults",
+  ],
+  cpp_std: "experimental",
+  init_rc: ["odsign.rc"],
+  srcs: [
+    "odsign_main.cpp",
+    "CertUtils.cpp",
+    "Keymaster.cpp",
+    "KeymasterSigningKey.cpp",
+    "KeystoreKey.cpp",
+    "VerityUtils.cpp",
+  ],
+
+  header_libs: ["odrefresh_headers"],
+
+  static_libs: [
+    "libmini_keyctl_static", // TODO need static?
+    "libc++fs",
+    "lib_odsign_proto",
+  ],
+
+  shared_libs: [
+    "android.hardware.keymaster@4.1",
+    "android.system.keystore2-V1-cpp",
+    "android.hardware.security.keymint-V1-cpp",
+    "libbase",
+    "libbinder",
+    "libcrypto",
+    "libcrypto_utils",
+    "libfsverity",
+    "libhidlbase",
+    "liblogwrap",
+    "libkeymaster4support", // For authorization_set
+    "libkeymaster4_1support",
+    "libkeyutils",
+    "libprotobuf-cpp-full",
+    "libutils",
+  ],
+}
diff --git a/ondevice-signing/CertUtils.cpp b/ondevice-signing/CertUtils.cpp
new file mode 100644
index 0000000..b0b75a6
--- /dev/null
+++ b/ondevice-signing/CertUtils.cpp
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/result.h>
+
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/pkcs7.h>
+#include <openssl/rsa.h>
+#include <openssl/x509v3.h>
+
+#include <fcntl.h>
+#include <vector>
+
+#include "KeyConstants.h"
+
+const char kBasicConstraints[] = "CA:TRUE";
+const char kKeyUsage[] = "critical,keyCertSign,cRLSign,digitalSignature";
+const char kSubjectKeyIdentifier[] = "hash";
+constexpr int kCertLifetimeSeconds = 10 * 365 * 24 * 60 * 60;
+
+using android::base::Result;
+// using android::base::ErrnoError;
+using android::base::Error;
+
+static bool add_ext(X509* cert, int nid, const char* value) {
+    size_t len = strlen(value) + 1;
+    std::vector<char> mutableValue(value, value + len);
+    X509V3_CTX context;
+
+    X509V3_set_ctx_nodb(&context);
+
+    X509V3_set_ctx(&context, cert, cert, nullptr, nullptr, 0);
+    X509_EXTENSION* ex = X509V3_EXT_nconf_nid(nullptr, &context, nid, mutableValue.data());
+    if (!ex) {
+        return false;
+    }
+
+    X509_add_ext(cert, ex, -1);
+    X509_EXTENSION_free(ex);
+    return true;
+}
+
+Result<bssl::UniquePtr<RSA>> getRsa(const std::vector<uint8_t>& publicKey) {
+    bssl::UniquePtr<RSA> rsaPubkey(RSA_new());
+    rsaPubkey->n = BN_new();
+    rsaPubkey->e = BN_new();
+
+    BN_bin2bn(publicKey.data(), publicKey.size(), rsaPubkey->n);
+    BN_set_word(rsaPubkey->e, kRsaKeyExponent);
+
+    return rsaPubkey;
+}
+
+Result<void> verifySignature(const std::string& message, const std::string& signature,
+                             const std::vector<uint8_t>& publicKey) {
+    auto rsaKey = getRsa(publicKey);
+    uint8_t hashBuf[SHA256_DIGEST_LENGTH];
+    SHA256(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(message.c_str())),
+           message.length(), hashBuf);
+
+    bool success = RSA_verify(NID_sha256, hashBuf, sizeof(hashBuf),
+                              (const uint8_t*)signature.c_str(), signature.length(), rsaKey->get());
+
+    if (!success) {
+        return Error() << "Failed to verify signature.";
+    }
+    return {};
+}
+
+Result<void> createSelfSignedCertificate(
+    const std::vector<uint8_t>& publicKey,
+    const std::function<Result<std::string>(const std::string&)>& signFunction,
+    const std::string& path) {
+    bssl::UniquePtr<X509> x509(X509_new());
+    if (!x509) {
+        return Error() << "Unable to allocate x509 container";
+    }
+    X509_set_version(x509.get(), 2);
+
+    ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 1);
+    X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
+    X509_gmtime_adj(X509_get_notAfter(x509.get()), kCertLifetimeSeconds);
+
+    // "publicKey" corresponds to the raw public key bytes - need to create
+    // a new RSA key with the correct exponent.
+    auto rsaPubkey = getRsa(publicKey);
+
+    EVP_PKEY* public_key = EVP_PKEY_new();
+    EVP_PKEY_assign_RSA(public_key, rsaPubkey->release());
+
+    if (!X509_set_pubkey(x509.get(), public_key)) {
+        return Error() << "Unable to set x509 public key";
+    }
+
+    X509_NAME* name = X509_get_subject_name(x509.get());
+    if (!name) {
+        return Error() << "Unable to get x509 subject name";
+    }
+    X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
+    X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
+    X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>("ODS"), -1, -1, 0);
+    if (!X509_set_issuer_name(x509.get(), name)) {
+        return Error() << "Unable to set x509 issuer name";
+    }
+
+    add_ext(x509.get(), NID_basic_constraints, kBasicConstraints);
+    add_ext(x509.get(), NID_key_usage, kKeyUsage);
+    add_ext(x509.get(), NID_subject_key_identifier, kSubjectKeyIdentifier);
+    add_ext(x509.get(), NID_authority_key_identifier, "keyid:always");
+
+    X509_ALGOR_set0(x509->cert_info->signature, OBJ_nid2obj(NID_sha256WithRSAEncryption),
+                    V_ASN1_NULL, NULL);
+    X509_ALGOR_set0(x509->sig_alg, OBJ_nid2obj(NID_sha256WithRSAEncryption), V_ASN1_NULL, NULL);
+
+    // Get the data to be signed
+    char* to_be_signed_buf(nullptr);
+    size_t to_be_signed_length = i2d_re_X509_tbs(x509.get(), (unsigned char**)&to_be_signed_buf);
+
+    auto signed_data = signFunction(std::string(to_be_signed_buf, to_be_signed_length));
+    if (!signed_data.ok()) {
+        return signed_data.error();
+    }
+
+    // This is the only part that doesn't use boringssl default functions - we manually copy in the
+    // signature that was provided to us.
+    x509->signature->data = (unsigned char*)OPENSSL_malloc(signed_data->size());
+    memcpy(x509->signature->data, signed_data->c_str(), signed_data->size());
+    x509->signature->length = signed_data->size();
+
+    x509->signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+    x509->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+    auto f = fopen(path.c_str(), "wbe");
+    if (f == nullptr) {
+        return Error() << "Failed to open " << path;
+    }
+    i2d_X509_fp(f, x509.get());
+    fclose(f);
+
+    EVP_PKEY_free(public_key);
+    return {};
+}
+
+Result<std::vector<uint8_t>> extractPublicKey(EVP_PKEY* pkey) {
+    if (pkey == nullptr) {
+        return Error() << "Failed to extract public key from x509 cert";
+    }
+
+    if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
+        return Error() << "The public key is not an RSA key";
+    }
+
+    RSA* rsa = EVP_PKEY_get1_RSA(pkey);
+    auto num_bytes = BN_num_bytes(rsa->n);
+    std::vector<uint8_t> pubKey(num_bytes);
+    int res = BN_bn2bin(rsa->n, pubKey.data());
+    RSA_free(rsa);
+
+    if (!res) {
+        return Error() << "Failed to convert public key to bytes";
+    }
+
+    return pubKey;
+}
+
+Result<std::vector<uint8_t>>
+extractPublicKeyFromSubjectPublicKeyInfo(const std::vector<uint8_t>& keyData) {
+    auto keyDataBytes = keyData.data();
+    EVP_PKEY* public_key = d2i_PUBKEY(nullptr, &keyDataBytes, keyData.size());
+
+    return extractPublicKey(public_key);
+}
+
+Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::vector<uint8_t>& keyData) {
+    auto keyDataBytes = keyData.data();
+    bssl::UniquePtr<X509> decoded_cert(d2i_X509(nullptr, &keyDataBytes, keyData.size()));
+    if (decoded_cert.get() == nullptr) {
+        return Error() << "Failed to decode X509 certificate.";
+    }
+    bssl::UniquePtr<EVP_PKEY> decoded_pkey(X509_get_pubkey(decoded_cert.get()));
+
+    return extractPublicKey(decoded_pkey.get());
+}
+
+Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path) {
+    X509* cert;
+    auto f = fopen(path.c_str(), "re");
+    if (f == nullptr) {
+        return Error() << "Failed to open " << path;
+    }
+    if (!d2i_X509_fp(f, &cert)) {
+        fclose(f);
+        return Error() << "Unable to decode x509 cert at " << path;
+    }
+
+    fclose(f);
+    return extractPublicKey(X509_get_pubkey(cert));
+}
+
+Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signed_digest) {
+    CBB out, outer_seq, wrapped_seq, seq, digest_algos_set, digest_algo, null;
+    CBB content_info, issuer_and_serial, signer_infos, signer_info, sign_algo, signature;
+    uint8_t *pkcs7_data, *name_der;
+    size_t pkcs7_data_len, name_der_len;
+    BIGNUM* serial = BN_new();
+    int sig_nid = NID_rsaEncryption;
+
+    X509_NAME* name = X509_NAME_new();
+    if (!name) {
+        return Error() << "Unable to get x509 subject name";
+    }
+    X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
+    X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
+    X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+                               reinterpret_cast<const unsigned char*>("ODS"), -1, -1, 0);
+
+    BN_set_word(serial, 1);
+    name_der_len = i2d_X509_NAME(name, &name_der);
+    CBB_init(&out, 1024);
+
+    if (!CBB_add_asn1(&out, &outer_seq, CBS_ASN1_SEQUENCE) ||
+        !OBJ_nid2cbb(&outer_seq, NID_pkcs7_signed) ||
+        !CBB_add_asn1(&outer_seq, &wrapped_seq,
+                      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
+        // See https://tools.ietf.org/html/rfc2315#section-9.1
+        !CBB_add_asn1(&wrapped_seq, &seq, CBS_ASN1_SEQUENCE) ||
+        !CBB_add_asn1_uint64(&seq, 1 /* version */) ||
+        !CBB_add_asn1(&seq, &digest_algos_set, CBS_ASN1_SET) ||
+        !CBB_add_asn1(&digest_algos_set, &digest_algo, CBS_ASN1_SEQUENCE) ||
+        !OBJ_nid2cbb(&digest_algo, NID_sha256) ||
+        !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
+        !CBB_add_asn1(&seq, &content_info, CBS_ASN1_SEQUENCE) ||
+        !OBJ_nid2cbb(&content_info, NID_pkcs7_data) ||
+        !CBB_add_asn1(&seq, &signer_infos, CBS_ASN1_SET) ||
+        !CBB_add_asn1(&signer_infos, &signer_info, CBS_ASN1_SEQUENCE) ||
+        !CBB_add_asn1_uint64(&signer_info, 1 /* version */) ||
+        !CBB_add_asn1(&signer_info, &issuer_and_serial, CBS_ASN1_SEQUENCE) ||
+        !CBB_add_bytes(&issuer_and_serial, name_der, name_der_len) ||
+        !BN_marshal_asn1(&issuer_and_serial, serial) ||
+        !CBB_add_asn1(&signer_info, &digest_algo, CBS_ASN1_SEQUENCE) ||
+        !OBJ_nid2cbb(&digest_algo, NID_sha256) ||
+        !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
+        !CBB_add_asn1(&signer_info, &sign_algo, CBS_ASN1_SEQUENCE) ||
+        !OBJ_nid2cbb(&sign_algo, sig_nid) || !CBB_add_asn1(&sign_algo, &null, CBS_ASN1_NULL) ||
+        !CBB_add_asn1(&signer_info, &signature, CBS_ASN1_OCTETSTRING) ||
+        !CBB_add_bytes(&signature, signed_digest.data(), signed_digest.size()) ||
+        !CBB_finish(&out, &pkcs7_data, &pkcs7_data_len)) {
+        return Error() << "Failed to create PKCS7 certificate.";
+    }
+
+    return std::vector<uint8_t>(&pkcs7_data[0], &pkcs7_data[pkcs7_data_len]);
+}
diff --git a/ondevice-signing/CertUtils.h b/ondevice-signing/CertUtils.h
new file mode 100644
index 0000000..66dff04
--- /dev/null
+++ b/ondevice-signing/CertUtils.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <android-base/result.h>
+
+android::base::Result<void> createSelfSignedCertificate(
+    const std::vector<uint8_t>& publicKey,
+    const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
+    const std::string& path);
+android::base::Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signedData);
+
+android::base::Result<std::vector<uint8_t>>
+extractPublicKeyFromX509(const std::vector<uint8_t>& x509);
+android::base::Result<std::vector<uint8_t>>
+extractPublicKeyFromSubjectPublicKeyInfo(const std::vector<uint8_t>& subjectKeyInfo);
+android::base::Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path);
+
+android::base::Result<void> verifySignature(const std::string& message,
+                                            const std::string& signature,
+                                            const std::vector<uint8_t>& publicKey);
diff --git a/keystore/binder/android/security/keymaster/OperationResult.aidl b/ondevice-signing/KeyConstants.h
similarity index 76%
copy from keystore/binder/android/security/keymaster/OperationResult.aidl
copy to ondevice-signing/KeyConstants.h
index db689d4..9e1a513 100644
--- a/keystore/binder/android/security/keymaster/OperationResult.aidl
+++ b/ondevice-signing/KeyConstants.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 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.
@@ -14,7 +14,5 @@
  * limitations under the License.
  */
 
-package android.security.keymaster;
-
-/* @hide */
-parcelable OperationResult cpp_header "keystore/OperationResult.h";
+static constexpr int kRsaKeySize = 2048;
+static constexpr int kRsaKeyExponent = 65537;
diff --git a/ondevice-signing/Keymaster.cpp b/ondevice-signing/Keymaster.cpp
new file mode 100644
index 0000000..f9bf9b2
--- /dev/null
+++ b/ondevice-signing/Keymaster.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <keymasterV4_1/Keymaster.h>
+#include <keymasterV4_1/authorization_set.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "Keymaster.h"
+
+using AuthorizationSet = ::android::hardware::keymaster::V4_0::AuthorizationSet;
+using AuthorizationSetBuilder = ::android::hardware::keymaster::V4_0::AuthorizationSetBuilder;
+using Digest = ::android::hardware::keymaster::V4_0::Digest;
+using ErrorCode = ::android::hardware::keymaster::V4_0::ErrorCode;
+using HardwareAuthToken = ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+using HidlBuf = ::android::hardware::hidl_vec<uint8_t>;
+using KeyCharacteristics = ::android::hardware::keymaster::V4_0::KeyCharacteristics;
+using KeyFormat = ::android::hardware::keymaster::V4_0::KeyFormat;
+using KeyParameter = ::android::hardware::keymaster::V4_0::KeyParameter;
+using KeyPurpose = ::android::hardware::keymaster::V4_0::KeyPurpose;
+using KmSupport = ::android::hardware::keymaster::V4_1::support::Keymaster;
+using KmDevice = ::android::hardware::keymaster::V4_1::IKeymasterDevice;
+using OperationHandle = ::android::hardware::keymaster::V4_0::OperationHandle;
+using PaddingMode = ::android::hardware::keymaster::V4_0::PaddingMode;
+using VerificationToken = ::android::hardware::keymaster::V4_0::VerificationToken;
+
+using android::sp;
+using android::base::Error;
+using android::base::Result;
+using android::hardware::hidl_vec;
+
+Keymaster::Keymaster() {}
+
+bool Keymaster::initialize() {
+    // TODO(b/165630556): Stop using Keymaster directly and migrate to keystore2
+    // (once available).
+    auto devices = KmSupport::enumerateAvailableDevices();
+    sp<KmDevice> devToUse = nullptr;
+    for (const auto& dev : devices) {
+        auto version = dev->halVersion();
+        if (version.majorVersion > 4 || (version.majorVersion == 4 && version.minorVersion >= 1)) {
+            // TODO we probably have a preference for the SE, hoping Keystore2 will provide this
+            LOG(INFO) << "Using keymaster " << version.keymasterName << " "
+                      << (int)version.majorVersion << "." << (int)version.minorVersion;
+            devToUse = dev;
+            break;
+        }
+    }
+
+    if (devToUse == nullptr) {
+        LOG(WARNING) << "Didn't find a keymaster to use.";
+    }
+    mDevice = devToUse;
+
+    return mDevice != nullptr;
+}
+
+std::optional<Keymaster> Keymaster::getInstance() {
+    static Keymaster keymaster;
+
+    if (!keymaster.initialize()) {
+        return {};
+    } else {
+        return {keymaster};
+    }
+}
+
+Result<std::vector<uint8_t>> Keymaster::createKey() const {
+    ErrorCode error;
+    HidlBuf keyBlob;
+
+    auto params = AuthorizationSetBuilder()
+                      .Authorization(::android::hardware::keymaster::V4_0::TAG_NO_AUTH_REQUIRED)
+                      // TODO MAKE SURE WE ADD THE EARLY_BOOT_ONLY FLAG here
+                      // currently doesn't work on cuttlefish (b/173618442)
+                      //.Authorization(::android::hardware::keymaster::V4_1::TAG_EARLY_BOOT_ONLY)
+                      .RsaSigningKey(2048, 65537)
+                      .Digest(Digest::SHA_2_256)
+                      .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
+
+    mDevice->generateKey(params.hidl_data(), [&](ErrorCode hidl_error, const HidlBuf& hidl_key_blob,
+                                                 const KeyCharacteristics&
+                                                 /* hidl_key_characteristics */) {
+        error = hidl_error;
+        keyBlob = hidl_key_blob;
+    });
+
+    if (error != ErrorCode::OK) {
+        return Error() << "Error creating keymaster signing key: "
+                       << static_cast<std::underlying_type<ErrorCode>::type>(error);
+    }
+
+    return keyBlob;
+}
+
+static ErrorCode Begin(const sp<KmDevice>& keymaster_, KeyPurpose purpose, const HidlBuf& key_blob,
+                       const AuthorizationSet& in_params, AuthorizationSet* out_params,
+                       OperationHandle* op_handle) {
+    ErrorCode error;
+    OperationHandle saved_handle = *op_handle;
+    CHECK(keymaster_
+              ->begin(purpose, key_blob, in_params.hidl_data(), HardwareAuthToken(),
+                      [&](ErrorCode hidl_error, const hidl_vec<KeyParameter>& hidl_out_params,
+                          uint64_t hidl_op_handle) {
+                          error = hidl_error;
+                          *out_params = hidl_out_params;
+                          *op_handle = hidl_op_handle;
+                      })
+              .isOk());
+    if (error != ErrorCode::OK) {
+        // Some implementations may modify *op_handle on error.
+        *op_handle = saved_handle;
+    }
+    return error;
+}
+
+static ErrorCode Update(const sp<KmDevice>& keymaster_, OperationHandle op_handle,
+                        const AuthorizationSet& in_params, const std::string& input,
+                        AuthorizationSet* out_params, std::string* output, size_t* input_consumed) {
+    ErrorCode error;
+    HidlBuf inputData(input.size());
+    memcpy(inputData.data(), input.c_str(), input.size());
+    CHECK(keymaster_
+              ->update(op_handle, in_params.hidl_data(), inputData, HardwareAuthToken(),
+                       VerificationToken(),
+                       [&](ErrorCode hidl_error, uint32_t hidl_input_consumed,
+                           const hidl_vec<KeyParameter>& hidl_out_params,
+                           const HidlBuf& hidl_output) {
+                           error = hidl_error;
+                           out_params->push_back(AuthorizationSet(hidl_out_params));
+                           std::string retdata(reinterpret_cast<const char*>(hidl_output.data()),
+                                               hidl_output.size());
+                           output->append(retdata);
+                           *input_consumed = hidl_input_consumed;
+                       })
+              .isOk());
+    return error;
+}
+
+static ErrorCode Finish(const sp<KmDevice>& keymaster_, OperationHandle op_handle,
+                        const AuthorizationSet& in_params, const std::string& input,
+                        const std::string& signature, AuthorizationSet* out_params,
+                        std::string* output) {
+    ErrorCode error;
+    HidlBuf inputData(input.size());
+    memcpy(inputData.data(), input.c_str(), input.size());
+    HidlBuf signatureData(signature.size());
+    memcpy(signatureData.data(), signature.c_str(), signature.size());
+    // TODO still need to handle error -62 - key requires upgrade
+    CHECK(keymaster_
+              ->finish(op_handle, in_params.hidl_data(), inputData, signatureData,
+                       HardwareAuthToken(), VerificationToken(),
+                       [&](ErrorCode hidl_error, const hidl_vec<KeyParameter>& hidl_out_params,
+                           const HidlBuf& hidl_output) {
+                           error = hidl_error;
+                           *out_params = hidl_out_params;
+                           std::string retdata(reinterpret_cast<const char*>(hidl_output.data()),
+                                               hidl_output.size());
+                           output->append(retdata);
+                       })
+              .isOk());
+    return error;
+}
+
+static std::string ProcessMessage(const sp<KmDevice>& keymaster_, const HidlBuf& key_blob,
+                                  KeyPurpose operation, const std::string& message,
+                                  const AuthorizationSet& in_params, AuthorizationSet* out_params) {
+    AuthorizationSet begin_out_params;
+    OperationHandle op_handle_;
+    ErrorCode ec =
+        Begin(keymaster_, operation, key_blob, in_params, &begin_out_params, &op_handle_);
+
+    std::string output;
+    size_t consumed = 0;
+    AuthorizationSet update_params;
+    AuthorizationSet update_out_params;
+    ec = Update(keymaster_, op_handle_, update_params, message, &update_out_params, &output,
+                &consumed);
+
+    std::string unused;
+    AuthorizationSet finish_params;
+    AuthorizationSet finish_out_params;
+    ec = Finish(keymaster_, op_handle_, finish_params, message.substr(consumed), unused,
+                &finish_out_params, &output);
+
+    out_params->push_back(begin_out_params);
+    out_params->push_back(finish_out_params);
+    return output;
+}
+
+Result<std::vector<uint8_t>>
+Keymaster::extractPublicKey(const std::vector<uint8_t>& keyBlob) const {
+    std::vector<uint8_t> publicKey;
+    ErrorCode error;
+
+    mDevice->exportKey(KeyFormat::X509, keyBlob, {} /* clientId */, {} /* appData */,
+                       [&](ErrorCode hidl_error, const HidlBuf& keyData) {
+                           error = hidl_error;
+                           publicKey = keyData;
+                       });
+
+    if (error != ErrorCode::OK) {
+        return Error() << "Error extracting public key: "
+                       << static_cast<std::underlying_type<ErrorCode>::type>(error);
+    }
+
+    return publicKey;
+}
+
+Result<KeymasterVerifyResult> Keymaster::verifyKey(const std::vector<uint8_t>& keyBlob) const {
+    ErrorCode error;
+    KeyCharacteristics characteristics;
+
+    mDevice->getKeyCharacteristics(
+        keyBlob, {} /* clientId */, {} /* appData */,
+        [&](ErrorCode hidl_error, const KeyCharacteristics& hidl_characteristics) {
+            error = hidl_error;
+            characteristics = hidl_characteristics;
+        });
+
+    if (error == ErrorCode::KEY_REQUIRES_UPGRADE) {
+        return KeymasterVerifyResult::UPGRADE;
+    }
+
+    if (error != ErrorCode::OK) {
+        return Error() << "Error getting key characteristics: "
+                       << static_cast<std::underlying_type<ErrorCode>::type>(error);
+    }
+
+    // TODO(b/165630556)
+    // Verify this is an early boot key and the other key parameters
+    return KeymasterVerifyResult::OK;
+}
+
+Result<std::vector<uint8_t>> Keymaster::upgradeKey(const std::vector<uint8_t>& keyBlob) const {
+    ErrorCode error;
+    HidlBuf newKeyBlob;
+
+    // TODO deduplicate
+    auto params = AuthorizationSetBuilder()
+                      .Authorization(::android::hardware::keymaster::V4_0::TAG_NO_AUTH_REQUIRED)
+                      // TODO MAKE SURE WE ADD THE EARLY_BOOT_ONLY FLAG here
+                      // currently doesn't work on cuttlefish (b/173618442)
+                      //.Authorization(::android::hardware::keymaster::V4_1::TAG_EARLY_BOOT_ONLY)
+                      .RsaSigningKey(2048, 65537)
+                      .Digest(Digest::SHA_2_256)
+                      .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
+
+    mDevice->upgradeKey(keyBlob, params.hidl_data(),
+                        [&](ErrorCode hidl_error, const HidlBuf& hidl_key_blob) {
+                            error = hidl_error;
+                            newKeyBlob = hidl_key_blob;
+                        });
+
+    if (error != ErrorCode::OK) {
+        return Error() << "Error upgrading keymaster signing key: "
+                       << static_cast<std::underlying_type<ErrorCode>::type>(error);
+    }
+
+    return newKeyBlob;
+}
+
+Result<std::string> Keymaster::sign(const std::vector<uint8_t>& keyBlob,
+                                    const std::string& message) const {
+    AuthorizationSet out_params;
+    auto params = AuthorizationSetBuilder()
+                      .Digest(Digest::SHA_2_256)
+                      .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
+    std::string signature =
+        ProcessMessage(mDevice, keyBlob, KeyPurpose::SIGN, message, params, &out_params);
+    if (!out_params.empty()) {
+        return Error() << "Error signing key: expected empty out params.";
+    }
+    return signature;
+}
diff --git a/ondevice-signing/Keymaster.h b/ondevice-signing/Keymaster.h
new file mode 100644
index 0000000..455289f
--- /dev/null
+++ b/ondevice-signing/Keymaster.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include <android-base/macros.h>
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+
+#include <keymasterV4_1/Keymaster.h>
+
+#include <utils/StrongPointer.h>
+
+enum class KeymasterVerifyResult {
+    OK = 0,
+    UPGRADE = -1,
+};
+
+class Keymaster {
+    using KmDevice = ::android::hardware::keymaster::V4_1::IKeymasterDevice;
+
+  public:
+    static std::optional<Keymaster> getInstance();
+
+    android::base::Result<std::vector<uint8_t>> createKey() const;
+
+    android::base::Result<std::vector<uint8_t>>
+    extractPublicKey(const std::vector<uint8_t>& keyBlob) const;
+
+    android::base::Result<KeymasterVerifyResult>
+    verifyKey(const std::vector<uint8_t>& keyBlob) const;
+
+    android::base::Result<std::vector<uint8_t>>
+    upgradeKey(const std::vector<uint8_t>& keyBlob) const;
+
+    /* Sign a message with an initialized signing key */
+    android::base::Result<std::string> sign(const std::vector<uint8_t>& keyBlob,
+                                            const std::string& message) const;
+
+  private:
+    Keymaster();
+    bool initialize();
+
+    android::sp<KmDevice> mDevice;
+};
diff --git a/ondevice-signing/KeymasterSigningKey.cpp b/ondevice-signing/KeymasterSigningKey.cpp
new file mode 100644
index 0000000..dc3ef8a
--- /dev/null
+++ b/ondevice-signing/KeymasterSigningKey.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "CertUtils.h"
+#include "Keymaster.h"
+#include "KeymasterSigningKey.h"
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::ReadFileToString;
+using android::base::Result;
+using android::base::unique_fd;
+
+const std::string kSigningKeyBlob = "/data/misc/odsign/key.blob";
+
+KeymasterSigningKey::KeymasterSigningKey() {}
+
+Result<std::unique_ptr<KeymasterSigningKey>>
+KeymasterSigningKey::loadFromBlobAndVerify(const std::string& path) {
+    auto signingKey = std::make_unique<KeymasterSigningKey>();
+
+    auto status = signingKey->initializeFromKeyblob(path);
+
+    if (!status.ok()) {
+        return status.error();
+    }
+
+    return signingKey;
+}
+
+Result<void> KeymasterSigningKey::saveKeyblob(const std::string& path) const {
+    int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, 0600)));
+    if (fd == -1) {
+        return ErrnoError() << "Error creating key blob file " << path;
+    }
+
+    if (!android::base::WriteFully(fd, mVerifiedKeyBlob.data(), mVerifiedKeyBlob.size())) {
+        return ErrnoError() << "Error writing key blob file " << path;
+    } else {
+        return {};
+    }
+}
+
+Result<void> KeymasterSigningKey::createSigningKey() {
+    KeymasterSigningKey signingKey;
+    auto keymaster = Keymaster::getInstance();
+    if (!keymaster.has_value()) {
+        return Error() << "Failed to initialize keymaster.";
+    }
+    mKeymaster = keymaster;
+
+    auto keyBlob = mKeymaster->createKey();
+
+    if (!keyBlob.ok()) {
+        return keyBlob.error();
+    }
+
+    mVerifiedKeyBlob.assign(keyBlob->begin(), keyBlob->end());
+
+    return {};
+}
+
+Result<std::unique_ptr<KeymasterSigningKey>> KeymasterSigningKey::createAndPersistNewKey() {
+    auto signingKey = std::make_unique<KeymasterSigningKey>();
+
+    auto status = signingKey->createSigningKey();
+
+    if (!status.ok()) {
+        return status.error();
+    }
+
+    status = signingKey->saveKeyblob(kSigningKeyBlob);
+    if (!status.ok()) {
+        return status.error();
+    }
+
+    return signingKey;
+}
+
+Result<SigningKey*> KeymasterSigningKey::getInstance() {
+    auto key = loadFromBlobAndVerify(kSigningKeyBlob);
+
+    if (!key.ok()) {
+        key = createAndPersistNewKey();
+        if (!key.ok()) {
+            return key.error();
+        }
+    }
+
+    return key->release();
+}
+
+Result<std::vector<uint8_t>> KeymasterSigningKey::getPublicKey() const {
+    auto publicKey = mKeymaster->extractPublicKey(mVerifiedKeyBlob);
+    if (!publicKey.ok()) {
+        return publicKey.error();
+    }
+
+    // Keymaster returns the public key not in a full X509 cert, but just the
+    // "SubjectPublicKeyInfo"
+    return extractPublicKeyFromSubjectPublicKeyInfo(publicKey.value());
+}
+
+Result<void> KeymasterSigningKey::initializeFromKeyblob(const std::string& path) {
+    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) {
+        return ErrnoError() << "Failed to read " << path;
+    }
+
+    std::vector<uint8_t> keyBlob = {keyBlobData.begin(), keyBlobData.end()};
+
+    auto verifyResult = mKeymaster->verifyKey(keyBlob);
+    if (!verifyResult.ok()) {
+        return Error() << "Failed to verify key: " << verifyResult.error().message();
+    }
+
+    if (*verifyResult == KeymasterVerifyResult::UPGRADE) {
+        auto upgradeResult = mKeymaster->upgradeKey(keyBlob);
+        if (!upgradeResult.ok()) {
+            return Error() << "Failed to upgrade key: " << upgradeResult.error().message();
+        }
+        mVerifiedKeyBlob = *upgradeResult;
+        // Make sure we persist the new blob
+        auto saveResult = saveKeyblob(path);
+        if (!saveResult.ok()) {
+            return Error() << "Failed to store upgraded key";
+        }
+    } else {
+        mVerifiedKeyBlob = keyBlob;
+    }
+
+    return {};
+}
+
+Result<std::string> KeymasterSigningKey::sign(const std::string& message) const {
+    return mKeymaster->sign(mVerifiedKeyBlob, message);
+}
diff --git a/ondevice-signing/KeymasterSigningKey.h b/ondevice-signing/KeymasterSigningKey.h
new file mode 100644
index 0000000..e66781f
--- /dev/null
+++ b/ondevice-signing/KeymasterSigningKey.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <android-base/macros.h>
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+
+#include <utils/StrongPointer.h>
+
+#include "Keymaster.h"
+#include "SigningKey.h"
+
+class KeymasterSigningKey : public SigningKey {
+    using KmDevice = ::android::hardware::keymaster::V4_1::IKeymasterDevice;
+
+  public:
+    friend std::unique_ptr<KeymasterSigningKey> std::make_unique<KeymasterSigningKey>();
+    virtual ~KeymasterSigningKey(){};
+
+    // Allow the key to be moved around
+    KeymasterSigningKey& operator=(KeymasterSigningKey&& other) = default;
+    KeymasterSigningKey(KeymasterSigningKey&& other) = default;
+
+    static android::base::Result<SigningKey*> getInstance();
+
+    virtual android::base::Result<std::string> sign(const std::string& message) const;
+    virtual android::base::Result<std::vector<uint8_t>> getPublicKey() const;
+
+  private:
+    KeymasterSigningKey();
+
+    static android::base::Result<std::unique_ptr<KeymasterSigningKey>> createAndPersistNewKey();
+    static android::base::Result<std::unique_ptr<KeymasterSigningKey>>
+    loadFromBlobAndVerify(const std::string& path);
+
+    android::base::Result<void> createSigningKey();
+    android::base::Result<void> initializeFromKeyblob(const std::string& path);
+    android::base::Result<void> saveKeyblob(const std::string& path) const;
+
+    static android::base::Result<KeymasterSigningKey> createNewKey();
+
+    std::optional<Keymaster> mKeymaster;
+    std::vector<uint8_t> mVerifiedKeyBlob;
+
+    DISALLOW_COPY_AND_ASSIGN(KeymasterSigningKey);
+};
diff --git a/ondevice-signing/KeystoreKey.cpp b/ondevice-signing/KeystoreKey.cpp
new file mode 100644
index 0000000..4e59c58
--- /dev/null
+++ b/ondevice-signing/KeystoreKey.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <binder/IServiceManager.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "CertUtils.h"
+#include "KeyConstants.h"
+#include "KeystoreKey.h"
+
+using android::defaultServiceManager;
+using android::IServiceManager;
+using android::sp;
+using android::String16;
+
+using android::hardware::security::keymint::Algorithm;
+using android::hardware::security::keymint::Digest;
+using android::hardware::security::keymint::KeyParameter;
+using android::hardware::security::keymint::KeyParameterValue;
+using android::hardware::security::keymint::KeyPurpose;
+using android::hardware::security::keymint::PaddingMode;
+using android::hardware::security::keymint::SecurityLevel;
+using android::hardware::security::keymint::Tag;
+
+using android::system::keystore2::CreateOperationResponse;
+using android::system::keystore2::Domain;
+using android::system::keystore2::KeyDescriptor;
+using android::system::keystore2::KeyEntryResponse;
+using android::system::keystore2::KeyMetadata;
+
+using android::base::Error;
+using android::base::Result;
+
+// Keystore boot level that the odsign key uses
+static const int kOdsignBootLevel = 30;
+
+static KeyDescriptor getKeyDescriptor() {
+    // AIDL parcelable objects don't have constructor
+    static KeyDescriptor descriptor;
+    static std::once_flag flag;
+    std::call_once(flag, [&]() {
+        descriptor.domain = Domain::SELINUX;
+        descriptor.alias = String16("ondevice-signing");
+        descriptor.nspace = 101;  // odsign_key
+    });
+
+    return descriptor;
+}
+
+KeystoreKey::KeystoreKey() {}
+
+Result<KeyMetadata> KeystoreKey::createNewKey(const KeyDescriptor& descriptor) {
+    std::vector<KeyParameter> params;
+
+    KeyParameter algo;
+    algo.tag = Tag::ALGORITHM;
+    algo.value = KeyParameterValue::make<KeyParameterValue::algorithm>(Algorithm::RSA);
+    params.push_back(algo);
+
+    KeyParameter key_size;
+    key_size.tag = Tag::KEY_SIZE;
+    key_size.value = KeyParameterValue::make<KeyParameterValue::integer>(kRsaKeySize);
+    params.push_back(key_size);
+
+    KeyParameter digest;
+    digest.tag = Tag::DIGEST;
+    digest.value = KeyParameterValue::make<KeyParameterValue::digest>(Digest::SHA_2_256);
+    params.push_back(digest);
+
+    KeyParameter padding;
+    padding.tag = Tag::PADDING;
+    padding.value =
+        KeyParameterValue::make<KeyParameterValue::paddingMode>(PaddingMode::RSA_PKCS1_1_5_SIGN);
+    params.push_back(padding);
+
+    KeyParameter exponent;
+    exponent.tag = Tag::RSA_PUBLIC_EXPONENT;
+    exponent.value = KeyParameterValue::make<KeyParameterValue::longInteger>(kRsaKeyExponent);
+    params.push_back(exponent);
+
+    KeyParameter purpose;
+    purpose.tag = Tag::PURPOSE;
+    purpose.value = KeyParameterValue::make<KeyParameterValue::keyPurpose>(KeyPurpose::SIGN);
+    params.push_back(purpose);
+
+    KeyParameter auth;
+    auth.tag = Tag::NO_AUTH_REQUIRED;
+    auth.value = KeyParameterValue::make<KeyParameterValue::boolValue>(true);
+    params.push_back(auth);
+
+    KeyParameter boot_level;
+    boot_level.tag = Tag::MAX_BOOT_LEVEL;
+    boot_level.value = KeyParameterValue::make<KeyParameterValue::integer>(kOdsignBootLevel);
+    params.push_back(boot_level);
+
+    KeyMetadata metadata;
+    auto status = mSecurityLevel->generateKey(descriptor, {}, params, 0, {}, &metadata);
+    if (!status.isOk()) {
+        return Error() << "Failed to create new key";
+    }
+
+    return metadata;
+}
+
+bool KeystoreKey::initialize() {
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == nullptr) {
+        return false;
+    }
+    auto service = sm->getService(String16("android.system.keystore2.IKeystoreService/default"));
+    if (service == nullptr) {
+        return false;
+    }
+    mService = interface_cast<android::system::keystore2::IKeystoreService>(service);
+    if (mService == nullptr) {
+        return false;
+    }
+
+    auto status = mService->getSecurityLevel(SecurityLevel::STRONGBOX, &mSecurityLevel);
+    if (!status.isOk()) {
+        status = mService->getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT, &mSecurityLevel);
+        if (!status.isOk()) {
+            return false;
+        }
+    }
+
+    auto descriptor = getKeyDescriptor();
+    // See if we can fetch an existing key
+    KeyEntryResponse keyEntryResponse;
+    LOG(INFO) << "Trying to retrieve existing keystore key...";
+    status = mService->getKeyEntry(descriptor, &keyEntryResponse);
+    bool keyValid = false;
+
+    if (status.isOk()) {
+        // Make sure this is an early boot key
+        for (const auto& auth : keyEntryResponse.metadata.authorizations) {
+            if (auth.keyParameter.tag == Tag::MAX_BOOT_LEVEL) {
+                if (auth.keyParameter.value.get<KeyParameterValue::integer>() == kOdsignBootLevel) {
+                    keyValid = true;
+                    break;
+                }
+            }
+        }
+        if (!keyValid) {
+            LOG(WARNING) << "Found invalid keystore key without MAX_BOOT_LEVEL tag";
+        }
+    }
+
+    if (!keyValid) {
+        LOG(INFO) << "Existing keystore key not found or invalid, creating new key";
+        auto newKeyStatus = createNewKey(descriptor);
+        if (!newKeyStatus.ok()) {
+            LOG(ERROR) << "Failed to create new key";
+            return false;
+        }
+        mKeyMetadata = *newKeyStatus;
+    } else {
+        mKeyMetadata = keyEntryResponse.metadata;
+    }
+
+    LOG(ERROR) << "Initialized Keystore key.";
+    return true;
+}
+
+Result<SigningKey*> KeystoreKey::getInstance() {
+    static KeystoreKey keystoreKey;
+
+    if (!keystoreKey.initialize()) {
+        return Error() << "Failed to initialize keystore key.";
+    } else {
+        return &keystoreKey;
+    }
+}
+
+static std::vector<KeyParameter> getSignOpParameters() {
+    std::vector<KeyParameter> opParameters;
+
+    KeyParameter algo;
+    algo.tag = Tag::ALGORITHM;
+    algo.value = KeyParameterValue::make<KeyParameterValue::algorithm>(Algorithm::RSA);
+    opParameters.push_back(algo);
+
+    KeyParameter digest;
+    digest.tag = Tag::DIGEST;
+    digest.value = KeyParameterValue::make<KeyParameterValue::digest>(Digest::SHA_2_256);
+    opParameters.push_back(digest);
+
+    KeyParameter padding;
+    padding.tag = Tag::PADDING;
+    padding.value =
+        KeyParameterValue::make<KeyParameterValue::paddingMode>(PaddingMode::RSA_PKCS1_1_5_SIGN);
+    opParameters.push_back(padding);
+
+    KeyParameter purpose;
+    purpose.tag = Tag::PURPOSE;
+    purpose.value = KeyParameterValue::make<KeyParameterValue::keyPurpose>(KeyPurpose::SIGN);
+    opParameters.push_back(purpose);
+
+    return opParameters;
+}
+
+Result<std::string> KeystoreKey::sign(const std::string& message) const {
+    static auto opParameters = getSignOpParameters();
+
+    CreateOperationResponse opResponse;
+
+    auto status =
+        mSecurityLevel->createOperation(getKeyDescriptor(), opParameters, false, &opResponse);
+    if (!status.isOk()) {
+        return Error() << "Failed to create keystore signing operation: "
+                       << status.serviceSpecificErrorCode();
+    }
+    auto operation = opResponse.iOperation;
+
+    std::optional<std::vector<uint8_t>> out;
+    status = operation->update({message.begin(), message.end()}, &out);
+    if (!status.isOk()) {
+        return Error() << "Failed to call keystore update operation.";
+    }
+
+    std::optional<std::vector<uint8_t>> signature;
+    status = operation->finish({}, {}, &signature);
+    if (!status.isOk()) {
+        return Error() << "Failed to call keystore finish operation.";
+    }
+
+    if (!signature.has_value()) {
+        return Error() << "Didn't receive a signature from keystore finish operation.";
+    }
+
+    std::string result{signature.value().begin(), signature.value().end()};
+
+    return result;
+}
+
+Result<std::vector<uint8_t>> KeystoreKey::getPublicKey() const {
+    auto cert = mKeyMetadata.certificate;
+    if (cert) {
+        return extractPublicKeyFromX509(cert.value());
+    } else {
+        return Error() << "Key did not have a certificate";
+    }
+}
diff --git a/ondevice-signing/KeystoreKey.h b/ondevice-signing/KeystoreKey.h
new file mode 100644
index 0000000..6b9cb57
--- /dev/null
+++ b/ondevice-signing/KeystoreKey.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include <android-base/macros.h>
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+
+#include <utils/StrongPointer.h>
+
+#include <android/system/keystore2/IKeystoreService.h>
+
+#include "SigningKey.h"
+
+class KeystoreKey : public SigningKey {
+    using IKeystoreService = ::android::system::keystore2::IKeystoreService;
+    using IKeystoreSecurityLevel = ::android::system::keystore2::IKeystoreSecurityLevel;
+    using KeyDescriptor = ::android::system::keystore2::KeyDescriptor;
+    using KeyMetadata = ::android::system::keystore2::KeyMetadata;
+
+  public:
+    virtual ~KeystoreKey(){};
+    static android::base::Result<SigningKey*> getInstance();
+
+    virtual android::base::Result<std::string> sign(const std::string& message) const;
+    virtual android::base::Result<std::vector<uint8_t>> getPublicKey() const;
+
+  private:
+    KeystoreKey();
+    bool initialize();
+    android::base::Result<KeyMetadata> createNewKey(const KeyDescriptor& descriptor);
+
+    android::sp<IKeystoreService> mService;
+    android::sp<IKeystoreSecurityLevel> mSecurityLevel;
+    KeyMetadata mKeyMetadata;
+};
diff --git a/ondevice-signing/OWNERS b/ondevice-signing/OWNERS
new file mode 100644
index 0000000..72a8eb5
--- /dev/null
+++ b/ondevice-signing/OWNERS
@@ -0,0 +1,3 @@
+maco@google.com
+ngeoffray@google.com
+oth@google.com
diff --git a/ondevice-signing/PREUPLOAD.cfg b/ondevice-signing/PREUPLOAD.cfg
new file mode 100644
index 0000000..4c6fbd6
--- /dev/null
+++ b/ondevice-signing/PREUPLOAD.cfg
@@ -0,0 +1,11 @@
+[Hook Scripts]
+checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
+
+[Builtin Hooks]
+clang_format = true
+commit_msg_changeid_field = true
+commit_msg_test_field = true
+gofmt = true
+
+[Builtin Hooks Options]
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
\ No newline at end of file
diff --git a/ondevice-signing/SigningKey.h b/ondevice-signing/SigningKey.h
new file mode 100644
index 0000000..89294fc
--- /dev/null
+++ b/ondevice-signing/SigningKey.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <android-base/macros.h>
+#include <android-base/result.h>
+
+class SigningKey {
+  public:
+    virtual ~SigningKey(){};
+    /* Sign a message with an initialized signing key */
+    virtual android::base::Result<std::string> sign(const std::string& message) const = 0;
+    /* Retrieve the associated public key */
+    virtual android::base::Result<std::vector<uint8_t>> getPublicKey() const = 0;
+};
diff --git a/ondevice-signing/TEST_MAPPING b/ondevice-signing/TEST_MAPPING
new file mode 100644
index 0000000..03b9b95
--- /dev/null
+++ b/ondevice-signing/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "odsign_e2e_tests"
+    }
+  ]
+}
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
new file mode 100644
index 0000000..cab92e2
--- /dev/null
+++ b/ondevice-signing/VerityUtils.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <filesystem>
+#include <map>
+#include <span>
+#include <string>
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <libfsverity.h>
+#include <linux/fsverity.h>
+
+#include "CertUtils.h"
+#include "SigningKey.h"
+
+#define FS_VERITY_MAX_DIGEST_SIZE 64
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+using android::base::unique_fd;
+
+static const char* kFsVerityInitPath = "/system/bin/fsverity_init";
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define cpu_to_le16(v) ((__force __le16)(uint16_t)(v))
+#define le16_to_cpu(v) ((__force uint16_t)(__le16)(v))
+#else
+#define cpu_to_le16(v) ((__force __le16)__builtin_bswap16(v))
+#define le16_to_cpu(v) (__builtin_bswap16((__force uint16_t)(v)))
+#endif
+
+struct fsverity_signed_digest {
+    char magic[8]; /* must be "FSVerity" */
+    __le16 digest_algorithm;
+    __le16 digest_size;
+    __u8 digest[];
+};
+
+static std::string toHex(std::span<uint8_t> data) {
+    std::stringstream ss;
+    for (auto it = data.begin(); it != data.end(); ++it) {
+        ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
+    }
+    return ss.str();
+}
+
+static int read_callback(void* file, void* buf, size_t count) {
+    int* fd = (int*)file;
+    if (TEMP_FAILURE_RETRY(read(*fd, buf, count)) < 0) return errno ? -errno : -EIO;
+    return 0;
+}
+
+Result<std::vector<uint8_t>> createDigest(const std::string& path) {
+    struct stat filestat;
+    unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) {
+        return ErrnoError() << "Failed to open " << path;
+    }
+
+    int ret = stat(path.c_str(), &filestat);
+    if (ret < 0) {
+        return ErrnoError() << "Failed to stat " << path;
+    }
+    struct libfsverity_merkle_tree_params params = {
+        .version = 1,
+        .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
+        .file_size = static_cast<uint64_t>(filestat.st_size),
+        .block_size = 4096,
+    };
+
+    struct libfsverity_digest* digest;
+    ret = libfsverity_compute_digest(&fd, &read_callback, &params, &digest);
+    if (ret < 0) {
+        return ErrnoError() << "Failed to compute fs-verity digest for " << path;
+    }
+    std::vector<uint8_t> digestVector(&digest->digest[0], &digest->digest[32]);
+    free(digest);
+    return digestVector;
+}
+
+namespace {
+template <typename T> struct DeleteAsPODArray {
+    void operator()(T* x) {
+        if (x) {
+            x->~T();
+            delete[](uint8_t*) x;
+        }
+    }
+};
+}  // namespace
+
+template <typename T> using trailing_unique_ptr = std::unique_ptr<T, DeleteAsPODArray<T>>;
+
+template <typename T>
+static trailing_unique_ptr<T> makeUniqueWithTrailingData(size_t trailing_data_size) {
+    uint8_t* memory = new uint8_t[sizeof(T*) + trailing_data_size];
+    T* ptr = new (memory) T;
+    return trailing_unique_ptr<T>{ptr};
+}
+
+static Result<std::vector<uint8_t>> signDigest(const SigningKey& key,
+                                               const std::vector<uint8_t>& digest) {
+    auto d = makeUniqueWithTrailingData<fsverity_signed_digest>(digest.size());
+
+    memcpy(d->magic, "FSVerity", 8);
+    d->digest_algorithm = cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
+    d->digest_size = cpu_to_le16(digest.size());
+    memcpy(d->digest, digest.data(), digest.size());
+
+    auto signed_digest = key.sign(std::string((char*)d.get(), sizeof(*d) + digest.size()));
+    if (!signed_digest.ok()) {
+        return signed_digest.error();
+    }
+
+    return std::vector<uint8_t>(signed_digest->begin(), signed_digest->end());
+}
+
+Result<std::string> enableFsVerity(const std::string& path, const SigningKey& key) {
+    auto digest = createDigest(path);
+    if (!digest.ok()) {
+        return digest.error();
+    }
+
+    auto signed_digest = signDigest(key, digest.value());
+    if (!signed_digest.ok()) {
+        return signed_digest.error();
+    }
+
+    auto pkcs7_data = createPkcs7(signed_digest.value());
+
+    struct fsverity_enable_arg arg = {.version = 1};
+
+    arg.sig_ptr = (uint64_t)pkcs7_data->data();
+    arg.sig_size = pkcs7_data->size();
+    arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
+    arg.block_size = 4096;
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+    int ret = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
+
+    if (ret != 0) {
+        return ErrnoError() << "Failed to call FS_IOC_ENABLE_VERITY on " << path;
+    }
+
+    // Return the root hash as a hex string
+    return toHex(digest.value());
+}
+
+Result<std::map<std::string, std::string>> addFilesToVerityRecursive(const std::string& path,
+                                                                     const SigningKey& key) {
+    std::map<std::string, std::string> digests;
+    std::error_code ec;
+
+    auto it = std::filesystem::recursive_directory_iterator(path, ec);
+    auto end = std::filesystem::recursive_directory_iterator();
+
+    while (!ec && it != end) {
+        if (it->is_regular_file()) {
+            LOG(INFO) << "Adding " << it->path() << " to fs-verity...";
+            auto result = enableFsVerity(it->path(), key);
+            if (!result.ok()) {
+                return result.error();
+            }
+            digests[it->path()] = *result;
+        }
+        ++it;
+    }
+    if (ec) {
+        return Error() << "Failed to iterate " << path << ": " << ec;
+    }
+
+    return digests;
+}
+
+Result<std::string> isFileInVerity(const std::string& path) {
+    unsigned int flags;
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) {
+        return ErrnoError() << "Failed to open " << path;
+    }
+
+    int ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
+    if (ret < 0) {
+        return ErrnoError() << "Failed to FS_IOC_GETFLAGS for " << path;
+    }
+    if (!(flags & FS_VERITY_FL)) {
+        return Error() << "File is not in fs-verity: " << path;
+    }
+
+    auto d = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE);
+    d->digest_size = FS_VERITY_MAX_DIGEST_SIZE;
+    ret = ioctl(fd, FS_IOC_MEASURE_VERITY, d.get());
+    if (ret < 0) {
+        return ErrnoError() << "Failed to FS_IOC_MEASURE_VERITY for " << path;
+    }
+    return toHex({&d->digest[0], &d->digest[d->digest_size]});
+}
+
+Result<std::map<std::string, std::string>> verifyAllFilesInVerity(const std::string& path) {
+    std::map<std::string, std::string> digests;
+    std::error_code ec;
+
+    auto it = std::filesystem::recursive_directory_iterator(path, ec);
+    auto end = std::filesystem::recursive_directory_iterator();
+
+    while (!ec && it != end) {
+        if (it->is_regular_file()) {
+            // Verify
+            auto result = isFileInVerity(it->path());
+            if (!result.ok()) {
+                return result.error();
+            }
+            digests[it->path()] = *result;
+        }  // TODO reject other types besides dirs?
+        ++it;
+    }
+    if (ec) {
+        return Error() << "Failed to iterate " << path << ": " << ec;
+    }
+
+    return digests;
+}
+
+Result<void> addCertToFsVerityKeyring(const std::string& path) {
+    const char* const argv[] = {kFsVerityInitPath, "--load-extra-key", "fsv_ods"};
+
+    int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+    pid_t pid = fork();
+    if (pid == 0) {
+        dup2(fd, STDIN_FILENO);
+        close(fd);
+        int argc = arraysize(argv);
+        char* argv_child[argc + 1];
+        memcpy(argv_child, argv, argc * sizeof(char*));
+        argv_child[argc] = nullptr;
+        execvp(argv_child[0], const_cast<char**>(argv_child));
+        PLOG(ERROR) << "exec in ForkExecvp";
+        _exit(EXIT_FAILURE);
+    } else {
+        close(fd);
+    }
+    if (pid == -1) {
+        return ErrnoError() << "Failed to fork.";
+    }
+    int status;
+    if (waitpid(pid, &status, 0) == -1) {
+        return ErrnoError() << "waitpid() failed.";
+    }
+    if (!WIFEXITED(status)) {
+        return Error() << kFsVerityInitPath << ": abnormal process exit";
+    }
+    if (WEXITSTATUS(status)) {
+        if (status != 0) {
+            return Error() << kFsVerityInitPath << " exited with " << status;
+        }
+    }
+
+    return {};
+}
diff --git a/ondevice-signing/VerityUtils.h b/ondevice-signing/VerityUtils.h
new file mode 100644
index 0000000..84af319
--- /dev/null
+++ b/ondevice-signing/VerityUtils.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <android-base/result.h>
+
+#include "SigningKey.h"
+
+android::base::Result<void> addCertToFsVerityKeyring(const std::string& path);
+android::base::Result<std::vector<uint8_t>> createDigest(const std::string& path);
+android::base::Result<std::map<std::string, std::string>>
+verifyAllFilesInVerity(const std::string& path);
+android::base::Result<std::map<std::string, std::string>>
+addFilesToVerityRecursive(const std::string& path, const SigningKey& key);
diff --git a/ondevice-signing/odsign.rc b/ondevice-signing/odsign.rc
new file mode 100644
index 0000000..044bae7
--- /dev/null
+++ b/ondevice-signing/odsign.rc
@@ -0,0 +1,6 @@
+service odsign /system/bin/odsign
+    class core
+    user root
+    group system
+    oneshot
+    disabled # does not start with the core class
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
new file mode 100644
index 0000000..6cab8b6
--- /dev/null
+++ b/ondevice-signing/odsign_main.cpp
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <fcntl.h>
+#include <filesystem>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <iterator>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <logwrap/logwrap.h>
+#include <odrefresh/odrefresh.h>
+
+#include "CertUtils.h"
+#include "KeymasterSigningKey.h"
+#include "KeystoreKey.h"
+#include "VerityUtils.h"
+
+#include "odsign_info.pb.h"
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+using android::base::SetProperty;
+
+using OdsignInfo = ::odsign::proto::OdsignInfo;
+
+const std::string kSigningKeyBlob = "/data/misc/odsign/key.blob";
+const std::string kSigningKeyCert = "/data/misc/odsign/key.cert";
+const std::string kOdsignInfo = "/data/misc/odsign/odsign.info";
+const std::string kOdsignInfoSignature = "/data/misc/odsign/odsign.info.signature";
+
+const std::string kArtArtifactsDir = "/data/misc/apexdata/com.android.art/dalvik-cache";
+
+static const char* kOdrefreshPath = "/apex/com.android.art/bin/odrefresh";
+
+static const char* kFsVerityProcPath = "/proc/sys/fs/verity";
+
+static const bool kForceCompilation = false;
+static const bool kUseKeystore = true;
+
+static const char* kOdsignVerificationDoneProp = "odsign.verification.done";
+static const char* kOdsignKeyDoneProp = "odsign.key.done";
+
+static const char* kOdsignVerificationStatusProp = "odsign.verification.success";
+static const char* kOdsignVerificationStatusValid = "1";
+static const char* kOdsignVerificationStatusError = "0";
+
+Result<void> verifyExistingCert(const SigningKey& key) {
+    if (access(kSigningKeyCert.c_str(), F_OK) < 0) {
+        return ErrnoError() << "Key certificate not found: " << kSigningKeyCert;
+    }
+    auto trustedPublicKey = key.getPublicKey();
+    if (!trustedPublicKey.ok()) {
+        return Error() << "Failed to retrieve signing public key.";
+    }
+
+    auto publicKeyFromExistingCert = extractPublicKeyFromX509(kSigningKeyCert);
+    if (!publicKeyFromExistingCert.ok()) {
+        return publicKeyFromExistingCert.error();
+    }
+    if (publicKeyFromExistingCert.value() != trustedPublicKey.value()) {
+        return Error() << "Public key of existing certificate at " << kSigningKeyCert
+                       << " does not match signing public key.";
+    }
+
+    // At this point, we know the cert matches
+    return {};
+}
+
+Result<void> createX509Cert(const SigningKey& key, const std::string& outPath) {
+    auto publicKey = key.getPublicKey();
+
+    if (!publicKey.ok()) {
+        return publicKey.error();
+    }
+
+    auto keymasterSignFunction = [&](const std::string& to_be_signed) {
+        return key.sign(to_be_signed);
+    };
+    createSelfSignedCertificate(*publicKey, keymasterSignFunction, outPath);
+    return {};
+}
+
+art::odrefresh::ExitCode compileArtifacts(bool force) {
+    const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
+    const int exit_code =
+        logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
+    return static_cast<art::odrefresh::ExitCode>(exit_code);
+}
+
+static std::string toHex(const std::vector<uint8_t>& digest) {
+    std::stringstream ss;
+    for (auto it = digest.begin(); it != digest.end(); ++it) {
+        ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
+    }
+    return ss.str();
+}
+
+Result<std::map<std::string, std::string>> computeDigests(const std::string& path) {
+    std::error_code ec;
+    std::map<std::string, std::string> digests;
+
+    auto it = std::filesystem::recursive_directory_iterator(path, ec);
+    auto end = std::filesystem::recursive_directory_iterator();
+
+    while (!ec && it != end) {
+        if (it->is_regular_file()) {
+            auto digest = createDigest(it->path());
+            if (!digest.ok()) {
+                return Error() << "Failed to compute digest for " << it->path();
+            }
+            digests[it->path()] = toHex(*digest);
+        }
+        ++it;
+    }
+    if (ec) {
+        return Error() << "Failed to iterate " << path << ": " << ec;
+    }
+
+    return digests;
+}
+
+Result<void> verifyDigests(const std::map<std::string, std::string>& digests,
+                           const std::map<std::string, std::string>& trusted_digests) {
+    for (const auto& path_digest : digests) {
+        auto path = path_digest.first;
+        auto digest = path_digest.second;
+        if ((trusted_digests.count(path) == 0)) {
+            return Error() << "Couldn't find digest for " << path;
+        }
+        if (trusted_digests.at(path) != digest) {
+            return Error() << "Digest mismatch for " << path;
+        }
+    }
+
+    // All digests matched!
+    if (digests.size() > 0) {
+        LOG(INFO) << "All root hashes match.";
+    }
+    return {};
+}
+
+Result<void> verifyIntegrityFsVerity(const std::map<std::string, std::string>& trusted_digests) {
+    // Just verify that the files are in verity, and get their digests
+    auto result = verifyAllFilesInVerity(kArtArtifactsDir);
+    if (!result.ok()) {
+        return result.error();
+    }
+
+    return verifyDigests(*result, trusted_digests);
+}
+
+Result<void> verifyIntegrityNoFsVerity(const std::map<std::string, std::string>& trusted_digests) {
+    // On these devices, just compute the digests, and verify they match the ones we trust
+    auto result = computeDigests(kArtArtifactsDir);
+    if (!result.ok()) {
+        return result.error();
+    }
+
+    return verifyDigests(*result, trusted_digests);
+}
+
+Result<OdsignInfo> getOdsignInfo(const SigningKey& key) {
+    std::string persistedSignature;
+    OdsignInfo odsignInfo;
+
+    if (!android::base::ReadFileToString(kOdsignInfoSignature, &persistedSignature)) {
+        return ErrnoError() << "Failed to read " << kOdsignInfoSignature;
+    }
+
+    std::fstream odsign_info(kOdsignInfo, std::ios::in | std::ios::binary);
+    if (!odsign_info) {
+        return Error() << "Failed to open " << kOdsignInfo;
+    }
+    odsign_info.seekg(0);
+    // Verify the hash
+    std::string odsign_info_str((std::istreambuf_iterator<char>(odsign_info)),
+                                std::istreambuf_iterator<char>());
+
+    auto publicKey = key.getPublicKey();
+    auto signResult = verifySignature(odsign_info_str, persistedSignature, *publicKey);
+    if (!signResult.ok()) {
+        return Error() << kOdsignInfoSignature << " does not match.";
+    } else {
+        LOG(INFO) << kOdsignInfoSignature << " matches.";
+    }
+
+    odsign_info.seekg(0);
+    if (!odsignInfo.ParseFromIstream(&odsign_info)) {
+        return Error() << "Failed to parse " << kOdsignInfo;
+    }
+
+    LOG(INFO) << "Loaded " << kOdsignInfo;
+    return odsignInfo;
+}
+
+Result<void> persistDigests(const std::map<std::string, std::string>& digests,
+                            const SigningKey& key) {
+    OdsignInfo signInfo;
+    google::protobuf::Map<std::string, std::string> proto_hashes(digests.begin(), digests.end());
+    auto map = signInfo.mutable_file_hashes();
+    *map = proto_hashes;
+
+    std::fstream odsign_info(kOdsignInfo,
+                             std::ios::in | std::ios::out | std::ios::trunc | std::ios::binary);
+    if (!signInfo.SerializeToOstream(&odsign_info)) {
+        return Error() << "Failed to persist root hashes in " << kOdsignInfo;
+    }
+
+    // Sign the signatures with our key itself, and write that to storage
+    odsign_info.seekg(0, std::ios::beg);
+    std::string odsign_info_str((std::istreambuf_iterator<char>(odsign_info)),
+                                std::istreambuf_iterator<char>());
+    auto signResult = key.sign(odsign_info_str);
+    if (!signResult.ok()) {
+        return Error() << "Failed to sign " << kOdsignInfo;
+    }
+    android::base::WriteStringToFile(*signResult, kOdsignInfoSignature);
+    return {};
+}
+
+static int removeArtifacts() {
+    std::error_code ec;
+    auto num_removed = std::filesystem::remove_all(kArtArtifactsDir, ec);
+    if (ec) {
+        LOG(ERROR) << "Can't remove " << kArtArtifactsDir << ": " << ec.message();
+        return 0;
+    } else {
+        if (num_removed > 0) {
+            LOG(INFO) << "Removed " << num_removed << " entries from " << kArtArtifactsDir;
+        }
+        return num_removed;
+    }
+}
+
+static Result<void> verifyArtifacts(const SigningKey& key, bool supportsFsVerity) {
+    auto signInfo = getOdsignInfo(key);
+    // Tell init we're done with the key; this is a boot time optimization
+    // in particular for the no fs-verity case, where we need to do a
+    // costly verification. If the files haven't been tampered with, which
+    // should be the common path, the verification will succeed, and we won't
+    // need the key anymore. If it turns out the artifacts are invalid (eg not
+    // in fs-verity) or the hash doesn't match, we won't be able to generate
+    // new artifacts without the key, so in those cases, remove the artifacts,
+    // and use JIT zygote for the current boot. We should recover automatically
+    // by the next boot.
+    SetProperty(kOdsignKeyDoneProp, "1");
+    if (!signInfo.ok()) {
+        return Error() << signInfo.error().message();
+    }
+    std::map<std::string, std::string> trusted_digests(signInfo->file_hashes().begin(),
+                                                       signInfo->file_hashes().end());
+    Result<void> integrityStatus;
+
+    if (supportsFsVerity) {
+        integrityStatus = verifyIntegrityFsVerity(trusted_digests);
+    } else {
+        integrityStatus = verifyIntegrityNoFsVerity(trusted_digests);
+    }
+    if (!integrityStatus.ok()) {
+        return Error() << integrityStatus.error().message();
+    }
+
+    return {};
+}
+
+int main(int /* argc */, char** /* argv */) {
+    auto errorScopeGuard = []() {
+        // In case we hit any error, remove the artifacts and tell Zygote not to use anything
+        removeArtifacts();
+        // Tell init we don't need to use our key anymore
+        SetProperty(kOdsignKeyDoneProp, "1");
+        // Tell init we're done with verification, and that it was an error
+        SetProperty(kOdsignVerificationDoneProp, "1");
+        SetProperty(kOdsignVerificationStatusProp, kOdsignVerificationStatusError);
+    };
+    auto scope_guard = android::base::make_scope_guard(errorScopeGuard);
+
+    if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
+        LOG(INFO) << "Device doesn't support updatable APEX, exiting.";
+        return 0;
+    }
+
+    SigningKey* key;
+    if (kUseKeystore) {
+        auto keystoreResult = KeystoreKey::getInstance();
+        if (!keystoreResult.ok()) {
+            LOG(ERROR) << "Could not create keystore key: " << keystoreResult.error().message();
+            return -1;
+        }
+        key = keystoreResult.value();
+    } else {
+        // TODO - keymaster will go away
+        auto keymasterResult = KeymasterSigningKey::getInstance();
+        if (!keymasterResult.ok()) {
+            LOG(ERROR) << "Failed to create keymaster key: " << keymasterResult.error().message();
+            return -1;
+        }
+        key = keymasterResult.value();
+    }
+
+    bool supportsFsVerity = access(kFsVerityProcPath, F_OK) == 0;
+    if (!supportsFsVerity) {
+        LOG(INFO) << "Device doesn't support fsverity. Falling back to full verification.";
+    }
+
+    if (supportsFsVerity) {
+        auto existing_cert = verifyExistingCert(*key);
+        if (!existing_cert.ok()) {
+            LOG(WARNING) << existing_cert.error().message();
+
+            // Try to create a new cert
+            auto new_cert = createX509Cert(*key, kSigningKeyCert);
+            if (!new_cert.ok()) {
+                LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error().message();
+                // TODO apparently the key become invalid - delete the blob / cert
+                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;
+        }
+    }
+
+    art::odrefresh::ExitCode odrefresh_status = compileArtifacts(kForceCompilation);
+    if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
+        LOG(INFO) << "odrefresh said artifacts are VALID";
+        // A post-condition of validating artifacts is that if the ones on /system
+        // are used, kArtArtifactsDir is removed. Conversely, if kArtArtifactsDir
+        // exists, those are artifacts that will be used, and we should verify them.
+        int err = access(kArtArtifactsDir.c_str(), F_OK);
+        // If we receive any error other than ENOENT, be suspicious
+        bool artifactsPresent = (err == 0) || (err < 0 && errno != ENOENT);
+        if (artifactsPresent) {
+            auto verificationResult = verifyArtifacts(*key, supportsFsVerity);
+            if (!verificationResult.ok()) {
+                LOG(ERROR) << verificationResult.error().message();
+                return -1;
+            }
+        }
+    } else if (odrefresh_status == art::odrefresh::ExitCode::kCompilationSuccess ||
+               odrefresh_status == art::odrefresh::ExitCode::kCompilationFailed) {
+        const bool compiled_all = odrefresh_status == art::odrefresh::ExitCode::kCompilationSuccess;
+        LOG(INFO) << "odrefresh compiled " << (compiled_all ? "all" : "partial")
+                  << " artifacts, returned " << odrefresh_status;
+        Result<std::map<std::string, std::string>> digests;
+        if (supportsFsVerity) {
+            digests = addFilesToVerityRecursive(kArtArtifactsDir, *key);
+        } else {
+            // If we can't use verity, just compute the root hashes and store
+            // those, so we can reverify them at the next boot.
+            digests = computeDigests(kArtArtifactsDir);
+        }
+        if (!digests.ok()) {
+            LOG(ERROR) << digests.error().message();
+            return -1;
+        }
+        auto persistStatus = persistDigests(*digests, *key);
+        if (!persistStatus.ok()) {
+            LOG(ERROR) << persistStatus.error().message();
+            return -1;
+        }
+    } else if (odrefresh_status == art::odrefresh::ExitCode::kCleanupFailed) {
+        LOG(ERROR) << "odrefresh failed cleaning up existing artifacts";
+        return -1;
+    } else {
+        LOG(ERROR) << "odrefresh exited unexpectedly, returned " << odrefresh_status;
+        return -1;
+    }
+
+    LOG(INFO) << "On-device signing done.";
+
+    scope_guard.Disable();
+    // At this point, we're done with the key for sure
+    SetProperty(kOdsignKeyDoneProp, "1");
+    // And we did a successful verification
+    SetProperty(kOdsignVerificationDoneProp, "1");
+    SetProperty(kOdsignVerificationStatusProp, kOdsignVerificationStatusValid);
+    return 0;
+}
diff --git a/keystore/key_attestation_log_handler.h b/ondevice-signing/proto/Android.bp
similarity index 64%
rename from keystore/key_attestation_log_handler.h
rename to ondevice-signing/proto/Android.bp
index a418bfa..fd48f31 100644
--- a/keystore/key_attestation_log_handler.h
+++ b/ondevice-signing/proto/Android.bp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef _KEY_ATTESTATION_LOG_HANDLER_H_
-#define _KEY_ATTESTATION_LOG_HANDLER_H_
-
-namespace keystore {
-
-void logKeystoreKeyAttestationEvent(bool wasSuccessful, int32_t errorCode);
-
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-#endif  //_KEY_ATTESTATION_LOG_HANDLER_H_
+cc_library_static {
+    name: "lib_odsign_proto",
+    host_supported: true,
+    proto: {
+        export_proto_headers: true,
+        type: "full",
+    },
+    srcs: ["odsign_info.proto"],
+}
diff --git a/keystore/binder/android/security/keymaster/OperationResult.aidl b/ondevice-signing/proto/odsign_info.proto
similarity index 73%
copy from keystore/binder/android/security/keymaster/OperationResult.aidl
copy to ondevice-signing/proto/odsign_info.proto
index db689d4..9d49c6c 100644
--- a/keystore/binder/android/security/keymaster/OperationResult.aidl
+++ b/ondevice-signing/proto/odsign_info.proto
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,7 +14,11 @@
  * limitations under the License.
  */
 
-package android.security.keymaster;
+syntax = "proto3";
 
-/* @hide */
-parcelable OperationResult cpp_header "keystore/OperationResult.h";
+package odsign.proto;
+
+message OdsignInfo {
+  // Map of artifact files to their hashes
+  map<string, string> file_hashes = 1;
+}
diff --git a/provisioner/Android.bp b/provisioner/Android.bp
new file mode 100644
index 0000000..12a21d1
--- /dev/null
+++ b/provisioner/Android.bp
@@ -0,0 +1,69 @@
+//
+// Copyright (C) 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_security_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+aidl_interface {
+    name: "android.security.provisioner",
+    unstable: true,
+    local_include_dir: "binder",
+    srcs: [
+        "binder/android/security/provisioner/*.aidl",
+    ],
+    backend: {
+        java: {
+            platform_apis: true,
+        },
+        cpp: {
+            enabled: false,
+        },
+        ndk: {
+            enabled: false,
+        },
+    },
+}
+
+java_binary {
+    name: "provisioner_cli",
+    wrapper: "provisioner_cli",
+    srcs: ["src/com/android/commands/provisioner/**/*.java"],
+    static_libs: [
+        "android.security.provisioner-java",
+    ],
+}
+
+cc_binary {
+    name: "rkp_factory_extraction_tool",
+    srcs: ["rkp_factory_extraction_tool.cpp"],
+    shared_libs: [
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "libbinder",
+        "libbinder_ndk",
+        "libcppbor_external",
+        "libcppcose_rkp",
+        "libcrypto",
+        "liblog",
+        "libvintf",
+    ],
+    //export_include_dirs: ["include"],
+}
diff --git a/keystore/binder/android/security/keystore/ICredstoreTokenCallback.aidl b/provisioner/binder/android/security/provisioner/IProvisionerService.aidl
similarity index 67%
rename from keystore/binder/android/security/keystore/ICredstoreTokenCallback.aidl
rename to provisioner/binder/android/security/provisioner/IProvisionerService.aidl
index b42e3d4..f81e9ab 100644
--- a/keystore/binder/android/security/keystore/ICredstoreTokenCallback.aidl
+++ b/provisioner/binder/android/security/provisioner/IProvisionerService.aidl
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-package android.security.keystore;
-
+package android.security.provisioner;
 
 /**
  * @hide
  */
-oneway interface ICredstoreTokenCallback {
-	void onFinished(boolean success, in byte[] authToken, in byte[] verificationToken);
+interface IProvisionerService {
+    byte[] getCertificateRequest(in boolean testMode,
+                                 in int keyCount,
+                                 in byte[] endpointEncryptionKey,
+                                 in byte[] challenge) = 0;
 }
diff --git a/provisioner/provisioner_cli b/provisioner/provisioner_cli
new file mode 100755
index 0000000..7b53d6e
--- /dev/null
+++ b/provisioner/provisioner_cli
@@ -0,0 +1,21 @@
+#!/system/bin/sh
+#
+# Copyright (C) 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.
+#
+# Script to start "provisioner_cli" on the device.
+#
+base=/system
+export CLASSPATH=$base/framework/provisioner_cli.jar
+exec app_process $base/bin com.android.commands.provisioner.Cli "$@"
diff --git a/provisioner/rkp_factory_extraction_tool.cpp b/provisioner/rkp_factory_extraction_tool.cpp
new file mode 100644
index 0000000..d4842b1
--- /dev/null
+++ b/provisioner/rkp_factory_extraction_tool.cpp
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+#include <string>
+#include <vector>
+
+#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <android/binder_manager.h>
+#include <cppbor.h>
+#include <keymaster/cppcose/cppcose.h>
+#include <log/log.h>
+#include <vintf/VintfObject.h>
+
+using std::set;
+using std::string;
+using std::vector;
+
+using aidl::android::hardware::security::keymint::DeviceInfo;
+using aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent;
+using aidl::android::hardware::security::keymint::MacedPublicKey;
+using aidl::android::hardware::security::keymint::ProtectedData;
+
+using android::vintf::HalManifest;
+using android::vintf::VintfObject;
+
+using namespace cppbor;
+using namespace cppcose;
+
+namespace {
+
+const string kPackage = "android.hardware.security.keymint";
+const string kInterface = "IRemotelyProvisionedComponent";
+const string kFormattedName = kPackage + "." + kInterface + "/";
+
+ErrMsgOr<vector<uint8_t>> generateEekChain(size_t length, const vector<uint8_t>& eekId) {
+    auto eekChain = cppbor::Array();
+
+    vector<uint8_t> prevPrivKey;
+    for (size_t i = 0; i < length - 1; ++i) {
+        vector<uint8_t> pubKey(ED25519_PUBLIC_KEY_LEN);
+        vector<uint8_t> privKey(ED25519_PRIVATE_KEY_LEN);
+
+        ED25519_keypair(pubKey.data(), privKey.data());
+
+        // The first signing key is self-signed.
+        if (prevPrivKey.empty()) prevPrivKey = privKey;
+
+        auto coseSign1 = constructCoseSign1(prevPrivKey,
+                                            cppbor::Map() /* payload CoseKey */
+                                                .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
+                                                .add(CoseKey::ALGORITHM, EDDSA)
+                                                .add(CoseKey::CURVE, ED25519)
+                                                .add(CoseKey::PUBKEY_X, pubKey)
+                                                .canonicalize()
+                                                .encode(),
+                                            {} /* AAD */);
+        if (!coseSign1) return coseSign1.moveMessage();
+        eekChain.add(coseSign1.moveValue());
+
+        prevPrivKey = privKey;
+    }
+
+    vector<uint8_t> pubKey(X25519_PUBLIC_VALUE_LEN);
+    vector<uint8_t> privKey(X25519_PRIVATE_KEY_LEN);
+    X25519_keypair(pubKey.data(), privKey.data());
+
+    auto coseSign1 = constructCoseSign1(prevPrivKey,
+                                        cppbor::Map() /* payload CoseKey */
+                                            .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
+                                            .add(CoseKey::KEY_ID, eekId)
+                                            .add(CoseKey::ALGORITHM, ECDH_ES_HKDF_256)
+                                            .add(CoseKey::CURVE, cppcose::X25519)
+                                            .add(CoseKey::PUBKEY_X, pubKey)
+                                            .canonicalize()
+                                            .encode(),
+                                        {} /* AAD */);
+    if (!coseSign1) return coseSign1.moveMessage();
+    eekChain.add(coseSign1.moveValue());
+
+    return eekChain.encode();
+}
+
+std::vector<uint8_t> getChallenge() {
+    return std::vector<uint8_t>(0);
+}
+
+std::vector<uint8_t> composeCertificateRequest(ProtectedData&& protectedData,
+                                               DeviceInfo&& deviceInfo) {
+    Array emptyMacedKeysToSign;
+    emptyMacedKeysToSign
+        .add(std::vector<uint8_t>(0))   // empty protected headers as bstr
+        .add(Map())                     // empty unprotected headers
+        .add(Null())                    // nil for the payload
+        .add(std::vector<uint8_t>(0));  // empty tag as bstr
+    Array certificateRequest;
+    certificateRequest.add(EncodedItem(std::move(deviceInfo.deviceInfo)))
+        .add(getChallenge())  // fake challenge
+        .add(EncodedItem(std::move(protectedData.protectedData)))
+        .add(std::move(emptyMacedKeysToSign));
+    return certificateRequest.encode();
+}
+
+int32_t errorMsg(string name) {
+    std::cerr << "Failed for rkp instance: " << name;
+    return -1;
+}
+
+}  // namespace
+
+int main() {
+    std::shared_ptr<const HalManifest> manifest = VintfObject::GetDeviceHalManifest();
+    set<string> rkpNames = manifest->getAidlInstances(kPackage, kInterface);
+    for (auto name : rkpNames) {
+        string fullName = kFormattedName + name;
+        if (!AServiceManager_isDeclared(fullName.c_str())) {
+            ALOGE("Could not find the following instance declared in the manifest: %s\n",
+                  fullName.c_str());
+            return errorMsg(name);
+        }
+        AIBinder* rkpAiBinder = AServiceManager_getService(fullName.c_str());
+        ::ndk::SpAIBinder rkp_binder(rkpAiBinder);
+        auto rkp_service = IRemotelyProvisionedComponent::fromBinder(rkp_binder);
+        std::vector<uint8_t> keysToSignMac;
+        std::vector<MacedPublicKey> emptyKeys;
+
+        // Replace this eek chain generation with the actual production GEEK
+        std::vector<uint8_t> eekId(10);  // replace with real KID later (EEK fingerprint)
+        auto eekOrErr = generateEekChain(3 /* chainlength */, eekId);
+        if (!eekOrErr) {
+            ALOGE("Failed to generate test EEK somehow: %s", eekOrErr.message().c_str());
+            return errorMsg(name);
+        }
+
+        std::vector<uint8_t> eek = eekOrErr.moveValue();
+        DeviceInfo deviceInfo;
+        ProtectedData protectedData;
+        if (rkp_service) {
+            ALOGE("extracting bundle");
+            ::ndk::ScopedAStatus status = rkp_service->generateCertificateRequest(
+                true /* testMode */, emptyKeys, eek, getChallenge(), &deviceInfo, &protectedData,
+                &keysToSignMac);
+            if (!status.isOk()) {
+                ALOGE("Bundle extraction failed. Error code: %d", status.getServiceSpecificError());
+                return errorMsg(name);
+            }
+            std::cout << "\n";
+            std::vector<uint8_t> certificateRequest =
+                composeCertificateRequest(std::move(protectedData), std::move(deviceInfo));
+            std::copy(certificateRequest.begin(), certificateRequest.end(),
+                      std::ostream_iterator<char>(std::cout));
+        }
+    }
+}
diff --git a/provisioner/src/com/android/commands/provisioner/Cli.java b/provisioner/src/com/android/commands/provisioner/Cli.java
new file mode 100644
index 0000000..62afdac
--- /dev/null
+++ b/provisioner/src/com/android/commands/provisioner/Cli.java
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+package com.android.commands.provisioner;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.security.provisioner.IProvisionerService;
+
+import com.android.internal.os.BaseCommand;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.IllegalArgumentException;
+
+/**
+ * Contains the implementation of the remote provisioning command-line interface.
+ */
+public class Cli extends BaseCommand {
+    /**
+     * Creates an instance of the command-line interface and runs it. This is the entry point of
+     * the tool.
+     */
+    public static void main(String[] args) {
+        new Cli().run(args);
+    }
+
+    /**
+     * Runs the command requested by the invoker. It parses the very first required argument, which
+     * is the command, and calls the appropriate handler.
+     */
+    @Override
+    public void onRun() throws Exception {
+        String cmd = nextArgRequired();
+        switch (cmd) {
+        case "get-req":
+            getRequest();
+            break;
+
+        case "help":
+            onShowUsage(System.out);
+            break;
+
+        default:
+            throw new IllegalArgumentException("unknown command: " + cmd);
+        }
+    }
+
+    /**
+     * Retrieves a 'certificate request' from the provisioning service. The COSE-encoded
+     * 'certificate chain' describing the endpoint encryption key (EEK) to use for encryption is
+     * read from the standard input. The retrieved request is written to the standard output.
+     */
+    private void getRequest() throws Exception {
+        // Process options.
+        boolean test = false;
+        byte[] challenge = null;
+        int count = 0;
+        String arg;
+        while ((arg = nextArg()) != null) {
+            switch (arg) {
+            case "--test":
+                test = true;
+                break;
+
+            case "--challenge":
+                // TODO: We may need a different encoding of the challenge.
+                challenge = nextArgRequired().getBytes();
+                break;
+
+            case "--count":
+                count = Integer.parseInt(nextArgRequired());
+                if (count < 0) {
+                    throw new IllegalArgumentException(
+                            "--count must be followed by non-negative number");
+                }
+                break;
+
+            default:
+                throw new IllegalArgumentException("unknown argument: " + arg);
+            }
+        }
+
+        // Send the request over to the provisioning service and write the result to stdout.
+        byte[] res = getService().getCertificateRequest(test, count, readAll(System.in), challenge);
+        if (res != null) {
+            System.out.write(res);
+        }
+    }
+
+    /**
+     * Retrieves an implementation of the IProvisionerService interface. It allows the caller to
+     * call into the service via binder.
+     */
+    private static IProvisionerService getService() throws RemoteException {
+        IBinder binder = ServiceManager.getService("remote-provisioner");
+        if (binder == null) {
+            throw new RemoteException("Provisioning service is inaccessible");
+        }
+        return IProvisionerService.Stub.asInterface(binder);
+    }
+
+    /** Reads all data from the provided input stream and returns it as a byte array. */
+    private static byte[] readAll(InputStream in) throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        byte[] buf = new byte[1024];
+        int read;
+        while ((read = in.read(buf)) != -1) {
+            out.write(buf, 0, read);
+        }
+        return out.toByteArray();
+    }
+
+    /**
+     * Writes the usage information to the given stream. This is displayed to users of the tool when
+     * they ask for help or when they pass incorrect arguments to the tool.
+     */
+    @Override
+    public void onShowUsage(PrintStream out) {
+        out.println(
+                "Usage: provisioner_cli <command> [options]\n" +
+                "Commands: help\n" +
+                "          get-req [--count <n>] [--test] [--challenge <v>]");
+    }
+}
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 120000
index 0000000..ee92d9e
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1 @@
+../../build/soong/scripts/rustfmt.toml
\ No newline at end of file