HMAC
HMAC is a hash-based MAC algorithm specified in FIPS 198. A HMAC is the hash equivalent of a CMAC. HMACs can be used when a hash function is more readily available than a block cipher.
Sample Programs
There are two sample programs below. The first sample program below demonstrates a HMAC with SHA256 using filters (see pipelining). The second sample program uses HashTransformation member functions.
Pipelines
AutoSeededRandomPool prng; SecByteBlock key(16); prng.GenerateBlock(key, key.size()); string plain = "HMAC 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 { HMAC< SHA256 > hmac(key, key.size()); StringSource ss2(plain, true, new HashFilter(hmac, 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 << "hmac: " << 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: 11A47EC4465DD95FCD393075E7D3C4EB plain text: HMAC Test hmac: 24DA0BDE78E7E277B42188165595CCEB0DDF303B9EF93534B04C6433DD57EF78
To verify a HMAC on a message, use a HashVerificationFilter.
try { HMAC< SHA256 > hmac(key, key.size()); const int flags = HashVerificationFilter::THROW_EXCEPTION | HashVerificationFilter::HASH_AT_END; StringSource(plain + mac, true, new HashVerificationFilter(hmac, 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:
HMAC< SHA256 > hmac(key, key.size()); // Tamper with message plain[0] ^= 0x01; // Tamper with MAC mac[0] ^= 0x01; StringSource(plain + mac, true, new HashVerificationFilter(hmac, NULL, THROW_EXCEPTION | HASH_AT_END) ); // StringSource
Switching to another hash, such as Whirlpool, is a simple as the following:
HMAC< Whirlpool > hmac(key, key.size()); StringSource(plain, true, new HashFilter(hmac, new StringSink(mac) ) // HashFilter ); // StringSource
HashTransformation
Below is an example of using HashTransformation member functions to calculate a HMAC. Internally, pipelines do this for you.
#include "cryptlib.h" #include "files.h" #include "hex.h" #include "sha.h" #include "hmac.h" using namespace CryptoPP; #include <string> #include <iostream> int main(int argc, char* argv[]) { const byte m[] = { 0x5,0x8,0xC,0xE,0x1,0xE,0x6,0x0,0x1,0x1, 0x1,0x1,0x1,0x1,0x1,0x1,0x6,0x4,0x6,0x1, 0x7,0x4,0x6,0x1,0x0,0x0,0x0,0x0 }; const byte k[] = { 0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1, 0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1, 0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1, 0x1,0x1 }; HexEncoder hex(new FileSink(std::cout)); HMAC<SHA1> hmac(k, sizeof(k)); hmac.Update(m, sizeof(m)); byte d[HMAC<SHA1>::DIGESTSIZE]; hmac.Final(d); std::cout << "Message: "; hex.Put(m, sizeof(m)); hex.MessageEnd(); std::cout << std::endl; std::cout << "Digest: "; hex.Put(d, sizeof(d)); hex.MessageEnd(); std::cout << std::endl; return 0; }
The program produces the expected output:
$ ./test.exe Message: 05080C0E010E06000101010101010101060406010704060100000000 Digest: 0EA5E034142BDECC5D3AEC9DE727EE836BF95E1D
Downloads
HMAC-SHA-Filter.zip - Demonstrates a SHA256 based HMAC with filters