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

Commit c0143434 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Check that KeyMint provides IRemotelyProvisionedComponent" am: cab97a7d am: 9cdb456c

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

Change-Id: Iae9bfedc5f42b1710d32e14f4f30139eefae4a91
parents 1f7df75f 9cdb456c
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -43,6 +43,8 @@ cc_test {
        "android.hardware.security.keymint-V1-ndk_platform",
        "android.hardware.security.secureclock-V1-ndk_platform",
        "libcppbor_external",
        "libcppcose",
        "libkeymint_remote_prov_support",
        "libkeymint_vts_test_utils",
    ],
    test_suites: [
@@ -73,6 +75,9 @@ cc_test_library {
        "android.hardware.security.keymint-V1-ndk_platform",
        "android.hardware.security.secureclock-V1-ndk_platform",
        "libcppbor_external",
        "libcppcose",
        "libgmock_ndk",
        "libkeymint_remote_prov_support",
    ],
}

+121 −0
Original line number Diff line number Diff line
@@ -22,8 +22,12 @@

#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <cppbor_parse.h>
#include <cppcose/cppcose.h>
#include <cutils/properties.h>
#include <gmock/gmock.h>
#include <openssl/mem.h>
#include <remote_prov/remote_prov_utils.h>

#include <keymint_support/attestation_record.h>
#include <keymint_support/key_param_output.h>
@@ -32,6 +36,7 @@

namespace aidl::android::hardware::security::keymint {

using namespace cppcose;
using namespace std::literals::chrono_literals;
using std::endl;
using std::optional;
@@ -39,6 +44,7 @@ using std::unique_ptr;
using ::testing::AssertionFailure;
using ::testing::AssertionResult;
using ::testing::AssertionSuccess;
using ::testing::MatchesRegex;

::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set) {
    if (set.size() == 0)
@@ -1118,6 +1124,121 @@ vector<uint8_t> make_name_from_str(const string& name) {
    return retval;
}

namespace {

void check_cose_key(const vector<uint8_t>& data, bool testMode) {
    auto [parsedPayload, __, payloadParseErr] = cppbor::parse(data);
    ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;

    // The following check assumes that canonical CBOR encoding is used for the COSE_Key.
    if (testMode) {
        EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
                    MatchesRegex("{\n"
                                 "  1 : 2,\n"   // kty: EC2
                                 "  3 : -7,\n"  // alg: ES256
                                 "  -1 : 1,\n"  // EC id: P256
                                 // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
                                 // sequence of 32 hexadecimal bytes, enclosed in braces and
                                 // separated by commas. In this case, some Ed25519 public key.
                                 "  -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"  // pub_x: data
                                 "  -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"  // pub_y: data
                                 "  -70000 : null,\n"                              // test marker
                                 "}"));
    } else {
        EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
                    MatchesRegex("{\n"
                                 "  1 : 2,\n"   // kty: EC2
                                 "  3 : -7,\n"  // alg: ES256
                                 "  -1 : 1,\n"  // EC id: P256
                                 // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
                                 // sequence of 32 hexadecimal bytes, enclosed in braces and
                                 // separated by commas. In this case, some Ed25519 public key.
                                 "  -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"  // pub_x: data
                                 "  -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"  // pub_y: data
                                 "}"));
    }
}

}  // namespace

void check_maced_pubkey(const MacedPublicKey& macedPubKey, bool testMode,
                        vector<uint8_t>* payload_value) {
    auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
    ASSERT_TRUE(coseMac0) << "COSE Mac0 parse failed " << mac0ParseErr;

    ASSERT_NE(coseMac0->asArray(), nullptr);
    ASSERT_EQ(coseMac0->asArray()->size(), kCoseMac0EntryCount);

    auto protParms = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
    ASSERT_NE(protParms, nullptr);

    // Header label:value of 'alg': HMAC-256
    ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n  1 : 5,\n}");

    auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
    ASSERT_NE(unprotParms, nullptr);
    ASSERT_EQ(unprotParms->size(), 0);

    // The payload is a bstr holding an encoded COSE_Key
    auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
    ASSERT_NE(payload, nullptr);
    check_cose_key(payload->value(), testMode);

    auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
    ASSERT_TRUE(coseMac0Tag);
    auto extractedTag = coseMac0Tag->value();
    EXPECT_EQ(extractedTag.size(), 32U);

    // Compare with tag generated with kTestMacKey.  Should only match in test mode
    auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
                                                payload->value());
    ASSERT_TRUE(testTag) << "Tag calculation failed: " << testTag.message();

    if (testMode) {
        EXPECT_EQ(*testTag, extractedTag);
    } else {
        EXPECT_NE(*testTag, extractedTag);
    }
    if (payload_value != nullptr) {
        *payload_value = payload->value();
    }
}

