Merge "Add more crypto operations."
diff --git a/keystore/keystore_keymaster_enforcement.h b/keystore/keystore_keymaster_enforcement.h
index e7515a1..b0dae48 100644
--- a/keystore/keystore_keymaster_enforcement.h
+++ b/keystore/keystore_keymaster_enforcement.h
@@ -86,16 +86,19 @@
     }
 
     bool is_device_locked(int32_t userId) const override {
+        std::lock_guard<std::mutex> lock(is_device_locked_for_user_map_lock_);
         // If we haven't had a set call for this user yet, assume the device is locked.
         if (mIsDeviceLockedForUser.count(userId) == 0) return true;
         return mIsDeviceLockedForUser.find(userId)->second;
     }
 
     void set_device_locked(bool isLocked, int32_t userId) {
+        std::lock_guard<std::mutex> lock(is_device_locked_for_user_map_lock_);
         mIsDeviceLockedForUser[userId] = isLocked;
     }
 
   private:
+    mutable std::mutex is_device_locked_for_user_map_lock_;
     std::map<int32_t, bool> mIsDeviceLockedForUser;
 };
 
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 250646a..354a6d6 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -18,11 +18,15 @@
     srcs: ["src/lib.rs"],
 
     rustlibs: [
-        "android.system.keystore2-rust",
         "android.hardware.security.keymint-rust",
+        "android.security.apc-rust",
+        "android.security.compat-rust",
+        "android.system.keystore2-rust",
         "libanyhow",
         "libbinder_rs",
+        "libkeystore2_apc_compat-rust",
         "libkeystore2_crypto_rust",
+        "libkeystore2_km_compat",
         "libkeystore2_selinux",
         "liblazy_static",
         "liblibc",
@@ -42,12 +46,17 @@
     auto_gen_config: false,
     test_config: "AndroidTest.xml",
     rustlibs: [
+        "android.hardware.security.keymint-rust",
+        "android.security.apc-rust",
+        "android.security.compat-rust",
         "android.system.keystore2-rust",
         "android.hardware.security.keymint-rust",
         "libandroid_logger",
         "libanyhow",
         "libbinder_rs",
+        "libkeystore2_apc_compat-rust",
         "libkeystore2_crypto_rust",
+        "libkeystore2_km_compat",
         "libkeystore2_selinux",
         "liblazy_static",
         "liblibc",
diff --git a/keystore2/TEST_MAPPING b/keystore2/TEST_MAPPING
index 70e3175..33d157e 100644
--- a/keystore2/TEST_MAPPING
+++ b/keystore2/TEST_MAPPING
@@ -1,6 +1,12 @@
 {
   "presubmit": [
     {
+      "name": "keystore2_certificate_test"
+    },
+    {
+      "name": "keystore2_km_compat_test"
+    },
+    {
       "name": "keystore2_test"
     }
   ]
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
index 00be2b7..0d05dfe 100644
--- a/keystore2/aidl/Android.bp
+++ b/keystore2/aidl/Android.bp
@@ -14,9 +14,7 @@
 
 aidl_interface {
     name: "android.security.attestationmanager",
-    srcs: [
-        "android/security/attestationmanager/*.aidl",
-    ],
+    srcs: [ "android/security/attestationmanager/*.aidl", ],
     imports: [ "android.hardware.security.keymint" ],
     unstable: true,
     backend: {
@@ -47,3 +45,34 @@
     },
 }
 
+aidl_interface {
+    name: "android.security.apc",
+    srcs: [ "android/security/apc/*.aidl" ],
+    unstable: true,
+    backend: {
+        java: {
+            enabled: true,
+        },
+        rust: {
+            enabled: true,
+        },
+    },
+}
+
+aidl_interface {
+    name: "android.security.compat",
+    srcs: [ "android/security/compat/*.aidl" ],
+    imports: [ "android.hardware.security.keymint" ],
+    unstable: true,
+    backend: {
+        java: {
+            sdk_version: "module_current",
+        },
+        rust: {
+            enabled: true,
+        },
+        ndk: {
+            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..f47d7f5
--- /dev/null
+++ b/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
@@ -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.
+ */
+
+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.
+ */
+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..26ccf0f
--- /dev/null
+++ b/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
@@ -0,0 +1,70 @@
+/*
+ * 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;
+
+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..7ae3e1c
--- /dev/null
+++ b/keystore2/aidl/android/security/apc/ResponseCode.aidl
@@ -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 android.security.apc;
+
+/**
+ * Used as service specific exception code by IProtectedConfirmation and as result
+ * code by IConfirmationCallback
+ */
+@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/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
new file mode 100644
index 0000000..6a72c75
--- /dev/null
+++ b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
@@ -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.
+ */
+
+package android.security.compat;
+
+import android.hardware.security.keymint.IKeyMintDevice;
+import android.hardware.security.keymint.SecurityLevel;
+
+/**
+ */
+interface IKeystoreCompatService {
+    /**
+     */
+    IKeyMintDevice getKeyMintDevice (SecurityLevel securityLevel);
+}
diff --git a/keystore2/apc_compat/Android.bp b/keystore2/apc_compat/Android.bp
new file mode 100644
index 0000000..405e9b8
--- /dev/null
+++ b/keystore2/apc_compat/Android.bp
@@ -0,0 +1,56 @@
+// 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.
+
+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: [
+        "--whitelist-function=tryGetUserConfirmationService",
+        "--whitelist-function=promptUserConfirmation",
+        "--whitelist-function=abortUserConfirmation",
+        "--whitelist-function=closeUserConfirmationService",
+        "--whitelist-var=INVALID_SERVICE_HANDLE",
+        "--whitelist-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..4391f5b
--- /dev/null
+++ b/keystore2/apc_compat/apc_compat.rs
@@ -0,0 +1,201 @@
+// 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.
+    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]>),
+    {
+        let cb_data_ptr = Box::into_raw(Box::new(Box::new(cb)));
+        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/src/apc.rs b/keystore2/src/apc.rs
new file mode 100644
index 0000000..105e071
--- /dev/null
+++ b/keystore2/src/apc.rs
@@ -0,0 +1,366 @@
+// 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.
+
+// TODO The confirmation token is yet unused.
+#![allow(unused_variables)]
+
+//! 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::{Arc, Mutex},
+};
+
+use crate::utils::{compat_2_response_code, ui_opts_2_compat};
+use android_security_apc::aidl::android::security::apc::{
+    IConfirmationCallback::IConfirmationCallback,
+    IProtectedConfirmation::{BnProtectedConfirmation, IProtectedConfirmation},
+    ResponseCode::ResponseCode,
+};
+use android_security_apc::binder::{
+    ExceptionCode, Interface, Result as BinderResult, SpIBinder, Status as BinderStatus,
+};
+use anyhow::{Context, Result};
+use binder::{IBinder, ThreadState};
+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,
+}
+
+#[derive(Default)]
+struct ApcState {
+    session: Option<ApcSessionState>,
+    rate_limiting: HashMap<u32, RateInfo>,
+}
+
+/// 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() -> Result<impl IProtectedConfirmation> {
+        let result = BnProtectedConfirmation::new_binder(Self {
+            state: Arc::new(Mutex::new(Default::default())),
+        });
+        result.as_binder().set_requesting_sid(true);
+        Ok(result)
+    }
+
+    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) {
+            // If the user confirmed the dialog.
+            (ResponseCode::OK, _) => {
+                // Reset counter.
+                state.rate_limiting.remove(&uid);
+                // TODO at this point we need to send the confirmation token to where keystore can
+                // use it.
+            }
+            // 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;
+            }
+            // 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: &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,
+            |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: &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: &dyn IConfirmationCallback,
+        prompt_text: &str,
+        extra_data: &[u8],
+        locale: &str,
+        ui_option_flags: i32,
+    ) -> BinderResult<()> {
+        map_or_log_err(
+            self.present_prompt(listener, prompt_text, extra_data, locale, ui_option_flags),
+            Ok,
+        )
+    }
+    fn cancelPrompt(&self, listener: &dyn IConfirmationCallback) -> BinderResult<()> {
+        map_or_log_err(self.cancel_prompt(listener), Ok)
+    }
+    fn isSupported(&self) -> BinderResult<bool> {
+        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..6edd760
--- /dev/null
+++ b/keystore2/src/async_task.rs
@@ -0,0 +1,144 @@
+// 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::time::Duration;
+use std::{
+    collections::VecDeque,
+    sync::Arc,
+    sync::{Condvar, Mutex, MutexGuard},
+    thread,
+};
+
+#[derive(Debug, PartialEq, Eq)]
+enum State {
+    Exiting,
+    Running,
+}
+
+struct AsyncTaskState {
+    state: State,
+    thread: Option<thread::JoinHandle<()>>,
+    hi_prio_req: VecDeque<Box<dyn FnOnce() + Send>>,
+    lo_prio_req: VecDeque<Box<dyn FnOnce() + Send>>,
+}
+
+/// AsyncTask spawns one worker thread on demand to process jobs inserted into
+/// a low and a high priority work queue.
+pub struct AsyncTask {
+    state: Arc<(Condvar, Mutex<AsyncTaskState>)>,
+}
+
+impl Default for AsyncTask {
+    fn default() -> Self {
+        Self {
+            state: Arc::new((
+                Condvar::new(),
+                Mutex::new(AsyncTaskState {
+                    state: State::Exiting,
+                    thread: None,
+                    hi_prio_req: VecDeque::new(),
+                    lo_prio_req: VecDeque::new(),
+                }),
+            )),
+        }
+    }
+}
+
+impl AsyncTask {
+    /// Adds a 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: FnOnce() + Send + 'static,
+    {
+        self.queue(f, true)
+    }
+
+    /// Adds a 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() + Send + 'static,
+    {
+        self.queue(f, false)
+    }
+
+    fn queue<F>(&self, f: F, hi_prio: bool)
+    where
+        F: FnOnce() + 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();
+
+        state.thread = Some(thread::spawn(move || {
+            let (ref condvar, ref state) = *cloned_state;
+            loop {
+                if let Some(f) = {
+                    let (mut state, timeout) = condvar
+                        .wait_timeout_while(
+                            state.lock().unwrap(),
+                            Duration::from_secs(30),
+                            |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(f),
+                        (None, false, _) => state.lo_prio_req.pop_front(),
+                        (None, true, true) => {
+                            state.state = State::Exiting;
+                            break;
+                        }
+                        (None, true, false) => None,
+                    }
+                } {
+                    f()
+                }
+            }
+        }));
+        state.state = State::Running;
+    }
+}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 0c49340..df1c24c 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -46,12 +46,15 @@
 
 #![allow(dead_code)]
 
-use crate::db_utils::{self, SqlField};
 use crate::error::{Error as KsError, ResponseCode};
 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;
+use crate::{
+    db_utils::{self, SqlField},
+    gc::Gc,
+};
 use anyhow::{anyhow, Context, Result};
 use std::{convert::TryFrom, convert::TryInto, time::SystemTimeError};
 
@@ -314,6 +317,39 @@
     }
 }
 
+#[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`
@@ -593,25 +629,18 @@
                      key_type INTEGER,
                      domain INTEGER,
                      namespace INTEGER,
-                     alias BLOB);",
+                     alias BLOB,
+                     state INTEGER);",
             NO_PARAMS,
         )
         .context("Failed to initialize \"keyentry\" table.")?;
 
         conn.execute(
-            "CREATE VIEW IF NOT EXISTS persistent.orphaned AS
-                    SELECT id FROM persistent.keyentry WHERE domain IS NULL;",
-            NO_PARAMS,
-        )
-        .context("Failed to initialize \"orphaned\" view")?;
-
-        conn.execute(
             "CREATE TABLE IF NOT EXISTS persistent.blobentry (
                     id INTEGER PRIMARY KEY,
                     subcomponent_type INTEGER,
                     keyentryid INTEGER,
-                    blob BLOB,
-                    sec_level INTEGER);",
+                    blob BLOB);",
             NO_PARAMS,
         )
         .context("Failed to initialize \"blobentry\" table.")?;
@@ -692,6 +721,96 @@
         Ok(conn)
     }
 
+    /// Get one unreferenced key. There is no particular order in which the keys are returned.
+    fn get_unreferenced_key_id(tx: &Transaction) -> Result<Option<i64>> {
+        tx.query_row(
+            "SELECT id FROM persistent.keyentry WHERE state = ?",
+            params![KeyLifeCycle::Unreferenced],
+            |row| row.get(0),
+        )
+        .optional()
+        .context("In get_unreferenced_key_id: Trying to get unreferenced key id.")
+    }
+
+    /// Returns a key id guard and key entry for one unreferenced key entry. Of the optional
+    /// fields of the key entry only the km_blob field will be populated. This is required
+    /// to subject the blob to its KeyMint instance for deletion.
+    pub fn get_unreferenced_key(&mut self) -> Result<Option<(KeyIdGuard, KeyEntry)>> {
+        self.with_transaction(TransactionBehavior::Deferred, |tx| {
+            let key_id = match Self::get_unreferenced_key_id(tx)
+                .context("Trying to get unreferenced key id")?
+            {
+                None => return Ok(None),
+                Some(id) => KEY_ID_LOCK.try_get(id).ok_or_else(KsError::sys).context(concat!(
+                    "A key id lock was held for an unreferenced key. ",
+                    "This should never happen."
+                ))?,
+            };
+            let key_entry = Self::load_key_components(tx, KeyEntryLoadBits::KM, key_id.id())
+                .context("Trying to get key components.")?;
+            Ok(Some((key_id, key_entry)))
+        })
+        .context("In get_unreferenced_key.")
+    }
+
+    /// This function purges all remnants of a key entry from the database.
+    /// Important: This does not check if the key was unreferenced, nor does it
+    /// subject the key to its KeyMint instance for permanent invalidation.
+    /// This function should only be called by the garbage collector.
+    /// To delete a key call `mark_unreferenced`, which transitions the key to the unreferenced
+    /// state, deletes all grants to the key, and notifies the garbage collector.
+    /// The garbage collector will:
+    ///  1. Call get_unreferenced_key.
+    ///  2. Determine the proper way to dispose of sensitive key material, e.g., call
+    ///     `KeyMintDevice::delete()`.
+    ///  3. Call `purge_key_entry`.
+    pub fn purge_key_entry(&mut self, key_id: KeyIdGuard) -> Result<()> {
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            tx.execute("DELETE FROM persistent.keyentry WHERE id = ?;", params![key_id.id()])
+                .context("Trying to delete keyentry.")?;
+            tx.execute(
+                "DELETE FROM persistent.blobentry WHERE keyentryid = ?;",
+                params![key_id.id()],
+            )
+            .context("Trying to delete blobentries.")?;
+            tx.execute(
+                "DELETE FROM persistent.keymetadata WHERE keyentryid = ?;",
+                params![key_id.id()],
+            )
+            .context("Trying to delete keymetadata.")?;
+            tx.execute(
+                "DELETE FROM persistent.keyparameter WHERE keyentryid = ?;",
+                params![key_id.id()],
+            )
+            .context("Trying to delete keyparameters.")?;
+            let grants_deleted = tx
+                .execute("DELETE FROM persistent.grant WHERE keyentryid = ?;", params![key_id.id()])
+                .context("Trying to delete grants.")?;
+            if grants_deleted != 0 {
+                log::error!("Purged key that still had grants. This should not happen.");
+            }
+            Ok(())
+        })
+        .context("In purge_key_entry.")
+    }
+
+    /// 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> {
+        self.conn
+            .execute(
+                "UPDATE persistent.keyentry SET state = ? WHERE state = ?;",
+                params![KeyLifeCycle::Unreferenced, KeyLifeCycle::Existing],
+            )
+            .context("In cleanup_leftovers.")
+    }
+
     /// 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
@@ -719,11 +838,12 @@
                     key_type = ?
                     AND domain = ?
                     AND namespace = ?
-                    AND alias = ?;",
+                    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])
+                .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| {
@@ -746,22 +866,16 @@
                 let id = Self::insert_with_retry(|id| {
                     tx.execute(
                         "INSERT into persistent.keyentry
-                        (id, key_type, domain, namespace, alias)
-                        VALUES(?, ?, ?, ?, ?);",
-                        params![id, KeyType::Super, domain.0, namespace, alias],
+                        (id, key_type, domain, namespace, alias, state)
+                        VALUES(?, ?, ?, ?, ?, ?);",
+                        params![id, KeyType::Super, domain.0, namespace, alias, KeyLifeCycle::Live],
                     )
                 })
                 .context("In get_or_create_key_with.")?;
 
                 let (blob, metadata) = create_new_key().context("In get_or_create_key_with.")?;
-                Self::insert_blob_internal(
-                    &tx,
-                    id,
-                    SubComponentType::KEY_BLOB,
-                    &blob,
-                    SecurityLevel::SOFTWARE,
-                )
-                .context("In get_of_create_key_with.")?;
+                Self::insert_blob_internal(&tx, id, SubComponentType::KEY_BLOB, &blob)
+                    .context("In get_of_create_key_with.")?;
                 metadata.store_in_db(id, &tx).context("In get_or_create_key_with.")?;
                 (id, KeyEntry { id, km_blob: Some(blob), metadata, ..Default::default() })
             }
@@ -770,13 +884,40 @@
         Ok((KEY_ID_LOCK.get(id), entry))
     }
 
+    /// Creates a transaction with the given behavior and executes f with the new transaction.
+    /// The transaction is committed only if f returns Ok.
+    fn with_transaction<T, F>(&mut self, behavior: TransactionBehavior, f: F) -> Result<T>
+    where
+        F: FnOnce(&Transaction) -> Result<T>,
+    {
+        let tx = self
+            .conn
+            .transaction_with_behavior(behavior)
+            .context("In with_transaction: Failed to initialize transaction.")?;
+        f(&tx).and_then(|result| {
+            tx.commit().context("In with_transaction: Failed to commit transaction.")?;
+            Ok(result)
+        })
+    }
+
     /// 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(&self, domain: Domain, namespace: i64) -> Result<KeyIdGuard> {
+    pub fn create_key_entry(&mut self, domain: Domain, namespace: i64) -> Result<KeyIdGuard> {
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            Self::create_key_entry_internal(tx, domain, namespace)
+        })
+        .context("In create_key_entry.")
+    }
+
+    fn create_key_entry_internal(
+        tx: &Transaction,
+        domain: Domain,
+        namespace: i64,
+    ) -> Result<KeyIdGuard> {
         match domain {
             Domain::APP | Domain::SELINUX => {}
             _ => {
@@ -786,14 +927,20 @@
         }
         Ok(KEY_ID_LOCK.get(
             Self::insert_with_retry(|id| {
-                self.conn.execute(
+                tx.execute(
                     "INSERT into persistent.keyentry
-                     (id, key_type, domain, namespace, alias)
-                     VALUES(?, ?, ?, ?, NULL);",
-                    params![id, KeyType::Client, domain.0 as u32, namespace],
+                     (id, key_type, domain, namespace, alias, state)
+                     VALUES(?, ?, ?, ?, NULL, ?);",
+                    params![
+                        id,
+                        KeyType::Client,
+                        domain.0 as u32,
+                        namespace,
+                        KeyLifeCycle::Existing
+                    ],
                 )
             })
-            .context("In create_key_entry")?,
+            .context("In create_key_entry_internal")?,
         ))
     }
 
@@ -808,17 +955,11 @@
         key_id: &KeyIdGuard,
         sc_type: SubComponentType,
         blob: &[u8],
-        sec_level: SecurityLevel,
     ) -> Result<()> {
-        let tx = self
-            .conn
-            .transaction_with_behavior(TransactionBehavior::Immediate)
-            .context("In insert_blob: Failed to initialize transaction.")?;
-
-        Self::insert_blob_internal(&tx, key_id.0, sc_type, blob, sec_level)
-            .context("In insert_blob.")?;
-
-        tx.commit().context("In insert_blob: Failed to commit transaction.")
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            Self::insert_blob_internal(&tx, key_id.0, sc_type, blob)
+        })
+        .context("In insert_blob.")
     }
 
     fn insert_blob_internal(
@@ -826,12 +967,11 @@
         key_id: i64,
         sc_type: SubComponentType,
         blob: &[u8],
-        sec_level: SecurityLevel,
     ) -> Result<()> {
         tx.execute(
-            "INSERT into persistent.blobentry (subcomponent_type, keyentryid, blob, sec_level)
-                VALUES (?, ?, ?, ?);",
-            params![sc_type, key_id, blob, sec_level.0],
+            "INSERT into persistent.blobentry (subcomponent_type, keyentryid, blob)
+                VALUES (?, ?, ?);",
+            params![sc_type, key_id, blob],
         )
         .context("In insert_blob_internal: Failed to insert blob.")?;
         Ok(())
@@ -844,30 +984,36 @@
         key_id: &KeyIdGuard,
         params: impl IntoIterator<Item = &'a KeyParameter>,
     ) -> Result<()> {
-        let tx = self
-            .conn
-            .transaction_with_behavior(TransactionBehavior::Immediate)
-            .context("In insert_keyparameter: Failed to start transaction.")?;
-        {
-            let mut stmt = tx
-                .prepare(
-                    "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level)
-                    VALUES (?, ?, ?, ?);",
-                )
-                .context("In insert_keyparameter: Failed to prepare statement.")?;
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            Self::insert_keyparameter_internal(tx, key_id, params)
+        })
+        .context("In insert_keyparameter.")
+    }
 
