def test_swap_methods(self):
        plugboard = enigma.Swapper(n_positions=10)

        # element swap
        plugboard.set_element_swap(3, 7)
        self.assertEqual(plugboard.get_output(3), 7)
        self.assertEqual(plugboard.get_output(7), 3)
        self.assertEqual(plugboard.get_output(2), 2)

        # swap move
        plugboard.move_one_swap_side(7, 8)
        self.assertEqual(plugboard.get_output(3), 8)
        self.assertEqual(plugboard.get_output(8), 3)
        self.assertEqual(plugboard.get_output(7), 7)
        self.assertEqual(plugboard.get_output(2), 2)

        with self.assertRaises(ValueError):
            plugboard.move_one_swap_side(2, 3)

        plugboard.set_element_swap(2, 1)
        with self.assertRaises(ValueError):
            plugboard.move_one_swap_side(8, 2)

        # swap remove
        plugboard.unset_element_swap(3, 8)
        self.assertEqual(plugboard.get_output(7), 7)
        self.assertEqual(plugboard.get_output(8), 8)
    def setup_enigma_and_msg(self, len_msg, n_rotors, n_plugs):
        self.charset = string.ascii_lowercase
        self.n_chars = len(self.charset)

        self.reflector = enigma.Swapper(n_positions=self.n_chars)
        self.reflector.assign_random_swaps(n_swaps=self.n_chars // 2, seed=3)

        rotor_seeds = list(range(n_rotors))
        self.rotors = [
            enigma.Rotor(n_positions=self.n_chars, seed=seed) for seed in rotor_seeds
        ]
        self.rotor_positions = n_rotors * [15]

        self.plugboard = enigma.Swapper(n_positions=self.n_chars)
        self.plugboard.assign_random_swaps(n_swaps=n_plugs, seed=41)

        message = (
            "The Enigma machine is a cipher device developed and used in the early- to mid-20th century to protect"
            " commercial, diplomatic, and military communication. It was employed extensively by Nazi Germany during "
            "World War II, in all branches of the German military. The Germans believed, erroneously, that use of the"
            " Enigma machine enabled them to communicate securely and thus enjoy a huge advantage in World War II. "
            "The Enigma machine was considered to be so secure that even the most top-secret messages were enciphered"
            " on its electrical circuits. Enigma has an electromechanical rotor mechanism that scrambles the 26 letters "
            "of the alphabet. In typical use, one person enters text on the Enigma's keyboard and another person writes"
            " down which of 26 lights above the keyboard lights up at each key press. If plain text is entered, the "
            "lit-up letters are the encoded ciphertext. Entering ciphertext transforms it back into readable plaintext. "
            "The rotor mechanism changes the electrical connections between the keys and the lights with each keypress. "
            "The security of the system depends on a set of machine settings that were generally changed daily during the "
            "war, based on secret key lists distributed in advance, and on other settings that were changed for "
            "each message. The receiving station has to know and use the exact settings employed by the transmitting "
            "station to successfully decrypt a message."
        )
        message = message.lower()
        self.message_full = "".join(c for c in message if c.islower())

        encoder = enigma.Enigma(
            self.rotors, self.plugboard, self.reflector, charset=self.charset
        )
        encoder.set_rotor_positions(self.rotor_positions)
        self.message = self.message_full[:len_msg]
        self.encrypted_message = encoder.encode_message(self.message)

        # reset all rotors so MC does not start with a good value
        encoder.set_rotor_positions(n_rotors * [0])
    def test_plugboard_random_swaps(self):
        plugboard = enigma.Swapper(n_positions=10)
        in_1 = 5
        out_1 = plugboard.get_output(in_1)
        self.assertEqual(in_1, out_1)

        plugboard.assign_random_swaps(n_swaps=5, seed=42)
        in_0 = 3
        out_0 = plugboard.get_output(in_0)
        self.assertNotEqual(in_0, out_0)
        self.assertEqual(in_0, plugboard.get_output(out_0))
    def test_encrypt_decrypt(self):
        plugboard = enigma.Swapper(n_positions=self.n_chars)
        plugboard.assign_random_swaps(n_swaps=10, seed=41)
        rotor_seeds = [21, 32, 34]
        rotors = [
            enigma.Rotor(n_positions=self.n_chars, seed=seed) for seed in rotor_seeds
        ]
        reflector = enigma.Swapper(n_positions=self.n_chars)
        reflector.assign_random_swaps(n_swaps=self.n_chars // 2, seed=3)

        encoder = enigma.Enigma(rotors, plugboard, reflector, charset=self.charset)
        rotor_positions = [3, 4, 7]

        encoder.set_rotor_positions(rotor_positions)
        encoded_message = encoder.encode_message(self.test_message)
        self.assertNotEqual(encoded_message, self.test_message)

        encoder.set_rotor_positions(rotor_positions)
        decoded_message = encoder.encode_message(encoded_message)
        self.assertEqual(decoded_message, self.test_message)
import time
import string
import random
import tqdm

import enigma

n_messages = 3000
chars_per_message = 256
charset = string.ascii_uppercase
n_chars = len(charset)

plugboard = enigma.Swapper(n_positions=n_chars, n_swaps=10, seed=41)
rotor_seeds = [21, 32, 34]
rotors = [enigma.Rotor(n_positions=n_chars, seed=seed) for seed in rotor_seeds]
reflector = enigma.Swapper(n_positions=n_chars, n_swaps=n_chars // 2, seed=3)

encoder = enigma.Enigma(rotors, plugboard, reflector, charset=charset)
rotor_positions = [3, 4, 7]

messages = [
    "".join(random.choices(charset, k=chars_per_message))
    for _ in range(n_messages)
]
tick = time.time()
for message in tqdm.tqdm(messages):
    encoder.set_rotor_positions(rotor_positions)
    encoded_message = encoder.encode_message(message)
tock = time.time()

avg_time = (tock - tick) / n_messages
Esempio n. 6
0
def decode_message_successive_best(
    encrypted_message,
    rotors: list,
    n_plugs,
    reflector: enigma.Swapper,
    scorer: TextScorerBase,
    charset=string.ascii_lowercase,
    disable_tqdm=False,
):
    n_chars = rotors[0].n_positions
    # test encoder knows the machine
    decoder_plugboard = enigma.Swapper(n_positions=n_chars)
    decoder_enigma = enigma.Enigma(
        copy.deepcopy(rotors),
        decoder_plugboard,
        copy.deepcopy(reflector),
        charset=charset,
    )

    # go through all positions and get the score of the output text
    highscore = -np.inf
    best_pos = decoder_enigma.get_rotor_positions()

    # TODO Parallel?
    positions = iter(MultiindexIiterator(len(rotors), n_chars))
    for pos in tqdm.tqdm(positions, disable=disable_tqdm):
        decoder_enigma.set_rotor_positions(pos)
        decoder_try = decoder_enigma.encode_message(encrypted_message)
        score = scorer.score_text(decoder_try)
        if score > highscore:
            highscore = score
            best_pos = pos

    # decode the plugboard
    # we have 10 plugs to distribute
    available_plug_positions = list(range(n_chars))
    for i in tqdm.tqdm(range(n_plugs), disable=disable_tqdm):
        highscore = -np.inf
        best_swap = (0, 1)
        # go through all positions for the plug and get their score
        # TODO loop parallel?
        for first in available_plug_positions:
            for second in available_plug_positions:
                # we have to set a plug, self-connections are not allowed
                if first == second:
                    continue
                decoder_plugboard.set_element_swap(first, second)
                decoder_enigma.set_rotor_positions(best_pos)
                decoder_try = decoder_enigma.encode_message(encrypted_message)
                score = scorer.score_text(decoder_try)
                if score > highscore:
                    highscore = score
                    best_swap = (first, second)
                decoder_plugboard.unset_element_swap(first, second)

        # use the best swap for further decrypting
        decoder_plugboard.set_element_swap(best_swap[0], best_swap[1])
        available_plug_positions.remove(best_swap[0])
        available_plug_positions.remove(best_swap[1])

    decoder_enigma.set_rotor_positions(best_pos)
    decoded_msg = decoder_enigma.encode_message(encrypted_message)

    return decoded_msg, best_pos, decoder_plugboard
Esempio n. 7
0
def decode_message_MC(
    encrypted_message,
    rotors: list,
    n_plugs: int,
    reflector: enigma.Swapper,
    scorer: TextScorerBase,
    score_scale: float = 1,
    n_attempts_per_block=1000,
    max_n_blocks=100,
    charset=string.ascii_lowercase,
):
    n_chars = rotors[0].n_positions
    # test encoder knows the machine
    decoder_plugboard = enigma.Swapper(n_positions=n_chars)
    decoder_plugboard.assign_random_swaps(n_swaps=n_plugs)
    decoder_enigma = enigma.Enigma(
        copy.deepcopy(rotors),
        decoder_plugboard,
        copy.deepcopy(reflector),
        charset=charset,
    )

    last_rotor_positions = decoder_enigma.get_rotor_positions()
    last_dec_msg = decoder_enigma.encode_message(encrypted_message)
    last_score = scorer.score_text(last_dec_msg)

    rng = np.random.default_rng(42)

    last_block_avg_score = -np.inf

    for i in range(max_n_blocks):
        block_scores = list()
        block_accepted_rot = 0
        block_accepted_plug = 0
        for _ in range(n_attempts_per_block):

            # rotor move
            prop_rot_pos = _propose_rot_move(last_rotor_positions, n_chars,
                                             rng)
            decoder_enigma.set_rotor_positions(prop_rot_pos)
            accept, new_score = _assess_move(decoder_enigma, encrypted_message,
                                             scorer, last_score, score_scale,
                                             rng)
            if accept:
                block_accepted_rot += 1
                last_score = new_score
                last_rotor_positions = prop_rot_pos

            if n_plugs > 0:
                # plugboard move
                prop_plug_move = _propose_plug_move(decoder_plugboard, rng)
                decoder_plugboard.move_one_swap_side(prop_plug_move[0],
                                                     prop_plug_move[1])
                accept, new_score = _assess_move(
                    decoder_enigma,
                    encrypted_message,
                    scorer,
                    last_score,
                    score_scale,
                    rng,
                )
                if accept:
                    last_score = new_score
                    block_accepted_plug += 1
                else:
                    # undo the move
                    decoder_plugboard.move_one_swap_side(
                        prop_plug_move[1], prop_plug_move[0])

            block_scores.append(last_score)

        block_avg_score = np.mean(block_scores)
        if (abs(
            (block_avg_score - last_block_avg_score) / last_block_avg_score) <
                0.0001):
            break
        last_block_avg_score = block_avg_score
        # print(f'avg score {last_block_avg_score}')
        # print(f'acceptance rate rot {block_accepted_rot / n_attempts_per_block}')
        # print(f'acceptance rate plug {block_accepted_plug / n_attempts_per_block}')

    # use the final settings to return
    decoded_msg = decoder_enigma.encode_message(encrypted_message)

    return decoded_msg, last_rotor_positions, decoder_plugboard