User Guide: filters.h

From Crypto++ Wiki
Jump to: navigation, search

Base Classes

Filter

Filter is an abstract base class derived from BufferedTransformation. Its public interface is no different than that of its parent; however, it implements attachment functionality that is only declared, but not implemented, in BufferedTransformation.

It is possible to distinguish between a Filter and a BufferedTransformation object by calling its Attachable() method. If the object is a Filter, the method will return true; if it is a BufferedTransformation, it will return false.

Attachment is an advanced and very practical concept; with filters and their attachment functionality, one can elegantly accomplish much in a relatively small amount of code.

When a BufferedTransformation-derived object, say B, is attached to a Filter-derived object, say F, then, when data is sent to F through any of its Put() methods, F will do whatever processing it normally does on this data, and will then forward its output to B by calling B's Put() functions. When any of the Get() methods is called on F, F will simply forward the call to B and return whatever data B returns. If B is another Filter-derived object, it will do the same, forming an attachment chain - which inevitably ends with an object that is a BufferedTransformation, but not derived from Filter.

It's really quite simple. And it works like magic! For example:

 char const* zInputFile = ...;
 char const* zOutputFile = ...;
 SHA hash;
 FileSource(zInputFile, true,
     new HashFilter(hash,
         new FileSink(zOutputFile),
         true));

The above chunk of code opens the file identified by zInputFile (via FileSource), feeds it to the SHA hash module (via HashFilter), and stores the original data along with the calculated digest to the file identified by zOutputFile (via FileSink). If the true parameter on the last line was omitted, the default value of false would apply, and only the digest would be saved to the output file - without the original data the digest was calculated upon.

Attachment methods

The constructor. All classes derived from Filter, as well as the Filter class itself, take a parameter of the form BufferedTransformation*. This parameter specifies a pointer to any object derived from BufferedTransformation that is to be attached to the object that is being constructed.

A pointer to an attached object becomes owned by the attached-to object as soon as the attachment relationship commences. The attached object will automatically be freed when the attached-to object is destructed.

If no object-to-be-attached is specified when constructing an object derived from Filter, or if a null pointer is passed, an object of a predetermined type will be created. Currently, the default is to create a MessageQueue object. A MessageQueue will store all data it receives through Put() methods for later retrieval via Get() methods, in the same order in which data was received.

Attach(). Attaches another transformation object to the end of the current attachment chain.

Detach(). Deletes the current attachment chain, destructing and freeing all objects it consists of; then, it attaches the specified object. If a null pointer is passed, an attachment object of the default type is created.

AttachedTransformation(). Returns a pointer to the currently attached transformation object. Should never be null.

Use the default MessageQueue object:

 // Similar to the earlier example, but no sink is used:
  // HashFilter is left to create a MessageQueue attachment object.
  // The calculated message digest is extracted via the Get() methods.
  
  char const* zInputFile = ...;
  char const* zOutputFile = ...;
  SHA hash;
  FileSource source(zInputFile, true, new HashFilter(hash));
 
  byte abDigest[SHA::DIGESTSIZE];
  if (source.Get(abDigest, sizeof(abDigest)) != sizeof(abDigest))
      throw "shouldn't happen";

Using Detach() and Attach():

  // Instead of pumping the whole file immediately, don't pump
  // anything at object creation time (hence the 'false' parameter
  // to FileSource instead of 'true' as used earlier).
 
  string sEncoding;
  FileSource source(zInputFile, false, new StringSink(sEncoding));
  source.Pump(3);
 
  // Replace the StringSink attachment with HexEncoder or Base64Encoder,
  // followed by FileSink; then pump everything into the output file.
  // The contents of the output file will be hex or base64-encoded,
  // depending on the value of sEncoding.
 
  if (sEncoding == "hex")
      source.Detach(new HexEncoder);
  else if (sEncoding == "b64")
      source.Detach(new Base64Encoder);
  else
      throw "Invalid encoding identifier";
 
  source.Attach(new FileSink(zOutputFile));
  source.PumpAll();
      // Ordinarily, we would now have to call MessageEnd() to flush
      // Base64Encoder's buffer; however, the call to PumpAll() will
      // take care of that implicitly. But a call to Pump() wouldn't!
      // If we called Pump() instead of PumpAll(), we would have to
      // follow-up with a MessageEnd().

Source

Source, derived from Filter, is an abstract base class for a type of filters that act as sources of data. A class derived from Source extracts data from any of a variety of locations - a file, memory, a network socket - and sends it to its attached transformation for further processing. If no transformation is explicitly attached to the Source object, data is stored in an attached object of the default type (MessageQueue), which only stores the data so that it can later be extracted through Get() methods.

