diff --git a/_locales/de/messages.json b/_locales/de/messages.json index deec5bb..bba5960 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -58,6 +58,10 @@ "message": "Einstellungen", "description": "" }, + "section_audio-api":{ + "message": "Audio API", + "description": "" + }, "displayAdvancedSettings_title": { "message": "Expertenmodus", @@ -224,7 +228,7 @@ "message": "Blockiermodus", "description": "" }, - + "urlSettings_title": { "message": "Seitenspezifische Einstellungen", "description": "" @@ -289,7 +293,7 @@ "message": "Zufallszahlengenerator", "description": "" }, - + "persistentRndStorage_title": { "message": "Persistenter Speicher", "description": "" @@ -436,6 +440,10 @@ "message": "Auslese vorgetäuscht auf {url}", "description": "" }, + "fakedAudioReadout": { + "message": "Audioauslese vorgetäuscht auf {url}", + "description": "" + }, "fakedInput": { "message": "Bei Ausgabe vorgetäuscht auf {url}", "description": "" @@ -555,6 +563,75 @@ "description": "" }, + "protectaudio_title": { + "message": "Audio-API beschützen", + "description": "" + }, + "protectaudio_description": { + "message": "Ob die Audio-API auch beschützt werden soll.", + "description": "" + }, + "audiofakerate_title": { + "message": "Buffer Vortäuschrate", + "description": "" + }, + "audiofakerate_description": { + "message": "Wie viele Werte pro Leseanfrage vorgetäuscht werden sollen.", + "description": "" + }, + "audiofakerate_options.1": { + "message": "1 Wert", + "description": "" + }, + "audiofakerate_options.10": { + "message": "10 Werte", + "description": "" + }, + "audiofakerate_options.100": { + "message": "100 Werte", + "description": "" + }, + "audiofakerate_options.1000": { + "message": "1000 Werte", + "description": "" + }, + "audiofakerate_options.0.1%": { + "message": "0.1% der Werte", + "description": "" + }, + "audiofakerate_options.1%": { + "message": "1% der Werte", + "description": "" + }, + "audionoiselevel_title": { + "message": "Rauschlevel", + "description": "" + }, + "audionoiselevel_description": { + "message": "", + "description": "" + }, + "audionoiselevel_options.minimal": { + "message": "minimal", + "description": "" + }, + "audionoiselevel_options.low": { + "message": "niedrig", + "description": "" + }, + "audionoiselevel_options.medium": { + "message": "mittel", + "description": "" + }, + "audionoiselevel_options.high": { + "message": "hoch", + "description": "" + }, + "audionoiselevel_options.maximal": { + "message": "maximal", + "description": "" + }, + "showReleaseNotes_title": { "message": "Versionsinformationen", "description": "" @@ -600,7 +677,7 @@ "message": "ausführlich", "description": "" }, - + "exportSettings_title": { "message": "Einstellungen exportieren", "description": "" diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 4802db0..4ce9716 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -7,7 +7,7 @@ "message": "Changes the JS-API for modifying to prevent Canvas-Fingerprinting.", "description": "" }, - + "more": { "message": "more", "description": "" @@ -58,6 +58,10 @@ "message": "Settings", "description": "" }, + "section_audio-api":{ + "message": "Audio API", + "description": "" + }, "displayAdvancedSettings_title": { "message": "Expert mode", @@ -246,7 +250,7 @@ "message": "Input domain or URL RegExp:", "description": "" }, - + "minFakeSize_description": { "message": "Canvas with a smaller or equal area than this number will not be faked. This is a parameter to prevent detection.\nCAUTION: This lowers the safety of the addon, therefore it is highly recommended not to set this value above 100.", "description": "" @@ -402,7 +406,7 @@ "message": "Allows certain parts of the canvas API.", "description": "" }, - + "disableNotifications": { "message": "disable notifications", "description": "" @@ -436,6 +440,10 @@ "message": "Faked readout on {url}", "description": "" }, + "fakedAudioReadout": { + "message": "Faked audio readout on {url}", + "description": "" + }, "fakedInput": { "message": "Faked at input on {url}", "description": "" @@ -555,6 +563,75 @@ "description": "" }, + "protectaudio_title": { + "message": "Protect audio API", + "description": "" + }, + "protectaudio_description": { + "message": "If the audio API should be protected as well", + "description": "" + }, + "audiofakerate_title": { + "message": "Buffer fake rate", + "description": "" + }, + "audiofakerate_description": { + "message": "How many of the values per read request should be faked.", + "description": "" + }, + "audiofakerate_options.1": { + "message": "1 value", + "description": "" + }, + "audiofakerate_options.10": { + "message": "10 values", + "description": "" + }, + "audiofakerate_options.100": { + "message": "100 values", + "description": "" + }, + "audiofakerate_options.1000": { + "message": "1000 values", + "description": "" + }, + "audiofakerate_options.0.1%": { + "message": "0.1% of the values", + "description": "" + }, + "audiofakerate_options.1%": { + "message": "1% of the values", + "description": "" + }, + "audionoiselevel_title": { + "message": "Noise level", + "description": "" + }, + "audionoiselevel_description": { + "message": "", + "description": "" + }, + "audionoiselevel_options.minimal": { + "message": "minimal", + "description": "" + }, + "audionoiselevel_options.low": { + "message": "low", + "description": "" + }, + "audionoiselevel_options.medium": { + "message": "medium", + "description": "" + }, + "audionoiselevel_options.high": { + "message": "high", + "description": "" + }, + "audionoiselevel_options.maximal": { + "message": "maximal", + "description": "" + }, + "showReleaseNotes_title": { "message": "Release notes", "description": "" @@ -613,7 +690,7 @@ "message": "Show", "description": "" }, - + "resetSettings_title": { "message": "Reset settings", "description": "" diff --git a/lib/modifiedAPI.js b/lib/modifiedAPI.js index d99b413..0d74e5a 100644 --- a/lib/modifiedAPI.js +++ b/lib/modifiedAPI.js @@ -17,6 +17,7 @@ const logging = require("./logging"); const {copyCanvasToWebgl} = require("./webgl"); const getWrapped = require("sdk/getWrapped"); + const modifiedAudioAPI = require("./modifiedAudioAPI"); var randomSupply = null; @@ -156,6 +157,7 @@ scope.setRandomSupply = function(supply){ randomSupply = supply; + modifiedAudioAPI.setRandomSupply(supply); }; var canvasContextType = new WeakMap(); // changed functions and their fakes @@ -441,4 +443,7 @@ } } }; + Object.keys(modifiedAudioAPI.changedFunctions).forEach(function(key){ + scope.changedFunctions[key] = modifiedAudioAPI.changedFunctions[key]; + }); }()); \ No newline at end of file diff --git a/lib/modifiedAudioAPI.js b/lib/modifiedAudioAPI.js new file mode 100644 index 0000000..12d7292 --- /dev/null +++ b/lib/modifiedAudioAPI.js @@ -0,0 +1,191 @@ +/* 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/. */ +(function(){ + "use strict"; + + var scope; + if ((typeof exports) !== "undefined"){ + scope = exports; + } + else { + window.scope.modifiedAudioAPI = {}; + scope = window.scope.modifiedAudioAPI; + } + + const logging = require("./logging"); + const getWrapped = require("sdk/getWrapped"); + + var randomSupply = null; + + const audioFakeRate = { + "1": function(array){return 1;}, + "10": function(array){return 10;}, + "100": function(array){return 100;}, + "1000": function(array){return 1000;}, + "0.1%": function(array){return array.length / 1000;}, + "1%": function(array){return array.length / 100;}, + }; + function getAudioFakeRate(array, prefs){ + var func = audioFakeRate[prefs("audioFakeRate")]; + if (typeof func === "function"){ + return func(array); + } + else { + return 10; + } + } + const audioNoiseLevel = { + "minimal": 0.0000001, + "low": 0.00001, + "medium": 0.001, + "high": 0.01, + "maximal": 0.1 + }; + function getAudioNoiseLevel(prefs){ + return audioNoiseLevel[prefs("audioNoiseLevel")] || 0.0000001; + } + + function fakeFloat32Array(array, window, prefs){ + if (prefs("protectAudio")){ + var l = array.length; + var rate = getAudioFakeRate(array, prefs); + var noiseLevel = getAudioNoiseLevel(prefs); + var indexRng = randomSupply.getIndexRng(rate, l, window); + var rng = randomSupply.getRng(rate, window); + for (var i = 0; i < rate; i += 1){ + var index = indexRng(i); + array[index] += (rng(i) / 0xffffffff - 0.5) * noiseLevel; + } + } + } + function fakeUint8Array(array, window, prefs){ + if (prefs("protectAudio")){ + var l = array.length; + var rate = getAudioFakeRate(array, prefs); + var indexRng = randomSupply.getIndexRng(rate, l, window); + var rng = randomSupply.getValueRng(rate, window); + for (var i = 0; i < rate; i += 1){ + var index = indexRng(i); + array[index] = rng(array[index], i); + } + } + } + + function hasType(status, type){ + return status.type.indexOf(type) !== -1; + } + + scope.setRandomSupply = function(supply){ + randomSupply = supply; + }; + + function getStatus(obj, status){ + status = Object.create(status); + status.active = hasType(status, "readout"); + return status; + } + + let notified = false; + function notifyOnce(notify){ + if (!notified){ + notify("fakedAudioReadout"); + notified = true; + } + } + // changed functions and their fakes + scope.changedFunctions = { + getFloatFrequencyData: { + type: "readout", + getStatus: getStatus, + object: ["AnalyserNode"], + fakeGenerator: function(prefs, notify, window, original){ + return function getFloatFrequencyData(array){ + notifyOnce(notify); + var ret = original.apply(this, window.Array.from(arguments)); + fakeFloat32Array(array, window, prefs); + return ret; + }; + } + }, + getByteFrequencyData: { + type: "readout", + getStatus: getStatus, + object: ["AnalyserNode"], + fakeGenerator: function(prefs, notify, window, original){ + return function getByteFrequencyData(array){ + notifyOnce(notify); + var ret = original.apply(this, window.Array.from(arguments)); + fakeUint8Array(array, window, prefs); + return ret; + }; + } + }, + getFloatTimeDomainData: { + type: "readout", + getStatus: getStatus, + object: ["AnalyserNode"], + fakeGenerator: function(prefs, notify, window, original){ + return function getFloatTimeDomainData(array){ + notifyOnce(notify); + var ret = original.apply(this, window.Array.from(arguments)); + fakeFloat32Array(array, window, prefs); + return ret; + }; + } + }, + getByteTimeDomainData: { + type: "readout", + getStatus: getStatus, + object: ["AnalyserNode"], + fakeGenerator: function(prefs, notify, window, original){ + return function getByteTimeDomainData(array){ + notifyOnce(notify); + var ret = original.apply(this, window.Array.from(arguments)); + fakeUint8Array(array, window, prefs); + return ret; + }; + } + }, + getChannelData: { + type: "readout", + getStatus: getStatus, + object: ["AudioBuffer"], + fakeGenerator: function(prefs, notify, window, original){ + return function getChannelData(channel){ + notifyOnce(notify); + var ret = original.apply(this, window.Array.from(arguments)); + fakeFloat32Array(ret, window, prefs); + return ret; + }; + } + }, + copyFromChannel: { + type: "readout", + getStatus: getStatus, + object: ["AudioBuffer"], + fakeGenerator: function(prefs, notify, window, original){ + return function copyFromChannel(destination, channelNumber, startInChannel){ + notifyOnce(notify); + var ret = original.apply(this, window.Array.from(arguments)); + fakeFloat32Array(destination, window, prefs); + return ret; + }; + } + }, + getFrequencyResponse: { + type: "readout", + getStatus: getStatus, + object: ["BiquadFilterNode", "IIRFilterNode"], + fakeGenerator: function(prefs, notify, window, original){ + return function getFrequencyResponse(frequencyArray, magResponseOutput, phaseResponseOutput){ + notifyOnce(notify); + var ret = original.apply(this, window.Array.from(arguments)); + fakeFloat32Array(magResponseOutput, window, prefs); + fakeFloat32Array(phaseResponseOutput, window, prefs); + return ret; + }; + } + }, + }; +}()); \ No newline at end of file diff --git a/lib/settingDefinitions.js b/lib/settingDefinitions.js index 08a8b78..bab31f9 100644 --- a/lib/settingDefinitions.js +++ b/lib/settingDefinitions.js @@ -67,7 +67,10 @@ "toDataURL", "toBlob", "mozGetAsFile", "getImageData", "isPointInPath", "isPointInStroke", "fillText", "strokeText", - "readPixels" + "readPixels", + "getFloatFrequencyData", "getByteFrequencyData", "getFloatTimeDomainData", "getByteTimeDomainData", + "getChannelData", "copyFromChannel", + "getFrequencyResponse" ], defaultKeyValue: false }, @@ -155,6 +158,20 @@ name: "stackList", defaultValue: "" }, + { + name: "protectAudio", + defaultValue: true + }, + { + name: "audioFakeRate", + defaultValue: "10", + options: ["1", "10", "100", "1000", "0.1%", "1%"] + }, + { + name: "audioNoiseLevel", + defaultValue: "low", + options: ["minimal", "low", "medium", "high", "maximal"] + }, { name: "displayAdvancedSettings", defaultValue: false diff --git a/manifest.json b/manifest.json index 2d2377f..feea14d 100644 --- a/manifest.json +++ b/manifest.json @@ -1,3 +1,4 @@ + { "name": "CanvasBlocker", "description": "__MSG_addon_description__", @@ -30,6 +31,7 @@ "lib/colorStatistics.js", "lib/webgl.js", + "lib/modifiedAudioAPI.js", "lib/modifiedAPI.js", "lib/randomSupplies.js", "lib/intercept.js", diff --git a/options/settingsDisplay.js b/options/settingsDisplay.js index 6f0feba..1c0a4ec 100644 --- a/options/settingsDisplay.js +++ b/options/settingsDisplay.js @@ -292,6 +292,24 @@ "displayAdvancedSettings": [true] } }, + "Audio-API", + { + "name": "protectAudio" + }, + { + "name": "audioFakeRate", + "displayDependencies": { + "protectAudio": [true], + "displayAdvancedSettings": [true] + } + }, + { + "name": "audioNoiseLevel", + "displayDependencies": { + "protectAudio": [true], + "displayAdvancedSettings": [true] + } + }, "misc", { "name": "showReleaseNotes" diff --git a/releaseNotes.txt b/releaseNotes.txt index 8afc3d0..1a49d4b 100644 --- a/releaseNotes.txt +++ b/releaseNotes.txt @@ -1,9 +1,9 @@ Version 0.4.6: changes: - - + - Changes in the random supply API new features: - - + - Can protect Audio API fixes: -