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(&params), Some(&message), None, None, None, &mut out_params);
+        assert!(result.is_ok(), "{:?}", result);
+        let ciphertext = result.unwrap();
+        assert!(!ciphertext.is_empty());
+        assert!(out_params.is_some());
+
+        let begin_result =
+            begin(legacy.as_ref(), &blob, KeyPurpose::DECRYPT, Some(begin_result.params));
+        let operation = begin_result.operation.unwrap();
+        let mut out_params = None;
+        let mut output = None;
+        let result = operation.update(
+            Some(&params),
+            Some(&ciphertext),
+            None,
+            None,
+            &mut out_params,
+            &mut output,
+        );
+        assert!(result.is_ok(), "{:?}", result);
+        assert_eq!(result.unwrap(), message.len() as i32);
+        assert!(output.is_some());
+        assert_eq!(output.unwrap().data, message.to_vec());
+        let result = operation.finish(Some(&params), None, None, None, None, &mut out_params);
+        assert!(result.is_ok(), "{:?}", result);
+        assert!(out_params.is_some());
+    }
+}
diff --git a/keystore2/src/km_compat/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));
+}