Refactor and expand KeyBlob capabilities.
KeyBlob's responsibilities have grown, it makes sense to make it a
first-class class, and to use the Serializable infrastructure.
Change-Id: I76a8dac5b4b4fe47d6677c27ab9eba2755f02dfe
diff --git a/key_blob_test.cpp b/key_blob_test.cpp
new file mode 100644
index 0000000..aaef45d
--- /dev/null
+++ b/key_blob_test.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+
+#include <gtest/gtest.h>
+
+#include <openssl/engine.h>
+
+#define KEYMASTER_NAME_TAGS
+#include "authorization_set.h"
+#include "google_keymaster_utils.h"
+#include "keymaster_tags.h"
+#include "key_blob.h"
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+ // Clean up stuff OpenSSL leaves around, so Valgrind doesn't complain.
+ CRYPTO_cleanup_all_ex_data();
+ ERR_free_strings();
+ return result;
+}
+
+namespace keymaster {
+
+bool operator==(const AuthorizationSet& a, const AuthorizationSet& b) {
+ if (a.size() != b.size())
+ return false;
+
+ for (size_t i = 0; i < a.size(); ++i) {
+ if (a[i].tag != b[i].tag)
+ return false;
+ // TODO(check value)
+ }
+
+ return true;
+}
+
+namespace test {
+
+class KeyBlobTest : public testing::Test {
+ protected:
+ KeyBlobTest()
+ : key_data_({21, 22, 23, 24, 25}),
+ master_key_data_({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}),
+ nonce_({12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}) {
+ key_.key_material = const_cast<uint8_t*>(key_data_);
+ key_.key_material_size = array_size(key_data_);
+ master_key_.key_material = const_cast<uint8_t*>(master_key_data_);
+ master_key_.key_material_size = array_size(master_key_data_);
+
+ enforced_.push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA);
+ enforced_.push_back(TAG_KEY_SIZE, 256);
+ enforced_.push_back(TAG_BLOB_USAGE_REQUIREMENTS, KM_BLOB_STANDALONE);
+ enforced_.push_back(TAG_MIN_SECONDS_BETWEEN_OPS, 10);
+ enforced_.push_back(TAG_ALL_USERS);
+ enforced_.push_back(TAG_NO_AUTH_REQUIRED);
+ enforced_.push_back(TAG_ORIGIN, KM_ORIGIN_HARDWARE);
+ enforced_.push_back(TAG_ROOT_OF_TRUST, "foo", 3);
+
+ unenforced_.push_back(TAG_ACTIVE_DATETIME, 10);
+ unenforced_.push_back(TAG_ORIGINATION_EXPIRE_DATETIME, 100);
+ unenforced_.push_back(TAG_CREATION_DATETIME, 10);
+ unenforced_.push_back(TAG_CHUNK_LENGTH, 10);
+ }
+
+ AuthorizationSet enforced_;
+ AuthorizationSet unenforced_;
+
+ keymaster_key_blob_t key_;
+ const uint8_t key_data_[5];
+ keymaster_key_blob_t master_key_;
+ const uint8_t master_key_data_[16];
+ uint8_t nonce_[KeyBlob::NONCE_LENGTH];
+};
+
+TEST_F(KeyBlobTest, EncryptDecrypt) {
+ KeyBlob blob(enforced_, unenforced_, key_, master_key_, nonce_);
+
+ size_t size = blob.SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob.Serialize(serialized_blob.get(), serialized_blob.get() + size);
+
+ // key_data shouldn't be anywhere in the blob.
+ uint8_t* begin = serialized_blob.get();
+ uint8_t* end = begin + size;
+ EXPECT_EQ(end, std::search(begin, end, key_data_, key_data_ + array_size(key_data_)));
+
+ // Recover the key material.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, master_key_);
+ EXPECT_EQ(KM_ERROR_OK, deserialized.error());
+ EXPECT_EQ(0, memcmp(deserialized.key_material(), key_data_, array_size(key_data_)));
+}
+
+TEST_F(KeyBlobTest, WrongKeyLength) {
+ KeyBlob blob(enforced_, unenforced_, key_, master_key_, nonce_);
+
+ size_t size = blob.SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob.Serialize(serialized_blob.get(), serialized_blob.get() + size);
+
+ // Find the nonce, then modify it.
+ serialized_blob[KeyBlob::NONCE_LENGTH]++;
+
+ // Decrypting with wrong nonce should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, master_key_);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+}
+
+TEST_F(KeyBlobTest, WrongNonce) {
+ KeyBlob blob(enforced_, unenforced_, key_, master_key_, nonce_);
+
+ size_t size = blob.SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob.Serialize(serialized_blob.get(), serialized_blob.get() + size);
+
+ // Find the nonce, then modify it.
+ uint8_t* begin = serialized_blob.get();
+ uint8_t* end = begin + size;
+ auto nonce_ptr = std::search(begin, end, nonce_, nonce_ + array_size(nonce_));
+ ASSERT_NE(nonce_ptr, end);
+ EXPECT_EQ(end, std::search(nonce_ptr + 1, end, nonce_, nonce_ + array_size(nonce_)));
+ (*nonce_ptr)++;
+
+ // Decrypting with wrong nonce should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, master_key_);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+ EXPECT_NE(0, memcmp(deserialized.key_material(), key_data_, array_size(key_data_)));
+}
+
+TEST_F(KeyBlobTest, WrongTag) {
+ KeyBlob blob(enforced_, unenforced_, key_, master_key_, nonce_);
+
+ size_t size = blob.SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob.Serialize(serialized_blob.get(), serialized_blob.get() + size);
+
+ // Find the tag, them modify it.
+ uint8_t* begin = serialized_blob.get();
+ uint8_t* end = begin + size;
+ auto tag_ptr = std::search(begin, end, blob.tag(), blob.tag() + KeyBlob::TAG_LENGTH);
+ ASSERT_NE(tag_ptr, end);
+ EXPECT_EQ(end, std::search(tag_ptr + 1, end, blob.tag(), blob.tag() + KeyBlob::TAG_LENGTH));
+ (*tag_ptr)++;
+
+ // Decrypting with wrong tag should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, master_key_);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+ EXPECT_NE(0, memcmp(deserialized.key_material(), key_data_, array_size(key_data_)));
+}
+
+TEST_F(KeyBlobTest, WrongCiphertext) {
+ KeyBlob blob(enforced_, unenforced_, key_, master_key_, nonce_);
+
+ size_t size = blob.SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob.Serialize(serialized_blob.get(), serialized_blob.get() + size);
+
+ // Find the ciphertext, them modify it.
+ uint8_t* begin = serialized_blob.get();
+ uint8_t* end = begin + size;
+ auto ciphertext_ptr = std::search(begin, end, blob.encrypted_key_material(),
+ blob.encrypted_key_material() + blob.key_material_length());
+ ASSERT_NE(ciphertext_ptr, end);
+ EXPECT_EQ(end, std::search(ciphertext_ptr + 1, end, blob.encrypted_key_material(),
+ blob.encrypted_key_material() + blob.key_material_length()));
+ (*ciphertext_ptr)++;
+
+ // Decrypting with wrong tag should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, master_key_);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+ EXPECT_NE(0, memcmp(deserialized.key_material(), key_data_, array_size(key_data_)));
+}
+
+TEST_F(KeyBlobTest, WrongMasterKey) {
+ KeyBlob blob(enforced_, unenforced_, key_, master_key_, nonce_);
+
+ size_t size = blob.SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob.Serialize(serialized_blob.get(), serialized_blob.get() + size);
+
+ uint8_t wrong_master_data[] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ keymaster_key_blob_t wrong_master;
+ wrong_master.key_material = wrong_master_data;
+ wrong_master.key_material_size = array_size(wrong_master_data);
+
+ // Decrypting with wrong master key should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, wrong_master);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+ EXPECT_NE(0, memcmp(deserialized.key_material(), key_data_, array_size(key_data_)));
+}
+
+TEST_F(KeyBlobTest, WrongEnforced) {
+ KeyBlob blob(enforced_, unenforced_, key_, master_key_, nonce_);
+
+ size_t size = blob.SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob.Serialize(serialized_blob.get(), serialized_blob.get() + size);
+ uint8_t* begin = serialized_blob.get();
+ uint8_t* end = begin + size;
+
+ // Find one element of enforced_ serialization and modify it.
+ keymaster_key_param_t entry = enforced_[0];
+ uint8_t* entry_begin = reinterpret_cast<uint8_t*>(&entry);
+ uint8_t* entry_end = entry_begin + sizeof(entry);
+ auto entry_ptr = std::search(begin, end, entry_begin, entry_end);
+ ASSERT_NE(end, entry_ptr);
+ EXPECT_EQ(end, std::search(entry_ptr + 1, end, entry_begin, entry_end));
+ reinterpret_cast<keymaster_key_param_t*>(entry_ptr)->integer++;
+
+ // Decrypting with wrong unenforced data should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, master_key_);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+ EXPECT_NE(0, memcmp(deserialized.key_material(), key_data_, array_size(key_data_)));
+}
+
+TEST_F(KeyBlobTest, WrongUnenforced) {
+ KeyBlob blob(enforced_, unenforced_, key_, master_key_, nonce_);
+
+ size_t size = blob.SerializedSize();
+ UniquePtr<uint8_t[]> serialized_blob(new uint8_t[size]);
+ blob.Serialize(serialized_blob.get(), serialized_blob.get() + size);
+ uint8_t* begin = serialized_blob.get();
+ uint8_t* end = begin + size;
+
+ // Find one element of unenforced_ serialization and modify it.
+ keymaster_key_param_t entry = unenforced_[0];
+ uint8_t* entry_begin = reinterpret_cast<uint8_t*>(&entry);
+ uint8_t* entry_end = entry_begin + sizeof(entry);
+ auto entry_ptr = std::search(begin, end, entry_begin, entry_end);
+ ASSERT_NE(end, entry_ptr);
+ EXPECT_EQ(end, std::search(entry_ptr + 1, end, entry_begin, entry_end));
+ reinterpret_cast<keymaster_key_param_t*>(entry_ptr)->integer++;
+
+ // Decrypting with wrong unenforced data should fail.
+ keymaster_key_blob_t encrypted_blob = {serialized_blob.get(), size};
+ KeyBlob deserialized(encrypted_blob, master_key_);
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, deserialized.error());
+ EXPECT_NE(0, memcmp(deserialized.key_material(), key_data_, array_size(key_data_)));
+}
+
+} // namespace test
+} // namespace keymaster