My encryption-decryption program in C

This is a program to encrypt and decrypt files on your computer, using a command-line prompt. It’s written in C; you can use gcc.exe from MinGW to compile it. Since I’m posting the source code for you to inspect, you can see that the program has no “back door” in it.

This is an improvement on Alberti’s method of encryption, which used a single word, endlessly repeated, as the key for encryption. In Alberti’s method, for each letter in the key, the number representing that letter’s place in the alphabet is added to the number representing the corresponding plain-text letter’s place in the alphabet, 26 is subtracted from the sum if it is greater than 26, then the encrypted letter becomes the letter that corresponds to that number.

For example, if the first letter of plain text is ‘S’ (the 19th letter of the alphabet), and the first letter of key-text is ‘L’ (the 12th letter of the alphabet) then the first letter of the encrypted text is ‘E’, because that’s the (19 + 12 – 26 = 5)th letter of the alphabet.


Plain text:     SENDMOREMONEY
Key text:       LOVELOVELOVEL
Encrypted text: ETJIYDNJYDJJK

My encryption method is to expand the Alberti table from the 26 letters of the alphabet to the 256 possible numeric values that an eight-bit byte can have. To encrypt, add the numeric value of the plain-file byte and the numeric value of the encoder-file byte, and this sum, modula 256, becomes the numeric value of the encrypted-file byte.

Furthermore, I use a file that is (at least temporarily) on my computer as the encoder (key) source, instead of Alberti’s repeated keyword. To be specific, I choose, as the encoder file for encryption, a file that is bigger than the plain file so that the encryption key never repeats.

That is my entire encrypt/decrypt idea in a nutshell. Once you actually examine the source code, you’ll see a few safety factors coded in, which I see no need to discuss here.

If you think that it’s unusual for a novelist to create an encryption algorithm, please remember that in my very first post, I mentioned that I’d earned a bachelor’s degree in mathematics.

I wrote the original C code for my encryption-decryption program in 1993, and have improved on it since then. For reasons I choose not to discuss, I am asserting my First Amendment right to post publicly my current version of the source code.

In 1993 and 1994, I submitted my early version of the source code to The National Security Agency (NSA), hoping to sell it to them. Eventually the NSA wrote me, in a letter dated December 15th, 1994, telling me that they declined to buy my algorithm, and that “…we have no objection to your publication of your findings, if this is your intent.”

I now believe that my circa-1993 version of my program had some security holes in it. I suspect this is why the NSA refused to buy it. I have since then, I believe, fixed those security holes. The biggest security-bug fixed: Any sufficiently big file on your computer, as well as any sufficiently big file that you download to your computer, can now be used as the key file. For example, you download an Al Jolson MP3 file from archive.org, use it as the key file when you run this program, then true-erase the Al Jolson MP3 file, and your encrypted file can’t be decrypted. (Until later, when you download the same Al Jolson MP3 file again.)

How to Compile ENCO (in MinGW)

Copy the source code below, paste it into a text file, and save the text file with a filename that has extension “c”. For simplicity’s sake, I recommend naming the file “enco.c”.

Get your computer to recognize a path to C:\MinGW\bin (which is where gcc.exe is).

Call up a command line with CMD.EXE (if you haven’t done so already), and use the CD command to change the default folder to whichever folder your brand-new C file is stored in.

Then enter this at the command prompt:

gcc [the name of your encryption-decryption program].c -o enco.exe -std=c99

examples:

gcc tomhrichardson_s_little_internet_program.c -o enco.exe -std=c99

gcc enco.c -o enco.exe -std=c99


/*
Source code by Thomas H. Richardson, posted 2014 -- copyright
waived/disclaimed.
*/
/*
Encoder, encrypt, and plain files must all have size (in bytes) less than
2 ** 31, which is roughly two billion, or exactly 2 gigabytes or 2,048 Mbytes.

Only three restrictions on what can be encoder file:
1) Must have file size at least 32 kb larger than file size of plain
(unencrypted) file;
2) Will not be changed or permanently deleted between time of encryption and
time of decryption; and
3) Must not have many null characters ('\x00') --  but a few are okay.
*/
/*
To minimize the chance of the code being broken, use a bigger file of suitable
file-extension:

Recommended: MP3, MPG, OGG, PNG, WMA
Not recommended: DOC, HTM, JPG, MOV, PDF, TXT, VOB (found on DVDs), WAV, WMV

To further minimize code-breaking of the encrypted file, the first 2.5 kb of
the encoder file are skipped-through without being used for
encryption/decryption.
*/


#include <stdio.h>                                        /* to define FILE */
#include <io.h>                                   /* for FILELENGTH, FILENO */
#include <string.h>


void main(int argc, char **argv), get_filename(char *cp_start);

