summaryrefslogtreecommitdiff
path: root/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
diff options
context:
space:
mode:
authorScott Lobdell <slobdell@google.com>2021-06-04 18:02:44 +0000
committerScott Lobdell <slobdell@google.com>2021-06-04 18:02:44 +0000
commitc85bde0398d2a185e66a61cdabd26e5435bb4807 (patch)
tree3d9efc2d95fccc971f519d1d323a19a4621683cd /security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
parenta01a9128380dad45a28f4740a57136a058eb9d12 (diff)
parente013dd1a8b8f0df65b20c4de224effa1a7fcc61b (diff)
Merge SP1A.210604.001
Change-Id: I78feee65e602d45bb0e3e90536729623b0f3d4f5
Diffstat (limited to 'security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp')
-rw-r--r--security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp233
1 files changed, 225 insertions, 8 deletions
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 47892042e7..a9a67bcc50 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -44,7 +44,9 @@ using std::unique_ptr;
using ::testing::AssertionFailure;
using ::testing::AssertionResult;
using ::testing::AssertionSuccess;
+using ::testing::ElementsAreArray;
using ::testing::MatchesRegex;
+using ::testing::Not;
::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set) {
if (set.size() == 0)
@@ -59,6 +61,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,
@@ -119,10 +126,10 @@ char nibble2hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
// Attestations don't contain everything in key authorization lists, so we need to filter the key
// lists to produce the lists that we expect to match the attestations.
auto kTagsToFilter = {
- Tag::CREATION_DATETIME, //
- Tag::EC_CURVE,
- Tag::HARDWARE_TYPE,
- Tag::INCLUDE_UNIQUE_ID,
+ Tag::CREATION_DATETIME,
+ Tag::EC_CURVE,
+ Tag::HARDWARE_TYPE,
+ Tag::INCLUDE_UNIQUE_ID,
};
AuthorizationSet filtered_tags(const AuthorizationSet& set) {
@@ -590,6 +597,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) {
@@ -981,6 +1187,14 @@ vector<uint8_t> build_serial_blob(const uint64_t serial_int) {
return {};
}
+ if (serial_blob.empty() || serial_blob[0] & 0x80) {
+ // An empty blob is OpenSSL's encoding of the zero value; we need single zero byte.
+ // Top bit being set indicates a negative number in two's complement, but our input
+ // was positive.
+ // In either case, prepend a zero byte.
+ serial_blob.insert(serial_blob.begin(), 0x00);
+ }
+
return serial_blob;
}
@@ -1336,14 +1550,17 @@ void check_maced_pubkey(const MacedPublicKey& macedPubKey, bool testMode,
EXPECT_EQ(extractedTag.size(), 32U);
// Compare with tag generated with kTestMacKey. Should only match in test mode
- auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
- payload->value());
+ auto macFunction = [](const cppcose::bytevec& input) {
+ return cppcose::generateHmacSha256(remote_prov::kTestMacKey, input);
+ };
+ auto testTag =
+ cppcose::generateCoseMac0Mac(macFunction, {} /* external_aad */, payload->value());
ASSERT_TRUE(testTag) << "Tag calculation failed: " << testTag.message();
if (testMode) {
- EXPECT_EQ(*testTag, extractedTag);
+ EXPECT_THAT(*testTag, ElementsAreArray(extractedTag));
} else {
- EXPECT_NE(*testTag, extractedTag);
+ EXPECT_THAT(*testTag, Not(ElementsAreArray(extractedTag)));
}
if (payload_value != nullptr) {
*payload_value = payload->value();