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

Commit fe42aa3a authored by David Drysdale's avatar David Drysdale
Browse files

KeyMint VTS: local asymmetric verification

Change verification of ECDSA and RSA signatures so it happens locally
in the test, rather than by invoking a VERIFY operation against KeyMint.

Bug: 188385353
Test: VtsAidlKeyMintTargetTest
Merged-In: I0efc30f3c96cd70ac636d34718eff53cc23f1480
Change-Id: I0efc30f3c96cd70ac636d34718eff53cc23f1480
parent 561d79e0
Loading
Loading
Loading
Loading
+109 −0
Original line number Diff line number Diff line
@@ -59,6 +59,11 @@ using ::testing::MatchesRegex;
namespace test {

namespace {

// Overhead for PKCS#1 v1.5 signature padding of undigested messages.  Digested messages have
// additional overhead, for the digest algorithmIdentifier required by PKCS#1.
const size_t kPkcs1UndigestedSignaturePaddingOverhead = 11;

typedef KeyMintAidlTestBase::KeyData KeyData;
// Predicate for testing basic characteristics validity in generation or import.
bool KeyCharacteristicsBasicallyValid(SecurityLevel secLevel,
@@ -590,6 +595,110 @@ void KeyMintAidlTestBase::VerifyMessage(const string& message, const string& sig
    VerifyMessage(key_blob_, message, signature, params);
}

void KeyMintAidlTestBase::LocalVerifyMessage(const string& message, const string& signature,
                                             const AuthorizationSet& params) {
    SCOPED_TRACE("LocalVerifyMessage");

    // Retrieve the public key from the leaf certificate.
    ASSERT_GT(cert_chain_.size(), 0);
    X509_Ptr key_cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
    ASSERT_TRUE(key_cert.get());
    EVP_PKEY_Ptr pub_key(X509_get_pubkey(key_cert.get()));
    ASSERT_TRUE(pub_key.get());

    Digest digest = params.GetTagValue(TAG_DIGEST).value();
    PaddingMode padding = PaddingMode::NONE;
    auto tag = params.GetTagValue(TAG_PADDING);
    if (tag.has_value()) {
        padding = tag.value();
    }

    if (digest == Digest::NONE) {
        switch (EVP_PKEY_id(pub_key.get())) {
            case EVP_PKEY_EC: {
                vector<uint8_t> data((EVP_PKEY_bits(pub_key.get()) + 7) / 8);
                size_t data_size = std::min(data.size(), message.size());
                memcpy(data.data(), message.data(), data_size);
                EC_KEY_Ptr ecdsa(EVP_PKEY_get1_EC_KEY(pub_key.get()));
                ASSERT_TRUE(ecdsa.get());
                ASSERT_EQ(1,
                          ECDSA_verify(0, reinterpret_cast<const uint8_t*>(data.data()), data_size,
                                       reinterpret_cast<const uint8_t*>(signature.data()),
                                       signature.size(), ecdsa.get()));
                break;
            }
            case EVP_PKEY_RSA: {
                vector<uint8_t> data(EVP_PKEY_size(pub_key.get()));
                size_t data_size = std::min(data.size(), message.size());
                memcpy(data.data(), message.data(), data_size);

                RSA_Ptr rsa(EVP_PKEY_get1_RSA(const_cast<EVP_PKEY*>(pub_key.get())));
                ASSERT_TRUE(rsa.get());

                size_t key_len = RSA_size(rsa.get());
                int openssl_padding = RSA_NO_PADDING;
                switch (padding) {
                    case PaddingMode::NONE:
                        ASSERT_TRUE(data_size <= key_len);
                        ASSERT_EQ(key_len, signature.size());
                        openssl_padding = RSA_NO_PADDING;
                        break;
                    case PaddingMode::RSA_PKCS1_1_5_SIGN:
                        ASSERT_TRUE(data_size + kPkcs1UndigestedSignaturePaddingOverhead <=
                                    key_len);
                        openssl_padding = RSA_PKCS1_PADDING;
                        break;
                    default:
                        ADD_FAILURE() << "Unsupported RSA padding mode " << padding;
                }

                vector<uint8_t> decrypted_data(key_len);
                int bytes_decrypted = RSA_public_decrypt(
                        signature.size(), reinterpret_cast<const uint8_t*>(signature.data()),
                        decrypted_data.data(), rsa.get(), openssl_padding);
                ASSERT_GE(bytes_decrypted, 0);

                const uint8_t* compare_pos = decrypted_data.data();
                size_t bytes_to_compare = bytes_decrypted;
                uint8_t zero_check_result = 0;
                if (padding == PaddingMode::NONE && data_size < bytes_to_compare) {
                    // If the data is short, for "unpadded" signing we zero-pad to the left.  So
                    // during verification we should have zeros on the left of the decrypted data.
                    // Do a constant-time check.
                    const uint8_t* zero_end = compare_pos + bytes_to_compare - data_size;
                    while (compare_pos < zero_end) zero_check_result |= *compare_pos++;
                    ASSERT_EQ(0, zero_check_result);
                    bytes_to_compare = data_size;
                }
                ASSERT_EQ(0, memcmp(compare_pos, data.data(), bytes_to_compare));
                break;
            }
            default:
                ADD_FAILURE() << "Unknown public key type";
        }
    } else {
        EVP_MD_CTX digest_ctx;
        EVP_MD_CTX_init(&digest_ctx);
        EVP_PKEY_CTX* pkey_ctx;
        const EVP_MD* md = openssl_digest(digest);
        ASSERT_NE(md, nullptr);
        ASSERT_EQ(1, EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, md, nullptr, pub_key.get()));

        if (padding == PaddingMode::RSA_PSS) {
            EXPECT_GT(EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING), 0);
            EXPECT_GT(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, EVP_MD_size(md)), 0);
        }

