Search code examples
crypto++

Crypto++ Init-Update-Final get cipher output


In a effort to encrypt large file using Crypto++, I am following ecrypting using Java-like Init-Update-Final in Crypto++,

The demo code seem to work, everytime the Put is invoked, the output byte is printed to screen:

encoder.Put(buffer, ready);

But I can't find anyway to get the output of all cipher text. For example

'H' -> print 01 to screen 
'E' -> print A9 to screen 
'L' -> print 5J to screen 
'L' -> print 13 to screen 
'O' -> print 3d to screen ...

But I can't retrieve the full result to a byte array 01A95J133d...

A question in here related to using Init-Update-Final to encrypt large files but seem like not provided a working solution yet.

This is the full demo code:

enum { ENCRYPT_MODE = 1, DECRYPT_MODE = 2 };
struct JavaAlgorithmParameter
{
    JavaAlgorithmParameter()
        : key(NULL), ksize(0), iv(NULL), vsize(0) {}

    const byte* key;
    size_t ksize;
    const byte* iv;
    size_t vsize;
};

/////////////////////////
/////////////////////////

class JavaCipher
{
public:
    static JavaCipher* getInstance(const std::string& transformation);

    void init(int opmode, const JavaAlgorithmParameter& params);
    size_t update(const byte* in, size_t isize, byte* out, size_t osize);
    size_t final(byte* out, size_t osize);

    std::string getAlgorithm() const;

protected:
    JavaCipher(const std::string& transformation);

private:
    std::string m_transformation;
    member_ptr<SymmetricCipher> m_cipher;
    member_ptr<StreamTransformationFilter> m_filter;
};

/////////////////////////
/////////////////////////

JavaCipher* JavaCipher::getInstance(const std::string& transformation)
{
    return new JavaCipher(transformation);
}

JavaCipher::JavaCipher(const std::string& transformation)
    : m_transformation(transformation) { }

std::string JavaCipher::getAlgorithm() const
{
    return m_transformation;
}

/////////////////////////
/////////////////////////

size_t JavaCipher::final(byte* out, size_t osize)
{
    m_filter.get()->MessageEnd();

    if (!out || !osize || !m_filter.get()->AnyRetrievable())
        return 0;

    size_t t = CryptoPP::STDMIN(m_filter.get()->MaxRetrievable(), (word64)osize);
    t = m_filter.get()->Get(out, t);
    return t;
}

/////////////////////////
/////////////////////////

size_t JavaCipher::update(const byte* in, size_t isize, byte* out, size_t osize)
{
    if (in && isize)
        m_filter.get()->Put(in, isize);

    if (!out || !osize || !m_filter.get()->AnyRetrievable())
        return 0;

    size_t t = STDMIN(m_filter.get()->MaxRetrievable(), (word64)osize);
    t = m_filter.get()->Get(out, t);
    return t;
}

/////////////////////////
/////////////////////////

void JavaCipher::init(int opmode, const JavaAlgorithmParameter& params)
{
    if (m_transformation == "AES/ECB/PKCSPadding" && opmode == ENCRYPT_MODE)
    {
        m_cipher.reset(new ECB_Mode<AES>::Encryption);
        m_cipher.get()->SetKey(params.key, params.ksize);
        m_filter.reset(new StreamTransformationFilter(*m_cipher.get(), NULL, BlockPaddingSchemeDef::PKCS_PADDING));
    }
    else if (m_transformation == "AES/ECB/PKCSPadding" && opmode == DECRYPT_MODE)
    {
        m_cipher.reset(new ECB_Mode<AES>::Decryption);
        m_cipher.get()->SetKey(params.key, params.ksize);
        m_filter.reset(new StreamTransformationFilter(*m_cipher.get(), NULL, BlockPaddingSchemeDef::PKCS_PADDING));
    }
    else if (m_transformation == "AES/CBC/PKCSPadding" && opmode == ENCRYPT_MODE)
    {
        m_cipher.reset(new CBC_Mode<AES>::Encryption);
        m_cipher.get()->SetKeyWithIV(params.key, params.ksize, params.iv);
        m_filter.reset(new StreamTransformationFilter(*m_cipher.get(), NULL, BlockPaddingSchemeDef::PKCS_PADDING));
    }
    else if (m_transformation == "AES/CBC/PKCSPadding" && opmode == DECRYPT_MODE)
    {
        m_cipher.reset(new CBC_Mode<AES>::Decryption);
        m_cipher.get()->SetKeyWithIV(params.key, params.ksize, params.iv);
        m_filter.reset(new StreamTransformationFilter(*m_cipher.get(), NULL, BlockPaddingSchemeDef::PKCS_PADDING));
    }
    else if (m_transformation == "AES/CTR/NoPadding" && opmode == ENCRYPT_MODE)
    {
        m_cipher.reset(new CTR_Mode<AES>::Encryption);
        m_cipher.get()->SetKeyWithIV(params.key, params.ksize, params.iv);
        m_filter.reset(new StreamTransformationFilter(*m_cipher.get(), NULL, BlockPaddingSchemeDef::NO_PADDING));
    }
    else if (m_transformation == "AES/CTR/NoPadding" && opmode == DECRYPT_MODE)
    {
        m_cipher.reset(new CTR_Mode<AES>::Decryption);
        m_cipher.get()->SetKeyWithIV(params.key, params.ksize, params.iv);
        m_filter.reset(new StreamTransformationFilter(*m_cipher.get(), NULL, BlockPaddingSchemeDef::NO_PADDING));
    }
    else
        throw NotImplemented(m_transformation + " is not implemented");
}

/////////////////////////
/////////////////////////

int main(int argc, char* argv[])
{
    try
    {
        byte key[32], iv[16];
        OS_GenerateRandomBlock(false, key, COUNTOF(key));
        OS_GenerateRandomBlock(false, iv, COUNTOF(iv));

        HexEncoder encoder(new FileSink(cout));

        JavaAlgorithmParameter params;
        params.key = key;
        params.ksize = COUNTOF(key);
        params.iv = iv;
        params.vsize = COUNTOF(iv);

        //JavaCipher* cipher = JavaCipher::getInstance("AES/CTR/NoPadding");
        JavaCipher* cipher = JavaCipher::getInstance("AES/CBC/PKCSPadding");
        cipher->init(ENCRYPT_MODE, params);

        cout << "Algorithm: " << cipher->getAlgorithm() << endl;

        cout << "Key: ";
        encoder.Put(key, COUNTOF(key));
        cout << endl;

        cout << "IV: ";
        encoder.Put(iv, COUNTOF(iv));
        cout << endl;

        char * allText = FileUtil::readAllByte("1MB.txt");
        long len = strlen(allText);

        byte buffer[64];

        size_t ready = 0;

        for (unsigned int i = 0; i <= len; i++)
        {
            byte b = allText[i];

            //cout << "Put 0x";
            encoder.Put(b);
            cout << endl;

            ready = cipher->update(&b, 1, buffer, COUNTOF(buffer));

            if (ready)
            {
                //cout << "Get: ";
                encoder.Put(buffer, ready);
                cout << endl;
            }
        }

        ready = cipher->final(NULL, 0);
        if (ready)
        {
            //cout << "Final: ";
            encoder.Put(buffer, ready);
            cout << endl;
        }

        ready = cipher->final(buffer, COUNTOF(buffer));
        if (ready)
        {
            //cout << "Final: ";
            encoder.Put(buffer, ready);
            cout << endl;
        }

        delete cipher;
        getchar();
    }
    catch (const Exception& ex)
    {
        cerr << ex.what() << endl;
    }

    return 0;
}

Solution

  • But I can't find anyway to get the output of all cipher text. For example

    'H' -> print 01 to screen 
    'E' -> print A9 to screen 
    'L' -> print 5J to screen 
    'L' -> print 13 to screen 
    'O' -> print 3d to screen ...
    

    But I can't retrieve the full result to a byte array 01A95J133d...

    Call cipher::update with no output buffer:

    cipher->update(&b, 1, NULL, 0);
    

    Then, retrieve the buffer after you call cipher::final:

    size_t size = <some appropriately size for the cipher text>;
    byte result[size];
    cipher->final(result, size);
    

    If you are having trouble with <some appropriately size for the cipher text>, then add a new method to the class:

    size_t ready() const
    {
        return m_filter.get()->MaxRetrievable();
    }