Bug 1425462 Add a timeorigin parameter to ReduceTimePrecision, and track the greatest provided timestamp. draft
authorTom Ritter <tom@mozilla.com>
Fri, 26 Jan 2018 15:06:32 -0600
changeset 747819 e1941f8cd39a62a6c8d9bae6e6763b9c0a607795
parent 747818 bd2e965f95bffd3d8997823951482cf02ce52194
child 747820 9202092b1ebbd575a1c6d34334546a88ed8dcaf5
push id97017
push userbmo:tom@mozilla.com
push dateFri, 26 Jan 2018 22:16:13 +0000
bugs1425462
milestone60.0a1
Bug 1425462 Add a timeorigin parameter to ReduceTimePrecision, and track the greatest provided timestamp. MozReview-Commit-ID: 6ITqSmyk4Ja
toolkit/components/resistfingerprinting/nsRFPService.cpp
toolkit/components/resistfingerprinting/nsRFPService.h
toolkit/components/resistfingerprinting/tests/test_reduceprecision.cpp
--- a/toolkit/components/resistfingerprinting/nsRFPService.cpp
+++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp
@@ -141,88 +141,112 @@ nsRFPService::IsResistFingerprintingEnab
 /* static */
 bool
 nsRFPService::IsTimerPrecisionReductionEnabled()
 {
   return (sPrivacyTimerPrecisionReduction || IsResistFingerprintingEnabled()) &&
          TimerResolution() > 0;
 }
 
+// This variable stores the largest timestamp we have ever seen. It is stored in microseconds.
+static long long greatestProvidedTimestamp = 0;
 /*
   @param aTime timestamp in native units (either seconds, milliseconds, or microseconds).
   @param aResolutionUS the precision, in microseconds, to clamp it to.
   @param aTimeScaleCorrection the amount aTime must be multiplied by to convert to microseconds.
 */
 /* static */
 double
