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.
|
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**
|
**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;
|
const runs = 10;
|
||||||
|
|
||||||
function measureOneLine() {
|
function measureOneLine() {
|
||||||
const LINE_SIZE = 16; // 64/sizeof(int)
|
const LINE_SIZE = 32; // 128/sizeof(int)
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
// Fill with -1 to ensure allocation
|
// Fill with -1 to ensure allocation
|
||||||
|
|
|
@ -6,22 +6,22 @@ from sklearn.metrics import classification_report
|
||||||
from sklearn.model_selection import train_test_split
|
from sklearn.model_selection import train_test_split
|
||||||
|
|
||||||
def eval():
|
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
|
# Re-train 10 times in order to reduce effects of randomness
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
### TODO: Exercise 2-4
|
### TODO: Exercise 2-5
|
||||||
### 1. Load data from traces file
|
### 1. Load data from traces file
|
||||||
### 2. Split data into X_train, X_test, y_train, y_test with train_test_split
|
### 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
|
### 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
|
### 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
|
# Do not modify the next two lines
|
||||||
y_test_full.extend(y_test)
|
y_test_full.extend(y_test)
|
||||||
y_pred_full.extend(y_pred)
|
y_pred_full.extend(y_pred)
|
||||||
|
|
||||||
### TODO: Exercise 2-4 (continued)
|
### TODO: Exercise 2-5 (continued)
|
||||||
### 5. Print classification report using y_test_full and y_pred_full
|
### 5. Print classification report using y_test_full and y_pred_full
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
eval()
|
eval()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Number of sweep counts
|
// Number of sweep counts
|
||||||
// TODO: Choose an appropriate value!
|
// TODO (Exercise 2-1): Choose an appropriate value!
|
||||||
let P;
|
let P = 1000;
|
||||||
|
|
||||||
// Number of elements in your trace
|
// Number of elements in your trace
|
||||||
let K = 5 * 1000 / P;
|
let K = 5 * 1000 / P;
|
||||||
|
@ -21,7 +21,7 @@ function record() {
|
||||||
// Save start timestamp
|
// Save start timestamp
|
||||||
start = performance.now();
|
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
|
// Once done recording, send result to main thread
|
||||||
postMessage(JSON.stringify(T));
|
postMessage(JSON.stringify(T));
|
||||||
|
|
|
@ -6,22 +6,22 @@ from sklearn.metrics import classification_report
|
||||||
from sklearn.model_selection import train_test_split
|
from sklearn.model_selection import train_test_split
|
||||||
|
|
||||||
def eval():
|
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
|
# Re-train 10 times in order to reduce effects of randomness
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
### TODO: Exercise 5
|
### TODO: Exercise 2-5
|
||||||
### 1. Load data from traces file
|
### 1. Load data from traces file
|
||||||
### 2. Split data into X_train, X_test, y_train, y_test with train_test_split
|
### 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
|
### 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
|
### 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
|
# Do not modify the next two lines
|
||||||
y_test_full.extend(y_test)
|
y_test_full.extend(y_test)
|
||||||
y_pred_full.extend(y_pred)
|
y_pred_full.extend(y_pred)
|
||||||
|
|
||||||
### TODO: Exercise 5 (continued)
|
### TODO: Exercise 2-5 (continued)
|
||||||
### 5. Print classification report using y_test_full and y_pred_full
|
### 5. Print classification report using y_test_full and y_pred_full
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
eval()
|
eval()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Number of sweep counts
|
// Number of sweep counts
|
||||||
// TODO: Choose an appropriate value!
|
// TODO (Exercise 3-1): Choose an appropriate value!
|
||||||
let P;
|
let P = 1000;
|
||||||
|
|
||||||
// Number of elements in your trace
|
// Number of elements in your trace
|
||||||
let K = 5 * 1000 / P;
|
let K = 5 * 1000 / P;
|
||||||
|
@ -21,7 +21,7 @@ function record() {
|
||||||
// Save start timestamp
|
// Save start timestamp
|
||||||
start = performance.now();
|
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
|
// Once done recording, send result to main thread
|
||||||
postMessage(JSON.stringify(T));
|
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