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 *