int encode(const char *restrict cp_helper, const char *restrict cp_plain,
      const char *restrict cp_encoded,
      const int both_encoder_and_plain_are_text,
      FILE *restrict fp_encoder);

int decode(const char *restrict cp_helper, const char *restrict cp_plain,
      const char *restrict cp_encoded,
      const int both_encoder_and_plain_are_text, FILE *fp_encoder);

#ifndef NULL
#define NULL ((void*)0)
#endif


char *too_small_msg_format = "/ / / helper file \"%s\" too small! \\ \\ \\\n";
char *too_many_null_chars_msg_format
     = "/ / / helper file \"%s\" has too many null characters! \\ \\ \\\n";


int encode(const char *restrict cp_helper, const char *restrict cp_plain,
      const char *restrict cp_encoded,
	  const int both_encoder_and_plain_are_text,
          FILE *restrict fp_encoder)
{
   int i, i_plain, i_encoder, ok_to_continue, is_ok;
   FILE *restrict fp_plain, *restrict fp_encrypt;
/* */
   is_ok = 0;  /* FALSE */
   if (both_encoder_and_plain_are_text)
      fp_plain = fopen(cp_plain, "rt");
   else
      fp_plain = fopen(cp_plain, "rb");
   if (fp_plain != NULL)
   {
      if ((filelength(fileno(fp_encoder)) - filelength(fileno(fp_plain)))
            >= 32768L)
   /* Even if the encoder file has size of only 32 kb more than the size of
       the plain file, and furthermore I throw away the first 2.5 kb of the
     encoder file, I'll still avoid hitting EOF for the encoder file. (This
  assumes that the encoder file has less than 30,208 null-characters in it,
         or else that the encoder file is MUCH bigger than the plain file.) */
      {
         for (i = 0; i < 2560; i++)  i_encoder = fgetc(fp_encoder);
              /* throw away first 2.5 kilobytes of encoder file. Hopefully,
                         encoder file's header file isn't more than 2.5 kb. */
         ok_to_continue = 1;  /* TRUE */
         fp_encrypt = fopen(cp_encoded, "wb");
         for (i_plain = fgetc(fp_plain);
               ((i_plain != EOF) && (ok_to_continue));
               i_plain = fgetc(fp_plain))
         {
            for (i_encoder = fgetc(fp_encoder);
                  i_encoder == 0;                              /* null char */
                  i_encoder = fgetc(fp_encoder))
               ;
/*
If I allow encoder-character to remain '\x00', then "encrypted" character is
the same as unencrypted ("plain") character. A number of consecutive encoder
null characters being allowed, leads to large portions of "encrypted" file
being easily sight-read.
*/
            ok_to_continue = (i_encoder != EOF);
            is_ok = ok_to_continue;
            if (ok_to_continue)
            {
               if (both_encoder_and_plain_are_text)
               {
                  if (i_plain == 10) /* \n */ i_plain = 128;
                  else;
                  if (i_encoder == 10)  i_encoder = 128;
                  else;
               }
               else;
               fputc(((i_plain + i_encoder) % 256), fp_encrypt);
            }
            else
               printf(too_many_null_chars_msg_format, cp_helper);
         }
         fclose(fp_encrypt);
      }
      else
         printf(too_small_msg_format, cp_helper);
      fclose(fp_plain);
   }
   else
      printf("/ / / plain-text file \"%s\" not found! \\ \\ \\\n", cp_plain);
   return(is_ok);
}


int decode(const char *restrict cp_helper, const char *restrict cp_plain,
      const char *restrict cp_encoded,
	  const int both_encoder_and_plain_are_text,
          FILE *restrict fp_encoder)
{
   int i, i_encrypt, i_encoder, diff, ok_to_continue, is_ok;
   FILE *restrict fp_plain, *restrict fp_encrypt;
/* */
   is_ok = 0;  /* FALSE */
   fp_encrypt = fopen(cp_encoded, "rb");
   if (fp_encrypt != NULL)
   {
      if ((filelength(fileno(fp_encoder)) - filelength(fileno(fp_encrypt)))
            >= 32768L)
   /* Even if the encoder file has size of only 32 kb more than the size of
   the encrypted file, and furthermore I throw away the first 2.5 kb of the
     encoder file, I'll still avoid hitting EOF for the encoder file. (This
  assumes that the encoder file has less than 30,208 null-characters in it,
     or else that the encoder file is MUCH bigger than the encrypted file.) */
      {
         if (both_encoder_and_plain_are_text)
            fp_plain = fopen(cp_plain, "wt");
         else
            fp_plain = fopen(cp_plain, "wb");
         for (i = 0; i < 2560; i++)  i_encoder = fgetc(fp_encoder);
              /* throw away first 2.5 kilobytes of encoder file. Hopefully,
                         encoder file's header file isn't more than 2.5 kb. */
         ok_to_continue = 1;  /* TRUE */
         for (i_encrypt = fgetc(fp_encrypt);
               ((i_encrypt != EOF) && (ok_to_continue));
               i_encrypt = fgetc(fp_encrypt))
         {
            for (i_encoder = fgetc(fp_encoder);
                  i_encoder == 0;                              /* null char */
                  i_encoder = fgetc(fp_encoder))
               ;            /* Because I did the same thing when encrypting */
            ok_to_continue = (i_encoder != EOF);
            is_ok = ok_to_continue;
            if (ok_to_continue)
            {
               if (both_encoder_and_plain_are_text)
                  if (i_encoder == 10) /* \n */ i_encoder = 128;
                  else;
               else;
               diff = (((i_encrypt + 256) - i_encoder) % 256);
               if (both_encoder_and_plain_are_text)
                  if (diff == 128)  diff = 10;  /* \n */
                  else;
               else;
               fputc(diff, fp_plain);
            }
            else
               printf(too_many_null_chars_msg_format, cp_helper);
         }
         fclose(fp_plain);
      }
      else
         printf(too_small_msg_format, cp_helper);
      fclose(fp_encrypt);
   }
   else
      printf("/ / / encrypted file \"%s\" not found! \\ \\ \\\n", cp_encoded);
   return(is_ok);
}


