summaryrefslogtreecommitdiff
path: root/www/chromium-uc/patches/extra/bromite/flag-fingerprinting-canvas-image-data-noise.patch
blob: 6ebc7a16c61e512a1e66ac7337b0ecc3b9351c9a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
# NOTE: Changes made:
# * Added flag --fingerprinting-canvas-image-data-noise to enable/disable
#   Canvas image data fingerprinting deception
# * Removed WebGLDebugRendererInfo disabling in favor of an alternative
#   implementation in ungoogled-chromium/disable-webgl-renderer-info.patch
# * Tweak subchannel noise generation to require fewer random number generation
From: csagan5 <32685696+csagan5@users.noreply.github.com>
Date: Sat, 24 Mar 2018 05:18:03 +0100
Subject: Canvas: fingerprinting mitigations for image data and webGL

Disable webGL renderer info and modify the color data returned by ToBlob,
ToDataURL and getImageData so that it will contain randomly manipulated
pixels (maximum 10) that slightly change the color of the R,G,B components
without a visible effect.

Credits to Slaviro (https://github.com/Slaviro) for coming up with a better
approach to change color components.
---
 .../platform/graphics/image_data_buffer.cc         |   5 +
 .../platform/graphics/static_bitmap_image.cc       | 154 +++++++++++++++++++++
 .../platform/graphics/static_bitmap_image.h        |   2 +
 4 files changed, 163 insertions(+), 2 deletions(-)

--- chrome/browser/bromite_flag_entries.h.orig
+++ chrome/browser/bromite_flag_entries.h
@@ -16,4 +16,8 @@
      flag_descriptions::kMaxConnectionsPerHostName,
      flag_descriptions::kMaxConnectionsPerHostDescription,
      kOsAll, MULTI_VALUE_TYPE(kMaxConnectionsPerHostChoices)},
+    {"fingerprinting-canvas-image-data-noise",
+     "Enable Canvas image data fingerprint deception",
+     "Slightly modifies at most 10 pixels in Canvas image data extracted via JS APIs.  ungoogled-chromium flag, Bromite feature.",
+     kOsAll, SINGLE_VALUE_TYPE(switches::kFingerprintingCanvasImageDataNoise)},
 #endif  // CHROME_BROWSER_BROMITE_FLAG_ENTRIES_H_
--- content/browser/renderer_host/render_process_host_impl.cc.orig
+++ content/browser/renderer_host/render_process_host_impl.cc
@@ -3468,6 +3468,7 @@ void RenderProcessHostImpl::PropagateBro
     switches::kFileUrlPathAlias,
     switches::kFingerprintingClientRectsNoise,
     switches::kFingerprintingCanvasMeasureTextNoise,
+    switches::kFingerprintingCanvasImageDataNoise,
     switches::kForceDeviceScaleFactor,
     switches::kForceDisplayColorProfile,
     switches::kForceGpuMemAvailableMb,
--- content/child/runtime_features.cc.orig
+++ content/child/runtime_features.cc
@@ -471,6 +471,8 @@ void SetRuntimeFeaturesFromCommandLine(c
        switches::kFingerprintingClientRectsNoise, true},
       {wrf::EnableFingerprintingCanvasMeasureTextNoise,
        switches::kFingerprintingCanvasMeasureTextNoise, true},
