/*
 * Copyright (C) 2022 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.
 */

// The tests in this file are intended to be run manually, to allow testing of whether
// keyblob upgrade works correctly.  The manual procedure is roughly:
//
// 1) Run the "*Before*" subset of these tests with the `--keyblob_dir <dir>` command-line argument
//    so that keyblobs are saved to a directory on the device:
//
//      VtsAidlKeyMintTargetTest --gtest_filter="*KeyBlobUpgradeTest*Before*" \
//                               --keyblob_dir /data/local/tmp/keymint-blobs
//
//    All tests should pass, and the `UpgradeKeyBlobs` test should indicate that no keyblob
//    upgrades were needed.
//
// 2) Copy the generated keyblobs off the device into a safe place.
//
//      adb pull /data/local/tmp/keymint-blobs
//
// 3) Upgrade the device to a new version.
//
// 4) Push the saved keyblobs back onto the upgraded device.
//
//      adb push keymint-blobs /data/local/tmp/keymint-blobs
//
// 5) Run the "*After*" subset of these tests, with the following command-line arguments
//    `--keyblob_dir <dir>`: pointing to the directory with the keyblobs.
//    `--expect_upgrade {yes|no}` (Optional): To specify if users expect an upgrade on the keyBlobs,
//                                            will be "yes" by default.
//
//      VtsAidlKeyMintTargetTest --gtest_filter="*KeyBlobUpgradeTest*After*" \
//                               --keyblob_dir /data/local/tmp/keymint-blobs \
//                               --expect_upgrade {yes|no}
//
//    (Note that this skips the `CreateKeyBlobs` test, which would otherwise replace the saved
//    keyblobs with freshly generated ones.).
//
//    All tests should pass, and the `UpgradeKeyBlobs` test should have output that matches whether
//    upgrade was expected or not.

#define LOG_TAG "keymint_1_test"
#include <cutils/log.h>

#include <algorithm>
#include <fstream>
#include <iostream>

#include <unistd.h>

#include <openssl/curve25519.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/x509v3.h>

#include "KeyMintAidlTestBase.h"

using aidl::android::hardware::security::keymint::KeyCharacteristics;

