Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ciphers/permutation_cipher.py. #9163

Merged
merged 18 commits into from
Oct 9, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions ciphers/permutation_cipher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
"""
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.edu/~christensen/1402%20permutation%20ciphers.pdf
"""
import random


def generate_valid_block_size(message_length: int) -> int:
"""
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
"""
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]:
"""
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]
"""
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]]:
"""
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'
"""
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:
"""
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'
"""
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:
"""
Driver function to pass message to get encrypted, then decrypted.

Example:
>>> main()
Decrypted message: HELLO WORLD
"""
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()