def forge_via_length_extension(attacker_id: str, victim_id: str) -> bytes: victim = ClientV2(victim_id) server = ServerV2() # Intercept transaction from victim to some recipient "8". payload = victim.send({"8": 100}) assert server.validate(payload) message, t = parse_payload_v2(payload) message = pkcs7(message) # It's important to keep track of the padding. # CBC-MAC extension works because for some t = mac(m_1), # mac(m1 || m_2) = mac(t xor m_2). # We want to concatonate the original message with our own extension, # which will be a transaction to our ID for 1,000,000 spacebucks. # Conveniently that's the largest order of magnitude amount that we # can squeeze into the extension. # We need to keep the "from=..." field intact. It's 6 bytes long. The # below xor ensures that in the extension attack, the resulting message # will retain the original 6 bytes. mandatory_xor = fixed_xor(message[:6], t[:6]) extension = mandatory_xor + b";" + attacker_id.encode() + b":1000000" # Extend and generate a new CBC-MAC. # Yeah, we call victim.cbc_mac here. m_prime = message + extension mac_prime = victim.cbc_mac(fixed_xor(t, extension)) payload = m_prime + mac_prime assert server.validate(payload) return payload
def hmac_sha1(key: bytes, message: bytes) -> SHA1: opad = b"\x5c" * SHA1.block_size ipad = b"\x36" * SHA1.block_size if len(key) > SHA1.block_size: key = SHA1(key).digest() if len(key) < SHA1.block_size: key += bytes(SHA1.block_size - len(key)) return SHA1( fixed_xor(key, opad) + SHA1(fixed_xor(key, ipad) + message).digest())
def key(cyphertext): keysize = findkeysize(cyphertext) blocks = [cyphertext[i:i + keysize] for i in range(0, len(cyphertext), keysize)] blocks = [bytes(x) for x in list(zip(*blocks[0:-1]))] cleartext = bytes([m03.break_single_byte_xor(x)[0] for x in blocks]) key = m02.fixed_xor(cleartext, cyphertext) return key
def key(cyphertext: bytes) -> bytes: keysize = find_keysize(cyphertext) blocks = [ cyphertext[i:i + keysize] for i in range(0, len(cyphertext), keysize) ] blocks = [bytes(x) for x in zip(*blocks[0:-1], strict=True)] plaintext = bytes([break_single_byte_xor(x)[0] for x in blocks]) return fixed_xor(plaintext, cyphertext[:len(plaintext)])
def aes_ctr(c, k, n = 0): c = [c[16*i:16*(i+1)] for i in range(0, len(c) // 16 + 1)] cypher = AES.new(k, AES.MODE_ECB) m = b'' ctr = 0 for block in c: keystream = pack('<Qq', n, ctr) m += fixed_xor(cypher.encrypt(keystream), block) ctr += 1 return m
def decrypt_aes_cbc(cyphertext, key, iv): cypher = AES.new(key, AES.MODE_ECB) blocks = [cyphertext[i:i + len(key)] for i in range(0, len(cyphertext), len(key))] vector = iv plaintext = b'' for aesblock in blocks: block = cypher.decrypt(aesblock) plaintext += fixed_xor(block, vector) vector = aesblock return plaintext
def encrypt_aes_cbc(plaintext, key, iv): cypher = AES.new(key, AES.MODE_ECB) blocks = [plaintext[i:i + len(key)] for i in range(0, len(plaintext), len(key))] vector = iv cyphertext = b'' for block in blocks: block = fixed_xor(block, vector) block = cypher.encrypt(block) cyphertext += block vector = block return cyphertext
def decrypt_aes_cbc(key: bytes, iv: bytes, cyphertext: bytes) -> bytes: cypher = AES.new(key, AES.MODE_ECB) blocks = [ cyphertext[i:i + len(key)] for i in range(0, len(cyphertext), len(key)) ] vector = iv plaintext = b"" for aesblock in blocks: block = cypher.decrypt(aesblock) plaintext += fixed_xor(block, vector) vector = aesblock return plaintext
def encrypt_aes_cbc(key: bytes, iv: bytes, plaintext: bytes) -> bytes: cypher = AES.new(key, AES.MODE_ECB) blocks = [ plaintext[i:i + len(key)] for i in range(0, len(plaintext), len(key)) ] vector = iv cyphertext = b"" for block in blocks: block = fixed_xor(block, vector) block = cypher.encrypt(block) cyphertext += block vector = block return cyphertext
def aes_ctr(cyphertext: bytes, key: bytes, nonce: int = 0) -> bytes: c = [ cyphertext[16 * i:16 * (i + 1)] for i in range(0, len(cyphertext) // 16 + 1) ] cypher = AES.new(key, AES.MODE_ECB) message = b"" ctr = 0 for block in c: keystream = pack("<Qq", nonce, ctr) message += fixed_xor(cypher.encrypt(keystream)[:len(block)], block) ctr += 1 return message
def cbc_iv_key(cyphertext: bytes) -> Optional[bytes]: c_prime = cyphertext[:16] + bytes(16) + cyphertext[:16] p_prime = oracle(c_prime) if p_prime: return fixed_xor(p_prime[0:16], p_prime[32:48]) return None
def repeating_key_xor(key, message): key = (key * (int(len(message) / len(key)) + 1))[:len(message)] return fixed_xor(key, message)
def repeating_key_xor(key: bytes, message: bytes) -> bytes: key = (key * (int(len(message) / len(key)) + 1))[:len(message)] return fixed_xor(key, message)
def xor_everything(s: bytes) -> list[bytes]: return [fixed_xor(bytes([k] * len(s)), s) for k in range(256)]
def break_rarw(cyphertext: bytes) -> bytes: edited_cyphertext = edit(cyphertext, RANDOM_KEY, 0, bytes(len(cyphertext))) return fixed_xor(cyphertext, edited_cyphertext)
def xor_everything(s): return [fixed_xor(bytes([k] * len(s)), s) for k in range(256)]
def break_fixed_nonce_ctr(c): k = single_byte_xor_key(c) return [fixed_xor(cyphertext, k) for cyphertext in c]
def single_byte_xor_key(c: list[bytes]) -> bytes: c_t = transpose_bytes(c) return bytes([fixed_xor(break_single_byte_xor(x), x)[0] for x in c_t])
def break_fixed_nonce_ctr(c: list[bytes]) -> list[bytes]: k = single_byte_xor_key(c) return [fixed_xor(cyphertext, k[:len(cyphertext)]) for cyphertext in c]
def single_byte_xor_key(c): c_T = transpose_bytes(c) return bytes([fixed_xor(break_single_byte_xor(x), x)[0] for x in c_T])