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_CURRENTLY_ON(BrowserThread::IO
);
62 DeviceIDFetcher::~DeviceIDFetcher() {}
64 bool DeviceIDFetcher::Start(const IDCallback
& callback
) {
65 DCHECK_CURRENTLY_ON(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
, true);
84 prefs
->RegisterStringPref(prefs::kDRMSalt
, "");
88 base::FilePath
DeviceIDFetcher::GetLegacyDeviceIDPath(
89 const base::FilePath
& profile_path
) {
90 return profile_path
.AppendASCII(kDRMIdentifierFile
);
93 void DeviceIDFetcher::CheckPrefsOnUIThread() {
94 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
96 Profile
* profile
= NULL
;
97 RenderProcessHost
* render_process_host
=
98 RenderProcessHost::FromID(render_process_id_
);
99 if (render_process_host
&& render_process_host
->GetBrowserContext()) {
101 Profile::FromBrowserContext(render_process_host
->GetBrowserContext());
104 if (!profile
|| profile
->IsOffTheRecord() ||
105 !profile
->GetPrefs()->GetBoolean(prefs::kEnableDRM
)) {
106 RunCallbackOnIOThread(std::string(), PP_ERROR_NOACCESS
);
110 // Check if the salt pref is set. If it isn't, set it.
111 std::string salt
= profile
->GetPrefs()->GetString(prefs::kDRMSalt
);
113 uint8_t salt_bytes
[kSaltLength
];
114 crypto::RandBytes(salt_bytes
, arraysize(salt_bytes
));
115 // Since it will be stored in a string pref, convert it to hex.
116 salt
= base::HexEncode(salt_bytes
, arraysize(salt_bytes
));
117 profile
->GetPrefs()->SetString(prefs::kDRMSalt
, salt
);
120 #if defined(OS_CHROMEOS)
121 // Try the legacy path first for ChromeOS. We pass the new salt in as well
122 // in case the legacy id doesn't exist.
123 BrowserThread::PostBlockingPoolTask(
125 base::Bind(&DeviceIDFetcher::LegacyComputeOnBlockingPool
,
130 // Get the machine ID and call ComputeOnUIThread with salt + machine_id.
132 base::Bind(&DeviceIDFetcher::ComputeOnUIThread
, this, salt
));
136 void DeviceIDFetcher::ComputeOnUIThread(const std::string
& salt
,
137 const std::string
& machine_id
) {
138 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
140 if (machine_id
.empty()) {
141 LOG(ERROR
) << "Empty machine id";
142 RunCallbackOnIOThread(std::string(), PP_ERROR_FAILED
);
146 // Build the identifier as follows:
147 // SHA256(machine-id||service||SHA256(machine-id||service||salt))
148 std::vector
<uint8
> salt_bytes
;
149 if (!base::HexStringToBytes(salt
, &salt_bytes
))
151 if (salt_bytes
.size() != kSaltLength
) {
152 LOG(ERROR
) << "Unexpected salt bytes length: " << salt_bytes
.size();
153 RunCallbackOnIOThread(std::string(), PP_ERROR_FAILED
);
157 char id_buf
[256 / 8]; // 256-bits for SHA256
158 std::string input
= machine_id
;
159 input
.append(kDRMIdentifierFile
);
160 input
.append(salt_bytes
.begin(), salt_bytes
.end());
161 crypto::SHA256HashString(input
, &id_buf
, sizeof(id_buf
));
162 std::string id
= base::StringToLowerASCII(
163 base::HexEncode(reinterpret_cast<const void*>(id_buf
), sizeof(id_buf
)));
165 input
.append(kDRMIdentifierFile
);
167 crypto::SHA256HashString(input
, &id_buf
, sizeof(id_buf
));
168 id
= base::StringToLowerASCII(
169 base::HexEncode(reinterpret_cast<const void*>(id_buf
), sizeof(id_buf
)));
171 RunCallbackOnIOThread(id
, PP_OK
);
174 // TODO(raymes): This is temporary code to migrate ChromeOS devices to the new
175 // scheme for generating device IDs. Delete this once we are sure most ChromeOS
176 // devices have been migrated.
177 void DeviceIDFetcher::LegacyComputeOnBlockingPool(
178 const base::FilePath
& profile_path
,
179 const std::string
& salt
) {
181 // First check if the legacy device ID file exists on ChromeOS. If it does, we
182 // should just return that.
183 base::FilePath id_path
= GetLegacyDeviceIDPath(profile_path
);
184 if (base::PathExists(id_path
)) {
185 if (base::ReadFileToString(id_path
, &id
) && !id
.empty()) {
186 RunCallbackOnIOThread(id
, PP_OK
);
190 // If we didn't find an ID, get the machine ID and call the new code path to
192 BrowserThread::PostTask(
195 base::Bind(&GetMachineIDAsync
,
196 base::Bind(&DeviceIDFetcher::ComputeOnUIThread
, this, salt
)));
199 void DeviceIDFetcher::RunCallbackOnIOThread(const std::string
& id
,
201 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
202 BrowserThread::PostTask(
205 base::Bind(&DeviceIDFetcher::RunCallbackOnIOThread
, this, id
, result
));
208 in_progress_
= false;
209 callback_
.Run(id
, result
);
212 } // namespace chrome