[tor-commits] [tor-browser/tor-browser-38.1.0esr-5.x-1] Bug 15646: Prevent keyboard layout fingerprinting in KeyboardEvent

mikeperry at torproject.org mikeperry at torproject.org
Thu Jun 25 23:21:47 UTC 2015


commit d888d0afb8e1f2770cc9420387ff031eb584e416
Author: Arthur Edelstein <arthuredelstein at gmail.com>
Date:   Sat Jun 13 19:27:43 2015 -0700

    Bug 15646: Prevent keyboard layout fingerprinting in KeyboardEvent
---
 dom/base/nsContentUtils.cpp   |    4 +
 dom/base/nsContentUtils.h     |   13 ++-
 dom/events/KeyCodeConsensus.h |  189 +++++++++++++++++++++++++++++++++++++++++
 dom/events/KeyboardEvent.cpp  |   68 ++++++++++++---
 4 files changed, 262 insertions(+), 12 deletions(-)

diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index 8899c3a..2cecc01 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -247,6 +247,7 @@ bool nsContentUtils::sIsResourceTimingEnabled = false;
 bool nsContentUtils::sIsUserTimingLoggingEnabled = false;
 bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false;
 bool nsContentUtils::sEncodeDecodeURLHash = false;
+bool nsContentUtils::sPrivacyResistFingerprinting = false;
 
 uint32_t nsContentUtils::sHandlingInputTimeout = 1000;
 
@@ -525,6 +526,9 @@ nsContentUtils::Init()
   Preferences::AddBoolVarCache(&sEncodeDecodeURLHash,
                                "dom.url.encode_decode_hash", false);
 
+  Preferences::AddBoolVarCache(&sPrivacyResistFingerprinting,
+                               "privacy.resistFingerprinting", false);
+
   Preferences::AddUintVarCache(&sHandlingInputTimeout,
                                "dom.event.handling-user-input-time-limit",
                                1000);
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
index 9785005..5fed502 100644
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1911,6 +1911,16 @@ public:
     return sEncodeDecodeURLHash;
   }
 
