--- 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/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);
}