device_id_fetcher.cc revision 58537e28ecd584eab876aee8be7156509866d23a
1// Copyright (c) 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/renderer_host/pepper/device_id_fetcher.h"
6
7#include "base/file_util.h"
8#include "base/prefs/pref_service.h"
9#include "base/strings/string_number_conversions.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/common/pref_names.h"
12#if defined(OS_CHROMEOS)
13#include "chromeos/cryptohome/cryptohome_library.h"
14#endif
15#include "components/user_prefs/pref_registry_syncable.h"
16#include "content/public/browser/browser_context.h"
17#include "content/public/browser/browser_ppapi_host.h"
18#include "content/public/browser/browser_thread.h"
19#include "content/public/browser/render_process_host.h"
20#include "crypto/encryptor.h"
21#include "crypto/random.h"
22#include "crypto/sha2.h"
23#if defined(ENABLE_RLZ)
24#include "rlz/lib/machine_id.h"
25#endif
26
27using content::BrowserPpapiHost;
28using content::BrowserThread;
29using content::RenderProcessHost;
30
31namespace chrome {
32
33namespace {
34
35const char kDRMIdentifierFile[] = "Pepper DRM ID.0";
36
37const uint32_t kSaltLength = 32;
38
39}  // namespace
40
41DeviceIDFetcher::DeviceIDFetcher(int render_process_id)
42    : in_progress_(false),
43      render_process_id_(render_process_id) {
44  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
45}
46
47DeviceIDFetcher::~DeviceIDFetcher() {
48}
49
50bool DeviceIDFetcher::Start(const IDCallback& callback) {
51  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
52
53  if (in_progress_)
54    return false;
55
56  in_progress_ = true;
57  callback_ = callback;
58
59  BrowserThread::PostTask(
60      BrowserThread::UI, FROM_HERE,
61      base::Bind(&DeviceIDFetcher::CheckPrefsOnUIThread, this));
62  return true;
63}
64
65// static
66void DeviceIDFetcher::RegisterProfilePrefs(
67    user_prefs::PrefRegistrySyncable* prefs) {
68  prefs->RegisterBooleanPref(prefs::kEnableDRM,
69                             true,
70                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
71  prefs->RegisterStringPref(
72      prefs::kDRMSalt,
73      "",
74      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
75}
76
77// static
78base::FilePath DeviceIDFetcher::GetLegacyDeviceIDPath(
79    const base::FilePath& profile_path) {
80  return profile_path.AppendASCII(kDRMIdentifierFile);
81}
82
83// TODO(raymes): Change this to just return the device id salt and call it with
84// PostTaskAndReply once the legacy ChromeOS codepath is removed.
85void DeviceIDFetcher::CheckPrefsOnUIThread() {
86  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
87
88  Profile* profile = NULL;
89  RenderProcessHost* render_process_host =
90      RenderProcessHost::FromID(render_process_id_);
91  if (render_process_host && render_process_host->GetBrowserContext()) {
92    profile = Profile::FromBrowserContext(
93        render_process_host->GetBrowserContext());
94  }
95
96  if (!profile ||
97      profile->IsOffTheRecord() ||
98      !profile->GetPrefs()->GetBoolean(prefs::kEnableDRM)) {
99    RunCallbackOnIOThread(std::string());
100    return;
101  }
102
103  // Check if the salt pref is set. If it isn't, set it.
104  std::string salt = profile->GetPrefs()->GetString(prefs::kDRMSalt);
105  if (salt.empty()) {
106    uint8_t salt_bytes[kSaltLength];
107    crypto::RandBytes(salt_bytes, arraysize(salt_bytes));
108    // Since it will be stored in a string pref, convert it to hex.
109    salt = base::HexEncode(salt_bytes, arraysize(salt_bytes));
110    profile->GetPrefs()->SetString(prefs::kDRMSalt, salt);
111  }
112
113#if defined(OS_CHROMEOS)
114  // Try the legacy path first for ChromeOS. We pass the new salt in as well
115  // in case the legacy id doesn't exist.
116  BrowserThread::PostBlockingPoolTask(FROM_HERE,
117      base::Bind(&DeviceIDFetcher::ComputeOnBlockingPool, this,
118                 profile->GetPath(), salt));
119#else
120  BrowserThread::PostTask(
121      BrowserThread::IO, FROM_HERE,
122      base::Bind(&DeviceIDFetcher::ComputeOnIOThread, this, salt));
123#endif
124}
125
126
127void DeviceIDFetcher::ComputeOnIOThread(const std::string& salt) {
128  std::vector<uint8> salt_bytes;
129  if (!base::HexStringToBytes(salt, &salt_bytes))
130    salt_bytes.clear();
131
132  // Build the identifier as follows:
133  // SHA256(machine-id||service||SHA256(machine-id||service||salt))
134  std::string machine_id = GetMachineID();
135  if (machine_id.empty() || salt_bytes.size() != kSaltLength) {
136    NOTREACHED();
137    RunCallbackOnIOThread(std::string());
138    return;
139  }
140
141  char id_buf[256 / 8];  // 256-bits for SHA256
142  std::string input = machine_id;
143  input.append(kDRMIdentifierFile);
144  input.append(salt_bytes.begin(), salt_bytes.end());
145  crypto::SHA256HashString(input, &id_buf, sizeof(id_buf));
146  std::string id = StringToLowerASCII(
147      base::HexEncode(reinterpret_cast<const void*>(id_buf), sizeof(id_buf)));
148  input = machine_id;
149  input.append(kDRMIdentifierFile);
150  input.append(id);
151  crypto::SHA256HashString(input, &id_buf, sizeof(id_buf));
152  id = StringToLowerASCII(base::HexEncode(
153        reinterpret_cast<const void*>(id_buf),
154        sizeof(id_buf)));
155
156  RunCallbackOnIOThread(id);
157}
158
159// TODO(raymes): This is temporary code to migrate ChromeOS devices to the new
160// scheme for generating device IDs. Delete this once we are sure most ChromeOS
161// devices have been migrated.
162void DeviceIDFetcher::ComputeOnBlockingPool(const base::FilePath& profile_path,
163                                            const std::string& salt) {
164  std::string id;
165  // First check if the legacy device ID file exists on ChromeOS. If it does, we
166  // should just return that.
167  base::FilePath id_path = GetLegacyDeviceIDPath(profile_path);
168  if (base::PathExists(id_path)) {
169    if (base::ReadFileToString(id_path, &id) && !id.empty()) {
170      RunCallbackOnIOThread(id);
171      return;
172    }
173  }
174  // If we didn't find an ID, go back to the new code path to generate an ID.
175  BrowserThread::PostTask(
176      BrowserThread::IO, FROM_HERE,
177      base::Bind(&DeviceIDFetcher::ComputeOnIOThread, this, salt));
178}
179
180
181void DeviceIDFetcher::RunCallbackOnIOThread(const std::string& id) {
182  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
183    BrowserThread::PostTask(
184        BrowserThread::IO, FROM_HERE,
185        base::Bind(&DeviceIDFetcher::RunCallbackOnIOThread, this, id));
186    return;
187  }
188  in_progress_ = false;
189  callback_.Run(id);
190}
191
192std::string DeviceIDFetcher::GetMachineID() {
193#if defined(OS_WIN) && defined(ENABLE_RLZ)
194  std::string result;
195  rlz_lib::GetMachineId(&result);
196  return result;
197#elif defined(OS_CHROMEOS)
198  chromeos::CryptohomeLibrary* c_home = chromeos::CryptohomeLibrary::Get();
199  return c_home->GetSystemSalt();
200#else
201  // Not implemented for other platforms.
202  NOTREACHED();
203  return "";
204#endif
205}
206
207}  // namespace chrome
208