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:
commit
acb80cfdf2
4 changed files with 151 additions and 57 deletions
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
#define LOG_TAG "keymint_1_attest_key_test"
|
#define LOG_TAG "keymint_1_attest_key_test"
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
#include <android-base/strings.h>
|
|
||||||
#include <cutils/log.h>
|
#include <cutils/log.h>
|
||||||
#include <cutils/properties.h>
|
#include <cutils/properties.h>
|
||||||
|
|
||||||
|
@ -29,66 +28,12 @@
|
||||||
namespace aidl::android::hardware::security::keymint::test {
|
namespace aidl::android::hardware::security::keymint::test {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
string TELEPHONY_CMD_GET_IMEI = "cmd phone get-imei ";
|
|
||||||
|
|
||||||
bool IsSelfSigned(const vector<Certificate>& chain) {
|
bool IsSelfSigned(const vector<Certificate>& chain) {
|
||||||
if (chain.size() != 1) return false;
|
if (chain.size() != 1) return false;
|
||||||
return ChainSignaturesAreValid(chain);
|
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
|
} // namespace
|
||||||
|
|
||||||
class AttestKeyTest : public KeyMintAidlTestBase {
|
class AttestKeyTest : public KeyMintAidlTestBase {
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "keymint_support/keymint_tags.h"
|
#include "keymint_support/keymint_tags.h"
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
|
#include <android-base/strings.h>
|
||||||
#include <android/binder_manager.h>
|
#include <android/binder_manager.h>
|
||||||
#include <android/content/pm/IPackageManagerNative.h>
|
#include <android/content/pm/IPackageManagerNative.h>
|
||||||
#include <cppbor_parse.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.
|
// 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
|
// This means that a device that launched prior to Android T (API level 33) may
|
||||||
// accept or even require KeyPurpose::SIGN too.
|
// 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;
|
AuthorizationSet key_desc_plus_sign = key_desc;
|
||||||
key_desc_plus_sign.push_back(TAG_PURPOSE, KeyPurpose::SIGN);
|
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;
|
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 test
|
||||||
|
|
||||||
} // namespace aidl::android::hardware::security::keymint
|
} // 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);
|
void device_id_attestation_check_acceptable_error(Tag tag, const ErrorCode& result);
|
||||||
bool check_feature(const std::string& name);
|
bool check_feature(const std::string& name);
|
||||||
std::optional<int32_t> keymint_feature_value(bool strongbox);
|
std::optional<int32_t> keymint_feature_value(bool strongbox);
|
||||||
|
std::string get_imei(int slot);
|
||||||
|
|
||||||
AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
|
AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
|
||||||
AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
|
AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
|
||||||
|
|
|
@ -2026,7 +2026,7 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) {
|
||||||
* NewKeyGenerationTest.EcdsaAttestationIdTags
|
* NewKeyGenerationTest.EcdsaAttestationIdTags
|
||||||
*
|
*
|
||||||
* Verifies that creation of an attested ECDSA key includes various ID tags in the
|
* 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) {
|
TEST_P(NewKeyGenerationTest, EcdsaAttestationIdTags) {
|
||||||
auto challenge = "hello";
|
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_MANUFACTURER, "manufacturer");
|
||||||
add_attestation_id(&extra_tags, TAG_ATTESTATION_ID_MODEL, "model");
|
add_attestation_id(&extra_tags, TAG_ATTESTATION_ID_MODEL, "model");
|
||||||
add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serialno");
|
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) {
|
for (const KeyParameter& tag : extra_tags) {
|
||||||
SCOPED_TRACE(testing::Message() << "tag-" << tag);
|
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
|
* NewKeyGenerationTest.EcdsaAttestationUniqueId
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue