CMAC
From Crypto++ Wiki
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