Initial fingerprinting lab commit
This commit is contained in:
		
						commit
						99454bfd29
					
				|  | @ -0,0 +1,22 @@ | ||||||
|  | MIT License | ||||||
|  | 
 | ||||||
|  | Copyright (c) 2022 Mengjia Yan, Joseph Ravichandran, Peter Deutsch, | ||||||
|  |                    Weon Taek Na, Jack Cook, Miles Dai, Miguel Gomez-Garcia | ||||||
|  | 
 | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  | 
 | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | SOFTWARE. | ||||||
|  | @ -0,0 +1,12 @@ | ||||||
|  | # Website Fingerprinting Lab | ||||||
|  | 
 | ||||||
|  | **Learning Objectives** | ||||||
|  | * Explore a new kind of side channel from a high level language. | ||||||
|  | * Understand how to reason about the root cause of misleading microarchitectural observations. | ||||||
|  | * Develop a real-world website fingerprinting attack that works on modern browsers. | ||||||
|  | 
 | ||||||
|  | **Description** | ||||||
|  | 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. | ||||||
|  | @ -0,0 +1,111 @@ | ||||||
|  | import argparse | ||||||
|  | import json | ||||||
|  | import logging | ||||||
|  | import os | ||||||
|  | import sys | ||||||
|  | import threading | ||||||
|  | import time | ||||||
|  | 
 | ||||||
|  | from flask import Flask, send_from_directory | ||||||
|  | from selenium import webdriver | ||||||
|  | from selenium.webdriver.chrome.options import Options | ||||||
|  | 
 | ||||||
|  | # Parse arguments | ||||||
|  | parser = argparse.ArgumentParser() | ||||||
|  | parser.add_argument("--browser", type=str, choices=["chrome", "firefox", "safari"], default="chrome", help="Browser to run automation in.") | ||||||
|  | parser.add_argument("--domains", type=str, default="google.com,youtube.com,baidu.com,facebook.com", help="Comma-separated list of domain names to collect traces from. Defaults to google.com,youtube.com,baidu.com,facebook.com") | ||||||
|  | parser.add_argument("--enable_countermeasure", type=bool, default=False, help="Set to true to enable the countermeasure. Browser must be set to Chrome. Defaults to false.") | ||||||
|  | parser.add_argument("--num_traces_per_domain", type=int, default=40, help="Number of traces to collect per domain.") | ||||||
|  | parser.add_argument("--trace_length", type=int, default=5000, help="The length of each recorded trace, in milliseconds. Defaults to 5000.") | ||||||
|  | 
 | ||||||
|  | required = parser.add_argument_group("required arguments") | ||||||
|  | required.add_argument("--out_filename", type=str, required=True, help="Name of the output file to save traces to.") | ||||||
|  | required.add_argument("--part", type=int, choices=[2, 3, 4], required=True, help="Set to the part of the lab you're working on.") | ||||||
|  | 
 | ||||||
|  | opts = parser.parse_args() | ||||||
|  | 
 | ||||||
|  | if opts.browser != "chrome" and opts.enable_countermeasure: | ||||||
|  |     print("Browser must be set to Chrome in order to enable the countermeasure.") | ||||||
|  |     sys.exit(1) | ||||||
|  | 
 | ||||||
|  | if os.path.exists(opts.out_filename): | ||||||
|  |     print(f"WARNING: Data already exists at {opts.out_filename}. What do you want to do?") | ||||||
|  |     res = input("[C]ancel [O]verwrite ").lower() | ||||||
|  |      | ||||||
|  |     if res == "o": | ||||||
|  |         os.remove(opts.out_filename) | ||||||
|  |     else: | ||||||
|  |         sys.exit(1) | ||||||
|  | 
 | ||||||
|  | # Start serving attacker app | ||||||
|  | app = Flask(__name__) | ||||||
|  | 
 | ||||||
|  | # Disable Flask logs | ||||||
|  | os.environ["WERKZEUG_RUN_MAIN"] = "true" | ||||||
|  | log = logging.getLogger("werkzeug") | ||||||
|  | log.disabled = True | ||||||
|  | 
 | ||||||
