Broken One-time pad


There are many mistakes to be made when implementing crypto. So let’s make some, abuse them and learn.

Key as a service

I keep forgetting the key to my bitcoin wallet.

QR-Code wallet My bitcoin wallet

So I saved my key as a QR-Code. Converted it to a PNG. base64 encoded the PNG, and set up the following service:

#!/usr/bin/env python3
# vim: set fenc=utf8 ts=4 sw=4 et :
import sys
import socket
import random

from threading import Thread
from math import gcd
from base64 import b64encode

def lcm(a, b):
    """Return lowest common multiple of a and b"""
    return a * b // gcd(a, b)

with open("key.png", "rb") as f:
    SECRET = b64encode(f.read())

PAD_LENGTH = 1024 * 100
REPEAT = lcm(PAD_LENGTH, len(SECRET))

def client_thread(clientsocket):
    random.seed()
    pad = [ random.getrandbits(8) for i in range(PAD_LENGTH) ]
    for i in range(REPEAT):
        s = SECRET[i % len(SECRET)]
        p = pad[i % len(pad)]
        b = bytes([s ^ p])
        if clientsocket.send(b) == 0:
            return
    clientsocket.close()

def main():
    serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serversocket.bind((socket.gethostname(), 8888))
    serversocket.listen()
    print('len(SECRET) = {} Bytes'.format(len(SECRET)))
    print('len(pad) = {} Bytes'.format(PAD_LENGTH))
    print('shift = {} Bytes'.format(PAD_LENGTH % len(SECRET)))
    print('Repeat after {} MiB'.format(REPEAT / 1024 ** 2))
    sys.stdout.flush()
    while True:
        # accept connections on socket
        (clientsocket, address) = serversocket.accept()
        print('Client connected {}'.format(address))
        sys.stdout.flush()
        thread = Thread(target = client_thread, args = (clientsocket, ))
        thread.start()

if __name__ == "__main__":
    main()

Now I can connect via TCP to duckpond.ch:8888 and retrieve my key. As safe as it gets! 12

  1. Keep the money if you can break it. 

  2. Please share your solution in the comment section.