-            let iter = params.into_iter();
-            for p in 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: Failed to insert {:?}", p))?;
-            }
+    fn insert_keyparameter_internal<'a>(
+        tx: &Transaction,
+        key_id: &KeyIdGuard,
+        params: impl IntoIterator<Item = &'a 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.")?;
+
+        let iter = params.into_iter();
+        for p in 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)
+            })?;
         }
-        tx.commit().context("In insert_keyparameter: Failed to commit transaction.")?;
         Ok(())
     }
 
@@ -877,19 +1023,30 @@
         key_id: &KeyIdGuard,
         metadata: &KeyMetaData,
     ) -> Result<()> {
-        let tx = self
-            .conn
-            .transaction_with_behavior(TransactionBehavior::Immediate)
-            .context("In insert_key_metadata: Failed to initialize transaction.")?;
-        metadata.store_in_db(key_id.0, &tx).context("In insert_key_metadata")?;
-        tx.commit().context("In insert_key_metadata: Failed to commit transaction")
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            metadata.store_in_db(key_id.0, &tx)
+        })
+        .context("In insert_key_metadata.")
+    }
+
+    fn rebind_alias(
+        &mut self,
+        newid: &KeyIdGuard,
+        alias: &str,
+        domain: Domain,
+        namespace: i64,
+    ) -> Result<()> {
+        self.with_transaction(TransactionBehavior::Immediate, |tx| {
+            Self::rebind_alias_internal(tx, newid, alias, domain, namespace)
+        })
+        .context("In rebind_alias.")
     }
 
     /// 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.
-    pub fn rebind_alias(
-        &mut self,
+    fn rebind_alias_internal(
+        tx: &Transaction,
         newid: &KeyIdGuard,
         alias: &str,
         domain: Domain,
@@ -899,41 +1056,94 @@
             Domain::APP | Domain::SELINUX => {}
             _ => {
                 return Err(KsError::sys()).context(format!(
-                    "In rebind_alias: Domain {:?} must be either App or SELinux.",
+                    "In rebind_alias_internal: Domain {:?} must be either App or SELinux.",
                     domain
                 ));
             }
         }
-        let tx = self
-            .conn
-            .transaction_with_behavior(TransactionBehavior::Immediate)
-            .context("In rebind_alias: Failed to initialize transaction.")?;
-        tx.execute(
-            "UPDATE persistent.keyentry
-                 SET alias = NULL, domain = NULL, namespace = NULL
+        let updated = tx
+            .execute(
+                "UPDATE persistent.keyentry
+                 SET alias = NULL, domain = NULL, namespace = NULL, state = ?
                  WHERE alias = ? AND domain = ? AND namespace = ?;",
-            params![alias, domain.0 as u32, namespace],
-        )
-        .context("In rebind_alias: Failed to rebind existing entry.")?;
+                params![KeyLifeCycle::Unreferenced, alias, domain.0 as u32, namespace],
+            )
+            .context("In rebind_alias_internal: Failed to rebind existing entry.")?;
+        if updated != 0 {
+            Gc::notify_gc();
+        }
         let result = tx
             .execute(
                 "UPDATE persistent.keyentry
-                    SET alias = ?
-                    WHERE id = ? AND domain = ? AND namespace = ?;",
-                params![alias, newid.0, domain.0 as u32, namespace],
+                    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.")?;
+            .context("In rebind_alias_internal: Failed to set alias.")?;
         if result != 1 {
-            // Note that this explicit rollback is not required, as
-            // the transaction should rollback if we do not commit it.
-            // We leave it here for readability.
-            tx.rollback().context("In rebind_alias: Failed to rollback a failed transaction.")?;
             return Err(KsError::sys()).context(format!(
-                "In rebind_alias: Expected to update a single entry but instead updated {}.",
+                "In rebind_alias_internal: Expected to update a single entry but instead updated {}.",
                 result
             ));
         }
-        tx.commit().context("In rebind_alias: Failed to commit transaction.")
+        Ok(())
+    }
+
+    /// 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.
+    pub fn store_new_key<'a>(
+        &mut self,
+        key: KeyDescriptor,
+        params: impl IntoIterator<Item = &'a KeyParameter>,
+        blob: &[u8],
+        cert: Option<&[u8]>,
+        cert_chain: Option<&[u8]>,
+        metadata: &KeyMetaData,
+    ) -> Result<KeyIdGuard> {
+        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)
+                .context("Trying to create new key entry.")?;
+            Self::insert_blob_internal(tx, key_id.id(), SubComponentType::KEY_BLOB, blob)
+                .context("Trying to insert the key blob.")?;
+            if let Some(cert) = cert {
+                Self::insert_blob_internal(tx, key_id.id(), SubComponentType::CERT, cert)
+                    .context("Trying to insert the certificate.")?;
+            }
+            if let Some(cert_chain) = cert_chain {
+                Self::insert_blob_internal(
+                    tx,
+                    key_id.id(),
+                    SubComponentType::CERT_CHAIN,
+                    cert_chain,
+                )
+                .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("Tryin to insert key metadata.")?;
+            Self::rebind_alias_internal(tx, &key_id, &alias, domain, namespace)
+                .context("Trying to rebind alias.")?;
+            Ok(key_id)
+        })
+        .context("In store_new_key.")
     }
 
     // Helper function loading the key_id given the key descriptor
@@ -952,11 +1162,12 @@
                     key_type =  ?
                     AND domain = ?
                     AND namespace = ?
-                    AND alias = ?;",
+                    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])
+            .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)?
@@ -1035,11 +1246,13 @@
                     .prepare(
                         "SELECT domain, namespace FROM persistent.keyentry
                             WHERE
-                            id = ?;",
+                            id = ?
+                            AND state = ?;",
                     )
                     .context("Domain::KEY_ID: prepare statement failed")?;
-                let mut rows =
-                    stmt.query(params![key.nspace]).context("Domain::KEY_ID: query failed.")?;
+                let mut rows = stmt
+                    .query(params![key.nspace, KeyLifeCycle::Live])
+                    .context("Domain::KEY_ID: query failed.")?;
                 let (domain, namespace): (Domain, i64) =
                     db_utils::with_rows_extract_one(&mut rows, |row| {
                         let r =
@@ -1065,10 +1278,10 @@
         key_id: i64,
         load_bits: KeyEntryLoadBits,
         tx: &Transaction,
-    ) -> Result<(SecurityLevel, Option<Vec<u8>>, Option<Vec<u8>>, Option<Vec<u8>>)> {
+    ) -> Result<(Option<Vec<u8>>, Option<Vec<u8>>, Option<Vec<u8>>)> {
         let mut stmt = tx
             .prepare(
-                "SELECT MAX(id), sec_level, subcomponent_type, blob FROM persistent.blobentry
+                "SELECT MAX(id), subcomponent_type, blob FROM persistent.blobentry
                     WHERE keyentryid = ? GROUP BY subcomponent_type;",
             )
             .context("In load_blob_components: prepare statement failed.")?;
@@ -1076,37 +1289,34 @@
         let mut rows =
             stmt.query(params![key_id]).context("In load_blob_components: query failed.")?;
 
-        let mut sec_level: SecurityLevel = Default::default();
         let mut km_blob: Option<Vec<u8>> = None;
         let mut cert_blob: Option<Vec<u8>> = None;
         let mut cert_chain_blob: Option<Vec<u8>> = None;
         db_utils::with_rows_extract_all(&mut rows, |row| {
             let sub_type: SubComponentType =
-                row.get(2).context("Failed to extract subcomponent_type.")?;
-            match (sub_type, load_bits.load_public()) {
-                (SubComponentType::KEY_BLOB, _) => {
-                    sec_level =
-                        SecurityLevel(row.get(1).context("Failed to extract security level.")?);
-                    if load_bits.load_km() {
-                        km_blob = Some(row.get(3).context("Failed to extract KM blob.")?);
-                    }
+                row.get(1).context("Failed to extract subcomponent_type.")?;
+            match (sub_type, load_bits.load_public(), load_bits.load_km()) {
+                (SubComponentType::KEY_BLOB, _, true) => {
+                    km_blob = Some(row.get(2).context("Failed to extract KM blob.")?);
                 }
-                (SubComponentType::CERT, true) => {
+                (SubComponentType::CERT, true, _) => {
                     cert_blob =
-                        Some(row.get(3).context("Failed to extract public certificate blob.")?);
+                        Some(row.get(2).context("Failed to extract public certificate blob.")?);
                 }
-                (SubComponentType::CERT_CHAIN, true) => {
+                (SubComponentType::CERT_CHAIN, true, _) => {
                     cert_chain_blob =
-                        Some(row.get(3).context("Failed to extract certificate chain blob.")?);
+                        Some(row.get(2).context("Failed to extract certificate chain blob.")?);
                 }
-                (SubComponentType::CERT, _) | (SubComponentType::CERT_CHAIN, _) => {}
+                (SubComponentType::CERT, _, _)
+                | (SubComponentType::CERT_CHAIN, _, _)
+                | (SubComponentType::KEY_BLOB, _, _) => {}
                 _ => Err(KsError::sys()).context("Unknown subcomponent type.")?,
             }
             Ok(())
         })
         .context("In load_blob_components.")?;
 
-        Ok((sec_level, km_blob, cert_blob, cert_chain_blob))
+        Ok((km_blob, cert_blob, cert_chain_blob))
     }
 
     fn load_key_parameters(key_id: i64, tx: &Transaction) -> Result<Vec<KeyParameter>> {
@@ -1223,6 +1433,44 @@
         Ok((key_id_guard, key_entry))
     }
 
+    fn mark_unreferenced(tx: &Transaction, key_id: i64) -> Result<()> {
+        let updated = tx
+            .execute(
+                "UPDATE persistent.keyentry SET state = ? WHERE id = ?;",
+                params![KeyLifeCycle::Unreferenced, key_id],
+            )
+            .context("In mark_unreferenced: Failed to update state of key entry.")?;
+        if updated != 0 {
+            Gc::notify_gc();
+        }
+        tx.execute("DELETE from persistent.grant WHERE keyentryid = ?;", params![key_id])
+            .context("In mark_unreferenced: Failed to drop grants.")?;
+        Ok(())
+    }
+
+    /// Marks the given key as unreferenced and removes all of the grants to this key.
+    pub fn unbind_key(
+        &mut self,
+        key: KeyDescriptor,
+        key_type: KeyType,
+        caller_uid: u32,
+        check_permission: impl FnOnce(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>,
+    ) -> Result<()> {
+        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).context("Trying to mark the key unreferenced.")
+        })
+        .context("In unbind_key.")
+    }
+
     fn load_key_components(
         tx: &Transaction,
         load_bits: KeyEntryLoadBits,
@@ -1230,13 +1478,23 @@
     ) -> Result<KeyEntry> {
         let metadata = KeyMetaData::load_from_db(key_id, &tx).context("In load_key_components.")?;
 
-        let (sec_level, km_blob, cert_blob, cert_chain_blob) =
+        let (km_blob, 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.")?;
 
+        // Extract the security level by checking the security level of the origin tag.
+        // Super keys don't have key parameters so we use security_level software by default.
+        let sec_level = parameters
+            .iter()
+            .find_map(|k| match k.get_tag() {
+                Tag::ORIGIN => Some(*k.security_level()),
+                _ => None,
+            })
+            .unwrap_or(SecurityLevel::SOFTWARE);
+
         Ok(KeyEntry {
             id: key_id,
             km_blob,
@@ -1256,12 +1514,13 @@
             .conn
             .prepare(
                 "SELECT alias FROM persistent.keyentry
-             WHERE domain = ? AND namespace = ? AND alias IS NOT NULL;",
+             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]).context("In list: Failed to query.")?;
+        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| {
@@ -1602,7 +1861,7 @@
     #[test]
     fn test_persistence_for_files() -> Result<()> {
         let temp_dir = TempDir::new("persistent_db_test")?;
-        let db = KeystoreDB::new(temp_dir.path())?;
+        let mut db = KeystoreDB::new(temp_dir.path())?;
 
         db.create_key_entry(Domain::APP, 100)?;
         let entries = get_keyentry(&db)?;
@@ -1621,7 +1880,7 @@
             (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref())
         }
 
-        let db = new_test_db()?;
+        let mut db = new_test_db()?;
 
         db.create_key_entry(Domain::APP, 100)?;
         db.create_key_entry(Domain::SELINUX, 101)?;
@@ -1712,8 +1971,8 @@
 
         let mut db = new_test_db()?;
         db.conn.execute(
-            "INSERT INTO persistent.keyentry (id, key_type, domain, namespace, alias)
-                VALUES (1, 0, 0, 15, 'key'), (2, 0, 2, 7, 'yek');",
+            "INSERT INTO persistent.keyentry (id, key_type, domain, namespace, alias, state)
+                VALUES (1, 0, 0, 15, 'key', 1), (2, 0, 2, 7, 'yek', 1);",
             NO_PARAMS,
         )?;
         let app_key = KeyDescriptor {
@@ -1730,8 +1989,8 @@
         reset_random();
         let next_random = 0i64;
 
-        let app_granted_key =
-            db.grant(app_key.clone(), CALLER_UID, GRANTEE_UID, PVEC1, |k, a| {
+        let app_granted_key = db
+            .grant(app_key.clone(), CALLER_UID, GRANTEE_UID, PVEC1, |k, a| {
                 assert_eq!(*a, PVEC1);
                 assert_eq!(
                     *k,
@@ -1744,7 +2003,8 @@
                     }
                 );
                 Ok(())
-            })?;
+            })
+            .unwrap();
 
         assert_eq!(
             app_granted_key,
@@ -1764,8 +2024,8 @@
             blob: None,
         };
 
-        let selinux_granted_key =
-            db.grant(selinux_key.clone(), CALLER_UID, 12, PVEC1, |k, a| {
+        let selinux_granted_key = db
+            .grant(selinux_key.clone(), CALLER_UID, 12, PVEC1, |k, a| {
                 assert_eq!(*a, PVEC1);
                 assert_eq!(
                     *k,
@@ -1779,7 +2039,8 @@
                     }
                 );
                 Ok(())
-            })?;
+            })
+            .unwrap();
 
         assert_eq!(
             selinux_granted_key,
@@ -1793,8 +2054,8 @@
         );
 
         // This should update the existing grant with PVEC2.
-        let selinux_granted_key =
-            db.grant(selinux_key.clone(), CALLER_UID, 12, PVEC2, |k, a| {
+        let selinux_granted_key = db
+            .grant(selinux_key.clone(), CALLER_UID, 12, PVEC2, |k, a| {
                 assert_eq!(*a, PVEC2);
                 assert_eq!(
                     *k,
@@ -1808,7 +2069,8 @@
                     }
                 );
                 Ok(())
-            })?;
+            })
+            .unwrap();
 
         assert_eq!(
             selinux_granted_key,
@@ -1859,40 +2121,27 @@
 
     #[test]
     fn test_insert_blob() -> Result<()> {
+        let key_id = KEY_ID_LOCK.get(3000);
         let mut db = new_test_db()?;
-        db.insert_blob(
-            &KEY_ID_LOCK.get(1),
-            SubComponentType::KEY_BLOB,
-            TEST_KEY_BLOB,
-            SecurityLevel::SOFTWARE,
-        )?;
-        db.insert_blob(
-            &KEY_ID_LOCK.get(1),
-            SubComponentType::CERT,
-            TEST_CERT_BLOB,
-            SecurityLevel::TRUSTED_ENVIRONMENT,
-        )?;
-        db.insert_blob(
-            &KEY_ID_LOCK.get(1),
-            SubComponentType::CERT_CHAIN,
-            TEST_CERT_CHAIN_BLOB,
-            SecurityLevel::STRONGBOX,
-        )?;
+        db.insert_blob(&key_id, SubComponentType::KEY_BLOB, TEST_KEY_BLOB)?;
+        db.insert_blob(&key_id, SubComponentType::CERT, TEST_CERT_BLOB)?;
+        db.insert_blob(&key_id, SubComponentType::CERT_CHAIN, TEST_CERT_CHAIN_BLOB)?;
+        drop(key_id);
 
         let mut stmt = db.conn.prepare(
-            "SELECT subcomponent_type, keyentryid, blob, sec_level FROM persistent.blobentry
-                ORDER BY sec_level ASC;",
+            "SELECT subcomponent_type, keyentryid, blob 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)?))
+            .query_map::<(SubComponentType, i64, Vec<u8>), _, _>(NO_PARAMS, |row| {
+                Ok((row.get(0)?, row.get(1)?, row.get(2)?))
             })?;
         let r = rows.next().unwrap().unwrap();
-        assert_eq!(r, (SubComponentType::KEY_BLOB, 1, TEST_KEY_BLOB.to_vec(), 0));
+        assert_eq!(r, (SubComponentType::KEY_BLOB, 3000, TEST_KEY_BLOB.to_vec()));
         let r = rows.next().unwrap().unwrap();
-        assert_eq!(r, (SubComponentType::CERT, 1, TEST_CERT_BLOB.to_vec(), 1));
+        assert_eq!(r, (SubComponentType::CERT, 3000, TEST_CERT_BLOB.to_vec()));
         let r = rows.next().unwrap().unwrap();
-        assert_eq!(r, (SubComponentType::CERT_CHAIN, 1, TEST_CERT_CHAIN_BLOB.to_vec(), 2));
+        assert_eq!(r, (SubComponentType::CERT_CHAIN, 3000, TEST_CERT_CHAIN_BLOB.to_vec()));
 
         Ok(())
     }
@@ -1905,7 +2154,23 @@
         let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS)
             .context("test_insert_and_load_full_keyentry_domain_app")?
             .0;
-        let (_key_guard, key_entry) = db.load_key_entry(
+        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));
+
+        db.unbind_key(
             KeyDescriptor {
                 domain: Domain::APP,
                 nspace: 0,
@@ -1913,11 +2178,30 @@
                 blob: None,
             },
             KeyType::Client,
-            KeyEntryLoadBits::BOTH,
             1,
-            |_k, _av| Ok(()),
-        )?;
-        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id));
+            |_, _| 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(())
     }
 
@@ -1927,7 +2211,23 @@
         let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS)
             .context("test_insert_and_load_full_keyentry_domain_selinux")?
             .0;
-        let (_key_guard, key_entry) = db.load_key_entry(
+        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));
+
+        db.unbind_key(
             KeyDescriptor {
                 domain: Domain::SELINUX,
                 nspace: 1,
@@ -1935,11 +2235,30 @@
                 blob: None,
             },
             KeyType::Client,
-            KeyEntryLoadBits::BOTH,
             1,
-            |_k, _av| Ok(()),
-        )?;
-        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id));
+            |_, _| 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(())
     }
 
