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
This commit is contained in:
parent
56e050362d
commit
ef1123b24e
4 changed files with 151 additions and 57 deletions
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue