Search code examples
c++encryptioncrypto++

Use of Pipelines to encrypt a file


I have just startet working with crypto++ and i have a question about the piplenes and how to use them to encrypt a file.

I want to use AES to encrypt a file.

1.)Would it be enough to just do:

EAX<AES>::Encryption encryptor;
encryptor.SetKeyWithIV(derived.data(), 16, ivb, ivb.size());
FileSource f("source", new AuthenticatedEncryptionFilter(encryptor,new FileSink("deststination")));

2.) If i have a huge input file, will this approach automaticly encrypt the files in blocks?

3.) Would this automaticly create the output file if it is not there?

EDIT:

Ok, i got it to wok with my approch.

The 2.) Question remains and i have a new one:

Can I tell it to skip the first 24 bytes of the file?


Solution

  • EAX<AES>::Encryption encryptor;
    encryptor.SetKeyWithIV(derived.data(), 16, ivb, ivb.size());
    FileSource f("source", new AuthenticatedEncryptionFilter(encryptor,new FileSink("deststination")));
    

    Close. The AuthenticatedEncryptionFilter will be coerced to the bool pumpAll parameter of FileSink. So you need:

    FileSource f("source", true, new AuthenticatedEncryptionFilter(encryptor,new FileSink("deststination")));
    

    Also see FileSource on the Crypto++ wiki. And the FileSource Class Reference from the manual might be of interest, too.


    If i have a huge input file, will this approach automatically encrypt the files in blocks?

    Yes. Internally, Crypto++ will "block" or "chunk" the processing in 4096-bytes, IIRC. A recent discussion about it occurred on the mailing list at ios locking up during encryption.

    A program that allows you to do the blocking is provided in the post. You can use it to throttle the processing, a place to update a progress bar or yield the processor, if needed. Its reproduced below.


    Would this automatically create the output file if it is not there?

    Yes. The FileSource is just a std::ifstream wrapper, while a FileSink is just a std::ofstream wrapper.

    Again, here are the wiki pages:


    Can I tell it to skip the first 24 bytes of the file?

    Yes. In this case, use bool pumpAll and set it to false. Then do something like:

    FileSource fs("source", false, new AuthenticatedEncryptionFilter(...));
    fs.Skip(24);
    
    size_t remaining = <size of file>;
    size_t BLOCK_SIZE = 512;
    while(remaining && !fs.SourceExhausted())
    {    
        const unsigned int req = STDMIN(remaining, BLOCK_SIZE);
        fs.Pump(req);
        fs.Flush(false);
    
        remaining -= req;
    }
    

    Or, you can:

    FileSource fs("source", false, new AuthenticatedEncryptionFilter(...));
    fs.Skip(24);
    fs.PumpAll();
    

    Also see FileSource Class Reference in the manual. Skip is part of BufferedTransformation; and PumpAll is part of Source.


    There are also wiki pages covering EAX mode and the authenticated {en|de}cryption filters. See:

    There's even a page on using a Java-like Init/Update/Final at:


    The program below uses CFB_Mode<AES>, but its easy enough to swap in another cipher and mode. It also demonstrates how to place objects on the stack and use them in a pipeline rather than creating them on the heap with new.

    int main(int argc, char* argv[])
    {
      static const unsigned int BIG_SIZE = 2U * 1024U * 1024U;    
      static const unsigned int BLOCK_SIZE = 4096U;
    
      try
        {
          SecByteBlock key(32);
          OS_GenerateRandomBlock(false, key.data(), key.size());
    
          // cout << "Key: ";
          // ArraySource as(key.data(), key.size(), true, new HexEncoder(new FileSink(cout)));
          // cout << endl;
    
          CFB_Mode<AES>::Encryption enc;
          enc.SetKeyWithIV(key.data(), key.size(), key.data());
    
          MeterFilter meter;
          StreamTransformationFilter stf(enc);
    
          FileSource source("/dev/zero", false);
          FileSink sink("zero.enc");
    
          source.Attach(new Redirector(stf));
          stf.Attach(new Redirector(meter));
          meter.Attach(new Redirector(sink));    
    
          unsigned int remaining = BIG_SIZE;
          while(remaining && !source.SourceExhausted())
          {
            if(remaining % (1024) == 0)
            {
              cout << "Processed: " << meter.GetTotalBytes() << endl;    
            }
    
            const unsigned int req = STDMIN(remaining, BLOCK_SIZE);
            source.Pump(req);
            source.Flush(false);
    
            remaining -= req;
        }
      }
      catch(const Exception& ex)
      {
        cerr << ex.what() << endl;
      }
    
      return 0;
    }