Can anyone explain why my crypto++ decrypted file 16 bytes short?
- by Tom Williams
I suspect it might be too much to hope for, but can anyone with experience with crypto++ explain why the "decrypted.out" file created by main() is 16 characters short (which probably not coincidentally is the block size)?
I think the issue must be in CryptStreamBuffer::GetNextChar(), but I've been staring at it and the crypto++ documentation for hours.
Any other comments about how crummy or naive my std::streambuf implementation are also welcome ;-) And I've just noticed I'm missing some calls to delete so you don't have to tell me about those.
Thanks,
Tom
// Runtime Includes
#include <iostream>
// Crypto++ Includes
#include "aes.h"
#include "modes.h"      // xxx_Mode< >
#include "filters.h"    // StringSource and
                        // StreamTransformation
#include "files.h"
using namespace std;
class CryptStreamBuffer: public std::streambuf {
public:
    CryptStreamBuffer(istream& encryptedInput, CryptoPP::StreamTransformation& c);
    CryptStreamBuffer(ostream& encryptedOutput, CryptoPP::StreamTransformation& c);
protected:
    virtual int_type overflow(int_type ch = traits_type::eof());
    virtual int_type uflow();
    virtual int_type underflow();
    virtual int_type pbackfail(int_type ch);
    virtual int sync();
private:
    int GetNextChar();
    int m_NextChar; // Buffered character
    CryptoPP::StreamTransformationFilter* m_StreamTransformationFilter;
    CryptoPP::FileSource* m_Source;
    CryptoPP::FileSink* m_Sink;
}; // class CryptStreamBuffer
CryptStreamBuffer::CryptStreamBuffer(istream& encryptedInput, CryptoPP::StreamTransformation& c) :
    m_NextChar(traits_type::eof()),
    m_StreamTransformationFilter(0),
    m_Source(0),
    m_Sink(0) {
    m_StreamTransformationFilter = new CryptoPP::StreamTransformationFilter(c);
    m_Source = new CryptoPP::FileSource(encryptedInput, false, m_StreamTransformationFilter);
}
CryptStreamBuffer::CryptStreamBuffer(ostream& encryptedOutput, CryptoPP::StreamTransformation& c) :
    m_NextChar(traits_type::eof()),
    m_StreamTransformationFilter(0),
    m_Source(0),
    m_Sink(0) {
    m_Sink = new CryptoPP::FileSink(encryptedOutput);
    m_StreamTransformationFilter = new CryptoPP::StreamTransformationFilter(c, m_Sink);
}
CryptStreamBuffer::int_type CryptStreamBuffer::overflow(int_type ch) {
    return m_StreamTransformationFilter->Put((byte)ch);
}
CryptStreamBuffer::int_type CryptStreamBuffer::uflow() {
    int_type result = GetNextChar();
    // Reset the buffered character
    m_NextChar = traits_type::eof();
    return result;
}
CryptStreamBuffer::int_type CryptStreamBuffer::underflow() {
    return GetNextChar();
}
CryptStreamBuffer::int_type CryptStreamBuffer::pbackfail(int_type ch) {
    return traits_type::eof();
}
int CryptStreamBuffer::sync() {
    if (m_Sink) {
        m_StreamTransformationFilter->MessageEnd();
    }
}
int CryptStreamBuffer::GetNextChar() {
    // If we have a buffered character do nothing
    if (m_NextChar != traits_type::eof()) {
        return m_NextChar;
    }
    // If there are no more bytes currently available then pump the source
    // *** I SUSPECT THE PROBLEM IS HERE ***
    if (m_StreamTransformationFilter->MaxRetrievable() == 0) {
        m_Source->Pump(1024);
    }
    // Retrieve the next byte
    byte nextByte;
    size_t noBytes = m_StreamTransformationFilter->Get(nextByte);
    if (0 == noBytes) {
        return traits_type::eof();
    }
    // Buffer up the next character
    m_NextChar = nextByte;
    return m_NextChar;
}
void InitKey(byte key[]) {
    key[0] = -62;
    key[1] = 102;
    key[2] = 78;
    key[3] = 75;
    key[4] = -96;
    key[5] = 125;
    key[6] = 66;
    key[7] = 125;
    key[8] = -95;
    key[9] = -66;
    key[10] = 114;
    key[11] = 22;
    key[12] = 48;
    key[13] = 111;
    key[14] = -51;
    key[15] = 112;
}
void DecryptFile(const char* sourceFileName, const char* destFileName) {
    ifstream ifs(sourceFileName, ios::in | ios::binary);
    ofstream ofs(destFileName, ios::out | ios::binary);
    byte key[CryptoPP::AES::DEFAULT_KEYLENGTH];
    InitKey(key);
    CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption decryptor(key, sizeof(key));
    if (ifs) {
        if (ofs) {
            CryptStreamBuffer cryptBuf(ifs, decryptor);
            std::istream decrypt(&cryptBuf);
            int c;
            while (EOF != (c = decrypt.get())) {
                ofs << (char)c;
            }
            ofs.flush();
        }
        else {
            std::cerr << "Failed to open file '" << destFileName << "'." << endl;
        }
    }
    else {
        std::cerr << "Failed to open file '" << sourceFileName << "'." << endl;
    }  
}
void EncryptFile(const char* sourceFileName, const char* destFileName) {
    ifstream ifs(sourceFileName, ios::in | ios::binary);
    ofstream ofs(destFileName, ios::out | ios::binary);
    byte key[CryptoPP::AES::DEFAULT_KEYLENGTH];
    InitKey(key);
    CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption encryptor(key, sizeof(key));
    if (ifs) {
        if (ofs) {
            CryptStreamBuffer cryptBuf(ofs, encryptor);
            std::ostream encrypt(&cryptBuf);
            int c;
            while (EOF != (c = ifs.get())) {
                encrypt << (char)c;
            }
            encrypt.flush();
        }
        else {
            std::cerr << "Failed to open file '" << destFileName << "'." << endl;
        }
    }
    else {
        std::cerr << "Failed to open file '" << sourceFileName << "'." << endl;
    }  
}
int main(int argc, char* argv[])
{
    EncryptFile(argv[1], "encrypted.out");
    DecryptFile("encrypted.out", "decrypted.out");
    return 0;
}