[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