#Hill Cipher
Code:
import numpy as np
def minor(matrix, row, col):
"""Calculate the minor of a matrix element by removing the given row and column"""
submatrix = np.delete(matrix, row, axis=0) # Remove the row
submatrix = np.delete(submatrix, col, axis=1) # Remove the column
return np.linalg.det(submatrix)
def cofactor_matrix(matrix):
"""Calculate the cofactor matrix"""
n = matrix.shape[0]
cofactor_matrix = np.zeros_like(matrix, dtype=float)
for i in range(n):
for j in range(n):
cofactor_matrix[i, j] = (-1) ** (i + j) * minor(matrix, i, j)
return cofactor_matrix
def adjoint(matrix):
"""Calculate the adjoint of the matrix (transpose of cofactor matrix)"""
cofactor_matrix_result = cofactor_matrix(matrix)
adjoint_matrix = np.transpose(cofactor_matrix_result)
return adjoint_matrix
def mod_inverse(d, m):
"""Extended Euclidean algorithm to find gcd and modular inverse"""
def extended_gcd(a, b):
if b == 0:
return a, 1, 0 # gcd, x, y
else:
gcd, x1, y1 = extended_gcd(b, a % b)
x = y1
y = x1 - (a // b) * y1
return gcd, x, y
gcd, x, y = extended_gcd(d, m)
if gcd != 1:
raise ValueError(f"{d} has no modular inverse modulo {m}")
else:
return x % m # Ensure the result is positive
def get_key():
"""Function to get key matrix and message"""
order = int(input("Enter order of Matrix: "))
key_matrix = np.zeros((order, order), dtype=int)
# Input the Key Matrix
for i in range(order):
for j in range(order):
key_matrix[i][j] = int(input(f"Enter key matrix element ({i + 1}, {j + 1}): "))
key_matrix[i][j] = key_matrix[i][j] % 26 # Make sure it's modulo 26
# Input the message to be Encrypted
message = input("Enter the message: ").replace(" ", "").lower()
return key_matrix, order, message
def crypt(message, order, key_matrix):
"""Encrypt or Decrypt the message using matrix multiplication"""
crypted = ""
for i in range(0, len(message), order):
mat = np.array([ord(c) - ord('a') for c in message[i:i + order]]) # Convert chars to integers
mod 26
# mat = mat.reshape((order, 1)) # Reshape for matrix multiplication
# Matrix multiply the values with the key matrix
encrypted_mat = np.dot(key_matrix, mat) % 26 # Apply mod 26
encrypted_chars = ''.join(chr(num + ord('a')) for num in encrypted_mat) # Convert back to
characters
crypted += encrypted_chars
return crypted
def encrypt(key_matrix, order, message):
"""Encryption function"""
# If the length of the message is not a multiple of the order, pad with "x"
if len(message) % order != 0:
message += "x" * (order - len(message) % order)
encrypted = crypt(message, order, key_matrix)
print(f"Encrypted message: {encrypted}")
def decrypt(key_matrix, order, text):
"""Decryption function"""
det = int(np.linalg.det(key_matrix)) % 26
det_inv = mod_inverse(det, 26)
adj_matrix = adjoint(key_matrix) % 26
inv_key_matrix = (det_inv * adj_matrix) % 26
inv_key_matrix=inv_key_matrix.astype(int)
decrypted = crypt(text, order, inv_key_matrix)
print(f"Decrypted Text: {decrypted}")
# Main driver function to run encryption or decryption
def main():
while True:
ch = int(input("Choose Operation: \n 1. Encryption(1) \n 2. Decryption(2) \n 3. Exit(3)\n"))
if ch == 1:
key_matrix, order, message = get_key()
encrypt(key_matrix, order, message)
elif ch == 2:
key_matrix, order, text = get_key()
decrypt(key_matrix, order, text)
elif ch == 3:
print("Goodbye!")
break
else:
print("Invalid Input! Please try again.")
# Driver Code
if __name__ == "__main__":
main()
Output:
Encryption:
Decryption: