commit d888d0afb8e1f2770cc9420387ff031eb584e416 Author: Arthur Edelstein arthuredelstein@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
tbb-commits@lists.torproject.org