the permutation cipher also called the transposition cipher is a simple encryption technique that rearranges the characters in a message based on a secret key it divides the message into blocks and applies a permutation to the characters within each block according to the key the key is a sequence of unique integers that determine the order of character rearrangement for more info https www nku educhristensen140220permutation20ciphers pdf generate a valid block size that is a factor of the message length args messagelength int the length of the message returns int a valid block size example random seed1 generatevalidblocksize12 3 generate a random permutation key of a specified block size args blocksize int the size of each permutation block returns listint a list containing a random permutation of digits example random seed0 generatepermutationkey4 2 0 1 3 encrypt a message using a permutation cipher with block rearrangement using a key args message str the plaintext message to be encrypted key listint the permutation key for decryption blocksize int the size of each permutation block returns tuple a tuple containing the encrypted message and the encryption key example encryptedmessage key encrypthello world decryptedmessage decryptencryptedmessage key decryptedmessage hello world decrypt an encrypted message using a permutation cipher with block rearrangement args encryptedmessage str the encrypted message key listint the permutation key for decryption returns str the decrypted plaintext message example encryptedmessage key encrypthello world decryptedmessage decryptencryptedmessage key decryptedmessage hello world driver function to pass message to get encrypted then decrypted example main decrypted message hello world generate a valid block size that is a factor of the message length args message_length int the length of the message returns int a valid block size example random seed 1 generate_valid_block_size 12 3 generate a random permutation key of a specified block size args block_size int the size of each permutation block returns list int a list containing a random permutation of digits example random seed 0 generate_permutation_key 4 2 0 1 3 encrypt a message using a permutation cipher with block rearrangement using a key args message str the plaintext message to be encrypted key list int the permutation key for decryption block_size int the size of each permutation block returns tuple a tuple containing the encrypted message and the encryption key example encrypted_message key encrypt hello world decrypted_message decrypt encrypted_message key decrypted_message hello world decrypt an encrypted message using a permutation cipher with block rearrangement args encrypted_message str the encrypted message key list int the permutation key for decryption returns str the decrypted plaintext message example encrypted_message key encrypt hello world decrypted_message decrypt encrypted_message key decrypted_message hello world driver function to pass message to get encrypted then decrypted example main decrypted message hello world
import random


def generate_valid_block_size(message_length: int) -> int:
    
    block_sizes = [
        block_size
        for block_size in range(2, message_length + 1)
        if message_length % block_size == 0
    ]
    return random.choice(block_sizes)


def generate_permutation_key(block_size: int) -> list[int]:
    
    digits = list(range(block_size))
    random.shuffle(digits)
    return digits


def encrypt(
    message: str, key: list[int] | None = None, block_size: int | None = None
) -> tuple[str, list[int]]:
    
    message = message.upper()
    message_length = len(message)

    if key is None or block_size is None:
        block_size = generate_valid_block_size(message_length)
        key = generate_permutation_key(block_size)

    encrypted_message = ""

    for i in range(0, message_length, block_size):
        block = message[i : i + block_size]
        rearranged_block = [block[digit] for digit in key]
        encrypted_message += "".join(rearranged_block)

    return encrypted_message, key


def decrypt(encrypted_message: str, key: list[int]) -> str:
    
    key_length = len(key)
    decrypted_message = ""

    for i in range(0, len(encrypted_message), key_length):
        block = encrypted_message[i : i + key_length]
        original_block = [""] * key_length
        for j, digit in enumerate(key):
            original_block[digit] = block[j]
        decrypted_message += "".join(original_block)

    return decrypted_message


def main() -> None:
    
    message = "HELLO WORLD"
    encrypted_message, key = encrypt(message)

    decrypted_message = decrypt(encrypted_message, key)
    print(f"Decrypted message: {decrypted_message}")


if __name__ == "__main__":
    import doctest

    doctest.testmod()
    main()