Search code examples
c++encryptiondescrypto++

Invalid block padding trying to decrypt 2-key triple DES


I am trying to implement 2-key triple DES with crypto++ in C++.

My implementation is based upon the code in the crypto++ wiki located here.

The code on the wiki builds properly; I can see that it is encrypting and decrypting correctly when I run the sample program.

For my implementation, I am trying to do the following:

  1. the user can run "desimp.exe encrypt test.txt". The program will encrypt test.txt and then output an encrypted file named test.txt.des . This seems to be working properly.

  2. the user can run "desimp.exe decrypt test.txt.des" and the program will decrypt test.txt.des and output the decrypted text to a file "decrypted.txt" I can't get this to work. The error that I get is "StreamTransformationFilter: invalid PKCS #7 block padding found"

I believe I might need to save the data in the iv to a file at the time of encryption as well. Is this correct? I have tried doing this, and I think I'm able to get the iv saved to a file correctly - but I think in order to read in the iv to be used to for decryption, it needs to be read in as an array of 8 bytes. The file size of test.txt.iv when I attempt to save the iv is 21 bytes. If this is the right approach, I'm not sure how to proceed. If this is the wrong approach, I'd like to know what I need to do differently. Here is the code:

#ifndef CRYPTOPP_DLL_ONLY
#define CRYPTOPP_DEFAULT_NO_DLL
#endif

#include "dll.h"
#include "rc6.h"
#include <stdio.h>  
#include <string.h>
#include <fstream>
#include <stdlib.h>
#include <string>
#include <streambuf>

USING_NAMESPACE(CryptoPP)
USING_NAMESPACE(std)

#ifdef CRYPTOPP_IMPORTS
static PNew s_pNew = NULL;
static PDelete s_pDelete = NULL;
#endif
#ifdef CRYPTOPP_DLL_ONLY

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

    AutoSeededRandomPool prng;
    SecByteBlock key(DES_EDE2::DEFAULT_KEYLENGTH);
    prng.GenerateBlock(key, key.size());
    byte iv[DES_EDE2::BLOCKSIZE];
    prng.GenerateBlock(iv, sizeof(iv));
    string plain = "CBC Mode Test";
    string cipher, encoded, recovered;


    char *fileName = argv[1];
    char *runMode = argv[2];
    char *ivFile = argv[3];

    cout << "ARGUMENT 1: " << fileName << endl;
    cout << "ARGUMENT 2: " << runMode << endl;

    string fileNameString(fileName);
    string encryptedFileNameString = fileNameString + ".des";//add .des to the filename of the encrypted file once it's generated
    string ivString = fileNameString + ".iv";//iv file
    string runModeString(runMode);

    if (runModeString == "encrypt")
    {
        ifstream t(fileName);
        string str((std::istreambuf_iterator<char>(t)),
        istreambuf_iterator<char>());
        try
        {
            cout << "plain text: " << str << endl;

            CBC_Mode< DES_EDE2 >::Encryption e;
            e.SetKeyWithIV(key, key.size(), iv);

            // The StreamTransformationFilter adds padding
            //  as required. ECB and CBC Mode must be padded
            //  to the block size of the cipher.
            StringSource ss1(str, true, 
                new StreamTransformationFilter(e,
                    new StringSink(cipher)
                ) // StreamTransformationFilter      
            ); // StringSource
        }
        catch(const CryptoPP::Exception& e)
        {
            cerr << e.what() << endl;
            exit(1);
        }
        // Pretty print
        StringSource ss2(cipher, true,
            new HexEncoder(
                new StringSink(encoded)
            ) // HexEncoder
        ); // StringSource

        cout << "cipher text: " << encoded << endl;//"encoded" is just the pretty print version of the ciphertext.
        ofstream fout(encryptedFileNameString); 
        fout << cipher; 
        fout.close();//outputs/saves the encrypted file
        cout << "Encrypted file was saved to local path as " << encryptedFileNameString << endl;

        ofstream fout2(ivString); 
        fout2 << iv; 
        fout2.close();
        cout << "iv was saved to local path as " << ivString << endl;
    }

    if (runModeString == "decrypt")// USER WANTS TO DECRYPT A FILE
        {           
            ifstream t2(fileName);
            string str2((istreambuf_iterator<char>(t2)),istreambuf_iterator<char>());
            cipher = str2;

        try
        {
            CBC_Mode< DES_EDE2 >::Decryption d;
            d.SetKeyWithIV(key, key.size(), iv);
            // The StreamTransformationFilter removes
            //  padding as required.
            StringSource ss3(cipher, true, 
                new StreamTransformationFilter(d,
                    new StringSink(recovered)
                ) // StreamTransformationFilter
            ); // StringSource

            cout << "recovered text: " << recovered << endl;
        }
        catch(const CryptoPP::Exception& e)
            {
                cerr << e.what() << endl;
                exit(1);
            }
        }
        return 0;
    }//end main

    extern "C" __declspec(dllexport) void __cdecl SetNewAndDeleteFromCryptoPP(PNew pNew, PDelete pDelete, PSetNewHandler pSetNewHandler)
    {
        s_pNew = pNew;
        s_pDelete = pDelete;
    }

    void * __cdecl operator new (size_t size)
    {
        return s_pNew(size);
    }

    void __cdecl operator delete (void * p)
    {
        s_pDelete(p);
    }

    #endif

Solution

  • I think you need to open your encrypted file with std::ios_base::binary in the mode (both for reading and writing); otherwise, the I/O library will mangle sequences which look like line-ends.