void get_filename(char *cp_start)
{
   char *cp;
   int intchar;
/* */
   cp = cp_start;
   for (intchar = getchar(); intchar != (int) '\n'; intchar = getchar())
      *(cp++) = (char) intchar;
   *cp = '';
}


void main(int argc, char **argv)
{
   char yn_block[16], helper_block[261], plain_block[261], encodd_block[261];
   int both_encoder_and_plain_are_text, choice, is_ok;
   FILE *restrict fp_encoder;
/* */
   printf("ENCO.EXE");
   both_encoder_and_plain_are_text = 0;  /* FALSE */
   if (argc <= 3)
   {
      printf("\nCommand-line format: \"enco helper-file plain-file ");
      printf("encoded-file [/t]\"\n");
      printf(
     "\nInstead, now you must write the name of the helper file (used for\n");
      printf("encoding/decoding).\n");
      printf(
        "\nRecommended for helper file: *.MP3  *.MPG  *.OGG  *.PNG  *.WMA\n");
      printf("\nAlso, helper-file must 1) be bigger than the file to be ");
      printf("encrypted/decrypted;\n");
      printf("and 2) helper-file must not change (be deleted or ");
      printf("overwritten) between the\n");
      printf("time that the plain-file gets encrypted and when the ");
      printf("encrypted file is\n");
      printf("unencrypted.\n");
      printf("\nName of helper file? ");
      get_filename(&helper_block[0]);
      printf("\nName of unencrypted (or decrypted) file? ");
      get_filename(&plain_block[0]);
      printf("\nName of encrypted file? ");
      get_filename(&encodd_block[0]);
   }
   else
   {
      strcpy(&helper_block[0], *(argv + 1));
      strcpy(&plain_block[0], *(argv + 2));
      strcpy(&encodd_block[0], *(argv + 3));
      if (argc > 4)
         if ((strcmp(*(argv + 4), "\\t") == 0)
               || (strcmp(*(argv + 4), "\\T") == 0)
               || (strcmp(*(argv + 4), "/t") == 0)
               || (strcmp(*(argv + 4), "/T") == 0))
            both_encoder_and_plain_are_text = 1;  /* TRUE */
         else;
      else;
   }
   for (choice = 0; ((choice <= 0) || (choice >= 3)); )
   {
      printf("\nDo you want to (1) encode or (2) decode? ");
      gets(&yn_block[0]);
      sscanf(&yn_block[0], "%d", &choice);
   }
   if (both_encoder_and_plain_are_text)
      fp_encoder = fopen(&helper_block[0], "rt");
   else
      fp_encoder = fopen(&helper_block[0], "rb");
   if (fp_encoder != NULL)
   {
      if (choice - 1)
         is_ok = decode(&helper_block[0], &plain_block[0], &encodd_block[0],
               both_encoder_and_plain_are_text, fp_encoder);
      else
         is_ok = encode(&helper_block[0], &plain_block[0], &encodd_block[0],
               both_encoder_and_plain_are_text, fp_encoder);
      fclose(fp_encoder);
      if (is_ok)
         printf("\n{{{ Program completed successfully }}}\n");
      else
         printf("\n*** ABNORMAL PROGRAM TERMINATION ***\n");
   }
   else
      printf("/ / / helper file \"%s\" not found! \\ \\ \\\n",
            &helper_block[0]);
   printf("\n(Press any key) ");
   choice = getchar();
   fflush(stdin);
}