186 lines
4.9 KiB
HTML
186 lines
4.9 KiB
HTML
|
<!DOCTYPE html>
|
||
|
<html>
|
||
|
<head>
|
||
|
<meta charset="utf-8" />
|
||
|
<title>Website Fingerprinting Lab</title>
|
||
|
<style>
|
||
|
* {
|
||
|
margin: 0;
|
||
|
padding: 0;
|
||
|
}
|
||
|
|
||
|
body {
|
||
|
font-family: Arial, Helvetica, sans-serif;
|
||
|
padding: 64px;
|
||
|
}
|
||
|
|
||
|
h1 {
|
||
|
font-size: 32px;
|
||
|
margin-bottom: 16px;
|
||
|
}
|
||
|
|
||
|
p {
|
||
|
margin-bottom: 8px;
|
||
|
}
|
||
|
|
||
|
#buttons {
|
||
|
margin-bottom: 16px;
|
||
|
}
|
||
|
|
||
|
button {
|
||
|
background: #fff;
|
||
|
border: 2px solid #3498db;
|
||
|
border-radius: 4px;
|
||
|
color: #3498db;
|
||
|
cursor: pointer;
|
||
|
display: inline-block;
|
||
|
font-size: 16px;
|
||
|
margin-right: 16px;
|
||
|
padding: 8px 16px;
|
||
|
}
|
||
|
|
||
|
button.disabled {
|
||
|
background: #ccc;
|
||
|
border-color: #ccc;
|
||
|
color: #666;
|
||
|
cursor: default;
|
||
|
}
|
||
|
|
||
|
.trace {
|
||
|
margin-bottom: 16px;
|
||
|
}
|
||
|
</style>
|
||
|
</head>
|
||
|
<body>
|
||
|
<h1>Website Fingerprinting Lab</h1>
|
||
|
<div id="buttons">
|
||
|
<button id="collect-trace">Collect trace</button>
|
||
|
<button id="download-traces">Download traces</button>
|
||
|
</div>
|
||
|
<div id="traces"></div>
|
||
|
<script src="https://d3js.org/d3.v6.js"></script>
|
||
|
<script type="text/javascript">
|
||
|
const worker = new Worker("worker.js");
|
||
|
|
||
|
const collectTraceButton = document.getElementById("collect-trace");
|
||
|
const downloadTracesButton = document.getElementById("download-traces");
|
||
|
|
||
|
// Default values for when the automation script isn't being used. When
|
||
|
// the script is in use, these values will get overwritten.
|
||
|
window.trace_length = 5000;
|
||
|
window.using_automation_script = false;
|
||
|
|
||
|
window.recording = false;
|
||
|
window.traces = [];
|
||
|
|
||
|
let traceIds = [];
|
||
|
|
||
|
worker.onmessage = (e) => {
|
||
|
window.recording = false;
|
||
|
|
||
|
const trace = JSON.parse(e.data);
|
||
|
window.traces.push(trace);
|
||
|
|
||
|
if (window.using_automation_script) {
|
||
|
// Don't display traces when automation script is in use
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Create new trace div
|
||
|
const parent = document.getElementById("traces");
|
||
|
const div = document.createElement("div");
|
||
|
const traceId = "a" + Math.random().toString().substring(2, 10);
|
||
|
div.setAttribute("id", traceId);
|
||
|
div.className = "trace";
|
||
|
parent.appendChild(div);
|
||
|
traceIds.push(traceId);
|
||
|
|
||
|
// Trace dimensions
|
||
|
const width = parent.getBoundingClientRect().width;
|
||
|
const height = 64;
|
||
|
|
||
|
// Create div for new trace
|
||
|
const svg = d3
|
||
|
.select("#" + traceId)
|
||
|
.append("svg")
|
||
|
.attr("width", width)
|
||
|
.attr("height", height);
|
||
|
|
||
|
// Find largest value across all traces
|
||
|
const maxVal = d3.max(window.traces, (d) => d3.max(d));
|
||
|
|
||
|
for (let i = 0; i < window.traces.length; i++) {
|
||
|
// Re-visualize all traces each time in case maxVal changes
|
||
|
const x = d3
|
||
|
.scaleLinear()
|
||
|
.domain([0, window.traces[i].length])
|
||
|
.range([0, width]);
|
||
|
|
||
|
const color = d3
|
||
|
.scaleQuantize()
|
||
|
.range(["#0d0887", "#7e03a8", "#cc4778", "#f89540", "#f0f921"])
|
||
|
.domain([0, maxVal]);
|
||
|
|
||
|
svg
|
||
|
.selectAll()
|
||
|
.data(window.traces[i].map((x, i) => ({ index: i, value: x })))
|
||
|
.join("rect")
|
||
|
.attr("x", (d) => x(d.index))
|
||
|
.attr("y", 0)
|
||
|
.attr("width", x(1))
|
||
|
.attr("height", height)
|
||
|
.style("fill", (d) => color(d.value));
|
||
|
}
|
||
|
|
||
|
// Reset UI
|
||
|
collectTraceButton.innerText = "Collect trace";
|
||
|
collectTraceButton.className = "";
|
||
|
};
|
||
|
|
||
|
function collectTrace() {
|
||
|
collectTraceButton.innerText = "Collecting trace...";
|
||
|
collectTraceButton.className = "disabled";
|
||
|
window.recording = true;
|
||
|
|
||
|
worker.postMessage({
|
||
|
type: "start",
|
||
|
trace_length: window.trace_length,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
collectTraceButton.onclick = () => {
|
||
|
if (window.recording) return;
|
||
|
|
||
|
window.recording = true;
|
||
|
collectTraceButton.innerText = "Starting in 3...";
|
||
|
collectTraceButton.className = "disabled";
|
||
|
|
||
|
setTimeout(() => {
|
||
|
collectTraceButton.innerText = "Starting in 2...";
|
||
|
|
||
|
setTimeout(() => {
|
||
|
collectTraceButton.innerText = "Starting in 1...";
|
||
|
setTimeout(collectTrace, 1000);
|
||
|
}, 1000);
|
||
|
}, 1000);
|
||
|
};
|
||
|
|
||
|
downloadTracesButton.onclick = () => {
|
||
|
const blob = new Blob([JSON.stringify({ traces: window.traces })], {
|
||
|
type: "application/json",
|
||
|
});
|
||
|
|
||
|
const url = URL.createObjectURL(blob);
|
||
|
|
||
|
const elem = document.createElement("a");
|
||
|
elem.href = url;
|
||
|
elem.download = "traces.json";
|
||
|
document.body.appendChild(elem);
|
||
|
|
||
|
elem.click();
|
||
|
document.body.removeChild(elem);
|
||
|
};
|
||
|
</script>
|
||
|
</body>
|
||
|
</html>
|