#!/usr/bin/python3 import qrcode # https://github.com/lincolnloop/python-qrcode import random import os from PIL import Image from flag import FLAG def vss22_gen(img): m, n = img.size share1, share2 = Image.new("L", (2*m, 2*n)), Image.new("L", (2*m, 2*n)) image_data = img.getdata() flipped_coins = [int(bit) for bit in bin(random.getrandbits(m*n))[2:].zfill(m*n)] for idx, pixel in enumerate(image_data): i, j = idx//n, idx % n color0 = 0 if flipped_coins[idx] else 255 color1 = 255 if flipped_coins[idx] else 0 if pixel: share1.putpixel((2*j, 2*i), color0) share1.putpixel((2*j, 2*i+1), color0) share1.putpixel((2*j+1, 2*i), color1) share1.putpixel((2*j+1, 2*i+1), color1) share2.putpixel((2*j, 2*i), color0) share2.putpixel((2*j, 2*i+1), color0) share2.putpixel((2*j+1, 2*i), color1) share2.putpixel((2*j+1, 2*i+1), color1) else: share1.putpixel((2*j, 2*i), color0) share1.putpixel((2*j, 2*i+1), color0) share1.putpixel((2*j+1, 2*i), color1) share1.putpixel((2*j+1, 2*i+1), color1) share2.putpixel((2*j, 2*i), color1) share2.putpixel((2*j, 2*i+1), color1) share2.putpixel((2*j+1, 2*i), color0) share2.putpixel((2*j+1, 2*i+1), color0) share1.save('share1.png') share2.save('share2.png') def vss22_superposition(): share1 = Image.open('share1.png') share2 = Image.open('share2.png') res = Image.new("L", share1.size, 255) share1_data = share1.getdata() share2_data = share2.getdata() res.putdata([p1 & p2 for p1, p2 in zip(share1_data, share2_data)]) res.save('result.png') def main(): qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=12, border=4, ) qr.add_data(FLAG) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") vss22_gen(img._img) img.save('res.png') vss22_superposition() if __name__ == '__main__': main()
QRコードの画像を random.getrandbits の生成結果でflipしているだけ。QRコードの周囲には白いマスがたくさんあるのでそこからrandbitsを647 * 32 bit 以上取得できればOK。 random.getrandbitsは rangeをprependするような挙動なので後ろから復元する。
from PIL import Image from mt19937predictor import MT19937Predictor img = Image.open("share2.png") # collect bits predict_bits = [] w, h = img.size for y in range(0, 96, 2): for x in range(0, w, 2): p = img.getpixel((w - x - 1, h - y - 1)) if p == 0: predict_bits.append(0) else: predict_bits.append(1) # predict bits = [] for i in range(624): x = "".join(str(b) for b in predict_bits[i*32:(i+1)*32][::-1]) bits.append(int(x, 2)) predictor = MT19937Predictor() for b in bits: predictor.setrandbits(b, 32) # get flipped_coins bs = [] for b in bits: bs = [int(b) for b in bin(b)[2:].zfill(32)] + bs while len(bs) < (w * h // 4): bs = [int(b) for b in bin(predictor.getrandbits(32))[2:].zfill(32)] + bs flipped_coins = bs[-(w * h // 4):] print(flipped_coins) # recover image w, h = w // 2, h // 2 qr = Image.new("L", (w, h)) for i in range(len(flipped_coins)): x, y = i % w, i // h p = img.getpixel((2*x, 2*y)) color0 = 0 if flipped_coins[i] else 255 if p == color0: qr.putpixel((x, y), 255) else: qr.putpixel((x, y), 0) qr.save("qr.png")