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

Test: VtsAidlKeyMintTargetTest
Bug: 171847641
Change-Id: Id9dc0ee3c69d9c2421ce7b0f228580a90411169e
This commit is contained in:
David Zeuthen
2021-01-08 12:54:11 -05:00
parent 1f00c21c28
commit e0c4089f06
5 changed files with 128 additions and 4 deletions

View File

@@ -24,4 +24,5 @@ enum KeyPurpose {
SIGN = 2,
VERIFY = 3,
WRAP_KEY = 5,
AGREE_KEY = 6,
}

View File

@@ -682,9 +682,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

View File

@@ -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*/
}

View File

@@ -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) {

View File

@@ -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)