-nsRFPService::ReduceTimePrecisionImpl(double aTime, TimeScale aTimeScale, double aResolutionUSec, bool jitter)
+nsRFPService::ReduceTimePrecisionImpl(double aTime, TimeScale aTimeScale, double aResolutionUSec, bool aJitter, double aTimeOrigin)
 {
   if (!IsTimerPrecisionReductionEnabled() ||
       aResolutionUSec <= 0) {
     return aTime;
   }
 
   // Increase the time as needed until it is in microseconds.
   // Note that a double can hold up to 2**53 with integer precision. This gives us
   // only until June 5, 2255 in time-since-the-epoch with integer precision.
   // So we will be losing microseconds precision after that date.
   // We think this is okay, and we codify it in some tests.
   double timeScaled = aTime * (1000000 / aTimeScale);
+  double originScaled = aTimeOrigin * (1000000 / aTimeScale);
   //Cut off anything less than a microsecond.
   long long timeAsInt = llround(timeScaled);
+  long long originAsInt = llround(originScaled);
   //Cast the resolution (in microseconds) to an int.
   long long resolutionAsInt = aResolutionUSec;
   // Perform the clamping.
-  long long rounded = (timeAsInt / resolutionAsInt) * resolutionAsInt;
+  long long clamped = (timeAsInt / resolutionAsInt) * resolutionAsInt;
 
   long jitterToAdd = 0;
   double randomMultiplier = 0;
-  if (jitter && sJitterUSec > 0) {
+  if (aJitter && sJitterUSec > 0) {
     // Get a random value between 0.0 and 1.0
     if(!NS_FAILED(RandomDouble(&randomMultiplier))) {
       // Add or Subtract up to the jitter value
-      jitterToAdd = (randomMultiplier * 2 * sJitterUSec) - sJitterUSec;
+      jitterToAdd = lround( (randomMultiplier * 2 * sJitterUSec) - sJitterUSec );
     }
   }
 
+  // Now we make sure time has not gone backwards
+  long long clampedAndJittered = clamped + jitterToAdd;
+  if (aTimeOrigin >= 0) {
+    long long valueWereGoingWith = clampedAndJittered;
+    if(clampedAndJittered + originAsInt < greatestProvidedTimestamp) {
+      valueWereGoingWith = greatestProvidedTimestamp - originAsInt;
+    }
+#if defined(DEBUG)
+    MOZ_LOG(gResistFingerprintingLog, LogLevel::Debug,
+      ("Given %16lli (%16lli + %16lli), clamped to %16lli, added %7li jitter to get %16lli. %sOK."
+       " (Prev: %16lli Diff: %7lli.) Ret: %16lli",
+        timeAsInt + originAsInt, timeAsInt, originAsInt, clamped, jitterToAdd, clampedAndJittered + originAsInt,
+        ((clampedAndJittered + originAsInt < greatestProvidedTimestamp) ? "NOT " : "    "),
+        greatestProvidedTimestamp, ((clampedAndJittered + originAsInt) - greatestProvidedTimestamp),
+        valueWereGoingWith));
+#endif
+    clampedAndJittered = valueWereGoingWith;
+    greatestProvidedTimestamp = max(clampedAndJittered + originAsInt, greatestProvidedTimestamp);
+  }
+
   // Cast it back to a double and reduce it to the correct units.
-  double ret = double(rounded + jitterToAdd) / (1000000.0 / aTimeScale);
+  double ret = double(clampedAndJittered) / (1000000.0 / aTimeScale);
 
 #if defined(DEBUG)
   uint32_t jitterLocal = sJitterUSec;
   MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose,
     ("Given: (%.*f, Scaled: %.*f, Converted: %lli), Rounding with (%lli, Originally %.*f), "
       "Intermediate: (%lli), Got: (%lli) Jitter: (%s Max: %u Multipler: %f Value: %li) Final: (%lli Converted: %.*f)",
     DBL_DIG-1, aTime, DBL_DIG-1, timeScaled, timeAsInt, resolutionAsInt, DBL_DIG-1, aResolutionUSec,
-    (timeAsInt / resolutionAsInt), rounded, (jitter && jitterLocal > 0 ? "On" : "Off"), jitterLocal, randomMultiplier,
-    jitterToAdd, (rounded + jitterToAdd), DBL_DIG-1, ret));
+    (timeAsInt / resolutionAsInt), clamped, (aJitter && jitterLocal > 0 ? "On" : "Off"), jitterLocal, randomMultiplier,
+    jitterToAdd, (clamped + jitterToAdd), DBL_DIG-1, ret));
 #endif
 
   return ret;
 }
 
 /* static */
 double
 nsRFPService::ReduceTimePrecisionAsUSecs(double aTime, bool jitter /* = false */)
 {
-  return nsRFPService::ReduceTimePrecisionImpl(aTime, MicroSeconds, TimerResolution(), jitter);
+  return nsRFPService::ReduceTimePrecisionImpl(aTime, MicroSeconds, TimerResolution(), jitter, -1);
 }
 
 /* static */
 double
-nsRFPService::ReduceTimePrecisionAsMSecs(double aTime, bool jitter /* = false */)
+nsRFPService::ReduceTimePrecisionAsMSecs(double aTime, bool jitter /* = false */, double timeOrigin /* = -1 */)
 {
-  return nsRFPService::ReduceTimePrecisionImpl(aTime, MilliSeconds, TimerResolution(), jitter);
+  return nsRFPService::ReduceTimePrecisionImpl(aTime, MilliSeconds, TimerResolution(), jitter, timeOrigin);
 }
 
 /* static */
 double
 nsRFPService::ReduceTimePrecisionAsSecs(double aTime, bool jitter /* = false */)
 {
-  return nsRFPService::ReduceTimePrecisionImpl(aTime, Seconds, TimerResolution(), jitter);
+  return nsRFPService::ReduceTimePrecisionImpl(aTime, Seconds, TimerResolution(), jitter, -1);
 }
 
 /* static */
 uint32_t
 nsRFPService::CalculateTargetVideoResolution(uint32_t aVideoQuality)
 {
   return aVideoQuality * NSToIntCeil(aVideoQuality * 16 / 9.0);
 }
