Hi all,

I'm currently working on a RC4 project for my computer security class. I have looked at the wikipedia page for RC4, and I'm trying to adapt that algorithm to my problem.

I can get the correct encrypted text when I just cout the values. However, when I try to store those values into an array and then cout those values, its not the same. Could this be because they are stored into a pointer array, maybe printing out the address of the data?

Furthermore when I try to decrypt the encrypted text by XOR'ing the encrypted data again with the keystream, it does not produce the correct plaintext. Maybe, again, because its trying to use the address of the array?

Below is my code. Please have a look.

#include <iostream>
#include <cstring>
#include <string>
#include <fstream>

using namespace std;

unsigned char s[256];
unsigned int i, j;
int size;
char *memblock;
char *enblock;

void swap(unsigned char *s, unsigned int i, unsigned int j)
{
	unsigned char temp = s[i];
	s[i] = s[j];
	s[j] = temp;
}

void rc4_input (unsigned char *pass, unsigned int keylen)
{
	for (i = 0; i < 256; i++) 
		s[i] = i;

	for (i = 0, j = 0; i < 256; i++)
	{
		j = (j + pass[i % keylen] + s[i]) & 255;
		swap(s, i , j);
	}
	i = 0;
	j = 0;
}

unsigned char rc4_output()
{
	i = (i + 1) & 255;
	j = (j + s[i]) & 255;
	swap(s, i, j);

	return s[(s[i] + s[j]) % 255];
}

int main()
{
	//for simplicity key is set to 'Key'
	unsigned char* key[] = {(unsigned char*)"Key"};

	//create the keystream
	rc4_input(key[0], strlen((char*)key[0]));

	//plaintext is held in 'plain.txt'
	ifstream original ("plain.txt", ios::in|ios::ate);

	//get the size of the file
	size = (int) original.tellg();
	
	//allocate new memory blocks to hold the data
	memblock = new char [size];
	enblock = new char [size];

	original.seekg(0, ios::beg);

	//store the plaintext into the memory block
	original.read(memblock, size);

	original.close();
	
	//produces the correct encrypted text
	for (int x = 0; x < size; x++)
	{
		cout << (memblock[x] ^ rc4_output());
	}

	cout << endl;

	//XOR the plaintext with keystream and store in enblock
	//for some reason when I try to store the encrypted text in enblock
	//it is not the same as just writing to console
	for (int x = 0; x < size; x++)
	{
		enblock[x] = (memblock[x] ^ rc4_output());
		cout << enblock[x];	
	}

	cout << endl;

	//XOR the encrypted text with keystream 
	//(does not produce original text)
	for (int x = 0; x < size; x++)
	{
		cout << ((memblock[x] ^ rc4_output()) ^ rc4_output());
	}
	
	cout << endl;	
	delete[] memblock;
	delete[] enblock;
	return 0;	
}

I have solved my problem, so I'm posting this information in hopes that it will be of some use to somebody. I know that there are few simple rc4 implementations out there so hopefully this will help clear the fog for people that are trying to adapt rc4 to their application.

In this simple test program I read in plain text from a .txt file and store it to a dynamically created array. Then the keystream is created with which I can encrypt the plain text.

After encryption, you must reset your keystream by calling the rc4_output() function again. This was where my previous code was messed up. So after resetting the keystream you XOR the encrypted bits with the output from rc4_output(), giving you back the plain text. You can apply the basics of this code to various applications. I used this code as testing grounds before I applied it to my project I was working on. The project read in a .wav file, encrypted it then decrypted it back to its original state.

I do not take credit for the actual rc4 algorithm I used here. I borrowed the algorithm from wikipedia, though I did have to change a couple things, perhaps because the original algorithm from wikipedia was in C, not C++. Everything else I wrote.

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

using namespace std;

unsigned char s[256];
unsigned int i, j;
int size;
char *memblock;
char *enblock;

void swap(unsigned char *s, unsigned int i, unsigned int j)
{
	unsigned char temp = s[i];
	s[i] = s[j];
	s[j] = temp;
}

void rc4_input (unsigned char *pass, unsigned int keylen)
{
	for (i = 0; i < 256; i++) 
		s[i] = i;

	for (i = 0, j = 0; i < 256; i++)
	{
		j = (j + pass[i % keylen] + s[i]) % 256;
		swap(s, i , j);
	}
	i = 0;
	j = 0;
}

unsigned char rc4_output()
{
	i = (i + 1) % 256;
	j = (j + s[i]) % 256;
	swap(s, i, j);

	return s[(s[i] + s[j]) % 256];
}

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

	string input =  argv[1];
	
	if (input.length() > 16)
	{
		cout << "Please enter a 16 character key." << endl;
		exit (1);
	}

	unsigned char* key[16] = {(unsigned char*)argv[1]};

	//create the keystream
	rc4_input(key[0], strlen((char*)key[0]));

	//plaintext is held in 'plain.txt'
	ifstream original ("plain.txt", ios::in|ios::ate);

	//get the size of the file
	size = (int) original.tellg();
	
	//allocate new memory blocks to hold the data
	memblock = new char [size];
	enblock = new char [size];

	original.seekg(0, ios::beg);

	//store the plaintext into the memory block
	original.read(memblock, size);

	original.close();
	
	//encrypt
	for (int x = 0; x < size; x++)
	{
		enblock[x] = (memblock[x] ^ rc4_output());
	}

	//reset s array
	rc4_input(key[0], strlen((char*)key[0]));

	//decrypt
	for (int x = 0; x < size; x++)
	{
		enblock[x] = (enblock[x] ^ rc4_output());
	}

	//print out the plaintext
	for (int x = 0; x < size; x++)
	{
		cout << enblock[x];
	}

	delete[] memblock;
	delete[] enblock;
	return 0;	
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.