        ASSERT_EQ(1, EVP_DigestVerifyUpdate(&digest_ctx,
                                            reinterpret_cast<const uint8_t*>(message.data()),
                                            message.size()));
        ASSERT_EQ(1, EVP_DigestVerifyFinal(&digest_ctx,
                                           reinterpret_cast<const uint8_t*>(signature.data()),
                                           signature.size()));
        EVP_MD_CTX_cleanup(&digest_ctx);
    }
}

string KeyMintAidlTestBase::EncryptMessage(const vector<uint8_t>& key_blob, const string& message,
                                           const AuthorizationSet& in_params,
                                           AuthorizationSet* out_params) {
+2 −0
Original line number Diff line number Diff line
@@ -162,6 +162,8 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam<string> {
                       const string& signature, const AuthorizationSet& params);
    void VerifyMessage(const string& message, const string& signature,
                       const AuthorizationSet& params);
    void LocalVerifyMessage(const string& message, const string& signature,
                            const AuthorizationSet& params);

    string EncryptMessage(const vector<uint8_t>& key_blob, const string& message,
                          const AuthorizationSet& in_params, AuthorizationSet* out_params);
+83 −209
Original line number Diff line number Diff line
@@ -482,7 +482,6 @@ class NewKeyGenerationTest : public KeyMintAidlTestBase {
    void CheckBaseParams(const vector<KeyCharacteristics>& keyCharacteristics) {
        AuthorizationSet auths = CheckCommonParams(keyCharacteristics);
        EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
        EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::VERIFY));

        // Check that some unexpected tags/values are NOT present.
        EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::ENCRYPT));
