Updated scripts and added new

This commit is contained in:
2023-11-25 20:23:04 +01:00
parent a5153cc997
commit 92df6f6b44
7 changed files with 566 additions and 251 deletions

View File

@ -1,11 +1,12 @@
import argparse
import json
import math
import sys
import os
import subprocess
import sys
from pathlib import Path
from zipfile import ZipFile
from util import is_valid_zip, select_zip
"""
Checks if all submission output the correct decrypted data.
@ -33,21 +34,31 @@ from zipfile import ZipFile
If more than one simulator ZIP file is present, the user will also be prompted to choose.
"""
SIMULATOR_PATH = "./minimax_simulator-2.0.0-cli.jar"
SIMULATOR_PATH = Path("./minimax_simulator-2.0.0-cli.jar")
# helper strings
OK = "\033[32mOK\033[0m"
ERROR = "\033[31mERROR\033[0m"
def compare_result(actual_file: str, expected_file: str) -> bool:
def ul(s: str) -> str:
"""
Adds ansi escape sequences to underline string.
"""
return f"\033[4m{s}\033[0m"
def compare_result(actual_file: Path, expected_file: Path) -> bool:
"""
Compares the file at path 'actual_file' with the file at path 'expected_file' bytewise.
If the actual file is larger than the expected file, additional bytes will be ignored.
"""
with open(actual_file, "rb") as actual, open(expected_file, "rb") as expected:
actual_bytes = actual.read()
expected_bytes = expected.read()
try:
with open(actual_file, "rb") as actual, open(expected_file, "rb") as expected:
actual_bytes = actual.read()
expected_bytes = expected.read()
except FileNotFoundError:
return False
return actual_bytes[: len(expected_bytes)] == expected_bytes
@ -73,47 +84,14 @@ def create_mem_layout() -> dict:
return mem_layout
def is_valid_zip(file: str) -> bool:
"""
Checks if a zip file is a save file from the minimax simulator, e.g. if the contents
are a 'machine.json' and 'signal.json' file.
"""
if not file.endswith(".zip"):
return False
with ZipFile(file) as machine_zip:
zip_content = machine_zip.namelist()
return set(zip_content) == set(("machine.json", "signal.json"))
def select_zip(zips: list) -> str:
"""
Prompts the user to select a single zip file from a list, and returns it.
"""
print("Multiple zip files found. Please select one:")
for index, f in enumerate(zips, start=1):
print(f"[{index}] {f}")
while True:
try:
selection = input("Enter the number of the zip file to select: ")
selection = int(selection) - 1
if selection <= 0 or selection > len(zips):
print(f"Please select a number between 1 and {len(zips)}.")
else:
return zips[selection]
except ValueError:
print("Please enter a valid integer.")
except KeyboardInterrupt:
sys.exit("Aborted")
def evaluate(
zip_file: str,
sbox_file: str,
key_file: str,
data_file: str,
result_file: str,
zip_file: Path,
sbox_file: Path,
key_file: Path,
data_file: Path,
result_file: Path,
mem_layout: dict,
simulator: str,
simulator: Path,
) -> None:
"""
Runs the minimax simulator on the given input. The resulting file is saved in 'result_file'.
@ -138,36 +116,38 @@ def evaluate(
"--export-file",
result_file,
"--export-from",
mem_layout["data"],
mem_layout.get("result", mem_layout["data"]),
"--export-to",
mem_layout["data"] + math.ceil(os.path.getsize(data_file)),
mem_layout.get("result", mem_layout["data"]) + math.ceil(os.path.getsize(data_file) / 4),
]
args = [
str(arg) for arg in args
] # subprocess.run requires all arguments to be strings
if "result" in mem_layout:
print("Decryption was not done in-place.")
args = [str(arg) for arg in args]
# subprocess.run requires all arguments to be strings
print(f"Running simulator, storing result in '{result_file}'")
result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print("\033[38;5;245m")
print(result.stdout.decode("utf-8"))
print("\033[38;5;124")
print(result.stderr.decode("utf-8"))
subprocess.run(args, stdout=sys.stdout, stderr=sys.stderr)
print("\033[0m")
if __name__ == "__main__": # only run if executed as script
parser = argparse.ArgumentParser()
parser.add_argument("submissions", type=str, help="Submissions root directory")
parser = argparse.ArgumentParser(
description="Runs the simulator on all projects found in the submissions directory."
)
parser.add_argument(
"group", type=str, help="Group directory, contains all project files"
"submissions",
type=Path,
help="Submissions root directory",
)
parser.add_argument(
"group",
type=Path,
help="Group directory, contains all project files",
)
parser.add_argument(
"-e",
@ -176,24 +156,20 @@ if __name__ == "__main__": # only run if executed as script
type=str,
help="Result file extension",
)
parser.add_argument("-j", "--jar", dest="jar", type=str, help="Simulator jar file")
parser.add_argument("-j", "--jar", dest="jar", type=Path, help="Simulator jar file")
args = parser.parse_args()
# Load teams
teams = [
e
for e in os.listdir(args.submissions)
if os.path.isdir(os.path.join(args.submissions, e))
]
teams = [e for e in args.submissions.iterdir() if e.is_dir()]
print(f"The following teams were found:")
for team in teams:
print(f"* {team}")
print(f"* {team.name}")
# Check directory structure
for file in ("sBox", "key", "data_encrypted", "data_decrypted"):
if not os.path.exists(os.path.join(args.group, file)):
sys.exit(f"Group project file '{file}' is missing.")
for filename in ("sBox", "key", "data_encrypted", "data_decrypted"):
if not (args.group / filename).exists():
sys.exit(f"Group project file '{filename}' is missing.")
# Check file extension
if args.file_ext is None:
@ -205,31 +181,24 @@ if __name__ == "__main__": # only run if executed as script
simulator = SIMULATOR_PATH if args.jar is None else args.jar
# Store evaluation results
expected_file = os.path.join(args.group, "data_decrypted")
expected_file = args.group / "data_decrypted"
results = []
# Evaluate each team
for team in teams:
print(ul(f"Evaluating team '{team.name}'"))
# load memory layout file if available (otherwise create and store it)
try:
with open(
os.path.join(args.submissions, team, "mem_layout.json"), "r"
) as mem_layout_file:
with open(team / "mem_layout.json", "r") as mem_layout_file:
mem_layout = json.load(mem_layout_file)
except FileNotFoundError:
mem_layout = create_mem_layout()
with open(
os.path.join(args.submissions, team, "mem_layout.json"), "w"
) as mem_layout_file:
with open(team / "mem_layout.json", "w") as mem_layout_file:
json.dump(mem_layout, mem_layout_file)
# Select project file (if more there is more than one zip file)
zip_files = [
os.path.join(args.submissions, team, f)
for f in os.listdir(os.path.join(args.submissions, team))
if is_valid_zip(os.path.join(args.submissions, team, f))
]
zip_files = [file for file in team.glob("*.zip") if is_valid_zip(file)]
zip_file = zip_files[0] if len(zip_files) == 1 else select_zip(zip_files)
# check memory layout and convert hexadecimal addresses
@ -246,12 +215,10 @@ if __name__ == "__main__": # only run if executed as script
mem_layout[key] = addr
# evaluate team
sbox_file = os.path.join(args.group, "sBox")
key_file = os.path.join(args.group, "key")
data_file = os.path.join(args.group, "data_encrypted")
result_file = os.path.join(
args.submissions, team, f"data_decrypted{args.file_ext}"
)
sbox_file = args.group / "sBox"
key_file = args.group / "key"
data_file = args.group / "data_encrypted"
result_file = team / f"data_decrypted{args.file_ext}"
evaluate(
zip_file,
@ -270,8 +237,8 @@ if __name__ == "__main__": # only run if executed as script
print("Summary:")
for team, result in zip(teams, results):
if result is True:
print(f"[{OK}] - {team}")
print(f"[{OK}] - {team.name}")
else:
print(f"[{ERROR}] - {team}")
print(f"[{ERROR}] - {team.name}")
print()