tor-browser

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

test_fingerprinting_resistance.html (3658B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4 <meta charset="utf-8">
      5 <script src="mediaStreamPlayback.js"></script>
      6 </head>
      7 <body>
      8 <script>
      9 /* global SimpleTest SpecialPowers */
     10 
     11 async function testEnumerateDevices(header,
     12                                    expectedCameras,
     13                                    expectedMicrophones,
     14                                    expectedSpeakers) {
     15  const devices = await navigator.mediaDevices.enumerateDevices();
     16  const cams = devices.filter(({kind}) => kind == "videoinput");
     17  const mics = devices.filter(({kind}) => kind == "audioinput");
     18  const spkr = devices.filter(({kind}) => kind == "audiooutput");
     19 
     20  SimpleTest.is(cams.length, expectedCameras, `${header}: cameras`);
     21  SimpleTest.is(mics.length, expectedMicrophones, `${header}: microphones`);
     22  if (expectedSpeakers !== undefined) {
     23    SimpleTest.is(spkr.length, expectedSpeakers, `${header}: speakers`);
     24  }
     25 }
     26 
     27 async function testGetUserMedia(expectDevices) {
     28  const constraints = [
     29    {audio: true},
     30    {video: true},
     31    {audio: true, video: true},
     32    {video: {width: {min: 1e9}}}, // impossible
     33    {audio: {channelCount: {exact: 1e3}}}, // impossible
     34  ];
     35  for (let constraint of constraints) {
     36    let message = "getUserMedia(" + JSON.stringify(constraint) + ")";
     37    try {
     38      let stream = await navigator.mediaDevices.getUserMedia(constraint);
     39      SimpleTest.ok(expectDevices, message + " resolved");
     40      if (!expectDevices) {
     41        continue;
     42      }
     43 
     44      // We only do testGetUserMedia(true) when privacy.resistFingerprinting
     45      // is true, test if MediaStreamTrack.label is spoofed.
     46      for (let track of stream.getTracks()) {
     47        switch (track.kind) {
     48          case "audio":
     49            SimpleTest.is(track.label, "Internal Microphone", "AudioStreamTrack.label");
     50            break;
     51          case "video":
     52            SimpleTest.is(track.label, "Internal Camera", "VideoStreamTrack.label");
     53            break;
     54          default:
     55            SimpleTest.ok(false, "Unknown kind: " + track.kind);
     56            break;
     57        }
     58        track.stop();
     59      }
     60    } catch (e) {
     61      if (!expectDevices) {
     62        SimpleTest.is(e.name, "NotAllowedError", message + " throws NotAllowedError");
     63      } else {
     64        SimpleTest.ok(false, message + " failed: " + e);
     65      }
     66    }
     67  }
     68 }
     69 
     70 async function testPreGum() {
     71  await SpecialPowers.pushPrefEnv({
     72    set: [
     73      ["privacy.resistFingerprinting", true],
     74      ["media.navigator.streams.fake", true]
     75    ]
     76  });
     77  await testEnumerateDevices("testPreGum w/resist", 1, 1, 0);
     78  await testGetUserMedia(true); // should get audio and video streams
     79 }
     80 
     81 async function testNoDevices() {
     82  await SpecialPowers.pushPrefEnv({
     83    set: [
     84      ["privacy.resistFingerprinting", false],
     85      ["media.navigator.permission.device", false],
     86      ["media.navigator.streams.fake", false],
     87      ["media.audio_loopback_dev", "foo"],
     88      ["media.video_loopback_dev", "bar"]
     89    ]
     90  });
     91  await testEnumerateDevices("testNoDevices wo/resist", 0, 0);
     92  await SpecialPowers.pushPrefEnv({
     93    set: [
     94      ["privacy.resistFingerprinting", true]
     95    ]
     96  });
     97  await testEnumerateDevices("testNoDevices w/resist", 1, 1, 1);
     98  await testGetUserMedia(false); // should reject with NotAllowedError
     99 }
    100 
    101 createHTML({
    102  title: "Neutralize the threat of fingerprinting of media devices API when 'privacy.resistFingerprinting' is true",
    103  bug: "1372073"
    104 });
    105 
    106 runTest(async () => {
    107  // Make sure enumerateDevices and getUserMedia work when
    108  // privacy.resistFingerprinting is true.
    109  await testPreGum();
    110 
    111  // Test that absence of devices can't be detected.
    112  await testNoDevices();
    113 });
    114 </script>
    115 </body>
    116 </html>