1
1
"""
2
2
3
3
Hill Cipher:
4
- The below defined class 'HillCipher' implements the Hill Cipher algorithm.
5
- The Hill Cipher is an algorithm that implements modern linear algebra techniques
6
- In this algorithm, you have an encryption key matrix. This is what will be used
7
- in encoding and decoding your text.
4
+ The 'HillCipher' class below implements the Hill Cipher algorithm which uses
5
+ modern linear algebra techniques to encode and decode text using an encryption
6
+ key matrix.
8
7
9
8
Algorithm:
10
9
Let the order of the encryption key be N (as it is a square matrix).
24
23
The determinant of the encryption key matrix must be relatively prime w.r.t 36.
25
24
26
25
Note:
27
- The algorithm implemented in this code considers only alphanumerics in the text.
28
- If the length of the text to be encrypted is not a multiple of the
29
- break key(the length of one batch of letters),the last character of the text
30
- is added to the text until the length of the text reaches a multiple of
31
- the break_key. So the text after decrypting might be a little different than
32
- the original text.
26
+ This implementation only considers alphanumerics in the text. If the length of
27
+ the text to be encrypted is not a multiple of the break key(the length of one
28
+ batch of letters),the last character of the text is added to the text until the
29
+ length of the text reaches a multiple of the break_key. So the text after
30
+ decrypting might be a little different than the original text.
33
31
34
32
References:
35
33
https://apprendre-en-ligne.net/crypto/hill/Hillciph.pdf
38
36
39
37
"""
40
38
39
+ from string import ascii_upper
41
40
import numpy
42
41
43
42
44
- def gcd (a : int , b : int ) -> int :
43
+ def greatest_common_divisor (a : int , b : int ) -> int :
45
44
"""
46
- >>> gcd (4, 8)
45
+ >>> greatest_common_divisor (4, 8)
47
46
4
48
- >>> gcd (8, 4)
47
+ >>> greatest_common_divisor (8, 4)
49
48
4
50
- >>> gcd (4, 7)
49
+ >>> greatest_common_divisor (4, 7)
51
50
1
52
- >>> gcd (0, 10)
51
+ >>> greatest_common_divisor (0, 10)
53
52
10
54
53
"""
55
- if a == 0 :
56
- return b
57
- return gcd (b % a , a )
54
+ return b if a == 0 else greatest_common_divisor (b % a , a )
58
55
59
56
60
57
class HillCipher :
61
- key_string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
58
+ key_string = string . ascii_uppercase + string . digits
62
59
# This cipher takes alphanumerics into account
63
60
# i.e. a total of 36 characters
64
61
65
62
# take x and return x % len(key_string)
66
63
modulus = numpy .vectorize (lambda x : x % 36 )
67
64
68
- toInt = numpy .vectorize (lambda x : round (x ))
65
+ to_int = numpy .vectorize (lambda x : round (x ))
69
66
70
67
def __init__ (self , encrypt_key ):
71
68
"""
@@ -76,22 +73,22 @@ def __init__(self, encrypt_key):
76
73
self .decrypt_key = None
77
74
self .break_key = encrypt_key .shape [0 ]
78
75
79
- def replaceLetters (self , letter : str ) -> int :
76
+ def replace_letters (self , letter : str ) -> int :
80
77
"""
81
78
>>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
82
- >>> hill_cipher.replaceLetters ('T')
79
+ >>> hill_cipher.replace_letters ('T')
83
80
19
84
- >>> hill_cipher.replaceLetters ('0')
81
+ >>> hill_cipher.replace_letters ('0')
85
82
26
86
83
"""
87
84
return self .key_string .index (letter )
88
85
89
- def replaceNumbers (self , num : int ) -> str :
86
+ def replace_digits (self , num : int ) -> str :
90
87
"""
91
88
>>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
92
- >>> hill_cipher.replaceNumbers (19)
89
+ >>> hill_cipher.replace_digits (19)
93
90
'T'
94
- >>> hill_cipher.replaceNumbers (26)
91
+ >>> hill_cipher.replace_digits (26)
95
92
'0'
96
93
"""
97
94
return self .key_string [round (num )]
@@ -107,7 +104,7 @@ def check_determinant(self) -> None:
107
104
det = det % len (self .key_string )
108
105
109
106
req_l = len (self .key_string )
110
- if gcd (det , len (self .key_string )) != 1 :
107
+ if greatest_common_divisor (det , len (self .key_string )) != 1 :
111
108
raise ValueError (
112
109
f"determinant modular { req_l } of encryption key({ det } ) is not co prime w.r.t { req_l } .\n Try another key."
113
110
)
@@ -118,8 +115,7 @@ def process_text(self, text: str) -> str:
118
115
>>> hill_cipher.process_text('Testing Hill Cipher')
119
116
'TESTINGHILLCIPHERR'
120
117
"""
121
- text = list (text .upper ())
122
- chars = [char for char in text if char in self .key_string ]
118
+ chars = [char for char in text .upper () if char in self .key_string ]
123
119
124
120
last = chars [- 1 ]
125
121
while len (chars ) % self .break_key != 0 :
@@ -138,13 +134,13 @@ def encrypt(self, text: str) -> str:
138
134
139
135
for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
140
136
batch = text [i : i + self .break_key ]
141
- batch_vec = [self .replaceLetters (char ) for char in batch ]
137
+ batch_vec = [self .replace_letters (char ) for char in batch ]
142
138
batch_vec = numpy .matrix ([batch_vec ]).T
143
139
batch_encrypted = self .modulus (self .encrypt_key .dot (batch_vec )).T .tolist ()[
144
140
0
145
141
]
146
142
encrypted_batch = "" .join (
147
- self .replaceNumbers (num ) for num in batch_encrypted
143
+ self .replace_digits (num ) for num in batch_encrypted
148
144
)
149
145
encrypted += encrypted_batch
150
146
@@ -173,7 +169,7 @@ def make_decrypt_key(self):
173
169
* numpy .linalg .inv (self .encrypt_key )
174
170
)
175
171
176
- return self .toInt (self .modulus (inv_key ))
172
+ return self .to_int (self .modulus (inv_key ))
177
173
178
174
def decrypt (self , text : str ) -> str :
179
175
"""
@@ -187,13 +183,13 @@ def decrypt(self, text: str) -> str:
187
183
188
184
for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
189
185
batch = text [i : i + self .break_key ]
190
- batch_vec = [self .replaceLetters (char ) for char in batch ]
186
+ batch_vec = [self .replace_letters (char ) for char in batch ]
191
187
batch_vec = numpy .matrix ([batch_vec ]).T
192
188
batch_decrypted = self .modulus (self .decrypt_key .dot (batch_vec )).T .tolist ()[
193
189
0
194
190
]
195
191
decrypted_batch = "" .join (
196
- self .replaceNumbers (num ) for num in batch_decrypted
192
+ self .replace_digits (num ) for num in batch_decrypted
197
193
)
198
194
decrypted += decrypted_batch
199
195
@@ -206,19 +202,13 @@ def main():
206
202
207
203
print ("Enter each row of the encryption key with space separated integers" )
208
204
for i in range (N ):
209
- row = list ( map ( int , input ().split ()))
205
+ row = [ int ( x ) for x in input ().split ()]
210
206
hill_matrix .append (row )
211
207
212
208
hc = HillCipher (numpy .matrix (hill_matrix ))
213
209
214
210
print ("Would you like to encrypt or decrypt some text? (1 or 2)" )
215
- option = input (
216
- """
217
- 1. Encrypt
218
- 2. Decrypt
219
- """
220
- )
221
-
211
+ option = input ("\n 1. Encrypt\n 2. Decrypt\n " )
222
212
if option == "1" :
223
213
text_e = input ("What text would you like to encrypt?: " )
224
214
print ("Your encrypted text is:" )
0 commit comments