| // 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 |