diff --git a/puer.c b/puer.c index 48f2d97..6bc3ec5 100644 --- a/puer.c +++ b/puer.c @@ -205,3 +205,46 @@ void finalize_hash(struct hashstate *state, unsigned char hash[32]) { // there explicit_bzero(state, sizeof(struct hashstate)); } + +void hmac(unsigned char output[32], unsigned char key[], size_t keylen, unsigned char message[], size_t messagelen) { + // The blocksize of the underlying has function is 128 bits (16B) + // but HMAC is specified assuming that the hash function output (in + // our case 256 bits or 32B) fits in one block. As far as I can + // tell extending the key to be two blocks long is not a problem. + + unsigned char padded_key[32]; + if (keylen > 16) { + // We hash it even if it is shorter than our extended key + // length to avoid giving attacker any funny surfaces to + // play with at the interface of two blocks + struct hashstate state; + initialize_hash(&state); + feed_hash(&state, key, keylen); + finalize_hash(&state, padded_key); + } else { + // Copy the key and zero-pad if necessary + memset(padded_key, 0, 32); + memcpy(padded_key, key, keylen); + } + + // Outer and inner key derivation + unsigned char outer_key[32], inner_key[32]; + for (size_t i = 0; i < 32; i++) { + outer_key[i] = padded_key[i] ^ 0x5c; + inner_key[i] = padded_key[i] ^ 0x36; + } + + // Inner hash + unsigned char inner_hash[32]; + struct hashstate state; + initialize_hash(&state); + feed_hash(&state, inner_key, 32); + feed_hash(&state, message, messagelen); + finalize_hash(&state, inner_hash); + + // Outer hash + initialize_hash(&state); + feed_hash(&state, outer_key, 32); + feed_hash(&state, inner_hash, 32); + finalize_hash(&state, output); +}