In order for a Source object to extract any data from whereever it is extracting the data from, it must be told so. The process of data extraction is called pumping. Currently, the Source base class declares three pumping functions (derived classes may provide others):

Pump(). Pumps the specified number of bytes or until the end of the current message is encountered, whichever comes first. If no argument is supplied, pumps until the end of the current message. Does not call MessageEnd() on its attached transformation when the end of the message is encountered.

PumpMessages(). Pumps the specified number of whole messages, or until there are no more messages, whichever comes first. If no argument is supplied, pumps until there are no more whole messages. Unless auto signal propagation has been disabled, PumpMessages() calls MessageEnd() on its attached transformation after the end of each message.

PumpAll(). Pumps all messages just like PumpMessages() would if called with no argument, and then pumps the following uncomplete message until there is no more data to extract. With FileSource and StringSource, PumpAll() extracts data like Pump() and then calls MessageEnd().

Note that, when a source object doesn't know the structure of data it is processing - as is usually the case - all data will be interpreted as a single message. Therefore, the PumpMessages() method primarily makes sense for custom source objects which know the structure of data that is being processed.

Classes derived from Source usually provide a constructor parameter that indicates whether or not to call PumpAll() from the constructor itself. Using this parameter, one can initialize and execute a chain of filters all in one statement:

 char const* zInputFile = ...;
 char const* zOutputFile = ...;
 SHA hash;
 FileSource(zInputFile, true,
     new HashFilter(hash,
         new FileSink(zOutputFile),
         true));

The true parameter to FileSource indicates that all data should be pumped immediately from the FileSource constructor, as soon as the chain is initialized.

Example - Using Pump():

 // Instead of pumping the whole file immediately, don't pump
 // anything at object creation time (hence the 'false' parameter
 // to FileSource instead of 'true' as used earlier).

 string sEncoding;
 FileSource source(zInputFile, false, new StringSink(sEncoding));
 source.Pump(3);

 // Replace the StringSink attachment with HexEncoder or Base64Encoder,
 // followed by FileSink; then pump everything into the output file.
 // The contents of the output file will be hex or base64-encoded,
 // depending on the value of sEncoding.

 if (sEncoding == "hex")
     source.Detach(new HexEncoder);
 else if (sEncoding == "b64")
     source.Detach(new Base64Encoder);
 else
     throw "Invalid encoding identifier";

 source.Attach(new FileSink(zOutputFile));
 source.PumpAll();
     // Ordinarily, we would now have to call MessageEnd() to flush
     // Base64Encoder's buffer; however, the call to PumpAll() will
     // take care of that implicitly. But a call to Pump() wouldn't!
     // If we called Pump() instead of PumpAll(), we would have to
     // follow-up with a MessageEnd().

Sink

Unlike Source, the Sink abstract base class is derived from BufferedTransformation. In an attachment chain of filters, a sink is always the last object in the chain: it stores all data it receives through its Put() interface to whatever storage implemented by the specific sink type.

Example - read the contents of a file into a string:

 char const* zInputFile = ...;
 string sContents;
 FileSource(zInputFile, true, new StringSink(sContents));

Example - store a hash value into a buffer of bytes:

 string sData = ...;
 byte abDigest[SHA::DIGESTSIZE];
 SHA sha;
 StringSource(sData, true,
     new HashFilter(sha,
         new ArraySink(abDigest, sizeof(abDigest))));

Example - send data through a network socket:

 Socket s;

 ...
 ... // Open connection  
 ...

 SocketSink sink(s);

 string sDataToSend;
 sink.PutWord32(sDataToSend.size());
 sink.Put((byte const*) sDataToSend.data(), sDataToSend.size());
 
 // An alternative way to say the same thing:
 // StringSource(sDataToSend, true, new Redirector(sink));

Derived Classes

MeterFilter, TransparentFilter, OpaqueFilter

Measures the number of bytes, messages and message series passed through the filter, as well as the number of bytes in the current message and the number of messages in the current series.

Depending on initialization parameters, MeterFilter can pass all input data to an attached transformation or discard all input data.

TransparentFilter and OpaqueFilter are derivatives of MeterFilter that either pass (TransparentFilter) or discard (OpaqueFilter) all incoming data.

StreamCipherFilter

Initialized with a stream cipher object, this filter class encrypts all input data with that stream cipher and passes the ciphertext to its attached transformation.

