diff options
Diffstat (limited to 'security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp')
-rw-r--r-- | security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp index 675e01d54f..3a2717b9d9 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp @@ -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,205 @@ 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::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, const AuthorizationSet& in_params, AuthorizationSet* out_params) { |