forked from TheAlgorithms/Python
-
Notifications
You must be signed in to change notification settings - Fork 0
/
permutation_cipher.py
143 lines (108 loc) · 3.96 KB
/
permutation_cipher.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
"""
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()