예제 #1
0
    def test_challenge16(self):
        key = encryption_key()
        init_vector = iv()
        ciphertext = encrypt_kvps('admin=true', key, init_vector)
        self.assertFalse(
            is_admin(ciphertext, key, init_vector),
            '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, init_vector)

        # Block 0 ciphertext is XOR'ed against the decryptor output of block 1
        # to produce the plaintext output. Twiddling a bit in the block 0
        # ciphertext will invert the corresponding bit in the block 1 plaintext
        combined = zip(
            ciphertext[:16],
            '%20MCs;userdata=',
            '%20MCs;us;admin='
        )

        twiddled = ''.join(
            twiddle_bits(iv_byte, actual_char, target_char)
            for iv_byte, actual_char, target_char in combined
        )

        ciphertext = twiddled + ciphertext[16:]

        self.assertTrue(is_admin(ciphertext, key, init_vector))
예제 #2
0
    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))
예제 #3
0
    def test_challenge13(self):
        # email=ryan%40rescale.com&uid=10&role=user

        # Generate some ciphertext where the 2nd block starts with admin
        # 0               1               2               3
        # email=AAAAAAAAAAadmin&uid=10&role=user
        key = encryption_key()
        ciphertext = encrypt_profile('AAAAAAAAAAadmin', key)
        admin_block = ciphertext[16:32]

        # print ecb_decrypt(admin_block, key)
        # Provide an email address that causes a cipher block to end with role=
        # 0               1               2               3
        # email=ryan%2BAAAAAAAAAA%40gmail.com&uid=10&role=user
        ciphertext = encrypt_profile('*****@*****.**', key)
        prefix_length = 16 * 3
        prefix = ciphertext[:prefix_length]
        suffix = ciphertext[prefix_length:]

        # Then paste the admin block to the end of that
        # Tack on the original last block to prevent two role qs args from
        # being created.
        manipulated = prefix + admin_block + suffix
        profile = decrypt_profile(manipulated, key)

        self.assertIn('admin', profile['role'])
예제 #4
0
def multimode_oracle(plaintext, mode):
    tampered = random_padding() + plaintext + random_padding()

    key = encryption_key()

    if mode == 'ecb':
        return ecb_encrypt(tampered, key)
    elif mode == 'cbc':
        return cbc_encrypt(tampered, key, iv())

    raise ValueError('Unknown mode')
예제 #5
0
    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
        )
예제 #6
0
    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))
예제 #7
0
    def test_challenge17(self):
        key = encryption_key()
        ciphertext, init_vector = encrypt_random_choice(key)

        block_size = len(init_vector)
        blocks = (
            ciphertext[i:(i+block_size)]
            for i in range(0, len(ciphertext), block_size)
        )

        prev_block = init_vector
        plaintext = ''
        for block in blocks:
            plaintext_block = [0] * block_size

            for i in reversed(range(block_size)):
                padding_value = block_size - i

                # Generate a tampered_iv that will produce a plaintext block
                # that is properly pkcs7-padded with padding_value
                tampered_iv = [0] * block_size

                for j in range(i + 1, block_size):
                    tampered_iv[j] = (
                        ord(prev_block[j]) ^ plaintext_block[j] ^ padding_value
                    )

                # Use the oracle to find the tampered iv value for block
                # position i that will produce padding_value in the plaintext
                for modified in range(256):
                    tampered_iv[i] = modified
                    if is_padding_valid(block, key, ''.join(chr(c) for c in tampered_iv)):
                        break

                # Now the plaintext for index i can be calculated:
                # Pi = prev_block[i] ^ modified ^ padding_value
                plaintext_block[i] = (
                    ord(prev_block[i]) ^ modified ^ padding_value
                )

            plaintext += ''.join(chr(c) for c in plaintext_block)
            prev_block = block

        plaintext = strip_pkcs_7(plaintext)
        # print plaintext
        self.assertIn(plaintext, secret_messages)
예제 #8
0
    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)
예제 #9
0
    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)
예제 #10
0
            'U28gc2Vuc2l0aXZlIGhpcyBuYXR1cmUgc2VlbWVkLA==',
            '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,
    #