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.
5 #include "chrome/browser/renderer_host/pepper/device_id_fetcher.h"
7 #include "base/files/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/system_salt_getter.h"
15 #include "components/pref_registry/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 #include "ppapi/c/pp_errors.h"
24 #if defined(ENABLE_RLZ)
25 #include "rlz/lib/machine_id.h"
28 using content::BrowserPpapiHost
;
29 using content::BrowserThread
;
30 using content::RenderProcessHost
;
36 const char kDRMIdentifierFile
[] = "Pepper DRM ID.0";
38 const uint32_t kSaltLength
= 32;
40 void GetMachineIDAsync(
41 const base::Callback
<void(const std::string
&)>& callback
) {
42 #if defined(OS_WIN) && defined(ENABLE_RLZ)
44 rlz_lib::GetMachineId(&result
);
46 #elif defined(OS_CHROMEOS)
47 chromeos::SystemSaltGetter::Get()->GetSystemSalt(callback
);
49 // Not implemented for other platforms.
51 callback
.Run(std::string());
57 DeviceIDFetcher::DeviceIDFetcher(int render_process_id
)
58 : in_progress_(false), render_process_id_(render_process_id
) {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
62 DeviceIDFetcher::~DeviceIDFetcher() {}
64 bool DeviceIDFetcher::Start(const IDCallback
& callback
) {
65 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
73 BrowserThread::PostTask(
76 base::Bind(&DeviceIDFetcher::CheckPrefsOnUIThread
, this));
81 void DeviceIDFetcher::RegisterProfilePrefs(
82 user_prefs::PrefRegistrySyncable
* prefs
) {
83 prefs
->RegisterBooleanPref(prefs::kEnableDRM
,
85 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
86 prefs
->RegisterStringPref(
87 prefs::kDRMSalt
, "", user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
91 base::FilePath
DeviceIDFetcher::GetLegacyDeviceIDPath(
92 const base::FilePath
& profile_path
) {
93 return profile_path
.AppendASCII(kDRMIdentifierFile
);
96 void DeviceIDFetcher::CheckPrefsOnUIThread() {
97 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
99 Profile
* profile
= NULL
;
100 RenderProcessHost
* render_process_host
=
101 RenderProcessHost::FromID(render_process_id_
);
102 if (render_process_host
&& render_process_host
->GetBrowserContext()) {
104 Profile::FromBrowserContext(render_process_host
->GetBrowserContext());
107 if (!profile
|| profile
->IsOffTheRecord() ||
108 !profile
->GetPrefs()->GetBoolean(prefs::kEnableDRM
)) {
109 RunCallbackOnIOThread(std::string(), PP_ERROR_NOACCESS
);
113 // Check if the salt pref is set. If it isn't, set it.
114 std::string salt
= profile
->GetPrefs()->GetString(prefs::kDRMSalt
);
116 uint8_t salt_bytes
[kSaltLength
];
117 crypto::RandBytes(salt_bytes
, arraysize(salt_bytes
));
118 // Since it will be stored in a string pref, convert it to hex.
119 salt
= base::HexEncode(salt_bytes
, arraysize(salt_bytes
));
120 profile
->GetPrefs()->SetString(prefs::kDRMSalt
, salt
);
123 #if defined(OS_CHROMEOS)
124 // Try the legacy path first for ChromeOS. We pass the new salt in as well
125 // in case the legacy id doesn't exist.
126 BrowserThread::PostBlockingPoolTask(
128 base::Bind(&DeviceIDFetcher::LegacyComputeOnBlockingPool
,
133 // Get the machine ID and call ComputeOnUIThread with salt + machine_id.
135 base::Bind(&DeviceIDFetcher::ComputeOnUIThread
, this, salt
));
139 void DeviceIDFetcher::ComputeOnUIThread(const std::string
& salt
,
140 const std::string
& machine_id
) {
141 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
143 if (machine_id
.empty()) {
144 LOG(ERROR
) << "Empty machine id";
145 RunCallbackOnIOThread(std::string(), PP_ERROR_FAILED
);
149 // Build the identifier as follows:
150 // SHA256(machine-id||service||SHA256(machine-id||service||salt))
151 std::vector
<uint8
> salt_bytes
;
152 if (!base::HexStringToBytes(salt
, &salt_bytes
))
154 if (salt_bytes
.size() != kSaltLength
) {
155 LOG(ERROR
) << "Unexpected salt bytes length: " << salt_bytes
.size();
156 RunCallbackOnIOThread(std::string(), PP_ERROR_FAILED
);
160 char id_buf
[256 / 8]; // 256-bits for SHA256
161 std::string input
= machine_id
;
162 input
.append(kDRMIdentifierFile
);
163 input
.append(salt_bytes
.begin(), salt_bytes
.end());
164 crypto::SHA256HashString(input
, &id_buf
, sizeof(id_buf
));
165 std::string id
= base::StringToLowerASCII(
166 base::HexEncode(reinterpret_cast<const void*>(id_buf
), sizeof(id_buf
)));
168 input
.append(kDRMIdentifierFile
);
170 crypto::SHA256HashString(input
, &id_buf
, sizeof(id_buf
));
171 id
= base::StringToLowerASCII(
172 base::HexEncode(reinterpret_cast<const void*>(id_buf
), sizeof(id_buf
)));
174 RunCallbackOnIOThread(id
, PP_OK
);
177 // TODO(raymes): This is temporary code to migrate ChromeOS devices to the new
178 // scheme for generating device IDs. Delete this once we are sure most ChromeOS
179 // devices have been migrated.
180 void DeviceIDFetcher::LegacyComputeOnBlockingPool(
181 const base::FilePath
& profile_path
,
182 const std::string
& salt
) {
184 // First check if the legacy device ID file exists on ChromeOS. If it does, we
185 // should just return that.
186 base::FilePath id_path
= GetLegacyDeviceIDPath(profile_path
);
187 if (base::PathExists(id_path
)) {
188 if (base::ReadFileToString(id_path
, &id
) && !id
.empty()) {
189 RunCallbackOnIOThread(id
, PP_OK
);
193 // If we didn't find an ID, get the machine ID and call the new code path to
195 BrowserThread::PostTask(
198 base::Bind(&GetMachineIDAsync
,
199 base::Bind(&DeviceIDFetcher::ComputeOnUIThread
, this, salt
)));
202 void DeviceIDFetcher::RunCallbackOnIOThread(const std::string
& id
,
204 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
205 BrowserThread::PostTask(
208 base::Bind(&DeviceIDFetcher::RunCallbackOnIOThread
, this, id
, result
));
211 in_progress_
= false;
212 callback_
.Run(id
, result
);
215 } // namespace chrome