--- a/toolkit/components/resistfingerprinting/nsRFPService.h
+++ b/toolkit/components/resistfingerprinting/nsRFPService.h
@@ -162,21 +162,21 @@ public:
 
   enum TimeScale {
     Seconds      = 1,
     MilliSeconds = 1000,
     MicroSeconds = 1000000
   };
 
   // The following Reduce methods can be called off main thread.
-  static double ReduceTimePrecisionAsUSecs(double aTime, bool jitter = false);
-  static double ReduceTimePrecisionAsMSecs(double aTime, bool jitter = false);
-  static double ReduceTimePrecisionAsSecs(double aTime, bool jitter = false);
+  static double ReduceTimePrecisionAsUSecs(double aTime, bool aJitter = false);
+  static double ReduceTimePrecisionAsMSecs(double aTime, bool aJitter = false, double aTimeOrigin = -1);
+  static double ReduceTimePrecisionAsSecs(double aTime, bool aJitter = false);
   // Public only for testing purposes.
-  static double ReduceTimePrecisionImpl(double aTime, TimeScale aTimeScale, double aResolutionUSec, bool jitter);
+  static double ReduceTimePrecisionImpl(double aTime, TimeScale aTimeScale, double aResolutionUSec, bool aJitter, double aTimeOrigin);
 
   // This method calculates the video resolution (i.e. height x width) based
   // on the video quality (480p, 720p, etc).
   static uint32_t CalculateTargetVideoResolution(uint32_t aVideoQuality);
 
   // Methods for getting spoofed media statistics and the return value will
   // depend on the video resolution.
   static uint32_t GetSpoofedTotalFrames(double aTime);
--- a/toolkit/components/resistfingerprinting/tests/test_reduceprecision.cpp
+++ b/toolkit/components/resistfingerprinting/tests/test_reduceprecision.cpp
@@ -38,18 +38,18 @@ using namespace mozilla;
   Look at the last two values:
     Got: 2064.83383999999978
     Got: 2064.83381999999983
 
   They're supposed to be equal. They're not. But they both round to 2064.83.
 */
 
 void process(double clock, nsRFPService::TimeScale clockUnits, double precision) {
-  double reduced1 = nsRFPService::ReduceTimePrecisionImpl(clock, clockUnits, precision, false);
-  double reduced2 = nsRFPService::ReduceTimePrecisionImpl(reduced1, clockUnits, precision, false);
+  double reduced1 = nsRFPService::ReduceTimePrecisionImpl(clock, clockUnits, precision, false, -1);
+  double reduced2 = nsRFPService::ReduceTimePrecisionImpl(reduced1, clockUnits, precision, false, -1);
   ASSERT_EQ(reduced1, reduced2);
 }
 
 TEST(ResistFingerprinting, ReducePrecision_Assumptions) {
   ASSERT_EQ(FLT_RADIX, 2);
   ASSERT_EQ(DBL_MANT_DIG, 53);
 }
 
