Message Authentication Codes
Hash-Based Message Authentication Code
A message authentication code (MAC) is a unique data tag used to check the integrity of a message and confirm the message origin. MAC computation is based on a secret key. Secret-key cryptosystems are symmetric: the same key is employed to compute and verify a MAC, so two parties exchanging security-sensitive information must agree on the shared secret.
Authentication code algorithms are divided into unconditionally secure MACs, MACs derived from stream ciphers, block-cipher based MACs and HMACs. The last type is supported by many software libraries and cryptographic tools.
HMAC stands for the Hash-Based Message Authentication Code: a symmetric key is used in combination with a cryptographic hash function.
HMAC=H(K XOR opad, H(K XOR ipad, message))
In the HMAC formula above H is any iterated cryptographic hash function, K is a secret key, opad and ipad are outer and inner paddings.
OpenSSL
OpenSSL is really a "Swiss Army knife" of the applied cryptography: the same OpenSSL dgst command that is launched for message digests calculation is employed to compute an HMAC of a string or a file.
string HMAC
echo -n "message" | openssl dgst -sha256 -hmac "secret"
file HMAC
openssl dgst -sha256 -hmac "secret" file.docx
The HMAC key in the examples above is a string. In real-world scenarios a strong key is created as a result of a preliminary cryptographic operation, then it is passed as an additional parameter to the dgst command:
export key=`cat key.dat`
openssl dgst -sha256 -mac HMAC -macopt hexkey:$key file.docx
The -mac option tells OpenSSL to create a keyed message authentication code. The -macopt passes necessary parameters to the selected MAC algorithm. The hexkey command argument above obtains an HMAC key as a hexadecimal string. The key is retrieved from a local file. The recommended key length is the block size of the associated hash function.
Programming Languages
HMAC routines are implemented in standard libraries of both dynamic and statically typed programming languages. Demos below will illustrate the use of hash-based MAC algorithms in PHP, Python, Java and C#.
PHP
HASH Message Digest Framework
PHP Hash extension contains two functions for computing HMACs:
<?php
echo hash_hmac('sha256', 'message', 'secret');
echo hash_hmac_file('sha256', 'file.xlsx', 'secret');
?>
If a secret key is stored in a local file, HMAC calculation is preceded by the binary reading of the key:
<?php
$handler = fopen('secret-key.dat', 'rb');
$key = fread($handler, filesize('secret-key.dat'));
fclose($handler);
echo "key: ".bin2hex($key)."\n";
echo "HMAC: ".hash_hmac_file('sha256', 'file.pdf', $key);
?>
Mhash
The Mhash extension is another way to get HMACs in PHP:
<?php
$hmac = mhash(MHASH_SHA256, 'message', 'secret');
echo bin2hex($hmac);
?>
Files are read as binary strings before being passed to the mhash() function:
<?php
$k = fopen('secret-key.dat', 'rb');
$key = fread($k, filesize('secret-key.dat'));
fclose($k);
$f = fopen('code.tar.gz', 'rb');
$message = fread($f, filesize('code.tar.gz'));
fclose($f);
$hmac = mhash(MHASH_SHA256, $message, $key);
echo "key: ".bin2hex($key)."\n";
echo "HMAC: ".bin2hex($hmac);
?>
HMAC Key Generation
A key for HMAC can be created by calling the mhash_keygen_s2k() function. This routine is borrowed from OpenPGP. In OpenPGP a string-to-key (S2k) conversion returns a secret key from the user-supplied password. Simple S2k just hashes the passphrase to produce the key. Salted S2k parses both the passphrase and a salt consisting of eight octets of arbitrary data. An iterated S2k operation repeatedly hashes the salt combined with the passphrase.
Native mhash functions give more detailed information about key generation process:
C code snippet
size_t count = mhash_keygen_count();
int i;
for(i = 0; i < count; i++) {
char *name = mhash_get_keygen_name(i);
if(name != NULL){
printf("%s\n", name);
free(name);
}
}
The mhash_keygen_count() function returns the ID of the last available key generation algorithm. Algorithms are numbered from 0 to that value. Passing the ID to the mhash_get_keygen_name() retrieves the name of the algorithm. The mhash library supports a number of key generation techniques. The S2K_SALTED is one of them:
// the function returns 1: key generation needs a salt
int salt = mhash_keygen_uses_salt(KEYGEN_S2K_SALTED);
// the function returns 0: no iteration
int iteration = mhash_keygen_uses_count(KEYGEN_S2K_SALTED);
// the size of the salt is 8
int saltsize = mhash_get_keygen_salt_size(KEYGEN_S2K_SALTED);
// the function returns 0: there is no limitation imposed on the key size
int maxkeysize = mhash_get_keygen_max_key_size(KEYGEN_S2K_SALTED);
Salted S2k conversion in C makes use of the KEYGEN structure:
C code snippet
int i;
unsigned char key[64];
unsigned char salt[8];
struct keygen algorithm;
RAND_bytes(salt, 8);
algorithm.hash_algorithm[0] = MHASH_SHA256;
algorithm.count = 0;
algorithm.salt = salt;
algorithm.salt_size = mhash_get_keygen_salt_size(KEYGEN_S2K_SALTED);
// arguments are algorithm, algorithm data, key, its length, password, password length
mhash_keygen_ext(KEYGEN_S2K_SALTED, algorithm, key, 64, "password", strlen("password"));
for(i = 0; i < 64; i++) {
printf("%.2x", key[i]);
}
The RAND_bytes() function is one of OpenSSL functions: the salt is generated as a random value. It must be different for every new key.
PHP code using the same password and salt produces the same secret key:
<?php
$salt = base64_decode('w19LHqSRCDY=');
$key = mhash_keygen_s2k (MHASH_SHA256, 'password', $salt, 64);
echo bin2hex($key);
?>
Python
Python cryptographic services from the language Standard Library contain the hmac module. The module should be brought into action in conjunction with the hashlib:
import hmac
import hashlib
h = hmac.new('secret', 'message', hashlib.sha256)
print h.hexdigest()
The raw binary HMAC is returned by the digest():
print h.digest()
print base64.b64encode(h.digest())
Java
Computing MACs in Java follows the same principles of Java cryptography that are applied to hash functions. The Mac class represents the cryptographic engine for message authentication codes:
Mac mac = Mac.getInstance("HmacSHA256");
The Mac object is initialized with a secret key:
KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA256");
keyGenerator.init(512);
SecretKey secretKey = keyGenerator.generateKey();
mac.init(secretKey);
The KeyGenerator used for the creation of a secret key is just another example of the engine class. Key generators are based on the concepts of a key length and a source of randomness. The init() method above uses a system-provided RNG, but the key size is defined explicitly (512 bits).
The primary encoding format of the generated key is RAW
, its material can be converted to various formats or saved to a file:
String keyFormat = secretKey.getFormat();
byte[] key = secretKey.getEncoded();
String hexKey = Hex.toHexString(key);
String b64Key = Base64.toBase64String(key);
Hex and Base64 are utility classes from the org.bouncycastle.util.encoders package.
An HMAC of a string is usually a single-part operation:
byte[] hmac = mac.doFinal("message".getBytes());
Incremental hashing presumes message data updates:
int i;
byte[] buffer = new byte[8192];
BufferedInputStream stream = new BufferedInputStream(new FileInputStream("file.xml"));
while((i = stream.read(buffer)) != -1) {
mac.update(buffer, 0, i);
}
byte[] hmac = mac.doFinal();
C#
Cryptographic Services of the .NET framework rely on the HashAlgorithm base class to provide HMAC functionality to .NET applications. The class API is inherited by the KeyedHashAlgorithm and HMAC from which all implementations of keyed hash algorithms must derive.
The Create() method of the "blueprint" classes can be used to obtain a working HMAC engine:
HashAlgorithm hmacAlg = HashAlgorithm.Create("HMACSHA256");
KeyedHashAlgorithm keyedHashAlg = KeyedHashAlgorithm.Create("HMACSHA256");
HMAC hmac = HMAC.Create("HMACSHA256");
Direct instantiation of HMACSHA256 is also possible:
HMACSHA256 hmacSHA256 = new HMACSHA256();
A 64-byte key is created for HMAC objects implicitly:
// secret key is an array of 64 random bytes
Console.WriteLine(hmacSHA256.Key.Length);
foreach (byte b in hmacSHA256.Key) {
Console.Write(b.ToString("x2"));
}
Programmatically, computing an HMAC of a string or a stream is similar to message digest computation:
HMACSHA256 hmacSHA256 = new HMACSHA256();
byte[] message = Encoding.Default.GetBytes("message");
byte[] result = hmacSHA256.ComputeHash(message);
foreach (byte b in result) {
Console.Write(b.ToString("x2"));
}