Simple File Encryption in C++: Rivest Cipher 4 (RC4), Byte Inversion, Cycling and XOR

Taking a detour from the usually cerebral exposition of algorithms, this time I’ll just offer a simple C++ implementation of RC4 cipher, including initialization. Of course, there is a ton of material you can find on it, so I won’t bore you here with it, just straight code. Attached is the file presented below, you can just take it, put to MinGW or some other standardized C++ compiler (you can make it compatible with C only in a few keystrokes as there is no real object orientation.)

The code below has some duplications but, since it’s simple enough, I thought to keep it that way. There are four file protection methods: byte inversion, byte cylcing, encypting one file using another file as a key (XOR) and RC4 (requires password for initialization of the stream.)

file_encryptor.cpp

/*
************************************************************
Author:       Monsi Terdex
Description:  RC4 cipher along with other, simpler methods
************************************************************
*/


#include <iostream>
#include <string>
#include <cstdlib>
#include <fstream>


using namespace std;
const unsigned int blockSize = 0x10000;
// these variables are RC4 facilities
unsigned char S [0x100]; // dec 256
unsigned int i, j;
// target file path and key file used for xor one-time-pad
string loadedFile;
string keyFile;
// functional prototypes
void rc4_init(unsigned char *, unsigned int);
unsigned char rc4_output(void);
void xorCipher (void);
void byteCipher (int);
char cycle (char);
int main (){
int mode;
     cout << " * * * File Protector * * *\n"
     << "Enter the cipher mode:\n"
     << "Mode 1: byte inversion\n"
      << "Mode 2: byte cycle\n"
     << "Mode 3: xor cipher\n"
     << "Mode 4: RC4 cipher\n"; cin >> mode;
     switch (mode) {
          case 1:
               byteCipher (0);
               break;
          case 2:
               byteCipher (1);
               break;
          case 3:
               xorCipher();
               break;
          case 4:
               byteCipher(2);
               break;
          default:
               cout << "Invalid mode\n";
          break;
     }
     cout << "\nCipher completed.\n"
               << "Program terminated.\n";
}
// ---------------------------------------------------------------------------
/* status: completed
* description:
* XOR cipher implementaiton, key file must be larger or equal in size to
* the message file
*/
void xorCipher () {
     int bufferSize = blockSize; // arbitrary choice of buffer size
     int difference = 0; // difference between current position and file size
     int targetFilePointer = 0; // starting at the beginning of the target file
     int targetFileSize = 0;
     int keyFileSize = 0;
     fstream fileStream; // using fstream for binary i/o for target file
     fstream keyStream; // using fstream for binary i/o for key file
     // target file is opened on this line
     // all flags must be present for successful completition of this step
     fileStream.open (loadedFile.c_str(), ios::in | ios::out | ios::binary);
     // verifying if target file was opened successfully
     if (!fileStream.is_open()) {
          cout << "Unable to load specified target file\n";
          exit(EXIT_FAILURE);
     }
     // key file is opened on this line
     keyStream.open(keyFile.c_str(), ios::in | ios::binary);
     if (!keyStream.is_open()) {
          cout << "Unable to load specified target file\n";
          fileStream.close(); // closing already opened target file stream
          exit(EXIT_FAILURE);
     }
     // setting the reading position of the target file stream to the end
     fileStream.seekg (0, ios::end);
     // getting the position of the reading pointer, thereby retrieving target file size
     targetFileSize = fileStream.tellg();
     // setting position of the reading pointer to the start of the file
     fileStream.seekg (0, ios::beg);
     keyStream.seekg (0, ios::end);
     keyFileSize = keyStream.tellg();
     keyStream.seekg (0, ios::beg);
     if (keyFileSize < targetFileSize) {
          cout << "Unable to carry out XOR cipher. Key file is smaller than target file.\n";
          fileStream.close();
          keyStream.close();
          exit (EXIT_FAILURE);
     }
     // adjusting interface
     // loop until file pointer has reached the end of the target file
     while (targetFilePointer < targetFileSize) {
     // initializing byte array of certain specified size
          char buffer [blockSize];
          char keyBuffer [blockSize];
          // calculating difference between the current position in the file and file size
          difference = targetFileSize - targetFilePointer;
          // if the difference is less than the buffer size, then no need to fill the
          // buffer completely, only by the difference
          if (difference < bufferSize) {
               bufferSize = difference;
          }
          fileStream.seekg (targetFilePointer);
          fileStream.read (buffer, bufferSize);
          keyStream.seekg (targetFilePointer);
          keyStream.read (keyBuffer, bufferSize);
          for (int i = 0; i < bufferSize; i++){
               // inverting every byte in the buffer
               buffer[i] = buffer[i] ^ keyBuffer [i];
          }
          // setting writing pointer to the current location
          fileStream.seekp (targetFilePointer);
          // writing the inverted file
          fileStream.write (buffer, bufferSize);
          // incrementing current file pointer by the amount of buffer
targetFilePointer += bufferSize;
          // adjusting interface
     }
     // closing the target file stream
     fileStream.close();
     // closing the key file stream
     keyStream.close();
     // resetting interface
}
//---------------------------------------------------------------------------
/* status: completed
* description:
* general subroutine responsible for iterating over every byte in
* in the target file and modifying based on mode
*/
void byteCipher (int mode){
     int bufferSize = blockSize; // arbitrary choice of buffer size
     int difference = 0; // difference between current position and file size
     int filePointer = 0; // starting at the beginning of a file
     fstream fileStream; // using fstream for binary i/o
     // file is opened on this line
     // all flags must be present for successful completition of this step
     cout << "Specify file path\n";
     cin.ignore();
     getline (cin, loadedFile);
     fileStream.open (loadedFile.c_str(), ios::in | ios::out | ios::binary);
     // verifying if file was opened successfully
     if (!fileStream.is_open()) {
          cout << "Unable to load specified file.\n";
          exit (EXIT_FAILURE);
     }
     // setting the reading position of the file stream to the end
     fileStream.seekg (0, ios::end);
     // getting this position of the reading pointer, thereby retrieving file size
     int fileSize = fileStream.tellg();
     // setting position of the reading pointer to the start of the file
     fileStream.seekg (0, ios::beg);
     // RC4 facilities
     string keyString = "";
     if (mode == 2) {
          cout << "Please enter the RC4 key (8 chars min)\n";
          //cin.ignore();
          getline (cin, keyString);
          //cin.getline(key, 256);
          cout << "The password is: " << keyString;
          //setting up RC4 key
          rc4_init ((unsigned char *)keyString.c_str(), keyString.length ()); // setting up RC4 using the password
     }
     // RC4 setup
     // loop until file pointer has reached the end of the file
     cout << "\nBeginning encryption\n";
     while (filePointer < fileSize) {
          // initializing byte array of certain specified size
          char buffer [blockSize];
          // calculating difference between the current position in the file and file size
     difference = fileSize - filePointer;
          // if the difference is less than the buffer size, then no need to fill the
          // buffer completely, only by the difference
          if (difference < bufferSize) {
               bufferSize = difference;
          }
          fileStream.seekg (filePointer);
          fileStream.read (buffer, bufferSize);
          for (int i = 0; i < bufferSize; i++){
               // going over every byte in the file
               switch (mode) {
                    case 0: // inversion
                         buffer[i] = ~buffer[i];
                         break;
                    case 1: // cycle
                         buffer [i] = cycle (buffer [i]);
                         break;
                    case 2: // RC4
                         buffer [i] = buffer [i] ^ rc4_output();
                         break;
               }
          }
          // setting writing pointer to the current location
          fileStream.seekp(filePointer);
          // writing the inverted file
          fileStream.write(buffer, bufferSize);
          // incrementing current file pointer by the amount of buffer
          filePointer += bufferSize;
          // adjusting interface
          float progress = (float) filePointer / (float) fileSize * 100.0f;
          cout << '\r';
          cout << "Completed: "
               << (int) progress
               << "%"; } // closing the stream fileStream.close(); // resetting interface } //---------------------------------------------------------------------------

/*status: completed

* description:

* byte cycling algorithm

*/

char cycle (char value) {

     int leftMask = 170;

     int rightMask = 85;

     int iLeft = value & leftMask;

     int iRight = value & rightMask;

     iLeft = iLeft >> 1;
     iRight = iRight << 1;
     return iLeft | iRight;
}
// ---------------------------------------------------------------------------
/* status: completed
* description:
* RC4 stream initializer
*/
void rc4_init (unsigned char *key, unsigned int key_length) {
     for (i = 0; i < 0x100; i++)
          S[i] = i;
          for (i = j = 0; i < 0x100; i++) {
               unsigned char temp;
               j = (j + key[i % key_length] + S[i]) & 0xFF;
               temp = S[i];
               S[i] = S[j];
               S[j] = temp;
          }
          i = j = 0;
     }
// ---------------------------------------------------------------------------
/* status: completed
* description:
* RC4 stream byte generator
*/
unsigned char rc4_output() {
     unsigned char temp;
     i = (i + 1) & 0xFF;
     j = (j + S[i]) & 0xFF;
     temp = S[i];
     S[i] = S[j];
     S[j] = temp;
     return S[(S[i] + S[j]) & 0xFF];
}
//---------------------------------------------------------------------------

About these ads

~ by Monsi.Terdex on May 17, 2013.

3 Responses to “Simple File Encryption in C++: Rivest Cipher 4 (RC4), Byte Inversion, Cycling and XOR”

  1. Hi Monsi,

    Thank you for this its helpfull. I only have 1 question about this.
    How do you decrypt the file if you have it encrypted.

    For example i am going for case1

    I let an .res file to be encrypted this is working as it should be.
    Now that .res file will be read by an other program but i need to decrypt it first inside that program. Could you help me with this?

    With kind regards,

    Thomas

    • Thomas,

      I think you should be able to apply the same function twice to decrypt – this is a symmetric encryptor.

      Let me know if that answers your question.

      Monsi

  2. Monsi,

    Thank you for answering back.
    I am looking for something like this only an bit different. This code can do multipe things however i am running an game made in C++ that is using an .exe file to read an .res file that contains data for the game. Now i am looking for an simple encrypter to encrypt the .res file and i need to past the code inside the file.cpp that is used by the game exe to decrypt the .res file so the game can read it.
    Hopefully it makes any sence. I can provide the sample code so you that you will know how i want it.

    So what i am looking for is just 1 piece of the code that will let me encrypt the .res file.

    With kind regards

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 

Normal Boy

Nothing out of the ordinary

Data Engineering Blog

Compare different philosophies, approaches and tools for Analytics.

Follow

Get every new post delivered to your Inbox.

Join 31 other followers

%d bloggers like this: