Search code examples
c++crypto++

How to change sink in Crypto++


I'm using Crypto++ to decrypt a file, so I use FileSource as my source but I want to be able to change sink, so I can achieve something like following:

std::string temp;
FileSource file("/path/to/file", false, new StringSink(temp));
file.Pump(14);
if (temp != "File Signature")
    return false;
//change file's sink to new CTR_Mode<AES>::Decryption(meta_key, 32, meta_iv, new StringSink(metainfo))
file.Pump(256);
/* use metainfo */
//change file's sink to new CTR_Mode<AES>::Decryption(key, 32, iv, new StringSink(decoded))
while(!file.SourceExhausted())
{
    file.Pump(512);
    std::cout << decoded;
}

How can I achieve this?


Solution

  • How to change sink in Crypto++?

    A Sink is just a Filter that has no attached transformation. To change a sink, you just change the attached filter of the predecessor or parent object. The tricky part is getting access to a filter that's two or three deep in the filter chain.

    Use something like the following. Filters have two methods for attaching filters: Attach and Detach. They both attach a new filter to the object; but Attach returns the old filter while Detach free's it.

    The other oddity is the Redirector. You can use it to break ownership in a chain. Its kind of needed for the StreamTransformationFilter filter. The stack based allocation will be free'd as a local variable, so you don't want it free'd as part of a chain, too.

    FileSource file("/path/to/file", false, new StringSink(temp));
    file.Pump(14);
    if (temp != "File Signature")
        return false;
    
    CTR_Mode<AES>::Decryption decryptor;
    StreamTransformationFilter filter(decryptor);
    
    // Detach StringSink(temp), Attach StreamTransformationFilter(decryptor)
    file.Detach(new Redirector(filter));
    
    // Set Key and IV
    decryptor.SetKeyWithIV(meta_key, 32, meta_iv);
    
    // Detach nothing, Attach StringSink(metainfo)
    filter.Detach(new StringSink(metainfo));
    
    // FileSource → decryptor → metainfo
    file.Pump(256);
    
    // Set Key and IV
    decryptor.SetKeyWithIV(key, 32, iv);
    
    // Detach StringSink(metainfo), Attach StringSink(decoded)
    filter.Detach(new StringSink(decoded)); 
    
    while(!file.SourceExhausted())
    {
        // FileSource → decryptor → decoded
        file.Pump(512);
        std::cout << decoded;
    }
    

    Here's another way to do it without the Redirector. It stashes away a pointer to the StreamTransformationFilter:

    FileSource file("/path/to/file", false, new StringSink(temp));
    file.Pump(14);
    if (temp != "File Signature")
        return false;
    
    CTR_Mode<AES>::Decryption decryptor;
    StreamTransformationFilter* filter = NULL;
    
    // Detach StringSink(temp), Attach StreamTransformationFilter(decryptor)
    file.Detach(filter = new StreamTransformationFilter(decryptor));
    
    // Set Key and IV
    decryptor.SetKeyWithIV(meta_key, 32, meta_iv);
    
    // Detach nothing, Attach StringSink(metainfo)
    filter->Detach(new StringSink(metainfo)); 
    
    // FileSource → decryptor → metainfo
    file.Pump(256);
    
    // Set Key and IV
    decryptor.SetKeyWithIV(key, 32, iv);
    
    // Detach StringSink(metainfo), Attach StringSink(decoded)
    filter->Detach(new StringSink(decoded)); 
    
    while(!file.SourceExhausted())
    {
        // FileSource → decryptor → decoded
        file.Pump(512);
        std::cout << decoded;
    }
    

    You might be interested in Pipelining on the Crypto++ wiki. Also of interest might be BufferedTransformation, which is the base class used for pipelining.