namespace aidl::android::hardware::security::keymint::test {

namespace {

// Names for individual key types to create and use.  Note that some the names
// induce specific behaviour, as indicated by the functions below.

std::vector<std::string> keyblob_names_tee = {
        "aes-key",        "aes-key-rr",      "des-key",           "hmac-key",
        "rsa-key",        "p256-key",        "ed25519-key",       "x25519-key",
        "rsa-attest-key", "p256-attest-key", "ed25519-attest-key"};

std::vector<std::string> keyblob_names_tee_no_25519 = {
        "aes-key", "aes-key-rr", "des-key",        "hmac-key",
        "rsa-key", "p256-key",   "rsa-attest-key", "p256-attest-key"};

std::vector<std::string> keyblob_names_sb = {"aes-key",        "aes-key-rr",     "des-key",
                                             "hmac-key",       "rsa-key",        "p256-key",
                                             "rsa-attest-key", "p256-attest-key"};

// Helper functions to detect particular key types based on the name.
bool requires_attest_key(const std::string& name) {
    return name.find("-attest-key") != std::string::npos;
}

bool requires_rr(const std::string& name) {
    return name.find("-rr") != std::string::npos;
}

bool is_asymmetric(const std::string& name) {
    return (name.find("rsa") != std::string::npos || name.find("25519") != std::string::npos ||
            name.find("p256") != std::string::npos);
}

std::string keyblob_subdir(const std::string& keyblob_dir, const std::string& full_name,
                           bool create) {
    if (keyblob_dir.empty()) {
        return "";
    }

    // Use a subdirectory for the specific instance, so two different KeyMint instances won't
    // clash with each other.
    size_t found = full_name.find_last_of('/');
    std::string subdir = keyblob_dir + "/" + full_name.substr(found + 1);

    if (create) {
        mkdir(keyblob_dir.c_str(), 0777);
        mkdir(subdir.c_str(), 0777);
    }
    return subdir;
}

void save_keyblob(const std::string& subdir, const std::string& name,
                  const vector<uint8_t>& keyblob,
                  const std::vector<KeyCharacteristics>& key_characteristics) {
    // Write the keyblob out to a file.
    std::string blobname(subdir + "/" + name + ".keyblob");
    std::ofstream blobfile(blobname, std::ios::out | std::ios::trunc | std::ios::binary);
    blobfile.write(reinterpret_cast<const char*>(keyblob.data()), keyblob.size());
    blobfile.close();

    // Dump the characteristics too.
    std::string charsname(subdir + "/" + name + ".chars");
    std::ofstream charsfile(charsname, std::ios::out | std::ios::trunc);
    charsfile << "{\n";
    for (const auto& characteristic : key_characteristics) {
        charsfile << "  " << characteristic.toString() << "\n";
    }
    charsfile << "}\n";
    charsfile.close();

    // Also write out a hexdump of the keyblob for convenience.
    std::string hexname(subdir + "/" + name + ".hex");
    std::ofstream hexfile(hexname, std::ios::out | std::ios::trunc);
    hexfile << bin2hex(keyblob) << "\n";
    hexfile.close();
}

void save_keyblob_and_cert(const std::string& subdir, const std::string& name,
                           const vector<uint8_t>& keyblob,
                           const std::vector<KeyCharacteristics>& key_characteristics,
                           const std::vector<Certificate>& cert_chain) {
    save_keyblob(subdir, name, keyblob, key_characteristics);

    if (is_asymmetric(name)) {
        // Dump the leaf certificate as DER.
        if (cert_chain.empty()) {
            FAIL() << "No cert available for " << name;
        } else {
            const vector<uint8_t>& certdata = cert_chain[0].encodedCertificate;
            std::string certname(subdir + "/" + name + ".cert");
            std::ofstream certfile(certname, std::ios::out | std::ios::trunc | std::ios::binary);
            certfile.write(reinterpret_cast<const char*>(certdata.data()), certdata.size());
            certfile.close();
        }
    }
}

void delete_keyblob(const std::string& subdir, const std::string& name) {
    std::string blobname(subdir + "/" + name + ".keyblob");
    unlink(blobname.c_str());
    std::string charsname(subdir + "/" + name + ".chars");
    unlink(charsname.c_str());
    std::string hexname(subdir + "/" + name + ".hex");
    unlink(hexname.c_str());
    std::string certname(subdir + "/" + name + ".cert");
    unlink(certname.c_str());
}

std::vector<uint8_t> load_file(const std::string& subdir, const std::string& name,
                               const std::string& suffix) {
    std::string blobname(subdir + "/" + name + suffix);
    std::ifstream blobfile(blobname, std::ios::in | std::ios::binary);

    std::vector<uint8_t> data((std::istreambuf_iterator<char>(blobfile)),
                              std::istreambuf_iterator<char>());
    return data;
}

std::vector<uint8_t> load_keyblob(const std::string& subdir, const std::string& name) {
    return load_file(subdir, name, ".keyblob");
}

std::vector<uint8_t> load_cert(const std::string& subdir, const std::string& name) {
    return load_file(subdir, name, ".cert");
}

}  // namespace

class KeyBlobUpgradeTest : public KeyMintAidlTestBase {
  protected:
    const std::vector<std::string>& keyblob_names() {
        if (SecLevel() == SecurityLevel::STRONGBOX) {
            return keyblob_names_sb;
        } else if (!Curve25519Supported()) {
            return keyblob_names_tee_no_25519;
        } else {
            return keyblob_names_tee;
        }
    }

