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 test_challenge18(self): ciphertext = base64_to_bytes('L77na/nrFsKvynd6HzOoG7GHTLXsTVu9qvY/2syLXzhPweyyMTJULu/6/kXX0KSvoOLSFQ==') nonce = '\x00' * 8 plaintext = ctr_decrypt(ciphertext, 'YELLOW SUBMARINE', nonce) self.assertEqual( 'Yo, VIP Let\'s kick it Ice, Ice, baby Ice, Ice, baby ', plaintext ) plaintext = 'the quick brown fox jumped over the lazy dog.' key = encryption_key() ciphertext = ctr_encrypt(plaintext, key, nonce) self.assertEqual(plaintext, ctr_decrypt(ciphertext, key, nonce))
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)
'U28gZGFyaW5nIGFuZCBzd2VldCBoaXMgdGhvdWdodC4=', 'VGhpcyBvdGhlciBtYW4gSSBoYWQgZHJlYW1lZA==', 'QSBkcnVua2VuLCB2YWluLWdsb3Jpb3VzIGxvdXQu', 'SGUgaGFkIGRvbmUgbW9zdCBiaXR0ZXIgd3Jvbmc=', 'VG8gc29tZSB3aG8gYXJlIG5lYXIgbXkgaGVhcnQs', 'WWV0IEkgbnVtYmVyIGhpbSBpbiB0aGUgc29uZzs=', 'SGUsIHRvbywgaGFzIHJlc2lnbmVkIGhpcyBwYXJ0', 'SW4gdGhlIGNhc3VhbCBjb21lZHk7', 'SGUsIHRvbywgaGFzIGJlZW4gY2hhbmdlZCBpbiBoaXMgdHVybiw=', 'VHJhbnNmb3JtZWQgdXR0ZXJseTo=', 'QSB0ZXJyaWJsZSBiZWF1dHkgaXMgYm9ybi4=', ] ] key = encryption_key() nonce = '\x00' * 8 ciphertexts = [ ctr_encrypt(m, key, nonce) for m in plaintexts ] translations = Translations(ciphertexts) view = TranslationsView(translations) curses.wrapper(view.event_loop) # Answer: Easter 1916 by Yeats # Set the line third from the bottom to: # # He, too, has been changed in his turn, # # To generate the complete keystream
def encrypt_kvps(content, key, nonce): full_content = ('comment1=cooking%20MCs;userdata=' + urllib.quote(content) + ';comment2=%20like%20a%20pound%20of%20bacon') return crypto.ctr_encrypt(full_content, key, nonce)
def edit(ciphertext, key, nonce, offset, plaintext): orig_plaintext = crypto.ctr_decrypt(ciphertext, key, nonce) new_plaintext = (orig_plaintext[:offset] + plaintext + orig_plaintext[offset + len(plaintext):]) return crypto.ctr_encrypt(new_plaintext, key, nonce)