diff --git a/README.md b/README.md index a2292fe..ae96df6 100644 --- a/README.md +++ b/README.md @@ -348,7 +348,7 @@ Because I don't have a dataset of WebGL fingerprints to rotate against, WebGL fi This repository includes a demo site (see [here](https://github.com/daijro/camoufox/blob/main/scripts/examples/webgl.html)) that prints your browser's WebGL parameters. You can use this site to generate WebGL fingerprints for Camoufox from other devices. - + ### Properties @@ -356,19 +356,22 @@ Camoufox supports spoofing WebGL parameters, supported extensions, context attri **Note**: Do NOT randomly assign values to these properties. WAFs hash your WebGL fingerprint and compare it against a dataset. Randomly assigning values will lead to detection as an unknown device. -| Property | Description | Example | -| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | -| webgl:renderer | Spoofs the name of the unmasked WebGL renderer. | `"NVIDIA GeForce GTX 980, or similar"` | -| webgl:vendor | Spoofs the name of the unmasked WebGL vendor. | `"NVIDIA Corporation"` | -| webgl:supportedExtensions | An array of supported WebGL extensions ([full list](https://registry.khronos.org/webgl/extensions/)). | `["ANGLE_instanced_arrays", "EXT_color_buffer_float", "EXT_disjoint_timer_query", ...]` | -| webgl:contextAttributes | A dictionary of WebGL context attributes. | `{"alpha": true, "antialias": true, "depth": true, ...}` | -| webgl2:contextAttributes | The same as `webgl:contextAttributes`, but for WebGL2. | `{"alpha": true, "antialias": true, "depth": true, ...}` | -| webgl:parameters | A dictionary of WebGL parameters. Keys must be GL enums, and values are the values to spoof them as. | `{"2849": 1, "2884": false, "2928": [0, 1], ...}` | -| webgl2:parameters | The same as `webgl:parameters`, but for WebGL2. | `{"2849": 1, "2884": false, "2928": [0, 1], ...}` | -| webgl:shaderPrecisionFormats | A dictionary of WebGL shader precision formats. Keys are formatted as `","`. | `{"35633,36336": {"rangeMin": 127, "rangeMax": 127, "precision": 23}, ...}` | -| webgl2:shaderPrecisionFormats | The same as `webGL:shaderPrecisionFormats`, but for WebGL2. | `{"35633,36336": {"rangeMin": 127, "rangeMax": 127, "precision": 23}, ...}` | -| webgl:shaderPrecisionFormats:blockIfNotDefined | If set to `true`, only the shader percisions in `webgl:shaderPrecisionFormats` will be passed. Everything else will be blocked. | `true` | -| webgl2:shaderPrecisionFormats:blockIfNotDefined | If set to `true`, only the shader percisions in `webgl2:shaderPrecisionFormats` will be passed. Everything else will be blocked. | `true` | +| Property | Description | Example | +| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| webgl:renderer | Spoofs the name of the unmasked WebGL renderer. | `"NVIDIA GeForce GTX 980, or similar"` | +| webgl:vendor | Spoofs the name of the unmasked WebGL vendor. | `"NVIDIA Corporation"` | +| webgl:supportedExtensions | An array of supported WebGL extensions ([full list](https://registry.khronos.org/webgl/extensions/)). | `["ANGLE_instanced_arrays", "EXT_color_buffer_float", "EXT_disjoint_timer_query", ...]` | +| webgl2:supportedExtensions | The same as `webgl:supportedExtensions`, but for WebGL2. | `["ANGLE_instanced_arrays", "EXT_color_buffer_float", "EXT_disjoint_timer_query", ...]` | +| webgl:contextAttributes | A dictionary of WebGL context attributes. | `{"alpha": true, "antialias": true, "depth": true, ...}` | +| webgl2:contextAttributes | The same as `webgl:contextAttributes`, but for WebGL2. | `{"alpha": true, "antialias": true, "depth": true, ...}` | +| webgl:parameters | A dictionary of WebGL parameters. Keys must be GL enums, and values are the values to spoof them as. | `{"2849": 1, "2884": false, "2928": [0, 1], ...}` | +| webgl2:parameters | The same as `webgl:parameters`, but for WebGL2. | `{"2849": 1, "2884": false, "2928": [0, 1], ...}` | +| webgl:parameters:blockIfNotDefined | If set to `true`, only the parameters in `webgl:parameters` will be allowed. Can be dangerous if not used correctly. | `true`/`false` | +| webgl2:parameters:blockIfNotDefined | If set to `true`, only the parameters in `webgl2:parameters` will be allowed. Can be dangerous if not used correctly. | `true`/`false` | +| webgl:shaderPrecisionFormats | A dictionary of WebGL shader precision formats. Keys are formatted as `","`. | `{"35633,36336": {"rangeMin": 127, "rangeMax": 127, "precision": 23}, ...}` | +| webgl2:shaderPrecisionFormats | The same as `webGL:shaderPrecisionFormats`, but for WebGL2. | `{"35633,36336": {"rangeMin": 127, "rangeMax": 127, "precision": 23}, ...}` | +| webgl:shaderPrecisionFormats:blockIfNotDefined | If set to `true`, only the shader percisions in `webgl:shaderPrecisionFormats` will be allowed. | `true`/`false` | +| webgl2:shaderPrecisionFormats:blockIfNotDefined | If set to `true`, only the shader percisions in `webgl2:shaderPrecisionFormats` will be allowed. | `true`/`false` | diff --git a/additions/camoucfg/MaskConfig.hpp b/additions/camoucfg/MaskConfig.hpp index 3e63b43..ba240c1 100644 --- a/additions/camoucfg/MaskConfig.hpp +++ b/additions/camoucfg/MaskConfig.hpp @@ -15,6 +15,7 @@ Written by daijro. #include #include #include +#include #ifdef _WIN32 # include @@ -217,14 +218,15 @@ inline std::optional GetAttribute(const std::string attrib, bool isWebGL2) { return value.value().get(); } -inline std::optional> GLParam( - uint32_t pname, bool isWebGL2) { +inline std::optional< + std::variant> +GLParam(uint32_t pname, bool isWebGL2) { auto value = MaskConfig::GetNested(isWebGL2 ? "webgl2:parameters" : "webgl:parameters", std::to_string(pname)); if (!value) return std::nullopt; auto data = value.value(); - // cast the data and return + if (data.is_null()) return std::nullptr_t(); if (data.is_number_integer()) return data.get(); if (data.is_boolean()) return data.get(); if (data.is_number_float()) return data.get(); @@ -249,7 +251,8 @@ inline std::vector MParamGLVector(uint32_t pname, bool isWebGL2) { if (auto value = MaskConfig::GetNested( isWebGL2 ? "webgl2:parameters" : "webgl:parameters", - std::to_string(pname)); value.has_value()) { + std::to_string(pname)); + value.has_value()) { if (value.value().is_array()) { std::array result = value.value().get>(); return std::vector(result.begin(), result.end()); diff --git a/patches/webgl-spoofing.patch b/patches/webgl-spoofing.patch index 104bfac..91af780 100644 --- a/patches/webgl-spoofing.patch +++ b/patches/webgl-spoofing.patch @@ -1,17 +1,18 @@ diff --git a/dom/canvas/ClientWebGLContext.cpp b/dom/canvas/ClientWebGLContext.cpp -index db60868f65..afed6eeb7c 100644 +index db60868f65..7361f0fc9c 100644 --- a/dom/canvas/ClientWebGLContext.cpp +++ b/dom/canvas/ClientWebGLContext.cpp -@@ -4,6 +4,8 @@ +@@ -4,6 +4,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ClientWebGLContext.h" +#include "MaskConfig.hpp" +#include ++#include #include -@@ -744,6 +746,13 @@ void ClientWebGLContext::SetDrawingBufferColorSpace( +@@ -744,6 +747,13 @@ void ClientWebGLContext::SetDrawingBufferColorSpace( Run(*mDrawingBufferColorSpace); } @@ -25,7 +26,7 @@ index db60868f65..afed6eeb7c 100644 void ClientWebGLContext::GetContextAttributes( dom::Nullable& retval) { retval.SetNull(); -@@ -754,14 +763,38 @@ void ClientWebGLContext::GetContextAttributes( +@@ -754,14 +764,38 @@ void ClientWebGLContext::GetContextAttributes( const auto& options = mNotLost->info.options; @@ -72,7 +73,7 @@ index db60868f65..afed6eeb7c 100644 } // ----------------------- -@@ -979,18 +1012,28 @@ bool ClientWebGLContext::CreateHostContext(const uvec2& requestedSize) { +@@ -979,18 +1013,28 @@ bool ClientWebGLContext::CreateHostContext(const uvec2& requestedSize) { std::unordered_map webgl::MakeIsEnabledMap(const bool webgl2) { auto ret = std::unordered_map{}; @@ -111,11 +112,13 @@ index db60868f65..afed6eeb7c 100644 } return ret; -@@ -2058,6 +2101,28 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, +@@ -2058,6 +2102,57 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, const auto& state = State(); // - -+ std::optional> data; ++ std::optional< ++ std::variant> ++ data; + data = MaskConfig::GLParam(pname, mIsWebGL2); + + if (data.has_value()) { @@ -136,11 +139,38 @@ index db60868f65..afed6eeb7c 100644 + retval.set(StringValue(cx, std::get(value), rv)); + return; + } ++ if (std::holds_alternative(value)) { ++ retval.set(JS::NullValue()); ++ return; ++ } ++ } ++ // If the value is not array (we will handle those later), ++ // then check if it should be blocked. ++ switch (pname) { ++ case LOCAL_GL_DEPTH_RANGE: ++ case LOCAL_GL_ALIASED_POINT_SIZE_RANGE: ++ case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: ++ case LOCAL_GL_COLOR_CLEAR_VALUE: ++ case LOCAL_GL_BLEND_COLOR: ++ case LOCAL_GL_MAX_VIEWPORT_DIMS: ++ case LOCAL_GL_SCISSOR_BOX: ++ case LOCAL_GL_VIEWPORT: ++ case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS: ++ case LOCAL_GL_COLOR_WRITEMASK: ++ case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_RENDERER_WEBGL: ++ case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_VENDOR_WEBGL: ++ break; ++ default: ++ if (MaskConfig::GetBool(mIsWebGL2 ? "webgl2:parameters:blockIfNotDefined" ++ : "webgl:parameters:blockIfNotDefined")) { ++ retval.set(JS::NullValue()); ++ return; ++ } + } const auto fnSetRetval_Buffer = [&](const GLenum target) { const auto buffer = *MaybeFind(state.mBoundBufferByTarget, target); -@@ -2163,49 +2228,84 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, +@@ -2163,49 +2258,84 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, // 2 floats case LOCAL_GL_DEPTH_RANGE: @@ -238,7 +268,7 @@ index db60868f65..afed6eeb7c 100644 return; } -@@ -2385,6 +2485,10 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, +@@ -2385,6 +2515,10 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, switch (pname) { case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_RENDERER_WEBGL: @@ -249,7 +279,7 @@ index db60868f65..afed6eeb7c 100644 ret = GetUnmaskedRenderer(); if (ret && StaticPrefs::webgl_sanitize_unmasked_renderer()) { *ret = webgl::SanitizeRenderer(*ret); -@@ -2392,6 +2496,10 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, +@@ -2392,6 +2526,10 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, break; case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_VENDOR_WEBGL: @@ -260,7 +290,7 @@ index db60868f65..afed6eeb7c 100644 ret = GetUnmaskedVendor(); break; -@@ -2482,7 +2590,9 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, +@@ -2482,7 +2620,9 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, case LOCAL_GL_COLOR_WRITEMASK: { const auto mask = uint8_t(*maybe); const auto bs = std::bitset<4>(mask); @@ -271,7 +301,7 @@ index db60868f65..afed6eeb7c 100644 JS::Rooted arr(cx); if (!dom::ToJSValue(cx, src.data(), src.size(), &arr)) { rv = NS_ERROR_OUT_OF_MEMORY; -@@ -2865,6 +2975,24 @@ ClientWebGLContext::GetShaderPrecisionFormat(const GLenum shadertype, +@@ -2865,6 +3005,24 @@ ClientWebGLContext::GetShaderPrecisionFormat(const GLenum shadertype, const GLenum precisiontype) { if (IsContextLost()) return nullptr; const auto info = [&]() { @@ -296,7 +326,7 @@ index db60868f65..afed6eeb7c 100644 const auto& inProcess = mNotLost->inProcess; if (inProcess) { return inProcess->GetShaderPrecisionFormat(shadertype, precisiontype); -@@ -5822,6 +5950,17 @@ bool ClientWebGLContext::IsSupported(const WebGLExtensionID ext, +@@ -5822,6 +5980,17 @@ bool ClientWebGLContext::IsSupported(const WebGLExtensionID ext, return false; } @@ -314,14 +344,15 @@ index db60868f65..afed6eeb7c 100644 const auto& limits = Limits(); return limits.supportedExtensions[ext]; } -@@ -5833,6 +5972,17 @@ void ClientWebGLContext::GetSupportedExtensions( +@@ -5833,6 +6002,18 @@ void ClientWebGLContext::GetSupportedExtensions( if (!mNotLost) return; auto& retarr = retval.SetValue(); + + // Implement separately to prevent O(n^2) timing + if (std::vector maskValues = -+ MaskConfig::GetStringList("webgl:supportedExtensions"); ++ MaskConfig::GetStringList(mIsWebGL2 ? "webgl2:supportedExtensions" ++ : "webgl:supportedExtensions"); + !maskValues.empty()) { + for (const auto& ext : maskValues) { + retarr.AppendElement(NS_ConvertUTF8toUTF16(ext)); diff --git a/scripts/examples/webgl.html b/scripts/examples/webgl.html index ab609a5..8f0a681 100644 --- a/scripts/examples/webgl.html +++ b/scripts/examples/webgl.html @@ -87,7 +87,8 @@ border-radius: 4px; display: none; position: relative; - transition: opacity 0.3s ease-out, max-height 0.3s ease-out, padding 0.3s ease-out; + transition: opacity 0.3s ease-out, max-height 0.3s ease-out, + padding 0.3s ease-out; opacity: 1; max-height: 100px; overflow: hidden; @@ -114,7 +115,10 @@