    void UpgradeKeyBlobs(bool expectUpgrade) {
        std::string subdir = keyblob_subdir(keyblob_dir, GetParam(), /* create? */ false);
        if (subdir.empty()) {
            GTEST_SKIP() << "No keyblob directory provided";
        }

        for (std::string name : keyblob_names()) {
            if (requires_attest_key(name) && shouldSkipAttestKeyTest()) {
                std::cerr << "Skipping variant '" << name
                          << "' which requires ATTEST_KEY support that has been waivered\n";
                continue;
            }
            for (bool with_hidden : {false, true}) {
                std::string app_id;
                std::string app_data;
                auto builder = AuthorizationSetBuilder();
                if (with_hidden) {
                    // Build a variant keyblob that requires app_id/app_data
                    app_id = "appid";
                    app_data = "appdata";
                    builder.Authorization(TAG_APPLICATION_ID, "appid")
                            .Authorization(TAG_APPLICATION_DATA, "appdata");
                    name += "-hidden";
                }
                SCOPED_TRACE(testing::Message() << name);

                // Load the old format keyblob.
                std::vector<uint8_t> keyblob = load_keyblob(subdir, name);
                if (keyblob.empty()) {
                    if (requires_rr(name)) {
                        std::cerr << "Skipping missing keyblob file '" << name
                                  << "', assuming rollback resistance unavailable\n";
                    } else {
                        FAIL() << "Missing keyblob file '" << name << "'";
                    }
                    continue;
                }

                // An upgrade will either produce a new keyblob or no data (if upgrade isn't
                // needed).
                std::vector<uint8_t> upgraded_keyblob;
                Status result =
                        keymint_->upgradeKey(keyblob, builder.vector_data(), &upgraded_keyblob);
                ASSERT_EQ(ErrorCode::OK, GetReturnErrorCode(result));

                if (upgraded_keyblob.empty()) {
                    std::cerr << "Keyblob '" << name << "' did not require upgrade\n";
                    EXPECT_FALSE(expectUpgrade)
                            << "Keyblob '" << name << "' unexpectedly left as-is";
                } else {
                    // Ensure the old format keyblob is deleted (so any secure deletion data is
                    // cleaned up).
                    EXPECT_EQ(ErrorCode::OK, DeleteKey(&keyblob));

                    std::vector<uint8_t> app_id_v(app_id.begin(), app_id.end());
                    std::vector<uint8_t> app_data_v(app_data.begin(), app_data.end());
                    std::vector<KeyCharacteristics> key_characteristics;
                    result = keymint_->getKeyCharacteristics(upgraded_keyblob, app_id_v, app_data_v,
                                                             &key_characteristics);
                    ASSERT_EQ(ErrorCode::OK, GetReturnErrorCode(result))
                            << "Failed getKeyCharacteristics() after upgrade";

                    save_keyblob(subdir, name, upgraded_keyblob, key_characteristics);
                    // Cert file is left unchanged.
                    std::cerr << "Keyblob '" << name << "' upgraded\n";
                    EXPECT_TRUE(expectUpgrade) << "Keyblob '" << name << "' unexpectedly upgraded";
                }
            }
        }
    }
};

// To save off keyblobs before upgrade, use:
//
//    VtsAidlKeyMintTargetTest --gtest_filter="*KeyBlobUpgradeTest.CreateKeyBlobs*" \
//                             --keyblob_dir /data/local/tmp/keymint-blobs
//
// Then copy the contents of the /data/local/tmp/keymint-blobs/ directory somewhere safe:
//
//    adb pull /data/local/tmp/keymint-blobs/
TEST_P(KeyBlobUpgradeTest, CreateKeyBlobsBefore) {
    std::string subdir = keyblob_subdir(keyblob_dir, GetParam(), /* create? */ true);

    std::map<const std::string, AuthorizationSetBuilder> keys_info = {
            {"aes-key", AuthorizationSetBuilder()
                                .AesEncryptionKey(256)
                                .BlockMode(BlockMode::ECB)
                                .Padding(PaddingMode::PKCS7)
                                .Authorization(TAG_NO_AUTH_REQUIRED)},
            {"aes-key-rr", AuthorizationSetBuilder()
                                   .AesEncryptionKey(256)
                                   .BlockMode(BlockMode::ECB)
                                   .Padding(PaddingMode::PKCS7)
                                   .Authorization(TAG_ROLLBACK_RESISTANCE)
                                   .Authorization(TAG_NO_AUTH_REQUIRED)},
            {"des-key", AuthorizationSetBuilder()
                                .TripleDesEncryptionKey(168)
                                .BlockMode(BlockMode::ECB)
                                .Padding(PaddingMode::PKCS7)
                                .Authorization(TAG_NO_AUTH_REQUIRED)},
            {"hmac-key", AuthorizationSetBuilder()
                                 .HmacKey(128)
                                 .Digest(Digest::SHA_2_256)
                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)
                                 .Authorization(TAG_NO_AUTH_REQUIRED)},
            {"rsa-key", AuthorizationSetBuilder()
                                .RsaEncryptionKey(2048, 65537)
                                .Authorization(TAG_PURPOSE, KeyPurpose::SIGN)
                                .Digest(Digest::NONE)
                                .Digest(Digest::SHA_2_256)
                                .Padding(PaddingMode::NONE)
                                .Authorization(TAG_NO_AUTH_REQUIRED)
                                .SetDefaultValidity()},
            {
                    "p256-key",
                    AuthorizationSetBuilder()
                            .EcdsaSigningKey(EcCurve::P_256)
                            .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
                            .Digest(Digest::NONE)
                            .Digest(Digest::SHA_2_256)
                            .Authorization(TAG_NO_AUTH_REQUIRED)
                            .SetDefaultValidity(),
            },
            {
                    "ed25519-key",
                    AuthorizationSetBuilder()
                            .EcdsaSigningKey(EcCurve::CURVE_25519)
                            .Digest(Digest::NONE)
                            .Authorization(TAG_NO_AUTH_REQUIRED)
                            .SetDefaultValidity(),
            },
            {"x25519-key", AuthorizationSetBuilder()
                                   .Authorization(TAG_EC_CURVE, EcCurve::CURVE_25519)
                                   .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
                                   .Authorization(TAG_ALGORITHM, Algorithm::EC)
                                   .Authorization(TAG_NO_AUTH_REQUIRED)
                                   .SetDefaultValidity()},
            {"rsa-attest-key", AuthorizationSetBuilder()
                                       .RsaKey(2048, 65537)
                                       .AttestKey()
                                       .Authorization(TAG_NO_AUTH_REQUIRED)
                                       .SetDefaultValidity()},
            {
                    "p256-attest-key",
                    AuthorizationSetBuilder()
                            .EcdsaKey(EcCurve::P_256)
                            .AttestKey()
                            .Authorization(TAG_NO_AUTH_REQUIRED)
                            .SetDefaultValidity(),
            },
            {
                    "ed25519-attest-key",
                    AuthorizationSetBuilder()
                            .EcdsaKey(EcCurve::CURVE_25519)
                            .AttestKey()
                            .Authorization(TAG_NO_AUTH_REQUIRED)
                            .SetDefaultValidity(),
            }};

