From: csagan5 <32685696+csagan5@users.noreply.github.com>
Date: Fri, 30 Mar 2018 10:09:03 +0200
Subject: getClientRects, getBoundingClientRect, measureText: add
 fingerprinting mitigation

Scale the result of Range::getClientRects, Element::getBoundingClientRect and
Canvas::measureText by a random +/-3/1000000th of the original value for each
float in the returned Rect/Quad.

Rationale is that the returned values are within the same order of magnitude
of the floating point precision being used for fingerprinting and sufficient
to poison the well.

See also: http://www.gsd.inesc-id.pt/~mpc/pubs/fingerprinting-trustcom2016.pdf
---
 third_party/blink/renderer/core/dom/document.cc        | 13 +++++++++++++
 third_party/blink/renderer/core/dom/document.h         |  5 +++++
 third_party/blink/renderer/core/dom/element.cc         | 16 ++++++++++++++++
 third_party/blink/renderer/core/dom/range.cc           | 18 +++++++++++++++++-
 .../blink/renderer/core/html/canvas/text_metrics.cc    | 18 ++++++++++++++++++
 .../blink/renderer/core/html/canvas/text_metrics.h     |  2 ++
 .../canvas/canvas2d/canvas_rendering_context_2d.cc     | 10 +++++++++-
 7 files changed, 80 insertions(+), 2 deletions(-)

diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -34,6 +34,7 @@
 #include "base/auto_reset.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "base/rand_util.h"
 #include "cc/input/overscroll_behavior.h"
 #include "cc/input/scroll_snap_data.h"
 #include "services/metrics/public/cpp/mojo_ukm_recorder.h"
@@ -738,6 +739,10 @@ Document::Document(const DocumentInit& initializer,
   }
   DCHECK(fetcher_);
 
+  // add X/Y noise factors that will be used to mitigate fingerprinting
+  shuffleFactorX_ = base::RandDouble();
+  shuffleFactorY_ = base::RandDouble();
+
   root_scroller_controller_ = RootScrollerController::Create(*this);
 
   // We depend on the url getting immediately set in subframes, but we
@@ -803,6 +808,14 @@ Range* Document::CreateRangeAdjustedToTreeScope(const TreeScope& tree_scope,
                        Position::BeforeNode(*shadow_host));
 }
 
+double Document::GetShuffleFactorX() {
+	return shuffleFactorX_;
+}
+
+double Document::GetShuffleFactorY() {
+	return shuffleFactorY_;
+}
+
 SelectorQueryCache& Document::GetSelectorQueryCache() {
   if (!selector_query_cache_)
     selector_query_cache_ = std::make_unique<SelectorQueryCache>();
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -393,6 +393,9 @@ class CORE_EXPORT Document : public ContainerNode,
     has_xml_declaration_ = has_xml_declaration ? 1 : 0;
   }
 
+  double GetShuffleFactorX();
+  double GetShuffleFactorY();
+
   String visibilityState() const;
   bool IsPageVisible() const;
   bool hidden() const;
@@ -1815,6 +1818,8 @@ class CORE_EXPORT Document : public ContainerNode,
 
   double start_time_;
 
+  double shuffleFactorX_, shuffleFactorY_;
+
   TraceWrapperMember<ScriptRunner> script_runner_;
 
   HeapVector<Member<ScriptElementBase>> current_script_stack_;
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -1222,6 +1222,15 @@ DOMRectList* Element::getClientRects() {
   DCHECK(element_layout_object);
   GetDocument().AdjustFloatQuadsForScrollAndAbsoluteZoom(
       quads, *element_layout_object);
+
+  // scale all quads
+  auto shuffleX = 1 + (GetDocument().GetShuffleFactorX() - 0.5) * 0.000003;
+  auto shuffleY = 1 + (GetDocument().GetShuffleFactorY() - 0.5) * 0.000003;
+
+  for (FloatQuad& quad : quads) {
+    quad.Scale(shuffleX, shuffleY);
+  }
+
   return DOMRectList::Create(quads);
 }
 
@@ -1239,6 +1248,13 @@ DOMRect* Element::getBoundingClientRect() {
   DCHECK(element_layout_object);
   GetDocument().AdjustFloatRectForScrollAndAbsoluteZoom(result,
                                                         *element_layout_object);
+
+  // scale rect by 3/1000000th
+  auto shuffleX = 1 + (GetDocument().GetShuffleFactorX() - 0.5) * 0.000003;
+  auto shuffleY = 1 + (GetDocument().GetShuffleFactorY() - 0.5) * 0.000003;
+
+  result.Scale(shuffleX, shuffleY);
+
   return DOMRect::FromFloatRect(result);
 }
 
