RC4 Encrypt Binary Data

I am trying to read in the contents of an entire file into a string then use the class someone else has written to RC4 encrypt the data. This works fine when testing with an actual string I define in the program, but when I use a file, it only reads the first 3 bytes or so (looks like it stops when it hits a null byte (00)). The output is as follows:

File read
File length: 192308
Encryption results: ═╢▌
Decryption results: MZÉ

I am just using a random windows executable to test before I move on to video files. Any ideas why it appears the null byte causes the issue with binary data but not with my string? Please let me know what other information would be helpful.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include "stdafx.h"
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
using std::cout;
using std::endl;

class CRC4
{
#define SWAP(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
private:
	unsigned char sbox[256];	// Encryption array             
	unsigned char key[256], k;  // Numeric key values           
	int  m, n, i, j, ilen;		// counters

public:
	CRC4()
	{
		memset(sbox, 0, 256);
		memset(key, 0, 256);
	}
	virtual ~CRC4()
	{
		memset(sbox, 0, 256);  // remove Key traces in memory  
		memset(key, 0, 256);
	}

	char *Encrypt(char *pszText, char *pszKey)
	{
		i = 0, j = 0, n = 0;
		ilen = (int)strlen(pszKey);

		for (m = 0; m < 256; m++)  // Initialize the key sequence 
		{
			*(key + m) = *(pszKey + (m % ilen));
			*(sbox + m) = m;
		}
		for (m = 0; m < 256; m++)
		{
			n = (n + *(sbox + m) + *(key + m)) & 0xff;
			SWAP(*(sbox + m), *(sbox + n));
		}

		ilen = (int)strlen(pszText);
		for (m = 0; m < ilen; m++)
		{
			i = (i + 1) & 0xff;
			j = (j + *(sbox + i)) & 0xff;
			SWAP(*(sbox + i), *(sbox + j));  // randomly Initialize the key sequence 
			k = *(sbox + ((*(sbox + i) + *(sbox + j)) & 0xff));
			if (k == *(pszText + m))       // avoid '\0' among the encoded data 
				k = 0;
			*(pszText + m) ^= k;
		}

		return pszText;
	}

	char *Decrypt(char *pszText, char *pszKey)
	{
		return Encrypt(pszText, pszKey);  // uses the same function as encoding */
	}
};

int main()
{
	
	CRC4 crc4;

	//std::string dataToEncrypt = "String\n to be encrytped";
	std::string encryptionKey = "EncryptionK3y!23";

	// read in the entire file into a string variable 
	std::ifstream f("test.exe", std::ifstream::in | std::ifstream::binary);
	std::string dataToEncrypt = std::string(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());

	cout << "File read" << endl;
	cout << "File length: " << dataToEncrypt.length() << endl;

	// convert string to char* to call encrypt function
	char* data = new char[dataToEncrypt.length() + 1];
	std::strcpy(data, dataToEncrypt.c_str());
	// convert key to char*
	char* key = new char[encryptionKey.length() + 1];
	std::strcpy(key, encryptionKey.c_str());

	// call function to encrypt (note the dataToEncrypt variable will be overwritten with the encrypted results
	cout << "Encryption results: " << crc4.Encrypt(data, key) << endl;

	// call function to decrypt (really the same as encrypt but prefer a different name)
	cout << "Decryption results: " << crc4.Decrypt(data, key) << endl;

    return 0;
}
 
memset(sbox, 0, 256);  // remove Key traces in memory 

If this is the intent, then those arrays are missing a volatile qualifier at least.
The right way to fill volatile memory is std::fill
Last edited on
Thanks for the quick response. I will address that. Any ideas on the null byte issue as well?
> Any ideas why it appears the null byte causes the issue with binary data but not with my string?
Yes, you can't use strlen or strcpy with binary data.
The first \0 that is seen is the end of the data, which will be way before the end of the file.

Nor should you be trying to print binary data in it's raw form, for pretty much the same reason.


Thanks, makes sense and I cannot believe I missed that. What would the binary safe versions then? memcpy instead of strcpy?

I edited my main function to look like the following, however it does not appear to actually encrypt the file. It does the first few bytes only

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

int main()
{
	
	CRC4 crc4;

	//std::string dataToEncrypt = "String\n to be encrytped";
	std::string encryptionKey = "EncryptionK3y!23";

	// read in the entire file into a string variable 
	std::ifstream f("test.exe", std::ifstream::in | std::ifstream::binary);
	std::string dataToEncrypt = std::string(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());

	cout << "File read" << endl;
	cout << "File length: " << dataToEncrypt.length() << endl;

	char* data = new char[dataToEncrypt.length() + 1];
	memcpy(data, dataToEncrypt.c_str(), dataToEncrypt.length());

	char* key = new char[encryptionKey.length() + 1];
	memcpy(key, encryptionKey.c_str(), encryptionKey.length());

	cout << "Writing contents to encrypted file" << endl;
	std::ofstream outFile("test.rc4", std::ios::binary);
	outFile.write(crc4.Encrypt(data, key), dataToEncrypt.length());
	outFile.close();

    return 0;
}


