From 9f5c0c57344cee58b026aabf78687dd1b2a470f9 Mon Sep 17 00:00:00 2001 From: David Drysdale Date: Thu, 3 Nov 2022 15:10:16 +0000 Subject: [PATCH] KeyMint manual key upgrade tests Test: VtsAidlKeyMintTargetTest Change-Id: I95d74cec80e94da316dab02b7a88b855742c877c --- .../keymint/aidl/vts/functional/Android.bp | 1 + .../vts/functional/KeyBlobUpgradeTest.cpp | 609 ++++++++++++++++++ .../vts/functional/KeyMintAidlTestBase.cpp | 11 +- .../aidl/vts/functional/KeyMintAidlTestBase.h | 6 + .../aidl/vts/functional/KeyMintTest.cpp | 9 + 5 files changed, 634 insertions(+), 2 deletions(-) create mode 100644 security/keymint/aidl/vts/functional/KeyBlobUpgradeTest.cpp diff --git a/security/keymint/aidl/vts/functional/Android.bp b/security/keymint/aidl/vts/functional/Android.bp index e7f5a0fbd8..88badc7f2a 100644 --- a/security/keymint/aidl/vts/functional/Android.bp +++ b/security/keymint/aidl/vts/functional/Android.bp @@ -57,6 +57,7 @@ cc_test { srcs: [ "AttestKeyTest.cpp", "DeviceUniqueAttestationTest.cpp", + "KeyBlobUpgradeTest.cpp", "KeyMintTest.cpp", "SecureElementProvisioningTest.cpp", ], diff --git a/security/keymint/aidl/vts/functional/KeyBlobUpgradeTest.cpp b/security/keymint/aidl/vts/functional/KeyBlobUpgradeTest.cpp new file mode 100644 index 0000000000..c952012106 --- /dev/null +++ b/security/keymint/aidl/vts/functional/KeyBlobUpgradeTest.cpp @@ -0,0 +1,609 @@ +/* + * 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 ` 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 `--keyblob_dir ` command-line argument +// pointing to the directory with the keyblobs: +// +// VtsAidlKeyMintTargetTest --gtest_filter="*KeyBlobUpgradeTest*After*" \ +// --keyblob_dir /data/local/tmp/keymint-blobs +// +// (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 + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "KeyMintAidlTestBase.h" + +using aidl::android::hardware::security::keymint::KeyCharacteristics; + +namespace aidl::android::hardware::security::keymint::test { + +namespace { + +std::vector 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 keyblob_names_sb = {"aes-key", "aes-key-rr", "des-key", + "hmac-key", "rsa-key", "p256-key", + "rsa-attest-key", "p256-attest-key"}; + +const std::vector& keyblob_names(SecurityLevel sec_level) { + if (sec_level == SecurityLevel::STRONGBOX) { + return keyblob_names_sb; + } else { + return keyblob_names_tee; + } +} + +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& keyblob, + const std::vector& 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(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& keyblob, + const std::vector& key_characteristics, + const std::vector& 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& 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(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 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 data((std::istreambuf_iterator(blobfile)), + std::istreambuf_iterator()); + return data; +} + +std::vector load_keyblob(const std::string& subdir, const std::string& name) { + return load_file(subdir, name, ".keyblob"); +} + +std::vector load_cert(const std::string& subdir, const std::string& name) { + return load_file(subdir, name, ".cert"); +} + +} // namespace + +class KeyBlobUpgradeTest : public KeyMintAidlTestBase { + protected: + 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(SecLevel())) { + 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 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 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_TRUE(!expectUpgrade) << "Keyblob '" << name << "' unexpectedly upgraded"; + } else { + // Ensure the old format keyblob is deleted (so any secure deletion data is + // cleaned up). + EXPECT_EQ(ErrorCode::OK, DeleteKey(&keyblob)); + + std::vector app_id_v(app_id.begin(), app_id.end()); + std::vector app_data_v(app_data.begin(), app_data.end()); + std::vector 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 left as-is"; + } + } + } + } +}; + +// 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 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::SHA1) + .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::SHA1) + .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::SHA1) + .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(SecLevel())) { + 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 keyblob; + vector key_characteristics; + vector 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 +// +// - 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) { + UpgradeKeyBlobs(/* expectUpgrade= */ true); +} + +// 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(SecLevel())) { + 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 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 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::SHA1); + 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::SHA1); + 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 localPublicKey( + reinterpret_cast(p), + reinterpret_cast(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 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 local_data(sharedKey, sharedKey + 32); + + // Both ways round should agree. + EXPECT_EQ(keymint_data, local_data); + } else if (name.find("-attest-key") != std::string::npos) { + // 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 attested_key_blob; + vector attested_key_characteristics; + vector 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)); + CheckedDeleteKey(&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(SecLevel())) { + 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 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 << ";"; + 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 diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp index 43ad30aa5e..6c012faec0 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp @@ -184,6 +184,7 @@ string x509NameToStr(X509_NAME* name) { bool KeyMintAidlTestBase::arm_deleteAllKeys = false; bool KeyMintAidlTestBase::dump_Attestations = false; +std::string KeyMintAidlTestBase::keyblob_dir; uint32_t KeyMintAidlTestBase::boot_patch_level( const vector& key_characteristics) { @@ -946,9 +947,15 @@ void KeyMintAidlTestBase::LocalVerifyMessage(const string& message, const string const AuthorizationSet& params) { SCOPED_TRACE("LocalVerifyMessage"); - // Retrieve the public key from the leaf certificate. ASSERT_GT(cert_chain_.size(), 0); - X509_Ptr key_cert(parse_cert_blob(cert_chain_[0].encodedCertificate)); + LocalVerifyMessage(cert_chain_[0].encodedCertificate, message, signature, params); +} + +void KeyMintAidlTestBase::LocalVerifyMessage(const vector& der_cert, const string& message, + const string& signature, + const AuthorizationSet& params) { + // Retrieve the public key from the leaf certificate. + X509_Ptr key_cert(parse_cert_blob(der_cert)); ASSERT_TRUE(key_cert.get()); EVP_PKEY_Ptr pub_key(X509_get_pubkey(key_cert.get())); ASSERT_TRUE(pub_key.get()); diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h index 5b09ca5c3e..908eeab19b 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h @@ -64,6 +64,10 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { static bool arm_deleteAllKeys; static bool dump_Attestations; + // Directory to store/retrieve keyblobs, using subdirectories named for the + // KeyMint instance in question (e.g. "./default/", "./strongbox/"). + static std::string keyblob_dir; + void SetUp() override; void TearDown() override { if (key_blob_.size()) { @@ -206,6 +210,8 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { const string& signature, const AuthorizationSet& params); void VerifyMessage(const string& message, const string& signature, const AuthorizationSet& params); + void LocalVerifyMessage(const vector& der_cert, const string& message, + const string& signature, const AuthorizationSet& params); void LocalVerifyMessage(const string& message, const string& signature, const AuthorizationSet& params); diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp index 5a86283b1f..c54dde4100 100644 --- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp @@ -8641,6 +8641,15 @@ int main(int argc, char** argv) { // interactions. aidl::android::hardware::security::keymint::test::check_boot_pl = false; } + if (std::string(argv[i]) == "--keyblob_dir") { + if (i + 1 >= argc) { + std::cerr << "Missing argument for --keyblob_dir\n"; + return 1; + } + aidl::android::hardware::security::keymint::test::KeyMintAidlTestBase::keyblob_dir = + std::string(argv[i + 1]); + ++i; + } } } return RUN_ALL_TESTS();