mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-07-08 02:04:31 +08:00
Add Tests for HillCipher (#5562)
This commit is contained in:

committed by
GitHub

parent
389d1d70d5
commit
393337fa8e
@ -1,178 +1,103 @@
|
|||||||
package com.thealgorithms.ciphers;
|
package com.thealgorithms.ciphers;
|
||||||
|
|
||||||
import java.util.Scanner;
|
public class HillCipher {
|
||||||
|
|
||||||
/*
|
// Encrypts the message using the key matrix
|
||||||
* Java Implementation of Hill Cipher
|
public String encrypt(String message, int[][] keyMatrix) {
|
||||||
* Hill cipher is a polyalphabetic substitution cipher. Each letter is represented by a number
|
message = message.toUpperCase().replaceAll("[^A-Z]", "");
|
||||||
* belonging to the set Z26 where A=0 , B=1, ..... Z=25. To encrypt a message, each block of n
|
int matrixSize = keyMatrix.length;
|
||||||
* 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
|
|
||||||
validateDeterminant(keyMatrix, matrixSize);
|
validateDeterminant(keyMatrix, matrixSize);
|
||||||
|
|
||||||
int[][] messageVector = new int[matrixSize][1];
|
StringBuilder cipherText = new StringBuilder();
|
||||||
String cipherText = "";
|
int[] messageVector = new int[matrixSize];
|
||||||
int[][] cipherMatrix = new int[matrixSize][1];
|
int[] cipherVector = new int[matrixSize];
|
||||||
int j = 0;
|
int index = 0;
|
||||||
while (j < message.length()) {
|
|
||||||
|
while (index < message.length()) {
|
||||||
for (int i = 0; i < matrixSize; i++) {
|
for (int i = 0; i < matrixSize; i++) {
|
||||||
if (j >= message.length()) {
|
if (index < message.length()) {
|
||||||
messageVector[i][0] = 23;
|
messageVector[i] = message.charAt(index++) - 'A';
|
||||||
} else {
|
} 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++) {
|
for (int i = 0; i < matrixSize; i++) {
|
||||||
cipherMatrix[i][0] += keyMatrix[i][x] * messageVector[x][0];
|
cipherVector[i] = 0;
|
||||||
|
for (int j = 0; j < matrixSize; j++) {
|
||||||
|
cipherVector[i] += keyMatrix[i][j] * messageVector[j];
|
||||||
}
|
}
|
||||||
System.out.println(cipherMatrix[i][0]);
|
cipherVector[i] = cipherVector[i] % 26;
|
||||||
cipherMatrix[i][0] = cipherMatrix[i][0] % 26;
|
cipherText.append((char) (cipherVector[i] + 'A'));
|
||||||
}
|
|
||||||
for (i = 0; i < matrixSize; i++) {
|
|
||||||
cipherText += (char) (cipherMatrix[i][0] + 65);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.out.println("Ciphertext: " + cipherText);
|
|
||||||
|
return cipherText.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Following function decrypts a message
|
// Decrypts the message using the inverse key matrix
|
||||||
static void decrypt(String message) {
|
public String decrypt(String message, int[][] inverseKeyMatrix) {
|
||||||
message = message.toUpperCase();
|
message = message.toUpperCase().replaceAll("[^A-Z]", "");
|
||||||
// Get key matrix
|
int matrixSize = inverseKeyMatrix.length;
|
||||||
System.out.println("Enter key matrix size");
|
validateDeterminant(inverseKeyMatrix, matrixSize);
|
||||||
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);
|
|
||||||
|
|
||||||
// solving for the required plaintext message
|
StringBuilder plainText = new StringBuilder();
|
||||||
int[][] messageVector = new int[n][1];
|
int[] messageVector = new int[matrixSize];
|
||||||
String plainText = "";
|
int[] plainVector = new int[matrixSize];
|
||||||
int[][] plainMatrix = new int[n][1];
|
int index = 0;
|
||||||
int j = 0;
|
|
||||||
while (j < message.length()) {
|
while (index < message.length()) {
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < matrixSize; i++) {
|
||||||
if (j >= message.length()) {
|
if (index < message.length()) {
|
||||||
messageVector[i][0] = 23;
|
messageVector[i] = message.charAt(index++) - 'A';
|
||||||
} else {
|
} 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++) {
|
for (int i = 0; i < matrixSize; i++) {
|
||||||
plainMatrix[i][0] += keyMatrix[i][x] * messageVector[x][0];
|
plainVector[i] = 0;
|
||||||
|
for (int j = 0; j < matrixSize; j++) {
|
||||||
|
plainVector[i] += inverseKeyMatrix[i][j] * messageVector[j];
|
||||||
}
|
}
|
||||||
|
plainVector[i] = plainVector[i] % 26;
|
||||||
plainMatrix[i][0] = plainMatrix[i][0] % 26;
|
plainText.append((char) (plainVector[i] + 'A'));
|
||||||
}
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
plainText += (char) (plainMatrix[i][0] + 65);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.out.println("Plaintext: " + plainText);
|
|
||||||
|
return plainText.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determinant calculator
|
// Validates that the determinant of the key matrix is not zero modulo 26
|
||||||
public static int determinant(int[][] a, int n) {
|
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 det = 0;
|
||||||
int sign = 1;
|
|
||||||
int p = 0;
|
|
||||||
int q = 0;
|
|
||||||
|
|
||||||
if (n == 1) {
|
if (n == 1) {
|
||||||
det = a[0][0];
|
return matrix[0][0];
|
||||||
} else {
|
}
|
||||||
int[][] b = new int[n - 1][n - 1];
|
int sign = 1;
|
||||||
for (int x = 0; x < n; x++) {
|
int[][] subMatrix = new int[n - 1][n - 1];
|
||||||
p = 0;
|
for (int x = 0; x < n; x++) {
|
||||||
q = 0;
|
int subI = 0;
|
||||||
for (int i = 1; i < n; i++) {
|
for (int i = 1; i < n; i++) {
|
||||||
for (int j = 0; j < n; j++) {
|
int subJ = 0;
|
||||||
if (j != x) {
|
for (int j = 0; j < n; j++) {
|
||||||
b[p][q++] = a[i][j];
|
if (j != x) {
|
||||||
if (q % (n - 1) == 0) {
|
subMatrix[subI][subJ++] = matrix[i][j];
|
||||||
p++;
|
|
||||||
q = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
det = det + a[0][x] * determinant(b, n - 1) * sign;
|
subI++;
|
||||||
sign = -sign;
|
|
||||||
}
|
}
|
||||||
|
det += sign * matrix[0][x] * determinant(subMatrix, n - 1);
|
||||||
|
sign = -sign;
|
||||||
}
|
}
|
||||||
return det;
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
36
src/test/java/com/thealgorithms/ciphers/HillCipherTest.java
Normal file
36
src/test/java/com/thealgorithms/ciphers/HillCipherTest.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user