Bug 1425462 Add a timeorigin parameter to ReduceTimePrecision, and track the greatest provided timestamp.
MozReview-Commit-ID: 6ITqSmyk4Ja
--- 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.