Skip to content

Added tests for HillCipher.java #5560

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 70 additions & 144 deletions src/main/java/com/thealgorithms/ciphers/HillCipher.java
Original file line number Diff line number Diff line change
@@ -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 subI = 0;
for (int i = 1; i < n; i++) {
int subJ = 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);
}
}

44 changes: 44 additions & 0 deletions src/test/java/com/thealgorithms/ciphers/HillCipherTest.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading