Bug 1440195 Actually integrate the context mix-in into the hash function calculation r?baku draft
authorTom Ritter <tom@mozilla.com>
Fri, 09 Mar 2018 16:26:56 -0600
changeset 767475 72e5474b314dd3dcd7d752f87d607d76ac23610e
parent 767474 8d7387c8395ff25e22957904ab50619390b5bd87
child 767476 9730f6de6ded6cadffb2ea0f04c5d987cd116e89
push id102611
push userbmo:tom@mozilla.com
push dateWed, 14 Mar 2018 17:48:37 +0000
reviewersbaku
bugs1440195
milestone60.0a1
Bug 1440195 Actually integrate the context mix-in into the hash function calculation r?baku MozReview-Commit-ID: 4k7w683UJOY
toolkit/components/resistfingerprinting/nsRFPService.cpp
toolkit/components/resistfingerprinting/tests/test_reduceprecision.cpp
--- a/toolkit/components/resistfingerprinting/nsRFPService.cpp
+++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp
@@ -143,86 +143,104 @@ nsRFPService::IsTimerPrecisionReductionE
 #define LRU_CACHE_SIZE         (45)
 #define HASH_DIGEST_SIZE_BITS  (256)
 #define HASH_DIGEST_SIZE_BYTES (HASH_DIGEST_SIZE_BITS / 8)
 
 class LRUCache final
 {
 public:
   LRUCache()
-    : mLock("mozilla.resistFingerprinting.LRUCache") {
+    : mLock("mozilla.resistFingerprinting.LRUCache")
+  {
     this->cache.SetLength(LRU_CACHE_SIZE);
   }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LRUCache)
 
-  nsCString Get(long long aKey) {
+  nsCString
+  Get(long long aKeyPart1, long long aKeyPart2)
+  {
     for (auto & cacheEntry : this->cache) {
       // Read optimistically befor locking
-      if (cacheEntry.key == aKey) {
+      if (cacheEntry.keyPart1 == aKeyPart1 &&
+          cacheEntry.keyPart2 == aKeyPart2) {
         MutexAutoLock lock(mLock);
 
         // Double check after we have a lock
-        if (MOZ_UNLIKELY(cacheEntry.key != aKey)) {
+        if (MOZ_UNLIKELY(cacheEntry.keyPart1 != aKeyPart1 ||
+                         cacheEntry.keyPart2 != aKeyPart2)) {
           // Got evicted in a race
-          long long tmp_key = cacheEntry.key;
+          long long tmp_keyPart1 = cacheEntry.keyPart1;
+          long long tmp_keyPart2 = cacheEntry.keyPart2;
           MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose,
-            ("LRU Cache HIT-MISS with %lli != %lli", aKey, tmp_key));
+            ("LRU Cache HIT-MISS with %lli != %lli and %lli != %lli",
+              aKeyPart1, tmp_keyPart1, aKeyPart2, tmp_keyPart2));
           return EmptyCString();
         }
 
         cacheEntry.accessTime = PR_Now();
         MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose,
-          ("LRU Cache HIT with %lli", aKey));
+          ("LRU Cache HIT with %lli %lli", aKeyPart1, aKeyPart2));
         return cacheEntry.data;
       }
     }
 
     return EmptyCString();
   }
 
-  void Store(long long aKey, const nsCString& aValue) {
+  void
+  Store(long long aKeyPart1, long long aKeyPart2, 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)) {
+      if (MOZ_UNLIKELY(cacheEntry.keyPart1 == aKeyPart1 &&
+                       cacheEntry.keyPart2 == aKeyPart2)) {
         // Another thread inserted before us, don't insert twice
         MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose,
-          ("LRU Cache DOUBLE STORE with %lli", aKey));
+          ("LRU Cache DOUBLE STORE with %lli %lli", aKeyPart1, aKeyPart2));
         return;
       }
       if (cacheEntry.accessTime < lowestKey->accessTime) {
         lowestKey = &cacheEntry;
       }
     }
 
-    lowestKey->key = aKey;
+    lowestKey->keyPart1 = aKeyPart1;
+    lowestKey->keyPart2 = aKeyPart2;
     lowestKey->data = aValue;
     lowestKey->accessTime = PR_Now();
-    MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose, ("LRU Cache STORE with %lli", aKey));
+    MOZ_LOG(gResistFingerprintingLog, LogLevel::Verbose,
+      ("LRU Cache STORE with %lli %lli", aKeyPart1, aKeyPart2));
   }
 
 
 private:
   ~LRUCache() = default;
 
