Crypto++ User's Guide

 This guide consists of the following chapters:
  • General Information
  • Header Files


General Information

 This section contains the following information:
  • Introduction
  • About the Author
  • About Crypto++
  • Want to Contribute?
  • Resources


Introduction

Welcome to Crypto++!

This guide was written in order to demonstrate the capabilities of the Crypto++ library [www.cryptopp.com] and to show how its features can be integrated into practical applications. To make the most of this guide, you should be:
  • fairly proficient in C++, and
  • acquainted with basic cryptographic techniques.
This guide does not attempt to teach what cryptography is, or how to use it properly. Learning cryptography takes time, and there are books and resources available that teach this subject very well.

I therefore assume that you are well acquainted with cryptography, and will try to show how you can use the Crypto++ library to achieve your objectives. Since Crypto++ is a moving target, this guide is arranged primarily as a user's guide; it would be impossible to provide a normative reference without a serious devotion of sustained effort. I therefore recommend that you learn to use Crypto++ source files as your primary reference, and only use this guide to help you in your initial orientation.

I hope you will enjoy reading this guide as much as I did writing it! If you have any questions or comments, feel free to contact me; you can reach me at [guide@denisbider.com].

Sincerely,
- denis


About the Author

About the author of this guide

denis bider is an independent consultant specialising in digital security and applied cryptography. His company, Bitvise, provides crypto consulting and integration services as well as support for Crypto++ [http://www.bitvise.com].

denis has several years of experience with digital security and cryptography, including:
  • As security consultant, development and integration of Crypto++-based security solutions with several applications in USA and Europe.
  • Design and integration of PKI-based session security solution for the stock trading system used by the Ljubljana Stock Exchange [http://www.ljse.si], with upcoming deployments in other Eastern European countries.
  • Design and implementation of Certify [http://www.bitvise.com/certify.html], a Crypto++-based digital certificate and smart card management library; and Surgeon [http://www.bitvise.com/surgeon.html], a Certify-based Certificate Authority application for smart-card-based digital certificate generation.
  • Major role in design of PKI-based security solutions for Slovenian corporate and personal e-banking services. These security solutions are now protecting the majority of e-banking users in Slovenia.


About Crypto++

Crypto++ is a comprehensive C++ class library that implements a wide range of cryptographic schemes. It covers a majority of mainstream cryptographic algorithms - including RSA, ECC, DSA, SHA-1, RC6, Twofish, and most others.

Crypto++ is widely recognized for its comprehensiveness, its sophisticated programming style as well as for a dedicated author, Wei Dai, who continues to support and develop the library.

All of these features distinguish Crypto++ as exceptional, not only when compared to other similar free or public domain products, but also compared to commercial cryptographic libraries. Crypto++ proves that quality cryptography does not necessarily cost a fortune.

Visit also the Resources section for links to Crypto++ web sites.


Want to contribute?

If you find the Crypto++ User's Guide useful, you can contribute!

And who says sending the author some money can't be fun once for a change?

First, take the Rich-Quiz [located at www.bitvise.com/rich-quiz.html] so that the Bitvise Oracle can tell you who you are. Then, just follow the links from the Rich-Quiz results page.


Resources

Useful Computer Security and Cryptography Resources

Crypto++

  • Wei Dai's official Crypto++ web site [www.cryptopp.com]
  • Crypto++ general information page [www.bitvise.com/crypto++.html]
  • Crypto++ support and integration services [www.bitvise.com/services.html]
  • Wei Dai's homepage [www.eskimo.com/~weidai]

Additional Libraries

  • AES PHM source code [www.denisbider.com/aesphm.zip]
  • BSL - Base Services Library [www.bitvise.com/bsl.html]

Recommended Books

  • If you frequently make use of cryptographic algorithms, you will most probably need a reference like Bruce Schneier - Applied Cryptography (ISBN: 0471128457 hardcover, 0471117099 paperback).
  • Regarding computer security overall, a good philosophy book might be Bruce Schneier - Secrets and Lies : Digital Security in a Networked World (ISBN: 0471253111 - hardcover). However, you can pretty much learn the same thing by reading past issues of CRYPTO-GRAM [www.counterpane.com/crypto-gram.html].
I don't want to appear biased; there are a bunch of other good books you will need or will find useful if you want to become a cryptographer/cryptologist yourself. However, if you are only going to use algorithms which have already been implemented, a reference like Applied Cryptography - although now already a bit outdated - is more or less all you will need.


Header Files

Crypto++ implements a large number of cryptographic schemes that have similar properties and behavior, but are largely independent from each other.

Consequentially, Crypto++ defines a number of common base classes from which most of the algorithm implementations inherit: you will encounter these classes in all programs that use Crypto++. Often, the headers in which these basic abstractions are declared do not have to be included explicitly, as they are automatically included in the more specific header files.

On the other hand, each separate algorithm that you need to implement is usually declared in its own header file that you need to include explicitly. Exactly which header file you need to include is sometimes obvious, and sometimes not. For the cases when it is not, this section describes the most important header files in Crypto++.

As a matter of convenience, we will divide Crypto++ header files into the following groups:
  • Crypto++ Infrastructure
  • Data Manipulation
  • Random Number Generation
  • Hash Functions and MACs
  • Block Ciphers
  • Stream Ciphers
  • Public Key Cryptosystems


Crypto++ Infrastructure Header Files

Header file | Description
cryptlib.h | Provides basic abstractions for the Crypto++ library. These include: Exception, BufferedTransformation, BlockTransformation, StreamCipher, RandomAccessStreamCipher, RandomNumberGenerator, HashModule, MessageAuthenticationCode, and public key crypto base classes. If Crypto++ had a most important header file, then cryptlib.h would be it. Almost all classes defined by Crypto++ derive from base classes defined in cryptlib.h. When you need to figure out how to use one of the derived classes, your best bet is to find the relevant base class in cryptlib.h.
filters.h | Provides additional input/output base classes (derived from BufferedTransformation): Filter and derivatives, Sink and derivatives, Source and derivatives. filters.h may be the second most important header file in Crypto++. A large number of classes in Crypto++ derive from Filter, which is an especially useful type of BufferedTransformation because of its attachment functionality. In order to make elegant and efficient use of Crypto++, you need to be acquainted with the Filter class, as well as derived classes such as StringSource, StringSink, FileSource and FileSink (declared in files.h).
oids.h | Defines specific object identifiers (OIDs) that are needed by or can be used on various classes throughout the library.
files.h | Defines classes FileStore, FileSource and FileSink (derived from Store, Source and Sink).
network.h | Defines base classes NonblockingSource, NonblockingSink; base classes NetworkReceiver, NetworkSender; and finally, NetworkSource and NetworkSink.
socketft.h | Encapsulates network sockets and provides Crypto++ source and sink classes for them. The following types are defined: Socket, SocketSource, SocketSink. Support for Berkeley-style as well as Windows-style sockets is provided.
misc.h | A bunch of bit and byte manipulating functions like BytePrecision(), bitReverse(), etc. Also defines template class SecBlock, a somewhat secure memory management class used for rudimentary protection of sensitive data in Crypto++.
smartptr.h | Definitions for various types of smart pointer types used in Crypto++: member_ptr, value_ptr, clonable_ptr, counted_ptr, vector_member_ptrs.


cryptlib.h

 The following components of this header file are described:
  • Exception
  • BufferedTransformation
  • RandomNumberGenerator
  • HashModule
  • MessageAuthenticationCode


Exception

Defined in: cryptlib.h

All exception classes in Crypto++ are derived from Exception, which is merely an implementation of the standard C++ library exception interface defined by std::exception. It is therefore possible to catch all Crypto++ exceptions like demonstrated in the below example.

Usage:

 class SpecificException : public CryptoPP::Exception
 {
 public:
     SpecificException(std::string const& strDesc)
         : Exception(strDesc) {}
 };

 ...
 void ProcessTrivialFailure();
 void ProcessCatastrophicFailure();
 ...

 try
 {
     ...

     if (!...)
         throw CryptoPP::Exception(
             "This and that failed, releasing such and such resources");

     ...

     if (!...)
         throw SpecificException(
             "Something specific went wrong, so we are using a derived "
             "exception type to make it possible for programs to recognize "
             "this kind of exception without having to investigate the "
             "description returned by std::exception::what().");

     ...
 }
 catch (SpecificException const& e)
 {
     cout << "SpecificException caught:" << endl
          << e.what() << endl;

     // In this example, we recognize this type of exception and
     // we know that it is a trivial one; we have a routine to process it.
     ProcessTrivialFailure();
 }
 catch (std::exception const& e)
 {
     // This clause will also catch CryptoPP::Exception as well as
     // all derived exception classes, except for SpecificException
     // which has already been caught above.
     cout << "std::exception caught:" << endl
          << e.what();

     // In this example, an exception that we do not explicitly
     // recognize is by default a catastrophic one.
     ProcessCatastrophicFailure();
 }

Example - CatchExceptions.cpp:

 // Crypto++
 #include "files.h"  // FileSource, FileSink
 #include "osrng.h"  // AutoSeededRandomPool

 // std
 #include <iostream>
 #include <cstdio>

 int main()
 {
     using namespace std;
     using namespace CryptoPP;

     try
     {
         static char const* szFilename = "Message.txt";

         string strMessageBefore = "How do you do?";
         cout << "Message before: " << strMessageBefore << endl;
         StringSource(strMessageBefore, true, new FileSink(szFilename));

         // This rogue block of code will sometimes remove the
         // file that was just written to, and sometimes not.
         // The decision depends on the system's random data generator.
         AutoSeededRandomPool rng;
         if (!(rng.GenerateByte() % 3))
             remove(szFilename);

         string strMessageAfter;
         FileSource(szFilename, true, new StringSink(strMessageAfter));
             // FileSource will throw if the file does not exist

         cout << "Message after: " << strMessageAfter << endl;
     }
     catch (CryptoPP::Exception const& e)
     {
         cout << "CryptoPP::Exception caught:" << endl
              << e.what() << endl;
         return 1;
     }

     return 0;
 }

Output - case 1:

 Message before: How do you do?
 Message after: How do you do?

Output - case 2:

 Message before: How do you do?
 CryptoPP::Exception caught:
 FileStore: error opening file for reading: Message.txt


BufferedTransformation

Defined in: cryptlib.h

The BufferedTransformation base class is the key to virtually everything else in Crypto++. BufferedTransformation is an abstract base class; it defines a versatile input/output interface that is used throughout the library. In order to make good use of Crypto++, you should be well acquainted with BufferedTransformation as well as its derived classes.

One of the most important characteristics of BufferedTransformation is that, although it provides definitions for a wide range of input/output methods, not all of them need to be supported by its derived classes. For example, the Source classes do not implement output methods, and the Sink classes do not implement input methods. But on the other hand, classes like ByteQueue or MessageQueue; or Base64Encoder or HexDecoder; implement both input as well as output functionality.

[As a consequence, Source classes always appear at the beginning of a Filter chain; Sink classes always appear at the end; whereas other Filter classes may appear at the beginning, at the end, or in the middle. See Filter.]

In a Crypto++ user's code, BufferedTransformation is typically used as an abstraction used in place of any object that might provide an input and/or output interface.

Usage:

 BufferedTransformation& bt = ...;

 ...

 // Data storage / processing

 byte bByte = ...;
 bt.Put(bByte);

 byte const* pbData = ...;
 unsigned int nDataLen = ...;
 bt.Put(pbData, nDataLen);

 // The Flush() call is meaningful if bt is actually a network socket,
 // or a compression object, or another object that buffers input in a way
 // that allows for flushing
 bt.Flush(true);

 word32 wWord = ...;
 bt.PutWord32(wWord);  // default is big-endian

 bt.MessageEnd();

 bt.Put(pbData, nDataLen);

 bt.MessageEnd();

 ...

 // Data retrieval

 unsigned int const nPeekSize = ...;
 byte peekBuffer[nPeekSize];
 unsigned int nNrBytesPeeked = bt.Peek(peekBuffer, nPeekSize);

 if (bt.AnyRetrievable())
 {
     unsigned int nOutputSize = bt.MaxRetrievable();
     byte* pbOutput = new byte[nOutputSize];
     CAutoFreePtr<byte> autoFreeOutput(pbOutput);

     if (bt.Get(pbOutput, nOutputSize) != nOutputSize)
         throw "Unable to retrieve data although MaxRetrievable() says it's there";

     // Do some processing
     ...
 }

 if (!bt.GetNextMessage())
 {
     cout << "No more messages" << endl;
     break;
 }

 byte bByte;
 if (bt.Get(bByte) != sizeof bByte)
 {
     cout << "No more data" << endl;
     break;
 }

 ...

Example - DumpMessage.cpp:

 // Crypto++
 #include "cryptlib.h"

 // std
 #include <iostream>

 void DumpMessage(CryptoPP::BufferedTransformation& bt)
 {
     using namespace std;

     static char const* szHexDigits = "0123456789abcdef";
     byte b;
     while (bt.AnyRetrievable())
     {
         if (!bt.Get(b))
             throw "Error: AnyRetrievable() returned true, "
                   "so this shouldn't happen.";

         // It is much easier to implement this using HexEncoder;
         // however, let's not get into that just yet. The below code
         // could do some special kind of processing that is not
         // supported by an off-the-shelf Filter class.

         cout << szHexDigits[(b >> 4) & 0x0f]
              << szHexDigits[b & 0x0f]
              << ' ';
     }
 }

 // Crypto++
 #include "filters.h"

 int main()
 {
     using namespace CryptoPP;

     char const* szMessage = "How do you do?";
     DumpMessage(StringSource(szMessage, true));
         // If we constructed StringSource with 'false' instead of 'true',
         // StringSource wouldn't call its PumpAll() method on construction,
         // and no data would be extractable from the StringSource object
         // until someone called the object's Pump() or PumpAll() method.

     return 0;
 }

Output:

 48 6f 77 20 64 6f 20 79 6f 75 20 64 6f 3f


RandomNumberGenerator

Defined in: cryptlib.h

The class RandomNumberGenerator is a virtual base class for various implementations of random number generation, the most useful of which is AutoSeededRandomPool (defined in osrng.h) or, when the latter is unavailable, RandomPool (defined in randpool.h).

Another useful class derived from RandomNumberGenerator is NullRNG (defined in rng.h), which can be passed to functions and methods which ask for an RNG but do not actually use it.

Usage:

 RandomNumberGenerator& rng = ...;

 ...

 byte bByte = rng.GenerateByte();

 ...

 unsigned int const nBlockSize = ...;
 byte abBlock[nBlockSize];
 rng.GenerateBlock(abBlock, nBlockSize);

 ...

 word32 wMinimum = ...;
 word32 wMaximum = ...;
 word32 wWord = rng.GenerateWord32(wMinimum, wMaximum);

Example - TestEncryptionWithRng.cpp:

 // Crypto++
 #include "cryptlib.h"
 #include "filters.h"    // StringSink
 #include "osrng.h"      // AutoSeededRandomPool
 #include "hex.h"        // HexEncoder

 // std
 #include <string>
 #include <vector>
 #include <iostream>

 void GeneratePlaintexts(
     std::vector<std::string>& vectPlaintexts,
     unsigned int nNrPlaintexts,
     unsigned int nMinPlaintextLen,
     unsigned int nMaxPlaintextLen,
     CryptoPP::RandomNumberGenerator& rng)
 {
     using namespace std;
     using namespace CryptoPP;

     vectPlaintexts.resize(0);

     cout << "Generating " << nNrPlaintexts << " plaintexts..." << endl;

     while (nNrPlaintexts--)
     {
         word32 nPlaintextLen = rng.GenerateWord32(nMinPlaintextLen, nMaxPlaintextLen);

         // Can't instantiate a string, resize it and write to it directly -
         // std::string's internal storage might not be contiguous
         SecByteBlock sbbGenerated(nPlaintextLen);
         rng.GenerateBlock(sbbGenerated.Begin(), nPlaintextLen);

         vectPlaintexts.push_back(
             std::string(
                 (char const*) sbbGenerated.Begin(),
                 sbbGenerated.Size()));
     }
 }

 void TestEncryption(
     std::string const& strPlaintext,
     CryptoPP::BufferedTransformation& encryptor,
     CryptoPP::BufferedTransformation& decryptor)
 {
     using namespace std;
     using namespace CryptoPP;

     string strEncrypted;
     string strDecrypted;

     cout << "Testing plaintext of length " << strPlaintext.size() << ": ";

     try
     {
         // Encrypt plaintext
         encryptor.Detach(new StringSink(strEncrypted));
         encryptor.Put((byte const*) strPlaintext.data(), strPlaintext.size());
         encryptor.MessageEnd();

         // Decrypt the resulting ciphertext
         decryptor.Detach(new StringSink(strDecrypted));
         decryptor.Put((byte const*) strEncrypted.data(), strEncrypted.size());
         decryptor.MessageEnd();

         // Compare decrypted plaintext with the original plaintext
         if (strDecrypted != strPlaintext)
             throw Exception("TestEncryption(): strDecrypted != strPlaintext");
     }
     catch (...)
     {
         string strPlaintextHex;
         string strEncryptedHex;
         string strDecryptedHex;
         StringSource(strPlaintext, true,
             new HexEncoder(new StringSink(strPlaintextHex)));
         StringSource(strEncrypted, true,
             new HexEncoder(new StringSink(strEncryptedHex)));
         StringSource(strDecrypted, true,
             new HexEncoder(new StringSink(strDecryptedHex)));

         cout << "ERROR" << endl
              << "strPlaintext (size " << strPlaintext.size() << "): " << endl
              << strPlaintextHex << endl
              << "strEncrypted (size " << strEncrypted.size() << "): " << endl
              << strEncryptedHex << endl
              << "strDecrypted (size " << strDecrypted.size() << "): " << endl
              << strDecryptedHex << endl;

         throw;
     }

     cout << "OK" << endl;
 }

 // Crypto++
 #include "default.h"    // DefaultEncryptorWithMAC

 int main()
 {
     using namespace std;
     using namespace CryptoPP;

     try
     {
         unsigned int nPlaintexts = 16;
         vector<string> vectPlaintexts;

         AutoSeededRandomPool rng;
         GeneratePlaintexts(vectPlaintexts, nPlaintexts, 0, 1024, rng);

         char const* szPassphrase = "some passphrase";
         DefaultEncryptorWithMAC encryptor(szPassphrase);
         DefaultDecryptorWithMAC decryptor(szPassphrase);

         for (unsigned int i=0; i!=nPlaintexts; ++i)
         {
             cout << "(" << i << ") ";
             TestEncryption(vectPlaintexts[i], encryptor, decryptor);
         }

         cout << "All tests completed successfully." << endl;
     }
     catch (CryptoPP::Exception const& e)
     {
         cout << "main() - CryptoPP::Exception caught:" << endl
              << e.what() << endl;
         return 1;
     }

     return 0;
 }

Output:

 Generating 16 plaintexts...
 (0) Testing plaintext of length 1007: OK
 (1) Testing plaintext of length 680: OK
 (2) Testing plaintext of length 644: OK
 (3) Testing plaintext of length 601: OK
 (4) Testing plaintext of length 787: OK
 (5) Testing plaintext of length 303: OK
 (6) Testing plaintext of length 531: OK
 (7) Testing plaintext of length 840: OK
 (8) Testing plaintext of length 584: OK
 (9) Testing plaintext of length 598: OK
 (10) Testing plaintext of length 1020: OK
 (11) Testing plaintext of length 753: OK
 (12) Testing plaintext of length 845: OK
 (13) Testing plaintext of length 304: OK
 (14) Testing plaintext of length 266: OK
 (15) Testing plaintext of length 206: OK
 All tests completed successfully.


HashModule

Defined in: cryptlib.h

All classes that implement one-way hash functions in Crypto++ are derived from HashModule. A class derived from HashModule can be used either directly or indirectly.

When used directly, the whole hashing operation can be completed in one step by calling the CalculateDigest method, or in multiple steps by first calling the Update method several times and then wrapping up by calling the Final method.

Indirect use of a HashModule derivative can be done through a wrapper class like HashFilter, which enables one to use a hashing algorithm in a manner conforming to the Filter paradigm (a family within the BufferedTransformation hierarchy).

After a hash module's Final method is called - either by calling Final directly, or indirectly by calling CalculateDigest or by calling HashFilter::MessageEnd() on a filter initialized with the hash module, the hash module is ready to calculate another hash, just as if the hash module had just been initialized.

The HashModule base class also provides the methods Verify and VerifyDigest. These methods are used similarly to Final and CalculateDigest, respectively, with the only distinction that they verify the computed digest to a supplied digest instead of calculating a digest. For implementations of regular one-way hash functions, calling VerifyDigest() is equivalent to calling CalculateDigest() and then manually comparing the calculated digest with a supplied one. However, note that this is not the case with message authentication codes (which are also derived from HashModule and use the same interface).

Usage - single-step: (using SHA for illustration)

 byte const* pbData = ...;
 unsigned int nDataLen = ...;
 byte abDigest[SHA::DIGESTSIZE];

 SHA().CalculateDigest(abDigest, pbData, nDataLen);

 // abDigest now contains the hash of pbData

 ...

 if (!SHA().VerifyDigest(abDigest, pbData, nDataLen))
     throw "abDigest does not contain the right hash";

Usage - multi-step: (using SHA for illustration)

 byte const* pbData1 = ...;
 byte const* pbData2 = ...
 unsigned int nData1Len = ...;
 unsigned int nData2Len = ...;
 byte abDigest[SHA::DIGESTSIZE];

 SHA hash;
 hash.Update(pbData1, nData1Len);
 hash.Update(pbData2, nData2Len);
 hash.Final(abDigest);

 // abDigest now contains the hash of pbData1 and pbData2;
 // the object 'hash' can now be reused to calculate another digest

 ...

 SHA hash;
 hash.Update(pbData1, nData1Len);
 hash.Update(pbData2, nData2Len);
 if (!hash.Verify(abDigest))
     throw "abDigest does not contain the right hash";

Usage - through HashFilter: (using SHA for illustration)

 byte const* pbData = ...;
 unsigned int nDataLen = ...;
 word32 wNumber = ...
 byte abDigest[SHA::DIGESTSIZE];

 // In this example, HashFilter only outputs the hash.
 // A flag can be passed to the HashFilter constructor to make it
 // output the data that is being hashed as well.
 SHA hash;
 HashFilter hf(hash, new ArraySink(abDigest, sizeof abDigest));
 hf.Put(pbData, nDataLen);
 hf.PutWord32(wNumber); // big-endian is default
 hf.MessageEnd();

 // abDigest now contains the hash of pbData and wNumber;
 // the object 'hf' can now be used to hash another message.

 // In this example, HashVerifier expects the hash to appear
 // at the beginning of input data, and it will only output
 // a single byte which will be non-zero if the hash verifies,
 // and zero otherwise. Again, there is a variety of other
 // behaviors that can be specified through the constructor.
 HashVerifier hv(hash);
 hv.Put(abDigest, sizeof abDigest);
 hv.Put(pbData, nDataLen);
 hv.PutWord32(wNumber);

 if (!hv.GetLastResult())
    throw "abDigest does not contain the right hash";

 // Alternative method of verification:

 byte bResult;
 if (!hv.Get(bResult))
    throw "Unexpected";

 if (!bResult)
    throw "abDigest does not contain the right hash";

Example - DumpHashes.cpp:

 // Crypto++
 #include "hex.h"        // HexEncoder

 // std
 #include <iostream>
 #include <string.h>

 void DumpHash_SingleStep(
     CryptoPP::HashModule& hash,
     char const* szModuleName,
     std::string const& strData)
 {
     using namespace std;
     using namespace CryptoPP;

     // Cannot use std::string for buffer;
     // its internal storage might not be contiguous
     SecByteBlock sbbDigest(hash.DigestSize());

     hash.CalculateDigest(
         sbbDigest.Begin(),
         (byte const*) strData.data(),
         strData.size());

     cout << szModuleName << " SS: ";
     HexEncoder(new FileSink(cout)).Put(sbbDigest.Begin(), sbbDigest.Size());
     cout << endl;
 }

 void DumpHash_MultiStep(
     CryptoPP::HashModule& hash,
     char const* szModuleName,
     std::string const& strDataPart1,
     std::string const& strDataPart2,
     std::string const& strDataPart3)
 {
     using namespace std;
     using namespace CryptoPP;

     hash.Update((byte const*) strDataPart1.data(), strDataPart1.size());
     hash.Update((byte const*) strDataPart2.data(), strDataPart2.size());
     hash.Update((byte const*) strDataPart3.data(), strDataPart3.size());

     // Cannot use std::string for buffer;
     // its internal storage might not be contiguous
     SecByteBlock sbbDigest(hash.DigestSize());

     hash.Final(sbbDigest.Begin());

     cout << szModuleName << " MS: ";
     HexEncoder(new FileSink(cout)).Put(sbbDigest.Begin(), sbbDigest.Size());
     cout << endl;
 }

 void DumpHash_HashFilter(
     CryptoPP::HashModule& hash,
     char const* szModuleName,
     std::string const& strDataPart1,
     std::string const& strDataPart2,
     std::string const& strDataPart3)
 {
     using namespace std;
     using namespace CryptoPP;

     // Here, we are free to use std::string as the destination,
     // because StringSink uses the correct std::string interface to append data
     string strDigest;
     HashFilter hashFilter(hash, new StringSink(strDigest));
     hashFilter.Put((byte const*) strDataPart1.data(), strDataPart1.size());
     hashFilter.Put((byte const*) strDataPart2.data(), strDataPart2.size());
     hashFilter.Put((byte const*) strDataPart3.data(), strDataPart3.size());
     hashFilter.MessageEnd();

     cout << szModuleName << " HF: ";
     StringSource(strDigest, true,
         new HexEncoder(
             new FileSink(cout)));
     cout << endl;
 }

 void DumpHash(
     CryptoPP::HashModule& hash,
     char const* szModuleName,
     std::string const& strDataPart1,
     std::string const& strDataPart2,
     std::string const& strDataPart3)
 {
     DumpHash_SingleStep(hash, szModuleName, strDataPart1 + strDataPart2 + strDataPart3);
     DumpHash_MultiStep(hash, szModuleName, strDataPart1, strDataPart2, strDataPart3);
     DumpHash_HashFilter(hash, szModuleName, strDataPart1, strDataPart2, strDataPart3);
 }

 // Crypto++
 #include "sha.h"        // SHA-1, SHA-256, SHA-384, SHA-512
 #include "ripemd.h"     // RIPEMD160
 #include "md5.h"        // MD5
 #include "crc.h"        // CRC-32

 int main()
 {
     using namespace std;
     using namespace CryptoPP;

     std::string strDataPart1 = "part 1; ";
     std::string strDataPart2 = "part two; ";
     std::string strDataPart3 = "PART THREE.";

     try
     {
         DumpHash(SHA(),       "SHA      ", strDataPart1, strDataPart2, strDataPart3);
         DumpHash(SHA256(),    "SHA256   ", strDataPart1, strDataPart2, strDataPart3);
         DumpHash(RIPEMD160(), "RIPEMD160", strDataPart1, strDataPart2, strDataPart3);
         DumpHash(MD5(),       "MD5      ", strDataPart1, strDataPart2, strDataPart3);
         DumpHash(CRC32(),     "CRC32    ", strDataPart1, strDataPart2, strDataPart3);
     }
     catch (CryptoPP::Exception const& e)
     {
         cout << "CryptoPP::Exception caught: " << endl
              << e.what() << endl;
         return 1;
     }

     return 0;
 }

Output:

 SHA       SS: 83F4A5C2778C5FC3FA4DC91BDE16203126E41D56
 SHA       MS: 83F4A5C2778C5FC3FA4DC91BDE16203126E41D56
 SHA       HF: 83F4A5C2778C5FC3FA4DC91BDE16203126E41D56
 SHA256    SS: 972226D00A46823E0A1FD0EB44EB7A865CA3AB981DE3927F8C1A98F4A0436AF9
 SHA256    MS: 972226D00A46823E0A1FD0EB44EB7A865CA3AB981DE3927F8C1A98F4A0436AF9
 SHA256    HF: 972226D00A46823E0A1FD0EB44EB7A865CA3AB981DE3927F8C1A98F4A0436AF9
 RIPEMD160 SS: 56583FCE48F5A443F3C7BE04DEA08465FCF8659F
 RIPEMD160 MS: 56583FCE48F5A443F3C7BE04DEA08465FCF8659F
 RIPEMD160 HF: 56583FCE48F5A443F3C7BE04DEA08465FCF8659F
 MD5       SS: F8F523DCDA45D469FCEFCCF09BC3D6D5
 MD5       MS: F8F523DCDA45D469FCEFCCF09BC3D6D5
 MD5       HF: F8F523DCDA45D469FCEFCCF09BC3D6D5
 CRC32     SS: CE4562A8
 CRC32     MS: CE4562A8
 CRC32     HF: CE4562A8


MessageAuthenticationCode

Defined in: cryptlib.h

All classes that implement message authentication codes in Crypto++ are derived from MessageAuthenticationCode. MessageAuthenticationCode, in turn, is derived from HashModule; hence, a class derived from MessageAuthenticationCode is used more or less the same as a class implementing a one-way hash function.

There are two primary distinctions between a message authentication code module and a hash module:
  • A class implementing a message authentication code needs to be supplied a key on construction;
  • Applying a MAC to the same input twice generally does not produce the same authentication code. Hence, verifying a MAC is not equivalent to calling CalculateDigest() and then manually comparing the calculated hash with the supplied one. Rather, either Verify or VerifyDigest must be used to verify a MAC.

Usage: (using HMAC for illustration)

 byte const* pbData = ...;
 unsigned int nDataLen = ...;

 byte const* pbKey = ...;
 unsigned int nKeyLen = ...;

 byte abMac[HMAC::DIGESTSIZE];

 HMAC(pbKey, nKeyLen).CalculateDigest(abMac, pbData, nDataLen);

 // abMac now contains the HMAC of pbData, keyed with pbKey

 ...

 if (!HMAC(pbKey, nKeyLen).VerifyDigest(abMac, pbData, nDataLen))
     throw "abMac does not contain a valid MAC for pbData, given pbKey";


filters.h

 The following components of this header file are described:
  • Base classes
  • Derived classes


filters.h - Base Classes

 The following base classes in this header file are described:
  • Filter
  • Source
  • Sink


Filter

Defined in: filters.h

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.

Example - letting the default MessageQueue object be used:

 // 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";

Example - 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

Defined in: filters.h

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

Defined in: filters.h

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));


filters.h - Derived Classes

 The following derived classes in this header file are described:
  • MeterFilter, TransparentFilter and OpaqueFilter
  • StreamCipherFilter
  • HashFilter and HashVerifier
  • Redirector
  • ArraySink
  • StringSinkTemplate and StringSink
  • StringSource


MeterFilter, TransparentFilter, OpaqueFilter

Defined in: filters.h

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

Defined in: filters.h

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

Defined in: filters.h

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.

Example:

 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, VerifierVerifier

Defined in: filters.h

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:

 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

Defined in: filters.h

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:

 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

Defined in: filters.h

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

Defined in: filters.h

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

Defined in: filters.h

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


Data Manipulation Header Files

Header file | Description
base64.h | Provides classes Base64Encoder and Base64Decoder, both derived from Filter.
hex.h | Provides classes HexEncoder and HexDecoder, both derived from Filter.
gzip.h | Provides compression and decompression classes Gzip and Gunzip. Both classes inherit indirectly from Filter.
zlib.h | Provides compression and decompression classes ZlibCompressor and ZlibDecompressor. Both classes inherit indirectly from Filter.

All classes provided in the above header files are very much alike, hence only base64.h is discussed as an example.


base64.h

Base64Encoder is a filter that takes any data as input and encodes it with the base-64 algorithm, optionally appending a line break after each full line (72 characters).

Note that, in the version of Crypto++ that is available at the time of this writing, Base64Encoder does not automatically encode buffered data on destruction. Therefore, if a chain of Filter objects includes Base64Encoder and no member of that chain that appears before Base64Encoder will call MessageEnd() automatically, it is necessary to terminate input by explicitly calling MessageEnd(). (The nPropagation argument to the MessageEnd() method can be used if propagation of the message-end signal to other filter objects attached to Base64Encoder is not desired.)

Base64Decoder is a filter that takes base-64 encoded data as input, with optional line breaks, and outputs the decoded binary data.

Usage:

 // Binary data
 byte const* pbData = ...;
 unsigned int nDataLen = ...;

 // Encode with line breaks (default).
 // Ordinarily, we would now have to call MessageEnd() to flush
 // Base64Encoder's buffer. However, the 'true' parameter will cause
 // the StringSource::PumpAll() method to be called, and that method
 // will cause MessageEnd() to be triggered implicitly.
 StringSource(pbData, nDataLen, true,
    new Base64Encoder(
        new FileSink("base64-encoded.dat")));

 // Encode without line breaks.
 StringSource(pbData, nDataLen, true,
    new Base64Encoder(
        new FileSink("base64-encoded.dat"),
        false));


Random Number Generation Header Files

Header file | Description
randpool.h | Defines RandomPool, a particularly useful type of RandomNumberGenerator. Given a quality random seed, this class uses hashing to provide virtually unlimited amounts of cryptographically strong pseudo random data.
osrng.h | Encapsulates platform-specific hooks for obtaining quality random data (classes NonblockingRng and BlockingRng). Provides AutoSeededRandomPool, a derivative of RandomPool that seeds itself automatically using BlockingRng or NonblockingRng, whichever is available.


Hash Function and MAC Header Files

Header file | Description
sha.h | Defines classes to implement various types of the SHA hash function: class SHA for SHA-1 and classes SHA256, SHA384 and SHA512 for SHA-2.
ripemd.h | Provides the RIPEMD160 class, implementing European Union's RIPEMD-160 hash function.
md5.h | Provides class MD5.
hmac.h | Defines HMAC, a one-way hash function MAC.
trunhash.h | Defines TruncatedHashModule, which, enveloped around a regular hash module, produces a shorter message digest; and class NullHash, a hash module that does nothing - that is, has a zero-length digest.

See also: HashModule.


Block Cipher Header Files

Header file | Description
default.h | Defines DefaultEncryptor, DefaultDecryptor, DefaultEncryptorWithMAC and DefaultDecryptorWithMAC. These classes currently provide a complete default implentation of Triple-DES encryption, including passphrase mashing, IV generation, block chaining (CBC) and message integrity (...WithMAC variants only).
modes.h | Provides interfaces for use of block ciphers in CFB mode (classes CFBEncryption and CFBDecryption), OFB mode (class OFB), Counter mode (class CounterMode), and PGP CFB mode (classes PGP_CFBEncryption and PGP_CFBDecryption).
cbc.h | Provides interfaces for use of block ciphers in raw CBC mode (classes CBCRawEncryptor and CBCRawDecryptor), padded CBC mode (classes CBCPaddedEncryptor and CBCPaddedDecryptor) and CBC with ciphertext stealing mode (classes CBC_CTS_Encryptor and CBC_CTS_Decryptor).
aes.h | Provides typedefs AESEncryption and AESDecryption mapping to RijndaelEncryption and RijndaelDecryption classes. *
rijndael.h | Defines RijndaelEncryption and RijndaelDecryption classes. *
twofish.h | Defines TwofishEncryption and TwofishDecryption classes. *
blowfish.h | Defines BlowfishEncryption and BlowfishDecryption classes. *
idea.h | Defines IDEAEncryption and IDEADecryption classes. *
cast.h | Defines classes for CAST with a 32..128-bit key (CAST128Encryption, CAST128Decryption) and with a 128...256-bit key (CAST256Encryption, CAST256Decryption). *
des.h Provides classes for:
  • regular DES (DESEncryption and DESDecryption); *
  • two-key triple DES (DES_EDE2_Encryption and DES_EDE2_Decryption); *
  • three-key triple DES (DES_EDE3_Encryption and DES_EDE3_Decryption); *
  • DESX (DES_XEX3_Encryption and DES_XEX3_Decryption). *

* These classes should not be used directly, but rather indirectly via one of the block-cipher mode classes.


Block Cipher Use Example: AES PHM

AES PHM stands for Padded Hashed Message encryptor/decryptor that uses AES as its underlying algorithm. I developed AES PHM because, at that time, Crypto++ did not have a general easy-to-use symmetric cipher class that would allow the developer to use AES without having to explicitly deal with various cipher modes and IVs. (There is/was DefaultEncryptorWithMAC, but that is hardcoded to use Triple-DES. A future release of Crypto++ may introduce a more general encryptor/decryptor to support any symmetric algorithm.)

AES PHM may not be the best it could be - it does not use the AES algorithm in any standardized way, so it is only useful in closed environments, and it is also hardcoded to support only the AES algorithm. However, AES PHM might make a good example of:
  • how you can implement your own classes facilitating the Crypto++ Filter paradigm;
  • how to use block cipher encryption/decryption classes if you need to use them directly.

A link to the AES PHM source code is available in the Resources section of this guide. Now on to the example:
  • Annotated aesphm.h
  • Annotated aesphm.cpp


Annotated aesphm.h

 #ifndef AESPHM_H_INCLUDED
 #define AESPHM_H_INCLUDED

 #include "filters.h"

 NAMESPACE_BEGIN(CryptoPP)
The AES PHM classes inherit from the Crypto++ Filter class, therefore filters.h has to be included. Since AES PHM is an extension to Crypto++, its classes are placed into the CryptoPP namespace; your own classes would be placed into your own namespace, or into the global namespace.

 class AES_PHM_Encryption : public Filter
 {
 public:
     AES_PHM_Encryption(
         RandomNumberGenerator& rng,
         byte const* pbKey, unsigned int nKeyLen,
         BufferedTransformation* pOutQueue = 0);
AES_PHM_Encryption is a filter class that uses the AES algorithm to encrypt all messages that enter the object, and outputs encrypted messages in the fashion of the Filter paradigm: if pOutQueue is non-null, the encrypted messages will be transferred to *pOutQueue; otherwise, a MessageQueue will be instantiated by the base Filter class to store the output of AES_PHM_Encryption. In this case, the stored output can later be retrieved using Get() methods on the AES_PHM_Encryption object; or transferred to another BufferedTransformation using the TransferTo() method; etc.

The rng parameter to the AES_PHM_Encryption constructor is necessary because AES PHM uses the random number generator to:
  • generate padding for the plaintext. Since the padding bytes are encrypted along with the plaintext, it might have been quite OK to use deterministic padding rather than to generate it randomly; using random padding in this case is entirely a matter of choice.
  • generate a new initialization vector (IV) for each message to be encrypted. Because the IV needs to be only unique, but not random, nor secret, nor unpredictable, any other method of IV generation that produces reasonably unique results would be acceptable; the decision to use a RandomNumberGenerator is just the simplest one.
The key specified with the pbKey and nKeyLen parameters can be of arbitrary length, and is the same key that will have to be supplied to AES_PHM_Decryption in order to decrypt the message. In the case of AES PHM, the contents of the key can be arbitrary - in any case, the key will be further processed to convert it into a form that is appropriate for use with the AES algorithm.

     void Put(byte b) { Put(&b, 1); }
     void Put(byte const* pbData, unsigned int nDataLen);
     void MessageEnd(int nPropagation = -1);
The Put() methods are the only BufferedTransformation virtual methods that need to be defined by a descendant of the Filter class. All other methods, including Get() functionality, have already been implemented properly by Filter and BufferedTransformation itself.

Apart from Put(), AES_PHM_Encryption also overrides MessageEnd() in order to perform special processing (encryption) at the end of each message. Specifically, AES_PHM_Encryption implements the Put() methods so that they store all input data into a (relatively) secure temporary buffer, whereas actual encryption only takes place when MessageEnd() is called. The algorithm could be designed so as to encrypt data on the fly, but this was not seen to be necessary at development time.

 private:
     RandomNumberGenerator& m_rng;
     SecByteBlock m_sbbKey;
     SecByteBlock m_sbbPlaintext;
     unsigned int m_nPlaintextBufSize;
     unsigned int m_nPlaintextLen;
 };
AES_PHM_Encryption keeps a reference to the random number generator supplied to the constructor, as it needs to use it to generate a new IV each time MessageEnd() is called.

The key or passphrase supplied to the AES_PHM_Encryption constructor, as transformed in the initialization process, is also kept throughout the object's lifetime (member variable m_sbbKey). Use of the SecByteBlock class ensures that the memory where the key/passphrase is stored will be zeroed when it is freed; however, there are no other guarantees, for instance that the memory will not be swapped to disk.

(To this end, I developed a library which provides a secure C++ allocator class, as well as associated string classes; this allocator automatically ensures that data allocated with it will not be swapped to disk. The name of the library is BSL (Base Services Library), and there is a link to it in the Resources section. The library currently only supports the Windows platform. Other platforms are meant to be supported later on as well, but there are no promises.)

The members m_sbbPlaintext, m_nPlaintextBufSize and m_nPlaintextLen are used to store the plaintext until MessageEnd() is called.

 class AES_PHM_Decryption : public Filter
 {
 public:

     class InvalidCiphertextOrKey : public BufferedTransformation::Err
     {
     public:
         InvalidCiphertextOrKey() : BufferedTransformation::Err(
             DATA_INTEGRITY_CHECK_FAILED,
             "AES_PHM_Decryption: Invalid Ciphertext or Key") {}
     };

     AES_PHM_Decryption(
         byte const* pbKey, unsigned int nKeyLen,
         BufferedTransformation* pOutQueue = 0);

     void Put(byte b) { Put(&b, 1); }
     void Put(byte const* pbData, unsigned int nDataLen);
     void MessageEnd(int nPropagation = -1);

 private:
     SecByteBlock m_sbbKey;
     SecByteBlock m_sbbCiphertext;
     unsigned int m_nCiphertextBufSize;
     unsigned int m_nCiphertextLen;
 };

 NAMESPACE_END

 #endif
The design of AES_PHM_Decryption is basically equivalent to that of AES_PHM_Encryption. As with AES_PHM_Encryption, all input data is stored to a temporary buffer and only processed when MessageEnd() is called. Notable differences are:
  • An exception class (InvalidCiphertextOrKey) is defined. The exception class is derived from BufferedTransformation::Err, which in turn is derived from CryptoPP::Exception, which in turn is derived from std::exception; this means that a catch clause that catches std::exception will also catch InvalidCiphertextOrKey. The InvalidCiphertextOrKey exception is thrown when, during processing triggered by MessageEnd(), it is found that the combination of the input ciphertext and the supplied key/passphrase does not produce a valid plaintext.
  • The constructor does not take a RandomNumberGenerator parameter; the decryption process reads the IV to decrypt the message with from the supplied ciphertext.
Note that AES_PHM_Decryption still uses SecByteBlock for its temporary buffer because decryption is done in-place, storing the decrypted plaintext into the same buffer.


Annotated aesphm.cpp

 // Crypto++
 #include "sha.h"
 #include "aes.h"
 #include "modes.h"
 #include "hmac.h"

 // AES PHM
 #include "aesphm.h"
AES PHM uses SHA-256 to derive a 256-bit encryption/decryption key from the supplied variable-length key or passphrase, hence sha.h needs to be included. AES PHM uses AES in CFB mode for encryption and decryption, hence aes.h and modes.h need to be included. Finally, AES PHM uses HMAC with SHA1 to achieve message integrity, therefore hmac.h is also required.

 NAMESPACE_BEGIN(CryptoPP)

 enum
 {
     MINIMUM_CIPHERTEXT_LENGTH = 46,
     IV_SEED_SIZE = 8,
     IV_SIZE = AESEncryption::BLOCKSIZE,
 };
To make the code less cluttered with magic numbers, the above constants are defined:
  • MINIMUM_CIPHERTEXT_LENGTH is used by AES_PHM_Decryption to quickly determine whether the input ciphertext is large enough to contain a valid plaintext.
  • IV_SEED_SIZE is the size of the randomly-generated seed from which the initialization vector (IV) is calculated. It is best if the length of the IV is the same as the cipher's block size (in this case, 16 bytes). However, the IV or some means to generate it needs to be transmitted along with the message to the message recipient. Therefore to decrease ciphertext bloat for small messages, I decided to transmit an 8-byte seed, which should suffice for IV uniqueness purposes, and generate the actual IV from that.
  • IV_SIZE is the size of the actual IV that is generated from the seed.

 void AES_PHM_GenerateIvFromSeed(byte const* pbIvSeed, byte* pbIv)
 {
     SecByteBlock sbbIvHash(SHA256::DIGESTSIZE);
     SHA256().CalculateDigest(sbbIvHash, pbIvSeed, IV_SEED_SIZE);

     // Assuming that SHA256::DIGESTSIZE == 32, IV_SEED_SIZE == 8
     for (unsigned int i=0; i!=IV_SIZE; ++i)
         pbIv[i] = sbbIvHash[i] ^ sbbIvHash[i+16];
 }
A utility function to generate the 16-byte IV from the 8-byte IV seed. I decided to use SHA-256 instead of SHA-1 to make the XOR-ing operation a bit easier to write - the size of a SHA-256 hash (32) is a multiple of the IV size (16), while the size of the SHA-1 hash (20) is not.

 void AES_PHM_AppendToBuffer(
     SecByteBlock& sbb, unsigned int& nBufSize, unsigned int& nTotalLen,
     byte const* pbData, unsigned int nDataLen)
 {
     if (nTotalLen + nDataLen > nBufSize)
     {
         nBufSize = 2*(nTotalLen + nDataLen);
         sbb.Grow(nBufSize);
     }
     memcpy(sbb+nTotalLen, pbData, nDataLen);
     nTotalLen += nDataLen;
 }
A utility function to append data to a SecByteBlock buffer, while exponentially increasing the capacity of the buffer to avoid memory reallocation for every append operation. Unlike std::string, which provides this functionality automatically, SecByteBlock does not have the notion of a capacity that is potentially greater than the size of the controlled sequence, therefore a utility function like the above is necessary if you want to use SecByteBlock as a temporary buffer.

(In BSL, a secure allocator is provided that can be used with the standard string class template to produce a new string class with secure memory allocation. The same allocator can also be used to produce new vector, list, map and other classes, all of them featuring secure allocation with the custom allocator provided by BSL. I propose that this is a better alternative to the SecBlock template in Crypto++. Also, it is more in-line with the spirit of the C++ standard template library; this is what the allocator concept was introduced into STL for.)

The AES_PHM_Encryption class

 AES_PHM_Encryption::AES_PHM_Encryption(
     RandomNumberGenerator& rng,
     byte const* pbKey, unsigned int nKeyLen,
     BufferedTransformation* pOutQueue)
          :   Filter(pOutQueue), m_rng(rng),
              m_nPlaintextBufSize(0), m_nPlaintextLen(0)
 {
     m_sbbKey.Resize(SHA256::DIGESTSIZE);
     SHA256().CalculateDigest(m_sbbKey, pbKey, nKeyLen);
 }
The AES_PHM_Encryption constructor initializes the object's Filter parent and calculates the SHA-256 hash of the supplied key or passphrase. The resulting digest is then used as a 256-bit symmetric key for the AES algorithm.

 void AES_PHM_Encryption::Put(
     byte const* pbData, unsigned int nDataLen)
 {
     AES_PHM_AppendToBuffer(
         m_sbbPlaintext, m_nPlaintextBufSize, m_nPlaintextLen,
         pbData, nDataLen);
 }
The Put() method uses the AES_PHM_AppendToBuffer() utility function to store the plaintext to encrypt into a temporary buffer.

 void AES_PHM_Encryption::MessageEnd(int nPropagation)
 {
     SecByteBlock sbbPadding(18);

     // We want the plaintext length to be a multiple of 15 bytes
     unsigned int nRandomPaddingLen = 15 - (m_nPlaintextLen%15);
     byte byFirst = m_rng.GenerateByte();
     sbbPadding[0] = ((byFirst ^ (byFirst << 4)) & 0xf0) | (nRandomPaddingLen & 0x0f);
     nRandomPaddingLen += 3;
     m_rng.GenerateBlock(sbbPadding+1, nRandomPaddingLen-1);
Actual encryption starts when the whole message has been input into the AES_PHM_Encryption object and the MessageEnd() method is called.

The encryption process is started by randomly generating an appropriate amount of padding based on the length of the plaintext to be encrypted. There are always 3 bytes of padding, followed by 0..15 additional bytes of padding to make the length of the padded plaintext minus 3 a multiple of 15. The figure 15 was selected because, that way, the number of padding bytes can be stored into the lower 4 bits of the first byte of the padding.

Padding is used so that an attacker cannot guess the size of the plaintext just by looking at the size of the ciphertext.

     // Generate IV seed
     SecByteBlock sbbIvSeed(IV_SEED_SIZE);
     m_rng.GenerateBlock(sbbIvSeed, IV_SEED_SIZE);

     // Generate IV from seed
     SecByteBlock sbbIv(IV_SIZE);
     AES_PHM_GenerateIvFromSeed(sbbIvSeed, sbbIv);
The seed for the initialization vector is generated, and the IV is generated from that seed using the AES_PHM_GenerateIvFromSeed utility function. The IV will be used to encrypt the message, and the IV seed will be stored with the ciphertext so that the recipient of the message will be able to recover the IV.

     // Initialize AES
     AESEncryption aes(m_sbbKey, SHA256::DIGESTSIZE);
     CFBEncryption encryptor(aes, sbbIv);

     // Encrypt
     encryptor.ProcessString(sbbPadding, nRandomPaddingLen);
     encryptor.ProcessString(m_sbbPlaintext, m_nPlaintextLen);
The AES encryption object and the CFB mode object are initialized with the generated 256-bit key and the IV. Then, the padding and the plaintext are encrypted in-place with the CFB mode object. After the above lines execute, sbbPadding and m_sbbPlaintext contain the encrypted padding and plaintext.

     // Calculate MAC over encrypted plaintext and IV seed
     SecByteBlock sbbMac(HMAC<SHA>::DIGESTSIZE);
     HMAC<SHA> mac(m_sbbKey, SHA256::DIGESTSIZE);
     mac.Update(sbbPadding, nRandomPaddingLen);		// encrypted
     mac.Update(m_sbbPlaintext, m_nPlaintextLen);	// encrypted
     mac.Update(sbbIvSeed, IV_SEED_SIZE);			// plain
     mac.Final(sbbMac);
A MAC (message authentication code) is calculated over the encrypted padding, plaintext and IV seed. The MAC is calculated in the same sequence the data will appear when it is output as ciphertext. The resulting MAC will be appended to the ciphertext.

Use of the MAC ensures to the recipient of the message that the ciphertext they are decrypting has not been tampered with. In order to verify the MAC, the recipient must have the key with which the MAC was calculated; the key is the same as the one that was used for encryption with AES.

A possible improvement would be to use a different key for MACing than was used for AES encryption. This way, if a vulnerability in HMAC with SHA-1 is found that enables an attacker to recover the MAC-ing key, the attacker will still not have recovered the decryption key.

     if (AttachedTransformation())
     {
         AttachedTransformation()->Put(sbbPadding, nRandomPaddingLen);
         AttachedTransformation()->Put(m_sbbPlaintext, m_nPlaintextLen);
         AttachedTransformation()->Put(sbbIvSeed, IV_SEED_SIZE);
         AttachedTransformation()->Put(sbbMac, HMAC<SHA>::DIGESTSIZE);
     }
AES_PHM_Encryption wisely checks whether there is an attached transformation where data can be written to. However, this verification is superfluous; with Filter classes (and AES_PHM_Encryption is a Filter class), there is always an attached transformation that can be written to. If the user did not supply one, a default MessageQueue object will be instantiated by the parent Filter class.

AES_PHM_Encryption then outputs first the encrypted padding, then the encrypted plaintext, then the IV seed that the recipient of the message will use to generate the IV, and then the HMAC-with-SHA-1 message authentication code that can be used by the recipient to verify that the ciphertext has not been tampered with.

     m_sbbPlaintext.Resize(0);
     m_nPlaintextLen = 0;
     m_nPlaintextBufSize = 0;

     Filter::MessageEnd(nPropagation);
 }
The object is now prepared to accept and encrypt a new message. The plaintext buffer is reset, and the parent object is told to forward the MessageEnd() signal to the attached transformation (if nPropagation is non-zero).

The AES_PHM_Decryption class

 AES_PHM_Decryption::AES_PHM_Decryption(
     byte const* pbKey, unsigned int nKeyLen,
     BufferedTransformation* pOutQueue)
         :   Filter(pOutQueue),
             m_nCiphertextBufSize(0), m_nCiphertextLen(0)
 {
     m_sbbKey.Resize(SHA256::DIGESTSIZE);
     SHA256().CalculateDigest(m_sbbKey, pbKey, nKeyLen);
 }

 void AES_PHM_Decryption::Put(
     byte const* pbData, unsigned int nDataLen)
 {
     AES_PHM_AppendToBuffer(
         m_sbbCiphertext, m_nCiphertextBufSize, m_nCiphertextLen,
         pbData, nDataLen);
 }
Similarly to AES_PHM_Encryption, the AES_PHM_Decryption class initializes m_sbbKey to contain the SHA-256 digest of the supplied key or passphrase, and stores all input data into a temporary buffer until MessageEnd() is called.

 void AES_PHM_Decryption::MessageEnd(int nPropagation)
 {
     if (m_nCiphertextLen < MINIMUM_CIPHERTEXT_LENGTH)
         throw InvalidCiphertextOrKey();

     // Make the code less cluttered by using the following values
     unsigned int nMacPosition = m_nCiphertextLen - HMAC<SHA>::DIGESTSIZE;
     unsigned int nIvSeedPosition = nMacPosition - IV_SEED_SIZE;
On decryption, AES_PHM_Decryption first verifies that the length of the plaintext is sufficient to contain valid ciphertext. If not, the InvalidCiphertextOrKey exception is thrown.

Then, pointers to important locations in the ciphertext are calculated to make the code less cluttered with in-line pointer calculations.

     // Verify MAC
     if (!HMAC<SHA>(m_sbbKey, SHA256::DIGESTSIZE).VerifyDigest(
             m_sbbCiphertext + nMacPosition,
             m_sbbCiphertext, nMacPosition))
         throw InvalidCiphertextOrKey();
Integrity of the ciphertext is validated by verifying the MAC. The MAC is verified using VerifyDigest() and not by calculating a new MAC and comparing it to the supplied value; this is because, in general, MACs should not be expected to produce the same output when calculated over the same input multiple times.

     // Generate IV from seed
     SecByteBlock sbbIv(IV_SIZE);
     AES_PHM_GenerateIvFromSeed(m_sbbCiphertext + nIvSeedPosition, sbbIv);

     // Initialize AES
     AESEncryption aes(m_sbbKey, SHA256::DIGESTSIZE);
     CFBDecryption decryptor(aes, sbbIv);

     // Decrypt
     decryptor.ProcessString(m_sbbCiphertext, nIvSeedPosition);
The IV is generated from the seed that is found in the ciphertext; the AES object and the CFB mode object are initialized using the 256-bit key and the IV; and the encrypted part of the ciphertext (padding + plaintext) are decrypted with a call to CFBDecryption::ProcessString.

Note that AESEncryption rather than AESDecryption is used: this is characteristic for CFB mode. For encryption as well as decryption, CFB mode always uses the underlying algorithm in encryption mode to generate a keystream to actually encrypt or decrypt data with.

     // See how much padding we have
     unsigned int nRandomPaddingLen = (m_sbbCiphertext[0] & 0x0f) + 3;

     // Output plaintext
     if (AttachedTransformation())
         AttachedTransformation()->Put(
             m_sbbCiphertext + nRandomPaddingLen,
             nIvSeedPosition - nRandomPaddingLen);
The amount of random padding is determined by investigating the first byte of the decryption result - which ought to be the first byte of the padding. The part of the decryption result which is determined not to be the padding is output into the attached transformation.

     m_sbbCiphertext.Resize(0);
     m_nCiphertextLen = 0;
     m_nCiphertextBufSize = 0;

     Filter::MessageEnd(nPropagation);
 }

 NAMESPACE_END
As with AES_PHM_Encryption, the object is now reset and the attached transformation is notified to prepare for a new message.


Stream Cipher Header Files

Header file | Description
arc4.h | Defines class ARC4 - implementation of a stream cipher compatible with RSA Data Security's RC4 algorithm. The name ARC4 is used because the name RC4 is trademarked. The class inherits from StreamCipher and RandomNumberGenerator.
seal.h | Defines class SEAL; inherits from RandomAccessStreamCipher and RandomNumberGenerator.


Public Key Cryptosystem Header Files

Header file | Description
rsa.h Defines:
  • classes RSAFunction and InvertibleRSAFunction, representing the RSA public key and the RSA private key, respectively;
  • typedefs for RSA encryption with OAEP padding scheme (RSAES_OAEP_SHA_Encryptor, RSAES_OAEP_SHA_Decryptor);
  • typedefs for RSA encryption with PKCS v1.5 padding scheme; (RSAES_PKCS1v15_Encryptor, RSAES_PKCS1v15_Decryptor);
  • typedefs for RSA signature as per PKCS 1.5 (RSASSA_PKCS1v15_xxx_Signer and RSASSA_PKCS1v15_xxx_Verifier, where xxx is either SHA, MD5 or MD2).
dh.h | Defines class DH, providing an interface for Diffie-Hellman key agreement.
dsa.h Defines:
  • classes GDSASigner and GDSAVerifier, representing a generalized-DSA private and public key, respectively;
  • classes DSAPrivateKey and DSAPublicKey, representing a classic-DSA private and public key, respectively.
eccrypto.h Defines:
  • class ECParameters, representing parameters for an elliptic curve with built-in support for recommended parameters;
  • class ECDHC, providing an interface for Elliptic Curve Diffie-Hellman with Cofactor Multiplication key agreement;
  • class ECMQVC, providing an interface for Elliptic Curve Menezes-Qu-Vanstone with Cofactor Multiplication key agreement;
  • classes ECPublicKey and ECPrivateKey, representing a raw elliptic curve public and private key, respectively;
  • classes ECSigner and ECVerifier, representing an EC private/public key, respectively, with message signing/verification capabilities;
  • classes ECEncryptor and ECDecryptor, representing an EC public/private key, respectively, with support for the ECIES encryption scheme.
elgamal.h | Defines classes ElGamalEncryptor and ElGamalDecryptor, representing an El-Gamal public and private key, respectively.
mqv.h | Defines class MQV, providing an interface for Menezes-Qu-Vanstone key agreement.
rw.h | Defines classes RWSigner and RWVerifier, representing a Rabin-Williams private and public key, respectively.
xtrcrypt.h | Defines class XTR_DH, providing an interface for XTR Diffie-Hellman key agreement.


rsa.h

The classes actually defined in rsa.h, RSAFunction and InvertibleRSAFunction, are basic classes that envelop a plain RSA public key and private key, respectively. They support direct RSA computation through methods ApplyFunction() (the public-key operation) and CalculateInverse() (the private-key operation). You should only use these methods directly if you know what you are doing, and are applying appropriate padding.

More commonly, you will want to use typedefs RSAES_OAEP_SHA_Decryptor, RSAES_OAEP_SHA_Encryptor, RSASSA_PKCS1v15_SHA_Signer, RSASSA_PKCS1v15_SHA_Verifier, and other typedefs of the same sort also defined in rsa.h. The classes identified by these typedefs are derived from an array of base classes declared in pubkey.h. The various RSA Encryptor/Decryptor/Signer/Verifier typedef'ed classes will be easiest to use if you remember that they all ultimately inherit either from PK_FixedLengthEncryptor, PK_FixedLengthDecryptor, PK_Signer or PK_Verifier, all of them declared in cryptlib.h.

Example - generate an RSA keypair and save it:

 // InvertibleRSAFunction is used directly only because the private key
 // won't actually be used to perform any cryptographic operation;
 // otherwise, an appropriate typedef'ed type from rsa.h would have been used.
 AutoSeededRandomPool rng;
 InvertibleRSAFunction privkey(rng, 1024);

 // With the current version of Crypto++, MessageEnd() needs to be called
 // explicitly because Base64Encoder doesn't flush its buffer on destruction.
 Base64Encoder privkeysink(new FileSink("c:\\privkey.txt"));
 privkey.DEREncode(privkeysink);
 privkeysink.MessageEnd();

 // Suppose we want to store the public key separately,
 // possibly because we will be sending the public key to a third party.
 RSAFunction pubkey(privkey);

 Base64Encoder pubkeysink(new FileSink("c:\\pubkey.txt"));
 pubkey.DEREncode(pubkeysink);
 pubkeysink.MessageEnd();

Example - load a private key and sign a file:

 string strContents;
 FileSource("c:\\tobesigned.dat", true,
     new StringSink(strContents));

 RSASSA_PKCS1v15_SHA_Signer privkey(
     FileSource("c:\\privkey.txt", true,
         new Base64Decoder)));

 SecByteBlock sbbSignature(privkey.SignatureLength());

 AutoSeededRandomPool rng;
 privkey.SignMessage(
     rng,
     (byte const*) strContents.data(),
     strContents.size(),
     sbbSignature.Begin());

 FileSink sink("c:\\signed.dat");
 sink.Put((byte const*) strContents.data(), strContents.size());
 sink.Put(sbbSignature.Begin(), sbbSignature.Size());