Your WebGL Information (in Camoufox format)

-
Warning: This browser is not Firefox. The fingerprint below will not run properly on Camoufox.
+
+ Warning: This browser is not Firefox. The fingerprint below will not run + properly on Camoufox. +

       
@@ -152,33 +156,21 @@

Your WebGL Information (in Camoufox format)

return {}; } - function getMaxAnisotropy(ctx) { - if (ctx) { - const ext = - ctx.getExtension("EXT_texture_filter_anisotropic") || - ctx.getExtension("WEBKIT_EXT_texture_filter_anisotropic") || - ctx.getExtension("MOZ_EXT_texture_filter_anisotropic"); - - if (ext) { - return ctx.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT); - } - } - - return null; - } - const glEnums = [ 2849, 2884, 2885, 2886, 2928, 2929, 2930, 2931, 2932, 2960, 2961, - 2962, 2963, 2964, 2965, 2966, 2967, 2968, 2978, 3024, 3042, 3088, - 3089, 3106, 3107, 3317, 3333, 3379, 3386, 3408, 3410, 3411, 3412, - 3413, 3414, 3415, 7936, 7937, 7938, 10752, 32773, 32777, 32823, 32824, - 32873, 32883, 32936, 32937, 32938, 32939, 32968, 32969, 32970, 32971, - 33170, 33901, 33902, 34016, 34024, 34045, 34047, 34068, 34076, 34467, - 34816, 34817, 34818, 34819, 34852, 34877, 34921, 34930, 34964, 34965, - 35071, 35076, 35077, 35371, 35373, 35374, 35375, 35376, 35377, 35379, - 35380, 35657, 35658, 35659, 35660, 35661, 35724, 35725, 35968, 35978, - 35979, 36003, 36004, 36005, 36006, 36007, 36063, 36183, 36347, 36348, - 36349, 37154, 37157, 37440, 37441, 37443, 37444, 37445, 37446, + 2962, 2963, 2964, 2965, 2966, 2967, 2968, 2978, 3024, 3042, 3074, + 3088, 3089, 3106, 3107, 3314, 3315, 3316, 3317, 3330, 3331, 3332, + 3333, 3379, 3386, 3408, 3410, 3411, 3412, 3413, 3414, 3415, 7936, + 7937, 7938, 10752, 32773, 32777, 32823, 32824, 32873, 32877, 32878, + 32883, 32926, 32928, 32936, 32937, 32938, 32939, 32968, 32969, 32970, + 32971, 33000, 33001, 33170, 33901, 33902, 34016, 34024, 34045, 34047, + 34068, 34076, 34467, 34816, 34817, 34818, 34819, 34852, 34853, 34854, + 34855, 34856, 34857, 34858, 34859, 34860, 34877, 34921, 34930, 34964, + 34965, 35071, 35076, 35077, 35371, 35373, 35374, 35375, 35376, 35377, + 35379, 35380, 35657, 35658, 35659, 35660, 35661, 35723, 35724, 35725, + 35738, 35739, 35968, 35977, 35978, 35979, 36003, 36004, 36005, 36006, + 36007, 36063, 36183, 36203, 36345, 36347, 36348, 36349, 36387, 36388, + 37137, 37154, 37157, 37440, 37441, 37443, 37444, 37445, 37446, 37447, ]; let rendererInfo = null; @@ -300,15 +292,18 @@

