mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-27 15:01:08 +00:00
Added ciphers/permutation_cipher.py. (#9163)
* Added permutation_cipher.py * Added type hints for parameters * Added doctest in functions * Update ciphers/permutation_cipher.py Ya i felt the same but held back because there is a implementation of transposition_cipher.py. But that's is different from the one i have implemented here. Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com> * Update ciphers/permutation_cipher.py Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com> * Update ciphers/permutation_cipher.py Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com> * Update ciphers/permutation_cipher.py Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com> * Update ciphers/permutation_cipher.py Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com> * Update ciphers/permutation_cipher.py Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com> * Update ciphers/permutation_cipher.py Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Changes --------- Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
844270c6e9
commit
5d0a46814e
142
ciphers/permutation_cipher.py
Normal file
142
ciphers/permutation_cipher.py
Normal file
|
@ -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()
|
Loading…
Reference in New Issue
Block a user