Example - load a public key and encrypt a short string using RSA:

 string strShortString =
     "Must be shorter than the size of the RSA key minus OAEP decoration.";

 RSAES_OAEP_SHA_Encryptor pubkey(
     FileSource("c:\\pubkey.txt", true,
         new Base64Decoder)));

 // Cannot use std::string for buffer;
 // its internal storage might not be contiguous
 SecByteBlock sbbCipherText(pubkey.CipherTextLength(strShortString.size()));

 AutoSeededRandomPool rng;
 pubkey.Encrypt(
     rng,
     (byte const*) strShortString.data(),
     strShortString.size(),
     sbbCipherText.Begin());

 FileSink("c:\\encrypted.dat").Put(sbbCipherText.Begin(), sbbCipherText.Size());


eccrypto.h

eccrypto.h provides template classes for elliptic-curve-based cryptographic operations. The classes are templates primarily because two distinct types of elliptic curves are in use; thus, an elliptic curve over GF(2^n) is represented by class EC2N (ec2n.h), whereas an elliptic curve over GF(p) is represented by class ECP (ecp.h).

Elliptic curve parameters are represented by template class ECParameters. Parameters can be initialized in various ways; however, one of the more practical ones is to use the method LoadRecommendedParameters(), supplying one of the recommended-parameters-OIDs as defined in oids.h.