    for (std::string name : keyblob_names()) {
        if (requires_attest_key(name) && shouldSkipAttestKeyTest()) {
            std::cerr << "Skipping variant '" << name
                      << "' which requires ATTEST_KEY support that has been waivered\n";
            continue;
        }
        auto entry = keys_info.find(name);
        ASSERT_NE(entry, keys_info.end()) << "no builder for " << name;
        auto builder = entry->second;
        for (bool with_hidden : {false, true}) {
            if (with_hidden) {
                // Build a variant keyblob that requires app_id/app_data
                builder.Authorization(TAG_APPLICATION_ID, "appid")
                        .Authorization(TAG_APPLICATION_DATA, "appdata");
                name += "-hidden";
            }
            SCOPED_TRACE(testing::Message() << name);

            vector<uint8_t> keyblob;
            vector<KeyCharacteristics> key_characteristics;
            vector<Certificate> cert_chain;
            auto result =
                    GenerateKey(builder, std::nullopt, &keyblob, &key_characteristics, &cert_chain);

            if (requires_rr(name) && result == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE) {
                // Rollback resistance support is optional.
                std::cerr << "Skipping '" << name << "' key as rollback resistance unavailable\n";
                continue;
            }
            ASSERT_EQ(ErrorCode::OK, result) << " failed for " << name;

            if (!subdir.empty()) {
                save_keyblob_and_cert(subdir, name, keyblob, key_characteristics, cert_chain);
            }
        }
    }

