Skip to content

Commit

Permalink
Implement farbling levels for webaudio
Browse files Browse the repository at this point in the history
code and tests

use inline function instead of define
  • Loading branch information
pilgrim-brave committed Jun 11, 2020
1 parent 1961dc2 commit 20ed9ac
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 49 deletions.
57 changes: 57 additions & 0 deletions browser/farbling/brave_webaudio_farbling_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "brave/common/brave_paths.h"
#include "brave/common/pref_names.h"
#include "brave/components/brave_component_updater/browser/local_data_files_service.h"
#include "brave/components/brave_shields/browser/brave_shields_util.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/ui/browser.h"
Expand All @@ -25,7 +26,10 @@
#include "content/public/test/browser_test_utils.h"
#include "net/dns/mock_host_resolver.h"

using brave_shields::ControlType;

const char kEmbeddedTestServerDirectory[] = "webaudio";
const char kTitleScript[] = "domAutomationController.send(document.title);";

class BraveWebAudioFarblingBrowserTest : public InProcessBrowserTest {
public:
Expand All @@ -48,6 +52,8 @@ class BraveWebAudioFarblingBrowserTest : public InProcessBrowserTest {

ASSERT_TRUE(embedded_test_server()->Start());

top_level_page_url_ = embedded_test_server()->GetURL("a.com", "/");
farbling_url_ = embedded_test_server()->GetURL("a.com", "/farbling.html");
copy_from_channel_url_ =
embedded_test_server()->GetURL("a.com", "/copyFromChannel.html");
}
Expand All @@ -59,6 +65,30 @@ class BraveWebAudioFarblingBrowserTest : public InProcessBrowserTest {

const GURL& copy_from_channel_url() { return copy_from_channel_url_; }

const GURL& farbling_url() { return farbling_url_; }

void AllowFingerprinting() {
brave_shields::SetFingerprintingControlType(
browser()->profile(), ControlType::ALLOW, top_level_page_url_);
}

void BlockFingerprinting() {
brave_shields::SetFingerprintingControlType(
browser()->profile(), ControlType::BLOCK, top_level_page_url_);
}

void SetFingerprintingDefault() {
brave_shields::SetFingerprintingControlType(
browser()->profile(), ControlType::DEFAULT, top_level_page_url_);
}

template <typename T>
std::string ExecScriptGetStr(const std::string& script, T* frame) {
std::string value;
EXPECT_TRUE(ExecuteScriptAndExtractString(frame, script, &value));
return value;
}

content::WebContents* contents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
Expand All @@ -69,7 +99,9 @@ class BraveWebAudioFarblingBrowserTest : public InProcessBrowserTest {
}

private:
GURL top_level_page_url_;
GURL copy_from_channel_url_;
GURL farbling_url_;
std::unique_ptr<ChromeContentClient> content_client_;
std::unique_ptr<BraveContentBrowserClient> browser_content_client_;
};
Expand All @@ -81,3 +113,28 @@ IN_PROC_BROWSER_TEST_F(BraveWebAudioFarblingBrowserTest,
CopyFromChannelNoCrash) {
NavigateToURLUntilLoadStop(copy_from_channel_url());
}

// Tests results of farbling known values
IN_PROC_BROWSER_TEST_F(BraveWebAudioFarblingBrowserTest, FarbleWebAudio) {
// Farbling level: maximum
// web audio: pseudo-random data with no relation to underlying audio channel
BlockFingerprinting();
NavigateToURLUntilLoadStop(farbling_url());
EXPECT_EQ(ExecScriptGetStr(kTitleScript, contents()), "405");
// second time, same as the first (tests that the PRNG properly resets itself
// at the beginning of each calculation)
NavigateToURLUntilLoadStop(farbling_url());
EXPECT_EQ(ExecScriptGetStr(kTitleScript, contents()), "405");

// Farbling level: balanced (default)
// web audio: farbled audio data
SetFingerprintingDefault();
NavigateToURLUntilLoadStop(farbling_url());
EXPECT_EQ(ExecScriptGetStr(kTitleScript, contents()), "7968");

// Farbling level: off
// web audio: original audio data
AllowFingerprinting();
NavigateToURLUntilLoadStop(farbling_url());
EXPECT_EQ(ExecScriptGetStr(kTitleScript, contents()), "8000");
}
67 changes: 55 additions & 12 deletions chromium_src/third_party/blink/renderer/core/dom/document.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,38 @@
#include "third_party/blink/renderer/platform/heap/handle.h"
#include "third_party/blink/renderer/platform/supplementable.h"

namespace {

const uint64_t zero = 0;

inline uint64_t lfsr_next(uint64_t v) {
return ((v >> 1) | (((v << 62) ^ (v << 61)) & (~(~zero << 63) << 62)));
}

float Identity(float value, size_t index) {
return value;
}

float ConstantMultiplier(double fudge_factor, float value, size_t index) {
return value * fudge_factor;
}

float PseudoRandomSequence(uint64_t seed, float value, size_t index) {
static uint64_t v;
const double maxUInt64AsDouble = UINT64_MAX;
if (index == 0) {
// start of loop, reset to initial seed which was passed in and is based on
// the domain key
v = seed;
}
// get next value in PRNG sequence
v = lfsr_next(v);
// return pseudo-random float between 0 and 0.1
return (v / maxUInt64AsDouble) / 10;
}

} // namespace

namespace brave {

const char kBraveSessionToken[] = "brave_session_token";
Expand Down Expand Up @@ -54,15 +86,28 @@ BraveSessionCache& BraveSessionCache::From(Document& document) {
return *cache;
}

double BraveSessionCache::GetFudgeFactor() {
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;
AudioFarblingCallback BraveSessionCache::GetAudioFarblingCallback(
blink::LocalFrame* frame) {
if (farbling_enabled_ && frame && frame->GetContentSettingsClient()) {
switch (frame->GetContentSettingsClient()->GetBraveFarblingLevel()) {
case BraveFarblingLevel::OFF: {
break;
}
case BraveFarblingLevel::BALANCED: {
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;
return base::BindRepeating(&ConstantMultiplier, fudge_factor);
}
case BraveFarblingLevel::MAXIMUM: {
uint64_t seed = *reinterpret_cast<uint64_t*>(domain_key_);
return base::BindRepeating(&PseudoRandomSequence, seed);
}
}
}
return fudge_factor;
return base::BindRepeating(&Identity);
}

scoped_refptr<blink::StaticBitmapImage> BraveSessionCache::PerturbPixels(
Expand Down Expand Up @@ -118,7 +163,6 @@ scoped_refptr<blink::StaticBitmapImage> BraveSessionCache::PerturbBalanced(
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 canvas key and use each bit to determine how to
// perturb the current pixel
Expand All @@ -129,7 +173,7 @@ scoped_refptr<blink::StaticBitmapImage> BraveSessionCache::PerturbBalanced(
pixels[pixel_index] = pixels[pixel_index] ^ (bit & 0x1);
bit = bit >> 1;
// find next pixel to perturb
v = ((v >> 1) | (((v << 62) ^ (v << 61)) & (~(~zero << 63) << 62)));
v = lfsr_next(v);
}
}
// convert back to a StaticBitmapImage to return to the caller
Expand All @@ -152,11 +196,10 @@ scoped_refptr<blink::StaticBitmapImage> BraveSessionCache::PerturbMax(
const uint64_t count = 4 * data_buffer->Width() * data_buffer->Height();
// initial seed based on domain key
uint64_t v = *reinterpret_cast<uint64_t*>(domain_key_);
const uint64_t zero = 0;
// iterate through pixel data and overwrite with next value in PRNG sequence
for (uint64_t i = 0; i < count; i++) {
pixels[i] = v % 256;
v = ((v >> 1) | (((v << 62) ^ (v << 61)) & (~(~zero << 63) << 62)));
v = lfsr_next(v);
}
// convert back to a StaticBitmapImage to return to the caller
scoped_refptr<blink::StaticBitmapImage> perturbed_bitmap =
Expand Down
8 changes: 7 additions & 1 deletion chromium_src/third_party/blink/renderer/core/dom/document.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#include "../../../../../../../third_party/blink/renderer/core/dom/document.h"

#include "base/callback.h"

using blink::Document;
using blink::GarbageCollected;
using blink::HeapObjectHeader;
Expand All @@ -22,6 +24,9 @@ class StaticBitmapImage;
} // namespace blink

namespace brave {

typedef base::RepeatingCallback<float(float, size_t)> AudioFarblingCallback;

class CORE_EXPORT BraveSessionCache final
: public GarbageCollected<BraveSessionCache>,
public Supplement<Document> {
Expand All @@ -35,7 +40,8 @@ class CORE_EXPORT BraveSessionCache final

static BraveSessionCache& From(Document&);

double GetFudgeFactor();
AudioFarblingCallback GetAudioFarblingCallback(
blink::LocalFrame* frame);
scoped_refptr<blink::StaticBitmapImage> PerturbPixels(
blink::LocalFrame* frame,
scoped_refptr<blink::StaticBitmapImage> image_bitmap);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@

#include "third_party/blink/renderer/core/dom/document.h"

#define BRAVE_ANALYSERHANDLER_CONSTRUCTOR \
analyser_.fudge_factor_ = brave::BraveSessionCache::From( \
*Document::From(node.GetExecutionContext())) \
.GetFudgeFactor();
#define BRAVE_ANALYSERHANDLER_CONSTRUCTOR \
ExecutionContext* context = node.GetExecutionContext(); \
analyser_.audio_farbling_callback_ = \
brave::BraveSessionCache::From(*Document::From(context)) \
.GetAudioFarblingCallback(Document::From(context)->GetFrame());

#include "../../../../../../third_party/blink/renderer/modules/webaudio/analyser_node.cc"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,41 @@
* 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/callback.h"
#include "brave/third_party/blink/renderer/brave_farbling_constants.h"
#include "third_party/blink/public/platform/web_content_settings_client.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/modules/webaudio/analyser_node.h"

#define BRAVE_AUDIOBUFFER_GETCHANNELDATA \
NotShared<DOMFloat32Array> array = getChannelData(channel_index); \
LocalDOMWindow* window = LocalDOMWindow::From(script_state); \
if (window) { \
DOMFloat32Array* destination_array = array.View(); \
size_t len = destination_array->lengthAsSizeT(); \
if (len > 0) { \
float* destination = destination_array->Data(); \
double fudge_factor = \
brave::BraveSessionCache::From(*(window->document())) \
.GetFudgeFactor(); \
for (unsigned i = 0; i < len; ++i) { \
destination[i] = destination[i] * fudge_factor; \
} \
return array; \
} \
#define BRAVE_AUDIOBUFFER_GETCHANNELDATA \
NotShared<DOMFloat32Array> array = getChannelData(channel_index); \
LocalDOMWindow* window = LocalDOMWindow::From(script_state); \
if (window) { \
LocalFrame* frame = window->document()->GetFrame(); \
if (frame && frame->GetContentSettingsClient()) { \
DOMFloat32Array* destination_array = array.View(); \
size_t len = destination_array->lengthAsSizeT(); \
if (len > 0) { \
float* destination = destination_array->Data(); \
brave::AudioFarblingCallback audio_farbling_callback = \
brave::BraveSessionCache::From(*(window->document())) \
.GetAudioFarblingCallback(frame); \
for (unsigned i = 0; i < len; ++i) { \
destination[i] = audio_farbling_callback.Run(destination[i], i); \
} \
} \
} \
}

#define BRAVE_AUDIOBUFFER_COPYFROMCHANNEL \
LocalDOMWindow* window = LocalDOMWindow::From(script_state); \
if (window) { \
double fudge_factor = \
brave::BraveSessionCache::From(*(window->document())) \
.GetFudgeFactor(); \
for (unsigned i = 0; i < count; i++) { \
dst[i] = dst[i] * fudge_factor; \
} \
#define BRAVE_AUDIOBUFFER_COPYFROMCHANNEL \
LocalDOMWindow* window = LocalDOMWindow::From(script_state); \
if (window) { \
brave::AudioFarblingCallback audio_farbling_callback = \
brave::BraveSessionCache::From(*(window->document())) \
.GetAudioFarblingCallback(window->document()->GetFrame()); \
for (unsigned i = 0; i < count; i++) { \
dst[i] = audio_farbling_callback.Run(dst[i], i); \
} \
}

#include "../../../../../../third_party/blink/renderer/modules/webaudio/audio_buffer.cc"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#define BRAVE_REALTIMEANALYSER_CONVERTFLOATTODB \
destination[i] = destination[i] * fudge_factor_;
destination[i] = audio_farbling_callback_.Run(destination[i], i);

#define BRAVE_REALTIMEANALYSER_CONVERTTOBYTEDATA \
scaled_value = scaled_value * fudge_factor_;
scaled_value = audio_farbling_callback_.Run(scaled_value, i);

#define BRAVE_REALTIMEANALYSER_GETFLOATTIMEDOMAINDATA \
destination[i] = value * fudge_factor_;
destination[i] = audio_farbling_callback_.Run(value, i);

#define BRAVE_REALTIMEANALYSER_GETBYTETIMEDOMAINDATA \
value = value * fudge_factor_;
value = audio_farbling_callback_.Run(value, i);

#include "../../../../../../third_party/blink/renderer/modules/webaudio/realtime_analyser.cc"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
#ifndef BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_REALTIME_ANALYSER_H_
#define BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_REALTIME_ANALYSER_H_

// RealtimeAnalyser doesn't have access to the owning Document
// so it needs its own copy of the fudge factor.
#define BRAVE_REALTIMEANALYSER_H double fudge_factor_;
#include "base/callback.h"

#define BRAVE_REALTIMEANALYSER_H \
base::RepeatingCallback<float(float, size_t)> audio_farbling_callback_;

#include "../../../../../../third_party/blink/renderer/modules/webaudio/realtime_analyser.h"

Expand Down
23 changes: 23 additions & 0 deletions test/data/webaudio/farbling.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Web Audio farbling test</title>
</head>
<body>
<script>
const duration = 1;
const sampleRate = 8000;
const ctx = new AudioContext();
const audioBuffer = ctx.createBuffer(1, sampleRate * duration, sampleRate);
const destArray = new Float32Array(sampleRate * duration);
for (var i = 0; i < sampleRate * duration; i++) {
destArray[i] = 1;
}
audioBuffer.copyToChannel(destArray, 0);
audioBuffer.copyFromChannel(destArray, 0);
var adder = (a, x) => a + x;
document.title = Math.round(destArray.reduce(adder));
</script>
</body>
</html>

0 comments on commit 20ed9ac

Please sign in to comment.