Bug 1425462 Add an empty context pointer for our Timer Precision Reduction Functions draft
authorTom Ritter <tom@mozilla.com>
Mon, 26 Feb 2018 11:17:38 -0600
changeset 760076 1d127d11fbc8f862f7fc6c5f52550e9b224c00d5
parent 760075 da8ce8d1b5f26616943ac7569fe44d6e87a6db7d
child 760077 deaa76de0a79e30ade47c12195eaed5db58e7b26
push id100534
push userbmo:tom@mozilla.com
push dateMon, 26 Feb 2018 21:19:19 +0000
bugs1425462, 1440195
milestone60.0a1
Bug 1425462 Add an empty context pointer for our Timer Precision Reduction Functions We need to include a seed for each context (origin, iframe, worker, etc) we reduce the time precision of. This prevents a replay attack of the random midpoint sequence. This per-context seed will be added in Bug 1440195, in this patch we simply provide a parameter for it. MozReview-Commit-ID: DNgXOtX65qZ
dom/animation/AnimationUtils.h
toolkit/components/resistfingerprinting/nsRFPService.cpp
toolkit/components/resistfingerprinting/nsRFPService.h
toolkit/components/resistfingerprinting/tests/test_reduceprecision.cpp
--- a/dom/animation/AnimationUtils.h
+++ b/dom/animation/AnimationUtils.h
@@ -28,17 +28,17 @@ class AnimationUtils
 public:
   static dom::Nullable<double>
   TimeDurationToDouble(const dom::Nullable<TimeDuration>& aTime)
   {
     dom::Nullable<double> result;
 
     if (!aTime.IsNull()) {
       result.SetValue(
-        nsRFPService::ReduceTimePrecisionAsMSecs(aTime.Value().ToMilliseconds(), TimerPrecisionType::RFPOnly)
+        nsRFPService::ReduceTimePrecisionAsMSecs(aTime.Value().ToMilliseconds(), nullptr, TimerPrecisionType::RFPOnly)
       );
     }
 
     return result;
   }
 
   static dom::Nullable<TimeDuration>
   DoubleToTimeDuration(const dom::Nullable<double>& aTime)
--- a/toolkit/components/resistfingerprinting/nsRFPService.cpp
+++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp
@@ -211,18 +211,18 @@ private:
  *                              random midpoint (e.g. 360)
  *
  * If our actual timestamp (e.g. 325) is below the midpoint, we keep it clamped
  * downwards. If it were equal to or above the midpoint (e.g. 365) we would
  * round it upwards to the largest clamped value (in this example: 400).
  *
  * The question is: does time go backwards?
  *
