1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/prefs/pref_hash_store_impl.h" 6 7 #include <string> 8 9 #include "base/macros.h" 10 #include "base/values.h" 11 #include "chrome/browser/prefs/pref_hash_store_impl.h" 12 #include "chrome/browser/prefs/pref_hash_store_transaction.h" 13 #include "chrome/browser/prefs/tracked/dictionary_hash_store_contents.h" 14 #include "chrome/browser/prefs/tracked/hash_store_contents.h" 15 #include "testing/gtest/include/gtest/gtest.h" 16 17 class PrefHashStoreImplTest : public testing::Test { 18 protected: 19 scoped_ptr<HashStoreContents> CreateHashStoreContents() { 20 return scoped_ptr<HashStoreContents>( 21 new DictionaryHashStoreContents(&pref_store_contents_)); 22 } 23 24 private: 25 base::DictionaryValue pref_store_contents_; 26 }; 27 28 TEST_F(PrefHashStoreImplTest, AtomicHashStoreAndCheck) { 29 base::StringValue string_1("string1"); 30 base::StringValue string_2("string2"); 31 32 { 33 // 32 NULL bytes is the seed that was used to generate the legacy hash. 34 PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true); 35 scoped_ptr<PrefHashStoreTransaction> transaction( 36 pref_hash_store.BeginTransaction(CreateHashStoreContents())); 37 38 // Only NULL should be trusted in the absence of a hash. 39 EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE, 40 transaction->CheckValue("path1", &string_1)); 41 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE, 42 transaction->CheckValue("path1", NULL)); 43 44 transaction->StoreHash("path1", &string_1); 45 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED, 46 transaction->CheckValue("path1", &string_1)); 47 EXPECT_EQ(PrefHashStoreTransaction::CLEARED, 48 transaction->CheckValue("path1", NULL)); 49 transaction->StoreHash("path1", NULL); 50 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED, 51 transaction->CheckValue("path1", NULL)); 52 EXPECT_EQ(PrefHashStoreTransaction::CHANGED, 53 transaction->CheckValue("path1", &string_2)); 54 55 base::DictionaryValue dict; 56 dict.Set("a", new base::StringValue("foo")); 57 dict.Set("d", new base::StringValue("bad")); 58 dict.Set("b", new base::StringValue("bar")); 59 dict.Set("c", new base::StringValue("baz")); 60 61 // Manually shove in a legacy hash. 62 (*CreateHashStoreContents()->GetMutableContents())->SetString( 63 "path1", 64 "C503FB7C65EEFD5C07185F616A0AA67923C069909933F362022B1F187E73E9A2"); 65 66 EXPECT_EQ(PrefHashStoreTransaction::WEAK_LEGACY, 67 transaction->CheckValue("path1", &dict)); 68 transaction->StoreHash("path1", &dict); 69 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED, 70 transaction->CheckValue("path1", &dict)); 71 } 72 73 ASSERT_FALSE(CreateHashStoreContents()->GetSuperMac().empty()); 74 75 { 76 // |pref_hash_store2| should trust its initial hashes dictionary and thus 77 // trust new unknown values. 78 PrefHashStoreImpl pref_hash_store2(std::string(32, 0), "device_id", true); 79 scoped_ptr<PrefHashStoreTransaction> transaction( 80 pref_hash_store2.BeginTransaction(CreateHashStoreContents())); 81 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE, 82 transaction->CheckValue("new_path", &string_1)); 83 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE, 84 transaction->CheckValue("new_path", &string_2)); 85 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE, 86 transaction->CheckValue("new_path", NULL)); 87 } 88 89 // Manually corrupt the super MAC. 90 CreateHashStoreContents()->SetSuperMac(std::string(64, 'A')); 91 92 { 93 // |pref_hash_store3| should no longer trust its initial hashes dictionary 94 // and thus shouldn't trust non-NULL unknown values. 95 PrefHashStoreImpl pref_hash_store3(std::string(32, 0), "device_id", true); 96 scoped_ptr<PrefHashStoreTransaction> transaction( 97 pref_hash_store3.BeginTransaction(CreateHashStoreContents())); 98 EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE, 99 transaction->CheckValue("new_path", &string_1)); 100 EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE, 101 transaction->CheckValue("new_path", &string_2)); 102 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE, 103 transaction->CheckValue("new_path", NULL)); 104 } 105 } 106 107 TEST_F(PrefHashStoreImplTest, ImportExportOperations) { 108 base::StringValue string_1("string1"); 109 base::StringValue string_2("string2"); 110 111 // Initial state: no super MAC. 112 { 113 PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true); 114 scoped_ptr<PrefHashStoreTransaction> transaction( 115 pref_hash_store.BeginTransaction(CreateHashStoreContents())); 116 ASSERT_FALSE(transaction->IsSuperMACValid()); 117 118 ASSERT_FALSE(transaction->HasHash("path1")); 119 120 // Storing a hash will stamp the super MAC. 121 transaction->StoreHash("path1", &string_1); 122 123 ASSERT_TRUE(transaction->HasHash("path1")); 124 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED, 125 transaction->CheckValue("path1", &string_1)); 126 EXPECT_EQ(PrefHashStoreTransaction::CHANGED, 127 transaction->CheckValue("path1", &string_2)); 128 } 129 130 // Make a copy of the stored hash for future use. 131 const base::Value* hash = NULL; 132 ASSERT_TRUE(CreateHashStoreContents()->GetContents()->Get("path1", &hash)); 133 scoped_ptr<base::Value> path_1_string_1_hash_copy(hash->DeepCopy()); 134 hash = NULL; 135 136 // Verify that the super MAC was stamped. 137 { 138 PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true); 139 scoped_ptr<PrefHashStoreTransaction> transaction( 140 pref_hash_store.BeginTransaction(CreateHashStoreContents())); 141 ASSERT_TRUE(transaction->IsSuperMACValid()); 142 ASSERT_TRUE(transaction->HasHash("path1")); 143 144 // Clearing the hash should preserve validity. 145 transaction->ClearHash("path1"); 146 147 // The effects of the clear should be immediately visible. 148 ASSERT_FALSE(transaction->HasHash("path1")); 149 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE, 150 transaction->CheckValue("path1", NULL)); 151 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE, 152 transaction->CheckValue("path1", &string_1)); 153 } 154 155 // Verify that validity was preserved and that the clear took effect. 156 { 157 PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true); 158 scoped_ptr<PrefHashStoreTransaction> transaction( 159 pref_hash_store.BeginTransaction(CreateHashStoreContents())); 160 ASSERT_TRUE(transaction->IsSuperMACValid()); 161 ASSERT_FALSE(transaction->HasHash("path1")); 162 } 163 164 // Invalidate the super MAC. 165 CreateHashStoreContents()->SetSuperMac(std::string()); 166 167 { 168 PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true); 169 scoped_ptr<PrefHashStoreTransaction> transaction( 170 pref_hash_store.BeginTransaction(CreateHashStoreContents())); 171 ASSERT_FALSE(transaction->IsSuperMACValid()); 172 ASSERT_FALSE(transaction->HasHash("path1")); 173 174 // An import should preserve invalidity. 175 transaction->ImportHash("path1", path_1_string_1_hash_copy.get()); 176 177 ASSERT_TRUE(transaction->HasHash("path1")); 178 179 // The imported hash should be usable for validating the original value. 180 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED, 181 transaction->CheckValue("path1", &string_1)); 182 } 183 184 // Verify that invalidity was preserved and that the import took effect. 185 { 186 PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true); 187 scoped_ptr<PrefHashStoreTransaction> transaction( 188 pref_hash_store.BeginTransaction(CreateHashStoreContents())); 189 ASSERT_FALSE(transaction->IsSuperMACValid()); 190 ASSERT_TRUE(transaction->HasHash("path1")); 191 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED, 192 transaction->CheckValue("path1", &string_1)); 193 194 // After clearing the hash, non-null values are UNTRUSTED_UNKNOWN. 195 transaction->ClearHash("path1"); 196 197 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE, 198 transaction->CheckValue("path1", NULL)); 199 EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE, 200 transaction->CheckValue("path1", &string_1)); 201 } 202 203 { 204 PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true); 205 scoped_ptr<PrefHashStoreTransaction> transaction( 206 pref_hash_store.BeginTransaction(CreateHashStoreContents())); 207 ASSERT_FALSE(transaction->IsSuperMACValid()); 208 209 // Test StampSuperMac. 210 transaction->StampSuperMac(); 211 } 212 213 // Verify that the store is now valid. 214 { 215 PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true); 216 scoped_ptr<PrefHashStoreTransaction> transaction( 217 pref_hash_store.BeginTransaction(CreateHashStoreContents())); 218 ASSERT_TRUE(transaction->IsSuperMACValid()); 219 220 // Store the hash of a different value to test an "over-import". 221 transaction->StoreHash("path1", &string_2); 222 EXPECT_EQ(PrefHashStoreTransaction::CHANGED, 223 transaction->CheckValue("path1", &string_1)); 224 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED, 225 transaction->CheckValue("path1", &string_2)); 226 } 227 228 { 229 PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true); 230 scoped_ptr<PrefHashStoreTransaction> transaction( 231 pref_hash_store.BeginTransaction(CreateHashStoreContents())); 232 ASSERT_TRUE(transaction->IsSuperMACValid()); 233 234 // "Over-import". An import should preserve validity. 235 transaction->ImportHash("path1", path_1_string_1_hash_copy.get()); 236 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED, 237 transaction->CheckValue("path1", &string_1)); 238 EXPECT_EQ(PrefHashStoreTransaction::CHANGED, 239 transaction->CheckValue("path1", &string_2)); 240 } 241 242 // Verify that validity was preserved and the "over-import" took effect. 243 { 244 PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true); 245 scoped_ptr<PrefHashStoreTransaction> transaction( 246 pref_hash_store.BeginTransaction(CreateHashStoreContents())); 247 ASSERT_TRUE(transaction->IsSuperMACValid()); 248 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED, 249 transaction->CheckValue("path1", &string_1)); 250 EXPECT_EQ(PrefHashStoreTransaction::CHANGED, 251 transaction->CheckValue("path1", &string_2)); 252 } 253 } 254 255 TEST_F(PrefHashStoreImplTest, SuperMACDisabled) { 256 base::StringValue string_1("string1"); 257 base::StringValue string_2("string2"); 258 259 { 260 // Pass |use_super_mac| => false. 261 PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", false); 262 scoped_ptr<PrefHashStoreTransaction> transaction( 263 pref_hash_store.BeginTransaction(CreateHashStoreContents())); 264 265 transaction->StoreHash("path1", &string_2); 266 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED, 267 transaction->CheckValue("path1", &string_2)); 268 } 269 270 ASSERT_TRUE(CreateHashStoreContents()->GetSuperMac().empty()); 271 272 { 273 PrefHashStoreImpl pref_hash_store2(std::string(32, 0), "device_id", false); 274 scoped_ptr<PrefHashStoreTransaction> transaction( 275 pref_hash_store2.BeginTransaction(CreateHashStoreContents())); 276 EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE, 277 transaction->CheckValue("new_path", &string_1)); 278 } 279 } 280 281 TEST_F(PrefHashStoreImplTest, SplitHashStoreAndCheck) { 282 base::DictionaryValue dict; 283 dict.Set("a", new base::StringValue("to be replaced")); 284 dict.Set("b", new base::StringValue("same")); 285 dict.Set("o", new base::StringValue("old")); 286 287 base::DictionaryValue modified_dict; 288 modified_dict.Set("a", new base::StringValue("replaced")); 289 modified_dict.Set("b", new base::StringValue("same")); 290 modified_dict.Set("c", new base::StringValue("new")); 291 292 base::DictionaryValue empty_dict; 293 294 std::vector<std::string> invalid_keys; 295 296 { 297 PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true); 298 scoped_ptr<PrefHashStoreTransaction> transaction( 299 pref_hash_store.BeginTransaction(CreateHashStoreContents())); 300 301 // No hashes stored yet and hashes dictionary is empty (and thus not 302 // trusted). 303 EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE, 304 transaction->CheckSplitValue("path1", &dict, &invalid_keys)); 305 EXPECT_TRUE(invalid_keys.empty()); 306 307 transaction->StoreSplitHash("path1", &dict); 308 309 // Verify match post storage. 310 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED, 311 transaction->CheckSplitValue("path1", &dict, &invalid_keys)); 312 EXPECT_TRUE(invalid_keys.empty()); 313 314 // Verify new path is still unknown. 315 EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE, 316 transaction->CheckSplitValue("path2", &dict, &invalid_keys)); 317 EXPECT_TRUE(invalid_keys.empty()); 318 319 // Verify NULL or empty dicts are declared as having been cleared. 320 EXPECT_EQ(PrefHashStoreTransaction::CLEARED, 321 transaction->CheckSplitValue("path1", NULL, &invalid_keys)); 322 EXPECT_TRUE(invalid_keys.empty()); 323 EXPECT_EQ( 324 PrefHashStoreTransaction::CLEARED, 325 transaction->CheckSplitValue("path1", &empty_dict, &invalid_keys)); 326 EXPECT_TRUE(invalid_keys.empty()); 327 328 // Verify changes are properly detected. 329 EXPECT_EQ( 330 PrefHashStoreTransaction::CHANGED, 331 transaction->CheckSplitValue("path1", &modified_dict, &invalid_keys)); 332 std::vector<std::string> expected_invalid_keys1; 333 expected_invalid_keys1.push_back("a"); 334 expected_invalid_keys1.push_back("c"); 335 expected_invalid_keys1.push_back("o"); 336 EXPECT_EQ(expected_invalid_keys1, invalid_keys); 337 invalid_keys.clear(); 338 339 // Verify |dict| still matches post check. 340 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED, 341 transaction->CheckSplitValue("path1", &dict, &invalid_keys)); 342 EXPECT_TRUE(invalid_keys.empty()); 343 344 // Store hash for |modified_dict|. 345 transaction->StoreSplitHash("path1", &modified_dict); 346 347 // Verify |modified_dict| is now the one that verifies correctly. 348 EXPECT_EQ( 349 PrefHashStoreTransaction::UNCHANGED, 350 transaction->CheckSplitValue("path1", &modified_dict, &invalid_keys)); 351 EXPECT_TRUE(invalid_keys.empty()); 352 353 // Verify old dict no longer matches. 354 EXPECT_EQ(PrefHashStoreTransaction::CHANGED, 355 transaction->CheckSplitValue("path1", &dict, &invalid_keys)); 356 std::vector<std::string> expected_invalid_keys2; 357 expected_invalid_keys2.push_back("a"); 358 expected_invalid_keys2.push_back("o"); 359 expected_invalid_keys2.push_back("c"); 360 EXPECT_EQ(expected_invalid_keys2, invalid_keys); 361 invalid_keys.clear(); 362 363 } 364 365 { 366 // |pref_hash_store2| should trust its initial hashes dictionary and thus 367 // trust new unknown values. 368 PrefHashStoreImpl pref_hash_store2(std::string(32, 0), "device_id", true); 369 scoped_ptr<PrefHashStoreTransaction> transaction( 370 pref_hash_store2.BeginTransaction(CreateHashStoreContents())); 371 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE, 372 transaction->CheckSplitValue("new_path", &dict, &invalid_keys)); 373 EXPECT_TRUE(invalid_keys.empty()); 374 } 375 376 // Manually corrupt the super MAC. 377 CreateHashStoreContents()->SetSuperMac(std::string(64, 'A')); 378 379 { 380 // |pref_hash_store3| should no longer trust its initial hashes dictionary 381 // and thus shouldn't trust unknown values. 382 PrefHashStoreImpl pref_hash_store3(std::string(32, 0), "device_id", true); 383 scoped_ptr<PrefHashStoreTransaction> transaction( 384 pref_hash_store3.BeginTransaction(CreateHashStoreContents())); 385 EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE, 386 transaction->CheckSplitValue("new_path", &dict, &invalid_keys)); 387 EXPECT_TRUE(invalid_keys.empty()); 388 } 389 } 390 391 TEST_F(PrefHashStoreImplTest, EmptyAndNULLSplitDict) { 392 base::DictionaryValue empty_dict; 393 394 std::vector<std::string> invalid_keys; 395 396 { 397 PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true); 398 scoped_ptr<PrefHashStoreTransaction> transaction( 399 pref_hash_store.BeginTransaction(CreateHashStoreContents())); 400 401 // Store hashes for a random dict to be overwritten below. 402 base::DictionaryValue initial_dict; 403 initial_dict.Set("a", new base::StringValue("foo")); 404 transaction->StoreSplitHash("path1", &initial_dict); 405 406 // Verify stored empty dictionary matches NULL and empty dictionary back. 407 transaction->StoreSplitHash("path1", &empty_dict); 408 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED, 409 transaction->CheckSplitValue("path1", NULL, &invalid_keys)); 410 EXPECT_TRUE(invalid_keys.empty()); 411 EXPECT_EQ( 412 PrefHashStoreTransaction::UNCHANGED, 413 transaction->CheckSplitValue("path1", &empty_dict, &invalid_keys)); 414 EXPECT_TRUE(invalid_keys.empty()); 415 416 // Same when storing NULL directly. 417 transaction->StoreSplitHash("path1", NULL); 418 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED, 419 transaction->CheckSplitValue("path1", NULL, &invalid_keys)); 420 EXPECT_TRUE(invalid_keys.empty()); 421 EXPECT_EQ( 422 PrefHashStoreTransaction::UNCHANGED, 423 transaction->CheckSplitValue("path1", &empty_dict, &invalid_keys)); 424 EXPECT_TRUE(invalid_keys.empty()); 425 } 426 427 { 428 // |pref_hash_store2| should trust its initial hashes dictionary (and thus 429 // trust new unknown values) even though the last action done was to clear 430 // the hashes for path1 by setting its value to NULL (this is a regression 431 // test ensuring that the internal action of clearing some hashes does 432 // update the stored hash of hashes). 433 PrefHashStoreImpl pref_hash_store2(std::string(32, 0), "device_id", true); 434 scoped_ptr<PrefHashStoreTransaction> transaction( 435 pref_hash_store2.BeginTransaction(CreateHashStoreContents())); 436 437 base::DictionaryValue tested_dict; 438 tested_dict.Set("a", new base::StringValue("foo")); 439 tested_dict.Set("b", new base::StringValue("bar")); 440 EXPECT_EQ( 441 PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE, 442 transaction->CheckSplitValue("new_path", &tested_dict, &invalid_keys)); 443 EXPECT_TRUE(invalid_keys.empty()); 444 } 445 } 446 447 // Test that the PrefHashStore returns TRUSTED_UNKNOWN_VALUE when checking for 448 // a split preference even if there is an existing atomic preference's hash 449 // stored. There is no point providing a migration path for preferences 450 // switching strategies after their initial release as split preferences are 451 // turned into split preferences specifically because the atomic hash isn't 452 // considered useful. 453 TEST_F(PrefHashStoreImplTest, TrustedUnknownSplitValueFromExistingAtomic) { 454 base::StringValue string("string1"); 455 456 base::DictionaryValue dict; 457 dict.Set("a", new base::StringValue("foo")); 458 dict.Set("d", new base::StringValue("bad")); 459 dict.Set("b", new base::StringValue("bar")); 460 dict.Set("c", new base::StringValue("baz")); 461 462 { 463 PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true); 464 scoped_ptr<PrefHashStoreTransaction> transaction( 465 pref_hash_store.BeginTransaction(CreateHashStoreContents())); 466 467 transaction->StoreHash("path1", &string); 468 EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED, 469 transaction->CheckValue("path1", &string)); 470 } 471 472 { 473 // Load a new |pref_hash_store2| in which the hashes dictionary is trusted. 474 PrefHashStoreImpl pref_hash_store2(std::string(32, 0), "device_id", true); 475 scoped_ptr<PrefHashStoreTransaction> transaction( 476 pref_hash_store2.BeginTransaction(CreateHashStoreContents())); 477 std::vector<std::string> invalid_keys; 478 EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE, 479 transaction->CheckSplitValue("path1", &dict, &invalid_keys)); 480 EXPECT_TRUE(invalid_keys.empty()); 481 } 482 } 483