|  | @app.route("/") | ||||||
|  | def root(): | ||||||
|  |     return send_from_directory(f"part{opts.part}", "index.html") | ||||||
|  | 
 | ||||||
|  | @app.route("/<path:path>") | ||||||
|  | def static_dir(path): | ||||||
|  |     return send_from_directory(f"part{opts.part}", path) | ||||||
|  | 
 | ||||||
|  | flask_thread = threading.Thread(target=app.run, kwargs={"port": 1234}) | ||||||
|  | flask_thread.setDaemon(True) | ||||||
|  | flask_thread.start() | ||||||
|  | 
 | ||||||
|  | # Setup | ||||||
|  | def get_browser(victim): | ||||||
|  |     if opts.browser == "chrome": | ||||||
|  |         chrome_opts = Options() | ||||||
|  |         chrome_opts.add_experimental_option("excludeSwitches", ["enable-automation"]) | ||||||
|  | 
 | ||||||
|  |         if opts.enable_countermeasure and victim: | ||||||
|  |             # Victim has the extension enabled -- attacker does not | ||||||
|  |             chrome_opts.add_extension("part4/extension.crx") | ||||||
|  | 
 | ||||||
|  |         return webdriver.Chrome(options=chrome_opts) | ||||||
|  |     elif opts.browser == "firefox": | ||||||
|  |         return webdriver.Firefox() | ||||||
|  |     elif opts.browser == "safari": | ||||||
|  |         return webdriver.Safari() | ||||||
|  | 
 | ||||||
|  | attacker = get_browser(victim=False) | ||||||
|  | attacker.get("http://localhost:1234") | ||||||
|  | attacker.execute_script(f"window.trace_length = {opts.trace_length}") | ||||||
|  | attacker.execute_script(f"window.using_automation_script = true") | ||||||
|  | 
 | ||||||
|  | def collect_trace(url): | ||||||
|  |     victim = get_browser(victim=True) | ||||||
|  | 
 | ||||||
|  |     attacker.execute_script("collectTrace()") | ||||||
|  |     victim.get(url) | ||||||
|  | 
 | ||||||
|  |     time.sleep(float(opts.trace_length) / 1000) | ||||||
|  | 
 | ||||||
|  |     while attacker.execute_script("return recording"): | ||||||
|  |         time.sleep(0.1) | ||||||
|  | 
 | ||||||
|  |     victim.quit() | ||||||
|  |     return attacker.execute_script("return traces")[-1] | ||||||
|  | 
 | ||||||
|  | # Collect traces | ||||||
|  | urls = [f"https://www.{domain}" for domain in opts.domains.split(",")] | ||||||
|  | traces = [] | ||||||
|  | labels = [] | ||||||
|  | 
 | ||||||
|  | for url in urls: | ||||||
|  |     for i in range(opts.num_traces_per_domain): | ||||||
|  |         traces.append(collect_trace(url)) | ||||||
|  |         labels.append(url) | ||||||
|  | 
 | ||||||
|  | with open(opts.out_filename, "w") as out: | ||||||
|  |     json.dump({ | ||||||
|  |         "traces": traces, | ||||||
|  |         "labels": labels | ||||||
|  |     }, out, separators=(",", ":")) | ||||||
|  | 
 | ||||||
|  | attacker.quit() | ||||||
|  | @ -0,0 +1,185 @@ | ||||||
|  | <!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> | ||||||
|  | @ -0,0 +1,33 @@ | ||||||
|  | <!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; | ||||||
|  |       } | ||||||
|  |     </style> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <h1>Website Fingerprinting Lab Warmup</h1> | ||||||
|  |     <p id="exercise1-values"></p> | ||||||
|  |     <p id="exercise2-values"></p> | ||||||
|  |     <script src="warmup.js"></script> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
|  | @ -0,0 +1,35 @@ | ||||||
|  | const runs = 10; | ||||||
|  | 
 | ||||||