- * The midpoint is deterministicly random
- * and generated from two components: a secret seed and a clamped time.
+ * The midpoint is deterministicly random and generated from two components:
+ * a secret seed and a clamped time.
  *
  * When comparing times across different seed values: time may go backwards.
  * For a clamped time of 300, one seed may generate a midpoint of 305 and another
  * 395. So comparing an (actual) timestamp of 325 and 351 could see the 325 clamped
  * up to 400 and the 351 clamped down to 300. The seed is per-process, so this case
  * occurs when one can compare timestamps cross-process. This is uncommon (because
  * we don't have site isolation.) The circumstances this could occur are
  * BroadcastChannel, Storage Notification, and in theory (but not yet implemented)
@@ -241,25 +241,31 @@ private:
  *
  *
  * TODO: The above comment is going to need to be entirely rewritten when we mix in
  * a per-context shared secret. Context is 'Any new object that gets a time origin
  * starting from zero'. The most obvious example is Documents and Workers. An attacker
  * could let time go forward and observe (roughly) where the random midpoints fall.
  * Then they create a new object, time starts back ovr at zero, and they know
  * (approximately) where the random midpoints are.
+ *
+ * The point of a per-context secret is to prevent an attacker from being able to
+ * play back the same sequence of jittered values. This would be possible whenever
+ * a "context's" (such as a Worker, a page, or anything with a unique time origin)
+ * time origin starts at zero. Workers are probably the easiest example of this.
  */
 
 static LRUCache* sCache = nullptr;
 
 /* static */
 nsresult
 nsRFPService::RandomMidpoint(long long aClampedTime,
                              long long aResolutionUSec,
                              long long* aMidpoint,
+                             void* aContextPointer,
                              uint8_t * aSecretSeed /* = nullptr */)
 {
   nsresult rv;
   const int kSeedSize = 16;
   const int kClampTimesPerDigest = HASH_DIGEST_SIZE_BITS / 32;
   static uint8_t * secretSeed = nullptr;
 
   if (!sCache) {
@@ -394,16 +400,17 @@ nsRFPService::RandomMidpoint(long long a
  * @return                 If clamping is appropriate, the clamped value of the input, otherwise the input.
  */
 /* static */
 double
 nsRFPService::ReduceTimePrecisionImpl(
   double aTime,
   TimeScale aTimeScale,
   double aResolutionUSec,
+  void* aContextPointer,
   TimerPrecisionType aType)
  {
    if (!IsTimerPrecisionReductionEnabled(aType) || 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
@@ -427,17 +434,17 @@ nsRFPService::ReduceTimePrecisionImpl(
   // round consistently towards positive infinity or negative infinity (we chose negative.)
   // This can't be done with a truncation, it must be done with floor.
   long long clamped = floor(double(timeAsInt) / resolutionAsInt) * resolutionAsInt;
 
 
   long long midpoint = 0,
             clampedAndJittered = clamped;
   if (sJitter) {
-    if(!NS_FAILED(RandomMidpoint(clamped, resolutionAsInt, &midpoint)) &&
+    if(!NS_FAILED(RandomMidpoint(clamped, resolutionAsInt, &midpoint, aContextPointer)) &&
        timeAsInt >= clamped + midpoint) {
       clampedAndJittered += resolutionAsInt;
     }
   }
 
   // Cast it back to a double and reduce it to the correct units.
   double ret = double(clampedAndJittered) / (1000000.0 / aTimeScale);
 
@@ -450,33 +457,42 @@ nsRFPService::ReduceTimePrecisionImpl(
      (long long)floor(double(timeAsInt) / resolutionAsInt), clamped, tmp_jitter, midpoint, clampedAndJittered, DBL_DIG-1, ret));
 #endif
 
   return ret;
 }
 
 /* static */
 double
-nsRFPService::ReduceTimePrecisionAsUSecs(double aTime, TimerPrecisionType aType /* = TimerPrecisionType::All */)
+nsRFPService::ReduceTimePrecisionAsUSecs(
+  double aTime,
+  void* aContextPointer /* = nullptr */,
+  TimerPrecisionType aType /* = TimerPrecisionType::All */)
 {
-  return nsRFPService::ReduceTimePrecisionImpl(aTime, MicroSeconds, TimerResolution(), aType);
+  return nsRFPService::ReduceTimePrecisionImpl(aTime, MicroSeconds, TimerResolution(), aContextPointer, aType);
 }
 
 /* static */
 double
-nsRFPService::ReduceTimePrecisionAsMSecs(double aTime, TimerPrecisionType aType /* = TimerPrecisionType::All */)
+nsRFPService::ReduceTimePrecisionAsMSecs(
+  double aTime,
+  void* aContextPointer /* = nullptr */,
+  TimerPrecisionType aType /* = TimerPrecisionType::All */)
 {
-  return nsRFPService::ReduceTimePrecisionImpl(aTime, MilliSeconds, TimerResolution(), aType);
+  return nsRFPService::ReduceTimePrecisionImpl(aTime, MilliSeconds, TimerResolution(), aContextPointer, aType);
 }
 
 /* static */
 double
-nsRFPService::ReduceTimePrecisionAsSecs(double aTime, TimerPrecisionType aType /* = TimerPrecisionType::All */)
+nsRFPService::ReduceTimePrecisionAsSecs(
+  double aTime,
+  void* aContextPointer /* = nullptr */,
+  TimerPrecisionType aType /* = TimerPrecisionType::All */)
 {
-  return nsRFPService::ReduceTimePrecisionImpl(aTime, Seconds, TimerResolution(), aType);
+  return nsRFPService::ReduceTimePrecisionImpl(aTime, Seconds, TimerResolution(), aContextPointer, aType);
 }
 
 /* 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
@@ -163,33 +163,38 @@ public:
     Seconds      = 1,
     MilliSeconds = 1000,
     MicroSeconds = 1000000
   };
 
   // The following Reduce methods can be called off main thread.
   static double ReduceTimePrecisionAsUSecs(
     double aTime,
+    void* aContextPointer = nullptr,
     TimerPrecisionType aType = TimerPrecisionType::All);
   static double ReduceTimePrecisionAsMSecs(
     double aTime,
+    void* aContextPointer = nullptr,
     TimerPrecisionType aType = TimerPrecisionType::All);
   static double ReduceTimePrecisionAsSecs(
     double aTime,
+    void* aContextPointer = nullptr,
     TimerPrecisionType aType = TimerPrecisionType::All);
 
   // Public only for testing purposes
   static double ReduceTimePrecisionImpl(
     double aTime,
     TimeScale aTimeScale,
     double aResolutionUSec,
+    void* aContextPointer,
     TimerPrecisionType aType);
   static nsresult RandomMidpoint(long long aExtraClampedTime,
                                  long long aExtraResolution,
                                  long long* aMidpoint,
+                                 void* aContextPointer,
                                  uint8_t * aSecretSeed = nullptr);
 
   // 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.
--- a/toolkit/components/resistfingerprinting/tests/test_reduceprecision.cpp
+++ b/toolkit/components/resistfingerprinting/tests/test_reduceprecision.cpp
@@ -59,18 +59,18 @@ bool setupJitter(bool enabled) {
 void cleanupJitter(bool jitterWasEnabled) {
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefs) {
     prefs->SetBoolPref("privacy.resistFingerprinting.reduceTimerPrecision.jitter", jitterWasEnabled);
   }
 }
 
 void process(double clock, nsRFPService::TimeScale clockUnits, double precision) {
-  double reduced1 = nsRFPService::ReduceTimePrecisionImpl(clock, clockUnits, precision, TimerPrecisionType::All);
-  double reduced2 = nsRFPService::ReduceTimePrecisionImpl(reduced1, clockUnits, precision, TimerPrecisionType::All);
+  double reduced1 = nsRFPService::ReduceTimePrecisionImpl(clock, clockUnits, precision, nullptr, TimerPrecisionType::All);
+  double reduced2 = nsRFPService::ReduceTimePrecisionImpl(reduced1, clockUnits, precision, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(reduced1, reduced2);
 }
 
 TEST(ResistFingerprinting, ReducePrecision_Assumptions) {
   ASSERT_EQ(FLT_RADIX, 2);
   ASSERT_EQ(DBL_MANT_DIG, 53);
 }
 
@@ -110,45 +110,45 @@ TEST(ResistFingerprinting, ReducePrecisi
   process(2595.16, nsRFPService::TimeScale::MilliSeconds, 20);
   process(2578.66, nsRFPService::TimeScale::MilliSeconds, 20);
   cleanupJitter(jitterEnabled);
 }
 
 TEST(ResistFingerprinting, ReducePrecision_Expectations) {
   bool jitterEnabled = setupJitter(false);
   double result;
-  result = nsRFPService::ReduceTimePrecisionImpl(2611.14, nsRFPService::TimeScale::MilliSeconds, 20, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(2611.14, nsRFPService::TimeScale::MilliSeconds, 20, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 2611.14);
-  result = nsRFPService::ReduceTimePrecisionImpl(2611.145, nsRFPService::TimeScale::MilliSeconds, 20, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(2611.145, nsRFPService::TimeScale::MilliSeconds, 20, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 2611.14);
-  result = nsRFPService::ReduceTimePrecisionImpl(2611.141, nsRFPService::TimeScale::MilliSeconds, 20, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(2611.141, nsRFPService::TimeScale::MilliSeconds, 20, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 2611.14);
-  result = nsRFPService::ReduceTimePrecisionImpl(2611.15999, nsRFPService::TimeScale::MilliSeconds, 20, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(2611.15999, nsRFPService::TimeScale::MilliSeconds, 20, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 2611.14);
-  result = nsRFPService::ReduceTimePrecisionImpl(2611.15, nsRFPService::TimeScale::MilliSeconds, 20, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(2611.15, nsRFPService::TimeScale::MilliSeconds, 20, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 2611.14);
-  result = nsRFPService::ReduceTimePrecisionImpl(2611.13, nsRFPService::TimeScale::MilliSeconds, 20, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(2611.13, nsRFPService::TimeScale::MilliSeconds, 20, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 2611.12);
   cleanupJitter(jitterEnabled);
 }
 
 TEST(ResistFingerprinting, ReducePrecision_ExpectedLossOfPrecision) {
   bool jitterEnabled = setupJitter(false);
   double result;
   // We lose integer precision at 9007199254740992 - let's confirm that.
-  result = nsRFPService::ReduceTimePrecisionImpl(9007199254740992.0, nsRFPService::TimeScale::MicroSeconds, 5, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(9007199254740992.0, nsRFPService::TimeScale::MicroSeconds, 5, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 9007199254740990.0);
   // 9007199254740995 is approximated to 9007199254740996
-  result = nsRFPService::ReduceTimePrecisionImpl(9007199254740995.0, nsRFPService::TimeScale::MicroSeconds, 5, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(9007199254740995.0, nsRFPService::TimeScale::MicroSeconds, 5, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 9007199254740996);
   // 9007199254740999 is approximated as 9007199254741000
-  result = nsRFPService::ReduceTimePrecisionImpl(9007199254740999.0, nsRFPService::TimeScale::MicroSeconds, 5, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(9007199254740999.0, nsRFPService::TimeScale::MicroSeconds, 5, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 9007199254741000.0);
   // 9007199254743568 can be represented exactly, but will be clamped to 9007199254743564
-  result = nsRFPService::ReduceTimePrecisionImpl(9007199254743568.0, nsRFPService::TimeScale::MicroSeconds, 5, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(9007199254743568.0, nsRFPService::TimeScale::MicroSeconds, 5, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 9007199254743564.0);
   cleanupJitter(jitterEnabled);
 }
 
 // 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()))
 
@@ -242,65 +242,65 @@ TEST(ResistFingerprinting, ReducePrecisi
    */
 
   // Set the secret
   long long throwAway;
   uint8_t hardcodedSecret[16] = {
     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
 
-  nsRFPService::RandomMidpoint(0, 500, &throwAway, hardcodedSecret);
+  nsRFPService::RandomMidpoint(0, 500, &throwAway, nullptr, hardcodedSecret);
 
   // Run the test vectors
   double result;
 
-  result = nsRFPService::ReduceTimePrecisionImpl(1, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(1, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 0);
-  result = nsRFPService::ReduceTimePrecisionImpl(129, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(129, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 0);
-  result = nsRFPService::ReduceTimePrecisionImpl(130, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(130, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 500);
-  result = nsRFPService::ReduceTimePrecisionImpl(131, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(131, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 500);
-  result = nsRFPService::ReduceTimePrecisionImpl(499, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(499, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 500);
 
-  result = nsRFPService::ReduceTimePrecisionImpl(500, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(500, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 500);
-  result = nsRFPService::ReduceTimePrecisionImpl(600, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(600, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 500);
-  result = nsRFPService::ReduceTimePrecisionImpl(928, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(928, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 500);
-  result = nsRFPService::ReduceTimePrecisionImpl(929, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(929, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 1000);
-  result = nsRFPService::ReduceTimePrecisionImpl(930, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(930, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 1000);
-  result = nsRFPService::ReduceTimePrecisionImpl(1255, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(1255, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 1000);
 
-  result = nsRFPService::ReduceTimePrecisionImpl(4000, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4000, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 4000);
-  result = nsRFPService::ReduceTimePrecisionImpl(4295, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4295, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 4000);
-  result = nsRFPService::ReduceTimePrecisionImpl(4296, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4296, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 4000);
-  result = nsRFPService::ReduceTimePrecisionImpl(4297, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4297, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 4500);
-  result = nsRFPService::ReduceTimePrecisionImpl(4298, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4298, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 4500);
-  result = nsRFPService::ReduceTimePrecisionImpl(4499, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4499, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 4500);
 
-  result = nsRFPService::ReduceTimePrecisionImpl(4500, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4500, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 4500);
-  result = nsRFPService::ReduceTimePrecisionImpl(4536, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4536, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 4500);
-  result = nsRFPService::ReduceTimePrecisionImpl(4537, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4537, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 4500);
-  result = nsRFPService::ReduceTimePrecisionImpl(4538, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4538, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 5000);
-  result = nsRFPService::ReduceTimePrecisionImpl(4539, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4539, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 5000);
-  result = nsRFPService::ReduceTimePrecisionImpl(5106, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(5106, nsRFPService::TimeScale::MicroSeconds, 500, nullptr, TimerPrecisionType::All);
   ASSERT_EQ(result, 5000);
 
   cleanupJitter(jitterEnabled);
 }