@@ -495,7 +494,6 @@ class NewKeyGenerationTest : public KeyMintAidlTestBase {
        EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::DECRYPT));

        EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
        EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::VERIFY));
    }

    AuthorizationSet CheckCommonParams(const vector<KeyCharacteristics>& keyCharacteristics) {
@@ -1986,6 +1984,50 @@ TEST_P(SigningOperationsTest, RsaSuccess) {
    string message = "12345678901234567890123456789012";
    string signature = SignMessage(
            message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
    LocalVerifyMessage(message, signature,
                       AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
}

/*
 * SigningOperationsTest.RsaAllPaddingsAndDigests
 *
 * Verifies RSA signature/verification for all padding modes and digests.
 */
TEST_P(SigningOperationsTest, RsaAllPaddingsAndDigests) {
    auto authorizations = AuthorizationSetBuilder()
                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                  .RsaSigningKey(2048, 65537)
                                  .Digest(ValidDigests(true /* withNone */, true /* withMD5 */))
                                  .Padding(PaddingMode::NONE)
                                  .Padding(PaddingMode::RSA_PSS)
                                  .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
                                  .SetDefaultValidity();

    ASSERT_EQ(ErrorCode::OK, GenerateKey(authorizations));

    string message(128, 'a');
    string corrupt_message(message);
    ++corrupt_message[corrupt_message.size() / 2];

    for (auto padding :
         {PaddingMode::NONE, PaddingMode::RSA_PSS, PaddingMode::RSA_PKCS1_1_5_SIGN}) {
        for (auto digest : ValidDigests(true /* withNone */, true /* withMD5 */)) {
            if (padding == PaddingMode::NONE && digest != Digest::NONE) {
                // Digesting only makes sense with padding.
                continue;
            }

            if (padding == PaddingMode::RSA_PSS && digest == Digest::NONE) {
                // PSS requires digesting.
                continue;
            }

            string signature =
                    SignMessage(message, AuthorizationSetBuilder().Digest(digest).Padding(padding));
            LocalVerifyMessage(message, signature,
                               AuthorizationSetBuilder().Digest(digest).Padding(padding));
        }
    }
}

/*
@@ -2431,6 +2473,39 @@ TEST_P(SigningOperationsTest, EcdsaAllSizesAndHashes) {
    }
}

/*
 * SigningOperationsTest.EcdsaAllDigestsAndCurves
 *
 * Verifies ECDSA signature/verification for all digests and 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);
        ErrorCode error = GenerateKey(AuthorizationSetBuilder()
                                              .Authorization(TAG_NO_AUTH_REQUIRED)
                                              .EcdsaSigningKey(curve)
                                              .Digest(digests)
                                              .SetDefaultValidity());
        EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate key for EC curve " << curve;
        if (error != ErrorCode::OK) {
            continue;
        }

        for (auto digest : digests) {
            SCOPED_TRACE(testing::Message() << "Digest::" << digest);
            string signature = SignMessage(message, AuthorizationSetBuilder().Digest(digest));
            LocalVerifyMessage(message, signature, AuthorizationSetBuilder().Digest(digest));
        }

        auto rc = DeleteKey();
        ASSERT_TRUE(rc == ErrorCode::OK || rc == ErrorCode::UNIMPLEMENTED);
    }
}

/*
 * SigningOperationsTest.EcdsaAllCurves
 *
@@ -2698,207 +2773,6 @@ INSTANTIATE_KEYMINT_AIDL_TEST(SigningOperationsTest);

typedef KeyMintAidlTestBase VerificationOperationsTest;

/*
 * VerificationOperationsTest.RsaSuccess
 *
 * Verifies that a simple RSA signature/verification sequence succeeds.
 */
TEST_P(VerificationOperationsTest, RsaSuccess) {
    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
                                                 .RsaSigningKey(2048, 65537)
                                                 .Digest(Digest::NONE)
                                                 .Padding(PaddingMode::NONE)
                                                 .SetDefaultValidity()));
    string message = "12345678901234567890123456789012";
    string signature = SignMessage(
            message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
    VerifyMessage(message, signature,
                  AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
}

/*
 * VerificationOperationsTest.RsaAllPaddingsAndDigests
 *
 * Verifies RSA signature/verification for all padding modes and digests.
 */
TEST_P(VerificationOperationsTest, RsaAllPaddingsAndDigests) {
    auto authorizations = AuthorizationSetBuilder()
                                  .Authorization(TAG_NO_AUTH_REQUIRED)
                                  .RsaSigningKey(2048, 65537)
                                  .Digest(ValidDigests(true /* withNone */, true /* withMD5 */))
                                  .Padding(PaddingMode::NONE)
                                  .Padding(PaddingMode::RSA_PSS)
                                  .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
                                  .SetDefaultValidity();

    ASSERT_EQ(ErrorCode::OK, GenerateKey(authorizations));

    string message(128, 'a');
    string corrupt_message(message);
    ++corrupt_message[corrupt_message.size() / 2];

    for (auto padding :
         {PaddingMode::NONE, PaddingMode::RSA_PSS, PaddingMode::RSA_PKCS1_1_5_SIGN}) {
        for (auto digest : ValidDigests(true /* withNone */, true /* withMD5 */)) {
            if (padding == PaddingMode::NONE && digest != Digest::NONE) {
                // Digesting only makes sense with padding.
                continue;
            }

            if (padding == PaddingMode::RSA_PSS && digest == Digest::NONE) {
                // PSS requires digesting.
                continue;
            }

            string signature =
                    SignMessage(message, AuthorizationSetBuilder().Digest(digest).Padding(padding));
            VerifyMessage(message, signature,
                          AuthorizationSetBuilder().Digest(digest).Padding(padding));

            /* TODO(seleneh) add exportkey tests back later when we have decided on
             * the new api.
                        if (digest != Digest::NONE) {
                            // Verify with OpenSSL.
                            vector<uint8_t> pubkey;
                            ASSERT_EQ(ErrorCode::OK, ExportKey(KeyFormat::X509, &pubkey));

                            const uint8_t* p = pubkey.data();
                            EVP_PKEY_Ptr pkey(d2i_PUBKEY(nullptr, &p, pubkey.size()));
                            ASSERT_TRUE(pkey.get());

                            EVP_MD_CTX digest_ctx;
                            EVP_MD_CTX_init(&digest_ctx);
                            EVP_PKEY_CTX* pkey_ctx;
                            const EVP_MD* md = openssl_digest(digest);
                            ASSERT_NE(md, nullptr);
                            EXPECT_EQ(1, EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, md,
             nullptr, pkey.get()));

                            switch (padding) {
                                case PaddingMode::RSA_PSS:
                                    EXPECT_GT(EVP_PKEY_CTX_set_rsa_padding(pkey_ctx,
               RSA_PKCS1_PSS_PADDING), 0); EXPECT_GT(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx,
               EVP_MD_size(md)), 0); break; case PaddingMode::RSA_PKCS1_1_5_SIGN:
                                    // PKCS1 is the default; don't need to set anything.
                                    break;
                                default:
                                    FAIL();
                                    break;
                            }

                            EXPECT_EQ(1, EVP_DigestVerifyUpdate(&digest_ctx, message.data(),
               message.size())); EXPECT_EQ(1, EVP_DigestVerifyFinal(&digest_ctx,
                                                            reinterpret_cast<const
               uint8_t*>(signature.data()), signature.size())); EVP_MD_CTX_cleanup(&digest_ctx);
                        }
            */

            // Corrupt signature shouldn't verify.
            string corrupt_signature(signature);
            ++corrupt_signature[corrupt_signature.size() / 2];

            EXPECT_EQ(ErrorCode::OK,
                      Begin(KeyPurpose::VERIFY,
                            AuthorizationSetBuilder().Digest(digest).Padding(padding)));
            string result;
            EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(message, corrupt_signature, &result));

            // Corrupt message shouldn't verify
            EXPECT_EQ(ErrorCode::OK,
                      Begin(KeyPurpose::VERIFY,
                            AuthorizationSetBuilder().Digest(digest).Padding(padding)));
            EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(corrupt_message, signature, &result));
        }
    }
}

/*
 * VerificationOperationsTest.RsaAllDigestsAndCurves
 *
 * Verifies ECDSA signature/verification for all digests and curves.
 */
TEST_P(VerificationOperationsTest, EcdsaAllDigestsAndCurves) {
    auto digests = ValidDigests(true /* withNone */, false /* withMD5 */);

    string message = "1234567890";
    string corrupt_message = "2234567890";
    for (auto curve : ValidCurves()) {
        ErrorCode error = GenerateKey(AuthorizationSetBuilder()
                                              .Authorization(TAG_NO_AUTH_REQUIRED)
                                              .EcdsaSigningKey(curve)
                                              .Digest(digests)
                                              .SetDefaultValidity());
        EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate key for EC curve " << curve;
        if (error != ErrorCode::OK) {
            continue;
        }

        for (auto digest : digests) {
            string signature = SignMessage(message, AuthorizationSetBuilder().Digest(digest));
            VerifyMessage(message, signature, AuthorizationSetBuilder().Digest(digest));

            /* TODO(seleneh) add exportkey tests back later when we have decided on
             * the new api.

                        // Verify with OpenSSL
                        if (digest != Digest::NONE) {
                            vector<uint8_t> pubkey;
                            ASSERT_EQ(ErrorCode::OK, ExportKey(KeyFormat::X509, &pubkey))
                                    << curve << ' ' << digest;

                            const uint8_t* p = pubkey.data();
                            EVP_PKEY_Ptr pkey(d2i_PUBKEY(nullptr, &p, pubkey.size()));
                            ASSERT_TRUE(pkey.get());

                            EVP_MD_CTX digest_ctx;
                            EVP_MD_CTX_init(&digest_ctx);
                            EVP_PKEY_CTX* pkey_ctx;
                            const EVP_MD* md = openssl_digest(digest);

                            EXPECT_EQ(1, EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, md,
             nullptr, pkey.get()))
                                    << curve << ' ' << digest;

                            EXPECT_EQ(1, EVP_DigestVerifyUpdate(&digest_ctx, message.data(),
               message.size()))
                                    << curve << ' ' << digest;

                            EXPECT_EQ(1,
                                      EVP_DigestVerifyFinal(&digest_ctx,
                                                            reinterpret_cast<const
               uint8_t*>(signature.data()), signature.size()))
                                    << curve << ' ' << digest;

                            EVP_MD_CTX_cleanup(&digest_ctx);
                        }
            */
            // Corrupt signature shouldn't verify.
            string corrupt_signature(signature);
            ++corrupt_signature[corrupt_signature.size() / 2];

            EXPECT_EQ(ErrorCode::OK,
                      Begin(KeyPurpose::VERIFY, AuthorizationSetBuilder().Digest(digest)))
                    << curve << ' ' << digest;

            string result;
            EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(message, corrupt_signature, &result))
                    << curve << ' ' << digest;

            // Corrupt message shouldn't verify
            EXPECT_EQ(ErrorCode::OK,
                      Begin(KeyPurpose::VERIFY, AuthorizationSetBuilder().Digest(digest)))
                    << curve << ' ' << digest;

            EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(corrupt_message, signature, &result))
                    << curve << ' ' << digest;
        }

        auto rc = DeleteKey();
        ASSERT_TRUE(rc == ErrorCode::OK || rc == ErrorCode::UNIMPLEMENTED);
    }
}

