changeset 21908:f786e478e08b

HMAC digest support from Elliott Sales de Andrade committer: Gary Kramlich <grim@reaperworld.com>
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Thu, 20 Dec 2007 03:44:01 +0000
parents 03463c52b9d7
children 6648cfa72842
files libpurple/cipher.c libpurple/cipher.h libpurple/tests/test_cipher.c
diffstat 3 files changed, 608 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/cipher.c	Thu Dec 20 03:40:56 2007 +0000
+++ b/libpurple/cipher.c	Thu Dec 20 03:44:01 2007 +0000
@@ -64,6 +64,8 @@
 /*******************************************************************************
  * MD5
  ******************************************************************************/
+#define MD5_HMAC_BLOCK_SIZE	64
+
 struct MD5Context {
 	guint32 total[2];
 	guint32 state[4];
@@ -325,6 +327,13 @@
 	return TRUE;
 }
 
+static size_t
+md5_get_block_size(PurpleCipherContext *context)
+{
+	/* This does not change (in this case) */
+	return MD5_HMAC_BLOCK_SIZE;
+}
+
 static PurpleCipherOps MD5Ops = {
 	NULL,			/* Set option */
 	NULL,			/* Get option */
@@ -342,10 +351,8 @@
 	NULL,			/* get key size */
 	NULL,			/* set batch mode */
 	NULL,			/* get batch mode */
-
-	/* padding */
-	NULL,
-	NULL
+	md5_get_block_size,	/* get block size */
+	NULL			/* set key with len */
 };
 
 /*******************************************************************************
@@ -580,6 +587,13 @@
 	md4_context = NULL;
 }
 
+static size_t
+md4_get_block_size(PurpleCipherContext *context)
+{
+	/* This does not change (in this case) */
+	return MD4_HMAC_BLOCK_SIZE;
+}
+
 static PurpleCipherOps MD4Ops = {
 	NULL,                   /* Set option */
 	NULL,                   /* Get option */
@@ -597,10 +611,200 @@
 	NULL,                   /* get key size */
 	NULL,                   /* set batch mode */
 	NULL,                   /* get batch mode */
-
-	/* padding */
-	NULL,
-	NULL
+	md4_get_block_size,     /* get block size */
+	NULL                    /* set key with len */
+};
+
+/*******************************************************************************
+ * HMAC
+ ******************************************************************************/
+
+struct HMAC_Context {
+	PurpleCipherContext *hash;
+	char *name;
+	int blocksize;
+	guchar *opad;
+};
+
+static void
+hmac_init(PurpleCipherContext *context, gpointer extra)
+{
+	struct HMAC_Context *hctx;
+	hctx = g_new0(struct HMAC_Context, 1);
+	purple_cipher_context_set_data(context, hctx);
+	purple_cipher_context_reset(context, extra);
+}
+
+static void
+hmac_reset(PurpleCipherContext *context, gpointer extra)
+{
+	struct HMAC_Context *hctx;
+
+	hctx = purple_cipher_context_get_data(context);
+
+	g_free(hctx->name);
+	hctx->name = NULL;
+	if (hctx->hash)
+		purple_cipher_context_destroy(hctx->hash);
+	hctx->hash = NULL;
+	hctx->blocksize = 0;
+	g_free(hctx->opad);
+	hctx->opad = NULL;
+}
+
+static void
+hmac_set_opt(PurpleCipherContext *context, const gchar *name, void *value)
+{
+	struct HMAC_Context *hctx;
+
+	hctx = purple_cipher_context_get_data(context);
+
+	if (!strcmp(name, "hash")) {
+		g_free(hctx->name);
+		if (hctx->hash)
+			purple_cipher_context_destroy(hctx->hash);
+		hctx->name = g_strdup((char*)value);
+		hctx->hash = purple_cipher_context_new_by_name((char *)value, NULL);
+		hctx->blocksize = purple_cipher_context_get_block_size(hctx->hash);
+	}
+}
+
+static void *
+hmac_get_opt(PurpleCipherContext *context, const gchar *name)
+{
+	struct HMAC_Context *hctx;
+
+	hctx = purple_cipher_context_get_data(context);
+
+	if (!strcmp(name, "hash")) {
+		return hctx->name;
+	}
+
+	return NULL;
+}
+
+static void
+hmac_append(PurpleCipherContext *context, const guchar *data, size_t len) 
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+
+	g_return_if_fail(hctx->hash != NULL);
+
+	purple_cipher_context_append(hctx->hash, data, len);
+}
+
+static gboolean
+hmac_digest(PurpleCipherContext *context, size_t in_len, guchar *out, size_t *out_len)
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+	PurpleCipherContext *hash = hctx->hash;
+	guchar *inner_hash;
+	size_t hash_len;
+	gboolean result;
+
+	g_return_val_if_fail(hash != NULL, FALSE);
+
+	inner_hash = g_malloc(100); /* TODO: Should be enough for now... */
+	result = purple_cipher_context_digest(hash, 100, inner_hash, &hash_len);
+
+	purple_cipher_context_reset(hash, NULL);
+
+	purple_cipher_context_append(hash, hctx->opad, hctx->blocksize);
+	purple_cipher_context_append(hash, inner_hash, hash_len);
+
+	g_free(inner_hash);
+
+	result = result && purple_cipher_context_digest(hash, in_len, out, out_len);
+
+	return result;
+}
+
+static void
+hmac_uninit(PurpleCipherContext *context)
+{
+	struct HMAC_Context *hctx;
+
+	purple_cipher_context_reset(context, NULL);
+
+	hctx = purple_cipher_context_get_data(context);
+
+	g_free(hctx);
+}
+
+static void
+hmac_set_key_with_len(PurpleCipherContext *context, const guchar * key, size_t key_len)
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+	int blocksize, i;
+	guchar *ipad;
+	guchar *full_key;
+
+	g_return_if_fail(hctx->hash != NULL);
+
+	g_free(hctx->opad);
+
+	blocksize = hctx->blocksize;
+	ipad = g_malloc(blocksize);
+	hctx->opad = g_malloc(blocksize);
+
+	if (key_len > blocksize) {
+		purple_cipher_context_reset(hctx->hash, NULL);
+		purple_cipher_context_append(hctx->hash, key, key_len);
+		full_key = g_malloc(100); /* TODO: Should be enough for now... */
+		purple_cipher_context_digest(hctx->hash, 100, full_key, &key_len);
+	} else
+		full_key = g_memdup(key, key_len);
+
+    if (key_len < blocksize) {
+    	full_key = g_realloc(full_key, blocksize);
+    	memset(full_key + key_len, 0, blocksize - key_len);
+    }
+
+	for(i = 0; i < blocksize; i++) {
+		ipad[i] = 0x36 ^ full_key[i];
+		hctx->opad[i] = 0x5c ^ full_key[i];
+	}
+
+	g_free(full_key);
+
+	purple_cipher_context_reset(hctx->hash, NULL);
+	purple_cipher_context_append(hctx->hash, ipad, blocksize);
+	g_free(ipad);
+}
+
+static void
+hmac_set_key(PurpleCipherContext *context, const guchar * key)
+{
+	hmac_set_key_with_len(context, key, strlen(key));
+}
+
+static size_t 
+hmac_get_block_size(PurpleCipherContext *context)
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+
+	return hctx->blocksize;
+}
+
+static PurpleCipherOps HMACOps = {
+	hmac_set_opt,           /* Set option */
+	hmac_get_opt,           /* Get option */
+	hmac_init,               /* init */
+	hmac_reset,              /* reset */
+	hmac_uninit,             /* uninit */
+	NULL,                   /* set iv */
+	hmac_append,             /* append */
+	hmac_digest,             /* digest */
+	NULL,                   /* encrypt */
+	NULL,                   /* decrypt */
+	NULL,                   /* set salt */
+	NULL,                   /* get salt size */
+	hmac_set_key,           /* set key */
+	NULL,                   /* get key size */
+	NULL,                   /* set batch mode */
+	NULL,                   /* get batch mode */
+	hmac_get_block_size,    /* get block size */
+	hmac_set_key_with_len   /* set key with len */
 };
 
 /******************************************************************************
@@ -1051,10 +1255,8 @@
 	NULL,              /* get key size */
 	NULL,              /* set batch mode */
 	NULL,              /* get batch mode */
