diff options
Diffstat (limited to 'security/keymint/aidl/vts')
4 files changed, 985 insertions, 82 deletions
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp index 374f2da7a8..146a527561 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp @@ -25,6 +25,7 @@ #include <cppbor_parse.h> #include <cutils/properties.h> #include <gmock/gmock.h> +#include <openssl/evp.h> #include <openssl/mem.h> #include <remote_prov/remote_prov_utils.h> @@ -206,6 +207,21 @@ uint32_t KeyMintAidlTestBase::boot_patch_level() { return boot_patch_level(key_characteristics_); } +bool KeyMintAidlTestBase::Curve25519Supported() { + // Strongbox never supports curve 25519. + if (SecLevel() == SecurityLevel::STRONGBOX) { + return false; + } + + // Curve 25519 was included in version 2 of the KeyMint interface. + int32_t version = 0; + auto status = keymint_->getInterfaceVersion(&version); + if (!status.isOk()) { + ADD_FAILURE() << "Failed to determine interface version"; + } + return version >= 2; +} + ErrorCode KeyMintAidlTestBase::GetReturnErrorCode(const Status& result) { if (result.isOk()) return ErrorCode::OK; @@ -543,7 +559,12 @@ ErrorCode KeyMintAidlTestBase::Update(const string& input, string* output) { std::vector<uint8_t> o_put; result = op_->update(vector<uint8_t>(input.begin(), input.end()), {}, {}, &o_put); - if (result.isOk()) output->append(o_put.begin(), o_put.end()); + if (result.isOk()) { + output->append(o_put.begin(), o_put.end()); + } else { + // Failure always terminates the operation. + op_ = {}; + } return GetReturnErrorCode(result); } @@ -740,6 +761,19 @@ void KeyMintAidlTestBase::LocalVerifyMessage(const string& message, const string if (digest == Digest::NONE) { switch (EVP_PKEY_id(pub_key.get())) { + case EVP_PKEY_ED25519: { + ASSERT_EQ(64, signature.size()); + uint8_t pub_keydata[32]; + size_t pub_len = sizeof(pub_keydata); + ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(pub_key.get(), pub_keydata, &pub_len)); + ASSERT_EQ(sizeof(pub_keydata), pub_len); + ASSERT_EQ(1, ED25519_verify(reinterpret_cast<const uint8_t*>(message.data()), + message.size(), + reinterpret_cast<const uint8_t*>(signature.data()), + pub_keydata)); + break; + } + case EVP_PKEY_EC: { vector<uint8_t> data((EVP_PKEY_bits(pub_key.get()) + 7) / 8); size_t data_size = std::min(data.size(), message.size()); @@ -1166,16 +1200,31 @@ vector<PaddingMode> KeyMintAidlTestBase::InvalidPaddingModes(Algorithm algorithm vector<EcCurve> KeyMintAidlTestBase::ValidCurves() { if (securityLevel_ == SecurityLevel::STRONGBOX) { return {EcCurve::P_256}; + } else if (Curve25519Supported()) { + return {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521, + EcCurve::CURVE_25519}; } else { - return {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521}; + return { + EcCurve::P_224, + EcCurve::P_256, + EcCurve::P_384, + EcCurve::P_521, + }; } } vector<EcCurve> KeyMintAidlTestBase::InvalidCurves() { if (SecLevel() == SecurityLevel::STRONGBOX) { - return {EcCurve::P_224, EcCurve::P_384, EcCurve::P_521}; + // Curve 25519 is not supported, either because: + // - KeyMint v1: it's an unknown enum value + // - KeyMint v2+: it's not supported by StrongBox. + return {EcCurve::P_224, EcCurve::P_384, EcCurve::P_521, EcCurve::CURVE_25519}; } else { - return {}; + if (Curve25519Supported()) { + return {}; + } else { + return {EcCurve::CURVE_25519}; + } } } diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h index 61f9d4d59a..27cb99c06a 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h @@ -80,6 +80,8 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam<string> { uint32_t boot_patch_level(const vector<KeyCharacteristics>& key_characteristics); uint32_t boot_patch_level(); + bool Curve25519Supported(); + ErrorCode GetReturnErrorCode(const Status& result); ErrorCode GenerateKey(const AuthorizationSet& key_desc, vector<uint8_t>* key_blob, diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp index 340010fec3..7357ac7425 100644 --- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp @@ -22,6 +22,7 @@ #include <algorithm> #include <iostream> +#include <openssl/curve25519.h> #include <openssl/ec.h> #include <openssl/evp.h> #include <openssl/mem.h> @@ -69,6 +70,9 @@ namespace aidl::android::hardware::security::keymint::test { namespace { +// Maximum supported Ed25519 message size. +const size_t MAX_ED25519_MSG_SIZE = 16 * 1024; + // Whether to check that BOOT_PATCHLEVEL is populated. bool check_boot_pl = true; @@ -415,6 +419,126 @@ string ec_256_key_sec1 = hex2str( // } end SEQUENCE (PrivateKeyInfo) ); +/** + * Ed25519 key pair generated as follows: + * ``` + * % openssl req -x509 -newkey ED25519 -days 700 -nodes \ + * -keyout ed25519_priv.key -out ed25519.pem * -subj "/CN=fake.ed25519.com" + * Generating a ED25519 private key writing new private key to + * 'ed25519_priv.key' + * ----- + * % cat ed25519_priv.key + * -----BEGIN PRIVATE KEY----- + * MC4CAQAwBQYDK2VwBCIEIKl3A5quNywcj1P+0XI9SBalFPIvO52NxceMLRH6dVmR + * -----END PRIVATE KEY----- + * % der2ascii -pem -i ed25519_priv.key + * SEQUENCE { + * INTEGER { 0 } + * SEQUENCE { + * # ed25519 + * OBJECT_IDENTIFIER { 1.3.101.112 } + * } + * OCTET_STRING { + * OCTET_STRING { `a977039aae372c1c8f53fed1723d4816a514f22f3b9d8dc5c78c2d11fa755991` } + * } + * } + * % cat ed25519.pem + * -----BEGIN CERTIFICATE----- + * MIIBSjCB/aADAgECAhR0Jron3eKcdgqyecv/eEfGWAzn8DAFBgMrZXAwGzEZMBcG + * A1UEAwwQZmFrZS5lZDI1NTE5LmNvbTAeFw0yMTEwMjAwODI3NDJaFw0yMzA5MjAw + * ODI3NDJaMBsxGTAXBgNVBAMMEGZha2UuZWQyNTUxOS5jb20wKjAFBgMrZXADIQDv + * uwHz+3TaQ69D2digxlz0fFfsZg0rPqgQae3jBPRWkaNTMFEwHQYDVR0OBBYEFN9O + * od30SY4JTs66ZR403UPya+iXMB8GA1UdIwQYMBaAFN9Ood30SY4JTs66ZR403UPy + * a+iXMA8GA1UdEwEB/wQFMAMBAf8wBQYDK2VwA0EAKjVrYQjuE/gEL2j/ABpDbFjV + * Ilg5tJ6MN/P3psAv3Cs7f0X1lFqdlt15nJ/6aj2cmGCwNRXt5wcyYDKNu+v2Dw== + * -----END CERTIFICATE----- + * % openssl x509 -in ed25519.pem -text -noout + * Certificate: + * Data: + * Version: 3 (0x2) + * Serial Number: + * 74:26:ba:27:dd:e2:9c:76:0a:b2:79:cb:ff:78:47:c6:58:0c:e7:f0 + * Signature Algorithm: ED25519 + * Issuer: CN = fake.ed25519.com + * Validity + * Not Before: Oct 20 08:27:42 2021 GMT + * Not After : Sep 20 08:27:42 2023 GMT + * Subject: CN = fake.ed25519.com + * Subject Public Key Info: + * Public Key Algorithm: ED25519 + * ED25519 Public-Key: + * pub: + * ef:bb:01:f3:fb:74:da:43:af:43:d9:d8:a0:c6:5c: + * f4:7c:57:ec:66:0d:2b:3e:a8:10:69:ed:e3:04:f4: + * 56:91 + * X509v3 extensions: + * X509v3 Subject Key Identifier: + * DF:4E:A1:DD:F4:49:8E:09:4E:CE:BA:65:1E:34:DD:43:F2:6B:E8:97 + * X509v3 Authority Key Identifier: + * keyid:DF:4E:A1:DD:F4:49:8E:09:4E:CE:BA:65:1E:34:DD:43:F2:6B:E8:97 + * + * X509v3 Basic Constraints: critical + * CA:TRUE + * Signature Algorithm: ED25519 + * 2a:35:6b:61:08:ee:13:f8:04:2f:68:ff:00:1a:43:6c:58:d5: + * 22:58:39:b4:9e:8c:37:f3:f7:a6:c0:2f:dc:2b:3b:7f:45:f5: + * 94:5a:9d:96:dd:79:9c:9f:fa:6a:3d:9c:98:60:b0:35:15:ed: + * e7:07:32:60:32:8d:bb:eb:f6:0f + * ``` + */ +string ed25519_key = hex2str("a977039aae372c1c8f53fed1723d4816a514f22f3b9d8dc5c78c2d11fa755991"); +string ed25519_pkcs8_key = hex2str( + // RFC 5208 s5 + "302e" // SEQUENCE length 0x2e (PrivateKeyInfo) { + "0201" // INTEGER length 1 (Version) + "00" // version 0 + "3005" // SEQUENCE length 05 (AlgorithmIdentifier) { + "0603" // OBJECT IDENTIFIER length 3 (algorithm) + "2b6570" // 1.3.101.112 (id-Ed125519 RFC 8410 s3) + // } end SEQUENCE (AlgorithmIdentifier) + "0422" // OCTET STRING length 0x22 (PrivateKey) + "0420" // OCTET STRING length 0x20 (RFC 8410 s7) + "a977039aae372c1c8f53fed1723d4816a514f22f3b9d8dc5c78c2d11fa755991" + // } end SEQUENCE (PrivateKeyInfo) +); +string ed25519_pubkey = hex2str("efbb01f3fb74da43af43d9d8a0c65cf47c57ec660d2b3ea81069ede304f45691"); + +/** + * X25519 key pair generated as follows: + * ``` + * % openssl genpkey -algorithm X25519 > x25519_priv.key + * % cat x25519_priv.key + * -----BEGIN PRIVATE KEY----- + * MC4CAQAwBQYDK2VuBCIEIGgPwF3NLwQx/Sfwr2nfJvXitwlDNh3Skzh+TISN/y1C + * -----END PRIVATE KEY----- + * % der2ascii -pem -i x25519_priv.key + * SEQUENCE { + * INTEGER { 0 } + * SEQUENCE { + * # x25519 + * OBJECT_IDENTIFIER { 1.3.101.110 } + * } + * OCTET_STRING { + * OCTET_STRING { `680fc05dcd2f0431fd27f0af69df26f5e2b70943361dd293387e4c848dff2d42` } + * } + * } + * ``` + */ + +string x25519_key = hex2str("680fc05dcd2f0431fd27f0af69df26f5e2b70943361dd293387e4c848dff2d42"); +string x25519_pkcs8_key = hex2str( + // RFC 5208 s5 + "302e" // SEQUENCE length 0x2e (PrivateKeyInfo) { + "0201" // INTEGER length 1 (Version) + "00" // version 0 + "3005" // SEQUENCE length 05 (AlgorithmIdentifier) { + "0603" // OBJECT IDENTIFIER length 3 (algorithm) + "2b656e" // 1.3.101.110 (id-X125519 RFC 8410 s3) + "0422" // OCTET STRING length 0x22 (PrivateKey) + "0420" // OCTET STRING length 0x20 (RFC 8410 s7) + "680fc05dcd2f0431fd27f0af69df26f5e2b70943361dd293387e4c848dff2d42"); +string x25519_pubkey = hex2str("be46925a857f17831d6d454b9d3d36a4a30166edf80eb82b684661c3e258f768"); + struct RSA_Delete { void operator()(RSA* p) { RSA_free(p); } }; @@ -1374,7 +1498,7 @@ TEST_P(NewKeyGenerationTest, RsaMissingParams) { /* * NewKeyGenerationTest.Ecdsa * - * Verifies that keymint can generate all required EC key sizes, and that the resulting keys + * Verifies that keymint can generate all required EC curves, and that the resulting keys * have correct characteristics. */ TEST_P(NewKeyGenerationTest, Ecdsa) { @@ -1400,6 +1524,65 @@ TEST_P(NewKeyGenerationTest, Ecdsa) { } /* + * NewKeyGenerationTest.EcdsaCurve25519 + * + * Verifies that keymint can generate a curve25519 key, and that the resulting key + * has correct characteristics. + */ +TEST_P(NewKeyGenerationTest, EcdsaCurve25519) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + EcCurve curve = EcCurve::CURVE_25519; + vector<uint8_t> key_blob; + vector<KeyCharacteristics> key_characteristics; + ErrorCode result = GenerateKey(AuthorizationSetBuilder() + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) + .SetDefaultValidity(), + &key_blob, &key_characteristics); + ASSERT_EQ(result, ErrorCode::OK); + ASSERT_GT(key_blob.size(), 0U); + + EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_)); + ASSERT_GT(cert_chain_.size(), 0); + + CheckBaseParams(key_characteristics); + CheckCharacteristics(key_blob, key_characteristics); + + AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics); + + EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC)); + EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing"; + + CheckedDeleteKey(&key_blob); +} + +/* + * NewKeyGenerationTest.EcCurve25519MultiPurposeFail + * + * Verifies that KeyMint rejects an attempt to generate a curve 25519 key for both + * SIGN and AGREE_KEY. + */ +TEST_P(NewKeyGenerationTest, EcdsaCurve25519MultiPurposeFail) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + EcCurve curve = EcCurve::CURVE_25519; + vector<uint8_t> key_blob; + vector<KeyCharacteristics> key_characteristics; + ErrorCode result = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) + .SetDefaultValidity(), + &key_blob, &key_characteristics); + ASSERT_EQ(result, ErrorCode::INCOMPATIBLE_PURPOSE); +} + +/* * NewKeyGenerationTest.EcdsaAttestation * * Verifies that for all Ecdsa key sizes, if challenge and app id is provided, @@ -1453,6 +1636,62 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { } /* + * NewKeyGenerationTest.EcdsaAttestationCurve25519 + * + * Verifies that for a curve 25519 key, if challenge and app id is provided, + * an attestation will be generated. + */ +TEST_P(NewKeyGenerationTest, EcdsaAttestationCurve25519) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + EcCurve curve = EcCurve::CURVE_25519; + 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 = 0xFFFFFFFFFFFFFFFF; + vector<uint8_t> serial_blob(build_serial_blob(serial_int)); + + vector<uint8_t> key_blob; + vector<KeyCharacteristics> key_characteristics; + ErrorCode result = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) + .AttestationChallenge(challenge) + .AttestationApplicationId(app_id) + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), + &key_blob, &key_characteristics); + ASSERT_EQ(ErrorCode::OK, result); + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); + CheckCharacteristics(key_blob, key_characteristics); + + AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics); + + EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC)); + EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing"; + + EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_)); + ASSERT_GT(cert_chain_.size(), 0); + verify_subject_and_serial(cert_chain_[0], serial_int, subject, false); + + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics); + AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics); + EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, // + sw_enforced, hw_enforced, SecLevel(), + cert_chain_[0].encodedCertificate)); + + CheckedDeleteKey(&key_blob); +} + +/* * NewKeyGenerationTest.EcdsaAttestationTags * * Verifies that creation of an attested ECDSA key includes various tags in the @@ -1984,20 +2223,22 @@ TEST_P(NewKeyGenerationTest, EcdsaDefaultSize) { } /* - * NewKeyGenerationTest.EcdsaInvalidSize + * NewKeyGenerationTest.EcdsaInvalidCurve * - * Verifies that specifying an invalid key size for EC key generation returns + * Verifies that specifying an invalid curve for EC key generation returns * UNSUPPORTED_KEY_SIZE. */ -TEST_P(NewKeyGenerationTest, EcdsaInvalidSize) { +TEST_P(NewKeyGenerationTest, EcdsaInvalidCurve) { for (auto curve : InvalidCurves()) { vector<uint8_t> key_blob; vector<KeyCharacteristics> key_characteristics; - ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE, GenerateKey(AuthorizationSetBuilder() - .EcdsaSigningKey(curve) - .Digest(Digest::NONE) - .SetDefaultValidity(), - &key_blob, &key_characteristics)); + auto result = GenerateKey(AuthorizationSetBuilder() + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) + .SetDefaultValidity(), + &key_blob, &key_characteristics); + ASSERT_TRUE(result == ErrorCode::UNSUPPORTED_KEY_SIZE || + result == ErrorCode::UNSUPPORTED_EC_CURVE); } ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE, @@ -2808,15 +3049,19 @@ TEST_P(SigningOperationsTest, RsaSignTooLargeMessage) { /* * SigningOperationsTest.EcdsaAllDigestsAndCurves * - * Verifies ECDSA signature/verification for all digests and curves. + * Verifies ECDSA signature/verification for all digests and required curves. */ TEST_P(SigningOperationsTest, EcdsaAllDigestsAndCurves) { - auto digests = ValidDigests(true /* withNone */, false /* withMD5 */); string message = "1234567890"; string corrupt_message = "2234567890"; for (auto curve : ValidCurves()) { SCOPED_TRACE(testing::Message() << "Curve::" << curve); + // Ed25519 only allows Digest::NONE. + auto digests = (curve == EcCurve::CURVE_25519) + ? std::vector<Digest>(1, Digest::NONE) + : ValidDigests(true /* withNone */, false /* withMD5 */); + ErrorCode error = GenerateKey(AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) .EcdsaSigningKey(curve) @@ -2841,25 +3086,141 @@ TEST_P(SigningOperationsTest, EcdsaAllDigestsAndCurves) { /* * SigningOperationsTest.EcdsaAllCurves * - * Verifies that ECDSA operations succeed with all possible curves. + * Verifies that ECDSA operations succeed with all required curves. */ TEST_P(SigningOperationsTest, EcdsaAllCurves) { for (auto curve : ValidCurves()) { + Digest digest = (curve == EcCurve::CURVE_25519 ? Digest::NONE : Digest::SHA_2_256); + SCOPED_TRACE(testing::Message() << "Curve::" << curve); ErrorCode error = GenerateKey(AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) .EcdsaSigningKey(curve) - .Digest(Digest::SHA_2_256) + .Digest(digest) .SetDefaultValidity()); EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve; if (error != ErrorCode::OK) continue; string message(1024, 'a'); - SignMessage(message, AuthorizationSetBuilder().Digest(Digest::SHA_2_256)); + SignMessage(message, AuthorizationSetBuilder().Digest(digest)); CheckedDeleteKey(); } } /* + * SigningOperationsTest.EcdsaCurve25519 + * + * Verifies that ECDSA operations succeed with curve25519. + */ +TEST_P(SigningOperationsTest, EcdsaCurve25519) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + EcCurve curve = EcCurve::CURVE_25519; + ErrorCode error = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) + .SetDefaultValidity()); + ASSERT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve; + + string message(1024, 'a'); + SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE)); + CheckedDeleteKey(); +} + +/* + * SigningOperationsTest.EcdsaCurve25519MaxSize + * + * Verifies that EDDSA operations with curve25519 under the maximum message size succeed. + */ +TEST_P(SigningOperationsTest, EcdsaCurve25519MaxSize) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + EcCurve curve = EcCurve::CURVE_25519; + ErrorCode error = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) + .SetDefaultValidity()); + ASSERT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve; + + auto params = AuthorizationSetBuilder().Digest(Digest::NONE); + + for (size_t msg_size : {MAX_ED25519_MSG_SIZE - 1, MAX_ED25519_MSG_SIZE}) { + SCOPED_TRACE(testing::Message() << "-msg-size=" << msg_size); + string message(msg_size, 'a'); + + // Attempt to sign via Begin+Finish. + AuthorizationSet out_params; + ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params)); + EXPECT_TRUE(out_params.empty()); + string signature; + auto result = Finish(message, &signature); + EXPECT_EQ(result, ErrorCode::OK); + LocalVerifyMessage(message, signature, params); + + // Attempt to sign via Begin+Update+Finish + ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params)); + EXPECT_TRUE(out_params.empty()); + string output; + result = Update(message, &output); + EXPECT_EQ(result, ErrorCode::OK); + EXPECT_EQ(output.size(), 0); + string signature2; + EXPECT_EQ(ErrorCode::OK, Finish({}, &signature2)); + LocalVerifyMessage(message, signature2, params); + } + + CheckedDeleteKey(); +} + +/* + * SigningOperationsTest.EcdsaCurve25519MaxSizeFail + * + * Verifies that EDDSA operations with curve25519 fail when message size is too large. + */ +TEST_P(SigningOperationsTest, EcdsaCurve25519MaxSizeFail) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + EcCurve curve = EcCurve::CURVE_25519; + ErrorCode error = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) + .SetDefaultValidity()); + ASSERT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve; + + auto params = AuthorizationSetBuilder().Digest(Digest::NONE); + + for (size_t msg_size : {MAX_ED25519_MSG_SIZE + 1, MAX_ED25519_MSG_SIZE * 2}) { + SCOPED_TRACE(testing::Message() << "-msg-size=" << msg_size); + string message(msg_size, 'a'); + + // Attempt to sign via Begin+Finish. + AuthorizationSet out_params; + ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params)); + EXPECT_TRUE(out_params.empty()); + string signature; + auto result = Finish(message, &signature); + EXPECT_EQ(result, ErrorCode::INVALID_INPUT_LENGTH); + + // Attempt to sign via Begin+Update (but never get to Finish) + ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params)); + EXPECT_TRUE(out_params.empty()); + string output; + result = Update(message, &output); + EXPECT_EQ(result, ErrorCode::INVALID_INPUT_LENGTH); + } + + CheckedDeleteKey(); +} + +/* * SigningOperationsTest.EcdsaNoDigestHugeData * * Verifies that ECDSA operations support very large messages, even without digesting. This @@ -3509,6 +3870,255 @@ TEST_P(ImportKeyTest, EcdsaAttestMultiPurposeFail) { } /* + * ImportKeyTest.Ed25519RawSuccess + * + * Verifies that importing and using a raw Ed25519 private key works correctly. + */ +TEST_P(ImportKeyTest, Ed25519RawSuccess) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(EcCurve::CURVE_25519) + .Digest(Digest::NONE) + .SetDefaultValidity(), + KeyFormat::RAW, ed25519_key)); + CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC); + CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519); + CheckOrigin(); + + // The returned cert should hold the correct public key. + ASSERT_GT(cert_chain_.size(), 0); + X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate)); + ASSERT_NE(kmKeyCert, nullptr); + EVP_PKEY_Ptr kmPubKey(X509_get_pubkey(kmKeyCert.get())); + ASSERT_NE(kmPubKey.get(), nullptr); + size_t kmPubKeySize = 32; + uint8_t kmPubKeyData[32]; + ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize)); + ASSERT_EQ(kmPubKeySize, 32); + EXPECT_EQ(string(kmPubKeyData, kmPubKeyData + 32), ed25519_pubkey); + + string message(32, 'a'); + auto params = AuthorizationSetBuilder().Digest(Digest::NONE); + string signature = SignMessage(message, params); + LocalVerifyMessage(message, signature, params); +} + +/* + * ImportKeyTest.Ed25519Pkcs8Success + * + * Verifies that importing and using a PKCS#8-encoded Ed25519 private key works correctly. + */ +TEST_P(ImportKeyTest, Ed25519Pkcs8Success) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(EcCurve::CURVE_25519) + .Digest(Digest::NONE) + .SetDefaultValidity(), + KeyFormat::PKCS8, ed25519_pkcs8_key)); + CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC); + CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519); + CheckOrigin(); + + // The returned cert should hold the correct public key. + ASSERT_GT(cert_chain_.size(), 0); + X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate)); + ASSERT_NE(kmKeyCert, nullptr); + EVP_PKEY_Ptr kmPubKey(X509_get_pubkey(kmKeyCert.get())); + ASSERT_NE(kmPubKey.get(), nullptr); + size_t kmPubKeySize = 32; + uint8_t kmPubKeyData[32]; + ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize)); + ASSERT_EQ(kmPubKeySize, 32); + EXPECT_EQ(string(kmPubKeyData, kmPubKeyData + 32), ed25519_pubkey); + + string message(32, 'a'); + auto params = AuthorizationSetBuilder().Digest(Digest::NONE); + string signature = SignMessage(message, params); + LocalVerifyMessage(message, signature, params); +} + +/* + * ImportKeyTest.Ed25519CurveMismatch + * + * Verifies that importing an Ed25519 key pair with a curve that doesn't match the key fails in + * the correct way. + */ +TEST_P(ImportKeyTest, Ed25519CurveMismatch) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_NE(ErrorCode::OK, + ImportKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::P_224 /* Doesn't match key */) + .Digest(Digest::NONE) + .SetDefaultValidity(), + KeyFormat::RAW, ed25519_key)); +} + +/* + * ImportKeyTest.Ed25519FormatMismatch + * + * Verifies that importing an Ed25519 key pair with an invalid format fails. + */ +TEST_P(ImportKeyTest, Ed25519FormatMismatch) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::CURVE_25519) + .Digest(Digest::NONE) + .SetDefaultValidity(), + KeyFormat::PKCS8, ed25519_key)); + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::CURVE_25519) + .Digest(Digest::NONE) + .SetDefaultValidity(), + KeyFormat::RAW, ed25519_pkcs8_key)); +} + +/* + * ImportKeyTest.Ed25519PurposeMismatch + * + * Verifies that importing an Ed25519 key pair with an invalid purpose fails. + */ +TEST_P(ImportKeyTest, Ed25519PurposeMismatch) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + // Can't have both SIGN and ATTEST_KEY + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::ATTEST_KEY) + .Digest(Digest::NONE) + .SetDefaultValidity(), + KeyFormat::RAW, ed25519_key)); + // AGREE_KEY is for X25519 (but can only tell the difference if the import key is in + // PKCS#8 format and so includes an OID). + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .Digest(Digest::NONE) + .SetDefaultValidity(), + KeyFormat::PKCS8, ed25519_pkcs8_key)); +} + +/* + * ImportKeyTest.X25519RawSuccess + * + * Verifies that importing and using a raw X25519 private key works correctly. + */ +TEST_P(ImportKeyTest, X25519RawSuccess) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .SetDefaultValidity(), + KeyFormat::RAW, x25519_key)); + + CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC); + CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519); + CheckOrigin(); +} + +/* + * ImportKeyTest.X25519Pkcs8Success + * + * Verifies that importing and using a PKCS#8-encoded X25519 private key works correctly. + */ +TEST_P(ImportKeyTest, X25519Pkcs8Success) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .SetDefaultValidity(), + KeyFormat::PKCS8, x25519_pkcs8_key)); + + CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC); + CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519); + CheckOrigin(); +} + +/* + * ImportKeyTest.X25519CurveMismatch + * + * Verifies that importing an X25519 key with a curve that doesn't match the key fails in + * the correct way. + */ +TEST_P(ImportKeyTest, X25519CurveMismatch) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaKey(EcCurve::P_224 /* Doesn't match key */) + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .SetDefaultValidity(), + KeyFormat::RAW, x25519_key)); +} + +/* + * ImportKeyTest.X25519FormatMismatch + * + * Verifies that importing an X25519 key with an invalid format fails. + */ +TEST_P(ImportKeyTest, X25519FormatMismatch) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .SetDefaultValidity(), + KeyFormat::PKCS8, x25519_key)); + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .SetDefaultValidity(), + KeyFormat::RAW, x25519_pkcs8_key)); +} + +/* + * ImportKeyTest.X25519PurposeMismatch + * + * Verifies that importing an X25519 key pair with an invalid format fails. + */ +TEST_P(ImportKeyTest, X25519PurposeMismatch) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::ATTEST_KEY) + .SetDefaultValidity(), + KeyFormat::PKCS8, x25519_pkcs8_key)); + ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::CURVE_25519) + .SetDefaultValidity(), + KeyFormat::PKCS8, x25519_pkcs8_key)); +} + +/* * ImportKeyTest.AesSuccess * * Verifies that importing and using an AES key works. @@ -6703,8 +7313,6 @@ TEST_P(TransportLimitTest, LargeFinishInput) { INSTANTIATE_KEYMINT_AIDL_TEST(TransportLimitTest); -typedef KeyMintAidlTestBase KeyAgreementTest; - static int EcdhCurveToOpenSslCurveName(EcCurve curve) { switch (curve) { case EcCurve::P_224: @@ -6720,10 +7328,108 @@ static int EcdhCurveToOpenSslCurveName(EcCurve curve) { } } +class KeyAgreementTest : public KeyMintAidlTestBase { + protected: + void GenerateLocalEcKey(EcCurve localCurve, EVP_PKEY_Ptr* localPrivKey, + std::vector<uint8_t>* localPublicKey) { + // Generate EC key locally (with access to private key material) + if (localCurve == EcCurve::CURVE_25519) { + uint8_t privKeyData[32]; + uint8_t pubKeyData[32]; + X25519_keypair(pubKeyData, privKeyData); + *localPublicKey = vector<uint8_t>(pubKeyData, pubKeyData + 32); + *localPrivKey = EVP_PKEY_Ptr(EVP_PKEY_new_raw_private_key( + EVP_PKEY_X25519, nullptr, privKeyData, sizeof(privKeyData))); + } else { + auto ecKey = EC_KEY_Ptr(EC_KEY_new()); + int curveName = EcdhCurveToOpenSslCurveName(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); + *localPrivKey = EVP_PKEY_Ptr(EVP_PKEY_new()); + ASSERT_EQ(EVP_PKEY_set1_EC_KEY(localPrivKey->get(), ecKey.get()), 1); + + // Get encoded form of the public part of the locally generated key... + unsigned char* p = nullptr; + int localPublicKeySize = i2d_PUBKEY(localPrivKey->get(), &p); + ASSERT_GT(localPublicKeySize, 0); + *localPublicKey = + vector<uint8_t>(reinterpret_cast<const uint8_t*>(p), + reinterpret_cast<const uint8_t*>(p + localPublicKeySize)); + OPENSSL_free(p); + } + } + + void GenerateKeyMintEcKey(EcCurve curve, EVP_PKEY_Ptr* kmPubKey) { + vector<uint8_t> challenge = {0x41, 0x42}; + ErrorCode result = + 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) + .SetDefaultValidity()); + ASSERT_EQ(ErrorCode::OK, result) << "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); + *kmPubKey = EVP_PKEY_Ptr(X509_get_pubkey(kmKeyCert.get())); + ASSERT_NE(*kmPubKey, nullptr); + if (dump_Attestations) { + for (size_t n = 0; n < cert_chain_.size(); n++) { + std::cout << bin2hex(cert_chain_[n].encodedCertificate) << std::endl; + } + } + } + + void CheckAgreement(EVP_PKEY_Ptr kmPubKey, EVP_PKEY_Ptr localPrivKey, + const std::vector<uint8_t>& localPublicKey) { + ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder())); + string ZabFromKeyMintStr; + ASSERT_EQ(ErrorCode::OK, + Finish(string(localPublicKey.begin(), localPublicKey.end()), &ZabFromKeyMintStr)); + vector<uint8_t> ZabFromKeyMint(ZabFromKeyMintStr.begin(), ZabFromKeyMintStr.end()); + vector<uint8_t> ZabFromTest; + + if (EVP_PKEY_id(kmPubKey.get()) == EVP_PKEY_X25519) { + size_t kmPubKeySize = 32; + uint8_t kmPubKeyData[32]; + ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize)); + ASSERT_EQ(kmPubKeySize, 32); + + uint8_t localPrivKeyData[32]; + size_t localPrivKeySize = 32; + ASSERT_EQ(1, EVP_PKEY_get_raw_private_key(localPrivKey.get(), localPrivKeyData, + &localPrivKeySize)); + ASSERT_EQ(localPrivKeySize, 32); + + uint8_t sharedKey[32]; + ASSERT_EQ(1, X25519(sharedKey, localPrivKeyData, kmPubKeyData)); + ZabFromTest = std::vector<uint8_t>(sharedKey, sharedKey + 32); + } else { + // 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(localPrivKey.get(), nullptr)); + ASSERT_NE(ctx, nullptr); + ASSERT_EQ(EVP_PKEY_derive_init(ctx.get()), 1); + ASSERT_EQ(EVP_PKEY_derive_set_peer(ctx.get(), kmPubKey.get()), 1); + size_t ZabFromTestLen = 0; + ASSERT_EQ(EVP_PKEY_derive(ctx.get(), nullptr, &ZabFromTestLen), 1); + ZabFromTest.resize(ZabFromTestLen); + ASSERT_EQ(EVP_PKEY_derive(ctx.get(), ZabFromTest.data(), &ZabFromTestLen), 1); + } + EXPECT_EQ(ZabFromKeyMint, ZabFromTest); + } +}; + /* * KeyAgreementTest.Ecdh * - * Verifies that ECDH works for all curves + * Verifies that ECDH works for all required curves */ TEST_P(KeyAgreementTest, Ecdh) { // Because it's possible to use this API with keys on different curves, we @@ -6737,49 +7443,13 @@ TEST_P(KeyAgreementTest, Ecdh) { 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 = EcdhCurveToOpenSslCurveName(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); + EVP_PKEY_Ptr localPrivKey; + vector<uint8_t> localPublicKey; + GenerateLocalEcKey(localCurve, &localPrivKey, &localPublicKey); // 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) - .SetDefaultValidity())) - << "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; - } - } + EVP_PKEY_Ptr kmPubKey; + GenerateKeyMintEcKey(curve, &kmPubKey); // Now that we have the two keys, we ask KeyMint to perform ECDH... if (curve != localCurve) { @@ -6788,30 +7458,12 @@ TEST_P(KeyAgreementTest, Ecdh) { EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder())); string ZabFromKeyMintStr; EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, - Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()), + Finish(string(localPublicKey.begin(), localPublicKey.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); + CheckAgreement(std::move(kmPubKey), std::move(localPrivKey), localPublicKey); } CheckedDeleteKey(); @@ -6819,6 +7471,140 @@ TEST_P(KeyAgreementTest, Ecdh) { } } +/* + * KeyAgreementTest.EcdhCurve25519 + * + * Verifies that ECDH works for curve25519. This is also covered by the general + * KeyAgreementTest.Ecdh case, but is pulled out separately here because this curve was added after + * KeyMint 1.0. + */ +TEST_P(KeyAgreementTest, EcdhCurve25519) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + // Generate EC key in KeyMint (only access to public key material) + EcCurve curve = EcCurve::CURVE_25519; + EVP_PKEY_Ptr kmPubKey = nullptr; + GenerateKeyMintEcKey(curve, &kmPubKey); + + // Generate EC key on same curve locally (with access to private key material). + EVP_PKEY_Ptr privKey; + vector<uint8_t> encodedPublicKey; + GenerateLocalEcKey(curve, &privKey, &encodedPublicKey); + + // Agree on a key between local and KeyMint and check it. + CheckAgreement(std::move(kmPubKey), std::move(privKey), encodedPublicKey); + + CheckedDeleteKey(); +} + +/* + * KeyAgreementTest.EcdhCurve25519Imported + * + * Verifies that ECDH works for an imported curve25519 key. + */ +TEST_P(KeyAgreementTest, EcdhCurve25519Imported) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + // Import x25519 key into KeyMint. + EcCurve curve = EcCurve::CURVE_25519; + ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaKey(EcCurve::CURVE_25519) + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .SetDefaultValidity(), + KeyFormat::PKCS8, x25519_pkcs8_key)); + ASSERT_GT(cert_chain_.size(), 0); + X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate)); + ASSERT_NE(kmKeyCert, nullptr); + EVP_PKEY_Ptr kmPubKey(X509_get_pubkey(kmKeyCert.get())); + ASSERT_NE(kmPubKey.get(), nullptr); + + // Expect the import to emit corresponding public key data. + size_t kmPubKeySize = 32; + uint8_t kmPubKeyData[32]; + ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize)); + ASSERT_EQ(kmPubKeySize, 32); + EXPECT_EQ(bin2hex(std::vector<uint8_t>(kmPubKeyData, kmPubKeyData + 32)), + bin2hex(std::vector<uint8_t>(x25519_pubkey.begin(), x25519_pubkey.end()))); + + // Generate EC key on same curve locally (with access to private key material). + EVP_PKEY_Ptr privKey; + vector<uint8_t> encodedPublicKey; + GenerateLocalEcKey(curve, &privKey, &encodedPublicKey); + + // Agree on a key between local and KeyMint and check it. + CheckAgreement(std::move(kmPubKey), std::move(privKey), encodedPublicKey); + + CheckedDeleteKey(); +} + +/* + * KeyAgreementTest.EcdhCurve25519InvalidSize + * + * Verifies that ECDH fails for curve25519 if the wrong size of public key is provided. + */ +TEST_P(KeyAgreementTest, EcdhCurve25519InvalidSize) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + // Generate EC key in KeyMint (only access to public key material) + EcCurve curve = EcCurve::CURVE_25519; + EVP_PKEY_Ptr kmPubKey = nullptr; + GenerateKeyMintEcKey(curve, &kmPubKey); + + // Generate EC key on same curve locally (with access to private key material). + EVP_PKEY_Ptr privKey; + vector<uint8_t> encodedPublicKey; + GenerateLocalEcKey(curve, &privKey, &encodedPublicKey); + + ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder())); + string ZabFromKeyMintStr; + // Send in an incomplete public key. + ASSERT_NE(ErrorCode::OK, Finish(string(encodedPublicKey.begin(), encodedPublicKey.end() - 1), + &ZabFromKeyMintStr)); + + CheckedDeleteKey(); +} + +/* + * KeyAgreementTest.EcdhCurve25519Mismatch + * + * Verifies that ECDH fails between curve25519 and other curves. + */ +TEST_P(KeyAgreementTest, EcdhCurve25519Mismatch) { + if (!Curve25519Supported()) { + GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519"; + } + + // Generate EC key in KeyMint (only access to public key material) + EcCurve curve = EcCurve::CURVE_25519; + EVP_PKEY_Ptr kmPubKey = nullptr; + GenerateKeyMintEcKey(curve, &kmPubKey); + + for (auto localCurve : ValidCurves()) { + if (localCurve == curve) { + continue; + } + // Generate EC key on a different curve locally (with access to private key material). + EVP_PKEY_Ptr privKey; + vector<uint8_t> encodedPublicKey; + GenerateLocalEcKey(localCurve, &privKey, &encodedPublicKey); + + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder())); + string ZabFromKeyMintStr; + EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, + Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()), + &ZabFromKeyMintStr)); + } + + CheckedDeleteKey(); +} + INSTANTIATE_KEYMINT_AIDL_TEST(KeyAgreementTest); using DestroyAttestationIdsTest = KeyMintAidlTestBase; diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp index c9d506f788..829780d442 100644 --- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp +++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp @@ -20,6 +20,7 @@ #include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h> #include <aidl/android/hardware/security/keymint/SecurityLevel.h> #include <android/binder_manager.h> +#include <binder/IServiceManager.h> #include <cppbor_parse.h> #include <gmock/gmock.h> #include <keymaster/cppcose/cppcose.h> @@ -29,6 +30,7 @@ #include <openssl/ec_key.h> #include <openssl/x509.h> #include <remote_prov/remote_prov_utils.h> +#include <set> #include <vector> #include "KeyMintAidlTestBase.h" @@ -40,6 +42,8 @@ using ::std::vector; namespace { +constexpr int32_t VERSION_WITH_UNIQUE_ID_SUPPORT = 2; + #define INSTANTIATE_REM_PROV_AIDL_TEST(name) \ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name); \ INSTANTIATE_TEST_SUITE_P( \ @@ -47,6 +51,7 @@ namespace { testing::ValuesIn(VtsRemotelyProvisionedComponentTests::build_params()), \ ::android::PrintInstanceNameToString) +using ::android::sp; using bytevec = std::vector<uint8_t>; using testing::MatchesRegex; using namespace remote_prov; @@ -175,6 +180,67 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std:: std::shared_ptr<IRemotelyProvisionedComponent> provisionable_; }; +/** + * Verify that every implementation reports a different unique id. + */ +TEST(NonParameterizedTests, eachRpcHasAUniqueId) { + std::set<std::string> uniqueIds; + for (auto hal : ::android::getAidlHalInstanceNames(IRemotelyProvisionedComponent::descriptor)) { + ASSERT_TRUE(AServiceManager_isDeclared(hal.c_str())); + ::ndk::SpAIBinder binder(AServiceManager_waitForService(hal.c_str())); + std::shared_ptr<IRemotelyProvisionedComponent> rpc = + IRemotelyProvisionedComponent::fromBinder(binder); + ASSERT_NE(rpc, nullptr); + + RpcHardwareInfo hwInfo; + ASSERT_TRUE(rpc->getHardwareInfo(&hwInfo).isOk()); + + int32_t version; + ASSERT_TRUE(rpc->getInterfaceVersion(&version).isOk()); + if (version >= VERSION_WITH_UNIQUE_ID_SUPPORT) { + ASSERT_TRUE(hwInfo.uniqueId); + auto [_, wasInserted] = uniqueIds.insert(*hwInfo.uniqueId); + EXPECT_TRUE(wasInserted); + } else { + ASSERT_FALSE(hwInfo.uniqueId); + } + } +} + +using GetHardwareInfoTests = VtsRemotelyProvisionedComponentTests; + +INSTANTIATE_REM_PROV_AIDL_TEST(GetHardwareInfoTests); + +/** + * Verify that a valid curve is reported by the implementation. + */ +TEST_P(GetHardwareInfoTests, supportsValidCurve) { + RpcHardwareInfo hwInfo; + ASSERT_TRUE(provisionable_->getHardwareInfo(&hwInfo).isOk()); + + const std::set<int> validCurves = {RpcHardwareInfo::CURVE_P256, RpcHardwareInfo::CURVE_25519}; + ASSERT_EQ(validCurves.count(hwInfo.supportedEekCurve), 1) + << "Invalid curve: " << hwInfo.supportedEekCurve; +} + +/** + * Verify that the unique id is within the length limits as described in RpcHardwareInfo.aidl. + */ +TEST_P(GetHardwareInfoTests, uniqueId) { + int32_t version; + ASSERT_TRUE(provisionable_->getInterfaceVersion(&version).isOk()); + + if (version < VERSION_WITH_UNIQUE_ID_SUPPORT) { + return; + } + + RpcHardwareInfo hwInfo; + ASSERT_TRUE(provisionable_->getHardwareInfo(&hwInfo).isOk()); + ASSERT_TRUE(hwInfo.uniqueId); + EXPECT_GE(hwInfo.uniqueId->size(), 1); + EXPECT_LE(hwInfo.uniqueId->size(), 32); +} + using GenerateKeyTests = VtsRemotelyProvisionedComponentTests; INSTANTIATE_REM_PROV_AIDL_TEST(GenerateKeyTests); |