/*
 * VerificationOperationsTest.HmacSigningKeyCannotVerify
 *
@@ -3016,7 +2890,7 @@ TEST_P(ImportKeyTest, RsaSuccess) {
    string message(1024 / 8, 'a');
    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS);
    string signature = SignMessage(message, params);
    VerifyMessage(message, signature, params);
    LocalVerifyMessage(message, signature, params);
}

/*
@@ -3058,7 +2932,7 @@ TEST_P(ImportKeyTest, RsaSuccessWithoutParams) {
    string message(1024 / 8, 'a');
    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS);
    string signature = SignMessage(message, params);
    VerifyMessage(message, signature, params);
    LocalVerifyMessage(message, signature, params);
}

/*
@@ -3116,7 +2990,7 @@ TEST_P(ImportKeyTest, EcdsaSuccess) {
    string message(32, 'a');
    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
    string signature = SignMessage(message, params);
    VerifyMessage(message, signature, params);
    LocalVerifyMessage(message, signature, params);
}

/*
@@ -3143,7 +3017,7 @@ TEST_P(ImportKeyTest, EcdsaP256RFC5915Success) {
    string message(32, 'a');
    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
    string signature = SignMessage(message, params);
    VerifyMessage(message, signature, params);
    LocalVerifyMessage(message, signature, params);
}

/*
@@ -3169,7 +3043,7 @@ TEST_P(ImportKeyTest, EcdsaP256SEC1Success) {
    string message(32, 'a');
    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
    string signature = SignMessage(message, params);
    VerifyMessage(message, signature, params);
    LocalVerifyMessage(message, signature, params);
}

/*
@@ -3195,7 +3069,7 @@ TEST_P(ImportKeyTest, Ecdsa521Success) {
    string message(32, 'a');
    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
    string signature = SignMessage(message, params);
    VerifyMessage(message, signature, params);
    LocalVerifyMessage(message, signature, params);
}

/*