Add compatibility wrapper
This implements the Keymint 1 spec by wrapping the legacy Keymaster
implementation.
Test: atest keystore2_km_compat_test
Test: atest keystore2_certificate_test
Test: Manually verify that keystore2 can find a legacy implementation.
Change-Id: Ia56c25eed0f97a7e6194954a655ceb62259b3273
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 883271d..354a6d6 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -20,11 +20,13 @@
rustlibs: [
"android.hardware.security.keymint-rust",
"android.security.apc-rust",
+ "android.security.compat-rust",
"android.system.keystore2-rust",
"libanyhow",
"libbinder_rs",
"libkeystore2_apc_compat-rust",
"libkeystore2_crypto_rust",
+ "libkeystore2_km_compat",
"libkeystore2_selinux",
"liblazy_static",
"liblibc",
@@ -46,12 +48,15 @@
rustlibs: [
"android.hardware.security.keymint-rust",
"android.security.apc-rust",
+ "android.security.compat-rust",
"android.system.keystore2-rust",
+ "android.hardware.security.keymint-rust",
"libandroid_logger",
"libanyhow",
"libbinder_rs",
"libkeystore2_apc_compat-rust",
"libkeystore2_crypto_rust",
+ "libkeystore2_km_compat",
"libkeystore2_selinux",
"liblazy_static",
"liblibc",
diff --git a/keystore2/TEST_MAPPING b/keystore2/TEST_MAPPING
index 70e3175..33d157e 100644
--- a/keystore2/TEST_MAPPING
+++ b/keystore2/TEST_MAPPING
@@ -1,6 +1,12 @@
{
"presubmit": [
{
+ "name": "keystore2_certificate_test"
+ },
+ {
+ "name": "keystore2_km_compat_test"
+ },
+ {
"name": "keystore2_test"
}
]
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
index 3051173..0d05dfe 100644
--- a/keystore2/aidl/Android.bp
+++ b/keystore2/aidl/Android.bp
@@ -58,3 +58,21 @@
},
},
}
+
+aidl_interface {
+ name: "android.security.compat",
+ srcs: [ "android/security/compat/*.aidl" ],
+ imports: [ "android.hardware.security.keymint" ],
+ unstable: true,
+ backend: {
+ java: {
+ sdk_version: "module_current",
+ },
+ rust: {
+ enabled: true,
+ },
+ ndk: {
+ enabled: true,
+ }
+ },
+}
diff --git a/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
new file mode 100644
index 0000000..6a72c75
--- /dev/null
+++ b/keystore2/aidl/android/security/compat/IKeystoreCompatService.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.compat;
+
+import android.hardware.security.keymint.IKeyMintDevice;
+import android.hardware.security.keymint.SecurityLevel;
+
+/**
+ */
+interface IKeystoreCompatService {
+ /**
+ */
+ IKeyMintDevice getKeyMintDevice (SecurityLevel securityLevel);
+}
diff --git a/keystore2/src/km_compat/Android.bp b/keystore2/src/km_compat/Android.bp
new file mode 100644
index 0000000..01cf2cc
--- /dev/null
+++ b/keystore2/src/km_compat/Android.bp
@@ -0,0 +1,105 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+rust_library {
+ name: "libkeystore2_km_compat",
+ crate_name: "keystore2_km_compat",
+ srcs: ["lib.rs"],
+
+ rustlibs: [
+ "android.hardware.security.keymint-rust",
+ "android.security.compat-rust",
+ ],
+ shared_libs: [
+ "libkm_compat_service",
+ ]
+}
+
+rust_test {
+ name: "keystore2_km_compat_test",
+ crate_name: "keystore2",
+ srcs: ["lib.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ rustlibs: [
+ "android.hardware.security.keymint-rust",
+ "android.security.compat-rust",
+ ],
+ shared_libs: [
+ "libkm_compat_service",
+ ],
+}
+
+cc_library {
+ name: "libkm_compat",
+ srcs: ["km_compat.cpp"],
+ shared_libs: [
+ "android.hardware.keymaster@3.0",
+ "android.hardware.keymaster@4.0",
+ "android.hardware.keymaster@4.1",
+ "android.hardware.security.keymint-unstable-ndk_platform",
+ "android.security.compat-ndk_platform",
+ "android.system.keystore2-ndk_platform",
+ "libbase",
+ "libbinder_ndk",
+ "libcrypto",
+ "libhidlbase",
+ "libkeymaster4_1support",
+ "libkeystore2_crypto",
+ "libutils",
+ ],
+}
+
+cc_library {
+ name: "libkm_compat_service",
+ srcs: ["km_compat_service.cpp"],
+ shared_libs: [
+ "android.security.compat-ndk_platform",
+ "libbinder_ndk",
+ "libcrypto",
+ "libkm_compat",
+ "libkeymaster4_1support",
+ "libkeystore2_crypto",
+ ],
+}
+
+cc_test {
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ srcs: [
+ "certificate_test.cpp",
+ "gtest_main.cpp",
+ "slot_test.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.keymaster@3.0",
+ "android.hardware.keymaster@4.0",
+ "android.hardware.keymaster@4.1",
+ "android.hardware.security.keymint-unstable-ndk_platform",
+ "android.security.compat-ndk_platform",
+ "android.system.keystore2-ndk_platform",
+ "libbase",
+ "libbinder_ndk",
+ "libcrypto",
+ "libhidlbase",
+ "libkeymaster4_1support",
+ "libkeystore2_crypto",
+ "libkm_compat",
+ "libutils",
+ ],
+ name: "keystore2_certificate_test",
+}
diff --git a/keystore2/src/km_compat/certificate_test.cpp b/keystore2/src/km_compat/certificate_test.cpp
new file mode 100644
index 0000000..d8c8c0a
--- /dev/null
+++ b/keystore2/src/km_compat/certificate_test.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "km_compat.h"
+
+#include <aidl/android/hardware/security/keymint/Algorithm.h>
+#include <aidl/android/hardware/security/keymint/BlockMode.h>
+#include <aidl/android/hardware/security/keymint/Digest.h>
+#include <aidl/android/hardware/security/keymint/PaddingMode.h>
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#define DEFINE_OPENSSL_OBJECT_POINTER(name) using name##_Ptr = bssl::UniquePtr<name>
+
+DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY);
+DEFINE_OPENSSL_OBJECT_POINTER(X509);
+
+using ::aidl::android::hardware::security::keymint::Algorithm;
+using ::aidl::android::hardware::security::keymint::BlockMode;
+using ::aidl::android::hardware::security::keymint::Certificate;
+using ::aidl::android::hardware::security::keymint::Digest;
+using ::aidl::android::hardware::security::keymint::PaddingMode;
+using ::aidl::android::hardware::security::keymint::SecurityLevel;
+using ::aidl::android::hardware::security::keymint::Tag;
+
+static std::variant<std::vector<Certificate>, ScopedAStatus>
+getCertificate(const std::vector<KeyParameter>& keyParams) {
+ static std::shared_ptr<KeyMintDevice> device =
+ KeyMintDevice::createKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
+ ByteArray blob;
+ KeyCharacteristics characteristics;
+ std::vector<Certificate> certChain;
+ auto status = device->generateKey(keyParams, &blob, &characteristics, &certChain);
+ if (!status.isOk()) {
+ return status;
+ }
+ return certChain;
+}
+
+static void ensureCertChainSize(const std::variant<std::vector<Certificate>, ScopedAStatus>& result,
+ uint32_t size) {
+ ASSERT_TRUE(std::holds_alternative<std::vector<Certificate>>(result));
+ auto certChain = std::get<std::vector<Certificate>>(result);
+ ASSERT_EQ(certChain.size(), size);
+}
+
+static void verify(const Certificate& certificate) {
+ const uint8_t* p = certificate.encodedCertificate.data();
+ X509_Ptr decoded_cert(d2i_X509(nullptr, &p, (long)certificate.encodedCertificate.size()));
+ EVP_PKEY_Ptr decoded_pkey(X509_get_pubkey(decoded_cert.get()));
+ ASSERT_TRUE(X509_verify(decoded_cert.get(), decoded_pkey.get()));
+}
+
+static std::vector<KeyParameter> getRSAKeyParams(const std::vector<KeyParameter>& extraParams) {
+ auto keyParams = std::vector<KeyParameter>({
+ KeyParameter{.tag = Tag::ALGORITHM, .integer = static_cast<int32_t>(Algorithm::RSA)},
+ KeyParameter{.tag = Tag::KEY_SIZE, .integer = 2048},
+ KeyParameter{.tag = Tag::RSA_PUBLIC_EXPONENT, .longInteger = 65537},
+ });
+ keyParams.insert(keyParams.end(), extraParams.begin(), extraParams.end());
+ return keyParams;
+}
+
+TEST(CertificateTest, TestRSAKeygen) {
+ auto keyParams = getRSAKeyParams({
+ KeyParameter{.tag = Tag::DIGEST, .integer = static_cast<int32_t>(Digest::SHA_2_256)},
+ KeyParameter{.tag = Tag::PADDING, .integer = static_cast<int32_t>(PaddingMode::RSA_PSS)},
+ KeyParameter{.tag = Tag::NO_AUTH_REQUIRED, .boolValue = true},
+ KeyParameter{.tag = Tag::PURPOSE, .integer = static_cast<int32_t>(KeyPurpose::SIGN)},
+ KeyParameter{.tag = Tag::PURPOSE, .integer = static_cast<int32_t>(KeyPurpose::ENCRYPT)},
+ });
+ auto result = getCertificate(keyParams);
+ ensureCertChainSize(result, 1);
+}
+
+TEST(CertificateTest, TestAES) {
+ auto keyParams = {
+ KeyParameter{.tag = Tag::ALGORITHM, .integer = static_cast<int32_t>(Algorithm::AES)},
+ KeyParameter{.tag = Tag::KEY_SIZE, .integer = 128},
+ KeyParameter{.tag = Tag::BLOCK_MODE, .integer = static_cast<int32_t>(BlockMode::CBC)},
+ KeyParameter{.tag = Tag::PADDING, .integer = static_cast<int32_t>(PaddingMode::NONE)},
+ KeyParameter{.tag = Tag::PURPOSE, .integer = static_cast<int32_t>(KeyPurpose::ENCRYPT)},
+ };
+ auto result = getCertificate(keyParams);
+ ensureCertChainSize(result, 0);
+}
+
+TEST(CertificateTest, TestAttestion) {
+ auto keyParams = getRSAKeyParams({
+ KeyParameter{.tag = Tag::PURPOSE, .integer = static_cast<int32_t>(KeyPurpose::SIGN)},
+ KeyParameter{.tag = Tag::ATTESTATION_CHALLENGE, .blob = {42}},
+ KeyParameter{.tag = Tag::ATTESTATION_APPLICATION_ID, .blob = {42}},
+ });
+ auto result = getCertificate(keyParams);
+ ensureCertChainSize(result, 3);
+ verify(std::get<std::vector<Certificate>>(result).back());
+}
+
+TEST(CertificateTest, TestRSAKeygenNoEncryptNoAuthRequired) {
+ auto keyParams = getRSAKeyParams({
+ KeyParameter{.tag = Tag::DIGEST, .integer = static_cast<int32_t>(Digest::SHA_2_256)},
+ KeyParameter{.tag = Tag::PADDING, .integer = static_cast<int32_t>(PaddingMode::RSA_PSS)},
+ KeyParameter{.tag = Tag::NO_AUTH_REQUIRED, .boolValue = true},
+ KeyParameter{.tag = Tag::PURPOSE, .integer = static_cast<int32_t>(KeyPurpose::SIGN)},
+ });
+ auto result = getCertificate(keyParams);
+ ensureCertChainSize(result, 1);
+ verify(std::get<std::vector<Certificate>>(result)[0]);
+}
+
+TEST(CertificateTest, TestRSAKeygenNoEncryptAuthRequired) {
+ auto keyParams = getRSAKeyParams({
+ KeyParameter{.tag = Tag::DIGEST, .integer = static_cast<int32_t>(Digest::SHA_2_256)},
+ KeyParameter{.tag = Tag::PADDING, .integer = static_cast<int32_t>(PaddingMode::RSA_PSS)},
+ KeyParameter{.tag = Tag::PURPOSE, .integer = static_cast<int32_t>(KeyPurpose::SIGN)},
+ });
+ auto result = getCertificate(keyParams);
+ ensureCertChainSize(result, 1);
+}
diff --git a/keystore2/src/km_compat/gtest_main.cpp b/keystore2/src/km_compat/gtest_main.cpp
new file mode 100644
index 0000000..149cbbc
--- /dev/null
+++ b/keystore2/src/km_compat/gtest_main.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
new file mode 100644
index 0000000..7900576
--- /dev/null
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -0,0 +1,924 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "km_compat.h"
+
+#include <aidl/android/hardware/security/keymint/Algorithm.h>
+#include <aidl/android/hardware/security/keymint/Digest.h>
+#include <aidl/android/hardware/security/keymint/PaddingMode.h>
+#include <aidl/android/system/keystore2/ResponseCode.h>
+#include <android-base/logging.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+#include <binder/IServiceManager.h>
+#include <keymasterV4_1/Keymaster.h>
+#include <keymasterV4_1/Keymaster3.h>
+#include <keymasterV4_1/Keymaster4.h>
+#include <keymasterV4_1/keymaster_tags.h>
+
+using ::aidl::android::hardware::security::keymint::Algorithm;
+using ::aidl::android::hardware::security::keymint::Digest;
+using ::aidl::android::hardware::security::keymint::PaddingMode;
+using ::aidl::android::hardware::security::keymint::Tag;
+using ::aidl::android::hardware::security::keymint::VerificationToken;
+using ::aidl::android::system::keystore2::ResponseCode;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::keymaster::V4_0::TagType;
+using ::android::hidl::manager::V1_2::IServiceManager;
+using V4_0_HardwareAuthToken = ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+using V4_0_KeyCharacteristics = ::android::hardware::keymaster::V4_0::KeyCharacteristics;
+using V4_0_KeyFormat = ::android::hardware::keymaster::V4_0::KeyFormat;
+using V4_0_KeyParameter = ::android::hardware::keymaster::V4_0::KeyParameter;
+using V4_0_VerificationToken = ::android::hardware::keymaster::V4_0::VerificationToken;
+
+// Utility functions
+
+// Converts a V4 error code into a ScopedAStatus
+ScopedAStatus convertErrorCode(V4_0_ErrorCode result) {
+ if (result == V4_0_ErrorCode::OK) {
+ return ScopedAStatus::ok();
+ }
+ return ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(result));
+}
+
+// TODO: The enum representation will need to be updated when we get unions.
+static V4_0_KeyParameter convertKeyParameterToLegacy(const KeyParameter& kp) {
+ V4_0_KeyParameter lkp;
+ lkp.tag = static_cast<::android::hardware::keymaster::V4_0::Tag>(kp.tag);
+ switch (::android::hardware::keymaster::V4_0::typeFromTag(lkp.tag)) {
+ case TagType::ENUM:
+ case TagType::ENUM_REP:
+ case TagType::UINT:
+ case TagType::UINT_REP:
+ lkp.f.integer = kp.integer;
+ break;
+ case TagType::ULONG:
+ case TagType::ULONG_REP:
+ lkp.f.longInteger = kp.longInteger;
+ break;
+ case TagType::DATE:
+ lkp.f.dateTime = kp.longInteger;
+ break;
+ case TagType::BOOL:
+ lkp.f.boolValue = kp.boolValue;
+ break;
+ case TagType::BIGNUM:
+ case TagType::BYTES:
+ lkp.blob = kp.blob;
+ break;
+ case TagType::INVALID:
+ break;
+ }
+ return lkp;
+}
+
+static std::vector<V4_0_KeyParameter>
+convertKeyParametersToLegacy(const std::vector<KeyParameter>& kps) {
+ std::vector<V4_0_KeyParameter> legacyKps(kps.size());
+ std::transform(kps.begin(), kps.end(), legacyKps.begin(), convertKeyParameterToLegacy);
+ return legacyKps;
+}
+
+// TODO: The enum representation will need to be updated when we get unions.
+static KeyParameter convertKeyParameterFromLegacy(const V4_0_KeyParameter& lkp) {
+ KeyParameter kp;
+ kp.tag = static_cast<Tag>(lkp.tag);
+ switch (::android::hardware::keymaster::V4_0::typeFromTag(lkp.tag)) {
+ case TagType::ENUM:
+ case TagType::ENUM_REP:
+ case TagType::UINT:
+ case TagType::UINT_REP:
+ kp.integer = lkp.f.integer;
+ break;
+ case TagType::ULONG:
+ case TagType::ULONG_REP:
+ kp.longInteger = lkp.f.longInteger;
+ break;
+ case TagType::DATE:
+ kp.longInteger = lkp.f.dateTime;
+ break;
+ case TagType::BOOL:
+ kp.boolValue = lkp.f.boolValue;
+ break;
+ case TagType::BIGNUM:
+ case TagType::BYTES:
+ kp.blob = lkp.blob;
+ break;
+ case TagType::INVALID:
+ break;
+ }
+ return kp;
+}
+
+static std::vector<KeyParameter>
+convertKeyParametersFromLegacy(const std::vector<V4_0_KeyParameter>& legacyKps) {
+ std::vector<KeyParameter> kps(legacyKps.size());
+ std::transform(legacyKps.begin(), legacyKps.end(), kps.begin(), convertKeyParameterFromLegacy);
+ return kps;
+}
+
+static KeyCharacteristics
+convertKeyCharacteristicsFromLegacy(const V4_0_KeyCharacteristics& legacyKc) {
+ KeyCharacteristics kc;
+ kc.softwareEnforced = convertKeyParametersFromLegacy(legacyKc.softwareEnforced);
+ kc.hardwareEnforced = convertKeyParametersFromLegacy(legacyKc.hardwareEnforced);
+ return kc;
+}
+
+static V4_0_KeyFormat convertKeyFormatToLegacy(const KeyFormat& kf) {
+ return static_cast<V4_0_KeyFormat>(kf);
+}
+
+static V4_0_HardwareAuthToken convertAuthTokenToLegacy(const HardwareAuthToken& at) {
+ V4_0_HardwareAuthToken legacyAt;
+ legacyAt.challenge = at.challenge;
+ legacyAt.userId = at.userId;
+ legacyAt.authenticatorId = at.authenticatorId;
+ legacyAt.authenticatorType =
+ static_cast<::android::hardware::keymaster::V4_0::HardwareAuthenticatorType>(
+ at.authenticatorType);
+ legacyAt.timestamp = at.timestamp.milliSeconds;
+ legacyAt.mac = at.mac;
+ return legacyAt;
+}
+
+static V4_0_VerificationToken convertVerificationTokenToLegacy(const VerificationToken& vt) {
+ V4_0_VerificationToken legacyVt;
+ legacyVt.challenge = vt.challenge;
+ legacyVt.timestamp = vt.timestamp.milliSeconds;
+ legacyVt.securityLevel =
+ static_cast<::android::hardware::keymaster::V4_0::SecurityLevel>(vt.securityLevel);
+ legacyVt.mac = vt.mac;
+ return legacyVt;
+}
+
+void OperationSlots::setNumFreeSlots(uint8_t numFreeSlots) {
+ std::lock_guard<std::mutex> lock(mNumFreeSlotsMutex);
+ mNumFreeSlots = numFreeSlots;
+}
+
+bool OperationSlots::claimSlot() {
+ std::lock_guard<std::mutex> lock(mNumFreeSlotsMutex);
+ if (mNumFreeSlots > 0) {
+ mNumFreeSlots--;
+ return true;
+ }
+ return false;
+}
+
+void OperationSlots::freeSlot() {
+ std::lock_guard<std::mutex> lock(mNumFreeSlotsMutex);
+ mNumFreeSlots++;
+}
+
+void OperationSlot::freeSlot() {
+ if (mIsActive) {
+ mOperationSlots->freeSlot();
+ mIsActive = false;
+ }
+}
+
+// KeyMintDevice implementation
+
+ScopedAStatus KeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* _aidl_return) {
+ // TODO: What do I do about the version number? Is it the version of the device I get?
+ auto result = mDevice->getHardwareInfo([&](auto securityLevel, const auto& keymasterName,
+ const auto& keymasterAuthorName) {
+ _aidl_return->securityLevel =
+ static_cast<::aidl::android::hardware::security::keymint::SecurityLevel>(securityLevel);
+ _aidl_return->keyMintName = keymasterName;
+ _aidl_return->keyMintAuthorName = keymasterAuthorName;
+ });
+ if (!result.isOk()) {
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+ }
+ return ScopedAStatus::ok();
+}
+
+// We're not implementing this.
+ScopedAStatus KeyMintDevice::verifyAuthorization(int64_t in_challenge ATTRIBUTE_UNUSED,
+ const HardwareAuthToken& in_token ATTRIBUTE_UNUSED,
+ VerificationToken* _aidl_return ATTRIBUTE_UNUSED) {
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(V4_0_ErrorCode::UNIMPLEMENTED));
+}
+
+ScopedAStatus KeyMintDevice::addRngEntropy(const std::vector<uint8_t>& in_data) {
+ V4_0_ErrorCode errorCode = mDevice->addRngEntropy(in_data);
+ return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintDevice::generateKey(const std::vector<KeyParameter>& in_keyParams,
+ ByteArray* out_generatedKeyBlob,
+ KeyCharacteristics* out_generatedKeyCharacteristics,
+ std::vector<Certificate>* out_outCertChain) {
+ auto legacyKeyParams = convertKeyParametersToLegacy(in_keyParams);
+ V4_0_ErrorCode errorCode;
+ auto result = mDevice->generateKey(
+ legacyKeyParams, [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
+ const V4_0_KeyCharacteristics& keyCharacteristics) {
+ errorCode = error;
+ out_generatedKeyBlob->data = keyBlob;
+ *out_generatedKeyCharacteristics =
+ convertKeyCharacteristicsFromLegacy(keyCharacteristics);
+ });
+ if (!result.isOk()) {
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+ }
+ if (errorCode == V4_0_ErrorCode::OK) {
+ auto cert = getCertificate(in_keyParams, out_generatedKeyBlob->data);
+ if (std::holds_alternative<V4_0_ErrorCode>(cert)) {
+ auto code = std::get<V4_0_ErrorCode>(cert);
+ // We return OK in successful cases that do not generate a certificate.
+ if (code != V4_0_ErrorCode::OK) {
+ errorCode = code;
+ deleteKey(out_generatedKeyBlob->data);
+ }
+ } else {
+ *out_outCertChain = std::get<std::vector<Certificate>>(cert);
+ }
+ }
+ return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintDevice::importKey(const std::vector<KeyParameter>& in_inKeyParams,
+ KeyFormat in_inKeyFormat,
+ const std::vector<uint8_t>& in_inKeyData,
+ ByteArray* out_outImportedKeyBlob,
+ KeyCharacteristics* out_outImportedKeyCharacteristics,
+ std::vector<Certificate>* out_outCertChain) {
+ auto legacyKeyParams = convertKeyParametersToLegacy(in_inKeyParams);
+ auto legacyKeyFormat = convertKeyFormatToLegacy(in_inKeyFormat);
+ V4_0_ErrorCode errorCode;
+ auto result =
+ mDevice->importKey(legacyKeyParams, legacyKeyFormat, in_inKeyData,
+ [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
+ const V4_0_KeyCharacteristics& keyCharacteristics) {
+ errorCode = error;
+ out_outImportedKeyBlob->data = keyBlob;
+ *out_outImportedKeyCharacteristics =
+ convertKeyCharacteristicsFromLegacy(keyCharacteristics);
+ });
+ if (!result.isOk()) {
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+ }
+ if (errorCode == V4_0_ErrorCode::OK) {
+ auto cert = getCertificate(in_inKeyParams, out_outImportedKeyBlob->data);
+ if (std::holds_alternative<V4_0_ErrorCode>(cert)) {
+ auto code = std::get<V4_0_ErrorCode>(cert);
+ // We return OK in successful cases that do not generate a certificate.
+ if (code != V4_0_ErrorCode::OK) {
+ errorCode = code;
+ deleteKey(out_outImportedKeyBlob->data);
+ }
+ } else {
+ *out_outCertChain = std::get<std::vector<Certificate>>(cert);
+ }
+ }
+ return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintDevice::importWrappedKey(
+ const std::vector<uint8_t>& in_inWrappedKeyData,
+ const std::vector<uint8_t>& in_inWrappingKeyBlob, const std::vector<uint8_t>& in_inMaskingKey,
+ const std::vector<KeyParameter>& in_inUnwrappingParams, int64_t in_inPasswordSid,
+ int64_t in_inBiometricSid, ByteArray* out_outImportedKeyBlob,
+ KeyCharacteristics* out_outImportedKeyCharacteristics) {
+ auto legacyUnwrappingParams = convertKeyParametersToLegacy(in_inUnwrappingParams);
+ V4_0_ErrorCode errorCode;
+ auto result =
+ mDevice->importWrappedKey(in_inWrappedKeyData, in_inWrappingKeyBlob, in_inMaskingKey,
+ legacyUnwrappingParams, in_inPasswordSid, in_inBiometricSid,
+ [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
+ const V4_0_KeyCharacteristics& keyCharacteristics) {
+ errorCode = error;
+ out_outImportedKeyBlob->data = keyBlob;
+ *out_outImportedKeyCharacteristics =
+ convertKeyCharacteristicsFromLegacy(keyCharacteristics);
+ });
+ if (!result.isOk()) {
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+ }
+ return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintDevice::upgradeKey(const std::vector<uint8_t>& in_inKeyBlobToUpgrade,
+ const std::vector<KeyParameter>& in_inUpgradeParams,
+ std::vector<uint8_t>* _aidl_return) {
+ auto legacyUpgradeParams = convertKeyParametersToLegacy(in_inUpgradeParams);
+ V4_0_ErrorCode errorCode;
+ auto result =
+ mDevice->upgradeKey(in_inKeyBlobToUpgrade, legacyUpgradeParams,
+ [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& upgradedKeyBlob) {
+ errorCode = error;
+ *_aidl_return = upgradedKeyBlob;
+ });
+ if (!result.isOk()) {
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+ }
+ return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintDevice::deleteKey(const std::vector<uint8_t>& in_inKeyBlob) {
+ V4_0_ErrorCode errorCode = mDevice->deleteKey(in_inKeyBlob);
+ return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintDevice::deleteAllKeys() {
+ V4_0_ErrorCode errorCode = mDevice->deleteAllKeys();
+ return convertErrorCode(errorCode);
+}
+
+// We're not implementing this.
+ScopedAStatus KeyMintDevice::destroyAttestationIds() {
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(V4_0_ErrorCode::UNIMPLEMENTED));
+}
+
+ScopedAStatus KeyMintDevice::begin(KeyPurpose in_inPurpose,
+ const std::vector<uint8_t>& in_inKeyBlob,
+ const std::vector<KeyParameter>& in_inParams,
+ const HardwareAuthToken& in_inAuthToken,
+ BeginResult* _aidl_return) {
+ if (!mOperationSlots.claimSlot()) {
+ return convertErrorCode(V4_0_ErrorCode::TOO_MANY_OPERATIONS);
+ }
+ auto legacyPurpose =
+ static_cast<::android::hardware::keymaster::V4_0::KeyPurpose>(in_inPurpose);
+ auto legacyParams = convertKeyParametersToLegacy(in_inParams);
+ auto legacyAuthToken = convertAuthTokenToLegacy(in_inAuthToken);
+ V4_0_ErrorCode errorCode;
+ auto result = mDevice->begin(
+ legacyPurpose, in_inKeyBlob, legacyParams, legacyAuthToken,
+ [&](V4_0_ErrorCode error, const hidl_vec<V4_0_KeyParameter>& outParams,
+ uint64_t operationHandle) {
+ errorCode = error;
+ _aidl_return->challenge = operationHandle; // TODO: Is this right?
+ _aidl_return->params = convertKeyParametersFromLegacy(outParams);
+ _aidl_return->operation = ndk::SharedRefBase::make<KeyMintOperation>(
+ mDevice, operationHandle, &mOperationSlots, error == V4_0_ErrorCode::OK);
+ });
+ if (!result.isOk()) {
+ // TODO: In this case we're guaranteed that _aidl_return was not initialized, right?
+ mOperationSlots.freeSlot();
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+ }
+ if (errorCode != V4_0_ErrorCode::OK) {
+ mOperationSlots.freeSlot();
+ }
+ return convertErrorCode(errorCode);
+}
+
+ScopedAStatus
+KeyMintOperation::update(const std::optional<KeyParameterArray>& in_inParams,
+ const std::optional<std::vector<uint8_t>>& in_input,
+ const std::optional<HardwareAuthToken>& in_inAuthToken,
+ const std::optional<VerificationToken>& in_inVerificationToken,
+ std::optional<KeyParameterArray>* out_outParams,
+ std::optional<ByteArray>* out_output, int32_t* _aidl_return) {
+ std::vector<V4_0_KeyParameter> legacyParams;
+ if (in_inParams.has_value()) {
+ legacyParams = convertKeyParametersToLegacy(in_inParams.value().params);
+ }
+ auto input = in_input.value_or(std::vector<uint8_t>());
+ V4_0_HardwareAuthToken authToken;
+ if (in_inAuthToken.has_value()) {
+ authToken = convertAuthTokenToLegacy(in_inAuthToken.value());
+ }
+ V4_0_VerificationToken verificationToken;
+ if (in_inVerificationToken.has_value()) {
+ verificationToken = convertVerificationTokenToLegacy(in_inVerificationToken.value());
+ }
+ V4_0_ErrorCode errorCode;
+ auto result = mDevice->update(
+ mOperationHandle, legacyParams, input, authToken, verificationToken,
+ [&](V4_0_ErrorCode error, uint32_t inputConsumed,
+ const hidl_vec<V4_0_KeyParameter>& outParams, const hidl_vec<uint8_t>& output) {
+ errorCode = error;
+ out_outParams->emplace();
+ out_outParams->value().params = convertKeyParametersFromLegacy(outParams);
+ out_output->emplace();
+ out_output->value().data = output;
+ *_aidl_return = inputConsumed;
+ });
+ if (!result.isOk()) {
+ mOperationSlot.freeSlot();
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+ }
+ if (errorCode != V4_0_ErrorCode::OK) {
+ mOperationSlot.freeSlot();
+ }
+ return convertErrorCode(errorCode);
+}
+
+ScopedAStatus
+KeyMintOperation::finish(const std::optional<KeyParameterArray>& in_inParams,
+ const std::optional<std::vector<uint8_t>>& in_input,
+ const std::optional<std::vector<uint8_t>>& in_inSignature,
+ const std::optional<HardwareAuthToken>& in_authToken,
+ const std::optional<VerificationToken>& in_inVerificationToken,
+ std::optional<KeyParameterArray>* out_outParams,
+ std::vector<uint8_t>* _aidl_return) {
+ V4_0_ErrorCode errorCode;
+ std::vector<V4_0_KeyParameter> legacyParams;
+ if (in_inParams.has_value()) {
+ legacyParams = convertKeyParametersToLegacy(in_inParams.value().params);
+ }
+ auto input = in_input.value_or(std::vector<uint8_t>());
+ auto signature = in_inSignature.value_or(std::vector<uint8_t>());
+ V4_0_HardwareAuthToken authToken;
+ if (in_authToken.has_value()) {
+ authToken = convertAuthTokenToLegacy(in_authToken.value());
+ }
+ V4_0_VerificationToken verificationToken;
+ if (in_inVerificationToken.has_value()) {
+ verificationToken = convertVerificationTokenToLegacy(in_inVerificationToken.value());
+ }
+ auto result = mDevice->finish(
+ mOperationHandle, legacyParams, input, signature, authToken, verificationToken,
+ [&](V4_0_ErrorCode error, const hidl_vec<V4_0_KeyParameter>& outParams,
+ const hidl_vec<uint8_t>& output) {
+ errorCode = error;
+ out_outParams->emplace();
+ out_outParams->value().params = convertKeyParametersFromLegacy(outParams);
+ *_aidl_return = output;
+ });
+ mOperationSlot.freeSlot();
+ if (!result.isOk()) {
+ return ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ResponseCode::SYSTEM_ERROR));
+ }
+ return convertErrorCode(errorCode);
+}
+
+ScopedAStatus KeyMintOperation::abort() {
+ V4_0_ErrorCode errorCode = mDevice->abort(mOperationHandle);
+ mOperationSlot.freeSlot();
+ return convertErrorCode(errorCode);
+}
+
+KeyMintOperation::~KeyMintOperation() {
+ if (mOperationSlot.hasSlot()) {
+ auto error = abort();
+ if (!error.isOk()) {
+ LOG(WARNING) << "Error calling abort in ~KeyMintOperation: " << error.getMessage();
+ }
+ }
+}
+
+// Certificate implementation
+
+static std::optional<KeyParameter> getParam(const std::vector<KeyParameter>& keyParams, Tag tag) {
+ auto it = find_if(keyParams.begin(), keyParams.end(),
+ [&](const KeyParameter& kp) { return kp.tag == tag; });
+ if (it == keyParams.end()) {
+ return std::nullopt;
+ }
+ return std::optional(*it);
+}
+
+static bool containsParam(const std::vector<KeyParameter>& keyParams, Tag tag) {
+ return getParam(keyParams, tag).has_value();
+}
+
+// Prefer the smallest.
+// If no options are found, return the first.
+template <typename T>
+static T getMaximum(const std::vector<KeyParameter>& keyParams, Tag tag,
+ std::vector<T> sortedOptions) {
+ auto bestSoFar = sortedOptions.end();
+ for (const KeyParameter& kp : keyParams) {
+ if (kp.tag == tag) {
+ auto it =
+ std::find(sortedOptions.begin(), sortedOptions.end(), static_cast<T>(kp.integer));
+ if (std::distance(it, bestSoFar) < 0) {
+ bestSoFar = it;
+ }
+ }
+ }
+ if (bestSoFar == sortedOptions.end()) {
+ return sortedOptions[0];
+ }
+ return *bestSoFar;
+}
+
+// TODO: What should I do if these tags don't exist? An empty blob might be okay, but what about
+// the integers and longs?
+// TODO: Migrate to using accessTagValue when possible.
+static std::vector<uint8_t> getBlob(const std::optional<KeyParameter> kp) {
+ if (!kp.has_value()) {
+ return {};
+ }
+ return kp->blob;
+}
+
+static int32_t getLong(std::optional<KeyParameter> kp) {
+ if (!kp.has_value()) {
+ return -1;
+ }
+ return kp->longInteger;
+}
+
+static int64_t getInteger(std::optional<KeyParameter> kp) {
+ if (!kp.has_value()) {
+ return -1;
+ }
+ return kp->integer;
+}
+
+static std::variant<keystore::X509_Ptr, V4_0_ErrorCode>
+makeCert(::android::sp<Keymaster> mDevice, const std::vector<KeyParameter>& keyParams,
+ const std::vector<uint8_t>& keyBlob) {
+ // Start generating the certificate.
+ // Get public key for makeCert.
+ V4_0_ErrorCode errorCode = V4_0_ErrorCode::OK;
+ std::vector<uint8_t> key;
+ auto result = mDevice->exportKey(
+ V4_0_KeyFormat::X509, keyBlob, getBlob(getParam(keyParams, Tag::APPLICATION_ID)),
+ getBlob(getParam(keyParams, Tag::APPLICATION_DATA)),
+ [&](V4_0_ErrorCode error, const hidl_vec<uint8_t>& keyMaterial) {
+ errorCode = error;
+ key = keyMaterial;
+ });
+ if (!result.isOk()) {
+ return V4_0_ErrorCode::UNKNOWN_ERROR;
+ }
+ if (errorCode != V4_0_ErrorCode::OK) {
+ return errorCode;
+ }
+ // Get pkey for makeCert.
+ CBS cbs;
+ CBS_init(&cbs, key.data(), key.size());
+ auto pkey = EVP_parse_public_key(&cbs);
+ // makeCert
+ // TODO: Get the serial and subject from key params once the tags are added. Also use new tags
+ // for the two datetime parameters once we get those.
+ auto certOrError = keystore::makeCert(
+ pkey, 42, "TODO", getLong(getParam(keyParams, Tag::ACTIVE_DATETIME)),
+ getLong(getParam(keyParams, Tag::USAGE_EXPIRE_DATETIME)),
+ false /* intentionally left blank */, std::nullopt /* intentionally left blank */,
+ std::nullopt /* intentionally left blank */);
+ if (std::holds_alternative<keystore::CertUtilsError>(certOrError)) {
+ // TODO: What error should I actually return? And the same everywhere below.
+ return V4_0_ErrorCode::UNKNOWN_ERROR;
+ }
+ return std::move(std::get<keystore::X509_Ptr>(certOrError));
+}
+
+static std::variant<keystore::Algo, V4_0_ErrorCode> getKeystoreAlgorithm(Algorithm algorithm) {
+ switch (algorithm) {
+ case Algorithm::RSA:
+ return keystore::Algo::RSA;
+ case Algorithm::EC:
+ return keystore::Algo::ECDSA;
+ default:
+ return V4_0_ErrorCode::UNKNOWN_ERROR;
+ }
+}
+
+static std::variant<keystore::Padding, V4_0_ErrorCode> getKeystorePadding(PaddingMode padding) {
+ switch (padding) {
+ case PaddingMode::RSA_PKCS1_1_5_SIGN:
+ return keystore::Padding::PKCS1_5;
+ case PaddingMode::RSA_PSS:
+ return keystore::Padding::PSS;
+ default:
+ return keystore::Padding::Ignored;
+ }
+}
+
+static std::variant<keystore::Digest, V4_0_ErrorCode> getKeystoreDigest(Digest digest) {
+ switch (digest) {
+ case Digest::SHA1:
+ return keystore::Digest::SHA1;
+ case Digest::SHA_2_224:
+ return keystore::Digest::SHA224;
+ case Digest::SHA_2_256:
+ case Digest::NONE:
+ return keystore::Digest::SHA256;
+ case Digest::SHA_2_384:
+ return keystore::Digest::SHA384;
+ case Digest::SHA_2_512:
+ return keystore::Digest::SHA512;
+ default:
+ return V4_0_ErrorCode::UNKNOWN_ERROR;
+ }
+}
+
+std::optional<V4_0_ErrorCode>
+KeyMintDevice::signCertificate(const std::vector<KeyParameter>& keyParams,
+ const std::vector<uint8_t>& keyBlob, X509* cert) {
+ auto algorithm = static_cast<Algorithm>(getInteger(getParam(keyParams, Tag::ALGORITHM)));
+ auto algoOrError = getKeystoreAlgorithm(algorithm);
+ if (std::holds_alternative<V4_0_ErrorCode>(algoOrError)) {
+ return std::get<V4_0_ErrorCode>(algoOrError);
+ }
+ auto algo = std::get<keystore::Algo>(algoOrError);
+ auto origPadding = getMaximum<PaddingMode>(
+ keyParams, Tag::PADDING, {PaddingMode::RSA_PSS, PaddingMode::RSA_PKCS1_1_5_SIGN});
+ auto paddingOrError = getKeystorePadding(origPadding);
+ if (std::holds_alternative<V4_0_ErrorCode>(paddingOrError)) {
+ return std::get<V4_0_ErrorCode>(paddingOrError);
+ }
+ auto padding = std::get<keystore::Padding>(paddingOrError);
+ auto origDigest = getMaximum<Digest>(
+ keyParams, Tag::DIGEST,
+ {Digest::SHA_2_256, Digest::SHA_2_512, Digest::SHA_2_384, Digest::SHA_2_224, Digest::SHA1});
+ auto digestOrError = getKeystoreDigest(origDigest);
+ if (std::holds_alternative<V4_0_ErrorCode>(digestOrError)) {
+ return std::get<V4_0_ErrorCode>(digestOrError);
+ }
+ auto digest = std::get<keystore::Digest>(digestOrError);
+
+ V4_0_ErrorCode errorCode = V4_0_ErrorCode::OK;
+ auto error = keystore::signCertWith(
+ &*cert,
+ [&](const uint8_t* data, size_t len) {
+ std::vector<uint8_t> dataVec(data, data + len);
+ std::vector<KeyParameter> kps = {
+ KeyParameter{.tag = Tag::PADDING, .integer = static_cast<int32_t>(origPadding)},
+ KeyParameter{.tag = Tag::DIGEST, .integer = static_cast<int32_t>(origDigest)},
+ };
+ BeginResult beginResult;
+ auto error = begin(KeyPurpose::SIGN, keyBlob, kps, HardwareAuthToken(), &beginResult);
+ if (!error.isOk()) {
+ errorCode = static_cast<V4_0_ErrorCode>(error.getServiceSpecificError());
+ return std::vector<uint8_t>();
+ }
+ std::optional<KeyParameterArray> outParams;
+ std::optional<ByteArray> outByte;
+ int32_t status;
+ beginResult.operation->update(std::nullopt, dataVec, HardwareAuthToken(),
+ VerificationToken(), &outParams, &outByte, &status);
+ if (!status) {
+ return std::vector<uint8_t>();
+ }
+ std::vector<uint8_t> result;
+ error = beginResult.operation->finish(std::nullopt, std::nullopt, std::nullopt,
+ std::nullopt, std::nullopt, &outParams, &result);
+ if (!error.isOk()) {
+ errorCode = static_cast<V4_0_ErrorCode>(error.getServiceSpecificError());
+ return std::vector<uint8_t>();
+ }
+ return result;
+ },
+ algo, padding, digest);
+ if (error) {
+ return V4_0_ErrorCode::UNKNOWN_ERROR;
+ }
+ if (errorCode != V4_0_ErrorCode::OK) {
+ return errorCode;
+ }
+ return std::nullopt;
+}
+
+std::variant<std::vector<Certificate>, V4_0_ErrorCode>
+KeyMintDevice::getCertificate(const std::vector<KeyParameter>& keyParams,
+ const std::vector<uint8_t>& keyBlob) {
+ // There are no certificates for symmetric keys.
+ auto algorithm = static_cast<Algorithm>(getInteger(getParam(keyParams, Tag::ALGORITHM)));
+ switch (algorithm) {
+ case Algorithm::RSA:
+ case Algorithm::EC:
+ break;
+ default:
+ return V4_0_ErrorCode::OK;
+ }
+ // If attestation was requested, call and use attestKey.
+ if (containsParam(keyParams, Tag::ATTESTATION_CHALLENGE)) {
+ auto legacyParams = convertKeyParametersToLegacy(keyParams);
+ std::vector<Certificate> certs;
+ V4_0_ErrorCode errorCode = V4_0_ErrorCode::OK;
+ auto result = mDevice->attestKey(
+ keyBlob, legacyParams,
+ [&](V4_0_ErrorCode error, const hidl_vec<hidl_vec<uint8_t>>& certChain) {
+ errorCode = error;
+ for (const auto& cert : certChain) {
+ Certificate certificate;
+ certificate.encodedCertificate = cert;
+ certs.push_back(certificate);
+ }
+ });
+ if (!result.isOk()) {
+ return V4_0_ErrorCode::UNKNOWN_ERROR;
+ }
+ if (errorCode != V4_0_ErrorCode::OK) {
+ return errorCode;
+ }
+ return certs;
+ }
+
+ // makeCert
+ auto certOrError = makeCert(mDevice, keyParams, keyBlob);
+ if (std::holds_alternative<V4_0_ErrorCode>(certOrError)) {
+ return std::get<V4_0_ErrorCode>(certOrError);
+ }
+ auto cert = std::move(std::get<keystore::X509_Ptr>(certOrError));
+
+ // setIssuer
+ auto error = keystore::setIssuer(&*cert, &*cert, false);
+ if (error) {
+ return V4_0_ErrorCode::UNKNOWN_ERROR;
+ }
+
+ // Signing
+ auto canSelfSign =
+ std::find_if(keyParams.begin(), keyParams.end(), [&](const KeyParameter& kp) {
+ return kp.tag == Tag::PURPOSE &&
+ static_cast<KeyPurpose>(kp.integer) == KeyPurpose::SIGN;
+ }) != keyParams.end();
+ auto noAuthRequired = containsParam(keyParams, Tag::NO_AUTH_REQUIRED);
+ if (canSelfSign && noAuthRequired) {
+ auto errorCode = signCertificate(keyParams, keyBlob, &*cert);
+ if (errorCode.has_value()) {
+ return errorCode.value();
+ }
+ } else {
+ keystore::EVP_PKEY_CTX_Ptr pkey_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL));
+ EVP_PKEY_keygen_init(pkey_ctx.get());
+ EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pkey_ctx.get(), NID_X9_62_prime256v1);
+ EVP_PKEY* pkey_ptr = nullptr;
+ EVP_PKEY_keygen(pkey_ctx.get(), &pkey_ptr);
+ error = keystore::signCert(&*cert, pkey_ptr);
+ }
+ if (error) {
+ return V4_0_ErrorCode::UNKNOWN_ERROR;
+ }
+
+ // encodeCert
+ auto encodedCertOrError = keystore::encodeCert(&*cert);
+ if (std::holds_alternative<keystore::CertUtilsError>(encodedCertOrError)) {
+ return V4_0_ErrorCode::UNKNOWN_ERROR;
+ }
+
+ Certificate certificate{.encodedCertificate =
+ std::get<std::vector<uint8_t>>(encodedCertOrError)};
+ std::vector certificates = {certificate};
+ return certificates;
+}
+
+// Code to find the Keymaster devices (copied from existing code).
+
+// Copied from system/security/keystore/include/keystore/keymaster_types.h.
+
+// Changing this namespace alias will change the keymaster version.
+namespace keymaster = ::android::hardware::keymaster::V4_1;
+
+using keymaster::SecurityLevel;
+
+// Copied from system/security/keystore/KeyStore.h.
+
+using ::android::sp;
+using keymaster::support::Keymaster;
+
+template <typename T, size_t count> class Devices : public std::array<T, count> {
+ public:
+ T& operator[](SecurityLevel secLevel) {
+ static_assert(uint32_t(SecurityLevel::SOFTWARE) == 0 &&
+ uint32_t(SecurityLevel::TRUSTED_ENVIRONMENT) == 1 &&
+ uint32_t(SecurityLevel::STRONGBOX) == 2,
+ "Numeric values of security levels have changed");
+ return std::array<T, count>::at(static_cast<uint32_t>(secLevel));
+ }
+ T operator[](SecurityLevel secLevel) const {
+ if (static_cast<uint32_t>(secLevel) > static_cast<uint32_t>(SecurityLevel::STRONGBOX)) {
+ LOG(ERROR) << "Invalid security level requested";
+ return {};
+ }
+ return (*const_cast<Devices*>(this))[secLevel];
+ }
+};
+
+using KeymasterDevices = Devices<sp<Keymaster>, 3>;
+
+// Copied from system/security/keystore/keystore_main.cpp.
+
+using ::android::hardware::hidl_string;
+using keymaster::support::Keymaster3;
+using keymaster::support::Keymaster4;
+
+template <typename Wrapper>
+KeymasterDevices enumerateKeymasterDevices(IServiceManager* serviceManager) {
+ KeymasterDevices result;
+ serviceManager->listManifestByInterface(
+ Wrapper::WrappedIKeymasterDevice::descriptor, [&](const hidl_vec<hidl_string>& names) {
+ auto try_get_device = [&](const auto& name, bool fail_silent) {
+ auto device = Wrapper::WrappedIKeymasterDevice::getService(name);
+ if (fail_silent && !device) return;
+ CHECK(device) << "Failed to get service for \""
+ << Wrapper::WrappedIKeymasterDevice::descriptor
+ << "\" with interface name \"" << name << "\"";
+
+ sp<Keymaster> kmDevice(new Wrapper(device, name));
+ auto halVersion = kmDevice->halVersion();
+ SecurityLevel securityLevel = halVersion.securityLevel;
+ LOG(INFO) << "found " << Wrapper::WrappedIKeymasterDevice::descriptor
+ << " with interface name " << name << " and seclevel "
+ << toString(securityLevel);
+ CHECK(static_cast<uint32_t>(securityLevel) < result.size())
+ << "Security level of \"" << Wrapper::WrappedIKeymasterDevice::descriptor
+ << "\" with interface name \"" << name << "\" out of range";
+ auto& deviceSlot = result[securityLevel];
+ if (deviceSlot) {
+ if (!fail_silent) {
+ LOG(WARNING) << "Implementation of \""
+ << Wrapper::WrappedIKeymasterDevice::descriptor
+ << "\" with interface name \"" << name
+ << "\" and security level: " << toString(securityLevel)
+ << " Masked by other implementation of Keymaster";
+ }
+ } else {
+ deviceSlot = kmDevice;
+ }
+ };
+ bool has_default = false;
+ for (auto& n : names) {
+ try_get_device(n, false);
+ if (n == "default") has_default = true;
+ }
+ // Make sure that we always check the default device. If we enumerate only what is
+ // known to hwservicemanager, we miss a possible passthrough HAL.
+ if (!has_default) {
+ try_get_device("default", true /* fail_silent */);
+ }
+ });
+ return result;
+}
+
+KeymasterDevices initializeKeymasters() {
+ auto serviceManager = IServiceManager::getService();
+ CHECK(serviceManager.get()) << "Failed to get ServiceManager";
+ auto result = enumerateKeymasterDevices<Keymaster4>(serviceManager.get());
+ auto softKeymaster = result[SecurityLevel::SOFTWARE];
+ if (!result[SecurityLevel::TRUSTED_ENVIRONMENT]) {
+ result = enumerateKeymasterDevices<Keymaster3>(serviceManager.get());
+ }
+ if (softKeymaster) result[SecurityLevel::SOFTWARE] = softKeymaster;
+ if (result[SecurityLevel::SOFTWARE] && !result[SecurityLevel::TRUSTED_ENVIRONMENT]) {
+ LOG(WARNING) << "No secure Keymaster implementation found, but device offers insecure"
+ " Keymaster HAL. Using as default.";
+ result[SecurityLevel::TRUSTED_ENVIRONMENT] = result[SecurityLevel::SOFTWARE];
+ result[SecurityLevel::SOFTWARE] = nullptr;
+ }
+ // The software bit was removed since we do not need it.
+ return result;
+}
+
+// KeyMintDevice implementation
+
+KeyMintDevice::KeyMintDevice(sp<Keymaster> device, KeyMintSecurityLevel securityLevel)
+ : mDevice(device) {
+ if (securityLevel == KeyMintSecurityLevel::STRONGBOX) {
+ mOperationSlots.setNumFreeSlots(3);
+ } else {
+ mOperationSlots.setNumFreeSlots(15);
+ }
+}
+
+void KeyMintDevice::setNumFreeSlots(uint8_t numFreeSlots) {
+ mOperationSlots.setNumFreeSlots(numFreeSlots);
+}
+
+std::shared_ptr<KeyMintDevice>
+KeyMintDevice::createKeyMintDevice(KeyMintSecurityLevel securityLevel) {
+ auto secLevel = static_cast<SecurityLevel>(securityLevel);
+ auto devices = initializeKeymasters();
+ auto device = devices[secLevel];
+ if (!device) {
+ return {};
+ }
+ return ndk::SharedRefBase::make<KeyMintDevice>(std::move(device), securityLevel);
+}
+
+ScopedAStatus
+KeystoreCompatService::getKeyMintDevice(KeyMintSecurityLevel in_securityLevel,
+ std::shared_ptr<IKeyMintDevice>* _aidl_return) {
+ if (mDeviceCache.find(in_securityLevel) == mDeviceCache.end()) {
+ auto device = KeyMintDevice::createKeyMintDevice(in_securityLevel);
+ if (!device) {
+ return ScopedAStatus::fromStatus(STATUS_NAME_NOT_FOUND);
+ }
+ mDeviceCache[in_securityLevel] = std::move(device);
+ }
+ *_aidl_return = mDeviceCache[in_securityLevel];
+ return ScopedAStatus::ok();
+}
diff --git a/keystore2/src/km_compat/km_compat.h b/keystore2/src/km_compat/km_compat.h
new file mode 100644
index 0000000..904d391
--- /dev/null
+++ b/keystore2/src/km_compat/km_compat.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/security/keymint/BnKeyMintDevice.h>
+#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
+#include <aidl/android/security/compat/BnKeystoreCompatService.h>
+#include <keymasterV4_1/Keymaster4.h>
+#include <unordered_map>
+#include <variant>
+
+#include "certificate_utils.h"
+
+using ::aidl::android::hardware::security::keymint::BeginResult;
+using ::aidl::android::hardware::security::keymint::ByteArray;
+using ::aidl::android::hardware::security::keymint::Certificate;
+using ::aidl::android::hardware::security::keymint::HardwareAuthToken;
+using ::aidl::android::hardware::security::keymint::KeyCharacteristics;
+using ::aidl::android::hardware::security::keymint::KeyFormat;
+using ::aidl::android::hardware::security::keymint::KeyMintHardwareInfo;
+using ::aidl::android::hardware::security::keymint::KeyParameter;
+using ::aidl::android::hardware::security::keymint::KeyParameterArray;
+using ::aidl::android::hardware::security::keymint::KeyPurpose;
+using ::aidl::android::hardware::security::keymint::VerificationToken;
+using KeyMintSecurityLevel = ::aidl::android::hardware::security::keymint::SecurityLevel;
+using V4_0_ErrorCode = ::android::hardware::keymaster::V4_0::ErrorCode;
+using ::aidl::android::hardware::security::keymint::IKeyMintDevice;
+using ::aidl::android::security::compat::BnKeystoreCompatService;
+using ::android::hardware::keymaster::V4_1::support::Keymaster;
+using ::ndk::ScopedAStatus;
+
+class OperationSlots {
+ private:
+ uint8_t mNumFreeSlots;
+ std::mutex mNumFreeSlotsMutex;
+
+ public:
+ void setNumFreeSlots(uint8_t numFreeSlots);
+ bool claimSlot();
+ void freeSlot();
+};
+
+// An abstraction for a single operation slot.
+// This contains logic to ensure that we do not free the slot multiple times,
+// e.g., if we call abort twice on the same operation.
+class OperationSlot {
+ private:
+ OperationSlots* mOperationSlots;
+ bool mIsActive;
+
+ public:
+ OperationSlot(OperationSlots* slots, bool isActive)
+ : mOperationSlots(slots), mIsActive(isActive) {}
+
+ void freeSlot();
+ bool hasSlot() { return mIsActive; }
+};
+
+class KeyMintDevice : public aidl::android::hardware::security::keymint::BnKeyMintDevice {
+ private:
+ ::android::sp<Keymaster> mDevice;
+ OperationSlots mOperationSlots;
+
+ public:
+ explicit KeyMintDevice(::android::sp<Keymaster>, KeyMintSecurityLevel);
+ static std::shared_ptr<KeyMintDevice> createKeyMintDevice(KeyMintSecurityLevel securityLevel);
+
+ ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* _aidl_return) override;
+ ScopedAStatus verifyAuthorization(int64_t in_challenge, const HardwareAuthToken& in_token,
+ VerificationToken* _aidl_return) override;
+ ScopedAStatus addRngEntropy(const std::vector<uint8_t>& in_data) override;
+ ScopedAStatus generateKey(const std::vector<KeyParameter>& in_keyParams,
+ ByteArray* out_generatedKeyBlob,
+ KeyCharacteristics* out_generatedKeyCharacteristics,
+ std::vector<Certificate>* out_outCertChain) override;
+ ScopedAStatus importKey(const std::vector<KeyParameter>& in_inKeyParams,
+ KeyFormat in_inKeyFormat, const std::vector<uint8_t>& in_inKeyData,
+ ByteArray* out_outImportedKeyBlob,
+ KeyCharacteristics* out_outImportedKeyCharacteristics,
+ std::vector<Certificate>* out_outCertChain) override;
+ ScopedAStatus importWrappedKey(const std::vector<uint8_t>& in_inWrappedKeyData,
+ const std::vector<uint8_t>& in_inWrappingKeyBlob,
+ const std::vector<uint8_t>& in_inMaskingKey,
+ const std::vector<KeyParameter>& in_inUnwrappingParams,
+ int64_t in_inPasswordSid, int64_t in_inBiometricSid,
+ ByteArray* out_outImportedKeyBlob,
+ KeyCharacteristics* out_outImportedKeyCharacteristics) override;
+ ScopedAStatus upgradeKey(const std::vector<uint8_t>& in_inKeyBlobToUpgrade,
+ const std::vector<KeyParameter>& in_inUpgradeParams,
+ std::vector<uint8_t>* _aidl_return) override;
+ ScopedAStatus deleteKey(const std::vector<uint8_t>& in_inKeyBlob) override;
+ ScopedAStatus deleteAllKeys() override;
+ ScopedAStatus destroyAttestationIds() override;
+ ScopedAStatus begin(KeyPurpose in_inPurpose, const std::vector<uint8_t>& in_inKeyBlob,
+ const std::vector<KeyParameter>& in_inParams,
+ const HardwareAuthToken& in_inAuthToken,
+ BeginResult* _aidl_return) override;
+
+ // These are public to allow testing code to use them directly.
+ // This class should not be used publicly anyway.
+
+ std::variant<std::vector<Certificate>, V4_0_ErrorCode>
+ getCertificate(const std::vector<KeyParameter>& keyParams, const std::vector<uint8_t>& keyBlob);
+
+ void setNumFreeSlots(uint8_t numFreeSlots);
+
+ private:
+ std::optional<V4_0_ErrorCode> signCertificate(const std::vector<KeyParameter>& keyParams,
+ const std::vector<uint8_t>& keyBlob, X509* cert);
+};
+
+class KeyMintOperation : public aidl::android::hardware::security::keymint::BnKeyMintOperation {
+ private:
+ ::android::sp<Keymaster> mDevice;
+ uint64_t mOperationHandle;
+ OperationSlot mOperationSlot;
+
+ public:
+ KeyMintOperation(::android::sp<Keymaster> device, uint64_t operationHandle,
+ OperationSlots* slots, bool isActive)
+ : mDevice(device), mOperationHandle(operationHandle), mOperationSlot(slots, isActive) {}
+ ~KeyMintOperation();
+
+ ScopedAStatus update(const std::optional<KeyParameterArray>& in_inParams,
+ const std::optional<std::vector<uint8_t>>& in_input,
+ const std::optional<HardwareAuthToken>& in_inAuthToken,
+ const std::optional<VerificationToken>& in_inVerificationToken,
+ std::optional<KeyParameterArray>* out_outParams,
+ std::optional<ByteArray>* out_output, int32_t* _aidl_return);
+ ScopedAStatus finish(const std::optional<KeyParameterArray>& in_inParams,
+ const std::optional<std::vector<uint8_t>>& in_input,
+ const std::optional<std::vector<uint8_t>>& in_inSignature,
+ const std::optional<HardwareAuthToken>& in_authToken,
+ const std::optional<VerificationToken>& in_inVerificationToken,
+ std::optional<KeyParameterArray>* out_outParams,
+ std::vector<uint8_t>* _aidl_return);
+ ScopedAStatus abort();
+};
+
+class KeystoreCompatService : public BnKeystoreCompatService {
+ private:
+ std::unordered_map<KeyMintSecurityLevel, std::shared_ptr<IKeyMintDevice>> mDeviceCache;
+
+ public:
+ KeystoreCompatService() {}
+ ScopedAStatus getKeyMintDevice(KeyMintSecurityLevel in_securityLevel,
+ std::shared_ptr<IKeyMintDevice>* _aidl_return) override;
+};
diff --git a/keystore2/src/km_compat/km_compat_service.cpp b/keystore2/src/km_compat/km_compat_service.cpp
new file mode 100644
index 0000000..56d7909
--- /dev/null
+++ b/keystore2/src/km_compat/km_compat_service.cpp
@@ -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.
+ */
+
+#include "km_compat.h"
+#include <android/binder_manager.h>
+
+extern "C" {
+
+// Create a KeyMintDevice and add it as a service.
+int32_t addKeyMintDeviceService() {
+ std::shared_ptr<KeystoreCompatService> ti = ndk::SharedRefBase::make<KeystoreCompatService>();
+ const auto instanceName = "android.security.compat";
+ binder_status_t status = AServiceManager_addService(ti->asBinder().get(), instanceName);
+ return status;
+}
+}
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
new file mode 100644
index 0000000..b6a6baf
--- /dev/null
+++ b/keystore2/src/km_compat/lib.rs
@@ -0,0 +1,310 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// TODO: Once this is stable, remove this and document everything public.
+#![allow(missing_docs)]
+
+extern "C" {
+ fn addKeyMintDeviceService() -> i32;
+}
+
+pub fn add_keymint_device_service() -> i32 {
+ unsafe { addKeyMintDeviceService() }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use super::*;
+ use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, BeginResult::BeginResult, BlockMode::BlockMode, ByteArray::ByteArray,
+ Certificate::Certificate, Digest::Digest, ErrorCode::ErrorCode,
+ HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::IKeyMintDevice,
+ KeyCharacteristics::KeyCharacteristics, KeyFormat::KeyFormat, KeyParameter::KeyParameter,
+ KeyParameterArray::KeyParameterArray, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+ SecurityLevel::SecurityLevel, Tag::Tag,
+ };
+ use android_hardware_security_keymint::binder;
+ use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
+
+ fn get_device() -> Box<dyn IKeyMintDevice> {
+ add_keymint_device_service();
+ let compat_service: Box<dyn IKeystoreCompatService> =
+ binder::get_interface("android.security.compat").unwrap();
+ compat_service.getKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap()
+ }
+
+ #[test]
+ fn test_get_hardware_info() {
+ let legacy = get_device();
+ let hinfo = legacy.getHardwareInfo().unwrap();
+ assert_eq!(hinfo.versionNumber, 0);
+ assert_ne!(hinfo.securityLevel, SecurityLevel::SOFTWARE);
+ assert_eq!(hinfo.keyMintName, "RemoteKeymaster");
+ assert_eq!(hinfo.keyMintAuthorName, "Google");
+ }
+
+ #[test]
+ fn test_verify_authorization() {
+ use android_hardware_security_keymint::aidl::android::hardware::security::keymint::HardwareAuthToken::HardwareAuthToken;
+ let legacy = get_device();
+ let result = legacy.verifyAuthorization(0, &HardwareAuthToken::default());
+ assert!(result.is_err());
+ assert_eq!(result.unwrap_err().service_specific_error(), ErrorCode::UNIMPLEMENTED.0,);
+ }
+
+ #[test]
+ fn test_add_rng_entropy() {
+ let legacy = get_device();
+ let result = legacy.addRngEntropy(&[42; 16]);
+ assert!(result.is_ok(), "{:?}", result);
+ }
+
+ // TODO: If I only need the key itself, don't return the other things.
+ fn generate_key(
+ legacy: &dyn IKeyMintDevice,
+ kps: Vec<KeyParameter>,
+ ) -> (ByteArray, KeyCharacteristics, Vec<Certificate>) {
+ let mut blob = ByteArray { data: vec![] };
+ let mut characteristics = KeyCharacteristics::default();
+ let mut cert_chain = vec![];
+ let result = legacy.generateKey(&kps, &mut blob, &mut characteristics, &mut cert_chain);
+ assert!(result.is_ok(), "{:?}", result);
+ assert_ne!(blob.data.len(), 0);
+ (blob, characteristics, cert_chain)
+ }
+
+ fn generate_rsa_key(legacy: &dyn IKeyMintDevice, encrypt: bool, attest: bool) -> Vec<u8> {
+ let mut kps = vec![
+ KeyParameter { tag: Tag::ALGORITHM, integer: Algorithm::RSA.0, ..Default::default() },
+ KeyParameter { tag: Tag::KEY_SIZE, integer: 2048, ..Default::default() },
+ KeyParameter {
+ tag: Tag::RSA_PUBLIC_EXPONENT,
+ longInteger: 65537,
+ ..Default::default()
+ },
+ KeyParameter { tag: Tag::DIGEST, integer: Digest::SHA_2_256.0, ..Default::default() },
+ KeyParameter {
+ tag: Tag::PADDING,
+ integer: PaddingMode::RSA_PSS.0,
+ ..Default::default()
+ },
+ KeyParameter { tag: Tag::NO_AUTH_REQUIRED, boolValue: true, ..Default::default() },
+ KeyParameter { tag: Tag::PURPOSE, integer: KeyPurpose::SIGN.0, ..Default::default() },
+ ];
+ if encrypt {
+ kps.push(KeyParameter {
+ tag: Tag::PURPOSE,
+ integer: KeyPurpose::ENCRYPT.0,
+ ..Default::default()
+ });
+ }
+ if attest {
+ kps.push(KeyParameter {
+ tag: Tag::ATTESTATION_CHALLENGE,
+ blob: vec![42; 8],
+ ..Default::default()
+ });
+ kps.push(KeyParameter {
+ tag: Tag::ATTESTATION_APPLICATION_ID,
+ blob: vec![42; 8],
+ ..Default::default()
+ });
+ }
+ let (blob, _, cert_chain) = generate_key(legacy, kps);
+ if attest {
+ // TODO: Will this always be greater than 1?
+ assert!(cert_chain.len() > 1);
+ } else {
+ assert_eq!(cert_chain.len(), 1);
+ }
+ blob.data
+ }
+
+ #[test]
+ fn test_generate_key_no_encrypt() {
+ let legacy = get_device();
+ generate_rsa_key(legacy.as_ref(), false, false);
+ }
+
+ #[test]
+ fn test_generate_key_encrypt() {
+ let legacy = get_device();
+ generate_rsa_key(legacy.as_ref(), true, false);
+ }
+
+ #[test]
+ fn test_generate_key_attested() {
+ let legacy = get_device();
+ generate_rsa_key(legacy.as_ref(), false, true);
+ }
+
+ #[test]
+ fn test_import_key() {
+ let legacy = get_device();
+ let kps =
+ [KeyParameter { tag: Tag::ALGORITHM, integer: Algorithm::AES.0, ..Default::default() }];
+ let kf = KeyFormat::RAW;
+ let kd = [0; 16];
+ let mut blob = ByteArray { data: vec![] };
+ let mut characteristics = KeyCharacteristics::default();
+ let mut cert_chain = vec![];
+ let result =
+ legacy.importKey(&kps, kf, &kd, &mut blob, &mut characteristics, &mut cert_chain);
+ assert!(result.is_ok(), "{:?}", result);
+ assert_ne!(blob.data.len(), 0);
+ assert_eq!(cert_chain.len(), 0);
+ }
+
+ #[test]
+ fn test_import_wrapped_key() {
+ let legacy = get_device();
+ let mut blob = ByteArray { data: vec![] };
+ let mut characteristics = KeyCharacteristics::default();
+ let result =
+ legacy.importWrappedKey(&[], &[], &[], &[], 0, 0, &mut blob, &mut characteristics);
+ // TODO: This test seems to fail on cuttlefish. How should I test it?
+ assert!(result.is_err());
+ }
+
+ #[test]
+ fn test_upgrade_key() {
+ let legacy = get_device();
+ let blob = generate_rsa_key(legacy.as_ref(), false, false);
+ let result = legacy.upgradeKey(&blob, &[]);
+ // TODO: This test seems to fail on cuttlefish. How should I test it?
+ assert!(result.is_err());
+ }
+
+ #[test]
+ fn test_delete_key() {
+ let legacy = get_device();
+ let blob = generate_rsa_key(legacy.as_ref(), false, false);
+ let result = legacy.deleteKey(&blob);
+ assert!(result.is_ok(), "{:?}", result);
+ }
+
+ #[test]
+ fn test_delete_all_keys() {
+ let legacy = get_device();
+ let result = legacy.deleteAllKeys();
+ assert!(result.is_ok(), "{:?}", result);
+ }
+
+ #[test]
+ fn test_destroy_attestation_ids() {
+ let legacy = get_device();
+ let result = legacy.destroyAttestationIds();
+ assert!(result.is_err());
+ assert_eq!(result.unwrap_err().service_specific_error(), ErrorCode::UNIMPLEMENTED.0,);
+ }
+
+ fn generate_aes_key(legacy: &dyn IKeyMintDevice) -> Vec<u8> {
+ let kps = vec![
+ KeyParameter { tag: Tag::ALGORITHM, integer: Algorithm::AES.0, ..Default::default() },
+ KeyParameter { tag: Tag::KEY_SIZE, integer: 128, ..Default::default() },
+ KeyParameter { tag: Tag::BLOCK_MODE, integer: BlockMode::CBC.0, ..Default::default() },
+ KeyParameter { tag: Tag::PADDING, integer: PaddingMode::NONE.0, ..Default::default() },
+ KeyParameter { tag: Tag::NO_AUTH_REQUIRED, boolValue: true, ..Default::default() },
+ KeyParameter {
+ tag: Tag::PURPOSE,
+ integer: KeyPurpose::ENCRYPT.0,
+ ..Default::default()
+ },
+ KeyParameter {
+ tag: Tag::PURPOSE,
+ integer: KeyPurpose::DECRYPT.0,
+ ..Default::default()
+ },
+ ];
+ let (blob, _, cert_chain) = generate_key(legacy, kps);
+ assert_eq!(cert_chain.len(), 0);
+ blob.data
+ }
+
+ fn begin(
+ legacy: &dyn IKeyMintDevice,
+ blob: &[u8],
+ purpose: KeyPurpose,
+ extra_params: Option<Vec<KeyParameter>>,
+ ) -> BeginResult {
+ let mut kps = vec![
+ KeyParameter { tag: Tag::BLOCK_MODE, integer: BlockMode::CBC.0, ..Default::default() },
+ KeyParameter { tag: Tag::PADDING, integer: PaddingMode::NONE.0, ..Default::default() },
+ ];
+ if let Some(mut extras) = extra_params {
+ kps.append(&mut extras);
+ }
+ let result = legacy.begin(purpose, &blob, &kps, &HardwareAuthToken::default());
+ assert!(result.is_ok(), "{:?}", result);
+ result.unwrap()
+ }
+
+ #[test]
+ fn test_begin_abort() {
+ let legacy = get_device();
+ let blob = generate_aes_key(legacy.as_ref());
+ let begin_result = begin(legacy.as_ref(), &blob, KeyPurpose::ENCRYPT, None);
+ let operation = begin_result.operation.unwrap();
+ let result = operation.abort();
+ assert!(result.is_ok(), "{:?}", result);
+ let result = operation.abort();
+ assert!(result.is_err());
+ }
+
+ #[test]
+ fn test_begin_update_finish() {
+ let legacy = get_device();
+ let blob = generate_aes_key(legacy.as_ref());
+
+ let begin_result = begin(legacy.as_ref(), &blob, KeyPurpose::ENCRYPT, None);
+ let operation = begin_result.operation.unwrap();
+ let params = KeyParameterArray {
+ params: vec![KeyParameter {
+ tag: Tag::ASSOCIATED_DATA,
+ blob: b"foobar".to_vec(),
+ ..Default::default()
+ }],
+ };
+ let message = [42; 128];
+ let mut out_params = None;
+ let result =
+ operation.finish(Some(¶ms), Some(&message), None, None, None, &mut out_params);
+ assert!(result.is_ok(), "{:?}", result);
+ let ciphertext = result.unwrap();
+ assert!(!ciphertext.is_empty());
+ assert!(out_params.is_some());
+
+ let begin_result =
+ begin(legacy.as_ref(), &blob, KeyPurpose::DECRYPT, Some(begin_result.params));
+ let operation = begin_result.operation.unwrap();
+ let mut out_params = None;
+ let mut output = None;
+ let result = operation.update(
+ Some(¶ms),
+ Some(&ciphertext),
+ None,
+ None,
+ &mut out_params,
+ &mut output,
+ );
+ assert!(result.is_ok(), "{:?}", result);
+ assert_eq!(result.unwrap(), message.len() as i32);
+ assert!(output.is_some());
+ assert_eq!(output.unwrap().data, message.to_vec());
+ let result = operation.finish(Some(¶ms), None, None, None, None, &mut out_params);
+ assert!(result.is_ok(), "{:?}", result);
+ assert!(out_params.is_some());
+ }
+}
diff --git a/keystore2/src/km_compat/slot_test.cpp b/keystore2/src/km_compat/slot_test.cpp
new file mode 100644
index 0000000..e56fb37
--- /dev/null
+++ b/keystore2/src/km_compat/slot_test.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "km_compat.h"
+#include <keymint_support/keymint_tags.h>
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/keymint/IKeyMintOperation.h>
+
+using ::aidl::android::hardware::security::keymint::Algorithm;
+using ::aidl::android::hardware::security::keymint::BlockMode;
+using ::aidl::android::hardware::security::keymint::ByteArray;
+using ::aidl::android::hardware::security::keymint::Certificate;
+using ::aidl::android::hardware::security::keymint::Digest;
+using ::aidl::android::hardware::security::keymint::ErrorCode;
+using ::aidl::android::hardware::security::keymint::IKeyMintOperation;
+using ::aidl::android::hardware::security::keymint::KeyCharacteristics;
+using ::aidl::android::hardware::security::keymint::KeyPurpose;
+using ::aidl::android::hardware::security::keymint::PaddingMode;
+using ::aidl::android::hardware::security::keymint::SecurityLevel;
+
+namespace KMV1 = ::aidl::android::hardware::security::keymint;
+
+static std::vector<uint8_t> generateAESKey(std::shared_ptr<KeyMintDevice> device) {
+ auto keyParams = std::vector<KeyParameter>({
+ KMV1::makeKeyParameter(KMV1::TAG_ALGORITHM, Algorithm::AES),
+ KMV1::makeKeyParameter(KMV1::TAG_KEY_SIZE, 128),
+ KMV1::makeKeyParameter(KMV1::TAG_BLOCK_MODE, BlockMode::CBC),
+ KMV1::makeKeyParameter(KMV1::TAG_PADDING, PaddingMode::NONE),
+ KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED, true),
+ KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::ENCRYPT),
+ KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::DECRYPT),
+ });
+ ByteArray blob;
+ KeyCharacteristics characteristics;
+ std::vector<Certificate> cert;
+ auto status = device->generateKey(keyParams, &blob, &characteristics, &cert);
+ if (!status.isOk()) {
+ return {};
+ }
+ return blob.data;
+}
+
+static std::variant<BeginResult, ScopedAStatus> begin(std::shared_ptr<KeyMintDevice> device,
+ bool valid) {
+ auto blob = generateAESKey(device);
+ std::vector<KeyParameter> kps;
+ if (valid) {
+ kps.push_back(KMV1::makeKeyParameter(KMV1::TAG_BLOCK_MODE, BlockMode::CBC));
+ kps.push_back(KMV1::makeKeyParameter(KMV1::TAG_PADDING, PaddingMode::NONE));
+ }
+ BeginResult beginResult;
+ auto status = device->begin(KeyPurpose::ENCRYPT, blob, kps, HardwareAuthToken(), &beginResult);
+ if (!status.isOk()) {
+ return status;
+ }
+ return beginResult;
+}
+
+static const int NUM_SLOTS = 2;
+
+TEST(SlotTest, TestSlots) {
+ static std::shared_ptr<KeyMintDevice> device =
+ KeyMintDevice::createKeyMintDevice(SecurityLevel::TRUSTED_ENVIRONMENT);
+ device->setNumFreeSlots(NUM_SLOTS);
+
+ // A begin() that returns a failure should not use a slot.
+ auto result = begin(device, false);
+ ASSERT_TRUE(std::holds_alternative<ScopedAStatus>(result));
+
+ // Fill up all the slots.
+ std::vector<std::shared_ptr<IKeyMintOperation>> operations;
+ for (int i = 0; i < NUM_SLOTS; i++) {
+ auto result = begin(device, true);
+ ASSERT_TRUE(std::holds_alternative<BeginResult>(result));
+ operations.push_back(std::get<BeginResult>(result).operation);
+ }
+
+ // We should not be able to create a new operation.
+ result = begin(device, true);
+ ASSERT_TRUE(std::holds_alternative<ScopedAStatus>(result));
+ ASSERT_EQ(std::get<ScopedAStatus>(result).getServiceSpecificError(),
+ static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
+
+ // TODO: I'm not sure how to generate a failing update call to test that.
+
+ // Calling finish should free up a slot.
+ auto last = operations.back();
+ operations.pop_back();
+ std::optional<KeyParameterArray> kpa;
+ std::vector<uint8_t> byteVec;
+ auto status = last->finish(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
+ &kpa, &byteVec);
+ ASSERT_TRUE(status.isOk());
+ result = begin(device, true);
+ ASSERT_TRUE(std::holds_alternative<BeginResult>(result));
+ operations.push_back(std::get<BeginResult>(result).operation);
+
+ // Calling finish and abort on an already-finished operation should not free up another slot.
+ status = last->finish(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
+ &kpa, &byteVec);
+ ASSERT_TRUE(!status.isOk());
+ status = last->abort();
+ ASSERT_TRUE(!status.isOk());
+ result = begin(device, true);
+ ASSERT_TRUE(std::holds_alternative<ScopedAStatus>(result));
+ ASSERT_EQ(std::get<ScopedAStatus>(result).getServiceSpecificError(),
+ static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
+
+ // Calling abort should free up a slot.
+ last = operations.back();
+ operations.pop_back();
+ status = last->abort();
+ ASSERT_TRUE(status.isOk());
+ result = begin(device, true);
+ ASSERT_TRUE(std::holds_alternative<BeginResult>(result));
+ operations.push_back(std::get<BeginResult>(result).operation);
+
+ // Calling finish and abort on an already-aborted operation should not free up another slot.
+ status = last->finish(std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,
+ &kpa, &byteVec);
+ ASSERT_TRUE(!status.isOk());
+ status = last->abort();
+ ASSERT_TRUE(!status.isOk());
+ result = begin(device, true);
+ ASSERT_TRUE(std::holds_alternative<ScopedAStatus>(result));
+ ASSERT_EQ(std::get<ScopedAStatus>(result).getServiceSpecificError(),
+ static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
+
+ // Generating a certificate with signWith also uses a slot.
+ auto kps = std::vector<KeyParameter>({
+ KMV1::makeKeyParameter(KMV1::TAG_ALGORITHM, Algorithm::RSA),
+ KMV1::makeKeyParameter(KMV1::TAG_KEY_SIZE, 2048),
+ KMV1::makeKeyParameter(KMV1::TAG_RSA_PUBLIC_EXPONENT, 65537),
+ KMV1::makeKeyParameter(KMV1::TAG_DIGEST, Digest::SHA_2_256),
+ KMV1::makeKeyParameter(KMV1::TAG_PURPOSE, KeyPurpose::SIGN),
+ KMV1::makeKeyParameter(KMV1::TAG_NO_AUTH_REQUIRED, true),
+ });
+ ByteArray blob;
+ KeyCharacteristics characteristics;
+ std::vector<Certificate> cert;
+ status = device->generateKey(kps, &blob, &characteristics, &cert);
+ ASSERT_TRUE(!status.isOk());
+ ASSERT_EQ(status.getServiceSpecificError(),
+ static_cast<int32_t>(ErrorCode::TOO_MANY_OPERATIONS));
+ // But generating a certificate with signCert does not use a slot.
+ kps.pop_back();
+ status = device->generateKey(kps, &blob, &characteristics, &cert);
+ ASSERT_TRUE(status.isOk());
+
+ // Destructing operations should free up their slots.
+ operations.clear();
+ result = begin(device, true);
+ ASSERT_TRUE(std::holds_alternative<BeginResult>(result));
+}