1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef __nsRFPService_h__
7 #define __nsRFPService_h__
8 
9 #include <cstdint>
10 #include "ErrorList.h"
11 #include "PLDHashTable.h"
12 #include "mozilla/BasicEvents.h"
13 #include "nsHashtablesFwd.h"
14 #include "nsIObserver.h"
15 #include "nsISupports.h"
16 #include "nsStringFwd.h"
17 
18 // Defines regarding spoofed values of Navigator object. These spoofed values
19 // are returned when 'privacy.resistFingerprinting' is true.
20 // We decided to give different spoofed values according to the platform. The
21 // reason is that it is easy to detect the real platform. So there is no benefit
22 // for hiding the platform: it only brings breakages, like keyboard shortcuts
23 // won't work in macOS if we spoof it as a Windows platform.
24 #ifdef XP_WIN
25 #  define SPOOFED_UA_OS "Windows NT 10.0; Win64; x64"
26 #  define SPOOFED_APPVERSION "5.0 (Windows)"
27 #  define SPOOFED_OSCPU "Windows NT 10.0; Win64; x64"
28 #  define SPOOFED_PLATFORM "Win32"
29 #elif defined(XP_MACOSX)
30 #  define SPOOFED_UA_OS "Macintosh; Intel Mac OS X 10.15"
31 #  define SPOOFED_APPVERSION "5.0 (Macintosh)"
32 #  define SPOOFED_OSCPU "Intel Mac OS X 10.15"
33 #  define SPOOFED_PLATFORM "MacIntel"
34 #elif defined(MOZ_WIDGET_ANDROID)
35 #  define SPOOFED_UA_OS "Android 10; Mobile"
36 #  define SPOOFED_APPVERSION "5.0 (Android 10)"
37 #  define SPOOFED_OSCPU "Linux aarch64"
38 #  define SPOOFED_PLATFORM "Linux aarch64"
39 #else
40 // For Linux and other platforms, like BSDs, SunOS and etc, we will use Linux
41 // platform.
42 #  define SPOOFED_UA_OS "X11; Linux x86_64"
43 #  define SPOOFED_APPVERSION "5.0 (X11)"
44 #  define SPOOFED_OSCPU "Linux x86_64"
45 #  define SPOOFED_PLATFORM "Linux x86_64"
46 #endif
47 
48 #define SPOOFED_APPNAME "Netscape"
49 #define LEGACY_BUILD_ID "20181001000000"
50 #define LEGACY_UA_GECKO_TRAIL "20100101"
51 
52 #define SPOOFED_POINTER_INTERFACE MouseEvent_Binding::MOZ_SOURCE_MOUSE
53 
54 // For the HTTP User-Agent header, we use a simpler set of spoofed values
55 // that do not reveal the specific desktop platform.
56 #if defined(MOZ_WIDGET_ANDROID)
57 #  define SPOOFED_HTTP_UA_OS "Android 10; Mobile"
58 #else
59 #  define SPOOFED_HTTP_UA_OS "Windows NT 10.0"
60 #endif
61 
62 struct JSContext;
63 
64 namespace mozilla {
65 class WidgetKeyboardEvent;
66 namespace dom {
67 class Document;
68 }
69 
70 enum KeyboardLang { EN = 0x01 };
71 
72 #define RFP_KEYBOARD_LANG_STRING_EN "en"
73 
74 typedef uint8_t KeyboardLangs;
75 
76 enum KeyboardRegion { US = 0x01 };
77 
78 #define RFP_KEYBOARD_REGION_STRING_US "US"
79 
80 typedef uint8_t KeyboardRegions;
81 
82 // This struct has the information about how to spoof the keyboardEvent.code,
83 // keyboardEvent.keycode and modifier states.
84 struct SpoofingKeyboardCode {
85   CodeNameIndex mCode;
86   uint8_t mKeyCode;
87   Modifiers mModifierStates;
88 };
89 
90 struct SpoofingKeyboardInfo {
91   nsString mKey;
92   KeyNameIndex mKeyIdx;
93   SpoofingKeyboardCode mSpoofingCode;
94 };
95 
96 class KeyboardHashKey : public PLDHashEntryHdr {
97  public:
98   typedef const KeyboardHashKey& KeyType;
99   typedef const KeyboardHashKey* KeyTypePointer;
100 
101   KeyboardHashKey(const KeyboardLangs aLang, const KeyboardRegions aRegion,
102                   const KeyNameIndexType aKeyIdx, const nsAString& aKey);
103 
104   explicit KeyboardHashKey(KeyTypePointer aOther);
105 
106   KeyboardHashKey(KeyboardHashKey&& aOther);
107 
108   ~KeyboardHashKey();
109 
110   bool KeyEquals(KeyTypePointer aOther) const;
111 
112   static KeyTypePointer KeyToPointer(KeyType aKey);
113 
114   static PLDHashNumber HashKey(KeyTypePointer aKey);
115 
116   enum { ALLOW_MEMMOVE = true };
117 
118   KeyboardLangs mLang;
119   KeyboardRegions mRegion;
120   KeyNameIndexType mKeyIdx;
121   nsString mKey;
122 };
123 
124 enum TimerPrecisionType {
125   DangerouslyNone = 1,
126   UnconditionalAKAHighRes = 2,
127   Normal = 3,
128   RFP = 4,
129 };
130 
131 class nsRFPService final : public nsIObserver {
132  public:
133   NS_DECL_ISUPPORTS
134   NS_DECL_NSIOBSERVER
135 
136   static nsRFPService* GetOrCreate();
137   static double TimerResolution();
138 
139   enum TimeScale { Seconds = 1, MilliSeconds = 1000, MicroSeconds = 1000000 };
140 
141   // The following Reduce methods can be called off main thread.
142   static double ReduceTimePrecisionAsUSecs(double aTime, int64_t aContextMixin,
143                                            bool aIsSystemPrincipal,
144                                            bool aCrossOriginIsolated);
145   static double ReduceTimePrecisionAsMSecs(double aTime, int64_t aContextMixin,
146                                            bool aIsSystemPrincipal,
147                                            bool aCrossOriginIsolated);
148   static double ReduceTimePrecisionAsMSecsRFPOnly(double aTime,
149                                                   int64_t aContextMixin);
150   static double ReduceTimePrecisionAsSecs(double aTime, int64_t aContextMixin,
151                                           bool aIsSystemPrincipal,
152                                           bool aCrossOriginIsolated);
153   static double ReduceTimePrecisionAsSecsRFPOnly(double aTime,
154                                                  int64_t aContextMixin);
155 
156   // Used by the JS Engine, as it doesn't know about the TimerPrecisionType enum
157   static double ReduceTimePrecisionAsUSecsWrapper(double aTime, JSContext* aCx);
158 
159   // Public only for testing purposes
160   static double ReduceTimePrecisionImpl(double aTime, TimeScale aTimeScale,
161                                         double aResolutionUSec,
162                                         int64_t aContextMixin,
163                                         TimerPrecisionType aType);
164   static nsresult RandomMidpoint(long long aClampedTimeUSec,
165                                  long long aResolutionUSec,
166                                  int64_t aContextMixin, long long* aMidpointOut,
167                                  uint8_t* aSecretSeed = nullptr);
168 
169   // This method calculates the video resolution (i.e. height x width) based
170   // on the video quality (480p, 720p, etc).
171   static uint32_t CalculateTargetVideoResolution(uint32_t aVideoQuality);
172 
173   // Methods for getting spoofed media statistics and the return value will
174   // depend on the video resolution.
175   static uint32_t GetSpoofedTotalFrames(double aTime);
176   static uint32_t GetSpoofedDroppedFrames(double aTime, uint32_t aWidth,
177                                           uint32_t aHeight);
178   static uint32_t GetSpoofedPresentedFrames(double aTime, uint32_t aWidth,
179                                             uint32_t aHeight);
180 
181   // This method generates the spoofed value of User Agent.
182   static void GetSpoofedUserAgent(nsACString& userAgent, bool isForHTTPHeader);
183 
184   /**
185    * This method for getting spoofed modifier states for the given keyboard
186    * event.
187    *
188    * @param aDoc           [in]  the owner's document for getting content
189    *                             language.
190    * @param aKeyboardEvent [in]  the keyboard event that needs to be spoofed.
191    * @param aModifier      [in]  the modifier that needs to be spoofed.
192    * @param aOut           [out] the spoofed state for the given modifier.
193    * @return               true if there is a spoofed state for the modifier.
194    */
195   static bool GetSpoofedModifierStates(
196       const mozilla::dom::Document* aDoc,
197       const WidgetKeyboardEvent* aKeyboardEvent, const Modifiers aModifier,
198       bool& aOut);
199 
200   /**
201    * This method for getting spoofed code for the given keyboard event.
202    *
203    * @param aDoc           [in]  the owner's document for getting content
204    *                             language.
205    * @param aKeyboardEvent [in]  the keyboard event that needs to be spoofed.
206    * @param aOut           [out] the spoofed code.
207    * @return               true if there is a spoofed code in the fake keyboard
208    *                       layout.
209    */
210   static bool GetSpoofedCode(const dom::Document* aDoc,
211                              const WidgetKeyboardEvent* aKeyboardEvent,
212                              nsAString& aOut);
213 
214   /**
215    * This method for getting spoofed keyCode for the given keyboard event.
216    *
217    * @param aDoc           [in]  the owner's document for getting content
218    *                             language.
219    * @param aKeyboardEvent [in]  the keyboard event that needs to be spoofed.
220    * @param aOut           [out] the spoofed keyCode.
221    * @return               true if there is a spoofed keyCode in the fake
222    *                       keyboard layout.
223    */
224   static bool GetSpoofedKeyCode(const mozilla::dom::Document* aDoc,
225                                 const WidgetKeyboardEvent* aKeyboardEvent,
226                                 uint32_t& aOut);
227 
228  private:
229   nsresult Init();
230 
231   nsRFPService() = default;
232 
233   ~nsRFPService() = default;
234 
235   void UpdateTimers();
236   void UpdateRFPPref();
237   void StartShutdown();
238 
239   void PrefChanged(const char* aPref);
240   static void PrefChanged(const char* aPref, void* aSelf);
241 
242   static void MaybeCreateSpoofingKeyCodes(const KeyboardLangs aLang,
243                                           const KeyboardRegions aRegion);
244   static void MaybeCreateSpoofingKeyCodesForEnUS();
245 
246   static void GetKeyboardLangAndRegion(const nsAString& aLanguage,
247                                        KeyboardLangs& aLocale,
248                                        KeyboardRegions& aRegion);
249   static bool GetSpoofedKeyCodeInfo(const mozilla::dom::Document* aDoc,
250                                     const WidgetKeyboardEvent* aKeyboardEvent,
251                                     SpoofingKeyboardCode& aOut);
252 
253   static nsTHashMap<KeyboardHashKey, const SpoofingKeyboardCode*>*
254       sSpoofingKeyboardCodes;
255 
256   static TimerPrecisionType GetTimerPrecisionType(bool aIsSystemPrincipal,
257                                                   bool aCrossOriginIsolated);
258 
259   static TimerPrecisionType GetTimerPrecisionTypeRFPOnly();
260 
261   static void TypeToText(TimerPrecisionType aType, nsACString& aText);
262 
263   nsCString mInitialTZValue;
264 };
265 
266 }  // namespace mozilla
267 
268 #endif /* __nsRFPService_h__ */
269