+      {wrf::EnableFingerprintingCanvasImageDataNoise,
+       switches::kFingerprintingCanvasImageDataNoise, true},
   };
   for (const auto& mapping : switchToFeatureMapping) {
     if (command_line.HasSwitch(mapping.switch_name))
--- third_party/blink/public/platform/web_runtime_features.h.orig
+++ third_party/blink/public/platform/web_runtime_features.h
@@ -220,6 +220,7 @@ class WebRuntimeFeatures {
   BLINK_PLATFORM_EXPORT static void EnableAllowSyncXHRInPageDismissal(bool);
   BLINK_PLATFORM_EXPORT static void EnableFingerprintingClientRectsNoise(bool);
   BLINK_PLATFORM_EXPORT static void EnableFingerprintingCanvasMeasureTextNoise(bool);
+  BLINK_PLATFORM_EXPORT static void EnableFingerprintingCanvasImageDataNoise(bool);
   BLINK_PLATFORM_EXPORT static void EnableSignedExchangeSubresourcePrefetch(
       bool);
   BLINK_PLATFORM_EXPORT static void EnableSubresourceWebBundles(bool);
--- third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc.orig
+++ third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/stroke_data.h"
 #include "third_party/blink/renderer/platform/graphics/video_frame_image_util.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
@@ -2311,6 +2312,9 @@ ImageData* BaseRenderingContext2D::getIm
           snapshot->PaintImageForCurrentFrame().GetSkImageInfo().bounds();
       DCHECK(!bounds.intersect(SkIRect::MakeXYWH(sx, sy, sw, sh)));
     }
+    if (read_pixels_successful && RuntimeEnabledFeatures::FingerprintingCanvasImageDataNoiseEnabled()) {
+      StaticBitmapImage::ShuffleSubchannelColorData(image_data_pixmap.addr(), image_data_pixmap.info(), sx, sy);
+    }
   }
 
   return image_data;
--- third_party/blink/renderer/platform/BUILD.gn.orig
+++ third_party/blink/renderer/platform/BUILD.gn
@@ -1645,7 +1645,9 @@ component("platform") {
     "//third_party/blink/renderer:non_test_config",
   ]
 
-  include_dirs = []
+  include_dirs = [
+    "//third_party/skia/include/private", # For shuffler in graphics/static_bitmap_image.cc
+  ]
 
   allow_circular_includes_from = [
     "//third_party/blink/renderer/platform/blob",
--- third_party/blink/renderer/platform/exported/web_runtime_features.cc.orig
+++ third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -697,4 +697,8 @@ void WebRuntimeFeatures::EnableFingerpri
   RuntimeEnabledFeatures::SetFingerprintingCanvasMeasureTextNoiseEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnableFingerprintingCanvasImageDataNoise(bool enable) {
+  RuntimeEnabledFeatures::SetFingerprintingCanvasImageDataNoiseEnabled(enable);
+}
+
 }  // namespace blink
--- third_party/blink/renderer/platform/graphics/image_data_buffer.cc.orig
+++ third_party/blink/renderer/platform/graphics/image_data_buffer.cc
@@ -36,6 +36,8 @@
 
 #include "base/compiler_specific.h"
 #include "base/memory/ptr_util.h"
