def padding_oracle_attack(cipher: bytes, bs: int = 16) -> bytes: size = len(cipher) if size < bs * 2: raise Exception("Can only attack more than 2 block sizes") res = reduce(list.__add__, [crack_block(cipher[index - (2 * bs):index]) for index in range(bs * 2, size + 1, bs)]) return strip_padding(bytes(res), False)
def test_padding(self): if "padding" not in self.tests: self.skipTest("") test = b"1" + b"\x0f" * 15 actual = strip_padding(test) expected = b"1" self.assertEqual(expected, actual) test = b"21" + b"\x0f" * 14 self.assertRaises(WrongPaddingException, strip_padding, test) test = b"\x10" * 15 + b"\0" self.assertRaises(WrongPaddingException, strip_padding, test) test = b"\x10" * 16 actual = strip_padding(test) expected = b"" self.assertEqual(expected, actual)
def crack_ecb_simple(func: Callable[[bytes], bytes], left_base_size: int = 0) -> bytes: # block_size = find_block_size(func) # wrong block_size = 16 answer = [] base_byte = b"A" base_offset = round_up(left_base_size, block_size) lefter = base_byte * (base_offset - left_base_size) if not ecb_or_cbc(func, base_offset): raise Exception("This is not a ECB oracle") guess_data_size = len(func(lefter)) zip_vars = ( (x // block_size, # distance block_size - x % block_size - 1) # padding_size for x in range(guess_data_size) ) for distance, padding_size in zip_vars: # as left part is always 15 bytes, for the first round we need padding # and for all consecutive blocks we don't, so: # bool(distance) ^ 1 returns 0 if (distance > 0) left_padding_size = padding_size * (bool(distance) ^ 1) left_data_size = block_size - left_padding_size - 1 left = (base_byte * left_padding_size) + bytes(answer[-left_data_size:]) right = (base_byte * padding_size) blocks = [left + bytes([b]) + right for b in range(256)] block_pairs = [(block, func(lefter + block)) for block in blocks] for block, enc_block in block_pairs: enc_offset = base_offset + block_size * (distance + 1) left_start = base_offset right_start = enc_offset if enc_block[left_start:left_start + block_size] == enc_block[right_start:right_start + block_size]: x = block[block_size - 1] answer.append(x) break if len(answer) % block_size > 0: return bytes(answer) else: return strip_padding(bytes(answer))
def test_cbc(self): if "cbc" not in self.tests: self.skipTest("") data = b"1234567890" key = b"YELLOW SUBMARINE" iv = 16 * b"\0" cipher = AES.new(key, AES.MODE_CBC, iv) expected = cipher.encrypt(pkcs7_pad(data, AES.block_size)) actual = encrypt_cbc(data, key, iv) self.assertEqual(expected, actual) cipher = AES.new(key, AES.MODE_CBC, iv) expected = strip_padding(cipher.decrypt(expected)) actual = decrypt_cbc(actual, key, iv) self.assertEqual(expected, actual) data = b"1234567890abcdef" cipher = AES.new(key, AES.MODE_CBC, iv) expected = cipher.encrypt(data) actual = encrypt_cbc(data, key, iv) self.assertEqual(expected, actual)
def decrypt_profile(data: bytes) -> dict: global key ret = decrypt_aes_128_ecb(data, key) ret = strip_padding(ret) ret = kv_parse(ret.decode("ascii")) return dict(ret)