Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit acb80cfd authored by David Drysdale's avatar David Drysdale Committed by Automerger Merge Worker
Browse files

KeyMint: add VTS test with all IDs am: ef1123b2

parents 7c0d9eb2 ef1123b2
Loading
Loading
Loading
Loading
+0 −55
Original line number Diff line number Diff line
@@ -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 {
+63 −1
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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);
+87 −1
Original line number Diff line number Diff line
@@ -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
 *