diff options
Diffstat (limited to 'security/keymint/aidl/vts/functional/KeyMintTest.cpp')
-rw-r--r-- | security/keymint/aidl/vts/functional/KeyMintTest.cpp | 2079 |
1 files changed, 1688 insertions, 391 deletions
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp index 4d7f1b8eec..4e746b2bb9 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,7 +70,17 @@ namespace aidl::android::hardware::security::keymint::test { namespace { -bool check_patchLevels = false; +// 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; + +// The maximum number of times we'll attempt to verify that corruption +// of an encrypted blob results in an error. Retries are necessary as there +// is a small (roughly 1/256) chance that corrupting ciphertext still results +// in valid PKCS7 padding. +constexpr size_t kMaxPaddingCorruptionRetries = 8; template <TagType tag_type, Tag tag, typename ValueT> bool contains(const vector<KeyParameter>& set, TypedTag<tag_type, tag> ttag, @@ -408,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); } }; @@ -523,11 +654,14 @@ class NewKeyGenerationTest : public KeyMintAidlTestBase { EXPECT_TRUE(os_pl); EXPECT_EQ(*os_pl, os_patch_level()); - if (check_patchLevels) { - // Should include vendor and boot patchlevels. - auto vendor_pl = auths.GetTagValue(TAG_VENDOR_PATCHLEVEL); - EXPECT_TRUE(vendor_pl); - EXPECT_EQ(*vendor_pl, vendor_patch_level()); + // Should include vendor patchlevel. + auto vendor_pl = auths.GetTagValue(TAG_VENDOR_PATCHLEVEL); + EXPECT_TRUE(vendor_pl); + EXPECT_EQ(*vendor_pl, vendor_patch_level()); + + // Should include boot patchlevel (but there are some test scenarios where this is not + // possible). + if (check_boot_pl) { auto boot_pl = auths.GetTagValue(TAG_BOOT_PATCHLEVEL); EXPECT_TRUE(boot_pl); } @@ -884,6 +1018,37 @@ TEST_P(NewKeyGenerationTest, Rsa) { } /* + * NewKeyGenerationTest.RsaWithMissingValidity + * + * Verifies that keymint returns an error while generating asymmetric key + * without providing NOT_BEFORE and NOT_AFTER parameters. + */ +TEST_P(NewKeyGenerationTest, RsaWithMissingValidity) { + // Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to + // GeneralizedTime 999912312359559, which is 253402300799000 ms from Jan 1, 1970. + constexpr uint64_t kUndefinedExpirationDateTime = 253402300799000; + + vector<uint8_t> key_blob; + vector<KeyCharacteristics> key_characteristics; + ASSERT_EQ(ErrorCode::MISSING_NOT_BEFORE, + GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(2048, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) + .Authorization(TAG_CERTIFICATE_NOT_AFTER, + kUndefinedExpirationDateTime), + &key_blob, &key_characteristics)); + + ASSERT_EQ(ErrorCode::MISSING_NOT_AFTER, + GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(2048, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) + .Authorization(TAG_CERTIFICATE_NOT_BEFORE, 0), + &key_blob, &key_characteristics)); +} + +/* * NewKeyGenerationTest.RsaWithAttestation * * Verifies that keymint can generate all required RSA key sizes with attestation, and that the @@ -902,19 +1067,30 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { for (auto key_size : ValidKeySizes(Algorithm::RSA)) { 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) - .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) - .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) - .SetDefaultValidity(), - &key_blob, &key_characteristics)); + auto builder = AuthorizationSetBuilder() + .RsaSigningKey(key_size, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) + .AttestationChallenge(challenge) + .AttestationApplicationId(app_id) + .Authorization(TAG_NO_AUTH_REQUIRED) + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(); + auto result = GenerateKey(builder, &key_blob, &key_characteristics); + // Strongbox may not support factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { + if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) { + result = GenerateKeyWithSelfSignedAttestKey( + AuthorizationSetBuilder() + .RsaKey(key_size, 65537) + .AttestKey() + .SetDefaultValidity(), /* attest key params */ + builder, &key_blob, &key_characteristics); + } + } + ASSERT_EQ(ErrorCode::OK, result); ASSERT_GT(key_blob.size(), 0U); CheckBaseParams(key_characteristics); CheckCharacteristics(key_blob, key_characteristics); @@ -932,7 +1108,7 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics); AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics); - EXPECT_TRUE(verify_attestation_record(challenge, app_id, // + EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, // sw_enforced, hw_enforced, SecLevel(), cert_chain_[0].encodedCertificate)); @@ -1035,17 +1211,29 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { vector<uint8_t> key_blob; vector<KeyCharacteristics> key_characteristics; - ASSERT_EQ(ErrorCode::OK, - GenerateKey(AuthorizationSetBuilder() - .RsaEncryptionKey(key_size, 65537) - .Padding(PaddingMode::NONE) - .AttestationChallenge(challenge) - .AttestationApplicationId(app_id) - .Authorization(TAG_NO_AUTH_REQUIRED) - .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) - .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) - .SetDefaultValidity(), - &key_blob, &key_characteristics)); + auto builder = AuthorizationSetBuilder() + .RsaEncryptionKey(key_size, 65537) + .Padding(PaddingMode::NONE) + .AttestationChallenge(challenge) + .AttestationApplicationId(app_id) + .Authorization(TAG_NO_AUTH_REQUIRED) + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(); + + auto result = GenerateKey(builder, &key_blob, &key_characteristics); + // Strongbox may not support factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { + if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) { + result = GenerateKeyWithSelfSignedAttestKey( + AuthorizationSetBuilder() + .RsaKey(key_size, 65537) + .AttestKey() + .SetDefaultValidity(), /* attest key params */ + builder, &key_blob, &key_characteristics); + } + } + ASSERT_EQ(ErrorCode::OK, result); ASSERT_GT(key_blob.size(), 0U); AuthorizationSet auths; @@ -1083,7 +1271,7 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics); AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics); - EXPECT_TRUE(verify_attestation_record(challenge, app_id, // + EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, // sw_enforced, hw_enforced, SecLevel(), cert_chain_[0].encodedCertificate)); @@ -1147,15 +1335,27 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestationMissAppId) { vector<uint8_t> key_blob; vector<KeyCharacteristics> key_characteristics; - ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, - GenerateKey(AuthorizationSetBuilder() - .RsaSigningKey(2048, 65537) - .Digest(Digest::NONE) - .Padding(PaddingMode::NONE) - .AttestationChallenge(challenge) - .Authorization(TAG_NO_AUTH_REQUIRED) - .SetDefaultValidity(), - &key_blob, &key_characteristics)); + auto builder = AuthorizationSetBuilder() + .RsaSigningKey(2048, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) + .AttestationChallenge(challenge) + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(); + + auto result = GenerateKey(builder, &key_blob, &key_characteristics); + // Strongbox may not support factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { + if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) { + result = GenerateKeyWithSelfSignedAttestKey( + AuthorizationSetBuilder() + .RsaKey(2048, 65537) + .AttestKey() + .SetDefaultValidity(), /* attest key params */ + builder, &key_blob, &key_characteristics); + } + } + ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, result); } /* @@ -1265,19 +1465,31 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { for (auto key_size : ValidKeySizes(Algorithm::RSA)) { 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) - .Authorization(TAG_USAGE_COUNT_LIMIT, 1) - .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) - .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) - .SetDefaultValidity(), - &key_blob, &key_characteristics)); + auto builder = AuthorizationSetBuilder() + .RsaSigningKey(key_size, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) + .AttestationChallenge(challenge) + .AttestationApplicationId(app_id) + .Authorization(TAG_NO_AUTH_REQUIRED) + .Authorization(TAG_USAGE_COUNT_LIMIT, 1) + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(); + + auto result = GenerateKey(builder, &key_blob, &key_characteristics); + // Strongbox may not support factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { + if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) { + result = GenerateKeyWithSelfSignedAttestKey( + AuthorizationSetBuilder() + .RsaKey(key_size, 65537) + .AttestKey() + .SetDefaultValidity(), /* attest key params */ + builder, &key_blob, &key_characteristics); + } + } + ASSERT_EQ(ErrorCode::OK, result); ASSERT_GT(key_blob.size(), 0U); CheckBaseParams(key_characteristics); @@ -1305,7 +1517,7 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics); AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics); - EXPECT_TRUE(verify_attestation_record(challenge, app_id, // + EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, // sw_enforced, hw_enforced, SecLevel(), cert_chain_[0].encodedCertificate)); @@ -1364,7 +1576,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) { @@ -1390,6 +1602,94 @@ 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.EcdsaWithMissingValidity + * + * Verifies that keymint returns an error while generating asymmetric key + * without providing NOT_BEFORE and NOT_AFTER parameters. + */ +TEST_P(NewKeyGenerationTest, EcdsaWithMissingValidity) { + // Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to + // GeneralizedTime 999912312359559, which is 253402300799000 ms from Jan 1, 1970. + constexpr uint64_t kUndefinedExpirationDateTime = 253402300799000; + + vector<uint8_t> key_blob; + vector<KeyCharacteristics> key_characteristics; + ASSERT_EQ(ErrorCode::MISSING_NOT_BEFORE, + GenerateKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::P_256) + .Digest(Digest::NONE) + .Authorization(TAG_CERTIFICATE_NOT_AFTER, + kUndefinedExpirationDateTime), + &key_blob, &key_characteristics)); + + ASSERT_EQ(ErrorCode::MISSING_NOT_AFTER, + GenerateKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::P_256) + .Digest(Digest::NONE) + .Authorization(TAG_CERTIFICATE_NOT_BEFORE, 0), + &key_blob, &key_characteristics)); +} + +/* * NewKeyGenerationTest.EcdsaAttestation * * Verifies that for all Ecdsa key sizes, if challenge and app id is provided, @@ -1408,17 +1708,29 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { for (auto curve : ValidCurves()) { vector<uint8_t> key_blob; vector<KeyCharacteristics> key_characteristics; - ASSERT_EQ(ErrorCode::OK, - 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)); + auto builder = 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(); + + auto result = GenerateKey(builder, &key_blob, &key_characteristics); + // Strongbox may not support factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { + if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) { + result = GenerateKeyWithSelfSignedAttestKey( + AuthorizationSetBuilder() + .EcdsaKey(curve) + .AttestKey() + .SetDefaultValidity(), /* attest key params */ + builder, &key_blob, &key_characteristics); + } + } + ASSERT_EQ(ErrorCode::OK, result); ASSERT_GT(key_blob.size(), 0U); CheckBaseParams(key_characteristics); CheckCharacteristics(key_blob, key_characteristics); @@ -1434,7 +1746,7 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics); AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics); - EXPECT_TRUE(verify_attestation_record(challenge, app_id, // + EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, // sw_enforced, hw_enforced, SecLevel(), cert_chain_[0].encodedCertificate)); @@ -1443,6 +1755,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 @@ -1480,6 +1848,7 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED) .Authorization(TAG_UNLOCKED_DEVICE_REQUIRED) .Authorization(TAG_CREATION_DATETIME, 1619621648000); + for (const KeyParameter& tag : extra_tags) { SCOPED_TRACE(testing::Message() << "tag-" << tag); vector<uint8_t> key_blob; @@ -1495,6 +1864,17 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { // Tag not required to be supported by all KeyMint implementations. continue; } + // Strongbox may not support factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { + if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) { + result = GenerateKeyWithSelfSignedAttestKey( + AuthorizationSetBuilder() + .EcdsaKey(EcCurve::P_256) + .AttestKey() + .SetDefaultValidity(), /* attest key params */ + builder, &key_blob, &key_characteristics); + } + } ASSERT_EQ(result, ErrorCode::OK); ASSERT_GT(key_blob.size(), 0U); @@ -1512,25 +1892,26 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { // Verifying the attestation record will check for the specific tag because // it's included in the authorizations. - EXPECT_TRUE(verify_attestation_record(challenge, app_id, sw_enforced, hw_enforced, - SecLevel(), cert_chain_[0].encodedCertificate)); + EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, sw_enforced, + hw_enforced, SecLevel(), + cert_chain_[0].encodedCertificate)); CheckedDeleteKey(&key_blob); } - // Device attestation IDs should be rejected for normal attestation requests; these fields - // are only used for device unique attestation. - auto invalid_tags = AuthorizationSetBuilder() - .Authorization(TAG_ATTESTATION_ID_BRAND, "brand") - .Authorization(TAG_ATTESTATION_ID_DEVICE, "device") - .Authorization(TAG_ATTESTATION_ID_PRODUCT, "product") - .Authorization(TAG_ATTESTATION_ID_SERIAL, "serial") - .Authorization(TAG_ATTESTATION_ID_IMEI, "imei") - .Authorization(TAG_ATTESTATION_ID_MEID, "meid") - .Authorization(TAG_ATTESTATION_ID_MANUFACTURER, "manufacturer") - .Authorization(TAG_ATTESTATION_ID_MODEL, "model"); + // Collection of invalid attestation ID tags. + auto invalid_tags = + AuthorizationSetBuilder() + .Authorization(TAG_ATTESTATION_ID_BRAND, "bogus-brand") + .Authorization(TAG_ATTESTATION_ID_DEVICE, "devious-device") + .Authorization(TAG_ATTESTATION_ID_PRODUCT, "punctured-product") + .Authorization(TAG_ATTESTATION_ID_SERIAL, "suspicious-serial") + .Authorization(TAG_ATTESTATION_ID_IMEI, "invalid-imei") + .Authorization(TAG_ATTESTATION_ID_MEID, "mismatching-meid") + .Authorization(TAG_ATTESTATION_ID_MANUFACTURER, "malformed-manufacturer") + .Authorization(TAG_ATTESTATION_ID_MODEL, "malicious-model"); for (const KeyParameter& tag : invalid_tags) { - SCOPED_TRACE(testing::Message() << "tag-" << tag); + SCOPED_TRACE(testing::Message() << "-incorrect-tag-" << tag); vector<uint8_t> key_blob; vector<KeyCharacteristics> key_characteristics; AuthorizationSetBuilder builder = @@ -1544,8 +1925,98 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) .SetDefaultValidity(); builder.push_back(tag); - ASSERT_EQ(ErrorCode::CANNOT_ATTEST_IDS, - GenerateKey(builder, &key_blob, &key_characteristics)); + + auto error = GenerateKey(builder, &key_blob, &key_characteristics); + // Strongbox may not support factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { + if (error == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) { + error = GenerateKeyWithSelfSignedAttestKey( + AuthorizationSetBuilder() + .EcdsaKey(EcCurve::P_256) + .AttestKey() + .SetDefaultValidity(), /* attest key params */ + builder, &key_blob, &key_characteristics); + } + } + ASSERT_EQ(error, ErrorCode::CANNOT_ATTEST_IDS); + } +} + +/* + * NewKeyGenerationTest.EcdsaAttestationIdTags + * + * Verifies that creation of an attested ECDSA key includes various ID tags in the + * attestation extension. + */ +TEST_P(NewKeyGenerationTest, EcdsaAttestationIdTags) { + if (is_gsi_image()) { + // GSI sets up a standard set of device identifiers that may not match + // the device identifiers held by the device. + GTEST_SKIP() << "Test not applicable under GSI"; + } + 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 = 0x1010; + vector<uint8_t> serial_blob(build_serial_blob(serial_int)); + const AuthorizationSetBuilder base_builder = + AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(EcCurve::P_256) + .Digest(Digest::NONE) + .AttestationChallenge(challenge) + .AttestationApplicationId(app_id) + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(); + + // Various ATTESTATION_ID_* tags that map to fields in the attestation extension ASN.1 schema. + auto extra_tags = AuthorizationSetBuilder(); + add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand"); + add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device"); + add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name"); + add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serial"); + add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_MANUFACTURER, "ro.product.manufacturer"); + add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model"); + + for (const KeyParameter& tag : extra_tags) { + SCOPED_TRACE(testing::Message() << "tag-" << tag); + vector<uint8_t> key_blob; + vector<KeyCharacteristics> key_characteristics; + AuthorizationSetBuilder builder = base_builder; + builder.push_back(tag); + auto result = GenerateKey(builder, &key_blob, &key_characteristics); + // Strongbox may not support factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { + if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) return; + } + if (result == ErrorCode::CANNOT_ATTEST_IDS && !isDeviceIdAttestationRequired()) { + // ID attestation was optional till api level 32, from api level 33 it is mandatory. + continue; + } + ASSERT_EQ(result, ErrorCode::OK); + ASSERT_GT(key_blob.size(), 0U); + + EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_)); + ASSERT_GT(cert_chain_.size(), 0); + verify_subject_and_serial(cert_chain_[0], serial_int, subject, /* self_signed = */ false); + + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics); + AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics); + + // The attested key characteristics will not contain APPLICATION_ID_* fields (their + // spec definitions all have "Must never appear in KeyCharacteristics"), but the + // attestation extension should contain them, so make sure the extra tag is added. + hw_enforced.push_back(tag); + + // Verifying the attestation record will check for the specific tag because + // it's included in the authorizations. + EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, sw_enforced, + hw_enforced, SecLevel(), + cert_chain_[0].encodedCertificate)); + + CheckedDeleteKey(&key_blob); } } @@ -1577,8 +2048,18 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationUniqueId) { if (reset) { builder.Authorization(TAG_RESET_SINCE_ID_ROTATION); } - - ASSERT_EQ(ErrorCode::OK, GenerateKey(builder)); + auto result = GenerateKey(builder); + if (SecLevel() == SecurityLevel::STRONGBOX) { + if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) { + result = GenerateKeyWithSelfSignedAttestKey( + AuthorizationSetBuilder() + .EcdsaKey(EcCurve::P_256) + .AttestKey() + .SetDefaultValidity(), /* attest key params */ + builder, &key_blob_, &key_characteristics_, &cert_chain_); + } + } + ASSERT_EQ(ErrorCode::OK, result); ASSERT_GT(key_blob_.size(), 0U); EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_)); @@ -1589,9 +2070,9 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationUniqueId) { AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics_); // Check that the unique ID field in the extension is non-empty. - EXPECT_TRUE(verify_attestation_record(challenge, app_id, sw_enforced, hw_enforced, - SecLevel(), cert_chain_[0].encodedCertificate, - unique_id)); + EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, sw_enforced, + hw_enforced, SecLevel(), + cert_chain_[0].encodedCertificate, unique_id)); EXPECT_GT(unique_id->size(), 0); CheckedDeleteKey(); }; @@ -1665,18 +2146,30 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTagNoApplicationId) { // to confirm that this field never makes it into the attestation extension. vector<uint8_t> key_blob; vector<KeyCharacteristics> key_characteristics; - auto result = GenerateKey(AuthorizationSetBuilder() - .Authorization(TAG_NO_AUTH_REQUIRED) - .EcdsaSigningKey(EcCurve::P_256) - .Digest(Digest::NONE) - .AttestationChallenge(challenge) - .AttestationApplicationId(attest_app_id) - .Authorization(TAG_APPLICATION_ID, "client_id") - .Authorization(TAG_APPLICATION_DATA, "appdata") - .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) - .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) - .SetDefaultValidity(), - &key_blob, &key_characteristics); + auto builder = AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(EcCurve::P_256) + .Digest(Digest::NONE) + .AttestationChallenge(challenge) + .AttestationApplicationId(attest_app_id) + .Authorization(TAG_APPLICATION_ID, "client_id") + .Authorization(TAG_APPLICATION_DATA, "appdata") + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(); + + auto result = GenerateKey(builder, &key_blob, &key_characteristics); + // Strongbox may not support factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { + if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) { + result = GenerateKeyWithSelfSignedAttestKey( + AuthorizationSetBuilder() + .EcdsaKey(EcCurve::P_256) + .AttestKey() + .SetDefaultValidity(), /* attest key params */ + builder, &key_blob, &key_characteristics); + } + } ASSERT_EQ(result, ErrorCode::OK); ASSERT_GT(key_blob.size(), 0U); @@ -1686,8 +2179,9 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTagNoApplicationId) { AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics); AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics); - EXPECT_TRUE(verify_attestation_record(challenge, attest_app_id, sw_enforced, hw_enforced, - SecLevel(), cert_chain_[0].encodedCertificate)); + EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, attest_app_id, sw_enforced, + hw_enforced, SecLevel(), + cert_chain_[0].encodedCertificate)); // Check that the app id is not in the cert. string app_id = "clientid"; @@ -1754,14 +2248,25 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationRequireAppId) { auto challenge = "hello"; vector<uint8_t> key_blob; vector<KeyCharacteristics> key_characteristics; - - ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, - GenerateKey(AuthorizationSetBuilder() - .EcdsaSigningKey(EcCurve::P_256) - .Digest(Digest::NONE) - .AttestationChallenge(challenge) - .SetDefaultValidity(), - &key_blob, &key_characteristics)); + auto builder = AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::P_256) + .Digest(Digest::NONE) + .AttestationChallenge(challenge) + .SetDefaultValidity(); + + auto result = GenerateKey(builder, &key_blob, &key_characteristics); + // Strongbox may not support factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { + if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) { + result = GenerateKeyWithSelfSignedAttestKey( + AuthorizationSetBuilder() + .EcdsaKey(EcCurve::P_256) + .AttestKey() + .SetDefaultValidity(), /* attest key params */ + builder, &key_blob, &key_characteristics); + } + } + ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, result); } /* @@ -1818,14 +2323,27 @@ TEST_P(NewKeyGenerationTest, AttestationApplicationIDLengthProperlyEncoded) { const string app_id(length, 'a'); vector<uint8_t> key_blob; vector<KeyCharacteristics> key_characteristics; - ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() - .Authorization(TAG_NO_AUTH_REQUIRED) - .EcdsaSigningKey(EcCurve::P_256) - .Digest(Digest::NONE) - .AttestationChallenge(challenge) - .AttestationApplicationId(app_id) - .SetDefaultValidity(), - &key_blob, &key_characteristics)); + auto builder = AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(EcCurve::P_256) + .Digest(Digest::NONE) + .AttestationChallenge(challenge) + .AttestationApplicationId(app_id) + .SetDefaultValidity(); + + auto result = GenerateKey(builder, &key_blob, &key_characteristics); + // Strongbox may not support factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { + if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) { + result = GenerateKeyWithSelfSignedAttestKey( + AuthorizationSetBuilder() + .EcdsaKey(EcCurve::P_256) + .AttestKey() + .SetDefaultValidity(), /* attest key params */ + builder, &key_blob, &key_characteristics); + } + } + ASSERT_EQ(ErrorCode::OK, result); ASSERT_GT(key_blob.size(), 0U); CheckBaseParams(key_characteristics); CheckCharacteristics(key_blob, key_characteristics); @@ -1840,7 +2358,7 @@ TEST_P(NewKeyGenerationTest, AttestationApplicationIDLengthProperlyEncoded) { AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics); AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics); - EXPECT_TRUE(verify_attestation_record(challenge, app_id, // + EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, // sw_enforced, hw_enforced, SecLevel(), cert_chain_[0].encodedCertificate)); @@ -1902,20 +2420,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, @@ -1934,7 +2454,9 @@ TEST_P(NewKeyGenerationTest, EcdsaInvalidSize) { * INVALID_ARGUMENT. */ TEST_P(NewKeyGenerationTest, EcdsaMismatchKeySize) { - if (SecLevel() == SecurityLevel::STRONGBOX) return; + if (SecLevel() == SecurityLevel::STRONGBOX) { + GTEST_SKIP() << "Test not applicable to StrongBox device"; + } auto result = GenerateKey(AuthorizationSetBuilder() .Authorization(TAG_ALGORITHM, Algorithm::EC) @@ -2161,7 +2683,9 @@ TEST_P(NewKeyGenerationTest, HmacCheckMinMacLengths) { * Verifies that keymint rejects HMAC key generation with multiple specified digest algorithms. */ TEST_P(NewKeyGenerationTest, HmacMultipleDigests) { - if (SecLevel() == SecurityLevel::STRONGBOX) return; + if (SecLevel() == SecurityLevel::STRONGBOX) { + GTEST_SKIP() << "Test not applicable to StrongBox device"; + } ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST, GenerateKey(AuthorizationSetBuilder() @@ -2385,7 +2909,9 @@ TEST_P(SigningOperationsTest, RsaPaddingNoneDoesNotAllowOther) { * presented. */ TEST_P(SigningOperationsTest, NoUserConfirmation) { - if (SecLevel() == SecurityLevel::STRONGBOX) return; + if (SecLevel() == SecurityLevel::STRONGBOX) { + GTEST_SKIP() << "Test not applicable to StrongBox device"; + } ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() .RsaSigningKey(1024, 65537) .Digest(Digest::NONE) @@ -2475,7 +3001,9 @@ TEST_P(SigningOperationsTest, RsaPkcs1NoDigestTooLong) { * for a 1024-bit key. */ TEST_P(SigningOperationsTest, RsaPssSha512TooSmallKey) { - if (SecLevel() == SecurityLevel::STRONGBOX) return; + if (SecLevel() == SecurityLevel::STRONGBOX) { + GTEST_SKIP() << "Test not applicable to StrongBox device"; + } ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() .RsaSigningKey(1024, 65537) .Digest(Digest::SHA_2_512) @@ -2718,15 +3246,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) @@ -2751,25 +3283,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 @@ -3064,6 +3712,58 @@ TEST_P(VerificationOperationsTest, HmacSigningKeyCannotVerify) { CheckedDeleteKey(&verification_key); } +/* + * VerificationOperationsTest.HmacVerificationFailsForCorruptSignature + * + * Verifies HMAC signature verification should fails if message or signature is corrupted. + */ +TEST_P(VerificationOperationsTest, HmacVerificationFailsForCorruptSignature) { + string key_material = "HelloThisIsAKey"; + + vector<uint8_t> signing_key, verification_key; + vector<KeyCharacteristics> signing_key_chars, verification_key_chars; + EXPECT_EQ(ErrorCode::OK, + ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .Authorization(TAG_ALGORITHM, Algorithm::HMAC) + .Authorization(TAG_PURPOSE, KeyPurpose::SIGN) + .Digest(Digest::SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 160), + KeyFormat::RAW, key_material, &signing_key, &signing_key_chars)); + EXPECT_EQ(ErrorCode::OK, + ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .Authorization(TAG_ALGORITHM, Algorithm::HMAC) + .Authorization(TAG_PURPOSE, KeyPurpose::VERIFY) + .Digest(Digest::SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 160), + KeyFormat::RAW, key_material, &verification_key, &verification_key_chars)); + + string message = "This is a message."; + string signature = SignMessage( + signing_key, message, + AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Authorization(TAG_MAC_LENGTH, 160)); + + AuthorizationSet begin_out_params; + ASSERT_EQ(ErrorCode::OK, + Begin(KeyPurpose::VERIFY, verification_key, + AuthorizationSetBuilder().Digest(Digest::SHA_2_256), &begin_out_params)); + + string corruptMessage = "This is b message."; // Corrupted message + string output; + EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(corruptMessage, signature, &output)); + + ASSERT_EQ(ErrorCode::OK, + Begin(KeyPurpose::VERIFY, verification_key, + AuthorizationSetBuilder().Digest(Digest::SHA_2_256), &begin_out_params)); + + signature[0] += 1; // Corrupt a signature + EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(message, signature, &output)); + + CheckedDeleteKey(&signing_key); + CheckedDeleteKey(&verification_key); +} + INSTANTIATE_KEYMINT_AIDL_TEST(VerificationOperationsTest); typedef KeyMintAidlTestBase ExportKeyTest; @@ -3213,6 +3913,33 @@ TEST_P(ImportKeyTest, RsaPublicExponentMismatch) { } /* + * ImportKeyTest.RsaAttestMultiPurposeFail + * + * Verifies that importing an RSA key pair with purpose ATTEST_KEY+SIGN fails. + */ +TEST_P(ImportKeyTest, RsaAttestMultiPurposeFail) { + if (AidlVersion() < 2) { + // The KeyMint v1 spec required that KeyPurpose::ATTEST_KEY not be combined + // with other key purposes. However, this was not checked at the time + // so we can only be strict about checking this for implementations of KeyMint + // version 2 and above. + GTEST_SKIP() << "Single-purpose for KeyPurpose::ATTEST_KEY only strict since KeyMint v2"; + } + uint32_t key_size = 2048; + string key = rsa_2048_key; + + ASSERT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE, + ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaSigningKey(key_size, 65537) + .AttestKey() + .Digest(Digest::SHA_2_256) + .Padding(PaddingMode::RSA_PSS) + .SetDefaultValidity(), + KeyFormat::PKCS8, key)); +} + +/* * ImportKeyTest.EcdsaSuccess * * Verifies that importing and using an ECDSA P-256 key pair works correctly. @@ -3294,7 +4021,9 @@ TEST_P(ImportKeyTest, EcdsaP256SEC1Success) { * Verifies that importing and using an ECDSA P-521 key pair works correctly. */ TEST_P(ImportKeyTest, Ecdsa521Success) { - if (SecLevel() == SecurityLevel::STRONGBOX) return; + if (SecLevel() == SecurityLevel::STRONGBOX) { + GTEST_SKIP() << "Test not applicable to StrongBox device"; + } ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) .EcdsaSigningKey(EcCurve::P_521) @@ -3329,6 +4058,278 @@ TEST_P(ImportKeyTest, EcdsaCurveMismatch) { } /* + * ImportKeyTest.EcdsaAttestMultiPurposeFail + * + * Verifies that importing and using an ECDSA P-256 key pair with purpose ATTEST_KEY+SIGN fails. + */ +TEST_P(ImportKeyTest, EcdsaAttestMultiPurposeFail) { + if (AidlVersion() < 2) { + // The KeyMint v1 spec required that KeyPurpose::ATTEST_KEY not be combined + // with other key purposes. However, this was not checked at the time + // so we can only be strict about checking this for implementations of KeyMint + // version 2 and above. + GTEST_SKIP() << "Single-purpose for KeyPurpose::ATTEST_KEY only strict since KeyMint v2"; + } + ASSERT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE, + ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(EcCurve::P_256) + .AttestKey() + .Digest(Digest::SHA_2_256) + .SetDefaultValidity(), + KeyFormat::PKCS8, ec_256_key)); +} + +/* + * 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. @@ -3366,10 +4367,10 @@ TEST_P(ImportKeyTest, AesFailure) { for (uint32_t key_size : {bitlen - 1, bitlen + 1, bitlen - 8, bitlen + 8}) { // Explicit key size doesn't match that of the provided key. auto result = ImportKey(AuthorizationSetBuilder() - .Authorization(TAG_NO_AUTH_REQUIRED) - .AesEncryptionKey(key_size) - .EcbMode() - .Padding(PaddingMode::PKCS7), + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(key_size) + .EcbMode() + .Padding(PaddingMode::PKCS7), KeyFormat::RAW, key); ASSERT_TRUE(result == ErrorCode::IMPORT_PARAMETER_MISMATCH || result == ErrorCode::UNSUPPORTED_KEY_SIZE) @@ -3433,10 +4434,10 @@ TEST_P(ImportKeyTest, TripleDesFailure) { for (uint32_t key_size : {bitlen - 1, bitlen + 1, bitlen - 8, bitlen + 8}) { // Explicit key size doesn't match that of the provided key. auto result = ImportKey(AuthorizationSetBuilder() - .Authorization(TAG_NO_AUTH_REQUIRED) - .TripleDesEncryptionKey(key_size) - .EcbMode() - .Padding(PaddingMode::PKCS7), + .Authorization(TAG_NO_AUTH_REQUIRED) + .TripleDesEncryptionKey(key_size) + .EcbMode() + .Padding(PaddingMode::PKCS7), KeyFormat::RAW, key); ASSERT_TRUE(result == ErrorCode::IMPORT_PARAMETER_MISMATCH || result == ErrorCode::UNSUPPORTED_KEY_SIZE) @@ -3856,7 +4857,7 @@ typedef KeyMintAidlTestBase EncryptionOperationsTest; * Verifies that raw RSA decryption works. */ TEST_P(EncryptionOperationsTest, RsaNoPaddingSuccess) { - for (uint64_t exponent : {3, 65537}) { + for (uint64_t exponent : ValidExponents()) { ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) .RsaEncryptionKey(2048, exponent) @@ -4003,7 +5004,9 @@ TEST_P(EncryptionOperationsTest, RsaOaepInvalidPadding) { * with a different digest than was used to encrypt. */ TEST_P(EncryptionOperationsTest, RsaOaepDecryptWithWrongDigest) { - if (SecLevel() == SecurityLevel::STRONGBOX) return; + if (SecLevel() == SecurityLevel::STRONGBOX) { + GTEST_SKIP() << "Test not applicable to StrongBox device"; + } ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) @@ -4426,8 +5429,10 @@ TEST_P(EncryptionOperationsTest, AesEcbPkcs7Padding) { auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); // Try various message lengths; all should work. - for (size_t i = 0; i < 32; ++i) { - string message(i, 'a'); + for (size_t i = 0; i <= 48; i++) { + SCOPED_TRACE(testing::Message() << "i = " << i); + // Edge case: '\t' (0x09) is also a valid PKCS7 padding character. + string message(i, '\t'); string ciphertext = EncryptMessage(message, params); EXPECT_EQ(i + 16 - (i % 16), ciphertext.size()); string plaintext = DecryptMessage(ciphertext, params); @@ -4451,7 +5456,7 @@ TEST_P(EncryptionOperationsTest, AesEcbWrongPadding) { auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); // Try various message lengths; all should fail - for (size_t i = 0; i < 32; ++i) { + for (size_t i = 0; i <= 48; i++) { string message(i, 'a'); EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, params)); } @@ -4475,11 +5480,49 @@ TEST_P(EncryptionOperationsTest, AesEcbPkcs7PaddingCorrupted) { string ciphertext = EncryptMessage(message, params); EXPECT_EQ(16U, ciphertext.size()); EXPECT_NE(ciphertext, message); - ++ciphertext[ciphertext.size() / 2]; + for (size_t i = 0; i < kMaxPaddingCorruptionRetries; ++i) { + ++ciphertext[ciphertext.size() / 2]; + + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params)); + string plaintext; + ErrorCode error = Finish(ciphertext, &plaintext); + if (error == ErrorCode::INVALID_ARGUMENT) { + // This is the expected error, we can exit the test now. + return; + } else { + // Very small chance we got valid decryption, so try again. + ASSERT_EQ(error, ErrorCode::OK) + << "Expected INVALID_ARGUMENT or (rarely) OK, got " << error; + } + } + FAIL() << "Corrupt ciphertext should have failed to decrypt by now."; +} + +/* + * EncryptionOperationsTest.AesEcbPkcs7CiphertextTooShort + * + * Verifies that AES decryption fails in the correct way when the padding is corrupted. + */ +TEST_P(EncryptionOperationsTest, AesEcbPkcs7CiphertextTooShort) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::ECB) + .Padding(PaddingMode::PKCS7))); + + auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); + + string message = "a"; + string ciphertext = EncryptMessage(message, params); + EXPECT_EQ(16U, ciphertext.size()); + EXPECT_NE(ciphertext, message); + + // Shorten the ciphertext. + ciphertext.resize(ciphertext.size() - 1); EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params)); string plaintext; - EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &plaintext)); + EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(ciphertext, &plaintext)); } vector<uint8_t> CopyIv(const AuthorizationSet& set) { @@ -4536,89 +5579,39 @@ TEST_P(EncryptionOperationsTest, AesCtrRoundTripSuccess) { } /* - * EncryptionOperationsTest.AesIncremental + * EncryptionOperationsTest.AesEcbIncremental * - * Verifies that AES works, all modes, when provided data in various size increments. + * Verifies that AES works for ECB block mode, when provided data in various size increments. */ -TEST_P(EncryptionOperationsTest, AesIncremental) { - auto block_modes = { - BlockMode::ECB, - BlockMode::CBC, - BlockMode::CTR, - BlockMode::GCM, - }; - - ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() - .Authorization(TAG_NO_AUTH_REQUIRED) - .AesEncryptionKey(128) - .BlockMode(block_modes) - .Padding(PaddingMode::NONE) - .Authorization(TAG_MIN_MAC_LENGTH, 128))); - - for (int increment = 1; increment <= 240; ++increment) { - for (auto block_mode : block_modes) { - string message(240, 'a'); - auto params = - AuthorizationSetBuilder().BlockMode(block_mode).Padding(PaddingMode::NONE); - if (block_mode == BlockMode::GCM) { - params.Authorization(TAG_MAC_LENGTH, 128) /* for GCM */; - } - - AuthorizationSet output_params; - EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &output_params)); - - string ciphertext; - string to_send; - for (size_t i = 0; i < message.size(); i += increment) { - EXPECT_EQ(ErrorCode::OK, Update(message.substr(i, increment), &ciphertext)); - } - EXPECT_EQ(ErrorCode::OK, Finish(to_send, &ciphertext)) - << "Error sending " << to_send << " with block mode " << block_mode; - - switch (block_mode) { - case BlockMode::GCM: - EXPECT_EQ(message.size() + 16, ciphertext.size()); - break; - case BlockMode::CTR: - EXPECT_EQ(message.size(), ciphertext.size()); - break; - case BlockMode::CBC: - case BlockMode::ECB: - EXPECT_EQ(message.size() + message.size() % 16, ciphertext.size()); - break; - } +TEST_P(EncryptionOperationsTest, AesEcbIncremental) { + CheckAesIncrementalEncryptOperation(BlockMode::ECB, 240); +} - auto iv = output_params.GetTagValue(TAG_NONCE); - switch (block_mode) { - case BlockMode::CBC: - case BlockMode::GCM: - case BlockMode::CTR: - ASSERT_TRUE(iv) << "No IV for block mode " << block_mode; - EXPECT_EQ(block_mode == BlockMode::GCM ? 12U : 16U, iv->get().size()); - params.push_back(TAG_NONCE, iv->get()); - break; - - case BlockMode::ECB: - EXPECT_FALSE(iv) << "ECB mode should not generate IV"; - break; - } +/* + * EncryptionOperationsTest.AesCbcIncremental + * + * Verifies that AES works for CBC block mode, when provided data in various size increments. + */ +TEST_P(EncryptionOperationsTest, AesCbcIncremental) { + CheckAesIncrementalEncryptOperation(BlockMode::CBC, 240); +} - EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params)) - << "Decrypt begin() failed for block mode " << block_mode; +/* + * EncryptionOperationsTest.AesCtrIncremental + * + * Verifies that AES works for CTR block mode, when provided data in various size increments. + */ +TEST_P(EncryptionOperationsTest, AesCtrIncremental) { + CheckAesIncrementalEncryptOperation(BlockMode::CTR, 240); +} - string plaintext; - for (size_t i = 0; i < ciphertext.size(); i += increment) { - EXPECT_EQ(ErrorCode::OK, Update(ciphertext.substr(i, increment), &plaintext)); - } - ErrorCode error = Finish(to_send, &plaintext); - ASSERT_EQ(ErrorCode::OK, error) << "Decryption failed for block mode " << block_mode - << " and increment " << increment; - if (error == ErrorCode::OK) { - ASSERT_EQ(message, plaintext) << "Decryption didn't match for block mode " - << block_mode << " and increment " << increment; - } - } - } +/* + * EncryptionOperationsTest.AesGcmIncremental + * + * Verifies that AES works for GCM block mode, when provided data in various size increments. + */ +TEST_P(EncryptionOperationsTest, AesGcmIncremental) { + CheckAesIncrementalEncryptOperation(BlockMode::GCM, 240); } struct AesCtrSp80038aTestVector { @@ -4761,6 +5754,49 @@ TEST_P(EncryptionOperationsTest, AesCbcRoundTripSuccess) { } /* + * EncryptionOperationsTest.AesCbcZeroInputSuccessb + * + * Verifies that keymaster generates correct output on zero-input with + * NonePadding mode + */ +TEST_P(EncryptionOperationsTest, AesCbcZeroInputSuccess) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .BlockMode(BlockMode::CBC) + .Padding(PaddingMode::NONE, PaddingMode::PKCS7))); + + // Zero input message + string message = ""; + for (auto padding : {PaddingMode::NONE, PaddingMode::PKCS7}) { + auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(padding); + AuthorizationSet out_params; + string ciphertext1 = EncryptMessage(message, params, &out_params); + vector<uint8_t> iv1 = CopyIv(out_params); + if (padding == PaddingMode::NONE) + EXPECT_EQ(message.size(), ciphertext1.size()) << "PaddingMode: " << padding; + else + EXPECT_EQ(message.size(), ciphertext1.size() - 16) << "PaddingMode: " << padding; + + out_params.Clear(); + + string ciphertext2 = EncryptMessage(message, params, &out_params); + vector<uint8_t> iv2 = CopyIv(out_params); + if (padding == PaddingMode::NONE) + EXPECT_EQ(message.size(), ciphertext2.size()) << "PaddingMode: " << padding; + else + EXPECT_EQ(message.size(), ciphertext2.size() - 16) << "PaddingMode: " << padding; + + // IVs should be random + EXPECT_NE(iv1, iv2) << "PaddingMode: " << padding; + + params.push_back(TAG_NONCE, iv1); + string plaintext = DecryptMessage(ciphertext1, params); + EXPECT_EQ(message, plaintext) << "PaddingMode: " << padding; + } +} + +/* * EncryptionOperationsTest.AesCallerNonce * * Verifies that AES caller-provided nonces work correctly. @@ -5442,15 +6478,27 @@ TEST_P(EncryptionOperationsTest, TripleDesEcbPkcs7PaddingCorrupted) { string ciphertext = EncryptMessage(message, BlockMode::ECB, PaddingMode::PKCS7); EXPECT_EQ(8U, ciphertext.size()); EXPECT_NE(ciphertext, message); - ++ciphertext[ciphertext.size() / 2]; AuthorizationSetBuilder begin_params; begin_params.push_back(TAG_BLOCK_MODE, BlockMode::ECB); begin_params.push_back(TAG_PADDING, PaddingMode::PKCS7); - EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)); - string plaintext; - EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext)); - EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, Finish(&plaintext)); + + for (size_t i = 0; i < kMaxPaddingCorruptionRetries; ++i) { + ++ciphertext[ciphertext.size() / 2]; + + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)); + string plaintext; + EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext)); + ErrorCode error = Finish(&plaintext); + if (error == ErrorCode::INVALID_ARGUMENT) { + // This is the expected error, we can exit the test now. + return; + } else { + // Very small chance we got valid decryption, so try again. + ASSERT_EQ(error, ErrorCode::OK); + } + } + FAIL() << "Corrupt ciphertext should have failed to decrypt by now."; } struct TripleDesTestVector { @@ -5570,8 +6618,8 @@ TEST_P(EncryptionOperationsTest, TripleDesCbcRoundTripSuccess) { ASSERT_GT(key_blob_.size(), 0U); - // Two-block message. - string message = "1234567890123456"; + // Four-block message. + string message = "12345678901234561234567890123456"; vector<uint8_t> iv1; string ciphertext1 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, &iv1); EXPECT_EQ(message.size(), ciphertext1.size()); @@ -5731,8 +6779,10 @@ TEST_P(EncryptionOperationsTest, TripleDesCbcPkcs7Padding) { .Padding(PaddingMode::PKCS7))); // Try various message lengths; all should work. - for (size_t i = 0; i < 32; ++i) { - string message(i, 'a'); + for (size_t i = 0; i <= 32; i++) { + SCOPED_TRACE(testing::Message() << "i = " << i); + // Edge case: '\t' (0x09) is also a valid PKCS7 padding character, albeit not for 3DES. + string message(i, '\t'); vector<uint8_t> iv; string ciphertext = EncryptMessage(message, BlockMode::CBC, PaddingMode::PKCS7, &iv); EXPECT_EQ(i + 8 - (i % 8), ciphertext.size()); @@ -5754,7 +6804,7 @@ TEST_P(EncryptionOperationsTest, TripleDesCbcNoPaddingKeyWithPkcs7Padding) { .Padding(PaddingMode::NONE))); // Try various message lengths; all should fail. - for (size_t i = 0; i < 32; ++i) { + for (size_t i = 0; i <= 32; i++) { auto begin_params = AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::PKCS7); EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, begin_params)); @@ -5778,16 +6828,28 @@ TEST_P(EncryptionOperationsTest, TripleDesCbcPkcs7PaddingCorrupted) { string ciphertext = EncryptMessage(message, BlockMode::CBC, PaddingMode::PKCS7, &iv); EXPECT_EQ(8U, ciphertext.size()); EXPECT_NE(ciphertext, message); - ++ciphertext[ciphertext.size() / 2]; auto begin_params = AuthorizationSetBuilder() .BlockMode(BlockMode::CBC) .Padding(PaddingMode::PKCS7) .Authorization(TAG_NONCE, iv); - EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)); - string plaintext; - EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext)); - EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, Finish(&plaintext)); + + for (size_t i = 0; i < kMaxPaddingCorruptionRetries; ++i) { + SCOPED_TRACE(testing::Message() << "i = " << i); + ++ciphertext[ciphertext.size() / 2]; + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)); + string plaintext; + EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext)); + ErrorCode error = Finish(&plaintext); + if (error == ErrorCode::INVALID_ARGUMENT) { + // This is the expected error, we can exit the test now. + return; + } else { + // Very small chance we got valid decryption, so try again. + ASSERT_EQ(error, ErrorCode::OK); + } + } + FAIL() << "Corrupt ciphertext should have failed to decrypt by now."; } /* @@ -5840,7 +6902,9 @@ typedef KeyMintAidlTestBase MaxOperationsTest; * Verifies that the max uses per boot tag works correctly with AES keys. */ TEST_P(MaxOperationsTest, TestLimitAes) { - if (SecLevel() == SecurityLevel::STRONGBOX) return; + if (SecLevel() == SecurityLevel::STRONGBOX) { + GTEST_SKIP() << "Test not applicable to StrongBox device"; + } ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) @@ -5867,7 +6931,9 @@ TEST_P(MaxOperationsTest, TestLimitAes) { * Verifies that the max uses per boot tag works correctly with RSA keys. */ TEST_P(MaxOperationsTest, TestLimitRsa) { - if (SecLevel() == SecurityLevel::STRONGBOX) return; + if (SecLevel() == SecurityLevel::STRONGBOX) { + GTEST_SKIP() << "Test not applicable to StrongBox device"; + } ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) @@ -5898,7 +6964,9 @@ typedef KeyMintAidlTestBase UsageCountLimitTest; * Verifies that the usage count limit tag = 1 works correctly with AES keys. */ TEST_P(UsageCountLimitTest, TestSingleUseAes) { - if (SecLevel() == SecurityLevel::STRONGBOX) return; + if (SecLevel() == SecurityLevel::STRONGBOX) { + GTEST_SKIP() << "Test not applicable to StrongBox device"; + } ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) @@ -5942,7 +7010,9 @@ TEST_P(UsageCountLimitTest, TestSingleUseAes) { * Verifies that the usage count limit tag > 1 works correctly with AES keys. */ TEST_P(UsageCountLimitTest, TestLimitedUseAes) { - if (SecLevel() == SecurityLevel::STRONGBOX) return; + if (SecLevel() == SecurityLevel::STRONGBOX) { + GTEST_SKIP() << "Test not applicable to StrongBox device"; + } ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) @@ -5987,7 +7057,9 @@ TEST_P(UsageCountLimitTest, TestLimitedUseAes) { * Verifies that the usage count limit tag = 1 works correctly with RSA keys. */ TEST_P(UsageCountLimitTest, TestSingleUseRsa) { - if (SecLevel() == SecurityLevel::STRONGBOX) return; + if (SecLevel() == SecurityLevel::STRONGBOX) { + GTEST_SKIP() << "Test not applicable to StrongBox device"; + } ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) @@ -6031,7 +7103,9 @@ TEST_P(UsageCountLimitTest, TestSingleUseRsa) { * Verifies that the usage count limit tag > 1 works correctly with RSA keys. */ TEST_P(UsageCountLimitTest, TestLimitUseRsa) { - if (SecLevel() == SecurityLevel::STRONGBOX) return; + if (SecLevel() == SecurityLevel::STRONGBOX) { + GTEST_SKIP() << "Test not applicable to StrongBox device"; + } ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) @@ -6078,7 +7152,9 @@ TEST_P(UsageCountLimitTest, TestLimitUseRsa) { * in hardware. */ TEST_P(UsageCountLimitTest, TestSingleUseKeyAndRollbackResistance) { - if (SecLevel() == SecurityLevel::STRONGBOX) return; + if (SecLevel() == SecurityLevel::STRONGBOX) { + GTEST_SKIP() << "Test not applicable to StrongBox device"; + } auto error = GenerateKey(AuthorizationSetBuilder() .RsaSigningKey(2048, 65537) @@ -6087,38 +7163,39 @@ TEST_P(UsageCountLimitTest, TestSingleUseKeyAndRollbackResistance) { .Authorization(TAG_NO_AUTH_REQUIRED) .Authorization(TAG_ROLLBACK_RESISTANCE) .SetDefaultValidity()); - ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK); + if (error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE) { + GTEST_SKIP() << "Rollback resistance not supported"; + } - if (error == ErrorCode::OK) { - // Rollback resistance is supported by KeyMint, verify it is enforced in hardware. - AuthorizationSet hardwareEnforced(SecLevelAuthorizations()); - ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE)); - ASSERT_EQ(ErrorCode::OK, DeleteKey()); + // Rollback resistance is supported by KeyMint, verify it is enforced in hardware. + ASSERT_EQ(ErrorCode::OK, error); + AuthorizationSet hardwareEnforced(SecLevelAuthorizations()); + ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE)); + ASSERT_EQ(ErrorCode::OK, DeleteKey()); - // The KeyMint should also enforce single use key in hardware when it supports rollback - // resistance. - ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() - .Authorization(TAG_NO_AUTH_REQUIRED) - .RsaSigningKey(1024, 65537) - .NoDigestOrPadding() - .Authorization(TAG_USAGE_COUNT_LIMIT, 1) - .SetDefaultValidity())); + // The KeyMint should also enforce single use key in hardware when it supports rollback + // resistance. + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaSigningKey(1024, 65537) + .NoDigestOrPadding() + .Authorization(TAG_USAGE_COUNT_LIMIT, 1) + .SetDefaultValidity())); - // Check the usage count limit tag appears in the hardware authorizations. - AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_); - EXPECT_TRUE(hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U)) - << "key usage count limit " << 1U << " missing"; + // Check the usage count limit tag appears in the hardware authorizations. + AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_); + EXPECT_TRUE(hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U)) + << "key usage count limit " << 1U << " missing"; - string message = "1234567890123456"; - auto params = AuthorizationSetBuilder().NoDigestOrPadding(); + string message = "1234567890123456"; + auto params = AuthorizationSetBuilder().NoDigestOrPadding(); - // First usage of RSA key should work. - SignMessage(message, params); + // First usage of RSA key should work. + SignMessage(message, params); - // Usage count limit tag is enforced by hardware. After using the key, the key blob - // must be invalidated from secure storage (such as RPMB partition). - EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::SIGN, params)); - } + // Usage count limit tag is enforced by hardware. After using the key, the key blob + // must be invalidated from secure storage (such as RPMB partition). + EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::SIGN, params)); } INSTANTIATE_KEYMINT_AIDL_TEST(UsageCountLimitTest); @@ -6195,24 +7272,25 @@ TEST_P(KeyDeletionTest, DeleteKey) { .Authorization(TAG_NO_AUTH_REQUIRED) .Authorization(TAG_ROLLBACK_RESISTANCE) .SetDefaultValidity()); - ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK); + if (error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE) { + GTEST_SKIP() << "Rollback resistance not supported"; + } // Delete must work if rollback protection is implemented - if (error == ErrorCode::OK) { - AuthorizationSet hardwareEnforced(SecLevelAuthorizations()); - ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE)); + ASSERT_EQ(ErrorCode::OK, error); + AuthorizationSet hardwareEnforced(SecLevelAuthorizations()); + ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE)); - ASSERT_EQ(ErrorCode::OK, DeleteKey(true /* keep key blob */)); + ASSERT_EQ(ErrorCode::OK, DeleteKey(true /* keep key blob */)); - string message = "12345678901234567890123456789012"; - AuthorizationSet begin_out_params; - EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, - Begin(KeyPurpose::SIGN, key_blob_, - AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE), - &begin_out_params)); - AbortIfNeeded(); - key_blob_ = AidlBuf(); - } + string message = "12345678901234567890123456789012"; + AuthorizationSet begin_out_params; + EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, + Begin(KeyPurpose::SIGN, key_blob_, + AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE), + &begin_out_params)); + AbortIfNeeded(); + key_blob_ = AidlBuf(); } /** @@ -6229,21 +7307,22 @@ TEST_P(KeyDeletionTest, DeleteInvalidKey) { .Authorization(TAG_NO_AUTH_REQUIRED) .Authorization(TAG_ROLLBACK_RESISTANCE) .SetDefaultValidity()); - ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK); + if (error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE) { + GTEST_SKIP() << "Rollback resistance not supported"; + } // Delete must work if rollback protection is implemented - if (error == ErrorCode::OK) { - AuthorizationSet enforced(SecLevelAuthorizations()); - ASSERT_TRUE(enforced.Contains(TAG_ROLLBACK_RESISTANCE)); + ASSERT_EQ(ErrorCode::OK, error); + AuthorizationSet enforced(SecLevelAuthorizations()); + ASSERT_TRUE(enforced.Contains(TAG_ROLLBACK_RESISTANCE)); - // Delete the key we don't care about the result at this point. - DeleteKey(); + // Delete the key we don't care about the result at this point. + DeleteKey(); - // Now create an invalid key blob and delete it. - key_blob_ = AidlBuf("just some garbage data which is not a valid key blob"); + // Now create an invalid key blob and delete it. + key_blob_ = AidlBuf("just some garbage data which is not a valid key blob"); - ASSERT_EQ(ErrorCode::OK, DeleteKey()); - } + ASSERT_EQ(ErrorCode::OK, DeleteKey()); } /** @@ -6258,32 +7337,37 @@ TEST_P(KeyDeletionTest, DeleteInvalidKey) { * credentials stored in Keystore/Keymint. */ TEST_P(KeyDeletionTest, DeleteAllKeys) { - if (!arm_deleteAllKeys) return; + if (!arm_deleteAllKeys) { + GTEST_SKIP() << "Option --arm_deleteAllKeys not set"; + return; + } auto error = GenerateKey(AuthorizationSetBuilder() .RsaSigningKey(2048, 65537) .Digest(Digest::NONE) .Padding(PaddingMode::NONE) .Authorization(TAG_NO_AUTH_REQUIRED) - .Authorization(TAG_ROLLBACK_RESISTANCE)); - ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK); + .Authorization(TAG_ROLLBACK_RESISTANCE) + .SetDefaultValidity()); + if (error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE) { + GTEST_SKIP() << "Rollback resistance not supported"; + } // Delete must work if rollback protection is implemented - if (error == ErrorCode::OK) { - AuthorizationSet hardwareEnforced(SecLevelAuthorizations()); - ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE)); + ASSERT_EQ(ErrorCode::OK, error); + AuthorizationSet hardwareEnforced(SecLevelAuthorizations()); + ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE)); - ASSERT_EQ(ErrorCode::OK, DeleteAllKeys()); + ASSERT_EQ(ErrorCode::OK, DeleteAllKeys()); - string message = "12345678901234567890123456789012"; - AuthorizationSet begin_out_params; + string message = "12345678901234567890123456789012"; + AuthorizationSet begin_out_params; - EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, - Begin(KeyPurpose::SIGN, key_blob_, - AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE), - &begin_out_params)); - AbortIfNeeded(); - key_blob_ = AidlBuf(); - } + EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, + Begin(KeyPurpose::SIGN, key_blob_, + AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE), + &begin_out_params)); + AbortIfNeeded(); + key_blob_ = AidlBuf(); } INSTANTIATE_KEYMINT_AIDL_TEST(KeyDeletionTest); @@ -6356,7 +7440,7 @@ TEST_P(ClearOperationsTest, TooManyOperations) { size_t i; for (i = 0; i < max_operations; i++) { - result = Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, op_handles[i]); + result = Begin(KeyPurpose::DECRYPT, key_blob_, params, &out_params, op_handles[i]); if (ErrorCode::OK != result) { break; } @@ -6364,12 +7448,12 @@ TEST_P(ClearOperationsTest, TooManyOperations) { EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, result); // Try again just in case there's a weird overflow bug EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, - Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params)); + Begin(KeyPurpose::DECRYPT, key_blob_, params, &out_params)); for (size_t j = 0; j < i; j++) { EXPECT_EQ(ErrorCode::OK, Abort(op_handles[j])) << "Aboort failed for i = " << j << std::endl; } - EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params)); + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, key_blob_, params, &out_params)); AbortIfNeeded(); } @@ -6417,9 +7501,7 @@ TEST_P(TransportLimitTest, LargeFinishInput) { INSTANTIATE_KEYMINT_AIDL_TEST(TransportLimitTest); -typedef KeyMintAidlTestBase KeyAgreementTest; - -int CurveToOpenSslCurveName(EcCurve curve) { +static int EcdhCurveToOpenSslCurveName(EcCurve curve) { switch (curve) { case EcCurve::P_224: return NID_secp224r1; @@ -6429,13 +7511,122 @@ int CurveToOpenSslCurveName(EcCurve curve) { return NID_secp384r1; case EcCurve::P_521: return NID_secp521r1; + case EcCurve::CURVE_25519: + return NID_X25519; } } +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); + *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}; + auto builder = 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(); + ErrorCode result = GenerateKey(builder); + + if (SecLevel() == SecurityLevel::STRONGBOX) { + if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) { + result = GenerateKeyWithSelfSignedAttestKey( + AuthorizationSetBuilder() + .EcdsaKey(EcCurve::P_256) + .AttestKey() + .SetDefaultValidity(), /* attest key params */ + builder, &key_blob_, &key_characteristics_, &cert_chain_); + } + } + 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 @@ -6448,50 +7639,17 @@ TEST_P(KeyAgreementTest, Ecdh) { // for (auto curve : ValidCurves()) { for (auto localCurve : ValidCurves()) { + SCOPED_TRACE(testing::Message() + << "local-curve-" << localCurve << "-keymint-curve-" << curve); + // 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); + 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) { @@ -6500,30 +7658,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(); @@ -6531,6 +7671,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; @@ -6580,14 +7854,23 @@ TEST_P(EarlyBootKeyTest, CreateAttestedEarlyBootKey) { }); for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) { + // Strongbox may not support factory attestation. Key creation might fail with + // ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED + if (SecLevel() == SecurityLevel::STRONGBOX && keyData.blob.size() == 0U) { + continue; + } ASSERT_GT(keyData.blob.size(), 0U); AuthorizationSet crypto_params = SecLevelAuthorizations(keyData.characteristics); EXPECT_TRUE(crypto_params.Contains(TAG_EARLY_BOOT_ONLY)) << crypto_params; } CheckedDeleteKey(&aesKeyData.blob); CheckedDeleteKey(&hmacKeyData.blob); - CheckedDeleteKey(&rsaKeyData.blob); - CheckedDeleteKey(&ecdsaKeyData.blob); + if (rsaKeyData.blob.size() != 0U) { + CheckedDeleteKey(&rsaKeyData.blob); + } + if (ecdsaKeyData.blob.size() != 0U) { + CheckedDeleteKey(&ecdsaKeyData.blob); + } } /* @@ -6708,6 +7991,18 @@ TEST_P(UnlockedDeviceRequiredTest, DISABLED_KeysBecomeUnusable) { INSTANTIATE_KEYMINT_AIDL_TEST(UnlockedDeviceRequiredTest); +using VsrRequirementTest = KeyMintAidlTestBase; + +TEST_P(VsrRequirementTest, Vsr13Test) { + int vsr_api_level = get_vsr_api_level(); + if (vsr_api_level < 33) { + GTEST_SKIP() << "Applies only to VSR API level 33, this device is: " << vsr_api_level; + } + EXPECT_GE(AidlVersion(), 2) << "VSR 13+ requires KeyMint version 2"; +} + +INSTANTIATE_KEYMINT_AIDL_TEST(VsrRequirementTest); + } // namespace aidl::android::hardware::security::keymint::test int main(int argc, char** argv) { @@ -6732,9 +8027,11 @@ int main(int argc, char** argv) { } else { std::cout << "NOT dumping attestations" << std::endl; } - // TODO(drysdale): Remove this flag when available KeyMint devices comply with spec - if (std::string(argv[i]) == "--check_patchLevels") { - aidl::android::hardware::security::keymint::test::check_patchLevels = true; + if (std::string(argv[i]) == "--skip_boot_pl_check") { + // Allow checks of BOOT_PATCHLEVEL to be disabled, so that the tests can + // be run in emulated environments that don't have the normal bootloader + // interactions. + aidl::android::hardware::security::keymint::test::check_boot_pl = false; } } } |