Reputation: 1
I am trying to decrypt an encrypted file Using the Crypto++ Library. The encryption seems to work perfectly as the file that I am encrypting has Cipher text when I open it. However , just in case I will give my encryption code should it be useful.
Here is my Encryption Code:
bool Cipher::encryptFile(const char* inputFileName, const char* outputFileName)
{
salt = generateRandomSalt();
generateRandomIV();
if (!deriveKeyFromPassword(storedPassword, strlen(storedPassword), salt))
{
cerr << "Key derivation failed" << endl;
return false;
}
if (iv.size() == 0 || key.size() == 0)
{
cerr << "Failed to generate IV" << endl;
}
try
{
ofstream outputFile(outputFileName, ios::binary | ios::trunc);
ifstream inputFile(inputFileName, ios::binary); //Input / Output files for actual encryption
if (!inputFile.is_open() || !outputFile.is_open())
{
cerr << "Error opening files" << endl;
return false;
}
outputFile.write(reinterpret_cast<const char*>(salt.data()), salt.size()); //IV and salt saved to output file
outputFile.write(reinterpret_cast<const char*>(iv.data()), iv.size());
//Show Salt and IV
cerr << "Read salt: ";
StringSource(reinterpret_cast<const byte*>(salt.data()), salt.size(), true, new HexEncoder(new FileSink(cerr)));
cerr << endl;
cerr << "Read IV: ";
StringSource(reinterpret_cast<const byte*>(iv.data()), iv.size(), true, new HexEncoder(new FileSink(cerr)));
cerr << endl;
CFB_Mode<AES>::Encryption encryption(key, key.size(), iv); //Choosing CFB algorithm with iv
StreamTransformationFilter filter(encryption, new FileSink(outputFileName)); //Data is encrypted and passed to output file
size_t fileSize = inputFile.tellg(); // Get the size of the file and reset the position to 0
inputFile.seekg(0);
vector<char> fileBuffer(fileSize); //Input contents of inputFile into buffer
inputFile.read(fileBuffer.data(), fileSize);
filter.Put(reinterpret_cast<const byte*>(fileBuffer.data()), fileSize); //Puts the fileBuffer content through the encryption filter
filter.MessageEnd(); //Ends Processsing
inputFile.close();
outputFile.close();
return true;
}
catch (const ifstream::failure& e) {
cerr << "File I/O error: " << e.what() << endl;
return false;
}
catch (const Exception& e) {
cerr << "Encryption error: " << e.what() << endl;
return false;
}
}
The below may be useless for my problem but I will give them just in case they are useful,they are functions I used for my encryption and decryption functions(except for Constructor and Destructor)
This is my constructor to get the user password so that PBKDF2 can be performed during the encryption and decryption.
Cipher::Cipher(const char* password)
{
size_t passwordLength = strlen(password);
storedPassword = new char[passwordLength + 1]; // Accounting for \0 char
strcpy_s(storedPassword, passwordLength + 1 , password); //strcpy was unsafe
}
This is the destructor to destroy any dangling objects and avoid memory leaks
Cipher::~Cipher() //Avoding memory leaks
{
delete[] storedPassword;
//Crypto++ manages memory interally no need to clean up objects from library
}
This is the function to derive the key from my given password in the constructor
bool Cipher::deriveKeyFromPassword(const char* password, size_t passwordLength, const SecByteBlock& salt)
{
const int iterations = 10000;
try {
key.resize(AES::DEFAULT_KEYLENGTH);
PKCS5_PBKDF2_HMAC<SHA256> PBKDF2; //The password-based key derivation function
PBKDF2.DeriveKey(key, key.size(), 0, reinterpret_cast<const byte*>(password), passwordLength, salt, salt.size(), iterations);
//Show the derived key
cerr << "Derived Key: ";
StringSource(reinterpret_cast<const byte*>(key.data()), key.size(), true, new HexEncoder(new FileSink(cerr)));
cerr << endl;
return true;
}
catch (const Exception e)
{
cerr << "Key Derivation error: " << e.what() << endl;
return false;
}
}
These are the two functions that generate a random Salt and a Random IV for random key Hashing and cipher text respectively , each time I encrypt a file
SecByteBlock Cipher::generateRandomSalt()
{
salt.resize(16); // 16 bytes
prng.GenerateBlock(salt, salt.size()); //A single salt is generated(Function name may be misleading)
cerr << "salt size: " << salt.size() << endl;
return salt;
}
bool Cipher::generateRandomIV()
{
iv.resize(AES::BLOCKSIZE); //128 bits(default)
prng.GenerateBlock(iv, iv.size()); //A single iv is generated(Function name may be misleading)
cerr << "IV size: " << iv.size() << endl;
return true;
}
This is the code in my main method to test encryption and decryption(by changing the cipher. each time)
int main()
{
const char* inputFileName = R"(C:path.<textfile>.txt)";
const char* otuputFileName = R"(C:path.<textfile>.txt)";
const char* password = "test";
Cipher cipher(password);
if (cipher.decryptFile(inputFileName, otuputFileName))
{
cout << "Decryption successfull" << endl;
}
else
{
cerr << "Decryption failed" << endl;
}
return 0;
}
Basically when I try to decrypt the file, I seem to have a problem with passing the cipher text through the decrypt filter, as when I do that all the data inside of the file disappears.
I will give the entire decryption function just in case it is useful and then below show only the relevant snippet.
Decryption function:
bool Cipher::decryptFile(const char* inputFileName, const char* outputFileName)
{
try {
ifstream inputFile(inputFileName, ios::binary);
ofstream outputFile(outputFileName, ios::binary | ios::trunc);
if (!inputFile.is_open() || !outputFile.is_open())
{
cerr << "Error opening files" << endl;
return false;
}
salt.resize(16);
iv.resize(16);
inputFile.read(reinterpret_cast<char*>(salt.data()) , salt.size());
inputFile.read(reinterpret_cast<char*>(iv.data()), iv.size());
cerr << "Read salt: ";
StringSource(reinterpret_cast<const byte*>(salt.data()), salt.size(), true, new HexEncoder(new FileSink(cerr)));
cerr << endl;
cerr << "Read IV: ";
StringSource(reinterpret_cast<const byte*>(iv.data()), iv.size(), true, new HexEncoder(new FileSink(cerr)));
cerr << endl;
if (!deriveKeyFromPassword(storedPassword, strlen(storedPassword), salt))
{
cerr << "Key derivation failed" << endl;
return false;
}
CFB_Mode<AES>::Decryption decryption(key, key.size(), iv); //Decryption Algorithm
StreamTransformationFilter filter(decryption, new FileSink(outputFileName)); //Decryption Filter
//inputFile.seekg(0, ios::end);
inputFile.seekg(salt.size() + iv.size()); //Skip salt and IV
size_t fileSize = inputFile.tellg();
inputFile.seekg(0);
vector<char> fileBuffer(fileSize);
inputFile.read(fileBuffer.data(), fileSize);
cerr << "size" << fileBuffer.size() << endl;
filter.Put(reinterpret_cast<const byte*>(fileBuffer.data()), fileSize);
filter.MessageEnd();
//Destory filter object
filter.Detach(new FileSink(outputFileName));
size_t outputSize = filter.MaxRetrievable();
cerr << "size after" << outputSize << endl;
if (outputSize > 0)
{
vector<byte> decryptedOutput(outputSize);
filter.Get(decryptedOutput.data(), outputSize);
// Write the decrypted data to the output file
outputFile.write(reinterpret_cast<const char*>(decryptedOutput.data()), outputSize);
cerr << "Data written" << endl;
}
else
{
cerr << "No data retrieved from the filter." << endl;
}
inputFile.close();
outputFile.close();
return true;
}
catch(const ifstream::failure& e)
{
cerr << "File I/O error: " << e.what() << endl;
return false;
}
catch(const Exception& e)
{
cerr << "Decryption error: " << e.what() << endl;
return false;
}
}
The problematic part is here:
CFB_Mode<AES>::Decryption decryption(key, key.size(), iv); //Decryption Algorithm
StreamTransformationFilter filter(decryption, new FileSink(outputFileName)); //Decryption Filter
//inputFile.seekg(0, ios::end);
inputFile.seekg(salt.size() + iv.size()); //Skip salt and IV
size_t fileSize = inputFile.tellg();
inputFile.seekg(0);
vector<char> fileBuffer(fileSize);
inputFile.read(fileBuffer.data(), fileSize);
cerr << "size" << fileBuffer.size() << endl;
filter.Put(reinterpret_cast<const byte*>(fileBuffer.data()), fileSize);
filter.MessageEnd();
//Destory filter object
filter.Detach(new FileSink(outputFileName));
size_t outputSize = filter.MaxRetrievable();
cerr << "size after" << outputSize << endl;
I am creating a new decryption algorithm and a new decryption filter for the outputFileName FileSink.I then skip the salt and iv in my cipher text file(As due to the best of my understanding these are stored at the beginning of the file) and read the file size while also starting from position 0. The contents are now put in a new fileBuffer(which excludes the salt and IV) to which then I proceed to pass these contents through the filter.
Up to this point it was not working so I tried to destroy the filter object to flush anything stored inside. When I retrieve the size after I still get 0 though, thus I am still left with this problem.
The output of putting a text file through encryption is as follows:
salt size: 16
IV size: 16
Derived Key: 4A68118A4B978617A7E38AAE2D91FDE6
Read salt: 42660BBE161C4E08E5DD4CBCC74CC952
Read IV: 7067030BACB4ED6F5AC0F1465383E46E
Encryption successful
The output of putting the new cipher file(from the previous text file) is as follows:
Derived Key: 4A68118A4B978617A7E38AAE2D91FDE6
Read salt: 42660BBE161C4E08E5DD4CBCC74CC952
Read IV: 7067030BACB4ED6F5AC0F1465383E46E
size32
size after0
No data retrieved from the filter.
Decryption successfull
The salt , IV and derive key are all being passed correctly at first glance, also the size before is 32 and size after is 0 bits hinting that the data just vanishes? Ofcourse no data is retrieved from the filter, this was just my testing of the matter and the decryption is indeed successfull but there was nothing to decrypt? I am a bit lost at this stage
Upvotes: 0
Views: 73