|  | function measureOneLine() { | ||||||
|  |   const LINE_SIZE = 16; // 64/sizeof(int)
 | ||||||
|  |   let result = []; | ||||||
|  | 
 | ||||||
|  |   // Fill with -1 to ensure allocation
 | ||||||
|  |   const M = new Array(runs * LINE_SIZE).fill(-1); | ||||||
|  | 
 | ||||||
|  |   for (let i = 0; i < runs; i++) { | ||||||
|  |     const start = performance.now(); | ||||||
|  |     let val = M[i * LINE_SIZE]; | ||||||
|  |     const end = performance.now(); | ||||||
|  | 
 | ||||||
|  |     result.push(end - start); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function measureNLines() { | ||||||
|  |   let result = []; | ||||||
|  | 
 | ||||||
|  |   // TODO: Exercise 2
 | ||||||
|  | 
 | ||||||
|  |   return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | document.getElementById( | ||||||
|  |   "exercise1-values" | ||||||
|  | ).innerText = `1 Cache Line: [${measureOneLine().join(", ")}]`; | ||||||
|  | 
 | ||||||
|  | document.getElementById( | ||||||
|  |   "exercise2-values" | ||||||
|  | ).innerText = `N Cache Lines: [${measureNLines().join(", ")}]`; | ||||||
|  | @ -0,0 +1,27 @@ | ||||||
|  | import json | ||||||
|  | import numpy as np | ||||||
|  | 
 | ||||||
|  | from sklearn.ensemble import RandomForestClassifier | ||||||
|  | from sklearn.metrics import classification_report | ||||||
|  | from sklearn.model_selection import train_test_split | ||||||
|  | 
 | ||||||
|  | def eval(): | ||||||
|  | 	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 | ||||||
|  | 
 | ||||||
|  | 		# 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 | ||||||
|  | 
 | ||||||
|  | if __name__ == "__main__": | ||||||
|  | 	eval() | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | ../index.html | ||||||
|  | @ -0,0 +1,32 @@ | ||||||
|  | // Duration of your trace, in milliseconds
 | ||||||
|  | let TRACE_LENGTH; | ||||||
|  | 
 | ||||||
|  | // Array of length TRACE_LENGTH with your trace's values
 | ||||||
|  | let T; | ||||||
|  | 
 | ||||||
|  | // Value of performance.now() when you started recording your trace
 | ||||||
|  | let start; | ||||||
|  | 
 | ||||||
|  | function record() { | ||||||
|  |   // Create empty array for saving trace values
 | ||||||
|  |   T = new Array(TRACE_LENGTH); | ||||||
|  | 
 | ||||||
|  |   // Fill array with -1 so we can be sure memory is allocated
 | ||||||
|  |   T.fill(-1, 0, T.length); | ||||||
|  | 
 | ||||||
|  |   // Save start timestamp
 | ||||||
|  |   start = performance.now(); | ||||||
|  | 
 | ||||||
|  |   // TODO (Exercise 3): Record data for TRACE_LENGTH seconds and save values to T.
 | ||||||
|  | 
 | ||||||
|  |   // Once done recording, send result to main thread
 | ||||||
|  |   postMessage(JSON.stringify(T)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // DO NOT MODIFY BELOW THIS LINE -- PROVIDED BY COURSE STAFF
 | ||||||
|  | self.onmessage = (e) => { | ||||||
|  |   if (e.data.type === "start") { | ||||||
|  |     TRACE_LENGTH = e.data.trace_length; | ||||||
|  |     setTimeout(record, 0); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | @ -0,0 +1,27 @@ | ||||||
|  | import json | ||||||
|  | import numpy as np | ||||||
|  | 
 | ||||||
|  | from sklearn.ensemble import RandomForestClassifier | ||||||
|  | from sklearn.metrics import classification_report | ||||||
|  | from sklearn.model_selection import train_test_split | ||||||
|  | 
 | ||||||
|  | def eval(): | ||||||
|  | 	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 | ||||||
|  | 
 | ||||||
|  | 		# 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 | ||||||
|  | 
 | ||||||
|  | if __name__ == "__main__": | ||||||
|  | 	eval() | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | ../index.html | ||||||
|  | @ -0,0 +1,32 @@ | ||||||
|  | // Duration of your trace, in milliseconds
 | ||||||
|  | let TRACE_LENGTH; | ||||||
|  | 
 | ||||||
|  | // Array of length TRACE_LENGTH with your trace's values
 | ||||||
|  | let T; | ||||||
|  | 
 | ||||||
|  | // Value of performance.now() when you started recording your trace
 | ||||||
|  | let start; | ||||||
|  | 
 | ||||||
|  | function record() { | ||||||
|  |   // Create empty array for saving trace values
 | ||||||
|  |   T = new Array(TRACE_LENGTH); | ||||||
|  | 
 | ||||||
|  |   // Fill array with -1 so we can be sure memory is allocated
 | ||||||
|  |   T.fill(-1, 0, T.length); | ||||||
|  | 
 | ||||||
|  |   // Save start timestamp
 | ||||||
|  |   start = performance.now(); | ||||||
|  | 
 | ||||||
|  |   // TODO (Exercise 7): Record data for TRACE_LENGTH seconds and save values to T.
 | ||||||
|  | 
 | ||||||
|  |   // Once done recording, send result to main thread
 | ||||||
|  |   postMessage(JSON.stringify(T)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // DO NOT MODIFY BELOW THIS LINE -- PROVIDED BY COURSE STAFF
 | ||||||
|  | self.onmessage = (e) => { | ||||||
|  |   if (e.data.type === "start") { | ||||||
|  |     TRACE_LENGTH = e.data.trace_length; | ||||||
|  |     setTimeout(record, 0); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | @ -0,0 +1,27 @@ | ||||||
|  | import json | ||||||
|  | import numpy as np | ||||||
|  | 
 | ||||||
|  | from sklearn.ensemble import RandomForestClassifier | ||||||
|  | from sklearn.metrics import classification_report | ||||||
|  | from sklearn.model_selection import train_test_split | ||||||
|  | 
 | ||||||
|  | def eval(): | ||||||
|  |     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 | ||||||
|  | 
 | ||||||
|  |         # 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 | ||||||
|  | 
 | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     eval() | ||||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,162 @@ | ||||||
|  | const domains = [ | ||||||
|  |   "https://www.google.com/", | ||||||
|  |   "https://www.youtube.com/", | ||||||
|  |   "https://www.tmall.com/", | ||||||
|  |   "https://www.qq.com/", | ||||||
|  |   "https://www.baidu.com/", | ||||||
|  |   "https://www.sohu.com/", | ||||||
|  |   "https://www.facebook.com/", | ||||||
|  |   "https://www.taobao.com/", | ||||||
|  |   "https://www.jd.com/", | ||||||
|  |   "https://www.amazon.com/", | ||||||
|  |   "https://www.yahoo.com/", | ||||||
|  |   "https://www.wikipedia.org/", | ||||||
|  |   "https://www.weibo.com/", | ||||||
|  |   "https://sina.com.cn/", | ||||||
|  |   "https://www.zoom.us/", | ||||||
|  |   "http://www.xinhuanet.com/", | ||||||
|  |   "https://www.live.com/", | ||||||
|  |   "https://www.reddit.com/", | ||||||
|  |   "https://www.netflix.com/", | ||||||
|  |   "https://www.microsoft.com/", | ||||||
|  |   "https://www.instagram.com/", | ||||||
|  |   "https://www.office.com/", | ||||||
|  |   "https://panda.tv/", | ||||||
|  |   "https://www.zhanqi.tv/", | ||||||
|  |   "https://www.alipay.com/", | ||||||
|  |   "https://www.bing.com/", | ||||||
|  |   "https://www.csdn.net/", | ||||||
|  |   "https://www.vk.com/", | ||||||
|  |   "https://www.myshopify.com/", | ||||||
|  |   "https://www.naver.com/", | ||||||
|  |   "https://www.okezone.com/", | ||||||
|  |   "https://www.twitch.tv/", | ||||||
|  |   "https://www.twitter.com/", | ||||||
|  |   "https://www.ebay.com/", | ||||||
|  |   "https://www.adobe.com/", | ||||||
|  |   "https://www.tianya.cn/", | ||||||
|  |   "https://www.huanqiu.com/", | ||||||
|  |   "https://www.yy.com/", | ||||||
|  |   "https://www.aliexpress.com/", | ||||||
|  |   "https://www.linkedin.com/", | ||||||
|  |   "https://www.force.com/", | ||||||
|  |   "https://www.aparat.com/", | ||||||
|  |   "https://www.mail.ru/", | ||||||
|  |   "https://www.msn.com/", | ||||||
|  |   "https://www.dropbox.com/", | ||||||
|  |   "https://www.whatsapp.com/", | ||||||
|  |   "https://www.apple.com/", | ||||||
|  |   "https://www.1688.com/", | ||||||
|  |   "https://www.wordpress.com/", | ||||||
|  |   "https://www.canva.com/", | ||||||
|  |   "https://www.indeed.com/", | ||||||
|  |   "https://www.stackoverflow.com/", | ||||||
|  |   "https://www.ok.ru/", | ||||||
|  |   "https://www.so.com/", | ||||||
|  |   "https://www.chase.com/", | ||||||
|  |   "https://www.imdb.com/", | ||||||
|  |   "https://www.slack.com/", | ||||||
|  |   "https://www.etsy.com/", | ||||||
|  |   "https://www.tiktok.com/", | ||||||
|  |   "https://www.booking.com/", | ||||||
|  |   "https://www.babytree.com/", | ||||||
|  |   "https://rakuten.co.jp/", | ||||||
|  |   "https://www.salesforce.com/", | ||||||
|  |   "https://www.spotify.com/", | ||||||
|  |   "https://www.tribunnews.com/", | ||||||
|  |   "https://www.fandom.com/", | ||||||
|  |   "https://www.tradingview.com/", | ||||||
|  |   "https://www.github.com/", | ||||||
|  |   "https://www.haosou.com/", | ||||||
|  |   "https://www.paypal.com/", | ||||||
|  |   "https://www.cnblogs.com/", | ||||||
|  |   "https://www.alibaba.com/", | ||||||
|  |   "https://www.kompas.com/", | ||||||
|  |   "https://gome.com.cn/", | ||||||
|  |   "https://www.walmart.com/", | ||||||
|  |   "https://www.roblox.com/", | ||||||
|  |   "https://www.6.cn/", | ||||||
|  |   "https://www.zillow.com/", | ||||||
|  |   "https://www.godaddy.com/", | ||||||
|  |   "https://www.imgur.com/", | ||||||
|  |   "https://www.espn.com/", | ||||||
|  |   "https://www.bbc.com/", | ||||||
|  |   "https://www.hao123.com/", | ||||||
|  |   "https://www.pikiran-rakyat.com/", | ||||||
|  |   "https://www.grammarly.com/", | ||||||
|  |   "https://www.cnn.com/", | ||||||
|  |   "https://www.telegram.org/", | ||||||
|  |   "https://www.tumblr.com/", | ||||||
|  |   "https://www.nytimes.com/", | ||||||
|  |   "https://www.detik.com/", | ||||||
|  |   "https://www.wetransfer.com/", | ||||||
|  |   "https://www.savefrom.net/", | ||||||
|  |   "https://www.rednet.cn/", | ||||||
|  |   "https://www.freepik.com/", | ||||||
|  |   "https://www.ilovepdf.com/", | ||||||
|  |   "https://www.daum.net/", | ||||||
|  |   "https://www.pinterest.com/", | ||||||
|  |   "https://www.primevideo.com/", | ||||||
|  |   "https://www.intuit.com/", | ||||||
|  |   "https://www.medium.com/", | ||||||
|  | ]; | ||||||
|  | 
 | ||||||
|  | const loadTime = 5000; | ||||||
|  | let loading = false; | ||||||
|  | let startTime = 0; | ||||||
|  | 
 | ||||||
|  | function randomPing() { | ||||||
|  |   const controller = new AbortController(); | ||||||
|  |   const id = setTimeout( | ||||||
|  |     () => controller.abort(), | ||||||
|  |     loadTime - (performance.now() - startTime) | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   fetch( | ||||||
|  |     domains[Math.floor(Math.random() * domains.length)] + | ||||||
|  |       "?" + | ||||||
|  |       new Date().getTime(), | ||||||
|  |     { | ||||||
|  |       signal: controller.signal, | ||||||
|  |     } | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function activityBurst() { | ||||||
|  |   switch (Math.floor(Math.random() * 2)) { | ||||||
|  |     case 0: | ||||||
|  |       let start = performance.now(); | ||||||
|  |       let counter = 0; | ||||||
|  | 
 | ||||||
|  |       while (performance.now() - start < 5) { | ||||||
|  |         counter += 1; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       console.log(counter); | ||||||
|  |       break; | ||||||
|  |     case 1: | ||||||
|  |       randomPing(); | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { | ||||||
|  |   if (changeInfo.status === "loading") { | ||||||
|  |     if (loading) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     startTime = performance.now(); | ||||||
|  |     loading = true; | ||||||
|  | 
 | ||||||
|  |     for (let i = 0; i < 20; i++) { | ||||||
|  |       randomPing(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (let i = 0; i < loadTime / 10; i++) { | ||||||
|  |       setTimeout(activityBurst, Math.random() * loadTime); | ||||||
|  |     } | ||||||
|  |   } else if (changeInfo.status === "complete") { | ||||||
|  |     loading = false; | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | @ -0,0 +1,10 @@ | ||||||
|  | { | ||||||
|  |   "name": "Lab Countermeasure", | ||||||
|  |   "version": "1.0", | ||||||
|  |   "manifest_version": 3, | ||||||
|  |   "background": { | ||||||
|  |     "service_worker": "background.js" | ||||||
|  |   }, | ||||||
|  |   "action": {}, | ||||||
|  |   "host_permissions": ["*://*/*"] | ||||||
|  | } | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | ../index.html | ||||||
|  | @ -0,0 +1,33 @@ | ||||||
|  | // Duration of your trace, in milliseconds
 | ||||||
|  | let TRACE_LENGTH; | ||||||
|  | 
 | ||||||
|  | // Array of length TRACE_LENGTH with your trace's values
 | ||||||
|  | let T; | ||||||
|  | 
 | ||||||
|  | // Value of performance.now() when you started recording your trace
 | ||||||
|  | let start; | ||||||
|  | 
 | ||||||
|  | function record() { | ||||||
|  |   // Create empty array for saving trace values
 | ||||||
|  |   T = new Array(TRACE_LENGTH); | ||||||
|  | 
 | ||||||
|  |   // Fill array with -1 so we can be sure memory is allocated
 | ||||||
|  |   T.fill(-1, 0, T.length); | ||||||
|  | 
 | ||||||
|  |   // Save start timestamp
 | ||||||
|  |   start = performance.now(); | ||||||
|  | 
 | ||||||
|  |   // TODO (Exercise 8): Copy your solution from part 3
 | ||||||
|  |   // TODO (Exercise 9): Optionally make changes to your part 3 solution if you need to
 | ||||||
|  | 
 | ||||||
|  |   // Once done recording, send result to main thread
 | ||||||
|  |   postMessage(JSON.stringify(T)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // DO NOT MODIFY BELOW THIS LINE -- PROVIDED BY COURSE STAFF
 | ||||||
|  | self.onmessage = (e) => { | ||||||
|  |   if (e.data.type === "start") { | ||||||
|  |     TRACE_LENGTH = e.data.trace_length; | ||||||
|  |     setTimeout(record, 0); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | @ -0,0 +1,48 @@ | ||||||
|  | #!/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