Search code examples
c++file-ioaescrypto++

PumpMessages in crypto++, reading a key file?


I'm writing an AES key and iv to the file with crypto++ by using following code:

// write the key:
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
SecByteBlock iv(AES::BLOCKSIZE);

string file = m_file_name + ".key";
FileSink* key_out = new FileSink(file.c_str());
Base64Encoder base64_key_enc(key_out);

base64_key_enc.Put(key.BytePtr(), key.size());
base64_key_enc.MessageEnd();
base64_key_enc.Put(iv.BytePtr(), iv.size());
base64_key_enc.MessageEnd();

and to read a the key and iv from the file back I use following:

// read key
string file = m_file_name + ".key";
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
ArraySink* arr_key_in = new ArraySink(key, key.size());
Base64Decoder* base64_key_dec = new Base64Decoder(arr_key_in);
FileSource source(file.c_str(), false, base64_key_dec);
source.PumpMessages(1); // read only the key 

// read iv
SecByteBlock iv(AES::BLOCKSIZE);
ArraySink* arr_iv_in = new ArraySink(iv, iv.size());
base64_key_dec->Detach(arr_iv_in);
source.PumpAll(); // read the rest (the iv)

Problem is that after reading a file the key is correct but iv is not, so i wornder what could be wrong with my sintax?

the contents of a key file are base64 hex encoded and it looks like so:

2Gnh3TbAJeQPmza9FKdqNg== FowuKut3pBl7g0Or+4FJUg==

the == means end of the message/key... First on is the key, while the other one is the iv, the above code does not read the iv from the file properly.

What is wrong with my code?


Solution

  • Problem is that after reading a file the key is correct but iv is not, so i wonder what could be wrong with my syntax?

    I think the problem is the library does not understand your notion of a message, so it does not know how to stream the message. I suspect its reading everything in one shot.

    You might be able to correct it with source.Pump(AES::DEFAULT_KEYLENGTH * 4 / 3). I think that's correct, to compensate for the base encoding expansion. Or, you could read Base64 concatenated strings, split on the space, and then process the two messages separately.

    Or you could try the following. Its not much different than yours, except it writes a length prefix so the Key and IV can be easily delineated and recovered.

    A typical run looks like:

    $ ./cryptopp-test.exe
    Key 1: 434477042E70083441961B2F98A9FB1C
    IV 1: 73A06DA15676B5AFB80C1741B3651982
    Encoded: ABBDRHcELnAINEGWGy+YqfscABBzoG2hVna1r7gMF0GzZRmC
    Key 2: 434477042E70083441961B2F98A9FB1C
    IV 2: 73A06DA15676B5AFB80C1741B3651982
    

    HexEncoder hex;
    string filename = "test.txt", r1, r2, r3, r4, r5;
    
    Base64Encoder encoder;
    AlgorithmParameters params = MakeParameters(Pad(), false)(InsertLineBreaks(), false);
    encoder.Initialize(params);
    
    SecByteBlock k1(AES::DEFAULT_KEYLENGTH), v1(AES::BLOCKSIZE);
    
    // Generate random key and iv
    OS_GenerateRandomBlock(false, k1, k1.size());
    OS_GenerateRandomBlock(false, v1, v1.size());
    
    // Print key
    hex.Detach(new StringSink(r1));
    hex.Put(k1, k1.size());
    hex.MessageEnd();
    
    cout << "Key 1: " << r1 << endl;
    
    // Print iv
    hex.Detach(new StringSink(r2));
    hex.Put(v1, v1.size());
    hex.MessageEnd();
    
    cout << "IV 1: " << r2 << endl;
    
    // Write key
    encoder.PutWord16(static_cast<word16>(k1.size()), BIG_ENDIAN_ORDER);
    encoder.Put(k1.data(), k1.size());
    
    // Write iv
    encoder.PutWord16(static_cast<word16>(v1.size()), BIG_ENDIAN_ORDER);
    encoder.Put(v1.data(), v1.size());
    encoder.MessageEnd();
    
    // Save it
    FileSink f1(filename.c_str(), true);
    encoder.CopyTo(f1);
    f1.MessageEnd();
    
    // Print Base64 encoded
    StringSink ss(r3);
    encoder.TransferTo(ss);
    ss.MessageEnd();
    
    cout << "Encoded: " << r3 << endl;
    
    // Read file
    ByteQueue queue;
    FileSource f2(filename.c_str(), false, new Base64Decoder(new Redirector(queue)));
    f2.PumpAll();
    
    // Read key and iv
    SecByteBlock k2, v2;
    word16 size = 0;
    
    // Read key
    queue.GetWord16(size, BIG_ENDIAN_ORDER);
    k2.resize(size);
    queue.Get(k2, k2.size());
    
    // Read iv
    queue.GetWord16(size, BIG_ENDIAN_ORDER);
    v2.resize(size);
    queue.Get(v2, v2.size());
    
    // Print key
    hex.Detach(new StringSink(r4));
    hex.Put(k2, k2.size());
    hex.MessageEnd();
    
    cout << "Key 2: " << r4 << endl;
    
    // Print iv
    hex.Detach(new StringSink(r5));
    hex.Put(v2, v2.size());
    hex.MessageEnd();
    
    cout << "IV 2: " << r5 << endl;
    

    The reference for the MakeParameters trick to remove padding and line breaks is at NameValuePairs on the Crypto++ wiki.

    The reference for the OS_GenerateRandomBlock is at RandomNumberGenerator on the Crypto++ wiki.

    And you can find HexEncoder, Base64Encoder, Redirector and friends on the Crypto++ wiki.


    Also, AES::DEFAULT_KEYLENGTH is 16 bytes (AES-128). That's usually enough for most people's needs. You could bump that to 32, if desired (AES-256).