Last active
December 2, 2021 09:34
-
-
Save avi-arora/4e91e6625a300f7f0a2ad488a5049edd to your computer and use it in GitHub Desktop.
Hill Cipher
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np | |
from sympy import Matrix | |
CHAR_TABLE = {chr(i+97): i for i in range(0,26)} | |
CHAR_TABLE_REV = dict(zip(CHAR_TABLE.values(), CHAR_TABLE.keys())) | |
def program(): | |
#take user input | |
#plain_text = sanitize(input("Enter Plain Text: ")) | |
print("Reading Plain Text From File ....") | |
print("\n\n\n") | |
file = open("plaintext.txt", "r+") | |
plain_text = sanitize(file.readline()) | |
print("Plain Text: ") | |
print(plain_text) | |
print("\n\n\n") | |
file.close() | |
print("Reading Key From File ...") | |
print("\n\n\n") | |
file = open("key.txt", "r+") | |
key = sanitize(file.readline()) | |
file.close() | |
#key = sanitize(input("Enter Key: ")) | |
print("Key: ") | |
print(key) | |
cipher_text = encrypt(plain_text, key) | |
print("\n\n\n") | |
print("Cipher Text: ") | |
print(cipher_text) | |
print("\n\n\n") | |
print("cipher text Saved in file encrypted.txt") | |
#print output on file | |
file = open("encrypted.txt", "w") | |
file.write(cipher_text) | |
file.close() | |
try: | |
decrypted_text = decrypt(cipher_text, key) | |
print("Decrypted Text: ") | |
print(decrypted_text) | |
except: | |
print("Inverse of Matrix Does Not Exist.") | |
def encrypt(plain_text, key): | |
""" | |
Hill Cipher Encryption | |
C = P*K MOD 26 | |
where | |
C = cipher text | |
P = plain text | |
K = key (matrix) | |
""" | |
matrix_size = findMatrixSize(len(key)) | |
#padding added from a and convert alpha to numeric valud | |
padded_key = getPaddedKey(key, matrix_size) | |
encoded_matrix = np.fromstring(padded_key,dtype=int, sep=' ').reshape(matrix_size,matrix_size) | |
#if plaintext size is not compatible with matrix size in such | |
#case matrix multiplication is not possible | |
#that why we are adding padding to plaintext | |
padded_pt = getPaddedPlainText(plain_text, matrix_size) | |
text_matrix = np.fromstring(padded_pt, dtype=int,sep=' ').reshape(-1, matrix_size) | |
cipher_text = '' | |
for column in text_matrix: | |
x = (encoded_matrix @ column) % 26 | |
#using reverse table we are assigning alphabets to numbers | |
for elem in x: | |
cipher_text += CHAR_TABLE_REV[elem] | |
return cipher_text | |
def decrypt(cipher_text, key): | |
""" | |
Hill Cipher Decryption | |
p = C*K(Inv) MOD 26 | |
where | |
P = plain text | |
C = cipher text | |
K = Key | |
K(INV) = Inverse of K | |
""" | |
matrix_size = findMatrixSize(len(key)) | |
padded_key = getPaddedKey(key, matrix_size) | |
key_matrix = np.fromstring(padded_key,dtype=int, sep=' ').reshape(matrix_size,matrix_size) | |
#change cipher text into matrix | |
encoded_ct_string = encode(cipher_text) | |
cipher_text_matrix = np.fromstring(encoded_ct_string, dtype=int, sep=' ').reshape(-1, matrix_size) | |
#inverted_key_matrix = np.linalg.inv(key_matrix) | |
matrix = Matrix(key_matrix) | |
inverted_key_matrix = np.array(matrix.inv_mod(26)).astype(np.int32) | |
plain_text = '' | |
for column in cipher_text_matrix: | |
x = (inverted_key_matrix @ column) % 26 | |
for elem in x: | |
plain_text += CHAR_TABLE_REV[elem] | |
return plain_text | |
def sanitize(text): | |
return ''.join(c for c in text if c.isalpha()).lower() | |
def findMatrixSize(length): | |
start, size = 1, 2 | |
while True: | |
if length in range(start, (size*size)+1): | |
return size | |
start = (size*size)+1 | |
size = size+1 | |
def encode(string): | |
""" | |
Returns space seperated encoded string | |
encoded string: string of numbers based of char table | |
""" | |
return ' '.join([str(CHAR_TABLE[c]) for c in string]) | |
def decode(matrix): | |
""" | |
Returns string decoded from encoded matrix | |
""" | |
text = '' | |
for row, col in np.ndindex(matrix.shape): | |
text += CHAR_TABLE_REV[matrix[row, col]] | |
return text | |
def getPaddedKey(string, k): | |
for i in range(0, (k*k) - len(string)): | |
string = string + list(CHAR_TABLE.keys())[i] | |
return encode(string) | |
def getPaddedPlainText(string, k): | |
if len(string) % k != 0: | |
if len(string) < k*k: | |
#closest multiple of k | |
for i in range(0,len(string) // k): | |
string = string + list(CHAR_TABLE.keys())[i] | |
elif len(string) > k*k: | |
#compute expected size | |
i = len(string) | |
while i % k != 0: | |
i+=1 | |
for i in range(0, i - len(string)): | |
string = string + list(CHAR_TABLE.keys())[i] | |
return encode(string) | |
#entry point of program | |
# int main / public static void main | |
#scripting | |
if __name__ == "__main__": | |
program() | |
Added exception handling.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
updated the code for encryption and decryption.