Blob Blame History Raw
/*
 * Soft:        Keepalived is a failover program for the LVS project
 *              <www.linuxvirtualserver.org>. It monitor & manipulate
 *              a loadbalanced server pool using multi-layer checks.
 *
 * Part:        IPSEC AH implementation according to RFC 2402. Processing
 *              authentication data encryption using HMAC MD5 according to
 *              RFCs 2085 & 2104.
 *
 * Author:      Alexandre Cassen, <acassen@linux-vs.org>
 *
 *              This program is distributed in the hope that it will be useful,
 *              but WITHOUT ANY WARRANTY; without even the implied warranty of
 *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *              See the GNU General Public License for more details.
 *
 *              This program is free software; you can redistribute it and/or
 *              modify it under the terms of the GNU General Public License
 *              as published by the Free Software Foundation; either version
 *              2 of the License, or (at your option) any later version.
 *
 * Copyright (C) 2001-2017 Alexandre Cassen, <acassen@gmail.com>
 */

#include "config.h"

#include <openssl/md5.h>
#include <string.h>

#include "vrrp_ipsecah.h"

#define	BLOCK_SIZE	64

/* hmac_md5 computation according to the RFCs 2085 & 2104 */
void
hmac_md5(unsigned char *buffer, size_t buffer_len, unsigned char *key, size_t key_len,
	 unsigned char *digest)
{
	MD5_CTX context;
	unsigned char k_ipad[BLOCK_SIZE+1];	/* inner padding - key XORd with ipad */
	unsigned char k_opad[BLOCK_SIZE+1];	/* outer padding - key XORd with opad */
	unsigned char tk[MD5_DIGEST_LENGTH];
	int i;

	/* Initialize data */
	memset(k_ipad, 0, sizeof (k_ipad));
	memset(k_opad, 0, sizeof (k_opad));
	memset(tk, 0, sizeof (tk));

	/* If the key is longer than 64 bytes => set it to key=MD5(key) */
	if (key_len > BLOCK_SIZE) {
		MD5_CTX tctx;

		/* Compute the MD5 digest */
		MD5_Init(&tctx);
		MD5_Update(&tctx, key, key_len);
		MD5_Final(tk, &tctx);

		key = tk;
		key_len = MD5_DIGEST_LENGTH;
	}

	/* The global HMAC_MD5 algo looks like (rfc2085.2.2) :
	   MD5(K XOR opad, MD5(K XOR ipad, buffer))
	   K : an n byte key
	   ipad : byte 0x36 repeated 64 times
	   opad : byte 0x5c repeated 64 times
	   buffer : buffer being protected
	 */
	memset(k_ipad, 0, sizeof (k_ipad));
	memset(k_opad, 0, sizeof (k_opad));
	memcpy(k_ipad, key, key_len);
	memcpy(k_opad, key, key_len);

	/* XOR key with ipad and opad values */
	for (i = 0; i < BLOCK_SIZE; i++) {
		k_ipad[i] ^= 0x36;
		k_opad[i] ^= 0x5c;
	}

	/* Compute inner MD5 */
	MD5_Init(&context);				/* Init context for 1st pass */
	MD5_Update(&context, k_ipad, BLOCK_SIZE);		/* start with inner pad */
	MD5_Update(&context, buffer, buffer_len);	/* next with buffer datagram */
	MD5_Final(digest, &context);			/* Finish 1st pass */

	/* Compute outer MD5 */
	MD5_Init(&context);				/* Init context for 2nd pass */
	MD5_Update(&context, k_opad, BLOCK_SIZE);		/* start with inner pad */
	MD5_Update(&context, digest, MD5_DIGEST_LENGTH); /* next result of 1st pass */
	MD5_Final(digest, &context);			/* Finish 2nd pass */
}