From ef1123b24e6e3fff7bf7c7d3286f41c0f814d3f0 Mon Sep 17 00:00:00 2001 From: David Drysdale Date: Tue, 28 May 2024 15:23:08 +0100 Subject: [PATCH] KeyMint: add VTS test with all IDs Add a test that includes all of the device IDs for attestation, which helps to check whether the emitted extension is including everything in the correct order. (This is already tested in CTS since aosp/2650044) Also fix test of first API level to use `get_vsr_api_level` helper. Test: VtsAidlKeyMintTargetTest Test: with/without KeyMint hacked to emit tags in wrong order Change-Id: Ic6e489aa99c773d794ad8cbddbe5153b1a145ea5 --- .../aidl/vts/functional/AttestKeyTest.cpp | 55 ------------ .../vts/functional/KeyMintAidlTestBase.cpp | 64 +++++++++++++- .../aidl/vts/functional/KeyMintAidlTestBase.h | 1 + .../aidl/vts/functional/KeyMintTest.cpp | 88 ++++++++++++++++++- 4 files changed, 151 insertions(+), 57 deletions(-) diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp index 7fbca3601e..5106561984 100644 --- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp +++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp @@ -16,7 +16,6 @@ #define LOG_TAG "keymint_1_attest_key_test" #include -#include #include #include @@ -29,66 +28,12 @@ namespace aidl::android::hardware::security::keymint::test { namespace { -string TELEPHONY_CMD_GET_IMEI = "cmd phone get-imei "; bool IsSelfSigned(const vector& chain) { if (chain.size() != 1) return false; return ChainSignaturesAreValid(chain); } -/* - * Run a shell command and collect the output of it. If any error, set an empty string as the - * output. - */ -string exec_command(string command) { - char buffer[128]; - string result = ""; - - FILE* pipe = popen(command.c_str(), "r"); - if (!pipe) { - LOG(ERROR) << "popen failed."; - return result; - } - - // read till end of process: - while (!feof(pipe)) { - if (fgets(buffer, 128, pipe) != NULL) { - result += buffer; - } - } - - pclose(pipe); - return result; -} - -/* - * Get IMEI using Telephony service shell command. If any error while executing the command - * then empty string will be returned as output. - */ -string get_imei(int slot) { - string cmd = TELEPHONY_CMD_GET_IMEI + std::to_string(slot); - string output = exec_command(cmd); - - if (output.empty()) { - LOG(ERROR) << "Command failed. Cmd: " << cmd; - return ""; - } - - vector out = ::android::base::Tokenize(::android::base::Trim(output), "Device IMEI:"); - - if (out.size() != 1) { - LOG(ERROR) << "Error in parsing the command output. Cmd: " << cmd; - return ""; - } - - string imei = ::android::base::Trim(out[0]); - if (imei.compare("null") == 0) { - LOG(WARNING) << "Failed to get IMEI from Telephony service: value is null. Cmd: " << cmd; - return ""; - } - - return imei; -} } // namespace class AttestKeyTest : public KeyMintAidlTestBase { diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp index 332fcd492a..cef8120b88 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp @@ -26,6 +26,7 @@ #include "keymint_support/keymint_tags.h" #include +#include #include #include #include @@ -1588,7 +1589,7 @@ ErrorCode KeyMintAidlTestBase::GenerateAttestKey(const AuthorizationSet& key_des // with any other key purpose, but the original VTS tests incorrectly did exactly that. // This means that a device that launched prior to Android T (API level 33) may // accept or even require KeyPurpose::SIGN too. - if (property_get_int32("ro.board.first_api_level", 0) < __ANDROID_API_T__) { + if (get_vsr_api_level() < __ANDROID_API_T__) { AuthorizationSet key_desc_plus_sign = key_desc; key_desc_plus_sign.push_back(TAG_PURPOSE, KeyPurpose::SIGN); @@ -2337,6 +2338,67 @@ std::optional keymint_feature_value(bool strongbox) { return result; } +namespace { + +std::string TELEPHONY_CMD_GET_IMEI = "cmd phone get-imei "; + +/* + * Run a shell command and collect the output of it. If any error, set an empty string as the + * output. + */ +std::string exec_command(const std::string& command) { + char buffer[128]; + std::string result = ""; + + FILE* pipe = popen(command.c_str(), "r"); + if (!pipe) { + LOG(ERROR) << "popen failed."; + return result; + } + + // read till end of process: + while (!feof(pipe)) { + if (fgets(buffer, 128, pipe) != NULL) { + result += buffer; + } + } + + pclose(pipe); + return result; +} + +} // namespace + +/* + * Get IMEI using Telephony service shell command. If any error while executing the command + * then empty string will be returned as output. + */ +std::string get_imei(int slot) { + std::string cmd = TELEPHONY_CMD_GET_IMEI + std::to_string(slot); + std::string output = exec_command(cmd); + + if (output.empty()) { + LOG(ERROR) << "Command failed. Cmd: " << cmd; + return ""; + } + + vector out = + ::android::base::Tokenize(::android::base::Trim(output), "Device IMEI:"); + + if (out.size() != 1) { + LOG(ERROR) << "Error in parsing the command output. Cmd: " << cmd; + return ""; + } + + std::string imei = ::android::base::Trim(out[0]); + if (imei.compare("null") == 0) { + LOG(WARNING) << "Failed to get IMEI from Telephony service: value is null. Cmd: " << cmd; + return ""; + } + + return imei; +} + } // namespace test } // namespace aidl::android::hardware::security::keymint diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h index b884cc7b6f..1bf2d9d59e 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h @@ -430,6 +430,7 @@ void p256_pub_key(const vector& coseKeyData, EVP_PKEY_Ptr* signingKey); void device_id_attestation_check_acceptable_error(Tag tag, const ErrorCode& result); bool check_feature(const std::string& name); std::optional keymint_feature_value(bool strongbox); +std::string get_imei(int slot); AuthorizationSet HwEnforcedAuthorizations(const vector& key_characteristics); AuthorizationSet SwEnforcedAuthorizations(const vector& key_characteristics); diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp index 35e5e84e6e..3c49a82ba6 100644 --- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp @@ -2026,7 +2026,7 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { * NewKeyGenerationTest.EcdsaAttestationIdTags * * Verifies that creation of an attested ECDSA key includes various ID tags in the - * attestation extension. + * attestation extension one by one. */ TEST_P(NewKeyGenerationTest, EcdsaAttestationIdTags) { auto challenge = "hello"; @@ -2054,6 +2054,15 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationIdTags) { add_attestation_id(&extra_tags, TAG_ATTESTATION_ID_MANUFACTURER, "manufacturer"); add_attestation_id(&extra_tags, TAG_ATTESTATION_ID_MODEL, "model"); add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serialno"); + string imei = get_imei(0); + if (!imei.empty()) { + extra_tags.Authorization(TAG_ATTESTATION_ID_IMEI, imei.data(), imei.size()); + } + string second_imei = get_imei(1); + if (!second_imei.empty()) { + extra_tags.Authorization(TAG_ATTESTATION_ID_SECOND_IMEI, second_imei.data(), + second_imei.size()); + } for (const KeyParameter& tag : extra_tags) { SCOPED_TRACE(testing::Message() << "tag-" << tag); @@ -2090,6 +2099,83 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationIdTags) { } } +/* + * NewKeyGenerationTest.EcdsaAttestationIdAllTags + * + * Verifies that creation of an attested ECDSA key includes various ID tags in the + * attestation extension all together. + */ +TEST_P(NewKeyGenerationTest, EcdsaAttestationIdAllTags) { + auto challenge = "hello"; + auto app_id = "foo"; + auto subject = "cert subj 2"; + vector subject_der(make_name_from_str(subject)); + uint64_t serial_int = 0x1010; + vector serial_blob(build_serial_blob(serial_int)); + AuthorizationSetBuilder builder = AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(EcCurve::P_256) + .Digest(Digest::NONE) + .AttestationChallenge(challenge) + .AttestationApplicationId(app_id) + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(); + + // Various ATTESTATION_ID_* tags that map to fields in the attestation extension ASN.1 schema. + auto extra_tags = AuthorizationSetBuilder(); + add_attestation_id(&extra_tags, TAG_ATTESTATION_ID_BRAND, "brand"); + add_attestation_id(&extra_tags, TAG_ATTESTATION_ID_DEVICE, "device"); + add_attestation_id(&extra_tags, TAG_ATTESTATION_ID_PRODUCT, "name"); + add_attestation_id(&extra_tags, TAG_ATTESTATION_ID_MANUFACTURER, "manufacturer"); + add_attestation_id(&extra_tags, TAG_ATTESTATION_ID_MODEL, "model"); + add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serialno"); + string imei = get_imei(0); + if (!imei.empty()) { + extra_tags.Authorization(TAG_ATTESTATION_ID_IMEI, imei.data(), imei.size()); + } + string second_imei = get_imei(1); + if (!second_imei.empty()) { + extra_tags.Authorization(TAG_ATTESTATION_ID_SECOND_IMEI, second_imei.data(), + second_imei.size()); + } + for (const KeyParameter& tag : extra_tags) { + builder.push_back(tag); + } + + vector key_blob; + vector key_characteristics; + auto result = GenerateKey(builder, &key_blob, &key_characteristics); + if (result == ErrorCode::CANNOT_ATTEST_IDS && !isDeviceIdAttestationRequired()) { + // ID attestation was optional till api level 32, from api level 33 it is mandatory. + return; + } + ASSERT_EQ(result, ErrorCode::OK); + KeyBlobDeleter deleter(keymint_, key_blob); + ASSERT_GT(key_blob.size(), 0U); + + EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_)); + ASSERT_GT(cert_chain_.size(), 0); + verify_subject_and_serial(cert_chain_[0], serial_int, subject, /* self_signed = */ false); + + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics); + AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics); + + // The attested key characteristics will not contain APPLICATION_ID_* fields (their + // spec definitions all have "Must never appear in KeyCharacteristics"), but the + // attestation extension should contain them, so make sure the extra tags are added. + for (const KeyParameter& tag : extra_tags) { + hw_enforced.push_back(tag); + } + + // Verifying the attestation record will check for the specific tag because + // it's included in the authorizations. + EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, sw_enforced, + hw_enforced, SecLevel(), + cert_chain_[0].encodedCertificate)) + << "failed to verify " << bin2hex(cert_chain_[0].encodedCertificate); +} + /* * NewKeyGenerationTest.EcdsaAttestationUniqueId *