def challenge_6(): with open('6.txt') as f: data = base64.b64decode(f.read()) keysize_candidates = list() for keysize in range(1, 30): chunks = chunk_into(data, keysize) pairs = itertools.combinations(chunks[:4], 2) score = 0 for a, b in pairs: hscore = hamming(a, b) logger.debug(hscore) score += hscore / keysize score /= 4 keysize_candidates.append((keysize, score)) keysize_probability = sorted(keysize_candidates, key=lambda x: x[1]) logger.info("Probable keysizes: {}".format(keysize_probability[:5])) for keysize, score in keysize_probability[:3]: key_ints = list() logger.debug(keysize) chunks = chunk_into(data, keysize) transposed = zip(*chunks[:-1]) for c in transposed: try: _, key_int = english_freq(bytes(c)) key_ints.append(key_int) except TypeError: break if len(key_ints) == keysize: return xor_with_key(data, bytes(key_ints))
def challenge_14(): oracle = C14Oracle() minimal_ct = oracle(b'') min_len = len(minimal_ct) logger.info("CT with empty payload {}: {}".format(min_len, minimal_ct)) pushout = hdr = None prefix_pad_to_block_boundary = 0 for i in range(1, BLOCKSIZE + 1): pairs = zip(chunk_into(oracle(i * b'x'), BLOCKSIZE), chunk_into(oracle(i * b'y'), BLOCKSIZE)) chunk_equality = [a == b for a, b in pairs] if hdr is None: hdr = chunk_equality.index(False) + 1 falses = chunk_equality.count(False) logger.debug('i: {}, {}'.format(i, falses)) logger.debug(chunk_equality) if not prefix_pad_to_block_boundary and falses > 1: prefix_pad_to_block_boundary = i - 1 if pushout is None and len(chunk_equality) > min_len / BLOCKSIZE: pushout = i + 1 prefix_len = hdr * BLOCKSIZE - prefix_pad_to_block_boundary plaintext_len = min_len - prefix_len - pushout + 1 discard = hdr * BLOCKSIZE logger.info('Prefix len: {}'.format(prefix_len)) logger.info('Plaintext len: {}'.format(plaintext_len)) logger.info('Prefix pad to block boundary: {}'.format( prefix_pad_to_block_boundary)) logger.info('Discard: {}'.format(discard)) known = list() logger.info('Push out: {}'.format(pushout)) goldindex = min_len while len(known) < plaintext_len: insert = (len(known) + pushout) * b'x' ct = oracle(insert) gold = ct[goldindex:] for c in string.printable: char = bytes((ord(c), )) filler = prefix_pad_to_block_boundary * b'x' plaintext = pkcs7pad(char + b''.join(known), BLOCKSIZE) compare_to = oracle(filler + plaintext)[discard:] # assert len(compare_to) == len(gold) # logger.info(compare_to) if compare_to.startswith(gold): known.insert(0, char) break logger.debug(known) return b''.join(known)
def main(): ct = encrypt() iv, *blocks = chunk_into(ct, BLOCKSIZE) logger.info(iv) logger.info(blocks) ret = list() prev_block = iv for block in blocks: known = list() while len(known) < BLOCKSIZE: i = len(known) known_trailer = bytes((ord(x) ^ y ^ (i + 1)) for x, y in (zip(known, prev_block[-i:]))) logger.debug("Trailer: {}".format(known_trailer)) for c in range(256): char = bytes((c, )) fake_block = b'\x00' * (BLOCKSIZE - len(known_trailer) - 1) + char + known_trailer if padding_oracle(fake_block + block): Ca = char[0] padsize = len(known) + 1 known.insert( 0, bytes((Ca ^ prev_block[-padsize] ^ padsize, ))) logger.info('Decrypted: {}'.format(b''.join(known))) break ret.extend(known) prev_block = block return unpad(b''.join(ret))
def challenge_12(): with open('12.txt') as f: suffix = b64decode(f.read()) blocksize = detect_blocksize(suffix) x = 2 * blocksize * b'A' ciphertext = deterministic_oracle(x, suffix) chunks = chunk_into(ciphertext, blocksize) if len(chunks) > len(set(chunks)): logger.info("Cipher is running in ECB mode") datalen = len(deterministic_oracle(b'', suffix)) logger.critical(datalen) known = list() for offset in range(1, datalen): padder = (datalen - offset) * b'A' rtable = dict() for c in range(256): char = bytes((c, )) plain = padder + b''.join(known) + char # logger.debug(plain) rtable[deterministic_oracle(plain, suffix)[:datalen]] = char ciphertext = deterministic_oracle(padder, suffix) try: known.append(rtable[ciphertext[:datalen]]) except KeyError: return unpad(b''.join(known))
def main(): ct = encrypt() iv, *blocks = chunk_into(ct, BLOCKSIZE) logger.info(iv) logger.info(blocks) ret = list() prev_block = iv for block in blocks: known = list() while len(known) < BLOCKSIZE: i = len(known) known_trailer = bytes((ord(x) ^ y ^ (i + 1)) for x, y in (zip(known, prev_block[-i:]))) logger.debug("Trailer: {}".format(known_trailer)) for c in range(256): char = bytes((c,)) fake_block = b'\x00' * (BLOCKSIZE - len(known_trailer) - 1) + char + known_trailer if padding_oracle(fake_block + block): Ca = char[0] padsize = len(known) + 1 known.insert(0, bytes((Ca ^ prev_block[-padsize] ^ padsize,))) logger.info('Decrypted: {}'.format(b''.join(known))) break ret.extend(known) prev_block = block return unpad(b''.join(ret))
def decrypt(self, data): assert len(data) % 16 == 0 blocks = chunk_into(data, 16) plain = list() for block in blocks: plain.append(xor_with_key(ECB(self.key).decrypt(block), self._state)) self._state = block return unpad(b''.join(plain))
def detect_blocksize(suffix): for i in range(3, 65): plain = b'A' * 64 cipher = deterministic_oracle(plain, suffix) chunks = chunk_into(cipher, i) if chunks[0] == chunks[1]: logger.info('Block size = {}'.format(i)) return i
def challenge_13(): min_len = len(profile_for('')) for i in range(BLOCKSIZE): if len(profile_for('x' * i)) > min_len: break spacing = i chunks = chunk_into( profile_for(spacing * b'x' + pkcs7pad(b'admin', BLOCKSIZE)), BLOCKSIZE) cut = chunk_into( profile_for(spacing * b'x' + pkcs7pad(b'user', BLOCKSIZE)), BLOCKSIZE)[1] paste = chunks[1] for i in range(BLOCKSIZE): chunks = chunk_into(profile_for(i * b'x'), BLOCKSIZE) if chunks[-1] == cut: chunks[-1] = paste return parse_profile(b''.join(chunks)).items()
def encrypt(self, data): ret = list() blocks = chunk_into(data, BLOCKSIZE) for i, block in enumerate(blocks): _ks = self.cipher.encrypt(struct.pack('<2Q', self.nonce, i)) logger.debug(block) logger.debug(_ks) ret.append(xor_with_key(block, _ks)) return b''.join(ret)
def decrypt(self, data): assert len(data) % 16 == 0 blocks = chunk_into(data, 16) plain = list() for block in blocks: plain.append( xor_with_key(ECB(self.key).decrypt(block), self._state)) self._state = block return unpad(b''.join(plain))
def encrypt(self, data): if not len(data) % 16 == 0: data = pkcs7pad(data, 16) blocks = chunk_into(data, 16) ciphertext = list() for block in blocks: cipherblock = ECB(self.key).encrypt(xor_with_key(block, self._state)) ciphertext.append(cipherblock) self._state = cipherblock return b''.join(ciphertext)
def encrypt(self, data): if not len(data) % 16 == 0: data = pkcs7pad(data, 16) blocks = chunk_into(data, 16) ciphertext = list() for block in blocks: cipherblock = ECB(self.key).encrypt( xor_with_key(block, self._state)) ciphertext.append(cipherblock) self._state = cipherblock return b''.join(ciphertext)
def challenge_8(): with open('8.txt') as f: candidates = (base64.b64decode(x) for x in f.readlines()) best = None for candidate in candidates: chunks = chunk_into(candidate, 16) unique_chunks = len(set(chunks)) if best is None: score = unique_chunks best = candidate elif unique_chunks < score: best = candidate score = unique_chunks logger.debug(unique_chunks) return best, score
def test_chunking(self): self.assertEquals(chunk_into('foobarx', 3), ['foo', 'bar', 'x'])
def analyze(plain, chunk_count): analyzed = len(set(chunk_into(encryption_oracle(plain), BLOCKSIZE))) if chunk_count > analyzed: return 'ECB' else: return 'CBC'