2024 release
This commit is contained in:
parent
b7b3a02bfe
commit
604a544978
|
@ -9,4 +9,4 @@
|
|||
In this lab, students implement the techniques from our group's ISCA 2022 paper `There's Always a Bigger Fish: A Case Study of a Misunderstood Timing Side Channel`. Students will begin by implementing a seemingly familiar cache-based side channel attack in Javascript, and will then be asked to reason about why this attack works. Then, students will remove a core part of the attack, but see that the code still works.
|
||||
|
||||
**Setup**
|
||||
Students can complete this lab on their own machines. MacOS, Linux, Windows all should work. Google Chrome is required for Part 4 of this lab.
|
||||
Students can complete this lab on their own machines. MacOS, Linux, Windows all should work.
|
||||
|
|
185
index.html
185
index.html
|
@ -1,185 +0,0 @@
|
|||
<!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>
|
|
@ -1,7 +1,7 @@
|
|||
const runs = 10;
|
||||
|
||||
function measureOneLine() {
|
||||
const LINE_SIZE = 16; // 64/sizeof(int)
|
||||
const LINE_SIZE = 32; // 128/sizeof(int)
|
||||
let result = [];
|
||||
|
||||
// Fill with -1 to ensure allocation
|
||||
|
|
|
@ -6,22 +6,22 @@ from sklearn.metrics import classification_report
|
|||
from sklearn.model_selection import train_test_split
|
||||
|
||||
def eval():
|
||||
y_pred_full, y_test_full = [], []
|
||||
y_pred_full, y_test_full = [], []
|
||||
|
||||
# Re-train 10 times in order to reduce effects of randomness
|
||||
for i in range(10):
|
||||
### TODO: Exercise 2-4
|
||||
### 1. Load data from traces file
|
||||
### 2. Split data into X_train, X_test, y_train, y_test with train_test_split
|
||||
### 3. Train classifier with X_train and y_train
|
||||
### 4. Use classifier to make predictions on X_test. Save the result to a variable called y_pred
|
||||
# Re-train 10 times in order to reduce effects of randomness
|
||||
for i in range(10):
|
||||
### TODO: Exercise 2-5
|
||||
### 1. Load data from traces file
|
||||
### 2. Split data into X_train, X_test, y_train, y_test with train_test_split
|
||||
### 3. Train classifier with X_train and y_train
|
||||
### 4. Use classifier to make predictions on X_test. Save the result to a variable called y_pred
|
||||
|
||||
# Do not modify the next two lines
|
||||
y_test_full.extend(y_test)
|
||||
y_pred_full.extend(y_pred)
|
||||
# Do not modify the next two lines
|
||||
y_test_full.extend(y_test)
|
||||
y_pred_full.extend(y_pred)
|
||||
|
||||
### TODO: Exercise 2-4 (continued)
|
||||
### 5. Print classification report using y_test_full and y_pred_full
|
||||
### TODO: Exercise 2-5 (continued)
|
||||
### 5. Print classification report using y_test_full and y_pred_full
|
||||
|
||||
if __name__ == "__main__":
|
||||
eval()
|
||||
eval()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Number of sweep counts
|
||||
// TODO: Choose an appropriate value!
|
||||
let P;
|
||||
// TODO (Exercise 2-1): Choose an appropriate value!
|
||||
let P = 1000;
|
||||
|
||||
// Number of elements in your trace
|
||||
let K = 5 * 1000 / P;
|
||||
|
@ -21,7 +21,7 @@ function record() {
|
|||
// Save start timestamp
|
||||
start = performance.now();
|
||||
|
||||
// TODO: Record data for 5 seconds and save values to T.
|
||||
// TODO (Exercise 2-1): Record data for 5 seconds and save values to T.
|
||||
|
||||
// Once done recording, send result to main thread
|
||||
postMessage(JSON.stringify(T));
|
||||
|
|
|
@ -6,22 +6,22 @@ from sklearn.metrics import classification_report
|
|||
from sklearn.model_selection import train_test_split
|
||||
|
||||
def eval():
|
||||
y_pred_full, y_test_full = [], []
|
||||
y_pred_full, y_test_full = [], []
|
||||
|
||||
# Re-train 10 times in order to reduce effects of randomness
|
||||
for i in range(10):
|
||||
### TODO: Exercise 5
|
||||
### 1. Load data from traces file
|
||||
### 2. Split data into X_train, X_test, y_train, y_test with train_test_split
|
||||
### 3. Train classifier with X_train and y_train
|
||||
### 4. Use classifier to make predictions on X_test. Save the result to a variable called y_pred
|
||||
# Re-train 10 times in order to reduce effects of randomness
|
||||
for i in range(10):
|
||||
### TODO: Exercise 2-5
|
||||
### 1. Load data from traces file
|
||||
### 2. Split data into X_train, X_test, y_train, y_test with train_test_split
|
||||
### 3. Train classifier with X_train and y_train
|
||||
### 4. Use classifier to make predictions on X_test. Save the result to a variable called y_pred
|
||||
|
||||
# Do not modify the next two lines
|
||||
y_test_full.extend(y_test)
|
||||
y_pred_full.extend(y_pred)
|
||||
# Do not modify the next two lines
|
||||
y_test_full.extend(y_test)
|
||||
y_pred_full.extend(y_pred)
|
||||
|
||||
### TODO: Exercise 5 (continued)
|
||||
### 5. Print classification report using y_test_full and y_pred_full
|
||||
### TODO: Exercise 2-5 (continued)
|
||||
### 5. Print classification report using y_test_full and y_pred_full
|
||||
|
||||
if __name__ == "__main__":
|
||||
eval()
|
||||
eval()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Number of sweep counts
|
||||
// TODO: Choose an appropriate value!
|
||||
let P;
|
||||
// TODO (Exercise 3-1): Choose an appropriate value!
|
||||
let P = 1000;
|
||||
|
||||
// Number of elements in your trace
|
||||
let K = 5 * 1000 / P;
|
||||
|
@ -21,7 +21,7 @@ function record() {
|
|||
// Save start timestamp
|
||||
start = performance.now();
|
||||
|
||||
// TODO: Record data for 5 seconds and save values to T.
|
||||
// TODO (Exercise 3-1): Record data for 5 seconds and save values to T.
|
||||
|
||||
// Once done recording, send result to main thread
|
||||
postMessage(JSON.stringify(T));
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
|
||||
|
||||
## Optional
|
||||
|
||||
**Report your browser version, CPU type, cache size, RAM amount, and OS. We use this information to learn about the attack’s behavior on different machines.**
|
||||
|
||||
- Browser:
|
||||
- CPU:
|
||||
- Cache sizes:
|
||||
- RAM:
|
||||
- OS:
|
||||
|
||||
|
||||
|
||||
|
||||
## 1-2
|
||||
|
||||
**Use the values printed on the webpage to find the median access time and report your results as follows.**
|
||||
|
||||
| Number of Cache Lines | Median Access Latency (ms) |
|
||||
| --------------------- | -------------------------- |
|
||||
| 1 | |
|
||||
| 10 | |
|
||||
| 100 | |
|
||||
| 1,000 | |
|
||||
| 10,000 | |
|
||||
| 100,000 | |
|
||||
| 1,000,000 | |
|
||||
| 10,000,000 | |
|
||||
|
||||
|
||||
|
||||
|
||||
## 1-3
|
||||
|
||||
**According to your measurement results, what is the resolution of your `performance.now()`? In order to measure differences in time with `performance.now()``, approximately how many cache accesses need to be performed?**
|
||||
|
||||
|
||||
|
||||
|
||||
## 2-2
|
||||
|
||||
**Report important parameters used in your attack. For each sweep operation, you access N addresses, and you count the number of sweep operations within a time interval P ms. What values of N and P do you use? How do you choose N? Why do not you choose P to be larger or smaller?**
|
||||
|
||||
|
||||
|
||||
|
||||
## 2-3
|
||||
|
||||
**Take screenshots of the three traces generated by your attack code and include them in the lab report.**
|
||||
|
||||
![Screenshot of traces](./part2/Screenshot.png)
|
||||
|
||||
|
||||
|
||||
|
||||
## 2-4
|
||||
|
||||
**Use the Python code we provided in Part 2.1 to analyze simple statistics (mean, median, etc.) on the traces from google.com and nytimes.com. Report the statistic numbers.**
|
||||
|
||||
|
||||
|
||||
|
||||
## 2-6
|
||||
|
||||
**Include your classification results in your report.**
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## 3-2
|
||||
|
||||
**Include your new accuracy results for the modified attack code in your report.**
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## 3-3
|
||||
|
||||
**Compare your accuracy numbers between Part 2 and 3. Does the accuracy decrease in Part 3? Do you think that our “cache-occupancy” attack actually exploits a cache side channel? If not, take a guess as to possible root causes of the modified attack.**
|
||||
|
48
update.sh
48
update.sh
|
@ -1,48 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Updates repository to latest starter code
|
||||
#
|
||||
# Adapted from Oliver Beckstein's ASU-CompMethodsPhysics-PHY494 course 2016-2020 placed into the public domain
|
||||
|
||||
# With GitHub template repositories one needs to use --allow-unrelated-histories
|
||||
# at least once. https://help.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template
|
||||
|
||||
progname="$0"
|
||||
REMOTE_NAME="startercode"
|
||||
REMOTE_URL="https://github.com/CSAIL-Arch-Sec/SHD-WebsiteFingerprintingLab.git"
|
||||
|
||||
# progname, from top dir
|
||||
UPDATESH="./deploy/$(basename $progname)"
|
||||
|
||||
CONTACT_MESSAGE="Contact the instructor and TA with a screen shot of ALL output from running $0."
|
||||
|
||||
function die () {
|
||||
local msg="$1" err=${2:-1}
|
||||
echo "ERROR: ${msg}."
|
||||
exit $err
|
||||
}
|
||||
|
||||
# ensure everything relative to top dir
|
||||
topdir="$(git rev-parse --show-toplevel)" || die "Failed to get rootdir"
|
||||
cd "${topdir}" || die "Failed to get to the git root dir ${rootdir}"
|
||||
|
||||
|
||||
# first time
|
||||
# 1. set remote repo
|
||||
# 2. merge histories between student (template) and remote skeleton
|
||||
|
||||
if ! git remote get-url ${REMOTE_NAME} >/dev/null 2>&1; then
|
||||
echo "Adding remote repository '${REMOTE_NAME}'."
|
||||
git remote add ${REMOTE_NAME} ${REMOTE_URL}
|
||||
|
||||
echo "Merging histories for the first time..."
|
||||
set -x
|
||||
git pull --allow-unrelated-histories -s recursive -X theirs --no-edit ${REMOTE_NAME} main || \
|
||||
{ git rev-list -1 MERGE_HEAD >/dev/null 2>&1 && git merge --abort ; \
|
||||
git remote rm ${REMOTE_NAME}; \
|
||||
die "Failed to merge histories. ${CONTACT_MESSAGE}" $?; }
|
||||
|
||||
set +x
|
||||
fi
|
||||
|
||||
echo "updating repository... git pull from ${REMOTE_NAME}"
|
||||
git pull --no-edit ${REMOTE_NAME} main || die "Failed to pull from ${REMOTE_NAME}. ${CONTACT_MESSAGE}"
|
Loading…
Reference in New Issue