    if (!subdir.empty()) {
        std::cerr << "Save generated keyblobs with:\n\n    adb pull " << keyblob_dir << "\n\n";
    }
}

TEST_P(KeyBlobUpgradeTest, UpgradeKeyBlobsBefore) {
    // Check that attempting to upgrade valid keyblobs does nothing.
    UpgradeKeyBlobs(/* expectUpgrade= */ false);
}

// To run this test:
//
// - save off some keyblobs before upgrade as per the CreateKeyBlobs test above.
// - upgrade the device to a version that should trigger keyblob upgrade (e.g. different patchlevel)
// - put the saved keyblobs back onto the upgraded device:
//
//     adb push keymint-blobs /data/local/tmp/keymint-blobs
//
// - run the test with:
//
//     VtsAidlKeyMintTargetTest --gtest_filter="*KeyBlobUpgradeTest.UpgradeKeyBlobsAfter*" \
//                              --keyblob_dir /data/local/tmp/keymint-blobs
//                              --expect_upgrade {yes|no}
//
// - this replaces the keyblob contents in that directory; if needed, save the upgraded keyblobs
//   with:
//      adb pull /data/local/tmp/keymint-blobs/
TEST_P(KeyBlobUpgradeTest, UpgradeKeyBlobsAfter) {
    bool expectUpgrade = true;  // this test expects upgrade to happen by default
    if (expect_upgrade.has_value() && expect_upgrade == false) {
        std::cout << "Not expecting key upgrade due to --expect_upgrade no\n";
        expectUpgrade = false;
    }
    UpgradeKeyBlobs(expectUpgrade);
}

