├── index.html ├── README.md ├── demo ├── sha1.js └── main.js └── fprandom.patch /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FP Random demo 6 | 7 | 8 |

FP Random demo

9 | 10 |

Canvas

11 | 12 |

Audio Hash & Sum

13 |

14 |
15 |

Order of navigator's properties
(less frequent changes because of cache)

16 |
17 |

Results

18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
TestCanvas hashAudio hashAudio sumOrder
29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FPRandom 2 | 3 | Welcome to the official repository of FPRandom, a modified browser 4 | whose goal is to counter advanced fingerprinting techniques! 5 | 6 | ### The browser 7 | FPRandom is a modified version of the Nightly version of Firefox 54 8 | from Mozilla. FPRandom's primary goal is to break the stability and determinism 9 | of very specific fingerprinting techniques while preserving the best user experience possible. 10 | By introducing enough noise during the fingerprinting process, trackers are fooled and cannot bound 11 | a freshly collected fingerprint with an old one, thus rendering the tracking across 12 | multiple sessions impossible. FPRandom is detailed in 13 | [a scientific publication](https://hal.inria.fr/hal-01527580/document) that has been presented at 14 | the [ESSoS 2017 conference](https://distrinet.cs.kuleuven.be/events/essos/2017/) where 15 | it received a "Distinguished Artifact" award :trophy:. 16 | 17 | ### The patch 18 | The **fprandom.patch** file contains the complete list of modifications brought 19 | to Firefox. The patch is structured as follows: 20 | * [From line 1 to 259](https://github.com/plaperdr/fprandom/blob/master/fprandom.patch#L1) - 21 | addition of two entries in the **Privacy** section of Firefox preferences: choose 22 | when the protection is activated ("Always", "Only in private windows" or "Never") and select the desired "Randomization" strategy ("Always random" or "Per session") 23 | * [From line 260 to 392](https://github.com/plaperdr/fprandom/blob/master/fprandom.patch#L260) - 24 | modification of the **Canvas API** to change the browser's fallback font and add imperceptible 25 | variations to selected colors 26 | * [From line 393 to 627](https://github.com/plaperdr/fprandom/blob/master/fprandom.patch#L393) - 27 | modification of the **AudioContext API** to reduce the volume of random parts of 28 | processed audio by a very small factor inaudible by the user 29 | * [From line 628 to 720](https://github.com/plaperdr/fprandom/blob/master/fprandom.patch#L628) - 30 | modification of the iterator of JavaScript objects to change the **enumeration order** of properties 31 | and to prevent browser's unmasking 32 | 33 | 34 | ### The Linux prototype 35 | You can find a fully-compiled prototype of FPRandom for x64 Linux systems 36 | in the _Release_ section of this repository 37 | [HERE](https://github.com/plaperdr/fprandom/releases). 38 | After downloading the archive, extract it and execute the following command: 39 | 40 | ./firefox 41 | 42 | If you want to launch FPRandom alongside your main instance of Firefox with a different 43 | profile, you can execute the following command: 44 | 45 | ./firefox -no-remote -P "New profile" 46 | 47 | 48 | ### Demo 49 | You can use the demo page at 50 | [https://plaperdr.github.io/fprandom/](https://plaperdr.github.io/fprandom/) 51 | to compare the impact of FPRandom with a vanilla version of Firefox or Chrome. 52 | By running the tests several times, you can see that FPRandom produces new values at every 53 | execution while a standard browser keeps the same stable ones. 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /demo/sha1.js: -------------------------------------------------------------------------------- 1 | /* 2 | CryptoJS v3.1.2 3 | code.google.com/p/crypto-js 4 | (c) 2009-2013 by Jeff Mott. All rights reserved. 5 | code.google.com/p/crypto-js/wiki/License 6 | */ 7 | var CryptoJS=CryptoJS||function(e,m){var p={},j=p.lib={},l=function(){},f=j.Base={extend:function(a){l.prototype=this;var c=new l;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}}, 8 | n=j.WordArray=f.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=m?c:4*a.length},toString:function(a){return(a||h).stringify(this)},concat:function(a){var c=this.words,q=a.words,d=this.sigBytes;a=a.sigBytes;this.clamp();if(d%4)for(var b=0;b>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((d+b)%4);else if(65535>>2]=q[b>>>2];else c.push.apply(c,q);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<< 9 | 32-8*(c%4);a.length=e.ceil(c/4)},clone:function(){var a=f.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],b=0;b>>2]>>>24-8*(d%4)&255;b.push((f>>>4).toString(16));b.push((f&15).toString(16))}return b.join("")},parse:function(a){for(var c=a.length,b=[],d=0;d>>3]|=parseInt(a.substr(d, 10 | 2),16)<<24-4*(d%8);return new n.init(b,c/2)}},g=b.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],d=0;d>>2]>>>24-8*(d%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],d=0;d>>2]|=(a.charCodeAt(d)&255)<<24-8*(d%4);return new n.init(b,c)}},r=b.Utf8={stringify:function(a){try{return decodeURIComponent(escape(g.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return g.parse(unescape(encodeURIComponent(a)))}}, 11 | k=j.BufferedBlockAlgorithm=f.extend({reset:function(){this._data=new n.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=r.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,b=c.words,d=c.sigBytes,f=this.blockSize,h=d/(4*f),h=a?e.ceil(h):e.max((h|0)-this._minBufferSize,0);a=h*f;d=e.min(4*a,d);if(a){for(var g=0;ga;a++){if(16>a)l[a]=f[n+a]|0;else{var c=l[a-3]^l[a-8]^l[a-14]^l[a-16];l[a]=c<<1|c>>>31}c=(h<<5|h>>>27)+j+l[a];c=20>a?c+((g&e|~g&k)+1518500249):40>a?c+((g^e^k)+1859775393):60>a?c+((g&e|g&k|e&k)-1894007588):c+((g^e^ 15 | k)-899497514);j=k;k=e;e=g<<30|g>>>2;g=h;h=c}b[0]=b[0]+h|0;b[1]=b[1]+g|0;b[2]=b[2]+e|0;b[3]=b[3]+k|0;b[4]=b[4]+j|0},_doFinalize:function(){var f=this._data,e=f.words,b=8*this._nDataBytes,h=8*f.sigBytes;e[h>>>5]|=128<<24-h%32;e[(h+64>>>9<<4)+14]=Math.floor(b/4294967296);e[(h+64>>>9<<4)+15]=b;f.sigBytes=4*e.length;this._process();return this._hash},clone:function(){var e=j.clone.call(this);e._hash=this._hash.clone();return e}});e.SHA1=j._createHelper(m);e.HmacSHA1=j._createHmacHelper(m)})(); 16 | -------------------------------------------------------------------------------- /demo/main.js: -------------------------------------------------------------------------------- 1 | //Variables 2 | var nbTest = 0; 3 | var canvasInfo; 4 | var audioHashInfo; 5 | var audioSumInfo; 6 | var orderInfo; 7 | 8 | //Functions 9 | function hashSha1(toHash){ 10 | var sha1 = CryptoJS.algo.SHA1.create(); 11 | sha1.update(toHash); 12 | var hash = sha1.finalize(); 13 | return hash.toString(CryptoJS.enc.Hex); 14 | } 15 | 16 | function canvasTest(resolve, reject) { 17 | try { 18 | var canvas = document.getElementById("canvas"); 19 | canvas.height = 60; 20 | canvas.width = 400; 21 | var canvasContext = canvas.getContext("2d"); 22 | canvas.style.display = "inline"; 23 | canvasContext.textBaseline = "alphabetic"; 24 | canvasContext.fillStyle = "#f60"; 25 | canvasContext.fillRect(125, 1, 62, 20); 26 | canvasContext.fillStyle = "#069"; 27 | canvasContext.font = "11pt no-real-font-123"; 28 | canvasContext.fillText("Cwm fjordbank glyphs vext quiz, \ud83d\ude03", 2, 15); 29 | canvasContext.fillStyle = "rgba(102, 204, 0, 0.7)"; 30 | canvasContext.font = "18pt Arial"; 31 | canvasContext.fillText("Cwm fjordbank glyphs vext quiz, \ud83d\ude03", 4, 45); 32 | canvasInfo = hashSha1(canvas.toDataURL()); 33 | resolve(); 34 | } catch (e) { 35 | reject(); 36 | } 37 | } 38 | 39 | 40 | function audioTest(resolve, reject) { 41 | try { 42 | var context = new (window.OfflineAudioContext || window.webkitOfflineAudioContext)(1, 44100, 44100); 43 | 44 | // Create oscillator 45 | var pxi_oscillator = context.createOscillator(); 46 | pxi_oscillator.type = "triangle"; 47 | pxi_oscillator.frequency.value = 1e4; 48 | 49 | // Create and configure compressor 50 | var pxi_compressor = context.createDynamicsCompressor(); 51 | pxi_compressor.threshold && (pxi_compressor.threshold.value = -50); 52 | pxi_compressor.knee && (pxi_compressor.knee.value = 40); 53 | pxi_compressor.ratio && (pxi_compressor.ratio.value = 12); 54 | pxi_compressor.reduction && (pxi_compressor.reduction.value = -20); 55 | pxi_compressor.attack && (pxi_compressor.attack.value = 0); 56 | pxi_compressor.release && (pxi_compressor.release.value = .25); 57 | 58 | // Connect nodes 59 | pxi_oscillator.connect(pxi_compressor); 60 | pxi_compressor.connect(context.destination); 61 | 62 | // Start audio processing 63 | pxi_oscillator.start(0); 64 | context.startRendering(); 65 | context.oncomplete = function (evnt) { 66 | var sha1 = CryptoJS.algo.SHA1.create(); 67 | for (var i = 0; i < evnt.renderedBuffer.length; i++) { 68 | sha1.update(evnt.renderedBuffer.getChannelData(0)[i].toString()); 69 | } 70 | var hash = sha1.finalize(); 71 | audioHashInfo = hash.toString(CryptoJS.enc.Hex); 72 | 73 | for (var i = 4500; 5e3 > i; i++) { 74 | audioSumInfo += Math.abs(evnt.renderedBuffer.getChannelData(0)[i]); 75 | } 76 | pxi_compressor.disconnect(); 77 | document.getElementById("audioHash").innerHTML = audioHashInfo; 78 | document.getElementById("audioSum").innerHTML = audioSumInfo; 79 | resolve(); 80 | } 81 | } catch (u) { 82 | reject(); 83 | } 84 | } 85 | 86 | function orderTest(resolve, reject) { 87 | var order = ""; 88 | for(var i in navigator){ 89 | order += i.toString(); 90 | } 91 | orderInfo = hashSha1(order); 92 | document.getElementById("order").innerHTML = orderInfo; 93 | resolve(); 94 | } 95 | 96 | 97 | function run(){ 98 | nbTest++; 99 | canvasInfo = ""; 100 | audioHashInfo = ""; 101 | audioSumInfo = 0; 102 | orderInfo = ""; 103 | 104 | console.log("Test n°"+nbTest); 105 | Promise.all([new Promise(canvasTest),new Promise(audioTest),new Promise(orderTest)]).then(function(){ 106 | var table = document.getElementById('table').getElementsByTagName('tbody')[0]; 107 | var newRow = table.insertRow(table.rows.length); 108 | var info = [nbTest,canvasInfo,audioHashInfo,audioSumInfo,orderInfo]; 109 | for(var i = 0; i< info.length; i++){ 110 | var newCell = newRow.insertCell(i); 111 | var newText = document.createTextNode(info[i]); 112 | newCell.appendChild(newText); 113 | } 114 | }); 115 | } -------------------------------------------------------------------------------- /fprandom.patch: -------------------------------------------------------------------------------- 1 | diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js 2 | --- a/browser/app/profile/firefox.js 3 | +++ b/browser/app/profile/firefox.js 4 | @@ -1131,6 +1131,9 @@ pref("services.sync.prefs.sync.privacy.d 5 | pref("services.sync.prefs.sync.privacy.sanitize.sanitizeOnShutdown", true); 6 | pref("services.sync.prefs.sync.privacy.trackingprotection.enabled", true); 7 | pref("services.sync.prefs.sync.privacy.trackingprotection.pbmode.enabled", true); 8 | +pref("services.sync.prefs.sync.privacy.fingerprintingprotection.enabled", true); 9 | +pref("services.sync.prefs.sync.privacy.fingerprintingprotection.pbmode.enabled", true); 10 | +pref("services.sync.prefs.sync.privacy.fingerprintingprotection.random.enabled", true); 11 | pref("services.sync.prefs.sync.security.OCSP.enabled", true); 12 | pref("services.sync.prefs.sync.security.OCSP.require", true); 13 | pref("services.sync.prefs.sync.security.default_personal_cert", true); 14 | @@ -1422,8 +1425,10 @@ pref("media.gmp-provider.enabled", true) 15 | 16 | #ifdef NIGHTLY_BUILD 17 | pref("privacy.trackingprotection.ui.enabled", true); 18 | +pref("privacy.fingerprintingprotection.ui.enabled", true); 19 | #else 20 | pref("privacy.trackingprotection.ui.enabled", false); 21 | +pref("privacy.fingerprintingprotection.ui.enabled", true); 22 | #endif 23 | pref("privacy.trackingprotection.introCount", 0); 24 | pref("privacy.trackingprotection.introURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/tracking-protection/start/"); 25 | diff --git a/browser/branding/unofficial/locales/en-US/brand.dtd b/browser/branding/unofficial/locales/en-US/brand.dtd 26 | --- a/browser/branding/unofficial/locales/en-US/brand.dtd 27 | +++ b/browser/branding/unofficial/locales/en-US/brand.dtd 28 | @@ -2,8 +2,8 @@ 29 | - License, v. 2.0. If a copy of the MPL was not distributed with this 30 | - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> 31 | 32 | - 33 | - 34 | - 35 | + 36 | + 37 | + 38 | 39 | 40 | diff --git a/browser/branding/unofficial/locales/en-US/brand.properties b/browser/branding/unofficial/locales/en-US/brand.properties 41 | --- a/browser/branding/unofficial/locales/en-US/brand.properties 42 | +++ b/browser/branding/unofficial/locales/en-US/brand.properties 43 | @@ -2,9 +2,9 @@ 44 | # License, v. 2.0. If a copy of the MPL was not distributed with this 45 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 46 | 47 | -brandShorterName=Nightly 48 | -brandShortName=Nightly 49 | -brandFullName=Nightly 50 | +brandShorterName=FP Random (DiverSE build) 51 | +brandShortName=FP Random (DiverSE build) 52 | +brandFullName=FP Random (DiverSE build) 53 | vendorShortName=Mozilla 54 | 55 | syncBrandShortName=Sync 56 | diff --git a/browser/components/preferences/in-content/privacy.js b/browser/components/preferences/in-content/privacy.js 57 | --- a/browser/components/preferences/in-content/privacy.js 58 | +++ b/browser/components/preferences/in-content/privacy.js 59 | @@ -52,6 +52,25 @@ var gPrivacyPane = { 60 | }, 61 | 62 | /** 63 | + * Show the Fingerprinting Protection UI depending on the 64 | + * privacy.fingerprintingprotection.ui.enabled pref, and linkify its Learn More link 65 | + */ 66 | + _initFingerprintingProtection: function() { 67 | + if (!Services.prefs.getBoolPref("privacy.fingerprintingprotection.ui.enabled")) { 68 | + return; 69 | + } 70 | + 71 | + let link = document.getElementById("fingerprintingProtectionLearnMore"); 72 | + let url = "https://amiunique.org/faq"; 73 | + link.setAttribute("href", url); 74 | + 75 | + document.getElementById("fingerprintingprotectionbox").hidden = false; 76 | + document.getElementById("fingerprintingmodebox").hidden = false; 77 | + 78 | + this.fingerprintingProtectionReadPrefs(); 79 | + }, 80 | + 81 | + /** 82 | * Initialize autocomplete to ensure prefs are in sync. 83 | */ 84 | _initAutocomplete() { 85 | @@ -129,6 +148,7 @@ var gPrivacyPane = { 86 | this.initAutoStartPrivateBrowsingReverter(); 87 | this._initTrackingProtection(); 88 | this._initTrackingProtectionPBM(); 89 | + this._initFingerprintingProtection(); 90 | this._initAutocomplete(); 91 | this._initBrowserContainers(); 92 | 93 | @@ -174,6 +194,10 @@ var gPrivacyPane = { 94 | gPrivacyPane.showBlockLists); 95 | setEventListener("changeBlockListPBM", "command", 96 | gPrivacyPane.showBlockLists); 97 | + setEventListener("fingerprintingProtectionRadioGroup", "command", 98 | + gPrivacyPane.fingerprintingProtectionWritePrefs); 99 | + setEventListener("fingerprintingModeRadioGroup", "command", 100 | + gPrivacyPane.fingerprintingProtectionWritePrefs); 101 | setEventListener("browserContainersCheckbox", "command", 102 | gPrivacyPane._checkBrowserContainers); 103 | setEventListener("browserContainersSettings", "command", 104 | @@ -224,6 +248,59 @@ var gPrivacyPane = { 105 | } 106 | }, 107 | 108 | + // Fingerprinting PROTECTION MODE 109 | + 110 | + /** 111 | + * Selects the right item of the Fingerprinting Protection radiogroup. 112 | + */ 113 | + fingerprintingProtectionReadPrefs() { 114 | + let enabledPref = document.getElementById("privacy.fingerprintingprotection.enabled"); 115 | + let pbmPref = document.getElementById("privacy.fingerprintingprotection.pbmode.enabled"); 116 | + let radiogroup = document.getElementById("fingerprintingProtectionRadioGroup"); 117 | + radiogroup.value = enabledPref.value ? "always" : 118 | + pbmPref.value ? "private" : 119 | + "never"; 120 | + 121 | + let randomModePref = document.getElementById("privacy.fingerprintingprotection.random.enabled"); 122 | + let modeRadioGroup = document.getElementById("fingerprintingModeRadioGroup"); 123 | + modeRadioGroup.value = randomModePref.value ? "random" : "session"; 124 | + }, 125 | + 126 | + /** 127 | + * Sets the pref values based on the selected item of the radiogroup. 128 | + */ 129 | + fingerprintingProtectionWritePrefs() { 130 | + let enabledPref = document.getElementById("privacy.fingerprintingprotection.enabled"); 131 | + let pbmPref = document.getElementById("privacy.fingerprintingprotection.pbmode.enabled"); 132 | + let radiogroup = document.getElementById("fingerprintingProtectionRadioGroup"); 133 | + switch (radiogroup.value) { 134 | + case "always": 135 | + enabledPref.value = true; 136 | + pbmPref.value = true; 137 | + break; 138 | + case "private": 139 | + enabledPref.value = false; 140 | + pbmPref.value = true; 141 | + break; 142 | + case "never": 143 | + enabledPref.value = false; 144 | + pbmPref.value = false; 145 | + break; 146 | + } 147 | + 148 | + let randomModePref = document.getElementById("privacy.fingerprintingprotection.random.enabled"); 149 | + let modeRadioGroup = document.getElementById("fingerprintingModeRadioGroup"); 150 | + 151 | + switch (modeRadioGroup.value) { 152 | + case "random": 153 | + randomModePref.value = true; 154 | + break; 155 | + case "session": 156 | + randomModePref.value = false; 157 | + break; 158 | + } 159 | + }, 160 | + 161 | // HISTORY MODE 162 | 163 | /** 164 | diff --git a/browser/components/preferences/in-content/privacy.xul b/browser/components/preferences/in-content/privacy.xul 165 | --- a/browser/components/preferences/in-content/privacy.xul 166 | +++ b/browser/components/preferences/in-content/privacy.xul 167 | @@ -45,6 +45,17 @@ 168 | name="browser.urlbar.suggest.openpage" 169 | type="bool"/> 170 | 171 | + 172 | + 175 | + 178 | + 181 | + 182 | 183 | 187 | 188 | 189 | + 190 | + 191 | + 235 | diff --git a/browser/locales/en-US/chrome/browser/preferences/privacy.dtd b/browser/locales/en-US/chrome/browser/preferences/privacy.dtd 236 | --- a/browser/locales/en-US/chrome/browser/preferences/privacy.dtd 237 | +++ b/browser/locales/en-US/chrome/browser/preferences/privacy.dtd 238 | @@ -26,6 +26,21 @@ 239 | 240 | 241 | 242 | + 243 | + 244 | + 245 | + 246 | + 247 | + 248 | + 249 | + 250 | + 251 | + 252 | + 253 | + 254 | + 255 | + 256 | + 257 | 258 | 259 | 260 | diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp 261 | --- a/dom/canvas/CanvasRenderingContext2D.cpp 262 | +++ b/dom/canvas/CanvasRenderingContext2D.cpp 263 | @@ -24,6 +24,7 @@ 264 | #include "nsIInterfaceRequestorUtils.h" 265 | #include "nsIFrame.h" 266 | #include "nsError.h" 267 | +#include "nsIFontEnumerator.h" 268 | 269 | #include "nsCSSParser.h" 270 | #include "mozilla/css/StyleRule.h" 271 | @@ -1106,6 +1107,34 @@ CanvasRenderingContext2D::WrapObject(JSC 272 | return CanvasRenderingContext2DBinding::Wrap(aCx, this, aGivenProto); 273 | } 274 | 275 | +int32_t MIN_HEX_CODE = 0; 276 | +int32_t MAX_HEX_CODE = 255; 277 | +int randomR, randomG, randomB; 278 | + 279 | +int getRandomRGB(){ 280 | + std::srand(time(NULL)); 281 | + //Range from -3 to +3 282 | + randomR = rand() % 7; 283 | + randomG = rand() % 7; 284 | + randomB = rand() % 7; 285 | + return 0; 286 | +} 287 | +int verifRGB = getRandomRGB(); 288 | + 289 | +int 290 | +getModifiedColor(int color, int randomNum) 291 | +{ 292 | + 293 | + int newColor = color - 3 + randomNum; 294 | + if (MIN_HEX_CODE > newColor) { 295 | + newColor = 0; 296 | + } 297 | + if(newColor > MAX_HEX_CODE) { 298 | + newColor = 255; 299 | + } 300 | + return newColor; 301 | +} 302 | + 303 | bool 304 | CanvasRenderingContext2D::ParseColor(const nsAString& aString, 305 | nscolor* aColor) 306 | @@ -1139,6 +1168,24 @@ CanvasRenderingContext2D::ParseColor(con 307 | value, presShell ? presShell->GetPresContext() : nullptr, parentContext, 308 | *aColor); 309 | } 310 | + 311 | + if (mozilla::Preferences::GetBool("privacy.fingerprintingprotection.enabled", true)) { 312 | + int r(NS_GET_R(*aColor)); 313 | + int g(NS_GET_G(*aColor)); 314 | + int b(NS_GET_B(*aColor)); 315 | + int a(NS_GET_A(*aColor)); 316 | + 317 | + //If random mode selected, reinitialize color variables 318 | + if(mozilla::Preferences::GetBool("privacy.fingerprintingprotection.random.enabled", true)){ 319 | + verifRGB = getRandomRGB(); 320 | + } 321 | + 322 | + r = getModifiedColor(r,randomR); 323 | + g = getModifiedColor(g,randomG); 324 | + b = getModifiedColor(b,randomB); 325 | + *aColor = NS_RGBA(r, g, b, a); 326 | + } 327 | + 328 | return true; 329 | } 330 | 331 | @@ -3604,11 +3651,60 @@ CanvasRenderingContext2D::TransformWillU 332 | // text 333 | // 334 | 335 | +nsString randomFont; 336 | +char16_t **fonts; 337 | +uint32_t count_fonts; 338 | + 339 | +int initDone = 0; 340 | + 341 | +int getRandomFont(){ 342 | + int randomNum = rand() % count_fonts; 343 | + randomFont.Assign(fonts[randomNum]); 344 | + return 0; 345 | +} 346 | + 347 | +int initFontList() { 348 | + nsresult rv; 349 | + nsCOMPtr fontEnum = do_GetService("@mozilla.org/gfx/fontenumerator;1", &rv); 350 | + const char *aLangGroup = "en"; 351 | + const char *aGeneric = "sans-serif"; 352 | + fontEnum->EnumerateFonts(aLangGroup, aGeneric, &count_fonts, &fonts); 353 | + std::srand(time(NULL)); 354 | + getRandomFont(); 355 | + return 0; 356 | +} 357 | + 358 | +nsString getModifiedFont(const nsAString& aFont) { 359 | + if(mozilla::Preferences::GetBool("privacy.fingerprintingprotection.random.enabled", true)){ 360 | + if(initDone == 0){ 361 | + initDone = 1; 362 | + initFontList(); 363 | + } 364 | + getRandomFont(); 365 | + } 366 | + nsString end = NS_LITERAL_STRING("'"); 367 | + int pos = aFont.FindChar(' '); 368 | + const nsAString &starting = Substring(aFont, 0, pos); 369 | + nsString userfont(starting); 370 | + userfont.Append(NS_LITERAL_STRING(" '")); 371 | + userfont.Append(randomFont); 372 | + userfont.Append(end); 373 | + 374 | + return userfont; 375 | +} 376 | + 377 | void 378 | CanvasRenderingContext2D::SetFont(const nsAString& aFont, 379 | ErrorResult& aError) 380 | { 381 | - SetFontInternal(aFont, aError); 382 | + nsString userfont; 383 | + if (mozilla::Preferences::GetBool("privacy.fingerprintingprotection.enabled", true)) { 384 | + userfont = getModifiedFont(aFont); 385 | + } 386 | + else { 387 | + userfont = aFont; 388 | + } 389 | + SetFontInternal(userfont, aError); 390 | } 391 | 392 | bool 393 | diff --git a/dom/media/webaudio/AudioNodeEngine.cpp b/dom/media/webaudio/AudioNodeEngine.cpp 394 | --- a/dom/media/webaudio/AudioNodeEngine.cpp 395 | +++ b/dom/media/webaudio/AudioNodeEngine.cpp 396 | @@ -53,16 +53,40 @@ WriteZeroesToAudioBlock(AudioBlock* aChu 397 | } 398 | } 399 | 400 | +float initScale(){ 401 | + std::srand(time(NULL)); 402 | + return rand(); 403 | +} 404 | +float ra = initScale(); 405 | + 406 | +float getNewScale(float aScale){ 407 | + if (mozilla::Preferences::GetBool("privacy.fingerprintingprotection.random.enabled", true)) { 408 | + ra = rand(); 409 | + } 410 | + if(aScale>0.05){ 411 | + return aScale - ra / ( RAND_MAX / 0.001 ); 412 | + } else { 413 | + return aScale; 414 | + }; 415 | +} 416 | + 417 | void AudioBufferCopyWithScale(const float* aInput, 418 | float aScale, 419 | float* aOutput, 420 | uint32_t aSize) 421 | { 422 | - if (aScale == 1.0f) { 423 | - PodCopy(aOutput, aInput, aSize); 424 | + if (mozilla::Preferences::GetBool("privacy.fingerprintingprotection.enabled", true)) { 425 | + float newScale = getNewScale(aScale); 426 | + for (uint32_t i = 0; i < aSize; ++i) { 427 | + aOutput[i] = aInput[i] * newScale; 428 | + } 429 | } else { 430 | - for (uint32_t i = 0; i < aSize; ++i) { 431 | - aOutput[i] = aInput[i]*aScale; 432 | + if (aScale == 1.0f) { 433 | + PodCopy(aOutput, aInput, aSize); 434 | + } else { 435 | + for (uint32_t i = 0; i < aSize; ++i) { 436 | + aOutput[i] = aInput[i] * aScale; 437 | + } 438 | } 439 | } 440 | } 441 | @@ -72,51 +96,59 @@ void AudioBufferAddWithScale(const float 442 | float* aOutput, 443 | uint32_t aSize) 444 | { 445 | + 446 | + if (mozilla::Preferences::GetBool("privacy.fingerprintingprotection.enabled", true)) { 447 | + float newScale = getNewScale(aScale); 448 | + for (uint32_t i = 0; i < aSize; ++i) { 449 | + aOutput[i] += aInput[i] * newScale; 450 | + } 451 | + } else { 452 | #ifdef BUILD_ARM_NEON 453 | - if (mozilla::supports_neon()) { 454 | - AudioBufferAddWithScale_NEON(aInput, aScale, aOutput, aSize); 455 | - return; 456 | - } 457 | + if (mozilla::supports_neon()) { 458 | + AudioBufferAddWithScale_NEON(aInput, aScale, aOutput, aSize); 459 | + return; 460 | + } 461 | #endif 462 | 463 | #ifdef USE_SSE2 464 | - if (mozilla::supports_sse2()) { 465 | - if (aScale == 1.0f) { 466 | - while (aSize && (!IS_ALIGNED16(aInput) || !IS_ALIGNED16(aOutput))) { 467 | - *aOutput += *aInput; 468 | - ++aOutput; 469 | - ++aInput; 470 | - --aSize; 471 | + if (mozilla::supports_sse2()) { 472 | + if (aScale == 1.0f) { 473 | + while (aSize && (!IS_ALIGNED16(aInput) || !IS_ALIGNED16(aOutput))) { 474 | + *aOutput += *aInput; 475 | + ++aOutput; 476 | + ++aInput; 477 | + --aSize; 478 | + } 479 | + } else { 480 | + while (aSize && (!IS_ALIGNED16(aInput) || !IS_ALIGNED16(aOutput))) { 481 | + *aOutput += *aInput*aScale; 482 | + ++aOutput; 483 | + ++aInput; 484 | + --aSize; 485 | + } 486 | } 487 | - } else { 488 | - while (aSize && (!IS_ALIGNED16(aInput) || !IS_ALIGNED16(aOutput))) { 489 | - *aOutput += *aInput*aScale; 490 | - ++aOutput; 491 | - ++aInput; 492 | - --aSize; 493 | + 494 | + // we need to round aSize down to the nearest multiple of 16 495 | + uint32_t alignedSize = aSize & ~0x0F; 496 | + if (alignedSize > 0) { 497 | + AudioBufferAddWithScale_SSE(aInput, aScale, aOutput, alignedSize); 498 | + 499 | + // adjust parameters for use with scalar operations below 500 | + aInput += alignedSize; 501 | + aOutput += alignedSize; 502 | + aSize -= alignedSize; 503 | } 504 | } 505 | - 506 | - // we need to round aSize down to the nearest multiple of 16 507 | - uint32_t alignedSize = aSize & ~0x0F; 508 | - if (alignedSize > 0) { 509 | - AudioBufferAddWithScale_SSE(aInput, aScale, aOutput, alignedSize); 510 | - 511 | - // adjust parameters for use with scalar operations below 512 | - aInput += alignedSize; 513 | - aOutput += alignedSize; 514 | - aSize -= alignedSize; 515 | - } 516 | - } 517 | #endif 518 | 519 | - if (aScale == 1.0f) { 520 | - for (uint32_t i = 0; i < aSize; ++i) { 521 | - aOutput[i] += aInput[i]; 522 | - } 523 | - } else { 524 | - for (uint32_t i = 0; i < aSize; ++i) { 525 | - aOutput[i] += aInput[i]*aScale; 526 | + if (aScale == 1.0f) { 527 | + for (uint32_t i = 0; i < aSize; ++i) { 528 | + aOutput[i] += aInput[i]; 529 | + } 530 | + } else { 531 | + for (uint32_t i = 0; i < aSize; ++i) { 532 | + aOutput[i] += aInput[i] * aScale; 533 | + } 534 | } 535 | } 536 | } 537 | @@ -134,25 +166,32 @@ AudioBlockCopyChannelWithScale(const flo 538 | float aScale, 539 | float* aOutput) 540 | { 541 | - if (aScale == 1.0f) { 542 | - memcpy(aOutput, aInput, WEBAUDIO_BLOCK_SIZE*sizeof(float)); 543 | + if (mozilla::Preferences::GetBool("privacy.fingerprintingprotection.enabled", true)) { 544 | + float newScale = getNewScale(aScale); 545 | + for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) { 546 | + aOutput[i] = aInput[i] * newScale; 547 | + } 548 | } else { 549 | + if (aScale == 1.0f) { 550 | + memcpy(aOutput, aInput, WEBAUDIO_BLOCK_SIZE*sizeof(float)); 551 | + } else { 552 | #ifdef BUILD_ARM_NEON 553 | - if (mozilla::supports_neon()) { 554 | - AudioBlockCopyChannelWithScale_NEON(aInput, aScale, aOutput); 555 | - return; 556 | - } 557 | + if (mozilla::supports_neon()) { 558 | + AudioBlockCopyChannelWithScale_NEON(aInput, aScale, aOutput); 559 | + return; 560 | + } 561 | #endif 562 | 563 | #ifdef USE_SSE2 564 | - if (mozilla::supports_sse2()) { 565 | - AudioBlockCopyChannelWithScale_SSE(aInput, aScale, aOutput); 566 | - return; 567 | - } 568 | + if (mozilla::supports_sse2()) { 569 | + AudioBlockCopyChannelWithScale_SSE(aInput, aScale, aOutput); 570 | + return; 571 | + } 572 | #endif 573 | 574 | - for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) { 575 | - aOutput[i] = aInput[i]*aScale; 576 | + for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) { 577 | + aOutput[i] = aInput[i] * aScale; 578 | + } 579 | } 580 | } 581 | } 582 | @@ -232,25 +271,32 @@ AudioBufferInPlaceScale(float* aBlock, 583 | float aScale, 584 | uint32_t aSize) 585 | { 586 | - if (aScale == 1.0f) { 587 | - return; 588 | - } 589 | + if (mozilla::Preferences::GetBool("privacy.fingerprintingprotection.enabled", true)) { 590 | + float newScale = getNewScale(aScale); 591 | + for (uint32_t i = 0; i < aSize; ++i) { 592 | + *aBlock++ *= newScale; 593 | + } 594 | + } else { 595 | + if (aScale == 1.0f) { 596 | + return; 597 | + } 598 | #ifdef BUILD_ARM_NEON 599 | - if (mozilla::supports_neon()) { 600 | - AudioBufferInPlaceScale_NEON(aBlock, aScale, aSize); 601 | - return; 602 | - } 603 | + if (mozilla::supports_neon()) { 604 | + AudioBufferInPlaceScale_NEON(aBlock, aScale, aSize); 605 | + return; 606 | + } 607 | #endif 608 | 609 | #ifdef USE_SSE2 610 | - if (mozilla::supports_sse2()) { 611 | - AudioBufferInPlaceScale_SSE(aBlock, aScale, aSize); 612 | - return; 613 | - } 614 | + if (mozilla::supports_sse2()) { 615 | + AudioBufferInPlaceScale_SSE(aBlock, aScale, aSize); 616 | + return; 617 | + } 618 | #endif 619 | 620 | - for (uint32_t i = 0; i < aSize; ++i) { 621 | - *aBlock++ *= aScale; 622 | + for (uint32_t i = 0; i < aSize; ++i) { 623 | + *aBlock++ *= aScale; 624 | + } 625 | } 626 | } 627 | 628 | diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp 629 | --- a/js/src/jsiter.cpp 630 | +++ b/js/src/jsiter.cpp 631 | @@ -39,6 +39,8 @@ 632 | #include "vm/NativeObject-inl.h" 633 | #include "vm/Stack-inl.h" 634 | #include "vm/String-inl.h" 635 | +#include 636 | +#include 637 | 638 | using namespace js; 639 | using namespace js::gc; 640 | @@ -275,7 +277,25 @@ EnumerateNativeProperties(JSContext* cx, 641 | return true; 642 | } 643 | 644 | -#ifdef JS_MORE_DETERMINISTIC 645 | +//#ifdef JS_MORE_DETERMINISTIC 646 | +bool b1,b2,b3,b4,b5,bMin,bMax; 647 | +bool bArray[250]; 648 | + 649 | +int initBool(){ 650 | + std::srand(time(NULL)); 651 | + b1 = (rand() % 2 == 1); 652 | + b2 = (rand() % 2 == 1); 653 | + b3 = (rand() % 2 == 1); 654 | + b4 = (rand() % 2 == 1); 655 | + b5 = (rand() % 2 == 1); 656 | + for (int i = 0; i < 250; ++i) { 657 | + bArray[i] = (rand() % 2 == 1); 658 | + } 659 | + bMin = (rand() % 2 == 1); 660 | + bMax = (rand() % 2 == 1); 661 | + return 0; 662 | +} 663 | +int verif = initBool(); 664 | 665 | struct SortComparatorIds 666 | { 667 | @@ -286,22 +306,30 @@ struct SortComparatorIds 668 | 669 | bool operator()(jsid a, jsid b, bool* lessOrEqualp) 670 | { 671 | + /* 672 | + *lessOrEqualp = (rand() % 2 == 1); 673 | + return true; 674 | + */ 675 | + 676 | // Pick an arbitrary order on jsids that is as stable as possible 677 | // across executions. 678 | if (a == b) { 679 | - *lessOrEqualp = true; 680 | + //*lessOrEqualp = true; 681 | + *lessOrEqualp = b1; 682 | return true; 683 | } 684 | 685 | size_t ta = JSID_BITS(a) & JSID_TYPE_MASK; 686 | size_t tb = JSID_BITS(b) & JSID_TYPE_MASK; 687 | if (ta != tb) { 688 | - *lessOrEqualp = (ta <= tb); 689 | + //*lessOrEqualp = (ta <= tb); 690 | + *lessOrEqualp = b2; 691 | return true; 692 | } 693 | 694 | if (JSID_IS_INT(a)) { 695 | - *lessOrEqualp = (JSID_TO_INT(a) <= JSID_TO_INT(b)); 696 | + //*lessOrEqualp = (JSID_TO_INT(a) <= JSID_TO_INT(b)); 697 | + *lessOrEqualp = b3; 698 | return true; 699 | } 700 | 701 | @@ -311,14 +339,16 @@ struct SortComparatorIds 702 | JS::SymbolCode ca = JSID_TO_SYMBOL(a)->code(); 703 | JS::SymbolCode cb = JSID_TO_SYMBOL(b)->code(); 704 | if (ca != cb) { 705 | - *lessOrEqualp = uint32_t(ca) <= uint32_t(cb); 706 | + //*lessOrEqualp = uint32_t(ca) <= uint32_t(cb); 707 | + *lessOrEqualp = b4; 708 | return true; 709 | } 710 | MOZ_ASSERT(ca == JS::SymbolCode::InSymbolRegistry || ca == JS::SymbolCode::UniqueSymbol); 711 | astr = JSID_TO_SYMBOL(a)->description(); 712 | bstr = JSID_TO_SYMBOL(b)->description(); 713 | if (!astr || !bstr) { 714 | - *lessOrEqualp = !astr; 715 | + //*lessOrEqualp = !astr; 716 | + *lessOrEqualp = b5; 717 | return true; 718 | } 719 | 720 | @@ -338,12 +368,20 @@ struct SortComparatorIds 721 | if (!CompareStrings(cx, astr, bstr, &result)) 722 | return false; 723 | 724 | - *lessOrEqualp = (result <= 0); 725 | + //*lessOrEqualp = (result <= 0); 726 | + if (result <= -125){ 727 | + *lessOrEqualp = bMin; 728 | + } else if(result>=125){ 729 | + *lessOrEqualp = bMax; 730 | + } else { 731 | + int ind = result+125; 732 | + *lessOrEqualp = bArray[ind]; 733 | + } 734 | return true; 735 | } 736 | }; 737 | 738 | -#endif /* JS_MORE_DETERMINISTIC */ 739 | +//#endif /* JS_MORE_DETERMINISTIC */ 740 | 741 | static bool 742 | Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags, AutoIdVector* props) 743 | @@ -426,7 +464,7 @@ Snapshot(JSContext* cx, HandleObject pob 744 | 745 | } while (pobj != nullptr); 746 | 747 | -#ifdef JS_MORE_DETERMINISTIC 748 | +//#ifdef JS_MORE_DETERMINISTIC 749 | 750 | /* 751 | * In some cases the enumeration order for an object depends on the 752 | @@ -454,7 +492,7 @@ Snapshot(JSContext* cx, HandleObject pob 753 | if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx))) 754 | return false; 755 | 756 | -#endif /* JS_MORE_DETERMINISTIC */ 757 | +//#endif /* JS_MORE_DETERMINISTIC */ 758 | 759 | return true; 760 | } 761 | --------------------------------------------------------------------------------