-
-	/* padding */
-	NULL,
-	NULL
+	NULL,              /* get block size */
+	NULL               /* set key with len */
 };
 
 /******************************************************************************
@@ -1403,15 +1605,14 @@
 	NULL,              /* get key size */
 	des3_set_batch,    /* set batch mode */
 	des3_get_batch,    /* get batch mode */
-
-	/* padding */
-	NULL,
-	NULL
+	NULL,              /* get block size */
+	NULL               /* set key with len */
 };
 
 /*******************************************************************************
  * SHA-1
  ******************************************************************************/
+#define SHA1_HMAC_BLOCK_SIZE	64
 #define SHA1_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xFFFFFFFF)
 
 struct SHA1Context {
@@ -1632,6 +1833,13 @@
 	return TRUE;
 }
 
+static size_t
+sha1_get_block_size(PurpleCipherContext *context)
+{
+	/* This does not change (in this case) */
+	return SHA1_HMAC_BLOCK_SIZE;
+}
+
 static PurpleCipherOps SHA1Ops = {
 	sha1_set_opt,	/* Set Option		*/
 	sha1_get_opt,	/* Get Option		*/
@@ -1649,10 +1857,8 @@
 	NULL,			/* get key size		*/
 	NULL,			/* set batch mode */
 	NULL,			/* get batch mode */
-
-	/* padding */
-	NULL,
-	NULL
+	sha1_get_block_size,	/* get block size */
+	NULL			/* set key with len */
 };
 
 /*******************************************************************************
@@ -1818,10 +2024,8 @@
 	rc4_get_key_size, /* get key size  */
 	NULL,          /* set batch mode */
 	NULL,          /* get batch mode */