Edit: nevermind, I see the issue you were referring to with strlen in the encrypt function now. Will try and figure out how to work around that, but would appreciate any ideas to get the true length: ilen = (int)strlen(pszKey);
Last edited on
Pass your buffer length as another parameter.
Ok, hopefully last issue. When I compare the original file to the decrypted file, I see a few places were the bytes do not match. The image below shows it, but essentially where there should be a null byte in the file header it will be replaced with something like 06. I am not sure why this is occurring. Any help on what is wrong about the encryption/decryption routines would be appreciated.

https://ibb.co/znzrJ4m

Thanks in advance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include "stdafx.h"
#include <string>
#include <iostream>
#include <fstream>
#include <algorithm>
using std::cout;
using std::endl;

class CRC4
{
#define SWAP(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
private:
	unsigned char sbox[256];	// Encryption array             
	unsigned char key[256], k;  // Numeric key values           
	int  m, n, i, j, ilen;		// counters

public:
	CRC4()
	{
		memset(sbox, 0, 256);
		memset(key, 0, 256);
	}
	virtual ~CRC4()
	{
		memset(sbox, 0, 256);  // remove Key traces in memory  
		memset(key, 0, 256);
	}

	char *Encrypt(char *pszText, char *pszKey, int szTextLength, int szKeyLength)
	{
		i = 0, j = 0, n = 0;
		//ilen = (int)strlen(pszKey);  // stlen terminates at null so causes an issue with binary data. Can be used for strings
		ilen = szKeyLength;
		
		for (m = 0; m < 256; m++)  // Initialize the key sequence 
		{
			*(key + m) = *(pszKey + (m % ilen));
			*(sbox + m) = m;
		}
		for (m = 0; m < 256; m++)
		{
			n = (n + *(sbox + m) + *(key + m)) & 0xff;
			SWAP(*(sbox + m), *(sbox + n));
		}

		//ilen = (int)strlen(pszText); // same comments as above, string safe
		ilen = szTextLength;
		for (m = 0; m < ilen; m++)
		{
			i = (i + 1) & 0xff;
			j = (j + *(sbox + i)) & 0xff;
			SWAP(*(sbox + i), *(sbox + j));  // randomly Initialize the key sequence 
			k = *(sbox + ((*(sbox + i) + *(sbox + j)) & 0xff));
			if (k == *(pszText + m))       // avoid '\0' among the encoded data 
				k = 0;
			*(pszText + m) ^= k;
		}

		return pszText;
	}

	char *Decrypt(char *pszText, char *pszKey, int szTextLength, int szKeyLength)
	{
		return Encrypt(pszText, pszKey, szTextLength, szKeyLength);  // uses the same function as encoding
	}
};



int main()
{
	
	CRC4 crc4;

	//std::string dataToEncrypt = "String\n to be encrypted";
	std::string encryptionKey = "test";

	cout << "Reading contents of file" << endl;

	// read in the entire file into a string variable 
	std::ifstream f("test.exe", std::ifstream::in | std::ifstream::binary);
	std::string dataToEncrypt = std::string(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());

	cout << "File length: " << dataToEncrypt.length() << endl;

	char* data = new char[dataToEncrypt.length()];
	memset(data, 0x00, dataToEncrypt.length()); // avoid junk characters
	memcpy(data, dataToEncrypt.c_str(), dataToEncrypt.length());

	char* key = new char[encryptionKey.length()];
	memset(key, 0x0, encryptionKey.length());
	memcpy(key, encryptionKey.c_str(), encryptionKey.length());

	cout << "Encryption key: "; 
	for (int i = 0; i < encryptionKey.length(); i++)
		std::cout << key[i]; 
	cout << endl;

	// ********** Example for use with strings **********
	// call function to encrypt (note the dataToEncrypt variable will be overwritten with the encrypted results
	//cout << "Encryption results: " << crc4.Encrypt(data, key) << endl;

	// call function to decrypt (really the same as encrypt but prefer a different name)
	//cout << "Decryption results: " << crc4.Decrypt(data, key) << endl;

	// encrypt the file
	cout << "Writing contents to encrypted file" << endl;
	std::ofstream outFile("test.rc4", std::ios::binary);
	outFile.write(crc4.Encrypt(data, key, dataToEncrypt.length(), encryptionKey.length()), dataToEncrypt.length());
	outFile.close();

	// decrypt the file
	cout << "Decrypting the RC4 encrypted file" << endl;
	// read in the entire file into a string variable 
	std::ifstream f2("test.rc4", std::ifstream::in | std::ifstream::binary);
	std::string dataToDecrypt = std::string(std::istreambuf_iterator<char>(f2), std::istreambuf_iterator<char>());

	cout << "Length of encrypted file: " << dataToDecrypt.length() << endl;

	char* decryptData = new char[dataToDecrypt.length()];
	memset(decryptData, 0x00, dataToDecrypt.length());
	memcpy(decryptData, dataToDecrypt.c_str(), dataToDecrypt.length());

	cout << "Writing decrypted contents to file" << endl;
	std::ofstream decryptedFile("decrypted.exe", std::ios::binary);
	decryptedFile.write(crc4.Decrypt(decryptData, key, dataToDecrypt.length(), encryptionKey.length()), dataToDecrypt.length());
	decryptedFile.close();

	cout << "Finished" << endl;

    return 0;
}
Last edited on
Topic archived. No new replies allowed.