@@ -1949,14 +2268,39 @@
         let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS)
             .context("test_insert_and_load_full_keyentry_domain_key_id")?
             .0;
-        let (_key_guard, key_entry) = db.load_key_entry(
+        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));
+
+        db.unbind_key(
             KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
             KeyType::Client,
-            KeyEntryLoadBits::BOTH,
             1,
-            |_k, _av| Ok(()),
-        )?;
-        assert_eq!(key_entry, make_test_key_entry_test_vector(key_id));
+            |_, _| 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(())
     }
@@ -1968,29 +2312,55 @@
             .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(()),
-        )?;
+        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(())
-            })?;
+        let (_key_guard, key_entry) = db
+            .load_key_entry(
+                granted_key.clone(),
+                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));
+
+        db.unbind_key(granted_key.clone(), 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(())
     }
 
@@ -2005,18 +2375,20 @@
             let key_id = make_test_key_entry(&mut db, Domain::APP, 33, KEY_LOCK_TEST_ALIAS)
                 .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(()),
-            )?;
+            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));
             let state = Arc::new(AtomicU8::new(1));
             let state2 = state.clone();
@@ -2182,6 +2554,7 @@
         domain: Option<Domain>,
         namespace: Option<i64>,
         alias: Option<String>,
+        state: KeyLifeCycle,
     }
 
     fn get_keyentry(db: &KeystoreDB) -> Result<Vec<KeyEntryRow>> {
@@ -2197,6 +2570,7 @@
                     },
                     namespace: row.get(3)?,
                     alias: row.get(4)?,
+                    state: row.get(5)?,
                 })
             })?
             .map(|r| r.context("Could not read keyentry row."))
@@ -2432,24 +2806,9 @@
         alias: &str,
     ) -> Result<KeyIdGuard> {
         let key_id = db.create_key_entry(domain, namespace)?;
-        db.insert_blob(
-            &key_id,
-            SubComponentType::KEY_BLOB,
-            TEST_KEY_BLOB,
-            SecurityLevel::TRUSTED_ENVIRONMENT,
-        )?;
-        db.insert_blob(
-            &key_id,
-            SubComponentType::CERT,
-            TEST_CERT_BLOB,
-            SecurityLevel::TRUSTED_ENVIRONMENT,
-        )?;
-        db.insert_blob(
-            &key_id,
-            SubComponentType::CERT_CHAIN,
-            TEST_CERT_CHAIN_BLOB,
-            SecurityLevel::TRUSTED_ENVIRONMENT,
-        )?;
+        db.insert_blob(&key_id, SubComponentType::KEY_BLOB, TEST_KEY_BLOB)?;
+        db.insert_blob(&key_id, SubComponentType::CERT, TEST_CERT_BLOB)?;
+        db.insert_blob(&key_id, SubComponentType::CERT_CHAIN, TEST_CERT_CHAIN_BLOB)?;
         db.insert_keyparameter(&key_id, &make_test_params())?;
         let mut metadata = KeyMetaData::new();
         metadata.add(KeyMetaEntry::EncryptedBy(EncryptedBy::Password));
@@ -2480,19 +2839,22 @@
     }
 
     fn debug_dump_keyentry_table(db: &mut KeystoreDB) -> Result<()> {
-        let mut stmt = db
-            .conn
-            .prepare("SELECT id, key_type, domain, namespace, alias FROM persistent.keyentry;")?;
-        let rows = stmt.query_map::<(i64, KeyType, i32, i64, String), _, _>(NO_PARAMS, |row| {
-            Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?, row.get(4)?))
-        })?;
+        let mut stmt = db.conn.prepare(
+            "SELECT id, key_type, domain, namespace, alias, state FROM persistent.keyentry;",
+        )?;
+        let rows = stmt.query_map::<(i64, KeyType, i32, i64, String, KeyLifeCycle), _, _>(
+            NO_PARAMS,
+            |row| {
+                Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?, row.get(4)?, row.get(5)?))
+            },
+        )?;
 
         println!("Key entry table rows:");
         for r in rows {
-            let (id, key_type, domain, namespace, alias) = r.unwrap();
+            let (id, key_type, domain, namespace, alias, state) = r.unwrap();
             println!(
-                "    id: {} KeyType: {:?} Domain: {} Namespace: {} Alias: {}",
-                id, key_type, domain, namespace, alias
+                "    id: {} KeyType: {:?} Domain: {} Namespace: {} Alias: {} State: {:?}",
+                id, key_type, domain, namespace, alias, state
             );
         }
         Ok(())
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index b4cf913..7227f62 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -38,7 +38,7 @@
 use keystore2_selinux as selinux;
 
 use android_system_keystore2::binder::{
-    ExceptionCode, Result as BinderResult, Status as BinderStatus,
+    ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode,
 };
 
 /// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
@@ -54,6 +54,9 @@
     /// 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),
 }
 
 impl Error {
@@ -98,6 +101,28 @@
     })
 }
 
+/// 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.
 ///
@@ -142,7 +167,9 @@
                 // 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(_, _)) => ResponseCode::SYSTEM_ERROR.0,
+                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,
diff --git a/keystore2/src/gc.rs b/keystore2/src/gc.rs
new file mode 100644
index 0000000..b5bdd98
--- /dev/null
+++ b/keystore2/src/gc.rs
@@ -0,0 +1,94 @@
+// 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::globals::{get_keymint_device, DB};
+use crate::{error::map_km_error, globals::ASYNC_TASK};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::IKeyMintDevice::IKeyMintDevice;
+use anyhow::Result;
+
+#[derive(Clone, Copy)]
+pub struct Gc {
+    remaining_tries: u32,
+}
+
+impl Gc {
+    const MAX_ERROR_RETRIES: u32 = 3u32;
+
+    /// Attempts to process one unreferenced key from the database.
+    /// Returns Ok(true) if a key was deleted and Ok(false) if there were no more keys to process.
+    /// 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.
+    fn process_one_key() -> Result<bool> {
+        DB.with(|db| {
+            let mut db = db.borrow_mut();
+            if let Some((key_id, mut key_entry)) = db.get_unreferenced_key()? {
+                if let Some(blob) = key_entry.take_km_blob() {
+                    let km_dev: Box<dyn IKeyMintDevice> =
+                        get_keymint_device(key_entry.sec_level())?.get_interface()?;
+                    if let Err(e) = map_km_error(km_dev.deleteKey(&blob)) {
+                        // Log but ignore error.
+                        log::error!("Error trying to delete key. {:?}", e);
+                    }
+                }
+                db.purge_key_entry(key_id)?;
+                return Ok(true);
+            }
+            Ok(false)
+        })
+    }
+
+    /// Processes one key and then schedules another attempt until it runs out of tries or keys
+    /// to delete.
+    fn process_all(mut self) {
+        match Self::process_one_key() {
+            // We successfully removed a key.
+            Ok(true) => self.remaining_tries = Self::MAX_ERROR_RETRIES,
+            // There were no more keys to remove. We may exit.
+            Ok(false) => self.remaining_tries = 0,
+            // An error occurred. We retry in case the error was transient, but
+            // we also count down the number of tries so that we don't spin
+            // indefinitely.
+            Err(e) => {
+                self.remaining_tries -= 1;
+                log::error!(
+                    concat!(
+                        "Failed to delete key. Retrying in case this error was transient. ",
+                        "(Tries remaining {}) {:?}"
+                    ),
+                    self.remaining_tries,
+                    e
+                )
+            }
+        }
+        if self.remaining_tries != 0 {
+            ASYNC_TASK.queue_lo(move || {
+                self.process_all();
+            })
+        }
+    }
+
+    /// Notifies the key garbage collector to iterate through unreferenced keys and attempt
+    /// 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() {
+        ASYNC_TASK.queue_lo(|| Self { remaining_tries: Self::MAX_ERROR_RETRIES }.process_all())
+    }
+}
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index eff3196..18bed9a 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -16,10 +16,53 @@
 //! database connections and connections to services that Keystore needs
 //! to talk to.
 
-use crate::database::KeystoreDB;
+use crate::async_task::AsyncTask;
+use crate::gc::Gc;
 use crate::super_key::SuperKeyManager;
+use crate::utils::Asp;
+use crate::{
+    database::KeystoreDB,
+    error::{map_binder_status, map_binder_status_code, Error, ErrorCode},
+};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use android_hardware_security_keymint::binder::StatusCode;
+use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
+use anyhow::{Context, Result};
 use lazy_static::lazy_static;
-use std::cell::RefCell;
+use std::collections::HashMap;
+use std::sync::Mutex;
+use std::{cell::RefCell, sync::Once};
+
+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.
+fn create_thread_local_db() -> KeystoreDB {
+    let mut db = KeystoreDB::new(
+        // Keystore changes to the database directory on startup
+        // (see keystore2_main.rs).
+        &std::env::current_dir().expect("Could not get the current working directory."),
+    )
+    .expect("Failed to open database.");
+    DB_INIT.call_once(|| {
+        log::info!("Touching Keystore 2.0 database for this first time since boot.");
+        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
+            );
+        }
+        Gc::notify_gc();
+    });
+    db
+}
 
 thread_local! {
     /// Database connections are not thread safe, but connecting to the
@@ -27,17 +70,73 @@
     /// 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(
-                KeystoreDB::new(
-                    // Keystore changes to the database directory on startup
-                    // (see keystor2_main.rs).
-                    &std::env::current_dir()
-                    .expect("Could not get the current working directory.")
-                )
-                .expect("Failed to open database."));
+            RefCell::new(create_thread_local_db());
 }
 
 lazy_static! {
     /// Runtime database of unwrapped super keys.
     pub static ref SUPER_KEY: SuperKeyManager = Default::default();
+    /// Map of KeyMint devices.
+    static ref KEY_MINT_DEVICES: Mutex<HashMap<SecurityLevel, Asp>> = Default::default();
+    /// A single on-demand worker thread that handles deferred tasks with two different
+    /// priorities.
+    pub static ref ASYNC_TASK: AsyncTask = Default::default();
+}
+
+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> {
+    let service_name = match security_level {
+        SecurityLevel::TRUSTED_ENVIRONMENT => format!("{}/default", KEYMINT_SERVICE_NAME),
+        SecurityLevel::STRONGBOX => format!("{}/strongbox", KEYMINT_SERVICE_NAME),
+        _ => {
+            return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
+                .context("In connect_keymint.")
+        }
+    };
+
+    let keymint = map_binder_status_code(binder::get_interface(&service_name))
+        .context("In connect_keymint: Trying to connect to genuine KeyMint service.")
+        .or_else(|e| {
+            match e.root_cause().downcast_ref::<Error>() {
+                Some(Error::BinderTransaction(StatusCode::NAME_NOT_FOUND)) => {
+                    // This is a no-op if it was called before.
+                    keystore2_km_compat::add_keymint_device_service();
+
+                    let keystore_compat_service: Box<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 connext_keymint: Trying to get Legacy wrapper.")
+                }
+                _ => Err(e),
+            }
+        })?;
+
+    Ok(Asp::new(keymint.as_binder()))
+}
+
+/// Get a keymint device for the given security level either from our cache or
+/// by making a new connection.
+pub fn get_keymint_device(security_level: SecurityLevel) -> Result<Asp> {
+    let mut devices_map = KEY_MINT_DEVICES.lock().unwrap();
+    if let Some(dev) = devices_map.get(&security_level) {
+        Ok(dev.clone())
+    } else {
+        let dev = connect_keymint(security_level).map_err(|e| {
+            anyhow::anyhow!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
+                .context(format!("In get_keymint_device: {:?}", e))
+        })?;
+        devices_map.insert(security_level, dev.clone());
+        Ok(dev)
+    }
 }
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
index 56c03aa..71a17fe 100644
--- a/keystore2/src/key_parameter.rs
+++ b/keystore2/src/key_parameter.rs
@@ -12,9 +12,85 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! KeyParameter is used to express different characteristics of a key requested by the user
-//! and enforced by the OEMs. This module implements the internal representation of KeyParameter
-//! and the methods to work with KeyParameter.
+//! 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.
+
+use std::convert::TryInto;
 
 use crate::db_utils::SqlField;
 use crate::error::Error as KeystoreError;
@@ -32,288 +108,868 @@
 use rusqlite::types::{Null, ToSql, ToSqlOutput};
 use rusqlite::Result as SqlResult;
 
-/// KeyParameter wraps the KeyParameterValue and the security level at which it is enforced.
-#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
-pub struct KeyParameter {
-    key_parameter_value: KeyParameterValue,
-    security_level: SecurityLevel,
+/// 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/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),
     /// 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>),
 }
