Caesar cipher in C - encryption and decryption

5.4k views Asked by At

I made this program and seems that doesn't work can you tell me why? I spent like 3 hours just figuring out why it's not working but I have no clue. I'm wondering if it's not something with shift but I'm not sure. That's my first program I'm making so sorry about some basic mistakes.

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

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

   char message[100];
   int shift;
   int i;
   int choice;

   printf("Enter operation with message\n");
   printf("encrypt - 1\n");
   printf("decrypt - 2\n");
   scanf("%d", &choice);

   switch(choice)
   {
      case 1:
      {
         printf("Enter message to encrypt\n");
         scanf("%d", &message);

         printf("Enter shift\n");
         scanf("%d", &shift);

         for (i = 0; message[i] != '\0'; i++)
         {
            message[i] = message[i] + shift;
            if (message[i] > 'z')
               message[i] = message[i] - 26;
         }
         printf("Encrypted message: %s\n", message);
         break;
      }
      case 2: 
      {
         printf("Enter message to decrypt\n");
         scanf("%d", &message);

         printf("Enter shift\n");
         scanf("%d", &shift);

         for (i = 0; message[i] != '\0'; i++)
         {
            message[i] = message[i] - shift;
            if (message[i] > 'z')
               message[i] = message[i] - 26;
         }
         printf("Decrypted message: %s\n", message);
         break;
      }
   }

   return (EXIT_SUCCESS);
}

Here a screen of output:

screenshot

1

There are 1 answers

2
Andre Kampling On BEST ANSWER

Always pay attention to the compiler warnings. If I compile your code I get the following warnings (and some more):

../main.c:21:10: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘char (*)[100]’ [-Wformat=]
          scanf("%d", &message);
          ^
../main.c:38:10: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘char (*)[100]’ [-Wformat=]
          scanf("%d", &message);
          ^

There are 2 main problems:

1. Wrong format specifier with scanf():

Read about the different format specifier here on cppreference.
In you code you're using:

char message[100];
scanf("%d", &message);

but instead of %d which is used for int it should be %s for C-strings (char*). Better use %99s to avoid a string format vulnerability. Further it should be just message without the ampersand (&) because the array name will decay to a char* but &message will be a pointer to an array: char (*)[100]. This would result in a warning because %s expects char*.

char message[100];
if (1 != scanf("%99s", message)) /* buffer size - 1 */
   return EXIT_FAILURE;

Moreover scanf() returns the number of successful read elements. I recommend always check the return values that indicate errors.

2. Logical error in decryption:

Instead of checking message[i] > 'z' it must be message[i] < 'a' as you're subtracting the shift value. Further add 26 instead of subtracting it in that case.

for (i = 0; message[i] != '\0'; i++)
{
   message[i] = message[i] - shift;
   if (message[i] < 'a')
      message[i] = message[i] + 26;
}

Edit #1:

There is also another logical problem about the message you type in. Your code will only work for messages that consists of lower letters. You can extend that to work with upper letters to.

Further your buffer will may hold signed chars1 as it is declared as char message[100];, that means if the calculation: message[i] = message[i] + shift; overflows the bahaviour is undefined. That would happen for example if you use character z (decimal 122 in ASCII code) and a shift of 6 which will lead to 128 but a signed char can hold at most 127.


1 I write "may hold signed chars" because the C standard leaves it up to the implementation to define wether a char is signed or unsigned, but for GCC and MSVC a char will be a signed char by default.