Your WebGL Information (in Camoufox format)

"DOMContentLoaded", async () => { // Check if the user agent is Firefox - const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; + const isFirefox = + navigator.userAgent.toLowerCase().indexOf("firefox") > -1; if (!isFirefox) { - document.getElementById('warning').style.display = 'block'; + document.getElementById("warning").style.display = "block"; } // Add event listener for close button - document.getElementById('close-warning').addEventListener('click', function() { - document.getElementById('warning').classList.add('hide'); - }); + document + .getElementById("close-warning") + .addEventListener("click", function () { + document.getElementById("warning").classList.add("hide"); + }); const [webglDetail, webgl2Detail] = await Promise.all([ dumpWebGLCore("webgl", "experimental-webgl"), diff --git a/settings/properties.json b/settings/properties.json index f2afac0..e36e76e 100644 --- a/settings/properties.json +++ b/settings/properties.json @@ -70,8 +70,11 @@ { "property": "webgl:renderer", "type": "str" }, { "property": "webgl:vendor", "type": "str" }, { "property": "webgl:supportedExtensions", "type": "array" }, + { "property": "webgl2:supportedExtensions", "type": "array" }, { "property": "webgl:parameters", "type": "dict" }, + { "property": "webgl:parameters:blockIfNotDefined", "type": "bool" }, { "property": "webgl2:parameters", "type": "dict" }, + { "property": "webgl2:parameters:blockIfNotDefined", "type": "bool" }, { "property": "webgl:shaderPrecisionFormats", "type": "dict" }, { "property": "webgl:shaderPrecisionFormats:blockIfNotDefined", "type": "bool" }, { "property": "webgl2:shaderPrecisionFormats", "type": "dict" },