+}
+
+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(key_parameter_value: KeyParameterValue, security_level: SecurityLevel) -> Self {
-        KeyParameter { key_parameter_value, security_level }
+    pub fn new(value: KeyParameterValue, security_level: SecurityLevel) -> Self {
+        KeyParameter { value, security_level }
     }
 
-    /// Returns the tag given the KeyParameter instance.
-    pub fn get_tag(&self) -> Tag {
-        match self.key_parameter_value {
-            KeyParameterValue::Invalid => Tag::INVALID,
-            KeyParameterValue::KeyPurpose(_) => Tag::PURPOSE,
-            KeyParameterValue::Algorithm(_) => Tag::ALGORITHM,
-            KeyParameterValue::KeySize(_) => Tag::KEY_SIZE,
-            KeyParameterValue::BlockMode(_) => Tag::BLOCK_MODE,
-            KeyParameterValue::Digest(_) => Tag::DIGEST,
-            KeyParameterValue::PaddingMode(_) => Tag::PADDING,
-            KeyParameterValue::CallerNonce => Tag::CALLER_NONCE,
-            KeyParameterValue::MinMacLength(_) => Tag::MIN_MAC_LENGTH,
-            KeyParameterValue::EcCurve(_) => Tag::EC_CURVE,
-            KeyParameterValue::RSAPublicExponent(_) => Tag::RSA_PUBLIC_EXPONENT,
-            KeyParameterValue::IncludeUniqueID => Tag::INCLUDE_UNIQUE_ID,
-            KeyParameterValue::BootLoaderOnly => Tag::BOOTLOADER_ONLY,
-            KeyParameterValue::RollbackResistance => Tag::ROLLBACK_RESISTANCE,
-            KeyParameterValue::ActiveDateTime(_) => Tag::ACTIVE_DATETIME,
-            KeyParameterValue::OriginationExpireDateTime(_) => Tag::ORIGINATION_EXPIRE_DATETIME,
-            KeyParameterValue::UsageExpireDateTime(_) => Tag::USAGE_EXPIRE_DATETIME,
-            KeyParameterValue::MinSecondsBetweenOps(_) => Tag::MIN_SECONDS_BETWEEN_OPS,
-            KeyParameterValue::MaxUsesPerBoot(_) => Tag::MAX_USES_PER_BOOT,
-            KeyParameterValue::UserID(_) => Tag::USER_ID,
-            KeyParameterValue::UserSecureID(_) => Tag::USER_SECURE_ID,
-            KeyParameterValue::NoAuthRequired => Tag::NO_AUTH_REQUIRED,
-            KeyParameterValue::HardwareAuthenticatorType(_) => Tag::USER_AUTH_TYPE,
-            KeyParameterValue::AuthTimeout(_) => Tag::AUTH_TIMEOUT,
-            KeyParameterValue::AllowWhileOnBody => Tag::ALLOW_WHILE_ON_BODY,
-            KeyParameterValue::TrustedUserPresenceRequired => Tag::TRUSTED_USER_PRESENCE_REQUIRED,
-            KeyParameterValue::TrustedConfirmationRequired => Tag::TRUSTED_CONFIRMATION_REQUIRED,
-            KeyParameterValue::UnlockedDeviceRequired => Tag::UNLOCKED_DEVICE_REQUIRED,
-            KeyParameterValue::ApplicationID(_) => Tag::APPLICATION_ID,
-            KeyParameterValue::ApplicationData(_) => Tag::APPLICATION_DATA,
-            KeyParameterValue::CreationDateTime(_) => Tag::CREATION_DATETIME,
-            KeyParameterValue::KeyOrigin(_) => Tag::ORIGIN,
-            KeyParameterValue::RootOfTrust(_) => Tag::ROOT_OF_TRUST,
-            KeyParameterValue::OSVersion(_) => Tag::OS_VERSION,
-            KeyParameterValue::OSPatchLevel(_) => Tag::OS_PATCHLEVEL,
-            KeyParameterValue::UniqueID(_) => Tag::UNIQUE_ID,
-            KeyParameterValue::AttestationChallenge(_) => Tag::ATTESTATION_CHALLENGE,
-            KeyParameterValue::AttestationApplicationID(_) => Tag::ATTESTATION_APPLICATION_ID,
-            KeyParameterValue::AttestationIdBrand(_) => Tag::ATTESTATION_ID_BRAND,
-            KeyParameterValue::AttestationIdDevice(_) => Tag::ATTESTATION_ID_DEVICE,
-            KeyParameterValue::AttestationIdProduct(_) => Tag::ATTESTATION_ID_PRODUCT,
-            KeyParameterValue::AttestationIdSerial(_) => Tag::ATTESTATION_ID_SERIAL,
-            KeyParameterValue::AttestationIdIMEI(_) => Tag::ATTESTATION_ID_IMEI,
-            KeyParameterValue::AttestationIdMEID(_) => Tag::ATTESTATION_ID_MEID,
-            KeyParameterValue::AttestationIdManufacturer(_) => Tag::ATTESTATION_ID_MANUFACTURER,
-            KeyParameterValue::AttestationIdModel(_) => Tag::ATTESTATION_ID_MODEL,
-            KeyParameterValue::VendorPatchLevel(_) => Tag::VENDOR_PATCHLEVEL,
-            KeyParameterValue::BootPatchLevel(_) => Tag::BOOT_PATCHLEVEL,
-            KeyParameterValue::AssociatedData(_) => Tag::ASSOCIATED_DATA,
-            KeyParameterValue::Nonce(_) => Tag::NONCE,
-            KeyParameterValue::MacLength(_) => Tag::MAC_LENGTH,
-            KeyParameterValue::ResetSinceIdRotation => Tag::RESET_SINCE_ID_ROTATION,
-            KeyParameterValue::ConfirmationToken(_) => Tag::CONFIRMATION_TOKEN,
-        }
-    }
-
-    /// Returns key parameter value.
-    pub fn key_parameter_value(&self) -> &KeyParameterValue {
-        &self.key_parameter_value
-    }
-
-    /// Returns the security level of a KeyParameter.
-    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.key_parameter_value.convert_to_wire(),
-        }
-    }
-}
-
-impl ToSql for KeyParameterValue {
-    /// Converts KeyParameterValue to be stored in rusqlite database.
-    /// 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 (i.e. enforcement module) and here we support
-    /// conversion for all the variants, to keep error handling simple.
-    fn to_sql(&self) -> SqlResult<ToSqlOutput> {
-        match self {
-            KeyParameterValue::Invalid => Ok(ToSqlOutput::from(Null)),
-            KeyParameterValue::KeyPurpose(k) => Ok(ToSqlOutput::from(k.0 as u32)),
-            KeyParameterValue::Algorithm(a) => Ok(ToSqlOutput::from(a.0 as u32)),
-            KeyParameterValue::KeySize(k) => Ok(ToSqlOutput::from(*k)),
-            KeyParameterValue::BlockMode(b) => Ok(ToSqlOutput::from(b.0 as u32)),
-            KeyParameterValue::Digest(d) => Ok(ToSqlOutput::from(d.0 as u32)),
-            KeyParameterValue::PaddingMode(p) => Ok(ToSqlOutput::from(p.0 as u32)),
-            KeyParameterValue::CallerNonce => Ok(ToSqlOutput::from(Null)),
-            KeyParameterValue::MinMacLength(m) => Ok(ToSqlOutput::from(*m)),
-            KeyParameterValue::EcCurve(e) => Ok(ToSqlOutput::from(e.0 as u32)),
-            KeyParameterValue::RSAPublicExponent(r) => Ok(ToSqlOutput::from(*r as i64)),
-            KeyParameterValue::IncludeUniqueID => Ok(ToSqlOutput::from(Null)),
-            KeyParameterValue::BootLoaderOnly => Ok(ToSqlOutput::from(Null)),
-            KeyParameterValue::RollbackResistance => Ok(ToSqlOutput::from(Null)),
-            KeyParameterValue::ActiveDateTime(a) => Ok(ToSqlOutput::from(*a as i64)),
-            KeyParameterValue::OriginationExpireDateTime(o) => Ok(ToSqlOutput::from(*o as i64)),
-            KeyParameterValue::UsageExpireDateTime(u) => Ok(ToSqlOutput::from(*u as i64)),
-            KeyParameterValue::MinSecondsBetweenOps(m) => Ok(ToSqlOutput::from(*m)),
-            KeyParameterValue::MaxUsesPerBoot(m) => Ok(ToSqlOutput::from(*m)),
-            KeyParameterValue::UserID(u) => Ok(ToSqlOutput::from(*u)),
-            KeyParameterValue::UserSecureID(u) => Ok(ToSqlOutput::from(*u as i64)),
-            KeyParameterValue::NoAuthRequired => Ok(ToSqlOutput::from(Null)),
-            KeyParameterValue::HardwareAuthenticatorType(h) => Ok(ToSqlOutput::from(h.0 as u32)),
-            KeyParameterValue::AuthTimeout(m) => Ok(ToSqlOutput::from(*m)),
-            KeyParameterValue::AllowWhileOnBody => Ok(ToSqlOutput::from(Null)),
-            KeyParameterValue::TrustedUserPresenceRequired => Ok(ToSqlOutput::from(Null)),
-            KeyParameterValue::TrustedConfirmationRequired => Ok(ToSqlOutput::from(Null)),
-            KeyParameterValue::UnlockedDeviceRequired => Ok(ToSqlOutput::from(Null)),
-            KeyParameterValue::ApplicationID(a) => Ok(ToSqlOutput::from(a.to_vec())),
-            KeyParameterValue::ApplicationData(a) => Ok(ToSqlOutput::from(a.to_vec())),
-            KeyParameterValue::CreationDateTime(c) => Ok(ToSqlOutput::from(*c as i64)),
-            KeyParameterValue::KeyOrigin(k) => Ok(ToSqlOutput::from(k.0 as u32)),
-            KeyParameterValue::RootOfTrust(r) => Ok(ToSqlOutput::from(r.to_vec())),
-            KeyParameterValue::OSVersion(o) => Ok(ToSqlOutput::from(*o)),
-            KeyParameterValue::OSPatchLevel(o) => Ok(ToSqlOutput::from(*o)),
-            KeyParameterValue::UniqueID(u) => Ok(ToSqlOutput::from(u.to_vec())),
-            KeyParameterValue::AttestationChallenge(a) => Ok(ToSqlOutput::from(a.to_vec())),
-            KeyParameterValue::AttestationApplicationID(a) => Ok(ToSqlOutput::from(a.to_vec())),
-            KeyParameterValue::AttestationIdBrand(a) => Ok(ToSqlOutput::from(a.to_vec())),
-            KeyParameterValue::AttestationIdDevice(a) => Ok(ToSqlOutput::from(a.to_vec())),
-            KeyParameterValue::AttestationIdProduct(a) => Ok(ToSqlOutput::from(a.to_vec())),
-            KeyParameterValue::AttestationIdSerial(a) => Ok(ToSqlOutput::from(a.to_vec())),
-            KeyParameterValue::AttestationIdIMEI(a) => Ok(ToSqlOutput::from(a.to_vec())),
-            KeyParameterValue::AttestationIdMEID(a) => Ok(ToSqlOutput::from(a.to_vec())),
-            KeyParameterValue::AttestationIdManufacturer(a) => Ok(ToSqlOutput::from(a.to_vec())),
-            KeyParameterValue::AttestationIdModel(a) => Ok(ToSqlOutput::from(a.to_vec())),
-            KeyParameterValue::VendorPatchLevel(v) => Ok(ToSqlOutput::from(*v)),
-            KeyParameterValue::BootPatchLevel(b) => Ok(ToSqlOutput::from(*b)),
-            KeyParameterValue::AssociatedData(a) => Ok(ToSqlOutput::from(a.to_vec())),
-            KeyParameterValue::Nonce(n) => Ok(ToSqlOutput::from(n.to_vec())),
-            KeyParameterValue::MacLength(m) => Ok(ToSqlOutput::from(*m)),
-            KeyParameterValue::ResetSinceIdRotation => Ok(ToSqlOutput::from(Null)),
-            KeyParameterValue::ConfirmationToken(c) => Ok(ToSqlOutput::from(c.to_vec())),
-        }
-    }
-}
-
-impl KeyParameter {
     /// 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,
@@ -325,569 +981,107 @@
         data: &SqlField,
         security_level_val: SecurityLevel,
     ) -> Result<Self> {
-        let key_param_value = match tag_val {
-            Tag::INVALID => KeyParameterValue::Invalid,
-            Tag::PURPOSE => {
-                let key_purpose: i32 = data
-                    .get()
-                    .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
-                    .context("Failed to read sql data for tag: PURPOSE.")?;
-                KeyParameterValue::KeyPurpose(KeyPurpose(key_purpose))
-            }
-            Tag::ALGORITHM => {
-                let algorithm: i32 = data
-                    .get()
-                    .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
-                    .context("Failed to read sql data for tag: ALGORITHM.")?;
-                KeyParameterValue::Algorithm(Algorithm(algorithm))
-            }
-            Tag::KEY_SIZE => {
-                let key_size: i32 =
-                    data.get().context("Failed to read sql data for tag: KEY_SIZE.")?;
-                KeyParameterValue::KeySize(key_size)
-            }
-            Tag::BLOCK_MODE => {
-                let block_mode: i32 = data
-                    .get()
-                    .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
-                    .context("Failed to read sql data for tag: BLOCK_MODE.")?;
-                KeyParameterValue::BlockMode(BlockMode(block_mode))
-            }
-            Tag::DIGEST => {
-                let digest: i32 = data
-                    .get()
-                    .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
-                    .context("Failed to read sql data for tag: DIGEST.")?;
-                KeyParameterValue::Digest(Digest(digest))
-            }
-            Tag::PADDING => {
-                let padding: i32 = data
-                    .get()
-                    .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
-                    .context("Failed to read sql data for tag: PADDING.")?;
-                KeyParameterValue::PaddingMode(PaddingMode(padding))
-            }
-            Tag::CALLER_NONCE => KeyParameterValue::CallerNonce,
-            Tag::MIN_MAC_LENGTH => {
-                let min_mac_length: i32 =
-                    data.get().context("Failed to read sql data for tag: MIN_MAC_LENGTH.")?;
-                KeyParameterValue::MinMacLength(min_mac_length)
-            }
-            Tag::EC_CURVE => {
-                let ec_curve: i32 = data
-                    .get()
-                    .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
-                    .context("Failed to read sql data for tag: EC_CURVE.")?;
-                KeyParameterValue::EcCurve(EcCurve(ec_curve))
-            }
-            Tag::RSA_PUBLIC_EXPONENT => {
-                let rsa_pub_exponent: i64 =
-                    data.get().context("Failed to read sql data for tag: RSA_PUBLIC_EXPONENT.")?;
+        Ok(Self {
+            value: KeyParameterValue::new_from_sql(tag_val, data)?,
+            security_level: security_level_val,
+        })
+    }
 
-                KeyParameterValue::RSAPublicExponent(rsa_pub_exponent)
-            }
-            Tag::INCLUDE_UNIQUE_ID => KeyParameterValue::IncludeUniqueID,
-            Tag::BOOTLOADER_ONLY => KeyParameterValue::BootLoaderOnly,
-            Tag::ROLLBACK_RESISTANCE => KeyParameterValue::RollbackResistance,
-            Tag::ACTIVE_DATETIME => {
-                let active_datetime: i64 =
-                    data.get().context("Failed to read sql data for tag: ACTIVE_DATETIME.")?;
-                KeyParameterValue::ActiveDateTime(active_datetime)
-            }
-            Tag::ORIGINATION_EXPIRE_DATETIME => {
-                let origination_expire_datetime: i64 = data
-                    .get()
-                    .context("Failed to read sql data for tag: ORIGINATION_EXPIRE_DATETIME.")?;
-                KeyParameterValue::OriginationExpireDateTime(origination_expire_datetime)
-            }
-            Tag::USAGE_EXPIRE_DATETIME => {
-                let usage_expire_datetime: i64 = data
-                    .get()
-                    .context("Failed to read sql data for tag: USAGE_EXPIRE_DATETIME.")?;
-                KeyParameterValue::UsageExpireDateTime(usage_expire_datetime)
-            }
-            Tag::MIN_SECONDS_BETWEEN_OPS => {
-                let min_secs_between_ops: i32 = data
-                    .get()
-                    .context("Failed to read sql data for tag: MIN_SECONDS_BETWEEN_OPS.")?;
-                KeyParameterValue::MinSecondsBetweenOps(min_secs_between_ops)
-            }
-            Tag::MAX_USES_PER_BOOT => {
-                let max_uses_per_boot: i32 =
-                    data.get().context("Failed to read sql data for tag: MAX_USES_PER_BOOT.")?;
-                KeyParameterValue::MaxUsesPerBoot(max_uses_per_boot)
-            }
-            Tag::USER_ID => {
-                let user_id: i32 =
-                    data.get().context("Failed to read sql data for tag: USER_ID.")?;
-                KeyParameterValue::UserID(user_id)
-            }
-            Tag::USER_SECURE_ID => {
-                let user_secure_id: i64 =
-                    data.get().context("Failed to read sql data for tag: USER_SECURE_ID.")?;
-                KeyParameterValue::UserSecureID(user_secure_id)
-            }
-            Tag::NO_AUTH_REQUIRED => KeyParameterValue::NoAuthRequired,
-            Tag::USER_AUTH_TYPE => {
-                let user_auth_type: i32 = data
-                    .get()
-                    .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
-                    .context("Failed to read sql data for tag: USER_AUTH_TYPE.")?;
-                KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType(
-                    user_auth_type,
-                ))
-            }
-            Tag::AUTH_TIMEOUT => {
-                let auth_timeout: i32 =
-                    data.get().context("Failed to read sql data for tag: AUTH_TIMEOUT.")?;
-                KeyParameterValue::AuthTimeout(auth_timeout)
-            }
-            Tag::ALLOW_WHILE_ON_BODY => KeyParameterValue::AllowWhileOnBody,
-            Tag::TRUSTED_USER_PRESENCE_REQUIRED => KeyParameterValue::TrustedUserPresenceRequired,
-            Tag::TRUSTED_CONFIRMATION_REQUIRED => KeyParameterValue::TrustedConfirmationRequired,
-            Tag::UNLOCKED_DEVICE_REQUIRED => KeyParameterValue::UnlockedDeviceRequired,
-            Tag::APPLICATION_ID => {
-                let app_id: Vec<u8> =
-                    data.get().context("Failed to read sql data for tag: APPLICATION_ID.")?;
-                KeyParameterValue::ApplicationID(app_id)
-            }
-            Tag::APPLICATION_DATA => {
-                let app_data: Vec<u8> =
-                    data.get().context("Failed to read sql data for tag: APPLICATION_DATA.")?;
-                KeyParameterValue::ApplicationData(app_data)
-            }
-            Tag::CREATION_DATETIME => {
-                let creation_datetime: i64 =
-                    data.get().context("Failed to read sql data for tag: CREATION_DATETIME.")?;
-                KeyParameterValue::CreationDateTime(creation_datetime)
-            }
-            Tag::ORIGIN => {
-                let origin: i32 = data
-                    .get()
-                    .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
-                    .context("Failed to read sql data for tag: ORIGIN.")?;
-                KeyParameterValue::KeyOrigin(KeyOrigin(origin))
-            }
-            Tag::ROOT_OF_TRUST => {
-                let root_of_trust: Vec<u8> =
-                    data.get().context("Failed to read sql data for tag: ROOT_OF_TRUST.")?;
-                KeyParameterValue::RootOfTrust(root_of_trust)
-            }
-            Tag::OS_VERSION => {
-                let os_version: i32 =
-                    data.get().context("Failed to read sql data for tag: OS_VERSION.")?;
-                KeyParameterValue::OSVersion(os_version)
-            }
-            Tag::OS_PATCHLEVEL => {
-                let os_patch_level: i32 =
-                    data.get().context("Failed to read sql data for tag: OS_PATCHLEVEL.")?;
-                KeyParameterValue::OSPatchLevel(os_patch_level)
-            }
-            Tag::UNIQUE_ID => {
-                let unique_id: Vec<u8> =
-                    data.get().context("Failed to read sql data for tag: UNIQUE_ID.")?;
-                KeyParameterValue::UniqueID(unique_id)
-            }
-            Tag::ATTESTATION_CHALLENGE => {
-                let attestation_challenge: Vec<u8> = data
-                    .get()
-                    .context("Failed to read sql data for tag: ATTESTATION_CHALLENGE.")?;
-                KeyParameterValue::AttestationChallenge(attestation_challenge)
-            }
-            Tag::ATTESTATION_APPLICATION_ID => {
-                let attestation_app_id: Vec<u8> = data
-                    .get()
-                    .context("Failed to read sql data for tag: ATTESTATION_APPLICATION_ID.")?;
-                KeyParameterValue::AttestationApplicationID(attestation_app_id)
-            }
-            Tag::ATTESTATION_ID_BRAND => {
-                let attestation_id_brand: Vec<u8> =
-                    data.get().context("Failed to read sql data for tag: ATTESTATION_ID_BRAND.")?;
-                KeyParameterValue::AttestationIdBrand(attestation_id_brand)
-            }
-            Tag::ATTESTATION_ID_DEVICE => {
-                let attestation_id_device: Vec<u8> = data
-                    .get()
-                    .context("Failed to read sql data for tag: ATTESTATION_ID_DEVICE.")?;
-                KeyParameterValue::AttestationIdDevice(attestation_id_device)
-            }
-            Tag::ATTESTATION_ID_PRODUCT => {
-                let attestation_id_product: Vec<u8> = data
-                    .get()
-                    .context("Failed to read sql data for tag: ATTESTATION_ID_PRODUCT.")?;
-                KeyParameterValue::AttestationIdProduct(attestation_id_product)
-            }
-            Tag::ATTESTATION_ID_SERIAL => {
-                let attestation_id_serial: Vec<u8> = data
-                    .get()
-                    .context("Failed to read sql data for tag: ATTESTATION_ID_SERIAL.")?;
-                KeyParameterValue::AttestationIdSerial(attestation_id_serial)
-            }
-            Tag::ATTESTATION_ID_IMEI => {
-                let attestation_id_imei: Vec<u8> =
-                    data.get().context("Failed to read sql data for tag: ATTESTATION_ID_IMEI.")?;
-                KeyParameterValue::AttestationIdIMEI(attestation_id_imei)
-            }
-            Tag::ATTESTATION_ID_MEID => {
-                let attestation_id_meid: Vec<u8> =
-                    data.get().context("Failed to read sql data for tag: ATTESTATION_ID_MEID.")?;
-                KeyParameterValue::AttestationIdMEID(attestation_id_meid)
-            }
-            Tag::ATTESTATION_ID_MANUFACTURER => {
-                let attestation_id_manufacturer: Vec<u8> = data
-                    .get()
-                    .context("Failed to read sql data for tag: ATTESTATION_ID_MANUFACTURER.")?;
-                KeyParameterValue::AttestationIdManufacturer(attestation_id_manufacturer)
-            }
-            Tag::ATTESTATION_ID_MODEL => {
-                let attestation_id_model: Vec<u8> =
-                    data.get().context("Failed to read sql data for tag: ATTESTATION_ID_MODEL.")?;
-                KeyParameterValue::AttestationIdModel(attestation_id_model)
-            }
-            Tag::VENDOR_PATCHLEVEL => {
-                let vendor_patch_level: i32 =
-                    data.get().context("Failed to read sql data for tag: VENDOR_PATCHLEVEL.")?;
-                KeyParameterValue::VendorPatchLevel(vendor_patch_level)
-            }
-            Tag::BOOT_PATCHLEVEL => {
-                let boot_patch_level: i32 =
-                    data.get().context("Failed to read sql data for tag: BOOT_PATCHLEVEL.")?;
-                KeyParameterValue::BootPatchLevel(boot_patch_level)
-            }
-            Tag::ASSOCIATED_DATA => {
-                let associated_data: Vec<u8> =
-                    data.get().context("Failed to read sql data for tag: ASSOCIATED_DATA.")?;
-                KeyParameterValue::AssociatedData(associated_data)
-            }
-            Tag::NONCE => {
-                let nonce: Vec<u8> =
-                    data.get().context("Failed to read sql data for tag: NONCE.")?;
-                KeyParameterValue::Nonce(nonce)
-            }
-            Tag::MAC_LENGTH => {
-                let mac_length: i32 =
-                    data.get().context("Failed to read sql data for tag: MAC_LENGTH.")?;
-                KeyParameterValue::MacLength(mac_length)
-            }
-            Tag::RESET_SINCE_ID_ROTATION => KeyParameterValue::ResetSinceIdRotation,
-            Tag::CONFIRMATION_TOKEN => {
-                let confirmation_token: Vec<u8> =
-                    data.get().context("Failed to read sql data for tag: CONFIRMATION_TOKEN.")?;
-                KeyParameterValue::ConfirmationToken(confirmation_token)
-            }
-            _ => {
-                return Err(KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
-                    .context("Failed to decode Tag enum from value.")?
-            }
-        };
-        Ok(KeyParameter::new(key_param_value, 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() }
     }
 }
 
