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

Commit e5ed85d5 authored by David Zeuthen's avatar David Zeuthen Committed by Automerger Merge Worker
Browse files

Merge "KeyMint: Add support for key agreement operation and use it for ECDH."...

Merge "KeyMint: Add support for key agreement operation and use it for ECDH." am: df543ea0 am: 30f799ff

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

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I95f1a083aa0880f57fb551cfb8bc74d7d8cbacb7
parents 87822e09 30f799ff
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -24,4 +24,5 @@ enum KeyPurpose {
  SIGN = 2,
  VERIFY = 3,
  WRAP_KEY = 5,
  AGREE_KEY = 6,
}
+3 −3
Original line number Diff line number Diff line
@@ -681,9 +681,9 @@ interface IKeyMintDevice {
     * values less than the key's minimum length, begin() must return ErrorCode::INVALID_MAC_LENGTH.
     *
     * @param inPurpose The purpose of the operation, one of KeyPurpose::ENCRYPT,
     *        KeyPurpose::DECRYPT, KeyPurpose::SIGN or KeyPurpose::VERIFY.  Note that for AEAD
     *        modes, encryption and decryption imply signing and verification, respectively, but
     *        must be specified as KeyPurpose::ENCRYPT and KeyPurpose::DECRYPT.
     *        KeyPurpose::DECRYPT, KeyPurpose::SIGN, KeyPurpose::VERIFY, or KeyPurpose::AGREE_KEY.
     *        Note that for AEAD modes, encryption and decryption imply signing and verification,
     *        respectively, but must be specified as KeyPurpose::ENCRYPT and KeyPurpose::DECRYPT.
     *
     * @param inKeyBlob The opaque key descriptor returned by generateKey() or importKey().  The key
     *        must have a purpose compatible with purpose and all of its usage requirements must be
+4 −1
Original line number Diff line number Diff line
@@ -39,5 +39,8 @@ enum KeyPurpose {
    /* Usable with wrapping keys. */
    WRAP_KEY = 5,

    /* TODO(seleneh) add AGREE_KEY and ATTEST_KEY and their corresponding codes and tests later*/
    /* Key Agreement, usable with EC keys. */
    AGREE_KEY = 6,

    /* TODO(seleneh) add ATTEST_KEY and their corresponding codes and tests later*/
}
+117 −0
Original line number Diff line number Diff line
@@ -20,9 +20,11 @@
#include <signal.h>
#include <iostream>

#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>

#include <cutils/properties.h>

@@ -4424,6 +4426,121 @@ TEST_P(TransportLimitTest, LargeFinishInput) {

INSTANTIATE_KEYMINT_AIDL_TEST(TransportLimitTest);

typedef KeyMintAidlTestBase KeyAgreementTest;

int CurveToOpenSslCurveName(EcCurve curve) {
    switch (curve) {
        case EcCurve::P_224:
            return NID_secp224r1;
        case EcCurve::P_256:
            return NID_X9_62_prime256v1;
        case EcCurve::P_384:
            return NID_secp384r1;
        case EcCurve::P_521:
            return NID_secp521r1;
    }
}

/*
 * KeyAgreementTest.Ecdh
 *
 * Verifies that ECDH works for all curves
 */
TEST_P(KeyAgreementTest, Ecdh) {
    // Because it's possible to use this API with keys on different curves, we
    // check all N^2 combinations where N is the number of supported
    // curves.
    //
    // This is not a big deal as N is 4 so we only do 16 runs. If we end up with a
    // lot more curves we can be smart about things and just pick |otherCurve| so
    // it's not |curve| and that way we end up with only 2*N runs
    //
    for (auto curve : ValidCurves()) {
        for (auto localCurve : ValidCurves()) {
            // Generate EC key locally (with access to private key material)
            auto ecKey = EC_KEY_Ptr(EC_KEY_new());
            int curveName = CurveToOpenSslCurveName(localCurve);
            auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(curveName));
            ASSERT_NE(group, nullptr);
            ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1);
            ASSERT_EQ(EC_KEY_generate_key(ecKey.get()), 1);
            auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
            ASSERT_EQ(EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()), 1);

            // Get encoded form of the public part of the locally generated key...
            unsigned char* p = nullptr;
            int encodedPublicKeySize = i2d_PUBKEY(pkey.get(), &p);
            ASSERT_GT(encodedPublicKeySize, 0);
            vector<uint8_t> encodedPublicKey(
                    reinterpret_cast<const uint8_t*>(p),
                    reinterpret_cast<const uint8_t*>(p + encodedPublicKeySize));
            OPENSSL_free(p);

            // Generate EC key in KeyMint (only access to public key material)
            vector<uint8_t> challenge = {0x41, 0x42};
            EXPECT_EQ(
                    ErrorCode::OK,
                    GenerateKey(AuthorizationSetBuilder()
                                        .Authorization(TAG_NO_AUTH_REQUIRED)
                                        .Authorization(TAG_EC_CURVE, curve)
                                        .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
                                        .Authorization(TAG_ALGORITHM, Algorithm::EC)
                                        .Authorization(TAG_ATTESTATION_APPLICATION_ID, {0x61, 0x62})
                                        .Authorization(TAG_ATTESTATION_CHALLENGE, challenge)))
                    << "Failed to generate key";
            ASSERT_GT(cert_chain_.size(), 0);
            X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
            ASSERT_NE(kmKeyCert, nullptr);
            // Check that keyAgreement (bit 4) is set in KeyUsage
            EXPECT_TRUE((X509_get_key_usage(kmKeyCert.get()) & X509v3_KU_KEY_AGREEMENT) != 0);
            auto kmPkey = EVP_PKEY_Ptr(X509_get_pubkey(kmKeyCert.get()));
            ASSERT_NE(kmPkey, nullptr);
            if (dump_Attestations) {
                for (size_t n = 0; n < cert_chain_.size(); n++) {
                    std::cout << bin2hex(cert_chain_[n].encodedCertificate) << std::endl;
                }
            }

            // Now that we have the two keys, we ask KeyMint to perform ECDH...
            if (curve != localCurve) {
                // If the keys are using different curves KeyMint should fail with
                // ErrorCode:INVALID_ARGUMENT. Check that.
                EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
                string ZabFromKeyMintStr;
                EXPECT_EQ(ErrorCode::INVALID_ARGUMENT,
                          Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()),
                                 &ZabFromKeyMintStr));

            } else {
                // Otherwise if the keys are using the same curve, it should work.
                EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
                string ZabFromKeyMintStr;
                EXPECT_EQ(ErrorCode::OK,
                          Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()),
                                 &ZabFromKeyMintStr));
                vector<uint8_t> ZabFromKeyMint(ZabFromKeyMintStr.begin(), ZabFromKeyMintStr.end());

                // Perform local ECDH between the two keys so we can check if we get the same Zab..
                auto ctx = EVP_PKEY_CTX_Ptr(EVP_PKEY_CTX_new(pkey.get(), nullptr));
                ASSERT_NE(ctx, nullptr);
                ASSERT_EQ(EVP_PKEY_derive_init(ctx.get()), 1);
                ASSERT_EQ(EVP_PKEY_derive_set_peer(ctx.get(), kmPkey.get()), 1);
                size_t ZabFromTestLen = 0;
                ASSERT_EQ(EVP_PKEY_derive(ctx.get(), nullptr, &ZabFromTestLen), 1);
                vector<uint8_t> ZabFromTest;
                ZabFromTest.resize(ZabFromTestLen);
                ASSERT_EQ(EVP_PKEY_derive(ctx.get(), ZabFromTest.data(), &ZabFromTestLen), 1);

                EXPECT_EQ(ZabFromKeyMint, ZabFromTest);
            }

            CheckedDeleteKey();
        }
    }
}

INSTANTIATE_KEYMINT_AIDL_TEST(KeyAgreementTest);

}  // namespace aidl::android::hardware::security::keymint::test

int main(int argc, char** argv) {
+3 −0
Original line number Diff line number Diff line
@@ -34,7 +34,10 @@ typedef UniquePtrDeleter<EVP_PKEY, EVP_PKEY_free> EVP_PKEY_Delete;
    typedef std::unique_ptr<type, UniquePtrDeleter<type, type##_free>> type##_Ptr;

MAKE_OPENSSL_PTR_TYPE(ASN1_OBJECT)
MAKE_OPENSSL_PTR_TYPE(EC_KEY)
MAKE_OPENSSL_PTR_TYPE(EC_GROUP)
MAKE_OPENSSL_PTR_TYPE(EVP_PKEY)
MAKE_OPENSSL_PTR_TYPE(EVP_PKEY_CTX)
MAKE_OPENSSL_PTR_TYPE(RSA)
MAKE_OPENSSL_PTR_TYPE(X509)
MAKE_OPENSSL_PTR_TYPE(BN_CTX)