+#include "base/rand_util.h"
+#include "base/logging.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/image-encoders/image_encoder.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
@@ -145,6 +147,11 @@ bool ImageDataBuffer::EncodeImageInterna
                                           const SkPixmap& pixmap) const {
   DCHECK(is_valid_);
 
+  if (RuntimeEnabledFeatures::FingerprintingCanvasImageDataNoiseEnabled()) {
+    // shuffle subchannel color data within the pixmap
+    StaticBitmapImage::ShuffleSubchannelColorData(pixmap_.writable_addr(), pixmap_.info(), 0, 0);
+  }
+
   if (mime_type == kMimeTypeJpeg) {
     SkJpegEncoder::Options options;
     options.fQuality = ImageEncoder::ComputeJpegQuality(quality);
--- third_party/blink/renderer/platform/graphics/static_bitmap_image.cc.orig
+++ third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 
+#include "base/rand_util.h"
+#include "base/logging.h"
 #include "base/numerics/checked_math.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
@@ -11,10 +13,12 @@
 #include "third_party/blink/renderer/platform/graphics/image_observer.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
 #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/private/SkColorData.h"
 #include "v8/include/v8.h"
 
 namespace blink {
@@ -80,4 +84,154 @@ void StaticBitmapImage::DrawHelper(cc::P
       WebCoreClampingModeToSkiaRectConstraint(draw_options.clamping_mode));
 }
 
+// set the component to maximum-delta if it is >= maximum, or add to existing color component (color + delta)
+#define shuffleComponent(color, max, delta) ( (color) >= (max) ? ((max)-(delta)) : ((color)+(delta)) )
+
+#define writable_addr(T, p, stride, x, y) (T*)((const char *)p + y * stride + x * sizeof(T))
+
+void StaticBitmapImage::ShuffleSubchannelColorData(const void *addr, const SkImageInfo& info, int srcX, int srcY) {
+  auto w = info.width() - srcX, h = info.height() - srcY;
+
+  // skip tiny images; info.width()/height() can also be 0
+  if ((w < 8) || (h < 8)) {
+    return;
+  }
+
+  // generate the first random number here
+  double shuffleX = base::RandDouble();
+
+  // cap maximum pixels to change
+  auto pixels = (w + h) / 128;
+  if (pixels > 10) {
+    pixels = 10;
+  } else if (pixels < 2) {
+    pixels = 2;
+  }
+
+  auto colorType = info.colorType();
+  auto fRowBytes = info.minRowBytes(); // stride
+
+  DLOG(INFO) << "BRM: ShuffleSubchannelColorData() w=" << w << " h=" << h << " colorType=" << colorType << " fRowBytes=" << fRowBytes;
+
+  // second random number (for y/height)
+  double shuffleY = base::RandDouble();
+
+  // calculate random coordinates using bisection
+  auto currentW = w, currentH = h;
+  for(;pixels >= 0; pixels--) {
+    int x = currentW * shuffleX, y = currentH * shuffleY;
+
+    // calculate randomisation amounts for each RGB component
+    uint8_t shuffleR = base::RandInt(0, 4);
+    uint8_t shuffleG = (shuffleR + x) % 4;
+    uint8_t shuffleB = (shuffleG + y) % 4;
+
+    // manipulate pixel data to slightly change the R, G, B components
+    switch (colorType) {
+      case kAlpha_8_SkColorType:
+      {
+         auto *pixel = writable_addr(uint8_t, addr, fRowBytes, x, y);
+         auto r = SkColorGetR(*pixel), g = SkColorGetG(*pixel), b = SkColorGetB(*pixel), a = SkColorGetA(*pixel);
+
+         r = shuffleComponent(r, UINT8_MAX-1, shuffleR);
+         g = shuffleComponent(g, UINT8_MAX-1, shuffleG);
+         b = shuffleComponent(b, UINT8_MAX-1, shuffleB);
+         // alpha is left unchanged
+
+         *pixel = SkColorSetARGB(a, r, g, b);
+      }
+      break;
+      case kGray_8_SkColorType:
+      {
+         auto *pixel = writable_addr(uint8_t, addr, fRowBytes, x, y);
+         *pixel = shuffleComponent(*pixel, UINT8_MAX-1, shuffleB);
+      }
+      break;
+      case kRGB_565_SkColorType:
+      {
+         auto *pixel = writable_addr(uint16_t, addr, fRowBytes, x, y);
+         unsigned    r = SkPacked16ToR32(*pixel);
+         unsigned    g = SkPacked16ToG32(*pixel);
+         unsigned    b = SkPacked16ToB32(*pixel);
+
+         r = shuffleComponent(r, 31, shuffleR);
+         g = shuffleComponent(g, 63, shuffleG);
+         b = shuffleComponent(b, 31, shuffleB);
+
+         unsigned r16 = (r & SK_R16_MASK) << SK_R16_SHIFT;
+         unsigned g16 = (g & SK_G16_MASK) << SK_G16_SHIFT;
+         unsigned b16 = (b & SK_B16_MASK) << SK_B16_SHIFT;
+
+         *pixel = r16 | g16 | b16;
+      }
+      break;
+      case kARGB_4444_SkColorType:
+      {
+         auto *pixel = writable_addr(uint16_t, addr, fRowBytes, x, y);
+         auto a = SkGetPackedA4444(*pixel), r = SkGetPackedR4444(*pixel), g = SkGetPackedG4444(*pixel), b = SkGetPackedB4444(*pixel);
+
+         r = shuffleComponent(r, 15, shuffleR);
+         g = shuffleComponent(g, 15, shuffleG);
+         b = shuffleComponent(b, 15, shuffleB);
+         // alpha is left unchanged
+
+         unsigned a4 = (a & 0xF) << SK_A4444_SHIFT;
+         unsigned r4 = (r & 0xF) << SK_R4444_SHIFT;
+         unsigned g4 = (g & 0xF) << SK_G4444_SHIFT;
+         unsigned b4 = (b & 0xF) << SK_B4444_SHIFT;
+
+         *pixel = r4 | b4 | g4 | a4;
+      }
+      break;
+      case kRGBA_8888_SkColorType:
+      {
+         auto *pixel = writable_addr(uint32_t, addr, fRowBytes, x, y);
+         auto a = SkGetPackedA32(*pixel), r = SkGetPackedR32(*pixel), g = SkGetPackedG32(*pixel), b = SkGetPackedB32(*pixel);
+
+         r = shuffleComponent(r, UINT8_MAX-1, shuffleR);
+         g = shuffleComponent(g, UINT8_MAX-1, shuffleG);
+         b = shuffleComponent(b, UINT8_MAX-1, shuffleB);
+         // alpha is left unchanged
+
+         *pixel = (a << SK_A32_SHIFT) | (r << SK_R32_SHIFT) |
+                  (g << SK_G32_SHIFT) | (b << SK_B32_SHIFT);
+      }
+      break;
+      case kBGRA_8888_SkColorType:
+      {
+         auto *pixel = writable_addr(uint32_t, addr, fRowBytes, x, y);
+         auto a = SkGetPackedA32(*pixel), b = SkGetPackedR32(*pixel), g = SkGetPackedG32(*pixel), r = SkGetPackedB32(*pixel);
+
+         r = shuffleComponent(r, UINT8_MAX-1, shuffleR);
+         g = shuffleComponent(g, UINT8_MAX-1, shuffleG);
+         b = shuffleComponent(b, UINT8_MAX-1, shuffleB);
+         // alpha is left unchanged
+
+         *pixel = (a << SK_BGRA_A32_SHIFT) | (r << SK_BGRA_R32_SHIFT) |
+                  (g << SK_BGRA_G32_SHIFT) | (b << SK_BGRA_B32_SHIFT);
+      }
+      break;
+      default:
+         // the remaining formats are not expected to be used in Chromium
+         LOG(WARNING) << "BRM: ShuffleSubchannelColorData(): Ignoring pixel format";
+         return;
+    }
+
+    // keep bisecting or reset current width/height as needed
+    if (x == 0) {
+       currentW = w;
+    } else {
+       currentW = x;
+    }
+    if (y == 0) {
+       currentH = h;
+    } else {
+       currentH = y;
+    }
+  }
+}
+
+#undef writable_addr
+#undef shuffleComponent
+
 }  // namespace blink
--- third_party/blink/renderer/platform/graphics/static_bitmap_image.h.orig
+++ third_party/blink/renderer/platform/graphics/static_bitmap_image.h
@@ -36,6 +36,8 @@ class PLATFORM_EXPORT StaticBitmapImage
 
   StaticBitmapImage(ImageOrientation orientation) : orientation_(orientation) {}
 
+  static void ShuffleSubchannelColorData(const void *addr, const SkImageInfo& info, int srcX, int srcY);
+
   bool IsStaticBitmapImage() const override { return true; }
 
   // Methods overridden by all sub-classes
--- third_party/blink/renderer/platform/runtime_enabled_features.json5.orig
+++ third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1082,6 +1082,9 @@
       name: "FingerprintingCanvasMeasureTextNoise",
     },
     {
+      name: "FingerprintingCanvasImageDataNoise",
+    },
+    {
       name: "FocuslessSpatialNavigation",
       settable_from_internals: true,
     },