flag-fingerprinting-canvas-image-data-noise.patch 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. # NOTE: Changes made:
  2. # * Added flag --fingerprinting-canvas-image-data-noise to enable/disable
  3. # Canvas image data fingerprinting deception
  4. # * Removed WebGLDebugRendererInfo disabling in favor of an alternative
  5. # implementation in ungoogled-chromium/disable-webgl-renderer-info.patch
  6. # * Tweak subchannel noise generation to require fewer random number generation
  7. From: csagan5 <32685696+csagan5@users.noreply.github.com>
  8. Date: Sat, 24 Mar 2018 05:18:03 +0100
  9. Subject: Canvas: fingerprinting mitigations for image data and webGL
  10. Disable webGL renderer info and modify the color data returned by ToBlob,
  11. ToDataURL and getImageData so that it will contain randomly manipulated
  12. pixels (maximum 10) that slightly change the color of the R,G,B components
  13. without a visible effect.
  14. Credits to Slaviro (https://github.com/Slaviro) for coming up with a better
  15. approach to change color components.
  16. ---
  17. .../platform/graphics/image_data_buffer.cc | 5 +
  18. .../platform/graphics/static_bitmap_image.cc | 154 +++++++++++++++++++++
  19. .../platform/graphics/static_bitmap_image.h | 2 +
  20. 4 files changed, 163 insertions(+), 2 deletions(-)
  21. --- a/chrome/browser/about_flags.cc
  22. +++ b/chrome/browser/about_flags.cc
  23. @@ -1815,6 +1815,10 @@ const FeatureEntry kFeatureEntries[] = {
  24. "Enable Canvas::measureText() fingerprint deception",
  25. "Scale the output values of Canvas::measureText() with a randomly selected factor in the range -0.0003% to 0.0003%, which are recomputed on every document initialization.",
  26. kOsAll, SINGLE_VALUE_TYPE(switches::kFingerprintingCanvasMeasureTextNoise)},
  27. + {"fingerprinting-canvas-image-data-noise",
  28. + "Enable Canvas image data fingerprint deception",
  29. + "Slightly modifies at most 10 pixels in Canvas image data extracted via JS APIs",
  30. + kOsAll, SINGLE_VALUE_TYPE(switches::kFingerprintingCanvasImageDataNoise)},
  31. {"ignore-gpu-blacklist", flag_descriptions::kIgnoreGpuBlacklistName,
  32. flag_descriptions::kIgnoreGpuBlacklistDescription, kOsAll,
  33. SINGLE_VALUE_TYPE(switches::kIgnoreGpuBlacklist)},
  34. --- a/content/browser/renderer_host/render_process_host_impl.cc
  35. +++ b/content/browser/renderer_host/render_process_host_impl.cc
  36. @@ -3282,6 +3282,7 @@ void RenderProcessHostImpl::PropagateBro
  37. switches::kFileUrlPathAlias,
  38. switches::kFingerprintingClientRectsNoise,
  39. switches::kFingerprintingCanvasMeasureTextNoise,
  40. + switches::kFingerprintingCanvasImageDataNoise,
  41. switches::kForceDeviceScaleFactor,
  42. switches::kForceDisableWebRtcApmInAudioService,
  43. switches::kForceDisplayColorProfile,
  44. --- a/content/child/runtime_features.cc
  45. +++ b/content/child/runtime_features.cc
  46. @@ -454,6 +454,8 @@ void SetRuntimeFeaturesFromCommandLine(c
  47. switches::kFingerprintingClientRectsNoise, true},
  48. {wrf::EnableFingerprintingCanvasMeasureTextNoise,
  49. switches::kFingerprintingCanvasMeasureTextNoise, true},
  50. + {wrf::EnableFingerprintingCanvasImageDataNoise,
  51. + switches::kFingerprintingCanvasImageDataNoise, true},
  52. {wrf::EnableShadowDOMV0, switches::kWebComponentsV0Enabled, true},
  53. {wrf::EnableCustomElementsV0, switches::kWebComponentsV0Enabled, true},
  54. {wrf::EnableHTMLImports, switches::kWebComponentsV0Enabled, true},
  55. --- a/third_party/blink/public/platform/web_runtime_features.h
  56. +++ b/third_party/blink/public/platform/web_runtime_features.h
  57. @@ -222,6 +222,7 @@ class WebRuntimeFeatures {
  58. BLINK_PLATFORM_EXPORT static void EnableAllowSyncXHRInPageDismissal(bool);
  59. BLINK_PLATFORM_EXPORT static void EnableFingerprintingClientRectsNoise(bool);
  60. BLINK_PLATFORM_EXPORT static void EnableFingerprintingCanvasMeasureTextNoise(bool);
  61. + BLINK_PLATFORM_EXPORT static void EnableFingerprintingCanvasImageDataNoise(bool);
  62. BLINK_PLATFORM_EXPORT static void EnableShadowDOMV0(bool);
  63. BLINK_PLATFORM_EXPORT static void EnableCustomElementsV0(bool);
  64. BLINK_PLATFORM_EXPORT static void EnableHTMLImports(bool);
  65. --- a/third_party/blink/renderer/platform/BUILD.gn
  66. +++ b/third_party/blink/renderer/platform/BUILD.gn
  67. @@ -1498,7 +1498,9 @@ jumbo_component("platform") {
  68. "//third_party/blink/renderer:non_test_config",
  69. ]
  70. - include_dirs = []
  71. + include_dirs = [
  72. + "//third_party/skia/include/private", # For shuffler in graphics/static_bitmap_image.cc
  73. + ]
  74. public_deps = [
  75. ":blink_platform_public_deps",
  76. --- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
  77. +++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
  78. @@ -675,4 +675,8 @@ void WebRuntimeFeatures::EnableFingerpri
  79. RuntimeEnabledFeatures::SetFingerprintingCanvasMeasureTextNoiseEnabled(enable);
  80. }
  81. +void WebRuntimeFeatures::EnableFingerprintingCanvasImageDataNoise(bool enable) {
  82. + RuntimeEnabledFeatures::SetFingerprintingCanvasImageDataNoiseEnabled(enable);
  83. +}
  84. +
  85. } // namespace blink
  86. --- a/third_party/blink/renderer/platform/graphics/image_data_buffer.cc
  87. +++ b/third_party/blink/renderer/platform/graphics/image_data_buffer.cc
  88. @@ -36,6 +36,8 @@
  89. #include "base/compiler_specific.h"
  90. #include "base/memory/ptr_util.h"
  91. +#include "base/rand_util.h"
  92. +#include "base/logging.h"
  93. #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
  94. #include "third_party/blink/renderer/platform/image-encoders/image_encoder.h"
  95. #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
  96. @@ -130,6 +132,11 @@ bool ImageDataBuffer::EncodeImageInterna
  97. const SkPixmap& pixmap) const {
  98. DCHECK(is_valid_);
  99. + if (RuntimeEnabledFeatures::FingerprintingCanvasImageDataNoiseEnabled()) {
  100. + // shuffle subchannel color data within the pixmap
  101. + StaticBitmapImage::ShuffleSubchannelColorData(pixmap_.writable_addr(), pixmap_.info(), 0, 0);
  102. + }
  103. +
  104. if (mime_type == kMimeTypeJpeg) {
  105. SkJpegEncoder::Options options;
  106. options.fQuality = ImageEncoder::ComputeJpegQuality(quality);
  107. --- a/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
  108. +++ b/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
  109. @@ -4,6 +4,8 @@
  110. #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
  111. +#include "base/rand_util.h"
  112. +#include "base/logging.h"
  113. #include "base/numerics/checked_math.h"
  114. #include "gpu/command_buffer/client/gles2_interface.h"
  115. #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
  116. @@ -11,11 +13,13 @@
  117. #include "third_party/blink/renderer/platform/graphics/image_observer.h"
  118. #include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
  119. #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
  120. +#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
  121. #include "third_party/skia/include/core/SkCanvas.h"
  122. #include "third_party/skia/include/core/SkImage.h"
  123. #include "third_party/skia/include/core/SkPaint.h"
  124. #include "third_party/skia/include/core/SkSurface.h"
  125. #include "third_party/skia/include/gpu/GrContext.h"
  126. +#include "third_party/skia/include/private/SkColorData.h"
  127. #include "v8/include/v8.h"
  128. namespace blink {
  129. @@ -128,7 +132,162 @@ bool StaticBitmapImage::CopyToByteArray(
  130. DCHECK(read_pixels_successful ||
  131. !sk_image->bounds().intersect(SkIRect::MakeXYWH(
  132. rect.X(), rect.Y(), info.width(), info.height())));
  133. +
  134. + if (RuntimeEnabledFeatures::FingerprintingCanvasImageDataNoiseEnabled()) {
  135. + ShuffleSubchannelColorData(dst.data(), info, rect.X(), rect.Y());
  136. + }
  137. +
  138. return true;
  139. }
  140. +// set the component to maximum-delta if it is >= maximum, or add to existing color component (color + delta)
  141. +#define shuffleComponent(color, max, delta) ( (color) >= (max) ? ((max)-(delta)) : ((color)+(delta)) )
  142. +
  143. +#define writable_addr(T, p, stride, x, y) (T*)((const char *)p + y * stride + x * sizeof(T))
  144. +
  145. +void StaticBitmapImage::ShuffleSubchannelColorData(const void *addr, const SkImageInfo& info, int srcX, int srcY) {
  146. + auto w = info.width() - srcX, h = info.height() - srcY;
  147. +
  148. + // skip tiny images; info.width()/height() can also be 0
  149. + if ((w < 8) || (h < 8)) {
  150. + return;
  151. + }
  152. +
  153. + // generate the first random number here
  154. + double shuffleX = base::RandDouble();
  155. +
  156. + // cap maximum pixels to change
  157. + auto pixels = (w + h) / 128;
  158. + if (pixels > 10) {
  159. + pixels = 10;
  160. + } else if (pixels < 2) {
  161. + pixels = 2;
  162. + }
  163. +
  164. + auto colorType = info.colorType();
  165. + auto fRowBytes = info.minRowBytes(); // stride
  166. +
  167. + DLOG(INFO) << "BRM: ShuffleSubchannelColorData() w=" << w << " h=" << h << " colorType=" << colorType << " fRowBytes=" << fRowBytes;
  168. +
  169. + // second random number (for y/height)
  170. + double shuffleY = base::RandDouble();
  171. +
  172. + // calculate random coordinates using bisection
  173. + auto currentW = w, currentH = h;
  174. + for(;pixels >= 0; pixels--) {
  175. + int x = currentW * shuffleX, y = currentH * shuffleY;
  176. +
  177. + // calculate randomisation amounts for each RGB component
  178. + uint8_t shuffleR = base::RandInt(0, 4);
  179. + uint8_t shuffleG = (shuffleR + x) % 4;
  180. + uint8_t shuffleB = (shuffleG + y) % 4;
  181. +
  182. + // manipulate pixel data to slightly change the R, G, B components
  183. + switch (colorType) {
  184. + case kAlpha_8_SkColorType:
  185. + {
  186. + auto *pixel = writable_addr(uint8_t, addr, fRowBytes, x, y);
  187. + auto r = SkColorGetR(*pixel), g = SkColorGetG(*pixel), b = SkColorGetB(*pixel), a = SkColorGetA(*pixel);
  188. +
  189. + r = shuffleComponent(r, UINT8_MAX-1, shuffleR);
  190. + g = shuffleComponent(g, UINT8_MAX-1, shuffleG);
  191. + b = shuffleComponent(b, UINT8_MAX-1, shuffleB);
  192. + // alpha is left unchanged
  193. +
  194. + *pixel = SkColorSetARGB(a, r, g, b);
  195. + }
  196. + break;
  197. + case kGray_8_SkColorType:
  198. + {
  199. + auto *pixel = writable_addr(uint8_t, addr, fRowBytes, x, y);
  200. + *pixel = shuffleComponent(*pixel, UINT8_MAX-1, shuffleB);
  201. + }
  202. + break;
  203. + case kRGB_565_SkColorType:
  204. + {
  205. + auto *pixel = writable_addr(uint16_t, addr, fRowBytes, x, y);
  206. + unsigned r = SkPacked16ToR32(*pixel);
  207. + unsigned g = SkPacked16ToG32(*pixel);
  208. + unsigned b = SkPacked16ToB32(*pixel);
  209. +
  210. + r = shuffleComponent(r, 31, shuffleR);
  211. + g = shuffleComponent(g, 63, shuffleG);
  212. + b = shuffleComponent(b, 31, shuffleB);
  213. +
  214. + unsigned r16 = (r & SK_R16_MASK) << SK_R16_SHIFT;
  215. + unsigned g16 = (g & SK_G16_MASK) << SK_G16_SHIFT;
  216. + unsigned b16 = (b & SK_B16_MASK) << SK_B16_SHIFT;
  217. +
  218. + *pixel = r16 | g16 | b16;
  219. + }
  220. + break;
  221. + case kARGB_4444_SkColorType:
  222. + {
  223. + auto *pixel = writable_addr(uint16_t, addr, fRowBytes, x, y);
  224. + auto a = SkGetPackedA4444(*pixel), r = SkGetPackedR4444(*pixel), g = SkGetPackedG4444(*pixel), b = SkGetPackedB4444(*pixel);
  225. +
  226. + r = shuffleComponent(r, 15, shuffleR);
  227. + g = shuffleComponent(g, 15, shuffleG);
  228. + b = shuffleComponent(b, 15, shuffleB);
  229. + // alpha is left unchanged
  230. +
  231. + unsigned a4 = (a & 0xF) << SK_A4444_SHIFT;
  232. + unsigned r4 = (r & 0xF) << SK_R4444_SHIFT;
  233. + unsigned g4 = (g & 0xF) << SK_G4444_SHIFT;
  234. + unsigned b4 = (b & 0xF) << SK_B4444_SHIFT;
  235. +
  236. + *pixel = r4 | b4 | g4 | a4;
  237. + }
  238. + break;
  239. + case kRGBA_8888_SkColorType:
  240. + {
  241. + auto *pixel = writable_addr(uint32_t, addr, fRowBytes, x, y);
  242. + auto a = SkGetPackedA32(*pixel), r = SkGetPackedR32(*pixel), g = SkGetPackedG32(*pixel), b = SkGetPackedB32(*pixel);
  243. +
  244. + r = shuffleComponent(r, UINT8_MAX-1, shuffleR);
  245. + g = shuffleComponent(g, UINT8_MAX-1, shuffleG);
  246. + b = shuffleComponent(b, UINT8_MAX-1, shuffleB);
  247. + // alpha is left unchanged
  248. +
  249. + *pixel = (a << SK_A32_SHIFT) | (r << SK_R32_SHIFT) |
  250. + (g << SK_G32_SHIFT) | (b << SK_B32_SHIFT);
  251. + }
  252. + break;
  253. + case kBGRA_8888_SkColorType:
  254. + {
  255. + auto *pixel = writable_addr(uint32_t, addr, fRowBytes, x, y);
  256. + auto a = SkGetPackedA32(*pixel), b = SkGetPackedR32(*pixel), g = SkGetPackedG32(*pixel), r = SkGetPackedB32(*pixel);
  257. +
  258. + r = shuffleComponent(r, UINT8_MAX-1, shuffleR);
  259. + g = shuffleComponent(g, UINT8_MAX-1, shuffleG);
  260. + b = shuffleComponent(b, UINT8_MAX-1, shuffleB);
  261. + // alpha is left unchanged
  262. +
  263. + *pixel = (a << SK_BGRA_A32_SHIFT) | (r << SK_BGRA_R32_SHIFT) |
  264. + (g << SK_BGRA_G32_SHIFT) | (b << SK_BGRA_B32_SHIFT);
  265. + }
  266. + break;
  267. + default:
  268. + // the remaining formats are not expected to be used in Chromium
  269. + LOG(WARNING) << "BRM: ShuffleSubchannelColorData(): Ignoring pixel format";
  270. + return;
  271. + }
  272. +
  273. + // keep bisecting or reset current width/height as needed
  274. + if (x == 0) {
  275. + currentW = w;
  276. + } else {
  277. + currentW = x;
  278. + }
  279. + if (y == 0) {
  280. + currentH = h;
  281. + } else {
  282. + currentH = y;
  283. + }
  284. + }
  285. +}
  286. +
  287. +#undef writable_addr
  288. +#undef shuffleComponent
  289. +
  290. } // namespace blink
  291. --- a/third_party/blink/renderer/platform/graphics/static_bitmap_image.h
  292. +++ b/third_party/blink/renderer/platform/graphics/static_bitmap_image.h
  293. @@ -35,6 +35,8 @@ class PLATFORM_EXPORT StaticBitmapImage
  294. StaticBitmapImage(ImageOrientation orientation) : orientation_(orientation) {}
  295. + static void ShuffleSubchannelColorData(const void *addr, const SkImageInfo& info, int srcX, int srcY);
  296. +
  297. bool IsStaticBitmapImage() const override { return true; }
  298. // Methods overridden by all sub-classes
  299. --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
  300. +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
  301. @@ -753,6 +753,9 @@
  302. {
  303. name: "FingerprintingCanvasMeasureTextNoise",
  304. },
  305. + {
  306. + name: "FingerprintingCanvasImageDataNoise",
  307. + },
  308. // Perform style recalc traversal in the flat tree order, including marking
  309. // flat-tree instead of shadow-including ancestors for child dirtiness.
  310. {