KeyMint: add VTS test with all IDs am: ef1123b24e

Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/3106417

Change-Id: Icfc31847731c3ca48492234751033d4be8ada033
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
David Drysdale 2024-05-31 12:19:59 +00:00 committed by Automerger Merge Worker
commit acb80cfdf2
4 changed files with 151 additions and 57 deletions

View file

@ -16,7 +16,6 @@
#define LOG_TAG "keymint_1_attest_key_test"
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <cutils/log.h>
#include <cutils/properties.h>
@ -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<Certificate>& 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<string> 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 {

View file

@ -26,6 +26,7 @@
#include "keymint_support/keymint_tags.h"
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <android/binder_manager.h>
#include <android/content/pm/IPackageManagerNative.h>
#include <cppbor_parse.h>
@ -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<int32_t> 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<std::string> 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

View file

@ -430,6 +430,7 @@ void p256_pub_key(const vector<uint8_t>& 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<int32_t> keymint_feature_value(bool strongbox);
std::string get_imei(int slot);
AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);

View file

@ -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<uint8_t> subject_der(make_name_from_str(subject));
uint64_t serial_int = 0x1010;
vector<uint8_t> 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<uint8_t> key_blob;
vector<KeyCharacteristics> 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
*