def test_challenge26(self): key = crypto.encryption_key() nonce = '\x00' * 8 ciphertext = encrypt_kvps('admin=true', key, nonce) self.assertFalse(is_admin(ciphertext, key, nonce), 'Should have escaped the =') # 0 1 2 3 4 # comment1=cooking%20MCs;userdata=true;comment2=%20like%20a%20pound%20of%20bacon # comment1=cooking%20MCs;us;admin=true;comment2=%20like%20a%20pound%20of%20bacon ciphertext = encrypt_kvps('true', key, nonce) self.assertFalse(is_admin(ciphertext, key, nonce)) # ciphertext ^ orig plaintext = keystream # keystream ^ (desired plaintext) = new ciphertext to splice in start = 25 orig_plaintext = 'erdata' keystream = bitops.xor(ciphertext[start:start + len(orig_plaintext)], orig_plaintext) tampered_chunk = bitops.xor(keystream, ';admin') tampered_ciphertext = (ciphertext[:start] + tampered_chunk + ciphertext[start + len(tampered_chunk):]) self.assertTrue(is_admin(tampered_ciphertext, key, nonce))
def hmac(key, message): padding_length = sha1.Sha1Hash.block_size - len(key) if padding_length > 0: key += '\x00' * padding_length else: key = sha1.Sha1Hash().update(key).digest() full_message = ( xor(key, '\x5c' * len(key)) + sha1.Sha1Hash().update(xor(key, '\x36' * len(key))).digest() + message) return sha1.Sha1Hash().update(full_message).digest()
def set_plaintext_char(self, index, pos, plaintext_val): if plaintext_val is None: self.keystream[pos] = None else: self.keystream[pos] = xor( self.ciphertexts[index][pos], plaintext_val )
def test_challenge2(self): hex_input1 = '1c0111001f010100061a024b53535009181c' hex_input2 = '686974207468652062756c6c277320657965' xor_result = xor(hex_to_bytes(hex_input1), hex_to_bytes(hex_input2)) self.assertEquals( '746865206b696420646f6e277420706c6179', bytes_to_hex(xor_result) )
def test_challenge20(self): plaintexts = [ base64_to_bytes(line) for line in utils.readlines('20.txt') ] key = encryption_key() nonce = '\0' * 8 ciphertexts = [ ctr_encrypt(m, key, nonce) for m in plaintexts ] # Because of the fixed-nonce, the encrypted keystream bytes are # repeated for every plaintext message. # # ciphertext[i] ^ keystream[i] = plaintext[i] # # We can create a transposed ciphertext message by concatenating # ciphertext[i] from every encrypted message and then xor'ing that # against a guessed keystream byte. Then we can test whether the # resulting plaintext looks like english based on character # distributions. If so, then we've figured out the keystream byte. keystream = '' for index in itertools.count(): transposed = ''.join(m[index:index+1] for m in ciphertexts) if not transposed: break allowed_chars = None if index == 0: allowed_chars = string.ascii_uppercase + '"\'' score, _, key = crack.find_best_single_byte_key( transposed, allowed_chars=allowed_chars ) # print 'Best score for index {}: {}'.format(index, score) keystream += key[0] recovered_plaintexts = [ bitops.xor(m, keystream) for m in ciphertexts ] # for m in recovered_plaintexts: # print m self.assertIn( '\'Cause my girl is definitely mad / \'Cause it took us too long to do this album', recovered_plaintexts )
def cbc_decrypt(ciphertext, key, iv): backend = default_backend() cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend) decryptor = cipher.decryptor() plaintext = '' block_size = len(iv) for start in range(0, len(ciphertext), block_size): end = start + block_size ciphertext_block = ciphertext[start:end] plaintext += xor(iv, decryptor.update(ciphertext_block)) iv = ciphertext_block return strip_pkcs_7(plaintext)
def find_best_single_byte_key(rawbytes, freq_vector=None, allowed_chars=None): best_key = None best_translation = None best_score = -1.0 for key in single_byte_keys(len(rawbytes)): translation = xor(rawbytes, key) score = english_score(translation, freq_vector=freq_vector, allowed_chars=allowed_chars) if score > best_score: best_score = score best_key = key best_translation = translation return (best_score, best_translation, best_key)
def cbc_encrypt(plaintext, key, iv): backend = default_backend() cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend) encryptor = cipher.encryptor() padded = pkcs_7(plaintext, len(iv)) ciphertext = '' for start in range(0, len(padded), len(iv)): end = start + len(iv) scrambled = xor(iv, padded[start:end]) ciphertext_block = encryptor.update(scrambled) ciphertext += ciphertext_block iv = ciphertext_block return ciphertext
def test_challenge25(self): ciphertext = convert.base64_to_bytes(utils.read('25.txt')) plaintext = crypto.ecb_decrypt(ciphertext, 'YELLOW SUBMARINE') key = crypto.encryption_key() nonce = '\x00' * 8 ciphertext = crypto.ctr_encrypt(plaintext, key, nonce) # The edit function lets us recover the original keystream because # in CTR mode: plaintext ^ keystream = ciphertext and then through # the magic of xor: # # plaintext ^ ciphertext = keystream # # Through the edit function, we know the plaintext and ciphertext # for every index in the byte stream. keystream = '' for offset in range(len(ciphertext)): new_ciphertext = edit(ciphertext, key, nonce, offset, 'A') keystream += bitops.xor(new_ciphertext[offset], 'A') recovered_plaintext = bitops.xor(ciphertext, keystream) self.assertEquals(plaintext, recovered_plaintext)
def test_challenge27(self): key = crypto.encryption_key() ciphertext = encrypt_kvps_cbc('hello', key) tampered = (ciphertext[:16] + ('\x00' * 16) + ciphertext[:16] + ciphertext[16:]) try: is_admin_cbc(tampered, key) except ValueError as e: expected_prefix = 'Invalid message ' plaintext = e.message[len(expected_prefix):] # C1 = 0 # P2 = D(E(P0 ^ IV)) ^ C1 # P2 = P0 ^ IV ^ 0 # P2 = P0 ^ IV # The first block in the recovered plaintext is P0 and iv = key so: # P0 ^ P2 = IV = KEY # # The 3rd plaintext block is # D(E(P0 ^ IV)) ^ 0 recovered_key = bitops.xor(plaintext[:16], plaintext[32:48]) self.assertEqual(key, recovered_key)
def test_xor_trim_to_shortest(self): self.assertEqual('\x00', bitops.xor('\x01', '\x01\x02'))
def _decrypt_char(self, index, ciphertext_val): if self.keystream[index] is None: return None else: return xor(self.keystream[index], ciphertext_val)
def twister_convert(text, key): r = myrandom.MT19937(key & 0xffff) keystream = (chr(r.next() & 0xff) for _ in itertools.count()) return ''.join(xor(a, b) for a, b in zip(text, keystream))
def ctr_convert(text, key, nonce): blocks = (text[s:(s + 16)] for s in range(0, len(text), 16)) return ''.join( xor(a, b) for a, b in zip(blocks, _ctr_keystream(key, nonce)))