-
-	/* padding */
-	NULL,
-	NULL
+	NULL,          /* get block size */
+	NULL           /* set key with len */
 };
 
 /*******************************************************************************
@@ -1895,6 +2099,10 @@
 		caps |= PURPLE_CIPHER_CAPS_SET_BATCH_MODE;
 	if(ops->get_batch_mode)
 		caps |= PURPLE_CIPHER_CAPS_GET_BATCH_MODE;
+	if(ops->get_block_size)
+		caps |= PURPLE_CIPHER_CAPS_GET_BLOCK_SIZE;
+	if(ops->set_key_with_len)
+		caps |= PURPLE_CIPHER_CAPS_SET_KEY_WITH_LEN;
 
 	return caps;
 }
@@ -2021,6 +2229,7 @@
 	purple_ciphers_register_cipher("md5", &MD5Ops);
 	purple_ciphers_register_cipher("sha1", &SHA1Ops);
 	purple_ciphers_register_cipher("md4", &MD4Ops);
+	purple_ciphers_register_cipher("hmac", &HMACOps);
 	purple_ciphers_register_cipher("des", &DESOps);
 	purple_ciphers_register_cipher("des3", &DES3Ops);
 	purple_ciphers_register_cipher("rc4", &RC4Ops);
@@ -2389,6 +2598,43 @@
 	}
 }
 
+size_t
+purple_cipher_context_get_block_size(PurpleCipherContext *context)
+{
+	PurpleCipher *cipher = NULL;
+
+	g_return_val_if_fail(context, -1);
+
+	cipher = context->cipher;
+	g_return_val_if_fail(cipher, -1);
+
+	if(cipher->ops && cipher->ops->get_block_size)
+		return cipher->ops->get_block_size(context);
+	else {
+		purple_debug_info("cipher", "The %s cipher does not support the "
+		                            "get_block_size operation\n", cipher->name);
+		return -1;
+	}
+}
+
+void
+purple_cipher_context_set_key_with_len(PurpleCipherContext *context,
+                                       const guchar *key, size_t len)
+{
+	PurpleCipher *cipher = NULL;
+
+	g_return_if_fail(context);
+
+	cipher = context->cipher;
+	g_return_if_fail(cipher);
+
+	if(cipher->ops && cipher->ops->set_key_with_len)
+		cipher->ops->set_key_with_len(context, key, len);
+	else
+		purple_debug_info("cipher", "The %s cipher does not support the "
+		                            "set_key_with_len operation\n", cipher->name);
+}
+
 void
 purple_cipher_context_set_data(PurpleCipherContext *context, gpointer data) {
 	g_return_if_fail(context);
--- a/libpurple/cipher.h	Thu Dec 20 03:40:56 2007 +0000
+++ b/libpurple/cipher.h	Thu Dec 20 03:44:01 2007 +0000
@@ -65,7 +65,9 @@
 	PURPLE_CIPHER_CAPS_GET_KEY_SIZE     = 1 << 14,  /**< Get key size flag	*/
 	PURPLE_CIPHER_CAPS_SET_BATCH_MODE   = 1 << 15,  /**< Set batch mode flag */
 	PURPLE_CIPHER_CAPS_GET_BATCH_MODE   = 1 << 16,  /**< Get batch mode flag */
-	PURPLE_CIPHER_CAPS_UNKNOWN			= 1 << 17   /**< Unknown			*/
+	PURPLE_CIPHER_CAPS_GET_BLOCK_SIZE   = 1 << 17,  /**< The get block size flag */
+	PURPLE_CIPHER_CAPS_SET_KEY_WITH_LEN = 1 << 18,  /**< The set key with length flag */
+	PURPLE_CIPHER_CAPS_UNKNOWN          = 1 << 19   /**< Unknown			*/
 } PurpleCipherCaps;
 
 /**
@@ -120,8 +122,11 @@
 	/** The get batch mode function */
 	PurpleCipherBatchMode (*get_batch_mode)(PurpleCipherContext *context);
 
