Add Tests for HillCipher (#5562)

This commit is contained in:
Benjamin Burstein
2024-10-04 13:18:51 -04:00
committed by GitHub
parent 389d1d70d5
commit 393337fa8e
2 changed files with 105 additions and 144 deletions

View File

@ -1,178 +1,103 @@
package com.thealgorithms.ciphers;
import java.util.Scanner;
public class HillCipher {
/*
* Java Implementation of Hill Cipher
* Hill cipher is a polyalphabetic substitution cipher. Each letter is represented by a number
* belonging to the set Z26 where A=0 , B=1, ..... Z=25. To encrypt a message, each block of n
* letters (since matrix size is n x n) is multiplied by an invertible n × n matrix, against
* modulus 26. To decrypt the message, each block is multiplied by the inverse of the matrix used
* for encryption. The cipher key and plaintext/ciphertext are user inputs.
* @author Ojasva Jain
*/
public final class HillCipher {
private HillCipher() {
}
static Scanner userInput = new Scanner(System.in);
/* Following function encrypts the message
*/
static void encrypt(String message) {
message = message.toUpperCase();
// Get key matrix
System.out.println("Enter key matrix size");
int matrixSize = userInput.nextInt();
System.out.println("Enter Key/encryptionKey matrix ");
int[][] keyMatrix = new int[matrixSize][matrixSize];
for (int i = 0; i < matrixSize; i++) {
for (int j = 0; j < matrixSize; j++) {
keyMatrix[i][j] = userInput.nextInt();
}
}
// check if det = 0
// Encrypts the message using the key matrix
public String encrypt(String message, int[][] keyMatrix) {
message = message.toUpperCase().replaceAll("[^A-Z]", "");
int matrixSize = keyMatrix.length;
validateDeterminant(keyMatrix, matrixSize);
int[][] messageVector = new int[matrixSize][1];
String cipherText = "";
int[][] cipherMatrix = new int[matrixSize][1];
int j = 0;
while (j < message.length()) {
StringBuilder cipherText = new StringBuilder();
int[] messageVector = new int[matrixSize];
int[] cipherVector = new int[matrixSize];
int index = 0;
while (index < message.length()) {
for (int i = 0; i < matrixSize; i++) {
if (j >= message.length()) {
messageVector[i][0] = 23;
if (index < message.length()) {
messageVector[i] = message.charAt(index++) - 'A';
} else {
messageVector[i][0] = (message.charAt(j)) % 65;
messageVector[i] = 'X' - 'A'; // Padding with 'X' if needed
}
System.out.println(messageVector[i][0]);
j++;
}
int x;
int i;
for (i = 0; i < matrixSize; i++) {
cipherMatrix[i][0] = 0;
for (x = 0; x < matrixSize; x++) {
cipherMatrix[i][0] += keyMatrix[i][x] * messageVector[x][0];
for (int i = 0; i < matrixSize; i++) {
cipherVector[i] = 0;
for (int j = 0; j < matrixSize; j++) {
cipherVector[i] += keyMatrix[i][j] * messageVector[j];
}
System.out.println(cipherMatrix[i][0]);
cipherMatrix[i][0] = cipherMatrix[i][0] % 26;
}
for (i = 0; i < matrixSize; i++) {
cipherText += (char) (cipherMatrix[i][0] + 65);
cipherVector[i] = cipherVector[i] % 26;
cipherText.append((char) (cipherVector[i] + 'A'));
}
}
System.out.println("Ciphertext: " + cipherText);
return cipherText.toString();
}
// Following function decrypts a message
static void decrypt(String message) {
message = message.toUpperCase();
// Get key matrix
System.out.println("Enter key matrix size");
int n = userInput.nextInt();
System.out.println("Enter inverseKey/decryptionKey matrix ");
int[][] keyMatrix = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
keyMatrix[i][j] = userInput.nextInt();
}
}
// check if det = 0
validateDeterminant(keyMatrix, n);
// Decrypts the message using the inverse key matrix
public String decrypt(String message, int[][] inverseKeyMatrix) {
message = message.toUpperCase().replaceAll("[^A-Z]", "");
int matrixSize = inverseKeyMatrix.length;
validateDeterminant(inverseKeyMatrix, matrixSize);
// solving for the required plaintext message
int[][] messageVector = new int[n][1];
String plainText = "";
int[][] plainMatrix = new int[n][1];
int j = 0;
while (j < message.length()) {
for (int i = 0; i < n; i++) {
if (j >= message.length()) {
messageVector[i][0] = 23;
StringBuilder plainText = new StringBuilder();
int[] messageVector = new int[matrixSize];
int[] plainVector = new int[matrixSize];
int index = 0;
while (index < message.length()) {
for (int i = 0; i < matrixSize; i++) {
if (index < message.length()) {
messageVector[i] = message.charAt(index++) - 'A';
} else {
messageVector[i][0] = (message.charAt(j)) % 65;
messageVector[i] = 'X' - 'A'; // Padding with 'X' if needed
}
System.out.println(messageVector[i][0]);
j++;
}
int x;
int i;
for (i = 0; i < n; i++) {
plainMatrix[i][0] = 0;
for (x = 0; x < n; x++) {
plainMatrix[i][0] += keyMatrix[i][x] * messageVector[x][0];
for (int i = 0; i < matrixSize; i++) {
plainVector[i] = 0;
for (int j = 0; j < matrixSize; j++) {
plainVector[i] += inverseKeyMatrix[i][j] * messageVector[j];
}
plainMatrix[i][0] = plainMatrix[i][0] % 26;
}
for (i = 0; i < n; i++) {
plainText += (char) (plainMatrix[i][0] + 65);
plainVector[i] = plainVector[i] % 26;
plainText.append((char) (plainVector[i] + 'A'));
}
}
System.out.println("Plaintext: " + plainText);
return plainText.toString();
}
// Determinant calculator
public static int determinant(int[][] a, int n) {
// Validates that the determinant of the key matrix is not zero modulo 26
private void validateDeterminant(int[][] keyMatrix, int n) {
int det = determinant(keyMatrix, n) % 26;
if (det == 0) {
throw new IllegalArgumentException("Invalid key matrix. Determinant is zero modulo 26.");
}
}
// Computes the determinant of a matrix recursively
private int determinant(int[][] matrix, int n) {
int det = 0;
int sign = 1;
int p = 0;
int q = 0;
if (n == 1) {
det = a[0][0];
} else {
int[][] b = new int[n - 1][n - 1];
for (int x = 0; x < n; x++) {
p = 0;
q = 0;
for (int i = 1; i < n; i++) {
for (int j = 0; j < n; j++) {
if (j != x) {
b[p][q++] = a[i][j];
if (q % (n - 1) == 0) {
p++;
q = 0;
}
}
return matrix[0][0];
}
int sign = 1;
int[][] subMatrix = new int[n - 1][n - 1];
for (int x = 0; x < n; x++) {
int subI = 0;
for (int i = 1; i < n; i++) {
int subJ = 0;
for (int j = 0; j < n; j++) {
if (j != x) {
subMatrix[subI][subJ++] = matrix[i][j];
}
}
det = det + a[0][x] * determinant(b, n - 1) * sign;
sign = -sign;
subI++;
}
det += sign * matrix[0][x] * determinant(subMatrix, n - 1);
sign = -sign;
}
return det;
}
// Function to implement Hill Cipher
static void hillCipher(String message) {
System.out.println("What do you want to process from the message?");
System.out.println("Press 1: To Encrypt");
System.out.println("Press 2: To Decrypt");
short sc = userInput.nextShort();
if (sc == 1) {
encrypt(message);
} else if (sc == 2) {
decrypt(message);
} else {
System.out.println("Invalid input, program terminated.");
}
}
static void validateDeterminant(int[][] keyMatrix, int n) {
if (determinant(keyMatrix, n) % 26 == 0) {
System.out.println("Invalid key, as determinant = 0. Program Terminated");
}
}
// Driver code
public static void main(String[] args) {
// Get the message to be encrypted
System.out.println("Enter message");
String message = userInput.nextLine();
hillCipher(message);
}
}