void p256_pub_key(const vector<uint8_t>& coseKeyData, EVP_PKEY_Ptr* signingKey) {
    // Extract x and y affine coordinates from the encoded Cose_Key.
    auto [parsedPayload, __, payloadParseErr] = cppbor::parse(coseKeyData);
    ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
    auto coseKey = parsedPayload->asMap();
    const std::unique_ptr<cppbor::Item>& xItem = coseKey->get(cppcose::CoseKey::PUBKEY_X);
    ASSERT_NE(xItem->asBstr(), nullptr);
    vector<uint8_t> x = xItem->asBstr()->value();
    const std::unique_ptr<cppbor::Item>& yItem = coseKey->get(cppcose::CoseKey::PUBKEY_Y);
    ASSERT_NE(yItem->asBstr(), nullptr);
    vector<uint8_t> y = yItem->asBstr()->value();

    // Concatenate: 0x04 (uncompressed form marker) | x | y
    vector<uint8_t> pubKeyData{0x04};
    pubKeyData.insert(pubKeyData.end(), x.begin(), x.end());
    pubKeyData.insert(pubKeyData.end(), y.begin(), y.end());

    EC_KEY_Ptr ecKey = EC_KEY_Ptr(EC_KEY_new());
    ASSERT_NE(ecKey, nullptr);
    EC_GROUP_Ptr group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
    ASSERT_NE(group, nullptr);
    ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1);
    EC_POINT_Ptr point = EC_POINT_Ptr(EC_POINT_new(group.get()));
    ASSERT_NE(point, nullptr);
    ASSERT_EQ(EC_POINT_oct2point(group.get(), point.get(), pubKeyData.data(), pubKeyData.size(),
                                 nullptr),
              1);
    ASSERT_EQ(EC_KEY_set_public_key(ecKey.get(), point.get()), 1);

    EVP_PKEY_Ptr pubKey = EVP_PKEY_Ptr(EVP_PKEY_new());
    ASSERT_NE(pubKey, nullptr);
    EVP_PKEY_assign_EC_KEY(pubKey.get(), ecKey.release());
    *signingKey = std::move(pubKey);
}

}  // namespace test

}  // namespace aidl::android::hardware::security::keymint
+5 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@

#include <aidl/android/hardware/security/keymint/ErrorCode.h>
#include <aidl/android/hardware/security/keymint/IKeyMintDevice.h>
#include <aidl/android/hardware/security/keymint/MacedPublicKey.h>

#include <keymint_support/authorization_set.h>
#include <keymint_support/openssl_utils.h>
@@ -277,6 +278,10 @@ bool verify_attestation_record(const string& challenge, //
string bin2hex(const vector<uint8_t>& data);
X509_Ptr parse_cert_blob(const vector<uint8_t>& blob);
vector<uint8_t> make_name_from_str(const string& name);
void check_maced_pubkey(const MacedPublicKey& macedPubKey, bool testMode,
                        vector<uint8_t>* payload_value);
void p256_pub_key(const vector<uint8_t>& coseKeyData, EVP_PKEY_Ptr* signingKey);

AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
::testing::AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain);
+100 −0
Original line number Diff line number Diff line
@@ -27,6 +27,9 @@

#include <cutils/properties.h>

#include <android/binder_manager.h>

#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
#include <aidl/android/hardware/security/keymint/KeyFormat.h>

#include <keymint_support/key_param_output.h>
@@ -209,6 +212,32 @@ class AidlBuf : public vector<uint8_t> {
    string to_string() const { return string(reinterpret_cast<const char*>(data()), size()); }
};

string device_suffix(const string& name) {
    size_t pos = name.find('/');
    if (pos == string::npos) {
        return name;
    }
    return name.substr(pos + 1);
}

