ArraySource

From Crypto++ Wiki
Jump to: navigation, search

An ArraySource, introduced in version 5.6 of Crypto++, is a source for byte arrays. An ArraySource is typedef'd from a StringSource's third constructor. Prior to version 5.6 of the library, use a StringSource.

An ArraySource functions like any other source in the library, including a FileSource and StringSource.

ArraySource
Documentation
#include <cryptopp/filters.h>

Constructor

ArraySource (const byte *string,
             size_t length,
             bool pumpAll,
             BufferedTransformation *attachment=NULL)

string is a byte array.

length is the length of the byte array.

pumpAll is a boolen value indicating if the ArraySource should pump all of the data immediately to its attached transformation.

attachment is a BufferedTransformation, such as another filter or sink.

Sample Program

Below is an example of using ArraySource and ArraySink. The Redirector ensures the ArraySink survives so you can call TotalPutLength.

#include <iostream>
#include <string>
using namespace std;

#include "cryptlib.h"
#include "filters.h"
#include "files.h"
#include "modes.h"
#include "hex.h"
#include "aes.h"
using namespace CryptoPP;

int main(int argc, char* argv[])
{
  byte key[AES::MAX_KEYLENGTH];
  byte iv[AES::BLOCKSIZE];
  vector<byte> plain, cipher, recover;
  HexEncoder encoder(new FileSink(cout));

  memset(key, 0x00, sizeof(key));
  memset(iv, 0x00, sizeof(iv));

  string str("Attack at dawn!");
  std::copy(str.begin(), str.end(), std::back_inserter(plain));

  cout << "Plain text: ";
  encoder.Put(plain.data(), plain.size());
  encoder.MessageEnd();
  cout << endl;

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

  CBC_Mode<AES>::Encryption enc;
  enc.SetKeyWithIV(key, sizeof(key), iv, sizeof(iv));

  // Make room for padding
  cipher.resize(plain.size()+AES::BLOCKSIZE);
  ArraySink cs(&cipher[0], cipher.size());

  ArraySource(plain.data(), plain.size(), true,
    new StreamTransformationFilter(enc, new Redirector(cs)));

  // Set cipher text length now that its known
  cipher.resize(cs.TotalPutLength());

  cout << "Cipher text: ";
  encoder.Put(cipher.data(), cipher.size());
  encoder.MessageEnd();
  cout << endl;

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

  CBC_Mode<AES>::Decryption dec;
  dec.SetKeyWithIV(key, sizeof(key), iv, sizeof(iv));

  // Recovered text will be less than cipher text
  recover.resize(cipher.size());
  ArraySink rs(&recover[0], recover.size());

  ArraySource(cipher.data(), cipher.size(), true,
    new StreamTransformationFilter(dec, new Redirector(rs)));

  // Set recovered text length now that its known
  recover.resize(rs.TotalPutLength());

  cout << "Recovered text: ";
  encoder.Put(recover.data(), recover.size());
  encoder.MessageEnd();
  cout << endl;

  return 0;
}

Missing Data

Its not uncommon to experience Missing Data in a pipeline. A source will send data through a pipeline but have nothing in the sink. This is usually due to the compiler matching the wrong function. For example:

string source = "FF 88 00", destination;
StringSink ss(source,
    new HexDecoder(
        new StringSink(destination)
    ) // HexDecoder
); // StringSink

After the above code executes, destination will likely be empty because the compiler coerces the HexDecoder (the pointer) to a bool (the pumpAll parameter), which leaves the StringSource's attached transformation NULL. The compiler will do so without warning, even with -Wall -Wextra -Wconversion. To resolve the issue, explicitly specify the pumpAll parameter:

string source = "FF 88 00", destination;
StringSink ss(source, true /*pumpAll*/,
    new HexDecoder(
        new StringSink(destination)
    ) // HexDecoder
); // StringSink

Another way data ends up missing is failing to call MessageEnd() when pumping data. For example, the following may not produce expected results:

// The 4-bit nibble will be buffered waiting for another nibble
string source = "FF 88 0", destination;

HexDecoder decoder(new StringSink(destination));
decoder.Put(source.data(), source.size());

// Do something with destination

Be sure to call MessageEnd() when data comes up missing:

string source = "FF 88 0", destination;

HexDecoder decoder(new StringSink(destination));
decoder.Put(source.data(), source.size());
decoder.MessageEnd();

// Do something with destination