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// Generating a fingerprint consists of two major steps:
6//   (1) Gather all the necessary data.
7//   (2) Write it into a protocol buffer.
8//
9// Step (2) is as simple as it sounds -- it's really just a matter of copying
10// data.  Step (1) requires waiting on several asynchronous callbacks, which are
11// managed by the FingerprintDataLoader class.
12
13#include "components/autofill/content/browser/risk/fingerprint.h"
14
15#include "base/bind.h"
16#include "base/callback.h"
17#include "base/cpu.h"
18#include "base/logging.h"
19#include "base/memory/weak_ptr.h"
20#include "base/scoped_observer.h"
21#include "base/strings/string_split.h"
22#include "base/strings/utf_string_conversions.h"
23#include "base/sys_info.h"
24#include "base/time/time.h"
25#include "base/timer/timer.h"
26#include "base/values.h"
27#include "components/autofill/content/browser/risk/proto/fingerprint.pb.h"
28#include "content/public/browser/browser_thread.h"
29#include "content/public/browser/font_list_async.h"
30#include "content/public/browser/geolocation_provider.h"
31#include "content/public/browser/gpu_data_manager.h"
32#include "content/public/browser/gpu_data_manager_observer.h"
33#include "content/public/browser/plugin_service.h"
34#include "content/public/browser/render_widget_host.h"
35#include "content/public/browser/render_widget_host_view.h"
36#include "content/public/browser/web_contents.h"
37#include "content/public/common/geoposition.h"
38#include "content/public/common/webplugininfo.h"
39#include "gpu/config/gpu_info.h"
40#include "third_party/WebKit/public/platform/WebRect.h"
41#include "third_party/WebKit/public/platform/WebScreenInfo.h"
42#include "ui/gfx/rect.h"
43#include "ui/gfx/screen.h"
44
45using blink::WebScreenInfo;
46
47namespace autofill {
48namespace risk {
49
50namespace {
51
52const int32 kFingerprinterVersion = 1;
53
54// Maximum amount of time, in seconds, to wait for loading asynchronous
55// fingerprint data.
56const int kTimeoutSeconds = 4;
57
58// Returns the delta between the local timezone and UTC.
59base::TimeDelta GetTimezoneOffset() {
60  const base::Time utc = base::Time::Now();
61
62  base::Time::Exploded local;
63  utc.LocalExplode(&local);
64
65  return base::Time::FromUTCExploded(local) - utc;
66}
67
68// Returns the concatenation of the operating system name and version, e.g.
69// "Mac OS X 10.6.8".
70std::string GetOperatingSystemVersion() {
71  return base::SysInfo::OperatingSystemName() + " " +
72      base::SysInfo::OperatingSystemVersion();
73}
74
75// Adds the list of |fonts| to the |machine|.
76void AddFontsToFingerprint(const base::ListValue& fonts,
77                           Fingerprint::MachineCharacteristics* machine) {
78  for (base::ListValue::const_iterator it = fonts.begin();
79       it != fonts.end(); ++it) {
80    // Each item in the list is a two-element list such that the first element
81    // is the font family and the second is the font name.
82    const base::ListValue* font_description = NULL;
83    bool success = (*it)->GetAsList(&font_description);
84    DCHECK(success);
85
86    std::string font_name;
87    success = font_description->GetString(1, &font_name);
88    DCHECK(success);
89
90    machine->add_font(font_name);
91  }
92}
93
94// Adds the list of |plugins| to the |machine|.
95void AddPluginsToFingerprint(const std::vector<content::WebPluginInfo>& plugins,
96                             Fingerprint::MachineCharacteristics* machine) {
97  for (std::vector<content::WebPluginInfo>::const_iterator it = plugins.begin();
98       it != plugins.end(); ++it) {
99    Fingerprint::MachineCharacteristics::Plugin* plugin =
100        machine->add_plugin();
101    plugin->set_name(base::UTF16ToUTF8(it->name));
102    plugin->set_description(base::UTF16ToUTF8(it->desc));
103    for (std::vector<content::WebPluginMimeType>::const_iterator mime_type =
104             it->mime_types.begin();
105         mime_type != it->mime_types.end(); ++mime_type) {
106      plugin->add_mime_type(mime_type->mime_type);
107    }
108    plugin->set_version(base::UTF16ToUTF8(it->version));
109  }
110}
111
112// Adds the list of HTTP accept languages to the |machine|.
113void AddAcceptLanguagesToFingerprint(
114    const std::string& accept_languages_str,
115    Fingerprint::MachineCharacteristics* machine) {
116  std::vector<std::string> accept_languages;
117  base::SplitString(accept_languages_str, ',', &accept_languages);
118  for (std::vector<std::string>::const_iterator it = accept_languages.begin();
119       it != accept_languages.end(); ++it) {
120    machine->add_requested_language(*it);
121  }
122}
123
124// This function writes
125//   (a) the number of screens,
126//   (b) the primary display's screen size,
127//   (c) the screen's color depth, and
128//   (d) the size of the screen unavailable to web page content,
129//       i.e. the Taskbar size on Windows
130// into the |machine|.
131void AddScreenInfoToFingerprint(const WebScreenInfo& screen_info,
132                                Fingerprint::MachineCharacteristics* machine) {
133  // TODO(scottmg): NativeScreen maybe wrong. http://crbug.com/133312
134  machine->set_screen_count(
135      gfx::Screen::GetNativeScreen()->GetNumDisplays());
136
137  const gfx::Size screen_size =
138      gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().GetSizeInPixel();
139  machine->mutable_screen_size()->set_width(screen_size.width());
140  machine->mutable_screen_size()->set_height(screen_size.height());
141
142  machine->set_screen_color_depth(screen_info.depth);
143
144  const gfx::Rect screen_rect(screen_info.rect);
145  const gfx::Rect available_rect(screen_info.availableRect);
146  const gfx::Rect unavailable_rect =
147      gfx::SubtractRects(screen_rect, available_rect);
148  machine->mutable_unavailable_screen_size()->set_width(
149      unavailable_rect.width());
150  machine->mutable_unavailable_screen_size()->set_height(
151      unavailable_rect.height());
152}
153
154// Writes info about the machine's CPU into the |machine|.
155void AddCpuInfoToFingerprint(Fingerprint::MachineCharacteristics* machine) {
156  base::CPU cpu;
157  machine->mutable_cpu()->set_vendor_name(cpu.vendor_name());
158  machine->mutable_cpu()->set_brand(cpu.cpu_brand());
159}
160
161// Writes info about the machine's GPU into the |machine|.
162void AddGpuInfoToFingerprint(Fingerprint::MachineCharacteristics* machine,
163                             const content::GpuDataManager& gpu_data_manager) {
164  if (!gpu_data_manager.IsEssentialGpuInfoAvailable())
165    return;
166
167  const gpu::GPUInfo gpu_info = gpu_data_manager.GetGPUInfo();
168
169  Fingerprint::MachineCharacteristics::Graphics* graphics =
170      machine->mutable_graphics_card();
171  graphics->set_vendor_id(gpu_info.gpu.vendor_id);
172  graphics->set_device_id(gpu_info.gpu.device_id);
173  graphics->set_driver_version(gpu_info.driver_version);
174  graphics->set_driver_date(gpu_info.driver_date);
175
176  Fingerprint::MachineCharacteristics::Graphics::PerformanceStatistics*
177      gpu_performance = graphics->mutable_performance_statistics();
178  gpu_performance->set_graphics_score(gpu_info.performance_stats.graphics);
179  gpu_performance->set_gaming_score(gpu_info.performance_stats.gaming);
180  gpu_performance->set_overall_score(gpu_info.performance_stats.overall);
181}
182
183// Waits for all asynchronous data required for the fingerprint to be loaded,
184// then fills out the fingerprint.
185class FingerprintDataLoader : public content::GpuDataManagerObserver {
186 public:
187  FingerprintDataLoader(
188      uint64 obfuscated_gaia_id,
189      const gfx::Rect& window_bounds,
190      const gfx::Rect& content_bounds,
191      const WebScreenInfo& screen_info,
192      const std::string& version,
193      const std::string& charset,
194      const std::string& accept_languages,
195      const base::Time& install_time,
196      const std::string& app_locale,
197      const std::string& user_agent,
198      const base::TimeDelta& timeout,
199      const base::Callback<void(scoped_ptr<Fingerprint>)>& callback);
200
201 private:
202  virtual ~FingerprintDataLoader() {}
203
204  // content::GpuDataManagerObserver:
205  virtual void OnGpuInfoUpdate() OVERRIDE;
206
207  // Callbacks for asynchronously loaded data.
208  void OnGotFonts(scoped_ptr<base::ListValue> fonts);
209  void OnGotPlugins(const std::vector<content::WebPluginInfo>& plugins);
210  void OnGotGeoposition(const content::Geoposition& geoposition);
211
212  // If all of the asynchronous data has been loaded, calls |callback_| with
213  // the fingerprint data.
214  void MaybeFillFingerprint();
215
216  // Calls |callback_| with the fingerprint data.
217  void FillFingerprint();
218
219  // The GPU data provider.
220  // Weak reference because the GpuDataManager class is a singleton.
221  content::GpuDataManager* const gpu_data_manager_;
222
223  // Ensures that any observer registrations for the GPU data are cleaned up by
224  // the time this object is destroyed.
225  ScopedObserver<content::GpuDataManager, FingerprintDataLoader> gpu_observer_;
226
227  // Data that will be passed on to the next loading phase.  See the comment for
228  // GetFingerprint() for a description of these variables.
229  const uint64 obfuscated_gaia_id_;
230  const gfx::Rect window_bounds_;
231  const gfx::Rect content_bounds_;
232  const WebScreenInfo screen_info_;
233  const std::string version_;
234  const std::string charset_;
235  const std::string accept_languages_;
236  const std::string app_locale_;
237  const std::string user_agent_;
238  const base::Time install_time_;
239
240  // Data that will be loaded asynchronously.
241  scoped_ptr<base::ListValue> fonts_;
242  std::vector<content::WebPluginInfo> plugins_;
243  bool waiting_on_plugins_;
244  content::Geoposition geoposition_;
245
246  // Timer to enforce a maximum timeout before the |callback_| is called, even
247  // if not all asynchronous data has been loaded.
248  base::OneShotTimer<FingerprintDataLoader> timeout_timer_;
249
250  // For invalidating asynchronous callbacks that might arrive after |this|
251  // instance is destroyed.
252  base::WeakPtrFactory<FingerprintDataLoader> weak_ptr_factory_;
253
254  // The callback that will be called once all the data is available.
255  base::Callback<void(scoped_ptr<Fingerprint>)> callback_;
256
257  // The callback used as an "observer" of the GeolocationProvider.
258  scoped_ptr<content::GeolocationProvider::Subscription>
259      geolocation_subscription_;
260
261  DISALLOW_COPY_AND_ASSIGN(FingerprintDataLoader);
262};
263
264FingerprintDataLoader::FingerprintDataLoader(
265    uint64 obfuscated_gaia_id,
266    const gfx::Rect& window_bounds,
267    const gfx::Rect& content_bounds,
268    const WebScreenInfo& screen_info,
269    const std::string& version,
270    const std::string& charset,
271    const std::string& accept_languages,
272    const base::Time& install_time,
273    const std::string& app_locale,
274    const std::string& user_agent,
275    const base::TimeDelta& timeout,
276    const base::Callback<void(scoped_ptr<Fingerprint>)>& callback)
277    : gpu_data_manager_(content::GpuDataManager::GetInstance()),
278      gpu_observer_(this),
279      obfuscated_gaia_id_(obfuscated_gaia_id),
280      window_bounds_(window_bounds),
281      content_bounds_(content_bounds),
282      screen_info_(screen_info),
283      version_(version),
284      charset_(charset),
285      accept_languages_(accept_languages),
286      app_locale_(app_locale),
287      user_agent_(user_agent),
288      install_time_(install_time),
289      waiting_on_plugins_(true),
290      weak_ptr_factory_(this),
291      callback_(callback) {
292  DCHECK(!install_time_.is_null());
293
294  timeout_timer_.Start(FROM_HERE, timeout,
295                       base::Bind(&FingerprintDataLoader::MaybeFillFingerprint,
296                                  weak_ptr_factory_.GetWeakPtr()));
297
298  // Load GPU data if needed.
299  if (gpu_data_manager_->GpuAccessAllowed(NULL) &&
300      !gpu_data_manager_->IsEssentialGpuInfoAvailable()) {
301    gpu_observer_.Add(gpu_data_manager_);
302    gpu_data_manager_->RequestCompleteGpuInfoIfNeeded();
303  }
304
305#if defined(ENABLE_PLUGINS)
306  // Load plugin data.
307  content::PluginService::GetInstance()->GetPlugins(
308      base::Bind(&FingerprintDataLoader::OnGotPlugins,
309                 weak_ptr_factory_.GetWeakPtr()));
310#else
311  waiting_on_plugins_ = false;
312#endif
313
314  // Load font data.
315  content::GetFontListAsync(
316      base::Bind(&FingerprintDataLoader::OnGotFonts,
317                 weak_ptr_factory_.GetWeakPtr()));
318
319  // Load geolocation data.
320  geolocation_subscription_ = content::GeolocationProvider::GetInstance()->
321      AddLocationUpdateCallback(
322          base::Bind(&FingerprintDataLoader::OnGotGeoposition,
323                      weak_ptr_factory_.GetWeakPtr()),
324          false);
325}
326
327void FingerprintDataLoader::OnGpuInfoUpdate() {
328  if (!gpu_data_manager_->IsEssentialGpuInfoAvailable())
329    return;
330
331  gpu_observer_.Remove(gpu_data_manager_);
332  MaybeFillFingerprint();
333}
334
335void FingerprintDataLoader::OnGotFonts(scoped_ptr<base::ListValue> fonts) {
336  DCHECK(!fonts_);
337  fonts_.reset(fonts.release());
338  MaybeFillFingerprint();
339}
340
341void FingerprintDataLoader::OnGotPlugins(
342    const std::vector<content::WebPluginInfo>& plugins) {
343  DCHECK(waiting_on_plugins_);
344  waiting_on_plugins_ = false;
345  plugins_ = plugins;
346  MaybeFillFingerprint();
347}
348
349void FingerprintDataLoader::OnGotGeoposition(
350    const content::Geoposition& geoposition) {
351  DCHECK(!geoposition_.Validate());
352
353  geoposition_ = geoposition;
354  DCHECK(geoposition_.Validate() ||
355         geoposition_.error_code != content::Geoposition::ERROR_CODE_NONE);
356  geolocation_subscription_.reset();
357
358  MaybeFillFingerprint();
359}
360
361void FingerprintDataLoader::MaybeFillFingerprint() {
362  // If all of the data has been loaded, or if the |timeout_timer_| has expired,
363  // fill the fingerprint and clean up.
364  if (!timeout_timer_.IsRunning() ||
365      ((!gpu_data_manager_->GpuAccessAllowed(NULL) ||
366        gpu_data_manager_->IsEssentialGpuInfoAvailable()) &&
367       fonts_ &&
368       !waiting_on_plugins_ &&
369       (geoposition_.Validate() ||
370        geoposition_.error_code != content::Geoposition::ERROR_CODE_NONE))) {
371    FillFingerprint();
372    delete this;
373  }
374}
375
376void FingerprintDataLoader::FillFingerprint() {
377  scoped_ptr<Fingerprint> fingerprint(new Fingerprint);
378  Fingerprint::MachineCharacteristics* machine =
379      fingerprint->mutable_machine_characteristics();
380
381  machine->set_operating_system_build(GetOperatingSystemVersion());
382  // We use the delta between the install time and the Unix epoch, in hours.
383  machine->set_browser_install_time_hours(
384      (install_time_ - base::Time::UnixEpoch()).InHours());
385  machine->set_utc_offset_ms(GetTimezoneOffset().InMilliseconds());
386  machine->set_browser_language(app_locale_);
387  machine->set_charset(charset_);
388  machine->set_user_agent(user_agent_);
389  machine->set_ram(base::SysInfo::AmountOfPhysicalMemory());
390  machine->set_browser_build(version_);
391  machine->set_browser_feature(
392      Fingerprint::MachineCharacteristics::FEATURE_REQUEST_AUTOCOMPLETE);
393  if (fonts_)
394    AddFontsToFingerprint(*fonts_, machine);
395  AddPluginsToFingerprint(plugins_, machine);
396  AddAcceptLanguagesToFingerprint(accept_languages_, machine);
397  AddScreenInfoToFingerprint(screen_info_, machine);
398  AddCpuInfoToFingerprint(machine);
399  AddGpuInfoToFingerprint(machine, *gpu_data_manager_);
400
401  // TODO(isherman): Record the user_and_device_name_hash.
402  // TODO(isherman): Record the partition size of the hard drives?
403
404  Fingerprint::TransientState* transient_state =
405      fingerprint->mutable_transient_state();
406  Fingerprint::Dimension* inner_window_size =
407      transient_state->mutable_inner_window_size();
408  inner_window_size->set_width(content_bounds_.width());
409  inner_window_size->set_height(content_bounds_.height());
410  Fingerprint::Dimension* outer_window_size =
411      transient_state->mutable_outer_window_size();
412  outer_window_size->set_width(window_bounds_.width());
413  outer_window_size->set_height(window_bounds_.height());
414
415  // TODO(isherman): Record network performance data, which is theoretically
416  // available to JS.
417
418  // TODO(isherman): Record more user behavior data.
419  if (geoposition_.Validate() &&
420      geoposition_.error_code == content::Geoposition::ERROR_CODE_NONE) {
421    Fingerprint::UserCharacteristics::Location* location =
422        fingerprint->mutable_user_characteristics()->mutable_location();
423    location->set_altitude(geoposition_.altitude);
424    location->set_latitude(geoposition_.latitude);
425    location->set_longitude(geoposition_.longitude);
426    location->set_accuracy(geoposition_.accuracy);
427    location->set_time_in_ms(
428        (geoposition_.timestamp - base::Time::UnixEpoch()).InMilliseconds());
429  }
430
431  Fingerprint::Metadata* metadata = fingerprint->mutable_metadata();
432  metadata->set_timestamp_ms(
433      (base::Time::Now() - base::Time::UnixEpoch()).InMilliseconds());
434  metadata->set_obfuscated_gaia_id(obfuscated_gaia_id_);
435  metadata->set_fingerprinter_version(kFingerprinterVersion);
436
437  callback_.Run(fingerprint.Pass());
438}
439
440}  // namespace
441
442namespace internal {
443
444void GetFingerprintInternal(
445    uint64 obfuscated_gaia_id,
446    const gfx::Rect& window_bounds,
447    const gfx::Rect& content_bounds,
448    const blink::WebScreenInfo& screen_info,
449    const std::string& version,
450    const std::string& charset,
451    const std::string& accept_languages,
452    const base::Time& install_time,
453    const std::string& app_locale,
454    const std::string& user_agent,
455    const base::TimeDelta& timeout,
456    const base::Callback<void(scoped_ptr<Fingerprint>)>& callback) {
457  // Begin loading all of the data that we need to load asynchronously.
458  // This class is responsible for freeing its own memory.
459  new FingerprintDataLoader(obfuscated_gaia_id, window_bounds, content_bounds,
460                            screen_info, version, charset, accept_languages,
461                            install_time, app_locale, user_agent, timeout,
462                            callback);
463}
464
465}  // namespace internal
466
467void GetFingerprint(
468    uint64 obfuscated_gaia_id,
469    const gfx::Rect& window_bounds,
470    content::WebContents* web_contents,
471    const std::string& version,
472    const std::string& charset,
473    const std::string& accept_languages,
474    const base::Time& install_time,
475    const std::string& app_locale,
476    const std::string& user_agent,
477    const base::Callback<void(scoped_ptr<Fingerprint>)>& callback) {
478  gfx::Rect content_bounds = web_contents->GetContainerBounds();
479
480  blink::WebScreenInfo screen_info;
481  content::RenderWidgetHostView* host_view =
482      web_contents->GetRenderWidgetHostView();
483  if (host_view)
484    host_view->GetRenderWidgetHost()->GetWebScreenInfo(&screen_info);
485
486  internal::GetFingerprintInternal(
487      obfuscated_gaia_id, window_bounds, content_bounds, screen_info, version,
488      charset, accept_languages, install_time, app_locale, user_agent,
489      base::TimeDelta::FromSeconds(kTimeoutSeconds), callback);
490}
491
492}  // namespace risk
493}  // namespace autofill
494