// To run this test:
//
// - save off some keyblobs before upgrade as per the CreateKeyBlobs test above
// - if needed, upgrade the saved keyblobs as per the UpgradeKeyBlobs test above
// - run the test with:
//
//     VtsAidlKeyMintTargetTest --gtest_filter="*KeyBlobUpgradeTest.UseKeyBlobs*" \
//                              --keyblob_dir /data/local/tmp/keymint-blobs
TEST_P(KeyBlobUpgradeTest, UseKeyBlobsBeforeOrAfter) {
    std::string subdir = keyblob_subdir(keyblob_dir, GetParam(), /* create? */ false);
    if (subdir.empty()) {
        GTEST_SKIP() << "No keyblob directory provided with (e.g.) --keyblob_dir "
                        "/data/local/tmp/keymint-blobs";
    }

    for (std::string name : keyblob_names()) {
        if (requires_attest_key(name) && shouldSkipAttestKeyTest()) {
            std::cerr << "Skipping variant '" << name
                      << "' which requires ATTEST_KEY support that has been waivered\n";
            continue;
        }
        for (bool with_hidden : {false, true}) {
            auto builder = AuthorizationSetBuilder();
            if (with_hidden) {
                // Build a variant keyblob that requires app_id/app_data
                builder.Authorization(TAG_APPLICATION_ID, "appid")
                        .Authorization(TAG_APPLICATION_DATA, "appdata");
                name += "-hidden";
            }
            SCOPED_TRACE(testing::Message() << name);
            std::vector<uint8_t> keyblob = load_keyblob(subdir, name);
            if (keyblob.empty()) {
                if (requires_rr(name)) {
                    std::cerr << "Skipping missing keyblob file '" << name
                              << "', assuming rollback resistance unavailable\n";
                } else {
                    FAIL() << "Missing keyblob file '" << name << "'";
                }
                continue;
            }

            std::vector<uint8_t> cert;
            if (is_asymmetric(name)) {
                cert = load_cert(subdir, name);
            }

            // Perform an algorithm-specific operation with the keyblob.
            string message = "Hello World!";
            AuthorizationSet out_params;
            if (name.find("aes-key") != std::string::npos) {
                builder.BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
                string ciphertext = EncryptMessage(keyblob, message, builder, &out_params);
                string plaintext = DecryptMessage(keyblob, ciphertext, builder);
                EXPECT_EQ(message, plaintext);
            } else if (name.find("des-key") != std::string::npos) {
                builder.BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
                string ciphertext = EncryptMessage(keyblob, message, builder, &out_params);
                string plaintext = DecryptMessage(keyblob, ciphertext, builder);
                EXPECT_EQ(message, plaintext);
            } else if (name.find("hmac-key") != std::string::npos) {
                builder.Digest(Digest::SHA_2_256);
                auto sign_builder = builder;
                sign_builder.Authorization(TAG_MAC_LENGTH, 128);
                string tag = SignMessage(keyblob, message, sign_builder);
                VerifyMessage(keyblob, message, tag, builder);
            } else if (name.find("rsa-key") != std::string::npos) {
                builder.Digest(Digest::NONE).Padding(PaddingMode::NONE);
                string signature = SignMessage(keyblob, message, builder);
                LocalVerifyMessage(cert, message, signature, builder);
            } else if (name.find("p256-key") != std::string::npos) {
                builder.Digest(Digest::SHA_2_256);
                string signature = SignMessage(keyblob, message, builder);
                LocalVerifyMessage(cert, message, signature, builder);
            } else if (name.find("ed25519-key") != std::string::npos) {
                builder.Digest(Digest::NONE);
                string signature = SignMessage(keyblob, message, builder);
                LocalVerifyMessage(cert, message, signature, builder);
            } else if (name.find("x25519-key") != std::string::npos) {
                // Generate EC key on same curve locally (with access to private key material).
                uint8_t localPrivKeyData[32];
                uint8_t localPubKeyData[32];
                X25519_keypair(localPubKeyData, localPrivKeyData);
                EVP_PKEY_Ptr localPrivKey(EVP_PKEY_new_raw_private_key(
                        EVP_PKEY_X25519, nullptr, localPrivKeyData, sizeof(localPrivKeyData)));
                // Get encoded form of the public part of the locally generated key.
                unsigned char* p = nullptr;
                int localPublicKeySize = i2d_PUBKEY(localPrivKey.get(), &p);
                ASSERT_GT(localPublicKeySize, 0);
                vector<uint8_t> localPublicKey(
                        reinterpret_cast<const uint8_t*>(p),
                        reinterpret_cast<const uint8_t*>(p + localPublicKeySize));
                OPENSSL_free(p);

                // Agree on a key between local and KeyMint.
                string data;
                ASSERT_EQ(ErrorCode::OK,
                          Begin(KeyPurpose::AGREE_KEY, keyblob, builder, &out_params));
                ASSERT_EQ(ErrorCode::OK,
                          Finish(string(localPublicKey.begin(), localPublicKey.end()), &data));
                vector<uint8_t> keymint_data(data.begin(), data.end());

                // Extract the public key for the KeyMint key from the cert.
                X509_Ptr kmKeyCert(parse_cert_blob(cert));
                ASSERT_NE(kmKeyCert, nullptr);
                EVP_PKEY_Ptr kmPubKey = EVP_PKEY_Ptr(X509_get_pubkey(kmKeyCert.get()));
                ASSERT_NE(kmPubKey.get(), nullptr);

                size_t kmPubKeySize = 32;
                uint8_t kmPubKeyData[32];
                ASSERT_EQ(1,
                          EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize));
                ASSERT_EQ(kmPubKeySize, 32);

                // Agree on a key between KeyMint and local.
                uint8_t sharedKey[32];
                ASSERT_EQ(1, X25519(sharedKey, localPrivKeyData, kmPubKeyData));
                vector<uint8_t> local_data(sharedKey, sharedKey + 32);

                // Both ways round should agree.
                EXPECT_EQ(keymint_data, local_data);
            } else if (requires_attest_key(name)) {
                // Covers rsa-attest-key, p256-attest-key, ed25519-attest-key.

                // Use attestation key to sign RSA signing key
                AttestationKey attest_key;
                attest_key.keyBlob = keyblob;
                attest_key.attestKeyParams = builder.vector_data();
                attest_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
                vector<uint8_t> attested_key_blob;
                vector<KeyCharacteristics> attested_key_characteristics;
                vector<Certificate> attested_key_cert_chain;
                EXPECT_EQ(ErrorCode::OK,
                          GenerateKey(AuthorizationSetBuilder()
                                              .RsaSigningKey(2048, 65537)
                                              .Authorization(TAG_NO_AUTH_REQUIRED)
                                              .AttestationChallenge("challenge")
                                              .AttestationApplicationId("app-id")
                                              .SetDefaultValidity(),
                                      attest_key, &attested_key_blob, &attested_key_characteristics,
                                      &attested_key_cert_chain));
                KeyBlobDeleter(keymint_, attested_key_blob);
            } else {
                FAIL() << "Unexpected name: " << name;
            }
        }
    }
}

