Home > other >  I have written this program in c using the hill cipher but the encryption and decryption output are
I have written this program in c using the hill cipher but the encryption and decryption output are

Time:11-24

I have an assignment to write a program using the Hill Cipher. The program needs to both encrypt and decrypt but not run at the same time. To run the encrypt the user must enter "./hill -e" on the command line and "./hill -d" for decrypt. The program must prompt for and read an integer key from the user for a 2X2 matrix. It must also verify that the key is valid by checking the determinant. The inverse of this matrix must also be calculated. The user is then prompted to enter 2 characters for the plaintext and those are then changed to integers. After this is when the program will prompt the encryption or decryption.

There are no errors when compiling. Creating the matrix, checking the determinant, calculating the inverse, and changing the characters to the correct integer all work exactly as expected. My issue is I'm not getting the correct output for the encryption or decryption. I have tried several different variations but none work. Would someone be willing to point out the errors?

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>


int main(int argc, char * argv[]){

    int i,j,k;

    int matrix[2][2],adjoint[2][2],plaintxt[2][0],encrypted[2][0],decrypted[2][0];

    long determinant;

    float inverse_matrix[2][2];

    char msg[10];

    printf("\nEnter your elements for a 2X2 matrix key.Each integer should be separated by a space.\n");

    for(i = 0; i < 2; i  )
    {
        for(j = 0; j < 2; j  )
        {
            scanf("%d", &matrix[i][j]);
        }
    }

    printf("\nThe 2X2 Key Matrix:\n");

    for(i = 0; i < 2; i  )
    {
        for(j = 0; j < 2; j  )
        {
            printf("%d\t", matrix[i][j]);
        }
        printf("\n");
    }


    determinant = matrix[0][0]*matrix[1][1] - matrix[1][0]*matrix[0][1];

    if(determinant == 0)
    {
        printf("\nInvalid key. The matrix does not have an inverse.\n");
        printf("\n");
        exit(EXIT_FAILURE);
    }


    adjoint[0][0] = matrix[1][1];
    adjoint[1][1] = matrix[0][0];
    adjoint[0][1] = -matrix[0][1];
    adjoint[1][0] = -matrix[1][0];

    printf("\nInverse of the 2X2 Key Matrix:\n");

    for(i = 0; i < 2; i  )
    {
        for(j = 0; j < 2; j  )
        {
            inverse_matrix[i][j] = (adjoint[i][j])/(float)determinant;
            printf("%lf\t", inverse_matrix[i][j]);
        }
        printf("\n");
    }


    printf("\n Enter 2 plaintext characters to cipher.\n");
    scanf("%s", msg);

    for(i = 0; i < 2; i  )
    {
        plaintxt[i][0] = msg[i] - 97;
        printf("%d", plaintxt[i][0]);
    }

    if(argc >= 2)
    {
        if(strcmp(argv[1], "-e") == 0)
        {
            for(i = 0; i < 2; i  ){
                k = 0;
                for(j = 0; j < 2; j  ){
                    k = k   matrix[i][j] * plaintxt[i][0];
                }
                encrypted[i][0] = k % 26;
            }
            printf("\nEncrypted cipher text: ");
            for(i = 0; i < 2; i  )
                printf("%c", encrypted[i][0]   97);
        }
        else if(strcmp(argv[1], "-d") == 0)
        {
            for(i = 0; i < 2; i  ){
                k = 0;
                for(j = 0; j < 2; j  ){
                    k = k   inverse_matrix[i][j] * encrypted[i][0];
                }
                plaintxt[i][0] = k % 26;
            }
            printf("\nDecrypted cipher text: ");
            for(i = 0; i < 2; i  )
                printf("%c", plaintxt[i][0]   97);
        }
    }
    
    return 0;}

    

CodePudding user response:

Continuing from my comments, you have a large number of logic errors in your implementation of the Hill cipher. Specifically, the primary issues are:

  1. C does not allow zero-size arrays. A 2D array is an array-of-arrays. You have [2][0] for both plaintxt and encrypted -- which is just a 2D array of zero-size arrays. Minimum you need [2][1] (or [2][2] is fine),
  2. you never initialize encrypted if "-d" is given on the command line,
  3. you must fully fill plaintxt and encrypted BEFORE you calculate the result of your matrix multiplication. Computing each encrypted/plaintxt character requires both of the original characters be in plaintxt or encrypted, respectively,
  4. your matrix multiplication with the character vector is wrong. You have i in the vector where you should have j, e.g. k = k matrix[i][j] * plaintxt[j][0]; and k = k inverse_matrix[i][j] * encrypted[j][0];,
  5. both your matrix and inverse values must be mod(26) as well as your vector formed by the entered 2-character input. You seem to want to use lowercase characters, so be consistent and convert the input to lowercase and subtract 'a' for the needed zero-based vector,
  6. you must VALIDATE every user-input by checking the return of whatever input function you use.

There are a number of additional small issues noted with comments in the code below, but putting everything together and using fgets() to read the string-input (after emptying stdin of the '\n' left by scanf()), you could do something like the following:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

/* quick helper-function to empty stdin */
void empty_stdin (void)
{
  int c = getchar();
  
  while (c != '\n' && c != EOF) {
    c = getchar();
  }
}

