EAX Mode

From Crypto++ Wiki
Jump to: navigation, search

EAX mode of operation is an AEAD mode of operation. It provides both confidentiality and authenticity, and authentication assurances on additional data. EAX is the work of Bellare, Rogaway, and Wagner. The mode was presented in A Conventional Authenticated-Encryption Mode. EAX was a candidate during NIST's Authenticated Encryption Mode selection process.

Additional modes of operation, such as CCM and GCM, offer encryption and authentication. Both CCM and GCM are NIST approved standards, while EAX was not selected. If NIST approval is not required, EAX is superior to CCM in many respects. However, GCM trumps EAX since it is NIST approved and supports parallelization. For a comparison of 4th generation authenticated encryption modes, visit AEAD Comparison.

EAX mode is an n-bit mode of operation. This allow the mode to operate on AES with 128 bits or SHACAL-2 with its 256 bit block size. EAX is online, meaning the data does not need to be known in advance - it can be streamed into the object (there are some practical implementation constraints). Unlike both CCM and GCM, EAX handles messages of arbitrary length.

The tag generated by the mode is from 0 bytes to that of the underlying block cipher size. If the block cipher is AES, the maximum tag size is 16 bytes; if SHACAL-2, the tag length can be no greater than 32 bytes.

Background

Given two message inputs, ADATA (additional authenticated data) and PDATA (plain text data), EAX mode will provide authentication assurances over the ADATA and provide both confidentiality and authentication over the PDATA. Note that either ADATA or PDATA may be NULL or unused, but at least one must be present. An simple example would be communications over the internet: the message sent would be the pair { iv, ciphertext }. The iv would be authenticated (and sent in the clear), while the cipher text would have both encryption and authentication applied.

The output of the mode is a single cipher text message which has two components: the concatenated pair { Encrypted, Tag }. Encrypted is the result of encrypting the PDATA, while Tag is a truncation of the authentication code (MAC) over both the ADATA and PDATA. Since the tag size is a compile time constant, it is trivial to split the cipher text data into constituent components. Note that the original ADATA is not returned in this operation - only the cipher text and tag.

Crypto++ Implementation

Crypto++ 5.6 intoduced a new interface for working with authenticated encryption objects: AuthenticatedSymmetricCipher. Crypto++ exposes EAX through the use of a EAX mode object and a pair of filters: AuthenticatedEncryptionFilter and AuthenticatedEncryptionFilter. Each filter combines a block cipher operated in EAX mode with a HashFilter to generate the MAC and and a HashVerificationFilter to verify the digest. In addition to EAX mode, CCM Mode and GCM Mode also use the interface.

Both the ADATA and PDATA should be fully available to the AuthenticatedSymmetricCipher objects ensure compatibility with other authenticate and encrypt modes such as CCM. With this in mind, changing modes to evaluate performance will be a trivial task. As with and CCM and GCM, operations on the channel data must be performed in strict order.

The parameters which must be supplied and used by both parties are:

  • key and key size
  • iv and iv size
  • tag size

Finally, do not use a StreamTransformationFilter on a EAX object to recover only the primary data channel (the cipher text). The StreamTransformationFilter will throw an exception.

Sample Programs

Two sample programs are provided for EAX mode. In the samples below, a few points are noteworthy:

  • The IV/Nonce size can be any size
  • A call to SpecifyDataLengths is not required
  • Pushing data into the objects in the wrong order will result in an exception
  • Data flow into the encryptor is slightly different than data flow into the decryptor/verifier
  • Exceptions for a verification failure can be supressed by not including THROW_EXCEPTION flag during construction.
  • Tag size is altered by specifying a supported value for the truncatedDigestSize parameter of either AuthenticatedEncryptionFilter or AuthenticatedDecryptionFilter. The value is specified in bytes, not bits

EAX test vectors can be found under Dr. Rogaways publications at UC davis (The EAX Mode of Operation). Crypto++ also provides the test vectors in TestVectors directory.

AE

The first sample, EAX-AE-Test.zip, performs authenticated encryption. It does not perform authentication over additional authenticated data (ADATA). Since only encryption is performed, only access to the default channel is needed. So the code below is similar to what one might expect for other modes such as CBC. The exception is SetKeyWithIV requires an iv size.

////////////////////////////////////////////////
// Generate keys
AutoSeededRandomPool rng;

SecByteBlock key( AES::DEFAULT_KEYLENGTH );
rng.GenerateBlock( key, key.size() );

byte iv[ AES::BLOCKSIZE * 16 ];
rng.GenerateBlock( iv, sizeof(iv) );

////////////////////////////////////////////////
// Encrpytion
EAX< AES >::Encryption enc;
enc.SetKeyWithIV( key, key.size(), iv, sizeof(iv) );

StringSource( plaintext, true,
    new AuthenticatedEncryptionFilter( enc,
        new StringSink( ciphertext )
    ) // AuthenticatedEncryptionFilter
); // StringSource

////////////////////////////////////////////////
// Tamper
ciphertext[0] ^= 0x01;

////////////////////////////////////////////////
// Decrpytion
EAX< AES >::Decryption dec;
dec.SetKeyWithIV( key, key.size(), iv, sizeof(iv) );