diff --git a/third_party/blink/renderer/core/dom/range.cc b/third_party/blink/renderer/core/dom/range.cc
--- a/third_party/blink/renderer/core/dom/range.cc
+++ b/third_party/blink/renderer/core/dom/range.cc
@@ -1642,11 +1642,27 @@ DOMRectList* Range::getClientRects() const {
   Vector<FloatQuad> quads;
   GetBorderAndTextQuads(quads);
 
+  // scale all quads by 3/1000000th
+  auto shuffleX = 1 + (owner_document_->GetShuffleFactorX() - 0.5) * 0.000003;
+  auto shuffleY = 1 + (owner_document_->GetShuffleFactorY() - 0.5) * 0.000003;
+
+  for (FloatQuad& quad : quads) {
+    quad.Scale(shuffleX, shuffleY);
+  }
+
   return DOMRectList::Create(quads);
 }
 
 DOMRect* Range::getBoundingClientRect() const {
-  return DOMRect::FromFloatRect(BoundingRect());
+  auto rect = BoundingRect();
+
+  // scale rect by 3/1000000th
+  auto shuffleX = 1 + (owner_document_->GetShuffleFactorX() - 0.5) * 0.000003;
+  auto shuffleY = 1 + (owner_document_->GetShuffleFactorY() - 0.5) * 0.000003;
+
+  rect.Scale(shuffleX, shuffleY);
+
+  return DOMRect::FromFloatRect(rect);
 }
 
 // TODO(editing-dev): We should make
diff --git a/third_party/blink/renderer/core/html/canvas/text_metrics.cc b/third_party/blink/renderer/core/html/canvas/text_metrics.cc
--- a/third_party/blink/renderer/core/html/canvas/text_metrics.cc
+++ b/third_party/blink/renderer/core/html/canvas/text_metrics.cc
@@ -45,6 +45,24 @@ void TextMetrics::Trace(Visitor* visitor) {
 
 TextMetrics::TextMetrics() : baselines_(Baselines::Create()) {}
 
+void TextMetrics::Shuffle(const double factor) {
+  // x-direction
+  width_ *= factor;
+  actual_bounding_box_left_ *= factor;
+  actual_bounding_box_right_ *= factor;
+
+  // y-direction
+  font_bounding_box_ascent_ *= factor;
+  font_bounding_box_descent_ *= factor;
+  actual_bounding_box_ascent_ *= factor;
+  actual_bounding_box_descent_ *= factor;
+  em_height_ascent_ *= factor;
+  em_height_descent_ *= factor;
+  baselines_->setAlphabetic(baselines_->alphabetic() * factor);
+  baselines_->setHanging(baselines_->hanging() * factor);
+  baselines_->setIdeographic(baselines_->ideographic() * factor);
+}
+
 void TextMetrics::Update(const Font& font,
                          const TextDirection& direction,
                          const TextBaseline& baseline,
diff --git a/third_party/blink/renderer/core/html/canvas/text_metrics.h b/third_party/blink/renderer/core/html/canvas/text_metrics.h
--- a/third_party/blink/renderer/core/html/canvas/text_metrics.h
+++ b/third_party/blink/renderer/core/html/canvas/text_metrics.h
@@ -71,6 +71,8 @@ class CORE_EXPORT TextMetrics final : public ScriptWrappable {
 
   void Trace(Visitor*) override;
 
+  void Shuffle(const double factor);
+
  private:
   void Update(const Font&,
               const TextDirection&,
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -33,6 +33,8 @@
 
 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h"
 
+#include "base/rand_util.h"
+
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
@@ -793,8 +795,14 @@ TextMetrics* CanvasRenderingContext2D::measureText(const String& text) {
   else
     direction = ToTextDirection(GetState().GetDirection(), canvas());
 
-  return TextMetrics::Create(font, direction, GetState().GetTextBaseline(),
+  TextMetrics* textMetrics = TextMetrics::Create(font, direction, GetState().GetTextBaseline(),
                              GetState().GetTextAlign(), text);
+
+  // scale text metrics by 3/1000000th
+  auto shuffleFactor = 1 + (base::RandDouble() - 0.5) * 0.000003;
+  textMetrics->Shuffle(shuffleFactor);
+
+  return textMetrics;
 }
 
 void CanvasRenderingContext2D::DrawTextInternal(
-- 
2.11.0

