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