From a851e183be393aa82eff37c3d3d68e1414d83442 Mon Sep 17 00:00:00 2001 From: Mark Pilgrim Date: Wed, 29 Apr 2020 14:47:24 -0400 Subject: [PATCH] Implement content-sensitive canvas farbling add missing patch fewer patches refactor content settings agent, add support for brave shields down, WIP browser tests fix browser test lint types lint . --- .../core/browser/content_settings_utils.cc | 19 ++++++- .../blink/renderer/core/dom/document.cc | 55 +++++++++++++------ .../blink/renderer/core/dom/document.h | 2 + renderer/brave_content_settings_agent_impl.cc | 47 +++++++++------- ...content_settings_agent_impl_browsertest.cc | 44 +++++++++------ 5 files changed, 110 insertions(+), 57 deletions(-) diff --git a/chromium_src/components/content_settings/core/browser/content_settings_utils.cc b/chromium_src/components/content_settings/core/browser/content_settings_utils.cc index c6c3587c659..bb55ef82a26 100644 --- a/chromium_src/components/content_settings/core/browser/content_settings_utils.cc +++ b/chromium_src/components/content_settings/core/browser/content_settings_utils.cc @@ -3,12 +3,25 @@ * 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/. */ +#include "base/feature_list.h" +#include "brave/components/brave_shields/common/brave_shield_constants.h" +#include "brave/components/brave_shields/common/features.h" + #define BRAVE_GET_RENDER_CONTENT_SETTING_RULES \ map->GetSettingsForOneType(ContentSettingsType::AUTOPLAY, \ ResourceIdentifier(), &(rules->autoplay_rules)); \ - map->GetSettingsForOneType(ContentSettingsType::PLUGINS, "fingerprinting", \ - &(rules->fingerprinting_rules)); \ - map->GetSettingsForOneType(ContentSettingsType::PLUGINS, "braveShields", \ + if (base::FeatureList::IsEnabled( \ + brave_shields::features::kFingerprintingProtectionV2)) { \ + map->GetSettingsForOneType(ContentSettingsType::PLUGINS, \ + brave_shields::kFingerprintingV2, \ + &(rules->fingerprinting_rules)); \ + } else { \ + map->GetSettingsForOneType(ContentSettingsType::PLUGINS, \ + brave_shields::kFingerprinting, \ + &(rules->fingerprinting_rules)); \ + } \ + map->GetSettingsForOneType(ContentSettingsType::PLUGINS, \ + brave_shields::kBraveShields, \ &(rules->brave_shields_rules)); #include "../../../../../components/content_settings/core/browser/content_settings_utils.cc" // NOLINT diff --git a/chromium_src/third_party/blink/renderer/core/dom/document.cc b/chromium_src/third_party/blink/renderer/core/dom/document.cc index b72375027e4..97d437cde22 100644 --- a/chromium_src/third_party/blink/renderer/core/dom/document.cc +++ b/chromium_src/third_party/blink/renderer/core/dom/document.cc @@ -31,13 +31,17 @@ BraveSessionCache::BraveSessionCache(Document& document) base::StringPiece(document.TopFrameOrigin()->ToUrlOrigin().host()); std::string domain = net::registry_controlled_domains::GetDomainAndRegistry( host, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); - crypto::HMAC h(crypto::HMAC::SHA256); - base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); - DCHECK(cmd_line->HasSwitch(kBraveSessionToken)); - uint64_t key; - base::StringToUint64(cmd_line->GetSwitchValueASCII(kBraveSessionToken), &key); - CHECK(h.Init(reinterpret_cast(&key), sizeof key)); - CHECK(h.Sign(domain, domain_key_, sizeof domain_key_)); + farbling_enabled_ = !domain.empty(); + if (farbling_enabled_) { + base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); + DCHECK(cmd_line->HasSwitch(kBraveSessionToken)); + base::StringToUint64(cmd_line->GetSwitchValueASCII(kBraveSessionToken), + &session_key_); + crypto::HMAC h(crypto::HMAC::SHA256); + CHECK(h.Init(reinterpret_cast(&session_key_), + sizeof session_key_)); + CHECK(h.Sign(domain, domain_key_, sizeof domain_key_)); + } } BraveSessionCache& BraveSessionCache::From(Document& document) { @@ -51,17 +55,20 @@ BraveSessionCache& BraveSessionCache::From(Document& document) { } double BraveSessionCache::GetFudgeFactor() { - const uint64_t* fudge = reinterpret_cast(domain_key_); - const double maxUInt64AsDouble = UINT64_MAX; - double fudge_factor = 0.99 + ((*fudge / maxUInt64AsDouble) / 100); - VLOG(1) << "audio fudge factor (based on session token) = " << fudge_factor; + double fudge_factor = 1.0; + if (farbling_enabled_) { + const uint64_t* fudge = reinterpret_cast(domain_key_); + const double maxUInt64AsDouble = UINT64_MAX; + fudge_factor = 0.99 + ((*fudge / maxUInt64AsDouble) / 100); + VLOG(1) << "audio fudge factor (based on session token) = " << fudge_factor; + } return fudge_factor; } scoped_refptr BraveSessionCache::PerturbPixels( blink::LocalFrame* frame, scoped_refptr image_bitmap) { - if (!frame || !frame->GetContentSettingsClient()) { + if (!farbling_enabled_ || !frame || !frame->GetContentSettingsClient()) { return image_bitmap; } switch (frame->GetContentSettingsClient()->GetBraveFarblingLevel()) { @@ -91,18 +98,32 @@ scoped_refptr BraveSessionCache::PerturbBalanced( std::unique_ptr data_buffer = blink::ImageDataBuffer::Create(image_bitmap); uint8_t* pixels = const_cast(data_buffer->Pixels()); - const uint64_t pixel_count = data_buffer->Width() * data_buffer->Height(); + // This needs to be type size_t because we pass it to base::StringPiece + // later for content hashing. This is safe because the maximum canvas + // dimensions are less than SIZE_T_MAX. (Width and height are each + // limited to 32,767 pixels.) + const size_t pixel_count = data_buffer->Width() * data_buffer->Height(); // choose which channel (R, G, or B) to perturb const uint8_t* first_byte = reinterpret_cast(domain_key_); uint8_t channel = *first_byte % 3; - // initial seed to find first pixel to perturb - uint64_t v = *reinterpret_cast(domain_key_); + // calculate initial seed to find first pixel to perturb, based on session + // key, domain key, and canvas contents + crypto::HMAC h(crypto::HMAC::SHA256); + uint64_t session_plus_domain_key = + session_key_ ^ *reinterpret_cast(domain_key_); + CHECK(h.Init(reinterpret_cast(&session_plus_domain_key), + sizeof session_plus_domain_key)); + uint8_t canvas_key[32]; + CHECK(h.Sign( + base::StringPiece(reinterpret_cast(pixels), pixel_count), + canvas_key, sizeof canvas_key)); + uint64_t v = *reinterpret_cast(canvas_key); const uint64_t zero = 0; uint64_t pixel_index; - // iterate through 32-byte domain key and use each bit to determine how to + // iterate through 32-byte canvas key and use each bit to determine how to // perturb the current pixel for (int i = 0; i < 32; i++) { - uint8_t bit = domain_key_[i]; + uint8_t bit = canvas_key[i]; for (int j = 8; j >= 0; j--) { pixel_index = 4 * (v % pixel_count) + channel; pixels[pixel_index] = pixels[pixel_index] ^ (bit & 0x1); diff --git a/chromium_src/third_party/blink/renderer/core/dom/document.h b/chromium_src/third_party/blink/renderer/core/dom/document.h index 0350a2f79d0..878e566fed2 100644 --- a/chromium_src/third_party/blink/renderer/core/dom/document.h +++ b/chromium_src/third_party/blink/renderer/core/dom/document.h @@ -41,6 +41,8 @@ class CORE_EXPORT BraveSessionCache final scoped_refptr image_bitmap); private: + bool farbling_enabled_; + uint64_t session_key_; uint8_t domain_key_[32]; scoped_refptr PerturbBalanced( diff --git a/renderer/brave_content_settings_agent_impl.cc b/renderer/brave_content_settings_agent_impl.cc index 355521ed455..e40521a253f 100644 --- a/renderer/brave_content_settings_agent_impl.cc +++ b/renderer/brave_content_settings_agent_impl.cc @@ -47,20 +47,35 @@ GURL GetOriginOrURL( return top_origin.GetURL(); } +bool IsBraveShieldsDown(const blink::WebFrame* frame, + const GURL& secondary_url, + const ContentSettingsForOneType& rules) { + ContentSetting setting = CONTENT_SETTING_DEFAULT; + const GURL& primary_url = GetOriginOrURL(frame); + + for (const auto& rule : rules) { + if (rule.primary_pattern.Matches(primary_url) && + rule.secondary_pattern.Matches(secondary_url)) { + setting = rule.GetContentSetting(); + break; + } + } + + return setting == CONTENT_SETTING_BLOCK; +} + // This method can only be used for brave plugin content settings because // they are implemented incorrectly and swap primary/secondary url template ContentSetting GetBraveContentSettingFromRules( + const ContentSettingsForOneType& shield_rules, const ContentSettingsForOneType& rules, const blink::WebFrame* frame, const URL& secondary_url) { - // If there is only one rule, it's the default rule and we don't need to match - // the patterns. - if (rules.size() == 1) { - DCHECK(rules[0].primary_pattern == ContentSettingsPattern::Wildcard()); - DCHECK(rules[0].secondary_pattern == ContentSettingsPattern::Wildcard()); - return rules[0].GetContentSetting(); - } + // if shields is down, allow everything + if (IsBraveShieldsDown(frame, secondary_url, shield_rules)) + return CONTENT_SETTING_ALLOW; + const GURL& primary_url = GetOriginOrURL(frame); const GURL& secondary_gurl = secondary_url; for (const auto& rule : rules) { @@ -219,20 +234,9 @@ ContentSetting BraveContentSettingsAgentImpl::GetFPContentSettingFromRules( bool BraveContentSettingsAgentImpl::IsBraveShieldsDown( const blink::WebFrame* frame, const GURL& secondary_url) { - ContentSetting setting = CONTENT_SETTING_DEFAULT; - const GURL& primary_url = GetOriginOrURL(frame); - - if (content_setting_rules_) { - for (const auto& rule : content_setting_rules_->brave_shields_rules) { - if (rule.primary_pattern.Matches(primary_url) && - rule.secondary_pattern.Matches(secondary_url)) { - setting = rule.GetContentSetting(); - break; - } - } - } - - return setting == CONTENT_SETTING_BLOCK; + return !content_setting_rules_ || + ::IsBraveShieldsDown(frame, secondary_url, + content_setting_rules_->brave_shields_rules); } bool BraveContentSettingsAgentImpl::AllowFingerprinting( @@ -279,6 +283,7 @@ BraveFarblingLevel BraveContentSettingsAgentImpl::GetBraveFarblingLevel() { ContentSetting setting = CONTENT_SETTING_DEFAULT; if (content_setting_rules_) { setting = GetBraveContentSettingFromRules( + content_setting_rules_->brave_shields_rules, content_setting_rules_->fingerprinting_rules, frame, url::Origin(frame->GetDocument().GetSecurityOrigin()).GetURL()); } diff --git a/renderer/brave_content_settings_agent_impl_browsertest.cc b/renderer/brave_content_settings_agent_impl_browsertest.cc index 47291c8ae90..9f82eeeee53 100644 --- a/renderer/brave_content_settings_agent_impl_browsertest.cc +++ b/renderer/brave_content_settings_agent_impl_browsertest.cc @@ -45,7 +45,7 @@ const char kGetImageDataScript[] = "domAutomationController.send(ctx.getImageData(0, 0, canvas.width, " "canvas.height).data.reduce(adder));"; -const int kExpectedImageDataHashFarblingBalanced = 261040; +const int kExpectedImageDataHashFarblingBalanced = 261046; const int kExpectedImageDataHashFarblingOff = 261120; const int kExpectedImageDataHashFarblingMaximum = 127574; @@ -303,11 +303,11 @@ class BraveContentSettingsAgentImplBrowserTest : public InProcessBrowserTest { // See https://github.com/brave/brave-browser/issues/8937 // Fails on Linux -// #if defined(OS_LINUX) +#if defined(OS_LINUX) #define MAYBE_FarbleGetImageData DISABLED_FarbleGetImageData -// #else -// #define MAYBE_FarbleGetImageData FarbleGetImageData -// #endif +#else +#define MAYBE_FarbleGetImageData FarbleGetImageData +#endif IN_PROC_BROWSER_TEST_F(BraveContentSettingsAgentImplBrowserTest, MAYBE_FarbleGetImageData) { @@ -344,8 +344,8 @@ IN_PROC_BROWSER_TEST_F(BraveContentSettingsAgentImplBrowserTest, ExecuteScriptAndExtractInt(contents(), kGetImageDataScript, &hash)); EXPECT_EQ(kExpectedImageDataHashFarblingOff, hash); - // Farbling should be default if 3rd-party fingerpringint is blocked - // via content settings and kBraveFingerpringintV2 is disabled + // Farbling should be default if 3rd-party fingerprinting is blocked + // via content settings and kBraveFingerprintingV2 is disabled BlockThirdPartyFingerprinting(); NavigateToPageWithIframe(); hash = -1; @@ -354,28 +354,40 @@ IN_PROC_BROWSER_TEST_F(BraveContentSettingsAgentImplBrowserTest, EXPECT_EQ(kExpectedImageDataHashFarblingBalanced, hash); // Farbling should be default if fingerprinting is blocked via - // content settings and kBraveFingerpringintV2 is disabled + // content settings and kBraveFingerprintingV2 is disabled BlockFingerprinting(); NavigateToPageWithIframe(); hash = -1; EXPECT_TRUE( ExecuteScriptAndExtractInt(contents(), kGetImageDataScript, &hash)); EXPECT_EQ(kExpectedImageDataHashFarblingBalanced, hash); +} - base::test::ScopedFeatureList scoped_feature_list_; - scoped_feature_list_.InitAndEnableFeature( +class BraveContentSettingsAgentImplV2BrowserTest + : public BraveContentSettingsAgentImplBrowserTest { + public: + void SetUp() override { + scoped_feature_list_.InitAndEnableFeature( brave_shields::features::kFingerprintingProtectionV2); + BraveContentSettingsAgentImplBrowserTest::SetUp(); + } - // Farbling should be default when kBraveFingerpringintV2 is enabled + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +IN_PROC_BROWSER_TEST_F(BraveContentSettingsAgentImplV2BrowserTest, + MAYBE_FarbleGetImageData) { + // Farbling should be default when kBraveFingerprintingV2 is enabled // because it uses a different content setting NavigateToPageWithIframe(); - hash = -1; + int hash = -1; EXPECT_TRUE( ExecuteScriptAndExtractInt(contents(), kGetImageDataScript, &hash)); EXPECT_EQ(kExpectedImageDataHashFarblingBalanced, hash); - // Farbling should be maximum if finerprinting is blocked via content settings - // and kBraveFingerpringintV2 is enabled + // Farbling should be maximum if fingerprinting is blocked via content + // settings and kBraveFingerprintingV2 is enabled BlockFingerprinting(); NavigateToPageWithIframe(); hash = -1; @@ -384,7 +396,7 @@ IN_PROC_BROWSER_TEST_F(BraveContentSettingsAgentImplBrowserTest, EXPECT_EQ(kExpectedImageDataHashFarblingMaximum, hash); // Farbling should be balanced if fingerprinting is default via - // content settings and kBraveFingerpringintV2 is enabled + // content settings and kBraveFingerprintingV2 is enabled SetFingerprintingDefault(); NavigateToPageWithIframe(); hash = -1; @@ -393,7 +405,7 @@ IN_PROC_BROWSER_TEST_F(BraveContentSettingsAgentImplBrowserTest, EXPECT_EQ(kExpectedImageDataHashFarblingBalanced, hash); // Farbling should be off if fingerprinting is allowed via - // content settings and kBraveFingerpringintV2 is enabled + // content settings and kBraveFingerprintingV2 is enabled AllowFingerprinting(); NavigateToPageWithIframe(); hash = -1;