ArraySource( ciphertext.data(), ciphertext.size(), true,
    new AuthenticatedDecryptionFilter( dec,
        new StringSink( recovered )
    ) // AuthenticatedDecryptionFilter
); //ArraySource

cout << "Recovered original message" << endl;

AEAD

The second sample, EAX-AEAD-Test.zip, performs authenticated encryption with additional authenticated data. It explicitly uses Put on the two channels rather than using pipelining. The default data channel provides confidentiality and authentication; the second channel ("AAD"), provides only authentication.

////////////////////////////////////////////////
// Generate keys
AutoSeededRandomPool rng;

SecByteBlock key( AES::DEFAULT_KEYLENGTH );
rng.GenerateBlock( key, key.size() );

byte iv[ AES::BLOCKSIZE * 16 ];
rng.GenerateBlock( iv, sizeof(iv) );

const int TAG_SIZE = 12;

////////////////////////////////////////////////
// Encrpytion
EAX< AES >::Encryption e;
e.SetKeyWithIV( key, key.size(), iv, sizeof(iv) );

AuthenticatedEncryptionFilter ef( e,
    NULL, false, TAG_SIZE
    /* MAC_AT_END */
 );  // AuthenticatedEncryptionFilter


// AuthenticatedEncryptionFilter::ChannelPut
//  defines two channels: DEFAULT_CHANNEL and AAD_CHANNEL
//   DEFAULT_CHANNEL is encrypted and authenticated
//   channel AAD_CHANNEL is authenticated
ef.ChannelPut( AAD_CHANNEL, adata.data(), adata.size() );
ef.ChannelMessageEnd(AAD_CHANNEL);

// Authenticated data *must* be pushed before
//  Confidential/Authenticated data. Otherwise
//  we must catch the BadState exception
ef.ChannelPut( DEFAULT_CHANNEL, pdata.data(), pdata.size() );
ef.ChannelMessageEnd(DEFAULT_CHANNEL);              

// Remove data from channel
n = (size_t)ef.MaxRetrievable();
ciphertext.resize( n );
if( n > 0 ) { ef.Get( ciphertext.data(), ciphertext.size() ); }

////////////////////////////////////////////////
// Authenticated Data is sent via clear text
radata = adata;

////////////////////////////////////////////////
// Tamper
// ciphertext[0] ^= 0x01;
// radata[0] ^= 0x01;

////////////////////////////////////////////////
// Break the cipher text out into it's
//  components: Encrypted and MAC (MAC_AT_END)
string enc = ciphertext.substr( 0, ciphertext.length()-TAG_SIZE );
string tag = ciphertext.substr( ciphertext.length()-TAG_SIZE );

// Sanity checks
assert( ciphertext.size() == enc.size() + tag.size() );
assert( enc.size() == pdata.size() );
assert( TAG_SIZE == tag.size() );

////////////////////////////////////////////////
// Decrpytion
EAX< AES >::Decryption d;
d.SetKeyWithIV( key, key.size(), iv, sizeof(iv) );

AuthenticatedDecryptionFilter df( d, NULL,
    AuthenticatedDecryptionFilter::DEFAULT_FLAGS, TAG_SIZE
    /* MAC_AT_END */
); // AuthenticatedDecryptionFilter

// ADATA must be inserted first
df.ChannelPut( AAD_CHANNEL, radata.data(), radata.size() );

// PDATA (encrypted)
df.ChannelPut( DEFAULT_CHANNEL, enc.data(), enc.size() );

// TAG last on default channel
df.ChannelPut( DEFAULT_CHANNEL, tag.data(), tag.size() );

// Finished
df.MessageEnd();

// If the object does not throw, here's the only
//  opportunity to check the data's integrity
bool b = df.GetLastResult();
assert( true == b );

// Plain text recovered from enc.data()
n = (size_t)df.MaxRetrievable();
rpdata.resize( n );

if( n > 0 ) { df.Get( rpdata.data(), n ); }

// All is well - work with data

assert( pdata == rpdata );
cout << "Recovered original message" << endl;

In the preceeding code, the MAC_AT_END was implied during contruction to indicate that the tag was the last item being inserted into the AuthenticatedDecryptionFilter. If the tag is to be inserted at the end of the process, the following code would be used. Note that ADATA must still be pushed before PDATA.

AuthenticatedDecryptionFilter df( d, NULL, MAC_AT_BEGIN );

df.ChannelPut( DEFAULT_CHANNEL, tag.data(), tag.size() );
df.ChannelPut( AAD_CHANNEL, adata.data(), adata.size() );
df.ChannelPut( DEFAULT_CHANNEL, enc.data(), enc.size() );

// Signal End on both channels
df.MessageEnd();

Downloads

EAX-AE-Test.zip - EAX Test using only PDATA

EAX-AEAD-Test.zip - EAX Test using both ADATA and PDATA

Blowfish-EAX-Filter.zip - Demonstrates encryption and decryption using Blowfish in EAX mode with filters

IDEA-EAX-Filter.zip - Demonstrates encryption and decryption using IDEA in EAX mode with filters

Camellia-EAX-Filter.zip - Demonstrates encryption and decryption using Camellia in EAX mode with filters