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

.
This commit is contained in:
Mark Pilgrim
2020-05-07 11:04:39 -04:00
parent d934e54815
commit a851e183be
5 changed files with 110 additions and 57 deletions
@@ -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
+38 -17
View File
@@ -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<const unsigned char*>(&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<const unsigned char*>(&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<const uint64_t*>(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<const uint64_t*>(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<blink::StaticBitmapImage> BraveSessionCache::PerturbPixels(
blink::LocalFrame* frame,
scoped_refptr<blink::StaticBitmapImage> 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<blink::StaticBitmapImage> BraveSessionCache::PerturbBalanced(
std::unique_ptr<blink::ImageDataBuffer> data_buffer =
blink::ImageDataBuffer::Create(image_bitmap);
uint8_t* pixels = const_cast<uint8_t*>(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<const uint8_t*>(domain_key_);
uint8_t channel = *first_byte % 3;
// initial seed to find first pixel to perturb
uint64_t v = *reinterpret_cast<uint64_t*>(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<uint64_t*>(domain_key_);
CHECK(h.Init(reinterpret_cast<const unsigned char*>(&session_plus_domain_key),
sizeof session_plus_domain_key));
uint8_t canvas_key[32];
CHECK(h.Sign(
base::StringPiece(reinterpret_cast<const char*>(pixels), pixel_count),
canvas_key, sizeof canvas_key));
uint64_t v = *reinterpret_cast<uint64_t*>(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);
@@ -41,6 +41,8 @@ class CORE_EXPORT BraveSessionCache final
scoped_refptr<blink::StaticBitmapImage> image_bitmap);
private:
bool farbling_enabled_;
uint64_t session_key_;
uint8_t domain_key_[32];
scoped_refptr<blink::StaticBitmapImage> PerturbBalanced(
+26 -21
View File
@@ -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 <typename URL>
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());
}
@@ -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;