def hmac(key, msg): """ https: // en.wikipedia.org / wiki / HMAC sha256 is SHA-2 """ sha2_block_size, sha2_output_size = 64, 32 if len(key) > sha2_block_size: key = hashlib.sha256().update(key).digest() if len(key) < sha2_block_size: key = key + bytes([0] * (sha2_block_size - len(key))) o_key_pad = xor(key, bytes([0x5c] * sha2_block_size)) i_key_pad = xor(key, bytes([0x36] * sha2_block_size)) h1 = hashlib.sha256(i_key_pad + msg).digest() return hashlib.sha256(o_key_pad + h1).digest()
def ctr_edit_decrypt(ct, edit): """ Attacker has an edit function and a ct. To recover the plaintext, we xor our specified pt with the associated edited ct to get E(nonce..counter) for that given block. Then E(nonce..counter) ^ original ct gives us the original pt. """ pt = b"" attacker_pt = bytes([0]*16) for i in range(0, len(ct), 16): new_ct = edit(ct, i, attacker_pt) e = xor(new_ct[i:i+16], attacker_pt) pt_block = xor(e, ct[i:i+16]) pt += pt_block return pt
def decrypt_aes_cbc(key, msg, block_size, iv): ct_prev, pt, i = iv, bytearray([]), 0 while i < len(msg) / block_size: ct = msg[i * block_size:(i + 1) * block_size] pt.extend(xor(ct_prev, decrypt_aes_ecb(key, ct))) ct_prev = ct i += 1 return bytes(pt)
def approach2(cts): # Break this exactly like a repeating xor # Concatenate the first byte from every key = b"" for i in range(min([len(c) for c in cts])): b = bytes(list(map(lambda x: x[i], cts))) _, _, k = decrypt_single_byte_xor(bytes.hex(b)) key += bytes([k]) return [xor(key, c[:len(key)]) for c in cts]
def encrypt_aes_cbc(key, msg, block_size, iv): msg = pad_pkcs7(msg, block_size) ct_prev, ct, i = iv, [], 0 while i < len(msg) / block_size: ct_block = encrypt_aes_ecb( key, xor(ct_prev, msg[i * block_size:(i + 1) * block_size])) ct.append(ct_block) ct_prev = ct_block i += 1 return b''.join(ct)
"SW4gdGhlIGNhc3VhbCBjb21lZHk7", "SGUsIHRvbywgaGFzIGJlZW4gY2hhbmdlZCBpbiBoaXMgdHVybiw=", "VHJhbnNmb3JtZWQgdXR0ZXJseTo=", "QSB0ZXJyaWJsZSBiZWF1dHkgaXMgYm9ybi4=" ] # Because we re-used the nonce in CTR mode, we should be able to # break all these ciphertexts. k = bytes([randrange(255) for _ in range(16)]) pts = [base64.decodebytes(bytes(i, 'utf-8')) for i in inputs] print(pts) cts = [ encrypt_ctr(base64.decodebytes(bytes(i, 'utf-8')), k, 0) for i in inputs ] # Try all possible 255 bytes for the first byte of the keystream # and xor it with the first byte of every cipher text. If thats an ascii character or space # then its very likely to be the keybyte. common_key = b"" for i in range(max([len(c) for c in cts])): common_key += bytes([best_k(cts, i)]) print(common_key) for c in cts: print(str(xor(common_key[:len(c)], c), 'utf-8')) with open('20.txt') as f: cts = [ encrypt_ctr(base64.decodebytes(bytes(i, 'utf-8')), k, 0) for i in f.readlines() ] for p in approach2(cts): print(p)
def encrypt(input): if b';' in input or b'=' in input: raise Exception("invalid characters") for b in input: if b > 128: raise Exception("non-ascii byte", b) # prefix = b"comment1=cooking%20MCs;userdata=" # # postfix = b";comment2=%20like%20a%20pound%20of%20bacon" return encrypt_aes_cbc(key, input, 16, key) def decrypt(ct): pt = decrypt_aes_cbc(key, ct, 16, key) non_ascii_byte = False for i, b in enumerate(pt): if b > 128: non_ascii_byte = True if non_ascii_byte: return errmsg + pt return pt if __name__ == "__main__": pt = b"yellow submarineyellow submarineyellow submarine" ct = encrypt(pt) # 3 blocks encrypted ct_modified = ct[:16] + bytes([0] * 16) + ct[:16] pt_t = decrypt(ct_modified) pt_t = pt_t[len(errmsg):] recovered_key = xor(b"yellow submarine", pt_t[16 * 2:16 * 3]) assert (key == recovered_key)