diff --git a/create_qrcode.py b/create_qrcode.py deleted file mode 100644 index cf57df2..0000000 --- a/create_qrcode.py +++ /dev/null @@ -1,69 +0,0 @@ -import argparse -from typing import List -import qrcode -from PIL import Image -from pathlib import Path -import random - -NUM_PIECES = 3 -TILE = 78 - -def generate_qrcode(data: str) -> Image: - qr = qrcode.QRCode(version=25, box_size=2, error_correction=qrcode.ERROR_CORRECT_L, border=0) - qr.add_data(data) - return qr.make_image() - - -def cut_pieces(image: Image) -> List[Image]: - pieces = [] - - for x, y in [(x, y) for x in range(NUM_PIECES) for y in range(NUM_PIECES)]: - pieces.append(image.crop((x * TILE, y * TILE, x * TILE + TILE, y * TILE + TILE))) - - return pieces - - -def scramble_qr_code(payloadList: List[Image], seed: str) -> Image: - height_piece, width_piece = payloadList[0].size - random.seed(seed) - scrambled_width = 3 - random.shuffle(payloadList) - - new_qr_code = Image.new("RGB", (width_piece * scrambled_width, height_piece * NUM_PIECES)) - for x, y in [(x, y) for x in range(scrambled_width) for y in range(NUM_PIECES)]: - piece = payloadList[x + scrambled_width * y] - new_qr_code.paste(piece, (x * width_piece, y * height_piece)) - - return new_qr_code - - -def generate_scrambled_qrcode(seed: str, output_dir: Path, name: str): - with open("./secret_message.txt", "r") as secret: - message = secret.read() - - qr_code = generate_qrcode(message + seed) - - qr_pieces = cut_pieces(qr_code) - scrambled_qr_code = scramble_qr_code(qr_pieces, seed) - - output_dir.mkdir(parents=True, exist_ok=True) - scrambled_qr_code.save(output_dir / name) - - -def main(): - parser = argparse.ArgumentParser(description="Generates a scrambled QR code with optional decoy pieces.") - parser.add_argument("seed", type=str, help="The password to scramble the QR code.") - parser.add_argument("output_dir", type=Path, help="Directory where the output will be stored") - parser.add_argument("-o", "--output", type=str, default="secret_message.png", help="The name of the output image file.") - - args = parser.parse_args() - - generate_scrambled_qrcode( - seed=args.seed, - output_dir=args.output_dir, - name=args.output - ) - - -if __name__ == "__main__": - main() diff --git a/description.yml b/description.yml index 399fccc..0822aee 100644 --- a/description.yml +++ b/description.yml @@ -16,3 +16,8 @@ Hint: content: "h4tum{sm4ll_key_spac3s_are_deadly}" dynamic: true files: dynamic # dynamic requires a gen_files script + dependencies: + type: debian + system: ["python3", "libzbar0"] + python: ["pillow", "qrcode", "pyzbar", "numpy", "tqdm"] + check: "python3 solve.py" diff --git a/res/decoy.txt b/files/decoy.txt similarity index 100% rename from res/decoy.txt rename to files/decoy.txt diff --git a/res/mosaic_secret_message b/files/secret_message.txt similarity index 96% rename from res/mosaic_secret_message rename to files/secret_message.txt index 0d4a94f..c216c17 100644 --- a/res/mosaic_secret_message +++ b/files/secret_message.txt @@ -9,7 +9,7 @@ Consider a toy "QR-encryption" scheme where a message is encoded into a QR code and then randomized by permuting its tiles with a secret seed. (a) Under ideal assumptions, explain why this scheme could satisfy -Kerckhoffs’ principle. +Kerckhoffs’libzbar0 principle. (b) Now assume an attacker can test permutations against the QR format (find patterns, error correction, alignment markers). Why does this reduce the effective security of the scheme? diff --git a/gen_files.py b/gen_files.py index 36ef666..90ec37e 100644 --- a/gen_files.py +++ b/gen_files.py @@ -40,8 +40,8 @@ def scramble_qr_code(payloadList, decoyList, seed): return new_qr_code -def generate_scrambled_qrcode(seed, output_dir, decoy, name): - with open("res/secret_message.txt", "r") as secret: +def generate_scrambled_qrcode(seed, output_dir, res_dir, decoy, name): + with open(res_dir / "secret_message.txt", "r") as secret: message = secret.read() qr_code = generate_qrcode(message + seed) @@ -49,7 +49,7 @@ def generate_scrambled_qrcode(seed, output_dir, decoy, name): qr_pieces = cut_pieces(qr_code) decoy_pieces = None if decoy: - with open("res/decoy.txt", "r") as decoy_msg: + with open(res_dir / "decoy.txt", "r") as decoy_msg: decoy = decoy_msg.read() decoy_qr_code = generate_qrcode(decoy) @@ -65,6 +65,7 @@ def main(): parser = argparse.ArgumentParser(description="Generates a scrambled QR code with optional decoy pieces.") parser.add_argument("seed", type=str, help="The password to scramble the QR code.") parser.add_argument("output_dir", type=Path, help="Directory where the output will be stored") + parser.add_argument("res_dir", type=Path, help="Directory where needed resources are stored") parser.add_argument("--without-decoy", action="store_false", help="Exclude decoy pieces in the scrambled QR code.") parser.add_argument("-o", "--output", type=str, default="secret_message.png", help="The name of the output image file.") @@ -73,6 +74,7 @@ def main(): generate_scrambled_qrcode( seed=args.seed, output_dir=args.output_dir, + res_dir=args.res_dir, decoy=args.without_decoy, name=args.output ) diff --git a/healthcheck.py b/healthcheck.py index 1d13030..254c560 100644 --- a/healthcheck.py +++ b/healthcheck.py @@ -12,14 +12,14 @@ test_flag = f"h4tum{{healthy_healthy_healthchecks_{random_bytes}}}" generate_scrambled_qrcode( seed=test_flag, - output_dir=Path("tmp/health_check"), + output_dir=Path("/tmp/health_check"), decoy=True, name=f"{random_bytes}.png" ) -output = main(f"tmp/health_check/{random_bytes}.png", False) +output = main(f"/tmp/health_check/{random_bytes}.png", False) if output and test_flag in output: - print("Healthy!") + print(output) else: print("Oh oh somethings wrong :()") diff --git a/res/secret_message.txt b/res/secret_message.txt deleted file mode 100644 index 9fc59d9..0000000 --- a/res/secret_message.txt +++ /dev/null @@ -1,4 +0,0 @@ -Wow you solved the challenge! -Meet us at our booth we really want to get in touch with you :D - -Show us the flag: diff --git a/solve.py b/solve.py index 679bbea..a8420b1 100644 --- a/solve.py +++ b/solve.py @@ -1,4 +1,3 @@ -from logging import disable from PIL import Image from pyzbar.pyzbar import decode import itertools @@ -104,7 +103,7 @@ def solve_combo(combo): return None -def main(img_path="student_fair/secret_message.png", verbose=True): +def main(img_path="files/secret_message.png", verbose=True): img = Image.open(img_path) global new_image new_image = Image.new("RGB", (img.height, img.height)) @@ -129,10 +128,8 @@ def main(img_path="student_fair/secret_message.png", verbose=True): result = None - MAX_WORKER = 6 - # Use `multiprocessing.Pool` with the number of available cores - with Pool(MAX_WORKER) as pool: + with Pool() as pool: # `imap_unordered` returns results as they are completed. # This is a lazy iterator, so it doesn't create all combos at once. # `tqdm` can still be used to show progress.