bool matching_rp_instance(const string& km_name,
                          std::shared_ptr<IRemotelyProvisionedComponent>* rp) {
    string km_suffix = device_suffix(km_name);

    vector<string> rp_names =
            ::android::getAidlHalInstanceNames(IRemotelyProvisionedComponent::descriptor);
    for (const string& rp_name : rp_names) {
        // If the suffix of the RemotelyProvisionedComponent instance equals the suffix of the
        // KeyMint instance, assume they match.
        if (device_suffix(rp_name) == km_suffix && AServiceManager_isDeclared(rp_name.c_str())) {
            ::ndk::SpAIBinder binder(AServiceManager_waitForService(rp_name.c_str()));
            *rp = IRemotelyProvisionedComponent::fromBinder(binder);
            return true;
        }
    }
    return false;
}

}  // namespace

class NewKeyGenerationTest : public KeyMintAidlTestBase {
@@ -321,6 +350,77 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) {
    }
}

/*
 * NewKeyGenerationTest.RsaWithRpkAttestation
 *
 * Verifies that keymint can generate all required RSA key sizes, using an attestation key
 * that has been generated using an associate IRemotelyProvisionedComponent.
 */
TEST_P(NewKeyGenerationTest, RsaWithRpkAttestation) {
    // There should be an IRemotelyProvisionedComponent instance associated with the KeyMint
    // instance.
    std::shared_ptr<IRemotelyProvisionedComponent> rp;
    ASSERT_TRUE(matching_rp_instance(GetParam(), &rp))
            << "No IRemotelyProvisionedComponent found that matches KeyMint device " << GetParam();

    // Generate a P-256 keypair to use as an attestation key.
    MacedPublicKey macedPubKey;
    std::vector<uint8_t> privateKeyBlob;
    auto status =
            rp->generateEcdsaP256KeyPair(/* testMode= */ false, &macedPubKey, &privateKeyBlob);
    ASSERT_TRUE(status.isOk());
    vector<uint8_t> coseKeyData;
    check_maced_pubkey(macedPubKey, /* testMode= */ false, &coseKeyData);

    AttestationKey attestation_key;
    attestation_key.keyBlob = std::move(privateKeyBlob);
    attestation_key.issuerSubjectName = make_name_from_str("Android Keystore Key");

    for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
        auto challenge = "hello";
        auto app_id = "foo";

        vector<uint8_t> key_blob;
        vector<KeyCharacteristics> key_characteristics;
        ASSERT_EQ(ErrorCode::OK,
                  GenerateKey(AuthorizationSetBuilder()
                                      .RsaSigningKey(key_size, 65537)
                                      .Digest(Digest::NONE)
                                      .Padding(PaddingMode::NONE)
                                      .AttestationChallenge(challenge)
                                      .AttestationApplicationId(app_id)
                                      .Authorization(TAG_NO_AUTH_REQUIRED)
                                      .SetDefaultValidity(),
                              attestation_key, &key_blob, &key_characteristics, &cert_chain_));

        ASSERT_GT(key_blob.size(), 0U);
        CheckBaseParams(key_characteristics);

        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);

        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
                << "Key size " << key_size << "missing";
        EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));

        // Attestation by itself is not valid (last entry is not self-signed).
        EXPECT_FALSE(ChainSignaturesAreValid(cert_chain_));

        // The signature over the attested key should correspond to the P256 public key.
        X509_Ptr key_cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
        ASSERT_TRUE(key_cert.get());
        EVP_PKEY_Ptr signing_pubkey;
        p256_pub_key(coseKeyData, &signing_pubkey);
        ASSERT_TRUE(signing_pubkey.get());

        ASSERT_TRUE(X509_verify(key_cert.get(), signing_pubkey.get()))
                << "Verification of attested certificate failed "
                << "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL);

        CheckedDeleteKey(&key_blob);
    }
}

