def do_padding(c1, known): if len(known)>0: padding = bytes([len(known)+1]*(len(known))) to_flip = xorstr(known, padding) flipped = xorstr(to_flip, c1[0-len(padding):]) return bytearray(c1[:-1*len(padding)]+flipped) else: return bytearray(c1)
def aes_ctr(key, nonce, data): crypt = AES.new(key, AES.MODE_ECB) blocks = to_blocks(data, 16) plaintext = b'' for i in range(0, len(blocks)): f = struct.pack("<qq", nonce, i) k = crypt.encrypt(f) plaintext += xorstr(blocks[i], k) return plaintext
def cbc_decrypt(data, key, iv): crypt = AES.new(key, AES.MODE_ECB) blocks = [iv] + to_blocks(data, len(key)) blocks = [ xorstr(crypt.decrypt(blocks[i]), blocks[i - 1]) for i in range(1, len(blocks)) ] plaintext = b"".join(blocks) assert check_padding(plaintext) return plaintext
key = rndfile.read(16) # Here is the challenge plaintexts = [b64decode(i) for i in open("c19_data.txt").readlines()] ciphertexts = [aes_ctr(key, 1234, i) for i in plaintexts] plaintexts = [b'' for i in ciphertexts] max_len = max([len(i) for i in ciphertexts]) print(plaintexts) # Let's try frequency analysis on the each byte of each of them # # (Okay, this turns out to be the same as transposing it and solving it # as a repeating xor key as in challenge 20) # key = b'' for n in range(max_len): n_bytes = bytes([i[n] for i in ciphertexts if n<len(i)]) key += bytes([break_single_xor(n_bytes)]) for i in ciphertexts: print(xorstr(i, key)) # Drop to a console to play with it some more # The approach above gets most of the keystream, up to # about 30 bytes it looks correct. code.InteractiveConsole(locals=globals()).interact()
def check_admin(token): global key crypt = AES.new(key, AES.MODE_CBC, token[0:16]) plaintext = unpad(crypt.decrypt( token[16:])) # oh no, padding oracles! (for later) pairs = [i.rsplit(b"=") for i in plaintext.rsplit(b";")] return ([b"admin", b"true"] in pairs) ########################################################## # Generate a token, what is in it doesn't really matter the way we're # doing it, since we know what lives in the last block - let's just make it # "TOKEN," because that makes us land on an even padding :) token = gen_token(b"TOKEN") # The last two blocks are going to be filled with, like, a pound of bacon # so we know what they are, and we know we can edit them at will without # really breaking anything important. blocks = to_blocks(token, 16) bits_to_flip = xorstr(b";admin=true;A=A\x01", b"nd%20of%20bacon\x01") blocks[-2] = xorstr(blocks[-2], bits_to_flip) token = b"".join(blocks) print(check_admin(token))
return [i[0] for i in sorted(keysizes, key=lambda x: x[1])] def to_blocks(data, blocksize): return [data[i:i+blocksize] for i in range(0, len(data), blocksize)] def transpose(blocks, blocksize): return [bytes([x[i] for x in blocks if len(x)>i]) for i in range(blocksize)] if __name__ == "__main__": data = b64decode(open("c06_data.txt").read()) keysize = get_keysizes(data, 10)[0] print("Using keysize %d" % keysize) blocks = to_blocks(data, keysize) blocks = transpose(blocks, keysize) key = bytes([break_single_xor(i) for i in blocks]) print("Using key '%s'\n=========================================" % key.decode("ascii")) print(xorstr(data, key).decode("ascii"))