@@ -81,43 +81,43 @@ TEST(ResistFingerprinting, ReducePrecisi
   process(2601.64, nsRFPService::TimeScale::MilliSeconds, 20);
   process(2595.16, nsRFPService::TimeScale::MilliSeconds, 20);
   process(2578.66, nsRFPService::TimeScale::MilliSeconds, 20);
 }
 
 TEST(ResistFingerprinting, ReducePrecision_ExpectedLossOfPrecision) {
   double result;
   // We lose integer precision at 9007199254740992 - let's confirm that.
-  result = nsRFPService::ReduceTimePrecisionImpl(9007199254740992, nsRFPService::TimeScale::MicroSeconds, 5, false);
+  result = nsRFPService::ReduceTimePrecisionImpl(9007199254740992, nsRFPService::TimeScale::MicroSeconds, 5, false, -1);
   ASSERT_EQ(result, 9007199254740990);
   // 9007199254740995 is approximated to 9007199254740996
-  result = nsRFPService::ReduceTimePrecisionImpl(9007199254740995, nsRFPService::TimeScale::MicroSeconds, 5, false);
+  result = nsRFPService::ReduceTimePrecisionImpl(9007199254740995, nsRFPService::TimeScale::MicroSeconds, 5, false, -1);
   ASSERT_EQ(result, 9007199254740996);
   // 9007199254740999 is approximated as 9007199254741000
-  result = nsRFPService::ReduceTimePrecisionImpl(9007199254740999, nsRFPService::TimeScale::MicroSeconds, 5, false);
+  result = nsRFPService::ReduceTimePrecisionImpl(9007199254740999, nsRFPService::TimeScale::MicroSeconds, 5, false, -1);
   ASSERT_EQ(result, 9007199254741000);
   // 9007199254743568 can be represented exactly, but will be clamped to 9007199254743564
-  result = nsRFPService::ReduceTimePrecisionImpl(9007199254743568, nsRFPService::TimeScale::MicroSeconds, 5, false);
+  result = nsRFPService::ReduceTimePrecisionImpl(9007199254743568, nsRFPService::TimeScale::MicroSeconds, 5, false, -1);
   ASSERT_EQ(result, 9007199254743564);
 }
 
 
 TEST(ResistFingerprinting, ReducePrecision_Expectations) {
   double result;
-  result = nsRFPService::ReduceTimePrecisionImpl(2611.14, nsRFPService::TimeScale::MilliSeconds, 20, false);
+  result = nsRFPService::ReduceTimePrecisionImpl(2611.14, nsRFPService::TimeScale::MilliSeconds, 20, false, -1);
   ASSERT_EQ(result, 2611.14);
-  result = nsRFPService::ReduceTimePrecisionImpl(2611.145, nsRFPService::TimeScale::MilliSeconds, 20, false);
+  result = nsRFPService::ReduceTimePrecisionImpl(2611.145, nsRFPService::TimeScale::MilliSeconds, 20, false, -1);
   ASSERT_EQ(result, 2611.14);
-  result = nsRFPService::ReduceTimePrecisionImpl(2611.141, nsRFPService::TimeScale::MilliSeconds, 20, false);
+  result = nsRFPService::ReduceTimePrecisionImpl(2611.141, nsRFPService::TimeScale::MilliSeconds, 20, false, -1);
   ASSERT_EQ(result, 2611.14);
-  result = nsRFPService::ReduceTimePrecisionImpl(2611.159, nsRFPService::TimeScale::MilliSeconds, 20, false);
+  result = nsRFPService::ReduceTimePrecisionImpl(2611.159, nsRFPService::TimeScale::MilliSeconds, 20, false, -1);
   ASSERT_EQ(result, 2611.14);
-  result = nsRFPService::ReduceTimePrecisionImpl(2611.15, nsRFPService::TimeScale::MilliSeconds, 20, false);
+  result = nsRFPService::ReduceTimePrecisionImpl(2611.15, nsRFPService::TimeScale::MilliSeconds, 20, false, -1);
   ASSERT_EQ(result, 2611.14);
-  result = nsRFPService::ReduceTimePrecisionImpl(2611.13, nsRFPService::TimeScale::MilliSeconds, 20, false);
+  result = nsRFPService::ReduceTimePrecisionImpl(2611.13, nsRFPService::TimeScale::MilliSeconds, 20, false, -1);
   ASSERT_EQ(result, 2611.12);
 }
 
 // Use an ugly but simple hack to turn an integer-based rand()
 // function to a double-based one.
 #define RAND_DOUBLE (rand() * (rand() / (double)rand()))
 
 // If you're doing logging, you really don't want to run this test.