-/// Macro rules for converting key parameter to/from wire type.
-/// This macro takes between three and four different pieces of information about each
-/// of the KeyParameterValue variants:
-/// 1. The KeyParameterValue variant name,
-/// 2. the tag name corresponding to the variant,
-/// 3. the field name in the KmKeyParameter struct, in which information about this variant is
-///    stored when converted, and
-/// 4. an optional enum type name when the nested value is of enum type.
-/// The macro takes a set of lines corresponding to each KeyParameterValue variant and generates
-/// the two conversion methods: convert_to_wire() and convert_from_wire().
-/// ## Example
-/// ```
-/// implement_key_parameter_conversion_to_from_wire! {
-///         Invalid, INVALID, na;
-///         KeyPurpose, PURPOSE, integer, KeyPurpose;
-///         CallerNonce, CALLER_NONCE, boolValue;
-///         UserSecureID, USER_SECURE_ID, longInteger;
-///         ApplicationID, APPLICATION_ID, blob;
-/// }
-/// ```
-/// expands to:
-/// ```
-/// pub fn convert_to_wire(self) -> KmKeyParameter {
-///         match self {
-///                 KeyParameterValue::Invalid => KmKeyParameter {
-///                         tag: Tag::INVALID,
-///                         ..Default::default()
-///                 },
-///                 KeyParameterValue::KeyPurpose(v) => KmKeyParameter {
-///                         tag: Tag::PURPOSE,
-///                         integer: v.0,
-///                         ..Default::default()
-///                 },
-///                 KeyParameterValue::CallerNonce => KmKeyParameter {
-///                         tag: Tag::CALLER_NONCE,
-///                         boolValue: true,
-///                         ..Default::default()
-///                 },
-///                 KeyParameterValue::UserSecureID(v) => KmKeyParameter {
-///                         tag: Tag::USER_SECURE_ID,
-///                         longInteger: v,
-///                         ..Default::default()
-///                 },
-///                 KeyParameterValue::ApplicationID(v) => KmKeyParameter {
-///                         tag: Tag::APPLICATION_ID,
-///                         blob: v,
-///                         ..Default::default()
-///                 },
-///         }
-/// }
-/// ```
-/// and
-/// ```
-/// pub fn convert_from_wire(aidl_kp: KmKeyParameter) -> KeyParameterValue {
-///         match aidl_kp {
-///                 KmKeyParameter {
-///                         tag: Tag::INVALID,
-///                         ..
-///                 } => KeyParameterValue::Invalid,
-///                 KmKeyParameter {
-///                         tag: Tag::PURPOSE,
-///                         integer: v,
-///                         ..
-///                 } => KeyParameterValue::KeyPurpose(KeyPurpose(v)),
-///                 KmKeyParameter {
-///                         tag: Tag::CALLER_NONCE,
-///                         boolValue: true,
-///                         ..
-///                 } => KeyParameterValue::CallerNonce,
-///                 KmKeyParameter {
-///                          tag: Tag::USER_SECURE_ID,
-///                          longInteger: v,
-///                          ..
-///                 } => KeyParameterValue::UserSecureID(v),
-///                 KmKeyParameter {
-///                          tag: Tag::APPLICATION_ID,
-///                          blob: v,
-///                          ..
-///                 } => KeyParameterValue::ApplicationID(v),
-///                 _ => KeyParameterValue::Invalid,
-///         }
-/// }
-///
-macro_rules! implement_key_parameter_conversion_to_from_wire {
-    // There are three groups of rules in this macro.
-    // 1. The first group contains the rule which acts as the public interface. It takes the input
-    //    given to this macro and prepares it to be given as input to the two groups of rules
-    //    mentioned below.
-    // 2. The second group starts with the prefix @to and generates convert_to_wire() method.
-    // 3. The third group starts with the prefix @from and generates convert_from_wire() method.
-    //
-    // Input to this macro is first handled by the first macro rule (belonging to the first
-    // group above), which pre-processes the input such that rules in the other two groups
-    // generate the code for the two methods, when called recursively.
-    // Each of convert_to_wire() and convert_from_wire() methods are generated using a set of
-    // four macro rules in the second two groups. These four rules intend to do the following
-    // tasks respectively:
-    // i) generates match arms related to Invalid KeyParameterValue variant.
-    // ii) generates match arms related to boolValue field in KmKeyParameter struct.
-    // iii) generates match arms related to all the other fields in KmKeyParameter struct.
-    // iv) generates the method definition including the match arms generated from the above
-    // three recursive macro rules.
+#[cfg(test)]
+mod generated_key_parameter_tests {
+    use super::*;
+    use android_hardware_security_keymint::aidl::android::hardware::security::keymint::TagType::TagType;
 
-    // This rule is applied on the input given to the macro invocations from outside the macro.
-    ($($variant:ident, $tag_name:ident, $field_name:ident $(,$enum_type:ident)?;)*) => {
-        // pre-processes input to target the rules that generate convert_to_wire() method.
-        implement_key_parameter_conversion_to_from_wire! {@to
-            [], $($variant, $tag_name, $field_name;)*
+    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()),
+            _ => {}
         }
-        // pre-processes input to target the rules that generate convert_from_wire() method.
-        implement_key_parameter_conversion_to_from_wire! {@from
-            [], $($variant, $tag_name, $field_name;)*
+        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);
+    }
 
-    // Following four rules (belonging to the aforementioned second group) generate
-    // convert_to_wire() conversion method.
-    // -----------------------------------------------------------------------
-    // This rule handles Invalid variant.
-    // On an input: `Invalid, INVALID, Invalid;` it generates a match arm like:
-    // KeyParameterValue::Invalid => KmKeyParameter {
-    //     tag: Tag::INVALID,
-    //     value: KmKeyParameterValue::Invalid(0),
-    // },
-    (@to [$($out:tt)*], Invalid, INVALID, Invalid; $($in:tt)*) => {
-        implement_key_parameter_conversion_to_from_wire! {@to
-            [$($out)*
-                KeyParameterValue::Invalid => KmKeyParameter {
-                    tag: Tag::INVALID,
-                    value: KmKeyParameterValue::Invalid(0),
-                },
-            ], $($in)*
-        }
-    };
-    // This rule handles all variants that correspond to bool values.
-    // On an input like: `CallerNonce, CALLER_NONCE, BoolValue;` it generates
-    // a match arm like:
-    // KeyParameterValue::CallerNonce => KmKeyParameter {
-    //     tag: Tag::CALLER_NONCE,
-    //     value: KmKeyParameterValue::BoolValue(true),
-    // },
-    (@to [$($out:tt)*], $variant:ident, $tag_val:ident, BoolValue; $($in:tt)*) => {
-        implement_key_parameter_conversion_to_from_wire! {@to
-            [$($out)*
-                KeyParameterValue::$variant => KmKeyParameter {
-                    tag: Tag::$tag_val,
-                    value: KmKeyParameterValue::BoolValue(true),
-                },
-            ], $($in)*
-        }
-    };
-    // This rule handles all variants that are neither invalid nor bool values nor enums
-    // (i.e. all variants which correspond to integer, longInteger, and blob fields in
-    // KmKeyParameter).
-    // On an input like: `ConfirmationToken, CONFIRMATION_TOKEN, Blob;` it generates a match arm
-    // like: KeyParameterValue::ConfirmationToken(v) => KmKeyParameter {
-    //     tag: Tag::CONFIRMATION_TOKEN,
-    //     value: KmKeyParameterValue::$field(v),
-    // },
-    (@to [$($out:tt)*], $variant:ident, $tag_val:ident, $field:ident; $($in:tt)*) => {
-        implement_key_parameter_conversion_to_from_wire! {@to
-            [$($out)*
-                KeyParameterValue::$variant(v) => KmKeyParameter {
-                    tag: Tag::$tag_val,
-                    value: KmKeyParameterValue::$field(v),
-                },
-            ], $($in)*
-        }
-    };
-    // After all the match arms are generated by the above three rules, this rule combines them
-    // into the convert_to_wire() method.
-    (@to [$($out:tt)*], ) => {
-        /// Conversion of key parameter to wire type
-        pub fn convert_to_wire(self) -> KmKeyParameter {
-            match self {
-                $($out)*
+    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
+                ),
             }
         }
-    };
+    }
 
-    // Following four rules (belonging to the aforementioned third group) generate
-    // convert_from_wire() conversion method.
-    // ------------------------------------------------------------------------
-    // This rule handles Invalid variant.
-    // On an input: `Invalid, INVALID, Invalid;` it generates a match arm like:
-    // KmKeyParameter { tag: Tag::INVALID, .. } => KeyParameterValue::Invalid,
-    (@from [$($out:tt)*], Invalid, INVALID, Invalid; $($in:tt)*) => {
-        implement_key_parameter_conversion_to_from_wire! {@from
-            [$($out)*
-                KmKeyParameter {
-                    tag: Tag::INVALID,
-                    ..
-                } => KeyParameterValue::Invalid,
-            ], $($in)*
-        }
-    };
-    // This rule handles all variants that correspond to bool values.
-    // On an input like: `CallerNonce, CALLER_NONCE, BoolValue;` it generates a match arm like:
-    // KmKeyParameter {
-    //      tag: Tag::CALLER_NONCE,
-    //      boolValue: true,
-    //      ..
-    // } => KeyParameterValue::CallerNonce,
-    (@from [$($out:tt)*], $variant:ident, $tag_val:ident, BoolValue; $($in:tt)*) => {
-        implement_key_parameter_conversion_to_from_wire! {@from
-            [$($out)*
-                KmKeyParameter {
-                    tag: Tag::$tag_val,
-                    value: KmKeyParameterValue::BoolValue(true),
-                } => KeyParameterValue::$variant,
-            ], $($in)*
-        }
-    };
-    // This rule handles all variants that are neither invalid nor bool values nor enums
-    // (i.e. all variants which correspond to integer, longInteger, and blob fields in
-    // KmKeyParameter).
-    // On an input like: `ConfirmationToken, CONFIRMATION_TOKEN, Blob;` it generates a match arm
-    // like:
-    // KmKeyParameter {
-    //         tag: Tag::CONFIRMATION_TOKEN,
-    //         value: KmKeyParameterValue::Blob(v),
-    // } => KeyParameterValue::ConfirmationToken(v),
-    (@from [$($out:tt)*], $variant:ident, $tag_val:ident, $field:ident; $($in:tt)*) => {
-        implement_key_parameter_conversion_to_from_wire! {@from
-            [$($out)*
-                KmKeyParameter {
-                    tag: Tag::$tag_val,
-                    value: KmKeyParameterValue::$field(v),
-                } => KeyParameterValue::$variant(v),
-            ], $($in)*
-        }
-    };
-    // After all the match arms are generated by the above three rules, this rule combines them
-    // into the convert_from_wire() method.
-    (@from [$($out:tt)*], ) => {
-        /// Conversion of key parameter from wire type
-        pub fn convert_from_wire(aidl_kp: KmKeyParameter) -> KeyParameterValue {
-            match aidl_kp {
-                $($out)*
-                _ => KeyParameterValue::Invalid,
-            }
-        }
-    };
-}
-
-impl KeyParameterValue {
-    // Invoke the macro that generates the code for key parameter conversion to/from wire type
-    // with all possible variants of KeyParameterValue. Each line corresponding to a variant
-    // contains: variant identifier, tag value, and the related field name (i.e.
-    // boolValue/integer/longInteger/blob) in the KmKeyParameter.
-    implement_key_parameter_conversion_to_from_wire! {
-        Invalid, INVALID, Invalid;
-        KeyPurpose, PURPOSE, KeyPurpose;
-        Algorithm, ALGORITHM, Algorithm;
-        KeySize, KEY_SIZE, Integer;
-        BlockMode, BLOCK_MODE, BlockMode;
-        Digest, DIGEST, Digest;
-        PaddingMode, PADDING, PaddingMode;
-        CallerNonce, CALLER_NONCE, BoolValue;
-        MinMacLength, MIN_MAC_LENGTH, Integer;
-        EcCurve, EC_CURVE, EcCurve;
-        RSAPublicExponent, RSA_PUBLIC_EXPONENT, LongInteger;
-        IncludeUniqueID, INCLUDE_UNIQUE_ID, BoolValue;
-        BootLoaderOnly, BOOTLOADER_ONLY, BoolValue;
-        RollbackResistance, ROLLBACK_RESISTANCE, BoolValue;
-        ActiveDateTime, ACTIVE_DATETIME, DateTime;
-        OriginationExpireDateTime, ORIGINATION_EXPIRE_DATETIME, DateTime;
-        UsageExpireDateTime, USAGE_EXPIRE_DATETIME, DateTime;
-        MinSecondsBetweenOps, MIN_SECONDS_BETWEEN_OPS, Integer;
-        MaxUsesPerBoot, MAX_USES_PER_BOOT, Integer;
-        UserID, USER_ID, Integer;
-        UserSecureID, USER_SECURE_ID, LongInteger;
-        NoAuthRequired, NO_AUTH_REQUIRED, BoolValue;
-        HardwareAuthenticatorType, USER_AUTH_TYPE, HardwareAuthenticatorType;
-        AuthTimeout, AUTH_TIMEOUT, Integer;
-        AllowWhileOnBody, ALLOW_WHILE_ON_BODY, BoolValue;
-        TrustedUserPresenceRequired, TRUSTED_USER_PRESENCE_REQUIRED, BoolValue;
-        TrustedConfirmationRequired, TRUSTED_CONFIRMATION_REQUIRED, BoolValue;
-        UnlockedDeviceRequired, UNLOCKED_DEVICE_REQUIRED, BoolValue;
-        ApplicationID, APPLICATION_ID, Blob;
-        ApplicationData, APPLICATION_DATA, Blob;
-        CreationDateTime, CREATION_DATETIME, DateTime;
-        KeyOrigin, ORIGIN, Origin;
-        RootOfTrust, ROOT_OF_TRUST, Blob;
-        OSVersion, OS_VERSION, Integer;
-        OSPatchLevel, OS_PATCHLEVEL, Integer;
-        UniqueID, UNIQUE_ID, Blob;
-        AttestationChallenge, ATTESTATION_CHALLENGE, Blob;
-        AttestationApplicationID, ATTESTATION_APPLICATION_ID, Blob;
-        AttestationIdBrand, ATTESTATION_ID_BRAND, Blob;
-        AttestationIdDevice, ATTESTATION_ID_DEVICE, Blob;
-        AttestationIdProduct, ATTESTATION_ID_PRODUCT, Blob;
-        AttestationIdSerial, ATTESTATION_ID_SERIAL, Blob;
-        AttestationIdIMEI, ATTESTATION_ID_IMEI, Blob;
-        AttestationIdMEID, ATTESTATION_ID_MEID, Blob;
-        AttestationIdManufacturer, ATTESTATION_ID_MANUFACTURER, Blob;
-        AttestationIdModel, ATTESTATION_ID_MODEL, Blob;
-        VendorPatchLevel, VENDOR_PATCHLEVEL, Integer;
-        BootPatchLevel, BOOT_PATCHLEVEL, Integer;
-        AssociatedData, ASSOCIATED_DATA, Blob;
-        Nonce, NONCE, Blob;
-        MacLength, MAC_LENGTH, Integer;
-        ResetSinceIdRotation, RESET_SINCE_ID_ROTATION, BoolValue;
-        ConfirmationToken, CONFIRMATION_TOKEN, Blob;
+    #[test]
+    fn key_parameter_value_field_matches_tag_type() {
+        check_field_matches_tag_type(&KeyParameterValue::make_field_matches_tag_type_test_vector());
     }
 }
 
@@ -1108,10 +1302,8 @@
     fn test_non_existing_enum_variant() -> Result<()> {
         let db = init_db()?;
         insert_into_keyparameter(&db, 1, 100, &123, 1)?;
-        tests::check_result_contains_error_string(
-            query_from_keyparameter(&db),
-            "Failed to decode Tag enum from value.",
-        );
+        let key_param = query_from_keyparameter(&db)?;
+        assert_eq!(Tag::INVALID, key_param.get_tag());
         Ok(())
     }
 
@@ -1197,15 +1389,18 @@
     #[test]
     fn test_convert_to_wire_invalid() {
         let kp = KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::STRONGBOX);
-        let actual = KeyParameterValue::convert_to_wire(kp.key_parameter_value);
-        assert_eq!(Tag::INVALID, actual.tag);
+        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);
-        let actual = KeyParameterValue::convert_to_wire(kp.key_parameter_value);
-        assert_eq!(Tag::CALLER_NONCE, actual.tag);
-        assert_eq!(KmKeyParameterValue::BoolValue(true), actual.value);
+        assert_eq!(
+            KmKeyParameter { tag: Tag::CALLER_NONCE, value: KmKeyParameterValue::BoolValue(true) },
+            kp.value.into()
+        );
     }
     #[test]
     fn test_convert_to_wire_integer() {
@@ -1213,17 +1408,25 @@
             KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
             SecurityLevel::STRONGBOX,
         );
-        let actual = KeyParameterValue::convert_to_wire(kp.key_parameter_value);
-        assert_eq!(Tag::PURPOSE, actual.tag);
-        assert_eq!(KmKeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT), actual.value);
+        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);
-        let actual = KeyParameterValue::convert_to_wire(kp.key_parameter_value);
-        assert_eq!(Tag::USER_SECURE_ID, actual.tag);
-        assert_eq!(KmKeyParameterValue::LongInteger(i64::MAX), actual.value);
+        assert_eq!(
+            KmKeyParameter {
+                tag: Tag::USER_SECURE_ID,
+                value: KmKeyParameterValue::LongInteger(i64::MAX)
+            },
+            kp.value.into()
+        );
     }
     #[test]
     fn test_convert_to_wire_blob() {
@@ -1231,11 +1434,12 @@
             KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
             SecurityLevel::STRONGBOX,
         );
-        let actual = KeyParameterValue::convert_to_wire(kp.key_parameter_value);
-        assert_eq!(Tag::CONFIRMATION_TOKEN, actual.tag);
         assert_eq!(
-            KmKeyParameterValue::Blob(String::from("ConfirmationToken").into_bytes()),
-            actual.value
+            KmKeyParameter {
+                tag: Tag::CONFIRMATION_TOKEN,
+                value: KmKeyParameterValue::Blob(String::from("ConfirmationToken").into_bytes())
+            },
+            kp.value.into()
         );
     }
 
@@ -1243,15 +1447,13 @@
     #[test]
     fn test_convert_from_wire_invalid() {
         let aidl_kp = KmKeyParameter { tag: Tag::INVALID, ..Default::default() };
-        let actual = KeyParameterValue::convert_from_wire(aidl_kp);
-        assert_eq!(KeyParameterValue::Invalid, actual);
+        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) };
-        let actual = KeyParameterValue::convert_from_wire(aidl_kp);
-        assert_eq!(KeyParameterValue::CallerNonce, actual);
+        assert_eq!(KeyParameterValue::CallerNonce, aidl_kp.into());
     }
     #[test]
     fn test_convert_from_wire_integer() {
@@ -1259,8 +1461,7 @@
             tag: Tag::PURPOSE,
             value: KmKeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
         };
-        let actual = KeyParameterValue::convert_from_wire(aidl_kp);
-        assert_eq!(KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT), actual);
+        assert_eq!(KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT), aidl_kp.into());
     }
     #[test]
     fn test_convert_from_wire_long_integer() {
@@ -1268,8 +1469,7 @@
             tag: Tag::USER_SECURE_ID,
             value: KmKeyParameterValue::LongInteger(i64::MAX),
         };
-        let actual = KeyParameterValue::convert_from_wire(aidl_kp);
-        assert_eq!(KeyParameterValue::UserSecureID(i64::MAX), actual);
+        assert_eq!(KeyParameterValue::UserSecureID(i64::MAX), aidl_kp.into());
     }
     #[test]
     fn test_convert_from_wire_blob() {
@@ -1277,10 +1477,9 @@
             tag: Tag::CONFIRMATION_TOKEN,
             value: KmKeyParameterValue::Blob(String::from("ConfirmationToken").into_bytes()),
         };
-        let actual = KeyParameterValue::convert_from_wire(aidl_kp);
         assert_eq!(
             KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
-            actual
+            aidl_kp.into()
         );
     }
 }
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index 2916549..7391f37 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -15,11 +15,13 @@
 //! This crate implements the Keystore 2.0 service entry point.
 
 use binder::Interface;
+use keystore2::apc::ApcManager;
 use keystore2::service::KeystoreService;
 use log::{error, info};
 use std::panic;
 
 static KS2_SERVICE_NAME: &str = "android.system.keystore2";
+static APC_SERVICE_NAME: &str = "android.security.apc";
 
 /// Keystore 2.0 takes one argument which is a path indicating its designated working directory.
 fn main() {
@@ -48,6 +50,9 @@
         panic!("Must specify a working directory.");
     }
 
