Merge RQ1A.210105.003 to aosp-master - DO NOT MERGE
Merged-In: Ida4af108e86b538ab64d1dea4809cfa3b36f74cd
Merged-In: I598802169728b46b0e6592f08d05936f9846b252
Change-Id: Ic188f4755abb38c71fa11d534add3f0ee8ad353c
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+ license_type: NOTICE
+}
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index dcf92be..9b96f36 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,8 +1,10 @@
[Builtin Hooks]
clang_format = true
+rustfmt = true
[Builtin Hooks Options]
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+rustfmt = --config-path=rustfmt.toml
[Hook Scripts]
aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
diff --git a/fsverity_init/fsverity_init.cpp b/fsverity_init/fsverity_init.cpp
index 4ed25cd..b81fb22 100644
--- a/fsverity_init/fsverity_init.cpp
+++ b/fsverity_init/fsverity_init.cpp
@@ -37,19 +37,36 @@
return true;
}
+void LoadKeyFromStdin(key_serial_t keyring_id, const char* keyname) {
+ std::string content;
+ if (!android::base::ReadFdToString(STDIN_FILENO, &content)) {
+ LOG(ERROR) << "Failed to read key from stdin";
+ return;
+ }
+ if (!LoadKeyToKeyring(keyring_id, keyname, content.c_str(), content.size())) {
+ LOG(ERROR) << "Failed to load key from stdin";
+ }
+}
+
+void LoadKeyFromFile(key_serial_t keyring_id, const char* keyname, const std::string& path) {
+ std::string content;
+ if (!android::base::ReadFileToString(path, &content)) {
+ LOG(ERROR) << "Failed to read key from " << path;
+ return;
+ }
+ if (!LoadKeyToKeyring(keyring_id, keyname, content.c_str(), content.size())) {
+ LOG(ERROR) << "Failed to load key from " << path;
+ }
+}
+
void LoadKeyFromDirectory(key_serial_t keyring_id, const char* keyname, const char* dir) {
if (!std::filesystem::exists(dir)) {
return;
}
for (const auto& entry : std::filesystem::directory_iterator(dir)) {
if (!android::base::EndsWithIgnoreCase(entry.path().c_str(), ".der")) continue;
- std::string content;
- if (!android::base::ReadFileToString(entry.path(), &content)) {
- continue;
- }
- if (!LoadKeyToKeyring(keyring_id, keyname, content.c_str(), content.size())) {
- LOG(ERROR) << "Failed to load key from " << entry.path();
- }
+
+ LoadKeyFromFile(keyring_id, keyname, entry.path());
}
}
@@ -60,25 +77,44 @@
LoadKeyFromDirectory(keyring_id, "fsv_product", "/product/etc/security/fsverity");
}
-int main(int /*argc*/, const char** /*argv*/) {
+int main(int argc, const char** argv) {
+ if (argc < 2) {
+ LOG(ERROR) << "Not enough arguments";
+ return -1;
+ }
+
key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
if (keyring_id < 0) {
LOG(ERROR) << "Failed to find .fs-verity keyring id";
return -1;
}
- // Requires files backed by fs-verity to be verified with a key in .fs-verity
- // keyring.
- if (!android::base::WriteStringToFile("1", "/proc/sys/fs/verity/require_signatures")) {
- PLOG(ERROR) << "Failed to enforce fs-verity signature";
- }
+ const std::string_view command = argv[1];
- LoadKeyFromVerifiedPartitions(keyring_id);
-
- if (!android::base::GetBoolProperty("ro.debuggable", false)) {
- if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
- PLOG(ERROR) << "Cannot restrict .fs-verity keyring";
+ if (command == "--load-verified-keys") {
+ LoadKeyFromVerifiedPartitions(keyring_id);
+ } else if (command == "--load-extra-key") {
+ if (argc != 3) {
+ LOG(ERROR) << "--load-extra-key requires <key_name> argument.";
+ return -1;
}
+ LoadKeyFromStdin(keyring_id, argv[2]);
+ } else if (command == "--lock") {
+ // Requires files backed by fs-verity to be verified with a key in .fs-verity
+ // keyring.
+ if (!android::base::WriteStringToFile("1", "/proc/sys/fs/verity/require_signatures")) {
+ PLOG(ERROR) << "Failed to enforce fs-verity signature";
+ }
+
+ if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+ if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
+ PLOG(ERROR) << "Cannot restrict .fs-verity keyring";
+ }
+ }
+ } else {
+ LOG(ERROR) << "Unknown argument(s).";
+ return -1;
}
+
return 0;
}
diff --git a/identity/Util.cpp b/identity/Util.cpp
index a962dc3..cd29017 100644
--- a/identity/Util.cpp
+++ b/identity/Util.cpp
@@ -110,7 +110,7 @@
remaining -= numWritten;
}
- if (TEMP_FAILURE_RETRY(fsync(fd) == -1)) {
+ if (TEMP_FAILURE_RETRY(fsync(fd))) {
PLOG(ERROR) << "Failed fsyncing temp file for '" << path << "'";
close(fd);
return false;
diff --git a/keystore/KeyAttestationApplicationId.cpp b/keystore/KeyAttestationApplicationId.cpp
index c62571f..1838b07 100644
--- a/keystore/KeyAttestationApplicationId.cpp
+++ b/keystore/KeyAttestationApplicationId.cpp
@@ -26,8 +26,8 @@
KeyAttestationApplicationId::KeyAttestationApplicationId() = default;
KeyAttestationApplicationId::KeyAttestationApplicationId(
- std::unique_ptr<KeyAttestationPackageInfo> package) :
- packageInfos_(new std::vector<std::unique_ptr<KeyAttestationPackageInfo>>()) {
+ std::optional<KeyAttestationPackageInfo> package)
+ : packageInfos_(new std::vector<std::optional<KeyAttestationPackageInfo>>()) {
packageInfos_->push_back(std::move(package));
}
@@ -39,10 +39,13 @@
}
status_t KeyAttestationApplicationId::readFromParcel(const Parcel* parcel) {
- std::unique_ptr<std::vector<std::unique_ptr<KeyAttestationPackageInfo>>> temp_vector;
+ std::optional<std::vector<std::optional<KeyAttestationPackageInfo>>> temp_vector;
auto rc = parcel->readParcelableVector(&temp_vector);
if (rc != NO_ERROR) return rc;
- packageInfos_.reset(temp_vector.release());
+ packageInfos_.reset();
+ if (temp_vector) {
+ packageInfos_ = std::make_shared<PackageInfoVector>(std::move(*temp_vector));
+ }
return NO_ERROR;
}
diff --git a/keystore/KeyAttestationPackageInfo.cpp b/keystore/KeyAttestationPackageInfo.cpp
index 75fbb7a..8e9a36a 100644
--- a/keystore/KeyAttestationPackageInfo.cpp
+++ b/keystore/KeyAttestationPackageInfo.cpp
@@ -28,7 +28,7 @@
KeyAttestationPackageInfo::KeyAttestationPackageInfo(const String16& packageName,
int64_t versionCode,
SharedSignaturesVector signatures)
- : packageName_(new String16(packageName)), versionCode_(versionCode), signatures_(signatures) {}
+ : packageName_(packageName), versionCode_(versionCode), signatures_(signatures) {}
status_t KeyAttestationPackageInfo::writeToParcel(Parcel* parcel) const {
auto rc = parcel->writeString16(packageName_);
@@ -44,10 +44,13 @@
rc = parcel->readInt64(&versionCode_);
if (rc != NO_ERROR) return rc;
- std::unique_ptr<SignaturesVector> temp_vector;
+ std::optional<SignaturesVector> temp_vector;
rc = parcel->readParcelableVector(&temp_vector);
if (rc != NO_ERROR) return rc;
- signatures_.reset(temp_vector.release());
+ signatures_.reset();
+ if (temp_vector) {
+ signatures_ = std::make_shared<SignaturesVector>(std::move(*temp_vector));
+ }
return NO_ERROR;
}
diff --git a/keystore/KeyStore.cpp b/keystore/KeyStore.cpp
index 7545397..1f80899 100644
--- a/keystore/KeyStore.cpp
+++ b/keystore/KeyStore.cpp
@@ -60,8 +60,8 @@
"KmasterDevices and KeymasterWorkers must have the same size");
for (size_t i = 0; i < kmDevices.size(); ++i) {
if (kmDevices[SecurityLevel(i)]) {
- mKmDevices[SecurityLevel(i)] =
- std::make_shared<KeymasterWorker>(kmDevices[SecurityLevel(i)], this);
+ mKmDevices[SecurityLevel(i)] = std::make_shared<KeymasterWorker>(
+ kmDevices[SecurityLevel(i)], this, SecurityLevel(i));
}
}
}
diff --git a/keystore/KeyStore.h b/keystore/KeyStore.h
index 0027ec8..7841a80 100644
--- a/keystore/KeyStore.h
+++ b/keystore/KeyStore.h
@@ -62,9 +62,9 @@
} // namespace keystore
namespace std {
-template <typename T, size_t count> struct tuple_size<keystore::Devices<T, count>> {
+template <typename T, size_t N> struct tuple_size<keystore::Devices<T, N>> {
public:
- static constexpr size_t value = std::tuple_size<std::array<T, count>>::value;
+ static constexpr size_t value = std::tuple_size<std::array<T, N>>::value;
};
} // namespace std
diff --git a/keystore/TEST_MAPPING b/keystore/TEST_MAPPING
new file mode 100644
index 0000000..0511967
--- /dev/null
+++ b/keystore/TEST_MAPPING
@@ -0,0 +1,74 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsKeystoreTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.RequiresDevice"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.SignatureTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.RsaSignaturePerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.RsaKeyGenPerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.RsaCipherPerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.MacTest#testLargeMsgKat"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.KeyPairGeneratorTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.KeyGeneratorTest#testHmacKeySupportedSizes"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.HmacMacPerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.EcdsaSignaturePerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.EcKeyGenPerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.DesCipherPerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.CipherTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.AttestationPerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.AndroidKeyStoreTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.AesCipherPerformanceTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.AESCipherNistCavpKatTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.DESedeECBPKCS7PaddingCipherTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.DESedeECBNoPaddingCipherTest"
+ },
+ {
+ "exclude-filter": "android.keystore.cts.DESedeECBPKCS7PaddingCipherTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsKeystoreTestCases"
+ }
+ ]
+}
diff --git a/keystore/binder/android/security/keystore/IKeystoreOperationResultCallback.aidl b/keystore/binder/android/security/keystore/IKeystoreOperationResultCallback.aidl
index 0a51511..f37b838 100644
--- a/keystore/binder/android/security/keystore/IKeystoreOperationResultCallback.aidl
+++ b/keystore/binder/android/security/keystore/IKeystoreOperationResultCallback.aidl
@@ -22,6 +22,7 @@
/**
* @hide
*/
+@SensitiveData
oneway interface IKeystoreOperationResultCallback {
void onFinished(in OperationResult result);
-}
\ No newline at end of file
+}
diff --git a/keystore/binder/android/security/keystore/IKeystoreService.aidl b/keystore/binder/android/security/keystore/IKeystoreService.aidl
index 6edca56..e0879dd 100644
--- a/keystore/binder/android/security/keystore/IKeystoreService.aidl
+++ b/keystore/binder/android/security/keystore/IKeystoreService.aidl
@@ -29,10 +29,11 @@
/**
* @hide
*/
+@SensitiveData
interface IKeystoreService {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
int getState(int userId);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
byte[] get(String name, int uid);
@UnsupportedAppUsage
int insert(String name, in byte[] item, int uid, int flags);
@@ -40,7 +41,7 @@
int del(String name, int uid);
@UnsupportedAppUsage
int exist(String name, int uid);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
String[] list(String namePrefix, int uid);
int onUserPasswordChanged(int userId, String newPassword);
int lock(int userId);
diff --git a/keystore/include/keystore/KeyAttestationApplicationId.h b/keystore/include/keystore/KeyAttestationApplicationId.h
index 861c2e1..0bf1aad 100644
--- a/keystore/include/keystore/KeyAttestationApplicationId.h
+++ b/keystore/include/keystore/KeyAttestationApplicationId.h
@@ -16,6 +16,7 @@
#define KEYSTORE_INCLUDE_KEYSTORE_KEYATTESTATIONAPPLICATIONID_H_
#include <memory>
+#include <optional>
#include <vector>
#include <binder/Parcelable.h>
@@ -30,10 +31,10 @@
public:
typedef SharedNullableIterator<const KeyAttestationPackageInfo, std::vector>
ConstKeyAttestationPackageInfoIterator;
- typedef std::vector<std::unique_ptr<KeyAttestationPackageInfo>> PackageInfoVector;
+ typedef std::vector<std::optional<KeyAttestationPackageInfo>> PackageInfoVector;
KeyAttestationApplicationId();
// Following c'tors are for initializing instances containing test data.
- explicit KeyAttestationApplicationId(std::unique_ptr<KeyAttestationPackageInfo> package);
+ explicit KeyAttestationApplicationId(std::optional<KeyAttestationPackageInfo> package);
explicit KeyAttestationApplicationId(PackageInfoVector packages);
status_t writeToParcel(Parcel*) const override;
@@ -47,7 +48,7 @@
}
private:
- std::shared_ptr<std::vector<std::unique_ptr<KeyAttestationPackageInfo>>> packageInfos_;
+ std::shared_ptr<PackageInfoVector> packageInfos_;
};
} // namespace keymaster
diff --git a/keystore/include/keystore/KeyAttestationPackageInfo.h b/keystore/include/keystore/KeyAttestationPackageInfo.h
index 92d4863..fa638f9 100644
--- a/keystore/include/keystore/KeyAttestationPackageInfo.h
+++ b/keystore/include/keystore/KeyAttestationPackageInfo.h
@@ -18,6 +18,7 @@
#include <stdint.h>
#include <memory>
+#include <optional>
#include <vector>
#include <binder/Parcelable.h>
@@ -33,8 +34,7 @@
public:
typedef SharedNullableIterator<const content::pm::Signature, std::vector>
ConstSignatureIterator;
- typedef std::vector<std::unique_ptr<content::pm::Signature>>
- SignaturesVector;
+ typedef std::vector<std::optional<content::pm::Signature>> SignaturesVector;
typedef std::shared_ptr<SignaturesVector> SharedSignaturesVector;
KeyAttestationPackageInfo(const String16& packageName, int64_t versionCode,
@@ -44,14 +44,14 @@
status_t writeToParcel(Parcel*) const override;
status_t readFromParcel(const Parcel* parcel) override;
- const std::unique_ptr<String16>& package_name() const { return packageName_; }
+ const std::optional<String16>& package_name() const { return packageName_; }
int64_t version_code() const { return versionCode_; }
ConstSignatureIterator sigs_begin() const { return ConstSignatureIterator(signatures_); }
ConstSignatureIterator sigs_end() const { return ConstSignatureIterator(); }
private:
- std::unique_ptr<String16> packageName_;
+ std::optional<String16> packageName_;
int64_t versionCode_;
SharedSignaturesVector signatures_;
};
diff --git a/keystore/include/keystore/KeystoreResponse.h b/keystore/include/keystore/KeystoreResponse.h
index 20f7274..4a7ef0d 100644
--- a/keystore/include/keystore/KeystoreResponse.h
+++ b/keystore/include/keystore/KeystoreResponse.h
@@ -31,29 +31,24 @@
public:
KeystoreResponse() = default;
explicit KeystoreResponse(const int response_code, const String16& error_msg)
- : response_code_(response_code), error_msg_(std::make_unique<String16>(error_msg)) {}
+ : response_code_(response_code), error_msg_(error_msg) {}
explicit KeystoreResponse(const int response_code)
: response_code_(response_code), error_msg_() {}
// NOLINTNEXTLINE(google-explicit-constructor)
KeystoreResponse(const ::keystore::KeyStoreServiceReturnCode& rc)
: response_code_(rc.getErrorCode()), error_msg_() {}
- KeystoreResponse(const KeystoreResponse& other)
- : response_code_(other.response_code_), error_msg_() {
- if (other.error_msg_) {
- error_msg_ = std::make_unique<String16>(*other.error_msg_);
- }
- }
+ KeystoreResponse(const KeystoreResponse& other) = default;
KeystoreResponse(KeystoreResponse&& other) = default;
status_t readFromParcel(const Parcel* in) override;
status_t writeToParcel(Parcel* out) const override;
int response_code() const { return response_code_; }
- const String16* error_msg() const { return error_msg_.get(); }
+ const std::optional<String16>& error_msg() const { return error_msg_; }
private:
int response_code_;
- std::unique_ptr<String16> error_msg_;
+ std::optional<String16> error_msg_;
};
} // namespace keystore
diff --git a/keystore/include/keystore/utils.h b/keystore/include/keystore/utils.h
index 544dd21..2143d3a 100644
--- a/keystore/include/keystore/utils.h
+++ b/keystore/include/keystore/utils.h
@@ -5,6 +5,7 @@
#include <iterator>
#include <memory>
+#include <optional>
#include <vector>
namespace android {
@@ -12,7 +13,7 @@
/*
* This iterator abstracts from a collection of the form
- * std::shared_ptr<COLLECTION_TYPE<std::unique_ptr<T>>>
+ * std::shared_ptr<COLLECTION_TYPE<std::optional<T>>>
* such that it is defined both for nulled outer pointer and
* nulled entries. If shared_ptr(nullptr) is passed in, the iterator behaves
* like the end iterator yielding an empty collection. Nulled
@@ -25,7 +26,7 @@
template <typename T, template <typename...> class Coll = std::vector>
class SharedNullableIterator {
public:
- typedef Coll<std::unique_ptr<typename std::remove_const<T>::type>> CollectionType;
+ typedef Coll<std::optional<typename std::remove_const<T>::type>> CollectionType;
typedef std::shared_ptr<CollectionType> CollectionPtr;
SharedNullableIterator() {}
diff --git a/keystore/key_attestation_log_handler.cpp b/keystore/key_attestation_log_handler.cpp
index 34c76a3..c3278cb 100644
--- a/keystore/key_attestation_log_handler.cpp
+++ b/keystore/key_attestation_log_handler.cpp
@@ -17,7 +17,11 @@
namespace keystore {
void logKeystoreKeyAttestationEvent(bool wasSuccessful, int32_t errorCode) {
- android::util::stats_write(android::util::KEYSTORE_KEY_EVENT_REPORTED,
+ // Due to a requirement in stats-write() method, the optional fields
+ // which are not required for attestation logging, are marked with -1 for
+ // non-repeated fields and 0 for repeated fields.
+ android::util::stats_write(android::util::KEYSTORE_KEY_EVENT_REPORTED, -1, -1, -1, -1, -1, 0, 0,
+ 0, 0, -1, -1,
android::util::KEYSTORE_KEY_EVENT_REPORTED__TYPE__KEY_ATTESTATION,
wasSuccessful, errorCode);
}
diff --git a/keystore/key_store_service.cpp b/keystore/key_store_service.cpp
index 1b38643..4e5bc48 100644
--- a/keystore/key_store_service.cpp
+++ b/keystore/key_store_service.cpp
@@ -1001,21 +1001,102 @@
return Status::ok();
}
+bool isDeviceIdAttestationTag(Tag tag) {
+ switch (tag) {
+ case Tag::ATTESTATION_ID_BRAND:
+ case Tag::ATTESTATION_ID_DEVICE:
+ case Tag::ATTESTATION_ID_MANUFACTURER:
+ case Tag::ATTESTATION_ID_MODEL:
+ case Tag::ATTESTATION_ID_PRODUCT:
+ case Tag::ATTESTATION_ID_IMEI:
+ case Tag::ATTESTATION_ID_MEID:
+ case Tag::ATTESTATION_ID_SERIAL:
+ return true;
+ case Tag::INVALID:
+ case Tag::PURPOSE:
+ case Tag::ALGORITHM:
+ case Tag::KEY_SIZE:
+ case Tag::BLOCK_MODE:
+ case Tag::DIGEST:
+ case Tag::PADDING:
+ case Tag::CALLER_NONCE:
+ case Tag::MIN_MAC_LENGTH:
+ case Tag::EC_CURVE:
+ case Tag::RSA_PUBLIC_EXPONENT:
+ case Tag::INCLUDE_UNIQUE_ID:
+ case Tag::BLOB_USAGE_REQUIREMENTS:
+ case Tag::BOOTLOADER_ONLY:
+ case Tag::ROLLBACK_RESISTANCE:
+ case Tag::HARDWARE_TYPE:
+ case Tag::ACTIVE_DATETIME:
+ case Tag::ORIGINATION_EXPIRE_DATETIME:
+ case Tag::USAGE_EXPIRE_DATETIME:
+ case Tag::MIN_SECONDS_BETWEEN_OPS:
+ case Tag::MAX_USES_PER_BOOT:
+ case Tag::USER_ID:
+ case Tag::USER_SECURE_ID:
+ case Tag::NO_AUTH_REQUIRED:
+ case Tag::USER_AUTH_TYPE:
+ case Tag::AUTH_TIMEOUT:
+ case Tag::ALLOW_WHILE_ON_BODY:
+ case Tag::TRUSTED_USER_PRESENCE_REQUIRED:
+ case Tag::TRUSTED_CONFIRMATION_REQUIRED:
+ case Tag::UNLOCKED_DEVICE_REQUIRED:
+ case Tag::APPLICATION_ID:
+ case Tag::APPLICATION_DATA:
+ case Tag::CREATION_DATETIME:
+ case Tag::ORIGIN:
+ case Tag::ROOT_OF_TRUST:
+ case Tag::OS_VERSION:
+ case Tag::OS_PATCHLEVEL:
+ case Tag::UNIQUE_ID:
+ case Tag::ATTESTATION_CHALLENGE:
+ case Tag::ATTESTATION_APPLICATION_ID:
+ case Tag::VENDOR_PATCHLEVEL:
+ case Tag::BOOT_PATCHLEVEL:
+ case Tag::ASSOCIATED_DATA:
+ case Tag::NONCE:
+ case Tag::MAC_LENGTH:
+ case Tag::RESET_SINCE_ID_ROTATION:
+ case Tag::CONFIRMATION_TOKEN:
+ return false;
+ // no default, all values must be present in the switch, in this way the compiler ensures
+ // that new values added in the Tag enum are also added here.
+ }
+}
+
+// These are attestation id tags that are not unique per device and don't require special permission
+// to be attested. Any addition to this list needs privacy review and approval (PWG).
+bool isDevicePropertyAttestationTag(Tag tag) {
+ switch (tag) {
+ case Tag::ATTESTATION_ID_BRAND:
+ case Tag::ATTESTATION_ID_DEVICE:
+ case Tag::ATTESTATION_ID_MANUFACTURER:
+ case Tag::ATTESTATION_ID_MODEL:
+ case Tag::ATTESTATION_ID_PRODUCT:
+ return true;
+ default:
+ return false;
+ }
+}
+
bool isDeviceIdAttestationRequested(const KeymasterArguments& params) {
const hardware::hidl_vec<KeyParameter>& paramsVec = params.getParameters();
for (size_t i = 0; i < paramsVec.size(); ++i) {
- switch (paramsVec[i].tag) {
- case Tag::ATTESTATION_ID_BRAND:
- case Tag::ATTESTATION_ID_DEVICE:
- case Tag::ATTESTATION_ID_MANUFACTURER:
- case Tag::ATTESTATION_ID_MODEL:
- case Tag::ATTESTATION_ID_PRODUCT:
- case Tag::ATTESTATION_ID_IMEI:
- case Tag::ATTESTATION_ID_MEID:
- case Tag::ATTESTATION_ID_SERIAL:
+ if (isDeviceIdAttestationTag(paramsVec[i].tag)) {
return true;
- default:
- continue;
+ }
+ }
+ return false;
+}
+
+// Device properties can be attested safely without special permission
+bool needsPermissionToAttestDeviceIds(const KeymasterArguments& params) {
+ const hardware::hidl_vec<KeyParameter>& paramsVec = params.getParameters();
+ for (size_t i = 0; i < paramsVec.size(); ++i) {
+ if (isDeviceIdAttestationTag(paramsVec[i].tag) &&
+ !isDevicePropertyAttestationTag(paramsVec[i].tag)) {
+ return true;
}
}
return false;
@@ -1031,7 +1112,7 @@
uid_t callingUid = IPCThreadState::self()->getCallingUid();
- if (isDeviceIdAttestationRequested(params) && (get_app_id(callingUid) != AID_SYSTEM)) {
+ if (needsPermissionToAttestDeviceIds(params) && (get_app_id(callingUid) != AID_SYSTEM)) {
return AIDL_RETURN(KeyStoreServiceReturnCode(ErrorCode::INVALID_ARGUMENT));
}
@@ -1106,14 +1187,19 @@
}
uid_t callingUid = IPCThreadState::self()->getCallingUid();
- sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
- if (binder == nullptr) {
- return AIDL_RETURN(ErrorCode::CANNOT_ATTEST_IDS);
- }
- if (!interface_cast<IPermissionController>(binder)->checkPermission(
- String16("android.permission.READ_PRIVILEGED_PHONE_STATE"),
- IPCThreadState::self()->getCallingPid(), callingUid)) {
- return AIDL_RETURN(ErrorCode::CANNOT_ATTEST_IDS);
+
+ // Request special permission only for unique ids
+ if (needsPermissionToAttestDeviceIds(params)) {
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
+ if (binder == nullptr) {
+ return AIDL_RETURN(ErrorCode::CANNOT_ATTEST_IDS);
+ }
+
+ if (!interface_cast<IPermissionController>(binder)->checkPermission(
+ String16("android.permission.READ_PRIVILEGED_PHONE_STATE"),
+ IPCThreadState::self()->getCallingPid(), callingUid)) {
+ return AIDL_RETURN(ErrorCode::CANNOT_ATTEST_IDS);
+ }
}
AuthorizationSet mutableParams = params.getParameters();
diff --git a/keystore/keymaster_worker.cpp b/keystore/keymaster_worker.cpp
index 7481a1e..cbb184c 100644
--- a/keystore/keymaster_worker.cpp
+++ b/keystore/keymaster_worker.cpp
@@ -83,8 +83,10 @@
}
}
-KeymasterWorker::KeymasterWorker(sp<Keymaster> keymasterDevice, KeyStore* keyStore)
- : keymasterDevice_(std::move(keymasterDevice)), operationMap_(keyStore), keyStore_(keyStore) {
+KeymasterWorker::KeymasterWorker(sp<Keymaster> keymasterDevice, KeyStore* keyStore,
+ SecurityLevel internalSecurityLevel)
+ : keymasterDevice_(std::move(keymasterDevice)), operationMap_(keyStore), keyStore_(keyStore),
+ internalSecurityLevel_(internalSecurityLevel) {
// make sure that hal version is cached.
if (keymasterDevice_) keymasterDevice_->halVersion();
}
@@ -821,7 +823,7 @@
outCharacteristics = keyCharacteristics;
Blob keyBlob(&hidlKeyBlob[0], hidlKeyBlob.size(), nullptr, 0, ::TYPE_KEYMASTER_10);
- keyBlob.setSecurityLevel(securityLevel);
+ keyBlob.setSecurityLevel(internalSecurityLevel_);
keyBlob.setCriticalToDeviceEncryption(flags &
KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION);
if (isAuthenticationBound(keyParams) && !keyBlob.isCriticalToDeviceEncryption()) {
@@ -929,7 +931,7 @@
outCharacteristics = keyCharacteristics;
Blob keyBlob(&hidlKeyBlob[0], hidlKeyBlob.size(), nullptr, 0, ::TYPE_KEYMASTER_10);
- keyBlob.setSecurityLevel(securityLevel);
+ keyBlob.setSecurityLevel(internalSecurityLevel_);
keyBlob.setCriticalToDeviceEncryption(flags &
KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION);
if (isAuthenticationBound(keyParams) && !keyBlob.isCriticalToDeviceEncryption()) {
@@ -1004,8 +1006,6 @@
CAPTURE_MOVE(worker_cb)]() mutable {
auto hidlWrappingKey = blob2hidlVec(wrappingBlob);
- SecurityLevel securityLevel = keymasterDevice_->halVersion().securityLevel;
-
KeyCharacteristics outCharacteristics;
KeyStoreServiceReturnCode error;
@@ -1019,7 +1019,7 @@
outCharacteristics = keyCharacteristics;
Blob keyBlob(hidlKeyBlob.data(), hidlKeyBlob.size(), nullptr, 0, ::TYPE_KEYMASTER_10);
- keyBlob.setSecurityLevel(securityLevel);
+ keyBlob.setSecurityLevel(internalSecurityLevel_);
if (isAuthenticationBound(keyCharacteristics.hardwareEnforced)) {
keyBlob.setSuperEncrypted(true);
}
diff --git a/keystore/keymaster_worker.h b/keystore/keymaster_worker.h
index f11af29..fbd52b4 100644
--- a/keystore/keymaster_worker.h
+++ b/keystore/keymaster_worker.h
@@ -135,6 +135,19 @@
OperationMap operationMap_;
KeyStore* keyStore_;
+ /**
+ * Models the security level of this worker internal to KeyStore.
+ *
+ * When the device has only a software Keymaster, KeyStore will set it on the TEE slot and
+ * instantiate a new in-process software Keymaster. In that case there is a mismatch between the
+ * security level used by KeyStore and what is reported from the HAL. This represents the level
+ * used internally by KeyStore.
+ *
+ * This value is used to associate blobs to the corresponding Keymaster backend. It does not
+ * indicate an actual Keymaster HAL security level and should never be exposed to users.
+ */
+ SecurityLevel internalSecurityLevel_;
+
template <typename KMFn, typename ErrorType, typename... Args, size_t... I>
void unwrap_tuple(KMFn kmfn, std::function<void(ErrorType)> cb,
const std::tuple<Args...>& tuple, std::index_sequence<I...>) {
@@ -200,7 +213,8 @@
hidl_vec<KeyParameter>* params);
public:
- KeymasterWorker(sp<Keymaster> keymasterDevice, KeyStore* keyStore);
+ KeymasterWorker(sp<Keymaster> keymasterDevice, KeyStore* keyStore,
+ SecurityLevel internalSecurityLevel);
void logIfKeymasterVendorError(ErrorCode ec) const;
diff --git a/keystore/keystore_attestation_id.cpp b/keystore/keystore_attestation_id.cpp
index 3d9e87e..ccd3808 100644
--- a/keystore/keystore_attestation_id.cpp
+++ b/keystore/keystore_attestation_id.cpp
@@ -271,7 +271,7 @@
if (uid == AID_SYSTEM) {
/* Use a fixed ID for system callers */
- auto pinfo = std::make_unique<KeyAttestationPackageInfo>(
+ auto pinfo = std::make_optional<KeyAttestationPackageInfo>(
String16(kAttestationSystemPackageName), 1 /* version code */,
std::make_shared<KeyAttestationPackageInfo::SignaturesVector>());
key_attestation_id = KeyAttestationApplicationId(std::move(pinfo));
@@ -284,7 +284,7 @@
if (!status.isOk()) {
ALOGW("package manager request for key attestation ID failed with: %s %d",
status.exceptionMessage().string(), status.exceptionCode());
- auto pinfo = std::make_unique<KeyAttestationPackageInfo>(
+ auto pinfo = std::make_optional<KeyAttestationPackageInfo>(
String16(kUnknownPackageName), 1 /* version code */,
std::make_shared<KeyAttestationPackageInfo::SignaturesVector>());
key_attestation_id = KeyAttestationApplicationId(std::move(pinfo));
diff --git a/keystore/legacy_keymaster_device_wrapper.cpp b/keystore/legacy_keymaster_device_wrapper.cpp
index 86d286e..052f394 100644
--- a/keystore/legacy_keymaster_device_wrapper.cpp
+++ b/keystore/legacy_keymaster_device_wrapper.cpp
@@ -532,7 +532,7 @@
sp<IKeymasterDevice> makeSoftwareKeymasterDevice() {
keymaster2_device_t* dev = nullptr;
- dev = (new SoftKeymasterDevice)->keymaster2_device();
+ dev = (new SoftKeymasterDevice(keymaster::KmVersion::KEYMASTER_2))->keymaster2_device();
auto kmrc = ::keymaster::ConfigureDevice(dev);
if (kmrc != KM_ERROR_OK) {
diff --git a/keystore/tests/Makefile b/keystore/tests/Makefile
index 2720b0f..b50b94a 100644
--- a/keystore/tests/Makefile
+++ b/keystore/tests/Makefile
@@ -17,7 +17,6 @@
KEYMASTER=$(BASE)/system/keymaster
INCLUDES=$(foreach dir,$(SUBS),-I $(BASE)/$(dir)/include) \
- -I $(BASE)/libnativehelper/include/nativehelper \
-I $(GTEST) -Iinclude
# Add USE_CLANG=1 to the make command line to build with clang, which has better error
diff --git a/keystore/tests/aaid_truncation_test.cpp b/keystore/tests/aaid_truncation_test.cpp
index 45c54df..fa4d769 100644
--- a/keystore/tests/aaid_truncation_test.cpp
+++ b/keystore/tests/aaid_truncation_test.cpp
@@ -75,15 +75,15 @@
using ::android::content::pm::Signature;
using ::android::security::build_attestation_application_id;
-std::unique_ptr<KeyAttestationPackageInfo>
+std::optional<KeyAttestationPackageInfo>
make_package_info_with_signatures(const char* package_name,
KeyAttestationPackageInfo::SignaturesVector signatures) {
- return std::make_unique<KeyAttestationPackageInfo>(
+ return std::make_optional<KeyAttestationPackageInfo>(
String16(package_name), 1 /* version code */,
std::make_shared<KeyAttestationPackageInfo::SignaturesVector>(std::move(signatures)));
}
-std::unique_ptr<KeyAttestationPackageInfo> make_package_info(const char* package_name) {
+std::optional<KeyAttestationPackageInfo> make_package_info(const char* package_name) {
return make_package_info_with_signatures(package_name,
KeyAttestationPackageInfo::SignaturesVector());
}
@@ -111,7 +111,7 @@
KeyAttestationPackageInfo::SignaturesVector signatures;
// Add 35 signatures which will surely exceed the 1K limit.
for (size_t i = 0; i < kTooManySignatures; ++i) {
- signatures.push_back(std::make_unique<Signature>(dummy_sig_data));
+ signatures.push_back(std::make_optional<Signature>(dummy_sig_data));
}
KeyAttestationApplicationId app_id(
@@ -131,7 +131,7 @@
KeyAttestationPackageInfo::SignaturesVector signatures;
// Add a few signatures for each package
for (int j = 0; j < 3; ++j) {
- signatures.push_back(std::make_unique<Signature>(dummy_sig_data));
+ signatures.push_back(std::make_optional<Signature>(dummy_sig_data));
}
packages.push_back(
make_package_info_with_signatures(kReasonablePackageName, std::move(signatures)));
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
new file mode 100644
index 0000000..883271d
--- /dev/null
+++ b/keystore2/Android.bp
@@ -0,0 +1,76 @@
+// 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",
+ crate_name: "keystore2",
+ srcs: ["src/lib.rs"],
+
+ rustlibs: [
+ "android.hardware.security.keymint-rust",
+ "android.security.apc-rust",
+ "android.system.keystore2-rust",
+ "libanyhow",
+ "libbinder_rs",
+ "libkeystore2_apc_compat-rust",
+ "libkeystore2_crypto_rust",
+ "libkeystore2_selinux",
+ "liblazy_static",
+ "liblibc",
+ "liblibsqlite3_sys",
+ "liblog_rust",
+ "librand",
+ "librusqlite",
+ "libthiserror",
+ ],
+}
+
+rust_test {
+ name: "keystore2_test",
+ crate_name: "keystore2",
+ srcs: ["src/lib.rs"],
+ test_suites: ["device-tests"],
+ auto_gen_config: false,
+ test_config: "AndroidTest.xml",
+ rustlibs: [
+ "android.hardware.security.keymint-rust",
+ "android.security.apc-rust",
+ "android.system.keystore2-rust",
+ "libandroid_logger",
+ "libanyhow",
+ "libbinder_rs",
+ "libkeystore2_apc_compat-rust",
+ "libkeystore2_crypto_rust",
+ "libkeystore2_selinux",
+ "liblazy_static",
+ "liblibc",
+ "liblibsqlite3_sys",
+ "liblog_rust",
+ "librand",
+ "librusqlite",
+ "libthiserror",
+ ],
+}
+
+rust_binary {
+ name: "keystore2",
+ srcs: ["src/keystore2_main.rs"],
+ rustlibs: [
+ "libandroid_logger",
+ "libbinder_rs",
+ "libkeystore2",
+ "liblog_rust",
+ ],
+ init_rc: ["keystore2.rc"],
+}
diff --git a/keystore2/AndroidTest.xml b/keystore2/AndroidTest.xml
new file mode 100644
index 0000000..b295947
--- /dev/null
+++ b/keystore2/AndroidTest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration description="Config to run keystore2_test device tests.">
+
+ <option name="test-suite-tag" value="rust-tests" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="keystore2_test->/data/local/tmp/keystore2_test" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.rust.RustBinaryTest" >
+ <option name="test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="keystore2_test" />
+ </test>
+</configuration>
diff --git a/keystore2/TEST_MAPPING b/keystore2/TEST_MAPPING
new file mode 100644
index 0000000..70e3175
--- /dev/null
+++ b/keystore2/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "keystore2_test"
+ }
+ ]
+}
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
new file mode 100644
index 0000000..3051173
--- /dev/null
+++ b/keystore2/aidl/Android.bp
@@ -0,0 +1,60 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+aidl_interface {
+ name: "android.security.attestationmanager",
+ srcs: [ "android/security/attestationmanager/*.aidl", ],
+ imports: [ "android.hardware.security.keymint" ],
+ unstable: true,
+ backend: {
+ java: {
+ sdk_version: "module_current",
+ },
+ rust: {
+ enabled: true,
+ },
+ },
+}
+
+aidl_interface {
+ name: "android.security.authorizations",
+ srcs: [ "android/security/authorizations/*.aidl" ],
+ imports: [ "android.hardware.security.keymint" ],
+ unstable: true,
+ backend: {
+ java: {
+ sdk_version: "module_current",
+ },
+ rust: {
+ enabled: true,
+ },
+ ndk: {
+ enabled: true,
+ }
+ },
+}
+
+aidl_interface {
+ name: "android.security.apc",
+ srcs: [ "android/security/apc/*.aidl" ],
+ unstable: true,
+ backend: {
+ java: {
+ enabled: true,
+ },
+ rust: {
+ enabled: true,
+ },
+ },
+}
diff --git a/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl b/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
new file mode 100644
index 0000000..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/attestationmanager/ByteArray.aidl b/keystore2/aidl/android/security/attestationmanager/ByteArray.aidl
new file mode 100644
index 0000000..a1592ec
--- /dev/null
+++ b/keystore2/aidl/android/security/attestationmanager/ByteArray.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.attestationmanager;
+
+/**
+ * Simple data holder for a byte array, allowing for multidimensional arrays in AIDL.
+ *
+ * @hide
+ */
+parcelable ByteArray {
+ byte[] data;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/attestationmanager/IAttestationManager.aidl b/keystore2/aidl/android/security/attestationmanager/IAttestationManager.aidl
new file mode 100644
index 0000000..85eee57
--- /dev/null
+++ b/keystore2/aidl/android/security/attestationmanager/IAttestationManager.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.attestationmanager;
+
+import android.security.attestationmanager.ByteArray;
+import android.hardware.security.keymint.KeyParameter;
+
+/**
+ * Internal interface for performing device attestation.
+ *
+ * @hide
+ */
+interface IAttestationManager {
+ /**
+ * Attest a provided list of device identifiers.
+ *
+ * @return The signed certificate chain, with each individual certificate encoded as a byte
+ * array.
+ */
+ ByteArray[] attestDevice(
+ in KeyParameter[] deviceIdentifiers, boolean useIndividualAttestation,
+ in byte[] attestationChallenge, int securityLevel);
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/authorizations/IKeystoreAuthorization.aidl b/keystore2/aidl/android/security/authorizations/IKeystoreAuthorization.aidl
new file mode 100644
index 0000000..d3e80ee
--- /dev/null
+++ b/keystore2/aidl/android/security/authorizations/IKeystoreAuthorization.aidl
@@ -0,0 +1,35 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android.security.authorizations;
+
+import android.hardware.security.keymint.HardwareAuthToken;
+
+/**
+* IKeystoreAuthorization interface exposes the methods for other system components to
+* provide keystore with the information required to enforce authorizations on key usage.
+*/
+interface IKeystoreAuthorization {
+
+ /**
+ * Allows the Android authenticators to hand over an auth token to Keystore.
+ * Callers require 'AddAuth' permission.
+ * ## Error conditions:
+ * `ResponseCode::SYSTEM_ERROR` - if failed to store the auth token in the database or if failed
+ * to add the auth token to the operation, if it is a per-op auth token.
+ *
+ * @param authToken The auth token created by an authenticator, upon user authentication.
+ */
+ void addAuthToken(in HardwareAuthToken authToken);
+}
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/keystore2.rc b/keystore2/keystore2.rc
new file mode 100644
index 0000000..bc040e5
--- /dev/null
+++ b/keystore2/keystore2.rc
@@ -0,0 +1,20 @@
+# Start the keystore2 service.
+# Keystore 2.0 changes its working directory to the first positional
+# command line option, i.e., /data/misc/keystore, where it stores its
+# database.
+# Keystore shall run as user keystore and groups keystore, readproc, and log.
+#
+# See system/core/init/README.md for information on the init.rc language.
+
+# Start Keystore 2 conditionally
+# TODO b/171563717 Remove when Keystore 2 migration is complete.
+on nonencrypted && property:ro.android.security.keystore2.enable=true
+ enable keystore2
+
+service keystore2 /system/bin/keystore2 /data/misc/keystore
+ class main
+ user keystore
+ group keystore readproc log
+ writepid /dev/cpuset/foreground/tasks
+ # TODO b/171563717 Remove when Keystore 2 migration is complete.
+ disabled
diff --git a/keystore2/selinux/Android.bp b/keystore2/selinux/Android.bp
new file mode 100644
index 0000000..acbf5ef
--- /dev/null
+++ b/keystore2/selinux/Android.bp
@@ -0,0 +1,54 @@
+// 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_selinux",
+ crate_name: "keystore2_selinux",
+ srcs: [
+ "src/lib.rs",
+ ],
+
+ shared_libs: [
+ "libselinux",
+ ],
+
+ rustlibs: [
+ "libanyhow",
+ "liblog_rust",
+ "libselinux_bindgen",
+ "libthiserror",
+ ],
+}
+
+rust_test {
+ name: "keystore2_selinux_test",
+ srcs: [
+ "src/lib.rs",
+ ],
+ crate_name: "keystore2_selinux_test",
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+
+ shared_libs: [
+ "libselinux",
+ ],
+
+ rustlibs: [
+ "libandroid_logger",
+ "libanyhow",
+ "liblog_rust",
+ "libselinux_bindgen",
+ "libthiserror",
+ ],
+}
diff --git a/keystore2/selinux/TEST_MAPPING b/keystore2/selinux/TEST_MAPPING
new file mode 100644
index 0000000..0e68257
--- /dev/null
+++ b/keystore2/selinux/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "keystore2_selinux_test"
+ }
+ ]
+}
diff --git a/keystore2/selinux/src/lib.rs b/keystore2/selinux/src/lib.rs
new file mode 100644
index 0000000..932c30e
--- /dev/null
+++ b/keystore2/selinux/src/lib.rs
@@ -0,0 +1,473 @@
+// 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(missing_docs)]
+
+//! This crate provides some safe wrappers around the libselinux API. It is currently limited
+//! to the API surface that Keystore 2.0 requires to perform permission checks against
+//! the SEPolicy. Notably, it provides wrappers for:
+//! * getcon
+//! * selinux_check_access
+//! * selabel_lookup for the keystore2_key backend.
+//! And it provides an owning wrapper around context strings `Context`.
+
+use std::ffi::{CStr, CString};
+use std::fmt;
+use std::io;
+use std::marker::{Send, Sync};
+pub use std::ops::Deref;
+use std::os::raw::c_char;
+use std::ptr;
+use std::sync;
+
+use selinux_bindgen as selinux;
+
+use anyhow::Context as AnyhowContext;
+use anyhow::{anyhow, Result};
+
+use selinux::SELABEL_CTX_ANDROID_KEYSTORE2_KEY;
+use selinux::SELINUX_CB_LOG;
+
+pub use selinux::pid_t;
+
+static SELINUX_LOG_INIT: sync::Once = sync::Once::new();
+
+fn redirect_selinux_logs_to_logcat() {
+ // `selinux_set_callback` assigns the static lifetime function pointer
+ // `selinux_log_callback` to a static lifetime variable.
+ let cb = selinux::selinux_callback { func_log: Some(selinux::selinux_log_callback) };
+ unsafe {
+ selinux::selinux_set_callback(SELINUX_CB_LOG as i32, cb);
+ }
+}
+
+// This function must be called before any entry point into lib selinux.
+// Or leave a comment reasoning why calling this macro is not necessary
+// for a given entry point.
+fn init_logger_once() {
+ SELINUX_LOG_INIT.call_once(redirect_selinux_logs_to_logcat)
+}
+
+/// Selinux Error code.
+#[derive(thiserror::Error, Debug, PartialEq)]
+pub enum Error {
+ /// Indicates that an access check yielded no access.
+ #[error("Permission Denied")]
+ PermissionDenied,
+ /// Indicates an unexpected system error. Nested string provides some details.
+ #[error("Selinux SystemError: {0}")]
+ SystemError(String),
+}
+
+impl Error {
+ /// Constructs a `PermissionDenied` error.
+ pub fn perm() -> Self {
+ Error::PermissionDenied
+ }
+ fn sys<T: Into<String>>(s: T) -> Self {
+ Error::SystemError(s.into())
+ }
+}
+
+/// Context represents an SELinux context string. It can take ownership of a raw
+/// s-string as allocated by `getcon` or `selabel_lookup`. In this case it uses
+/// `freecon` to free the resources when dropped. In its second variant it stores
+/// an `std::ffi::CString` that can be initialized from a Rust string slice.
+#[derive(Debug)]
+pub enum Context {
+ /// Wraps a raw context c-string as returned by libselinux.
+ Raw(*mut ::std::os::raw::c_char),
+ /// Stores a context string as `std::ffi::CString`.
+ CString(CString),
+}
+
+impl PartialEq for Context {
+ fn eq(&self, other: &Self) -> bool {
+ // We dereference both and thereby delegate the comparison
+ // to `CStr`'s implementation of `PartialEq`.
+ **self == **other
+ }
+}
+
+impl Eq for Context {}
+
+impl fmt::Display for Context {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", (**self).to_str().unwrap_or("Invalid context"))
+ }
+}
+
+impl Drop for Context {
+ fn drop(&mut self) {
+ if let Self::Raw(p) = self {
+ // No need to initialize the logger here, because
+ // `freecon` cannot run unless `Backend::lookup` or `getcon`
+ // has run.
+ unsafe { selinux::freecon(*p) };
+ }
+ }
+}
+
+impl Deref for Context {
+ type Target = CStr;
+
+ fn deref(&self) -> &Self::Target {
+ match self {
+ Self::Raw(p) => unsafe { CStr::from_ptr(*p) },
+ Self::CString(cstr) => &cstr,
+ }
+ }
+}
+
+impl Context {
+ /// Initializes the `Context::CString` variant from a Rust string slice.
+ pub fn new(con: &str) -> Result<Self> {
+ Ok(Self::CString(
+ CString::new(con)
+ .with_context(|| format!("Failed to create Context with \"{}\"", con))?,
+ ))
+ }
+}
+
+/// The backend trait provides a uniform interface to all libselinux context backends.
+/// Currently, we only implement the KeystoreKeyBackend though.
+pub trait Backend {
+ /// Implementers use libselinux `selabel_lookup` to lookup the context for the given `key`.
+ fn lookup(&self, key: &str) -> Result<Context>;
+}
+
+/// Keystore key backend takes onwnership of the SELinux context handle returned by
+/// `selinux_android_keystore2_key_context_handle` and uses `selabel_close` to free
+/// the handle when dropped.
+/// It implements `Backend` to provide keystore_key label lookup functionality.
+pub struct KeystoreKeyBackend {
+ handle: *mut selinux::selabel_handle,
+}
+
+// KeystoreKeyBackend is Sync because selabel_lookup is thread safe.
+unsafe impl Sync for KeystoreKeyBackend {}
+unsafe impl Send for KeystoreKeyBackend {}
+
+impl KeystoreKeyBackend {
+ const BACKEND_TYPE: i32 = SELABEL_CTX_ANDROID_KEYSTORE2_KEY as i32;
+
+ /// Creates a new instance representing an SELinux context handle as returned by
+ /// `selinux_android_keystore2_key_context_handle`.
+ pub fn new() -> Result<Self> {
+ init_logger_once();
+ let handle = unsafe { selinux::selinux_android_keystore2_key_context_handle() };
+ if handle.is_null() {
+ return Err(anyhow!(Error::sys("Failed to open KeystoreKeyBackend")));
+ }
+ Ok(KeystoreKeyBackend { handle })
+ }
+}
+
+impl Drop for KeystoreKeyBackend {
+ fn drop(&mut self) {
+ // No need to initialize the logger here because it cannot be called unless
+ // KeystoreKeyBackend::new has run.
+ unsafe { selinux::selabel_close(self.handle) };
+ }
+}
+
+// Because KeystoreKeyBackend is Sync and Send, member function must never call
+// non thread safe libselinux functions. As of this writing no non thread safe
+// functions exist that could be called on a label backend handle.
+impl Backend for KeystoreKeyBackend {
+ fn lookup(&self, key: &str) -> Result<Context> {
+ let mut con: *mut c_char = ptr::null_mut();
+ let c_key = CString::new(key).with_context(|| {
+ format!("selabel_lookup: Failed to convert key \"{}\" to CString.", key)
+ })?;
+ match unsafe {
+ // No need to initialize the logger here because it cannot run unless
+ // KeystoreKeyBackend::new has run.
+ selinux::selabel_lookup(self.handle, &mut con, c_key.as_ptr(), Self::BACKEND_TYPE)
+ } {
+ 0 => {
+ if !con.is_null() {
+ Ok(Context::Raw(con))
+ } else {
+ Err(anyhow!(Error::sys(format!(
+ "selabel_lookup returned a NULL context for key \"{}\"",
+ key
+ ))))
+ }
+ }
+ _ => Err(anyhow!(io::Error::last_os_error()))
+ .with_context(|| format!("selabel_lookup failed for key \"{}\"", key)),
+ }
+ }
+}
+
+/// Safe wrapper around libselinux `getcon`. It initializes the `Context::Raw` variant of the
+/// returned `Context`.
+///
+/// ## Return
+/// * Ok(Context::Raw()) if successful.
+/// * Err(Error::sys()) if getcon succeeded but returned a NULL pointer.
+/// * Err(io::Error::last_os_error()) if getcon failed.
+pub fn getcon() -> Result<Context> {
+ init_logger_once();
+ let mut con: *mut c_char = ptr::null_mut();
+ match unsafe { selinux::getcon(&mut con) } {
+ 0 => {
+ if !con.is_null() {
+ Ok(Context::Raw(con))
+ } else {
+ Err(anyhow!(Error::sys("getcon returned a NULL context")))
+ }
+ }
+ _ => Err(anyhow!(io::Error::last_os_error())).context("getcon failed"),
+ }
+}
+
+/// Safe wrapper around libselinux `getpidcon`. It initializes the `Context::Raw` variant of the
+/// returned `Context`.
+///
+/// ## Return
+/// * Ok(Context::Raw()) if successful.
+/// * Err(Error::sys()) if getpidcon succeeded but returned a NULL pointer.
+/// * Err(io::Error::last_os_error()) if getpidcon failed.
+pub fn getpidcon(pid: selinux::pid_t) -> Result<Context> {
+ init_logger_once();
+ let mut con: *mut c_char = ptr::null_mut();
+ match unsafe { selinux::getpidcon(pid, &mut con) } {
+ 0 => {
+ if !con.is_null() {
+ Ok(Context::Raw(con))
+ } else {
+ Err(anyhow!(Error::sys(format!(
+ "getpidcon returned a NULL context for pid {}",
+ pid
+ ))))
+ }
+ }
+ _ => Err(anyhow!(io::Error::last_os_error()))
+ .context(format!("getpidcon failed for pid {}", pid)),
+ }
+}
+
+/// Safe wrapper around selinux_check_access.
+///
+/// ## Return
+/// * Ok(()) iff the requested access was granted.
+/// * Err(anyhow!(Error::perm()))) if the permission was denied.
+/// * Err(anyhow!(ioError::last_os_error())) if any other error occurred while performing
+/// the access check.
+pub fn check_access(source: &CStr, target: &CStr, tclass: &str, perm: &str) -> Result<()> {
+ init_logger_once();
+ let c_tclass = CString::new(tclass).with_context(|| {
+ format!("check_access: Failed to convert tclass \"{}\" to CString.", tclass)
+ })?;
+ let c_perm = CString::new(perm).with_context(|| {
+ format!("check_access: Failed to convert perm \"{}\" to CString.", perm)
+ })?;
+
+ match unsafe {
+ selinux::selinux_check_access(
+ source.as_ptr(),
+ target.as_ptr(),
+ c_tclass.as_ptr(),
+ c_perm.as_ptr(),
+ ptr::null_mut(),
+ )
+ } {
+ 0 => Ok(()),
+ _ => {
+ let e = io::Error::last_os_error();
+ match e.kind() {
+ io::ErrorKind::PermissionDenied => Err(anyhow!(Error::perm())),
+ _ => Err(anyhow!(e)),
+ }
+ .with_context(|| {
+ format!(
+ concat!(
+ "check_access: Failed with sctx: {:?} tctx: {:?}",
+ " with target class: \"{}\" perm: \"{}\""
+ ),
+ source, target, tclass, perm
+ )
+ })
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use anyhow::Result;
+
+ /// The su_key namespace as defined in su.te and keystore_key_contexts of the
+ /// SePolicy (system/sepolicy).
+ static SU_KEY_NAMESPACE: &str = "0";
+ /// The shell_key namespace as defined in shell.te and keystore_key_contexts of the
+ /// SePolicy (system/sepolicy).
+ static SHELL_KEY_NAMESPACE: &str = "1";
+
+ fn check_context() -> Result<(Context, &'static str, bool)> {
+ let context = getcon()?;
+ match context.to_str().unwrap() {
+ "u:r:su:s0" => Ok((context, SU_KEY_NAMESPACE, true)),
+ "u:r:shell:s0" => Ok((context, SHELL_KEY_NAMESPACE, false)),
+ c => Err(anyhow!(format!(
+ "This test must be run as \"su\" or \"shell\". Current context: \"{}\"",
+ c
+ ))),
+ }
+ }
+
+ #[test]
+ fn test_getcon() -> Result<()> {
+ check_context()?;
+ Ok(())
+ }
+
+ #[test]
+ fn test_label_lookup() -> Result<()> {
+ let (_context, namespace, is_su) = check_context()?;
+ let backend = crate::KeystoreKeyBackend::new()?;
+ let context = backend.lookup(namespace)?;
+ if is_su {
+ assert_eq!(context.to_str(), Ok("u:object_r:su_key:s0"));
+ } else {
+ assert_eq!(context.to_str(), Ok("u:object_r:shell_key:s0"));
+ }
+ Ok(())
+ }
+
+ #[test]
+ fn context_from_string() -> Result<()> {
+ let tctx = Context::new("u:object_r:keystore:s0").unwrap();
+ let sctx = Context::new("u:r:system_server:s0").unwrap();
+ check_access(&sctx, &tctx, "keystore2_key", "use")?;
+ Ok(())
+ }
+
+ mod perm {
+ use super::super::*;
+ use super::*;
+ use anyhow::Result;
+
+ /// check_key_perm(perm, privileged, priv_domain)
+ /// `perm` is a permission of the keystore2_key class and `privileged` is a boolean
+ /// indicating whether the permission is considered privileged.
+ /// Privileged permissions are expected to be denied to `shell` users but granted
+ /// to the given priv_domain.
+ macro_rules! check_key_perm {
+ // "use" is a keyword and cannot be used as an identifier, but we must keep
+ // the permission string intact. So we map the identifier name on use_ while using
+ // the permission string "use". In all other cases we can simply use the stringified
+ // identifier as permission string.
+ (use, $privileged:expr) => {
+ check_key_perm!(use_, $privileged, "use");
+ };
+ ($perm:ident, $privileged:expr) => {
+ check_key_perm!($perm, $privileged, stringify!($perm));
+ };
+ ($perm:ident, $privileged:expr, $p_str:expr) => {
+ #[test]
+ fn $perm() -> Result<()> {
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("keystore_selinux_tests")
+ .with_min_level(log::Level::Debug),
+ );
+ let scontext = Context::new("u:r:shell:s0")?;
+ let backend = KeystoreKeyBackend::new()?;
+ let tcontext = backend.lookup(SHELL_KEY_NAMESPACE)?;
+
+ if $privileged {
+ assert_eq!(
+ Some(&Error::perm()),
+ check_access(
+ &scontext,
+ &tcontext,
+ "keystore2_key",
+ $p_str
+ )
+ .err()
+ .unwrap()
+ .root_cause()
+ .downcast_ref::<Error>()
+ );
+ } else {
+ assert!(check_access(
+ &scontext,
+ &tcontext,
+ "keystore2_key",
+ $p_str
+ )
+ .is_ok());
+ }
+ Ok(())
+ }
+ };
+ }
+
+ check_key_perm!(manage_blob, true);
+ check_key_perm!(delete, false);
+ check_key_perm!(use_dev_id, true);
+ check_key_perm!(req_forced_op, true);
+ check_key_perm!(gen_unique_id, true);
+ check_key_perm!(grant, true);
+ check_key_perm!(get_info, false);
+ check_key_perm!(rebind, false);
+ check_key_perm!(update, false);
+ check_key_perm!(use, false);
+
+ macro_rules! check_keystore_perm {
+ ($perm:ident) => {
+ #[test]
+ fn $perm() -> Result<()> {
+ let ks_context = Context::new("u:object_r:keystore:s0")?;
+ let priv_context = Context::new("u:r:system_server:s0")?;
+ let unpriv_context = Context::new("u:r:shell:s0")?;
+ assert!(check_access(
+ &priv_context,
+ &ks_context,
+ "keystore2",
+ stringify!($perm)
+ )
+ .is_ok());
+ assert_eq!(
+ Some(&Error::perm()),
+ check_access(&unpriv_context, &ks_context, "keystore2", stringify!($perm))
+ .err()
+ .unwrap()
+ .root_cause()
+ .downcast_ref::<Error>()
+ );
+ Ok(())
+ }
+ };
+ }
+
+ check_keystore_perm!(add_auth);
+ check_keystore_perm!(clear_ns);
+ check_keystore_perm!(get_state);
+ check_keystore_perm!(lock);
+ check_keystore_perm!(reset);
+ check_keystore_perm!(unlock);
+ }
+
+ #[test]
+ fn test_getpidcon() {
+ // Check that `getpidcon` of our pid is equal to what `getcon` returns.
+ // And by using `unwrap` we make sure that both also have to return successfully
+ // fully to pass the test.
+ assert_eq!(getpidcon(std::process::id() as i32).unwrap(), getcon().unwrap());
+ }
+}
diff --git a/keystore2/src/apc.rs b/keystore2/src/apc.rs
new file mode 100644
index 0000000..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/auth_token_handler.rs b/keystore2/src/auth_token_handler.rs
new file mode 100644
index 0000000..8c10442
--- /dev/null
+++ b/keystore2/src/auth_token_handler.rs
@@ -0,0 +1,83 @@
+// 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 defines the AuthTokenHandler enum and its methods. AuthTokenHandler enum represents
+//! the different states an auth token and an associated verification token can be expressed during
+//! the operation life cycle.
+use crate::error::Error as KeystoreError;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ HardwareAuthToken::HardwareAuthToken, VerificationToken::VerificationToken,
+};
+use anyhow::{Context, Result};
+use std::sync::mpsc::Receiver;
+
+/// AuthTokenHandler enum has five different variants which are described by the comments above
+// each variant.
+pub enum AuthTokenHandler {
+ /// Used when an operation does not require an auth token for authorization.
+ NoAuthRequired,
+ /// Used to represent the intermediate state between the time the operation is found to be
+ /// requiring per-op auth and the time the auth token for the operation is found.
+ OpAuthRequired,
+ /// Used to represent the intermediate state between the time the operation is found to be
+ /// using a time_out key with STRONGBOX keymint, and the time a verficiation token is requested
+ /// from the worker thread which obtains verification tokens from the TEE KeyMint.
+ VerificationRequired(HardwareAuthToken),
+ /// Used to represent the intermediate state between the time a verification token is requested
+ /// from the worker thread which obtains verification tokens from the TEE KeyMint and the time
+ /// the verification token is received from the worker thread.
+ Channel(Receiver<(HardwareAuthToken, VerificationToken)>),
+ /// Used to represent the final state for all operations requiring an auth token for
+ /// authorization, after the matching auth token (and the associated verification token if
+ /// required) is found.
+ Token(HardwareAuthToken, Option<VerificationToken>),
+}
+
+impl AuthTokenHandler {
+ /// Retrieve auth token and verification token from the Token variant of an AuthTokenHandler
+ /// instance
+ pub fn get_auth_and_verification_tokens(
+ &self,
+ ) -> Option<(&HardwareAuthToken, &VerificationToken)> {
+ if let AuthTokenHandler::Token(auth_token, Some(verification_token)) = self {
+ Some((auth_token, verification_token))
+ } else {
+ None
+ }
+ }
+
+ /// Retrieve auth token from the Token variant of an AuthTokenHandler instance
+ pub fn get_auth_token(&self) -> Option<&HardwareAuthToken> {
+ if let AuthTokenHandler::Token(auth_token, _) = self {
+ Some(auth_token)
+ } else {
+ None
+ }
+ }
+
+ /// If Channel variant, block on it until the verification token is sent by the
+ /// keystore2 worker thread which obtains verification tokens from TEE Keymint
+ pub fn receive_verification_token(&mut self) -> Result<()> {
+ if let AuthTokenHandler::Channel(recv) = self {
+ let (auth_token, verification_token) =
+ recv.recv().context("In receive_verification_token: sender disconnected.")?;
+ *self = AuthTokenHandler::Token(auth_token, Some(verification_token));
+ Ok(())
+ } else {
+ Err(KeystoreError::sys()).context(
+ "In receive_verification_token: Wrong variant found in the authorization object.",
+ )
+ }
+ }
+}
diff --git a/keystore2/src/crypto/Android.bp b/keystore2/src/crypto/Android.bp
new file mode 100644
index 0000000..03c42b2
--- /dev/null
+++ b/keystore2/src/crypto/Android.bp
@@ -0,0 +1,92 @@
+// 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_crypto_rust",
+ crate_name: "keystore2_crypto",
+ srcs: ["lib.rs"],
+ rustlibs: [
+ "libkeystore2_crypto_bindgen",
+ "liblog_rust",
+ "libnix",
+ "libthiserror",
+ ],
+ shared_libs: [
+ "libkeystore2_crypto",
+ "libcrypto",
+ ],
+}
+
+cc_library {
+ name: "libkeystore2_crypto",
+ srcs: [
+ "crypto.cpp",
+ "certificate_utils.cpp",
+ ],
+ export_include_dirs: ["include"],
+ shared_libs: [
+ "libcrypto",
+ "liblog",
+ ],
+}
+
+rust_bindgen {
+ name: "libkeystore2_crypto_bindgen",
+ wrapper_src: "crypto.hpp",
+ crate_name: "keystore2_crypto_bindgen",
+ source_stem: "bindings",
+ host_supported: true,
+}
+
+rust_test {
+ name: "keystore2_crypto_test_rust",
+ crate_name: "keystore2_crypto_test_rust",
+ srcs: ["lib.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ rustlibs: [
+ "libkeystore2_crypto_bindgen",
+ "liblog_rust",
+ "libnix",
+ "libthiserror",
+ ],
+ static_libs: [
+ "libkeystore2_crypto",
+ ],
+ shared_libs: [
+ "libc++",
+ "libcrypto",
+ "liblog",
+ ],
+}
+
+cc_test {
+ name: "keystore2_crypto_test",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ srcs: [
+ "tests/certificate_utils_test.cpp",
+ "tests/gtest_main.cpp",
+ ],
+ test_suites: ["general-tests"],
+ static_libs: [
+ "libkeystore2_crypto",
+ ],
+ shared_libs: [
+ "libcrypto",
+ ],
+}
diff --git a/keystore2/src/crypto/TEST_MAPPING b/keystore2/src/crypto/TEST_MAPPING
new file mode 100644
index 0000000..f6fb97a
--- /dev/null
+++ b/keystore2/src/crypto/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "keystore2_crypto_test_rust"
+ },
+ {
+ "name": "keystore2_crypto_test"
+ }
+ ]
+}
diff --git a/keystore2/src/crypto/certificate_utils.cpp b/keystore2/src/crypto/certificate_utils.cpp
new file mode 100644
index 0000000..500600f
--- /dev/null
+++ b/keystore2/src/crypto/certificate_utils.cpp
@@ -0,0 +1,558 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <certificate_utils.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/x509v3.h>
+
+#include <functional>
+#include <limits>
+#include <string>
+#include <variant>
+#include <vector>
+
+namespace keystore {
+
+namespace {
+
+constexpr int kDigitalSignatureKeyUsageBit = 0;
+constexpr int kKeyEnciphermentKeyUsageBit = 2;
+constexpr int kDataEnciphermentKeyUsageBit = 3;
+constexpr int kKeyCertSignBit = 5;
+constexpr int kMaxKeyUsageBit = 8;
+
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_STRING);
+DEFINE_OPENSSL_OBJECT_POINTER(RSA_PSS_PARAMS);
+DEFINE_OPENSSL_OBJECT_POINTER(AUTHORITY_KEYID);
+DEFINE_OPENSSL_OBJECT_POINTER(BASIC_CONSTRAINTS);
+DEFINE_OPENSSL_OBJECT_POINTER(X509_ALGOR);
+
+} // namespace
+
+std::variant<CertUtilsError, X509_NAME_Ptr> makeCommonName(const std::string& name) {
+ X509_NAME_Ptr x509_name(X509_NAME_new());
+ if (!x509_name) {
+ return CertUtilsError::BoringSsl;
+ }
+ if (!X509_NAME_add_entry_by_txt(x509_name.get(), "CN", MBSTRING_ASC,
+ reinterpret_cast<const uint8_t*>(name.c_str()), name.length(),
+ -1 /* loc */, 0 /* set */)) {
+ return CertUtilsError::BoringSsl;
+ }
+ return x509_name;
+}
+
+std::variant<CertUtilsError, std::vector<uint8_t>> makeKeyId(const X509* cert) {
+ std::vector<uint8_t> keyid(20);
+ unsigned int len;
+ if (!X509_pubkey_digest(cert, EVP_sha1(), keyid.data(), &len)) {
+ return CertUtilsError::Encoding;
+ }
+ return keyid;
+}
+
+std::variant<CertUtilsError, AUTHORITY_KEYID_Ptr>
+makeAuthorityKeyExtension(const std::vector<uint8_t>& keyid) {
+ AUTHORITY_KEYID_Ptr auth_key(AUTHORITY_KEYID_new());
+ if (!auth_key) {
+ return CertUtilsError::MemoryAllocation;
+ }
+
+ auth_key->keyid = ASN1_OCTET_STRING_new();
+ if (auth_key->keyid == nullptr) {
+ return CertUtilsError::MemoryAllocation;
+ }
+
+ if (!ASN1_OCTET_STRING_set(auth_key->keyid, keyid.data(), keyid.size())) {
+ return CertUtilsError::BoringSsl;
+ }
+
+ return auth_key;
+}
+
+std::variant<CertUtilsError, ASN1_OCTET_STRING_Ptr>
+makeSubjectKeyExtension(const std::vector<uint8_t>& keyid) {
+
+ // Build OCTET_STRING
+ ASN1_OCTET_STRING_Ptr keyid_str(ASN1_OCTET_STRING_new());
+ if (!keyid_str || !ASN1_OCTET_STRING_set(keyid_str.get(), keyid.data(), keyid.size())) {
+ return CertUtilsError::BoringSsl;
+ }
+
+ return keyid_str;
+}
+
+std::variant<CertUtilsError, BASIC_CONSTRAINTS_Ptr>
+makeBasicConstraintsExtension(bool is_ca, std::optional<int> path_length) {
+
+ BASIC_CONSTRAINTS_Ptr bcons(BASIC_CONSTRAINTS_new());
+ if (!bcons) {
+ return CertUtilsError::MemoryAllocation;
+ }
+
+ bcons->ca = is_ca;
+ bcons->pathlen = nullptr;
+ if (path_length) {
+ bcons->pathlen = ASN1_INTEGER_new();
+ if (bcons->pathlen == nullptr || !ASN1_INTEGER_set(bcons->pathlen, *path_length)) {
+ return CertUtilsError::BoringSsl;
+ }
+ }
+
+ return bcons;
+}
+
+std::variant<CertUtilsError, ASN1_BIT_STRING_Ptr>
+makeKeyUsageExtension(bool is_signing_key, bool is_encryption_key, bool is_cert_key) {
+ // Build BIT_STRING with correct contents.
+ ASN1_BIT_STRING_Ptr key_usage(ASN1_BIT_STRING_new());
+ if (!key_usage) {
+ return CertUtilsError::BoringSsl;
+ }
+
+ for (size_t i = 0; i <= kMaxKeyUsageBit; ++i) {
+ if (!ASN1_BIT_STRING_set_bit(key_usage.get(), i, 0)) {
+ return CertUtilsError::BoringSsl;
+ }
+ }
+
+ if (is_signing_key) {
+ if (!ASN1_BIT_STRING_set_bit(key_usage.get(), kDigitalSignatureKeyUsageBit, 1)) {
+ return CertUtilsError::BoringSsl;
+ }
+ }
+
+ if (is_encryption_key) {
+ if (!ASN1_BIT_STRING_set_bit(key_usage.get(), kKeyEnciphermentKeyUsageBit, 1) ||
+ !ASN1_BIT_STRING_set_bit(key_usage.get(), kDataEnciphermentKeyUsageBit, 1)) {
+ return CertUtilsError::BoringSsl;
+ }
+ }
+
+ if (is_cert_key) {
+ if (!ASN1_BIT_STRING_set_bit(key_usage.get(), kKeyCertSignBit, 1)) {
+ return CertUtilsError::BoringSsl;
+ }
+ }
+
+ return key_usage;
+}
+
+// Creates a rump certificate structure with serial, subject and issuer names, as well as
+// activation and expiry date.
+// Callers should pass an empty X509_Ptr and check the return value for CertUtilsError::Ok (0)
+// before accessing the result.
+std::variant<CertUtilsError, X509_Ptr>
+makeCertRump(const uint32_t serial, const char subject[], const uint64_t activeDateTimeMilliSeconds,
+ const uint64_t usageExpireDateTimeMilliSeconds) {
+
+ // Sanitize pointer arguments.
+ if (!subject || strlen(subject) == 0) {
+ return CertUtilsError::InvalidArgument;
+ }
+
+ // Create certificate structure.
+ X509_Ptr certificate(X509_new());
+ if (!certificate) {
+ return CertUtilsError::BoringSsl;
+ }
+
+ // Set the X509 version.
+ if (!X509_set_version(certificate.get(), 2 /* version 3, but zero-based */)) {
+ return CertUtilsError::BoringSsl;
+ }
+
+ // Set the certificate serialNumber
+ ASN1_INTEGER_Ptr serialNumber(ASN1_INTEGER_new());
+ if (!serialNumber || !ASN1_INTEGER_set(serialNumber.get(), serial) ||
+ !X509_set_serialNumber(certificate.get(), serialNumber.get() /* Don't release; copied */))
+ return CertUtilsError::BoringSsl;
+
+ // Set Subject Name
+ auto subjectName = makeCommonName(subject);
+ if (auto x509_subject = std::get_if<X509_NAME_Ptr>(&subjectName)) {
+ if (!X509_set_subject_name(certificate.get(), x509_subject->get() /* copied */)) {
+ return CertUtilsError::BoringSsl;
+ }
+ } else {
+ return std::get<CertUtilsError>(subjectName);
+ }
+
+ // Set activation date.
+ ASN1_TIME_Ptr notBefore(ASN1_TIME_new());
+ if (!notBefore || !ASN1_TIME_set(notBefore.get(), activeDateTimeMilliSeconds / 1000) ||
+ !X509_set_notBefore(certificate.get(), notBefore.get() /* Don't release; copied */))
+ return CertUtilsError::BoringSsl;
+
+ // Set expiration date.
+ time_t notAfterTime;
+ notAfterTime = (time_t)std::min((uint64_t)std::numeric_limits<time_t>::max(),
+ usageExpireDateTimeMilliSeconds / 1000);
+
+ ASN1_TIME_Ptr notAfter(ASN1_TIME_new());
+ if (!notAfter || !ASN1_TIME_set(notAfter.get(), notAfterTime) ||
+ !X509_set_notAfter(certificate.get(), notAfter.get() /* Don't release; copied */)) {
+ return CertUtilsError::BoringSsl;
+ }
+
+ return certificate;
+}
+
+std::variant<CertUtilsError, X509_Ptr>
+makeCert(const EVP_PKEY* evp_pkey, const uint32_t serial, const char subject[],
+ const uint64_t activeDateTimeMilliSeconds, const uint64_t usageExpireDateTimeMilliSeconds,
+ bool addSubjectKeyIdEx, std::optional<KeyUsageExtension> keyUsageEx,
+ std::optional<BasicConstraintsExtension> basicConstraints) {
+
+ // Make the rump certificate with serial, subject, not before and not after dates.
+ auto certificateV =
+ makeCertRump(serial, subject, activeDateTimeMilliSeconds, usageExpireDateTimeMilliSeconds);
+ if (auto error = std::get_if<CertUtilsError>(&certificateV)) {
+ return *error;
+ }
+ auto certificate = std::move(std::get<X509_Ptr>(certificateV));
+
+ // Set the public key.
+ if (!X509_set_pubkey(certificate.get(), const_cast<EVP_PKEY*>(evp_pkey))) {
+ return CertUtilsError::BoringSsl;
+ }
+
+ if (keyUsageEx) {
+ // Make and add the key usage extension.
+ auto key_usage_extensionV = makeKeyUsageExtension(
+ keyUsageEx->isSigningKey, keyUsageEx->isEncryptionKey, keyUsageEx->isCertificationKey);
+ if (auto error = std::get_if<CertUtilsError>(&key_usage_extensionV)) {
+ return *error;
+ }
+ auto key_usage_extension = std::move(std::get<ASN1_BIT_STRING_Ptr>(key_usage_extensionV));
+ if (!X509_add1_ext_i2d(certificate.get(), NID_key_usage,
+ key_usage_extension.get() /* Don't release; copied */,
+ true /* critical */, 0 /* flags */)) {
+ return CertUtilsError::BoringSsl;
+ }
+ }
+
+ if (basicConstraints) {
+ // Make and add basic constraints
+ auto basic_constraints_extensionV =
+ makeBasicConstraintsExtension(basicConstraints->isCa, basicConstraints->pathLength);
+ if (auto error = std::get_if<CertUtilsError>(&basic_constraints_extensionV)) {
+ return *error;
+ }
+ auto basic_constraints_extension =
+ std::move(std::get<BASIC_CONSTRAINTS_Ptr>(basic_constraints_extensionV));
+ if (!X509_add1_ext_i2d(certificate.get(), NID_basic_constraints,
+ basic_constraints_extension.get() /* Don't release; copied */,
+ true /* critical */, 0 /* flags */)) {
+ return CertUtilsError::BoringSsl;
+ }
+ }
+
+ if (addSubjectKeyIdEx) {
+ // Make and add subject key id extension.
+ auto keyidV = makeKeyId(certificate.get());
+ if (auto error = std::get_if<CertUtilsError>(&keyidV)) {
+ return *error;
+ }
+ auto& keyid = std::get<std::vector<uint8_t>>(keyidV);
+
+ auto subject_key_extensionV = makeSubjectKeyExtension(keyid);
+ if (auto error = std::get_if<CertUtilsError>(&subject_key_extensionV)) {
+ return *error;
+ }
+ auto subject_key_extension =
+ std::move(std::get<ASN1_OCTET_STRING_Ptr>(subject_key_extensionV));
+ if (!X509_add1_ext_i2d(certificate.get(), NID_subject_key_identifier,
+ subject_key_extension.get() /* Don't release; copied */,
+ false /* critical */, 0 /* flags */)) {
+ return CertUtilsError::BoringSsl;
+ }
+ }
+
+ return certificate;
+}
+
+CertUtilsError setIssuer(X509* cert, const X509* signingCert, bool addAuthKeyExt) {
+
+ X509_NAME* issuerName(X509_get_subject_name(signingCert));
+
+ // Set Issuer Name
+ if (issuerName) {
+ if (!X509_set_issuer_name(cert, issuerName /* copied */)) {
+ return CertUtilsError::BoringSsl;
+ }
+ } else {
+ return CertUtilsError::Encoding;
+ }
+
+ if (addAuthKeyExt) {
+ // Make and add authority key extension - self signed.
+ auto keyidV = makeKeyId(signingCert);
+ if (auto error = std::get_if<CertUtilsError>(&keyidV)) {
+ return *error;
+ }
+ auto& keyid = std::get<std::vector<uint8_t>>(keyidV);
+
+ auto auth_key_extensionV = makeAuthorityKeyExtension(keyid);
+ if (auto error = std::get_if<CertUtilsError>(&auth_key_extensionV)) {
+ return *error;
+ }
+ auto auth_key_extension = std::move(std::get<AUTHORITY_KEYID_Ptr>(auth_key_extensionV));
+ if (!X509_add1_ext_i2d(cert, NID_authority_key_identifier, auth_key_extension.get(), false,
+ 0)) {
+ return CertUtilsError::BoringSsl;
+ }
+ }
+ return CertUtilsError::Ok;
+}
+
+// Takes a certificate a signing certificate and the raw private signing_key. And signs
+// the certificate with the latter.
+CertUtilsError signCert(X509* certificate, EVP_PKEY* signing_key) {
+
+ if (certificate == nullptr) {
+ return CertUtilsError::UnexpectedNullPointer;
+ }
+
+ if (!X509_sign(certificate, signing_key, EVP_sha256())) {
+ return CertUtilsError::BoringSsl;
+ }
+
+ return CertUtilsError::Ok;
+}
+
+std::variant<CertUtilsError, std::vector<uint8_t>> encodeCert(X509* certificate) {
+ int len = i2d_X509(certificate, nullptr);
+ if (len < 0) {
+ return CertUtilsError::BoringSsl;
+ }
+
+ auto result = std::vector<uint8_t>(len);
+ uint8_t* p = result.data();
+
+ if (i2d_X509(certificate, &p) < 0) {
+ return CertUtilsError::BoringSsl;
+ }
+ return result;
+}
+
+CertUtilsError setRsaDigestAlgorField(X509_ALGOR** alg_ptr, const EVP_MD* digest) {
+ if (alg_ptr == nullptr || digest == nullptr) {
+ return CertUtilsError::UnexpectedNullPointer;
+ }
+ *alg_ptr = X509_ALGOR_new();
+ if (*alg_ptr == nullptr) {
+ return CertUtilsError::MemoryAllocation;
+ }
+ X509_ALGOR_set_md(*alg_ptr, digest);
+ return CertUtilsError::Ok;
+}
+
+CertUtilsError setPssMaskGeneratorField(X509_ALGOR** alg_ptr, const EVP_MD* digest) {
+ X509_ALGOR* mgf1_digest = nullptr;
+ if (auto error = setRsaDigestAlgorField(&mgf1_digest, digest)) {
+ return error;
+ }
+ X509_ALGOR_Ptr mgf1_digest_ptr(mgf1_digest);
+
+ ASN1_OCTET_STRING* mgf1_digest_algor_str = nullptr;
+ if (!ASN1_item_pack(mgf1_digest, ASN1_ITEM_rptr(X509_ALGOR), &mgf1_digest_algor_str)) {
+ return CertUtilsError::Encoding;
+ }
+ ASN1_OCTET_STRING_Ptr mgf1_digest_algor_str_ptr(mgf1_digest_algor_str);
+
+ *alg_ptr = X509_ALGOR_new();
+ if (*alg_ptr == nullptr) {
+ return CertUtilsError::MemoryAllocation;
+ }
+ X509_ALGOR_set0(*alg_ptr, OBJ_nid2obj(NID_mgf1), V_ASN1_SEQUENCE, mgf1_digest_algor_str);
+ // *alg_ptr took ownership of the octet string
+ mgf1_digest_algor_str_ptr.release();
+ return CertUtilsError::Ok;
+}
+
+static CertUtilsError setSaltLength(RSA_PSS_PARAMS* pss_params, unsigned length) {
+ pss_params->saltLength = ASN1_INTEGER_new();
+ if (pss_params->saltLength == nullptr) {
+ return CertUtilsError::MemoryAllocation;
+ }
+ if (!ASN1_INTEGER_set(pss_params->saltLength, length)) {
+ return CertUtilsError::Encoding;
+ };
+ return CertUtilsError::Ok;
+}
+
+std::variant<CertUtilsError, ASN1_STRING_Ptr> buildRsaPssParameter(Digest digest) {
+ RSA_PSS_PARAMS_Ptr pss(RSA_PSS_PARAMS_new());
+ if (!pss) {
+ return CertUtilsError::MemoryAllocation;
+ }
+
+ const EVP_MD* md = nullptr;
+
+ switch (digest) {
+ case Digest::SHA1:
+ break;
+ case Digest::SHA224:
+ md = EVP_sha224();
+ break;
+ case Digest::SHA256:
+ md = EVP_sha256();
+ break;
+ case Digest::SHA384:
+ md = EVP_sha384();
+ break;
+ case Digest::SHA512:
+ md = EVP_sha512();
+ break;
+ default:
+ return CertUtilsError::InvalidArgument;
+ }
+
+ if (md != nullptr) {
+ if (auto error = setSaltLength(pss.get(), EVP_MD_size(md))) {
+ return error;
+ }
+ if (auto error = setRsaDigestAlgorField(&pss->hashAlgorithm, md)) {
+ return error;
+ }
+ if (auto error = setPssMaskGeneratorField(&pss->maskGenAlgorithm, md)) {
+ return error;
+ }
+ }
+
+ ASN1_STRING* algo_str = nullptr;
+ if (!ASN1_item_pack(pss.get(), ASN1_ITEM_rptr(RSA_PSS_PARAMS), &algo_str)) {
+ return CertUtilsError::BoringSsl;
+ }
+
+ return ASN1_STRING_Ptr(algo_str);
+}
+
+CertUtilsError makeAndSetAlgo(X509_ALGOR* algo_field, Algo algo, Padding padding, Digest digest) {
+ if (algo_field == nullptr) {
+ return CertUtilsError::UnexpectedNullPointer;
+ }
+ ASN1_STRING_Ptr param;
+ int param_type = V_ASN1_UNDEF;
+ int nid = 0;
+ switch (algo) {
+ case Algo::ECDSA:
+ switch (digest) {
+ case Digest::SHA1:
+ nid = NID_ecdsa_with_SHA1;
+ break;
+ case Digest::SHA224:
+ nid = NID_ecdsa_with_SHA224;
+ break;
+ case Digest::SHA256:
+ nid = NID_ecdsa_with_SHA256;
+ break;
+ case Digest::SHA384:
+ nid = NID_ecdsa_with_SHA384;
+ break;
+ case Digest::SHA512:
+ nid = NID_ecdsa_with_SHA512;
+ break;
+ default:
+ return CertUtilsError::InvalidArgument;
+ }
+ break;
+ case Algo::RSA:
+ switch (padding) {
+ case Padding::PKCS1_5:
+ param_type = V_ASN1_NULL;
+ switch (digest) {
+ case Digest::SHA1:
+ nid = NID_sha1WithRSAEncryption;
+ break;
+ case Digest::SHA224:
+ nid = NID_sha224WithRSAEncryption;
+ break;
+ case Digest::SHA256:
+ nid = NID_sha256WithRSAEncryption;
+ break;
+ case Digest::SHA384:
+ nid = NID_sha384WithRSAEncryption;
+ break;
+ case Digest::SHA512:
+ nid = NID_sha512WithRSAEncryption;
+ break;
+ default:
+ return CertUtilsError::InvalidArgument;
+ }
+ break;
+ case Padding::PSS: {
+ auto v = buildRsaPssParameter(digest);
+ if (auto param_str = std::get_if<ASN1_STRING_Ptr>(&v)) {
+ param = std::move(*param_str);
+ param_type = V_ASN1_SEQUENCE;
+ nid = NID_rsassaPss;
+ } else {
+ return std::get<CertUtilsError>(v);
+ }
+ break;
+ }
+ default:
+ return CertUtilsError::InvalidArgument;
+ }
+ break;
+ default:
+ return CertUtilsError::InvalidArgument;
+ }
+
+ if (!X509_ALGOR_set0(algo_field, OBJ_nid2obj(nid), param_type, param.get())) {
+ return CertUtilsError::Encoding;
+ }
+ // The X509 struct took ownership.
+ param.release();
+ return CertUtilsError::Ok;
+}
+
+// This function allows for signing a
+CertUtilsError signCertWith(X509* certificate,
+ std::function<std::vector<uint8_t>(const uint8_t*, size_t)> sign,
+ Algo algo, Padding padding, Digest digest) {
+ if (auto error = makeAndSetAlgo(certificate->sig_alg, algo, padding, digest)) {
+ return error;
+ }
+ if (auto error = makeAndSetAlgo(certificate->cert_info->signature, algo, padding, digest)) {
+ return error;
+ }
+
+ uint8_t* cert_buf = nullptr;
+ int buf_len = i2d_re_X509_tbs(certificate, &cert_buf);
+ if (buf_len < 0) {
+ return CertUtilsError::Encoding;
+ }
+
+ bssl::UniquePtr<uint8_t> free_cert_buf(cert_buf);
+ auto signature = sign(cert_buf, buf_len);
+
+ if (!ASN1_STRING_set(certificate->signature, signature.data(), signature.size())) {
+ return CertUtilsError::BoringSsl;
+ }
+
+ certificate->signature->flags &= ~(0x07);
+ certificate->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
+ return CertUtilsError::Ok;
+}
+
+} // namespace keystore
diff --git a/keystore2/src/crypto/crypto.cpp b/keystore2/src/crypto/crypto.cpp
new file mode 100644
index 0000000..173ed11
--- /dev/null
+++ b/keystore2/src/crypto/crypto.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "keystore2"
+
+#include "crypto.hpp"
+
+#include <log/log.h>
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+
+#include <vector>
+
+// Copied from system/security/keystore/blob.h.
+
+constexpr size_t kGcmTagLength = 128 / 8;
+constexpr size_t kAes128KeySizeBytes = 128 / 8;
+
+// Copied from system/security/keystore/blob.cpp.
+
+#if defined(__clang__)
+#define OPTNONE __attribute__((optnone))
+#elif defined(__GNUC__)
+#define OPTNONE __attribute__((optimize("O0")))
+#else
+#error Need a definition for OPTNONE
+#endif
+
+class ArrayEraser {
+ public:
+ ArrayEraser(uint8_t* arr, size_t size) : mArr(arr), mSize(size) {}
+ OPTNONE ~ArrayEraser() { std::fill(mArr, mArr + mSize, 0); }
+
+ private:
+ volatile uint8_t* mArr;
+ size_t mSize;
+};
+
+/**
+ * Returns a EVP_CIPHER appropriate for the given key size.
+ */
+const EVP_CIPHER* getAesCipherForKey(size_t key_size) {
+ const EVP_CIPHER* cipher = EVP_aes_256_gcm();
+ if (key_size == kAes128KeySizeBytes) {
+ cipher = EVP_aes_128_gcm();
+ }
+ return cipher;
+}
+
+bool randomBytes(uint8_t* out, size_t len) {
+ return RAND_bytes(out, len);
+}
+
+/*
+ * Encrypt 'len' data at 'in' with AES-GCM, using 128-bit or 256-bit key at 'key', 96-bit IV at
+ * 'iv' and write output to 'out' (which may be the same location as 'in') and 128-bit tag to
+ * 'tag'.
+ */
+bool AES_gcm_encrypt(const uint8_t* in, uint8_t* out, size_t len, const uint8_t* key,
+ size_t key_size, const uint8_t* iv, uint8_t* tag) {
+
+ // There can be 128-bit and 256-bit keys
+ const EVP_CIPHER* cipher = getAesCipherForKey(key_size);
+
+ bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
+
+ EVP_EncryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key, iv);
+ EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
+
+ std::vector<uint8_t> out_tmp(len);
+ uint8_t* out_pos = out_tmp.data();
+ int out_len;
+
+ EVP_EncryptUpdate(ctx.get(), out_pos, &out_len, in, len);
+ out_pos += out_len;
+ EVP_EncryptFinal_ex(ctx.get(), out_pos, &out_len);
+ out_pos += out_len;
+ if (out_pos - out_tmp.data() != static_cast<ssize_t>(len)) {
+ ALOGD("Encrypted ciphertext is the wrong size, expected %zu, got %zd", len,
+ out_pos - out_tmp.data());
+ return false;
+ }
+
+ std::copy(out_tmp.data(), out_pos, out);
+ EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, kGcmTagLength, tag);
+
+ return true;
+}
+
+/*
+ * Decrypt 'len' data at 'in' with AES-GCM, using 128-bit or 256-bit key at 'key', 96-bit IV at
+ * 'iv', checking 128-bit tag at 'tag' and writing plaintext to 'out'(which may be the same
+ * location as 'in').
+ */
+bool AES_gcm_decrypt(const uint8_t* in, uint8_t* out, size_t len, const uint8_t* key,
+ size_t key_size, const uint8_t* iv, const uint8_t* tag) {
+
+ // There can be 128-bit and 256-bit keys
+ const EVP_CIPHER* cipher = getAesCipherForKey(key_size);
+
+ bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
+
+ EVP_DecryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key, iv);
+ EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
+ EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, kGcmTagLength, const_cast<uint8_t*>(tag));
+
+ std::vector<uint8_t> out_tmp(len);
+ ArrayEraser out_eraser(out_tmp.data(), len);
+ uint8_t* out_pos = out_tmp.data();
+ int out_len;
+
+ EVP_DecryptUpdate(ctx.get(), out_pos, &out_len, in, len);
+ out_pos += out_len;
+ if (!EVP_DecryptFinal_ex(ctx.get(), out_pos, &out_len)) {
+ ALOGE("Failed to decrypt blob; ciphertext or tag is likely corrupted");
+ return false;
+ }
+ out_pos += out_len;
+ if (out_pos - out_tmp.data() != static_cast<ssize_t>(len)) {
+ ALOGE("Encrypted plaintext is the wrong size, expected %zu, got %zd", len,
+ out_pos - out_tmp.data());
+ return false;
+ }
+
+ std::copy(out_tmp.data(), out_pos, out);
+
+ return true;
+}
+
+// Copied from system/security/keystore/keymaster_enforcement.cpp.
+
+class EvpMdCtx {
+ public:
+ EvpMdCtx() { EVP_MD_CTX_init(&ctx_); }
+ ~EvpMdCtx() { EVP_MD_CTX_cleanup(&ctx_); }
+
+ EVP_MD_CTX* get() { return &ctx_; }
+
+ private:
+ EVP_MD_CTX ctx_;
+};
+
+bool CreateKeyId(const uint8_t* key_blob, size_t len, km_id_t* out_id) {
+ EvpMdCtx ctx;
+
+ uint8_t hash[EVP_MAX_MD_SIZE];
+ unsigned int hash_len;
+ if (EVP_DigestInit_ex(ctx.get(), EVP_sha256(), nullptr /* ENGINE */) &&
+ EVP_DigestUpdate(ctx.get(), key_blob, len) &&
+ EVP_DigestFinal_ex(ctx.get(), hash, &hash_len)) {
+ assert(hash_len >= sizeof(*out_id));
+ memcpy(out_id, hash, sizeof(*out_id));
+ return true;
+ }
+
+ return false;
+}
+
+// Copied from system/security/keystore/user_state.h
+
+static constexpr size_t SALT_SIZE = 16;
+
+// Copied from system/security/keystore/user_state.cpp.
+
+void generateKeyFromPassword(uint8_t* key, size_t key_len, const char* pw, size_t pw_len,
+ const uint8_t* salt) {
+ size_t saltSize;
+ if (salt != nullptr) {
+ saltSize = SALT_SIZE;
+ } else {
+ // Pre-gingerbread used this hardwired salt, readMasterKey will rewrite these when found
+ salt = reinterpret_cast<const uint8_t*>("keystore");
+ // sizeof = 9, not strlen = 8
+ saltSize = sizeof("keystore");
+ }
+
+ const EVP_MD* digest = EVP_sha256();
+
+ // SHA1 was used prior to increasing the key size
+ if (key_len == kAes128KeySizeBytes) {
+ digest = EVP_sha1();
+ }
+
+ PKCS5_PBKDF2_HMAC(pw, pw_len, salt, saltSize, 8192, digest, key_len, key);
+}
diff --git a/keystore2/src/crypto/crypto.hpp b/keystore2/src/crypto/crypto.hpp
new file mode 100644
index 0000000..2e597f1
--- /dev/null
+++ b/keystore2/src/crypto/crypto.hpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CRYPTO_H__
+#define __CRYPTO_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+extern "C" {
+ bool randomBytes(uint8_t* out, size_t len);
+ bool AES_gcm_encrypt(const uint8_t* in, uint8_t* out, size_t len,
+ const uint8_t* key, size_t key_size, const uint8_t* iv, uint8_t* tag);
+ bool AES_gcm_decrypt(const uint8_t* in, uint8_t* out, size_t len,
+ const uint8_t* key, size_t key_size, const uint8_t* iv,
+ const uint8_t* tag);
+
+ // Copied from system/security/keystore/keymaster_enforcement.h.
+ typedef uint64_t km_id_t;
+
+ bool CreateKeyId(const uint8_t* key_blob, size_t len, km_id_t* out_id);
+
+ void generateKeyFromPassword(uint8_t* key, size_t key_len, const char* pw,
+ size_t pw_len, const uint8_t* salt);
+}
+
+#endif // __CRYPTO_H__
diff --git a/keystore2/src/crypto/error.rs b/keystore2/src/crypto/error.rs
new file mode 100644
index 0000000..2eb97b9
--- /dev/null
+++ b/keystore2/src/crypto/error.rs
@@ -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.
+
+//! This module implements Error for the keystore2_crypto library.
+
+/// Crypto specific error codes.
+#[derive(Debug, thiserror::Error, Eq, PartialEq)]
+pub enum Error {
+ /// This is returned if the C/C++ implementation of AES_gcm_decrypt returned false.
+ #[error("Failed to decrypt.")]
+ DecryptionFailed,
+
+ /// This is returned if the C/C++ implementation of AES_gcm_encrypt returned false.
+ #[error("Failed to encrypt.")]
+ EncryptionFailed,
+
+ /// The initialization vector has the wrong length.
+ #[error("Invalid IV length.")]
+ InvalidIvLength,
+
+ /// The aead tag has the wrong length.
+ #[error("Invalid AEAD tag length.")]
+ InvalidAeadTagLength,
+
+ /// The key has the wrong length.
+ #[error("Invalid key length.")]
+ InvalidKeyLength,
+
+ /// Invalid data length.
+ #[error("Invalid data length.")]
+ InvalidDataLength,
+
+ /// Invalid salt length.
+ #[error("Invalid salt length.")]
+ InvalidSaltLength,
+
+ /// Random number generation failed.
+ #[error("Random number generation failed.")]
+ RandomNumberGenerationFailed,
+
+ /// ZVec construction failed.
+ #[error(transparent)]
+ LayoutError(#[from] std::alloc::LayoutErr),
+
+ /// Nix error.
+ #[error(transparent)]
+ NixError(#[from] nix::Error),
+}
diff --git a/keystore2/src/crypto/include/certificate_utils.h b/keystore2/src/crypto/include/certificate_utils.h
new file mode 100644
index 0000000..9d41eb8
--- /dev/null
+++ b/keystore2/src/crypto/include/certificate_utils.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <stdint.h>
+
+#include <memory>
+#include <optional>
+#include <variant>
+
+namespace keystore {
+// We use boringssl error codes. Error codes that we add are folded into LIB_USER.
+// The CertificateUtilsInternallErrorCodes enum should not be used by callers, instead use the
+// BoringSslError constant definitions below for error codes.
+using BoringSslError = unsigned long;
+
+#define DEFINE_OPENSSL_OBJECT_POINTER(name) using name##_Ptr = bssl::UniquePtr<name>
+
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_BIT_STRING);
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_STRING);
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_INTEGER);
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_OCTET_STRING);
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_TIME);
+DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY);
+DEFINE_OPENSSL_OBJECT_POINTER(X509);
+DEFINE_OPENSSL_OBJECT_POINTER(X509_EXTENSION);
+DEFINE_OPENSSL_OBJECT_POINTER(X509_NAME);
+DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY_CTX);
+
+class CertUtilsError {
+ public:
+ enum Error {
+ Ok = 0,
+ BoringSsl,
+ Encoding,
+ MemoryAllocation,
+ InvalidArgument,
+ UnexpectedNullPointer,
+ };
+
+ private:
+ Error e_;
+
+ public:
+ constexpr CertUtilsError(Error e) : e_(e) {}
+ explicit constexpr operator bool() const { return e_ != Ok; }
+};
+
+struct KeyUsageExtension {
+ bool isSigningKey;
+ bool isEncryptionKey;
+ bool isCertificationKey;
+};
+
+struct BasicConstraintsExtension {
+ bool isCa;
+ std::optional<int> pathLength;
+};
+
+/**
+ * This function allocates and prepares an X509 certificate structure with all of the information
+ * given. Next steps would be to set an Issuer with `setIssuer` and sign it with either
+ * `signCert` or `signCertWith`.
+ * @param evp_pkey The public key that the certificate is issued for.
+ * @param serial The certificate serial number.
+ * @param subject The subject common name.
+ * @param activeDateTimeMilliSeconds The not before date in epoch milliseconds.
+ * @param usageExpireDateTimeMilliSeconds The not after date in epoch milliseconds.
+ * @param addSubjectKeyIdEx If true, adds the subject key id extension.
+ * @param keyUsageEx If given adds, the key usage extension with the given flags.
+ * @param basicConstraints If given, adds the basic constraints extension with the given data.
+ * @return CertUtilsError::Ok on success.
+ */
+std::variant<CertUtilsError, X509_Ptr>
+makeCert(const EVP_PKEY* evp_pkey, //
+ const uint32_t serial, //
+ const char subject[], //
+ const uint64_t activeDateTimeMilliSeconds, //
+ const uint64_t usageExpireDateTimeMilliSeconds, //
+ bool addSubjectKeyIdEx, //
+ std::optional<KeyUsageExtension> keyUsageEx, //
+ std::optional<BasicConstraintsExtension> basicConstraints); //
+
+/**
+ * Takes the subject name from `signingCert` and sets it as issuer name in `cert`.
+ * if `addAuthKeyExt` is true it also generates the digest of the signing certificates's public key
+ * and sets it as authority key id extension in `cert`.
+ * For self signed certificates pass the same pointer to both `cert` and `signingCert`.
+ *
+ * @param cert
+ * @param signingCert
+ * @param addAuthKeyExt
+ * @return CertUtilsError::Ok on success.
+ */
+CertUtilsError setIssuer(X509* cert, const X509* signingCert, bool addAuthKeyExt);
+
+/**
+ * Takes a certificate, and private signing_key.
+ * Signs the certificate with the latter.
+ */
+CertUtilsError signCert(X509* certificate, EVP_PKEY* signing_key);
+
+enum class Digest {
+ SHA1,
+ SHA224,
+ SHA256,
+ SHA384,
+ SHA512,
+};
+
+enum class Algo {
+ ECDSA,
+ RSA,
+};
+
+enum class Padding {
+ Ignored,
+ PKCS1_5,
+ PSS,
+};
+
+/**
+ * Sets the signature specifier of the certificate and the signature according to the parameters
+ * c. Then it signs the certificate with the `sign` callback.
+ * IMPORTANT: The parameters `algo`, `padding`, and `digest` do not control the actual signing
+ * algorithm. The caller is responsible to provide a callback that actually performs the signature
+ * as described by this triplet.
+ * The `padding` argument is ignored if `algo` is Algo::EC.
+ * The `digest` field controls the message digest used, and, in case of RSA with PSS padding,
+ * also the MGF1 digest.
+ *
+ * @param certificate X509 certificate structure to be signed.
+ * @param sign Callback function used to digest and sign the DER encoded to-be-signed certificate.
+ * @param algo Algorithm specifier used to encode the signing algorithm id of the X509 certificate.
+ * @param padding Padding specifier used to encode the signing algorithm id of the X509 certificate.
+ * @param digest Digest specifier used to encode the signing algorithm id of the X509 certificate.
+ * @return CertUtilsError::Ok on success.
+ */
+CertUtilsError signCertWith(X509* certificate,
+ std::function<std::vector<uint8_t>(const uint8_t*, size_t)> sign,
+ Algo algo, Padding padding, Digest digest);
+
+/**
+ * Generates the DER representation of the given signed X509 certificate structure.
+ * @param certificate
+ * @return std::vector<uint8_t> with the DER encoded certificate on success. An error code
+ * otherwise.
+ */
+std::variant<CertUtilsError, std::vector<uint8_t>> encodeCert(X509* certificate);
+
+} // namespace keystore
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
new file mode 100644
index 0000000..338bdb9
--- /dev/null
+++ b/keystore2/src/crypto/lib.rs
@@ -0,0 +1,252 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This module implements safe wrappers for some crypto operations required by
+//! Keystore 2.0.
+
+mod error;
+mod zvec;
+pub use error::Error;
+use keystore2_crypto_bindgen::{
+ generateKeyFromPassword, randomBytes, size_t, AES_gcm_decrypt, AES_gcm_encrypt,
+};
+pub use zvec::ZVec;
+
+/// Length of the expected initialization vector.
+pub const IV_LENGTH: usize = 16;
+/// Length of the expected AEAD TAG.
+pub const TAG_LENGTH: usize = 16;
+/// Length of an AES 256 key in bytes.
+pub const AES_256_KEY_LENGTH: usize = 32;
+/// Length of an AES 128 key in bytes.
+pub const AES_128_KEY_LENGTH: usize = 16;
+/// Length of the expected salt for key from password generation.
+pub const SALT_LENGTH: usize = 16;
+
+// This is the number of bytes of the GCM IV that is expected to be initialized
+// with random bytes.
+const GCM_IV_LENGTH: usize = 12;
+
+/// Generate an AES256 key, essentially 32 random bytes from the underlying
+/// boringssl library discretely stuffed into a ZVec.
+pub fn generate_aes256_key() -> Result<ZVec, Error> {
+ // Safety: key has the same length as the requested number of random bytes.
+ let mut key = ZVec::new(AES_256_KEY_LENGTH)?;
+ if unsafe { randomBytes(key.as_mut_ptr(), AES_256_KEY_LENGTH as size_t) } {
+ Ok(key)
+ } else {
+ Err(Error::RandomNumberGenerationFailed)
+ }
+}
+
+/// Generate a salt.
+pub fn generate_salt() -> Result<Vec<u8>, Error> {
+ // Safety: salt has the same length as the requested number of random bytes.
+ let mut salt = vec![0; SALT_LENGTH];
+ if unsafe { randomBytes(salt.as_mut_ptr(), SALT_LENGTH as size_t) } {
+ Ok(salt)
+ } else {
+ Err(Error::RandomNumberGenerationFailed)
+ }
+}
+
+/// Uses AES GCM to decipher a message given an initialization vector, aead tag, and key.
+/// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based
+/// on the key length.
+/// This function returns the plaintext message in a ZVec because it is assumed that
+/// it contains sensitive information that should be zeroed from memory before its buffer is
+/// freed. Input key is taken as a slice for flexibility, but it is recommended that it is held
+/// in a ZVec as well.
+pub fn aes_gcm_decrypt(data: &[u8], iv: &[u8], tag: &[u8], key: &[u8]) -> Result<ZVec, Error> {
+ if iv.len() != IV_LENGTH {
+ return Err(Error::InvalidIvLength);
+ }
+
+ if tag.len() != TAG_LENGTH {
+ return Err(Error::InvalidAeadTagLength);
+ }
+
+ match key.len() {
+ AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
+ _ => return Err(Error::InvalidKeyLength),
+ }
+
+ let mut result = ZVec::new(data.len())?;
+
+ // Safety: The first two arguments must point to buffers with a size given by the third
+ // argument. The key must have a size of 16 or 32 bytes which we check above.
+ // The iv and tag arguments must be 16 bytes, which we also check above.
+ match unsafe {
+ AES_gcm_decrypt(
+ data.as_ptr(),
+ result.as_mut_ptr(),
+ data.len() as size_t,
+ key.as_ptr(),
+ key.len() as size_t,
+ iv.as_ptr(),
+ tag.as_ptr(),
+ )
+ } {
+ true => Ok(result),
+ false => Err(Error::DecryptionFailed),
+ }
+}
+
+/// Uses AES GCM to encrypt a message given a key.
+/// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based on
+/// the key length. The function generates an initialization vector. The return value is a tuple
+/// of `(ciphertext, iv, tag)`.
+pub fn aes_gcm_encrypt(data: &[u8], key: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>), Error> {
+ let mut iv = vec![0; IV_LENGTH];
+ // Safety: iv is longer than GCM_IV_LENGTH, which is 12 while IV_LENGTH is 16.
+ // The iv needs to be 16 bytes long, but the last 4 bytes remain zeroed.
+ if !unsafe { randomBytes(iv.as_mut_ptr(), GCM_IV_LENGTH as size_t) } {
+ return Err(Error::RandomNumberGenerationFailed);
+ }
+
+ match key.len() {
+ AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
+ _ => return Err(Error::InvalidKeyLength),
+ }
+
+ let mut result: Vec<u8> = vec![0; data.len()];
+ let mut tag: Vec<u8> = vec![0; TAG_LENGTH];
+ match unsafe {
+ AES_gcm_encrypt(
+ data.as_ptr(),
+ result.as_mut_ptr(),
+ data.len() as size_t,
+ key.as_ptr(),
+ key.len() as size_t,
+ iv.as_ptr(),
+ tag.as_mut_ptr(),
+ )
+ } {
+ true => Ok((result, iv, tag)),
+ false => Err(Error::EncryptionFailed),
+ }
+}
+
+/// Generates a key from the given password and salt.
+/// The salt must be exactly 16 bytes long.
+/// Two key sizes are accepted: 16 and 32 bytes.
+pub fn derive_key_from_password(
+ pw: &[u8],
+ salt: Option<&[u8]>,
+ key_length: usize,
+) -> Result<ZVec, Error> {
+ let salt: *const u8 = match salt {
+ Some(s) => {
+ if s.len() != SALT_LENGTH {
+ return Err(Error::InvalidSaltLength);
+ }
+ s.as_ptr()
+ }
+ None => std::ptr::null(),
+ };
+
+ match key_length {
+ AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
+ _ => return Err(Error::InvalidKeyLength),
+ }
+
+ let mut result = ZVec::new(key_length)?;
+
+ unsafe {
+ generateKeyFromPassword(
+ result.as_mut_ptr(),
+ result.len() as size_t,
+ pw.as_ptr() as *const std::os::raw::c_char,
+ pw.len() as size_t,
+ salt,
+ )
+ };
+
+ Ok(result)
+}
+
+#[cfg(test)]
+mod tests {
+
+ use super::*;
+ use keystore2_crypto_bindgen::{
+ generateKeyFromPassword, AES_gcm_decrypt, AES_gcm_encrypt, CreateKeyId,
+ };
+
+ #[test]
+ fn test_wrapper_roundtrip() {
+ let key = generate_aes256_key().unwrap();
+ let message = b"totally awesome message";
+ let (cipher_text, iv, tag) = aes_gcm_encrypt(message, &key).unwrap();
+ let message2 = aes_gcm_decrypt(&cipher_text, &iv, &tag, &key).unwrap();
+ assert_eq!(message[..], message2[..])
+ }
+
+ #[test]
+ fn test_encrypt_decrypt() {
+ let input = vec![0; 16];
+ let mut out = vec![0; 16];
+ let mut out2 = vec![0; 16];
+ let key = vec![0; 16];
+ let iv = vec![0; 12];
+ let mut tag = vec![0; 16];
+ unsafe {
+ let res = AES_gcm_encrypt(
+ input.as_ptr(),
+ out.as_mut_ptr(),
+ 16,
+ key.as_ptr(),
+ 16,
+ iv.as_ptr(),
+ tag.as_mut_ptr(),
+ );
+ assert!(res);
+ assert_ne!(out, input);
+ assert_ne!(tag, input);
+ let res = AES_gcm_decrypt(
+ out.as_ptr(),
+ out2.as_mut_ptr(),
+ 16,
+ key.as_ptr(),
+ 16,
+ iv.as_ptr(),
+ tag.as_ptr(),
+ );
+ assert!(res);
+ assert_eq!(out2, input);
+ }
+ }
+
+ #[test]
+ fn test_create_key_id() {
+ let blob = vec![0; 16];
+ let mut out: u64 = 0;
+ unsafe {
+ let res = CreateKeyId(blob.as_ptr(), 16, &mut out);
+ assert!(res);
+ assert_ne!(out, 0);
+ }
+ }
+
+ #[test]
+ fn test_generate_key_from_password() {
+ let mut key = vec![0; 16];
+ let pw = vec![0; 16];
+ let mut salt = vec![0; 16];
+ unsafe {
+ generateKeyFromPassword(key.as_mut_ptr(), 16, pw.as_ptr(), 16, salt.as_mut_ptr());
+ }
+ assert_ne!(key, vec![0; 16]);
+ }
+}
diff --git a/keystore2/src/crypto/tests/certificate_utils_test.cpp b/keystore2/src/crypto/tests/certificate_utils_test.cpp
new file mode 100644
index 0000000..2df9ce5
--- /dev/null
+++ b/keystore2/src/crypto/tests/certificate_utils_test.cpp
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "certificate_utils.h"
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <variant>
+
+#include "test_keys.h"
+
+using namespace keystore;
+
+// I leave these here in case they are needed for debugging.
+namespace debug_utils {
+
+void log_ssl_error() {
+ unsigned long error = ERR_peek_last_error();
+
+ char buf[128];
+ ERR_error_string_n(error, buf, sizeof(buf));
+ std::cout << "BoringSslError: " << buf << std::endl;
+}
+
+std::string hexdump(const std::vector<uint8_t>& data) {
+ std::stringstream s;
+ size_t column_count = 0;
+ for (auto& c : data) {
+ s << std::setw(2) << std::setfill('0') << std::hex << (unsigned int)c;
+ if (++column_count % 40 == 0) s << "\n";
+ }
+ return s.str();
+}
+
+} // namespace debug_utils
+
+constexpr uint64_t kValidity = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
+
+const EVP_MD* getMD(Digest digest) {
+ switch (digest) {
+ case Digest::SHA1:
+ return EVP_sha1();
+ case Digest::SHA224:
+ return EVP_sha224();
+ case Digest::SHA256:
+ return EVP_sha256();
+ case Digest::SHA384:
+ return EVP_sha384();
+ case Digest::SHA512:
+ return EVP_sha512();
+ }
+}
+
+std::array<Digest, 5> digests = {
+ Digest::SHA1, Digest::SHA224, Digest::SHA256, Digest::SHA384, Digest::SHA512,
+};
+
+static const char* toString(Digest d) {
+ switch (d) {
+ case Digest::SHA1:
+ return "SHA1";
+ case Digest::SHA224:
+ return "SHA224";
+ case Digest::SHA256:
+ return "SHA256";
+ case Digest::SHA384:
+ return "SHA384";
+ case Digest::SHA512:
+ return "SHA512";
+ }
+}
+
+std::array<Padding, 2> rsa_paddings = {
+ Padding::PSS,
+ Padding::PKCS1_5,
+};
+
+enum class EcCurve {
+ P224,
+ P256,
+ P384,
+ P521,
+};
+
+std::array<int, 4> ec_curves = {
+ NID_secp224r1,
+ NID_X9_62_prime256v1,
+ NID_secp384r1,
+ NID_secp521r1,
+};
+
+static const char* curveNidToString(int nid) {
+ switch (nid) {
+ case NID_secp224r1:
+ return "P224";
+ case NID_X9_62_prime256v1:
+ return "P256";
+ case NID_secp384r1:
+ return "P384";
+ case NID_secp521r1:
+ return "P521";
+ default:
+ return "Unknown";
+ }
+}
+
+std::array<long, 2> rsa_key_sizes = {
+ 2048,
+ 4096,
+};
+
+using EcParam = std::tuple<int /* EC curve NID */, Digest>;
+
+class CertificateUtilsWithEcCurve : public testing::TestWithParam<EcParam> {};
+
+static std::string paramToStringEc(testing::TestParamInfo<EcParam> param) {
+ std::stringstream s;
+ auto [curve_nid, digest] = param.param;
+ s << param.index << "_" << curveNidToString(curve_nid) << "_" << toString(digest);
+ return s.str();
+}
+
+INSTANTIATE_TEST_SUITE_P(CertSigningWithCallbackEC, CertificateUtilsWithEcCurve,
+ testing::Combine(testing::ValuesIn(ec_curves), testing::ValuesIn(digests)),
+ paramToStringEc);
+
+TEST_P(CertificateUtilsWithEcCurve, CertSigningWithCallbackEC) {
+ // Structured decomposition (e.g.: auto [a, b, c] = ...) does not work here because
+ // names bound this way cannot be captured in lambda expressions so we use std::tie instead.
+ int curve_nid;
+ Digest digest;
+ std::tie(curve_nid, digest) = GetParam();
+ EVP_PKEY_CTX_Ptr pkey_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL));
+ ASSERT_TRUE((bool)pkey_ctx);
+ ASSERT_TRUE(EVP_PKEY_keygen_init(pkey_ctx.get()));
+ ASSERT_TRUE(EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pkey_ctx.get(), curve_nid));
+
+ EVP_PKEY* pkey_ptr = nullptr;
+ ASSERT_TRUE(EVP_PKEY_keygen(pkey_ctx.get(), &pkey_ptr));
+ EVP_PKEY_Ptr pkey(pkey_ptr);
+ ASSERT_TRUE(pkey);
+
+ uint64_t now_ms = (uint64_t)time(nullptr) * 1000;
+
+ BasicConstraintsExtension bcons{
+ .isCa = true,
+ .pathLength = {},
+ };
+
+ KeyUsageExtension keyUsage{
+ .isSigningKey = true,
+ .isEncryptionKey = false,
+ .isCertificationKey = true,
+ };
+
+ auto certV = makeCert(pkey.get(), 1, "Me", now_ms - kValidity, now_ms + kValidity,
+ true /* subject key id extension */, keyUsage, bcons);
+ ASSERT_TRUE(std::holds_alternative<X509_Ptr>(certV));
+ auto& cert = std::get<X509_Ptr>(certV);
+ ASSERT_TRUE(!setIssuer(cert.get(), cert.get(), true));
+
+ ASSERT_TRUE(!signCertWith(
+ cert.get(),
+ [&](const uint8_t* data, size_t len) {
+ bssl::ScopedEVP_MD_CTX sign_ctx;
+ EXPECT_TRUE(
+ EVP_DigestSignInit(sign_ctx.get(), nullptr, getMD(digest), nullptr, pkey.get()));
+
+ std::vector<uint8_t> sig_buf(512);
+ size_t sig_len = 512;
+ EVP_DigestSign(sign_ctx.get(), sig_buf.data(), &sig_len, data, len);
+ sig_buf.resize(sig_len);
+ return sig_buf;
+ },
+ Algo::ECDSA, Padding::Ignored, digest));
+
+ auto encCertV = encodeCert(cert.get());
+ ASSERT_TRUE(std::holds_alternative<std::vector<uint8_t>>(encCertV));
+
+ auto& encCert = std::get<1>(encCertV);
+ // Uncomment the next line to dump the DER encoded signed certificate as hex string.
+ // You can pipe this dump into "xxd -r -p | openssl x509 -inform der -text -noout"
+ // to inspect the certificate.
+ // std::cout << "DER encoded cert:\n" << debug_utils::hexdump(encCert) << std::endl;
+
+ const uint8_t* p = encCert.data();
+ X509_Ptr decoded_cert(d2i_X509(nullptr, &p, (long)encCert.size()));
+ EVP_PKEY_Ptr decoded_pkey(X509_get_pubkey(decoded_cert.get()));
+ ASSERT_TRUE(X509_verify(decoded_cert.get(), decoded_pkey.get()));
+}
+
+using RsaParams = std::tuple<long /* key size */, Padding, Digest>;
+
+class CertificateUtilsWithRsa : public testing::TestWithParam<RsaParams> {};
+
+static std::string paramsToStringRsa(testing::TestParamInfo<RsaParams> param) {
+ std::stringstream s;
+ auto [key_size, padding, digest] = param.param;
+ s << param.index << "_" << key_size << "_";
+ switch (padding) {
+ case Padding::PSS:
+ s << "PSS";
+ break;
+ case Padding::PKCS1_5:
+ s << "PKCS1_5";
+ break;
+ case Padding::Ignored:
+ s << "Ignored";
+ }
+ s << "_" << toString(digest);
+ return s.str();
+}
+
+INSTANTIATE_TEST_SUITE_P(CertSigningWithCallbackRsa, CertificateUtilsWithRsa,
+ testing::Combine(testing::ValuesIn(rsa_key_sizes),
+ testing::ValuesIn(rsa_paddings),
+ testing::ValuesIn(digests)),
+ paramsToStringRsa);
+
+TEST_P(CertificateUtilsWithRsa, CertSigningWithCallbackRsa) {
+ // Structured decomposition (e.g.: auto [a, b, c] = ...) does not work here because
+ // names bound this way cannot be captured in lambda expressions so we use std::tie instead.
+ long key_size;
+ Padding padding;
+ Digest digest;
+ std::tie(key_size, padding, digest) = GetParam();
+
+ CBS cbs;
+ switch (key_size) {
+ case 2048:
+ CBS_init(&cbs, rsa_key_2k, rsa_key_2k_len);
+ break;
+ case 4096:
+ CBS_init(&cbs, rsa_key_4k, rsa_key_4k_len);
+ break;
+ default:
+ FAIL();
+ }
+ EVP_PKEY_Ptr pkey(EVP_parse_private_key(&cbs));
+ ASSERT_TRUE(pkey);
+
+ uint64_t now_ms = (uint64_t)time(nullptr) * 1000;
+
+ BasicConstraintsExtension bcons{
+ .isCa = true,
+ .pathLength = 0,
+ };
+
+ KeyUsageExtension keyUsage{
+ .isSigningKey = true,
+ .isEncryptionKey = false,
+ .isCertificationKey = true,
+ };
+
+ auto certV = makeCert(pkey.get(), 1, "Me", now_ms - kValidity, now_ms + kValidity,
+ true /* subject key id extension */, keyUsage, bcons);
+ ASSERT_TRUE(std::holds_alternative<X509_Ptr>(certV));
+ auto& cert = std::get<X509_Ptr>(certV);
+ ASSERT_TRUE(!setIssuer(cert.get(), cert.get(), true));
+
+ ASSERT_TRUE(!signCertWith(
+ cert.get(),
+ [&](const uint8_t* data, size_t len) {
+ bssl::ScopedEVP_MD_CTX sign_ctx;
+ EVP_PKEY_CTX* pkey_sign_ctx_ptr;
+ EXPECT_TRUE(EVP_DigestSignInit(sign_ctx.get(), &pkey_sign_ctx_ptr, getMD(digest),
+ nullptr, pkey.get()));
+
+ if (padding == Padding::PSS) {
+ EXPECT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pkey_sign_ctx_ptr, RSA_PKCS1_PSS_PADDING));
+ EXPECT_TRUE(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_sign_ctx_ptr, -1));
+ } else {
+ EXPECT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pkey_sign_ctx_ptr, RSA_PKCS1_PADDING));
+ }
+
+ std::vector<uint8_t> sig_buf(1024);
+ size_t sig_len = 1024;
+ EVP_DigestSign(sign_ctx.get(), sig_buf.data(), &sig_len, data, len);
+ sig_buf.resize(sig_len);
+ return sig_buf;
+ },
+ Algo::RSA, padding, digest));
+
+ auto encCertV = encodeCert(cert.get());
+ ASSERT_TRUE(std::holds_alternative<std::vector<uint8_t>>(encCertV));
+
+ auto& encCert = std::get<1>(encCertV);
+ // Uncomment the next line to dump the DER encoded signed certificate as hex string.
+ // You can pipe this dump into "xxd -r -p | openssl x509 -inform der -text -noout"
+ // to inspect the certificate.
+ // std::cout << "DER encoded cert:\n" << debug_utils::hexdump(encCert) << std::endl;
+
+ const uint8_t* p = encCert.data();
+ X509_Ptr decoded_cert(d2i_X509(nullptr, &p, (long)encCert.size()));
+ EVP_PKEY_Ptr decoded_pkey(X509_get_pubkey(decoded_cert.get()));
+ ASSERT_TRUE(X509_verify(decoded_cert.get(), decoded_pkey.get()));
+}
diff --git a/keystore2/src/crypto/tests/gtest_main.cpp b/keystore2/src/crypto/tests/gtest_main.cpp
new file mode 100644
index 0000000..149cbbc
--- /dev/null
+++ b/keystore2/src/crypto/tests/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/crypto/tests/test_keys.h b/keystore2/src/crypto/tests/test_keys.h
new file mode 100644
index 0000000..d3b1175
--- /dev/null
+++ b/keystore2/src/crypto/tests/test_keys.h
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+constexpr const unsigned char rsa_key_4k[] = {
+ 0x30, 0x82, 0x09, 0x41, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x09, 0x2b, 0x30, 0x82, 0x09, 0x27, 0x02, 0x01,
+ 0x00, 0x02, 0x82, 0x02, 0x01, 0x00, 0xac, 0x07, 0x4e, 0xc6, 0x20, 0xb9, 0x78, 0x03, 0xd0, 0x53,
+ 0x22, 0xa4, 0xef, 0x6d, 0xd1, 0x7a, 0x7e, 0x5e, 0x5e, 0x1d, 0xe7, 0x9b, 0x30, 0x31, 0x99, 0xe8,
+ 0x1c, 0xdf, 0x32, 0x0c, 0xdb, 0xdb, 0xc2, 0x4c, 0x22, 0x10, 0x71, 0xd0, 0x76, 0x83, 0xb0, 0x50,
+ 0x66, 0xa4, 0x43, 0x75, 0xe8, 0x46, 0xde, 0x58, 0x9e, 0x31, 0x82, 0x81, 0x8c, 0x36, 0x20, 0xbb,
+ 0x42, 0xcd, 0xb9, 0x21, 0x29, 0x67, 0x39, 0x4d, 0x2a, 0x1c, 0xc2, 0x89, 0x48, 0x23, 0x5f, 0xdd,
+ 0x60, 0xc4, 0x04, 0x42, 0xbe, 0x6f, 0x01, 0xa5, 0xf8, 0xac, 0xba, 0x27, 0xd1, 0x87, 0x08, 0x6b,
+ 0xe6, 0x1b, 0xfc, 0xba, 0xff, 0x7e, 0xc9, 0xaf, 0x76, 0x28, 0xe8, 0x93, 0x2c, 0xbf, 0x6f, 0xed,
+ 0xf3, 0x6f, 0x21, 0xc1, 0xe7, 0x56, 0xf5, 0x15, 0x56, 0xa8, 0xa1, 0xfd, 0xb7, 0xb3, 0x8c, 0x3d,
+ 0x1f, 0xf3, 0x80, 0xea, 0x79, 0xff, 0x0c, 0xc6, 0xb6, 0x59, 0xa0, 0x3f, 0x13, 0xb0, 0x4f, 0xb3,
+ 0x1c, 0xe1, 0x2a, 0xa5, 0x45, 0x02, 0x51, 0xa3, 0x74, 0x15, 0xee, 0xf0, 0xca, 0xba, 0x7d, 0xca,
+ 0xc6, 0x87, 0xd6, 0x12, 0x9b, 0xbb, 0x4b, 0x86, 0xc7, 0xcb, 0x88, 0xc6, 0x10, 0xee, 0x34, 0x51,
+ 0x4b, 0xe9, 0x9d, 0xbb, 0x59, 0x03, 0xff, 0xbf, 0x98, 0x0a, 0xfd, 0xf2, 0xd0, 0x06, 0x0e, 0x51,
+ 0xd1, 0xbb, 0x8b, 0xbd, 0xca, 0x26, 0x4d, 0x05, 0x0e, 0xee, 0x82, 0x0f, 0xf6, 0x38, 0x46, 0x2a,
+ 0x7f, 0xfd, 0x37, 0xe6, 0xcf, 0xaa, 0x6a, 0x84, 0xbc, 0xa7, 0xd2, 0x06, 0x29, 0x49, 0x4c, 0xd2,
+ 0x28, 0xd4, 0x48, 0x09, 0xfc, 0x91, 0x1e, 0xb0, 0x06, 0xf9, 0x5b, 0xbe, 0xd9, 0xb8, 0x01, 0xec,
+ 0x3a, 0xc3, 0xa2, 0x1d, 0x89, 0x0b, 0x22, 0xf8, 0xf0, 0x8e, 0x95, 0xe0, 0x3d, 0x5b, 0x80, 0x38,
+ 0x21, 0x80, 0x10, 0x04, 0x54, 0xaa, 0xa5, 0x81, 0x8b, 0x47, 0x73, 0x54, 0xcb, 0x06, 0xa9, 0x01,
+ 0xf5, 0x57, 0x72, 0xdd, 0x5f, 0x9f, 0xe1, 0x14, 0x1f, 0xc1, 0x3b, 0xda, 0xd1, 0xad, 0xc7, 0x9c,
+ 0x70, 0x18, 0xfd, 0x07, 0x7e, 0x37, 0x0d, 0xb4, 0x30, 0xff, 0x6d, 0x4b, 0x99, 0xdb, 0x3f, 0x23,
+ 0xd7, 0x15, 0x3d, 0x4c, 0x14, 0x63, 0x98, 0xf5, 0x03, 0x63, 0x4e, 0x63, 0x15, 0x6a, 0xce, 0x7f,
+ 0xfc, 0x4f, 0xa0, 0x72, 0x49, 0x3f, 0xd9, 0xa2, 0xde, 0x65, 0xac, 0x35, 0x88, 0x03, 0x55, 0xf5,
+ 0x27, 0x57, 0x4e, 0x4b, 0xef, 0x44, 0x1d, 0xd1, 0xe9, 0xaf, 0x89, 0x7e, 0x19, 0x2a, 0xc4, 0x48,
+ 0x80, 0x8d, 0x22, 0xd6, 0x4f, 0x79, 0x39, 0x15, 0x00, 0xc0, 0xa8, 0x93, 0x5a, 0x05, 0xce, 0xc8,
+ 0x3f, 0x27, 0x40, 0x09, 0x1f, 0x2a, 0x0c, 0x32, 0x6a, 0x2d, 0xef, 0x51, 0x79, 0x51, 0xf1, 0xfe,
+ 0xdb, 0x2c, 0xb5, 0x2f, 0x4d, 0x94, 0x1d, 0xd2, 0xb8, 0xed, 0x53, 0x58, 0x3e, 0xb4, 0x65, 0xfb,
+ 0xca, 0x9f, 0xfe, 0x5c, 0x68, 0x20, 0x58, 0xd9, 0xfd, 0xc7, 0x4d, 0x0f, 0x03, 0x37, 0x61, 0xaf,
+ 0x2b, 0x90, 0xe1, 0xfc, 0x15, 0xa6, 0xf1, 0xac, 0x5d, 0xad, 0xb4, 0xbe, 0xf6, 0xb4, 0x7b, 0x42,
+ 0xe6, 0x17, 0xe9, 0x79, 0xde, 0x65, 0x50, 0x70, 0x6b, 0x46, 0x24, 0x73, 0x4d, 0xa3, 0x54, 0x3a,
+ 0x21, 0xc2, 0x1c, 0x80, 0x1d, 0xf6, 0xd4, 0x0e, 0x7a, 0xa5, 0x4e, 0x97, 0xc1, 0x15, 0x37, 0x02,
+ 0xea, 0x55, 0xff, 0xad, 0x59, 0x95, 0xa0, 0x24, 0xd0, 0x58, 0x59, 0xf7, 0x9a, 0x4e, 0x2b, 0x78,
+ 0x89, 0x91, 0x9c, 0x67, 0x14, 0xe4, 0x24, 0xc3, 0xb8, 0xd3, 0x7d, 0x06, 0xaf, 0x46, 0x8f, 0xe7,
+ 0x07, 0x88, 0xf0, 0x4c, 0xf4, 0xb8, 0x3a, 0x2a, 0x9c, 0x89, 0x2f, 0x7c, 0x32, 0xfd, 0x52, 0x55,
+ 0xd8, 0xe1, 0x58, 0x46, 0xe5, 0xc3, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x02, 0x00, 0x08,
+ 0x79, 0xcf, 0xb2, 0xc9, 0x57, 0xfa, 0x06, 0xce, 0x13, 0xda, 0x88, 0x1f, 0xd7, 0xdc, 0x53, 0x59,
+ 0xb8, 0x92, 0x90, 0x8c, 0xa1, 0xc3, 0xcd, 0x1d, 0xd5, 0x26, 0xdf, 0x04, 0x5b, 0x47, 0xd5, 0xdb,
+ 0x0b, 0xdf, 0x3d, 0xca, 0x2d, 0xc8, 0x39, 0x12, 0xcd, 0xd3, 0x50, 0xd2, 0x96, 0x13, 0x9c, 0xb2,
+ 0x45, 0xd1, 0x7d, 0x84, 0xfd, 0x97, 0x07, 0xef, 0xb2, 0xea, 0x46, 0xb2, 0x91, 0x64, 0xb5, 0xd6,
+ 0x47, 0xec, 0x04, 0x40, 0xbd, 0x7c, 0xd5, 0x69, 0x5a, 0xc1, 0xf2, 0xc6, 0x76, 0xf7, 0x65, 0x06,
+ 0xc2, 0xc3, 0xae, 0xd6, 0xf9, 0x31, 0x44, 0xa2, 0xf0, 0x96, 0x04, 0xd8, 0xfd, 0xe9, 0xaa, 0xb8,
+ 0x8b, 0x31, 0x9a, 0x30, 0x63, 0x57, 0xf8, 0x12, 0xae, 0xb9, 0xa8, 0xc7, 0x14, 0x03, 0xae, 0xf0,
+ 0x22, 0x5e, 0x03, 0xae, 0xff, 0x8e, 0x36, 0x85, 0x79, 0x59, 0x82, 0xa8, 0xde, 0x64, 0xa6, 0x61,
+ 0x5d, 0xc5, 0x0c, 0x43, 0x6d, 0xf8, 0x2d, 0x5e, 0xaf, 0xe7, 0x83, 0x5c, 0x93, 0x8a, 0x03, 0xe4,
+ 0x3b, 0xd6, 0x73, 0x62, 0x33, 0x70, 0xf9, 0xa1, 0x4b, 0x05, 0x5f, 0x19, 0xf8, 0x0e, 0xbe, 0x3a,
+ 0xa9, 0x68, 0x5c, 0xa9, 0xdf, 0x80, 0x64, 0x0c, 0x25, 0xd9, 0x44, 0xa8, 0x65, 0xdb, 0xab, 0xeb,
+ 0xc4, 0xe7, 0xdb, 0xda, 0xc9, 0x44, 0xe0, 0x97, 0x82, 0x06, 0x80, 0x64, 0x11, 0x34, 0xcd, 0x90,
+ 0x4b, 0xe8, 0x81, 0x6d, 0xdd, 0x15, 0x77, 0x8e, 0x55, 0x77, 0xba, 0xe5, 0x2f, 0x35, 0x1a, 0x23,
+ 0x67, 0x68, 0xd8, 0x27, 0xeb, 0xef, 0xca, 0xd1, 0xc3, 0x25, 0x09, 0xd8, 0x86, 0xcd, 0x6f, 0xe5,
+ 0x00, 0x2d, 0x47, 0xc9, 0xf2, 0x6c, 0x4d, 0xb9, 0xa2, 0x86, 0xfe, 0xae, 0x95, 0x1f, 0xf4, 0x71,
+ 0x83, 0xac, 0x6e, 0x8a, 0x09, 0xe3, 0x5c, 0x07, 0xc4, 0x3c, 0x3a, 0x50, 0x0f, 0xb6, 0x90, 0x21,
+ 0x25, 0xdf, 0x2d, 0x61, 0x89, 0xb6, 0x3c, 0xb3, 0xc3, 0xc0, 0xd9, 0x2e, 0x73, 0x76, 0xf6, 0x46,
+ 0x46, 0xf8, 0x01, 0xbd, 0x0b, 0x94, 0x80, 0xf6, 0x94, 0x5b, 0x17, 0x9e, 0xbc, 0xdb, 0x9d, 0xfd,
+ 0xc2, 0x3e, 0x56, 0xa1, 0xa0, 0x1a, 0xed, 0x82, 0xcb, 0xb0, 0xb3, 0xd4, 0x58, 0xb4, 0x91, 0x8e,
+ 0xd9, 0x84, 0xb8, 0x94, 0xc2, 0x86, 0x42, 0xa6, 0x86, 0xf2, 0x9c, 0x1d, 0xc7, 0xc9, 0xed, 0x6d,
+ 0x0d, 0x8d, 0x98, 0x08, 0xf3, 0x52, 0xe7, 0x7f, 0xfe, 0xe0, 0x9f, 0xdf, 0x43, 0x8e, 0xbc, 0x3f,
+ 0x93, 0x41, 0x44, 0x7b, 0x26, 0x0e, 0x2e, 0xc1, 0xc2, 0x4e, 0xc7, 0xb4, 0x5c, 0xc5, 0x55, 0xae,
+ 0xbb, 0xb4, 0x2f, 0xd5, 0x1f, 0x34, 0xce, 0x94, 0x66, 0x12, 0x41, 0x8a, 0x15, 0x8b, 0xcb, 0xdd,
+ 0x00, 0xf9, 0xa0, 0x55, 0xa6, 0x8b, 0xe9, 0x13, 0x14, 0xc6, 0x0b, 0x98, 0x8f, 0xff, 0x2f, 0x9a,
+ 0xe8, 0x78, 0x68, 0x94, 0xeb, 0x53, 0xeb, 0x4b, 0xed, 0x5a, 0x6d, 0x1c, 0xe2, 0xe5, 0xc7, 0xde,
+ 0x13, 0xa9, 0xaf, 0xa3, 0xc7, 0x54, 0x26, 0xa0, 0x3f, 0x44, 0xdd, 0x9f, 0x14, 0xe2, 0xc4, 0x9a,
+ 0xf5, 0x30, 0xea, 0x2e, 0x75, 0xc6, 0xd3, 0xf8, 0x5e, 0x9b, 0x02, 0x0d, 0xc8, 0x81, 0x6c, 0x13,
+ 0x3b, 0x9c, 0x89, 0x54, 0x52, 0x41, 0x62, 0xad, 0xa0, 0x52, 0xaa, 0x2c, 0xc4, 0x2c, 0x63, 0x58,
+ 0xf6, 0xb1, 0xfa, 0xdc, 0x77, 0xed, 0x3e, 0x8c, 0x12, 0x94, 0x09, 0x57, 0x18, 0xb3, 0x04, 0x53,
+ 0x3c, 0xad, 0xa8, 0x45, 0x4b, 0x19, 0xa0, 0xcb, 0x8f, 0x6f, 0x5d, 0x2d, 0xcc, 0xe1, 0xfe, 0x4a,
+ 0xe6, 0x20, 0xed, 0x9a, 0x76, 0x4f, 0x17, 0xfd, 0xed, 0x1e, 0x6e, 0x41, 0x21, 0x43, 0xe4, 0xe9,
+ 0x47, 0x01, 0x1f, 0x76, 0x68, 0x4d, 0xbb, 0xd3, 0xba, 0x68, 0x34, 0x06, 0x6a, 0xf5, 0xe9, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xdb, 0xc5, 0x0d, 0x95, 0x88, 0x5f, 0x2c, 0xa9, 0x7c, 0xb7, 0x06, 0xd8,
+ 0x20, 0x60, 0x72, 0x80, 0xec, 0xae, 0xf4, 0xdc, 0x48, 0x5d, 0x9d, 0x6d, 0x2a, 0x54, 0x36, 0xe1,
+ 0x32, 0x25, 0x10, 0x4d, 0x56, 0x0f, 0x51, 0xe9, 0xbc, 0x95, 0x3f, 0x38, 0x43, 0xed, 0xc6, 0x6a,
+ 0x16, 0xb0, 0x06, 0x90, 0x85, 0x51, 0x07, 0xea, 0x7b, 0x1d, 0x64, 0x12, 0x39, 0x7e, 0x32, 0x92,
+ 0x3e, 0xcc, 0x97, 0x91, 0x02, 0x6c, 0xf8, 0x95, 0xc7, 0x39, 0xa1, 0x5c, 0xd3, 0x62, 0xcc, 0x29,
+ 0x83, 0x27, 0xe6, 0x36, 0xba, 0xf6, 0x62, 0x80, 0xf6, 0x15, 0xda, 0x0f, 0xc4, 0x47, 0x81, 0x44,
+ 0x96, 0xba, 0xec, 0x37, 0x8c, 0x95, 0xb2, 0x30, 0x12, 0xc0, 0x4d, 0x62, 0x88, 0x5e, 0x7d, 0x13,
+ 0x6d, 0x83, 0x14, 0x1e, 0xd7, 0xc2, 0xeb, 0x9d, 0xbd, 0xa4, 0x70, 0x78, 0xf3, 0xb1, 0x6b, 0x6c,
+ 0xe9, 0x37, 0x63, 0x47, 0x6f, 0xed, 0xea, 0xd6, 0x32, 0x80, 0x71, 0xa2, 0x7f, 0x38, 0x67, 0xb4,
+ 0x1f, 0xf0, 0x7f, 0xb6, 0xb7, 0x56, 0xe7, 0x24, 0xaa, 0x2f, 0xb9, 0x93, 0xb8, 0xab, 0x2a, 0xec,
+ 0xc4, 0x19, 0x9a, 0xea, 0x5d, 0x01, 0x1e, 0xdd, 0x5c, 0x05, 0x37, 0x12, 0x04, 0x32, 0xb2, 0x5d,
+ 0x16, 0xdc, 0x88, 0x0c, 0xfe, 0x1e, 0xf6, 0x9f, 0x63, 0xbf, 0xb2, 0x00, 0x6b, 0x99, 0xbe, 0x42,
+ 0x96, 0x41, 0xe0, 0x3d, 0xd1, 0x35, 0x6a, 0xf1, 0x81, 0x09, 0xd6, 0x4c, 0xdd, 0xc0, 0x20, 0x6e,
+ 0x0d, 0xcb, 0x30, 0x11, 0x73, 0x11, 0x82, 0x5e, 0x1b, 0x9a, 0xa0, 0x57, 0xcd, 0xd7, 0xab, 0xa2,
+ 0xe9, 0xed, 0xe7, 0x26, 0x8d, 0x30, 0x09, 0xe7, 0x5a, 0xa1, 0xd0, 0x62, 0xac, 0x7f, 0x15, 0xd1,
+ 0x9a, 0x0a, 0x97, 0x5d, 0x8a, 0xc0, 0x47, 0xa4, 0xa8, 0x8b, 0x26, 0x75, 0x35, 0x5d, 0xa9, 0xf1,
+ 0x1a, 0x61, 0xdc, 0xbf, 0x02, 0x82, 0x01, 0x01, 0x00, 0xc8, 0x63, 0x6e, 0x9c, 0x88, 0xcd, 0x33,
+ 0xef, 0x72, 0x80, 0x00, 0x6b, 0x8e, 0xb0, 0xd2, 0xa9, 0x5c, 0x78, 0xf7, 0x25, 0x8f, 0xba, 0x49,
+ 0x60, 0xa3, 0x33, 0xf4, 0x16, 0x1c, 0x81, 0xad, 0x82, 0x39, 0xa5, 0xa8, 0x12, 0xc2, 0x7e, 0x05,
+ 0x33, 0x9c, 0xd9, 0xa9, 0xa4, 0x02, 0xf8, 0x43, 0xdf, 0xa6, 0x39, 0xdb, 0xc0, 0x60, 0xdc, 0x53,
+ 0x76, 0x7c, 0xef, 0xf0, 0x58, 0x44, 0x3d, 0xc7, 0x77, 0xe6, 0x6f, 0x2a, 0xbd, 0x89, 0x8b, 0x40,
+ 0xe3, 0x02, 0x80, 0x8c, 0x46, 0x24, 0x4c, 0x63, 0xc4, 0x06, 0xb1, 0x48, 0x92, 0x0a, 0x86, 0x58,
+ 0xe3, 0x08, 0x2a, 0x2e, 0x4b, 0xdd, 0xa6, 0x64, 0x68, 0xe9, 0xac, 0x07, 0x2b, 0x3d, 0x2f, 0x42,
+ 0x68, 0x5a, 0x42, 0xdb, 0xb7, 0x5a, 0x67, 0xc4, 0x5f, 0x19, 0xba, 0xff, 0xed, 0x17, 0x0b, 0xf8,
+ 0x26, 0x36, 0xd7, 0x9d, 0x3a, 0x3d, 0x0e, 0x5f, 0x31, 0x29, 0x3f, 0xf4, 0x24, 0x2f, 0x0a, 0xa2,
+ 0x21, 0x0e, 0x6e, 0x86, 0xf5, 0x1e, 0xb7, 0xce, 0x81, 0x95, 0xd0, 0xcd, 0x97, 0xd1, 0x2c, 0xd4,
+ 0x9f, 0x95, 0xd6, 0x7c, 0x68, 0x80, 0x4f, 0xe6, 0x5c, 0xab, 0xfe, 0x6b, 0xf9, 0x8b, 0x12, 0x99,
+ 0xad, 0xc9, 0x64, 0x21, 0x99, 0xef, 0xfb, 0xd3, 0x16, 0x8b, 0x95, 0x93, 0xa7, 0xb7, 0x24, 0x36,
+ 0x00, 0xb2, 0xa2, 0xbe, 0x79, 0x4e, 0x66, 0xbd, 0xe1, 0x2d, 0xd0, 0xa9, 0x76, 0xe1, 0x13, 0x0d,
+ 0xb5, 0x4b, 0xc5, 0xe9, 0x63, 0x23, 0x45, 0xcf, 0x5f, 0x0d, 0x5c, 0xce, 0x93, 0x7f, 0xa9, 0x68,
+ 0xf6, 0xfc, 0x2c, 0x54, 0x40, 0x43, 0xca, 0x9d, 0xfd, 0x43, 0x81, 0x4d, 0xbe, 0x98, 0x87, 0xca,
+ 0x2c, 0x82, 0x32, 0xb7, 0xcf, 0xa1, 0xc5, 0xf8, 0x55, 0xea, 0x2b, 0x6d, 0xfc, 0xe7, 0x5d, 0xcf,
+ 0xde, 0xf8, 0x15, 0xc2, 0xc3, 0xa2, 0xe6, 0x83, 0xfd, 0x02, 0x82, 0x01, 0x00, 0x2c, 0x83, 0x3a,
+ 0xff, 0x20, 0x81, 0xf6, 0x6f, 0xd5, 0xbc, 0xd4, 0x7c, 0x0e, 0x02, 0xba, 0xee, 0x76, 0x01, 0xf1,
+ 0xc2, 0x74, 0x3d, 0xd1, 0xd6, 0xfc, 0x8d, 0xd6, 0x17, 0xc2, 0xaa, 0x53, 0x24, 0xf6, 0xdb, 0x5f,
+ 0x81, 0xf2, 0x1a, 0x60, 0x95, 0xaa, 0xdc, 0x8c, 0x25, 0x8c, 0xb6, 0xd6, 0x7d, 0x8b, 0x23, 0x20,
+ 0x71, 0x53, 0xc2, 0x5e, 0x34, 0x7a, 0xc4, 0x9e, 0xc5, 0x94, 0x46, 0xa8, 0x24, 0x4c, 0xd3, 0x79,
+ 0x7e, 0x0c, 0xbe, 0x15, 0x7a, 0xd1, 0xad, 0xdf, 0x20, 0x41, 0x5a, 0x61, 0x7c, 0x90, 0x5d, 0xbb,
+ 0x11, 0xd7, 0xc6, 0x11, 0x46, 0xc4, 0x40, 0x9f, 0x64, 0x1f, 0x0b, 0x79, 0x30, 0xbf, 0x1e, 0xca,
+ 0xda, 0x85, 0xd1, 0xc1, 0x5a, 0xc5, 0xb8, 0x2d, 0xa9, 0x33, 0xb3, 0x2a, 0xee, 0x1c, 0x51, 0x74,
+ 0x9b, 0x9c, 0x7f, 0xa3, 0xf0, 0x3b, 0x9b, 0xa1, 0xe0, 0x8b, 0x54, 0x16, 0x9d, 0xaf, 0x84, 0x06,
+ 0xde, 0x9f, 0x97, 0xf8, 0x6c, 0x2b, 0x4c, 0x67, 0x64, 0xca, 0x5b, 0x51, 0xe2, 0xd6, 0x3b, 0x99,
+ 0xd1, 0x89, 0x4e, 0xe5, 0x4d, 0x90, 0x47, 0xcb, 0x07, 0xed, 0xa8, 0x2a, 0x02, 0x72, 0x17, 0xfa,
+ 0x02, 0x67, 0xd2, 0xfe, 0x96, 0x7d, 0x97, 0x2f, 0x1d, 0x3f, 0xb6, 0x27, 0x30, 0x4a, 0x80, 0x46,
+ 0xff, 0x7d, 0x9a, 0xa4, 0x19, 0x05, 0xb2, 0x3c, 0x21, 0x0c, 0x82, 0x07, 0x43, 0x3e, 0x0e, 0x8d,
+ 0xbc, 0xa0, 0xa0, 0x37, 0x71, 0x96, 0x30, 0x85, 0xe1, 0x04, 0x96, 0x35, 0x04, 0x33, 0xc4, 0x46,
+ 0x1d, 0x7d, 0x85, 0xd2, 0x18, 0x36, 0xaf, 0x0a, 0x2a, 0x93, 0x2b, 0x06, 0x78, 0x7e, 0x7c, 0x4e,
+ 0x65, 0x37, 0xac, 0x32, 0xa2, 0xe9, 0xc1, 0x4b, 0xd0, 0x0a, 0x5d, 0x3e, 0xcf, 0x49, 0x7d, 0x2c,
+ 0x85, 0xa3, 0x45, 0x9b, 0xe2, 0x7d, 0x8e, 0x9d, 0x0f, 0x22, 0x82, 0xd3, 0xcd, 0x02, 0x82, 0x01,
+ 0x00, 0x46, 0xe8, 0x18, 0x3d, 0xbf, 0x92, 0x8c, 0xec, 0x0f, 0xa2, 0x07, 0x84, 0x07, 0xab, 0xbd,
+ 0xff, 0x3b, 0xbf, 0x7a, 0x04, 0x8a, 0x85, 0x2a, 0x6d, 0xcd, 0x92, 0x16, 0xae, 0xb4, 0x4b, 0x96,
+ 0xaf, 0xdb, 0xe2, 0x28, 0x44, 0xeb, 0x19, 0x58, 0x91, 0xd8, 0xd0, 0x94, 0x5c, 0x7a, 0xc8, 0x8a,
+ 0x8b, 0xda, 0xef, 0xe2, 0x38, 0x82, 0x8d, 0xb3, 0xe2, 0xdb, 0x76, 0xb3, 0x9f, 0x28, 0x16, 0x8c,
+ 0x3c, 0x7b, 0x07, 0x9f, 0x22, 0x0e, 0x47, 0x7e, 0x20, 0x55, 0xc4, 0x52, 0xde, 0x86, 0xfd, 0x98,
+ 0xd7, 0xc6, 0x5e, 0x79, 0x05, 0x64, 0x40, 0x01, 0xb7, 0xe4, 0x2d, 0xb8, 0xd0, 0x13, 0x90, 0x4b,
+ 0x3b, 0x6c, 0x63, 0xf8, 0xed, 0x6d, 0xeb, 0x09, 0x1e, 0x8f, 0xc1, 0xd4, 0xa9, 0x5e, 0x8e, 0x15,
+ 0x48, 0x69, 0x7c, 0x68, 0x0e, 0xe6, 0xf6, 0xcf, 0x4a, 0x06, 0x61, 0xe9, 0x3a, 0xb0, 0x5c, 0x23,
+ 0x86, 0xeb, 0xc7, 0xbb, 0x86, 0x0a, 0x37, 0x43, 0x03, 0x5b, 0x6d, 0xf4, 0xc7, 0x4b, 0xa5, 0x52,
+ 0xa7, 0x3b, 0xf1, 0xf4, 0xad, 0xe1, 0xd0, 0x71, 0x34, 0x3e, 0xfa, 0xf4, 0x6e, 0xad, 0xe8, 0x97,
+ 0xe4, 0xf6, 0xdf, 0x42, 0x29, 0xbc, 0xf2, 0x49, 0xfa, 0xda, 0xa6, 0x59, 0xd5, 0x74, 0xbb, 0xb1,
+ 0x07, 0xeb, 0x40, 0x74, 0x4d, 0x06, 0x5b, 0x03, 0xd8, 0xdf, 0x5d, 0x02, 0xf5, 0x3d, 0xae, 0xd1,
+ 0x45, 0x9a, 0xc6, 0x99, 0x10, 0x7d, 0xb8, 0x16, 0x43, 0xae, 0x9a, 0x4b, 0x69, 0x4f, 0x13, 0xe6,
+ 0xbb, 0x05, 0xa9, 0x6f, 0x57, 0x75, 0xf6, 0xe6, 0x33, 0x6f, 0x2b, 0xe8, 0x6c, 0x0d, 0x10, 0xe7,
+ 0x32, 0xb4, 0xee, 0x4e, 0x2a, 0x41, 0x22, 0xdb, 0x81, 0x40, 0x58, 0xdd, 0xfd, 0xd4, 0x8a, 0x8e,
+ 0xc3, 0x27, 0xe7, 0x52, 0x36, 0x09, 0x50, 0x82, 0xbb, 0xad, 0x21, 0x56, 0x17, 0x8f, 0xce, 0xed,
+ 0xa9, 0x02, 0x82, 0x01, 0x00, 0x78, 0x0c, 0x4f, 0x07, 0x4a, 0x2d, 0x91, 0xa3, 0xfd, 0x1c, 0xb3,
+ 0xb5, 0xcb, 0x11, 0x1c, 0x04, 0xc9, 0x7f, 0xbf, 0x86, 0x7d, 0x5e, 0x2e, 0xf4, 0x2c, 0xad, 0x9e,
+ 0x35, 0x74, 0x2e, 0x55, 0xc6, 0xaf, 0x1c, 0x76, 0xb9, 0x3d, 0x44, 0xe7, 0xdd, 0x5f, 0x32, 0x0e,
+ 0xce, 0x4b, 0x43, 0x6d, 0x7c, 0x10, 0xd3, 0x01, 0x6f, 0x22, 0xa5, 0xb3, 0xaf, 0x40, 0x80, 0x57,
+ 0xf0, 0x96, 0x7c, 0xb8, 0xae, 0xbc, 0x35, 0x8b, 0xa1, 0x59, 0xdc, 0xc4, 0xf7, 0x8b, 0xda, 0xe7,
+ 0x91, 0x9b, 0xa7, 0x54, 0x61, 0xad, 0x9d, 0x0a, 0x68, 0xc3, 0xc5, 0x10, 0x9e, 0x39, 0x16, 0x9c,
+ 0x3b, 0xf3, 0x3a, 0xdf, 0xb9, 0x72, 0xed, 0x4f, 0x56, 0x1e, 0x5c, 0x8b, 0x35, 0x0b, 0xa6, 0x08,
+ 0x83, 0x96, 0xd4, 0x6d, 0x3f, 0xe3, 0x6a, 0x25, 0xa2, 0xe8, 0x0f, 0xce, 0x0c, 0x26, 0x85, 0x02,
+ 0xba, 0xe1, 0xfc, 0xdc, 0xa0, 0x18, 0xfc, 0x7c, 0x87, 0xa6, 0x6b, 0xf5, 0x92, 0xa8, 0x83, 0x5f,
+ 0xbf, 0xa5, 0xe3, 0x0e, 0x21, 0x10, 0xbf, 0x44, 0x49, 0xc7, 0x62, 0x36, 0x46, 0x0b, 0x97, 0xdc,
+ 0xd4, 0x30, 0x02, 0xd4, 0x04, 0xcb, 0x8f, 0xfe, 0xde, 0x76, 0x44, 0x9f, 0xd6, 0x55, 0x8e, 0x45,
+ 0xaa, 0x10, 0xd4, 0xa0, 0x2c, 0x49, 0x72, 0x6e, 0x5f, 0xf1, 0xd5, 0x0a, 0x1e, 0xba, 0xc6, 0xb4,
+ 0xb6, 0x93, 0x9d, 0x6a, 0xe4, 0xaa, 0x61, 0x3a, 0xca, 0x77, 0xa4, 0x16, 0xa2, 0x4f, 0x2f, 0xad,
+ 0xe6, 0xb5, 0x64, 0x93, 0x37, 0xf9, 0xdc, 0x28, 0x70, 0x54, 0x0b, 0xb2, 0x15, 0x96, 0x60, 0xbc,
+ 0x86, 0x16, 0xfc, 0x5c, 0xf0, 0x75, 0xc2, 0x6f, 0x51, 0x4f, 0x71, 0xbb, 0x77, 0xed, 0xe8, 0x2b,
+ 0xf2, 0xe3, 0x7d, 0x02, 0xa8, 0xfe, 0x26, 0x8a, 0x40, 0x63, 0x8f, 0x14, 0x84, 0xd2, 0x00, 0x70,
+ 0x6a, 0xf3, 0xde, 0xf8, 0x66};
+constexpr const unsigned int rsa_key_4k_len = sizeof(rsa_key_4k);
+
+constexpr const unsigned char rsa_key_2k[] = {
+ 0x30, 0x82, 0x04, 0xbd, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x04, 0xa7, 0x30, 0x82, 0x04, 0xa3, 0x02, 0x01,
+ 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0x9d, 0xd0, 0xca, 0x63, 0xaa, 0x59, 0x73, 0x13, 0x55, 0x53,
+ 0xdc, 0x5d, 0x4d, 0xf9, 0x5c, 0x4f, 0x31, 0xe3, 0x34, 0x45, 0xbf, 0xc5, 0x8b, 0x43, 0x8f, 0x10,
+ 0xb6, 0x22, 0xaf, 0xe0, 0x12, 0xff, 0xf5, 0xa8, 0x71, 0x0a, 0xe9, 0x67, 0x8d, 0x05, 0xd2, 0x69,
+ 0x35, 0x32, 0x8d, 0x94, 0x9f, 0x52, 0x63, 0x4e, 0x54, 0x0b, 0x29, 0x3d, 0x7b, 0x5e, 0xc0, 0x98,
+ 0x94, 0x23, 0x43, 0x46, 0x39, 0xce, 0xc2, 0xe1, 0x2b, 0xcd, 0x00, 0x6c, 0x9d, 0xe7, 0x81, 0xb9,
+ 0x6b, 0x97, 0xea, 0x3a, 0xe7, 0x09, 0x06, 0x6b, 0xf3, 0xe1, 0x7a, 0x03, 0xcd, 0x51, 0x2c, 0x16,
+ 0x19, 0x6c, 0xdf, 0x2c, 0xd5, 0x96, 0x92, 0x19, 0xaa, 0x91, 0xe3, 0xa5, 0xc7, 0xe1, 0x0b, 0x07,
+ 0xb6, 0x84, 0xd3, 0xa7, 0x1f, 0x0d, 0x22, 0xb9, 0xc1, 0x76, 0x16, 0x81, 0x53, 0x50, 0x7d, 0x54,
+ 0x2a, 0x26, 0x9e, 0xfa, 0xb1, 0xb7, 0x83, 0x05, 0x24, 0x81, 0xea, 0x5a, 0x6c, 0xb5, 0x92, 0x69,
+ 0x63, 0x35, 0xfa, 0x04, 0xae, 0xee, 0xc5, 0xdb, 0xf0, 0x9b, 0xfe, 0xe6, 0xc4, 0x73, 0x18, 0x3d,
+ 0xd3, 0x01, 0xaf, 0x03, 0x43, 0x95, 0xca, 0xab, 0x43, 0x04, 0x64, 0x49, 0xe7, 0x47, 0xf8, 0x97,
+ 0xe5, 0x63, 0xd1, 0x3d, 0x21, 0x9b, 0xd5, 0x13, 0x2a, 0xb0, 0xf1, 0xf9, 0xff, 0xd2, 0xb7, 0x12,
+ 0xa8, 0xa0, 0x20, 0x38, 0xde, 0x41, 0x8e, 0xa3, 0xc7, 0xce, 0x5b, 0x9c, 0x30, 0x1a, 0xaf, 0x13,
+ 0x11, 0xd1, 0xd0, 0x71, 0x7f, 0x1e, 0x47, 0xa8, 0x32, 0x3b, 0x4a, 0x36, 0xa8, 0x6d, 0x8c, 0xd7,
+ 0x5f, 0x93, 0x95, 0xa8, 0xe0, 0xfa, 0x59, 0xb5, 0x6c, 0x1f, 0xfb, 0x01, 0x64, 0xf5, 0x5d, 0xf5,
+ 0x17, 0x75, 0x53, 0xfb, 0xc3, 0x3f, 0xd7, 0xc5, 0x45, 0x53, 0xb4, 0xa2, 0xcf, 0xa5, 0x71, 0xf0,
+ 0x7a, 0x8b, 0x66, 0x09, 0xfa, 0x4d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x00, 0x03,
+ 0xe9, 0x06, 0x4d, 0x6a, 0xe2, 0xa0, 0x2b, 0x24, 0xa1, 0x99, 0x23, 0xb2, 0x08, 0xbc, 0x2f, 0x4a,
+ 0xd2, 0xa6, 0x30, 0x6b, 0x91, 0xd6, 0xf8, 0xb1, 0x0f, 0x9e, 0x71, 0x04, 0x94, 0xe8, 0xe8, 0xf1,
+ 0x00, 0x1f, 0xf0, 0xea, 0x70, 0x97, 0x8f, 0x6d, 0xde, 0x65, 0x24, 0x35, 0x5a, 0xd9, 0xdf, 0x13,
+ 0x8e, 0x7f, 0x74, 0x44, 0x02, 0x28, 0x7a, 0x39, 0x61, 0x19, 0x1b, 0xe3, 0x3b, 0xac, 0x62, 0x01,
+ 0x9f, 0xcc, 0xfd, 0xdd, 0x85, 0x53, 0x72, 0x46, 0xdb, 0x69, 0x1d, 0x90, 0xa9, 0xad, 0xf6, 0x22,
+ 0x1e, 0x6e, 0xf8, 0x06, 0xc0, 0x6d, 0x08, 0x16, 0x5a, 0x0e, 0x7e, 0x37, 0xec, 0xbc, 0xa1, 0x68,
+ 0x49, 0xa7, 0x84, 0x1e, 0xdf, 0xb4, 0x30, 0xa6, 0x1d, 0x26, 0x4f, 0x6b, 0x39, 0x1d, 0xad, 0x58,
+ 0x7a, 0x16, 0xca, 0x38, 0xc0, 0xdd, 0x3c, 0xc6, 0x26, 0x32, 0xe1, 0xd5, 0xc2, 0xeb, 0x6a, 0xa6,
+ 0x70, 0x1f, 0x50, 0x82, 0x2d, 0xf6, 0x09, 0x27, 0x56, 0x4f, 0xf1, 0xed, 0x62, 0x60, 0xe5, 0x55,
+ 0x0f, 0x8d, 0xbe, 0xd7, 0x5a, 0xb7, 0x7c, 0x57, 0x25, 0xe5, 0xa3, 0x46, 0xb6, 0x97, 0xe0, 0x87,
+ 0x43, 0x04, 0x46, 0xd8, 0x4f, 0xbe, 0x80, 0x75, 0x40, 0x48, 0x2d, 0xc0, 0x57, 0xf8, 0x76, 0xbd,
+ 0xd8, 0x14, 0xa0, 0x7b, 0x39, 0x4f, 0xcf, 0xdc, 0x34, 0x5b, 0x1e, 0x91, 0xef, 0xa7, 0xc8, 0x82,
+ 0x2c, 0xe8, 0xe4, 0x01, 0x2b, 0xa6, 0x92, 0x4e, 0x0b, 0xd1, 0x98, 0xdc, 0x45, 0x46, 0x3f, 0x89,
+ 0xb1, 0x01, 0xb5, 0xa7, 0xeb, 0x71, 0x2f, 0x09, 0x10, 0x3c, 0x71, 0x14, 0xf3, 0x86, 0x18, 0xa8,
+ 0x5a, 0x30, 0xef, 0xfe, 0x87, 0x65, 0xb1, 0xaf, 0x9c, 0x8d, 0x3e, 0xc8, 0x8d, 0x72, 0xf5, 0x16,
+ 0xcb, 0x3a, 0xb8, 0xb1, 0x18, 0xa5, 0x43, 0x1d, 0x24, 0xa1, 0x1c, 0x2d, 0x2d, 0x87, 0xc1, 0x02,
+ 0x81, 0x81, 0x00, 0xde, 0x87, 0xff, 0x74, 0x9d, 0x86, 0x9b, 0x0b, 0x18, 0xb7, 0xa4, 0x50, 0xda,
+ 0x2d, 0x27, 0xff, 0x0e, 0x4d, 0xae, 0x40, 0x21, 0x92, 0x0b, 0x1d, 0x8b, 0xdd, 0x81, 0xdc, 0x40,
+ 0x1c, 0xed, 0x7d, 0x39, 0xd6, 0x1d, 0xdd, 0x88, 0xd0, 0x92, 0xee, 0x74, 0xca, 0x96, 0xa7, 0x6a,
+ 0x58, 0xd3, 0xc6, 0xf4, 0x3e, 0x93, 0x43, 0x54, 0x07, 0x72, 0x3a, 0x8d, 0x3b, 0x09, 0xe0, 0x16,
+ 0x16, 0x71, 0xf1, 0xae, 0xc4, 0xc0, 0x46, 0xce, 0x40, 0x6d, 0xba, 0xf0, 0x4d, 0x1d, 0x04, 0x9a,
+ 0x32, 0x31, 0xe1, 0x07, 0x9b, 0x90, 0x7c, 0xc2, 0xb2, 0xfc, 0x4e, 0xb9, 0x61, 0xd4, 0xdb, 0x4a,
+ 0xa1, 0xb9, 0x78, 0x46, 0x98, 0xa9, 0xfb, 0x21, 0x60, 0xb8, 0x07, 0x6b, 0x24, 0x4d, 0x4d, 0x35,
+ 0x83, 0x0b, 0x21, 0xac, 0xdf, 0x93, 0x2f, 0xb5, 0xec, 0xe5, 0x99, 0x1a, 0x59, 0xaa, 0xd3, 0xbb,
+ 0x8f, 0xe9, 0xad, 0x02, 0x81, 0x81, 0x00, 0xb5, 0x8d, 0x14, 0x8e, 0x85, 0xd9, 0x7c, 0x9e, 0xfc,
+ 0xa1, 0x1f, 0x1a, 0x84, 0x31, 0x07, 0x71, 0x1a, 0x72, 0x91, 0xc0, 0xc5, 0xac, 0x8f, 0xa7, 0x0f,
+ 0x37, 0x48, 0x51, 0x12, 0xda, 0x0d, 0x30, 0x6d, 0x97, 0x21, 0x20, 0x90, 0x49, 0x4d, 0x2b, 0xc1,
+ 0x89, 0x8e, 0x00, 0x66, 0x18, 0x47, 0xd5, 0x68, 0x62, 0xe7, 0x29, 0xf4, 0x95, 0x59, 0x5b, 0xba,
+ 0x4b, 0xc2, 0x20, 0xda, 0xef, 0x4f, 0x33, 0x0e, 0x99, 0xfe, 0x6c, 0xec, 0xf9, 0xd8, 0x81, 0x3a,
+ 0x46, 0x1a, 0xbd, 0xba, 0xf7, 0xfc, 0xd7, 0x19, 0xf8, 0x2d, 0xd1, 0x81, 0x88, 0xce, 0x55, 0x98,
+ 0xe6, 0xbc, 0x25, 0x67, 0xa6, 0xbe, 0x2b, 0x0f, 0x1d, 0xb6, 0x0d, 0xea, 0xc6, 0xb6, 0x95, 0xee,
+ 0x42, 0x3e, 0x1b, 0xf5, 0x8c, 0xf3, 0x19, 0x8e, 0x59, 0xfc, 0xe1, 0x42, 0x1b, 0x26, 0x20, 0x09,
+ 0x8a, 0x1b, 0x57, 0x8e, 0xe1, 0xa7, 0x21, 0x02, 0x81, 0x80, 0x78, 0xc7, 0x33, 0x7d, 0x25, 0xaa,
+ 0x53, 0x28, 0x38, 0xa8, 0x23, 0x84, 0xc6, 0x85, 0xcf, 0xb9, 0x7d, 0x17, 0xe8, 0x45, 0x62, 0x73,
+ 0x13, 0x99, 0x5b, 0xba, 0x43, 0xab, 0x39, 0x18, 0xfa, 0x45, 0x07, 0x49, 0x11, 0x38, 0x95, 0xf3,
+ 0x2e, 0x6c, 0x41, 0xf3, 0x5a, 0xc5, 0x4e, 0xd1, 0x1b, 0x50, 0x56, 0x6c, 0x48, 0x1d, 0x38, 0xd4,
+ 0x39, 0xc9, 0x51, 0xb2, 0x03, 0x70, 0x1e, 0x4c, 0xdc, 0x57, 0x22, 0x56, 0x23, 0x4d, 0xca, 0xcf,
+ 0xe9, 0x3e, 0x97, 0x02, 0x23, 0x87, 0xc5, 0xf1, 0x0c, 0x65, 0x68, 0x6d, 0xa4, 0x84, 0x32, 0x60,
+ 0x56, 0xd4, 0x9b, 0x85, 0x5f, 0xb4, 0x0d, 0xd3, 0xad, 0x08, 0x7c, 0xb8, 0x8b, 0x39, 0x84, 0x2a,
+ 0x2c, 0x77, 0xca, 0x4d, 0x0f, 0xaf, 0xa2, 0x25, 0x97, 0xbb, 0x15, 0x4a, 0xdb, 0x65, 0xff, 0xc5,
+ 0xad, 0xef, 0xe4, 0xff, 0x59, 0xda, 0x45, 0x68, 0x9c, 0x99, 0x02, 0x81, 0x80, 0x16, 0x45, 0x0a,
+ 0xfb, 0x7c, 0x91, 0xb4, 0x06, 0xb0, 0x88, 0x77, 0x0f, 0x42, 0x9d, 0xdd, 0x02, 0xd3, 0xb2, 0xb0,
+ 0x0c, 0x4c, 0x73, 0x21, 0x5f, 0xe5, 0xae, 0xeb, 0x50, 0xfe, 0x95, 0xfe, 0xbe, 0x2d, 0x03, 0x37,
+ 0xce, 0x0d, 0xc4, 0xe0, 0x11, 0x78, 0xf9, 0x0d, 0x91, 0x20, 0xf4, 0xe3, 0x82, 0xda, 0xfe, 0x1e,
+ 0xca, 0xf7, 0xb4, 0x86, 0x34, 0x89, 0x42, 0x97, 0xba, 0x7e, 0x00, 0x92, 0xdf, 0x79, 0x70, 0x0c,
+ 0x54, 0x82, 0x31, 0x17, 0x8c, 0xaa, 0x80, 0x44, 0xf1, 0x77, 0x08, 0xca, 0x5b, 0xfc, 0x54, 0x84,
+ 0x12, 0x49, 0xe8, 0x65, 0x1e, 0xfc, 0xd5, 0x78, 0xc8, 0xc1, 0xd1, 0x23, 0x4c, 0x96, 0xdb, 0x17,
+ 0x24, 0xd7, 0xe2, 0xae, 0x2c, 0xef, 0xff, 0xf2, 0x2c, 0x6d, 0xcf, 0x6f, 0x56, 0x78, 0x2e, 0xb3,
+ 0xa5, 0x51, 0xfd, 0x90, 0x8c, 0xa7, 0x7e, 0xe8, 0x61, 0xb2, 0x26, 0x1d, 0xe1, 0x02, 0x81, 0x81,
+ 0x00, 0x88, 0xf6, 0x9a, 0xec, 0xab, 0xb6, 0x25, 0x2c, 0x12, 0x4e, 0x90, 0x8f, 0xea, 0xa2, 0x7e,
+ 0x62, 0x41, 0xd7, 0xfd, 0x7c, 0x5d, 0xaa, 0x83, 0xfa, 0xc7, 0x48, 0x51, 0x54, 0xed, 0x72, 0x59,
+ 0x95, 0xc0, 0x61, 0xdc, 0xfa, 0xc6, 0xb8, 0xd5, 0x5f, 0x9a, 0xd0, 0x7e, 0xcb, 0x3f, 0xc4, 0xfd,
+ 0xb4, 0x4d, 0x46, 0x74, 0xc4, 0xf0, 0x45, 0xd4, 0x62, 0xdc, 0x27, 0x37, 0x4a, 0x8a, 0xcf, 0x27,
+ 0x1a, 0x6f, 0x00, 0x50, 0x12, 0x99, 0x8d, 0xd7, 0xa8, 0xdf, 0xf3, 0xa4, 0x61, 0x18, 0x4f, 0xed,
+ 0x1d, 0xac, 0x51, 0xd4, 0x44, 0x2f, 0x73, 0xea, 0xe7, 0xd7, 0xd2, 0x81, 0xe1, 0xe8, 0x4e, 0x0a,
+ 0xeb, 0xbd, 0x40, 0x2b, 0x62, 0xb3, 0x43, 0x60, 0x72, 0x73, 0x31, 0xc0, 0x7a, 0x16, 0x15, 0x83,
+ 0x71, 0x2f, 0x2e, 0xb4, 0x17, 0x43, 0x12, 0x30, 0x08, 0xef, 0x72, 0xdb, 0xc9, 0x50, 0x28, 0x5a,
+ 0xda};
+constexpr const unsigned int rsa_key_2k_len = sizeof(rsa_key_2k);
\ No newline at end of file
diff --git a/keystore2/src/crypto/zvec.rs b/keystore2/src/crypto/zvec.rs
new file mode 100644
index 0000000..52addfc
--- /dev/null
+++ b/keystore2/src/crypto/zvec.rs
@@ -0,0 +1,102 @@
+// 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)]
+
+use crate::error::Error;
+use nix::sys::mman::{mlock, munlock};
+use std::convert::TryFrom;
+use std::fmt;
+use std::ops::{Deref, DerefMut};
+use std::ptr::write_volatile;
+
+/// A fixed size u8 vector that is zeroed when dropped. Also the data is
+/// pinned in memory with mlock.
+#[derive(Default, Eq, PartialEq)]
+pub struct ZVec(Box<[u8]>);
+
+impl ZVec {
+ /// Create a ZVec with the given size.
+ pub fn new(size: usize) -> Result<Self, Error> {
+ let v: Vec<u8> = vec![0; size];
+ let b = v.into_boxed_slice();
+ if size > 0 {
+ unsafe { mlock(b.as_ptr() as *const std::ffi::c_void, b.len()) }?;
+ }
+ Ok(Self(b))
+ }
+}
+
+impl Drop for ZVec {
+ fn drop(&mut self) {
+ for i in 0..self.0.len() {
+ unsafe { write_volatile(self.0.as_mut_ptr().add(i), 0) };
+ }
+ if !self.0.is_empty() {
+ if let Err(e) =
+ unsafe { munlock(self.0.as_ptr() as *const std::ffi::c_void, self.0.len()) }
+ {
+ log::error!("In ZVec::drop: `munlock` failed: {:?}.", e);
+ }
+ }
+ }
+}
+
+impl Deref for ZVec {
+ type Target = [u8];
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl DerefMut for ZVec {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+impl fmt::Debug for ZVec {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if self.0.is_empty() {
+ write!(f, "Zvec empty")
+ } else {
+ write!(f, "Zvec size: {} [ Sensitive information redacted ]", self.0.len())
+ }
+ }
+}
+
+impl TryFrom<&[u8]> for ZVec {
+ type Error = Error;
+
+ fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
+ let mut z = ZVec::new(v.len())?;
+ if !v.is_empty() {
+ z.clone_from_slice(v);
+ }
+ Ok(z)
+ }
+}
+
+impl TryFrom<Vec<u8>> for ZVec {
+ type Error = Error;
+
+ fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> {
+ let b = v.into_boxed_slice();
+ if !b.is_empty() {
+ unsafe { mlock(b.as_ptr() as *const std::ffi::c_void, b.len()) }?;
+ }
+ Ok(Self(b))
+ }
+}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
new file mode 100644
index 0000000..0c49340
--- /dev/null
+++ b/keystore2/src/database.rs
@@ -0,0 +1,2537 @@
+// 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: remove this in the future CLs in the stack.
+#![allow(dead_code)]
+
+//! This is the Keystore 2.0 database module.
+//! The database module provides a connection to the backing SQLite store.
+//! We have two databases one for persistent key blob storage and one for
+//! items that have a per boot life cycle.
+//!
+//! ## Persistent database
+//! The persistent database has tables for key blobs. They are organized
+//! as follows:
+//! The `keyentry` table is the primary table for key entries. It is
+//! accompanied by two tables for blobs and parameters.
+//! Each key entry occupies exactly one row in the `keyentry` table and
+//! zero or more rows in the tables `blobentry` and `keyparameter`.
+//!
+//! ## Per boot database
+//! The per boot database stores items with a per boot lifecycle.
+//! Currently, there is only the `grant` table in this database.
+//! Grants are references to a key that can be used to access a key by
+//! clients that don't own that key. Grants can only be created by the
+//! owner of a key. And only certain components can create grants.
+//! This is governed by SEPolicy.
+//!
+//! ## Access control
+//! Some database functions that load keys or create grants perform
+//! access control. This is because in some cases access control
+//! can only be performed after some information about the designated
+//! key was loaded from the database. To decouple the permission checks
+//! from the database module these functions take permission check
+//! callbacks.
+
+#![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 anyhow::{anyhow, Context, Result};
+use std::{convert::TryFrom, convert::TryInto, time::SystemTimeError};
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType,
+ SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor,
+};
+
+use lazy_static::lazy_static;
+#[cfg(not(test))]
+use rand::prelude::random;
+use rusqlite::{
+ params,
+ types::FromSql,
+ types::FromSqlResult,
+ types::ToSqlOutput,
+ types::{FromSqlError, Value, ValueRef},
+ Connection, OptionalExtension, ToSql, Transaction, TransactionBehavior, NO_PARAMS,
+};
+use std::{
+ collections::{HashMap, HashSet},
+ path::Path,
+ sync::{Condvar, Mutex},
+ time::{Duration, SystemTime},
+};
+#[cfg(test)]
+use tests::random;
+
+impl_metadata!(
+ /// A set of metadata for key entries.
+ #[derive(Debug, Default, Eq, PartialEq)]
+ pub struct KeyMetaData;
+ /// A metadata entry for key entries.
+ #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
+ pub enum KeyMetaEntry {
+ /// If present, indicates that the sensitive part of key
+ /// is encrypted with another key or a key derived from a password.
+ EncryptedBy(EncryptedBy) with accessor encrypted_by,
+ /// If the blob is password encrypted this field is set to the
+ /// salt used for the key derivation.
+ Salt(Vec<u8>) with accessor salt,
+ /// If the blob is encrypted, this field is set to the initialization vector.
+ Iv(Vec<u8>) with accessor iv,
+ /// If the blob is encrypted, this field holds the AEAD TAG.
+ AeadTag(Vec<u8>) with accessor aead_tag,
+ /// Creation date of a the key entry.
+ CreationDate(DateTime) with accessor creation_date,
+ /// Expiration date for attestation keys.
+ AttestationExpirationDate(DateTime) with accessor attestation_expiration_date,
+ // --- ADD NEW META DATA FIELDS HERE ---
+ // For backwards compatibility add new entries only to
+ // end of this list and above this comment.
+ };
+);
+
+impl KeyMetaData {
+ fn load_from_db(key_id: i64, tx: &Transaction) -> Result<Self> {
+ let mut stmt = tx
+ .prepare(
+ "SELECT tag, data from persistent.keymetadata
+ WHERE keyentryid = ?;",
+ )
+ .context("In KeyMetaData::load_from_db: prepare statement failed.")?;
+
+ let mut metadata: HashMap<i64, KeyMetaEntry> = Default::default();
+
+ let mut rows =
+ stmt.query(params![key_id]).context("In KeyMetaData::load_from_db: query failed.")?;
+ db_utils::with_rows_extract_all(&mut rows, |row| {
+ let db_tag: i64 = row.get(0).context("Failed to read tag.")?;
+ metadata.insert(
+ db_tag,
+ KeyMetaEntry::new_from_sql(db_tag, &SqlField::new(1, &row))
+ .context("Failed to read KeyMetaEntry.")?,
+ );
+ Ok(())
+ })
+ .context("In KeyMetaData::load_from_db.")?;
+
+ Ok(Self { data: metadata })
+ }
+
+ fn store_in_db(&self, key_id: i64, tx: &Transaction) -> Result<()> {
+ let mut stmt = tx
+ .prepare(
+ "INSERT into persistent.keymetadata (keyentryid, tag, data)
+ VALUES (?, ?, ?);",
+ )
+ .context("In KeyMetaData::store_in_db: Failed to prepare statement.")?;
+
+ let iter = self.data.iter();
+ for (tag, entry) in iter {
+ stmt.insert(params![key_id, tag, entry,]).with_context(|| {
+ format!("In KeyMetaData::store_in_db: Failed to insert {:?}", entry)
+ })?;
+ }
+ Ok(())
+ }
+}
+
+/// Indicates the type of the keyentry.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub enum KeyType {
+ /// This is a client key type. These keys are created or imported through the Keystore 2.0
+ /// AIDL interface android.system.keystore2.
+ Client,
+ /// This is a super key type. These keys are created by keystore itself and used to encrypt
+ /// other key blobs to provide LSKF binding.
+ Super,
+ /// This is an attestation key. These keys are created by the remote provisioning mechanism.
+ Attestation,
+}
+
+impl ToSql for KeyType {
+ fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
+ Ok(ToSqlOutput::Owned(Value::Integer(match self {
+ KeyType::Client => 0,
+ KeyType::Super => 1,
+ KeyType::Attestation => 2,
+ })))
+ }
+}
+
+impl FromSql for KeyType {
+ fn column_result(value: ValueRef) -> FromSqlResult<Self> {
+ match i64::column_result(value)? {
+ 0 => Ok(KeyType::Client),
+ 1 => Ok(KeyType::Super),
+ 2 => Ok(KeyType::Attestation),
+ v => Err(FromSqlError::OutOfRange(v)),
+ }
+ }
+}
+
+/// Indicates how the sensitive part of this key blob is encrypted.
+#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub enum EncryptedBy {
+ /// The keyblob is encrypted by a user password.
+ /// In the database this variant is represented as NULL.
+ Password,
+ /// The keyblob is encrypted by another key with wrapped key id.
+ /// In the database this variant is represented as non NULL value
+ /// that is convertible to i64, typically NUMERIC.
+ KeyId(i64),
+}
+
+impl ToSql for EncryptedBy {
+ fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
+ match self {
+ Self::Password => Ok(ToSqlOutput::Owned(Value::Null)),
+ Self::KeyId(id) => id.to_sql(),
+ }
+ }
+}
+
+impl FromSql for EncryptedBy {
+ fn column_result(value: ValueRef) -> FromSqlResult<Self> {
+ match value {
+ ValueRef::Null => Ok(Self::Password),
+ _ => Ok(Self::KeyId(i64::column_result(value)?)),
+ }
+ }
+}
+
+/// A database representation of wall clock time. DateTime stores unix epoch time as
+/// i64 in milliseconds.
+#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)]
+pub struct DateTime(i64);
+
+/// Error type returned when creating DateTime or converting it from and to
+/// SystemTime.
+#[derive(thiserror::Error, Debug)]
+pub enum DateTimeError {
+ /// This is returned when SystemTime and Duration computations fail.
+ #[error(transparent)]
+ SystemTimeError(#[from] SystemTimeError),
+
+ /// This is returned when type conversions fail.
+ #[error(transparent)]
+ TypeConversion(#[from] std::num::TryFromIntError),
+
+ /// This is returned when checked time arithmetic failed.
+ #[error("Time arithmetic failed.")]
+ TimeArithmetic,
+}
+
+impl DateTime {
+ /// Constructs a new DateTime object denoting the current time. This may fail during
+ /// conversion to unix epoch time and during conversion to the internal i64 representation.
+ pub fn now() -> Result<Self, DateTimeError> {
+ Ok(Self(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis().try_into()?))
+ }
+
+ /// Constructs a new DateTime object from milliseconds.
+ pub fn from_millis_epoch(millis: i64) -> Self {
+ Self(millis)
+ }
+
+ /// Returns unix epoch time in milliseconds.
+ pub fn to_millis_epoch(&self) -> i64 {
+ self.0
+ }
+
+ /// Returns unix epoch time in seconds.
+ pub fn to_secs_epoch(&self) -> i64 {
+ self.0 / 1000
+ }
+}
+
+impl ToSql for DateTime {
+ fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
+ Ok(ToSqlOutput::Owned(Value::Integer(self.0)))
+ }
+}
+
+impl FromSql for DateTime {
+ fn column_result(value: ValueRef) -> FromSqlResult<Self> {
+ Ok(Self(i64::column_result(value)?))
+ }
+}
+
+impl TryInto<SystemTime> for DateTime {
+ type Error = DateTimeError;
+
+ fn try_into(self) -> Result<SystemTime, Self::Error> {
+ // We want to construct a SystemTime representation equivalent to self, denoting
+ // a point in time THEN, but we cannot set the time directly. We can only construct
+ // a SystemTime denoting NOW, and we can get the duration between EPOCH and NOW,
+ // and between EPOCH and THEN. With this common reference we can construct the
+ // duration between NOW and THEN which we can add to our SystemTime representation
+ // of NOW to get a SystemTime representation of THEN.
+ // Durations can only be positive, thus the if statement below.
+ let now = SystemTime::now();
+ let now_epoch = now.duration_since(SystemTime::UNIX_EPOCH)?;
+ let then_epoch = Duration::from_millis(self.0.try_into()?);
+ Ok(if now_epoch > then_epoch {
+ // then = now - (now_epoch - then_epoch)
+ now_epoch
+ .checked_sub(then_epoch)
+ .and_then(|d| now.checked_sub(d))
+ .ok_or(DateTimeError::TimeArithmetic)?
+ } else {
+ // then = now + (then_epoch - now_epoch)
+ then_epoch
+ .checked_sub(now_epoch)
+ .and_then(|d| now.checked_add(d))
+ .ok_or(DateTimeError::TimeArithmetic)?
+ })
+ }
+}
+
+impl TryFrom<SystemTime> for DateTime {
+ type Error = DateTimeError;
+
+ fn try_from(t: SystemTime) -> Result<Self, Self::Error> {
+ Ok(Self(t.duration_since(SystemTime::UNIX_EPOCH)?.as_millis().try_into()?))
+ }
+}
+
+/// Keys have a KeyMint blob component and optional public certificate and
+/// certificate chain components.
+/// KeyEntryLoadBits is a bitmap that indicates to `KeystoreDB::load_key_entry`
+/// which components shall be loaded from the database if present.
+#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub struct KeyEntryLoadBits(u32);
+
+impl KeyEntryLoadBits {
+ /// Indicate to `KeystoreDB::load_key_entry` that no component shall be loaded.
+ pub const NONE: KeyEntryLoadBits = Self(0);
+ /// Indicate to `KeystoreDB::load_key_entry` that the KeyMint component shall be loaded.
+ pub const KM: KeyEntryLoadBits = Self(1);
+ /// Indicate to `KeystoreDB::load_key_entry` that the Public components shall be loaded.
+ pub const PUBLIC: KeyEntryLoadBits = Self(2);
+ /// Indicate to `KeystoreDB::load_key_entry` that both components shall be loaded.
+ pub const BOTH: KeyEntryLoadBits = Self(3);
+
+ /// Returns true if this object indicates that the public components shall be loaded.
+ pub const fn load_public(&self) -> bool {
+ self.0 & Self::PUBLIC.0 != 0
+ }
+
+ /// Returns true if the object indicates that the KeyMint component shall be loaded.
+ pub const fn load_km(&self) -> bool {
+ self.0 & Self::KM.0 != 0
+ }
+}
+
+lazy_static! {
+ static ref KEY_ID_LOCK: KeyIdLockDb = KeyIdLockDb::new();
+}
+
+struct KeyIdLockDb {
+ locked_keys: Mutex<HashSet<i64>>,
+ cond_var: Condvar,
+}
+
+/// A locked key. While a guard exists for a given key id, the same key cannot be loaded
+/// from the database a second time. Most functions manipulating the key blob database
+/// require a KeyIdGuard.
+#[derive(Debug)]
+pub struct KeyIdGuard(i64);
+
+impl KeyIdLockDb {
+ fn new() -> Self {
+ Self { locked_keys: Mutex::new(HashSet::new()), cond_var: Condvar::new() }
+ }
+
+ /// This function blocks until an exclusive lock for the given key entry id can
+ /// be acquired. It returns a guard object, that represents the lifecycle of the
+ /// acquired lock.
+ pub fn get(&self, key_id: i64) -> KeyIdGuard {
+ let mut locked_keys = self.locked_keys.lock().unwrap();
+ while locked_keys.contains(&key_id) {
+ locked_keys = self.cond_var.wait(locked_keys).unwrap();
+ }
+ locked_keys.insert(key_id);
+ KeyIdGuard(key_id)
+ }
+
+ /// This function attempts to acquire an exclusive lock on a given key id. If the
+ /// given key id is already taken the function returns None immediately. If a lock
+ /// can be acquired this function returns a guard object, that represents the
+ /// lifecycle of the acquired lock.
+ pub fn try_get(&self, key_id: i64) -> Option<KeyIdGuard> {
+ let mut locked_keys = self.locked_keys.lock().unwrap();
+ if locked_keys.insert(key_id) {
+ Some(KeyIdGuard(key_id))
+ } else {
+ None
+ }
+ }
+}
+
+impl KeyIdGuard {
+ /// Get the numeric key id of the locked key.
+ pub fn id(&self) -> i64 {
+ self.0
+ }
+}
+
+impl Drop for KeyIdGuard {
+ fn drop(&mut self) {
+ let mut locked_keys = KEY_ID_LOCK.locked_keys.lock().unwrap();
+ locked_keys.remove(&self.0);
+ drop(locked_keys);
+ KEY_ID_LOCK.cond_var.notify_all();
+ }
+}
+
+/// This type represents a Keystore 2.0 key entry.
+/// An entry has a unique `id` by which it can be found in the database.
+/// It has a security level field, key parameters, and three optional fields
+/// for the KeyMint blob, public certificate and a public certificate chain.
+#[derive(Debug, Default, Eq, PartialEq)]
+pub struct KeyEntry {
+ id: i64,
+ km_blob: Option<Vec<u8>>,
+ cert: Option<Vec<u8>>,
+ cert_chain: Option<Vec<u8>>,
+ sec_level: SecurityLevel,
+ parameters: Vec<KeyParameter>,
+ metadata: KeyMetaData,
+}
+
+impl KeyEntry {
+ /// Returns the unique id of the Key entry.
+ pub fn id(&self) -> i64 {
+ self.id
+ }
+ /// Exposes the optional KeyMint blob.
+ pub fn km_blob(&self) -> &Option<Vec<u8>> {
+ &self.km_blob
+ }
+ /// Extracts the Optional KeyMint blob.
+ pub fn take_km_blob(&mut self) -> Option<Vec<u8>> {
+ self.km_blob.take()
+ }
+ /// Exposes the optional public certificate.
+ pub fn cert(&self) -> &Option<Vec<u8>> {
+ &self.cert
+ }
+ /// Extracts the optional public certificate.
+ pub fn take_cert(&mut self) -> Option<Vec<u8>> {
+ self.cert.take()
+ }
+ /// Exposes the optional public certificate chain.
+ pub fn cert_chain(&self) -> &Option<Vec<u8>> {
+ &self.cert_chain
+ }
+ /// Extracts the optional public certificate_chain.
+ pub fn take_cert_chain(&mut self) -> Option<Vec<u8>> {
+ self.cert_chain.take()
+ }
+ /// Returns the security level of the key entry.
+ pub fn sec_level(&self) -> SecurityLevel {
+ self.sec_level
+ }
+ /// Exposes the key parameters of this key entry.
+ pub fn key_parameters(&self) -> &Vec<KeyParameter> {
+ &self.parameters
+ }
+ /// Consumes this key entry and extracts the keyparameters from it.
+ pub fn into_key_parameters(self) -> Vec<KeyParameter> {
+ self.parameters
+ }
+ /// Exposes the key metadata of this key entry.
+ pub fn metadata(&self) -> &KeyMetaData {
+ &self.metadata
+ }
+}
+
+/// Indicates the sub component of a key entry for persistent storage.
+#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub struct SubComponentType(u32);
+impl SubComponentType {
+ /// Persistent identifier for a key blob.
+ pub const KEY_BLOB: SubComponentType = Self(0);
+ /// Persistent identifier for a certificate blob.
+ pub const CERT: SubComponentType = Self(1);
+ /// Persistent identifier for a certificate chain blob.
+ pub const CERT_CHAIN: SubComponentType = Self(2);
+}
+
+impl ToSql for SubComponentType {
+ fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
+ self.0.to_sql()
+ }
+}
+
+impl FromSql for SubComponentType {
+ fn column_result(value: ValueRef) -> FromSqlResult<Self> {
+ Ok(Self(u32::column_result(value)?))
+ }
+}
+
+/// KeystoreDB wraps a connection to an SQLite database and tracks its
+/// ownership. It also implements all of Keystore 2.0's database functionality.
+pub struct KeystoreDB {
+ conn: Connection,
+}
+
+/// Database representation of the monotonic time retrieved from the system call clock_gettime with
+/// CLOCK_MONOTONIC_RAW. Stores monotonic time as i64 in seconds.
+#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)]
+pub struct MonotonicRawTime(i64);
+
+impl MonotonicRawTime {
+ /// Constructs a new MonotonicRawTime
+ pub fn now() -> Self {
+ Self(get_current_time_in_seconds())
+ }
+
+ /// Returns the integer value of MonotonicRawTime as i64
+ pub fn seconds(&self) -> i64 {
+ self.0
+ }
+}
+
+impl ToSql for MonotonicRawTime {
+ fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
+ Ok(ToSqlOutput::Owned(Value::Integer(self.0)))
+ }
+}
+
+impl FromSql for MonotonicRawTime {
+ fn column_result(value: ValueRef) -> FromSqlResult<Self> {
+ Ok(Self(i64::column_result(value)?))
+ }
+}
+
+/// This struct encapsulates the information to be stored in the database about the auth tokens
+/// received by keystore.
+pub struct AuthTokenEntry {
+ auth_token: HardwareAuthToken,
+ time_received: MonotonicRawTime,
+}
+
+impl AuthTokenEntry {
+ fn new(auth_token: HardwareAuthToken, time_received: MonotonicRawTime) -> Self {
+ AuthTokenEntry { auth_token, time_received }
+ }
+
+ /// Checks if this auth token satisfies the given authentication information.
+ pub fn satisfies_auth(
+ auth_token: &HardwareAuthToken,
+ user_secure_ids: &[i64],
+ auth_type: HardwareAuthenticatorType,
+ ) -> bool {
+ user_secure_ids.iter().any(|&sid| {
+ (sid == auth_token.userId || sid == auth_token.authenticatorId)
+ && (((auth_type.0 as i32) & (auth_token.authenticatorType.0 as i32)) != 0)
+ })
+ }
+
+ fn is_newer_than(&self, other: &AuthTokenEntry) -> bool {
+ // NOTE: Although in legacy keystore both timestamp and time_received are involved in this
+ // check, we decided to only consider time_received in keystore2 code.
+ self.time_received.seconds() > other.time_received.seconds()
+ }
+
+ /// Returns the auth token wrapped by the AuthTokenEntry
+ pub fn get_auth_token(self) -> HardwareAuthToken {
+ self.auth_token
+ }
+}
+
+impl KeystoreDB {
+ /// This will create a new database connection connecting the two
+ /// files persistent.sqlite and perboot.sqlite in the given directory.
+ /// It also attempts to initialize all of the tables.
+ /// KeystoreDB cannot be used by multiple threads.
+ /// Each thread should open their own connection using `thread_local!`.
+ pub fn new(db_root: &Path) -> Result<Self> {
+ // Build the path to the sqlite files.
+ let mut persistent_path = db_root.to_path_buf();
+ persistent_path.push("persistent.sqlite");
+ let mut perboot_path = db_root.to_path_buf();
+ perboot_path.push("perboot.sqlite");
+
+ // Now convert them to strings prefixed with "file:"
+ let mut persistent_path_str = "file:".to_owned();
+ persistent_path_str.push_str(&persistent_path.to_string_lossy());
+ let mut perboot_path_str = "file:".to_owned();
+ perboot_path_str.push_str(&perboot_path.to_string_lossy());
+
+ let conn = Self::make_connection(&persistent_path_str, &perboot_path_str)?;
+
+ Self::init_tables(&conn)?;
+ Ok(Self { conn })
+ }
+
+ fn init_tables(conn: &Connection) -> Result<()> {
+ conn.execute(
+ "CREATE TABLE IF NOT EXISTS persistent.keyentry (
+ id INTEGER UNIQUE,
+ key_type INTEGER,
+ domain INTEGER,
+ namespace INTEGER,
+ alias BLOB);",
+ 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);",
+ NO_PARAMS,
+ )
+ .context("Failed to initialize \"blobentry\" table.")?;
+
+ conn.execute(
+ "CREATE TABLE IF NOT EXISTS persistent.keyparameter (
+ keyentryid INTEGER,
+ tag INTEGER,
+ data ANY,
+ security_level INTEGER);",
+ NO_PARAMS,
+ )
+ .context("Failed to initialize \"keyparameter\" table.")?;
+
+ conn.execute(
+ "CREATE TABLE IF NOT EXISTS persistent.keymetadata (
+ keyentryid INTEGER,
+ tag INTEGER,
+ data ANY);",
+ NO_PARAMS,
+ )
+ .context("Failed to initialize \"keymetadata\" table.")?;
+
+ conn.execute(
+ "CREATE TABLE IF NOT EXISTS persistent.grant (
+ id INTEGER UNIQUE,
+ grantee INTEGER,
+ keyentryid INTEGER,
+ access_vector INTEGER);",
+ NO_PARAMS,
+ )
+ .context("Failed to initialize \"grant\" table.")?;
+
+ //TODO: only drop the following two perboot tables if this is the first start up
+ //during the boot (b/175716626).
+ // conn.execute("DROP TABLE IF EXISTS perboot.authtoken;", NO_PARAMS)
+ // .context("Failed to drop perboot.authtoken table")?;
+ conn.execute(
+ "CREATE TABLE IF NOT EXISTS perboot.authtoken (
+ id INTEGER PRIMARY KEY,
+ challenge INTEGER,
+ user_id INTEGER,
+ auth_id INTEGER,
+ authenticator_type INTEGER,
+ timestamp INTEGER,
+ mac BLOB,
+ time_received INTEGER,
+ UNIQUE(user_id, auth_id, authenticator_type));",
+ NO_PARAMS,
+ )
+ .context("Failed to initialize \"authtoken\" table.")?;
+
+ // conn.execute("DROP TABLE IF EXISTS perboot.metadata;", NO_PARAMS)
+ // .context("Failed to drop perboot.metadata table")?;
+ // metadata table stores certain miscellaneous information required for keystore functioning
+ // during a boot cycle, as key-value pairs.
+ conn.execute(
+ "CREATE TABLE IF NOT EXISTS perboot.metadata (
+ key TEXT,
+ value BLOB,
+ UNIQUE(key));",
+ NO_PARAMS,
+ )
+ .context("Failed to initialize \"metadata\" table.")?;
+
+ Ok(())
+ }
+
+ fn make_connection(persistent_file: &str, perboot_file: &str) -> Result<Connection> {
+ let conn =
+ Connection::open_in_memory().context("Failed to initialize SQLite connection.")?;
+
+ conn.execute("ATTACH DATABASE ? as persistent;", params![persistent_file])
+ .context("Failed to attach database persistent.")?;
+ conn.execute("ATTACH DATABASE ? as perboot;", params![perboot_file])
+ .context("Failed to attach database perboot.")?;
+
+ Ok(conn)
+ }
+
+ /// Atomically loads a key entry and associated metadata or creates it using the
+ /// callback create_new_key callback. The callback is called during a database
+ /// transaction. This means that implementers should be mindful about using
+ /// blocking operations such as IPC or grabbing mutexes.
+ pub fn get_or_create_key_with<F>(
+ &mut self,
+ domain: Domain,
+ namespace: i64,
+ alias: &str,
+ create_new_key: F,
+ ) -> Result<(KeyIdGuard, KeyEntry)>
+ where
+ F: FnOnce() -> Result<(Vec<u8>, KeyMetaData)>,
+ {
+ let tx = self
+ .conn
+ .transaction_with_behavior(TransactionBehavior::Immediate)
+ .context("In get_or_create_key_with: Failed to initialize transaction.")?;
+
+ let id = {
+ let mut stmt = tx
+ .prepare(
+ "SELECT id FROM persistent.keyentry
+ WHERE
+ key_type = ?
+ AND domain = ?
+ AND namespace = ?
+ AND alias = ?;",
+ )
+ .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])
+ .context("In get_or_create_key_with: Failed to query from keyentry table.")?;
+
+ db_utils::with_rows_extract_one(&mut rows, |row| {
+ Ok(match row {
+ Some(r) => r.get(0).context("Failed to unpack id.")?,
+ None => None,
+ })
+ })
+ .context("In get_or_create_key_with.")?
+ };
+
+ let (id, entry) = match id {
+ Some(id) => (
+ id,
+ Self::load_key_components(&tx, KeyEntryLoadBits::KM, id)
+ .context("In get_or_create_key_with.")?,
+ ),
+
+ None => {
+ let id = Self::insert_with_retry(|id| {
+ tx.execute(
+ "INSERT into persistent.keyentry
+ (id, key_type, domain, namespace, alias)
+ VALUES(?, ?, ?, ?, ?);",
+ params![id, KeyType::Super, domain.0, namespace, alias],
+ )
+ })
+ .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.")?;
+ metadata.store_in_db(id, &tx).context("In get_or_create_key_with.")?;
+ (id, KeyEntry { id, km_blob: Some(blob), metadata, ..Default::default() })
+ }
+ };
+ tx.commit().context("In get_or_create_key_with: Failed to commit transaction.")?;
+ Ok((KEY_ID_LOCK.get(id), entry))
+ }
+
+ /// 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> {
+ match domain {
+ Domain::APP | Domain::SELINUX => {}
+ _ => {
+ return Err(KsError::sys())
+ .context(format!("Domain {:?} must be either App or SELinux.", domain));
+ }
+ }
+ Ok(KEY_ID_LOCK.get(
+ Self::insert_with_retry(|id| {
+ self.conn.execute(
+ "INSERT into persistent.keyentry
+ (id, key_type, domain, namespace, alias)
+ VALUES(?, ?, ?, ?, NULL);",
+ params![id, KeyType::Client, domain.0 as u32, namespace],
+ )
+ })
+ .context("In create_key_entry")?,
+ ))
+ }
+
+ /// Inserts a new blob and associates it with the given key id. Each blob
+ /// has a sub component type and a security level.
+ /// Each key can have one of each sub component type associated. If more
+ /// are added only the most recent can be retrieved, and superseded blobs
+ /// will get garbage collected. The security level field of components
+ /// other than `SubComponentType::KEY_BLOB` are ignored.
+ pub fn insert_blob(
+ &mut self,
+ 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.")
+ }
+
+ fn insert_blob_internal(
+ tx: &Transaction,
+ 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],
+ )
+ .context("In insert_blob_internal: Failed to insert blob.")?;
+ Ok(())
+ }
+
+ /// Inserts a collection of key parameters into the `persistent.keyparameter` table
+ /// and associates them with the given `key_id`.
+ pub fn insert_keyparameter<'a>(
+ &mut self,
+ 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.")?;
+
+ 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))?;
+ }
+ }
+ tx.commit().context("In insert_keyparameter: Failed to commit transaction.")?;
+ Ok(())
+ }
+
+ /// Insert a set of key entry specific metadata into the database.
+ pub fn insert_key_metadata(
+ &mut self,
+ 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")
+ }
+
+ /// 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,
+ newid: &KeyIdGuard,
+ alias: &str,
+ domain: Domain,
+ namespace: i64,
+ ) -> Result<()> {
+ match domain {
+ Domain::APP | Domain::SELINUX => {}
+ _ => {
+ return Err(KsError::sys()).context(format!(
+ "In rebind_alias: 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
+ WHERE alias = ? AND domain = ? AND namespace = ?;",
+ params![alias, domain.0 as u32, namespace],
+ )
+ .context("In rebind_alias: Failed to rebind existing entry.")?;
+ let result = tx
+ .execute(
+ "UPDATE persistent.keyentry
+ SET alias = ?
+ WHERE id = ? AND domain = ? AND namespace = ?;",
+ params![alias, newid.0, domain.0 as u32, namespace],
+ )
+ .context("In rebind_alias: 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 {}.",
+ result
+ ));
+ }
+ tx.commit().context("In rebind_alias: Failed to commit transaction.")
+ }
+
+ // Helper function loading the key_id given the key descriptor
+ // tuple comprising domain, namespace, and alias.
+ // Requires a valid transaction.
+ fn load_key_entry_id(tx: &Transaction, key: &KeyDescriptor, key_type: KeyType) -> Result<i64> {
+ let alias = key
+ .alias
+ .as_ref()
+ .map_or_else(|| Err(KsError::sys()), Ok)
+ .context("In load_key_entry_id: Alias must be specified.")?;
+ let mut stmt = tx
+ .prepare(
+ "SELECT id FROM persistent.keyentry
+ WHERE
+ key_type = ?
+ AND domain = ?
+ AND namespace = ?
+ AND alias = ?;",
+ )
+ .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])
+ .context("In load_key_entry_id: Failed to read from keyentry table.")?;
+ db_utils::with_rows_extract_one(&mut rows, |row| {
+ row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?
+ .get(0)
+ .context("Failed to unpack id.")
+ })
+ .context("In load_key_entry_id.")
+ }
+
+ /// This helper function completes the access tuple of a key, which is required
+ /// to perform access control. The strategy depends on the `domain` field in the
+ /// key descriptor.
+ /// * Domain::SELINUX: The access tuple is complete and this function only loads
+ /// the key_id for further processing.
+ /// * Domain::APP: Like Domain::SELINUX, but the tuple is completed by `caller_uid`
+ /// which serves as the namespace.
+ /// * Domain::GRANT: The grant table is queried for the `key_id` and the
+ /// `access_vector`.
+ /// * Domain::KEY_ID: The keyentry table is queried for the owning `domain` and
+ /// `namespace`.
+ /// In each case the information returned is sufficient to perform the access
+ /// check and the key id can be used to load further key artifacts.
+ fn load_access_tuple(
+ tx: &Transaction,
+ key: KeyDescriptor,
+ key_type: KeyType,
+ caller_uid: u32,
+ ) -> Result<(i64, KeyDescriptor, Option<KeyPermSet>)> {
+ match key.domain {
+ // Domain App or SELinux. In this case we load the key_id from
+ // the keyentry database for further loading of key components.
+ // We already have the full access tuple to perform access control.
+ // The only distinction is that we use the caller_uid instead
+ // of the caller supplied namespace if the domain field is
+ // Domain::APP.
+ Domain::APP | Domain::SELINUX => {
+ let mut access_key = key;
+ if access_key.domain == Domain::APP {
+ access_key.nspace = caller_uid as i64;
+ }
+ let key_id = Self::load_key_entry_id(&tx, &access_key, key_type)
+ .with_context(|| format!("With key.domain = {:?}.", access_key.domain))?;
+
+ Ok((key_id, access_key, None))
+ }
+
+ // Domain::GRANT. In this case we load the key_id and the access_vector
+ // from the grant table.
+ Domain::GRANT => {
+ let mut stmt = tx
+ .prepare(
+ "SELECT keyentryid, access_vector FROM persistent.grant
+ WHERE grantee = ? AND id = ?;",
+ )
+ .context("Domain::GRANT prepare statement failed")?;
+ let mut rows = stmt
+ .query(params![caller_uid as i64, key.nspace])
+ .context("Domain:Grant: query failed.")?;
+ let (key_id, access_vector): (i64, i32) =
+ db_utils::with_rows_extract_one(&mut rows, |row| {
+ let r =
+ row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?;
+ Ok((
+ r.get(0).context("Failed to unpack key_id.")?,
+ r.get(1).context("Failed to unpack access_vector.")?,
+ ))
+ })
+ .context("Domain::GRANT.")?;
+ Ok((key_id, key, Some(access_vector.into())))
+ }
+
+ // Domain::KEY_ID. In this case we load the domain and namespace from the
+ // keyentry database because we need them for access control.
+ Domain::KEY_ID => {
+ let mut stmt = tx
+ .prepare(
+ "SELECT domain, namespace FROM persistent.keyentry
+ WHERE
+ id = ?;",
+ )
+ .context("Domain::KEY_ID: prepare statement failed")?;
+ let mut rows =
+ stmt.query(params![key.nspace]).context("Domain::KEY_ID: query failed.")?;
+ let (domain, namespace): (Domain, i64) =
+ db_utils::with_rows_extract_one(&mut rows, |row| {
+ let r =
+ row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?;
+ Ok((
+ Domain(r.get(0).context("Failed to unpack domain.")?),
+ r.get(1).context("Failed to unpack namespace.")?,
+ ))
+ })
+ .context("Domain::KEY_ID.")?;
+ let key_id = key.nspace;
+ let mut access_key = key;
+ access_key.domain = domain;
+ access_key.nspace = namespace;
+
+ Ok((key_id, access_key, None))
+ }
+ _ => Err(anyhow!(KsError::sys())),
+ }
+ }
+
+ fn load_blob_components(
+ key_id: i64,
+ load_bits: KeyEntryLoadBits,
+ tx: &Transaction,
+ ) -> Result<(SecurityLevel, 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
+ WHERE keyentryid = ? GROUP BY subcomponent_type;",
+ )
+ .context("In load_blob_components: prepare statement failed.")?;
+
+ let mut rows =
+ stmt.query(params![key_id]).context("In load_blob_components: query failed.")?;
+
+ let mut 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.")?);
+ }
+ }
+ (SubComponentType::CERT, true) => {
+ cert_blob =
+ Some(row.get(3).context("Failed to extract public certificate blob.")?);
+ }
+ (SubComponentType::CERT_CHAIN, true) => {
+ cert_chain_blob =
+ Some(row.get(3).context("Failed to extract certificate chain blob.")?);
+ }
+ (SubComponentType::CERT, _) | (SubComponentType::CERT_CHAIN, _) => {}
+ _ => Err(KsError::sys()).context("Unknown subcomponent type.")?,
+ }
+ Ok(())
+ })
+ .context("In load_blob_components.")?;
+
+ Ok((sec_level, km_blob, cert_blob, cert_chain_blob))
+ }
+
+ fn load_key_parameters(key_id: i64, tx: &Transaction) -> Result<Vec<KeyParameter>> {
+ let mut stmt = tx
+ .prepare(
+ "SELECT tag, data, security_level from persistent.keyparameter
+ WHERE keyentryid = ?;",
+ )
+ .context("In load_key_parameters: prepare statement failed.")?;
+
+ let mut parameters: Vec<KeyParameter> = Vec::new();
+
+ let mut rows =
+ stmt.query(params![key_id]).context("In load_key_parameters: query failed.")?;
+ db_utils::with_rows_extract_all(&mut rows, |row| {
+ let tag = Tag(row.get(0).context("Failed to read tag.")?);
+ let sec_level = SecurityLevel(row.get(2).context("Failed to read sec_level.")?);
+ parameters.push(
+ KeyParameter::new_from_sql(tag, &SqlField::new(1, &row), sec_level)
+ .context("Failed to read KeyParameter.")?,
+ );
+ Ok(())
+ })
+ .context("In load_key_parameters.")?;
+
+ Ok(parameters)
+ }
+
+ /// Load a key entry by the given key descriptor.
+ /// It uses the `check_permission` callback to verify if the access is allowed
+ /// given the key access tuple read from the database using `load_access_tuple`.
+ /// With `load_bits` the caller may specify which blobs shall be loaded from
+ /// the blob database.
+ pub fn load_key_entry(
+ &mut self,
+ key: KeyDescriptor,
+ key_type: KeyType,
+ load_bits: KeyEntryLoadBits,
+ caller_uid: u32,
+ check_permission: impl FnOnce(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>,
+ ) -> Result<(KeyIdGuard, KeyEntry)> {
+ // KEY ID LOCK 1/2
+ // If we got a key descriptor with a key id we can get the lock right away.
+ // Otherwise we have to defer it until we know the key id.
+ let key_id_guard = match key.domain {
+ Domain::KEY_ID => Some(KEY_ID_LOCK.get(key.nspace)),
+ _ => None,
+ };
+
+ let tx = self
+ .conn
+ .unchecked_transaction()
+ .context("In load_key_entry: Failed to initialize transaction.")?;
+
+ // Load the key_id and complete the access control tuple.
+ let (key_id, access_key_descriptor, access_vector) =
+ Self::load_access_tuple(&tx, key, key_type, caller_uid)
+ .context("In load_key_entry.")?;
+
+ // Perform access control. It is vital that we return here if the permission is denied.
+ // So do not touch that '?' at the end.
+ check_permission(&access_key_descriptor, access_vector).context("In load_key_entry.")?;
+
+ // KEY ID LOCK 2/2
+ // If we did not get a key id lock by now, it was because we got a key descriptor
+ // without a key id. At this point we got the key id, so we can try and get a lock.
+ // However, we cannot block here, because we are in the middle of the transaction.
+ // So first we try to get the lock non blocking. If that fails, we roll back the
+ // transaction and block until we get the lock. After we successfully got the lock,
+ // we start a new transaction and load the access tuple again.
+ //
+ // We don't need to perform access control again, because we already established
+ // that the caller had access to the given key. But we need to make sure that the
+ // key id still exists. So we have to load the key entry by key id this time.
+ let (key_id_guard, tx) = match key_id_guard {
+ None => match KEY_ID_LOCK.try_get(key_id) {
+ None => {
+ // Roll back the transaction.
+ tx.rollback().context("In load_key_entry: Failed to roll back transaction.")?;
+
+ // Block until we have a key id lock.
+ let key_id_guard = KEY_ID_LOCK.get(key_id);
+
+ // Create a new transaction.
+ let tx = self.conn.unchecked_transaction().context(
+ "In load_key_entry: Failed to initialize transaction. (deferred key lock)",
+ )?;
+
+ Self::load_access_tuple(
+ &tx,
+ // This time we have to load the key by the retrieved key id, because the
+ // alias may have been rebound after we rolled back the transaction.
+ KeyDescriptor {
+ domain: Domain::KEY_ID,
+ nspace: key_id,
+ ..Default::default()
+ },
+ key_type,
+ caller_uid,
+ )
+ .context("In load_key_entry. (deferred key lock)")?;
+ (key_id_guard, tx)
+ }
+ Some(l) => (l, tx),
+ },
+ Some(key_id_guard) => (key_id_guard, tx),
+ };
+
+ let key_entry = Self::load_key_components(&tx, load_bits, key_id_guard.id())
+ .context("In load_key_entry.")?;
+
+ tx.commit().context("In load_key_entry: Failed to commit transaction.")?;
+
+ Ok((key_id_guard, key_entry))
+ }
+
+ fn load_key_components(
+ tx: &Transaction,
+ load_bits: KeyEntryLoadBits,
+ key_id: i64,
+ ) -> Result<KeyEntry> {
+ let metadata = KeyMetaData::load_from_db(key_id, &tx).context("In load_key_components.")?;
+
+ let (sec_level, 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.")?;
+
+ Ok(KeyEntry {
+ id: key_id,
+ km_blob,
+ cert: cert_blob,
+ cert_chain: cert_chain_blob,
+ sec_level,
+ parameters,
+ metadata,
+ })
+ }
+
+ /// Returns a list of KeyDescriptors in the selected domain/namespace.
+ /// The key descriptors will have the domain, nspace, and alias field set.
+ /// Domain must be APP or SELINUX, the caller must make sure of that.
+ pub fn list(&mut self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
+ let mut stmt = self
+ .conn
+ .prepare(
+ "SELECT alias FROM persistent.keyentry
+ WHERE domain = ? AND namespace = ? AND alias IS NOT NULL;",
+ )
+ .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 descriptors: Vec<KeyDescriptor> = Vec::new();
+ db_utils::with_rows_extract_all(&mut rows, |row| {
+ descriptors.push(KeyDescriptor {
+ domain,
+ nspace: namespace,
+ alias: Some(row.get(0).context("Trying to extract alias.")?),
+ blob: None,
+ });
+ Ok(())
+ })
+ .context("In list.")?;
+ Ok(descriptors)
+ }
+
+ /// Adds a grant to the grant table.
+ /// Like `load_key_entry` this function loads the access tuple before
+ /// it uses the callback for a permission check. Upon success,
+ /// it inserts the `grantee_uid`, `key_id`, and `access_vector` into the
+ /// grant table. The new row will have a randomized id, which is used as
+ /// grant id in the namespace field of the resulting KeyDescriptor.
+ pub fn grant(
+ &mut self,
+ key: KeyDescriptor,
+ caller_uid: u32,
+ grantee_uid: u32,
+ access_vector: KeyPermSet,
+ check_permission: impl FnOnce(&KeyDescriptor, &KeyPermSet) -> Result<()>,
+ ) -> Result<KeyDescriptor> {
+ let tx = self
+ .conn
+ .transaction_with_behavior(TransactionBehavior::Immediate)
+ .context("In grant: Failed to initialize transaction.")?;
+
+ // Load the key_id and complete the access control tuple.
+ // We ignore the access vector here because grants cannot be granted.
+ // The access vector returned here expresses the permissions the
+ // grantee has if key.domain == Domain::GRANT. But this vector
+ // cannot include the grant permission by design, so there is no way the
+ // subsequent permission check can pass.
+ // We could check key.domain == Domain::GRANT and fail early.
+ // But even if we load the access tuple by grant here, the permission
+ // check denies the attempt to create a grant by grant descriptor.
+ let (key_id, access_key_descriptor, _) =
+ Self::load_access_tuple(&tx, key, KeyType::Client, caller_uid).context("In grant")?;
+
+ // Perform access control. It is vital that we return here if the permission
+ // was denied. So do not touch that '?' at the end of the line.
+ // This permission check checks if the caller has the grant permission
+ // for the given key and in addition to all of the permissions
+ // expressed in `access_vector`.
+ check_permission(&access_key_descriptor, &access_vector)
+ .context("In grant: check_permission failed.")?;
+
+ let grant_id = if let Some(grant_id) = tx
+ .query_row(
+ "SELECT id FROM persistent.grant
+ WHERE keyentryid = ? AND grantee = ?;",
+ params![key_id, grantee_uid],
+ |row| row.get(0),
+ )
+ .optional()
+ .context("In grant: Failed get optional existing grant id.")?
+ {
+ tx.execute(
+ "UPDATE persistent.grant
+ SET access_vector = ?
+ WHERE id = ?;",
+ params![i32::from(access_vector), grant_id],
+ )
+ .context("In grant: Failed to update existing grant.")?;
+ grant_id
+ } else {
+ Self::insert_with_retry(|id| {
+ tx.execute(
+ "INSERT INTO persistent.grant (id, grantee, keyentryid, access_vector)
+ VALUES (?, ?, ?, ?);",
+ params![id, grantee_uid, key_id, i32::from(access_vector)],
+ )
+ })
+ .context("In grant")?
+ };
+ tx.commit().context("In grant: failed to commit transaction.")?;
+
+ Ok(KeyDescriptor { domain: Domain::GRANT, nspace: grant_id, alias: None, blob: None })
+ }
+
+ /// This function checks permissions like `grant` and `load_key_entry`
+ /// before removing a grant from the grant table.
+ pub fn ungrant(
+ &mut self,
+ key: KeyDescriptor,
+ caller_uid: u32,
+ grantee_uid: u32,
+ check_permission: impl FnOnce(&KeyDescriptor) -> Result<()>,
+ ) -> Result<()> {
+ let tx = self
+ .conn
+ .transaction_with_behavior(TransactionBehavior::Immediate)
+ .context("In ungrant: Failed to initialize transaction.")?;
+
+ // Load the key_id and complete the access control tuple.
+ // We ignore the access vector here because grants cannot be granted.
+ let (key_id, access_key_descriptor, _) =
+ Self::load_access_tuple(&tx, key, KeyType::Client, caller_uid)
+ .context("In ungrant.")?;
+
+ // Perform access control. We must return here if the permission
+ // was denied. So do not touch the '?' at the end of this line.
+ check_permission(&access_key_descriptor).context("In grant: check_permission failed.")?;
+
+ tx.execute(
+ "DELETE FROM persistent.grant
+ WHERE keyentryid = ? AND grantee = ?;",
+ params![key_id, grantee_uid],
+ )
+ .context("Failed to delete grant.")?;
+
+ tx.commit().context("In ungrant: failed to commit transaction.")?;
+
+ Ok(())
+ }
+
+ // Generates a random id and passes it to the given function, which will
+ // try to insert it into a database. If that insertion fails, retry;
+ // otherwise return the id.
+ fn insert_with_retry(inserter: impl Fn(i64) -> rusqlite::Result<usize>) -> Result<i64> {
+ loop {
+ let newid: i64 = random();
+ match inserter(newid) {
+ // If the id already existed, try again.
+ Err(rusqlite::Error::SqliteFailure(
+ libsqlite3_sys::Error {
+ code: libsqlite3_sys::ErrorCode::ConstraintViolation,
+ extended_code: libsqlite3_sys::SQLITE_CONSTRAINT_UNIQUE,
+ },
+ _,
+ )) => (),
+ Err(e) => {
+ return Err(e).context("In insert_with_retry: failed to insert into database.")
+ }
+ _ => return Ok(newid),
+ }
+ }
+ }
+
+ /// Insert or replace the auth token based on the UNIQUE constraint of the auth token table
+ pub fn insert_auth_token(&mut self, auth_token: &HardwareAuthToken) -> Result<()> {
+ self.conn
+ .execute(
+ "INSERT OR REPLACE INTO perboot.authtoken (challenge, user_id, auth_id,
+ authenticator_type, timestamp, mac, time_received) VALUES(?, ?, ?, ?, ?, ?, ?);",
+ params![
+ auth_token.challenge,
+ auth_token.userId,
+ auth_token.authenticatorId,
+ auth_token.authenticatorType.0 as i32,
+ auth_token.timestamp.milliSeconds as i64,
+ auth_token.mac,
+ MonotonicRawTime::now(),
+ ],
+ )
+ .context("In insert_auth_token: failed to insert auth token into the database")?;
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use super::*;
+ use crate::key_parameter::{
+ Algorithm, BlockMode, Digest, EcCurve, HardwareAuthenticatorType, KeyOrigin, KeyParameter,
+ KeyParameterValue, KeyPurpose, PaddingMode, SecurityLevel,
+ };
+ use crate::key_perm_set;
+ use crate::permission::{KeyPerm, KeyPermSet};
+ use crate::test::utils::TempDir;
+ use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ HardwareAuthToken::HardwareAuthToken,
+ HardwareAuthenticatorType::HardwareAuthenticatorType as kmhw_authenticator_type,
+ Timestamp::Timestamp,
+ };
+ use rusqlite::Error;
+ use rusqlite::NO_PARAMS;
+ use std::cell::RefCell;
+ use std::sync::atomic::{AtomicU8, Ordering};
+ use std::sync::Arc;
+ use std::thread;
+ use std::time::SystemTime;
+
+ fn new_test_db() -> Result<KeystoreDB> {
+ let conn = KeystoreDB::make_connection("file::memory:", "file::memory:")?;
+
+ KeystoreDB::init_tables(&conn).context("Failed to initialize tables.")?;
+ Ok(KeystoreDB { conn })
+ }
+
+ #[test]
+ fn datetime() -> Result<()> {
+ let conn = Connection::open_in_memory()?;
+ conn.execute("CREATE TABLE test (ts DATETIME);", NO_PARAMS)?;
+ let now = SystemTime::now();
+ let duration = Duration::from_secs(1000);
+ let then = now.checked_sub(duration).unwrap();
+ let soon = now.checked_add(duration).unwrap();
+ conn.execute(
+ "INSERT INTO test (ts) VALUES (?), (?), (?);",
+ params![DateTime::try_from(now)?, DateTime::try_from(then)?, DateTime::try_from(soon)?],
+ )?;
+ let mut stmt = conn.prepare("SELECT ts FROM test ORDER BY ts ASC;")?;
+ let mut rows = stmt.query(NO_PARAMS)?;
+ assert_eq!(DateTime::try_from(then)?, rows.next()?.unwrap().get(0)?);
+ assert_eq!(DateTime::try_from(now)?, rows.next()?.unwrap().get(0)?);
+ assert_eq!(DateTime::try_from(soon)?, rows.next()?.unwrap().get(0)?);
+ assert!(rows.next()?.is_none());
+ assert!(DateTime::try_from(then)? < DateTime::try_from(now)?);
+ assert!(DateTime::try_from(then)? < DateTime::try_from(soon)?);
+ assert!(DateTime::try_from(now)? < DateTime::try_from(soon)?);
+ Ok(())
+ }
+
+ // Ensure that we're using the "injected" random function, not the real one.
+ #[test]
+ fn test_mocked_random() {
+ let rand1 = random();
+ let rand2 = random();
+ let rand3 = random();
+ if rand1 == rand2 {
+ assert_eq!(rand2 + 1, rand3);
+ } else {
+ assert_eq!(rand1 + 1, rand2);
+ assert_eq!(rand2, rand3);
+ }
+ }
+
+ // Test that we have the correct tables.
+ #[test]
+ fn test_tables() -> Result<()> {
+ let db = new_test_db()?;
+ let tables = db
+ .conn
+ .prepare("SELECT name from persistent.sqlite_master WHERE type='table' ORDER BY name;")?
+ .query_map(params![], |row| row.get(0))?
+ .collect::<rusqlite::Result<Vec<String>>>()?;
+ assert_eq!(tables.len(), 5);
+ assert_eq!(tables[0], "blobentry");
+ assert_eq!(tables[1], "grant");
+ assert_eq!(tables[2], "keyentry");
+ assert_eq!(tables[3], "keymetadata");
+ assert_eq!(tables[4], "keyparameter");
+ let tables = db
+ .conn
+ .prepare("SELECT name from perboot.sqlite_master WHERE type='table' ORDER BY name;")?
+ .query_map(params![], |row| row.get(0))?
+ .collect::<rusqlite::Result<Vec<String>>>()?;
+
+ assert_eq!(tables.len(), 2);
+ assert_eq!(tables[0], "authtoken");
+ assert_eq!(tables[1], "metadata");
+ Ok(())
+ }
+
+ #[test]
+ fn test_auth_token_table_invariant() -> Result<()> {
+ let mut db = new_test_db()?;
+ let auth_token1 = HardwareAuthToken {
+ challenge: i64::MAX,
+ userId: 200,
+ authenticatorId: 200,
+ authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+ timestamp: Timestamp { milliSeconds: 500 },
+ mac: String::from("mac").into_bytes(),
+ };
+ db.insert_auth_token(&auth_token1)?;
+ let auth_tokens_returned = get_auth_tokens(&mut db)?;
+ assert_eq!(auth_tokens_returned.len(), 1);
+
+ // insert another auth token with the same values for the columns in the UNIQUE constraint
+ // of the auth token table and different value for timestamp
+ let auth_token2 = HardwareAuthToken {
+ challenge: i64::MAX,
+ userId: 200,
+ authenticatorId: 200,
+ authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+ timestamp: Timestamp { milliSeconds: 600 },
+ mac: String::from("mac").into_bytes(),
+ };
+
+ db.insert_auth_token(&auth_token2)?;
+ let mut auth_tokens_returned = get_auth_tokens(&mut db)?;
+ assert_eq!(auth_tokens_returned.len(), 1);
+
+ if let Some(auth_token) = auth_tokens_returned.pop() {
+ assert_eq!(auth_token.auth_token.timestamp.milliSeconds, 600);
+ }
+
+ // insert another auth token with the different values for the columns in the UNIQUE
+ // constraint of the auth token table
+ let auth_token3 = HardwareAuthToken {
+ challenge: i64::MAX,
+ userId: 201,
+ authenticatorId: 200,
+ authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0),
+ timestamp: Timestamp { milliSeconds: 600 },
+ mac: String::from("mac").into_bytes(),
+ };
+
+ db.insert_auth_token(&auth_token3)?;
+ let auth_tokens_returned = get_auth_tokens(&mut db)?;
+ assert_eq!(auth_tokens_returned.len(), 2);
+
+ Ok(())
+ }
+
+ // utility function for test_auth_token_table_invariant()
+ fn get_auth_tokens(db: &mut KeystoreDB) -> Result<Vec<AuthTokenEntry>> {
+ let mut stmt = db.conn.prepare("SELECT * from perboot.authtoken;")?;
+
+ let auth_token_entries: Vec<AuthTokenEntry> = stmt
+ .query_map(NO_PARAMS, |row| {
+ Ok(AuthTokenEntry::new(
+ HardwareAuthToken {
+ challenge: row.get(1)?,
+ userId: row.get(2)?,
+ authenticatorId: row.get(3)?,
+ authenticatorType: HardwareAuthenticatorType(row.get(4)?),
+ timestamp: Timestamp { milliSeconds: row.get(5)? },
+ mac: row.get(6)?,
+ },
+ row.get(7)?,
+ ))
+ })?
+ .collect::<Result<Vec<AuthTokenEntry>, Error>>()?;
+ Ok(auth_token_entries)
+ }
+
+ #[test]
+ fn test_persistence_for_files() -> Result<()> {
+ let temp_dir = TempDir::new("persistent_db_test")?;
+ let db = KeystoreDB::new(temp_dir.path())?;
+
+ db.create_key_entry(Domain::APP, 100)?;
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 1);
+
+ let db = KeystoreDB::new(temp_dir.path())?;
+
+ let entries_new = get_keyentry(&db)?;
+ assert_eq!(entries, entries_new);
+ Ok(())
+ }
+
+ #[test]
+ fn test_create_key_entry() -> Result<()> {
+ fn extractor(ke: &KeyEntryRow) -> (Domain, i64, Option<&str>) {
+ (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref())
+ }
+
+ let db = new_test_db()?;
+
+ db.create_key_entry(Domain::APP, 100)?;
+ db.create_key_entry(Domain::SELINUX, 101)?;
+
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 2);
+ assert_eq!(extractor(&entries[0]), (Domain::APP, 100, None));
+ assert_eq!(extractor(&entries[1]), (Domain::SELINUX, 101, None));
+
+ // Test that we must pass in a valid Domain.
+ check_result_is_error_containing_string(
+ db.create_key_entry(Domain::GRANT, 102),
+ "Domain Domain(1) must be either App or SELinux.",
+ );
+ check_result_is_error_containing_string(
+ db.create_key_entry(Domain::BLOB, 103),
+ "Domain Domain(3) must be either App or SELinux.",
+ );
+ check_result_is_error_containing_string(
+ db.create_key_entry(Domain::KEY_ID, 104),
+ "Domain Domain(4) must be either App or SELinux.",
+ );
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_rebind_alias() -> Result<()> {
+ fn extractor(ke: &KeyEntryRow) -> (Option<Domain>, Option<i64>, Option<&str>) {
+ (ke.domain, ke.namespace, ke.alias.as_deref())
+ }
+
+ let mut db = new_test_db()?;
+ db.create_key_entry(Domain::APP, 42)?;
+ db.create_key_entry(Domain::APP, 42)?;
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 2);
+ assert_eq!(extractor(&entries[0]), (Some(Domain::APP), Some(42), None));
+ assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None));
+
+ // Test that the first call to rebind_alias sets the alias.
+ db.rebind_alias(&KEY_ID_LOCK.get(entries[0].id), "foo", Domain::APP, 42)?;
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 2);
+ assert_eq!(extractor(&entries[0]), (Some(Domain::APP), Some(42), Some("foo")));
+ assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None));
+
+ // Test that the second call to rebind_alias also empties the old one.
+ db.rebind_alias(&KEY_ID_LOCK.get(entries[1].id), "foo", Domain::APP, 42)?;
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 2);
+ assert_eq!(extractor(&entries[0]), (None, None, None));
+ assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), Some("foo")));
+
+ // Test that we must pass in a valid Domain.
+ check_result_is_error_containing_string(
+ db.rebind_alias(&KEY_ID_LOCK.get(0), "foo", Domain::GRANT, 42),
+ "Domain Domain(1) must be either App or SELinux.",
+ );
+ check_result_is_error_containing_string(
+ db.rebind_alias(&KEY_ID_LOCK.get(0), "foo", Domain::BLOB, 42),
+ "Domain Domain(3) must be either App or SELinux.",
+ );
+ check_result_is_error_containing_string(
+ db.rebind_alias(&KEY_ID_LOCK.get(0), "foo", Domain::KEY_ID, 42),
+ "Domain Domain(4) must be either App or SELinux.",
+ );
+
+ // Test that we correctly handle setting an alias for something that does not exist.
+ check_result_is_error_containing_string(
+ db.rebind_alias(&KEY_ID_LOCK.get(0), "foo", Domain::SELINUX, 42),
+ "Expected to update a single entry but instead updated 0",
+ );
+ // Test that we correctly abort the transaction in this case.
+ let entries = get_keyentry(&db)?;
+ assert_eq!(entries.len(), 2);
+ assert_eq!(extractor(&entries[0]), (None, None, None));
+ assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), Some("foo")));
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_grant_ungrant() -> Result<()> {
+ const CALLER_UID: u32 = 15;
+ const GRANTEE_UID: u32 = 12;
+ const SELINUX_NAMESPACE: i64 = 7;
+
+ let mut db = new_test_db()?;
+ db.conn.execute(
+ "INSERT INTO persistent.keyentry (id, key_type, domain, namespace, alias)
+ VALUES (1, 0, 0, 15, 'key'), (2, 0, 2, 7, 'yek');",
+ NO_PARAMS,
+ )?;
+ let app_key = KeyDescriptor {
+ domain: super::Domain::APP,
+ nspace: 0,
+ alias: Some("key".to_string()),
+ blob: None,
+ };
+ const PVEC1: KeyPermSet = key_perm_set![KeyPerm::use_(), KeyPerm::get_info()];
+ const PVEC2: KeyPermSet = key_perm_set![KeyPerm::use_()];
+
+ // Reset totally predictable random number generator in case we
+ // are not the first test running on this thread.
+ reset_random();
+ let next_random = 0i64;
+
+ let app_granted_key =
+ db.grant(app_key.clone(), CALLER_UID, GRANTEE_UID, PVEC1, |k, a| {
+ assert_eq!(*a, PVEC1);
+ assert_eq!(
+ *k,
+ KeyDescriptor {
+ domain: super::Domain::APP,
+ // namespace must be set to the caller_uid.
+ nspace: CALLER_UID as i64,
+ alias: Some("key".to_string()),
+ blob: None,
+ }
+ );
+ Ok(())
+ })?;
+
+ assert_eq!(
+ app_granted_key,
+ KeyDescriptor {
+ domain: super::Domain::GRANT,
+ // The grantid is next_random due to the mock random number generator.
+ nspace: next_random,
+ alias: None,
+ blob: None,
+ }
+ );
+
+ let selinux_key = KeyDescriptor {
+ domain: super::Domain::SELINUX,
+ nspace: SELINUX_NAMESPACE,
+ alias: Some("yek".to_string()),
+ blob: None,
+ };
+
+ let selinux_granted_key =
+ db.grant(selinux_key.clone(), CALLER_UID, 12, PVEC1, |k, a| {
+ assert_eq!(*a, PVEC1);
+ assert_eq!(
+ *k,
+ KeyDescriptor {
+ domain: super::Domain::SELINUX,
+ // namespace must be the supplied SELinux
+ // namespace.
+ nspace: SELINUX_NAMESPACE,
+ alias: Some("yek".to_string()),
+ blob: None,
+ }
+ );
+ Ok(())
+ })?;
+
+ assert_eq!(
+ selinux_granted_key,
+ KeyDescriptor {
+ domain: super::Domain::GRANT,
+ // The grantid is next_random + 1 due to the mock random number generator.
+ nspace: next_random + 1,
+ alias: None,
+ blob: None,
+ }
+ );
+
+ // This should update the existing grant with PVEC2.
+ let selinux_granted_key =
+ db.grant(selinux_key.clone(), CALLER_UID, 12, PVEC2, |k, a| {
+ assert_eq!(*a, PVEC2);
+ assert_eq!(
+ *k,
+ KeyDescriptor {
+ domain: super::Domain::SELINUX,
+ // namespace must be the supplied SELinux
+ // namespace.
+ nspace: SELINUX_NAMESPACE,
+ alias: Some("yek".to_string()),
+ blob: None,
+ }
+ );
+ Ok(())
+ })?;
+
+ assert_eq!(
+ selinux_granted_key,
+ KeyDescriptor {
+ domain: super::Domain::GRANT,
+ // Same grant id as before. The entry was only updated.
+ nspace: next_random + 1,
+ alias: None,
+ blob: None,
+ }
+ );
+
+ {
+ // Limiting scope of stmt, because it borrows db.
+ let mut stmt = db
+ .conn
+ .prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
+ let mut rows =
+ stmt.query_map::<(i64, u32, i64, KeyPermSet), _, _>(NO_PARAMS, |row| {
+ Ok((
+ row.get(0)?,
+ row.get(1)?,
+ row.get(2)?,
+ KeyPermSet::from(row.get::<_, i32>(3)?),
+ ))
+ })?;
+
+ let r = rows.next().unwrap().unwrap();
+ assert_eq!(r, (next_random, GRANTEE_UID, 1, PVEC1));
+ let r = rows.next().unwrap().unwrap();
+ assert_eq!(r, (next_random + 1, GRANTEE_UID, 2, PVEC2));
+ assert!(rows.next().is_none());
+ }
+
+ debug_dump_keyentry_table(&mut db)?;
+ println!("app_key {:?}", app_key);
+ println!("selinux_key {:?}", selinux_key);
+
+ db.ungrant(app_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
+ db.ungrant(selinux_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
+
+ Ok(())
+ }
+
+ static TEST_KEY_BLOB: &[u8] = b"my test blob";
+ static TEST_CERT_BLOB: &[u8] = b"my test cert";
+ static TEST_CERT_CHAIN_BLOB: &[u8] = b"my test cert_chain";
+
+ #[test]
+ fn test_insert_blob() -> Result<()> {
+ 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,
+ )?;
+
+ let mut stmt = db.conn.prepare(
+ "SELECT subcomponent_type, keyentryid, blob, sec_level FROM persistent.blobentry
+ ORDER BY sec_level ASC;",
+ )?;
+ let mut rows = stmt
+ .query_map::<(SubComponentType, i64, Vec<u8>, i64), _, _>(NO_PARAMS, |row| {
+ Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?))
+ })?;
+ let r = rows.next().unwrap().unwrap();
+ assert_eq!(r, (SubComponentType::KEY_BLOB, 1, TEST_KEY_BLOB.to_vec(), 0));
+ let r = rows.next().unwrap().unwrap();
+ assert_eq!(r, (SubComponentType::CERT, 1, TEST_CERT_BLOB.to_vec(), 1));
+ let r = rows.next().unwrap().unwrap();
+ assert_eq!(r, (SubComponentType::CERT_CHAIN, 1, TEST_CERT_CHAIN_BLOB.to_vec(), 2));
+
+ Ok(())
+ }
+
+ static TEST_ALIAS: &str = "my super duper key";
+
+ #[test]
+ fn test_insert_and_load_full_keyentry_domain_app() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS)
+ .context("test_insert_and_load_full_keyentry_domain_app")?
+ .0;
+ let (_key_guard, key_entry) = db.load_key_entry(
+ KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 1,
+ |_k, _av| Ok(()),
+ )?;
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id));
+ Ok(())
+ }
+
+ #[test]
+ fn test_insert_and_load_full_keyentry_domain_selinux() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS)
+ .context("test_insert_and_load_full_keyentry_domain_selinux")?
+ .0;
+ let (_key_guard, key_entry) = db.load_key_entry(
+ KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 1,
+ |_k, _av| Ok(()),
+ )?;
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id));
+ Ok(())
+ }
+
+ #[test]
+ fn test_insert_and_load_full_keyentry_domain_key_id() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS)
+ .context("test_insert_and_load_full_keyentry_domain_key_id")?
+ .0;
+ let (_key_guard, key_entry) = db.load_key_entry(
+ KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 1,
+ |_k, _av| Ok(()),
+ )?;
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id));
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_insert_and_load_full_keyentry_from_grant() -> Result<()> {
+ let mut db = new_test_db()?;
+ let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS)
+ .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(()),
+ )?;
+
+ 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(())
+ })?;
+
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id));
+ Ok(())
+ }
+
+ static KEY_LOCK_TEST_ALIAS: &str = "my super duper locked key";
+
+ #[test]
+ fn test_insert_and_load_full_keyentry_domain_app_concurrently() -> Result<()> {
+ let handle = {
+ let temp_dir = Arc::new(TempDir::new("id_lock_test")?);
+ let temp_dir_clone = temp_dir.clone();
+ let mut db = KeystoreDB::new(temp_dir.path())?;
+ 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(()),
+ )?;
+ assert_eq!(key_entry, make_test_key_entry_test_vector(key_id));
+ let state = Arc::new(AtomicU8::new(1));
+ let state2 = state.clone();
+
+ // Spawning a second thread that attempts to acquire the key id lock
+ // for the same key as the primary thread. The primary thread then
+ // waits, thereby forcing the secondary thread into the second stage
+ // of acquiring the lock (see KEY ID LOCK 2/2 above).
+ // The test succeeds if the secondary thread observes the transition
+ // of `state` from 1 to 2, despite having a whole second to overtake
+ // the primary thread.
+ let handle = thread::spawn(move || {
+ let temp_dir = temp_dir_clone;
+ let mut db = KeystoreDB::new(temp_dir.path()).unwrap();
+ assert!(db
+ .load_key_entry(
+ KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(KEY_LOCK_TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::BOTH,
+ 33,
+ |_k, _av| Ok(()),
+ )
+ .is_ok());
+ // We should only see a 2 here because we can only return
+ // from load_key_entry when the `_key_guard` expires,
+ // which happens at the end of the scope.
+ assert_eq!(2, state2.load(Ordering::Relaxed));
+ });
+
+ thread::sleep(std::time::Duration::from_millis(1000));
+
+ assert_eq!(Ok(1), state.compare_exchange(1, 2, Ordering::Relaxed, Ordering::Relaxed));
+
+ // Return the handle from this scope so we can join with the
+ // secondary thread after the key id lock has expired.
+ handle
+ // This is where the `_key_guard` goes out of scope,
+ // which is the reason for concurrent load_key_entry on the same key
+ // to unblock.
+ };
+ // Join with the secondary thread and unwrap, to propagate failing asserts to the
+ // main test thread. We will not see failing asserts in secondary threads otherwise.
+ handle.join().unwrap();
+ Ok(())
+ }
+
+ #[test]
+ fn list() -> Result<()> {
+ let temp_dir = TempDir::new("list_test")?;
+ let mut db = KeystoreDB::new(temp_dir.path())?;
+ static LIST_O_ENTRIES: &[(Domain, i64, &str)] = &[
+ (Domain::APP, 1, "test1"),
+ (Domain::APP, 1, "test2"),
+ (Domain::APP, 1, "test3"),
+ (Domain::APP, 1, "test4"),
+ (Domain::APP, 1, "test5"),
+ (Domain::APP, 1, "test6"),
+ (Domain::APP, 1, "test7"),
+ (Domain::APP, 2, "test1"),
+ (Domain::APP, 2, "test2"),
+ (Domain::APP, 2, "test3"),
+ (Domain::APP, 2, "test4"),
+ (Domain::APP, 2, "test5"),
+ (Domain::APP, 2, "test6"),
+ (Domain::APP, 2, "test8"),
+ (Domain::SELINUX, 100, "test1"),
+ (Domain::SELINUX, 100, "test2"),
+ (Domain::SELINUX, 100, "test3"),
+ (Domain::SELINUX, 100, "test4"),
+ (Domain::SELINUX, 100, "test5"),
+ (Domain::SELINUX, 100, "test6"),
+ (Domain::SELINUX, 100, "test9"),
+ ];
+
+ let list_o_keys: Vec<(i64, i64)> = LIST_O_ENTRIES
+ .iter()
+ .map(|(domain, ns, alias)| {
+ let entry =
+ make_test_key_entry(&mut db, *domain, *ns, *alias).unwrap_or_else(|e| {
+ panic!("Failed to insert {:?} {} {}. Error {:?}", domain, ns, alias, e)
+ });
+ (entry.id(), *ns)
+ })
+ .collect();
+
+ for (domain, namespace) in
+ &[(Domain::APP, 1i64), (Domain::APP, 2i64), (Domain::SELINUX, 100i64)]
+ {
+ let mut list_o_descriptors: Vec<KeyDescriptor> = LIST_O_ENTRIES
+ .iter()
+ .filter_map(|(domain, ns, alias)| match ns {
+ ns if *ns == *namespace => Some(KeyDescriptor {
+ domain: *domain,
+ nspace: *ns,
+ alias: Some(alias.to_string()),
+ blob: None,
+ }),
+ _ => None,
+ })
+ .collect();
+ list_o_descriptors.sort();
+ let mut list_result = db.list(*domain, *namespace)?;
+ list_result.sort();
+ assert_eq!(list_o_descriptors, list_result);
+
+ let mut list_o_ids: Vec<i64> = list_o_descriptors
+ .into_iter()
+ .map(|d| {
+ let (_, entry) = db
+ .load_key_entry(
+ d,
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ *namespace as u32,
+ |_, _| Ok(()),
+ )
+ .unwrap();
+ entry.id()
+ })
+ .collect();
+ list_o_ids.sort_unstable();
+ let mut loaded_entries: Vec<i64> = list_o_keys
+ .iter()
+ .filter_map(|(id, ns)| match ns {
+ ns if *ns == *namespace => Some(*id),
+ _ => None,
+ })
+ .collect();
+ loaded_entries.sort_unstable();
+ assert_eq!(list_o_ids, loaded_entries);
+ }
+ assert_eq!(Vec::<KeyDescriptor>::new(), db.list(Domain::SELINUX, 101)?);
+
+ Ok(())
+ }
+
+ // Helpers
+
+ // Checks that the given result is an error containing the given string.
+ fn check_result_is_error_containing_string<T>(result: Result<T>, target: &str) {
+ let error_str = format!(
+ "{:#?}",
+ result.err().unwrap_or_else(|| panic!("Expected the error: {}", target))
+ );
+ assert!(
+ error_str.contains(target),
+ "The string \"{}\" should contain \"{}\"",
+ error_str,
+ target
+ );
+ }
+
+ #[derive(Debug, PartialEq)]
+ #[allow(dead_code)]
+ struct KeyEntryRow {
+ id: i64,
+ key_type: KeyType,
+ domain: Option<Domain>,
+ namespace: Option<i64>,
+ alias: Option<String>,
+ }
+
+ fn get_keyentry(db: &KeystoreDB) -> Result<Vec<KeyEntryRow>> {
+ db.conn
+ .prepare("SELECT * FROM persistent.keyentry;")?
+ .query_map(NO_PARAMS, |row| {
+ Ok(KeyEntryRow {
+ id: row.get(0)?,
+ key_type: row.get(1)?,
+ domain: match row.get(2)? {
+ Some(i) => Some(Domain(i)),
+ None => None,
+ },
+ namespace: row.get(3)?,
+ alias: row.get(4)?,
+ })
+ })?
+ .map(|r| r.context("Could not read keyentry row."))
+ .collect::<Result<Vec<_>>>()
+ }
+
+ // Note: The parameters and SecurityLevel associations are nonsensical. This
+ // collection is only used to check if the parameters are preserved as expected by the
+ // database.
+ fn make_test_params() -> Vec<KeyParameter> {
+ vec![
+ KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(
+ KeyParameterValue::KeyPurpose(KeyPurpose::SIGN),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::Algorithm(Algorithm::RSA),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::KeySize(1024), SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(
+ KeyParameterValue::BlockMode(BlockMode::ECB),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::BlockMode(BlockMode::GCM),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::Digest(Digest::NONE), SecurityLevel::STRONGBOX),
+ KeyParameter::new(
+ KeyParameterValue::Digest(Digest::MD5),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::Digest(Digest::SHA_2_224),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::Digest(Digest::SHA_2_256),
+ SecurityLevel::STRONGBOX,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::PaddingMode(PaddingMode::NONE),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::PaddingMode(PaddingMode::RSA_OAEP),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS),
+ SecurityLevel::STRONGBOX,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::PaddingMode(PaddingMode::RSA_PKCS1_1_5_SIGN),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(KeyParameterValue::MinMacLength(256), SecurityLevel::STRONGBOX),
+ KeyParameter::new(
+ KeyParameterValue::EcCurve(EcCurve::P_224),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::EcCurve(EcCurve::P_256), SecurityLevel::STRONGBOX),
+ KeyParameter::new(
+ KeyParameterValue::EcCurve(EcCurve::P_384),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::EcCurve(EcCurve::P_521),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::RSAPublicExponent(3),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::IncludeUniqueID,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::BootLoaderOnly, SecurityLevel::STRONGBOX),
+ KeyParameter::new(KeyParameterValue::RollbackResistance, SecurityLevel::STRONGBOX),
+ KeyParameter::new(
+ KeyParameterValue::ActiveDateTime(1234567890),
+ SecurityLevel::STRONGBOX,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::OriginationExpireDateTime(1234567890),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::UsageExpireDateTime(1234567890),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::MinSecondsBetweenOps(1234567890),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::MaxUsesPerBoot(1234567890),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::UserID(1), SecurityLevel::STRONGBOX),
+ KeyParameter::new(KeyParameterValue::UserSecureID(42), SecurityLevel::STRONGBOX),
+ KeyParameter::new(
+ KeyParameterValue::NoAuthRequired,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType::PASSWORD),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::AuthTimeout(1234567890), SecurityLevel::SOFTWARE),
+ KeyParameter::new(KeyParameterValue::AllowWhileOnBody, SecurityLevel::SOFTWARE),
+ KeyParameter::new(
+ KeyParameterValue::TrustedUserPresenceRequired,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::TrustedConfirmationRequired,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::UnlockedDeviceRequired,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::ApplicationID(vec![1u8, 2u8, 3u8, 4u8]),
+ SecurityLevel::SOFTWARE,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::ApplicationData(vec![4u8, 3u8, 2u8, 1u8]),
+ SecurityLevel::SOFTWARE,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::CreationDateTime(12345677890),
+ SecurityLevel::SOFTWARE,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::KeyOrigin(KeyOrigin::GENERATED),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::RootOfTrust(vec![3u8, 2u8, 1u8, 4u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(KeyParameterValue::OSVersion(1), SecurityLevel::TRUSTED_ENVIRONMENT),
+ KeyParameter::new(KeyParameterValue::OSPatchLevel(2), SecurityLevel::SOFTWARE),
+ KeyParameter::new(
+ KeyParameterValue::UniqueID(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::SOFTWARE,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationChallenge(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationApplicationID(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdBrand(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdDevice(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdProduct(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdSerial(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdIMEI(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdMEID(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdManufacturer(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AttestationIdModel(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::VendorPatchLevel(3),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::BootPatchLevel(4),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::AssociatedData(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::Nonce(vec![4u8, 3u8, 1u8, 2u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::MacLength(256),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::ResetSinceIdRotation,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ KeyParameter::new(
+ KeyParameterValue::ConfirmationToken(vec![5u8, 5u8, 5u8, 5u8]),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ),
+ ]
+ }
+
+ fn make_test_key_entry(
+ db: &mut KeystoreDB,
+ domain: Domain,
+ namespace: i64,
+ 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_keyparameter(&key_id, &make_test_params())?;
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::EncryptedBy(EncryptedBy::Password));
+ metadata.add(KeyMetaEntry::Salt(vec![1, 2, 3]));
+ metadata.add(KeyMetaEntry::Iv(vec![2, 3, 1]));
+ metadata.add(KeyMetaEntry::AeadTag(vec![3, 1, 2]));
+ db.insert_key_metadata(&key_id, &metadata)?;
+ db.rebind_alias(&key_id, alias, domain, namespace)?;
+ Ok(key_id)
+ }
+
+ fn make_test_key_entry_test_vector(key_id: i64) -> KeyEntry {
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::EncryptedBy(EncryptedBy::Password));
+ metadata.add(KeyMetaEntry::Salt(vec![1, 2, 3]));
+ metadata.add(KeyMetaEntry::Iv(vec![2, 3, 1]));
+ metadata.add(KeyMetaEntry::AeadTag(vec![3, 1, 2]));
+
+ KeyEntry {
+ id: key_id,
+ km_blob: Some(TEST_KEY_BLOB.to_vec()),
+ cert: Some(TEST_CERT_BLOB.to_vec()),
+ cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
+ sec_level: SecurityLevel::TRUSTED_ENVIRONMENT,
+ parameters: make_test_params(),
+ metadata,
+ }
+ }
+
+ 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)?))
+ })?;
+
+ println!("Key entry table rows:");
+ for r in rows {
+ let (id, key_type, domain, namespace, alias) = r.unwrap();
+ println!(
+ " id: {} KeyType: {:?} Domain: {} Namespace: {} Alias: {}",
+ id, key_type, domain, namespace, alias
+ );
+ }
+ Ok(())
+ }
+
+ fn debug_dump_grant_table(db: &mut KeystoreDB) -> Result<()> {
+ let mut stmt = db
+ .conn
+ .prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
+ let rows = stmt.query_map::<(i64, i64, i64, i64), _, _>(NO_PARAMS, |row| {
+ Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?))
+ })?;
+
+ println!("Grant table rows:");
+ for r in rows {
+ let (id, gt, ki, av) = r.unwrap();
+ println!(" id: {} grantee: {} key_id: {} access_vector: {}", id, gt, ki, av);
+ }
+ Ok(())
+ }
+
+ // Use a custom random number generator that repeats each number once.
+ // This allows us to test repeated elements.
+
+ thread_local! {
+ static RANDOM_COUNTER: RefCell<i64> = RefCell::new(0);
+ }
+
+ fn reset_random() {
+ RANDOM_COUNTER.with(|counter| {
+ *counter.borrow_mut() = 0;
+ })
+ }
+
+ pub fn random() -> i64 {
+ RANDOM_COUNTER.with(|counter| {
+ let result = *counter.borrow() / 2;
+ *counter.borrow_mut() += 1;
+ result
+ })
+ }
+}
diff --git a/keystore2/src/db_utils.rs b/keystore2/src/db_utils.rs
new file mode 100644
index 0000000..90f5616
--- /dev/null
+++ b/keystore2/src/db_utils.rs
@@ -0,0 +1,241 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::error::Error as KsError;
+use anyhow::{Context, Result};
+use rusqlite::{types::FromSql, Row, Rows};
+
+// Takes Rows as returned by a query call on prepared statement.
+// Extracts exactly one row with the `row_extractor` and fails if more
+// rows are available.
+// If no row was found, `None` is passed to the `row_extractor`.
+// This allows the row extractor to decide on an error condition or
+// a different default behavior.
+pub fn with_rows_extract_one<'a, T, F>(rows: &mut Rows<'a>, row_extractor: F) -> Result<T>
+where
+ F: FnOnce(Option<&Row<'a>>) -> Result<T>,
+{
+ let result =
+ row_extractor(rows.next().context("with_rows_extract_one: Failed to unpack row.")?);
+
+ rows.next()
+ .context("In with_rows_extract_one: Failed to unpack unexpected row.")?
+ .map_or_else(|| Ok(()), |_| Err(KsError::sys()))
+ .context("In with_rows_extract_one: Unexpected row.")?;
+
+ result
+}
+
+pub fn with_rows_extract_all<'a, F>(rows: &mut Rows<'a>, mut row_extractor: F) -> Result<()>
+where
+ F: FnMut(&Row<'a>) -> Result<()>,
+{
+ loop {
+ match rows.next().context("In with_rows_extract_all: Failed to unpack row")? {
+ Some(row) => {
+ row_extractor(&row).context("In with_rows_extract_all.")?;
+ }
+ None => break Ok(()),
+ }
+ }
+}
+
+/// This struct is defined to postpone converting rusqlite column value to the
+/// appropriate key parameter value until we know the corresponding tag value.
+/// Wraps the column index and a rusqlite row.
+pub struct SqlField<'a>(usize, &'a Row<'a>);
+
+impl<'a> SqlField<'a> {
+ /// Creates a new SqlField with the given index and row.
+ pub fn new(index: usize, row: &'a Row<'a>) -> Self {
+ Self(index, row)
+ }
+ /// Returns the column value from the row, when we know the expected type.
+ pub fn get<T: FromSql>(&self) -> rusqlite::Result<T> {
+ self.1.get(self.0)
+ }
+}
+
+/// This macro implements two types to aid in the implementation of a type safe metadata
+/// store. The first is a collection of metadata and the second is the entry in that
+/// collection. The caller has to provide the infrastructure to load and store the
+/// the collection or individual entries in a SQLite database. The idea is that once
+/// the infrastructure for a metadata collection is in place all it takes to add another
+/// field is make a new entry in the list of variants (see details below).
+///
+/// # Usage
+/// ```
+/// impl_metadata!{
+/// /// This is the name of the collection.
+/// #[derive(Debug, Default)]
+/// pub struct CollectionName;
+/// /// This is the name of the Entry type followed by a list of variants, accessor function
+/// /// names, and types.
+/// #[derive(Debug, Eq, PartialEq)]
+/// pub enum EntryName {
+/// /// An enum variant with an accessor function name.
+/// VariantA(u32) with accessor get_variant_a,
+/// /// A second variant. `MyType` must implement rusqlite::types::ToSql and FromSql.
+/// VariantB(MyType) with accessor get_variant_b,
+/// // --- ADD NEW META DATA FIELDS HERE ---
+/// // For backwards compatibility add new entries only to
+/// // end of this list and above this comment.
+/// };
+/// }
+/// ```
+///
+/// expands to:
+///
+/// ```
+/// pub enum EntryName {
+/// VariantA(u32),
+/// VariantB(MyType),
+/// }
+///
+/// impl EntryName {}
+/// /// Returns a numeric variant id that can be used for persistent storage.
+/// fn db_tag(&self) -> i64 {...}
+/// /// Helper function that constructs a new `EntryName` given a variant identifier
+/// /// and a to-be-extracted `SqlFiled`
+/// fn new_from_sql(db_tag: i64, data: &SqlField) -> Result<Self> {...}
+/// }
+///
+/// impl ToSql for EntryName {...}
+///
+/// pub struct CollectionName {
+/// data: std::collections::HashMap<i64, EntryName>,
+/// }
+///
+/// impl CollectionName {
+/// /// Create a new collection of meta data.
+/// pub fn new() -> Self {...}
+/// /// Add a new entry to this collection. Replaces existing entries of the
+/// /// same variant unconditionally.
+/// pub fn add(&mut self, e: EntryName) {...}
+/// /// Type safe accessor function for the defined fields.
+/// pub fn get_variant_a() -> Option<u32> {...}
+/// pub fn get_variant_b() -> Option<MyType> {...}
+/// }
+///
+/// let mut collection = CollectionName::new();
+/// collection.add(EntryName::VariantA(3));
+/// let three: u32 = collection.get_variant_a().unwrap()
+/// ```
+///
+/// The caller of this macro must implement the actual database queries to load and store
+/// either a whole collection of metadata or individual fields. For example by associating
+/// with the given type:
+/// ```
+/// impl CollectionName {
+/// fn load(tx: &Transaction) -> Result<Self> {...}
+/// }
+/// ```
+#[macro_export]
+macro_rules! impl_metadata {
+ // These two macros assign incrementing numeric ids to each field which are used as
+ // database tags.
+ (@gen_consts {} {$($n:ident $nid:tt,)*} {$($count:tt)*}) => {
+ $(
+ // This allows us to reuse the variant name for these constants. The constants
+ // are private so that this exception does not spoil the public interface.
+ #[allow(non_upper_case_globals)]
+ const $n: i64 = $nid;
+ )*
+ };
+ (@gen_consts {$first:ident $(,$tail:ident)*} {$($out:tt)*} {$($count:tt)*}) => {
+ impl_metadata!(@gen_consts {$($tail),*} {$($out)* $first ($($count)*),} {$($count)* + 1});
+ };
+ (
+ $(#[$nmeta:meta])*
+ $nvis:vis struct $name:ident;
+ $(#[$emeta:meta])*
+ $evis:vis enum $entry:ident {
+ $($(#[$imeta:meta])* $vname:ident($t:ty) with accessor $func:ident),* $(,)?
+ };
+ ) => {
+ $(#[$emeta])*
+ $evis enum $entry {
+ $(
+ $(#[$imeta])*
+ $vname($t),
+ )*
+ }
+
+ impl $entry {
+ fn db_tag(&self) -> i64 {
+ match self {
+ $(Self::$vname(_) => $name::$vname,)*
+ }
+ }
+
+ fn new_from_sql(db_tag: i64, data: &SqlField) -> anyhow::Result<Self> {
+ match db_tag {
+ $(
+ $name::$vname => {
+ Ok($entry::$vname(
+ data.get()
+ .with_context(|| format!(
+ "In {}::new_from_sql: Unable to get {}.",
+ stringify!($entry),
+ stringify!($vname)
+ ))?
+ ))
+ },
+ )*
+ _ => Err(anyhow!(format!(
+ "In {}::new_from_sql: unknown db tag {}.",
+ stringify!($entry), db_tag
+ ))),
+ }
+ }
+ }
+
+ impl rusqlite::types::ToSql for $entry {
+ fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> {
+ match self {
+ $($entry::$vname(v) => v.to_sql(),)*
+ }
+ }
+ }
+
+ $(#[$nmeta])*
+ $nvis struct $name {
+ data: std::collections::HashMap<i64, $entry>,
+ }
+
+ impl $name {
+ /// Create a new instance of $name
+ pub fn new() -> Self {
+ Self{data: std::collections::HashMap::new()}
+ }
+
+ impl_metadata!{@gen_consts {$($vname),*} {} {0}}
+
+ /// Add a new instance of $entry to this collection of metadata.
+ pub fn add(&mut self, entry: $entry) {
+ self.data.insert(entry.db_tag(), entry);
+ }
+ $(
+ /// If the variant $vname is set, returns the wrapped value or None otherwise.
+ pub fn $func(&self) -> Option<&$t> {
+ if let Some($entry::$vname(v)) = self.data.get(&Self::$vname) {
+ Some(v)
+ } else {
+ None
+ }
+ }
+ )*
+ }
+ };
+}
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
new file mode 100644
index 0000000..473686c
--- /dev/null
+++ b/keystore2/src/enforcements.rs
@@ -0,0 +1,388 @@
+// 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: remove this after implementing the methods.
+#![allow(dead_code)]
+
+//! This is the Keystore 2.0 Enforcements module.
+// TODO: more description to follow.
+use crate::auth_token_handler::AuthTokenHandler;
+use crate::database::AuthTokenEntry;
+use crate::error::Error as KeystoreError;
+use crate::globals::DB;
+use crate::key_parameter::{KeyParameter, KeyParameterValue};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
+ HardwareAuthenticatorType::HardwareAuthenticatorType, KeyPurpose::KeyPurpose,
+ SecurityLevel::SecurityLevel, Tag::Tag, Timestamp::Timestamp,
+};
+use android_system_keystore2::aidl::android::system::keystore2::OperationChallenge::OperationChallenge;
+use anyhow::{Context, Result};
+use std::collections::{HashMap, HashSet};
+use std::sync::Mutex;
+use std::time::SystemTime;
+
+/// Enforcements data structure
+pub struct Enforcements {
+ // This hash set contains the user ids for whom the device is currently unlocked. If a user id
+ // is not in the set, it implies that the device is locked for the user.
+ device_unlocked_set: Mutex<HashSet<i32>>,
+ // This maps the operation challenge to an optional auth token, to maintain op-auth tokens
+ // in-memory, until they are picked up and given to the operation by authorise_update_finish().
+ op_auth_map: Mutex<HashMap<i64, Option<HardwareAuthToken>>>,
+}
+
+impl Enforcements {
+ /// Creates an enforcement object with the two data structures it holds.
+ pub fn new() -> Self {
+ Enforcements {
+ device_unlocked_set: Mutex::new(HashSet::new()),
+ op_auth_map: Mutex::new(HashMap::new()),
+ }
+ }
+
+ /// Checks if update or finish calls are authorized. If the operation is based on per-op key,
+ /// try to receive the auth token from the op_auth_map. We assume that by the time update/finish
+ /// is called, the auth token has been delivered to keystore. Therefore, we do not wait for it
+ /// and if the auth token is not found in the map, an error is returned.
+ pub fn authorize_update_or_finish(
+ &self,
+ key_params: &[KeyParameter],
+ op_challenge: Option<OperationChallenge>,
+ ) -> Result<AuthTokenHandler> {
+ let mut user_auth_type: Option<HardwareAuthenticatorType> = None;
+ let mut user_secure_ids = Vec::<i64>::new();
+ let mut is_timeout_key = false;
+
+ for key_param in key_params.iter() {
+ match key_param.key_parameter_value() {
+ KeyParameterValue::NoAuthRequired => {
+ // unlike in authorize_create, we do not check if both NoAuthRequired and user
+ // secure id are present, because that is already checked in authorize_create.
+ return Ok(AuthTokenHandler::NoAuthRequired);
+ }
+ KeyParameterValue::AuthTimeout(_) => {
+ is_timeout_key = true;
+ }
+ KeyParameterValue::HardwareAuthenticatorType(a) => {
+ user_auth_type = Some(*a);
+ }
+ KeyParameterValue::UserSecureID(u) => {
+ user_secure_ids.push(*u);
+ }
+ _ => {}
+ }
+ }
+
+ // If either of auth_type or secure_id is present and the other is not present,
+ // authorize_create would have already returned error.
+ // At this point, if UserSecureID is present and AuthTimeout is not present in
+ // key parameters, per-op auth is required.
+ // Obtain and validate the auth token.
+ if !is_timeout_key && !user_secure_ids.is_empty() {
+ let challenge =
+ op_challenge.ok_or(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
+ "In authorize_update_or_finish: Auth required, but operation challenge is not
+ present.",
+ )?;
+ let auth_type =
+ user_auth_type.ok_or(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
+ "In authorize_update_or_finish: Auth required, but authenticator type is not
+ present.",
+ )?;
+ // It is ok to unwrap here, because there is no way this lock can get poisoned and
+ // and there is no way to recover if it is poisoned.
+ let mut op_auth_map_guard = self.op_auth_map.lock().unwrap();
+ let auth_entry = op_auth_map_guard.remove(&(challenge.challenge));
+
+ match auth_entry {
+ Some(Some(auth_token)) => {
+ if AuthTokenEntry::satisfies_auth(&auth_token, &user_secure_ids, auth_type) {
+ return Ok(AuthTokenHandler::Token(auth_token, None));
+ } else {
+ return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
+ .context("In authorize_update_or_finish: Auth token does not match.");
+ }
+ }
+ _ => {
+ // there was no auth token
+ return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
+ "In authorize_update_or_finish: Auth required, but an auth token
+ is not found for the given operation challenge, in the op_auth_map.",
+ );
+ }
+ }
+ }
+
+ // If we don't find HardwareAuthenticatorType and UserSecureID, we assume that
+ // authentication is not required, because in legacy keys, authentication related
+ // key parameters may not present.
+ // TODO: METRICS: count how many times (if any) this code path is executed, in order
+ // to identify if any such keys are in use
+ Ok(AuthTokenHandler::NoAuthRequired)
+ }
+
+ /// Checks if a create call is authorized, given key parameters and operation parameters.
+ /// With regard to auth tokens, the following steps are taken:
+ /// If the key is time-bound, find a matching auth token from the database.
+ /// If the above step is successful, and if the security level is STRONGBOX, return a
+ /// VerificationRequired variant of the AuthTokenHandler with the found auth token to signal
+ /// the operation that it may need to obtain a verification token from TEE KeyMint.
+ /// If the security level is not STRONGBOX, return a Token variant of the AuthTokenHandler with
+ /// the found auth token to signal the operation that no more authorization required.
+ /// If the key is per-op, return an OpAuthRequired variant of the AuthTokenHandler to signal
+ /// create_operation() that it needs to add the operation challenge to the op_auth_map, once it
+ /// is received from the keymint, and that operation needs to be authorized before update/finish
+ /// is called.
+ pub fn authorize_create(
+ &self,
+ purpose: KeyPurpose,
+ key_params: &[KeyParameter],
+ op_params: &[KeyParameter],
+ // security_level will be used in the next CL
+ _security_level: SecurityLevel,
+ ) -> Result<AuthTokenHandler> {
+ match purpose {
+ // Allow SIGN, DECRYPT for both symmetric and asymmetric keys.
+ KeyPurpose::SIGN | KeyPurpose::DECRYPT => {}
+ // Rule out WRAP_KEY purpose
+ KeyPurpose::WRAP_KEY => {
+ return Err(KeystoreError::Km(Ec::INCOMPATIBLE_PURPOSE))
+ .context("In authorize_create: WRAP_KEY purpose is not allowed here.");
+ }
+ KeyPurpose::VERIFY | KeyPurpose::ENCRYPT => {
+ // We do not support ENCRYPT and VERIFY (the remaining two options of purpose) for
+ // asymmetric keys.
+ for kp in key_params.iter() {
+ match *kp.key_parameter_value() {
+ KeyParameterValue::Algorithm(Algorithm::RSA)
+ | KeyParameterValue::Algorithm(Algorithm::EC) => {
+ return Err(KeystoreError::Km(Ec::UNSUPPORTED_PURPOSE)).context(
+ "In authorize_create: public operations on asymmetric keys are not
+ supported.",
+ );
+ }
+ _ => {}
+ }
+ }
+ }
+ _ => {
+ return Err(KeystoreError::Km(Ec::UNSUPPORTED_PURPOSE))
+ .context("In authorize_create: specified purpose is not supported.");
+ }
+ }
+ // The following variables are to record information from key parameters to be used in
+ // enforcements, when two or more such pieces of information are required for enforcements.
+ // There is only one additional variable than what legacy keystore has, but this helps
+ // reduce the number of for loops on key parameters from 3 to 1, compared to legacy keystore
+ let mut key_purpose_authorized: bool = false;
+ let mut is_time_out_key: bool = false;
+ let mut auth_type: Option<HardwareAuthenticatorType> = None;
+ let mut no_auth_required: bool = false;
+ let mut caller_nonce_allowed = false;
+ let mut user_id: i32 = -1;
+ let mut user_secure_ids = Vec::<i64>::new();
+
+ // iterate through key parameters, recording information we need for authorization
+ // enforcements later, or enforcing authorizations in place, where applicable
+ for key_param in key_params.iter() {
+ match key_param.key_parameter_value() {
+ KeyParameterValue::NoAuthRequired => {
+ no_auth_required = true;
+ }
+ KeyParameterValue::AuthTimeout(_) => {
+ is_time_out_key = true;
+ }
+ KeyParameterValue::HardwareAuthenticatorType(a) => {
+ auth_type = Some(*a);
+ }
+ KeyParameterValue::KeyPurpose(p) => {
+ // Note: if there can be multiple KeyPurpose key parameters (TODO: confirm this),
+ // following check has the effect of key_params.contains(purpose)
+ // Also, authorizing purpose can not be completed here, if there can be multiple
+ // key parameters for KeyPurpose
+ if !key_purpose_authorized && *p == purpose {
+ key_purpose_authorized = true;
+ }
+ }
+ KeyParameterValue::CallerNonce => {
+ caller_nonce_allowed = true;
+ }
+ KeyParameterValue::ActiveDateTime(a) => {
+ if !Enforcements::is_given_time_passed(*a, true) {
+ return Err(KeystoreError::Km(Ec::KEY_NOT_YET_VALID))
+ .context("In authorize_create: key is not yet active.");
+ }
+ }
+ KeyParameterValue::OriginationExpireDateTime(o) => {
+ if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN)
+ && Enforcements::is_given_time_passed(*o, false)
+ {
+ return Err(KeystoreError::Km(Ec::KEY_EXPIRED))
+ .context("In authorize_create: key is expired.");
+ }
+ }
+ KeyParameterValue::UsageExpireDateTime(u) => {
+ if (purpose == KeyPurpose::DECRYPT || purpose == KeyPurpose::VERIFY)
+ && Enforcements::is_given_time_passed(*u, false)
+ {
+ return Err(KeystoreError::Km(Ec::KEY_EXPIRED))
+ .context("In authorize_create: key is expired.");
+ }
+ }
+ KeyParameterValue::UserSecureID(s) => {
+ user_secure_ids.push(*s);
+ }
+ KeyParameterValue::UserID(u) => {
+ user_id = *u;
+ }
+ KeyParameterValue::UnlockedDeviceRequired => {
+ // check the device locked status. If locked, operations on the key are not
+ // allowed.
+ if self.is_device_locked(user_id) {
+ return Err(KeystoreError::Km(Ec::DEVICE_LOCKED))
+ .context("In authorize_create: device is locked.");
+ }
+ }
+ // NOTE: as per offline discussion, sanitizing key parameters and rejecting
+ // create operation if any non-allowed tags are present, is not done in
+ // authorize_create (unlike in legacy keystore where AuthorizeBegin is rejected if
+ // a subset of non-allowed tags are present). Because santizing key parameters
+ // should have been done during generate/import key, by KeyMint.
+ _ => { /*Do nothing on all the other key parameters, as in legacy keystore*/ }
+ }
+ }
+
+ // authorize the purpose
+ if !key_purpose_authorized {
+ return Err(KeystoreError::Km(Ec::INCOMPATIBLE_PURPOSE))
+ .context("In authorize_create: the purpose is not authorized.");
+ }
+
+ // if both NO_AUTH_REQUIRED and USER_SECURE_ID tags are present, return error
+ if !user_secure_ids.is_empty() && no_auth_required {
+ return Err(KeystoreError::Km(Ec::INVALID_KEY_BLOB)).context(
+ "In authorize_create: key has both NO_AUTH_REQUIRED
+ and USER_SECURE_ID tags.",
+ );
+ }
+
+ // if either of auth_type or secure_id is present and the other is not present, return error
+ if (auth_type.is_some() && user_secure_ids.is_empty())
+ || (auth_type.is_none() && !user_secure_ids.is_empty())
+ {
+ return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
+ "In authorize_create: Auth required, but either auth type or secure ids
+ are not present.",
+ );
+ }
+ // validate caller nonce for origination purposes
+ if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN)
+ && !caller_nonce_allowed
+ && op_params.iter().any(|kp| kp.get_tag() == Tag::NONCE)
+ {
+ return Err(KeystoreError::Km(Ec::CALLER_NONCE_PROHIBITED)).context(
+ "In authorize_create, NONCE is present,
+ although CALLER_NONCE is not present",
+ );
+ }
+
+ if !user_secure_ids.is_empty() {
+ // per op auth token
+ if !is_time_out_key {
+ return Ok(AuthTokenHandler::OpAuthRequired);
+ } else {
+ //time out token
+ // TODO: retrieve it from the database
+ // - in an upcoming CL
+ }
+ }
+
+ // If we reach here, all authorization enforcements have passed and no auth token required.
+ Ok(AuthTokenHandler::NoAuthRequired)
+ }
+
+ /// Checks if the time now since epoch is greater than (or equal, if is_given_time_inclusive is
+ /// set) the given time (in milliseconds)
+ fn is_given_time_passed(given_time: i64, is_given_time_inclusive: bool) -> bool {
+ let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH);
+
+ let time_since_epoch = match duration_since_epoch {
+ Ok(duration) => duration.as_millis(),
+ Err(_) => return false,
+ };
+
+ if is_given_time_inclusive {
+ time_since_epoch >= (given_time as u128)
+ } else {
+ time_since_epoch > (given_time as u128)
+ }
+ }
+
+ /// Check if the device is locked for the given user. If there's no entry yet for the user,
+ /// we assume that the device is locked
+ fn is_device_locked(&self, user_id: i32) -> bool {
+ // unwrap here because there's no way this mutex guard can be poisoned and
+ // because there's no way to recover, even if it is poisoned.
+ let set = self.device_unlocked_set.lock().unwrap();
+ !set.contains(&user_id)
+ }
+
+ /// Sets the device locked status for the user. This method is called externally.
+ pub fn set_device_locked(&self, user_id: i32, device_locked_status: bool) {
+ // unwrap here because there's no way this mutex guard can be poisoned and
+ // because there's no way to recover, even if it is poisoned.
+ let mut set = self.device_unlocked_set.lock().unwrap();
+ if device_locked_status {
+ set.remove(&user_id);
+ } else {
+ set.insert(user_id);
+ }
+ }
+
+ /// Add this auth token to the database.
+ /// Then check if there is an entry in the op_auth_map, indexed by the challenge of this
+ /// auth token (which could have been inserted during create_operation of an operation on a
+ /// per-op-auth key). If so, add a copy of this auth token to the map indexed by the
+ /// challenge.
+ pub fn add_auth_token(&self, auth_token: HardwareAuthToken) -> Result<()> {
+ //it is ok to unwrap here, because there is no way this lock can get poisoned and
+ //and there is no way to recover if it is poisoned.
+ let mut op_auth_map_guard = self.op_auth_map.lock().unwrap();
+
+ if op_auth_map_guard.contains_key(&auth_token.challenge) {
+ let auth_token_copy = HardwareAuthToken {
+ challenge: auth_token.challenge,
+ userId: auth_token.userId,
+ authenticatorId: auth_token.authenticatorId,
+ authenticatorType: HardwareAuthenticatorType(auth_token.authenticatorType.0),
+ timestamp: Timestamp { milliSeconds: auth_token.timestamp.milliSeconds },
+ mac: auth_token.mac.clone(),
+ };
+ op_auth_map_guard.insert(auth_token.challenge, Some(auth_token_copy));
+ }
+
+ DB.with(|db| db.borrow_mut().insert_auth_token(&auth_token))
+ .context("In add_auth_token.")?;
+ Ok(())
+ }
+}
+
+impl Default for Enforcements {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+// TODO: Add tests to enforcement module (b/175578618).
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
new file mode 100644
index 0000000..b4cf913
--- /dev/null
+++ b/keystore2/src/error.rs
@@ -0,0 +1,327 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Keystore error provides convenience methods and types for Keystore error handling.
+//! Clients of Keystore expect one of two error codes, i.e., a Keystore ResponseCode as
+//! defined by the Keystore AIDL interface, or a Keymint ErrorCode as defined by
+//! the Keymint HAL specification.
+//! This crate provides `Error` which can wrap both. It is to be used
+//! internally by Keystore to diagnose error conditions that need to be reported to
+//! the client. To report the error condition to the client the Keystore AIDL
+//! interface defines a wire type `Result` which is distinctly different from Rust's
+//! `enum Result<T,E>`.
+//!
+//! This crate provides the convenience method `map_or_log_err` to convert `anyhow::Error`
+//! into this wire type. In addition to handling the conversion of `Error`
+//! to the `Result` wire type it handles any other error by mapping it to
+//! `ResponseCode::SYSTEM_ERROR` and logs any error condition.
+//!
+//! Keystore functions should use `anyhow::Result` to return error conditions, and
+//! context should be added every time an error is forwarded.
+
+use std::cmp::PartialEq;
+
+pub use android_hardware_security_keymint::aidl::android::hardware::security::keymint::ErrorCode::ErrorCode;
+pub use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
+
+use keystore2_selinux as selinux;
+
+use android_system_keystore2::binder::{
+ ExceptionCode, Result as BinderResult, Status as BinderStatus,
+};
+
+/// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
+/// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
+#[derive(Debug, thiserror::Error, PartialEq)]
+pub enum Error {
+ /// Wraps a Keystore `ResponseCode` as defined by the Keystore AIDL interface specification.
+ #[error("Error::Rc({0:?})")]
+ Rc(ResponseCode),
+ /// Wraps a Keymint `ErrorCode` as defined by the Keymint AIDL interface specification.
+ #[error("Error::Km({0:?})")]
+ Km(ErrorCode),
+ /// Wraps a Binder exception code other than a service specific exception.
+ #[error("Binder exception code {0:?}, {1:?}")]
+ Binder(ExceptionCode, i32),
+}
+
+impl Error {
+ /// Short hand for `Error::Rc(ResponseCode::SYSTEM_ERROR)`
+ pub fn sys() -> Self {
+ Error::Rc(ResponseCode::SYSTEM_ERROR)
+ }
+
+ /// Short hand for `Error::Rc(ResponseCode::PERMISSION_DENIED`
+ pub fn perm() -> Self {
+ Error::Rc(ResponseCode::PERMISSION_DENIED)
+ }
+}
+
+/// Helper function to map the binder status we get from calls into KeyMint
+/// to a Keystore Error. We don't create an anyhow error here to make
+/// it easier to evaluate KeyMint errors, which we must do in some cases, e.g.,
+/// when diagnosing authentication requirements, update requirements, and running
+/// out of operation slots.
+pub fn map_km_error<T>(r: BinderResult<T>) -> Result<T, Error> {
+ r.map_err(|s| {
+ match s.exception_code() {
+ ExceptionCode::SERVICE_SPECIFIC => {
+ let se = s.service_specific_error();
+ if se < 0 {
+ // Negative service specific errors are KM error codes.
+ Error::Km(ErrorCode(s.service_specific_error()))
+ } else {
+ // Non negative error codes cannot be KM error codes.
+ // So we create an `Error::Binder` variant to preserve
+ // the service specific error code for logging.
+ // `map_or_log_err` will map this on a system error,
+ // but not before logging the details to logcat.
+ Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
+ }
+ }
+ // We create `Error::Binder` to preserve the exception code
+ // for logging.
+ // `map_or_log_err` will map this on a system error.
+ e_code => Error::Binder(e_code, 0),
+ }
+ })
+}
+
+/// This function should be used by Keystore service calls to translate error conditions
+/// into service specific exceptions.
+///
+/// All error conditions get logged by this function.
+///
+/// All `Error::Rc(x)` and `Error::Km(x)` variants get mapped onto a service specific error
+/// code of x. This is possible because KeyMint `ErrorCode` errors are always negative and
+/// `ResponseCode` codes are always positive.
+/// `selinux::Error::PermissionDenied` is mapped on `ResponseCode::PERMISSION_DENIED`.
+///
+/// All non `Error` error conditions and the Error::Binder variant get mapped onto
+/// ResponseCode::SYSTEM_ERROR`.
+///
+/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
+/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
+/// typically returns Ok(value).
+///
+/// # Examples
+///
+/// ```
+/// fn loadKey() -> anyhow::Result<Vec<u8>> {
+/// if (good_but_auth_required) {
+/// Ok(vec!['k', 'e', 'y'])
+/// } else {
+/// Err(anyhow!(Error::Rc(ResponseCode::KEY_NOT_FOUND)))
+/// }
+/// }
+///
+/// map_or_log_err(loadKey(), Ok)
+/// ```
+pub fn map_or_log_err<T, U, F>(result: anyhow::Result<U>, handle_ok: F) -> BinderResult<T>
+where
+ F: FnOnce(U) -> BinderResult<T>,
+{
+ 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::Km(ec)) => ec.0,
+ // If an Error::Binder reaches this stage we report a system error.
+ // The exception code and possible service specific error will be
+ // printed in the error log above.
+ Some(Error::Binder(_, _)) => 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,
+ )
+}
+
+#[cfg(test)]
+pub mod tests {
+
+ use super::*;
+ use android_system_keystore2::binder::{
+ ExceptionCode, Result as BinderResult, Status as BinderStatus,
+ };
+ use anyhow::{anyhow, Context};
+
+ fn nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
+ Err(anyhow!(Error::Rc(rc))).context("nested nested rc")
+ }
+
+ fn nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
+ nested_nested_rc(rc).context("nested rc")
+ }
+
+ fn nested_nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
+ Err(anyhow!(Error::Km(ec))).context("nested nested ec")
+ }
+
+ fn nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
+ nested_nested_ec(ec).context("nested ec")
+ }
+
+ fn nested_nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
+ Ok(rc)
+ }
+
+ fn nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
+ nested_nested_ok(rc).context("nested ok")
+ }
+
+ fn nested_nested_selinux_perm() -> anyhow::Result<()> {
+ Err(anyhow!(selinux::Error::perm())).context("nested nexted selinux permission denied")
+ }
+
+ fn nested_selinux_perm() -> anyhow::Result<()> {
+ nested_nested_selinux_perm().context("nested selinux permission denied")
+ }
+
+ #[derive(Debug, thiserror::Error)]
+ enum TestError {
+ #[error("TestError::Fail")]
+ Fail = 0,
+ }
+
+ fn nested_nested_other_error() -> anyhow::Result<()> {
+ Err(anyhow!(TestError::Fail)).context("nested nested other error")
+ }
+
+ fn nested_other_error() -> anyhow::Result<()> {
+ nested_nested_other_error().context("nested other error")
+ }
+
+ fn binder_sse_error(sse: i32) -> BinderResult<()> {
+ Err(BinderStatus::new_service_specific_error(sse, None))
+ }
+
+ fn binder_exception(ex: ExceptionCode) -> BinderResult<()> {
+ Err(BinderStatus::new_exception(ex, None))
+ }
+
+ #[test]
+ fn keystore_error_test() -> anyhow::Result<(), String> {
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("keystore_error_tests")
+ .with_min_level(log::Level::Debug),
+ );
+ // All Error::Rc(x) get mapped on a service specific error
+ // code of x.
+ for rc in ResponseCode::LOCKED.0..ResponseCode::BACKEND_BUSY.0 {
+ assert_eq!(
+ Result::<(), i32>::Err(rc),
+ map_or_log_err(nested_rc(ResponseCode(rc)), |_| Err(BinderStatus::ok()))
+ .map_err(|s| s.service_specific_error())
+ );
+ }
+
+ // All Keystore Error::Km(x) get mapped on a service
+ // specific error of x.
+ for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
+ assert_eq!(
+ Result::<(), i32>::Err(ec),
+ map_or_log_err(nested_ec(ErrorCode(ec)), |_| Err(BinderStatus::ok()))
+ .map_err(|s| s.service_specific_error())
+ );
+ }
+
+ // All Keymint errors x received through a Binder Result get mapped on
+ // a service specific error of x.
+ for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
+ assert_eq!(
+ Result::<(), i32>::Err(ec),
+ map_or_log_err(
+ map_km_error(binder_sse_error(ec))
+ .with_context(|| format!("Km error code: {}.", ec)),
+ |_| Err(BinderStatus::ok())
+ )
+ .map_err(|s| s.service_specific_error())
+ );
+ }
+
+ // map_km_error creates an Error::Binder variant storing
+ // ExceptionCode::SERVICE_SPECIFIC and the given
+ // service specific error.
+ let sse = map_km_error(binder_sse_error(1));
+ assert_eq!(Err(Error::Binder(ExceptionCode::SERVICE_SPECIFIC, 1)), sse);
+ // map_or_log_err then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
+ assert_eq!(
+ Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
+ map_or_log_err(sse.context("Non negative service specific error."), |_| Err(
+ BinderStatus::ok()
+ ))
+ .map_err(|s| ResponseCode(s.service_specific_error()))
+ );
+
+ // map_km_error creates a Error::Binder variant storing the given exception code.
+ let binder_exception = map_km_error(binder_exception(ExceptionCode::TRANSACTION_FAILED));
+ assert_eq!(Err(Error::Binder(ExceptionCode::TRANSACTION_FAILED, 0)), binder_exception);
+ // map_or_log_err then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
+ assert_eq!(
+ Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
+ map_or_log_err(binder_exception.context("Binder Exception."), |_| Err(
+ BinderStatus::ok()
+ ))
+ .map_err(|s| ResponseCode(s.service_specific_error()))
+ );
+
+ // selinux::Error::Perm() needs to be mapped to ResponseCode::PERMISSION_DENIED
+ assert_eq!(
+ Result::<(), ResponseCode>::Err(ResponseCode::PERMISSION_DENIED),
+ map_or_log_err(nested_selinux_perm(), |_| Err(BinderStatus::ok()))
+ .map_err(|s| ResponseCode(s.service_specific_error()))
+ );
+
+ // All other errors get mapped on System Error.
+ assert_eq!(
+ Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
+ map_or_log_err(nested_other_error(), |_| Err(BinderStatus::ok()))
+ .map_err(|s| ResponseCode(s.service_specific_error()))
+ );
+
+ // Result::Ok variants get passed to the ok handler.
+ assert_eq!(Ok(ResponseCode::LOCKED), map_or_log_err(nested_ok(ResponseCode::LOCKED), Ok));
+ assert_eq!(
+ Ok(ResponseCode::SYSTEM_ERROR),
+ map_or_log_err(nested_ok(ResponseCode::SYSTEM_ERROR), Ok)
+ );
+
+ Ok(())
+ }
+
+ //Helper function to test whether error cases are handled as expected.
+ pub fn check_result_contains_error_string<T>(
+ result: anyhow::Result<T>,
+ expected_error_string: &str,
+ ) {
+ let error_str = format!(
+ "{:#?}",
+ result.err().unwrap_or_else(|| panic!("Expected the error: {}", expected_error_string))
+ );
+ assert!(
+ error_str.contains(expected_error_string),
+ "The string \"{}\" should contain \"{}\"",
+ error_str,
+ expected_error_string
+ );
+ }
+} // mod tests
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
new file mode 100644
index 0000000..eff3196
--- /dev/null
+++ b/keystore2/src/globals.rs
@@ -0,0 +1,43 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This module holds global state of Keystore such as the thread local
+//! database connections and connections to services that Keystore needs
+//! to talk to.
+
+use crate::database::KeystoreDB;
+use crate::super_key::SuperKeyManager;
+use lazy_static::lazy_static;
+use std::cell::RefCell;
+
+thread_local! {
+ /// Database connections are not thread safe, but connecting to the
+ /// same database multiple times is safe as long as each connection is
+ /// used by only one thread. So we store one database connection per
+ /// thread in this thread local key.
+ pub static DB: RefCell<KeystoreDB> =
+ RefCell::new(
+ 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."));
+}
+
+lazy_static! {
+ /// Runtime database of unwrapped super keys.
+ pub static ref SUPER_KEY: SuperKeyManager = Default::default();
+}
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
new file mode 100644
index 0000000..ac1164e
--- /dev/null
+++ b/keystore2/src/key_parameter.rs
@@ -0,0 +1,1327 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Key parameters are declared by KeyMint to describe properties of keys and operations.
+//! During key generation and import, key parameters are used to characterize a key, its usage
+//! restrictions, and additional parameters for attestation. During the lifetime of the key,
+//! the key characteristics are expressed as set of key parameters. During cryptographic
+//! operations, clients may specify additional operation specific parameters.
+//! This module provides a Keystore 2.0 internal representation for key parameters and
+//! implements traits to convert it from and into KeyMint KeyParameters and store it in
+//! the SQLite database.
+//!
+//! ## Synopsis
+//!
+//! enum KeyParameterValue {
+//! Invalid,
+//! Algorithm(Algorithm),
+//! ...
+//! }
+//!
+//! impl KeyParameterValue {
+//! pub fn get_tag(&self) -> Tag;
+//! pub fn new_from_sql(tag: Tag, data: &SqlField) -> Result<Self>;
+//! pub fn new_from_tag_primitive_pair<T: Into<Primitive>>(tag: Tag, v: T)
+//! -> Result<Self, PrimitiveError>;
+//! fn to_sql(&self) -> SqlResult<ToSqlOutput>
+//! }
+//!
+//! use ...::keymint::KeyParameter as KmKeyParameter;
+//! impl Into<KmKeyParameter> for KeyParameterValue {}
+//! impl From<KmKeyParameter> for KeyParameterValue {}
+//!
+//! ## Implementation
+//! Each of the six functions is implemented as match statement over each key parameter variant.
+//! We bootstrap these function as well as the KeyParameterValue enum itself from a single list
+//! of key parameters, that needs to be kept in sync with the KeyMint AIDL specification.
+//!
+//! The list resembles an enum declaration with a few extra fields.
+//! enum KeyParameterValue {
+//! Invalid with tag INVALID and field Invalid,
+//! Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+//! ...
+//! }
+//! The tag corresponds to the variant of the keymint::Tag, and the field corresponds to the
+//! variant of the keymint::KeyParameterValue union. There is no one to one mapping between
+//! tags and union fields, e.g., the values of both tags BOOT_PATCHLEVEL and VENDOR_PATCHLEVEL
+//! are stored in the Integer field.
+//!
+//! The macros interpreting them all follow a similar pattern and follow the following fragment
+//! naming scheme:
+//!
+//! Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+//! $vname $(($vtype ))? with tag $tag_name and field $field_name,
+//!
+//! Further, KeyParameterValue appears in the macro as $enum_name.
+//! Note that $vtype is optional to accommodate variants like Invalid which don't wrap a value.
+//!
+//! In some cases $vtype is not part of the expansion, but we still have to modify the expansion
+//! depending on the presence of $vtype. In these cases we recurse through the list following the
+//! following pattern:
+//!
+//! (@<marker> <non repeating args>, [<out list>], [<in list>])
+//!
+//! These macros usually have four rules:
+//! * Two main recursive rules, of the form:
+//! (
+//! @<marker>
+//! <non repeating args>,
+//! [<out list>],
+//! [<one element pattern> <in tail>]
+//! ) => {
+//! macro!{@<marker> <non repeating args>, [<out list>
+//! <one element expansion>
+//! ], [<in tail>]}
+//! };
+//! They pop one element off the <in list> and add one expansion to the out list.
+//! The element expansion is kept on a separate line (or lines) for better readability.
+//! The two variants differ in whether or not $vtype is expected.
+//! * The termination condition which has an empty in list.
+//! * The public interface, which does not have @marker and calls itself with an empty out list.
+
+use std::convert::TryInto;
+
+use crate::db_utils::SqlField;
+use crate::error::Error as KeystoreError;
+use crate::error::ResponseCode;
+
+pub use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+ HardwareAuthenticatorType::HardwareAuthenticatorType, KeyOrigin::KeyOrigin,
+ KeyParameter::KeyParameter as KmKeyParameter,
+ KeyParameterValue::KeyParameterValue as KmKeyParameterValue, KeyPurpose::KeyPurpose,
+ PaddingMode::PaddingMode, SecurityLevel::SecurityLevel, Tag::Tag,
+};
+use android_system_keystore2::aidl::android::system::keystore2::Authorization::Authorization;
+use anyhow::{Context, Result};
+use rusqlite::types::{Null, ToSql, ToSqlOutput};
+use rusqlite::Result as SqlResult;
+
+/// This trait is used to associate a primitive to any type that can be stored inside a
+/// KeyParameterValue, especially the AIDL enum types, e.g., keymint::{Algorithm, Digest, ...}.
+/// This allows for simplifying the macro rules, e.g., for reading from the SQL database.
+/// An expression like `KeyParameterValue::Algorithm(row.get(0))` would not work because
+/// a type of `Algorithm` is expected which does not implement `FromSql` and we cannot
+/// implement it because we own neither the type nor the trait.
+/// With AssociatePrimitive we can write an expression
+/// `KeyParameter::Algorithm(<Algorithm>::from_primitive(row.get(0)))` to inform `get`
+/// about the expected primitive type that it can convert into. By implementing this
+/// trait for all inner types we can write a single rule to cover all cases (except where
+/// there is no wrapped type):
+/// `KeyParameterValue::$vname(<$vtype>::from_primitive(row.get(0)))`
+trait AssociatePrimitive {
+ type Primitive;
+
+ fn from_primitive(v: Self::Primitive) -> Self;
+ fn to_primitive(&self) -> Self::Primitive;
+}
+
+/// Associates the given type with i32. The macro assumes that the given type is actually a
+/// tuple struct wrapping i32, such as AIDL enum types.
+macro_rules! implement_associate_primitive_for_aidl_enum {
+ ($t:ty) => {
+ impl AssociatePrimitive for $t {
+ type Primitive = i32;
+
+ fn from_primitive(v: Self::Primitive) -> Self {
+ Self(v)
+ }
+ fn to_primitive(&self) -> Self::Primitive {
+ self.0
+ }
+ }
+ };
+}
+
+/// Associates the given type with itself.
+macro_rules! implement_associate_primitive_identity {
+ ($t:ty) => {
+ impl AssociatePrimitive for $t {
+ type Primitive = $t;
+
+ fn from_primitive(v: Self::Primitive) -> Self {
+ v
+ }
+ fn to_primitive(&self) -> Self::Primitive {
+ self.clone()
+ }
+ }
+ };
+}
+
+implement_associate_primitive_for_aidl_enum! {Algorithm}
+implement_associate_primitive_for_aidl_enum! {BlockMode}
+implement_associate_primitive_for_aidl_enum! {Digest}
+implement_associate_primitive_for_aidl_enum! {EcCurve}
+implement_associate_primitive_for_aidl_enum! {HardwareAuthenticatorType}
+implement_associate_primitive_for_aidl_enum! {KeyOrigin}
+implement_associate_primitive_for_aidl_enum! {KeyPurpose}
+implement_associate_primitive_for_aidl_enum! {PaddingMode}
+implement_associate_primitive_for_aidl_enum! {SecurityLevel}
+
+implement_associate_primitive_identity! {Vec<u8>}
+implement_associate_primitive_identity! {i64}
+implement_associate_primitive_identity! {i32}
+
+/// This enum allows passing a primitive value to `KeyParameterValue::new_from_tag_primitive_pair`
+/// Usually, it is not necessary to use this type directly because the function uses
+/// `Into<Primitive>` as a trait bound.
+pub enum Primitive {
+ /// Wraps an i64.
+ I64(i64),
+ /// Wraps an i32.
+ I32(i32),
+ /// Wraps a Vec<u8>.
+ Vec(Vec<u8>),
+}
+
+impl From<i64> for Primitive {
+ fn from(v: i64) -> Self {
+ Self::I64(v)
+ }
+}
+impl From<i32> for Primitive {
+ fn from(v: i32) -> Self {
+ Self::I32(v)
+ }
+}
+impl From<Vec<u8>> for Primitive {
+ fn from(v: Vec<u8>) -> Self {
+ Self::Vec(v)
+ }
+}
+
+/// This error is returned by `KeyParameterValue::new_from_tag_primitive_pair`.
+#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum PrimitiveError {
+ /// Returned if this primitive is unsuitable for the given tag type.
+ #[error("Primitive does not match the expected tag type.")]
+ TypeMismatch,
+ /// Return if the tag type is unknown.
+ #[error("Unknown tag.")]
+ UnknownTag,
+}
+
+impl TryInto<i64> for Primitive {
+ type Error = PrimitiveError;
+
+ fn try_into(self) -> Result<i64, Self::Error> {
+ match self {
+ Self::I64(v) => Ok(v),
+ _ => Err(Self::Error::TypeMismatch),
+ }
+ }
+}
+impl TryInto<i32> for Primitive {
+ type Error = PrimitiveError;
+
+ fn try_into(self) -> Result<i32, Self::Error> {
+ match self {
+ Self::I32(v) => Ok(v),
+ _ => Err(Self::Error::TypeMismatch),
+ }
+ }
+}
+impl TryInto<Vec<u8>> for Primitive {
+ type Error = PrimitiveError;
+
+ fn try_into(self) -> Result<Vec<u8>, Self::Error> {
+ match self {
+ Self::Vec(v) => Ok(v),
+ _ => Err(Self::Error::TypeMismatch),
+ }
+ }
+}
+
+/// Expands the list of KeyParameterValue variants as follows:
+///
+/// Input:
+/// Invalid with tag INVALID and field Invalid,
+/// Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+///
+/// Output:
+/// ```
+/// pub fn new_from_tag_primitive_pair<T: Into<Primitive>>(
+/// tag: Tag,
+/// v: T
+/// ) -> Result<KeyParameterValue, PrimitiveError> {
+/// let p: Primitive = v.into();
+/// Ok(match tag {
+/// Tag::INVALID => KeyParameterValue::Invalid,
+/// Tag::ALGORITHM => KeyParameterValue::Algorithm(
+/// <Algorithm>::from_primitive(p.try_into()?)
+/// ),
+/// _ => return Err(PrimitiveError::UnknownTag),
+/// })
+/// }
+/// ```
+macro_rules! implement_from_tag_primitive_pair {
+ ($enum_name:ident; $($vname:ident$(($vtype:ty))? $tag_name:ident),*) => {
+ /// Returns the an instance of $enum_name or an error if the given primitive does not match
+ /// the tag type or the tag is unknown.
+ pub fn new_from_tag_primitive_pair<T: Into<Primitive>>(
+ tag: Tag,
+ v: T
+ ) -> Result<$enum_name, PrimitiveError> {
+ let p: Primitive = v.into();
+ Ok(match tag {
+ $(Tag::$tag_name => $enum_name::$vname$((
+ <$vtype>::from_primitive(p.try_into()?)
+ ))?,)*
+ _ => return Err(PrimitiveError::UnknownTag),
+ })
+ }
+ };
+}
+
+/// Expands the list of KeyParameterValue variants as follows:
+///
+/// Input:
+/// pub enum KeyParameterValue {
+/// Invalid with tag INVALID and field Invalid,
+/// Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+/// }
+///
+/// Output:
+/// ```
+/// pub enum KeyParameterValue {
+/// Invalid,
+/// Algorithm(Algorithm),
+/// }
+/// ```
+macro_rules! implement_enum {
+ (
+ $(#[$enum_meta:meta])*
+ $enum_vis:vis enum $enum_name:ident {
+ $($(#[$emeta:meta])* $vname:ident$(($vtype:ty))?),* $(,)?
+ }
+ ) => {
+ $(#[$enum_meta])*
+ $enum_vis enum $enum_name {
+ $(
+ $(#[$emeta])*
+ $vname$(($vtype))?
+ ),*
+ }
+ };
+}
+
+/// Expands the list of KeyParameterValue variants as follows:
+///
+/// Input:
+/// Invalid with tag INVALID and field Invalid,
+/// Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+///
+/// Output:
+/// ```
+/// pub fn get_tag(&self) -> Tag {
+/// match self {
+/// KeyParameterValue::Invalid => Tag::INVALID,
+/// KeyParameterValue::Algorithm(_) => Tag::ALGORITHM,
+/// }
+/// }
+/// ```
+macro_rules! implement_get_tag {
+ (
+ @replace_type_spec
+ $enum_name:ident,
+ [$($out:tt)*],
+ [$vname:ident($vtype:ty) $tag_name:ident, $($in:tt)*]
+ ) => {
+ implement_get_tag!{@replace_type_spec $enum_name, [$($out)*
+ $enum_name::$vname(_) => Tag::$tag_name,
+ ], [$($in)*]}
+ };
+ (
+ @replace_type_spec
+ $enum_name:ident,
+ [$($out:tt)*],
+ [$vname:ident $tag_name:ident, $($in:tt)*]
+ ) => {
+ implement_get_tag!{@replace_type_spec $enum_name, [$($out)*
+ $enum_name::$vname => Tag::$tag_name,
+ ], [$($in)*]}
+ };
+ (@replace_type_spec $enum_name:ident, [$($out:tt)*], []) => {
+ /// Returns the tag of the given instance.
+ pub fn get_tag(&self) -> Tag {
+ match self {
+ $($out)*
+ }
+ }
+ };
+
+ ($enum_name:ident; $($vname:ident$(($vtype:ty))? $tag_name:ident),*) => {
+ implement_get_tag!{@replace_type_spec $enum_name, [], [$($vname$(($vtype))? $tag_name,)*]}
+ };
+}
+
+/// Expands the list of KeyParameterValue variants as follows:
+///
+/// Input:
+/// Invalid with tag INVALID and field Invalid,
+/// Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+///
+/// Output:
+/// ```
+/// fn to_sql(&self) -> SqlResult<ToSqlOutput> {
+/// match self {
+/// KeyParameterValue::Invalid => Ok(ToSqlOutput::from(Null)),
+/// KeyParameterValue::Algorithm(v) => Ok(ToSqlOutput::from(v.to_primitive())),
+/// }
+/// }
+/// ```
+macro_rules! implement_to_sql {
+ (
+ @replace_type_spec
+ $enum_name:ident,
+ [$($out:tt)*],
+ [$vname:ident($vtype:ty), $($in:tt)*]
+ ) => {
+ implement_to_sql!{@replace_type_spec $enum_name, [ $($out)*
+ $enum_name::$vname(v) => Ok(ToSqlOutput::from(v.to_primitive())),
+ ], [$($in)*]}
+ };
+ (
+ @replace_type_spec
+ $enum_name:ident,
+ [$($out:tt)*],
+ [$vname:ident, $($in:tt)*]
+ ) => {
+ implement_to_sql!{@replace_type_spec $enum_name, [ $($out)*
+ $enum_name::$vname => Ok(ToSqlOutput::from(Null)),
+ ], [$($in)*]}
+ };
+ (@replace_type_spec $enum_name:ident, [$($out:tt)*], []) => {
+ /// Converts $enum_name to be stored in a rusqlite database.
+ fn to_sql(&self) -> SqlResult<ToSqlOutput> {
+ match self {
+ $($out)*
+ }
+ }
+ };
+
+
+ ($enum_name:ident; $($vname:ident$(($vtype:ty))?),*) => {
+ impl ToSql for $enum_name {
+ implement_to_sql!{@replace_type_spec $enum_name, [], [$($vname$(($vtype))?,)*]}
+ }
+
+ }
+}
+
+/// Expands the list of KeyParameterValue variants as follows:
+///
+/// Input:
+/// Invalid with tag INVALID and field Invalid,
+/// Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+///
+/// Output:
+/// ```
+/// pub fn new_from_sql(
+/// tag: Tag,
+/// data: &SqlField,
+/// ) -> Result<Self> {
+/// Ok(match self {
+/// Tag::Invalid => KeyParameterValue::Invalid,
+/// Tag::ALGORITHM => {
+/// KeyParameterValue::Algorithm(<Algorithm>::from_primitive(data
+/// .get()
+/// .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
+/// .context(concat!("Failed to read sql data for tag: ", "ALGORITHM", "."))?
+/// ))
+/// },
+/// })
+/// }
+/// ```
+macro_rules! implement_new_from_sql {
+ ($enum_name:ident; $($vname:ident$(($vtype:ty))? $tag_name:ident),*) => {
+ /// Takes a tag and an SqlField and attempts to construct a KeyParameter value.
+ /// This function may fail if the parameter value cannot be extracted from the
+ /// database cell.
+ pub fn new_from_sql(
+ tag: Tag,
+ data: &SqlField,
+ ) -> Result<Self> {
+ Ok(match tag {
+ $(
+ Tag::$tag_name => {
+ $enum_name::$vname$((<$vtype>::from_primitive(data
+ .get()
+ .map_err(|_| KeystoreError::Rc(ResponseCode::VALUE_CORRUPTED))
+ .context(concat!(
+ "Failed to read sql data for tag: ",
+ stringify!($tag_name),
+ "."
+ ))?
+ )))?
+ },
+ )*
+ _ => $enum_name::Invalid,
+ })
+ }
+ };
+}
+
+/// This key parameter default is used during the conversion from KeyParameterValue
+/// to keymint::KeyParameterValue. Keystore's version does not have wrapped types
+/// for boolean tags and the tag Invalid. The AIDL version uses bool and integer
+/// variants respectively. This default function is invoked in these cases to
+/// homogenize the rules for boolean and invalid tags.
+/// The bool variant returns true because boolean parameters are implicitly true
+/// if present.
+trait KpDefault {
+ fn default() -> Self;
+}
+
+impl KpDefault for i32 {
+ fn default() -> Self {
+ 0
+ }
+}
+
+impl KpDefault for bool {
+ fn default() -> Self {
+ true
+ }
+}
+
+/// Expands the list of KeyParameterValue variants as follows:
+///
+/// Input:
+/// Invalid with tag INVALID and field Invalid,
+/// Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+///
+/// Output:
+/// ```
+/// impl From<KmKeyParameter> for KeyParameterValue {
+/// fn from(kp: KmKeyParameter) -> Self {
+/// match kp {
+/// KmKeyParameter { tag: Tag::INVALID, value: KmKeyParameterValue::Invalid(_) }
+/// => $enum_name::$vname,
+/// KmKeyParameter { tag: Tag::Algorithm, value: KmKeyParameterValue::Algorithm(v) }
+/// => $enum_name::Algorithm(v),
+/// _ => $enum_name::Invalid,
+/// }
+/// }
+/// }
+///
+/// impl Into<KmKeyParameter> for KeyParameterValue {
+/// fn into(self) -> KmKeyParameter {
+/// match self {
+/// KeyParameterValue::Invalid => KmKeyParameter {
+/// tag: Tag::INVALID,
+/// value: KmKeyParameterValue::Invalid(KpDefault::default())
+/// },
+/// KeyParameterValue::Algorithm(v) => KmKeyParameter {
+/// tag: Tag::ALGORITHM,
+/// value: KmKeyParameterValue::Algorithm(v)
+/// },
+/// }
+/// }
+/// }
+/// ```
+macro_rules! implement_try_from_to_km_parameter {
+ // The first three rules expand From<KmKeyParameter>.
+ (
+ @from
+ $enum_name:ident,
+ [$($out:tt)*],
+ [$vname:ident($vtype:ty) $tag_name:ident $field_name:ident, $($in:tt)*]
+ ) => {
+ implement_try_from_to_km_parameter!{@from $enum_name, [$($out)*
+ KmKeyParameter {
+ tag: Tag::$tag_name,
+ value: KmKeyParameterValue::$field_name(v)
+ } => $enum_name::$vname(v),
+ ], [$($in)*]
+ }};
+ (
+ @from
+ $enum_name:ident,
+ [$($out:tt)*],
+ [$vname:ident $tag_name:ident $field_name:ident, $($in:tt)*]
+ ) => {
+ implement_try_from_to_km_parameter!{@from $enum_name, [$($out)*
+ KmKeyParameter {
+ tag: Tag::$tag_name,
+ value: KmKeyParameterValue::$field_name(_)
+ } => $enum_name::$vname,
+ ], [$($in)*]
+ }};
+ (@from $enum_name:ident, [$($out:tt)*], []) => {
+ impl From<KmKeyParameter> for $enum_name {
+ fn from(kp: KmKeyParameter) -> Self {
+ match kp {
+ $($out)*
+ _ => $enum_name::Invalid,
+ }
+ }
+ }
+ };
+
+ // The next three rules expand Into<KmKeyParameter>.
+ (
+ @into
+ $enum_name:ident,
+ [$($out:tt)*],
+ [$vname:ident($vtype:ty) $tag_name:ident $field_name:ident, $($in:tt)*]
+ ) => {
+ implement_try_from_to_km_parameter!{@into $enum_name, [$($out)*
+ $enum_name::$vname(v) => KmKeyParameter {
+ tag: Tag::$tag_name,
+ value: KmKeyParameterValue::$field_name(v)
+ },
+ ], [$($in)*]
+ }};
+ (
+ @into
+ $enum_name:ident,
+ [$($out:tt)*],
+ [$vname:ident $tag_name:ident $field_name:ident, $($in:tt)*]
+ ) => {
+ implement_try_from_to_km_parameter!{@into $enum_name, [$($out)*
+ $enum_name::$vname => KmKeyParameter {
+ tag: Tag::$tag_name,
+ value: KmKeyParameterValue::$field_name(KpDefault::default())
+ },
+ ], [$($in)*]
+ }};
+ (@into $enum_name:ident, [$($out:tt)*], []) => {
+ impl Into<KmKeyParameter> for $enum_name {
+ fn into(self) -> KmKeyParameter {
+ match self {
+ $($out)*
+ }
+ }
+ }
+ };
+
+
+ ($enum_name:ident; $($vname:ident$(($vtype:ty))? $tag_name:ident $field_name:ident),*) => {
+ implement_try_from_to_km_parameter!(
+ @from $enum_name,
+ [],
+ [$($vname$(($vtype))? $tag_name $field_name,)*]
+ );
+ implement_try_from_to_km_parameter!(
+ @into $enum_name,
+ [],
+ [$($vname$(($vtype))? $tag_name $field_name,)*]
+ );
+ };
+}
+
+/// This is the top level macro. While the other macros do most of the heavy lifting, this takes
+/// the key parameter list and passes it on to the other macros to generate all of the conversion
+/// functions. In addition, it generates an important test vector for verifying that tag type of the
+/// keymint tag matches the associated keymint KeyParameterValue field.
+macro_rules! implement_key_parameter_value {
+ (
+ $(#[$enum_meta:meta])*
+ $enum_vis:vis enum $enum_name:ident {
+ $(
+ $(#[$emeta:meta])*
+ $vname:ident$(($vtype:ty))? with tag $tag_name:ident and field $field_name:ident
+ ),* $(,)?
+ }
+ ) => {
+ 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
+ Invalid with tag INVALID and field Invalid,
+ /// Set of purposes for which the key may be used
+ KeyPurpose(KeyPurpose) with tag PURPOSE and field KeyPurpose,
+ /// Cryptographic algorithm with which the key is used
+ Algorithm(Algorithm) with tag ALGORITHM and field Algorithm,
+ /// Size of the key , in bits
+ KeySize(i32) with tag KEY_SIZE and field Integer,
+ /// Block cipher mode(s) with which the key may be used
+ BlockMode(BlockMode) with tag BLOCK_MODE and field BlockMode,
+ /// Digest algorithms that may be used with the key to perform signing and verification
+ Digest(Digest) with tag DIGEST and field Digest,
+ /// Padding modes that may be used with the key. Relevant to RSA, AES and 3DES keys.
+ PaddingMode(PaddingMode) with tag PADDING and field PaddingMode,
+ /// Can the caller provide a nonce for nonce-requiring operations
+ CallerNonce with tag CALLER_NONCE and field BoolValue,
+ /// Minimum length of MAC for HMAC keys and AES keys that support GCM mode
+ MinMacLength(i32) with tag MIN_MAC_LENGTH and field Integer,
+ /// The elliptic curve
+ EcCurve(EcCurve) with tag EC_CURVE and field EcCurve,
+ /// Value of the public exponent for an RSA key pair
+ RSAPublicExponent(i64) with tag RSA_PUBLIC_EXPONENT and field LongInteger,
+ /// An attestation certificate for the generated key should contain an application-scoped
+ /// and time-bounded device-unique ID
+ IncludeUniqueID with tag INCLUDE_UNIQUE_ID and field BoolValue,
+ //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
+ BootLoaderOnly with tag BOOTLOADER_ONLY and field BoolValue,
+ /// When deleted, the key is guaranteed to be permanently deleted and unusable
+ RollbackResistance with tag ROLLBACK_RESISTANCE and field BoolValue,
+ /// The date and time at which the key becomes active
+ ActiveDateTime(i64) with tag ACTIVE_DATETIME and field DateTime,
+ /// The date and time at which the key expires for signing and encryption
+ OriginationExpireDateTime(i64) with tag ORIGINATION_EXPIRE_DATETIME and field DateTime,
+ /// The date and time at which the key expires for verification and decryption
+ UsageExpireDateTime(i64) with tag USAGE_EXPIRE_DATETIME and field DateTime,
+ /// Minimum amount of time that elapses between allowed operations
+ MinSecondsBetweenOps(i32) with tag MIN_SECONDS_BETWEEN_OPS and field Integer,
+ /// Maximum number of times that a key may be used between system reboots
+ MaxUsesPerBoot(i32) with tag MAX_USES_PER_BOOT and field Integer,
+ /// ID of the Android user that is permitted to use the key
+ UserID(i32) with tag USER_ID and field Integer,
+ /// A key may only be used under a particular secure user authentication state
+ UserSecureID(i64) with tag USER_SECURE_ID and field LongInteger,
+ /// No authentication is required to use this key
+ NoAuthRequired with tag NO_AUTH_REQUIRED and field BoolValue,
+ /// The types of user authenticators that may be used to authorize this key
+ HardwareAuthenticatorType(HardwareAuthenticatorType) with tag USER_AUTH_TYPE and field HardwareAuthenticatorType,
+ /// The time in seconds for which the key is authorized for use, after user authentication
+ AuthTimeout(i32) with tag AUTH_TIMEOUT and field Integer,
+ /// The key may be used after authentication timeout if device is still on-body
+ AllowWhileOnBody with tag ALLOW_WHILE_ON_BODY and field BoolValue,
+ /// The key must be unusable except when the user has provided proof of physical presence
+ TrustedUserPresenceRequired with tag TRUSTED_USER_PRESENCE_REQUIRED and field BoolValue,
+ /// 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
+ TrustedConfirmationRequired with tag TRUSTED_CONFIRMATION_REQUIRED and field BoolValue,
+ /// The key may only be used when the device is unlocked
+ UnlockedDeviceRequired with tag UNLOCKED_DEVICE_REQUIRED and field BoolValue,
+ /// When provided to generateKey or importKey, this tag specifies data
+ /// that is necessary during all uses of the key
+ ApplicationID(Vec<u8>) with tag APPLICATION_ID and field Blob,
+ /// When provided to generateKey or importKey, this tag specifies data
+ /// that is necessary during all uses of the key
+ ApplicationData(Vec<u8>) with tag APPLICATION_DATA and field Blob,
+ /// Specifies the date and time the key was created
+ CreationDateTime(i64) with tag CREATION_DATETIME and field DateTime,
+ /// Specifies where the key was created, if known
+ KeyOrigin(KeyOrigin) with tag ORIGIN and field Origin,
+ /// The key used by verified boot to validate the operating system booted
+ RootOfTrust(Vec<u8>) with tag ROOT_OF_TRUST and field Blob,
+ /// System OS version with which the key may be used
+ OSVersion(i32) with tag OS_VERSION and field Integer,
+ /// Specifies the system security patch level with which the key may be used
+ OSPatchLevel(i32) with tag OS_PATCHLEVEL and field Integer,
+ /// Specifies a unique, time-based identifier
+ UniqueID(Vec<u8>) with tag UNIQUE_ID and field Blob,
+ /// Used to deliver a "challenge" value to the attestKey() method
+ AttestationChallenge(Vec<u8>) with tag ATTESTATION_CHALLENGE and field Blob,
+ /// The set of applications which may use a key, used only with attestKey()
+ AttestationApplicationID(Vec<u8>) with tag ATTESTATION_APPLICATION_ID and field Blob,
+ /// Provides the device's brand name, to attestKey()
+ AttestationIdBrand(Vec<u8>) with tag ATTESTATION_ID_BRAND and field Blob,
+ /// Provides the device's device name, to attestKey()
+ AttestationIdDevice(Vec<u8>) with tag ATTESTATION_ID_DEVICE and field Blob,
+ /// Provides the device's product name, to attestKey()
+ AttestationIdProduct(Vec<u8>) with tag ATTESTATION_ID_PRODUCT and field Blob,
+ /// Provides the device's serial number, to attestKey()
+ AttestationIdSerial(Vec<u8>) with tag ATTESTATION_ID_SERIAL and field Blob,
+ /// Provides the IMEIs for all radios on the device, to attestKey()
+ AttestationIdIMEI(Vec<u8>) with tag ATTESTATION_ID_IMEI and field Blob,
+ /// Provides the MEIDs for all radios on the device, to attestKey()
+ AttestationIdMEID(Vec<u8>) with tag ATTESTATION_ID_MEID and field Blob,
+ /// Provides the device's manufacturer name, to attestKey()
+ AttestationIdManufacturer(Vec<u8>) with tag ATTESTATION_ID_MANUFACTURER and field Blob,
+ /// Provides the device's model name, to attestKey()
+ AttestationIdModel(Vec<u8>) with tag ATTESTATION_ID_MODEL and field Blob,
+ /// Specifies the vendor image security patch level with which the key may be used
+ VendorPatchLevel(i32) with tag VENDOR_PATCHLEVEL and field Integer,
+ /// Specifies the boot image (kernel) security patch level with which the key may be used
+ BootPatchLevel(i32) with tag BOOT_PATCHLEVEL and field Integer,
+ /// Provides "associated data" for AES-GCM encryption or decryption
+ AssociatedData(Vec<u8>) with tag ASSOCIATED_DATA and field Blob,
+ /// Provides or returns a nonce or Initialization Vector (IV) for AES-GCM,
+ /// AES-CBC, AES-CTR, or 3DES-CBC encryption or decryption
+ Nonce(Vec<u8>) with tag NONCE and field Blob,
+ /// Provides the requested length of a MAC or GCM authentication tag, in bits
+ MacLength(i32) with tag MAC_LENGTH and field Integer,
+ /// Specifies whether the device has been factory reset since the
+ /// last unique ID rotation. Used for key attestation
+ ResetSinceIdRotation with tag RESET_SINCE_ID_ROTATION and field BoolValue,
+ /// Used to deliver a cryptographic token proving that the user
+ /// confirmed a signing request
+ ConfirmationToken(Vec<u8>) with tag CONFIRMATION_TOKEN and field Blob,
+}
+}
+
+impl From<&KmKeyParameter> for KeyParameterValue {
+ fn from(kp: &KmKeyParameter) -> Self {
+ kp.clone().into()
+ }
+}
+
+/// KeyParameter wraps the KeyParameterValue and the security level at which it is enforced.
+#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub struct KeyParameter {
+ value: KeyParameterValue,
+ security_level: SecurityLevel,
+}
+
+impl KeyParameter {
+ /// Create an instance of KeyParameter, given the value and the security level.
+ pub fn new(value: KeyParameterValue, security_level: SecurityLevel) -> Self {
+ KeyParameter { value, security_level }
+ }
+
+ /// Construct a KeyParameter from the data from a rusqlite row.
+ /// Note that following variants of KeyParameterValue should not be stored:
+ /// IncludeUniqueID, ApplicationID, ApplicationData, RootOfTrust, UniqueID,
+ /// Attestation*, AssociatedData, Nonce, MacLength, ResetSinceIdRotation, ConfirmationToken.
+ /// This filtering is enforced at a higher level and here we support conversion for all the
+ /// variants.
+ pub fn new_from_sql(
+ tag_val: Tag,
+ data: &SqlField,
+ security_level_val: SecurityLevel,
+ ) -> Result<Self> {
+ Ok(Self {
+ value: KeyParameterValue::new_from_sql(tag_val, data)?,
+ security_level: security_level_val,
+ })
+ }
+
+ /// Get the KeyMint Tag of this this key parameter.
+ pub fn get_tag(&self) -> Tag {
+ self.value.get_tag()
+ }
+
+ /// Returns key parameter value.
+ pub fn key_parameter_value(&self) -> &KeyParameterValue {
+ &self.value
+ }
+
+ /// Returns the security level of this key parameter.
+ pub fn security_level(&self) -> &SecurityLevel {
+ &self.security_level
+ }
+
+ /// An authorization is a KeyParameter with an associated security level that is used
+ /// to convey the key characteristics to keystore clients. This function consumes
+ /// an internal KeyParameter representation to produce the Authorization wire type.
+ pub fn into_authorization(self) -> Authorization {
+ Authorization { securityLevel: self.security_level, keyParameter: self.value.into() }
+ }
+}
+
+#[cfg(test)]
+mod generated_key_parameter_tests {
+ use super::*;
+ use android_hardware_security_keymint::aidl::android::hardware::security::keymint::TagType::TagType;
+
+ fn get_field_by_tag_type(tag: Tag) -> KmKeyParameterValue {
+ let tag_type = TagType((tag.0 as u32 & 0xF0000000) as i32);
+ match tag {
+ Tag::ALGORITHM => return KmKeyParameterValue::Algorithm(Default::default()),
+ Tag::BLOCK_MODE => return KmKeyParameterValue::BlockMode(Default::default()),
+ Tag::PADDING => return KmKeyParameterValue::PaddingMode(Default::default()),
+ Tag::DIGEST => return KmKeyParameterValue::Digest(Default::default()),
+ Tag::EC_CURVE => return KmKeyParameterValue::EcCurve(Default::default()),
+ Tag::ORIGIN => return KmKeyParameterValue::Origin(Default::default()),
+ Tag::PURPOSE => return KmKeyParameterValue::KeyPurpose(Default::default()),
+ Tag::USER_AUTH_TYPE => {
+ return KmKeyParameterValue::HardwareAuthenticatorType(Default::default())
+ }
+ Tag::HARDWARE_TYPE => return KmKeyParameterValue::SecurityLevel(Default::default()),
+ _ => {}
+ }
+ match tag_type {
+ TagType::INVALID => return KmKeyParameterValue::Invalid(Default::default()),
+ TagType::ENUM | TagType::ENUM_REP => {}
+ TagType::UINT | TagType::UINT_REP => {
+ return KmKeyParameterValue::Integer(Default::default())
+ }
+ TagType::ULONG | TagType::ULONG_REP => {
+ return KmKeyParameterValue::LongInteger(Default::default())
+ }
+ TagType::DATE => return KmKeyParameterValue::DateTime(Default::default()),
+ TagType::BOOL => return KmKeyParameterValue::BoolValue(Default::default()),
+ TagType::BIGNUM | TagType::BYTES => {
+ return KmKeyParameterValue::Blob(Default::default())
+ }
+ _ => {}
+ }
+ panic!("Unknown tag/tag_type: {:?} {:?}", tag, tag_type);
+ }
+
+ fn check_field_matches_tag_type(list_o_parameters: &[KmKeyParameter]) {
+ for kp in list_o_parameters.iter() {
+ match (&kp.value, get_field_by_tag_type(kp.tag)) {
+ (&KmKeyParameterValue::Algorithm(_), KmKeyParameterValue::Algorithm(_))
+ | (&KmKeyParameterValue::BlockMode(_), KmKeyParameterValue::BlockMode(_))
+ | (&KmKeyParameterValue::PaddingMode(_), KmKeyParameterValue::PaddingMode(_))
+ | (&KmKeyParameterValue::Digest(_), KmKeyParameterValue::Digest(_))
+ | (&KmKeyParameterValue::EcCurve(_), KmKeyParameterValue::EcCurve(_))
+ | (&KmKeyParameterValue::Origin(_), KmKeyParameterValue::Origin(_))
+ | (&KmKeyParameterValue::KeyPurpose(_), KmKeyParameterValue::KeyPurpose(_))
+ | (
+ &KmKeyParameterValue::HardwareAuthenticatorType(_),
+ KmKeyParameterValue::HardwareAuthenticatorType(_),
+ )
+ | (&KmKeyParameterValue::SecurityLevel(_), KmKeyParameterValue::SecurityLevel(_))
+ | (&KmKeyParameterValue::Invalid(_), KmKeyParameterValue::Invalid(_))
+ | (&KmKeyParameterValue::Integer(_), KmKeyParameterValue::Integer(_))
+ | (&KmKeyParameterValue::LongInteger(_), KmKeyParameterValue::LongInteger(_))
+ | (&KmKeyParameterValue::DateTime(_), KmKeyParameterValue::DateTime(_))
+ | (&KmKeyParameterValue::BoolValue(_), KmKeyParameterValue::BoolValue(_))
+ | (&KmKeyParameterValue::Blob(_), KmKeyParameterValue::Blob(_)) => {}
+ (actual, expected) => panic!(
+ "Tag {:?} associated with variant {:?} expected {:?}",
+ kp.tag, actual, expected
+ ),
+ }
+ }
+ }
+
+ #[test]
+ fn key_parameter_value_field_matches_tag_type() {
+ check_field_matches_tag_type(&KeyParameterValue::make_field_matches_tag_type_test_vector());
+ }
+}
+
+#[cfg(test)]
+mod basic_tests {
+ use crate::key_parameter::*;
+
+ // Test basic functionality of KeyParameter.
+ #[test]
+ fn test_key_parameter() {
+ let key_parameter = KeyParameter::new(
+ KeyParameterValue::Algorithm(Algorithm::RSA),
+ SecurityLevel::STRONGBOX,
+ );
+
+ assert_eq!(key_parameter.get_tag(), Tag::ALGORITHM);
+
+ assert_eq!(
+ *key_parameter.key_parameter_value(),
+ KeyParameterValue::Algorithm(Algorithm::RSA)
+ );
+
+ assert_eq!(*key_parameter.security_level(), SecurityLevel::STRONGBOX);
+ }
+}
+
+/// The storage_tests module first tests the 'new_from_sql' method for KeyParameters of different
+/// data types and then tests 'to_sql' method for KeyParameters of those
+/// different data types. The five different data types for KeyParameter values are:
+/// i) enums of u32
+/// ii) u32
+/// iii) u64
+/// iv) Vec<u8>
+/// v) bool
+#[cfg(test)]
+mod storage_tests {
+ use crate::error::*;
+ use crate::key_parameter::*;
+ use anyhow::Result;
+ use rusqlite::types::ToSql;
+ use rusqlite::{params, Connection, NO_PARAMS};
+
+ /// Test initializing a KeyParameter (with key parameter value corresponding to an enum of i32)
+ /// from a database table row.
+ #[test]
+ fn test_new_from_sql_enum_i32() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(
+ &db,
+ 1,
+ Tag::ALGORITHM.0,
+ &Algorithm::RSA.0,
+ SecurityLevel::STRONGBOX.0,
+ )?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::ALGORITHM, key_param.get_tag());
+ assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::Algorithm(Algorithm::RSA));
+ assert_eq!(*key_param.security_level(), SecurityLevel::STRONGBOX);
+ Ok(())
+ }
+
+ /// Test initializing a KeyParameter (with key parameter value which is of i32)
+ /// from a database table row.
+ #[test]
+ fn test_new_from_sql_i32() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(&db, 1, Tag::KEY_SIZE.0, &1024, SecurityLevel::STRONGBOX.0)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::KEY_SIZE, key_param.get_tag());
+ assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::KeySize(1024));
+ Ok(())
+ }
+
+ /// Test initializing a KeyParameter (with key parameter value which is of i64)
+ /// from a database table row.
+ #[test]
+ fn test_new_from_sql_i64() -> Result<()> {
+ let db = init_db()?;
+ // max value for i64, just to test corner cases
+ insert_into_keyparameter(
+ &db,
+ 1,
+ Tag::RSA_PUBLIC_EXPONENT.0,
+ &(i64::MAX),
+ SecurityLevel::STRONGBOX.0,
+ )?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::RSA_PUBLIC_EXPONENT, key_param.get_tag());
+ assert_eq!(
+ *key_param.key_parameter_value(),
+ KeyParameterValue::RSAPublicExponent(i64::MAX)
+ );
+ Ok(())
+ }
+
+ /// Test initializing a KeyParameter (with key parameter value which is of bool)
+ /// from a database table row.
+ #[test]
+ fn test_new_from_sql_bool() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(&db, 1, Tag::CALLER_NONCE.0, &Null, SecurityLevel::STRONGBOX.0)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::CALLER_NONCE, key_param.get_tag());
+ assert_eq!(*key_param.key_parameter_value(), KeyParameterValue::CallerNonce);
+ Ok(())
+ }
+
+ /// Test initializing a KeyParameter (with key parameter value which is of Vec<u8>)
+ /// from a database table row.
+ #[test]
+ fn test_new_from_sql_vec_u8() -> Result<()> {
+ let db = init_db()?;
+ let app_id = String::from("MyAppID");
+ let app_id_bytes = app_id.into_bytes();
+ insert_into_keyparameter(
+ &db,
+ 1,
+ Tag::APPLICATION_ID.0,
+ &app_id_bytes,
+ SecurityLevel::STRONGBOX.0,
+ )?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::APPLICATION_ID, key_param.get_tag());
+ assert_eq!(
+ *key_param.key_parameter_value(),
+ KeyParameterValue::ApplicationID(app_id_bytes)
+ );
+ Ok(())
+ }
+
+ /// Test storing a KeyParameter (with key parameter value which corresponds to an enum of i32)
+ /// in the database
+ #[test]
+ fn test_to_sql_enum_i32() -> Result<()> {
+ let db = init_db()?;
+ let kp = KeyParameter::new(
+ KeyParameterValue::Algorithm(Algorithm::RSA),
+ SecurityLevel::STRONGBOX,
+ );
+ store_keyparameter(&db, 1, &kp)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(kp.get_tag(), key_param.get_tag());
+ assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+ assert_eq!(kp.security_level(), key_param.security_level());
+ Ok(())
+ }
+
+ /// Test storing a KeyParameter (with key parameter value which is of i32) in the database
+ #[test]
+ fn test_to_sql_i32() -> Result<()> {
+ let db = init_db()?;
+ let kp = KeyParameter::new(KeyParameterValue::KeySize(1024), SecurityLevel::STRONGBOX);
+ store_keyparameter(&db, 1, &kp)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(kp.get_tag(), key_param.get_tag());
+ assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+ assert_eq!(kp.security_level(), key_param.security_level());
+ Ok(())
+ }
+
+ /// Test storing a KeyParameter (with key parameter value which is of i64) in the database
+ #[test]
+ fn test_to_sql_i64() -> Result<()> {
+ let db = init_db()?;
+ // max value for i64, just to test corner cases
+ let kp = KeyParameter::new(
+ KeyParameterValue::RSAPublicExponent(i64::MAX),
+ SecurityLevel::STRONGBOX,
+ );
+ store_keyparameter(&db, 1, &kp)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(kp.get_tag(), key_param.get_tag());
+ assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+ assert_eq!(kp.security_level(), key_param.security_level());
+ Ok(())
+ }
+
+ /// Test storing a KeyParameter (with key parameter value which is of Vec<u8>) in the database
+ #[test]
+ fn test_to_sql_vec_u8() -> Result<()> {
+ let db = init_db()?;
+ let kp = KeyParameter::new(
+ KeyParameterValue::ApplicationID(String::from("MyAppID").into_bytes()),
+ SecurityLevel::STRONGBOX,
+ );
+ store_keyparameter(&db, 1, &kp)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(kp.get_tag(), key_param.get_tag());
+ assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+ assert_eq!(kp.security_level(), key_param.security_level());
+ Ok(())
+ }
+
+ /// Test storing a KeyParameter (with key parameter value which is of i32) in the database
+ #[test]
+ fn test_to_sql_bool() -> Result<()> {
+ let db = init_db()?;
+ let kp = KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::STRONGBOX);
+ store_keyparameter(&db, 1, &kp)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(kp.get_tag(), key_param.get_tag());
+ assert_eq!(kp.key_parameter_value(), key_param.key_parameter_value());
+ assert_eq!(kp.security_level(), key_param.security_level());
+ Ok(())
+ }
+
+ #[test]
+ /// Test Tag::Invalid
+ fn test_invalid_tag() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(&db, 1, 0, &123, 1)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::INVALID, key_param.get_tag());
+ Ok(())
+ }
+
+ #[test]
+ fn test_non_existing_enum_variant() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(&db, 1, 100, &123, 1)?;
+ let key_param = query_from_keyparameter(&db)?;
+ assert_eq!(Tag::INVALID, key_param.get_tag());
+ Ok(())
+ }
+
+ #[test]
+ fn test_invalid_conversion_from_sql() -> Result<()> {
+ let db = init_db()?;
+ insert_into_keyparameter(&db, 1, Tag::ALGORITHM.0, &Null, 1)?;
+ tests::check_result_contains_error_string(
+ query_from_keyparameter(&db),
+ "Failed to read sql data for tag: ALGORITHM.",
+ );
+ Ok(())
+ }
+
+ /// Helper method to init database table for key parameter
+ fn init_db() -> Result<Connection> {
+ let db = Connection::open_in_memory().context("Failed to initialize sqlite connection.")?;
+ db.execute("ATTACH DATABASE ? as 'persistent';", params![""])
+ .context("Failed to attach databases.")?;
+ db.execute(
+ "CREATE TABLE IF NOT EXISTS persistent.keyparameter (
+ keyentryid INTEGER,
+ tag INTEGER,
+ data ANY,
+ security_level INTEGER);",
+ NO_PARAMS,
+ )
+ .context("Failed to initialize \"keyparameter\" table.")?;
+ Ok(db)
+ }
+
+ /// Helper method to insert an entry into key parameter table, with individual parameters
+ fn insert_into_keyparameter<T: ToSql>(
+ db: &Connection,
+ key_id: i64,
+ tag: i32,
+ value: &T,
+ security_level: i32,
+ ) -> Result<()> {
+ db.execute(
+ "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level)
+ VALUES(?, ?, ?, ?);",
+ params![key_id, tag, *value, security_level],
+ )?;
+ Ok(())
+ }
+
+ /// Helper method to store a key parameter instance.
+ fn store_keyparameter(db: &Connection, key_id: i64, kp: &KeyParameter) -> Result<()> {
+ db.execute(
+ "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level)
+ VALUES(?, ?, ?, ?);",
+ params![key_id, kp.get_tag().0, kp.key_parameter_value(), kp.security_level().0],
+ )?;
+ Ok(())
+ }
+
+ /// Helper method to query a row from keyparameter table
+ fn query_from_keyparameter(db: &Connection) -> Result<KeyParameter> {
+ let mut stmt =
+ db.prepare("SELECT tag, data, security_level FROM persistent.keyparameter")?;
+ let mut rows = stmt.query(NO_PARAMS)?;
+ let row = rows.next()?.unwrap();
+ Ok(KeyParameter::new_from_sql(
+ Tag(row.get(0)?),
+ &SqlField::new(1, row),
+ SecurityLevel(row.get(2)?),
+ )?)
+ }
+}
+
+/// The wire_tests module tests the 'convert_to_wire' and 'convert_from_wire' methods for
+/// KeyParameter, for the four different types used in KmKeyParameter, in addition to Invalid
+/// key parameter.
+/// i) bool
+/// ii) integer
+/// iii) longInteger
+/// iv) blob
+#[cfg(test)]
+mod wire_tests {
+ use crate::key_parameter::*;
+ /// unit tests for to conversions
+ #[test]
+ fn test_convert_to_wire_invalid() {
+ let kp = KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::STRONGBOX);
+ assert_eq!(
+ KmKeyParameter { tag: Tag::INVALID, value: KmKeyParameterValue::Invalid(0) },
+ kp.value.into()
+ );
+ }
+ #[test]
+ fn test_convert_to_wire_bool() {
+ let kp = KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::STRONGBOX);
+ assert_eq!(
+ KmKeyParameter { tag: Tag::CALLER_NONCE, value: KmKeyParameterValue::BoolValue(true) },
+ kp.value.into()
+ );
+ }
+ #[test]
+ fn test_convert_to_wire_integer() {
+ let kp = KeyParameter::new(
+ KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
+ SecurityLevel::STRONGBOX,
+ );
+ assert_eq!(
+ KmKeyParameter {
+ tag: Tag::PURPOSE,
+ value: KmKeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT)
+ },
+ kp.value.into()
+ );
+ }
+ #[test]
+ fn test_convert_to_wire_long_integer() {
+ let kp =
+ KeyParameter::new(KeyParameterValue::UserSecureID(i64::MAX), SecurityLevel::STRONGBOX);
+ assert_eq!(
+ KmKeyParameter {
+ tag: Tag::USER_SECURE_ID,
+ value: KmKeyParameterValue::LongInteger(i64::MAX)
+ },
+ kp.value.into()
+ );
+ }
+ #[test]
+ fn test_convert_to_wire_blob() {
+ let kp = KeyParameter::new(
+ KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
+ SecurityLevel::STRONGBOX,
+ );
+ assert_eq!(
+ KmKeyParameter {
+ tag: Tag::CONFIRMATION_TOKEN,
+ value: KmKeyParameterValue::Blob(String::from("ConfirmationToken").into_bytes())
+ },
+ kp.value.into()
+ );
+ }
+
+ /// unit tests for from conversion
+ #[test]
+ fn test_convert_from_wire_invalid() {
+ let aidl_kp = KmKeyParameter { tag: Tag::INVALID, ..Default::default() };
+ assert_eq!(KeyParameterValue::Invalid, aidl_kp.into());
+ }
+ #[test]
+ fn test_convert_from_wire_bool() {
+ let aidl_kp =
+ KmKeyParameter { tag: Tag::CALLER_NONCE, value: KmKeyParameterValue::BoolValue(true) };
+ assert_eq!(KeyParameterValue::CallerNonce, aidl_kp.into());
+ }
+ #[test]
+ fn test_convert_from_wire_integer() {
+ let aidl_kp = KmKeyParameter {
+ tag: Tag::PURPOSE,
+ value: KmKeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT),
+ };
+ assert_eq!(KeyParameterValue::KeyPurpose(KeyPurpose::ENCRYPT), aidl_kp.into());
+ }
+ #[test]
+ fn test_convert_from_wire_long_integer() {
+ let aidl_kp = KmKeyParameter {
+ tag: Tag::USER_SECURE_ID,
+ value: KmKeyParameterValue::LongInteger(i64::MAX),
+ };
+ assert_eq!(KeyParameterValue::UserSecureID(i64::MAX), aidl_kp.into());
+ }
+ #[test]
+ fn test_convert_from_wire_blob() {
+ let aidl_kp = KmKeyParameter {
+ tag: Tag::CONFIRMATION_TOKEN,
+ value: KmKeyParameterValue::Blob(String::from("ConfirmationToken").into_bytes()),
+ };
+ assert_eq!(
+ KeyParameterValue::ConfirmationToken(String::from("ConfirmationToken").into_bytes()),
+ aidl_kp.into()
+ );
+ }
+}
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
new file mode 100644
index 0000000..59e5972
--- /dev/null
+++ b/keystore2/src/keystore2_main.rs
@@ -0,0 +1,74 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This crate implements the Keystore 2.0 service entry point.
+
+use 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() {
+ // Initialize android logging.
+ android_logger::init_once(
+ android_logger::Config::default().with_tag("keystore2").with_min_level(log::Level::Debug),
+ );
+ // Redirect panic messages to logcat.
+ panic::set_hook(Box::new(|panic_info| {
+ error!("{}", panic_info);
+ }));
+
+ // Saying hi.
+ info!("Keystore2 is starting.");
+
+ let mut args = std::env::args();
+ args.next().expect("That's odd. How is there not even a first argument?");
+
+ // Keystore changes to the database directory on startup (typically /data/misc/keystore).
+ // For the ground truth check the service startup rule for init (typically in keystore2.rc).
+ if let Some(dir) = args.next() {
+ if std::env::set_current_dir(dir.clone()).is_err() {
+ panic!("Failed to set working directory {}.", dir)
+ }
+ } else {
+ panic!("Must specify a working directory.");
+ }
+
+ let ks_service = KeystoreService::new_native_binder().unwrap_or_else(|e| {
+ panic!("Failed to create service {} because of {:?}.", KS2_SERVICE_NAME, e);
+ });
+ binder::add_service(KS2_SERVICE_NAME, ks_service.as_binder()).unwrap_or_else(|e| {
+ panic!("Failed to register service {} because of {:?}.", KS2_SERVICE_NAME, e);
+ });
+
+ let apc_service = ApcManager::new_native_binder().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!("Successfully registered Keystore 2.0 service.");
+
+ info!("Starting thread pool now.");
+ binder::ProcessState::start_thread_pool();
+
+ info!("Joining thread pool now.");
+ binder::ProcessState::join_thread_pool();
+}
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
new file mode 100644
index 0000000..3fb938c
--- /dev/null
+++ b/keystore2/src/lib.rs
@@ -0,0 +1,38 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This crate implements the Android Keystore 2.0 service.
+
+pub mod apc;
+pub mod auth_token_handler;
+pub mod database;
+pub mod enforcements;
+pub mod error;
+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 db_utils;
+mod super_key;
+
+#[cfg(test)]
+mod test {
+ pub mod utils;
+}
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
new file mode 100644
index 0000000..9a35154
--- /dev/null
+++ b/keystore2/src/operation.rs
@@ -0,0 +1,821 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This crate implements the `IKeystoreOperation` AIDL interface, which represents
+//! an ongoing key operation, as well as the operation database, which is mainly
+//! required for tracking operations for the purpose of pruning.
+//! This crate also implements an operation pruning strategy.
+//!
+//! Operations implement the API calls update, finish, and abort.
+//! Additionally, an operation can be dropped and pruned. The former
+//! happens if the client deletes a binder to the operation object.
+//! An existing operation may get pruned when running out of operation
+//! slots and a new operation takes precedence.
+//!
+//! ## Operation Lifecycle
+//! An operation gets created when the client calls `IKeystoreSecurityLevel::create`.
+//! It may receive zero or more update request. The lifecycle ends when:
+//! * `update` yields an error.
+//! * `finish` is called.
+//! * `abort` is called.
+//! * The operation gets dropped.
+//! * The operation gets pruned.
+//! `Operation` has an `Outcome` member. While the outcome is `Outcome::Unknown`,
+//! the operation is active and in a good state. Any of the above conditions may
+//! change the outcome to one of the defined outcomes Success, Abort, Dropped,
+//! Pruned, or ErrorCode. The latter is chosen in the case of an unexpected error, during
+//! `update` or `finish`. `Success` is chosen iff `finish` completes without error.
+//! Note that all operations get dropped eventually in the sense that they lose
+//! their last reference and get destroyed. At that point, the fate of the operation
+//! gets logged. However, an operation will transition to `Outcome::Dropped` iff
+//! the operation was still active (`Outcome::Unknown`) at that time.
+//!
+//! ## Operation Dropping
+//! To observe the dropping of an operation, we have to make sure that there
+//! are no strong references to the IBinder representing this operation.
+//! This would be simple enough if the operation object would need to be accessed
+//! only by transactions. But to perform pruning, we have to retain a reference to the
+//! original operation object.
+//!
+//! ## Operation Pruning
+//! Pruning an operation happens during the creation of a new operation.
+//! We have to iterate through the operation database to find a suitable
+//! candidate. Then we abort and finalize this operation setting its outcome to
+//! `Outcome::Pruned`. The corresponding KeyMint operation slot will have been freed
+//! up at this point, but the `Operation` object lingers. When the client
+//! attempts to use the operation again they will receive
+//! ErrorCode::INVALID_OPERATION_HANDLE indicating that the operation no longer
+//! exits. This should be the cue for the client to destroy its binder.
+//! At that point the operation gets dropped.
+//!
+//! ## Architecture
+//! The `IKeystoreOperation` trait is implemented by `KeystoreOperation`.
+//! This acts as a proxy object holding a strong reference to actual operation
+//! implementation `Operation`.
+//!
+//! ```
+//! struct KeystoreOperation {
+//! operation: Mutex<Option<Arc<Operation>>>,
+//! }
+//! ```
+//!
+//! The `Mutex` serves two purposes. It provides interior mutability allowing
+//! us to set the Option to None. We do this when the life cycle ends during
+//! a call to `update`, `finish`, or `abort`. As a result most of the Operation
+//! related resources are freed. The `KeystoreOperation` proxy object still
+//! lingers until dropped by the client.
+//! The second purpose is to protect operations against concurrent usage.
+//! Failing to lock this mutex yields `ResponseCode::OPERATION_BUSY` and indicates
+//! a programming error in the client.
+//!
+//! Note that the Mutex only protects the operation against concurrent client calls.
+//! We still retain weak references to the operation in the operation database:
+//!
+//! ```
+//! struct OperationDb {
+//! operations: Mutex<Vec<Weak<Operation>>>
+//! }
+//! ```
+//!
+//! This allows us to access the operations for the purpose of pruning.
+//! We do this in three phases.
+//! 1. We gather the pruning information. Besides non mutable information,
+//! we access `last_usage` which is protected by a mutex.
+//! We only lock this mutex for single statements at a time. During
+//! this phase we hold the operation db lock.
+//! 2. We choose a pruning candidate by computing the pruning resistance
+//! of each operation. We do this entirely with information we now
+//! have on the stack without holding any locks.
+//! (See `OperationDb::prune` for more details on the pruning strategy.)
+//! 3. During pruning we briefly lock the operation database again to get the
+//! the pruning candidate by index. We then attempt to abort the candidate.
+//! If the candidate was touched in the meantime or is currently fulfilling
+//! a request (i.e., the client calls update, finish, or abort),
+//! we go back to 1 and try again.
+//!
+//! So the outer Mutex in `KeystoreOperation::operation` only protects
+//! operations against concurrent client calls but not against concurrent
+//! pruning attempts. This is what the `Operation::outcome` mutex is used for.
+//!
+//! ```
+//! struct Operation {
+//! ...
+//! outcome: Mutex<Outcome>,
+//! ...
+//! }
+//! ```
+//!
+//! Any request that can change the outcome, i.e., `update`, `finish`, `abort`,
+//! `drop`, and `prune` has to take the outcome lock and check if the outcome
+//! is still `Outcome::Unknown` before entering. `prune` is special in that
+//! it will `try_lock`, because we don't want to be blocked on a potentially
+//! long running request at another operation. If it fails to get the lock
+//! the operation is either being touched, which changes its pruning resistance,
+//! or it transitions to its end-of-life, which means we may get a free slot.
+//! Either way, we have to revaluate the pruning scores.
+
+use std::{
+ collections::HashMap,
+ sync::{Arc, Mutex, MutexGuard, Weak},
+ time::Duration,
+ time::Instant,
+};
+
+use crate::error::{map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
+use crate::utils::Asp;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ ByteArray::ByteArray, IKeyMintOperation::IKeyMintOperation,
+ KeyParameter::KeyParameter as KmParam, KeyParameterArray::KeyParameterArray,
+ KeyParameterValue::KeyParameterValue as KmParamValue, Tag::Tag,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ IKeystoreOperation::BnKeystoreOperation, IKeystoreOperation::IKeystoreOperation,
+};
+use anyhow::{anyhow, Context, Result};
+use binder::{IBinder, Interface};
+
+/// Operations have `Outcome::Unknown` as long as they are active. They transition
+/// to one of the other variants exactly once. The distinction in outcome is mainly
+/// for the statistic.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+enum Outcome {
+ Unknown,
+ Success,
+ Abort,
+ Dropped,
+ Pruned,
+ ErrorCode(ErrorCode),
+}
+
+/// Operation bundles all of the operation related resources and tracks the operation's
+/// outcome.
+#[derive(Debug)]
+pub struct Operation {
+ // The index of this operation in the OperationDb.
+ index: usize,
+ km_op: Asp,
+ last_usage: Mutex<Instant>,
+ outcome: Mutex<Outcome>,
+ owner: u32, // Uid of the operation's owner.
+}
+
+struct PruningInfo {
+ last_usage: Instant,
+ owner: u32,
+ index: usize,
+}
+
+// We don't except more than 32KiB of data in `update`, `updateAad`, and `finish`.
+const MAX_RECEIVE_DATA: usize = 0x8000;
+
+impl Operation {
+ /// Constructor
+ pub fn new(index: usize, km_op: Box<dyn IKeyMintOperation>, owner: u32) -> Self {
+ Self {
+ index,
+ km_op: Asp::new(km_op.as_binder()),
+ last_usage: Mutex::new(Instant::now()),
+ outcome: Mutex::new(Outcome::Unknown),
+ owner,
+ }
+ }
+
+ fn get_pruning_info(&self) -> Option<PruningInfo> {
+ // An operation may be finalized.
+ if let Ok(guard) = self.outcome.try_lock() {
+ match *guard {
+ Outcome::Unknown => {}
+ // If the outcome is any other than unknown, it has been finalized,
+ // and we can no longer consider it for pruning.
+ _ => return None,
+ }
+ }
+ // Else: If we could not grab the lock, this means that the operation is currently
+ // being used and it may be transitioning to finalized or it was simply updated.
+ // In any case it is fair game to consider it for pruning. If the operation
+ // transitioned to a final state, we will notice when we attempt to prune, and
+ // a subsequent attempt to create a new operation will succeed.
+ Some(PruningInfo {
+ // Expect safety:
+ // `last_usage` is locked only for primitive single line statements.
+ // There is no chance to panic and poison the mutex.
+ last_usage: *self.last_usage.lock().expect("In get_pruning_info."),
+ owner: self.owner,
+ index: self.index,
+ })
+ }
+
+ fn prune(&self, last_usage: Instant) -> Result<(), Error> {
+ let mut locked_outcome = match self.outcome.try_lock() {
+ Ok(guard) => match *guard {
+ Outcome::Unknown => guard,
+ _ => return Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)),
+ },
+ Err(_) => return Err(Error::Rc(ResponseCode::OPERATION_BUSY)),
+ };
+
+ // In `OperationDb::prune`, which is our caller, we first gather the pruning
+ // information including the last usage. When we select a candidate
+ // we call `prune` on that candidate passing the last_usage
+ // that we gathered earlier. If the actual last usage
+ // has changed since than, it means the operation was busy in the
+ // meantime, which means that we have to reevaluate the pruning score.
+ //
+ // Expect safety:
+ // `last_usage` is locked only for primitive single line statements.
+ // There is no chance to panic and poison the mutex.
+ if *self.last_usage.lock().expect("In Operation::prune()") != last_usage {
+ return Err(Error::Rc(ResponseCode::OPERATION_BUSY));
+ }
+ *locked_outcome = Outcome::Pruned;
+
+ let km_op: Box<dyn IKeyMintOperation> = match self.km_op.get_interface() {
+ Ok(km_op) => km_op,
+ Err(e) => {
+ log::error!("In prune: Failed to get KeyMintOperation interface.\n {:?}", e);
+ return Err(Error::sys());
+ }
+ };
+
+ // We abort the operation. If there was an error we log it but ignore it.
+ if let Err(e) = map_km_error(km_op.abort()) {
+ log::error!("In prune: KeyMint::abort failed with {:?}.", e);
+ }
+
+ Ok(())
+ }
+
+ // This function takes a Result from a KeyMint call and inspects it for errors.
+ // If an error was found it updates the given `locked_outcome` accordingly.
+ // It forwards the Result unmodified.
+ // The precondition to this call must be *locked_outcome == Outcome::Unknown.
+ // Ideally the `locked_outcome` came from a successful call to `check_active`
+ // see below.
+ fn update_outcome<T>(
+ &self,
+ locked_outcome: &mut Outcome,
+ err: Result<T, Error>,
+ ) -> Result<T, Error> {
+ match &err {
+ Err(Error::Km(e)) => *locked_outcome = Outcome::ErrorCode(*e),
+ Err(_) => *locked_outcome = Outcome::ErrorCode(ErrorCode::UNKNOWN_ERROR),
+ Ok(_) => (),
+ }
+ err
+ }
+
+ // This function grabs the outcome lock and checks the current outcome state.
+ // If the outcome is still `Outcome::Unknown`, this function returns
+ // the locked outcome for further updates. In any other case it returns
+ // ErrorCode::INVALID_OPERATION_HANDLE indicating that this operation has
+ // been finalized and is no longer active.
+ fn check_active(&self) -> Result<MutexGuard<Outcome>> {
+ let guard = self.outcome.lock().expect("In check_active.");
+ match *guard {
+ Outcome::Unknown => Ok(guard),
+ _ => Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)).context(format!(
+ "In check_active: Call on finalized operation with outcome: {:?}.",
+ *guard
+ )),
+ }
+ }
+
+ // This function checks the amount of input data sent to us. We reject any buffer
+ // exceeding MAX_RECEIVE_DATA bytes as input to `update`, `update_aad`, and `finish`
+ // in order to force clients into using reasonable limits.
+ fn check_input_length(data: &[u8]) -> Result<()> {
+ if data.len() > MAX_RECEIVE_DATA {
+ // This error code is unique, no context required here.
+ return Err(anyhow!(Error::Rc(ResponseCode::TOO_MUCH_DATA)));
+ }
+ Ok(())
+ }
+
+ // Update the last usage to now.
+ fn touch(&self) {
+ // Expect safety:
+ // `last_usage` is locked only for primitive single line statements.
+ // There is no chance to panic and poison the mutex.
+ *self.last_usage.lock().expect("In touch.") = Instant::now();
+ }
+
+ /// Implementation of `IKeystoreOperation::updateAad`.
+ /// Refer to the AIDL spec at system/hardware/interfaces/keystore2 for details.
+ fn update_aad(&self, aad_input: &[u8]) -> Result<()> {
+ let mut outcome = self.check_active().context("In update_aad")?;
+ Self::check_input_length(aad_input).context("In update_aad")?;
+ self.touch();
+
+ let params = KeyParameterArray {
+ params: vec![KmParam {
+ tag: Tag::ASSOCIATED_DATA,
+ value: KmParamValue::Blob(aad_input.into()),
+ }],
+ };
+
+ let mut out_params: Option<KeyParameterArray> = None;
+ let mut output: Option<ByteArray> = None;
+
+ let km_op: Box<dyn IKeyMintOperation> =
+ self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
+
+ self.update_outcome(
+ &mut *outcome,
+ map_km_error(km_op.update(
+ Some(¶ms),
+ None,
+ // TODO Get auth token from enforcement module if required.
+ None,
+ // TODO Get verification token from enforcement module if required.
+ None,
+ &mut out_params,
+ &mut output,
+ )),
+ )
+ .context("In update_aad: KeyMint::update failed.")?;
+
+ Ok(())
+ }
+
+ /// Implementation of `IKeystoreOperation::update`.
+ /// Refer to the AIDL spec at system/hardware/interfaces/keystore2 for details.
+ fn update(&self, input: &[u8]) -> Result<Option<Vec<u8>>> {
+ let mut outcome = self.check_active().context("In update")?;
+ Self::check_input_length(input).context("In update")?;
+ self.touch();
+
+ let mut out_params: Option<KeyParameterArray> = None;
+ let mut output: Option<ByteArray> = None;
+
+ let km_op: Box<dyn IKeyMintOperation> =
+ self.km_op.get_interface().context("In update: Failed to get KeyMintOperation.")?;
+
+ self.update_outcome(
+ &mut *outcome,
+ map_km_error(km_op.update(
+ None,
+ Some(input),
+ // TODO Get auth token from enforcement module if required.
+ None,
+ // TODO Get verification token from enforcement module if required.
+ None,
+ &mut out_params,
+ &mut output,
+ )),
+ )
+ .context("In update: KeyMint::update failed.")?;
+
+ match output {
+ Some(blob) => {
+ if blob.data.is_empty() {
+ Ok(None)
+ } else {
+ Ok(Some(blob.data))
+ }
+ }
+ None => Ok(None),
+ }
+ }
+
+ /// Implementation of `IKeystoreOperation::finish`.
+ /// Refer to the AIDL spec at system/hardware/interfaces/keystore2 for details.
+ fn finish(&self, input: Option<&[u8]>, signature: Option<&[u8]>) -> Result<Option<Vec<u8>>> {
+ let mut outcome = self.check_active().context("In finish")?;
+ if let Some(input) = input {
+ Self::check_input_length(input).context("In finish")?;
+ }
+ self.touch();
+
+ let mut out_params: Option<KeyParameterArray> = None;
+
+ let km_op: Box<dyn IKeyMintOperation> =
+ self.km_op.get_interface().context("In finish: Failed to get KeyMintOperation.")?;
+
+ let output = self
+ .update_outcome(
+ &mut *outcome,
+ map_km_error(km_op.finish(
+ None,
+ input,
+ signature,
+ // TODO Get auth token from enforcement module if required.
+ None,
+ // TODO Get verification token from enforcement module if required.
+ None,
+ &mut out_params,
+ )),
+ )
+ .context("In finish: KeyMint::finish failed.")?;
+
+ // At this point the operation concluded successfully.
+ *outcome = Outcome::Success;
+
+ if output.is_empty() {
+ Ok(None)
+ } else {
+ Ok(Some(output))
+ }
+ }
+
+ /// Aborts the operation if it is active. IFF the operation is aborted the outcome is
+ /// set to `outcome`. `outcome` must reflect the reason for the abort. Since the operation
+ /// gets aborted `outcome` must not be `Operation::Success` or `Operation::Unknown`.
+ fn abort(&self, outcome: Outcome) -> Result<()> {
+ let mut locked_outcome = self.check_active().context("In abort")?;
+ *locked_outcome = outcome;
+ let km_op: Box<dyn IKeyMintOperation> =
+ self.km_op.get_interface().context("In abort: Failed to get KeyMintOperation.")?;
+
+ map_km_error(km_op.abort()).context("In abort: KeyMint::abort failed.")
+ }
+}
+
+impl Drop for Operation {
+ fn drop(&mut self) {
+ if let Ok(Outcome::Unknown) = self.outcome.get_mut() {
+ // If the operation was still active we call abort, setting
+ // the outcome to `Outcome::Dropped`
+ if let Err(e) = self.abort(Outcome::Dropped) {
+ log::error!("While dropping Operation: abort failed:\n {:?}", e);
+ }
+ }
+ }
+}
+
+/// The OperationDb holds weak references to all ongoing operations.
+/// Its main purpose is to facilitate operation pruning.
+#[derive(Debug, Default)]
+pub struct OperationDb {
+ // TODO replace Vec with WeakTable when the weak_table crate becomes
+ // available.
+ operations: Mutex<Vec<Weak<Operation>>>,
+}
+
+impl OperationDb {
+ /// Creates a new OperationDb.
+ pub fn new() -> Self {
+ Self { operations: Mutex::new(Vec::new()) }
+ }
+
+ /// Creates a new operation.
+ /// This function takes a KeyMint operation and an associated
+ /// owner uid and returns a new Operation wrapped in a `std::sync::Arc`.
+ pub fn create_operation(
+ &self,
+ km_op: Box<dyn IKeyMintOperation>,
+ owner: u32,
+ ) -> Arc<Operation> {
+ // We use unwrap because we don't allow code that can panic while locked.
+ let mut operations = self.operations.lock().expect("In create_operation.");
+
+ let mut index: usize = 0;
+ // First we iterate through the operation slots to try and find an unused
+ // slot. If we don't find one, we append the new entry instead.
+ match (*operations).iter_mut().find(|s| {
+ index += 1;
+ s.upgrade().is_none()
+ }) {
+ Some(free_slot) => {
+ let new_op = Arc::new(Operation::new(index - 1, km_op, owner));
+ *free_slot = Arc::downgrade(&new_op);
+ new_op
+ }
+ None => {
+ let new_op = Arc::new(Operation::new(operations.len(), km_op, owner));
+ operations.push(Arc::downgrade(&new_op));
+ new_op
+ }
+ }
+ }
+
+ fn get(&self, index: usize) -> Option<Arc<Operation>> {
+ self.operations.lock().expect("In OperationDb::get.").get(index).and_then(|op| op.upgrade())
+ }
+
+ /// Attempts to prune an operation.
+ ///
+ /// This function is used during operation creation, i.e., by
+ /// `KeystoreSecurityLevel::create_operation`, to try and free up an operation slot
+ /// if it got `ErrorCode::TOO_MANY_OPERATIONS` from the KeyMint backend. It is not
+ /// guaranteed that an operation slot is available after this call successfully
+ /// returned for various reasons. E.g., another thread may have snatched up the newly
+ /// available slot. Callers may have to call prune multiple times before they get a
+ /// free operation slot. Prune may also return `Err(Error::Rc(ResponseCode::BACKEND_BUSY))`
+ /// which indicates that no prunable operation was found.
+ ///
+ /// To find a suitable candidate we compute the malus for the caller and each existing
+ /// operation. The malus is the inverse of the pruning power (caller) or pruning
+ /// resistance (existing operation).
+ ///
+ /// The malus is based on the number of sibling operations and age. Sibling
+ /// operations are operations that have the same owner (UID).
+ ///
+ /// Every operation, existing or new, starts with a malus of 1. Every sibling
+ /// increases the malus by one. The age is the time since an operation was last touched.
+ /// It increases the malus by log6(<age in seconds> + 1) rounded down to the next
+ /// integer. So the malus increases stepwise after 5s, 35s, 215s, ...
+ /// Of two operations with the same malus the least recently used one is considered
+ /// weaker.
+ ///
+ /// For the caller to be able to prune an operation it must find an operation
+ /// with a malus higher than its own.
+ ///
+ /// The malus can be expressed as
+ /// ```
+ /// malus = 1 + no_of_siblings + floor(log6(age_in_seconds + 1))
+ /// ```
+ /// where the constant `1` accounts for the operation under consideration.
+ /// In reality we compute it as
+ /// ```
+ /// caller_malus = 1 + running_siblings
+ /// ```
+ /// because the new operation has no age and is not included in the `running_siblings`,
+ /// and
+ /// ```
+ /// running_malus = running_siblings + floor(log6(age_in_seconds + 1))
+ /// ```
+ /// because a running operation is included in the `running_siblings` and it has
+ /// an age.
+ ///
+ /// ## Example
+ /// A caller with no running operations has a malus of 1. Young (age < 5s) operations
+ /// also with no siblings have a malus of one and cannot be pruned by the caller.
+ /// We have to find an operation that has at least one sibling or is older than 5s.
+ ///
+ /// A caller with one running operation has a malus of 2. Now even young siblings
+ /// or single child aging (5s <= age < 35s) operations are off limit. An aging
+ /// sibling of two, however, would have a malus of 3 and would be fair game.
+ ///
+ /// ## Rationale
+ /// Due to the limitation of KeyMint operation slots, we cannot get around pruning or
+ /// a single app could easily DoS KeyMint.
+ /// Keystore 1.0 used to always prune the least recently used operation. This at least
+ /// guaranteed that new operations can always be started. With the increased usage
+ /// of Keystore we saw increased pruning activity which can lead to a livelock
+ /// situation in the worst case.
+ ///
+ /// With the new pruning strategy we want to provide well behaved clients with
+ /// progress assurances while punishing DoS attempts. As a result of this
+ /// strategy we can be in the situation where no operation can be pruned and the
+ /// creation of a new operation fails. This allows single child operations which
+ /// are frequently updated to complete, thereby breaking up livelock situations
+ /// and facilitating system wide progress.
+ ///
+ /// ## Update
+ /// We also allow callers to cannibalize their own sibling operations if no other
+ /// slot can be found. In this case the least recently used sibling is pruned.
+ pub fn prune(&self, caller: u32) -> Result<(), Error> {
+ loop {
+ // Maps the uid of the owner to the number of operations that owner has
+ // (running_siblings). More operations per owner lowers the pruning
+ // resistance of the operations of that owner. Whereas the number of
+ // ongoing operations of the caller lowers the pruning power of the caller.
+ let mut owners: HashMap<u32, u64> = HashMap::new();
+ let mut pruning_info: Vec<PruningInfo> = Vec::new();
+
+ let now = Instant::now();
+ self.operations
+ .lock()
+ .expect("In OperationDb::prune: Trying to lock self.operations.")
+ .iter()
+ .for_each(|op| {
+ if let Some(op) = op.upgrade() {
+ if let Some(p_info) = op.get_pruning_info() {
+ let owner = p_info.owner;
+ pruning_info.push(p_info);
+ // Count operations per owner.
+ *owners.entry(owner).or_insert(0) += 1;
+ }
+ }
+ });
+
+ let caller_malus = 1u64 + *owners.entry(caller).or_default();
+
+ // We iterate through all operations computing the malus and finding
+ // the candidate with the highest malus which must also be higher
+ // than the caller_malus.
+ struct CandidateInfo {
+ index: usize,
+ malus: u64,
+ last_usage: Instant,
+ age: Duration,
+ }
+ let mut oldest_caller_op: Option<CandidateInfo> = None;
+ let candidate = pruning_info.iter().fold(
+ None,
+ |acc: Option<CandidateInfo>, &PruningInfo { last_usage, owner, index }| {
+ // Compute the age of the current operation.
+ let age = now
+ .checked_duration_since(last_usage)
+ .unwrap_or_else(|| Duration::new(0, 0));
+
+ // Find the least recently used sibling as an alternative pruning candidate.
+ if owner == caller {
+ if let Some(CandidateInfo { age: a, .. }) = oldest_caller_op {
+ if age > a {
+ oldest_caller_op =
+ Some(CandidateInfo { index, malus: 0, last_usage, age });
+ }
+ } else {
+ oldest_caller_op =
+ Some(CandidateInfo { index, malus: 0, last_usage, age });
+ }
+ }
+
+ // Compute the malus of the current operation.
+ // Expect safety: Every owner in pruning_info was counted in
+ // the owners map. So this unwrap cannot panic.
+ let malus = *owners
+ .get(&owner)
+ .expect("This is odd. We should have counted every owner in pruning_info.")
+ + ((age.as_secs() + 1) as f64).log(6.0).floor() as u64;
+
+ // Now check if the current operation is a viable/better candidate
+ // the one currently stored in the accumulator.
+ match acc {
+ // First we have to find any operation that is prunable by the caller.
+ None => {
+ if caller_malus < malus {
+ Some(CandidateInfo { index, malus, last_usage, age })
+ } else {
+ None
+ }
+ }
+ // If we have found one we look for the operation with the worst score.
+ // If there is a tie, the older operation is considered weaker.
+ Some(CandidateInfo { index: i, malus: m, last_usage: l, age: a }) => {
+ if malus > m || (malus == m && age > a) {
+ Some(CandidateInfo { index, malus, last_usage, age })
+ } else {
+ Some(CandidateInfo { index: i, malus: m, last_usage: l, age: a })
+ }
+ }
+ }
+ },
+ );
+
+ // If we did not find a suitable candidate we may cannibalize our oldest sibling.
+ let candidate = candidate.or(oldest_caller_op);
+
+ match candidate {
+ Some(CandidateInfo { index, malus: _, last_usage, age: _ }) => {
+ match self.get(index) {
+ Some(op) => {
+ match op.prune(last_usage) {
+ // We successfully freed up a slot.
+ Ok(()) => break Ok(()),
+ // This means the operation we tried to prune was on its way
+ // out. It also means that the slot it had occupied was freed up.
+ Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)) => break Ok(()),
+ // This means the operation we tried to prune was currently
+ // servicing a request. There are two options.
+ // * Assume that it was touched, which means that its
+ // pruning resistance increased. In that case we have
+ // to start over and find another candidate.
+ // * Assume that the operation is transitioning to end-of-life.
+ // which means that we got a free slot for free.
+ // If we assume the first but the second is true, we prune
+ // a good operation without need (aggressive approach).
+ // If we assume the second but the first is true, our
+ // caller will attempt to create a new KeyMint operation,
+ // fail with `ErrorCode::TOO_MANY_OPERATIONS`, and call
+ // us again (conservative approach).
+ Err(Error::Rc(ResponseCode::OPERATION_BUSY)) => {
+ // We choose the conservative approach, because
+ // every needlessly pruned operation can impact
+ // the user experience.
+ // To switch to the aggressive approach replace
+ // the following line with `continue`.
+ break Ok(());
+ }
+
+ // The candidate may have been touched so the score
+ // has changed since our evaluation.
+ _ => continue,
+ }
+ }
+ // This index does not exist any more. The operation
+ // in this slot was dropped. Good news, a slot
+ // has freed up.
+ None => break Ok(()),
+ }
+ }
+ // We did not get a pruning candidate.
+ None => break Err(Error::Rc(ResponseCode::BACKEND_BUSY)),
+ }
+ }
+ }
+}
+
+/// Implementation of IKeystoreOperation.
+pub struct KeystoreOperation {
+ operation: Mutex<Option<Arc<Operation>>>,
+}
+
+impl KeystoreOperation {
+ /// Creates a new operation instance wrapped in a
+ /// BnKeystoreOperation proxy object. It also
+ /// calls `IBinder::set_requesting_sid` on the new interface, because
+ /// we need it for checking Keystore permissions.
+ pub fn new_native_binder(operation: Arc<Operation>) -> impl IKeystoreOperation + Send {
+ let result =
+ BnKeystoreOperation::new_binder(Self { operation: Mutex::new(Some(operation)) });
+ result.as_binder().set_requesting_sid(true);
+ result
+ }
+
+ /// Grabs the outer operation mutex and calls `f` on the locked operation.
+ /// The function also deletes the operation if it returns with an error or if
+ /// `delete_op` is true.
+ fn with_locked_operation<T, F>(&self, f: F, delete_op: bool) -> Result<T>
+ where
+ for<'a> F: FnOnce(&'a Operation) -> Result<T>,
+ {
+ let mut delete_op: bool = delete_op;
+ match self.operation.try_lock() {
+ Ok(mut mutex_guard) => {
+ let result = match &*mutex_guard {
+ Some(op) => {
+ let result = f(&*op);
+ // Any error here means we can discard the operation.
+ if result.is_err() {
+ delete_op = true;
+ }
+ result
+ }
+ None => Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE))
+ .context("In KeystoreOperation::with_locked_operation"),
+ };
+
+ if delete_op {
+ // We give up our reference to the Operation, thereby freeing up our
+ // internal resources and ending the wrapped KeyMint operation.
+ // This KeystoreOperation object will still be owned by an SpIBinder
+ // until the client drops its remote reference.
+ *mutex_guard = None;
+ }
+ result
+ }
+ Err(_) => Err(Error::Rc(ResponseCode::OPERATION_BUSY))
+ .context("In KeystoreOperation::with_locked_operation"),
+ }
+ }
+}
+
+impl binder::Interface for KeystoreOperation {}
+
+impl IKeystoreOperation for KeystoreOperation {
+ fn updateAad(&self, aad_input: &[u8]) -> binder::public_api::Result<()> {
+ map_or_log_err(
+ self.with_locked_operation(
+ |op| op.update_aad(aad_input).context("In KeystoreOperation::updateAad"),
+ false,
+ ),
+ Ok,
+ )
+ }
+
+ fn update(&self, input: &[u8]) -> binder::public_api::Result<Option<Vec<u8>>> {
+ map_or_log_err(
+ self.with_locked_operation(
+ |op| op.update(input).context("In KeystoreOperation::update"),
+ false,
+ ),
+ Ok,
+ )
+ }
+ fn finish(
+ &self,
+ input: Option<&[u8]>,
+ signature: Option<&[u8]>,
+ ) -> binder::public_api::Result<Option<Vec<u8>>> {
+ map_or_log_err(
+ self.with_locked_operation(
+ |op| op.finish(input, signature).context("In KeystoreOperation::finish"),
+ true,
+ ),
+ Ok,
+ )
+ }
+
+ fn abort(&self) -> binder::public_api::Result<()> {
+ map_or_log_err(
+ self.with_locked_operation(
+ |op| op.abort(Outcome::Abort).context("In KeystoreOperation::abort"),
+ true,
+ ),
+ Ok,
+ )
+ }
+}
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
new file mode 100644
index 0000000..0917256
--- /dev/null
+++ b/keystore2/src/permission.rs
@@ -0,0 +1,945 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This crate provides access control primitives for Keystore 2.0.
+//! It provides high level functions for checking permissions in the keystore2 and keystore2_key
+//! SELinux classes based on the keystore2_selinux backend.
+//! It also provides KeystorePerm and KeyPerm as convenience wrappers for the SELinux permission
+//! defined by keystore2 and keystore2_key respectively.
+
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
+};
+
+use std::cmp::PartialEq;
+use std::convert::From;
+use std::ffi::CStr;
+
+use crate::error::Error as KsError;
+use keystore2_selinux as selinux;
+
+use anyhow::Context as AnyhowContext;
+
+use selinux::Backend;
+
+use lazy_static::lazy_static;
+
+// Replace getcon with a mock in the test situation
+#[cfg(not(test))]
+use selinux::getcon;
+#[cfg(test)]
+use tests::test_getcon as getcon;
+
+lazy_static! {
+ // Panicking here is allowed because keystore cannot function without this backend
+ // and it would happen early and indicate a gross misconfiguration of the device.
+ static ref KEYSTORE2_KEY_LABEL_BACKEND: selinux::KeystoreKeyBackend =
+ selinux::KeystoreKeyBackend::new().unwrap();
+}
+
+fn lookup_keystore2_key_context(namespace: i64) -> anyhow::Result<selinux::Context> {
+ KEYSTORE2_KEY_LABEL_BACKEND.lookup(&namespace.to_string())
+}
+
+/// ## Background
+///
+/// AIDL enums are represented as constants of the form:
+/// ```
+/// mod EnumName {
+/// pub type EnumName = i32;
+/// pub const Variant1: EnumName = <value1>;
+/// pub const Variant2: EnumName = <value2>;
+/// ...
+/// }
+///```
+/// This macro wraps the enum in a new type, e.g., `MyPerm` and maps each variant to an SELinux
+/// permission while providing the following interface:
+/// * From<EnumName> and Into<EnumName> are implemented. Where the implementation of From maps
+/// any variant not specified to the default.
+/// * Every variant has a constructor with a name corresponding to its lower case SELinux string
+/// representation.
+/// * `MyPerm.to_selinux(&self)` returns the SELinux string representation of the
+/// represented permission.
+///
+/// ## Special behavior
+/// If the keyword `use` appears as an selinux name `use_` is used as identifier for the
+/// constructor function (e.g. `MePerm::use_()`) but the string returned by `to_selinux` will
+/// still be `"use"`.
+///
+/// ## Example
+/// ```
+///
+/// implement_permission!(
+/// /// MyPerm documentation.
+/// #[derive(Clone, Copy, Debug, PartialEq)]
+/// MyPerm from EnumName with default (None, none) {}
+/// Variant1, selinux name: variant1;
+/// Variant2, selinux name: variant1;
+/// }
+/// );
+/// ```
+macro_rules! implement_permission_aidl {
+ // This rule provides the public interface of the macro. And starts the preprocessing
+ // recursion (see below).
+ ($(#[$m:meta])* $name:ident from $aidl_name:ident with default ($($def:tt)*)
+ { $($element:tt)* })
+ => {
+ implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*), [],
+ $($element)*);
+ };
+
+ // The following three rules recurse through the elements of the form
+ // `<enum variant>, selinux name: <selinux_name>;`
+ // preprocessing the input.
+
+ // The first rule terminates the recursion and passes the processed arguments to the final
+ // rule that spills out the implementation.
+ (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*], ) => {
+ implement_permission_aidl!(@end $($m)*, $name, $aidl_name, ($($def)*) { $($out)* } );
+ };
+
+ // The second rule is triggered if the selinux name of an element is literally `use`.
+ // It produces the tuple `<enum variant>, use_, use;`
+ // and appends it to the out list.
+ (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*],
+ $e_name:ident, selinux name: use; $($element:tt)*)
+ => {
+ implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*),
+ [$($out)* $e_name, use_, use;], $($element)*);
+ };
+
+ // The third rule is the default rule which replaces every input tuple with
+ // `<enum variant>, <selinux_name>, <selinux_name>;`
+ // and appends the result to the out list.
+ (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*],
+ $e_name:ident, selinux name: $e_str:ident; $($element:tt)*)
+ => {
+ implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*),
+ [$($out)* $e_name, $e_str, $e_str;], $($element)*);
+ };
+
+ (@end $($m:meta)*, $name:ident, $aidl_name:ident,
+ ($def_name:ident, $def_selinux_name:ident) {
+ $($element_name:ident, $element_identifier:ident,
+ $selinux_name:ident;)*
+ })
+ =>
+ {
+ $(#[$m])*
+ pub struct $name(pub $aidl_name);
+
+ impl From<$aidl_name> for $name {
+ fn from (p: $aidl_name) -> Self {
+ match p {
+ $aidl_name::$def_name => Self($aidl_name::$def_name),
+ $($aidl_name::$element_name => Self($aidl_name::$element_name),)*
+ _ => Self($aidl_name::$def_name),
+ }
+ }
+ }
+
+ impl Into<$aidl_name> for $name {
+ fn into(self) -> $aidl_name {
+ self.0
+ }
+ }
+
+ impl $name {
+ /// Returns a string representation of the permission as required by
+ /// `selinux::check_access`.
+ pub fn to_selinux(&self) -> &'static str {
+ match self {
+ Self($aidl_name::$def_name) => stringify!($def_selinux_name),
+ $(Self($aidl_name::$element_name) => stringify!($selinux_name),)*
+ _ => stringify!($def_selinux_name),
+ }
+ }
+
+ /// Creates an instance representing a permission with the same name.
+ pub const fn $def_selinux_name() -> Self { Self($aidl_name::$def_name) }
+ $(
+ /// Creates an instance representing a permission with the same name.
+ pub const fn $element_identifier() -> Self { Self($aidl_name::$element_name) }
+ )*
+ }
+ };
+}
+
+implement_permission_aidl!(
+ /// KeyPerm provides a convenient abstraction from the SELinux class `keystore2_key`.
+ /// At the same time it maps `KeyPermissions` from the Keystore 2.0 AIDL Grant interface to
+ /// the SELinux permissions. With the implement_permission macro, we conveniently
+ /// provide mappings between the wire type bit field values, the rust enum and the SELinux
+ /// string representation.
+ ///
+ /// ## Example
+ ///
+ /// In this access check `KeyPerm::get_info().to_selinux()` would return the SELinux representation
+ /// "info".
+ /// ```
+ /// selinux::check_access(source_context, target_context, "keystore2_key",
+ /// KeyPerm::get_info().to_selinux());
+ /// ```
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ KeyPerm from KeyPermission with default (NONE, none) {
+ DELETE, selinux name: delete;
+ GEN_UNIQUE_ID, selinux name: gen_unique_id;
+ GET_INFO, selinux name: get_info;
+ GRANT, selinux name: grant;
+ MANAGE_BLOB, selinux name: manage_blob;
+ REBIND, selinux name: rebind;
+ REQ_FORCED_OP, selinux name: req_forced_op;
+ UPDATE, selinux name: update;
+ USE, selinux name: use;
+ USE_DEV_ID, selinux name: use_dev_id;
+ }
+);
+
+/// This macro implements an enum with values mapped to SELinux permission names.
+/// The below example wraps the enum MyPermission in the tuple struct `MyPerm` and implements
+/// * From<i32> and Into<i32> are implemented. Where the implementation of From maps
+/// any variant not specified to the default.
+/// * Every variant has a constructor with a name corresponding to its lower case SELinux string
+/// representation.
+/// * `MyPerm.to_selinux(&self)` returns the SELinux string representation of the
+/// represented permission.
+///
+/// ## Example
+/// ```
+/// implement_permission!(
+/// /// MyPerm documentation.
+/// #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+/// MyPerm with default (None = 0, none) {
+/// Foo = 1, selinux name: foo;
+/// Bar = 2, selinux name: bar;
+/// }
+/// );
+/// ```
+macro_rules! implement_permission {
+ // This rule provides the public interface of the macro. And starts the preprocessing
+ // recursion (see below).
+ ($(#[$m:meta])* $name:ident with default
+ ($def_name:ident = $def_val:expr, $def_selinux_name:ident)
+ {
+ $($(#[$element_meta:meta])*
+ $element_name:ident = $element_val:expr, selinux name: $selinux_name:ident;)*
+ })
+ => {
+ $(#[$m])*
+ pub enum $name {
+ /// The default variant of an enum.
+ $def_name = $def_val,
+ $(
+ $(#[$element_meta])*
+ $element_name = $element_val,
+ )*
+ }
+
+ impl From<i32> for $name {
+ fn from (p: i32) -> Self {
+ match p {
+ $def_val => Self::$def_name,
+ $($element_val => Self::$element_name,)*
+ _ => Self::$def_name,
+ }
+ }
+ }
+
+ impl Into<i32> for $name {
+ fn into(self) -> i32 {
+ self as i32
+ }
+ }
+
+ impl $name {
+ /// Returns a string representation of the permission as required by
+ /// `selinux::check_access`.
+ pub fn to_selinux(&self) -> &'static str {
+ match self {
+ Self::$def_name => stringify!($def_selinux_name),
+ $(Self::$element_name => stringify!($selinux_name),)*
+ }
+ }
+
+ /// Creates an instance representing a permission with the same name.
+ pub const fn $def_selinux_name() -> Self { Self::$def_name }
+ $(
+ /// Creates an instance representing a permission with the same name.
+ pub const fn $selinux_name() -> Self { Self::$element_name }
+ )*
+ }
+ };
+}
+
+implement_permission!(
+ /// KeystorePerm provides a convenient abstraction from the SELinux class `keystore2`.
+ /// Using the implement_permission macro we get the same features as `KeyPerm`.
+ #[derive(Clone, Copy, Debug, PartialEq)]
+ KeystorePerm with default (None = 0, none) {
+ /// Checked when a new auth token is installed.
+ AddAuth = 1, selinux name: add_auth;
+ /// Checked when an app is uninstalled or wiped.
+ ClearNs = 2, selinux name: clear_ns;
+ /// Checked when Keystore 2.0 gets locked.
+ GetState = 4, selinux name: get_state;
+ /// Checked when Keystore 2.0 is asked to list a namespace that the caller
+ /// does not have the get_info permission for.
+ List = 8, selinux name: list;
+ /// Checked when Keystore 2.0 gets locked.
+ Lock = 0x10, selinux name: lock;
+ /// Checked when Keystore 2.0 shall be reset.
+ Reset = 0x20, selinux name: reset;
+ /// Checked when Keystore 2.0 shall be unlocked.
+ Unlock = 0x40, selinux name: unlock;
+ }
+);
+
+/// Represents a set of `KeyPerm` permissions.
+/// `IntoIterator` is implemented for this struct allowing the iteration through all the
+/// permissions in the set.
+/// It also implements a function `includes(self, other)` that checks if the permissions
+/// in `other` are included in `self`.
+///
+/// KeyPermSet can be created with the macro `key_perm_set![]`.
+///
+/// ## Example
+/// ```
+/// let perms1 = key_perm_set![KeyPerm::use_(), KeyPerm::manage_blob(), KeyPerm::grant()];
+/// let perms2 = key_perm_set![KeyPerm::use_(), KeyPerm::manage_blob()];
+///
+/// assert!(perms1.includes(perms2))
+/// assert!(!perms2.includes(perms1))
+///
+/// let i = perms1.into_iter();
+/// // iteration in ascending order of the permission's numeric representation.
+/// assert_eq(Some(KeyPerm::manage_blob()), i.next());
+/// assert_eq(Some(KeyPerm::grant()), i.next());
+/// assert_eq(Some(KeyPerm::use_()), i.next());
+/// assert_eq(None, i.next());
+/// ```
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub struct KeyPermSet(pub i32);
+
+mod perm {
+ use super::*;
+
+ pub struct IntoIter {
+ vec: KeyPermSet,
+ pos: u8,
+ }
+
+ impl IntoIter {
+ pub fn new(v: KeyPermSet) -> Self {
+ Self { vec: v, pos: 0 }
+ }
+ }
+
+ impl std::iter::Iterator for IntoIter {
+ type Item = KeyPerm;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ loop {
+ if self.pos == 32 {
+ return None;
+ }
+ let p = self.vec.0 & (1 << self.pos);
+ self.pos += 1;
+ if p != 0 {
+ return Some(KeyPerm::from(KeyPermission(p)));
+ }
+ }
+ }
+ }
+}
+
+impl From<KeyPerm> for KeyPermSet {
+ fn from(p: KeyPerm) -> Self {
+ Self((p.0).0 as i32)
+ }
+}
+
+/// allow conversion from the AIDL wire type i32 to a permission set.
+impl From<i32> for KeyPermSet {
+ fn from(p: i32) -> Self {
+ Self(p)
+ }
+}
+
+impl From<KeyPermSet> for i32 {
+ fn from(p: KeyPermSet) -> i32 {
+ p.0
+ }
+}
+
+impl KeyPermSet {
+ /// Returns true iff this permission set has all of the permissions that are in `other`.
+ pub fn includes<T: Into<KeyPermSet>>(&self, other: T) -> bool {
+ let o: KeyPermSet = other.into();
+ (self.0 & o.0) == o.0
+ }
+}
+
+/// This macro can be used to create a `KeyPermSet` from a list of `KeyPerm` values.
+///
+/// ## Example
+/// ```
+/// let v = key_perm_set![Perm::delete(), Perm::manage_blob()];
+/// ```
+#[macro_export]
+macro_rules! key_perm_set {
+ () => { KeyPermSet(0) };
+ ($head:expr $(, $tail:expr)* $(,)?) => {
+ KeyPermSet(($head.0).0 $(| ($tail.0).0)*)
+ };
+}
+
+impl IntoIterator for KeyPermSet {
+ type Item = KeyPerm;
+ type IntoIter = perm::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ Self::IntoIter::new(self)
+ }
+}
+
+/// Uses `selinux::check_access` to check if the given caller context `caller_cxt` may access
+/// the given permision `perm` of the `keystore2` security class.
+pub fn check_keystore_permission(caller_ctx: &CStr, perm: KeystorePerm) -> anyhow::Result<()> {
+ let target_context = getcon().context("check_keystore_permission: getcon failed.")?;
+ selinux::check_access(caller_ctx, &target_context, "keystore2", perm.to_selinux())
+}
+
+/// Uses `selinux::check_access` to check if the given caller context `caller_cxt` has
+/// all the permissions indicated in `access_vec` for the target domain indicated by the key
+/// descriptor `key` in the security class `keystore2_key`.
+///
+/// Also checks if the caller has the grant permission for the given target domain.
+///
+/// Attempts to grant the grant permission are always denied.
+///
+/// The only viable target domains are
+/// * `Domain::APP` in which case u:r:keystore:s0 is used as target context and
+/// * `Domain::SELINUX` in which case the `key.nspace` parameter is looked up in
+/// SELinux keystore key backend, and the result is used
+/// as target context.
+pub fn check_grant_permission(
+ caller_ctx: &CStr,
+ access_vec: KeyPermSet,
+ key: &KeyDescriptor,
+) -> anyhow::Result<()> {
+ let target_context = match key.domain {
+ Domain::APP => getcon().context("check_grant_permission: getcon failed.")?,
+ Domain::SELINUX => lookup_keystore2_key_context(key.nspace)
+ .context("check_grant_permission: Domain::SELINUX: Failed to lookup namespace.")?,
+ _ => return Err(KsError::sys()).context(format!("Cannot grant {:?}.", key.domain)),
+ };
+
+ selinux::check_access(caller_ctx, &target_context, "keystore2_key", "grant")
+ .context("Grant permission is required when granting.")?;
+
+ if access_vec.includes(KeyPerm::grant()) {
+ return Err(selinux::Error::perm()).context("Grant permission cannot be granted.");
+ }
+
+ for p in access_vec.into_iter() {
+ selinux::check_access(caller_ctx, &target_context, "keystore2_key", p.to_selinux())
+ .context(concat!(
+ "check_grant_permission: check_access failed. ",
+ "The caller may have tried to grant a permission that they don't possess."
+ ))?
+ }
+ Ok(())
+}
+
+/// Uses `selinux::check_access` to check if the given caller context `caller_cxt`
+/// has the permissions indicated by `perm` for the target domain indicated by the key
+/// descriptor `key` in the security class `keystore2_key`.
+///
+/// The behavior differs slightly depending on the selected target domain:
+/// * `Domain::APP` u:r:keystore:s0 is used as target context.
+/// * `Domain::SELINUX` `key.nspace` parameter is looked up in the SELinux keystore key
+/// backend, and the result is used as target context.
+/// * `Domain::BLOB` Same as SELinux but the "manage_blob" permission is always checked additionally
+/// to the one supplied in `perm`.
+/// * `Domain::GRANT` Does not use selinux::check_access. Instead the `access_vector`
+/// parameter is queried for permission, which must be supplied in this case.
+///
+/// ## Return values.
+/// * Ok(()) If the requested permissions were granted.
+/// * Err(selinux::Error::perm()) If the requested permissions were denied.
+/// * Err(KsError::sys()) This error is produced if `Domain::GRANT` is selected but no `access_vec`
+/// was supplied. It is also produced if `Domain::KEY_ID` was selected, and
+/// on various unexpected backend failures.
+pub fn check_key_permission(
+ caller_ctx: &CStr,
+ perm: KeyPerm,
+ key: &KeyDescriptor,
+ access_vector: &Option<KeyPermSet>,
+) -> anyhow::Result<()> {
+ let target_context = match key.domain {
+ // apps get the default keystore context
+ Domain::APP => getcon().context("check_key_permission: getcon failed.")?,
+ Domain::SELINUX => lookup_keystore2_key_context(key.nspace)
+ .context("check_key_permission: Domain::SELINUX: Failed to lookup namespace.")?,
+ Domain::GRANT => {
+ match access_vector {
+ Some(pv) => {
+ if pv.includes(perm) {
+ return Ok(());
+ } else {
+ return Err(selinux::Error::perm())
+ .context(format!("\"{}\" not granted", perm.to_selinux()));
+ }
+ }
+ None => {
+ // If DOMAIN_GRANT was selected an access vector must be supplied.
+ return Err(KsError::sys()).context(
+ "Cannot check permission for Domain::GRANT without access vector.",
+ );
+ }
+ }
+ }
+ Domain::KEY_ID => {
+ // We should never be called with `Domain::KEY_ID. The database
+ // lookup should have converted this into one of `Domain::APP`
+ // or `Domain::SELINUX`.
+ return Err(KsError::sys()).context("Cannot check permission for Domain::KEY_ID.");
+ }
+ Domain::BLOB => {
+ let tctx = lookup_keystore2_key_context(key.nspace)
+ .context("Domain::BLOB: Failed to lookup namespace.")?;
+ // If DOMAIN_KEY_BLOB was specified, we check for the "manage_blob"
+ // permission in addition to the requested permission.
+ selinux::check_access(
+ caller_ctx,
+ &tctx,
+ "keystore2_key",
+ KeyPerm::manage_blob().to_selinux(),
+ )?;
+
+ tctx
+ }
+ _ => {
+ return Err(KsError::sys())
+ .context(format!("Unknown domain value: \"{:?}\".", key.domain))
+ }
+ };
+
+ selinux::check_access(caller_ctx, &target_context, "keystore2_key", perm.to_selinux())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use anyhow::anyhow;
+ use anyhow::Result;
+ use keystore2_selinux::*;
+
+ const ALL_PERMS: KeyPermSet = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::use_dev_id(),
+ KeyPerm::req_forced_op(),
+ KeyPerm::gen_unique_id(),
+ KeyPerm::grant(),
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+
+ const NOT_GRANT_PERMS: KeyPermSet = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::use_dev_id(),
+ KeyPerm::req_forced_op(),
+ KeyPerm::gen_unique_id(),
+ // No KeyPerm::grant()
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+
+ const UNPRIV_PERMS: KeyPermSet = key_perm_set![
+ KeyPerm::delete(),
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+
+ /// The su_key namespace as defined in su.te and keystore_key_contexts of the
+ /// SePolicy (system/sepolicy).
+ const SU_KEY_NAMESPACE: i32 = 0;
+ /// The shell_key namespace as defined in shell.te and keystore_key_contexts of the
+ /// SePolicy (system/sepolicy).
+ const SHELL_KEY_NAMESPACE: i32 = 1;
+
+ pub fn test_getcon() -> Result<Context> {
+ Context::new("u:object_r:keystore:s0")
+ }
+
+ // This macro evaluates the given expression and checks that
+ // a) evaluated to Result::Err() and that
+ // b) the wrapped error is selinux::Error::perm() (permission denied).
+ // We use a macro here because a function would mask which invocation caused the failure.
+ //
+ // TODO b/164121720 Replace this macro with a function when `track_caller` is available.
+ macro_rules! assert_perm_failed {
+ ($test_function:expr) => {
+ let result = $test_function;
+ assert!(result.is_err(), "Permission check should have failed.");
+ assert_eq!(
+ Some(&selinux::Error::perm()),
+ result.err().unwrap().root_cause().downcast_ref::<selinux::Error>()
+ );
+ };
+ }
+
+ fn check_context() -> Result<(selinux::Context, i32, bool)> {
+ // Calling the non mocked selinux::getcon here intended.
+ let context = selinux::getcon()?;
+ match context.to_str().unwrap() {
+ "u:r:su:s0" => Ok((context, SU_KEY_NAMESPACE, true)),
+ "u:r:shell:s0" => Ok((context, SHELL_KEY_NAMESPACE, false)),
+ c => Err(anyhow!(format!(
+ "This test must be run as \"su\" or \"shell\". Current context: \"{}\"",
+ c
+ ))),
+ }
+ }
+
+ #[test]
+ fn check_keystore_permission_test() -> Result<()> {
+ let system_server_ctx = Context::new("u:r:system_server:s0")?;
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::add_auth()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::clear_ns()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::get_state()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::list()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::lock()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::reset()).is_ok());
+ assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::unlock()).is_ok());
+ let shell_ctx = Context::new("u:r:shell:s0")?;
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::add_auth()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::clear_ns()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::get_state()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::list()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::lock()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::reset()));
+ assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::unlock()));
+ Ok(())
+ }
+
+ #[test]
+ fn check_grant_permission_app() -> Result<()> {
+ let system_server_ctx = Context::new("u:r:system_server:s0")?;
+ let shell_ctx = Context::new("u:r:shell:s0")?;
+ let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
+ assert!(check_grant_permission(&system_server_ctx, NOT_GRANT_PERMS, &key).is_ok());
+ // attempts to grant the grant permission must always fail even when privileged.
+
+ assert_perm_failed!(check_grant_permission(
+ &system_server_ctx,
+ KeyPerm::grant().into(),
+ &key
+ ));
+ // unprivileged grant attempts always fail. shell does not have the grant permission.
+ assert_perm_failed!(check_grant_permission(&shell_ctx, UNPRIV_PERMS, &key));
+ Ok(())
+ }
+
+ #[test]
+ fn check_grant_permission_selinux() -> Result<()> {
+ let (sctx, namespace, is_su) = check_context()?;
+ let key = KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: namespace as i64,
+ alias: None,
+ blob: None,
+ };
+ if is_su {
+ assert!(check_grant_permission(&sctx, NOT_GRANT_PERMS, &key).is_ok());
+ // attempts to grant the grant permission must always fail even when privileged.
+ assert_perm_failed!(check_grant_permission(&sctx, KeyPerm::grant().into(), &key));
+ } else {
+ // unprivileged grant attempts always fail. shell does not have the grant permission.
+ assert_perm_failed!(check_grant_permission(&sctx, UNPRIV_PERMS, &key));
+ }
+ Ok(())
+ }
+
+ #[test]
+ fn check_key_permission_domain_grant() -> Result<()> {
+ let key = KeyDescriptor { domain: Domain::GRANT, nspace: 0, alias: None, blob: None };
+
+ assert_perm_failed!(check_key_permission(
+ &selinux::Context::new("ignored").unwrap(),
+ KeyPerm::grant(),
+ &key,
+ &Some(UNPRIV_PERMS)
+ ));
+
+ check_key_permission(
+ &selinux::Context::new("ignored").unwrap(),
+ KeyPerm::use_(),
+ &key,
+ &Some(ALL_PERMS),
+ )
+ }
+
+ #[test]
+ fn check_key_permission_domain_app() -> Result<()> {
+ let system_server_ctx = Context::new("u:r:system_server:s0")?;
+ let shell_ctx = Context::new("u:r:shell:s0")?;
+ let gmscore_app = Context::new("u:r:gmscore_app:s0")?;
+
+ let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
+
+ assert!(check_key_permission(&system_server_ctx, KeyPerm::use_(), &key, &None).is_ok());
+ assert!(check_key_permission(&system_server_ctx, KeyPerm::delete(), &key, &None).is_ok());
+ assert!(check_key_permission(&system_server_ctx, KeyPerm::get_info(), &key, &None).is_ok());
+ assert!(check_key_permission(&system_server_ctx, KeyPerm::rebind(), &key, &None).is_ok());
+ assert!(check_key_permission(&system_server_ctx, KeyPerm::update(), &key, &None).is_ok());
+ assert!(check_key_permission(&system_server_ctx, KeyPerm::grant(), &key, &None).is_ok());
+ assert!(
+ check_key_permission(&system_server_ctx, KeyPerm::use_dev_id(), &key, &None).is_ok()
+ );
+ assert!(check_key_permission(&gmscore_app, KeyPerm::gen_unique_id(), &key, &None).is_ok());
+
+ assert!(check_key_permission(&shell_ctx, KeyPerm::use_(), &key, &None).is_ok());
+ assert!(check_key_permission(&shell_ctx, KeyPerm::delete(), &key, &None).is_ok());
+ assert!(check_key_permission(&shell_ctx, KeyPerm::get_info(), &key, &None).is_ok());
+ assert!(check_key_permission(&shell_ctx, KeyPerm::rebind(), &key, &None).is_ok());
+ assert!(check_key_permission(&shell_ctx, KeyPerm::update(), &key, &None).is_ok());
+ assert_perm_failed!(check_key_permission(&shell_ctx, KeyPerm::grant(), &key, &None));
+ assert_perm_failed!(check_key_permission(
+ &shell_ctx,
+ KeyPerm::req_forced_op(),
+ &key,
+ &None
+ ));
+ assert_perm_failed!(check_key_permission(&shell_ctx, KeyPerm::manage_blob(), &key, &None));
+ assert_perm_failed!(check_key_permission(&shell_ctx, KeyPerm::use_dev_id(), &key, &None));
+ assert_perm_failed!(check_key_permission(
+ &shell_ctx,
+ KeyPerm::gen_unique_id(),
+ &key,
+ &None
+ ));
+
+ Ok(())
+ }
+
+ #[test]
+ fn check_key_permission_domain_selinux() -> Result<()> {
+ let (sctx, namespace, is_su) = check_context()?;
+ let key = KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: namespace as i64,
+ alias: None,
+ blob: None,
+ };
+
+ if is_su {
+ assert!(check_key_permission(&sctx, KeyPerm::use_(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::delete(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::get_info(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::rebind(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::update(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::grant(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::manage_blob(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::use_dev_id(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::gen_unique_id(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::req_forced_op(), &key, &None).is_ok());
+ } else {
+ assert!(check_key_permission(&sctx, KeyPerm::use_(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::delete(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::get_info(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::rebind(), &key, &None).is_ok());
+ assert!(check_key_permission(&sctx, KeyPerm::update(), &key, &None).is_ok());
+ assert_perm_failed!(check_key_permission(&sctx, KeyPerm::grant(), &key, &None));
+ assert_perm_failed!(check_key_permission(&sctx, KeyPerm::req_forced_op(), &key, &None));
+ assert_perm_failed!(check_key_permission(&sctx, KeyPerm::manage_blob(), &key, &None));
+ assert_perm_failed!(check_key_permission(&sctx, KeyPerm::use_dev_id(), &key, &None));
+ assert_perm_failed!(check_key_permission(&sctx, KeyPerm::gen_unique_id(), &key, &None));
+ }
+ Ok(())
+ }
+
+ #[test]
+ fn check_key_permission_domain_blob() -> Result<()> {
+ let (sctx, namespace, is_su) = check_context()?;
+ let key = KeyDescriptor {
+ domain: Domain::BLOB,
+ nspace: namespace as i64,
+ alias: None,
+ blob: None,
+ };
+
+ if is_su {
+ check_key_permission(&sctx, KeyPerm::use_(), &key, &None)
+ } else {
+ assert_perm_failed!(check_key_permission(&sctx, KeyPerm::use_(), &key, &None));
+ Ok(())
+ }
+ }
+
+ #[test]
+ fn check_key_permission_domain_key_id() -> Result<()> {
+ let key = KeyDescriptor { domain: Domain::KEY_ID, nspace: 0, alias: None, blob: None };
+
+ assert_eq!(
+ Some(&KsError::sys()),
+ check_key_permission(
+ &selinux::Context::new("ignored").unwrap(),
+ KeyPerm::use_(),
+ &key,
+ &None
+ )
+ .err()
+ .unwrap()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn key_perm_set_all_test() {
+ let v = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::use_dev_id(),
+ KeyPerm::req_forced_op(),
+ KeyPerm::gen_unique_id(),
+ KeyPerm::grant(),
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_() // Test if the macro accepts missing comma at the end of the list.
+ ];
+ let mut i = v.into_iter();
+ assert_eq!(i.next().unwrap().to_selinux(), "delete");
+ assert_eq!(i.next().unwrap().to_selinux(), "gen_unique_id");
+ assert_eq!(i.next().unwrap().to_selinux(), "get_info");
+ assert_eq!(i.next().unwrap().to_selinux(), "grant");
+ assert_eq!(i.next().unwrap().to_selinux(), "manage_blob");
+ assert_eq!(i.next().unwrap().to_selinux(), "rebind");
+ assert_eq!(i.next().unwrap().to_selinux(), "req_forced_op");
+ assert_eq!(i.next().unwrap().to_selinux(), "update");
+ assert_eq!(i.next().unwrap().to_selinux(), "use");
+ assert_eq!(i.next().unwrap().to_selinux(), "use_dev_id");
+ assert_eq!(None, i.next());
+ }
+ #[test]
+ fn key_perm_set_sparse_test() {
+ let v = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::req_forced_op(),
+ KeyPerm::gen_unique_id(),
+ KeyPerm::update(),
+ KeyPerm::use_(), // Test if macro accepts the comma at the end of the list.
+ ];
+ let mut i = v.into_iter();
+ assert_eq!(i.next().unwrap().to_selinux(), "gen_unique_id");
+ assert_eq!(i.next().unwrap().to_selinux(), "manage_blob");
+ assert_eq!(i.next().unwrap().to_selinux(), "req_forced_op");
+ assert_eq!(i.next().unwrap().to_selinux(), "update");
+ assert_eq!(i.next().unwrap().to_selinux(), "use");
+ assert_eq!(None, i.next());
+ }
+ #[test]
+ fn key_perm_set_empty_test() {
+ let v = key_perm_set![];
+ let mut i = v.into_iter();
+ assert_eq!(None, i.next());
+ }
+ #[test]
+ fn key_perm_set_include_subset_test() {
+ let v1 = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::use_dev_id(),
+ KeyPerm::req_forced_op(),
+ KeyPerm::gen_unique_id(),
+ KeyPerm::grant(),
+ KeyPerm::get_info(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+ let v2 = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+ assert!(v1.includes(v2));
+ assert!(!v2.includes(v1));
+ }
+ #[test]
+ fn key_perm_set_include_equal_test() {
+ let v1 = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+ let v2 = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+ assert!(v1.includes(v2));
+ assert!(v2.includes(v1));
+ }
+ #[test]
+ fn key_perm_set_include_overlap_test() {
+ let v1 = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::grant(), // only in v1
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+ let v2 = key_perm_set![
+ KeyPerm::manage_blob(),
+ KeyPerm::delete(),
+ KeyPerm::req_forced_op(), // only in v2
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+ assert!(!v1.includes(v2));
+ assert!(!v2.includes(v1));
+ }
+ #[test]
+ fn key_perm_set_include_no_overlap_test() {
+ let v1 = key_perm_set![KeyPerm::manage_blob(), KeyPerm::delete(), KeyPerm::grant(),];
+ let v2 = key_perm_set![
+ KeyPerm::req_forced_op(),
+ KeyPerm::rebind(),
+ KeyPerm::update(),
+ KeyPerm::use_(),
+ ];
+ assert!(!v1.includes(v2));
+ assert!(!v2.includes(v1));
+ }
+}
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
new file mode 100644
index 0000000..a89f309
--- /dev/null
+++ b/keystore2/src/security_level.rs
@@ -0,0 +1,608 @@
+// 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(unused_variables)]
+
+//! This crate implements the IKeystoreSecurityLevel interface.
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, ByteArray::ByteArray, Certificate::Certificate as KmCertificate,
+ HardwareAuthenticatorType::HardwareAuthenticatorType, IKeyMintDevice::IKeyMintDevice,
+ KeyCharacteristics::KeyCharacteristics, KeyFormat::KeyFormat, KeyParameter::KeyParameter,
+ KeyParameterValue::KeyParameterValue, SecurityLevel::SecurityLevel, Tag::Tag,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ AuthenticatorSpec::AuthenticatorSpec, CreateOperationResponse::CreateOperationResponse,
+ Domain::Domain, IKeystoreOperation::IKeystoreOperation,
+ IKeystoreSecurityLevel::BnKeystoreSecurityLevel,
+ IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+ KeyMetadata::KeyMetadata, KeyParameters::KeyParameters,
+};
+
+use crate::utils::{check_key_permission, Asp};
+use crate::{database::KeyIdGuard, globals::DB};
+use crate::{
+ database::{DateTime, KeyMetaData, KeyMetaEntry, KeyType},
+ permission::KeyPerm,
+};
+use crate::{
+ database::{KeyEntry, KeyEntryLoadBits, SubComponentType},
+ operation::KeystoreOperation,
+ operation::OperationDb,
+};
+use crate::{
+ error::{self, map_km_error, map_or_log_err, Error, ErrorCode},
+ utils::key_characteristics_to_internal,
+};
+use anyhow::{anyhow, Context, Result};
+use binder::{IBinder, Interface, ThreadState};
+
+/// Implementation of the IKeystoreSecurityLevel Interface.
+pub struct KeystoreSecurityLevel {
+ security_level: SecurityLevel,
+ keymint: Asp,
+ 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];
+
+impl KeystoreSecurityLevel {
+ /// Creates a new security level instance wrapped in a
+ /// BnKeystoreSecurityLevel proxy object. It also
+ /// calls `IBinder::set_requesting_sid` on the new interface, because
+ /// we need it for checking keystore permissions.
+ 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()),
+ operation_db: OperationDb::new(),
+ });
+ result.as_binder().set_requesting_sid(true);
+ Ok(result)
+ }
+
+ fn store_new_key(
+ &self,
+ key: KeyDescriptor,
+ key_characteristics: KeyCharacteristics,
+ km_cert_chain: Option<Vec<KmCertificate>>,
+ blob: ByteArray,
+ ) -> Result<KeyMetadata> {
+ let (cert, cert_chain): (Option<Vec<u8>>, Option<Vec<u8>>) = match km_cert_chain {
+ Some(mut chain) => (
+ match chain.len() {
+ 0 => None,
+ _ => Some(chain.remove(0).encodedCertificate),
+ },
+ match chain.len() {
+ 0 => None,
+ _ => Some(
+ chain
+ .iter()
+ .map(|c| c.encodedCertificate.iter())
+ .flatten()
+ .copied()
+ .collect(),
+ ),
+ },
+ ),
+ None => (None, None),
+ };
+
+ let key_parameters =
+ key_characteristics_to_internal(key_characteristics, self.security_level);
+
+ let creation_date = DateTime::now().context("Trying to make creation time.")?;
+
+ let key = match key.domain {
+ Domain::BLOB => {
+ 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.")?;
+ 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.)",
+ )
+ }
+ }
+ Ok(KeyDescriptor {
+ domain: Domain::KEY_ID,
+ nspace: key_id.id(),
+ ..Default::default()
+ })
+ })
+ .context("In store_new_key.")?,
+ };
+
+ Ok(KeyMetadata {
+ key,
+ keySecurityLevel: self.security_level,
+ certificate: cert,
+ certificateChain: cert_chain,
+ authorizations: crate::utils::key_parameters_to_authorizations(key_parameters),
+ modificationTimeMs: creation_date.to_millis_epoch(),
+ })
+ }
+
+ fn create_operation(
+ &self,
+ key: &KeyDescriptor,
+ operation_parameters: &[KeyParameter],
+ forced: bool,
+ ) -> Result<CreateOperationResponse> {
+ let caller_uid = ThreadState::get_calling_uid();
+ // We use `scoping_blob` to extend the life cycle of the blob loaded from the database,
+ // so that we can use it by reference like the blob provided by the key descriptor.
+ // Otherwise, we would have to clone the blob from the key descriptor.
+ let scoping_blob: Vec<u8>;
+ let (km_blob, key_id_guard) = match key.domain {
+ Domain::BLOB => {
+ check_key_permission(KeyPerm::use_(), key, &None)
+ .context("In create_operation: checking use permission for Domain::BLOB.")?;
+ (
+ match &key.blob {
+ Some(blob) => blob,
+ None => {
+ return Err(Error::sys()).context(concat!(
+ "In create_operation: Key blob must be specified when",
+ " using Domain::BLOB."
+ ))
+ }
+ },
+ None,
+ )
+ }
+ _ => {
+ let (key_id_guard, mut key_entry) = DB
+ .with::<_, Result<(KeyIdGuard, KeyEntry)>>(|db| {
+ db.borrow_mut().load_key_entry(
+ key.clone(),
+ KeyType::Client,
+ KeyEntryLoadBits::KM,
+ caller_uid,
+ |k, av| check_key_permission(KeyPerm::use_(), k, &av),
+ )
+ })
+ .context("In create_operation: Failed to load key blob.")?;
+ scoping_blob = match key_entry.take_km_blob() {
+ Some(blob) => blob,
+ None => {
+ return Err(Error::sys()).context(concat!(
+ "In create_operation: Successfully loaded key entry,",
+ " but KM blob was missing."
+ ))
+ }
+ };
+ (&scoping_blob, Some(key_id_guard))
+ }
+ };
+
+ // TODO Authorize begin operation.
+ // Check if we need an authorization token.
+ // Lookup authorization token and request VerificationToken if required.
+
+ let purpose = operation_parameters.iter().find(|p| p.tag == Tag::PURPOSE).map_or(
+ Err(Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("In create_operation: No operation purpose specified."),
+ |kp| match kp.value {
+ KeyParameterValue::KeyPurpose(p) => Ok(p),
+ _ => Err(Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("In create_operation: Malformed KeyParameter."),
+ },
+ )?;
+
+ let km_dev: Box<dyn IKeyMintDevice> = self
+ .keymint
+ .get_interface()
+ .context("In create_operation: Failed to get KeyMint device")?;
+
+ let (begin_result, upgraded_blob) = self
+ .upgrade_keyblob_if_required_with(
+ &*km_dev,
+ key_id_guard,
+ &km_blob,
+ &operation_parameters,
+ |blob| loop {
+ match map_km_error(km_dev.begin(
+ purpose,
+ blob,
+ &operation_parameters,
+ &Default::default(),
+ )) {
+ Err(Error::Km(ErrorCode::TOO_MANY_OPERATIONS)) => {
+ self.operation_db.prune(caller_uid)?;
+ continue;
+ }
+ v => return v,
+ }
+ },
+ )
+ .context("In create_operation: Failed to begin operation.")?;
+
+ let operation = match begin_result.operation {
+ Some(km_op) => self.operation_db.create_operation(km_op, caller_uid),
+ None => return Err(Error::sys()).context("In create_operation: Begin operation returned successfully, but did not return a valid operation."),
+ };
+
+ let op_binder: Box<dyn IKeystoreOperation> =
+ KeystoreOperation::new_native_binder(operation)
+ .as_binder()
+ .into_interface()
+ .context("In create_operation: Failed to create IKeystoreOperation.")?;
+
+ // TODO we need to the enforcement module to determine if we need to return the challenge.
+ // We return None for now because we don't support auth bound keys yet.
+ Ok(CreateOperationResponse {
+ iOperation: Some(op_binder),
+ operationChallenge: None,
+ parameters: match begin_result.params.len() {
+ 0 => None,
+ _ => Some(KeyParameters { keyParameter: begin_result.params }),
+ },
+ })
+ }
+
+ fn generate_key(
+ &self,
+ key: &KeyDescriptor,
+ attestation_key: Option<&KeyDescriptor>,
+ params: &[KeyParameter],
+ flags: i32,
+ entropy: &[u8],
+ ) -> Result<KeyMetadata> {
+ if key.domain != Domain::BLOB && key.alias.is_none() {
+ return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("In generate_key: Alias must be specified");
+ }
+
+ let key = match key.domain {
+ Domain::APP => KeyDescriptor {
+ domain: key.domain,
+ nspace: ThreadState::get_calling_uid() as i64,
+ alias: key.alias.clone(),
+ blob: None,
+ },
+ _ => key.clone(),
+ };
+
+ // generate_key requires the rebind permission.
+ check_key_permission(KeyPerm::rebind(), &key, &None).context("In generate_key.")?;
+
+ let km_dev: Box<dyn IKeyMintDevice> = self.keymint.get_interface()?;
+ map_km_error(km_dev.addRngEntropy(entropy))?;
+ let mut blob: ByteArray = Default::default();
+ let mut key_characteristics: KeyCharacteristics = Default::default();
+ let mut certificate_chain: Vec<KmCertificate> = Default::default();
+ map_km_error(km_dev.generateKey(
+ ¶ms,
+ &mut blob,
+ &mut key_characteristics,
+ &mut certificate_chain,
+ ))?;
+
+ self.store_new_key(key, key_characteristics, Some(certificate_chain), blob)
+ .context("In generate_key.")
+ }
+
+ fn import_key(
+ &self,
+ key: &KeyDescriptor,
+ attestation_key: Option<&KeyDescriptor>,
+ params: &[KeyParameter],
+ flags: i32,
+ key_data: &[u8],
+ ) -> Result<KeyMetadata> {
+ if key.domain != Domain::BLOB && key.alias.is_none() {
+ return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("In import_key: Alias must be specified");
+ }
+
+ let key = match key.domain {
+ Domain::APP => KeyDescriptor {
+ domain: key.domain,
+ nspace: ThreadState::get_calling_uid() as i64,
+ alias: key.alias.clone(),
+ blob: None,
+ },
+ _ => key.clone(),
+ };
+
+ // import_key requires the rebind permission.
+ check_key_permission(KeyPerm::rebind(), &key, &None).context("In import_key.")?;
+
+ let mut blob: ByteArray = Default::default();
+ let mut key_characteristics: KeyCharacteristics = Default::default();
+ let mut certificate_chain: Vec<KmCertificate> = Default::default();
+
+ let format = params
+ .iter()
+ .find(|p| p.tag == Tag::ALGORITHM)
+ .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("No KeyParameter 'Algorithm'.")
+ .and_then(|p| match &p.value {
+ KeyParameterValue::Algorithm(Algorithm::AES)
+ | KeyParameterValue::Algorithm(Algorithm::HMAC)
+ | KeyParameterValue::Algorithm(Algorithm::TRIPLE_DES) => Ok(KeyFormat::RAW),
+ KeyParameterValue::Algorithm(Algorithm::RSA)
+ | KeyParameterValue::Algorithm(Algorithm::EC) => Ok(KeyFormat::PKCS8),
+ v => Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context(format!("Unknown Algorithm {:?}.", v)),
+ })
+ .context("In import_key.")?;
+
+ let km_dev: Box<dyn IKeyMintDevice> = self.keymint.get_interface()?;
+ map_km_error(km_dev.importKey(
+ ¶ms,
+ format,
+ key_data,
+ &mut blob,
+ &mut key_characteristics,
+ &mut certificate_chain,
+ ))?;
+
+ self.store_new_key(key, key_characteristics, Some(certificate_chain), blob)
+ .context("In import_key.")
+ }
+
+ fn import_wrapped_key(
+ &self,
+ key: &KeyDescriptor,
+ wrapping_key: &KeyDescriptor,
+ masking_key: Option<&[u8]>,
+ params: &[KeyParameter],
+ authenticators: &[AuthenticatorSpec],
+ ) -> Result<KeyMetadata> {
+ if key.domain != Domain::BLOB && key.alias.is_none() {
+ return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("In import_wrapped_key: Alias must be specified.");
+ }
+
+ if wrapping_key.domain == Domain::BLOB {
+ return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context(
+ "In import_wrapped_key: Import wrapped key not supported for self managed blobs.",
+ );
+ }
+
+ let wrapped_data = match &key.blob {
+ Some(d) => d,
+ None => {
+ return Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context(
+ "In import_wrapped_key: Blob must be specified and hold wrapped key data.",
+ )
+ }
+ };
+
+ let key = match key.domain {
+ Domain::APP => KeyDescriptor {
+ domain: key.domain,
+ nspace: ThreadState::get_calling_uid() as i64,
+ alias: key.alias.clone(),
+ blob: None,
+ },
+ _ => key.clone(),
+ };
+
+ // import_wrapped_key requires the rebind permission for the new key.
+ check_key_permission(KeyPerm::rebind(), &key, &None).context("In import_wrapped_key.")?;
+
+ let (wrapping_key_id_guard, wrapping_key_entry) = DB
+ .with(|db| {
+ db.borrow_mut().load_key_entry(
+ wrapping_key.clone(),
+ KeyType::Client,
+ KeyEntryLoadBits::KM,
+ ThreadState::get_calling_uid(),
+ |k, av| check_key_permission(KeyPerm::use_(), k, &av),
+ )
+ })
+ .context("Failed to load wrapping key.")?;
+ let wrapping_key_blob = match wrapping_key_entry.km_blob() {
+ Some(blob) => blob,
+ None => {
+ return Err(error::Error::sys()).context(concat!(
+ "No km_blob after successfully loading key.",
+ " This should never happen."
+ ))
+ }
+ };
+
+ // km_dev.importWrappedKey does not return a certificate chain.
+ // TODO Do we assume that all wrapped keys are symmetric?
+ // let certificate_chain: Vec<KmCertificate> = Default::default();
+
+ let pw_sid = authenticators
+ .iter()
+ .find_map(|a| match a.authenticatorType {
+ HardwareAuthenticatorType::PASSWORD => Some(a.authenticatorId),
+ _ => None,
+ })
+ .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("A password authenticator SID must be specified.")?;
+
+ let fp_sid = authenticators
+ .iter()
+ .find_map(|a| match a.authenticatorType {
+ HardwareAuthenticatorType::FINGERPRINT => Some(a.authenticatorId),
+ _ => None,
+ })
+ .ok_or(error::Error::Km(ErrorCode::INVALID_ARGUMENT))
+ .context("A fingerprint authenticator SID must be specified.")?;
+
+ let masking_key = masking_key.unwrap_or(ZERO_BLOB_32);
+
+ let km_dev: Box<dyn IKeyMintDevice> = self.keymint.get_interface()?;
+ let ((blob, key_characteristics), _) = self.upgrade_keyblob_if_required_with(
+ &*km_dev,
+ Some(wrapping_key_id_guard),
+ wrapping_key_blob,
+ &[],
+ |wrapping_blob| {
+ let mut blob: ByteArray = Default::default();
+ let mut key_characteristics: KeyCharacteristics = Default::default();
+ map_km_error(km_dev.importWrappedKey(
+ wrapped_data,
+ wrapping_key_blob,
+ masking_key,
+ ¶ms,
+ pw_sid,
+ fp_sid,
+ &mut blob,
+ &mut key_characteristics,
+ ))?;
+ Ok((blob, key_characteristics))
+ },
+ )?;
+
+ self.store_new_key(key, key_characteristics, None, blob).context("In import_wrapped_key.")
+ }
+
+ fn upgrade_keyblob_if_required_with<T, F>(
+ &self,
+ km_dev: &dyn IKeyMintDevice,
+ key_id_guard: Option<KeyIdGuard>,
+ blob: &[u8],
+ params: &[KeyParameter],
+ f: F,
+ ) -> Result<(T, Option<Vec<u8>>)>
+ where
+ F: Fn(&[u8]) -> Result<T, Error>,
+ {
+ match f(blob) {
+ Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
+ let upgraded_blob = map_km_error(km_dev.upgradeKey(blob, params))
+ .context("In upgrade_keyblob_if_required_with: Upgrade failed.")?;
+ key_id_guard.map_or(Ok(()), |key_id_guard| {
+ DB.with(|db| {
+ db.borrow_mut().insert_blob(
+ &key_id_guard,
+ SubComponentType::KEY_BLOB,
+ &upgraded_blob,
+ self.security_level,
+ )
+ })
+ .context(concat!(
+ "In upgrade_keyblob_if_required_with: ",
+ "Failed to insert upgraded blob into the database.",
+ ))
+ })?;
+ match f(&upgraded_blob) {
+ Ok(v) => Ok((v, Some(upgraded_blob))),
+ Err(e) => Err(e).context(concat!(
+ "In upgrade_keyblob_if_required_with: ",
+ "Failed to perform operation on second try."
+ )),
+ }
+ }
+ Err(e) => {
+ Err(e).context("In upgrade_keyblob_if_required_with: Failed perform operation.")
+ }
+ Ok(v) => Ok((v, None)),
+ }
+ }
+}
+
+impl binder::Interface for KeystoreSecurityLevel {}
+
+impl IKeystoreSecurityLevel for KeystoreSecurityLevel {
+ fn createOperation(
+ &self,
+ key: &KeyDescriptor,
+ operation_parameters: &[KeyParameter],
+ forced: bool,
+ ) -> binder::public_api::Result<CreateOperationResponse> {
+ map_or_log_err(self.create_operation(key, operation_parameters, forced), Ok)
+ }
+ fn generateKey(
+ &self,
+ key: &KeyDescriptor,
+ attestation_key: Option<&KeyDescriptor>,
+ params: &[KeyParameter],
+ flags: i32,
+ entropy: &[u8],
+ ) -> binder::public_api::Result<KeyMetadata> {
+ map_or_log_err(self.generate_key(key, attestation_key, params, flags, entropy), Ok)
+ }
+ fn importKey(
+ &self,
+ key: &KeyDescriptor,
+ attestation_key: Option<&KeyDescriptor>,
+ params: &[KeyParameter],
+ flags: i32,
+ key_data: &[u8],
+ ) -> binder::public_api::Result<KeyMetadata> {
+ map_or_log_err(self.import_key(key, attestation_key, params, flags, key_data), Ok)
+ }
+ fn importWrappedKey(
+ &self,
+ key: &KeyDescriptor,
+ wrapping_key: &KeyDescriptor,
+ masking_key: Option<&[u8]>,
+ params: &[KeyParameter],
+ authenticators: &[AuthenticatorSpec],
+ ) -> binder::public_api::Result<KeyMetadata> {
+ map_or_log_err(
+ self.import_wrapped_key(key, wrapping_key, masking_key, params, authenticators),
+ Ok,
+ )
+ }
+}
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
new file mode 100644
index 0000000..d475b7e
--- /dev/null
+++ b/keystore2/src/service.rs
@@ -0,0 +1,281 @@
+// 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 remove when fully implemented.
+#![allow(unused_variables)]
+
+//! This crate implement the core Keystore 2.0 service API as defined by the Keystore 2.0
+//! AIDL spec.
+
+use crate::error::{self, map_or_log_err, ErrorCode};
+use crate::globals::DB;
+use crate::permission;
+use crate::permission::{KeyPerm, KeystorePerm};
+use crate::security_level::KeystoreSecurityLevel;
+use crate::utils::{
+ check_grant_permission, check_key_permission, check_keystore_permission,
+ key_parameters_to_authorizations, Asp,
+};
+use crate::{
+ database::{KeyEntryLoadBits, KeyType, SubComponentType},
+ error::ResponseCode,
+};
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
+ IKeystoreService::BnKeystoreService, IKeystoreService::IKeystoreService,
+ KeyDescriptor::KeyDescriptor, KeyEntryResponse::KeyEntryResponse, KeyMetadata::KeyMetadata,
+};
+use anyhow::{anyhow, Context, Result};
+use binder::{IBinder, Interface, ThreadState};
+use error::Error;
+use keystore2_selinux as selinux;
+
+/// Implementation of the IKeystoreService.
+pub struct KeystoreService {
+ sec_level: Asp,
+}
+
+impl KeystoreService {
+ /// Create a new instance of the Keystore 2.0 service.
+ pub fn new_native_binder() -> Result<impl IKeystoreService> {
+ 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()
+ }),
+ });
+ result.as_binder().set_requesting_sid(true);
+ Ok(result)
+ }
+
+ 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))),
+ }
+ }
+
+ fn get_key_entry(&self, key: &KeyDescriptor) -> Result<KeyEntryResponse> {
+ let (key_id_guard, mut key_entry) = DB
+ .with(|db| {
+ db.borrow_mut().load_key_entry(
+ key.clone(),
+ KeyType::Client,
+ KeyEntryLoadBits::PUBLIC,
+ ThreadState::get_calling_uid(),
+ |k, av| check_key_permission(KeyPerm::get_info(), k, &av),
+ )
+ })
+ .context("In get_key_entry, while trying to load key info.")?;
+
+ let i_sec_level = 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))),
+ };
+
+ Ok(KeyEntryResponse {
+ iSecurityLevel: Some(i_sec_level),
+ metadata: KeyMetadata {
+ key: KeyDescriptor {
+ domain: Domain::KEY_ID,
+ nspace: key_id_guard.id(),
+ ..Default::default()
+ },
+ keySecurityLevel: key_entry.sec_level(),
+ certificate: key_entry.take_cert(),
+ certificateChain: key_entry.take_cert_chain(),
+ modificationTimeMs: key_entry
+ .metadata()
+ .creation_date()
+ .map(|d| d.to_millis_epoch())
+ .ok_or(Error::Rc(ResponseCode::VALUE_CORRUPTED))
+ .context("In get_key_entry: Trying to get creation date.")?,
+ authorizations: key_parameters_to_authorizations(key_entry.into_key_parameters()),
+ },
+ })
+ }
+
+ fn update_subcomponent(
+ &self,
+ key: &KeyDescriptor,
+ public_cert: Option<&[u8]>,
+ certificate_chain: Option<&[u8]>,
+ ) -> Result<()> {
+ DB.with::<_, Result<()>>(|db| {
+ let mut db = db.borrow_mut();
+ let (key_id_guard, key_entry) = db
+ .load_key_entry(
+ key.clone(),
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ ThreadState::get_calling_uid(),
+ |k, av| {
+ check_key_permission(KeyPerm::update(), k, &av)
+ .context("In update_subcomponent.")
+ },
+ )
+ .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())
+ .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.")?;
+ }
+ Ok(())
+ })
+ .context("In update_subcomponent.")
+ }
+
+ fn list_entries(&self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
+ let mut k = match domain {
+ Domain::APP => KeyDescriptor {
+ domain,
+ nspace: ThreadState::get_calling_uid() as u64 as i64,
+ ..Default::default()
+ },
+ Domain::SELINUX => KeyDescriptor{domain, nspace: namespace, ..Default::default()},
+ _ => return Err(Error::perm()).context(
+ "In list_entries: List entries is only supported for Domain::APP and Domain::SELINUX."
+ ),
+ };
+
+ // First we check if the caller has the info permission for the selected domain/namespace.
+ // By default we use the calling uid as namespace if domain is Domain::APP.
+ // If the first check fails we check if the caller has the list permission allowing to list
+ // any namespace. In that case we also adjust the queried namespace if a specific uid was
+ // selected.
+ match check_key_permission(KeyPerm::get_info(), &k, &None) {
+ Err(e) => {
+ if let Some(selinux::Error::PermissionDenied) =
+ e.root_cause().downcast_ref::<selinux::Error>()
+ {
+ check_keystore_permission(KeystorePerm::list())
+ .context("In list_entries: While checking keystore permission.")?;
+ if namespace != -1 {
+ k.nspace = namespace;
+ }
+ } else {
+ return Err(e).context("In list_entries: While checking key permission.")?;
+ }
+ }
+ Ok(()) => {}
+ };
+
+ DB.with(|db| {
+ let mut db = db.borrow_mut();
+ db.list(k.domain, k.nspace)
+ })
+ }
+
+ fn delete_key(&self, key: &KeyDescriptor) -> Result<()> {
+ // TODO implement.
+ Ok(())
+ }
+
+ fn grant(
+ &self,
+ key: &KeyDescriptor,
+ grantee_uid: i32,
+ access_vector: permission::KeyPermSet,
+ ) -> Result<KeyDescriptor> {
+ DB.with(|db| {
+ db.borrow_mut().grant(
+ key.clone(),
+ ThreadState::get_calling_uid(),
+ grantee_uid as u32,
+ access_vector,
+ |k, av| check_grant_permission(*av, k).context("During grant."),
+ )
+ })
+ .context("In KeystoreService::grant.")
+ }
+
+ fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> Result<()> {
+ DB.with(|db| {
+ db.borrow_mut().ungrant(
+ key.clone(),
+ ThreadState::get_calling_uid(),
+ grantee_uid as u32,
+ |k| check_key_permission(KeyPerm::grant(), k, &None),
+ )
+ })
+ .context("In KeystoreService::ungrant.")
+ }
+}
+
+impl binder::Interface for KeystoreService {}
+
+// Implementation of IKeystoreService. See AIDL spec at
+// system/security/keystore2/binder/android/security/keystore2/IKeystoreService.aidl
+impl IKeystoreService for KeystoreService {
+ fn getSecurityLevel(
+ &self,
+ security_level: SecurityLevel,
+ ) -> binder::public_api::Result<Box<dyn IKeystoreSecurityLevel>> {
+ map_or_log_err(self.get_security_level(SecurityLevel(security_level.0)), Ok)
+ }
+ fn getKeyEntry(&self, key: &KeyDescriptor) -> binder::public_api::Result<KeyEntryResponse> {
+ map_or_log_err(self.get_key_entry(key), Ok)
+ }
+ fn updateSubcomponent(
+ &self,
+ key: &KeyDescriptor,
+ public_cert: Option<&[u8]>,
+ certificate_chain: Option<&[u8]>,
+ ) -> binder::public_api::Result<()> {
+ map_or_log_err(self.update_subcomponent(key, public_cert, certificate_chain), Ok)
+ }
+ fn listEntries(
+ &self,
+ domain: Domain,
+ namespace: i64,
+ ) -> binder::public_api::Result<Vec<KeyDescriptor>> {
+ map_or_log_err(self.list_entries(domain, namespace), Ok)
+ }
+ fn deleteKey(&self, key: &KeyDescriptor) -> binder::public_api::Result<()> {
+ map_or_log_err(self.delete_key(key), Ok)
+ }
+ fn grant(
+ &self,
+ key: &KeyDescriptor,
+ grantee_uid: i32,
+ access_vector: i32,
+ ) -> binder::public_api::Result<KeyDescriptor> {
+ map_or_log_err(self.grant(key, grantee_uid, access_vector.into()), Ok)
+ }
+ fn ungrant(&self, key: &KeyDescriptor, grantee_uid: i32) -> binder::public_api::Result<()> {
+ map_or_log_err(self.ungrant(key, grantee_uid), Ok)
+ }
+}
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
new file mode 100644
index 0000000..4ffe897
--- /dev/null
+++ b/keystore2/src/super_key.rs
@@ -0,0 +1,218 @@
+// 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)]
+
+use crate::{
+ database::EncryptedBy, database::KeyMetaData, database::KeyMetaEntry, database::KeystoreDB,
+ error::Error, error::ResponseCode, legacy_blob::LegacyBlobLoader,
+};
+use android_system_keystore2::aidl::android::system::keystore2::Domain::Domain;
+use anyhow::{Context, Result};
+use keystore2_crypto::{
+ aes_gcm_decrypt, aes_gcm_encrypt, derive_key_from_password, generate_salt, ZVec,
+ AES_256_KEY_LENGTH,
+};
+use std::{
+ collections::HashMap,
+ sync::Arc,
+ sync::{Mutex, Weak},
+};
+
+type UserId = u32;
+
+#[derive(Default)]
+struct UserSuperKeys {
+ /// The per boot key is used for LSKF binding of authentication bound keys. There is one
+ /// key per android user. The key is stored on flash encrypted with a key derived from a
+ /// secret, that is itself derived from the user's lock screen knowledge factor (LSKF).
+ /// When the user unlocks the device for the first time, this key is unlocked, i.e., decrypted,
+ /// and stays memory resident until the device reboots.
+ per_boot: Option<Arc<ZVec>>,
+ /// The screen lock key works like the per boot key with the distinction that it is cleared
+ /// from memory when the screen lock is engaged.
+ /// TODO the life cycle is not fully implemented at this time.
+ screen_lock: Option<Arc<ZVec>>,
+}
+
+#[derive(Default)]
+struct SkmState {
+ user_keys: HashMap<UserId, UserSuperKeys>,
+ key_index: HashMap<i64, Weak<ZVec>>,
+}
+
+#[derive(Default)]
+pub struct SuperKeyManager {
+ data: Mutex<SkmState>,
+}
+
+impl SuperKeyManager {
+ pub fn new() -> Self {
+ Self { data: Mutex::new(Default::default()) }
+ }
+
+ pub fn forget_screen_lock_key_for_user(&self, user: UserId) {
+ let mut data = self.data.lock().unwrap();
+ if let Some(usk) = data.user_keys.get_mut(&user) {
+ usk.screen_lock = None;
+ }
+ }
+
+ pub fn forget_screen_lock_keys(&self) {
+ let mut data = self.data.lock().unwrap();
+ for (_, usk) in data.user_keys.iter_mut() {
+ usk.screen_lock = None;
+ }
+ }
+
+ pub fn forget_all_keys_for_user(&self, user: UserId) {
+ let mut data = self.data.lock().unwrap();
+ data.user_keys.remove(&user);
+ }
+
+ pub fn forget_all_keys(&self) {
+ let mut data = self.data.lock().unwrap();
+ data.user_keys.clear();
+ data.key_index.clear();
+ }
+
+ fn install_per_boot_key_for_user(&self, user: UserId, key_id: i64, key: ZVec) {
+ let mut data = self.data.lock().unwrap();
+ let key = Arc::new(key);
+ data.key_index.insert(key_id, Arc::downgrade(&key));
+ data.user_keys.entry(user).or_default().per_boot = Some(key);
+ }
+
+ fn get_key(&self, key_id: &i64) -> Option<Arc<ZVec>> {
+ self.data.lock().unwrap().key_index.get(key_id).and_then(|k| k.upgrade())
+ }
+
+ pub fn get_per_boot_key_by_user_id(&self, user_id: u32) -> Option<Arc<ZVec>> {
+ let data = self.data.lock().unwrap();
+ data.user_keys.get(&user_id).map(|e| e.per_boot.clone()).flatten()
+ }
+
+ /// This function unlocks the super keys for a given user.
+ /// This means the key is loaded from the database, decrypted and placed in the
+ /// super key cache. If there is no such key a new key is created, encrypted with
+ /// a key derived from the given password and stored in the database.
+ pub fn unlock_user_key(
+ &self,
+ 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", || {
+ // 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)
+ .context("In create_new_key: Failed to derive password.")?;
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::EncryptedBy(EncryptedBy::Password));
+ metadata.add(KeyMetaEntry::Salt(salt));
+ let (encrypted_key, iv, tag) = aes_gcm_encrypt(&super_key, &derived_key)
+ .context("In create_new_key: Failed to encrypt new super key.")?;
+ metadata.add(KeyMetaEntry::Iv(iv));
+ metadata.add(KeyMetaEntry::AeadTag(tag));
+ Ok((encrypted_key, metadata))
+ })
+ .context("In unlock_user_key: Failed to get key id.")?;
+
+ let metadata = entry.metadata();
+ let super_key = match (
+ metadata.encrypted_by(),
+ metadata.salt(),
+ metadata.iv(),
+ metadata.aead_tag(),
+ entry.km_blob(),
+ ) {
+ (Some(&EncryptedBy::Password), Some(salt), Some(iv), Some(tag), Some(blob)) => {
+ let key = derive_key_from_password(pw, Some(salt), AES_256_KEY_LENGTH)
+ .context("In unlock_user_key: Failed to generate key from password.")?;
+
+ aes_gcm_decrypt(blob, iv, tag, &key)
+ .context("In unlock_user_key: Failed to decrypt key blob.")?
+ }
+ (enc_by, salt, iv, tag, blob) => {
+ return Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
+ concat!(
+ "In unlock_user_key: Super key has incomplete metadata.",
+ "Present: encrypted_by: {}, salt: {}, iv: {}, aead_tag: {}, blob: {}."
+ ),
+ enc_by.is_some(),
+ salt.is_some(),
+ iv.is_some(),
+ tag.is_some(),
+ blob.is_some()
+ ));
+ }
+ };
+
+ self.install_per_boot_key_for_user(user, entry.id(), super_key);
+
+ Ok(())
+ }
+
+ /// Unwraps an encrypted key blob given metadata identifying the encryption key.
+ /// The function queries `metadata.encrypted_by()` to determine the encryption key.
+ /// It then check if the required key is memory resident, and if so decrypts the
+ /// blob.
+ pub fn unwrap_key(&self, blob: &[u8], metadata: &KeyMetaData) -> Result<ZVec> {
+ match metadata.encrypted_by() {
+ Some(EncryptedBy::KeyId(key_id)) => match self.get_key(key_id) {
+ Some(key) => {
+ Self::unwrap_key_with_key(blob, metadata, &key).context("In unwrap_key.")
+ }
+ None => Err(Error::Rc(ResponseCode::LOCKED))
+ .context("In unwrap_key: Key is not usable until the user entered their LSKF."),
+ },
+ _ => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED))
+ .context("In unwrap_key: Cannot determined wrapping key."),
+ }
+ }
+
+ /// Unwraps an encrypted key blob given an encryption key.
+ fn unwrap_key_with_key(blob: &[u8], metadata: &KeyMetaData, key: &[u8]) -> Result<ZVec> {
+ match (metadata.iv(), metadata.aead_tag()) {
+ (Some(iv), Some(tag)) => aes_gcm_decrypt(blob, iv, tag, key)
+ .context("In unwrap_key_with_key: Failed to decrypt the key blob."),
+ (iv, tag) => Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(format!(
+ concat!(
+ "In unwrap_key_with_key: Key has incomplete metadata.",
+ "Present: iv: {}, aead_tag: {}."
+ ),
+ iv.is_some(),
+ tag.is_some(),
+ )),
+ }
+ }
+}
diff --git a/keystore2/src/test/utils.rs b/keystore2/src/test/utils.rs
new file mode 100644
index 0000000..8c93859
--- /dev/null
+++ b/keystore2/src/test/utils.rs
@@ -0,0 +1,84 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::fs::{create_dir, remove_dir_all};
+use std::io::ErrorKind;
+use std::path::{Path, PathBuf};
+use std::{env::temp_dir, ops::Deref};
+
+#[derive(Debug)]
+pub struct TempDir {
+ path: std::path::PathBuf,
+ do_drop: bool,
+}
+
+impl TempDir {
+ pub fn new(prefix: &str) -> std::io::Result<Self> {
+ let tmp = loop {
+ let mut tmp = temp_dir();
+ let number: u16 = rand::random();
+ tmp.push(format!("{}_{:05}", prefix, number));
+ match create_dir(&tmp) {
+ Err(e) => match e.kind() {
+ ErrorKind::AlreadyExists => continue,
+ _ => return Err(e),
+ },
+ Ok(()) => break tmp,
+ }
+ };
+ Ok(Self { path: tmp, do_drop: true })
+ }
+
+ pub fn path(&self) -> &Path {
+ &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)]
+ pub fn do_not_drop(&mut self) {
+ println!("Disabled automatic cleanup for: {:?}", self.path);
+ log::info!("Disabled automatic cleanup for: {:?}", self.path);
+ self.do_drop = false;
+ }
+}
+
+impl Drop for TempDir {
+ fn drop(&mut self) {
+ if self.do_drop {
+ remove_dir_all(&self.path).expect("Cannot delete temporary dir.");
+ }
+ }
+}
+
+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
new file mode 100644
index 0000000..eab9b4d
--- /dev/null
+++ b/keystore2/src/utils.rs
@@ -0,0 +1,190 @@
+// 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 suppresses the compiler's complaint about converting tv_sec to i64 in method
+// get_current_time_in_seconds.
+#![allow(clippy::useless_conversion)]
+
+//! 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 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;
+
+/// This function uses its namesake in the permission module and in
+/// combination with with_calling_sid from the binder crate to check
+/// if the caller has the given keystore permission.
+pub fn check_keystore_permission(perm: KeystorePerm) -> anyhow::Result<()> {
+ ThreadState::with_calling_sid(|calling_sid| {
+ permission::check_keystore_permission(
+ &calling_sid.ok_or_else(Error::sys).context(
+ "In check_keystore_permission: Cannot check permission without calling_sid.",
+ )?,
+ perm,
+ )
+ })
+}
+
+/// This function uses its namesake in the permission module and in
+/// combination with with_calling_sid from the binder crate to check
+/// if the caller has the given grant permission.
+pub fn check_grant_permission(access_vec: KeyPermSet, key: &KeyDescriptor) -> anyhow::Result<()> {
+ ThreadState::with_calling_sid(|calling_sid| {
+ permission::check_grant_permission(
+ &calling_sid.ok_or_else(Error::sys).context(
+ "In check_grant_permission: Cannot check permission without calling_sid.",
+ )?,
+ access_vec,
+ key,
+ )
+ })
+}
+
+/// This function uses its namesake in the permission module and in
+/// combination with with_calling_sid from the binder crate to check
+/// if the caller has the given key permission.
+pub fn check_key_permission(
+ perm: KeyPerm,
+ key: &KeyDescriptor,
+ access_vector: &Option<KeyPermSet>,
+) -> anyhow::Result<()> {
+ ThreadState::with_calling_sid(|calling_sid| {
+ permission::check_key_permission(
+ &calling_sid
+ .ok_or_else(Error::sys)
+ .context("In check_key_permission: Cannot check permission without calling_sid.")?,
+ perm,
+ key,
+ access_vector,
+ )
+ })
+}
+
+/// Thread safe wrapper around SpIBinder. It is safe to have SpIBinder smart pointers to the
+/// same object in multiple threads, but cloning a SpIBinder is not thread safe.
+/// Keystore frequently hands out binder tokens to the security level interface. If this
+/// is to happen from a multi threaded thread pool, the SpIBinder needs to be protected by a
+/// Mutex.
+#[derive(Debug)]
+pub struct Asp(Mutex<SpIBinder>);
+
+impl Asp {
+ /// Creates a new instance owning a SpIBinder wrapped in a Mutex.
+ pub fn new(i: SpIBinder) -> Self {
+ Self(Mutex::new(i))
+ }
+
+ /// Clones the owned SpIBinder and attempts to convert it into the requested interface.
+ pub fn get_interface<T: FromIBinder + ?Sized>(&self) -> anyhow::Result<Box<T>> {
+ // We can use unwrap here because we never panic when locked, so the mutex
+ // can never be poisoned.
+ let lock = self.0.lock().unwrap();
+ (*lock)
+ .clone()
+ .into_interface()
+ .map_err(|e| anyhow!(format!("get_interface failed with error code {:?}", e)))
+ }
+}
+
+/// 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
+/// parameters found in the hardware enforced parameter list.
+pub fn key_characteristics_to_internal(
+ key_characteristics: KeyCharacteristics,
+ hw_security_level: SecurityLevel,
+) -> Vec<crate::key_parameter::KeyParameter> {
+ key_characteristics
+ .hardwareEnforced
+ .into_iter()
+ .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(aidl_kp.into(), SecurityLevel::SOFTWARE)
+ }))
+ .collect()
+}
+
+/// Converts a set of key characteristics from the internal representation into a set of
+/// Authorizations as they are used to convey key characteristics to the clients of keystore.
+pub fn key_parameters_to_authorizations(
+ parameters: Vec<crate::key_parameter::KeyParameter>,
+) -> Vec<Authorization> {
+ parameters.into_iter().map(|p| p.into_authorization()).collect()
+}
+
+/// This returns the current time (in seconds) as an instance of a monotonic clock, by invoking the
+/// system call since Rust does not support getting monotonic time instance as an integer.
+pub fn get_current_time_in_seconds() -> i64 {
+ let mut current_time = libc::timespec { tv_sec: 0, tv_nsec: 0 };
+ // Following unsafe block includes one system call to get monotonic time.
+ // Therefore, it is not considered harmful.
+ unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_RAW, &mut current_time) };
+ // It is safe to unwrap here because try_from() returns std::convert::Infallible, which is
+ // defined to be an error that can never happen (i.e. the result is always ok).
+ 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;
+
+/// Extracts the android user from the given uid.
+pub fn uid_to_android_user(uid: u32) -> u32 {
+ uid / AID_USER_OFFSET
+}
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
new file mode 100644
index 0000000..8da28f2
--- /dev/null
+++ b/ondevice-signing/Android.bp
@@ -0,0 +1,102 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// List of clang-tidy checks that are reported as errors.
+// Please keep this list ordered lexicographically.
+
+tidy_errors = [
+ "cert-err34-c",
+ "google-default-arguments",
+ "google-explicit-constructor",
+ "google-runtime-int",
+ "google-runtime-member-string-references",
+ "misc-move-const-arg",
+ "misc-move-forwarding-reference",
+ "misc-unused-parameters",
+ "misc-unused-using-decls",
+ "misc-use-after-move",
+ "modernize-pass-by-value",
+ "performance-faster-string-find",
+ "performance-for-range-copy",
+ "performance-implicit-conversion-in-loop",
+ "performance-inefficient-vector-operation",
+ "performance-move-const-arg",
+ "performance-move-constructor-init",
+ "performance-noexcept-move-constructor",
+ "performance-unnecessary-value-param",
+]
+
+cc_defaults {
+ cpp_std: "experimental",
+ name: "odsign_flags_defaults",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wno-unused-parameter",
+
+ // Some extra flags.
+ "-fstrict-aliasing",
+ "-Wredundant-decls",
+ "-Wshadow",
+ "-Wstrict-aliasing",
+ "-Wthread-safety",
+ "-Wthread-safety-negative",
+ "-Wunreachable-code",
+ "-Wunreachable-code-break",
+ "-Wunreachable-code-return",
+ "-Wunused",
+ "-Wused-but-marked-unused",
+ ],
+ tidy: true,
+ tidy_checks: tidy_errors,
+ tidy_checks_as_errors: tidy_errors,
+ tidy_flags: [
+ "-format-style='file'",
+ ],
+}
+
+cc_binary {
+ name: "odsign",
+ defaults: [
+ "odsign_flags_defaults",
+ ],
+ cpp_std: "experimental",
+ init_rc: ["odsign.rc"],
+ srcs: [
+ "odsign_main.cpp",
+ "CertUtils.cpp",
+ "Keymaster.cpp",
+ "KeymasterSigningKey.cpp",
+ "VerityUtils.cpp",
+ ],
+
+ static_libs: [
+ "libmini_keyctl_static", // TODO need static?
+ "libc++fs",
+ ],
+
+ shared_libs: [
+ "android.hardware.keymaster@4.1",
+ "libbase",
+ "libcrypto",
+ "libcrypto_utils",
+ "libfsverity",
+ "libhidlbase",
+ "liblogwrap",
+ "libkeymaster4support", // For authorization_set
+ "libkeymaster4_1support",
+ "libkeyutils",
+ "libutils",
+ ],
+}
diff --git a/ondevice-signing/CertUtils.cpp b/ondevice-signing/CertUtils.cpp
new file mode 100644
index 0000000..6b24391
--- /dev/null
+++ b/ondevice-signing/CertUtils.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/result.h>
+
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/pkcs7.h>
+#include <openssl/rsa.h>
+#include <openssl/x509v3.h>
+
+#include <fcntl.h>
+#include <vector>
+const char kBasicConstraints[] = "CA:TRUE";
+const char kKeyUsage[] = "critical,keyCertSign,cRLSign,digitalSignature";
+const char kSubjectKeyIdentifier[] = "hash";
+constexpr int kCertLifetimeSeconds = 10 * 365 * 24 * 60 * 60;
+
+using android::base::Result;
+// using android::base::ErrnoError;
+using android::base::Error;
+
+static bool add_ext(X509* cert, int nid, const char* value) {
+ size_t len = strlen(value) + 1;
+ std::vector<char> mutableValue(value, value + len);
+ X509V3_CTX context;
+
+ X509V3_set_ctx_nodb(&context);
+
+ X509V3_set_ctx(&context, cert, cert, nullptr, nullptr, 0);
+ X509_EXTENSION* ex = X509V3_EXT_nconf_nid(nullptr, &context, nid, mutableValue.data());
+ if (!ex) {
+ return false;
+ }
+
+ X509_add_ext(cert, ex, -1);
+ X509_EXTENSION_free(ex);
+ return true;
+}
+
+Result<void> createSelfSignedCertificate(
+ const std::vector<uint8_t>& publicKey,
+ const std::function<Result<std::string>(const std::string&)>& signFunction,
+ const std::string& path) {
+ bssl::UniquePtr<X509> x509(X509_new());
+ if (!x509) {
+ return Error() << "Unable to allocate x509 container";
+ }
+ X509_set_version(x509.get(), 2);
+
+ ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 1);
+ X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
+ X509_gmtime_adj(X509_get_notAfter(x509.get()), kCertLifetimeSeconds);
+
+ auto pubKeyData = publicKey.data();
+ EVP_PKEY* public_key = d2i_PUBKEY(nullptr, &pubKeyData, publicKey.size());
+ if (!X509_set_pubkey(x509.get(), public_key)) {
+ return Error() << "Unable to set x509 public key";
+ }
+
+ X509_NAME* name = X509_get_subject_name(x509.get());
+ if (!name) {
+ return Error() << "Unable to get x509 subject name";
+ }
+ X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("ODS"), -1, -1, 0);
+ if (!X509_set_issuer_name(x509.get(), name)) {
+ return Error() << "Unable to set x509 issuer name";
+ }
+
+ add_ext(x509.get(), NID_basic_constraints, kBasicConstraints);
+ add_ext(x509.get(), NID_key_usage, kKeyUsage);
+ add_ext(x509.get(), NID_subject_key_identifier, kSubjectKeyIdentifier);
+ add_ext(x509.get(), NID_authority_key_identifier, "keyid:always");
+
+ X509_ALGOR_set0(x509->cert_info->signature, OBJ_nid2obj(NID_sha256WithRSAEncryption),
+ V_ASN1_NULL, NULL);
+ X509_ALGOR_set0(x509->sig_alg, OBJ_nid2obj(NID_sha256WithRSAEncryption), V_ASN1_NULL, NULL);
+
+ // Get the data to be signed
+ char* to_be_signed_buf(nullptr);
+ size_t to_be_signed_length = i2d_re_X509_tbs(x509.get(), (unsigned char**)&to_be_signed_buf);
+
+ auto signed_data = signFunction(std::string(to_be_signed_buf, to_be_signed_length));
+ if (!signed_data.ok()) {
+ return signed_data.error();
+ }
+
+ // This is the only part that doesn't use boringssl default functions - we manually copy in the
+ // signature that was provided to us.
+ x509->signature->data = (unsigned char*)OPENSSL_malloc(signed_data->size());
+ memcpy(x509->signature->data, signed_data->c_str(), signed_data->size());
+ x509->signature->length = signed_data->size();
+
+ x509->signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+ x509->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+ auto f = fopen(path.c_str(), "wb");
+ // TODO error checking
+ i2d_X509_fp(f, x509.get());
+ fclose(f);
+
+ return {};
+}
+
+Result<std::vector<uint8_t>> extractPublicKey(EVP_PKEY* pkey) {
+ if (pkey == nullptr) {
+ return Error() << "Failed to extract public key from x509 cert";
+ }
+
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
+ return Error() << "The public key is not an RSA key";
+ }
+
+ RSA* rsa = EVP_PKEY_get1_RSA(pkey);
+ auto num_bytes = BN_num_bytes(rsa->n);
+ std::vector<uint8_t> pubKey(num_bytes);
+ int res = BN_bn2bin(rsa->n, pubKey.data());
+ RSA_free(rsa);
+
+ if (!res) {
+ return Error() << "Failed to convert public key to bytes";
+ }
+
+ return pubKey;
+}
+
+Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::vector<uint8_t>& keyData) {
+ auto keyDataBytes = keyData.data();
+ EVP_PKEY* public_key = d2i_PUBKEY(nullptr, &keyDataBytes, keyData.size());
+
+ return extractPublicKey(public_key);
+}
+
+Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path) {
+ X509* cert;
+ auto f = fopen(path.c_str(), "r");
+ if (!d2i_X509_fp(f, &cert)) {
+ return Error() << "Unable to decode x509 cert at " << path;
+ }
+
+ fclose(f);
+ return extractPublicKey(X509_get_pubkey(cert));
+}
+
+Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signed_digest) {
+ CBB out, outer_seq, wrapped_seq, seq, digest_algos_set, digest_algo, null;
+ CBB content_info, issuer_and_serial, signer_infos, signer_info, sign_algo, signature;
+ uint8_t *pkcs7_data, *name_der;
+ size_t pkcs7_data_len, name_der_len;
+ BIGNUM* serial = BN_new();
+ int sig_nid = NID_rsaEncryption;
+
+ X509_NAME* name = X509_NAME_new();
+ if (!name) {
+ return Error() << "Unable to get x509 subject name";
+ }
+ X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("ODS"), -1, -1, 0);
+
+ BN_set_word(serial, 1);
+ name_der_len = i2d_X509_NAME(name, &name_der);
+ CBB_init(&out, 1024);
+
+ if (!CBB_add_asn1(&out, &outer_seq, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&outer_seq, NID_pkcs7_signed) ||
+ !CBB_add_asn1(&outer_seq, &wrapped_seq,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
+ // See https://tools.ietf.org/html/rfc2315#section-9.1
+ !CBB_add_asn1(&wrapped_seq, &seq, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1_uint64(&seq, 1 /* version */) ||
+ !CBB_add_asn1(&seq, &digest_algos_set, CBS_ASN1_SET) ||
+ !CBB_add_asn1(&digest_algos_set, &digest_algo, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&digest_algo, NID_sha256) ||
+ !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
+ !CBB_add_asn1(&seq, &content_info, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&content_info, NID_pkcs7_data) ||
+ !CBB_add_asn1(&seq, &signer_infos, CBS_ASN1_SET) ||
+ !CBB_add_asn1(&signer_infos, &signer_info, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1_uint64(&signer_info, 1 /* version */) ||
+ !CBB_add_asn1(&signer_info, &issuer_and_serial, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_bytes(&issuer_and_serial, name_der, name_der_len) ||
+ !BN_marshal_asn1(&issuer_and_serial, serial) ||
+ !CBB_add_asn1(&signer_info, &digest_algo, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&digest_algo, NID_sha256) ||
+ !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
+ !CBB_add_asn1(&signer_info, &sign_algo, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&sign_algo, sig_nid) || !CBB_add_asn1(&sign_algo, &null, CBS_ASN1_NULL) ||
+ !CBB_add_asn1(&signer_info, &signature, CBS_ASN1_OCTETSTRING) ||
+ !CBB_add_bytes(&signature, signed_digest.data(), signed_digest.size()) ||
+ !CBB_finish(&out, &pkcs7_data, &pkcs7_data_len)) {
+ return Error() << "Failed to create PKCS7 certificate.";
+ }
+
+ return std::vector<uint8_t>(&pkcs7_data[0], &pkcs7_data[pkcs7_data_len]);
+}
diff --git a/ondevice-signing/CertUtils.h b/ondevice-signing/CertUtils.h
new file mode 100644
index 0000000..d9172d0
--- /dev/null
+++ b/ondevice-signing/CertUtils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/result.h>
+
+android::base::Result<void> createSelfSignedCertificate(
+ const std::vector<uint8_t>& publicKey,
+ const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
+ const std::string& path);
+android::base::Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signedData);
+
+android::base::Result<std::vector<uint8_t>>
+extractPublicKeyFromX509(const std::vector<uint8_t>& path);
+android::base::Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path);
diff --git a/ondevice-signing/Keymaster.cpp b/ondevice-signing/Keymaster.cpp
new file mode 100644
index 0000000..d43828a
--- /dev/null
+++ b/ondevice-signing/Keymaster.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <keymasterV4_1/Keymaster.h>
+#include <keymasterV4_1/authorization_set.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "Keymaster.h"
+
+using AuthorizationSet = ::android::hardware::keymaster::V4_0::AuthorizationSet;
+using AuthorizationSetBuilder = ::android::hardware::keymaster::V4_0::AuthorizationSetBuilder;
+using Digest = ::android::hardware::keymaster::V4_0::Digest;
+using ErrorCode = ::android::hardware::keymaster::V4_0::ErrorCode;
+using HardwareAuthToken = ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+using HidlBuf = ::android::hardware::hidl_vec<uint8_t>;
+using KeyCharacteristics = ::android::hardware::keymaster::V4_0::KeyCharacteristics;
+using KeyFormat = ::android::hardware::keymaster::V4_0::KeyFormat;
+using KeyParameter = ::android::hardware::keymaster::V4_0::KeyParameter;
+using KeyPurpose = ::android::hardware::keymaster::V4_0::KeyPurpose;
+using KmSupport = ::android::hardware::keymaster::V4_1::support::Keymaster;
+using KmDevice = ::android::hardware::keymaster::V4_1::IKeymasterDevice;
+using OperationHandle = ::android::hardware::keymaster::V4_0::OperationHandle;
+using PaddingMode = ::android::hardware::keymaster::V4_0::PaddingMode;
+using VerificationToken = ::android::hardware::keymaster::V4_0::VerificationToken;
+
+using android::sp;
+using android::base::Error;
+using android::base::Result;
+using android::base::unique_fd;
+using android::hardware::hidl_vec;
+
+Keymaster::Keymaster() {}
+
+bool Keymaster::initialize() {
+ // TODO(b/165630556): Stop using Keymaster directly and migrate to keystore2
+ // (once available).
+ auto devices = KmSupport::enumerateAvailableDevices();
+ sp<KmDevice> devToUse = nullptr;
+ for (const auto& dev : devices) {
+ auto version = dev->halVersion();
+ if (version.majorVersion > 4 || (version.majorVersion == 4 && version.minorVersion >= 1)) {
+ // TODO we probably have a preference for the SE, hoping Keystore2 will provide this
+ LOG(INFO) << "Using keymaster " << version.keymasterName << " "
+ << (int)version.majorVersion << "." << (int)version.minorVersion;
+ devToUse = dev;
+ break;
+ }
+ }
+
+ mDevice = devToUse;
+
+ return true;
+}
+
+std::optional<Keymaster> Keymaster::getInstance() {
+ static Keymaster keymaster;
+
+ if (!keymaster.initialize()) {
+ return {};
+ } else {
+ return {keymaster};
+ }
+}
+
+Result<std::vector<uint8_t>> Keymaster::createKey() const {
+ ErrorCode error;
+ HidlBuf keyBlob;
+
+ auto params = AuthorizationSetBuilder()
+ .Authorization(::android::hardware::keymaster::V4_0::TAG_NO_AUTH_REQUIRED)
+ // TODO MAKE SURE WE ADD THE EARLY_BOOT_ONLY FLAG here
+ // currently doesn't work on cuttlefish (b/173618442)
+ //.Authorization(::android::hardware::keymaster::V4_1::TAG_EARLY_BOOT_ONLY)
+ .RsaSigningKey(2048, 65537)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
+
+ mDevice->generateKey(params.hidl_data(), [&](ErrorCode hidl_error, const HidlBuf& hidl_key_blob,
+ const KeyCharacteristics&
+ /* hidl_key_characteristics */) {
+ error = hidl_error;
+ keyBlob = hidl_key_blob;
+ });
+
+ if (error != ErrorCode::OK) {
+ return Error() << "Error creating keymaster signing key: "
+ << static_cast<std::underlying_type<ErrorCode>::type>(error);
+ }
+
+ return keyBlob;
+}
+
+static ErrorCode Begin(const sp<KmDevice>& keymaster_, KeyPurpose purpose, const HidlBuf& key_blob,
+ const AuthorizationSet& in_params, AuthorizationSet* out_params,
+ OperationHandle* op_handle) {
+ ErrorCode error;
+ OperationHandle saved_handle = *op_handle;
+ CHECK(keymaster_
+ ->begin(purpose, key_blob, in_params.hidl_data(), HardwareAuthToken(),
+ [&](ErrorCode hidl_error, const hidl_vec<KeyParameter>& hidl_out_params,
+ uint64_t hidl_op_handle) {
+ error = hidl_error;
+ *out_params = hidl_out_params;
+ *op_handle = hidl_op_handle;
+ })
+ .isOk());
+ if (error != ErrorCode::OK) {
+ // Some implementations may modify *op_handle on error.
+ *op_handle = saved_handle;
+ }
+ return error;
+}
+
+static ErrorCode Update(const sp<KmDevice>& keymaster_, OperationHandle op_handle,
+ const AuthorizationSet& in_params, const std::string& input,
+ AuthorizationSet* out_params, std::string* output, size_t* input_consumed) {
+ ErrorCode error;
+ HidlBuf inputData(input.size());
+ memcpy(inputData.data(), input.c_str(), input.size());
+ CHECK(keymaster_
+ ->update(op_handle, in_params.hidl_data(), inputData, HardwareAuthToken(),
+ VerificationToken(),
+ [&](ErrorCode hidl_error, uint32_t hidl_input_consumed,
+ const hidl_vec<KeyParameter>& hidl_out_params,
+ const HidlBuf& hidl_output) {
+ error = hidl_error;
+ out_params->push_back(AuthorizationSet(hidl_out_params));
+ std::string retdata(reinterpret_cast<const char*>(hidl_output.data()),
+ hidl_output.size());
+ output->append(retdata);
+ *input_consumed = hidl_input_consumed;
+ })
+ .isOk());
+ return error;
+}
+
+static ErrorCode Finish(const sp<KmDevice>& keymaster_, OperationHandle op_handle,
+ const AuthorizationSet& in_params, const std::string& input,
+ const std::string& signature, AuthorizationSet* out_params,
+ std::string* output) {
+ ErrorCode error;
+ HidlBuf inputData(input.size());
+ memcpy(inputData.data(), input.c_str(), input.size());
+ HidlBuf signatureData(signature.size());
+ memcpy(signatureData.data(), signature.c_str(), signature.size());
+ // TODO still need to handle error -62 - key requires upgrade
+ CHECK(keymaster_
+ ->finish(op_handle, in_params.hidl_data(), inputData, signatureData,
+ HardwareAuthToken(), VerificationToken(),
+ [&](ErrorCode hidl_error, const hidl_vec<KeyParameter>& hidl_out_params,
+ const HidlBuf& hidl_output) {
+ error = hidl_error;
+ *out_params = hidl_out_params;
+ std::string retdata(reinterpret_cast<const char*>(hidl_output.data()),
+ hidl_output.size());
+ output->append(retdata);
+ })
+ .isOk());
+ return error;
+}
+
+static std::string ProcessMessage(const sp<KmDevice>& keymaster_, const HidlBuf& key_blob,
+ KeyPurpose operation, const std::string& message,
+ const AuthorizationSet& in_params, AuthorizationSet* out_params) {
+ AuthorizationSet begin_out_params;
+ OperationHandle op_handle_;
+ ErrorCode ec =
+ Begin(keymaster_, operation, key_blob, in_params, &begin_out_params, &op_handle_);
+
+ std::string output;
+ size_t consumed = 0;
+ AuthorizationSet update_params;
+ AuthorizationSet update_out_params;
+ ec = Update(keymaster_, op_handle_, update_params, message, &update_out_params, &output,
+ &consumed);
+
+ std::string unused;
+ AuthorizationSet finish_params;
+ AuthorizationSet finish_out_params;
+ ec = Finish(keymaster_, op_handle_, finish_params, message.substr(consumed), unused,
+ &finish_out_params, &output);
+
+ out_params->push_back(begin_out_params);
+ out_params->push_back(finish_out_params);
+ return output;
+}
+
+Result<std::vector<uint8_t>>
+Keymaster::extractPublicKey(const std::vector<uint8_t>& keyBlob) const {
+ std::vector<uint8_t> publicKey;
+ ErrorCode error;
+
+ mDevice->exportKey(KeyFormat::X509, keyBlob, {} /* clientId */, {} /* appData */,
+ [&](ErrorCode hidl_error, const HidlBuf& keyData) {
+ error = hidl_error;
+ publicKey = keyData;
+ });
+
+ if (error != ErrorCode::OK) {
+ return Error() << "Error extracting public key: "
+ << static_cast<std::underlying_type<ErrorCode>::type>(error);
+ }
+
+ return publicKey;
+}
+
+Result<KeymasterVerifyResult> Keymaster::verifyKey(const std::vector<uint8_t>& keyBlob) const {
+ ErrorCode error;
+ KeyCharacteristics characteristics;
+
+ mDevice->getKeyCharacteristics(
+ keyBlob, {} /* clientId */, {} /* appData */,
+ [&](ErrorCode hidl_error, const KeyCharacteristics& hidl_characteristics) {
+ error = hidl_error;
+ characteristics = hidl_characteristics;
+ });
+
+ if (error == ErrorCode::KEY_REQUIRES_UPGRADE) {
+ return KeymasterVerifyResult::UPGRADE;
+ }
+
+ if (error != ErrorCode::OK) {
+ return Error() << "Error getting key characteristics: "
+ << static_cast<std::underlying_type<ErrorCode>::type>(error);
+ }
+
+ // TODO(b/165630556)
+ // Verify this is an early boot key and the other key parameters
+ return KeymasterVerifyResult::OK;
+}
+
+Result<std::vector<uint8_t>> Keymaster::upgradeKey(const std::vector<uint8_t>& keyBlob) const {
+ ErrorCode error;
+ HidlBuf newKeyBlob;
+
+ // TODO deduplicate
+ auto params = AuthorizationSetBuilder()
+ .Authorization(::android::hardware::keymaster::V4_0::TAG_NO_AUTH_REQUIRED)
+ // TODO MAKE SURE WE ADD THE EARLY_BOOT_ONLY FLAG here
+ // currently doesn't work on cuttlefish (b/173618442)
+ //.Authorization(::android::hardware::keymaster::V4_1::TAG_EARLY_BOOT_ONLY)
+ .RsaSigningKey(2048, 65537)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
+
+ mDevice->upgradeKey(keyBlob, params.hidl_data(),
+ [&](ErrorCode hidl_error, const HidlBuf& hidl_key_blob) {
+ error = hidl_error;
+ newKeyBlob = hidl_key_blob;
+ });
+
+ if (error != ErrorCode::OK) {
+ return Error() << "Error upgrading keymaster signing key: "
+ << static_cast<std::underlying_type<ErrorCode>::type>(error);
+ }
+
+ return newKeyBlob;
+}
+
+Result<std::string> Keymaster::sign(const std::vector<uint8_t>& keyBlob,
+ const std::string& message) const {
+ AuthorizationSet out_params;
+ auto params = AuthorizationSetBuilder()
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN);
+ std::string signature =
+ ProcessMessage(mDevice, keyBlob, KeyPurpose::SIGN, message, params, &out_params);
+ if (!out_params.empty()) {
+ return Error() << "Error signing key: expected empty out params.";
+ }
+ return signature;
+}
diff --git a/ondevice-signing/Keymaster.h b/ondevice-signing/Keymaster.h
new file mode 100644
index 0000000..455289f
--- /dev/null
+++ b/ondevice-signing/Keymaster.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include <android-base/macros.h>
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+
+#include <keymasterV4_1/Keymaster.h>
+
+#include <utils/StrongPointer.h>
+
+enum class KeymasterVerifyResult {
+ OK = 0,
+ UPGRADE = -1,
+};
+
+class Keymaster {
+ using KmDevice = ::android::hardware::keymaster::V4_1::IKeymasterDevice;
+
+ public:
+ static std::optional<Keymaster> getInstance();
+
+ android::base::Result<std::vector<uint8_t>> createKey() const;
+
+ android::base::Result<std::vector<uint8_t>>
+ extractPublicKey(const std::vector<uint8_t>& keyBlob) const;
+
+ android::base::Result<KeymasterVerifyResult>
+ verifyKey(const std::vector<uint8_t>& keyBlob) const;
+
+ android::base::Result<std::vector<uint8_t>>
+ upgradeKey(const std::vector<uint8_t>& keyBlob) const;
+
+ /* Sign a message with an initialized signing key */
+ android::base::Result<std::string> sign(const std::vector<uint8_t>& keyBlob,
+ const std::string& message) const;
+
+ private:
+ Keymaster();
+ bool initialize();
+
+ android::sp<KmDevice> mDevice;
+};
diff --git a/ondevice-signing/KeymasterSigningKey.cpp b/ondevice-signing/KeymasterSigningKey.cpp
new file mode 100644
index 0000000..f35f92b
--- /dev/null
+++ b/ondevice-signing/KeymasterSigningKey.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "CertUtils.h"
+#include "Keymaster.h"
+#include "KeymasterSigningKey.h"
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::ReadFileToString;
+using android::base::Result;
+using android::base::unique_fd;
+
+KeymasterSigningKey::KeymasterSigningKey() {}
+
+Result<KeymasterSigningKey> KeymasterSigningKey::loadFromBlobAndVerify(const std::string& path) {
+ KeymasterSigningKey signingKey;
+
+ auto status = signingKey.initializeFromKeyblob(path);
+
+ if (!status.ok()) {
+ return status.error();
+ }
+
+ return std::move(signingKey);
+}
+
+Result<KeymasterSigningKey> KeymasterSigningKey::createNewKey() {
+ KeymasterSigningKey signingKey;
+
+ auto status = signingKey.createSigningKey();
+
+ if (!status.ok()) {
+ return status.error();
+ }
+
+ return std::move(signingKey);
+}
+
+Result<void> KeymasterSigningKey::createSigningKey() {
+ KeymasterSigningKey signingKey;
+ mKeymaster = Keymaster::getInstance();
+
+ auto keyBlob = mKeymaster->createKey();
+
+ if (!keyBlob.ok()) {
+ return keyBlob.error();
+ }
+
+ mVerifiedKeyBlob.assign(keyBlob->begin(), keyBlob->end());
+
+ return {};
+}
+
+Result<void> KeymasterSigningKey::saveKeyblob(const std::string& path) const {
+ int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, 0600)));
+ if (fd == -1) {
+ return ErrnoError() << "Error creating key blob file " << path;
+ }
+
+ if (!android::base::WriteFully(fd, mVerifiedKeyBlob.data(), mVerifiedKeyBlob.size())) {
+ return ErrnoError() << "Error writing key blob file " << path;
+ } else {
+ return {};
+ }
+}
+
+Result<std::vector<uint8_t>> KeymasterSigningKey::getPublicKey() const {
+ auto publicKeyX509 = mKeymaster->extractPublicKey(mVerifiedKeyBlob);
+ if (!publicKeyX509.ok()) {
+ return publicKeyX509.error();
+ }
+ return extractPublicKeyFromX509(publicKeyX509.value());
+}
+
+Result<void> KeymasterSigningKey::createX509Cert(const std::string& outPath) const {
+ auto publicKey = mKeymaster->extractPublicKey(mVerifiedKeyBlob);
+
+ if (!publicKey.ok()) {
+ return publicKey.error();
+ }
+
+ auto keymasterSignFunction = [&](const std::string& to_be_signed) {
+ return this->sign(to_be_signed);
+ };
+ createSelfSignedCertificate(*publicKey, keymasterSignFunction, outPath);
+ return {};
+}
+
+Result<void> KeymasterSigningKey::initializeFromKeyblob(const std::string& path) {
+ mKeymaster = Keymaster::getInstance();
+ std::string keyBlobData;
+
+ bool result = ReadFileToString(path, &keyBlobData);
+ if (!result) {
+ return ErrnoError() << "Failed to read " << path;
+ }
+
+ std::vector<uint8_t> keyBlob = {keyBlobData.begin(), keyBlobData.end()};
+
+ auto verifyResult = mKeymaster->verifyKey(keyBlob);
+ if (!verifyResult.ok()) {
+ return Error() << "Failed to verify key: " << verifyResult.error().message();
+ }
+
+ if (*verifyResult == KeymasterVerifyResult::UPGRADE) {
+ auto upgradeResult = mKeymaster->upgradeKey(keyBlob);
+ if (!upgradeResult.ok()) {
+ return Error() << "Failed to upgrade key: " << upgradeResult.error().message();
+ }
+ mVerifiedKeyBlob = *upgradeResult;
+ // Make sure we persist the new blob
+ auto saveResult = saveKeyblob(path);
+ if (!saveResult.ok()) {
+ return Error() << "Failed to store upgraded key";
+ }
+ } else {
+ mVerifiedKeyBlob = keyBlob;
+ }
+
+ return {};
+}
+
+Result<std::string> KeymasterSigningKey::sign(const std::string& message) const {
+ return mKeymaster->sign(mVerifiedKeyBlob, message);
+}
diff --git a/ondevice-signing/KeymasterSigningKey.h b/ondevice-signing/KeymasterSigningKey.h
new file mode 100644
index 0000000..7631059
--- /dev/null
+++ b/ondevice-signing/KeymasterSigningKey.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/macros.h>
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+
+#include <utils/StrongPointer.h>
+
+#include "Keymaster.h"
+
+class KeymasterSigningKey {
+ using KmDevice = ::android::hardware::keymaster::V4_1::IKeymasterDevice;
+
+ public:
+ // Allow the key to be moved around
+ KeymasterSigningKey& operator=(KeymasterSigningKey&& other) = default;
+ KeymasterSigningKey(KeymasterSigningKey&& other) = default;
+
+ static android::base::Result<KeymasterSigningKey>
+ loadFromBlobAndVerify(const std::string& path);
+ static android::base::Result<KeymasterSigningKey> createNewKey();
+
+ /* Sign a message with an initialized signing key */
+ android::base::Result<std::string> sign(const std::string& message) const;
+ android::base::Result<void> saveKeyblob(const std::string& path) const;
+ android::base::Result<std::vector<uint8_t>> getPublicKey() const;
+ android::base::Result<void> createX509Cert(const std::string& path) const;
+
+ private:
+ KeymasterSigningKey();
+
+ android::base::Result<void> createSigningKey();
+ android::base::Result<void> initializeFromKeyblob(const std::string& path);
+
+ std::optional<Keymaster> mKeymaster;
+ std::vector<uint8_t> mVerifiedKeyBlob;
+
+ DISALLOW_COPY_AND_ASSIGN(KeymasterSigningKey);
+};
diff --git a/ondevice-signing/OWNERS b/ondevice-signing/OWNERS
new file mode 100644
index 0000000..72a8eb5
--- /dev/null
+++ b/ondevice-signing/OWNERS
@@ -0,0 +1,3 @@
+maco@google.com
+ngeoffray@google.com
+oth@google.com
diff --git a/ondevice-signing/PREUPLOAD.cfg b/ondevice-signing/PREUPLOAD.cfg
new file mode 100644
index 0000000..4c6fbd6
--- /dev/null
+++ b/ondevice-signing/PREUPLOAD.cfg
@@ -0,0 +1,11 @@
+[Hook Scripts]
+checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
+
+[Builtin Hooks]
+clang_format = true
+commit_msg_changeid_field = true
+commit_msg_test_field = true
+gofmt = true
+
+[Builtin Hooks Options]
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
\ No newline at end of file
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
new file mode 100644
index 0000000..579d3d8
--- /dev/null
+++ b/ondevice-signing/VerityUtils.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <filesystem>
+#include <string>
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <libfsverity.h>
+#include <linux/fsverity.h>
+
+#include "CertUtils.h"
+#include "KeymasterSigningKey.h"
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+using android::base::unique_fd;
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define cpu_to_le16(v) ((__force __le16)(uint16_t)(v))
+#define le16_to_cpu(v) ((__force uint16_t)(__le16)(v))
+#else
+#define cpu_to_le16(v) ((__force __le16)__builtin_bswap16(v))
+#define le16_to_cpu(v) (__builtin_bswap16((__force uint16_t)(v)))
+#endif
+
+struct fsverity_signed_digest {
+ char magic[8]; /* must be "FSVerity" */
+ __le16 digest_algorithm;
+ __le16 digest_size;
+ __u8 digest[];
+};
+
+static int read_callback(void* file, void* buf, size_t count) {
+ int* fd = (int*)file;
+ if (TEMP_FAILURE_RETRY(read(*fd, buf, count)) < 0) return errno ? -errno : -EIO;
+ return 0;
+}
+
+static Result<std::vector<uint8_t>> createDigest(const std::string& path) {
+ struct stat filestat;
+ unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+
+ stat(path.c_str(), &filestat);
+ struct libfsverity_merkle_tree_params params = {
+ .version = 1,
+ .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
+ .file_size = static_cast<uint64_t>(filestat.st_size),
+ .block_size = 4096,
+ };
+
+ struct libfsverity_digest* digest;
+ libfsverity_compute_digest(&fd, &read_callback, ¶ms, &digest);
+
+ return std::vector<uint8_t>(&digest->digest[0], &digest->digest[32]);
+}
+
+static Result<std::vector<uint8_t>> signDigest(const KeymasterSigningKey& key,
+ const std::vector<uint8_t>& digest) {
+ struct fsverity_signed_digest* d = NULL;
+ size_t signed_digest_size = sizeof(*d) + digest.size();
+ d = (struct fsverity_signed_digest*)malloc(signed_digest_size);
+
+ memcpy(d->magic, "FSVerity", 8);
+ d->digest_algorithm = cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
+ d->digest_size = cpu_to_le16(digest.size());
+ memcpy(d->digest, digest.data(), digest.size());
+
+ auto signed_digest = key.sign(std::string((char*)d, signed_digest_size));
+ if (!signed_digest.ok()) {
+ return signed_digest.error();
+ }
+
+ return std::vector<uint8_t>(signed_digest->begin(), signed_digest->end());
+}
+
+Result<void> enableFsVerity(const std::string& path, const KeymasterSigningKey& key) {
+ auto digest = createDigest(path);
+ if (!digest.ok()) {
+ return digest.error();
+ }
+
+ auto signed_digest = signDigest(key, digest.value());
+ if (!signed_digest.ok()) {
+ return signed_digest.error();
+ }
+
+ auto pkcs7_data = createPkcs7(signed_digest.value());
+
+ struct fsverity_enable_arg arg = {.version = 1};
+
+ arg.sig_ptr = (uint64_t)pkcs7_data->data();
+ arg.sig_size = pkcs7_data->size();
+ arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
+ arg.block_size = 4096;
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+ int ret = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
+
+ if (ret != 0) {
+ return ErrnoError() << "Failed to call FS_IOC_ENABLE_VERITY on " << path;
+ }
+
+ return {};
+}
+
+Result<void> addFilesToVerityRecursive(const std::string& path, const KeymasterSigningKey& key) {
+ std::error_code ec;
+
+ auto it = std::filesystem::recursive_directory_iterator(path, ec);
+ auto end = std::filesystem::recursive_directory_iterator();
+
+ while (!ec && it != end) {
+ if (it->is_regular_file()) {
+ LOG(INFO) << "Adding " << it->path() << " to fs-verity...";
+ auto result = enableFsVerity(it->path(), key);
+ if (!result.ok()) {
+ return result.error();
+ }
+ }
+ ++it;
+ }
+
+ return {};
+}
+
+Result<bool> isFileInVerity(const std::string& path) {
+ unsigned int flags;
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ return ErrnoError() << "Failed to open " << path;
+ }
+
+ int ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
+ if (ret < 0) {
+ return ErrnoError() << "Failed to FS_IOC_GETFLAGS for " << path;
+ }
+
+ return (flags & FS_VERITY_FL);
+}
+
+Result<void> verifyAllFilesInVerity(const std::string& path) {
+ std::error_code ec;
+
+ auto it = std::filesystem::recursive_directory_iterator(path, ec);
+ auto end = std::filesystem::recursive_directory_iterator();
+
+ while (!ec && it != end) {
+ if (it->is_regular_file()) {
+ // Verify
+ auto result = isFileInVerity(it->path());
+ if (!result.ok()) {
+ return result.error();
+ }
+ if (!*result) {
+ return Error() << "File " << it->path() << " not in fs-verity";
+ }
+ } // TODO reject other types besides dirs?
+ ++it;
+ }
+
+ return {};
+}
diff --git a/ondevice-signing/VerityUtils.h b/ondevice-signing/VerityUtils.h
new file mode 100644
index 0000000..1eca5a6
--- /dev/null
+++ b/ondevice-signing/VerityUtils.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/result.h>
+
+#include "KeymasterSigningKey.h"
+
+android::base::Result<void> verifyAllFilesInVerity(const std::string& path);
+android::base::Result<void> addFilesToVerityRecursive(const std::string& path,
+ const KeymasterSigningKey& key);
diff --git a/ondevice-signing/odsign.rc b/ondevice-signing/odsign.rc
new file mode 100644
index 0000000..044bae7
--- /dev/null
+++ b/ondevice-signing/odsign.rc
@@ -0,0 +1,6 @@
+service odsign /system/bin/odsign
+ class core
+ user root
+ group system
+ oneshot
+ disabled # does not start with the core class
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
new file mode 100644
index 0000000..efe7d35
--- /dev/null
+++ b/ondevice-signing/odsign_main.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <filesystem>
+#include <iomanip>
+#include <iostream>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
+#include <logwrap/logwrap.h>
+
+#include "CertUtils.h"
+#include "KeymasterSigningKey.h"
+#include "VerityUtils.h"
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+
+const std::string kSigningKeyBlob = "/data/misc/odsign/key.blob";
+const std::string kSigningKeyCert = "/data/misc/odsign/key.cert";
+
+const std::string kArtArtifactsDir = "/data/misc/apexdata/com.android.art/system";
+
+static const char* kOdrefreshPath = "/apex/com.android.art/bin/odrefresh";
+
+static const char* kFsVerityInitPath = "/system/bin/fsverity_init";
+
+static const bool kForceCompilation = false;
+
+Result<void> addCertToFsVerityKeyring(const std::string& path) {
+ const char* const argv[] = {kFsVerityInitPath, "--load-extra-key", "fsv_ods"};
+
+ int fd = open(path.c_str(), O_RDONLY);
+ pid_t pid = fork();
+ if (pid == 0) {
+ dup2(fd, STDIN_FILENO);
+ close(fd);
+ int argc = arraysize(argv);
+ char* argv_child[argc + 1];
+ memcpy(argv_child, argv, argc * sizeof(char*));
+ argv_child[argc] = nullptr;
+ execvp(argv_child[0], const_cast<char**>(argv_child));
+ PLOG(ERROR) << "exec in ForkExecvp";
+ _exit(EXIT_FAILURE);
+ } else {
+ close(fd);
+ }
+ if (pid == -1) {
+ return ErrnoError() << "Failed to fork.";
+ }
+ int status;
+ if (waitpid(pid, &status, 0) == -1) {
+ return ErrnoError() << "waitpid() failed.";
+ }
+ if (!WIFEXITED(status)) {
+ return Error() << kFsVerityInitPath << ": abnormal process exit";
+ }
+ if (WEXITSTATUS(status)) {
+ if (status != 0) {
+ return Error() << kFsVerityInitPath << " exited with " << status;
+ }
+ }
+
+ return {};
+}
+
+Result<KeymasterSigningKey> loadAndVerifyExistingKey() {
+ if (access(kSigningKeyBlob.c_str(), F_OK) < 0) {
+ return ErrnoError() << "Key blob not found: " << kSigningKeyBlob;
+ }
+ return KeymasterSigningKey::loadFromBlobAndVerify(kSigningKeyBlob);
+}
+
+Result<void> verifyAndLoadExistingCert(const KeymasterSigningKey& key) {
+ if (access(kSigningKeyCert.c_str(), F_OK) < 0) {
+ return ErrnoError() << "Key certificate not found: " << kSigningKeyCert;
+ }
+ auto trustedPublicKey = key.getPublicKey();
+ if (!trustedPublicKey.ok()) {
+ return Error() << "Failed to retrieve signing public key.";
+ }
+
+ auto publicKeyFromExistingCert = extractPublicKeyFromX509(kSigningKeyCert);
+ if (!publicKeyFromExistingCert.ok()) {
+ return publicKeyFromExistingCert.error();
+ }
+ if (publicKeyFromExistingCert.value() != trustedPublicKey.value()) {
+ return Error() << "Public key of existing certificate at " << kSigningKeyCert
+ << " does not match signing public key.";
+ }
+
+ auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
+ if (!cert_add_result.ok()) {
+ return cert_add_result.error();
+ }
+
+ // At this point, we know the cert matches
+ return {};
+}
+
+Result<KeymasterSigningKey> createAndPersistKey(const std::string& path) {
+ auto key = KeymasterSigningKey::createNewKey();
+
+ if (!key.ok()) {
+ return key.error();
+ }
+
+ auto result = key->saveKeyblob(path);
+ if (!result.ok()) {
+ return result.error();
+ }
+
+ return key;
+}
+
+bool compileArtifacts(bool force) {
+ const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
+
+ return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
+ 0;
+}
+
+bool validateArtifacts() {
+ const char* const argv[] = {kOdrefreshPath, "--check"};
+
+ return logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr) ==
+ 0;
+}
+
+int main(int /* argc */, char** /* argv */) {
+ auto removeArtifacts = []() {
+ std::error_code ec;
+ auto num_removed = std::filesystem::remove_all(kArtArtifactsDir, ec);
+ if (ec) {
+ // TODO can't remove artifacts, signal Zygote shouldn't use them
+ LOG(ERROR) << "Can't remove " << kArtArtifactsDir << ": " << ec.message();
+ } else {
+ LOG(INFO) << "Removed " << num_removed << " entries from " << kArtArtifactsDir;
+ }
+ };
+ // Make sure we delete the artifacts in all early (error) exit paths
+ auto scope_guard = android::base::make_scope_guard(removeArtifacts);
+
+ auto key = loadAndVerifyExistingKey();
+ if (!key.ok()) {
+ LOG(WARNING) << key.error().message();
+
+ key = createAndPersistKey(kSigningKeyBlob);
+ if (!key.ok()) {
+ LOG(ERROR) << "Failed to create or persist new key: " << key.error().message();
+ return -1;
+ }
+ } else {
+ LOG(INFO) << "Found and verified existing key: " << kSigningKeyBlob;
+ }
+
+ auto existing_cert = verifyAndLoadExistingCert(key.value());
+ if (!existing_cert.ok()) {
+ LOG(WARNING) << existing_cert.error().message();
+
+ // Try to create a new cert
+ auto new_cert = key->createX509Cert(kSigningKeyCert);
+ if (!new_cert.ok()) {
+ LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error().message();
+ // TODO apparently the key become invalid - delete the blob / cert
+ return -1;
+ }
+ auto cert_add_result = addCertToFsVerityKeyring(kSigningKeyCert);
+ if (!cert_add_result.ok()) {
+ LOG(ERROR) << "Failed to add certificate to fs-verity keyring: "
+ << cert_add_result.error().message();
+ return -1;
+ }
+ } else {
+ LOG(INFO) << "Found and verified existing public key certificate: " << kSigningKeyCert;
+ }
+
+ auto verityStatus = verifyAllFilesInVerity(kArtArtifactsDir);
+ if (!verityStatus.ok()) {
+ LOG(WARNING) << verityStatus.error().message() << ", removing " << kArtArtifactsDir;
+ removeArtifacts();
+ }
+
+ bool artifactsValid = validateArtifacts();
+
+ if (!artifactsValid || kForceCompilation) {
+ removeArtifacts();
+
+ LOG(INFO) << "Starting compilation... ";
+ bool ret = compileArtifacts(kForceCompilation);
+ LOG(INFO) << "Compilation done, returned " << ret;
+
+ verityStatus = addFilesToVerityRecursive(kArtArtifactsDir, key.value());
+
+ if (!verityStatus.ok()) {
+ LOG(ERROR) << "Failed to add " << verityStatus.error().message();
+ return -1;
+ }
+ }
+
+ // TODO we want to make sure Zygote only picks up the artifacts if we deemed
+ // everything was ok here. We could use a sysprop, or some other mechanism?
+ LOG(INFO) << "On-device signing done.";
+
+ scope_guard.Disable();
+ return 0;
+}
diff --git a/provisioner/Android.bp b/provisioner/Android.bp
new file mode 100644
index 0000000..c1c8d15
--- /dev/null
+++ b/provisioner/Android.bp
@@ -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.
+//
+
+aidl_interface {
+ name: "android.security.provisioner",
+ unstable: true,
+ local_include_dir: "binder",
+ srcs: [
+ "binder/android/security/provisioner/*.aidl",
+ ],
+ backend: {
+ java: {
+ platform_apis: true,
+ },
+ cpp: {
+ enabled: false,
+ },
+ ndk: {
+ enabled: false,
+ },
+ },
+}
+
+java_binary {
+ name: "provisioner_cli",
+ wrapper: "provisioner_cli",
+ srcs: ["src/com/android/commands/provisioner/**/*.java"],
+ static_libs: [
+ "android.security.provisioner-java",
+ ],
+}
diff --git a/provisioner/binder/android/security/provisioner/IProvisionerService.aidl b/provisioner/binder/android/security/provisioner/IProvisionerService.aidl
new file mode 100644
index 0000000..f81e9ab
--- /dev/null
+++ b/provisioner/binder/android/security/provisioner/IProvisionerService.aidl
@@ -0,0 +1,27 @@
+/**
+ * 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.provisioner;
+
+/**
+ * @hide
+ */
+interface IProvisionerService {
+ byte[] getCertificateRequest(in boolean testMode,
+ in int keyCount,
+ in byte[] endpointEncryptionKey,
+ in byte[] challenge) = 0;
+}
diff --git a/provisioner/provisioner_cli b/provisioner/provisioner_cli
new file mode 100755
index 0000000..7b53d6e
--- /dev/null
+++ b/provisioner/provisioner_cli
@@ -0,0 +1,21 @@
+#!/system/bin/sh
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Script to start "provisioner_cli" on the device.
+#
+base=/system
+export CLASSPATH=$base/framework/provisioner_cli.jar
+exec app_process $base/bin com.android.commands.provisioner.Cli "$@"
diff --git a/provisioner/src/com/android/commands/provisioner/Cli.java b/provisioner/src/com/android/commands/provisioner/Cli.java
new file mode 100644
index 0000000..62afdac
--- /dev/null
+++ b/provisioner/src/com/android/commands/provisioner/Cli.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.commands.provisioner;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.security.provisioner.IProvisionerService;
+
+import com.android.internal.os.BaseCommand;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.IllegalArgumentException;
+
+/**
+ * Contains the implementation of the remote provisioning command-line interface.
+ */
+public class Cli extends BaseCommand {
+ /**
+ * Creates an instance of the command-line interface and runs it. This is the entry point of
+ * the tool.
+ */
+ public static void main(String[] args) {
+ new Cli().run(args);
+ }
+
+ /**
+ * Runs the command requested by the invoker. It parses the very first required argument, which
+ * is the command, and calls the appropriate handler.
+ */
+ @Override
+ public void onRun() throws Exception {
+ String cmd = nextArgRequired();
+ switch (cmd) {
+ case "get-req":
+ getRequest();
+ break;
+
+ case "help":
+ onShowUsage(System.out);
+ break;
+
+ default:
+ throw new IllegalArgumentException("unknown command: " + cmd);
+ }
+ }
+
+ /**
+ * Retrieves a 'certificate request' from the provisioning service. The COSE-encoded
+ * 'certificate chain' describing the endpoint encryption key (EEK) to use for encryption is
+ * read from the standard input. The retrieved request is written to the standard output.
+ */
+ private void getRequest() throws Exception {
+ // Process options.
+ boolean test = false;
+ byte[] challenge = null;
+ int count = 0;
+ String arg;
+ while ((arg = nextArg()) != null) {
+ switch (arg) {
+ case "--test":
+ test = true;
+ break;
+
+ case "--challenge":
+ // TODO: We may need a different encoding of the challenge.
+ challenge = nextArgRequired().getBytes();
+ break;
+
+ case "--count":
+ count = Integer.parseInt(nextArgRequired());
+ if (count < 0) {
+ throw new IllegalArgumentException(
+ "--count must be followed by non-negative number");
+ }
+ break;
+
+ default:
+ throw new IllegalArgumentException("unknown argument: " + arg);
+ }
+ }
+
+ // Send the request over to the provisioning service and write the result to stdout.
+ byte[] res = getService().getCertificateRequest(test, count, readAll(System.in), challenge);
+ if (res != null) {
+ System.out.write(res);
+ }
+ }
+
+ /**
+ * Retrieves an implementation of the IProvisionerService interface. It allows the caller to
+ * call into the service via binder.
+ */
+ private static IProvisionerService getService() throws RemoteException {
+ IBinder binder = ServiceManager.getService("remote-provisioner");
+ if (binder == null) {
+ throw new RemoteException("Provisioning service is inaccessible");
+ }
+ return IProvisionerService.Stub.asInterface(binder);
+ }
+
+ /** Reads all data from the provided input stream and returns it as a byte array. */
+ private static byte[] readAll(InputStream in) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] buf = new byte[1024];
+ int read;
+ while ((read = in.read(buf)) != -1) {
+ out.write(buf, 0, read);
+ }
+ return out.toByteArray();
+ }
+
+ /**
+ * Writes the usage information to the given stream. This is displayed to users of the tool when
+ * they ask for help or when they pass incorrect arguments to the tool.
+ */
+ @Override
+ public void onShowUsage(PrintStream out) {
+ out.println(
+ "Usage: provisioner_cli <command> [options]\n" +
+ "Commands: help\n" +
+ " get-req [--count <n>] [--test] [--challenge <v>]");
+ }
+}
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 120000
index 0000000..ee92d9e
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1 @@
+../../build/soong/scripts/rustfmt.toml
\ No newline at end of file