Example - generate an EC2N keypair and save it:

 // ECPrivateKey is used directly only because the private key
 // won't actually be used to perform any cryptographic operation.
 AutoSeededRandomPool rng;
 ECPrivateKey privkey(rng, ASN1::sect233k1);

 Base64Encoder privkeysink(new FileSink("c:\\privkey.txt"));
 privkey.DEREncode(privkeysink);
 privkeysink.MessageEnd();   // Need to flush Base64Encoder's buffer

 // Suppose we want to store the public key separately,
 // possibly because we will be sending the public key to a third party.
 ECPublicKey pubkey(privkey);

 Base64Encoder pubkeysink(new FileSink("c:\\pubkey.txt"));
 pubkey.DEREncode(pubkeysink);
 pubkeysink.MessageEnd();    // Need to flush Base64Encoder's buffer

Example - load a public key and encrypt a file:

 string sContents;
 FileSource("c:\\tobesigned.dat", true,
     new StringSink(sContents));

 ECEncryptor pubkey(
     FileSource("c:\\pubkey.txt", true,
         new Base64Decoder)));

 // Cannot use std::string for buffer;
 // its internal storage might not be contiguous
 SecByteBlock sbbCipherText(pubkey.CipherTextLength(sContents.size()));

 // ECIES encryption is nice because it handles the entire encryption
 // process internally, regardless of the length of input data.
 // We don't have to generate a symmetric session key and encrypt
 // with it separately.
 AutoSeededRandomPool rng;
 pubkey.Encrypt(
     rng,
     (byte const*) sContents.data(),
     sContents.size(),
     sbbCipherText.Begin());

 FileSink("c:\\encrypted.dat").Put(sbbCipherText.Begin(), sbbCipherText.Size());