+    info!("Starting thread pool now.");
+    binder::ProcessState::start_thread_pool();
+
     let ks_service = KeystoreService::new_native_binder().unwrap_or_else(|e| {
         panic!("Failed to create service {} because of {:?}.", KS2_SERVICE_NAME, e);
     });
@@ -55,10 +60,14 @@
         panic!("Failed to register service {} because of {:?}.", KS2_SERVICE_NAME, e);
     });
 
-    info!("Successfully registered Keystore 2.0 service.");
+    let apc_service = ApcManager::new_native_binder().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);
+    });
 
-    info!("Starting thread pool now.");
-    binder::ProcessState::start_thread_pool();
+    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..7ca6a63
--- /dev/null
+++ b/keystore2/src/km_compat/Android.bp
@@ -0,0 +1,108 @@
+// 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_library {
+    name: "libkeystore2_km_compat",
+    crate_name: "keystore2_km_compat",
+    srcs: ["lib.rs"],
+
+    rustlibs: [
+        "android.hardware.security.keymint-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-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-unstable-ndk_platform",
+        "android.security.compat-ndk_platform",
+        "android.system.keystore2-ndk_platform",
+        "libbase",
+        "libbinder_ndk",
+        "libcrypto",
+        "libhidlbase",
+        "libkeymaster4_1support",
+        "libkeymint_support",
+        "libkeystore2_crypto",
+        "libutils",
+    ],
+}
+
+cc_library {
+    name: "libkm_compat_service",
+    srcs: ["km_compat_service.cpp"],
+    shared_libs: [
+        "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-unstable-ndk_platform",
+        "android.security.compat-ndk_platform",
+        "android.system.keystore2-ndk_platform",
+        "libbase",
+        "libbinder_ndk",
+        "libcrypto",
+        "libhidlbase",
+        "libkeymaster4_1support",
+        "libkeymint_support",
+        "libkeystore2_crypto",
+        "libkm_compat",
+        "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..9307569
--- /dev/null
+++ b/keystore2/src/km_compat/certificate_test.cpp
@@ -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.
+ */
+
+#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 <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;
+
+namespace KMV1 = ::aidl::android::hardware::security::keymint;
+
+static std::variant<std::vector<Certificate>, ScopedAStatus>
+getCertificate(const std::vector<KeyParameter>& keyParams) {
+    static std::shared_ptr<KeyMintDevice> device =
+        KeyMintDevice::createKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
+    if (!device) {
+        return ScopedAStatus::fromStatus(STATUS_NAME_NOT_FOUND);
+    }
+    ByteArray blob;
+    KeyCharacteristics characteristics;
+    std::vector<Certificate> certChain;
+    auto status = device->generateKey(keyParams, &blob, &characteristics, &certChain);
+    if (!status.isOk()) {
+        return status;
+    }
+    return certChain;
+}
+
+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),
+    });
+    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),
+        KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::ENCRYPT),
+    });
+    auto result = getCertificate(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 result = getCertificate(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 result = getCertificate(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 result = getCertificate(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 result = getCertificate(keyParams);
+    ensureCertChainSize(result, 1);
+}
diff --git a/keystore2/src/km_compat/gtest_main.cpp b/keystore2/src/km_compat/gtest_main.cpp
new file mode 100644
index 0000000..149cbbc
--- /dev/null
+++ b/keystore2/src/km_compat/gtest_main.cpp
@@ -0,0 +1,21 @@
+/*
+ * 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 <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..c9af80d
--- /dev/null
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -0,0 +1,879 @@
+/*
+ * 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 <aidl/android/hardware/security/keymint/Algorithm.h>
+#include <aidl/android/hardware/security/keymint/Digest.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 "certificate_utils.h"
+
+using ::aidl::android::hardware::security::keymint::Algorithm;
+using ::aidl::android::hardware::security::keymint::Digest;
+using ::aidl::android::hardware::security::keymint::PaddingMode;
+using ::aidl::android::hardware::security::keymint::Tag;
+using ::aidl::android::hardware::security::keymint::VerificationToken;
+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_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;
+
+// Utility functions
+
+// Converts a V4 error code into a ScopedAStatus
+ScopedAStatus convertErrorCode(V4_0_ErrorCode result) {
+    if (result == V4_0_ErrorCode::OK) {
+        return ScopedAStatus::ok();
+    }
+    return ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(result));
+}
+
+static std::vector<V4_0::KeyParameter>
+convertKeyParametersToLegacy(const std::vector<KeyParameter>& kps) {
+    std::vector<V4_0::KeyParameter> legacyKps(kps.size());
+    std::transform(kps.begin(), kps.end(), legacyKps.begin(), convertKeyParameterToLegacy);
+    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 KeyCharacteristics
+convertKeyCharacteristicsFromLegacy(const V4_0_KeyCharacteristics& legacyKc) {
+    KeyCharacteristics kc;
+    kc.softwareEnforced = convertKeyParametersFromLegacy(legacyKc.softwareEnforced);
+    kc.hardwareEnforced = convertKeyParametersFromLegacy(legacyKc.hardwareEnforced);
+    return kc;
+}
+
+static V4_0_KeyFormat convertKeyFormatToLegacy(const KeyFormat& kf) {
+    return static_cast<V4_0_KeyFormat>(kf);
+}
+
+static V4_0_HardwareAuthToken convertAuthTokenToLegacy(const HardwareAuthToken& at) {
+    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 convertVerificationTokenToLegacy(const VerificationToken& vt) {
+    V4_0_VerificationToken legacyVt;
+    legacyVt.challenge = vt.challenge;
+    legacyVt.timestamp = vt.timestamp.milliSeconds;
+    legacyVt.securityLevel =
+        static_cast<::android::hardware::keymaster::V4_0::SecurityLevel>(vt.securityLevel);
+    legacyVt.mac = vt.mac;
+    return legacyVt;
+}
+
+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) {
+    // TODO: What do I do about the version number?  Is it the version of the device I get?
+    auto result = mDevice->getHardwareInfo([&](auto securityLevel, const auto& keymasterName,
+                                               const auto& keymasterAuthorName) {
+        _aidl_return->securityLevel =
+            static_cast<::aidl::android::hardware::security::keymint::SecurityLevel>(securityLevel);
+        _aidl_return->keyMintName = keymasterName;
+        _aidl_return->keyMintAuthorName = keymasterAuthorName;
+    });
+    if (!result.isOk()) {
+        return ScopedAStatus::fromServiceSpecificError(
+            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+    }
+    return ScopedAStatus::ok();
+}
+
+// We're not implementing this.
+ScopedAStatus KeyMintDevice::verifyAuthorization(int64_t in_challenge ATTRIBUTE_UNUSED,
+                                                 const HardwareAuthToken& in_token ATTRIBUTE_UNUSED,
+                                                 VerificationToken* _aidl_return ATTRIBUTE_UNUSED) {
+    return ScopedAStatus::fromServiceSpecificError(
+        static_cast<int32_t>(V4_0_ErrorCode::UNIMPLEMENTED));
+}
+
+ScopedAStatus KeyMintDevice::addRngEntropy(const std::vector<uint8_t>& in_data) {
+    V4_0_ErrorCode errorCode = mDevice->addRngEntropy(in_data);
+    return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintDevice::generateKey(const std::vector<KeyParameter>& in_keyParams,
+                                         ByteArray* out_generatedKeyBlob,
+                                         KeyCharacteristics* out_generatedKeyCharacteristics,
+                                         std::vector<Certificate>* out_outCertChain) {
+    auto legacyKeyParams = convertKeyParametersToLegacy(in_keyParams);
+    V4_0_ErrorCode errorCode;
+    auto result = mDevice->generateKey(
+        legacyKeyParams, [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
+                             const V4_0_KeyCharacteristics& keyCharacteristics) {
+            errorCode = error;
+            out_generatedKeyBlob->data = keyBlob;
+            *out_generatedKeyCharacteristics =
+                convertKeyCharacteristicsFromLegacy(keyCharacteristics);
+        });
+    if (!result.isOk()) {
+        return ScopedAStatus::fromServiceSpecificError(
+            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+    }
+    if (errorCode == V4_0_ErrorCode::OK) {
+        auto cert = getCertificate(in_keyParams, out_generatedKeyBlob->data);
+        if (std::holds_alternative<V4_0_ErrorCode>(cert)) {
+            auto code = std::get<V4_0_ErrorCode>(cert);
+            // We return OK in successful cases that do not generate a certificate.
+            if (code != V4_0_ErrorCode::OK) {
+                errorCode = code;
+                deleteKey(out_generatedKeyBlob->data);
+            }
+        } else {
+            *out_outCertChain = std::get<std::vector<Certificate>>(cert);
+        }
+    }
+    return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintDevice::importKey(const std::vector<KeyParameter>& in_inKeyParams,
+                                       KeyFormat in_inKeyFormat,
+                                       const std::vector<uint8_t>& in_inKeyData,
+                                       ByteArray* out_outImportedKeyBlob,
+                                       KeyCharacteristics* out_outImportedKeyCharacteristics,
+                                       std::vector<Certificate>* out_outCertChain) {
+    auto legacyKeyParams = convertKeyParametersToLegacy(in_inKeyParams);
+    auto legacyKeyFormat = convertKeyFormatToLegacy(in_inKeyFormat);
+    V4_0_ErrorCode errorCode;
+    auto result =
+        mDevice->importKey(legacyKeyParams, legacyKeyFormat, in_inKeyData,
+                           [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
+                               const V4_0_KeyCharacteristics& keyCharacteristics) {
+                               errorCode = error;
+                               out_outImportedKeyBlob->data = keyBlob;
+                               *out_outImportedKeyCharacteristics =
+                                   convertKeyCharacteristicsFromLegacy(keyCharacteristics);
+                           });
+    if (!result.isOk()) {
+        return ScopedAStatus::fromServiceSpecificError(
+            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+    }
+    if (errorCode == V4_0_ErrorCode::OK) {
+        auto cert = getCertificate(in_inKeyParams, out_outImportedKeyBlob->data);
+        if (std::holds_alternative<V4_0_ErrorCode>(cert)) {
+            auto code = std::get<V4_0_ErrorCode>(cert);
+            // We return OK in successful cases that do not generate a certificate.
+            if (code != V4_0_ErrorCode::OK) {
+                errorCode = code;
+                deleteKey(out_outImportedKeyBlob->data);
+            }
+        } else {
+            *out_outCertChain = 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_inWrappingKeyBlob, const std::vector<uint8_t>& in_inMaskingKey,
+    const std::vector<KeyParameter>& in_inUnwrappingParams, int64_t in_inPasswordSid,
+    int64_t in_inBiometricSid, ByteArray* out_outImportedKeyBlob,
+    KeyCharacteristics* out_outImportedKeyCharacteristics) {
+    auto legacyUnwrappingParams = convertKeyParametersToLegacy(in_inUnwrappingParams);
+    V4_0_ErrorCode errorCode;
+    auto result =
+        mDevice->importWrappedKey(in_inWrappedKeyData, in_inWrappingKeyBlob, in_inMaskingKey,
+                                  legacyUnwrappingParams, in_inPasswordSid, in_inBiometricSid,
+                                  [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
+                                      const V4_0_KeyCharacteristics& keyCharacteristics) {
+                                      errorCode = error;
+                                      out_outImportedKeyBlob->data = keyBlob;
+                                      *out_outImportedKeyCharacteristics =
+                                          convertKeyCharacteristicsFromLegacy(keyCharacteristics);
+                                  });
+    if (!result.isOk()) {
+        return ScopedAStatus::fromServiceSpecificError(
+            static_cast<int32_t>(ResponseCode::SYSTEM_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(in_inKeyBlobToUpgrade, legacyUpgradeParams,
+                            [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& upgradedKeyBlob) {
+                                errorCode = error;
+                                *_aidl_return = upgradedKeyBlob;
+                            });
+    if (!result.isOk()) {
+        return ScopedAStatus::fromServiceSpecificError(
+            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+    }
+    return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintDevice::deleteKey(const std::vector<uint8_t>& in_inKeyBlob) {
+    V4_0_ErrorCode errorCode = mDevice->deleteKey(in_inKeyBlob);
+    return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintDevice::deleteAllKeys() {
+    V4_0_ErrorCode errorCode = mDevice->deleteAllKeys();
+    return convertErrorCode(errorCode);
+}
+
+// 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>& in_inKeyBlob,
+                                   const std::vector<KeyParameter>& in_inParams,
+                                   const HardwareAuthToken& in_inAuthToken,
+                                   BeginResult* _aidl_return) {
+    if (!mOperationSlots.claimSlot()) {
+        return convertErrorCode(V4_0_ErrorCode::TOO_MANY_OPERATIONS);
+    }
+    auto legacyPurpose =
+        static_cast<::android::hardware::keymaster::V4_0::KeyPurpose>(in_inPurpose);
+    auto legacyParams = convertKeyParametersToLegacy(in_inParams);
+    auto legacyAuthToken = convertAuthTokenToLegacy(in_inAuthToken);
+    V4_0_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 = error;
+            _aidl_return->challenge = operationHandle;  // TODO: Is this right?
+            _aidl_return->params = convertKeyParametersFromLegacy(outParams);
+            _aidl_return->operation = ndk::SharedRefBase::make<KeyMintOperation>(
+                mDevice, operationHandle, &mOperationSlots, error == V4_0_ErrorCode::OK);
+        });
+    if (!result.isOk()) {
+        // TODO: In this case we're guaranteed that _aidl_return was not initialized, right?
+        mOperationSlots.freeSlot();
+        return ScopedAStatus::fromServiceSpecificError(
+            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+    }
+    if (errorCode != V4_0_ErrorCode::OK) {
+        mOperationSlots.freeSlot();
+    }
+    return convertErrorCode(errorCode);
+}
+
+ScopedAStatus
+KeyMintOperation::update(const std::optional<KeyParameterArray>& in_inParams,
+                         const std::optional<std::vector<uint8_t>>& in_input,
+                         const std::optional<HardwareAuthToken>& in_inAuthToken,
+                         const std::optional<VerificationToken>& in_inVerificationToken,
+                         std::optional<KeyParameterArray>* out_outParams,
+                         std::optional<ByteArray>* out_output, int32_t* _aidl_return) {
+    std::vector<V4_0_KeyParameter> legacyParams;
+    if (in_inParams.has_value()) {
+        legacyParams = convertKeyParametersToLegacy(in_inParams.value().params);
+    }
+    auto input = in_input.value_or(std::vector<uint8_t>());
+    V4_0_HardwareAuthToken authToken;
+    if (in_inAuthToken.has_value()) {
+        authToken = convertAuthTokenToLegacy(in_inAuthToken.value());
+    }
+    V4_0_VerificationToken verificationToken;
+    if (in_inVerificationToken.has_value()) {
+        verificationToken = convertVerificationTokenToLegacy(in_inVerificationToken.value());
+    }
+    V4_0_ErrorCode errorCode;
+    auto result = mDevice->update(
+        mOperationHandle, legacyParams, input, authToken, verificationToken,
+        [&](V4_0_ErrorCode error, uint32_t inputConsumed,
+            const hidl_vec<V4_0_KeyParameter>& outParams, const hidl_vec<uint8_t>& output) {
+            errorCode = error;
+            out_outParams->emplace();
+            out_outParams->value().params = convertKeyParametersFromLegacy(outParams);
+            out_output->emplace();
+            out_output->value().data = output;
+            *_aidl_return = inputConsumed;
+        });
+    if (!result.isOk()) {
+        mOperationSlot.freeSlot();
+        return ScopedAStatus::fromServiceSpecificError(
+            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+    }
+    if (errorCode != V4_0_ErrorCode::OK) {
+        mOperationSlot.freeSlot();
+    }
+    return convertErrorCode(errorCode);
+}
+
+ScopedAStatus
+KeyMintOperation::finish(const std::optional<KeyParameterArray>& in_inParams,
+                         const std::optional<std::vector<uint8_t>>& in_input,
+                         const std::optional<std::vector<uint8_t>>& in_inSignature,
+                         const std::optional<HardwareAuthToken>& in_authToken,
+                         const std::optional<VerificationToken>& in_inVerificationToken,
+                         std::optional<KeyParameterArray>* out_outParams,
+                         std::vector<uint8_t>* _aidl_return) {
+    V4_0_ErrorCode errorCode;
+    std::vector<V4_0_KeyParameter> legacyParams;
+    if (in_inParams.has_value()) {
+        legacyParams = convertKeyParametersToLegacy(in_inParams.value().params);
+    }
+    auto input = in_input.value_or(std::vector<uint8_t>());
+    auto signature = in_inSignature.value_or(std::vector<uint8_t>());
+    V4_0_HardwareAuthToken authToken;
+    if (in_authToken.has_value()) {
+        authToken = convertAuthTokenToLegacy(in_authToken.value());
+    }
+    V4_0_VerificationToken verificationToken;
+    if (in_inVerificationToken.has_value()) {
+        verificationToken = convertVerificationTokenToLegacy(in_inVerificationToken.value());
+    }
+    auto result = mDevice->finish(
+        mOperationHandle, legacyParams, input, signature, authToken, verificationToken,
+        [&](V4_0_ErrorCode error, const hidl_vec<V4_0_KeyParameter>& outParams,
+            const hidl_vec<uint8_t>& output) {
+            errorCode = error;
+            out_outParams->emplace();
+            out_outParams->value().params = convertKeyParametersFromLegacy(outParams);
+            *_aidl_return = output;
+        });
+    mOperationSlot.freeSlot();
+    if (!result.isOk()) {
+        return ScopedAStatus::fromServiceSpecificError(
+            static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+    }
+    return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintOperation::abort() {
+    V4_0_ErrorCode errorCode = mDevice->abort(mOperationHandle);
+    mOperationSlot.freeSlot();
+    return convertErrorCode(errorCode);
+}
+
+KeyMintOperation::~KeyMintOperation() {
+    if (mOperationSlot.hasSlot()) {
+        auto error = abort();
+        if (!error.isOk()) {
+            LOG(WARNING) << "Error calling abort in ~KeyMintOperation: " << error.getMessage();
+        }
+    }
+}
+
+// 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 it = std::find(sortedOptions.begin(), sortedOptions.end(), *value);
+            if (std::distance(it, bestSoFar) < 0) {
+                bestSoFar = it;
+            }
+        }
+    }
+    if (bestSoFar == sortedOptions.end()) {
+        return sortedOptions[0];
+    }
+    return *bestSoFar;
+}
+
+static std::variant<keystore::X509_Ptr, V4_0_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.
+    V4_0_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 = error;
+            key = keyMaterial;
+        });
+    if (!result.isOk()) {
+        return V4_0_ErrorCode::UNKNOWN_ERROR;
+    }
+    if (errorCode != V4_0_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
+    // TODO: Get the serial and subject from key params once the tags are added.  Also use new tags
+    // for the two datetime parameters once we get those.
+
+    uint64_t activation = 0;
+    if (auto date = getParam(keyParams, KMV1::TAG_ACTIVE_DATETIME)) {
+        activation = *date;
+    }
+    uint64_t expiration = std::numeric_limits<uint64_t>::max();
+    if (auto date = getParam(keyParams, KMV1::TAG_USAGE_EXPIRE_DATETIME)) {
+        expiration = *date;
+    }
+
+    auto certOrError = keystore::makeCert(
+        pkey, 42, "TODO", 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 V4_0_ErrorCode::UNKNOWN_ERROR;
+    }
+    return std::move(std::get<keystore::X509_Ptr>(certOrError));
+}
+
+static std::variant<keystore::Algo, V4_0_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 V4_0_ErrorCode::UNKNOWN_ERROR;
+    }
+}
+
+static std::variant<keystore::Padding, V4_0_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, V4_0_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 V4_0_ErrorCode::UNKNOWN_ERROR;
+    }
+}
+
+std::optional<V4_0_ErrorCode>
+KeyMintDevice::signCertificate(const std::vector<KeyParameter>& keyParams,
+                               const std::vector<uint8_t>& keyBlob, X509* cert) {
+    auto algorithm = getParam(keyParams, KMV1::TAG_ALGORITHM);
+    auto algoOrError = getKeystoreAlgorithm(*algorithm);
+    if (std::holds_alternative<V4_0_ErrorCode>(algoOrError)) {
+        return std::get<V4_0_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<V4_0_ErrorCode>(paddingOrError)) {
+        return std::get<V4_0_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});
+    auto digestOrError = getKeystoreDigest(origDigest);
+    if (std::holds_alternative<V4_0_ErrorCode>(digestOrError)) {
+        return std::get<V4_0_ErrorCode>(digestOrError);
+    }
+    auto digest = std::get<keystore::Digest>(digestOrError);
+
+    V4_0_ErrorCode errorCode = V4_0_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_PADDING, origPadding),
+                KMV1::makeKeyParameter(KMV1::TAG_DIGEST, origDigest),
+            };
+            BeginResult beginResult;
+            auto error = begin(KeyPurpose::SIGN, keyBlob, kps, HardwareAuthToken(), &beginResult);
+            if (!error.isOk()) {
+                errorCode = static_cast<V4_0_ErrorCode>(error.getServiceSpecificError());
+                return std::vector<uint8_t>();
+            }
+            std::optional<KeyParameterArray> outParams;
+            std::optional<ByteArray> outByte;
+            int32_t status;
+            beginResult.operation->update(std::nullopt, dataVec, HardwareAuthToken(),
+                                          VerificationToken(), &outParams, &outByte, &status);
+            if (!status) {
+                return std::vector<uint8_t>();
+            }
+            std::vector<uint8_t> result;
+            error = beginResult.operation->finish(std::nullopt, std::nullopt, std::nullopt,
+                                                  std::nullopt, std::nullopt, &outParams, &result);
+            if (!error.isOk()) {
+                errorCode = static_cast<V4_0_ErrorCode>(error.getServiceSpecificError());
+                return std::vector<uint8_t>();
+            }
+            return result;
+        },
+        algo, padding, digest);
+    if (error) {
+        LOG(ERROR) << __func__
+                   << ": signCertWith failed. (Callback diagnosed: " << toString(errorCode) << ")";
+        return V4_0_ErrorCode::UNKNOWN_ERROR;
+    }
+    if (errorCode != V4_0_ErrorCode::OK) {
+        return errorCode;
+    }
+    return std::nullopt;
+}
+
+std::variant<std::vector<Certificate>, V4_0_ErrorCode>
+KeyMintDevice::getCertificate(const std::vector<KeyParameter>& keyParams,
+                              const std::vector<uint8_t>& keyBlob) {
+    // 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 V4_0_ErrorCode::UNKNOWN_ERROR;
+    }
+    switch (*algorithm) {
+    case Algorithm::RSA:
+    case Algorithm::EC:
+        break;
+    default:
+        return V4_0_ErrorCode::OK;
+    }
+
+    // If attestation was requested, call and use attestKey.
+    if (containsParam(keyParams, KMV1::TAG_ATTESTATION_CHALLENGE)) {
+        auto legacyParams = convertKeyParametersToLegacy(keyParams);
+        std::vector<Certificate> certs;
+        V4_0_ErrorCode errorCode = V4_0_ErrorCode::OK;
+        auto result = mDevice->attestKey(
+            keyBlob, legacyParams,
+            [&](V4_0_ErrorCode error, const hidl_vec<hidl_vec<uint8_t>>& certChain) {
+                errorCode = error;
+                for (const auto& cert : certChain) {
+                    Certificate certificate;
+                    certificate.encodedCertificate = cert;
+                    certs.push_back(certificate);
+                }
+            });
+        if (!result.isOk()) {
+            return V4_0_ErrorCode::UNKNOWN_ERROR;
+        }
+        if (errorCode != V4_0_ErrorCode::OK) {
+            return errorCode;
+        }
+        return certs;
+    }
+
+    // makeCert
+    auto certOrError = makeCert(mDevice, keyParams, keyBlob);
+    if (std::holds_alternative<V4_0_ErrorCode>(certOrError)) {
+        return std::get<V4_0_ErrorCode>(certOrError);
+    }
+    auto cert = std::move(std::get<keystore::X509_Ptr>(certOrError));
+
+    // setIssuer
+    auto error = keystore::setIssuer(&*cert, &*cert, false);
+    if (error) {
+        return V4_0_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 (canSelfSign && noAuthRequired) {
+        auto errorCode = signCertificate(keyParams, keyBlob, &*cert);
+        if (errorCode.has_value()) {
+            return errorCode.value();
+        }
+    } else {
+        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) {
+        return V4_0_ErrorCode::UNKNOWN_ERROR;
+    }
+
+    // encodeCert
+    auto encodedCertOrError = keystore::encodeCert(&*cert);
+    if (std::holds_alternative<keystore::CertUtilsError>(encodedCertOrError)) {
+        return V4_0_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 keymaster = ::android::hardware::keymaster::V4_1;
+
+using keymaster::SecurityLevel;
+
+// Copied from system/security/keystore/KeyStore.h.
+
+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];
+    }
+};
+
+using KeymasterDevices = Devices<sp<Keymaster>, 3>;
+
+// Copied from system/security/keystore/keystore_main.cpp.
+
+using ::android::hardware::hidl_string;
+using keymaster::support::Keymaster3;
+using keymaster::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;
+}
+
+// KeyMintDevice implementation
+
+KeyMintDevice::KeyMintDevice(sp<Keymaster> device, KeyMintSecurityLevel securityLevel)
+    : mDevice(device) {
+    if (securityLevel == KeyMintSecurityLevel::STRONGBOX) {
+        mOperationSlots.setNumFreeSlots(3);
+    } else {
+        mOperationSlots.setNumFreeSlots(15);
+    }
+}
+
+void KeyMintDevice::setNumFreeSlots(uint8_t numFreeSlots) {
+    mOperationSlots.setNumFreeSlots(numFreeSlots);
+}
+
+std::shared_ptr<KeyMintDevice>
+KeyMintDevice::createKeyMintDevice(KeyMintSecurityLevel securityLevel) {
+    static std::mutex mutex;
+    std::lock_guard<std::mutex> lock(mutex);
+    static std::shared_ptr<KeyMintDevice> device_ptr;
+    if (!device_ptr) {
+        auto secLevel = static_cast<SecurityLevel>(securityLevel);
+        auto devices = initializeKeymasters();
+        auto device = devices[secLevel];
+        if (!device) {
+            return {};
+        }
+        device_ptr = ndk::SharedRefBase::make<KeyMintDevice>(std::move(device), securityLevel);
+    }
+    return device_ptr;
+}
+
+ScopedAStatus
+KeystoreCompatService::getKeyMintDevice(KeyMintSecurityLevel in_securityLevel,
+                                        std::shared_ptr<IKeyMintDevice>* _aidl_return) {
+    if (mDeviceCache.find(in_securityLevel) == mDeviceCache.end()) {
+        auto device = KeyMintDevice::createKeyMintDevice(in_securityLevel);
+        if (!device) {
+            return ScopedAStatus::fromStatus(STATUS_NAME_NOT_FOUND);
+        }
+        mDeviceCache[in_securityLevel] = std::move(device);
+    }
+    *_aidl_return = mDeviceCache[in_securityLevel];
+    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..904d391
--- /dev/null
+++ b/keystore2/src/km_compat/km_compat.h
@@ -0,0 +1,162 @@
+/*
+ * 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/security/compat/BnKeystoreCompatService.h>
+#include <keymasterV4_1/Keymaster4.h>
+#include <unordered_map>
+#include <variant>
+
+#include "certificate_utils.h"
+
+using ::aidl::android::hardware::security::keymint::BeginResult;
+using ::aidl::android::hardware::security::keymint::ByteArray;
+using ::aidl::android::hardware::security::keymint::Certificate;
+using ::aidl::android::hardware::security::keymint::HardwareAuthToken;
+using ::aidl::android::hardware::security::keymint::KeyCharacteristics;
+using ::aidl::android::hardware::security::keymint::KeyFormat;
+using ::aidl::android::hardware::security::keymint::KeyMintHardwareInfo;
+using ::aidl::android::hardware::security::keymint::KeyParameter;
+using ::aidl::android::hardware::security::keymint::KeyParameterArray;
+using ::aidl::android::hardware::security::keymint::KeyPurpose;
+using ::aidl::android::hardware::security::keymint::VerificationToken;
+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 ::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 verifyAuthorization(int64_t in_challenge, const HardwareAuthToken& in_token,
+                                      VerificationToken* _aidl_return) override;
+    ScopedAStatus addRngEntropy(const std::vector<uint8_t>& in_data) override;
+    ScopedAStatus generateKey(const std::vector<KeyParameter>& in_keyParams,
+                              ByteArray* out_generatedKeyBlob,
+                              KeyCharacteristics* out_generatedKeyCharacteristics,
+                              std::vector<Certificate>* out_outCertChain) override;
+    ScopedAStatus importKey(const std::vector<KeyParameter>& in_inKeyParams,
+                            KeyFormat in_inKeyFormat, const std::vector<uint8_t>& in_inKeyData,
+                            ByteArray* out_outImportedKeyBlob,
+                            KeyCharacteristics* out_outImportedKeyCharacteristics,
+                            std::vector<Certificate>* out_outCertChain) 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,
+                                   ByteArray* out_outImportedKeyBlob,
+                                   KeyCharacteristics* out_outImportedKeyCharacteristics) 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 HardwareAuthToken& in_inAuthToken,
+                        BeginResult* _aidl_return) 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>, V4_0_ErrorCode>
+    getCertificate(const std::vector<KeyParameter>& keyParams, const std::vector<uint8_t>& keyBlob);
+
+    void setNumFreeSlots(uint8_t numFreeSlots);
+
+  private:
+    std::optional<V4_0_ErrorCode> signCertificate(const std::vector<KeyParameter>& keyParams,
+                                                  const std::vector<uint8_t>& keyBlob, X509* cert);
+};
+
+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 update(const std::optional<KeyParameterArray>& in_inParams,
+                         const std::optional<std::vector<uint8_t>>& in_input,
+                         const std::optional<HardwareAuthToken>& in_inAuthToken,
+                         const std::optional<VerificationToken>& in_inVerificationToken,
+                         std::optional<KeyParameterArray>* out_outParams,
+                         std::optional<ByteArray>* out_output, int32_t* _aidl_return);
+    ScopedAStatus finish(const std::optional<KeyParameterArray>& in_inParams,
+                         const std::optional<std::vector<uint8_t>>& in_input,
+                         const std::optional<std::vector<uint8_t>>& in_inSignature,
+                         const std::optional<HardwareAuthToken>& in_authToken,
+                         const std::optional<VerificationToken>& in_inVerificationToken,
+                         std::optional<KeyParameterArray>* out_outParams,
+                         std::vector<uint8_t>* _aidl_return);
+    ScopedAStatus abort();
+};
+
+class KeystoreCompatService : public BnKeystoreCompatService {
+  private:
+    std::unordered_map<KeyMintSecurityLevel, std::shared_ptr<IKeyMintDevice>> mDeviceCache;
+
+  public:
+    KeystoreCompatService() {}
+    ScopedAStatus getKeyMintDevice(KeyMintSecurityLevel in_securityLevel,
+                                   std::shared_ptr<IKeyMintDevice>* _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..97c61ce
--- /dev/null
+++ b/keystore2/src/km_compat/km_compat_type_conversion.h
@@ -0,0 +1,886 @@
+/*
+ * 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 <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 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;
+    }
+}
+
+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;
+    }
+}
+
+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)) {
+            return V4_0::makeKeyParameter(V4_0::TAG_PURPOSE, convert(v->get()));
+        }
+        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::BLOB_USAGE_REQUIREMENTS:
+        // This tag has been removed. Mapped on invalid.
+        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::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:
+        // TODO This tag is missing from 4.0 keymaster_tags.h
+        break;
+    case KMV1::Tag::ATTESTATION_ID_IMEI:
+        // TODO This tag is missing from 4.0 keymaster_tags.h
+        break;
+    case KMV1::Tag::ATTESTATION_ID_MEID:
+        // TODO This tag is missing from 4.0 keymaster_tags.h
+        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:
+        // Does not exist in KM < KeyMint 1.0.
+        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:
+        // TODO This tag is missing from 4.0 keymaster_tags.h
+        break;
+    case V4_0::Tag::ATTESTATION_ID_IMEI:
+        // TODO This tag is missing from 4.0 keymaster_tags.h
+        break;
+    case V4_0::Tag::ATTESTATION_ID_MEID:
+        // TODO This tag is missing from 4.0 keymaster_tags.h
+        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..36f1303
--- /dev/null
+++ b/keystore2/src/km_compat/lib.rs
@@ -0,0 +1,325 @@
+// 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.
+
+// TODO: Once this is stable, remove this and document everything public.
+#![allow(missing_docs)]
+
+extern "C" {
+    fn addKeyMintDeviceService() -> i32;
+}
+
+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, ByteArray::ByteArray,
+        Certificate::Certificate, Digest::Digest, ErrorCode::ErrorCode,
+        HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::IKeyMintDevice,
+        KeyCharacteristics::KeyCharacteristics, KeyFormat::KeyFormat, KeyParameter::KeyParameter,
+        KeyParameterArray::KeyParameterArray, KeyParameterValue::KeyParameterValue,
+        KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, SecurityLevel::SecurityLevel, Tag::Tag,
+    };
+    use android_hardware_security_keymint::binder;
+    use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
+
+    fn get_device() -> Box<dyn IKeyMintDevice> {
+        add_keymint_device_service();
+        let compat_service: Box<dyn IKeystoreCompatService> =
+            binder::get_interface("android.security.compat").unwrap();
+        compat_service.getKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap()
+    }
+
+    #[test]
+    fn test_get_hardware_info() {
+        let legacy = get_device();
+        let hinfo = legacy.getHardwareInfo().unwrap();
+        assert_eq!(hinfo.versionNumber, 0);
+        assert_ne!(hinfo.securityLevel, SecurityLevel::SOFTWARE);
+        assert_eq!(hinfo.keyMintName, "RemoteKeymaster");
+        assert_eq!(hinfo.keyMintAuthorName, "Google");
+    }
+
+    #[test]
+    fn test_verify_authorization() {
+        use android_hardware_security_keymint::aidl::android::hardware::security::keymint::HardwareAuthToken::HardwareAuthToken;
+        let legacy = get_device();
+        let result = legacy.verifyAuthorization(0, &HardwareAuthToken::default());
+        assert!(result.is_err());
+        assert_eq!(result.unwrap_err().service_specific_error(), ErrorCode::UNIMPLEMENTED.0,);
+    }
+
+    #[test]
+    fn test_add_rng_entropy() {
+        let legacy = get_device();
+        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>,
+    ) -> (ByteArray, KeyCharacteristics, Vec<Certificate>) {
+        let mut blob = ByteArray { data: vec![] };
+        let mut characteristics = KeyCharacteristics::default();
+        let mut cert_chain = vec![];
+        let result = legacy.generateKey(&kps, &mut blob, &mut characteristics, &mut cert_chain);
+        assert!(result.is_ok(), "{:?}", result);
+        assert_ne!(blob.data.len(), 0);
+        (blob, characteristics, cert_chain)
+    }
+
+    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),
+            },
+        ];
+        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 (blob, _, cert_chain) = generate_key(legacy, kps);
+        if attest {
+            // TODO: Will this always be greater than 1?
+            assert!(cert_chain.len() > 1);
+        } else {
+            assert_eq!(cert_chain.len(), 1);
+        }
+        blob.data
+    }
+
+    #[test]
+    fn test_generate_key_no_encrypt() {
+        let legacy = get_device();
+        generate_rsa_key(legacy.as_ref(), false, false);
+    }
+
+    #[test]
+    fn test_generate_key_encrypt() {
+        let legacy = get_device();
+        generate_rsa_key(legacy.as_ref(), true, false);
+    }
+
+    #[test]
+    fn test_generate_key_attested() {
+        let legacy = get_device();
+        generate_rsa_key(legacy.as_ref(), false, true);
+    }
+
+    #[test]
+    fn test_import_key() {
+        let legacy = get_device();
+        let kps = [KeyParameter {
+            tag: Tag::ALGORITHM,
+            value: KeyParameterValue::Algorithm(Algorithm::AES),
+        }];
+        let kf = KeyFormat::RAW;
+        let kd = [0; 16];
+        let mut blob = ByteArray { data: vec![] };
+        let mut characteristics = KeyCharacteristics::default();
+        let mut cert_chain = vec![];
+        let result =
+            legacy.importKey(&kps, kf, &kd, &mut blob, &mut characteristics, &mut cert_chain);
+        assert!(result.is_ok(), "{:?}", result);
+        assert_ne!(blob.data.len(), 0);
+        assert_eq!(cert_chain.len(), 0);
+    }
+
+    #[test]
+    fn test_import_wrapped_key() {
+        let legacy = get_device();
+        let mut blob = ByteArray { data: vec![] };
+        let mut characteristics = KeyCharacteristics::default();
+        let result =
+            legacy.importWrappedKey(&[], &[], &[], &[], 0, 0, &mut blob, &mut characteristics);
+        // TODO: This test seems to fail on cuttlefish.  How should I test it?
+        assert!(result.is_err());
+    }
+
+    #[test]
+    fn test_upgrade_key() {
+        let legacy = get_device();
+        let blob = generate_rsa_key(legacy.as_ref(), false, false);
+        let result = legacy.upgradeKey(&blob, &[]);
+        // TODO: This test seems to fail on cuttlefish.  How should I test it?
+        assert!(result.is_err());
+    }
+
+    #[test]
+    fn test_delete_key() {
+        let legacy = get_device();
+        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();
+        let result = legacy.deleteAllKeys();
+        assert!(result.is_ok(), "{:?}", result);
+    }
+
+    #[test]
+    fn test_destroy_attestation_ids() {
+        let legacy = get_device();
+        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 (blob, _, cert_chain) = generate_key(legacy, kps);
+        assert_eq!(cert_chain.len(), 0);
+        blob.data
+    }
+
+    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, &HardwareAuthToken::default());
+        assert!(result.is_ok(), "{:?}", result);
+        result.unwrap()
+    }
+
+    #[test]
+    fn test_begin_abort() {
+        let legacy = get_device();
+        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();
+        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 params = KeyParameterArray {
+            params: vec![KeyParameter {
+                tag: Tag::ASSOCIATED_DATA,
+                value: KeyParameterValue::Blob(b"foobar".to_vec()),
+            }],
+        };
+        let message = [42; 128];
+        let mut out_params = None;
+        let result =
+            operation.finish(Some(&params), Some(&message), None, None, None, &mut out_params);
+        assert!(result.is_ok(), "{:?}", result);
+        let ciphertext = result.unwrap();
+        assert!(!ciphertext.is_empty());
+        assert!(out_params.is_some());
+
+        let begin_result =
+            begin(legacy.as_ref(), &blob, KeyPurpose::DECRYPT, Some(begin_result.params));
+        let operation = begin_result.operation.unwrap();
+        let mut out_params = None;
+        let mut output = None;
+        let result = operation.update(
+            Some(&params),
+            Some(&ciphertext),
+            None,
+            None,
+            &mut out_params,
+            &mut output,
+        );
+        assert!(result.is_ok(), "{:?}", result);
+        assert_eq!(result.unwrap(), message.len() as i32);
+        assert!(output.is_some());
+        assert_eq!(output.unwrap().data, message.to_vec());
+        let result = operation.finish(Some(&params), None, None, None, None, &mut out_params);
+        assert!(result.is_ok(), "{:?}", result);
+        assert!(out_params.is_some());
+    }
+}
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..41be067
--- /dev/null
+++ b/keystore2/src/km_compat/parameter_conversion_test.cpp
@@ -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.
+ */
+
+#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);
+}
diff --git a/keystore2/src/km_compat/slot_test.cpp b/keystore2/src/km_compat/slot_test.cpp
new file mode 100644
index 0000000..e56fb37
--- /dev/null
+++ b/keystore2/src/km_compat/slot_test.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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::ByteArray;
+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),
+    });
+    ByteArray blob;
+    KeyCharacteristics characteristics;
+    std::vector<Certificate> cert;
+    auto status = device->generateKey(keyParams, &blob, &characteristics, &cert);
+    if (!status.isOk()) {
+        return {};
+    }
+    return blob.data;
+}
+
+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::optional<KeyParameterArray> kpa;
+    std::vector<uint8_t> byteVec;
+    auto status = last->finish(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
+                               &kpa, &byteVec);
+    ASSERT_TRUE(status.isOk());
+    result = begin(device, true);
+    ASSERT_TRUE(std::holds_alternative<BeginResult>(result));
+    operations.push_back(std::get<BeginResult>(result).operation);
+
+    // Calling finish and abort on an already-finished operation should not free up another slot.
+    status = last->finish(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
+                          &kpa, &byteVec);
+    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, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
+                          &kpa, &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 also uses a slot.
+    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_NO_AUTH_REQUIRED, true),
+    });
+    ByteArray blob;
+    KeyCharacteristics characteristics;
+    std::vector<Certificate> cert;
+    status = device->generateKey(kps, &blob, &characteristics, &cert);
+    ASSERT_TRUE(!status.isOk());
+    ASSERT_EQ(status.getServiceSpecificError(),
+              static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
+    // But generating a certificate with signCert does not use a slot.
+    kps.pop_back();
+    status = device->generateKey(kps, &blob, &characteristics, &cert);
+    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..34a0eca
--- /dev/null
+++ b/keystore2/src/legacy_blob.rs
@@ -0,0 +1,978 @@
+// 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.
+
+#![allow(dead_code)]
+
+//! This module implements methods to load legacy keystore key blob files.
+
+use crate::{
+    database::KeyMetaData,
+    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, derive_key_from_password, ZVec};
+use std::io::{ErrorKind, Read};
+use std::{convert::TryInto, fs::File, path::Path, path::PathBuf};
+
+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::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 gcb 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::SOFTWARE));
+
+        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", "USERSKEY"].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 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.")?))
+    }
+
+    /// This function constructs the blob file name which has the form:
+    /// user_<android user id>/<uid>_<alias>.
+    fn make_blob_filename(&self, uid: u32, alias: &str, prefix: &str) -> PathBuf {
+        let mut path = self.path.clone();
+        let user_id = uid_to_android_user(uid);
+        let encoded_alias = Self::encode_alias(&format!("{}_{}", prefix, alias));
+        path.push(format!("user_{}", 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_<alias>.
+    fn make_chr_filename(&self, uid: u32, alias: &str, prefix: &str) -> PathBuf {
+        let mut path = self.path.clone();
+        let user_id = uid_to_android_user(uid);
+        let encoded_alias = Self::encode_alias(&format!("{}_{}", prefix, alias));
+        path.push(format!("user_{}", user_id));
+        path.push(format!(".{}_chr_{}", uid, encoded_alias));
+        path
+    }
+
+    fn load_by_uid_alias(
+        &self,
+        uid: u32,
+        alias: &str,
+        key_manager: &SuperKeyManager,
+    ) -> Result<(Option<(Blob, Vec<KeyParameter>)>, Option<Vec<u8>>, Option<Vec<u8>>, KeyMetaData)>
+    {
+        let metadata = KeyMetaData::new();
+
+        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.
+                        Blob { flags, value: BlobValue::Encrypted { iv, tag, data } } => {
+                            let decrypted = match key_manager
+                                .get_per_boot_key_by_user_id(uid_to_android_user(uid))
+                            {
+                                Some(key) => aes_gcm_decrypt(&data, &iv, &tag, &key).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) }
+                        }
+                        _ => 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, metadata))
+    }
+
+    /// Load and decrypt legacy super key blob.
+    pub fn load_super_key(&self, user_id: u32, pw: &[u8]) -> Result<Option<ZVec>> {
+        let mut path = self.path.clone();
+        path.push(&format!("user_{}", user_id));
+        path.push(".masterkey");
+        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 {
+                    value: BlobValue::PwEncrypted { iv, tag, data, salt, key_size }, ..
+                } => {
+                    let key = derive_key_from_password(pw, 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)
+                }
+                _ => {
+                    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)
+    }
+}
+
+#[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 crate::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!(format!("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_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 = crate::super_key::SuperKeyManager::new();
+        let mut db = crate::database::KeystoreDB::new(temp_dir.path())?;
+        let legacy_blob_loader = LegacyBlobLoader::new(temp_dir.path());
+
+        assert_eq!(
+            legacy_blob_loader
+                .load_by_uid_alias(10223, "authbound", &key_manager)
+                .unwrap_err()
+                .root_cause()
+                .downcast_ref::<error::Error>(),
+            Some(&error::Error::Rc(ResponseCode::LOCKED))
+        );
+
+        key_manager.unlock_user_key(0, PASSWORD, &mut db, &legacy_blob_loader)?;
+
+        if let (Some((Blob { flags, value }, _params)), Some(cert), Some(chain), _kp) =
+            legacy_blob_loader.load_by_uid_alias(10223, "authbound", &key_manager)?
+        {
+            assert_eq!(flags, 4);
+            assert_eq!(value, BlobValue::Decrypted(DECRYPTED_USRPKEY_AUTHBOUND.try_into()?));
+            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), _kp) =
+            legacy_blob_loader.load_by_uid_alias(10223, "non_authbound", &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!("");
+        }
+
+        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..aa99162
--- /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/lib.rs b/keystore2/src/lib.rs
index e13d6c0..29b3992 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -13,7 +13,9 @@
 // limitations under the License.
 
 //! This crate implements the Android Keystore 2.0 service.
