tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

browser_canvascompare_popups_data.js (6969B)


      1 /**
      2 * This test compares canvas randomization on a parent and a data popup, and ensures that the canvas randomization key
      3 * is inherited correctly.  (e.g. that the canvases have the same random value)
      4 *
      5 * Covers the following cases:
      6 *  - RFP/FPP is disabled entirely
      7 *  - RFP is enabled entirely, and only in PBM
      8 *  - FPP is enabled entirely, and only in PBM
      9 *  - A normal window when FPP is enabled globally and RFP is enabled in PBM, Protections Enabled and Disabled
     10 *
     11 */
     12 
     13 "use strict";
     14 
     15 // =============================================================================================
     16 
     17 /**
     18 * Compares two Uint8Arrays and returns the number of bits that are different.
     19 *
     20 * @param {Uint8ClampedArray} arr1 - The first Uint8ClampedArray to compare.
     21 * @param {Uint8ClampedArray} arr2 - The second Uint8ClampedArray to compare.
     22 * @returns {number} - The number of bits that are different between the two
     23 *                     arrays.
     24 */
     25 function countDifferencesInUint8Arrays(arr1, arr2) {
     26  let count = 0;
     27  for (let i = 0; i < arr1.length; i++) {
     28    let diff = arr1[i] ^ arr2[i];
     29    while (diff > 0) {
     30      count += diff & 1;
     31      diff >>= 1;
     32    }
     33  }
     34  return count;
     35 }
     36 
     37 // =============================================================================================
     38 
     39 async function testCanvasRandomization(result, expectedResults, extraData) {
     40  let testDesc = extraData.testDesc;
     41 
     42  let parent = result.mine;
     43  let child = result.theirs;
     44 
     45  let differencesInRandom = countDifferencesInUint8Arrays(parent, child);
     46  let differencesFromUnmodifiedParent = countDifferencesInUint8Arrays(
     47    UNMODIFIED_CANVAS_DATA,
     48    parent
     49  );
     50  let differencesFromUnmodifiedChild = countDifferencesInUint8Arrays(
     51    UNMODIFIED_CANVAS_DATA,
     52    child
     53  );
     54 
     55  Assert.greaterOrEqual(
     56    differencesFromUnmodifiedParent,
     57    expectedResults[0],
     58    `Checking ${testDesc} for canvas randomization, comparing parent - lower bound for random pixels.`
     59  );
     60  Assert.lessOrEqual(
     61    differencesFromUnmodifiedParent,
     62    expectedResults[1],
     63    `Checking ${testDesc} for canvas randomization, comparing parent - upper bound for random pixels.`
     64  );
     65 
     66  Assert.greaterOrEqual(
     67    differencesFromUnmodifiedChild,
     68    expectedResults[0],
     69    `Checking ${testDesc} for canvas randomization, comparing child - lower bound for random pixels.`
     70  );
     71  Assert.lessOrEqual(
     72    differencesFromUnmodifiedChild,
     73    expectedResults[1],
     74    `Checking ${testDesc} for canvas randomization, comparing child - upper bound for random pixels.`
     75  );
     76 
     77  Assert.greaterOrEqual(
     78    differencesInRandom,
     79    expectedResults[2],
     80    `Checking ${testDesc} and comparing randomization - lower bound for different random pixels.`
     81  );
     82  Assert.lessOrEqual(
     83    differencesInRandom,
     84    expectedResults[3],
     85    `Checking ${testDesc} and comparing randomization - upper bound for different random pixels.`
     86  );
     87 }
     88 
     89 requestLongerTimeout(2);
     90 
     91 let expectedResults = {};
     92 var UNMODIFIED_CANVAS_DATA = undefined;
     93 
     94 add_setup(async function () {
     95  // Make sure Old Randomization is the only one on
     96  await SpecialPowers.pushPrefEnv({
     97    set: [
     98      [
     99        "privacy.fingerprintingProtection.overrides",
    100        "-EfficientCanvasRandomization,+CanvasRandomization",
    101      ],
    102    ],
    103  });
    104 
    105  // Disable the fingerprinting randomization.
    106  await SpecialPowers.pushPrefEnv({
    107    set: [
    108      ["privacy.fingerprintingProtection", false],
    109      ["privacy.fingerprintingProtection.pbmode", false],
    110      ["privacy.resistFingerprinting", false],
    111    ],
    112  });
    113 
    114  let extractCanvasData = function () {
    115    let offscreenCanvas = new OffscreenCanvas(100, 100);
    116 
    117    const context = offscreenCanvas.getContext("2d");
    118 
    119    // Draw a red rectangle
    120    context.fillStyle = "#EE2222";
    121    context.fillRect(0, 0, 100, 100);
    122    context.fillStyle = "#2222EE";
    123    context.fillRect(20, 20, 100, 100);
    124 
    125    const imageData = context.getImageData(0, 0, 100, 100);
    126    return imageData.data;
    127  };
    128 
    129  function runExtractCanvasData(tab) {
    130    let code = extractCanvasData.toString();
    131    return SpecialPowers.spawn(tab.linkedBrowser, [code], async funccode => {
    132      await content.eval(`var extractCanvasData = ${funccode}`);
    133      let result = await content.eval(`extractCanvasData()`);
    134      return result;
    135    });
    136  }
    137 
    138  const emptyPage =
    139    getRootDirectory(gTestPath).replace(
    140      "chrome://mochitests/content",
    141      "https://example.com"
    142    ) + "empty.html";
    143 
    144  // Open a tab for extracting the canvas data.
    145  const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, emptyPage);
    146 
    147  let data = await runExtractCanvasData(tab);
    148  UNMODIFIED_CANVAS_DATA = data;
    149 
    150  BrowserTestUtils.removeTab(tab);
    151  await SpecialPowers.popPrefEnv();
    152 });
    153 
    154 // Be sure to always use `let expectedResults = structuredClone(allNotSpoofed)` to do a
    155 //   deep copy and avoiding corrupting the original 'const' object
    156 // The first value represents the minimum number of random pixels we should see
    157 // The second, the maximum number of random pixels
    158 // The third, the minimum number of differences between the canvases of the parent and child
    159 // The fourth, the maximum number of differences between the canvases of the parent and child
    160 const rfpFullyRandomized = [10000, 999999999, 20000, 999999999];
    161 const fppRandomizedSameDomain = [1, 260, 0, 0];
    162 const noRandom = [0, 0, 0, 0];
    163 
    164 let uri = `https://${IFRAME_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_canvascompare_data_popupmaker.html`;
    165 
    166 expectedResults = structuredClone(noRandom);
    167 add_task(
    168  defaultsTest.bind(null, uri, testCanvasRandomization, expectedResults)
    169 );
    170 
    171 expectedResults = structuredClone(fppRandomizedSameDomain);
    172 add_task(
    173  defaultsPBMTest.bind(null, uri, testCanvasRandomization, expectedResults)
    174 );
    175 
    176 expectedResults = structuredClone(rfpFullyRandomized);
    177 add_task(
    178  simpleRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
    179 );
    180 
    181 // Test a private window with RFP enabled in PBMode
    182 expectedResults = structuredClone(rfpFullyRandomized);
    183 add_task(
    184  simplePBMRFPTest.bind(null, uri, testCanvasRandomization, expectedResults)
    185 );
    186 
    187 expectedResults = structuredClone(fppRandomizedSameDomain);
    188 add_task(
    189  simpleFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
    190 );
    191 
    192 // Test a Private Window with FPP Enabled in PBM
    193 expectedResults = structuredClone(fppRandomizedSameDomain);
    194 add_task(
    195  simplePBMFPPTest.bind(null, uri, testCanvasRandomization, expectedResults)
    196 );
    197 
    198 // Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, No Protections
    199 expectedResults = structuredClone(noRandom);
    200 add_task(
    201  RFPPBMFPP_NormalMode_NoProtectionsTest.bind(
    202    null,
    203    uri,
    204    testCanvasRandomization,
    205    expectedResults
    206  )
    207 );
    208 
    209 // Test RFP Enabled in PBM and FPP enabled in Normal Browsing Mode, Protections Enabled
    210 expectedResults = structuredClone(fppRandomizedSameDomain);
    211 add_task(
    212  RFPPBMFPP_NormalMode_ProtectionsTest.bind(
    213    null,
    214    uri,
    215    testCanvasRandomization,
    216    expectedResults
    217  )
    218 );