Namevaluepairs

From Crypto++ Wiki
Jump to: navigation, search
NameValuePairs
Documentation
#include <cryptopp/algparam.h>

NameValuePairs are used to safely pass a variable number of arbitrarily typed arguments to functions and to read values from keys and crypto parameters. Sometimes, NameValuePairs is the only way to setup an object to a desired state.

To obtain an object that implements NameValuePairs for the purpose of parameter passing, use the MakeParameters() function. According to the comments in algparam.h:

A NameValuePairs object containing an arbitrary number of name value pairs may be constructed by
repeatedly using operator() on the object returned by MakeParameters, for example:
AlgorithmParameters parameters = MakeParameters(name1, value1)(name2, value2)(name3, value3);

To get a value from NameValuePairs, you need to know the name and the type of the value. Call GetValueNames() on a NameValuePairs object to obtain a list of value names that it supports. Then look at the Name namespace documentation to see what the type of each value is, or alternatively, call GetIntValue() with the value name, and if the type is not int, a ValueTypeMismatch exception will be thrown and you can get the actual type from the exception object.

If you encounter a function that requires a NameValuePairs object but you don't have any parameters, then use g_nullNameValuePairs.

For a list of names recognized by the library, see the Name Namespace Reference; or see argnames.h.

Initialization

Class objects that take NameValuePairs can be initialized after construction with Initialize or IsolatedInitialize. They are virtual functions provided by BufferedTransformation. The one line comment with them is Initialize or reinitialize this object. The difference between Initialize or IsolatedInitialize appears to lie in signal propagation to attached transformations. Initialize appears to effect the object and its attached transformations; while IsolatedInitialize effects only the object itself. (See more on behavior below in Initialize vs IsolatedInitialize).

Filters should use IsolatedInitialize. Classes like HexEncoder, HexDecoder, Base64Encoder and Base64Decoder provides an override for IsolatedInitialize (but not Initialize). For example, the HexEncoder constructor is shown below:

HexEncoder(BufferedTransformation *attachment = NULL, bool uppercase = true, int outputGroupSize = 0,
           const std::string &separator = ":", const std::string &terminator = "")
    : SimpleProxyFilter(new BaseN_Encoder(new Grouper), attachment)
{
    IsolatedInitialize(MakeParameters(Name::Uppercase(), uppercase)
                                     (Name::GroupSize(), outputGroupSize)
                                     (Name::Separator(), ConstByteArrayParameter(separator))
                                     (Name::Terminator(), ConstByteArrayParameter(terminator)));
}

If a library user calls IsolatedInitialize with different parameters, then the IsolatedInitialize implementation uses a CombinedNameValuePairs to blend the caller's parameter's with the library's default parameters. Again, from the the HexEncoder:

void HexEncoder::IsolatedInitialize(const NameValuePairs &parameters)
{
	bool uppercase = parameters.GetValueWithDefault(Name::Uppercase(), true);
	m_filter->Initialize(CombinedNameValuePairs(
		parameters,
		MakeParameters(Name::EncodingLookupArray(), uppercase ? &s_vecUpper[0] : &s_vecLower[0], false)
                              (Name::Log2Base(), 4, true)));
}

The parameters are the caller's parameters; while the value from MakeParameters are the library's default parameters. When the object needs a setting, CombinedNameValuePairs consults the caller's parameters first and uses them if present. If not present, then the library uses the built-in defaults. In the case of HexEncoder, the default provided by the library is just an uppercase or lowercase alphabet.

Initialize vs IsolatedInitialize

The behavior of Initialize or IsolatedInitialize can be tested with a simple program that encodes a block of bytes.

string encoded;
Base64Encoder encoder(NULL, true, 100);
encoder.Attach(new StringSink( encoded ));

AlgorithmParameters params = MakeParameters(Pad(), false);
encoder.Initialize(params);

ArraySource as(raw, sizeof(raw), true, new Redirector(encoder));
cout << encoded << endl;

Note the program uses Initialize. Running the program results in:

$ ./cryptopp-test.exe
StringSink: OutputStringPointer not specified

Its hard to say what the exact state of the object is other than we broke something related to the attached StringSink's referenced string.

If the program is modified to use IsolatedInitialize, then running the program results in:

$ ./cryptopp-test.exe
/+7dzLuqmYh3ZlVEMyIRAP/u3cy7qpmId2ZVRDMiEQD/7t3Mu6qZiHdmVUQzIhEA/+7dzLuq
mYh3ZlVEMyIRAA

In the program above, the filter provided the default alphabet, the line length was reset to 72, the padding was modified, and the attached transformation was not modified.

Sample Programs

The following are two sample programs that exercise NameValuePairs. The first retrieves the names available from a parameter object and then prints a couple of values; the second disables padding on an encoder.

Get Names and Values

GetNameValues allows you to retrieve the names used for AlgorithmParameters. Note: not all objects expose this method.

using CryptoPP::Name::Pad;
using CryptoPP::Name::InputStreamPointer;
using CryptoPP::Name::OutputStreamPointer;