-  struct CacheEntry {
-    Atomic<long long, Relaxed> key;
+  struct CacheEntry
+  {
+    Atomic<long long, Relaxed> keyPart1;
+    Atomic<long long, Relaxed> keyPart2;
     PRTime accessTime = 0;
     nsCString data;
 
-    CacheEntry() {
-      this->key = 0xFFFFFFFFFFFFFFFF;
+    CacheEntry()
+    {
+      this->keyPart1 = 0xFFFFFFFFFFFFFFFF;
+      this->keyPart2 = 0xFFFFFFFFFFFFFFFF;
       this->accessTime = 0;
       this->data = nullptr;
     }
-    CacheEntry(const CacheEntry &obj) {
-      this->key.exchange(obj.key);
+    CacheEntry(const CacheEntry &obj)
+    {
+      this->keyPart1.exchange(obj.keyPart1);
+      this->keyPart2.exchange(obj.keyPart2);
       this->accessTime = obj.accessTime;
       this->data = obj.data;
     }
   };
 
   AutoTArray<CacheEntry, LRU_CACHE_SIZE> cache;
   mozilla::Mutex mLock;
 };
@@ -338,17 +356,17 @@ nsRFPService::RandomMidpoint(long long a
    * 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 = cache->Get(extraClampedTime);
+  nsCString hashResult = cache->Get(extraClampedTime, aContextMixin);
 
   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;
       }
@@ -397,25 +415,28 @@ nsRFPService::RandomMidpoint(long long a
      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 *)&aContextMixin, sizeof(aContextMixin));
+     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
-     cache->Store(extraClampedTime, derivedSecret);
+     cache->Store(extraClampedTime, aContextMixin, derivedSecret);
      hashResult = derivedSecret;
   }
 
   // Offset the appropriate index into the hash output, and then turn it into a random midpoint
   // between 0 and aResolutionUSec. Sometimes out input time is negative, we ride the negative
   // out to the end until we start doing pointer math. (We also triple check we're in bounds.)
   int byteOffset = abs(((aClampedTimeUSec - extraClampedTime) / aResolutionUSec) * 4);
   if (MOZ_UNLIKELY(byteOffset > (HASH_DIGEST_SIZE_BYTES - 4))) {
--- a/toolkit/components/resistfingerprinting/tests/test_reduceprecision.cpp
+++ b/toolkit/components/resistfingerprinting/tests/test_reduceprecision.cpp
@@ -217,90 +217,91 @@ TEST(ResistFingerprinting, ReducePrecisi
    * 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
+   *    0:    SHA-256(0x0001020304050607 || 0x1011121314151617 || 0xa00f000000000000 || 0x0000000000000000)
+   *          78d2d811 804fcaa4 7d472a1e 9fe043d2 dd77b3df 06c1c4f2 9f35f28a e3afbec0
+   *    4000: SHA-256(0x0001020304050607 || 0x1011121314151617 || 0xa00f000000000000 || 0xa00f000000000000)
+   *          1571bf19 92a89cd0 829259d5 b260a4a6 b8da8ad5 2e3ae33c 5571bb8d 8f69cca6
    *
-   * 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
+   * The midpoints are (if you're doing this manually, you need to correct endian-ness):
+   *   0   : 78d2d811 % 500 = 328
+   *   500 : 804fcaa4 % 500 = 48
+   *   1000: 7d472a1e % 500 = 293
+   *   1500: 9fe043d2 % 500 = 275
+   *   2000: dd77b3df % 500 = 297
+   *   2500: 06c1c4f2 % 500 = 242
+   *   3000: 9f35f28a % 500 = 247
+   *   3500: e3afbec0 % 500 = 339
+   *   4000: 1571bf19 % 500 = 225
+   *   4500: 92a89cd0 % 500 = 198
+   *   5000: 829259d5 % 500 = 218
+   *   5500: b260a4a6 % 500 = 14
    */
 
   // 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, -1, &throwAway, hardcodedSecret);
 
   // Run the test vectors
   double result;
 
-  result = nsRFPService::ReduceTimePrecisionImpl(1, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(1, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 0);
-  result = nsRFPService::ReduceTimePrecisionImpl(129, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(327, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 0);
-  result = nsRFPService::ReduceTimePrecisionImpl(130, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(328, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 500);
-  result = nsRFPService::ReduceTimePrecisionImpl(131, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(329, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 500);
-  result = nsRFPService::ReduceTimePrecisionImpl(499, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(499, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 500);
 
-  result = nsRFPService::ReduceTimePrecisionImpl(500, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(500, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 500);
-  result = nsRFPService::ReduceTimePrecisionImpl(600, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(540, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 500);
-  result = nsRFPService::ReduceTimePrecisionImpl(928, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(547, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 500);
-  result = nsRFPService::ReduceTimePrecisionImpl(929, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(548, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 1000);
-  result = nsRFPService::ReduceTimePrecisionImpl(930, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(930, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 1000);
-  result = nsRFPService::ReduceTimePrecisionImpl(1255, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(1255, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 1000);
 
-  result = nsRFPService::ReduceTimePrecisionImpl(4000, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4000, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 4000);
-  result = nsRFPService::ReduceTimePrecisionImpl(4295, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4220, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 4000);
-  result = nsRFPService::ReduceTimePrecisionImpl(4296, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4224, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 4000);
-  result = nsRFPService::ReduceTimePrecisionImpl(4297, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4225, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 4500);
-  result = nsRFPService::ReduceTimePrecisionImpl(4298, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4340, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 4500);
-  result = nsRFPService::ReduceTimePrecisionImpl(4499, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4499, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 4500);
 
-  result = nsRFPService::ReduceTimePrecisionImpl(4500, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4500, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 4500);
-  result = nsRFPService::ReduceTimePrecisionImpl(4536, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4536, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 4500);
-  result = nsRFPService::ReduceTimePrecisionImpl(4537, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4695, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 4500);
-  result = nsRFPService::ReduceTimePrecisionImpl(4538, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4698, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 5000);
-  result = nsRFPService::ReduceTimePrecisionImpl(4539, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(4726, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 5000);
-  result = nsRFPService::ReduceTimePrecisionImpl(5106, nsRFPService::TimeScale::MicroSeconds, 500, -1, TimerPrecisionType::All);
+  result = nsRFPService::ReduceTimePrecisionImpl(5106, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All);
   ASSERT_EQ(result, 5000);
 
   cleanupJitter(jitterEnabled);
 }