int main (int argc, char * argv[]){

    int i,j,k;

    int matrix[2][2],
        adjoint[2][2],
        plaintxt[2][2] = {{0},{0}},   /* initialize your text arrays */
        encrypted[2][2] = {{0},{0}};

    long determinant;

    float inverse_matrix[2][2];

    char msg[1024];   /* don't SKIMP on buffer size */

    /* avoid long strings, split them, puts() is fine for no converisons */
    puts ("\nEnter your elements for a 2X2 matrix key."
          "Each integer should be separated by a space.");

    for (i = 0; i < 2; i  )
    {
        for (j = 0; j < 2; j  )
        {
            /* validate EVERY input */
            if (scanf ("%d", &matrix[i][j]) != 1) {
                fputs ("invalid integer input.\n", stderr);
                return 1;
            }
            matrix[i][j] %= 26;   /* matrix must be % 26 */
        }
    }
    empty_stdin();    /* get rid of chars   '\n' remaining in stdin */
    
    puts ("\nThe 2X2 Key Matrix:");

    for (i = 0; i < 2; i  )
    {
        for (j = 0; j < 2; j  )
        {
            printf("%d\t", matrix[i][j]);
        }
        putchar ('\n');   /* you putchar() a single-character */
    }


    determinant = matrix[0][0] * matrix[1][1] - matrix[1][0] * matrix[0][1];

    if (determinant == 0)
    {
        /* error output goes to stderr */
        fputs ("\nInvalid key. The matrix does not have an inverse.\n", 
               stderr);
        exit (EXIT_FAILURE);
    }


    adjoint[0][0] = matrix[1][1];
    adjoint[1][1] = matrix[0][0];
    adjoint[0][1] = -matrix[0][1];
    adjoint[1][0] = -matrix[1][0];

    puts ("\nInverse of the 2X2 Key Matrix:");

    for (i = 0; i < 2; i  )
    {
        for (j = 0; j < 2; j  )
        {
            /* inverse matrix must be % 26 as well */
            inverse_matrix[i][j] = fmodf ((adjoint[i][j])/(float)determinant,
                                          26.);
            printf ("%lf\t", inverse_matrix[i][j]);
        }
        putchar ('\n');
    }

    if (argc >= 2)
    {
        if (strcmp(argv[1], "-e") == 0)
        {
            /* move prompt and read into proper block */
            fputs ("\nenter 2 plaintext chars to encipher: ", stdout);
            /* should read/validate all user input with fgets */
            if (!fgets (msg, sizeof msg, stdin)) {
                puts ("(user canceled input)");
                return 0;
            }
            
            /* simply assign plaintxt before loop, zero-based for cipher */
            plaintxt[0][0] = tolower(msg[0]) - 'a';
            plaintxt[1][0] = tolower(msg[1]) - 'a';
            
            /* ensure you don't read beyond end of msg */
            for (i = 0; msg[i] && i < 2; i  ){
                k = 0;
                for(j = 0; j < 2; j  ){
                    k = k   matrix[i][j] * plaintxt[j][0];
                }
                encrypted[i][0] = k % 26;
            }
            /* should exit if i != 2 here */
            
            printf("\nEncrypted cipher text: ");
            for (i = 0; i < 2; i  )
                putchar (encrypted[i][0]   'a');
            putchar ('\n');
        }
        else if (strcmp(argv[1], "-d") == 0)
        {
            /* move prompt and read into proper block */
            fputs ("\nenter 2 enciphered chars to decipher: ", stdout);
            /* should read/validate all user input with fgets */
            if (!fgets (msg, sizeof msg, stdin)) {
                puts ("(user canceled input)");
                return 0;
            }
            
            /* simply assign encrypted before loop, zero-based for cipher */
            encrypted[0][0] = tolower(msg[0]) - 'a';
            encrypted[1][0] = tolower(msg[1]) - 'a';
            
            /* ensure you don't read beyond end of msg */
            for (i = 0; msg[i] && i < 2; i  ){
                k = 0;
                for(j = 0; j < 2; j  ){
                    k = k   inverse_matrix[i][j] * encrypted[j][0];
                }
                plaintxt[i][0] = k % 26;
            }
            /* should exit if i != 2 here */
            
            printf("\nDecrypted cipher text: ");
            for(i = 0; i < 2; i  )
                putchar (plaintxt[i][0]   'a');
            putchar ('\n');
        }
    }
    
    return 0;
}

(note: these are minimal changes to make your code work, not an optimal rewrite of your code -- that is left to you)

Example Use/Output

Using the simple test-case where the matrix is formed by 1 2 3 4 and the string input of "ab" the code enciphers and deciphers as desired, e.g.

$ ./bin/hillcipher -e

Enter your elements for a 2X2 matrix key.Each integer should be separated by a space.
1 2 3 4

The 2X2 Key Matrix:
1       2
3       4

Inverse of the 2X2 Key Matrix:
-2.000000       1.000000
1.500000        -0.500000

enter 2 plaintext chars to encipher: ab

Encrypted cipher text: ce

And the decipher:

$ ./bin/hillcipher -d

Enter your elements for a 2X2 matrix key.Each integer should be separated by a space.
1 2 3 4

The 2X2 Key Matrix:
1       2
3       4

Inverse of the 2X2 Key Matrix:
-2.000000       1.000000
1.500000        -0.500000

enter 2 enciphered chars to decipher: ce

Decrypted cipher text: ab

Look things over and let me know if you have questions.

  •  Tags:  
  • c
  • Related