Luca Alfino
Luca Alfino

Reputation: 1

Crypto++ text file Decryption not working properly

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

Answers (0)

Related Questions