123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- # 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(-)
- --- a/chrome/browser/bromite_flag_entries.h
- +++ b/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_
- --- a/content/browser/renderer_host/render_process_host_impl.cc
- +++ b/content/browser/renderer_host/render_process_host_impl.cc
- @@ -3341,6 +3341,7 @@ void RenderProcessHostImpl::PropagateBro
- switches::kFileUrlPathAlias,
- switches::kFingerprintingClientRectsNoise,
- switches::kFingerprintingCanvasMeasureTextNoise,
- + switches::kFingerprintingCanvasImageDataNoise,
- switches::kForceDeviceScaleFactor,
- switches::kForceDisplayColorProfile,
- switches::kForceGpuMemAvailableMb,
- --- a/content/child/runtime_features.cc
- +++ b/content/child/runtime_features.cc
- @@ -489,6 +489,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))
- --- a/third_party/blink/public/platform/web_runtime_features.h
- +++ b/third_party/blink/public/platform/web_runtime_features.h
- @@ -223,6 +223,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);
- --- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
- +++ b/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/garbage_collected.h"
- @@ -2055,6 +2056,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;
- --- a/third_party/blink/renderer/platform/BUILD.gn
- +++ b/third_party/blink/renderer/platform/BUILD.gn
- @@ -1641,7 +1641,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",
- --- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
- +++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
- @@ -705,4 +705,8 @@ void WebRuntimeFeatures::EnableFingerpri
- RuntimeEnabledFeatures::SetFingerprintingCanvasMeasureTextNoiseEnabled(enable);
- }
-
- +void WebRuntimeFeatures::EnableFingerprintingCanvasImageDataNoise(bool enable) {
- + RuntimeEnabledFeatures::SetFingerprintingCanvasImageDataNoiseEnabled(enable);
- +}
- +
- } // namespace blink
- --- a/third_party/blink/renderer/platform/graphics/image_data_buffer.cc
- +++ b/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);
- --- a/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
- +++ b/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 "ui/gfx/geometry/skia_conversions.h"
- #include "v8/include/v8.h"
-
- @@ -79,4 +83,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
- --- a/third_party/blink/renderer/platform/graphics/static_bitmap_image.h
- +++ b/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
- --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
- +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
- @@ -1108,6 +1108,9 @@
- name: "FingerprintingCanvasMeasureTextNoise",
- },
- {
- + name: "FingerprintingCanvasImageDataNoise",
- + },
- + {
- name: "FocuslessSpatialNavigation",
- settable_from_internals: true,
- },
|