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 "base/logging.h"
8#include "base/metrics/histogram.h"
9#include "base/values.h"
10#include "chrome/browser/prefs/pref_hash_store_transaction.h"
11#include "chrome/browser/prefs/tracked/hash_store_contents.h"
12
13class PrefHashStoreImpl::PrefHashStoreTransactionImpl
14    : public PrefHashStoreTransaction {
15 public:
16  // Constructs a PrefHashStoreTransactionImpl which can use the private
17  // members of its |outer| PrefHashStoreImpl.
18  PrefHashStoreTransactionImpl(PrefHashStoreImpl* outer,
19                               scoped_ptr<HashStoreContents> storage);
20  virtual ~PrefHashStoreTransactionImpl();
21
22  // PrefHashStoreTransaction implementation.
23  virtual ValueState CheckValue(const std::string& path,
24                                const base::Value* value) const OVERRIDE;
25  virtual void StoreHash(const std::string& path,
26                         const base::Value* value) OVERRIDE;
27  virtual ValueState CheckSplitValue(
28      const std::string& path,
29      const base::DictionaryValue* initial_split_value,
30      std::vector<std::string>* invalid_keys) const OVERRIDE;
31  virtual void StoreSplitHash(
32      const std::string& path,
33      const base::DictionaryValue* split_value) OVERRIDE;
34  virtual bool HasHash(const std::string& path) const OVERRIDE;
35  virtual void ImportHash(const std::string& path,
36                          const base::Value* hash) OVERRIDE;
37  virtual void ClearHash(const std::string& path) OVERRIDE;
38  virtual bool IsSuperMACValid() const OVERRIDE;
39  virtual bool StampSuperMac() OVERRIDE;
40
41 private:
42  bool GetSplitMacs(const std::string& path,
43                    std::map<std::string, std::string>* split_macs) const;
44
45  HashStoreContents* contents() {
46    return outer_->legacy_hash_store_contents_
47               ? outer_->legacy_hash_store_contents_.get()
48               : contents_.get();
49  }
50
51  const HashStoreContents* contents() const {
52    return outer_->legacy_hash_store_contents_
53               ? outer_->legacy_hash_store_contents_.get()
54               : contents_.get();
55  }
56
57  PrefHashStoreImpl* outer_;
58  scoped_ptr<HashStoreContents> contents_;
59
60  bool super_mac_valid_;
61  bool super_mac_dirty_;
62
63  DISALLOW_COPY_AND_ASSIGN(PrefHashStoreTransactionImpl);
64};
65
66PrefHashStoreImpl::PrefHashStoreImpl(const std::string& seed,
67                                     const std::string& device_id,
68                                     bool use_super_mac)
69    : pref_hash_calculator_(seed, device_id),
70      use_super_mac_(use_super_mac) {
71}
72
73PrefHashStoreImpl::~PrefHashStoreImpl() {
74}
75
76void PrefHashStoreImpl::set_legacy_hash_store_contents(
77    scoped_ptr<HashStoreContents> legacy_hash_store_contents) {
78  legacy_hash_store_contents_ = legacy_hash_store_contents.Pass();
79}
80
81scoped_ptr<PrefHashStoreTransaction> PrefHashStoreImpl::BeginTransaction(
82    scoped_ptr<HashStoreContents> storage) {
83  return scoped_ptr<PrefHashStoreTransaction>(
84      new PrefHashStoreTransactionImpl(this, storage.Pass()));
85}
86
87PrefHashStoreImpl::PrefHashStoreTransactionImpl::PrefHashStoreTransactionImpl(
88    PrefHashStoreImpl* outer,
89    scoped_ptr<HashStoreContents> storage)
90    : outer_(outer),
91      contents_(storage.Pass()),
92      super_mac_valid_(false),
93      super_mac_dirty_(false) {
94  if (!outer_->use_super_mac_)
95    return;
96
97  // The store must be initialized and have a valid super MAC to be trusted.
98
99  const base::DictionaryValue* store_contents = contents()->GetContents();
100  if (!store_contents)
101    return;
102
103  std::string super_mac = contents()->GetSuperMac();
104  if (super_mac.empty())
105    return;
106
107  super_mac_valid_ =
108      outer_->pref_hash_calculator_.Validate(
109          contents()->hash_store_id(), store_contents, super_mac) ==
110      PrefHashCalculator::VALID;
111}
112
113PrefHashStoreImpl::PrefHashStoreTransactionImpl::
114    ~PrefHashStoreTransactionImpl() {
115  if (super_mac_dirty_ && outer_->use_super_mac_) {
116    // Get the dictionary of hashes (or NULL if it doesn't exist).
117    const base::DictionaryValue* hashes_dict = contents()->GetContents();
118    contents()->SetSuperMac(outer_->pref_hash_calculator_.Calculate(
119        contents()->hash_store_id(), hashes_dict));
120  }
121}
122
123PrefHashStoreTransaction::ValueState
124PrefHashStoreImpl::PrefHashStoreTransactionImpl::CheckValue(
125    const std::string& path,
126    const base::Value* initial_value) const {
127  const base::DictionaryValue* hashes_dict = contents()->GetContents();
128
129  std::string last_hash;
130  if (hashes_dict)
131    hashes_dict->GetString(path, &last_hash);
132
133  if (last_hash.empty()) {
134    // In the absence of a hash for this pref, always trust a NULL value, but
135    // only trust an existing value if the initial hashes dictionary is trusted.
136    return (!initial_value || super_mac_valid_) ? TRUSTED_UNKNOWN_VALUE
137                                                : UNTRUSTED_UNKNOWN_VALUE;
138  }
139
140  PrefHashCalculator::ValidationResult validation_result =
141      outer_->pref_hash_calculator_.Validate(path, initial_value, last_hash);
142  switch (validation_result) {
143    case PrefHashCalculator::VALID:
144      return UNCHANGED;
145    case PrefHashCalculator::VALID_SECURE_LEGACY:
146      return SECURE_LEGACY;
147    case PrefHashCalculator::INVALID:
148      return initial_value ? CHANGED : CLEARED;
149  }
150  NOTREACHED() << "Unexpected PrefHashCalculator::ValidationResult: "
151               << validation_result;
152  return UNTRUSTED_UNKNOWN_VALUE;
153}
154
155void PrefHashStoreImpl::PrefHashStoreTransactionImpl::StoreHash(
156    const std::string& path,
157    const base::Value* new_value) {
158  const std::string mac =
159      outer_->pref_hash_calculator_.Calculate(path, new_value);
160  (*contents()->GetMutableContents())->SetString(path, mac);
161  super_mac_dirty_ = true;
162}
163
164PrefHashStoreTransaction::ValueState
165PrefHashStoreImpl::PrefHashStoreTransactionImpl::CheckSplitValue(
166    const std::string& path,
167    const base::DictionaryValue* initial_split_value,
168    std::vector<std::string>* invalid_keys) const {
169  DCHECK(invalid_keys && invalid_keys->empty());
170
171  std::map<std::string, std::string> split_macs;
172  const bool has_hashes = GetSplitMacs(path, &split_macs);
173
174  // Treat NULL and empty the same; otherwise we would need to store a hash
175  // for the entire dictionary (or some other special beacon) to
176  // differentiate these two cases which are really the same for
177  // dictionaries.
178  if (!initial_split_value || initial_split_value->empty())
179    return has_hashes ? CLEARED : UNCHANGED;
180
181  if (!has_hashes)
182    return super_mac_valid_ ? TRUSTED_UNKNOWN_VALUE : UNTRUSTED_UNKNOWN_VALUE;
183
184  bool has_secure_legacy_id_hashes = false;
185  std::string keyed_path(path);
186  keyed_path.push_back('.');
187  const size_t common_part_length = keyed_path.length();
188  for (base::DictionaryValue::Iterator it(*initial_split_value); !it.IsAtEnd();
189       it.Advance()) {
190    std::map<std::string, std::string>::iterator entry =
191        split_macs.find(it.key());
192    if (entry == split_macs.end()) {
193      invalid_keys->push_back(it.key());
194    } else {
195      // Keep the common part from the old |keyed_path| and replace the key to
196      // get the new |keyed_path|.
197      keyed_path.replace(common_part_length, std::string::npos, it.key());
198      switch (outer_->pref_hash_calculator_.Validate(
199          keyed_path, &it.value(), entry->second)) {
200        case PrefHashCalculator::VALID:
201          break;
202        case SECURE_LEGACY:
203          // Secure legacy device IDs based hashes are still accepted, but we
204          // should make sure to notify the caller for him to update the legacy
205          // hashes.
206          has_secure_legacy_id_hashes = true;
207          break;
208        case PrefHashCalculator::INVALID:
209          invalid_keys->push_back(it.key());
210          break;
211      }
212      // Remove processed MACs, remaining MACs at the end will also be
213      // considered invalid.
214      split_macs.erase(entry);
215    }
216  }
217
218  // Anything left in the map is missing from the data.
219  for (std::map<std::string, std::string>::const_iterator it =
220           split_macs.begin();
221       it != split_macs.end();
222       ++it) {
223    invalid_keys->push_back(it->first);
224  }
225
226  return invalid_keys->empty()
227             ? (has_secure_legacy_id_hashes ? SECURE_LEGACY : UNCHANGED)
228             : CHANGED;
229}
230
231void PrefHashStoreImpl::PrefHashStoreTransactionImpl::StoreSplitHash(
232    const std::string& path,
233    const base::DictionaryValue* split_value) {
234  scoped_ptr<HashStoreContents::MutableDictionary> mutable_dictionary =
235      contents()->GetMutableContents();
236  (*mutable_dictionary)->Remove(path, NULL);
237
238  if (split_value) {
239    std::string keyed_path(path);
240    keyed_path.push_back('.');
241    const size_t common_part_length = keyed_path.length();
242    for (base::DictionaryValue::Iterator it(*split_value); !it.IsAtEnd();
243         it.Advance()) {
244      // Keep the common part from the old |keyed_path| and replace the key to
245      // get the new |keyed_path|.
246      keyed_path.replace(common_part_length, std::string::npos, it.key());
247      (*mutable_dictionary)->SetString(
248          keyed_path,
249          outer_->pref_hash_calculator_.Calculate(keyed_path, &it.value()));
250    }
251  }
252  super_mac_dirty_ = true;
253}
254
255bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::GetSplitMacs(
256    const std::string& key,
257    std::map<std::string, std::string>* split_macs) const {
258  DCHECK(split_macs);
259  DCHECK(split_macs->empty());
260
261  const base::DictionaryValue* hashes_dict = contents()->GetContents();
262  const base::DictionaryValue* split_mac_dictionary = NULL;
263  if (!hashes_dict || !hashes_dict->GetDictionary(key, &split_mac_dictionary))
264    return false;
265  for (base::DictionaryValue::Iterator it(*split_mac_dictionary); !it.IsAtEnd();
266       it.Advance()) {
267    std::string mac_string;
268    if (!it.value().GetAsString(&mac_string)) {
269      NOTREACHED();
270      continue;
271    }
272    split_macs->insert(make_pair(it.key(), mac_string));
273  }
274  return true;
275}
276
277bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::HasHash(
278    const std::string& path) const {
279  const base::DictionaryValue* hashes_dict = contents()->GetContents();
280  return hashes_dict && hashes_dict->Get(path, NULL);
281}
282
283void PrefHashStoreImpl::PrefHashStoreTransactionImpl::ImportHash(
284    const std::string& path,
285    const base::Value* hash) {
286  DCHECK(hash);
287
288  (*contents()->GetMutableContents())->Set(path, hash->DeepCopy());
289
290  if (super_mac_valid_)
291    super_mac_dirty_ = true;
292}
293
294void PrefHashStoreImpl::PrefHashStoreTransactionImpl::ClearHash(
295    const std::string& path) {
296  if ((*contents()->GetMutableContents())->RemovePath(path, NULL) &&
297      super_mac_valid_) {
298    super_mac_dirty_ = true;
299  }
300}
301
302bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::IsSuperMACValid() const {
303  return super_mac_valid_;
304}
305
306bool PrefHashStoreImpl::PrefHashStoreTransactionImpl::StampSuperMac() {
307  if (!outer_->use_super_mac_ || super_mac_valid_)
308    return false;
309  super_mac_dirty_ = true;
310  return true;
311}
312