// This test target deletes any keys from the keyblob subdirectory that have rollback resistance
// enabled.
TEST_P(KeyBlobUpgradeTest, DeleteRRKeyBlobsAfter) {
    std::string subdir = keyblob_subdir(keyblob_dir, GetParam(), /* create? */ false);
    if (subdir.empty()) {
        GTEST_SKIP() << "No keyblob directory provided with (e.g.) --keyblob_dir "
                        "/data/local/tmp/keymint-blobs";
    }

    for (std::string name : keyblob_names()) {
        for (bool with_hidden : {false, true}) {
            auto builder = AuthorizationSetBuilder();
            if (with_hidden) {
                // Build a variant keyblob that requires app_id/app_data
                builder.Authorization(TAG_APPLICATION_ID, "appid")
                        .Authorization(TAG_APPLICATION_DATA, "appdata");
                name += "-hidden";
            }
            if (!requires_rr(name)) {
                std::cerr << "Skipping keyblob file '" << name
                          << "' which does not use rollback resistance\n";
                continue;
            }
            SCOPED_TRACE(testing::Message() << name);
            std::vector<uint8_t> keyblob = load_keyblob(subdir, name);
            if (keyblob.empty()) {
                std::cerr << "Skipping missing keyblob file '" << name
                          << "', assuming rollback resistance unavailable\n";
                continue;
            }

            // Delete the key
            ASSERT_EQ(ErrorCode::OK, DeleteKey(&keyblob));

            // Remove all files relating to the deleted key.
            std::cerr << "Deleting files for deleted key '" << name << "';\n";
            delete_keyblob(subdir, name);

            // Attempting to use the keyblob after deletion should fail.
            AuthorizationSet out_params;
            if (name.find("aes-key") != std::string::npos) {
                builder.BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
                EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
                          Begin(KeyPurpose::ENCRYPT, keyblob, builder, &out_params));
            } else {
                FAIL() << "Unexpected name: " << name;
            }
        }
    }
}

INSTANTIATE_KEYMINT_AIDL_TEST(KeyBlobUpgradeTest);

}  // namespace aidl::android::hardware::security::keymint::test
