TestAuthenticatedSymmetricCipher

From Crypto++ Wiki
Jump to: navigation, search

TestAuthenticatedSymmetricCipher is a function in datatest.cpp. It is used to test authenticated encryption ciphers, such as AES operated in CCM mode or GCM mode.

AuthenticatedSymmetricCipher offers the function SpecifyDataLengths. Some modes, such as CCM, require the length of the authenticated data (ADATA) and payload data (PDATA) in advance. Other modes, such as GCM, do not. To determine if SpecifyDataLengths must be used, query the object with NeedsPrespecifiedDataLengths.

Crypto++ Example/Demonstration

To create the encryptor, one would perfrom the following steps. The library's validation code uses a base class pointer due to the Object Factory. This allows a sufficiently generic interface for all classes of authenticed-encrption ciphers. It is not strictly necessary to use the base class pointer in an application.

Encryption/Authentication

member_ptr<AuthenticatedSymmetricCipher> asc1;
asc1.reset(ObjectFactoryRegistry<AuthenticatedSymmetricCipher,
    ENCRYPTION>::Registry().CreateObject(name.c_str()));
asc1->SetKey((const byte *)key.data(), key.size(), pairs);

std::string encrypted, decrypted;

AuthenticatedEncryptionFilter ef(*asc1, new StringSink(encrypted));

if (asc1->NeedsPrespecifiedDataLengths())
{
    asc1->SpecifyDataLengths(header.size(), plaintext.size(), footer.size());
}

StringStore sh(header), sp(plaintext), sc(ciphertext), sf(footer), sm(mac);
sh.TransferTo(ef, sh.MaxRetrievable()/2+1, "AAD");
sh.TransferTo(ef, LWORD_MAX, "AAD");
sp.TransferTo(ef, sp.MaxRetrievable()/2+1);
sp.TransferTo(ef);
sf.TransferTo(ef, sf.MaxRetrievable()/2+1, "AAD");
sf.TransferTo(ef, LWORD_MAX, "AAD");
ef.MessageEnd();

In the code above, Crypto++ demonstrates one way to move discrete data into different channels using TransferTo. Note that TransferTo takes a filter, size, and an optional channel. The StringStore object will handle the Put. Crypto++ also demonstrates the order of opertions during the encryption/authentication process. The data is consumed in the following order (MAC_AT_BEGIN is not a flag to an encryption filter - only decryption filters).

  • header (additional authenticated data or ADATA)
  • plain text (PDATA)
  • footer (not used in CCM or GCM)

By using the following techniques (which clearly removes readability), Crypto++ admits it is OK to perform multiple Puts on a data/channel pair. The first TransferTo moves half the data, the second moves the remaining data.

sh.TransferTo(ef, sh.MaxRetrievable()/2+1, "AAD");
sh.TransferTo(ef, LWORD_MAX, "AAD");

Decryption/Verification

member_ptr<AuthenticatedSymmetricCipher> asc2;
asc2.reset(ObjectFactoryRegistry
    <AuthenticatedSymmetricCipher, DECRYPTION>::Registry()
        .CreateObject(name.c_str()));
asc2->SetKey((const byte *)key.data(), key.size(), pairs);

std::string encrypted, decrypted;

AuthenticatedDecryptionFilter df(*asc2, new StringSink(decrypted),
  AuthenticatedDecryptionFilter::MAC_AT_BEGIN);

if (asc2->NeedsPrespecifiedDataLengths())
{
    asc2->SpecifyDataLengths(header.size(), plaintext.size(), footer.size());
}

StringStore sh(header), sp(plaintext), sc(ciphertext), sf(footer), sm(mac);

sm.TransferTo(df);
sh.CopyTo(df, LWORD_MAX, "AAD");
sc.TransferTo(df);
sf.CopyTo(df, LWORD_MAX, "AAD");
df.MessageEnd();

Above, Crypto++ shows yet another way to move data from a source (the StringStore) to a sink (the separate channels of the filter). Code such as sm.TransferTo(df); has been simplified because part of the object's internal queue was drained during encryption/authentication. sm.TransferTo(df); will drain the remaining data. During decryption/verification - and when using MAC_AT_BEGIN - the order of operations is:

  • tag (derived from the MAC)
  • header (additional authenticated data or ADATA)
  • cipher text
  • footer (not used in CCM or GCM)

If MAC_AT_BEGIN were not specified, the code would proceed in the following order:

  • header (additional authenticated data or ADATA)
  • cipher text
  • footer (not used in CCM or GCM)
  • tag (derived from the MAC)

Finally, the verification occurs in the code with the following tests. In practice, application code will only be able to perform the df.GetLastResult() != true test.

if (test == "Encrypt" && encrypted != ciphertext+mac)
{
    ...
    SignalTestFailure();
}
if (decrypted != plaintext)
{
    ...
    SignalTestFailure();
}

if (mac.size() != asc1->DigestSize())
{
    ...
    SignalTestFailure();
}
if (df.GetLastResult() != (test == "Encrypt"))
{
    ...
    SignalTestFailure();
}