AlgorithmParameters p = MakeParameters(Pad(), false)
                                      (InputStreamPointer(), NULL)
                                      (OutputStreamPointer(), NULL);

const string& names = p.GetValueNames();
cout << names << endl;

if(names.find("Pad", 0) != string::npos)
{
    bool value;
    p.GetValue("Pad", value);
            
    cout << "Pad: " << (value ? "true" : "false") << endl;
}

if(names.find("Foo", 0) == string::npos)
{
    cout << "Foo: not found" << endl;
}

The example above will produce the following output:

Pad;InputStreamPointer;OutputStreamPointer;
Pad: false
Foo: not found

Padding and Encoders

The following program disables padding and line breaks on a Base64Encoder. You have to use NameValuePairs in this case because there is no way to disable padding through a constructor.

using CryptoPP::Name::Pad;
using CryptoPP::Name::InsertLineBreaks;

byte raw[] = {
    0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88,
    0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00,
    0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88,
    0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00,
    0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88,
    0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00,
    0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88,
    0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00 };

string encoded, hexed;
Base64Encoder encoder;

AlgorithmParameters params = MakeParameters(Pad(), false)(InsertLineBreaks(), false);
encoder.IsolatedInitialize(params);
encoder.Attach(new StringSink( encoded ));

ArraySource as(raw, sizeof(raw), true, new Redirector(encoder));
cout << encoded << endl;

StringSource ss(encoded, true, new Base64Decoder(new HexEncoder(new StringSink(hexed))));
cout << hexed << endl;

In the code above, the Base64Encoder constructor sets up a standard object. Then the parameters are tuned for this particular use. In this case, the use is (1) no padding and (2) no line breaks.

The call to IsolatedInitialize initializes or reinitializes the object. Under the covers, Base64Encoder's override of IsolatedInitialize uses CombinedNameValuePairs to blend the library's default parameters with the callers's parameters. That means parameters like the encoding alphabet will always be present so a caller does not have to specify it again (see base64.cpp).

A run of the program produces the following. The padding is not added to the tail of the encoded data (which would be ...VEMyIRAA==).

$ ./cryptopp-test.exe
/+7dzLuqmYh3ZlVEMyIRAP/u3cy7qpmId2ZVRDMiEQD/7t3Mu6qZiHdmVUQzIhEA/+7dzLuqmYh3ZlVEMyIRAA
FFEEDDCCBBAA99887766554433221100FFEEDDCCBBAA99887766554433221100FFEEDDCCBBAA99887766554433221100FFEEDDCCBBAA99887766554433221100

CFB Mode and Feedback

Crypto++ uses the full blocksize for the feedback size by default. Other libraries sometimes use a different size and it leads to interoperability issues. For example, Mcrypt and .Net use a smaller feedback size. Using less than the full block size for the feedback size can reduce the security in some modes of operation. If given a choice, you should probably prefer libraries like Mcrypt and .Net use the full block size rather than a smaller feedback size.

Here's how to reduce the feedback size in Crypto++ (taken from CFB Mode):

SecByteBlock key(AES::DEFAULT_KEYLENGTH), iv(AES::BLOCKSIZE);
...

AlgorithmParameters params = MakeParameters
                                 (Name::FeedbackSize(), 1 /*8-bits*/)
                                 (Name::IV(), ConstByteArrayParameter(iv, iv.size()));

string plain = "CFB Mode Test", cipher;

try
{
   CFB_Mode< AES >::Encryption enc;
   enc.SetKey( key, key.size(), params );

   StringSource ss( plain, true, 
      new StreamTransformationFilter( enc,
         new StringSink( cipher )
      ) // StreamTransformationFilter      
   ); // StringSource
}
catch( CryptoPP::Exception& ex )
{
   cerr << ex.what() << endl;
   exit(1);
}

Compile Error

Prior to Commit fd278c2e8b5c3688... If you receive a compiler error when compiling algparam.h:

1>d:...\cryptopp\algparam.h(322): error C2061: syntax error : identifier 'buffer'
1>          d:\work\app\tools\cryptopp\algparam.h(321) : while compiling class template member function
1>              'void CryptoPP::AlgorithmParametersTemplate<T>::MoveInto(void *) const'
1>          with
1>          [
1>              T=bool
1>          ]
1>          d:\work\app\tools\cryptopp\algparam.h(329) : see reference to class template instantiation 
1>              'CryptoPP::AlgorithmParametersTemplate<T>' being compiled
1>          with
1>          [
1>              T=bool
1>          ]

Then temporarily disable new when including Crypto++ headers:

#pragma push_macro("new")
#undef new
/* #includes for Crypto++ go here */
#pragma pop_macro("new")

Thanks to Angelo Geels at Stack Overflow.

To avoid the endless stream of warnings from the unused variable p:

void MoveInto(void *buffer) const
{
    AlgorithmParametersTemplate<T>* p = new(buffer)
    AlgorithmParametersTemplate<T>(*this);
}

Then cast the variable p to void:

void MoveInto(void *buffer) const
{
    AlgorithmParametersTemplate<T>* p = new(buffer)
    AlgorithmParametersTemplate<T>(*this);
    (void)p;
}