зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 6 changesets (bug 1425462) for XPCShell failure on multiple files
Backed out changeset 9ace3811f525 (bug 1425462) Backed out changeset 7d440e52e3a4 (bug 1425462) Backed out changeset 85896ea96faf (bug 1425462) Backed out changeset 127b5d2e6779 (bug 1425462) Backed out changeset 95ce64d3a29a (bug 1425462) Backed out changeset ddd2c4da4ba4 (bug 1425462)
This commit is contained in:
Родитель
187eb592e5
Коммит
cafaae9ee7
|
@ -1223,7 +1223,6 @@ pref("services.sync.prefs.sync.privacy.trackingprotection.pbmode.enabled", true)
|
|||
pref("services.sync.prefs.sync.privacy.resistFingerprinting", true);
|
||||
pref("services.sync.prefs.sync.privacy.reduceTimerPrecision", true);
|
||||
pref("services.sync.prefs.sync.privacy.resistFingerprinting.reduceTimerPrecision.microseconds", true);
|
||||
pref("services.sync.prefs.sync.privacy.resistFingerprinting.reduceTimerPrecision.jitter", true);
|
||||
pref("services.sync.prefs.sync.security.OCSP.enabled", true);
|
||||
pref("services.sync.prefs.sync.security.OCSP.require", true);
|
||||
pref("services.sync.prefs.sync.security.default_personal_cert", true);
|
||||
|
|
|
@ -293,7 +293,6 @@ const char* mozilla::dom::ContentPrefs::gEarlyPrefs[] = {
|
|||
"privacy.reduceTimerPrecision",
|
||||
"privacy.resistFingerprinting",
|
||||
"privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts",
|
||||
"privacy.resistFingerprinting.reduceTimerPrecision.jitter",
|
||||
"privacy.resistFingerprinting.reduceTimerPrecision.microseconds",
|
||||
"privacy.resistFingerprinting.target_video_res",
|
||||
"privacy.resistFingerprinting.video_dropped_ratio",
|
||||
|
|
|
@ -27,7 +27,6 @@ https://trac.torproject.org/projects/tor/ticket/15757
|
|||
gTestPrefs.push(
|
||||
["privacy.resistFingerprinting", true],
|
||||
["privacy.resistFingerprinting.reduceTimerPrecision.microseconds", 100000],
|
||||
["privacy.resistFingerprinting.reduceTimerPrecision.jitter", false],
|
||||
// We use 240p as the target resoultion since 480p is greater than every video
|
||||
// source in our test suite, so we need to use 240p here for allowing us to
|
||||
// test dropped rate here.
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
async function runTests() {
|
||||
await MIDITestUtils.permissionSetup(true);
|
||||
await SpecialPowers.pushPrefEnv({"set": [["privacy.resistFingerprinting.reduceTimerPrecision.jitter", false]]});
|
||||
var checkCount = 0;
|
||||
var lastTime = 0;
|
||||
var reopened = false;
|
||||
|
|
|
@ -171,21 +171,10 @@ DayFromYear(double year);
|
|||
JS_PUBLIC_API(double)
|
||||
DayWithinYear(double time, double year);
|
||||
|
||||
// The callback will be a wrapper function that accepts a single double (the time
|
||||
// to clamp and jitter.) Inside the JS Engine, other parameters that may be needed
|
||||
// are all constant, so they are handled inside the wrapper function
|
||||
using ReduceMicrosecondTimePrecisionCallback = double(*)(double);
|
||||
|
||||
// Set a callback into the toolkit/components/resistfingerprinting function that
|
||||
// will centralize time resolution and jitter into one place.
|
||||
// Sets the time resolution for fingerprinting protection.
|
||||
// If it's set to zero, then no rounding will happen.
|
||||
JS_PUBLIC_API(void)
|
||||
SetReduceMicrosecondTimePrecisionCallback(ReduceMicrosecondTimePrecisionCallback callback);
|
||||
|
||||
// Sets the time resolution for fingerprinting protection, and whether jitter
|
||||
// should occur. If resolution is set to zero, then no rounding or jitter will
|
||||
// occur. This is used if the callback above is not specified.
|
||||
JS_PUBLIC_API(void)
|
||||
SetTimeResolutionUsec(uint32_t resolution, bool jitter);
|
||||
SetTimeResolutionUsec(uint32_t resolution);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
|
|
|
@ -63,11 +63,6 @@ using JS::ToInteger;
|
|||
|
||||
// When this value is non-zero, we'll round the time by this resolution.
|
||||
static Atomic<uint32_t, Relaxed> sResolutionUsec;
|
||||
// This is not implemented yet, but we will use this to know to jitter the time in the JS shell
|
||||
static Atomic<bool, Relaxed> sJitter;
|
||||
// The callback we will use for the Gecko implementation of Timer Clamping/Jittering
|
||||
static Atomic<JS::ReduceMicrosecondTimePrecisionCallback, Relaxed> sReduceMicrosecondTimePrecisionCallback;
|
||||
|
||||
|
||||
/*
|
||||
* The JS 'Date' object is patterned after the Java 'Date' object.
|
||||
|
@ -410,16 +405,9 @@ JS::DayWithinYear(double time, double year)
|
|||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::SetReduceMicrosecondTimePrecisionCallback(JS::ReduceMicrosecondTimePrecisionCallback callback)
|
||||
{
|
||||
sReduceMicrosecondTimePrecisionCallback = callback;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::SetTimeResolutionUsec(uint32_t resolution, bool jitter)
|
||||
JS::SetTimeResolutionUsec(uint32_t resolution)
|
||||
{
|
||||
sResolutionUsec = resolution;
|
||||
sJitter = jitter;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1308,11 +1296,9 @@ static ClippedTime
|
|||
NowAsMillis()
|
||||
{
|
||||
double now = PRMJ_Now();
|
||||
if (sReduceMicrosecondTimePrecisionCallback)
|
||||
now = sReduceMicrosecondTimePrecisionCallback(now);
|
||||
else if (sResolutionUsec)
|
||||
if (sResolutionUsec) {
|
||||
now = floor(now / sResolutionUsec) * sResolutionUsec;
|
||||
|
||||
}
|
||||
return TimeClip(now / PRMJ_USEC_PER_MSEC);
|
||||
}
|
||||
|
||||
|
|
|
@ -1429,8 +1429,6 @@ pref("privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts", true);
|
|||
pref("privacy.reduceTimerPrecision", true);
|
||||
// Dynamically tune the resolution of the timer reduction for both of the two above prefs
|
||||
pref("privacy.resistFingerprinting.reduceTimerPrecision.microseconds", 2000);
|
||||
// Enable jittering the clock one precision value forward
|
||||
pref("privacy.resistFingerprinting.reduceTimerPrecision.jitter", true);
|
||||
// Lower the priority of network loads for resources on the tracking protection list.
|
||||
// Note that this requires the privacy.trackingprotection.annotate_channels pref to be on in order to have any effect.
|
||||
#ifdef NIGHTLY_BUILD
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
#include "nsRFPService.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <time.h>
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
@ -25,11 +23,9 @@
|
|||
#include "nsXULAppAPI.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIRandomGenerator.h"
|
||||
#include "nsIXULAppInfo.h"
|
||||
#include "nsIXULRuntime.h"
|
||||
#include "nsJSUtils.h"
|
||||
|
@ -49,8 +45,6 @@ static mozilla::LazyLogModule gResistFingerprintingLog("nsResistFingerprinting")
|
|||
#define RFP_TIMER_PREF "privacy.reduceTimerPrecision"
|
||||
#define RFP_TIMER_VALUE_PREF "privacy.resistFingerprinting.reduceTimerPrecision.microseconds"
|
||||
#define RFP_TIMER_VALUE_DEFAULT 2000
|
||||
#define RFP_JITTER_VALUE_PREF "privacy.resistFingerprinting.reduceTimerPrecision.jitter"
|
||||
#define RFP_JITTER_VALUE_DEFAULT true
|
||||
#define RFP_SPOOFED_FRAMES_PER_SEC_PREF "privacy.resistFingerprinting.video_frames_per_sec"
|
||||
#define RFP_SPOOFED_DROPPED_RATIO_PREF "privacy.resistFingerprinting.video_dropped_ratio"
|
||||
#define RFP_TARGET_VIDEO_RES_PREF "privacy.resistFingerprinting.target_video_res"
|
||||
|
@ -80,13 +74,11 @@ Atomic<bool, Relaxed> nsRFPService::sPrivacyResistFingerprinting;
|
|||
Atomic<bool, Relaxed> nsRFPService::sPrivacyTimerPrecisionReduction;
|
||||
// Note: anytime you want to use this variable, you should probably use TimerResolution() instead
|
||||
Atomic<uint32_t, Relaxed> sResolutionUSec;
|
||||
Atomic<bool, Relaxed> sJitter;
|
||||
static uint32_t sVideoFramesPerSec;
|
||||
static uint32_t sVideoDroppedRatio;
|
||||
static uint32_t sTargetVideoRes;
|
||||
nsDataHashtable<KeyboardHashKey, const SpoofingKeyboardCode*>*
|
||||
nsRFPService::sSpoofingKeyboardCodes = nullptr;
|
||||
static mozilla::StaticMutex sLock;
|
||||
|
||||
/* static */
|
||||
nsRFPService*
|
||||
|
@ -136,290 +128,6 @@ nsRFPService::IsTimerPrecisionReductionEnabled(TimerPrecisionType aType)
|
|||
TimerResolution() > 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The below is a simple time-based Least Recently Used cache used to store the
|
||||
* result of a cryptographic hash function. It has LRU_CACHE_SIZE slots, and will
|
||||
* be used from multiple threads. It is thread-safe.
|
||||
*/
|
||||
#define LRU_CACHE_SIZE (45)
|
||||
#define HASH_DIGEST_SIZE_BITS (256)
|
||||
#define HASH_DIGEST_SIZE_BYTES (HASH_DIGEST_SIZE_BITS / 8)
|
||||
|
||||
class LRUCache
|
||||
{
|
||||
public:
|
||||
LRUCache()
|
||||
: mLock("mozilla.resistFingerprinting.LRUCache") {
|
||||
this->cache.SetLength(LRU_CACHE_SIZE);
|
||||
}
|
||||
|
||||
nsCString Get(long long aKey) {
|
||||
for (auto & cacheEntry : this->cache) {
|
||||
// Read optimistically befor locking
|
||||
if (cacheEntry.key == aKey) {
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
// Double check after we have a lock
|
||||
if (MOZ_UNLIKELY(cacheEntry.key != aKey)) {
|
||||
// Got evicted in a race
|
||||
#if defined(DEBUG)
|
||||
long long tmp_key = cacheEntry.key;
|
||||
MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose,
|
||||
("LRU Cache HIT-MISS with %lli != %lli", aKey, tmp_key));
|
||||
#endif
|
||||
return EmptyCString();
|
||||
}
|
||||
|
||||
cacheEntry.accessTime = PR_Now();
|
||||
|
||||
#if defined(DEBUG)
|
||||
MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose,
|
||||
("LRU Cache HIT with %lli", aKey));
|
||||
#endif
|
||||
return cacheEntry.data;
|
||||
}
|
||||
}
|
||||
|
||||
return EmptyCString();
|
||||
}
|
||||
|
||||
void Store(long long aKey, const nsCString& aValue) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(aValue.Length() == HASH_DIGEST_SIZE_BYTES);
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
CacheEntry* lowestKey = &this->cache[0];
|
||||
for (auto & cacheEntry : this->cache) {
|
||||
if (MOZ_UNLIKELY(cacheEntry.key == aKey)) {
|
||||
// Another thread inserted before us, don't insert twice
|
||||
#if defined(DEBUG)
|
||||
MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose,
|
||||
("LRU Cache DOUBLE STORE with %lli", aKey));
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (cacheEntry.accessTime < lowestKey->accessTime) {
|
||||
lowestKey = &cacheEntry;
|
||||
}
|
||||
}
|
||||
|
||||
lowestKey->key = aKey;
|
||||
lowestKey->data = aValue;
|
||||
lowestKey->accessTime = PR_Now();
|
||||
#if defined(DEBUG)
|
||||
MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose, ("LRU Cache STORE with %lli", aKey));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
struct CacheEntry {
|
||||
Atomic<long long, Relaxed> key;
|
||||
PRTime accessTime = 0;
|
||||
nsCString data;
|
||||
|
||||
CacheEntry() {
|
||||
this->key = 0xFFFFFFFFFFFFFFFF;
|
||||
this->accessTime = 0;
|
||||
this->data = nullptr;
|
||||
}
|
||||
CacheEntry(const CacheEntry &obj) {
|
||||
this->key.exchange(obj.key);
|
||||
this->accessTime = obj.accessTime;
|
||||
this->data = obj.data;
|
||||
}
|
||||
};
|
||||
|
||||
AutoTArray<CacheEntry, LRU_CACHE_SIZE> cache;
|
||||
mozilla::Mutex mLock;
|
||||
};
|
||||
|
||||
// We make a single LRUCache
|
||||
static StaticAutoPtr<LRUCache> sCache;
|
||||
|
||||
/**
|
||||
* The purpose of this function is to deterministicly generate a random midpoint
|
||||
* between a lower clamped value and an upper clamped value. Assuming a clamping
|
||||
* resolution of 100, here is an example:
|
||||
*
|
||||
* |---------------------------------------|--------------------------|
|
||||
* lower clamped value (e.g. 300) | upper clamped value (400)
|
||||
* 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.
|
||||
*
|
||||
* 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)
|
||||
* SharedWorker. This should be an exhaustive list (at time of comment writing!).
|
||||
*
|
||||
* Aside from cross-process communication, derived timestamps across different
|
||||
* time origins may go backwards. (Specifically, derived means adding two timestamps
|
||||
* together to get an (approximate) absolute time.)
|
||||
* Assume a page and a worker. If one calls performance.now() in the page and then
|
||||
* triggers a call to performance.now() in the worker, the following invariant should
|
||||
* hold true:
|
||||
* page.performance.timeOrigin + page.performance.now() <
|
||||
* worker.performance.timeOrigin + worker.performance.now()
|
||||
*
|
||||
* We break this invariant.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @param aClampedTimeUSec [in] The clamped input time in microseconds.
|
||||
* @param aResolutionUSec [in] The current resolution for clamping in microseconds.
|
||||
* @param aMidpointOut [out] The midpoint, in microseconds, between [0, aResolutionUSec].
|
||||
* @param aSecretSeed [in] TESTING ONLY. When provided, the current seed will be
|
||||
* replaced with this value.
|
||||
* @return A nsresult indicating success of failure. If the function failed,
|
||||
* nothing is written to aMidpointOut
|
||||
*/
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
nsRFPService::RandomMidpoint(long long aClampedTimeUSec,
|
||||
long long aResolutionUSec,
|
||||
long long* aMidpointOut,
|
||||
uint8_t * aSecretSeed /* = nullptr */)
|
||||
{
|
||||
nsresult rv;
|
||||
const int kSeedSize = 16;
|
||||
const int kClampTimesPerDigest = HASH_DIGEST_SIZE_BITS / 32;
|
||||
static uint8_t * sSecretMidpointSeed = nullptr;
|
||||
|
||||
if(MOZ_UNLIKELY(!sCache)) {
|
||||
StaticMutexAutoLock lock(sLock);
|
||||
if(MOZ_LIKELY(!sCache)) {
|
||||
sCache = new LRUCache();
|
||||
ClearOnShutdown(&sCache);
|
||||
}
|
||||
}
|
||||
|
||||
if(MOZ_UNLIKELY(!aMidpointOut)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/*
|
||||
* Below, we will call a cryptographic hash function. That's expensive. We look for ways to
|
||||
* make it more efficient.
|
||||
*
|
||||
* We only need as much output from the hash function as the maximum resolution we will
|
||||
* ever support, because we will reduce the output modulo that value. The maximum resolution
|
||||
* we think is likely is in the low seconds value, or about 1-10 million microseconds.
|
||||
* 2**24 is 16 million, so we only need 24 bits of output. Practically speaking though,
|
||||
* it's way easier to work with 32 bits.
|
||||
*
|
||||
* So we're using 32 bits of output and throwing away the other DIGEST_SIZE - 32 (in the case of
|
||||
* SHA-256, DIGEST_SIZE is 256.) That's a lot of waste.
|
||||
*
|
||||
* Instead of throwing it away, we're going to use all of it. We can handle DIGEST_SIZE / 32
|
||||
* Clamped Time's per hash function - call that , so we reduce aClampedTime to a multiple of
|
||||
* kClampTimesPerDigest (just like we reduced the real time value to aClampedTime!)
|
||||
*
|
||||
* Then we hash _that_ value (assuming it's not in the cache) and index into the digest result
|
||||
* the appropriate bit offset.
|
||||
*/
|
||||
long long reducedResolution = aResolutionUSec * kClampTimesPerDigest;
|
||||
long long extraClampedTime = (aClampedTimeUSec / reducedResolution) * reducedResolution;
|
||||
|
||||
nsCString hashResult = sCache->Get(extraClampedTime);
|
||||
|
||||
if(hashResult.Length() != HASH_DIGEST_SIZE_BYTES) { // Cache Miss =(
|
||||
// If someone has pased in the testing-only parameter, replace our seed with it
|
||||
if (aSecretSeed != nullptr) {
|
||||
StaticMutexAutoLock lock(sLock);
|
||||
if (sSecretMidpointSeed) {
|
||||
delete[] sSecretMidpointSeed;
|
||||
}
|
||||
sSecretMidpointSeed = new uint8_t[kSeedSize];
|
||||
memcpy(sSecretMidpointSeed, aSecretSeed, kSeedSize);
|
||||
}
|
||||
|
||||
// If we don't have a seed, we need to get one.
|
||||
if(MOZ_UNLIKELY(!sSecretMidpointSeed)) {
|
||||
StaticMutexAutoLock lock(sLock);
|
||||
if(MOZ_LIKELY(!sSecretMidpointSeed)) {
|
||||
nsCOMPtr<nsIRandomGenerator> randomGenerator =
|
||||
do_GetService("@mozilla.org/security/random-generator;1", &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
|
||||
rv = randomGenerator->GenerateRandomBytes(kSeedSize, &sSecretMidpointSeed);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Use a cryptographicly secure hash function, but do _not_ use an HMAC.
|
||||
* Obviously we're not using this data for authentication purposes, but
|
||||
* even still an HMAC is a perfect fit here, as we're hashing a value
|
||||
* using a seed that never changes, and an input that does. So why not
|
||||
* use one?
|
||||
*
|
||||
* Basically - we don't need to, it's two invocations of the hash function,
|
||||
* and speed really counts here.
|
||||
*
|
||||
* With authentication off the table, the properties we would get by
|
||||
* using an HMAC here would be:
|
||||
* - Resistence to length extension
|
||||
* - Resistence to collision attacks on the underlying hash function
|
||||
* - Resistence to chosen prefix attacks
|
||||
*
|
||||
* There is no threat of length extension here. Nor is there any real
|
||||
* practical threat of collision: not only are we using a good hash
|
||||
* function (you may mock me in 10 years if it is broken) but we don't
|
||||
* provide the attacker much control over the input. Nor do we let them
|
||||
* have the prefix.
|
||||
*/
|
||||
|
||||
// Then hash extraClampedTime and store it in the cache
|
||||
nsCOMPtr<nsICryptoHash> hasher = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = hasher->Init(nsICryptoHash::SHA256);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = hasher->Update(sSecretMidpointSeed, kSeedSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = hasher->Update((const uint8_t *)&extraClampedTime, sizeof(extraClampedTime));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCStringN<HASH_DIGEST_SIZE_BYTES> derivedSecret;
|
||||
rv = hasher->Finish(false, derivedSecret);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Finally, store it in the cache
|
||||
sCache->Store(extraClampedTime, derivedSecret);
|
||||
hashResult = derivedSecret;
|
||||
}
|
||||
|
||||
// Offset the appropriate index into the hash output, and then turn it into a random midpoint
|
||||
// between 0 and aResolutionUSec
|
||||
int byteOffset = ((aClampedTimeUSec - extraClampedTime) / aResolutionUSec) * 4;
|
||||
uint32_t deterministiclyRandomValue = *BitwiseCast<uint32_t*>(PromiseFlatCString(hashResult).get() + byteOffset);
|
||||
deterministiclyRandomValue %= aResolutionUSec;
|
||||
*aMidpointOut = deterministiclyRandomValue;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given a precision value, this function will reduce a given input time to the nearest
|
||||
* multiple of that precision.
|
||||
|
@ -474,31 +182,18 @@ 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)) &&
|
||||
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);
|
||||
double ret = double(clamped) / (1000000.0 / aTimeScale);
|
||||
|
||||
#if defined(DEBUG)
|
||||
bool tmp_jitter = sJitter;
|
||||
MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose,
|
||||
("Given: (%.*f, Scaled: %.*f, Converted: %lli), Rounding with (%lli, Originally %.*f), "
|
||||
"Intermediate: (%lli), Clamped: (%lli) Jitter: (%i Midpoint: %lli) Final: (%lli Converted: %.*f)",
|
||||
DBL_DIG-1, aTime, DBL_DIG-1, timeScaled, timeAsInt, resolutionAsInt, DBL_DIG-1, aResolutionUSec,
|
||||
(long long)floor(double(timeAsInt) / resolutionAsInt), clamped, tmp_jitter, midpoint, clampedAndJittered, DBL_DIG-1, ret));
|
||||
MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose,
|
||||
("Given: (%.*f, Scaled: %.*f, Converted: %lli), Rounding with (%lli, Originally %.*f), Intermediate: (%lli), Got: (%lli Converted: %.*f)",
|
||||
DBL_DIG-1, aTime, DBL_DIG-1, timeScaled, timeAsInt, resolutionAsInt, DBL_DIG-1, aResolutionUSec,
|
||||
(long long)floor(double(timeAsInt) / resolutionAsInt), clamped, DBL_DIG-1, ret));
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* static */
|
||||
double
|
||||
|
@ -507,13 +202,6 @@ nsRFPService::ReduceTimePrecisionAsUSecs(double aTime, TimerPrecisionType aType
|
|||
return nsRFPService::ReduceTimePrecisionImpl(aTime, MicroSeconds, TimerResolution(), aType);
|
||||
}
|
||||
|
||||
/* static */
|
||||
double
|
||||
nsRFPService::ReduceTimePrecisionAsUSecsWrapper(double aTime)
|
||||
{
|
||||
return nsRFPService::ReduceTimePrecisionImpl(aTime, MicroSeconds, TimerResolution(), TimerPrecisionType::All);
|
||||
}
|
||||
|
||||
/* static */
|
||||
double
|
||||
nsRFPService::ReduceTimePrecisionAsMSecs(double aTime, TimerPrecisionType aType /* = TimerPrecisionType::All */)
|
||||
|
@ -665,9 +353,6 @@ nsRFPService::Init()
|
|||
rv = prefs->AddObserver(RFP_TIMER_VALUE_PREF, this, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = prefs->AddObserver(RFP_JITTER_VALUE_PREF, this, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
Preferences::AddAtomicBoolVarCache(&sPrivacyTimerPrecisionReduction,
|
||||
RFP_TIMER_PREF,
|
||||
true);
|
||||
|
@ -675,9 +360,6 @@ nsRFPService::Init()
|
|||
Preferences::AddAtomicUintVarCache(&sResolutionUSec,
|
||||
RFP_TIMER_VALUE_PREF,
|
||||
RFP_TIMER_VALUE_DEFAULT);
|
||||
Preferences::AddAtomicBoolVarCache(&sJitter,
|
||||
RFP_JITTER_VALUE_PREF,
|
||||
RFP_JITTER_VALUE_DEFAULT);
|
||||
Preferences::AddUintVarCache(&sVideoFramesPerSec,
|
||||
RFP_SPOOFED_FRAMES_PER_SEC_PREF,
|
||||
RFP_SPOOFED_FRAMES_PER_SEC_DEFAULT);
|
||||
|
@ -706,10 +388,9 @@ nsRFPService::UpdateTimers() {
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sPrivacyResistFingerprinting || sPrivacyTimerPrecisionReduction) {
|
||||
JS::SetTimeResolutionUsec(TimerResolution(), sJitter);
|
||||
JS::SetReduceMicrosecondTimePrecisionCallback(nsRFPService::ReduceTimePrecisionAsUSecsWrapper);
|
||||
JS::SetTimeResolutionUsec(TimerResolution());
|
||||
} else if (sInitialized) {
|
||||
JS::SetTimeResolutionUsec(0, false);
|
||||
JS::SetTimeResolutionUsec(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -775,7 +456,6 @@ nsRFPService::StartShutdown()
|
|||
prefs->RemoveObserver(RESIST_FINGERPRINTING_PREF, this);
|
||||
prefs->RemoveObserver(RFP_TIMER_PREF, this);
|
||||
prefs->RemoveObserver(RFP_TIMER_VALUE_PREF, this);
|
||||
prefs->RemoveObserver(RFP_JITTER_VALUE_PREF, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1012,9 +692,7 @@ nsRFPService::Observe(nsISupports* aObject, const char* aTopic,
|
|||
if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
|
||||
NS_ConvertUTF16toUTF8 pref(aMessage);
|
||||
|
||||
if (pref.EqualsLiteral(RFP_TIMER_PREF) ||
|
||||
pref.EqualsLiteral(RFP_TIMER_VALUE_PREF) ||
|
||||
pref.EqualsLiteral(RFP_JITTER_VALUE_PREF)) {
|
||||
if (pref.EqualsLiteral(RFP_TIMER_PREF) || pref.EqualsLiteral(RFP_TIMER_VALUE_PREF)) {
|
||||
UpdateTimers();
|
||||
}
|
||||
else if (pref.EqualsLiteral(RESIST_FINGERPRINTING_PREF)) {
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIObserver.h"
|
||||
|
||||
|
@ -48,9 +47,6 @@
|
|||
#define SPOOFED_APPNAME "Netscape"
|
||||
#define LEGACY_BUILD_ID "20100101"
|
||||
|
||||
// Forward declare LRUCache, defined in nsRFPService.cpp
|
||||
class LRUCache;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
enum KeyboardLang {
|
||||
|
@ -179,21 +175,13 @@ public:
|
|||
static double ReduceTimePrecisionAsSecs(
|
||||
double aTime,
|
||||
TimerPrecisionType aType = TimerPrecisionType::All);
|
||||
|
||||
// Used by the JS Engine, as it doesn't know about the TimerPrecisionType enum
|
||||
static double ReduceTimePrecisionAsUSecsWrapper(
|
||||
double aTime);
|
||||
|
||||
// Public only for testing purposes
|
||||
static double ReduceTimePrecisionImpl(
|
||||
double aTime,
|
||||
TimeScale aTimeScale,
|
||||
double aResolutionUSec,
|
||||
TimerPrecisionType aType);
|
||||
static nsresult RandomMidpoint(long long aClampedTimeUSec,
|
||||
long long aResolutionUSec,
|
||||
long long* aMidpointOut,
|
||||
uint8_t * aSecretSeed = nullptr);
|
||||
|
||||
|
||||
// This method calculates the video resolution (i.e. height x width) based
|
||||
// on the video quality (480p, 720p, etc).
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
#include <math.h>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#include "nsRFPService.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
@ -44,25 +42,6 @@ using namespace mozilla;
|
|||
They're supposed to be equal. They're not. But they both round to 2064.83.
|
||||
*/
|
||||
|
||||
bool setupJitter(bool enabled) {
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
|
||||
bool jitterEnabled = false;
|
||||
if (prefs) {
|
||||
prefs->GetBoolPref("privacy.resistFingerprinting.reduceTimerPrecision.jitter", &jitterEnabled);
|
||||
prefs->SetBoolPref("privacy.resistFingerprinting.reduceTimerPrecision.jitter", enabled);
|
||||
}
|
||||
|
||||
return jitterEnabled;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -75,45 +54,36 @@ TEST(ResistFingerprinting, ReducePrecision_Assumptions) {
|
|||
}
|
||||
|
||||
TEST(ResistFingerprinting, ReducePrecision_Reciprocal) {
|
||||
bool jitterEnabled = setupJitter(false);
|
||||
// This one has a rounding error in the Reciprocal case:
|
||||
process(2064.8338460, nsRFPService::TimeScale::MicroSeconds, 20);
|
||||
// These are just big values
|
||||
process(1516305819, nsRFPService::TimeScale::MicroSeconds, 20);
|
||||
process(69053.12, nsRFPService::TimeScale::MicroSeconds, 20);
|
||||
cleanupJitter(jitterEnabled);
|
||||
}
|
||||
|
||||
TEST(ResistFingerprinting, ReducePrecision_KnownGood) {
|
||||
bool jitterEnabled = setupJitter(false);
|
||||
process(2064.8338460, nsRFPService::TimeScale::MilliSeconds, 20);
|
||||
process(69027.62, nsRFPService::TimeScale::MilliSeconds, 20);
|
||||
process(69053.12, nsRFPService::TimeScale::MilliSeconds, 20);
|
||||
cleanupJitter(jitterEnabled);
|
||||
}
|
||||
|
||||
TEST(ResistFingerprinting, ReducePrecision_KnownBad) {
|
||||
bool jitterEnabled = setupJitter(false);
|
||||
process(1054.842405, nsRFPService::TimeScale::MilliSeconds, 20);
|
||||
process(273.53038600000002, nsRFPService::TimeScale::MilliSeconds, 20);
|
||||
process(628.66686500000003, nsRFPService::TimeScale::MilliSeconds, 20);
|
||||
process(521.28919100000007, nsRFPService::TimeScale::MilliSeconds, 20);
|
||||
cleanupJitter(jitterEnabled);
|
||||
}
|
||||
|
||||
TEST(ResistFingerprinting, ReducePrecision_Edge) {
|
||||
bool jitterEnabled = setupJitter(false);
|
||||
process(2611.14, nsRFPService::TimeScale::MilliSeconds, 20);
|
||||
process(2611.16, nsRFPService::TimeScale::MilliSeconds, 20);
|
||||
process(2612.16, nsRFPService::TimeScale::MilliSeconds, 20);
|
||||
process(2601.64, nsRFPService::TimeScale::MilliSeconds, 20);
|
||||
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);
|
||||
ASSERT_EQ(result, 2611.14);
|
||||
|
@ -127,11 +97,9 @@ TEST(ResistFingerprinting, ReducePrecision_Expectations) {
|
|||
ASSERT_EQ(result, 2611.14);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(2611.13, nsRFPService::TimeScale::MilliSeconds, 20, 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);
|
||||
|
@ -145,7 +113,6 @@ TEST(ResistFingerprinting, ReducePrecision_ExpectedLossOfPrecision) {
|
|||
// 9007199254743568 can be represented exactly, but will be clamped to 9007199254743564
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(9007199254743568.0, nsRFPService::TimeScale::MicroSeconds, 5, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 9007199254743564.0);
|
||||
cleanupJitter(jitterEnabled);
|
||||
}
|
||||
|
||||
// Use an ugly but simple hack to turn an integer-based rand()
|
||||
|
@ -160,8 +127,6 @@ TEST(ResistFingerprinting, ReducePrecision_Aggressive) {
|
|||
return;
|
||||
}
|
||||
|
||||
bool jitterEnabled = setupJitter(false);
|
||||
|
||||
for (int i=0; i<10000; i++) {
|
||||
// Test three different time magnitudes, with decimals.
|
||||
// Note that we need separate variables for the different units, as scaling
|
||||
|
@ -206,101 +171,4 @@ TEST(ResistFingerprinting, ReducePrecision_Aggressive) {
|
|||
process(time3_us, nsRFPService::TimeScale::MicroSeconds, precision1);
|
||||
process(time3_us, nsRFPService::TimeScale::MicroSeconds, precision2);
|
||||
}
|
||||
cleanupJitter(jitterEnabled);
|
||||
}
|
||||
|
||||
|
||||
TEST(ResistFingerprinting, ReducePrecision_JitterTestVectors) {
|
||||
bool jitterEnabled = setupJitter(true);
|
||||
|
||||
/*
|
||||
* Here's our test vector. First we set the secret to the 16 byte value
|
||||
* 0x000102030405060708 0x101112131415161718
|
||||
*
|
||||
* Then we work with a resolution of 500 us which will bucket things as such:
|
||||
* Per-Clamp Buckets: [0, 500], [500, 1000], ...
|
||||
* Per-Hash Buckets: [0, 4000], [4000, 8000], ...
|
||||
*
|
||||
* The first two hash values should be
|
||||
* 0: SHA-256(0x000102030405060708 || 0x101112131415161718 || 0x0000000000000000)
|
||||
* 32ca0459 bdb518be c72096dc 2667cd7a a76f94e4 c33fa679 9a1bd499 bfa4ec57
|
||||
* 4000: SHA-256(0x000102030405060708 || 0x101112131415161718 || 0xa00f000000000000)
|
||||
* bd0bf282 120fd8c2 459c4d05 0170179c 25136f6f 70db5c82 5807558d 148c7745
|
||||
*
|
||||
* The midpoints are:
|
||||
* 0 : 32ca0459 % 500 = 130
|
||||
* 500 : bdb518be % 500 = 429
|
||||
* 1500: c72096dc % 500 = 311
|
||||
* 2000: 2667cd7a % 500 = 138
|
||||
* 2500: a76f94e4 % 500 = 159
|
||||
* 3000: c33fa679 % 500 = 435
|
||||
* 3500: 9a1bd499 % 500 = 246
|
||||
* 4000: bfa4ec57 % 500 = 463
|
||||
* 4500: bd0bf282 % 500 = 297
|
||||
* 5000: 120fd8c2 % 500 = 38
|
||||
* 5500: 459c4d05 % 500 = 357
|
||||
*/
|
||||
|
||||
// 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);
|
||||
|
||||
// Run the test vectors
|
||||
double result;
|
||||
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(1, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 0);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(129, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 0);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(130, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 500);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(131, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 500);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(499, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 500);
|
||||
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(500, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 500);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(600, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 500);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(928, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 500);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(929, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 1000);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(930, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 1000);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(1255, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 1000);
|
||||
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(4000, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 4000);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(4295, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 4000);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(4296, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 4000);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(4297, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 4500);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(4298, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 4500);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(4499, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 4500);
|
||||
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(4500, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 4500);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(4536, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 4500);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(4537, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 4500);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(4538, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 5000);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(4539, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 5000);
|
||||
result = nsRFPService::ReduceTimePrecisionImpl(5106, nsRFPService::TimeScale::MicroSeconds, 500, TimerPrecisionType::All);
|
||||
ASSERT_EQ(result, 5000);
|
||||
|
||||
cleanupJitter(jitterEnabled);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче