tor-browser

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

commit 988dc26f437bd875ce8417a485cb50b550c86876
parent 88e897fd97c61714e4f03d19ef23b6de43bfcb1c
Author: Tom Ritter <tom@mozilla.com>
Date:   Wed, 17 Dec 2025 17:44:39 +0000

Bug 1976287: Add an RFPTarget for adding a random suffix to the WebGL Vendor r=timhuang

Differential Revision: https://phabricator.services.mozilla.com/D276243

Diffstat:
Mdom/canvas/ClientWebGLContext.cpp | 20++++++++++++++++++++
Mdom/canvas/test/webgl-mochitest/mochitest.toml | 6++++++
Adom/canvas/test/webgl-mochitest/test_webgl_vendor_randomize_fpp.html | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adom/canvas/test/webgl-mochitest/test_webgl_vendor_randomize_fpp_explicit.html | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adom/canvas/test/webgl-mochitest/test_webgl_vendor_randomize_rfp.html | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/components/resistfingerprinting/RFPTargets.inc | 1+
6 files changed, 197 insertions(+), 0 deletions(-)

diff --git a/dom/canvas/ClientWebGLContext.cpp b/dom/canvas/ClientWebGLContext.cpp @@ -20,7 +20,9 @@ #include "gfxCrashReporterUtils.h" #include "js/PropertyAndElement.h" // JS_DefineElement #include "js/ScalarType.h" // js::Scalar::Type +#include "mozilla/Base64.h" #include "mozilla/EnumeratedRange.h" +#include "mozilla/RandomNum.h" #include "mozilla/ResultVariant.h" #include "mozilla/ScopeExit.h" #include "mozilla/StaticPrefs_webgl.h" @@ -2458,6 +2460,24 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, if (ShouldResistFingerprinting(RFPTarget::WebGLRenderInfo)) { ret = Some("Mozilla"_ns); } else if (ShouldResistFingerprinting( + RFPTarget::WebGLVendorRandomize)) { + // Generate "Mozilla <Base64(uint64)>" + auto randomValue = RandomUint64(); + if (randomValue.isSome()) { + uint64_t value = randomValue.value(); + nsCString base64; + nsresult rv = + Base64Encode(reinterpret_cast<const char*>(&value), + sizeof(value), base64); + if (NS_SUCCEEDED(rv)) { + ret = Some(std::string("Mozilla ") + base64.get()); + } else { + ret = Some("Mozilla"_ns); + } + } else { + ret = Some("Mozilla"_ns); + } + } else if (ShouldResistFingerprinting( RFPTarget::WebGLVendorConstant)) { ret = Some("Mozilla"_ns); } else { diff --git a/dom/canvas/test/webgl-mochitest/mochitest.toml b/dom/canvas/test/webgl-mochitest/mochitest.toml @@ -285,6 +285,12 @@ skip-if = [ "os == 'android'", # bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests ] +["test_webgl_vendor_randomize_fpp.html"] + +["test_webgl_vendor_randomize_fpp_explicit.html"] + +["test_webgl_vendor_randomize_rfp.html"] + ["test_webgl_vendor_sanitize_fpp.html"] ["test_webgl_vendor_sanitize_fpp_explicit.html"] diff --git a/dom/canvas/test/webgl-mochitest/test_webgl_vendor_randomize_fpp.html b/dom/canvas/test/webgl-mochitest/test_webgl_vendor_randomize_fpp.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>WebGL Vendor Randomize with FPP - Vendor should be randomized</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script> +/* global SimpleTest SpecialPowers */ +SimpleTest.waitForExplicitFinish(); +document.addEventListener("DOMContentLoaded", async function() { + // Enable FPP with WebGLVendorRandomize + await SpecialPowers.pushPrefEnv({ + set: [ + ["privacy.fingerprintingProtection", true], + ["privacy.fingerprintingProtection.overrides", "+WebGLVendorRandomize"], + ["privacy.resistFingerprinting", false] + ] + }); + + let canvas = document.body.appendChild(document.createElement("canvas")); + if (!canvas) { + SimpleTest.ok(false, "Cannot create canvas"); + SimpleTest.finish(); + return; + } + + let gl = canvas.getContext("webgl"); + if (!gl) { + SimpleTest.ok(false, "Cannot get WebGL context"); + SimpleTest.finish(); + return; + } + + // Try to get the WEBGL_debug_renderer_info extension + let ext = gl.getExtension("WEBGL_debug_renderer_info"); + if (!ext) { + SimpleTest.ok(false, "WEBGL_debug_renderer_info extension should be available with FPP"); + SimpleTest.finish(); + return; + } + + // With FPP and WebGLVendorRandomize enabled, the vendor should be "Mozilla <Base64>" + let vendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL); + + // Verify it starts with "Mozilla " + SimpleTest.ok(vendor.startsWith("Mozilla "), + "UNMASKED_VENDOR_WEBGL should start with 'Mozilla ' with WebGLVendorRandomize enabled. Got: " + vendor); + + // Verify it has additional content (the Base64 part) + SimpleTest.ok(vendor.length > "Mozilla ".length, + "UNMASKED_VENDOR_WEBGL should have Base64 suffix. Got: " + vendor); + + // Verify the Base64 part looks like Base64 (alphanumeric and =) + let base64Part = vendor.substring("Mozilla ".length); + let base64Regex = /^[A-Za-z0-9+/=]+$/; + SimpleTest.ok(base64Regex.test(base64Part), + "Base64 part should be valid Base64. Got: " + base64Part); + + SimpleTest.finish(); +}); +</script> diff --git a/dom/canvas/test/webgl-mochitest/test_webgl_vendor_randomize_fpp_explicit.html b/dom/canvas/test/webgl-mochitest/test_webgl_vendor_randomize_fpp_explicit.html @@ -0,0 +1,60 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>WebGL Vendor Randomize with FPP (explicit) - Vendor should be randomized</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script> +/* global SimpleTest SpecialPowers */ +SimpleTest.waitForExplicitFinish(); +document.addEventListener("DOMContentLoaded", async function() { + // Enable FPP with explicit -AllTargets,+WebGLVendorRandomize + // This ensures only WebGLVendorRandomize is active, nothing else + await SpecialPowers.pushPrefEnv({ + set: [ + ["privacy.fingerprintingProtection", true], + ["privacy.fingerprintingProtection.overrides", "-AllTargets,+WebGLVendorRandomize"], + ["privacy.resistFingerprinting", false] + ] + }); + + let canvas = document.body.appendChild(document.createElement("canvas")); + if (!canvas) { + SimpleTest.ok(false, "Cannot create canvas"); + SimpleTest.finish(); + return; + } + + let gl = canvas.getContext("webgl"); + if (!gl) { + SimpleTest.ok(false, "Cannot get WebGL context"); + SimpleTest.finish(); + return; + } + + // Try to get the WEBGL_debug_renderer_info extension + let ext = gl.getExtension("WEBGL_debug_renderer_info"); + if (!ext) { + SimpleTest.ok(false, "WEBGL_debug_renderer_info extension should be available with FPP"); + SimpleTest.finish(); + return; + } + + // With FPP and WebGLVendorRandomize enabled, the vendor should be "Mozilla <Base64>" + let vendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL); + + // Verify it starts with "Mozilla " + SimpleTest.ok(vendor.startsWith("Mozilla "), + "UNMASKED_VENDOR_WEBGL should start with 'Mozilla ' with WebGLVendorRandomize enabled. Got: " + vendor); + + // Verify it has additional content (the Base64 part) + SimpleTest.ok(vendor.length > "Mozilla ".length, + "UNMASKED_VENDOR_WEBGL should have Base64 suffix. Got: " + vendor); + + // Verify the Base64 part looks like Base64 (alphanumeric and =) + let base64Part = vendor.substring("Mozilla ".length); + let base64Regex = /^[A-Za-z0-9+/=]+$/; + SimpleTest.ok(base64Regex.test(base64Part), + "Base64 part should be valid Base64. Got: " + base64Part); + + SimpleTest.finish(); +}); +</script> diff --git a/dom/canvas/test/webgl-mochitest/test_webgl_vendor_randomize_rfp.html b/dom/canvas/test/webgl-mochitest/test_webgl_vendor_randomize_rfp.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>WebGL Vendor Randomize with RFP - WebGLRenderInfo takes precedence</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script> +/* global SimpleTest SpecialPowers */ +SimpleTest.waitForExplicitFinish(); +document.addEventListener("DOMContentLoaded", async function() { + // Enable RFP - WebGLRenderInfo should take precedence over WebGLVendorRandomize + await SpecialPowers.pushPrefEnv({ + set: [ + ["privacy.resistFingerprinting", true], + ["privacy.fingerprintingProtection.overrides", "+WebGLVendorRandomize"], + ["privacy.fingerprintingProtection", true], + ] + }); + + let canvas = document.body.appendChild(document.createElement("canvas")); + if (!canvas) { + SimpleTest.ok(false, "Cannot create canvas"); + SimpleTest.finish(); + return; + } + + let gl = canvas.getContext("webgl"); + if (!gl) { + SimpleTest.ok(false, "Cannot get WebGL context"); + SimpleTest.finish(); + return; + } + + // Try to get the WEBGL_debug_renderer_info extension + let ext = gl.getExtension("WEBGL_debug_renderer_info"); + if (!ext) { + SimpleTest.ok(false, "WEBGL_debug_renderer_info extension should be available in RFP"); + SimpleTest.finish(); + return; + } + + // With RFP enabled, WebGLRenderInfo takes precedence over WebGLVendorRandomize + // The vendor should be "Mozilla", NOT "Mozilla <Base64>" + let vendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL); + SimpleTest.is(vendor, "Mozilla", "UNMASKED_VENDOR_WEBGL should be 'Mozilla' with RFP enabled (WebGLRenderInfo precedence)"); + + // Also check renderer for completeness + let renderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL); + SimpleTest.is(renderer, "Mozilla", "UNMASKED_RENDERER_WEBGL should be 'Mozilla' with RFP enabled"); + + SimpleTest.finish(); +}); +</script> diff --git a/toolkit/components/resistfingerprinting/RFPTargets.inc b/toolkit/components/resistfingerprinting/RFPTargets.inc @@ -111,6 +111,7 @@ ITEM_VALUE(WebGLRandomization, 75) ITEM_VALUE(EfficientCanvasRandomization, 76) ITEM_VALUE(WebGLVendorSanitize, 77) ITEM_VALUE(WebGLVendorConstant, 78) +ITEM_VALUE(WebGLVendorRandomize, 79) // !!! Adding a new target? Rename PointerId and repurpose it.