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

Commit bb2b88ae authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I10c4beea,I0efc30f3 into sc-dev

* changes:
  KeyMint VTS: local RSA encryption
  KeyMint VTS: local asymmetric verification
parents cb806689 2b6c351a
Loading
Loading
Loading
Loading
+204 −0
Original line number Original line Diff line number Diff line
@@ -59,6 +59,11 @@ using ::testing::MatchesRegex;
namespace test {
namespace test {


namespace {
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;
typedef KeyMintAidlTestBase::KeyData KeyData;
// Predicate for testing basic characteristics validity in generation or import.
// Predicate for testing basic characteristics validity in generation or import.
bool KeyCharacteristicsBasicallyValid(SecurityLevel secLevel,
bool KeyCharacteristicsBasicallyValid(SecurityLevel secLevel,
@@ -590,6 +595,205 @@ void KeyMintAidlTestBase::VerifyMessage(const string& message, const string& sig
    VerifyMessage(key_blob_, message, signature, params);
    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::LocalRsaEncryptMessage(const string& message,
                                                   const AuthorizationSet& params) {
    SCOPED_TRACE("LocalRsaEncryptMessage");

    // Retrieve the public key from the leaf certificate.
    if (cert_chain_.empty()) {
        ADD_FAILURE() << "No public key available";
        return "Failure";
    }
    X509_Ptr key_cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
    EVP_PKEY_Ptr pub_key(X509_get_pubkey(key_cert.get()));
    RSA_Ptr rsa(EVP_PKEY_get1_RSA(const_cast<EVP_PKEY*>(pub_key.get())));

    // Retrieve relevant tags.
    Digest digest = Digest::NONE;
    Digest mgf_digest = Digest::NONE;
    PaddingMode padding = PaddingMode::NONE;

    auto digest_tag = params.GetTagValue(TAG_DIGEST);
    if (digest_tag.has_value()) digest = digest_tag.value();
    auto pad_tag = params.GetTagValue(TAG_PADDING);
    if (pad_tag.has_value()) padding = pad_tag.value();
    auto mgf_tag = params.GetTagValue(TAG_RSA_OAEP_MGF_DIGEST);
    if (mgf_tag.has_value()) mgf_digest = mgf_tag.value();

    const EVP_MD* md = openssl_digest(digest);
    const EVP_MD* mgf_md = openssl_digest(mgf_digest);

    // Set up encryption context.
    EVP_PKEY_CTX_Ptr ctx(EVP_PKEY_CTX_new(pub_key.get(), /* engine= */ nullptr));
    if (EVP_PKEY_encrypt_init(ctx.get()) <= 0) {
        ADD_FAILURE() << "Encryption init failed: " << ERR_peek_last_error();
        return "Failure";
    }

    int rc = -1;
    switch (padding) {
        case PaddingMode::NONE:
            rc = EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING);
            break;
        case PaddingMode::RSA_PKCS1_1_5_ENCRYPT:
            rc = EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_PADDING);
            break;
        case PaddingMode::RSA_OAEP:
            rc = EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING);
            break;
        default:
            break;
    }
    if (rc <= 0) {
        ADD_FAILURE() << "Set padding failed: " << ERR_peek_last_error();
        return "Failure";
    }
    if (padding == PaddingMode::RSA_OAEP) {
        if (!EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), md)) {
            ADD_FAILURE() << "Set digest failed: " << ERR_peek_last_error();
            return "Failure";
        }
        if (!EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), mgf_md)) {
            ADD_FAILURE() << "Set MGF digest failed: " << ERR_peek_last_error();
            return "Failure";
        }
    }

    // Determine output size.
    size_t outlen;
    if (EVP_PKEY_encrypt(ctx.get(), nullptr /* out */, &outlen,
                         reinterpret_cast<const uint8_t*>(message.data()), message.size()) <= 0) {
        ADD_FAILURE() << "Determine output size failed: " << ERR_peek_last_error();
        return "Failure";
    }

    // Left-zero-pad the input if necessary.
    const uint8_t* to_encrypt = reinterpret_cast<const uint8_t*>(message.data());
    size_t to_encrypt_len = message.size();

    std::unique_ptr<string> zero_padded_message;
    if (padding == PaddingMode::NONE && to_encrypt_len < outlen) {
        zero_padded_message.reset(new string(outlen, '\0'));
        memcpy(zero_padded_message->data() + (outlen - to_encrypt_len), message.data(),
               message.size());
        to_encrypt = reinterpret_cast<const uint8_t*>(zero_padded_message->data());
        to_encrypt_len = outlen;
    }

    // Do the encryption.
    string output(outlen, '\0');
    if (EVP_PKEY_encrypt(ctx.get(), reinterpret_cast<uint8_t*>(output.data()), &outlen, to_encrypt,
                         to_encrypt_len) <= 0) {
        ADD_FAILURE() << "Encryption failed: " << ERR_peek_last_error();
        return "Failure";
    }
    return output;
}

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


    string LocalRsaEncryptMessage(const string& message, const AuthorizationSet& params);
    string EncryptMessage(const vector<uint8_t>& key_blob, const string& message,
    string EncryptMessage(const vector<uint8_t>& key_blob, const string& message,
                          const AuthorizationSet& in_params, AuthorizationSet* out_params);
                          const AuthorizationSet& in_params, AuthorizationSet* out_params);
    string EncryptMessage(const string& message, const AuthorizationSet& params,
    string EncryptMessage(const string& message, const AuthorizationSet& params,
+119 −320

File changed.

Preview size limit exceeded, changes collapsed.