+#![recursion_limit = "256"]
 
+pub mod apc;
 pub mod auth_token_handler;
 pub mod database;
 pub mod enforcements;
@@ -21,13 +23,16 @@
 pub mod globals;
 /// Internal Representation of Key Parameter and convenience functions.
 pub mod key_parameter;
+pub mod legacy_blob;
 pub mod operation;
 pub mod permission;
 pub mod security_level;
 pub mod service;
 pub mod utils;
 
+mod async_task;
 mod db_utils;
+mod gc;
 mod super_key;
 
 #[cfg(test)]
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index a89f309..af59f79 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -45,7 +45,7 @@
     error::{self, map_km_error, map_or_log_err, Error, ErrorCode},
     utils::key_characteristics_to_internal,
 };
-use anyhow::{anyhow, Context, Result};
+use anyhow::{Context, Result};
 use binder::{IBinder, Interface, ThreadState};
 
 /// Implementation of the IKeystoreSecurityLevel Interface.
@@ -55,8 +55,6 @@
     operation_db: OperationDb,
 }
 
-static KEYMINT_SERVICE_NAME: &str = "android.hardware.security.keymint.IKeyMintDevice";
-
 // Blob of 32 zeroes used as empty masking key.
 static ZERO_BLOB_32: &[u8] = &[0; 32];
 
