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

Test: VtsAidlKeyMintTargetTest
Bug: 171847641
Change-Id: Id9dc0ee3c69d9c2421ce7b0f228580a90411169e
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
index ff8d85a..ab5e815 100644
--- a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
@@ -24,4 +24,5 @@
   SIGN = 2,
   VERIFY = 3,
   WRAP_KEY = 5,
+  AGREE_KEY = 6,
 }
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index d5f7a1f..f4d43b3 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -682,9 +682,9 @@
      * 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
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
index cb4682e..68c1740 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
@@ -39,5 +39,8 @@
     /* 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*/
 }
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index e7c94f3..5989cde 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -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 @@
 
 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) {
diff --git a/security/keymint/support/include/keymint_support/openssl_utils.h b/security/keymint/support/include/keymint_support/openssl_utils.h
index 9ae7e52..c3bc60b 100644
--- a/security/keymint/support/include/keymint_support/openssl_utils.h
+++ b/security/keymint/support/include/keymint_support/openssl_utils.h
@@ -34,7 +34,10 @@
     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)