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 4 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
132 changes: 132 additions & 0 deletions ciphers/permutation_cipher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
"""
The Permutation Cipher, implemented above, 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 read:-
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:
generate_valid_block_size(12)
3
"""
while True:
block_size = random.randint(2, message_length)
if message_length % block_size == 0:
return block_size


def generate_permutation_key(block_size: int) -> list:
"""
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:
generate_permutation_key(4)
[3, 4, 1, 2]
"""
digits = list(range(1, block_size + 1))
random.shuffle(digits)
key = digits
return key


def encrypt(
message: str, key: list | None = None, block_size: int | None = None
) -> tuple:
"""
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 - 1] for digit in key]
encrypted_message += "".join(rearranged_block)

return encrypted_message, key


def decrypt(encrypted_message: str, key: list) -> 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 - 1] = 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()