@@ -68,18 +66,10 @@
     pub fn new_native_binder(
         security_level: SecurityLevel,
     ) -> Result<impl IKeystoreSecurityLevel + Send> {
-        let service_name = format!("{}/default", KEYMINT_SERVICE_NAME);
-        let keymint: Box<dyn IKeyMintDevice> =
-            binder::get_interface(&service_name).map_err(|e| {
-                anyhow!(format!(
-                    "Could not get KeyMint instance: {} failed with error code {:?}",
-                    service_name, e
-                ))
-            })?;
-
         let result = BnKeystoreSecurityLevel::new_binder(Self {
             security_level,
-            keymint: Asp::new(keymint.as_binder()),
+            keymint: crate::globals::get_keymint_device(security_level)
+                .context("In KeystoreSecurityLevel::new_native_binder.")?,
             operation_db: OperationDb::new(),
         });
         result.as_binder().set_requesting_sid(true);
@@ -124,47 +114,21 @@
                 KeyDescriptor { domain: Domain::BLOB, blob: Some(blob.data), ..Default::default() }
             }
             _ => DB
-                .with(|db| {
-                    let mut db = db.borrow_mut();
-                    let key_id = db
-                        .create_key_entry(key.domain, key.nspace)
-                        .context("Trying to create a key entry.")?;
-                    db.insert_blob(
-                        &key_id,
-                        SubComponentType::KEY_BLOB,
-                        &blob.data,
-                        self.security_level,
-                    )
-                    .context("Trying to insert km blob.")?;
-                    if let Some(c) = &cert {
-                        db.insert_blob(&key_id, SubComponentType::CERT, c, self.security_level)
-                            .context("Trying to insert cert blob.")?;
-                    }
-                    if let Some(c) = &cert_chain {
-                        db.insert_blob(
-                            &key_id,
-                            SubComponentType::CERT_CHAIN,
-                            c,
-                            self.security_level,
-                        )
-                        .context("Trying to insert cert chain blob.")?;
-                    }
-                    db.insert_keyparameter(&key_id, &key_parameters)
-                        .context("Trying to insert key parameters.")?;
+                .with::<_, Result<KeyDescriptor>>(|db| {
                     let mut metadata = KeyMetaData::new();
                     metadata.add(KeyMetaEntry::CreationDate(creation_date));
-                    db.insert_key_metadata(&key_id, &metadata)
-                        .context("Trying to insert key metadata.")?;
-                    match &key.alias {
-                        Some(alias) => db
-                            .rebind_alias(&key_id, alias, key.domain, key.nspace)
-                            .context("Failed to rebind alias.")?,
-                        None => {
-                            return Err(error::Error::sys()).context(
-                                "Alias must be specified. (This should have been checked earlier.)",
-                            )
-                        }
-                    }
+
+                    let mut db = db.borrow_mut();
+                    let key_id = db
+                        .store_new_key(
+                            key,
+                            &key_parameters,
+                            &blob.data,
+                            cert.as_deref(),
+                            cert_chain.as_deref(),
+                            &metadata,
+                        )
+                        .context("In store_new_key.")?;
                     Ok(KeyDescriptor {
                         domain: Domain::KEY_ID,
                         nspace: key_id.id(),
@@ -537,7 +501,6 @@
                             &key_id_guard,
                             SubComponentType::KEY_BLOB,
                             &upgraded_blob,
-                            self.security_level,
                         )
                     })
                     .context(concat!(
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index 2abf9be..9c5a697 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -44,35 +44,55 @@
 
 /// Implementation of the IKeystoreService.
 pub struct KeystoreService {
-    sec_level: Asp,
+    sec_level_tee: Asp,
+    sec_level_strongbox: Option<Asp>,
 }
 
 impl KeystoreService {
     /// Create a new instance of the Keystore 2.0 service.
     pub fn new_native_binder() -> Result<impl IKeystoreService> {
+        let tee = KeystoreSecurityLevel::new_native_binder(SecurityLevel::TRUSTED_ENVIRONMENT)
+            .map(|tee| Asp::new(tee.as_binder()))
+            .context(concat!(
+                "In KeystoreService::new_native_binder: ",
+                "Trying to construct mendatory security level TEE."
+            ))?;
+        // Strongbox is optional, so we ignore errors and turn the result into an Option.
+        let strongbox =
+            KeystoreSecurityLevel::new_native_binder(SecurityLevel::TRUSTED_ENVIRONMENT)
+                .map(|tee| Asp::new(tee.as_binder()))
+                .ok();
+
         let result = BnKeystoreService::new_binder(Self {
-            sec_level: Asp::new({
-                let sec_level =
-                    KeystoreSecurityLevel::new_native_binder(SecurityLevel::TRUSTED_ENVIRONMENT)
-                        .context("While trying to create IKeystoreSecurityLevel")?;
-                sec_level.as_binder()
-            }),
+            sec_level_tee: tee,
+            sec_level_strongbox: strongbox,
         });
         result.as_binder().set_requesting_sid(true);
         Ok(result)
     }
 
+    fn get_security_level_internal(
+        &self,
+        security_level: SecurityLevel,
+    ) -> Result<Option<Box<dyn IKeystoreSecurityLevel>>> {
+        Ok(match (security_level, &self.sec_level_strongbox) {
+            (SecurityLevel::TRUSTED_ENVIRONMENT, _) => Some(self.sec_level_tee.get_interface().context(
+                "In get_security_level_internal: Failed to get IKeystoreSecurityLevel (TEE).",
+            )?),
+            (SecurityLevel::STRONGBOX, Some(strongbox)) => Some(strongbox.get_interface().context(
+                "In get_security_level_internal: Failed to get IKeystoreSecurityLevel (Strongbox).",
+            )?),
+            _ => None,
+        })
+    }
+
     fn get_security_level(
         &self,
         security_level: SecurityLevel,
     ) -> Result<Box<dyn IKeystoreSecurityLevel>> {
-        match security_level {
-            SecurityLevel::TRUSTED_ENVIRONMENT => self
-                .sec_level
-                .get_interface()
-                .context("In get_security_level: Failed to get IKeystoreSecurityLevel."),
-            _ => Err(anyhow!(error::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))),
-        }
+        self.get_security_level_internal(security_level)
+            .context("In get_security_level.")?
+            .ok_or_else(|| anyhow!(error::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)))
     }
 
     fn get_key_entry(&self, key: &KeyDescriptor) -> Result<KeyEntryResponse> {
@@ -88,13 +108,18 @@
             })
             .context("In get_key_entry, while trying to load key info.")?;
 
-        let i_sec_level = match key_entry.sec_level() {
-            SecurityLevel::TRUSTED_ENVIRONMENT => self
-                .sec_level
-                .get_interface()
-                .context("In get_key_entry: Failed to get IKeystoreSecurityLevel.")?,
-            _ => return Err(anyhow!(error::Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))),
-        };
+        let i_sec_level = self
+            .get_security_level_internal(key_entry.sec_level())
+            .context("In get_key_entry.")?
+            .ok_or_else(|| {
+                anyhow!(error::Error::sys()).context(format!(
+                    concat!(
+                        "Found key with security level {:?} ",
+                        "but no KeyMint instance of that security level."
+                    ),
+                    key_entry.sec_level()
+                ))
+            })?;
 
         Ok(KeyEntryResponse {
             iSecurityLevel: Some(i_sec_level),
@@ -140,18 +165,13 @@
                 .context("Failed to load key_entry.")?;
 
             if let Some(cert) = public_cert {
-                db.insert_blob(&key_id_guard, SubComponentType::CERT, cert, key_entry.sec_level())
+                db.insert_blob(&key_id_guard, SubComponentType::CERT, cert)
                     .context("Failed to update cert subcomponent.")?;
             }
 
             if let Some(cert_chain) = certificate_chain {
-                db.insert_blob(
-                    &key_id_guard,
-                    SubComponentType::CERT_CHAIN,
-                    cert_chain,
-                    key_entry.sec_level(),
-                )
-                .context("Failed to update cert chain subcomponent.")?;
+                db.insert_blob(&key_id_guard, SubComponentType::CERT_CHAIN, cert_chain)
+                    .context("Failed to update cert chain subcomponent.")?;
             }
             Ok(())
         })
@@ -200,8 +220,14 @@
     }
 
     fn delete_key(&self, key: &KeyDescriptor) -> Result<()> {
-        // TODO implement.
-        Err(anyhow!(error::Error::sys()))
+        let caller_uid = ThreadState::get_calling_uid();
+        DB.with(|db| {
+            db.borrow_mut().unbind_key(key.clone(), 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(
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index d204ae7..4ffe897 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -16,7 +16,7 @@
 
 use crate::{
     database::EncryptedBy, database::KeyMetaData, database::KeyMetaEntry, database::KeystoreDB,
-    error::Error, error::ResponseCode,
+    error::Error, error::ResponseCode, legacy_blob::LegacyBlobLoader,
 };
 use android_system_keystore2::aidl::android::system::keystore2::Domain::Domain;
 use anyhow::{Context, Result};
@@ -107,12 +107,31 @@
     /// 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, user: UserId, pw: &[u8], db: &mut KeystoreDB) -> Result<()> {
+    pub fn unlock_user_key(
+        &self,
+        user: UserId,
+        pw: &[u8],
+        db: &mut KeystoreDB,
+        legacy_blob_loader: &LegacyBlobLoader,
+    ) -> Result<()> {
         let (_, entry) = db
             .get_or_create_key_with(Domain::APP, user as u64 as i64, &"USER_SUPER_KEY", || {
-                let super_key = keystore2_crypto::generate_aes256_key()
-                    .context("In create_new_key: Failed to generate AES 256 key.")?;
-
+                // 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.
+                        keystore2_crypto::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 a new AES256 key,
+                // we derive a AES256 key and re-encrypt the 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.
                 let salt =
                     generate_salt().context("In create_new_key: Failed to generate salt.")?;
                 let derived_key = derive_key_from_password(pw, Some(&salt), AES_256_KEY_LENGTH)
diff --git a/keystore2/src/test/utils.rs b/keystore2/src/test/utils.rs
index e016ec0..8c93859 100644
--- a/keystore2/src/test/utils.rs
+++ b/keystore2/src/test/utils.rs
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use std::env::temp_dir;
 use std::fs::{create_dir, remove_dir_all};
 use std::io::ErrorKind;
-use std::path::Path;
+use std::path::{Path, PathBuf};
+use std::{env::temp_dir, ops::Deref};
 
 #[derive(Debug)]
 pub struct TempDir {
@@ -44,6 +44,10 @@
         &self.path
     }
 
+    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)]
@@ -61,3 +65,20 @@
         }
     }
 }
+
+pub struct PathBuilder(PathBuf);
+
+impl PathBuilder {
+    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/src/utils.rs b/keystore2/src/utils.rs
index 7a6d6d7..86a86dd 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -19,17 +19,26 @@
 //! This module implements utility functions used by the Keystore 2.0 service
 //! implementation.
 
+use crate::error::Error;
 use crate::permission;
 use crate::permission::{KeyPerm, KeyPermSet, KeystorePerm};
-use crate::{error::Error, key_parameter::KeyParameterValue};
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     KeyCharacteristics::KeyCharacteristics, SecurityLevel::SecurityLevel,
 };
+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;
 
@@ -108,6 +117,13 @@
     }
 }
 
+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.
 /// The parameter `hw_security_level` indicates which security level shall be used for
@@ -119,17 +135,9 @@
     key_characteristics
         .hardwareEnforced
         .into_iter()
-        .map(|aidl_kp| {
-            crate::key_parameter::KeyParameter::new(
-                KeyParameterValue::convert_from_wire(aidl_kp),
-                hw_security_level,
-            )
-        })
+        .map(|aidl_kp| crate::key_parameter::KeyParameter::new(aidl_kp.into(), hw_security_level))
         .chain(key_characteristics.softwareEnforced.into_iter().map(|aidl_kp| {
-            crate::key_parameter::KeyParameter::new(
-                KeyParameterValue::convert_from_wire(aidl_kp),
-                SecurityLevel::SOFTWARE,
-            )
+            crate::key_parameter::KeyParameter::new(aidl_kp.into(), SecurityLevel::SOFTWARE)
         }))
         .collect()
 }
@@ -154,6 +162,31 @@
     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.
 /// TODO: Replace with bindgen generated from libcutils. b/175619259
 pub const AID_USER_OFFSET: u32 = 100000;