Click here to Skip to main content
15,885,933 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I've been having a few issues with this program that is meant to work with RSA. It reads data from a file in parts and has no linker or run time errors other than a crash during decryption. I am a little bit at a brick wall with why this doesn't work and I have no idea how to remedy this issue.
C++
#include <iostream>
#include <string>
// Linked via .lib file with VS2015 console
#include "lib/rsa.h"
#include "lib/modes.h"      
#include "lib/filters.h"
#include "lib/files.h"
#include "lib/hex.h"
#include "lib/randpool.h"
#include "lib/cryptlib.h"
#include "lib/seed.h"
#include "lib/osrng.h"

using namespace CryptoPP;
using namespace std;

class Core
{
private:
	// Pretty much everything under private in this class is from the wiki. So far it all seems to work rather well.
	void Load(const string& filename, BufferedTransformation& bt)
	{
		// http://www.cryptopp.com/docs/ref/class_file_source.html
		FileSource file(filename.c_str(), true /*pumpAll*/);

		file.TransferTo(bt);
		bt.MessageEnd();
	}

	void Save(const string& filename, const BufferedTransformation& bt)
	{
		// http://www.cryptopp.com/docs/ref/class_file_sink.html
		FileSink file(filename.c_str());

		bt.CopyTo(file);
		file.MessageEnd();
	}

	void SavePrivateKey(const string& filename, const PrivateKey& key)
	{
		// http://www.cryptopp.com/docs/ref/class_byte_queue.html
		ByteQueue queue;
		key.Save(queue);

		Save(filename, queue);
	}

	void SavePublicKey(const string& filename, const PublicKey& key)
	{
		// http://www.cryptopp.com/docs/ref/class_byte_queue.html
		ByteQueue queue;
		key.Save(queue);

		Save(filename, queue);
	}

	void LoadPrivateKey(const string& filename, PrivateKey& key)
	{
		// http://www.cryptopp.com/docs/ref/class_byte_queue.html
		ByteQueue queue;

		Load(filename, queue);
		key.Load(queue);	
	}

	void LoadPublicKey(const string& filename, PublicKey& key)
	{
		// http://www.cryptopp.com/docs/ref/class_byte_queue.html
		ByteQueue queue;

		Load(filename, queue);
		key.Load(queue);	
	}
	
	// Encode and Decode do what they are labled as
	void encode(string in,string &out,string keyFileName)
	{
		AutoSeededRandomPool rng;
		RSA::PublicKey publicKey;
		LoadPublicKey(keyFileName,publicKey);
		RSAES_OAEP_SHA_Encryptor e(publicKey);
			StringSource ss1(in, true,
				new PK_EncryptorFilter(rng, e,
					new StringSink(out)
			   ) // PK_EncryptorFilter
			); // StringSource
	}
	
	void decode(string in,string &out,string keyFileName)
	{
		AutoSeededRandomPool rng;
		RSA::PrivateKey privateKey;
		LoadPrivateKey(keyFileName,privateKey);
		RSAES_OAEP_SHA_Decryptor d(privateKey);
		StringSource ss2(in, true,
			new PK_DecryptorFilter(rng, d,
				new StringSink(out)
		   ) // PK_DecryptorFilter
		); // StringSource
	}
	
public:
	// The title says it all
	void makeKeys()
	{
		AutoSeededRandomPool rnd;
		RSA::PrivateKey rsaPrivate;
		rsaPrivate.GenerateRandomWithKeySize(rnd, 3072);

		RSA::PublicKey rsaPublic(rsaPrivate);

		SavePrivateKey("rsa-private.Rkey", rsaPrivate);
		SavePublicKey("rsa-public.Rkey", rsaPublic);
	}


	void encodeFile(string inputFileName,string outputFileName,string keyFileName)
	{
		// Stuff that runs the file
		string in;
		size_t buffer_size = 342;
		string buffer(buffer_size,'\0');
		ifstream fileStreamIn(inputFileName,ios::binary);
		ofstream fileStreamOut(outputFileName,ios::binary);
		while(fileStreamIn)
		{
			fileStreamIn.read(&buffer.front(), buffer_size);
			size_t count = fileStreamIn.gcount();
			buffer.resize(count);
			encode(buffer,in,keyFileName);		
			buffer = in;			
			fileStreamOut.write(buffer.c_str(),buffer.length()); 
			buffer.resize(buffer_size);
			buffer = "";
			in = "";
			if (!count) 
			break;  
		}
		fileStreamIn.close();
		fileStreamOut.close();
	}
	// This function for one reason or another seems to cause a crash
	void decodeFile(string inputFileName,string outputFileName,string keyFileName)
	{
		// Stuff that runs the file
		string in;
		size_t buffer_size = 384;
		string buffer(buffer_size,'\0');
		ifstream fileStreamIn(inputFileName,ios::binary);
		ofstream fileStreamOut(outputFileName,ios::binary);
		while(fileStreamIn)
		{
			fileStreamIn.read(&buffer.front(), buffer_size);
			size_t count = fileStreamIn.gcount();
			buffer.resize(buffer_size);
		
			// This bit of logic is to prevent crash
			if(count == buffer_size)
			{
				decode(buffer,in,keyFileName);
			}
			buffer = in;
			fileStreamOut.write(buffer.c_str(),buffer.length()); 

			buffer = "";
			in = "";
			if (!count) 
			break;  
		}
		fileStreamIn.close();
		fileStreamOut.close();
	}
};

