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.

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 ss1(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.data(), key.size());

    StringSource ss2(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 ss3(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.data(), key.size());
    const int flags = HashVerificationFilter::THROW_EXCEPTION | HashVerificationFilter::HASH_AT_END;
    
    StringSource ss(plain + mac, true, 
        new HashVerificationFilter(cmac, NULL, flags)
    ); // StringSource

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

You can also specify the length of the CMAC. By default, the HashVerificationFilter uses the size of the underlying block cipher's block size.

cout << "NIST SP 800-38B, Example 12" << endl;

key = HexDecode("603deb10 15ca71be 2b73aef0 857d7781 1f352c07 3b6108d7 2d9810a3 0914dff4");
message = HexDecode("6bc1bee2 2e409f96 e93d7e11 7393172a ae2d8a57 1e03ac9c 9eb76fac 45af8e51" \
                    "30c81c46 a35ce411 e5fbc119 1a0a52ef f69f2445 df4f9b17 ad2b417b e66c3710");
mac = HexDecode("e1992190 549f6ed5 696a2c05 6c315410");

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

try
{
    StringSource ss(message + mac, true,
        new HashVerificationFilter(cmac, NULL, THROW_EXCEPTION | HASH_AT_END, mac.size())
    ); // StringSource
}
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.data(), key.size());

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

StringSource ss(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.data(), key.size());

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

Downloads

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

Cmac-sp800-38b.zip - Program that consumes NIST SP 800-38B text vectors