6.857: Computer and Network Security

Problem Set 2

Successful Teams (in chronological order)

Source code

Here's our implementation of the Advanced Bit-Diddling Standard. The server will accept any key that encrypts 100 random plaintexts to the same ciphertexts as the secret key.

def xor(s1, s2):
    """Return s1 XOR s2. Truncates the longer input."""
    return ''.join(chr(ord(a) ^ ord(b)) for (a, b) in zip(s1, s2))

def permutebits(m, p):
    """Permute each bit in m according to the string p, which encodes an
    8*len(m)-long permutation."""
    out = ""
    for byte in xrange(len(m)):
        v = 0
        for bit in xrange(8):
            # Fetch the appropriate index from the permutation.
            i = ord(p[8 * byte + bit])
            # Find the message byte and shift it down to extract the bit
            # we want.
            b = (ord(m[i / 8]) >> (7 - (i % 8))) & 1
            # Add this as the next bit to v.
            v = (v << 1) + b
        out += chr(v)
    return out

def diddle_bits(data, p, S, rounds):
        l = data[0:8]
        r = data[8:16]
        for i in xrange(rounds):
            rp = permutebits(r, p)
            rS = ''.join(S[ord(b)] for b in rp)
            (l, r) = (r, xor(l, rS))
        return l + r

Interface

This web server exposes the following interface. You can access it with either GET or POST; GET will be demonstrated for simplicity.

Don't forget that there's a Python client library on the class website, which takes care of all of the below for you.

Key Generation

Each key in the database is associated with a team label and a number of rounds. Make a call to /ps2/genkey with parameters team and rounds (for the problem set, the number of rounds should be 2 or more). For instance, if team 0 wanted to attempt cracking 3 rounds, they would access http://6.857.scripts.mit.edu/ps2/genkey?team=0&rounds=3.

The output is of the form Key <b>100</b> generated.

Encryption

All data is encoded in uppercase hexadecimal. For instance, to transmit the binary string 0001001000111111, you would encode it as 123F. (This is the encoding of Python's base64.b16encode and base64.b16decode, for instance.) Bits and bytes are big-endian, i.e., the first bit is the highest-order bit (0 in this example), and the first byte is the highest-order byte (0x12).

Send a request to /ps2/encrypt with parameters key, the key ID you specified in the previous round, and data, the block you want encrypted. Each block should be 128 bits, so therefore a properly-encoded bock is 32 hexadecimal digits.

For instance, to encrypt the zero block with the key that team 0 previously generated, access http://6.857.scripts.mit.edu/ps2/encrypt?key=100&data=00000000000000000000000000000000.

The output will just be the encrypted block, in the same hexademical encoding.

You can only encode 1000 plaintexts with the same key. After this point you will need to generate a new key.

Guessing the key

Encode your p and S arrays with one byte per cell and encode them in the same manner as above (so if p = [15, 3, 5...], you'd use 0F0305...). Pass the parameters p and S (note that S is upper-case), along with key to /ps2/guess.

If you guess wrong, the key will no longer be valid for encryption.