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(¶ms), 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(¶ms),
+ 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(¶ms), 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;