void main ()
{
	Core Function;
//	Function.makeKeys(); 
	Function.encodeFile("in","out","rsa-public.Rkey");
	Function.decodeFile("out","out1","rsa-private.Rkey");	

}
// The build file using the .lib file
/*
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64
del main.exe
cl /EHsc /c main.cpp
link /LTCG main.obj cryptlibX64.lib /out:main.exe
del main.obj
timeout /t 9000 /nobreak
*/
Posted
Comments
CPallini 20-Nov-15 5:46am    
You should debug it and report more useful info here, in order to get help.

1 solution

This line might be the source:
fileStreamIn.read(&buffer.front(), buffer_size);

The content of a std::string should not be modified directly. You are using front() here which allows the modification of the referenced character. But you are using it to modifiy the string content. Because ifstream::read() does not append a NULL character, this might work with the first call (when the string is still filled with NULL chars) but results in remaining characters when less has been read in which probably occurs with the last reading.
[EDIT]
Additionally the string is re-used later by assigning the encoded string and then set to an empty string (which effectively places a NULL character in front). As a result there are random characters at the end of the string when the next reading from file did not not use the full length.
[/EDIT]

I would not use std::string as buffer type here but just a char array and append a NULL byte after reading. If you don't want to rewrite your decode function to use a char*, just pass a temporary string (std::string(buffer)).
[EDIT]
When using a char array, the size must be one more than the max. number of chars read in from file to store the terminating NULL character.
[EDIT]

[EDIT 2]
A possible solution reading the whole file into a string and decoding it afterwards (untested):
C++
void decodeFile(string inputFileName,string outputFileName,string keyFileName)
{
    string fileBuffer;
    ifstream fileStreamIn(inputFileName,ios::binary);
    ofstream fileStreamOut(outputFileName,ios::binary);
    while(fileStreamIn)
    {
        // Local buffer to be filled from file
        char readBuffer[385];
        // Read from file letting space for terminating NULL char
        fileStreamIn.read(readBuffer, sizeof(read_buffer) - 1);
        size_t count = fileStreamIn.gcount();
        // Terminate local string
        readBuffer[count] = '\0';
        // Append to input file buffer
        fileBuffer += readBuffer;
    }
    std::string decoded;
    decode(fileBuffer, decoded, keyFileName);
    fileStreamOut.write(decoded.c_str(), decoded.length()); 
    fileStreamIn.close();
    fileStreamOut.close();
    }
};


And another tip:
When passing strings you should pass them by reference. Actually you will always create a copy of the string. Additionally, strings that are not modified should be const. The resulting function definitions are then:
C++
void decodeFile(const string& inputFileName, const string& outputFileName, const string& keyFileName);
void decode(const string& in, string &out, const string& keyFileName);

Similar for the other functions. But note that the const modifier can not be used when calling another function that expects a non-const parameter (this might be so for LoadPrivateKey() and ss2()).
 
Share this answer
 
v3
Comments
[no name] 20-Nov-15 7:18am    
My 5.
A question: Is "buffer[fileStreamIn.gcount()]= '\0'" a bad idea, of course with the necessary range checks?
Jochen Arndt 20-Nov-15 7:27am    
Thank you.

It is allowed, so it is not really a bad idea. And it is missing here and might be the real source of the problem.

But using a char reference to modfiy multiple characters is a bad idea. It might work, but would be worth to check the C++ reference. If it is not explicitly allowed the behaviour depends on the implementation.

And while writing this I recognized that I have to edit may answer.
[no name] 20-Nov-15 13:29pm    
:-)
Member 10657083 21-Nov-15 8:10am    
I plan to rewrite the code using the char array as the buffer. But how would that look on the code above I am not sure how to implement that.
Jochen Arndt 21-Nov-15 8:35am    
See my updated answer.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900