diff options
Diffstat (limited to 'data/extensions/jsr@javascriptrestrictor/wrappingS-H-C.js')
-rw-r--r-- | data/extensions/jsr@javascriptrestrictor/wrappingS-H-C.js | 337 |
1 files changed, 0 insertions, 337 deletions
diff --git a/data/extensions/jsr@javascriptrestrictor/wrappingS-H-C.js b/data/extensions/jsr@javascriptrestrictor/wrappingS-H-C.js deleted file mode 100644 index f641d4b..0000000 --- a/data/extensions/jsr@javascriptrestrictor/wrappingS-H-C.js +++ /dev/null @@ -1,337 +0,0 @@ -/** \file - * \brief This file contains wrappers for Canvas-related calls - * - * \see https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API - * \see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D - * \see https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas - * - * \author Copyright (C) 2019 Libor Polcak - * \author Copyright (C) 2021 Matus Svancar - * \author Copyright (C) 2023 Martin Zmitko - * - * \license SPDX-License-Identifier: GPL-3.0-or-later - * \license SPDX-License-Identifier: MPL-2.0 - */ -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. -// -// Alternatively, the contents of this file may be used under the terms -// of the Mozilla Public License, v. 2.0, as described below: -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -// -// \copyright Copyright (c) 2020 The Brave Authors. - -/** \file - * \ingroup wrappers - * This file contains wrappers for calls related to the Canvas API, about which you can read more at MDN: - * * [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) - * * [CanvasRenderingContext2D](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D) - * * [OffscreenCanvas](https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas) - * - * The goal is to prevent fingerprinting by modifying the values that can be read from the canvas. - * So the visual content of wrapped canvases as displayed on the screen is the same as intended. - * - * The modified content can be either an empty image or a fake image that is modified according to - * session and domain keys to be different than the original albeit very similar (i.e. the approach - * inspired by the algorithms created by [Brave Software](https://brave.com) available [here](https://github.com/brave/brave-core/blob/master/chromium_src/third_party/blink/renderer/core/execution_context/execution_context.cc). - * - * Note that both approaches are detectable by a fingerprinter that checks if a predetermined image - * inserted to the canvas is the same as the read one, see [here](https://arkenfox.github.io/TZP/tests/canvasnoise.html) for an example, - * Nevertheless, the aim of the wrappers is to limit the finerprintability. - * - * Also note that a determined fingerprinter can reveal the modifications and consequently uncover - * the original image. This can be avoided with the approach that completely clears the data stored - * in the canvas. Use the modifications based on session and domain keys if you want to provide an - * image that is similar to the original or if you want to produce a fake image that is not - * obviously spoofed to a naked eye. Otherwise, use the clearing approach. - */ - -/* - * Create private namespace - */ -(function() { - - const DEF_CANVAS_COPY = ` - let canvasCopy = ctx => { - let {width, height} = ctx.canvas; - let fake = document.createElement("canvas"); - fake.setAttribute("width", width); - fake.setAttribute("height", height); - let stx = fake.getContext("2d"); - let imageData = window.CanvasRenderingContext2D.prototype.getImageData.call(ctx, 0, 0, width, height); - stx.putImageData(imageData, 0, 0); - return fake; - }; - `; - - /** @var String helping_code. - * Selects if the canvas should be cleared (1) or a fake image should be created based on session - * and domain keys (0). - */ - var helping_code = `var approach = args[0];`; - var wrappers = [{ - parent_object: "HTMLCanvasElement.prototype", - parent_object_property: "toDataURL", - wrapped_objects: [{ - original_name: "HTMLCanvasElement.prototype.toDataURL", - wrapped_name: "origToDataURL", - }], - helping_code: helping_code, - wrapping_code_function_name: "wrapping", - wrapping_code_function_params: "parent", - wrapping_code_function_call_window: true, - original_function: "parent.HTMLCanvasElement.prototype.toDataURL", - replace_original_function: true, - wrapping_function_args: "...args", - /** \fn fake HTMLCanvasElement.prototype.toDataURL - * \brief Returns fake canvas content, see CanvasRenderingContext2D.prototype for more details. - * - * Internally creates a fake canvas of the same height and width as the original and calls - * CanvasRenderingContext2D.getImageData() that determines the result. If canvas uses WebGLRenderingContext - * the content is copied to new canvas using CanvasRenderingContext2D and function toDataURL is called on it. - */ - wrapping_function_body: ` - var ctx = this.getContext("2d"); - if(ctx){ - ${DEF_CANVAS_COPY} - return origToDataURL.call(canvasCopy(ctx), ...args); - } - else { - var ctx = this.getContext("webgl2", {preserveDrawingBuffer: true}) || - this.getContext("experimental-webgl2", {preserveDrawingBuffer: true}) || - this.getContext("webgl", {preserveDrawingBuffer: true}) || - this.getContext("experimental-webgl", {preserveDrawingBuffer: true}) || - this.getContext("moz-webgl", {preserveDrawingBuffer: true}); - if(ctx){ - var fake = document.createElement("canvas"); - fake.setAttribute("width", this.width); - fake.setAttribute("height", this.height); - var stx = fake.getContext("2d"); - stx.drawImage(ctx.canvas, 0, 0); - return HTMLCanvasElement.prototype.toDataURL.call(fake); - } - } - `, - }, - { - parent_object: "CanvasRenderingContext2D.prototype", - parent_object_property: "getImageData", - wrapped_objects: [{ - original_name: "CanvasRenderingContext2D.prototype.getImageData", - wrapped_name: "origGetImageData", - }], - helping_code: helping_code + farbleCanvasDataBrave.toString() + ` - var farble = function(context, fake) { - if(approach === 1){ - fake.fillStyle = "white"; - fake.fillRect(0, 0, context.canvas.width, context.canvas.height); - return; - } - else if(approach === 0){ - const width = context.canvas.width; - const height = context.canvas.height; - const imageData = origGetImageData.call(context, 0, 0, width, height); - const len = imageData.data.length; - if (wasm.ready && wasm.grow(len)) { - try { - farblePixelsWASM(); - } catch (e) { - console.error("WebAssembly optimized farbling failed, falling back to JavaScript implementation", e); - farblePixelsJS(); - } - } else { - farblePixelsJS(); - } - // Do not modify the original canvas, always modify the fake canvas. - // Always farble the whole image so that the farbled data do not depend - // on the page-specified extraction data rectangle. - fake.putImageData(imageData, 0, 0); - - function farblePixelsWASM() { - wasm.set(imageData.data); - const crc = wasm.crc16(len); - const mash = new Mash(); - mash.addData(' '); - mash.addData(domainHash); - mash.addData("CanvasFarbling"); - mash.addData(crc); - wasm.farbleBytes(len, mash.n | 0, true); - imageData.data.set(wasm.get(len)); - } - - function farblePixelsJS() { - const BYTES_PER_ROW = width * 4; - farbleCanvasDataBrave(function*() { - let data = imageData.data; - let offset = 0; - while (offset < len) { - yield data.subarray(offset, offset + BYTES_PER_ROW); - offset += BYTES_PER_ROW; - } - }, width); - } - } - };`, - wrapping_code_function_name: "wrapping", - wrapping_code_function_params: "parent", - wrapping_code_function_call_window: true, - original_function: "parent.CanvasRenderingContext2D.prototype.getImageData", - replace_original_function: true, - wrapping_function_args: "...args", - /** \fn fake CanvasRenderingContext2D.prototype.getImageData - * \brief Returns a fake image data of the same height and width as stored in the original canvas. - * - * Internally calls the farbling that select the output which can be either an empty image or - * a fake image that is modified according to session and domain keys to be different than the - * original albeit very similar. - */ - wrapping_function_body: ` - var fake = document.createElement("canvas"); - fake.setAttribute("width", this.canvas.width); - fake.setAttribute("height", this.canvas.height); - var stx = fake.getContext("2d"); - farble(this,stx); - return origGetImageData.call(stx, ...args); - `, - }, - { - parent_object: "HTMLCanvasElement.prototype", - parent_object_property: "toBlob", - wrapped_objects: [{ - original_name: "HTMLCanvasElement.prototype.toBlob", - wrapped_name: "origToBlob", - }], - helping_code: ``, - wrapping_code_function_name: "wrapping", - wrapping_code_function_params: "parent", - wrapping_code_function_call_window: true, - original_function: "parent.HTMLCanvasElement.prototype.toBlob", - replace_original_function: true, - wrapping_function_args: "...args", - /** \fn fake HTMLCanvasElement.prototype.toBlob - * \brief Returns fake canvas content, see CanvasRenderingContext2D.prototype for more details. - * - * Internally creates a fake canvas of the same height and width as the original and calls - * CanvasRenderingContext2D.getImageData() that detemines the result. - */ - wrapping_function_body: ` - ${DEF_CANVAS_COPY} - return origToBlob.call(canvasCopy(this.getContext("2d")), ...args); - `, - }, - { - parent_object: "OffscreenCanvas.prototype", - parent_object_property: "convertToBlob", - wrapped_objects: [{ - original_name: "OffscreenCanvas.prototype.convertToBlob", - wrapped_name: "origConvertToBlob", - }], - helping_code: ``, - wrapping_code_function_name: "wrapping", - wrapping_code_function_params: "parent", - wrapping_code_function_call_window: true, - original_function: "parent.OffscreenCanvas.prototype.convertToBlob", - replace_original_function: true, - wrapping_function_args: "...args", - /** \fn fake OffscreenCanvas.prototype.convertToBlob - * \brief Returns fake canvas content, see CanvasRenderingContext2D.prototype for more details. - * - * Internally creates a fake canvas of the same height and width as the original and calls - * CanvasRenderingContext2D.getImageData() that detemines the result. - */ - wrapping_function_body: ` - ${DEF_CANVAS_COPY} - return origConvertToBlob.call(canvasCopy(this.getContext("2d")), ...args); - `, - }, - { - parent_object: "CanvasRenderingContext2D.prototype", - parent_object_property: "isPointInPath", - wrapped_objects: [{ - original_name: "CanvasRenderingContext2D.prototype.isPointInPath", - wrapped_name: "origIsPointInPath", - }], - helping_code: helping_code + ` - function farbleIsPointInPath(ctx, ...args){ - if(approach === 0){ - var ret = origIsPointInPath.call(ctx, ...args); - return (ret && ((prng()*20) > 1)); - } - else if(approach === 1){ - return false; - } - }; - `, - wrapping_code_function_name: "wrapping", - wrapping_code_function_params: "parent", - wrapping_code_function_call_window: true, - original_function: "parent.CanvasRenderingContext2D.prototype.isPointInPath", - replace_original_function: true, - wrapping_function_args: "...args", - /** \fn fake CanvasRenderingContext2D.prototype.isPointInPath - * \brief Returns modified result - * - * Either returns false or original function return value which is changed to false with 1/20 probability - * - * \bug Changing value with probability has some issues: - * * multiple calls with the same pixel can return different values - * * inconsistencies among adjacent pixels - */ - wrapping_function_body: ` - return farbleIsPointInPath(this, ...args); - `, - }, - { - parent_object: "CanvasRenderingContext2D.prototype", - parent_object_property: "isPointInStroke", - wrapped_objects: [{ - original_name: "CanvasRenderingContext2D.prototype.isPointInStroke", - wrapped_name: "origIsPointInStroke", - }], - helping_code: helping_code + ` - function farbleIsPointInStroke(ctx, ...args){ - if(approach === 0){ - var ret = origIsPointInStroke.call(ctx, ...args); - return (ret && ((prng()*20) > 1)); - } - else if(approach === 1){ - return false; - } - }; - `, - wrapping_code_function_name: "wrapping", - wrapping_code_function_params: "parent", - wrapping_code_function_call_window: true, - original_function: "parent.CanvasRenderingContext2D.prototype.isPointInStroke", - replace_original_function: true, - wrapping_function_args: "...args", - /** \fn fake CanvasRenderingContext2D.prototype.isPointInStroke - * \brief Returns modified result - * - * Either returns false or original function return value which is changed to false with 1/20 probability - * - * \bug Changing value with probability has some issues: - * * multiple calls with the same pixel can return different values - * * inconsistencies among adjacent pixels - */ - wrapping_function_body: ` - return farbleIsPointInStroke(this, ...args); - `, - }, - ] - add_wrappers(wrappers); -})(); |