blob: 694a66bd70f72c730575c10a94acfc857d1b321e [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Unit tests for SignInHashTree.
#include <utility>
#include <base/files/scoped_temp_dir.h>
#include <gtest/gtest_prod.h>
#include <gmock/gmock.h>
#include "cryptohome/sign_in_hash_tree.h"
using ::testing::_;
using ::testing::Expectation;
using ::testing::Return;
using ::testing::SetArgPointee;
namespace {
// The following constant names have the format:
// kAuxLabels<l>_<k>_<constant_number>
const char kAuxKey4_2_1[] = "1100";
const std::vector<std::string> kAuxLabels4_2_1 = {{"1101", "111", "10", "0"}};
const char kAuxKey4_2_2[] = "0111";
const std::vector<std::string> kAuxLabels4_2_2 = {{"0110", "010", "00", "1"}};
const char kAuxKey6_4_1[] = "010110";
const std::vector<std::string> kAuxLabels6_4_1 = {
{"010100", "010101", "010111", "0100", "0110", "0111", "00", "10", "11"}};
const char kAuxKey6_4_2[] = "000010";
const std::vector<std::string> kAuxLabels6_4_2 = {
{"000000", "000001", "000011", "0001", "0010", "0011", "01", "10", "11"}};
const std::vector<uint8_t> kRootHash4_2 = {
{0x53, 0x6D, 0x98, 0x83, 0x7F, 0x2D, 0xD1, 0x65, 0xA5, 0x5D, 0x5E,
0xEA, 0xE9, 0x14, 0x85, 0x95, 0x44, 0x72, 0xD5, 0x6F, 0x24, 0x6D,
0xF2, 0x56, 0xBF, 0x3C, 0xAE, 0x19, 0x35, 0x2A, 0x12, 0x3c}};
const std::vector<uint8_t> kSampleHash1 = {
{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1, 0x2, 0x3,
0x4, 0x5, 0x6, 0x7, 0x8, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6,
0x7, 0x8, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}};
const std::vector<uint8_t> kRootHash6_4_1 = {
{0x42, 0xA8, 0x59, 0x26, 0xBA, 0xF5, 0x62, 0xFB, 0x10, 0xCC, 0x33,
0x79, 0x66, 0xA5, 0xC4, 0x74, 0xD1, 0x81, 0x44, 0x08, 0xB4, 0x78,
0xA7, 0x92, 0x1E, 0x07, 0x89, 0xBB, 0x9A, 0x8D, 0xBC, 0x02}};
const std::vector<uint8_t> kRootHash14_4_1 = {
{0x91, 0x3C, 0xA7, 0x20, 0x82, 0x23, 0xB8, 0xC8, 0x92, 0xA6, 0x1E,
0x83, 0xD9, 0x68, 0x07, 0x28, 0xE3, 0xE1, 0xD6, 0xBB, 0x10, 0x63,
0xF2, 0xDD, 0xCE, 0x92, 0x25, 0x71, 0x80, 0x3D, 0xA9, 0xEE}};
const std::vector<uint8_t> kRootHash14_4_2 = {
{0x59, 0x72, 0x23, 0x5E, 0xF3, 0x89, 0x4B, 0xE6, 0x6B, 0x59, 0x97,
0x22, 0xCC, 0x95, 0xC8, 0xEC, 0xB8, 0x74, 0x0E, 0x97, 0x3C, 0x77,
0x60, 0x41, 0xB4, 0x50, 0x4F, 0xE8, 0xCA, 0x4E, 0x71, 0x05}};
const std::vector<uint8_t> kSampleCredData1 = {{0xA, 0xB, 0xC, 0xD}};
std::vector<std::string> ConvertLabelsIntoStrings(
const std::vector<cryptohome::SignInHashTree::Label>& labels) {
std::vector<std::string> result_strings;
for (auto const& label : labels) {
result_strings.push_back(
std::bitset<64>(label.value()).to_string().substr(64 - label.length()));
}
return result_strings;
}
} // namespace
namespace cryptohome {
TEST(SignInHashTreeUnitTest, GetAuxiliaryLabelsTest) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
SignInHashTree tree(4, 1, temp_dir.GetPath());
// Convert the string labels into Label which the code understands.
uint64_t key_val = static_cast<uint64_t>(std::stoi(kAuxKey4_2_1, nullptr, 2));
auto label = SignInHashTree::Label(key_val, 4, 1);
auto result_labels = tree.GetAuxiliaryLabels(label);
// Convert the labels into strings for easy comparison.
EXPECT_EQ(kAuxLabels4_2_1, ConvertLabelsIntoStrings(result_labels));
key_val = static_cast<uint64_t>(std::stoi(kAuxKey4_2_2, nullptr, 2));
label = SignInHashTree::Label(key_val, 4, 1);
result_labels = tree.GetAuxiliaryLabels(label);
EXPECT_EQ(kAuxLabels4_2_2, ConvertLabelsIntoStrings(result_labels));
base::ScopedTempDir temp_dir2;
ASSERT_TRUE(temp_dir2.CreateUniqueTempDir());
SignInHashTree tree2(6, 2, temp_dir2.GetPath());
key_val = static_cast<uint64_t>(std::stoi(kAuxKey6_4_1, nullptr, 2));
label = SignInHashTree::Label(key_val, 6, 2);
result_labels = tree2.GetAuxiliaryLabels(label);
EXPECT_EQ(kAuxLabels6_4_1, ConvertLabelsIntoStrings(result_labels));
key_val = static_cast<uint64_t>(std::stoi(kAuxKey6_4_2, nullptr, 2));
label = SignInHashTree::Label(key_val, 6, 2);
result_labels = tree2.GetAuxiliaryLabels(label);
EXPECT_EQ(kAuxLabels6_4_2, ConvertLabelsIntoStrings(result_labels));
}
// Test that we can generate a hash file with an expected root hash.
// Also test that we can write to the hash file and read back from it
// when we update an inner label.
TEST(SignInHashTreeUnitTest, GenerateAndStoreHashCacheFile) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
SignInHashTree tree(4, 1, temp_dir.GetPath());
tree.GenerateAndStoreHashCache();
// Check that the root hash was calculated successfully.
std::vector<uint8_t> result_hash;
std::vector<uint8_t> cred_data;
bool metadata_lost = false;
auto label = SignInHashTree::Label(0, 0, 1);
ASSERT_TRUE(
tree.GetLabelData(label, &result_hash, &cred_data, &metadata_lost));
EXPECT_EQ(kRootHash4_2, result_hash);
// Try updating Label "00".
ASSERT_TRUE(tree.StoreLabel(SignInHashTree::Label(0, 2, 1), kSampleHash1,
cred_data, false));
// Try updating Label "101".
ASSERT_TRUE(tree.StoreLabel(SignInHashTree::Label(5, 3, 1), kSampleHash1,
cred_data, false));
result_hash.clear();
ASSERT_TRUE(tree.GetLabelData(SignInHashTree::Label(0, 2, 1), &result_hash,
&cred_data, &metadata_lost));
EXPECT_EQ(kSampleHash1, result_hash);
EXPECT_EQ(false, metadata_lost);
result_hash.clear();
ASSERT_TRUE(tree.GetLabelData(SignInHashTree::Label(5, 3, 1), &result_hash,
&cred_data, &metadata_lost));
EXPECT_EQ(kSampleHash1, result_hash);
EXPECT_EQ(false, metadata_lost);
}
// Test that we can insert and retrieve a leaf label when we initialize
// a SignInHashTree. Also make sure the hash tree can understand that
// the label has been taken. Also test that once we re-initialize a
// SignInHashTree, it will have the correct label entry, and the
// root hash will also be what we expect.
TEST(SignInHashTreeUnitTest, InsertAndRetrieveLeafLabel) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
auto tree = std::make_unique<SignInHashTree>(6, 2, temp_dir.GetPath());
tree->GenerateAndStoreHashCache();
ASSERT_TRUE(tree->StoreLabel(SignInHashTree::Label(21, 6, 2), kSampleHash1,
kSampleCredData1, false));
std::vector<uint8_t> returned_hash, cred_data;
bool metadata_lost = true;
ASSERT_TRUE(tree->GetLabelData(SignInHashTree::Label(21, 6, 2),
&returned_hash, &cred_data, &metadata_lost));
EXPECT_EQ(kSampleHash1, returned_hash);
EXPECT_EQ(kSampleCredData1, cred_data);
EXPECT_EQ(false, metadata_lost);
// Try the insert and retrieve for invalid labels too.
returned_hash.clear();
cred_data.clear();
metadata_lost = false;
ASSERT_TRUE(tree->StoreLabel(SignInHashTree::Label(21, 6, 2), kSampleHash1,
kSampleCredData1, true));
ASSERT_TRUE(tree->GetLabelData(SignInHashTree::Label(21, 6, 2),
&returned_hash, &cred_data, &metadata_lost));
EXPECT_EQ(kSampleHash1, returned_hash);
EXPECT_EQ(true, metadata_lost);
// Regenerate the hash cache so the root hash gets recalculated.
tree->GenerateAndStoreHashCache();
returned_hash.clear();
cred_data.clear();
ASSERT_TRUE(tree->GetLabelData(SignInHashTree::Label(0, 0, 2), &returned_hash,
&cred_data, &metadata_lost));
EXPECT_EQ(kRootHash6_4_1, returned_hash);
returned_hash.clear();
tree->GetRootHash(&returned_hash);
EXPECT_EQ(kRootHash6_4_1, returned_hash);
}
// Test another hash tree, and check that when you insert / remove a label,
// the |hash_cache_| gets updated without having to call
// GenerateAndStoreHsahCache on the entire tree.
TEST(SignInHashTreeUnitTest, UpdateHashCacheOnInsertRemove) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
// Create initial table and HashCache.
auto tree = std::make_unique<SignInHashTree>(14, 2, temp_dir.GetPath());
tree->GenerateAndStoreHashCache();
std::vector<uint8_t> returned_hash, cred_data;
bool metadata_lost = false;
ASSERT_TRUE(tree->GetLabelData(SignInHashTree::Label(0, 0, 2), &returned_hash,
&cred_data, &metadata_lost));
ASSERT_EQ(kRootHash14_4_1, returned_hash);
// Insert a label.
ASSERT_TRUE(tree->StoreLabel(SignInHashTree::Label(21, 14, 2), kSampleHash1,
kSampleCredData1, false));
returned_hash.clear();
tree->GetRootHash(&returned_hash);
EXPECT_EQ(kRootHash14_4_2, returned_hash);
// Remove the label; the root hash should be what it was earlier.
ASSERT_TRUE(tree->RemoveLabel(SignInHashTree::Label(21, 14, 2)));
returned_hash.clear();
tree->GetRootHash(&returned_hash);
EXPECT_EQ(kRootHash14_4_1, returned_hash);
}
} // namespace cryptohome