+  /*
+   * Returns true if the browser should attempt to prevent content scripts
+   * from collecting distinctive information about the browser that could
+   * be used to "fingerprint" and track the user across websites.
+   */
+  static bool ResistFingerprinting()
+  {
+    return sPrivacyResistFingerprinting;
+  }
+
   /**
    * Returns true if the doc tree branch which contains aDoc contains any
    * plugins which we don't control event dispatch for, i.e. do any plugins
@@ -2385,7 +2395,8 @@ private:
   static bool sIsUserTimingLoggingEnabled;
   static bool sIsExperimentalAutocompleteEnabled;
   static bool sEncodeDecodeURLHash;
-
+  static bool sPrivacyResistFingerprinting;
+  
   static nsHtml5StringParser* sHTMLFragmentParser;
   static nsIParser* sXMLFragmentParser;
   static nsIFragmentContentSink* sXMLFragmentSink;
diff --git a/dom/events/KeyCodeConsensus.h b/dom/events/KeyCodeConsensus.h
new file mode 100644
index 0000000..8d66d19
--- /dev/null
+++ b/dom/events/KeyCodeConsensus.h
@@ -0,0 +1,189 @@
+// See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode
+
+// When privacy.resistFingerprinting is active, we hide the user's use of
+// the numpad, right modifer keys, and any non-QWERTY US English keyboard.
+
+#include "nsString.h"
+#include "nsClassHashTable.h"
+
+// KEY_INTERNAL is called by KEY or SHIFT.
+#define KEY_INTERNAL(key, code, keyCode, shift)                    \
+    gCodes->Put(NS_LITERAL_STRING(key), NS_LITERAL_STRING(#code));  \
+    gKeyCodes->Put(NS_LITERAL_STRING(key), keyCode);                \
+    gShiftStates->Put(NS_LITERAL_STRING(key), shift);
+
+// KEY and SHIFT Assign a consensus codeName and keyCode for the given keyName.
+// KEY indicates that shift is off.
+#define KEY(key, code, keyCode) KEY_INTERNAL(key, code, keyCode, false)
+// SHIFT indicates that shift is on.
+#define SHIFT(key, code, keyCode) KEY_INTERNAL(key, code, keyCode, true)
+
+// Three global constant static maps.
+// gCodes provides a codeName for each keyName.
+static nsDataHashtable<nsStringHashKey, nsString>* gCodes;
+// gKeyCodes provides a keyCode for each keyName.
+static nsDataHashtable<nsStringHashKey, uint32_t>* gKeyCodes;
+// gShiftStates provides a shift value for each keyName.
+static nsDataHashtable<nsStringHashKey, bool>* gShiftStates;
+
+// Populate the global static maps gCodes, gKeCodes, gShiftStates
+// with their constant values.
+static void createKeyCodes()
+{
+  if (gCodes) return;
+
+  gCodes = new nsDataHashtable<nsStringHashKey, nsString>();
+  gKeyCodes = new nsDataHashtable<nsStringHashKey, uint32_t>();
+  gShiftStates = new nsDataHashtable<nsStringHashKey, bool>();
+
+  KEY("AltLeft", AltLeft, 18)
+  KEY("AltRight", AltRight, 18)
+  KEY("CapsLock", CapsLock, 20)
+  KEY("Control", ControlLeft, 17)
+  KEY("Meta", OSLeft, 91)
+  KEY("Shift", ShiftLeft, 16)
+  KEY("ContextMenu", ContextMenu, 93)
+  KEY("Enter", Enter, 13)
+  KEY(" ", Space, 32)
+  KEY("Tab", Tab, 9)
+  KEY("Delete", Delete, 46)
+  KEY("End", End, 35)
+  KEY("Help", Help, 6)
+  KEY("Home", Home, 36)
+  KEY("Insert", Insert, 45)
+  KEY("PageDown", PageDown, 34)
+  KEY("PageUp", PageUp, 33)
+  KEY("ArrowDown", ArrowDown, 40)
+  KEY("ArrowLeft", ArrowLeft, 37)
+  KEY("ArrowRight", ArrowRight, 39)
+  KEY("ArrowUp", ArrowUp, 38)
+  KEY("Escape", Escape, 27)
+  KEY("PrintScreen", PrintScreen, 44)
+  KEY("ScrollLock", ScrollLock, 145)
+  KEY("Pause", Pause, 19)
+
+  KEY(",", Comma, 188)
+  SHIFT("<", Comma, 188)
+  KEY(".", Period, 190)
+  SHIFT(">", Period, 190)
+  KEY("/", Slash, 191)
+  SHIFT("?", Slash, 191)
+  KEY(";", Semicolon, 59)
+  SHIFT(":", Semicolon, 59)
+  KEY("'", Quote, 222)
+  SHIFT("\"", Quote, 222)
+  KEY("[", BracketLeft, 219)
+  SHIFT("{", BracketLeft, 219)
+  KEY("]", BracketRight, 221)
+  SHIFT("}", BracketRight, 221)
+  KEY("`", Backquote, 192)
+  SHIFT("~", Backquote, 192)
+  KEY("\\", Backslash, 220)
+  SHIFT("|", Backslash, 220)
+  KEY("-", Minus, 173)
+  SHIFT("_", Minus, 173)
+  KEY("=", Equal, 61)
+  SHIFT("+", Equal, 61)
+
+  SHIFT("A", KeyA, 65)
+  SHIFT("B", KeyB, 66)
+  SHIFT("C", KeyC, 67)
+  SHIFT("D", KeyD, 68)
+  SHIFT("E", KeyE, 69)
+  SHIFT("F", KeyF, 70)
+  SHIFT("G", KeyG, 71)
+  SHIFT("H", KeyH, 72)
+  SHIFT("I", KeyI, 73)
+  SHIFT("J", KeyJ, 74)
+  SHIFT("K", KeyK, 75)
+  SHIFT("L", KeyL, 76)
+  SHIFT("M", KeyM, 77)
+  SHIFT("N", KeyN, 78)
+  SHIFT("O", KeyO, 79)
+  SHIFT("P", KeyP, 80)
+  SHIFT("Q", KeyQ, 81)
+  SHIFT("R", KeyR, 82)
+  SHIFT("S", KeyS, 83)
+  SHIFT("T", KeyT, 84)
+  SHIFT("U", KeyU, 85)
+  SHIFT("V", KeyV, 86)
+  SHIFT("W", KeyW, 87)
+  SHIFT("X", KeyX, 88)
+  SHIFT("Y", KeyY, 89)
+  SHIFT("Z", KeyZ, 90)
+
+  KEY("a", KeyA, 65)
+  KEY("b", KeyB, 66)
+  KEY("c", KeyC, 67)
+  KEY("d", KeyD, 68)
+  KEY("e", KeyE, 69)
+  KEY("f", KeyF, 70)
+  KEY("g", KeyG, 71)
+  KEY("h", KeyH, 72)
+  KEY("i", KeyI, 73)
+  KEY("j", KeyJ, 74)
+  KEY("k", KeyK, 75)
+  KEY("l", KeyL, 76)
+  KEY("m", KeyM, 77)
+  KEY("n", KeyN, 78)
+  KEY("o", KeyO, 79)
+  KEY("p", KeyP, 80)
+  KEY("q", KeyQ, 81)
+  KEY("r", KeyR, 82)
+  KEY("s", KeyS, 83)
+  KEY("t", KeyT, 84)
+  KEY("u", KeyU, 85)
+  KEY("v", KeyV, 86)
+  KEY("w", KeyW, 87)
+  KEY("x", KeyX, 88)
+  KEY("y", KeyY, 89)
+  KEY("z", KeyZ, 90)
+
+  KEY("F1", F1, 112)
+  KEY("F2", F2, 113)
+  KEY("F3", F3, 114)
+  KEY("F4", F4, 115)
+  KEY("F5", F5, 116)
+  KEY("F6", F6, 117)
+  KEY("F7", F7, 118)
+  KEY("F8", F8, 119)
+  KEY("F9", F9, 120)
+  KEY("F10", F10, 121)
+  KEY("F11", F11, 122)
+  KEY("F12", F12, 123)
+  KEY("F13", F13, 124)
+  KEY("F14", F14, 125)
+  KEY("F15", F15, 126)
+  KEY("F16", F16, 127)
+  KEY("F17", F17, 128)
+  KEY("F18", F18, 129)
+  KEY("F19", F19, 130)
+  KEY("F20", F20, 131)
+  KEY("F21", F21, 132)
+  KEY("F22", F22, 133)
+  KEY("F23", F23, 134)
+  KEY("F24", F24, 135)
+
+  KEY("0", Digit0, 48)
+  KEY("1", Digit1, 49)
+  KEY("2", Digit2, 50)
+  KEY("3", Digit3, 51)
+  KEY("4", Digit4, 52)
+  KEY("5", Digit5, 53)
+  KEY("6", Digit6, 54)
+  KEY("7", Digit7, 55)
+  KEY("8", Digit8, 56)
+  KEY("9", Digit9, 57)
+
+  SHIFT("!", Digit0, 48)
+  SHIFT("@", Digit1, 49)
+  SHIFT("#", Digit2, 50)
+  SHIFT("$", Digit3, 51)
+  SHIFT("%", Digit4, 52)
+  SHIFT("^", Digit5, 53)
+  SHIFT("&", Digit6, 54)
+  SHIFT("*", Digit7, 55)
+  SHIFT("(", Digit8, 56)
+  SHIFT(")", Digit9, 57)
+
+}
diff --git a/dom/events/KeyboardEvent.cpp b/dom/events/KeyboardEvent.cpp
index cd5352b..8829b8f 100644
--- a/dom/events/KeyboardEvent.cpp
+++ b/dom/events/KeyboardEvent.cpp
@@ -6,10 +6,17 @@
 #include "mozilla/dom/KeyboardEvent.h"
 #include "mozilla/TextEvents.h"
 #include "prtime.h"
+#include "KeyCodeConsensus.h"
+#include "nsContentUtils.h"
 
 namespace mozilla {
 namespace dom {
 
+static bool ResistFingerprinting() {
+  return nsContentUtils::ResistFingerprinting() &&
+         !nsContentUtils::IsCallerChrome();
+}
+
 KeyboardEvent::KeyboardEvent(EventTarget* aOwner,
                              nsPresContext* aPresContext,
                              WidgetKeyboardEvent* aEvent)
@@ -26,6 +33,7 @@ KeyboardEvent::KeyboardEvent(EventTarget* aOwner,
     mEvent->time = PR_Now();
     mEvent->AsKeyboardEvent()->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
   }
+  createKeyCodes();
 }
 
 NS_IMPL_ADDREF_INHERITED(KeyboardEvent, UIEvent)
@@ -66,7 +74,16 @@ KeyboardEvent::GetCtrlKey(bool* aIsDown)
 bool
 KeyboardEvent::ShiftKey()
 {
-  return mEvent->AsKeyboardEvent()->IsShift();
+  bool shiftState = mEvent->AsKeyboardEvent()->IsShift();
+  if (!ResistFingerprinting()) {
+    return shiftState;
+  }
+  // Find a consensus fake shift state for the given key name.
+  bool fakeShiftState;
+  nsString keyName;
+  GetKey(keyName);
+  bool exists = gShiftStates->Get(keyName, &fakeShiftState);
+  return exists ? fakeShiftState : shiftState;
 }
 
 NS_IMETHODIMP
@@ -131,7 +148,17 @@ KeyboardEvent::GetKey(nsAString& aKeyName)
 void
 KeyboardEvent::GetCode(nsAString& aCodeName)
 {
-  mEvent->AsKeyboardEvent()->GetDOMCodeName(aCodeName);
+  if (!ResistFingerprinting()) {
+    mEvent->AsKeyboardEvent()->GetDOMCodeName(aCodeName);
+  } else {
+    // Use a consensus code name corresponding to the
+    // key name.
+    nsString keyName, codeNameTemp;
+    GetKey(keyName);
+    if (gCodes->Get(keyName, &codeNameTemp)) {
+      aCodeName = codeNameTemp;
+    }
+  }
 }
 
 NS_IMETHODIMP
@@ -157,7 +184,7 @@ KeyboardEvent::CharCode()
   case NS_KEY_BEFORE_UP:
   case NS_KEY_UP:
   case NS_KEY_AFTER_UP:
-    return 0;
+    //   return 0;
   case NS_KEY_PRESS:
     return mEvent->AsKeyboardEvent()->charCode;
   }
@@ -176,12 +203,19 @@ uint32_t
 KeyboardEvent::KeyCode()
 {
   // If this event is initialized with ctor, we shouldn't check event type.
-  if (mInitializedByCtor) {
-    return mEvent->AsKeyboardEvent()->keyCode;
-  }
-
-  if (mEvent->HasKeyEventMessage()) {
-    return mEvent->AsKeyboardEvent()->keyCode;
+  if (mInitializedByCtor || mEvent->HasKeyEventMessage()) {
+    if (!ResistFingerprinting()) {
+      return mEvent->AsKeyboardEvent()->keyCode;
+    } else {
+      if (CharCode() != 0) {
+        return 0;
+      }
+      // Find a consensus key code for the given key name.
+      nsString keyName;
+      GetKey(keyName);
+      uint32_t keyCode;
+      return gKeyCodes->Get(keyName, &keyCode) ? keyCode : 0;
+    }
   }
   return 0;
 }
@@ -206,7 +240,7 @@ KeyboardEvent::Which()
       //Special case for 4xp bug 62878.  Try to make value of which
       //more closely mirror the values that 4.x gave for RETURN and BACKSPACE
       {
-        uint32_t keyCode = mEvent->AsKeyboardEvent()->keyCode;
+        uint32_t keyCode = KeyCode();
         if (keyCode == NS_VK_RETURN || keyCode == NS_VK_BACK) {
           return keyCode;
         }
@@ -229,7 +263,19 @@ KeyboardEvent::GetLocation(uint32_t* aLocation)
 uint32_t
 KeyboardEvent::Location()
 {
-  return mEvent->AsKeyboardEvent()->location;
+  uint32_t location = mEvent->AsKeyboardEvent()->location;
+  if (!ResistFingerprinting()) {
+    return location;
+  }
+  // To resist fingerprinting, hide right modifier keys, as
+  // well as the numpad.
+  switch (location) {
+    case 0 : return 0;
+    case 1 : return 1;
+    case 2 : return 1;
+    case 3 : return 0;
+    default: return 0;
+  }
 }
 
 // static



More information about the tor-commits mailing list