HashFilter, HashVerifier

HashFilter uses the message digest algorithm it is initialized with to calculate the hash of all input data up to the first MessageEnd() signal, at which time it outputs the resulting hash value to its attached transformation. Depending on initialization parameters, HashFilter does or does not forward input data to its attached transformation before outputing the hash. Once MessageEnd() is processed, the message digest object is reset and ready to process another message.

HashVerifier is a flexible filter that encapsulates a message digest algorithm with the purpose of verifying digests of input messages. There are numerous flags that can be set at HashVerifier initialization, depending on where the hash value will be located (beginning or end of message), whether the message and/or the hash should be forwarded to the attached transformation, whether the result of the verification (a boolean) should be forwarded to the attached transformation, and whether an exception should be thrown if digest verification fails.

 char const* zInputFile = ...;
 char const* zOutputFile = ...;
 SHA hash;
 FileSource(zInputFile, true,
     new HashFilter(hash,
         new FileSink(zOutputFile),
         true));
 
 // The last 'true' parameter means that the whole message
 // will be forwarded to FileSink, not just the calculated hash.
 // If omitted, the default value would be 'false'.

SignerFilter, VerifierFilter

SignerFilter is (currently) a rather rudimentary class that encapsulates a PK_Signer-derived class to apply a digital signature to input data. Only the digital signature is transferred to the attached transformation.

VerifierFilter is (currently) also a rather rudimentary class that encapsulates a PK_Verifier-derived class to verify a digital signature on input data. The digital signature itself has to be passed to the VerifierFilter object via the PutSignature() method.

In practice, it is often simpler to use SignMessage() and VerifyMessage() methods on the PK_Signer and PK_Verifier objects, respectively.

Example - sign a message

 AutoSeededRandomPool rng;

 // The below type is derived from PK_Signer
 RSASSA_PKCS1v15_SHA_Signer privkey = ...;

 char const* zInputFile = ...;
 string sSignature;
 FileSource(zInputFile, true,
     new SignerFilter(rng, privkey,
         new StringSink(sSignature)));
 
 // sSignature now contains the digital signature computed on the
 // contents of the input file using SHA and RSA

Redirector

A Sink that ends an attachment chain, but passes input data into another attachment chain. The difference between using Redirector and using regular attachment is that Redirector doesn't own its attached transformation; it will not destruct and free the attached object once it destructs.

Example - concatenate files

 void ConcatenateFiles(BufferedTransformation& bt)
  {
      char const* zInputFile1 = ...;
      char const* zInputFile2 = ...;
      char const* zInputFile3 = ...;
  
      // Concatenate the three files into the same BufferedTransformation object.
      // Only allow the MessageEnd() signal to be passed into bt at the end
      // of the last file.
      
      FileSource(zInputFile1, true, new Redirector(bt, false));
      FileSource(zInputFile2, true, new Redirector(bt, false));
      FileSource(zInputFile3, true, new Redirector(bt));
  }
  
  ...
  
  string sConcatenated;
  ConcatenateFiles(StringSink(sConcatenated));

Example - send data through a network socket:

Socket s;
 
...
... // Open connection  
...
 
SocketSink sink(s);
 
string sDataToSend;
sink.PutWord32(sDataToSend.size());
sink.Put((byte const*) sDataToSend.data(), sDataToSend.size()); 
  
// An alternative way to say the same thing:
// StringSource(sDataToSend, true, new Redirector(sink));

ArraySink

A Sink that stores data into a buffer of predetermined size; any exceding data is discarded. The number of bytes already stored and the amount of space still available can be determined through methods AvailableSize() and TotalPutLength().

Example - store a hash value into a buffer of bytes:

 string sData = ...;
 byte abDigest[SHA::DIGESTSIZE];
 SHA sha;
 StringSource(sData, true,
     new HashFilter(sha,
         new ArraySink(abDigest, sizeof(abDigest))));

StringSinkTemplate, StringSink

StringSinkTemplate is a template Sink that stores data into any string type derived from std::basic_string. StringSink is a typedef for StringSinkTemplate<std::string>.

When StringSink is initialized with a non-empty target string, the target string will be appended to; the original contents of the string will be left intact.

Example - read the contents of a file into a string:

 char const* zInputFile = ...;
 string sContents;
 FileSource(zInputFile, true, new StringSink(sContents));

StringSource

A Source that extracts data from a string type, from an ASCIIZ string, or from a buffer of bytes.

To find other topics in the User Guide, visit Category:User Guide.