From 76de12cb1aad68e989a16aa851a34a66a42b3ed3 Mon Sep 17 00:00:00 2001 From: bennybebo Date: Thu, 3 Oct 2024 15:57:52 -0400 Subject: [PATCH 1/2] Added tests for HillCipher.java --- .../com/thealgorithms/ciphers/HillCipher.java | 214 ++++++------------ .../thealgorithms/ciphers/HillCipherTest.java | 44 ++++ 2 files changed, 114 insertions(+), 144 deletions(-) create mode 100644 src/test/java/com/thealgorithms/ciphers/HillCipherTest.java diff --git a/src/main/java/com/thealgorithms/ciphers/HillCipher.java b/src/main/java/com/thealgorithms/ciphers/HillCipher.java index 780009c2f1d6..44e65c93e748 100644 --- a/src/main/java/com/thealgorithms/ciphers/HillCipher.java +++ b/src/main/java/com/thealgorithms/ciphers/HillCipher.java @@ -1,178 +1,104 @@ 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) { - int det = 0; - int sign = 1; - int p = 0; - int q = 0; + // 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; 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 sub_i = 0; + for (int i = 1; i < n; i++) { + int sub_j = 0; + for (int j = 0; j < n; j++) { + if (j != x) { + subMatrix[sub_i][sub_j++] = matrix[i][j]; } } - det = det + a[0][x] * determinant(b, n - 1) * sign; - sign = -sign; + sub_i++; } + 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); - } } + diff --git a/src/test/java/com/thealgorithms/ciphers/HillCipherTest.java b/src/test/java/com/thealgorithms/ciphers/HillCipherTest.java new file mode 100644 index 000000000000..56208692a618 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/HillCipherTest.java @@ -0,0 +1,44 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class HillCipherTest { + + HillCipher hillCipher = new HillCipher(); + + @Test + void hillCipherEncryptTest() { + // given + String message = "ACT"; // Plaintext message + int[][] keyMatrix = { + {6, 24, 1}, + {13, 16, 10}, + {20, 17, 15} + }; // Encryption key matrix + + // when + String cipherText = hillCipher.encrypt(message, keyMatrix); + + // then + assertEquals("POH", cipherText); + } + + @Test + void hillCipherDecryptTest() { + // given + String cipherText = "POH"; // Ciphertext message + int[][] inverseKeyMatrix = { + {8, 5, 10}, + {21, 8, 21}, + {21, 12, 8} + }; // Decryption (inverse key) matrix + + // when + String plainText = hillCipher.decrypt(cipherText, inverseKeyMatrix); + + // then + assertEquals("ACT", plainText); + } +} From 6a53215b2bd8d39a5a4aa3d972e38c9b8f272c6c Mon Sep 17 00:00:00 2001 From: bennybebo Date: Thu, 3 Oct 2024 16:20:27 -0400 Subject: [PATCH 2/2] Fixed checkstyle error --- src/main/java/com/thealgorithms/ciphers/HillCipher.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/thealgorithms/ciphers/HillCipher.java b/src/main/java/com/thealgorithms/ciphers/HillCipher.java index 44e65c93e748..687267ccc694 100644 --- a/src/main/java/com/thealgorithms/ciphers/HillCipher.java +++ b/src/main/java/com/thealgorithms/ciphers/HillCipher.java @@ -85,9 +85,9 @@ private int determinant(int[][] matrix, int n) { int sign = 1; int[][] subMatrix = new int[n - 1][n - 1]; for (int x = 0; x < n; x++) { - int sub_i = 0; + int subI = 0; for (int i = 1; i < n; i++) { - int sub_j = 0; + int subJ = 0; for (int j = 0; j < n; j++) { if (j != x) { subMatrix[sub_i][sub_j++] = matrix[i][j];