-	void (*_purple_reserved1)(void);
-	void (*_purple_reserved2)(void);
+	/** The get block size function */
+	size_t (*get_block_size)(PurpleCipherContext *context);
+
+	/** The set key with length function */
+	void (*set_key_with_len)(PurpleCipherContext *context, const guchar *key, size_t len);
 };
 
 #ifdef __cplusplus
@@ -408,6 +413,25 @@
 PurpleCipherBatchMode purple_cipher_context_get_batch_mode(PurpleCipherContext *context);
 
 /**
+ * Gets the block size of a context
+ *
+ * @param context The context whose block size to get
+ *
+ * @return The block size of the context
+ */
+size_t purple_cipher_context_get_block_size(PurpleCipherContext *context);
+
+/**
+ * Sets the key with a given length on a context 
+ *
+ * @param context The context whose key to set
+ * @param key     The key
+ * @param len     The length of the key
+ *
+ */
+void purple_cipher_context_set_key_with_len(PurpleCipherContext *context, const guchar *key, size_t len);
+
+/**
  * Sets the cipher data for a context
  *
  * @param context The context whose cipher data to set
--- a/libpurple/tests/test_cipher.c	Thu Dec 20 03:40:56 2007 +0000
+++ b/libpurple/tests/test_cipher.c	Thu Dec 20 03:44:01 2007 +0000
@@ -408,6 +408,293 @@
 END_TEST
 
 /******************************************************************************
+ * HMAC Tests
+ * See RFC2202 and some other NULL tests I made up
+ *****************************************************************************/
+
+#define HMAC_TEST(data, data_len, key, key_len, type, digest) { \
+	PurpleCipher *cipher = NULL; \
+	PurpleCipherContext *context = NULL; \
+	gchar cdigest[41]; \
+	gboolean ret = FALSE; \
+	\
+	cipher = purple_ciphers_find_cipher("hmac"); \
+	context = purple_cipher_context_new(cipher, NULL); \
+	purple_cipher_context_set_option(context, "hash", type); \
+	purple_cipher_context_set_key_with_len(context, (guchar *)key, (key_len)); \
+	\
+	purple_cipher_context_append(context, (guchar *)(data), (data_len)); \
+	ret = purple_cipher_context_digest_to_str(context, sizeof(cdigest), cdigest, \
+	                                        NULL); \
+	\
+	fail_unless(ret == TRUE, NULL); \
+	fail_unless(strcmp((digest), cdigest) == 0, NULL); \
+	\
+	purple_cipher_context_destroy(context); \
+}
+
+/* HMAC MD5 */
+
+START_TEST(test_hmac_md5_Hi) {
+	HMAC_TEST("Hi There",
+	          8,
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+	          16,
+	          "md5",
+	          "9294727a3638bb1c13f48ef8158bfc9d");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_what) {
+	HMAC_TEST("what do ya want for nothing?",
+	          28,
+	          "Jefe",
+	          4,
+	          "md5",
+	          "750c783e6ab0b503eaa86e310a5db738");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_dd) {
+	HMAC_TEST("\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd",
+	          50,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          16,
+	          "md5",
+	          "56be34521d144c88dbb8c733f0e8b3f6");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_cd) {
+	HMAC_TEST("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd",
+	          50,
+	          "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
+	          "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
+	          "\x15\x16\x17\x18\x19",
+	          25,
+	          "md5",
+	          "697eaf0aca3a3aea3a75164746ffaa79");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_truncation) {
+	HMAC_TEST("Test With Truncation",
+	          20,
+	          "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
+	          16,
+	          "md5",
+	          "56461ef2342edc00f9bab995690efd4c");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_large_key) {
+	HMAC_TEST("Test Using Larger Than Block-Size Key - Hash Key First",
+	          54,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          80,
+	          "md5",
+	          "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_large_key_and_data) {
+	HMAC_TEST("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+	          73,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          80,
+	          "md5",
+	          "6f630fad67cda0ee1fb1f562db3aa53e");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_null_key) {
+	HMAC_TEST("Hi There",
+	          8,
+	          "\x0a\x0b\x00\x0d\x0e\x0f\x1a\x2f\x0b\x0b"
+	          "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
+	          20,
+	          "md5",
+	          "597bfd644b797a985561eeb03a169e59");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_null_text) {
+	HMAC_TEST("Hi\x00There",
+	          8,
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+	          20,
+	          "md5",
+	          "70be8e1b7b50dfcc335d6cd7992c564f");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_null_key_and_text) {
+	HMAC_TEST("Hi\x00Th\x00re",
+	          8,
+	          "\x0c\x0d\x00\x0f\x10\x1a\x3a\x3a\xe6\x34"
+	          "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
+	          20,
+	          "md5",
+	          "b31bcbba35a33a067cbba9131cba4889");
+}
+END_TEST
+
+/* HMAC SHA1 */
+
+START_TEST(test_hmac_sha1_Hi) {
+	HMAC_TEST("Hi There",
+	          8,
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+	          20,
+	          "sha1",
+	          "b617318655057264e28bc0b6fb378c8ef146be00");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_what) {
+	HMAC_TEST("what do ya want for nothing?",
+	          28,
+	          "Jefe",
+	          4,
+	          "sha1",
+	          "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_dd) {
+	HMAC_TEST("\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd",
+	          50,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          20,
+	          "sha1",
+	          "125d7342b9ac11cd91a39af48aa17b4f63f175d3");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_cd) {
+	HMAC_TEST("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd",
+	          50,
+	          "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
+	          "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
+	          "\x15\x16\x17\x18\x19",
+	          25,
+	          "sha1",
+	          "4c9007f4026250c6bc8414f9bf50c86c2d7235da");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_truncation) {
+	HMAC_TEST("Test With Truncation",
+	          20,
+	          "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"
+	          "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
+	          20,
+	          "sha1",
+	          "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_large_key) {
+	HMAC_TEST("Test Using Larger Than Block-Size Key - Hash Key First",
+	          54,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          80,
+	          "sha1",
+	          "aa4ae5e15272d00e95705637ce8a3b55ed402112");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_large_key_and_data) {
+	HMAC_TEST("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+	          73,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          80,
+	          "sha1",
+	          "e8e99d0f45237d786d6bbaa7965c7808bbff1a91");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_null_key) {
+	HMAC_TEST("Hi There",
+	          8,
+	          "\x0a\x0b\x00\x0d\x0e\x0f\x1a\x2f\x0b\x0b"
+	          "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
+	          20,
+	          "sha1",
+	          "eb62a2e0e33d300be669c52aab3f591bc960aac5");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_null_text) {
+	HMAC_TEST("Hi\x00There",
+	          8,
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+	          20,
+	          "sha1",
+	          "31ca58d849e971e418e3439de2c6f83144b6abb7");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_null_key_and_text) {
+	HMAC_TEST("Hi\x00Th\x00re",
+	          8,
+	          "\x0c\x0d\x00\x0f\x10\x1a\x3a\x3a\xe6\x34"
+	          "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
+	          20,
+	          "sha1",
+	          "e6b8e2fede87aa09dcb13e554df1435e056eae36");
+}
+END_TEST
+
+/******************************************************************************
  * Suite
  *****************************************************************************/
 Suite *
@@ -468,6 +755,30 @@
 	tcase_add_test(tc, test_des3_cbc_null_key_and_text);
 	suite_add_tcase(s, tc);
 
+	/* hmac tests */
+	tc = tcase_create("HMAC");
+	tcase_add_test(tc, test_hmac_md5_Hi);
+	tcase_add_test(tc, test_hmac_md5_what);
+	tcase_add_test(tc, test_hmac_md5_dd);
+	tcase_add_test(tc, test_hmac_md5_cd);
+	tcase_add_test(tc, test_hmac_md5_truncation);
+	tcase_add_test(tc, test_hmac_md5_large_key);
+	tcase_add_test(tc, test_hmac_md5_large_key_and_data);
+	tcase_add_test(tc, test_hmac_md5_null_key);
+	tcase_add_test(tc, test_hmac_md5_null_text);
+	tcase_add_test(tc, test_hmac_md5_null_key_and_text);
+	tcase_add_test(tc, test_hmac_sha1_Hi);
+	tcase_add_test(tc, test_hmac_sha1_what);
+	tcase_add_test(tc, test_hmac_sha1_dd);
+	tcase_add_test(tc, test_hmac_sha1_cd);
+	tcase_add_test(tc, test_hmac_sha1_truncation);
+	tcase_add_test(tc, test_hmac_sha1_large_key);
+	tcase_add_test(tc, test_hmac_sha1_large_key_and_data);
+	tcase_add_test(tc, test_hmac_sha1_null_key);
+	tcase_add_test(tc, test_hmac_sha1_null_text);
+	tcase_add_test(tc, test_hmac_sha1_null_key_and_text);
+	suite_add_tcase(s, tc);
+
 	return s;
 }