CMAC

From Crypto++ Wiki

Jump to: navigation, search

CMAC is a block cipher-based MAC algorithm specified in NIST SP 800-38B. A CMAC is the block cipher equivalent of an HMAC. CMACs can be used when a block cipher is more readily available than a hash function. A CMAC accepts variable length messages (unlike CBC-MAC) and is equivalent to OMAC1.

[edit] Sample Program

The sample program below demonstrates a CMAC with AES using filters (see pipelining). The key is declared on the stack and a SecByteBlock is used to ensure the sensitive material is zeroized. Similar could be used for the message and MAC if desired.

AutoSeededRandomPool prng;

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

string plain = "CMAC Test";
string mac, encoded;

/*********************************\
\*********************************/

// Pretty print key
encoded.clear();
StringSource(key, key.size(), true,
    new HexEncoder(
        new StringSink(encoded)
    ) // HexEncoder
); // StringSource

cout << "key: " << encoded << endl;
cout << "plain text: " << plain << endl;

/*********************************\
\*********************************/

try
{
    CMAC< AES > cmac(key, key.size());

    StringSource(plain, true, 
        new HashFilter(cmac,
            new StringSink(mac)
        ) // HashFilter      
    ); // StringSource
}
catch(const CryptoPP::Exception& e)
{
    cerr << e.what() << endl;
    exit(1);
}

/*********************************\
\*********************************/

// Pretty print
encoded.clear();
StringSource(mac, true,
    new HexEncoder(
        new StringSink(encoded)
    ) // HexEncoder
); // StringSource

cout << "cmac: " << encoded << endl;

A typical output is shown below. Note that each run will produce different results because the key is randomly generated.

$ ./Driver.exe
key: B8B34DA2D4C4D578D8494390E3DFE7A7
plain text: CMAC Test
cmac: 8C72D147FF9B25699B6898379AF44D8F

Though a CMAC uses a block cipher, the CMAC does not use an IV (see section 6.2 of SP 800-38B). Calling IVRequirement on a CMAC object will return INTERNALLY_GENERATED_IV. Attempting to set an IV will result in exception, AlgorithmParametersBase: parameter "IV" not used. The following will produce the exception when attempting to set an IV:

SecByteBlock key(AES::DEFAULT_KEYLENGTH); // Null string
SecByteBlock iv(AES::BLOCKSIZE);          // Null string
...
CMAC< AES > cmac;
cmac.SetKeyWithIV(key, key.size(), iv);	

To verify a CMAC on a message, use a HashVerificationFilter.

try
{
    CMAC< AES > cmac(key, key.size());
    const int flags = HashVerificationFilter::THROW_EXCEPTION | HashVerificationFilter::HASH_AT_END;
    
    StringSource(plain + mac, true, 
        new HashVerificationFilter(cmac, NULL, flags)
    ); // StringSource

    cout << "Verified message" << endl;
}
catch(const CryptoPP::Exception& e)
{
    cerr << e.what() << endl;
    ...
}

We can tamper with a message as follows, which will cause the HashVerificationFilter to throw the exception, HashVerificationFilter: message hash or MAC not valid:

CMAC< AES > cmac(key, key.size());

// Tamper with message
plain[0] ^= 0x01;

StringSource(plain + mac, true, 
    new HashVerificationFilter(cmac, NULL, THROW_EXCEPTION | HASH_AT_END)
); // StringSource

Switching to another block cipher, such as TDEA, is a simple as the following:

CMAC< DES_EDE3 > cmac(key, key.size());

StringSource(plain, true, 
    new HashFilter(cmac,
        new StringSink(mac)
    ) // HashFilter      
); // StringSource

[edit] Downloads

CMAC-AES-Filter.zip - Demonstrates an AES based CMAC with filters

Personal tools