Home | History | Annotate | Download | only in prefs
      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_calculator.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/bind.h"
     10 #include "base/json/json_string_value_serializer.h"
     11 #include "base/logging.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/values.h"
     15 #include "crypto/hmac.h"
     16 
     17 namespace {
     18 
     19 // Calculates an HMAC of |message| using |key|, encoded as a hexadecimal string.
     20 std::string GetDigestString(const std::string& key,
     21                             const std::string& message) {
     22   crypto::HMAC hmac(crypto::HMAC::SHA256);
     23   std::vector<uint8> digest(hmac.DigestLength());
     24   if (!hmac.Init(key) || !hmac.Sign(message, &digest[0], digest.size())) {
     25     NOTREACHED();
     26     return std::string();
     27   }
     28   return base::HexEncode(&digest[0], digest.size());
     29 }
     30 
     31 // Verifies that |digest_string| is a valid HMAC of |message| using |key|.
     32 // |digest_string| must be encoded as a hexadecimal string.
     33 bool VerifyDigestString(const std::string& key,
     34                         const std::string& message,
     35                         const std::string& digest_string) {
     36   crypto::HMAC hmac(crypto::HMAC::SHA256);
     37   std::vector<uint8> digest;
     38   return base::HexStringToBytes(digest_string, &digest) &&
     39       hmac.Init(key) &&
     40       hmac.Verify(message,
     41                   base::StringPiece(reinterpret_cast<char*>(&digest[0]),
     42                                     digest.size()));
     43 }
     44 
     45 // Renders |value| as a string. |value| may be NULL, in which case the result
     46 // is an empty string. This method can be expensive and its result should be
     47 // re-used rather than recomputed where possible.
     48 std::string ValueAsString(const base::Value* value) {
     49   // Dictionary values may contain empty lists and sub-dictionaries. Make a
     50   // deep copy with those removed to make the hash more stable.
     51   const base::DictionaryValue* dict_value;
     52   scoped_ptr<base::DictionaryValue> canonical_dict_value;
     53   if (value && value->GetAsDictionary(&dict_value)) {
     54     canonical_dict_value.reset(dict_value->DeepCopyWithoutEmptyChildren());
     55     value = canonical_dict_value.get();
     56   }
     57 
     58   std::string value_as_string;
     59   if (value) {
     60     JSONStringValueSerializer serializer(&value_as_string);
     61     serializer.Serialize(*value);
     62   }
     63 
     64   return value_as_string;
     65 }
     66 
     67 // Concatenates |device_id|, |path|, and |value_as_string| to give the hash
     68 // input.
     69 std::string GetMessage(const std::string& device_id,
     70                        const std::string& path,
     71                        const std::string& value_as_string) {
     72   std::string message;
     73   message.reserve(device_id.size() + path.size() + value_as_string.size());
     74   message.append(device_id);
     75   message.append(path);
     76   message.append(value_as_string);
     77   return message;
     78 }
     79 
     80 // Generates a device ID based on the input device ID. The derived device ID has
     81 // no useful properties beyond those of the input device ID except that it is
     82 // consistent with previous implementations.
     83 // TODO(gab): Remove this once UMA reports for
     84 // Settings.TrackedPreferenceMigratedLegacyDeviceId become insignificant.
     85 std::string GenerateDeviceIdLikePrefMetricsServiceDid(
     86     const std::string& original_device_id) {
     87   if (original_device_id.empty())
     88     return std::string();
     89   return base::StringToLowerASCII(
     90       GetDigestString(original_device_id, "PrefMetricsService"));
     91 }
     92 
     93 }  // namespace
     94 
     95 PrefHashCalculator::PrefHashCalculator(const std::string& seed,
     96                                        const std::string& device_id)
     97     : seed_(seed),
     98       device_id_(device_id),
     99       legacy_device_id_(GenerateDeviceIdLikePrefMetricsServiceDid(device_id)) {}
    100 
    101 PrefHashCalculator::~PrefHashCalculator() {}
    102 
    103 std::string PrefHashCalculator::Calculate(const std::string& path,
    104                                           const base::Value* value) const {
    105   return GetDigestString(seed_,
    106                          GetMessage(device_id_, path, ValueAsString(value)));
    107 }
    108 
    109 PrefHashCalculator::ValidationResult PrefHashCalculator::Validate(
    110     const std::string& path,
    111     const base::Value* value,
    112     const std::string& digest_string) const {
    113   const std::string value_as_string(ValueAsString(value));
    114   if (VerifyDigestString(seed_, GetMessage(device_id_, path, value_as_string),
    115                          digest_string)) {
    116     return VALID;
    117   }
    118   if (VerifyDigestString(seed_,
    119                          GetMessage(legacy_device_id_, path, value_as_string),
    120                          digest_string)) {
    121     return VALID_SECURE_LEGACY;
    122   }
    123   return INVALID;
    124 }
    125