/*
 * NewKeyGenerationTest.LimitedUsageRsa
 *
+0 −111
Original line number Diff line number Diff line
@@ -55,117 +55,6 @@ bytevec string_to_bytevec(const char* s) {
    return bytevec(p, p + strlen(s));
}

void p256_pub_key(const vector<uint8_t>& coseKeyData, EVP_PKEY_Ptr* signingKey) {
    // Extract x and y affine coordinates from the encoded Cose_Key.
    auto [parsedPayload, __, payloadParseErr] = cppbor::parse(coseKeyData);
    ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
    auto coseKey = parsedPayload->asMap();
    const std::unique_ptr<cppbor::Item>& xItem = coseKey->get(cppcose::CoseKey::PUBKEY_X);
    ASSERT_NE(xItem->asBstr(), nullptr);
    vector<uint8_t> x = xItem->asBstr()->value();
    const std::unique_ptr<cppbor::Item>& yItem = coseKey->get(cppcose::CoseKey::PUBKEY_Y);
    ASSERT_NE(yItem->asBstr(), nullptr);
    vector<uint8_t> y = yItem->asBstr()->value();

    // Concatenate: 0x04 (uncompressed form marker) | x | y
    vector<uint8_t> pubKeyData{0x04};
    pubKeyData.insert(pubKeyData.end(), x.begin(), x.end());
    pubKeyData.insert(pubKeyData.end(), y.begin(), y.end());

    EC_KEY_Ptr ecKey = EC_KEY_Ptr(EC_KEY_new());
    ASSERT_NE(ecKey, nullptr);
    EC_GROUP_Ptr group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
    ASSERT_NE(group, nullptr);
    ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1);
    EC_POINT_Ptr point = EC_POINT_Ptr(EC_POINT_new(group.get()));
    ASSERT_NE(point, nullptr);
    ASSERT_EQ(EC_POINT_oct2point(group.get(), point.get(), pubKeyData.data(), pubKeyData.size(),
                                 nullptr),
              1);
    ASSERT_EQ(EC_KEY_set_public_key(ecKey.get(), point.get()), 1);

    EVP_PKEY_Ptr pubKey = EVP_PKEY_Ptr(EVP_PKEY_new());
    ASSERT_NE(pubKey, nullptr);
    EVP_PKEY_assign_EC_KEY(pubKey.get(), ecKey.release());
    *signingKey = std::move(pubKey);
}

void check_cose_key(const vector<uint8_t>& data, bool testMode) {
    auto [parsedPayload, __, payloadParseErr] = cppbor::parse(data);
    ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;

    // The following check assumes that canonical CBOR encoding is used for the COSE_Key.
    if (testMode) {
        EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
                    MatchesRegex("{\n"
                                 "  1 : 2,\n"   // kty: EC2
                                 "  3 : -7,\n"  // alg: ES256
                                 "  -1 : 1,\n"  // EC id: P256
                                 // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
                                 // sequence of 32 hexadecimal bytes, enclosed in braces and
                                 // separated by commas. In this case, some Ed25519 public key.
                                 "  -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"  // pub_x: data
                                 "  -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"  // pub_y: data
                                 "  -70000 : null,\n"                              // test marker
                                 "}"));
    } else {
        EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
                    MatchesRegex("{\n"
                                 "  1 : 2,\n"   // kty: EC2
                                 "  3 : -7,\n"  // alg: ES256
                                 "  -1 : 1,\n"  // EC id: P256
                                 // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
                                 // sequence of 32 hexadecimal bytes, enclosed in braces and
                                 // separated by commas. In this case, some Ed25519 public key.
                                 "  -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"  // pub_x: data
                                 "  -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"  // pub_y: data
                                 "}"));
    }
}

void check_maced_pubkey(const MacedPublicKey& macedPubKey, bool testMode,
                        vector<uint8_t>* payload_value) {
    auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
    ASSERT_TRUE(coseMac0) << "COSE Mac0 parse failed " << mac0ParseErr;

    ASSERT_NE(coseMac0->asArray(), nullptr);
    ASSERT_EQ(coseMac0->asArray()->size(), kCoseMac0EntryCount);

    auto protParms = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
    ASSERT_NE(protParms, nullptr);

    // Header label:value of 'alg': HMAC-256
    ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n  1 : 5,\n}");

    auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
    ASSERT_NE(unprotParms, nullptr);
    ASSERT_EQ(unprotParms->size(), 0);

    // The payload is a bstr holding an encoded COSE_Key
    auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
    ASSERT_NE(payload, nullptr);
    check_cose_key(payload->value(), testMode);

    auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
    ASSERT_TRUE(coseMac0Tag);
    auto extractedTag = coseMac0Tag->value();
    EXPECT_EQ(extractedTag.size(), 32U);

    // Compare with tag generated with kTestMacKey.  Should only match in test mode
    auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
                                                payload->value());
    ASSERT_TRUE(testTag) << "Tag calculation failed: " << testTag.message();

    if (testMode) {
        EXPECT_EQ(*testTag, extractedTag);
    } else {
        EXPECT_NE(*testTag, extractedTag);
    }
    if (payload_value != nullptr) {
        *payload_value = payload->value();
    }
}

ErrMsgOr<MacedPublicKey> corrupt_maced_key(const MacedPublicKey& macedPubKey) {
    auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
    if (!coseMac0 || coseMac0->asArray()->size() != kCoseMac0EntryCount) {