85 lines
2.8 KiB
Python
85 lines
2.8 KiB
Python
import argparse
|
|
import qrcode
|
|
from PIL import Image
|
|
from pathlib import Path
|
|
import random
|
|
|
|
NUM_PIECES = 3
|
|
TILE = 78
|
|
|
|
def generate_qrcode(data: str):
|
|
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):
|
|
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, decoyList, seed):
|
|
height_piece, width_piece = payloadList[0].size
|
|
random.seed(seed)
|
|
scrambled_width = 3
|
|
if decoyList is not None:
|
|
payloadList += random.sample(decoyList, NUM_PIECES)
|
|
assert len(payloadList) == 12
|
|
scrambled_width = 4
|
|
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, 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)
|
|
|
|
qr_pieces = cut_pieces(qr_code)
|
|
decoy_pieces = None
|
|
if decoy:
|
|
with open(res_dir / "decoy.txt", "r") as decoy_msg:
|
|
decoy = decoy_msg.read()
|
|
|
|
decoy_qr_code = generate_qrcode(decoy)
|
|
decoy_pieces = [piece for id, piece in enumerate(cut_pieces(decoy_qr_code)) if id not in [0, 2, 6]]
|
|
|
|
scrambled_qr_code = scramble_qr_code(qr_pieces, decoy_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("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.")
|
|
|
|
args = parser.parse_args()
|
|
|
|
generate_scrambled_qrcode(
|
|
seed=args.seed,
|
|
output_dir=args.output_dir,
|
|
res_dir=args.res_dir,
|
|
decoy=args.without_decoy,
|
|
name=args.output
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|