Add HMAC-SHA1
authorDan Farina <drfarina@acm.org>
Thu, 10 May 2012 09:42:11 +0000 (02:42 -0700)
committerDan Farina <drfarina@acm.org>
Thu, 10 May 2012 09:54:44 +0000 (02:54 -0700)
Signed-off-by: Dan Farina <drfarina@acm.org>
Makefile
test/test_crypto.c
usual/hmac.c [new file with mode: 0644]
usual/hmac.h [new file with mode: 0644]

index 3b12a4101dd222b54084e3dfee159569698e0f2e..a5a2cf98844254f6b772424717822f5f685415b9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -29,6 +29,7 @@ libusual_la_SOURCES = usual/config.h.in \
        usual/getopt.h usual/getopt.c \
        usual/hashtab-impl.h \
        usual/heap.h usual/heap.c \
+       usual/hmac.h usual/hmac.c \
        usual/list.h usual/list.c \
        usual/logging.h usual/logging.c \
        usual/lookup3.h usual/lookup3.c \
index 379fb6c5c271ba8ebac3e7455bd3856c92dbd695..aaf28b558b1a4a2730c0740c0865a970ef5af3ce 100644 (file)
@@ -6,6 +6,7 @@
 
 #define str_check(a, b) tt_str_op(a, ==, b)
 
+#include <usual/hmac.h>
 #include <usual/md5.h>
 #include <usual/sha1.h>
 
@@ -104,6 +105,62 @@ static void test_sha1(void *ptr)
 end:;
 }
 
+
+/*
+ * HMAC
+ */
+
+static const char *run_hmac_sha1(const char *key, const char *str)
+{
+       struct hmac_sha1_ctx ctx[1];
+       uint8_t monolithic_res[SHA1_DIGEST_LENGTH];
+       uint8_t incremental_res[SHA1_DIGEST_LENGTH];
+       int i, len = strlen(str), step;
+
+       /* Compute HMAC all at once */
+       hmac_sha1_reset(ctx, (void *) key, strlen(key));
+       hmac_sha1_update(ctx, str, len);
+       hmac_sha1_final(monolithic_res, ctx);
+
+       /* Compute HMAC incrementally */
+       hmac_sha1_reset(ctx, (void *) key, strlen(key));
+       step = 3;
+       for (i = 0; i < len; i += step)
+               hmac_sha1_update(ctx, str+i,
+                                       (i + step <= len)
+                                       ? (step) : (len - i));
+       hmac_sha1_final(incremental_res, ctx);
+
+       if (memcmp(monolithic_res, incremental_res, SHA1_DIGEST_LENGTH) != 0)
+               return "FAIL";
+
+       return mkhex(monolithic_res, SHA1_DIGEST_LENGTH);
+}
+
+static void test_hmac(void *ptr)
+{
+       const char *long_key = (
+               "quite a very long key, longer than a sha1 block size, "
+               "so it needs to be sha-1d before being used as a key");
+       const char *text = "The quick brown fox jumps over the lazy dog";
+
+       str_check(run_hmac_sha1("", ""),
+                         "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d");
+
+       str_check(run_hmac_sha1("shrt", ""),
+                         "41fee95de96c437cf6c2f38363eb38eb0067ff64");
+
+       str_check(run_hmac_sha1(long_key, ""),
+               "496ca9bda3e523814ba7f99f68a2035e4de7702a");
+
+       str_check(run_hmac_sha1(long_key, text),
+               "924e1ee84da31f5f569a27dd6201533b42c999c6");
+
+       str_check(run_hmac_sha1("key", text),
+               "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9");
+end:;
+}
+
 /*
  * Laucher.
  */
@@ -111,6 +168,7 @@ end:;
 struct testcase_t crypto_tests[] = {
        { "md5", test_md5 },
        { "sha1", test_sha1 },
+       { "hmac", test_hmac },
        END_OF_TESTCASES
 };
 
diff --git a/usual/hmac.c b/usual/hmac.c
new file mode 100644 (file)
index 0000000..0543253
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * HMAC-SHA1 implementation based on OpenBSD hmac.c
+ *
+ * Copyright (c) 2012 Daniel Farina
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <string.h>
+
+#include <usual/hmac.h>
+#include <usual/sha1.h>
+
+/* Clean HMAC-SHA1 state */
+void
+hmac_sha1_reset(struct hmac_sha1_ctx *ctx,
+                               const u_int8_t *key, u_int key_len)
+{
+       uint8_t                          k_ipad[65];
+       int                                      i;
+
+       if (key_len > SHA1_BLOCK_SIZE) {
+               sha1_reset(&ctx->ctx);
+               sha1_update(&ctx->ctx, key, key_len);
+               sha1_final(ctx->key, &ctx->ctx);
+               ctx->key_len = SHA1_DIGEST_LENGTH;
+       } else {
+               memcpy(ctx->key, key, key_len);
+               ctx->key_len = key_len;
+       }
+
+       memset(k_ipad, 0, sizeof k_ipad);
+       memcpy(k_ipad, ctx->key, ctx->key_len);
+
+       for (i = 0; i < SHA1_BLOCK_SIZE; i += 1)
+               k_ipad[i] ^= 0x36;
+
+       sha1_reset(&ctx->ctx);
+       sha1_update(&ctx->ctx, k_ipad, SHA1_BLOCK_SIZE);
+
+       /*
+        * Seen in OpenBSD source, presumably to prevent key leakage through
+        * uninitialized memory.
+        */
+       memset(k_ipad, 0, sizeof k_ipad);
+}
+
+
+/* Update HMAC-SHA1 state with more data */
+void
+hmac_sha1_update(struct hmac_sha1_ctx *ctx,
+                                const void *data, unsigned int len) {
+       sha1_update(&ctx->ctx, data, len);
+}
+
+
+/* Get final HMAC-SHA1 result */
+void hmac_sha1_final(uint8_t *dst, struct hmac_sha1_ctx *ctx) {
+       u_int8_t k_opad[SHA1_BLOCK_SIZE];
+       int i;
+
+       sha1_final(dst, &ctx->ctx);
+
+       memset(k_opad, 0, sizeof k_opad);
+       memcpy(k_opad, ctx->key, ctx->key_len);
+       for (i = 0; i < SHA1_BLOCK_SIZE; i += 1)
+               k_opad[i] ^= 0x5c;
+
+       sha1_reset(&ctx->ctx);
+       sha1_update(&ctx->ctx, k_opad, SHA1_BLOCK_SIZE);
+       sha1_update(&ctx->ctx, dst, SHA1_DIGEST_LENGTH);
+       sha1_final(dst, &ctx->ctx);
+
+       /*
+        * Seen in OpenBSD source, presumably to prevent key leakage through
+        * uninitialized memory.
+        */
+       memset(k_opad, 0, sizeof k_opad);
+}
diff --git a/usual/hmac.h b/usual/hmac.h
new file mode 100644 (file)
index 0000000..61568f2
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * HMAC implementation based on OpenBSD
+ *
+ * Copyright (c) 2012  Daniel Farina
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _USUAL_HMAC_H_
+#define _USUAL_HMAC_H_
+
+#include <usual/base.h>
+
+#include <usual/sha1.h>
+
+/* HMAC-SHA1 Context */
+struct hmac_sha1_ctx {
+       struct  sha1_ctx        ctx;
+       uint8_t                         key[SHA1_BLOCK_SIZE];
+       size_t                          key_len;
+};
+
+void hmac_sha1_reset(struct hmac_sha1_ctx *ctx,
+                               const u_int8_t *key, u_int key_len);
+void hmac_sha1_update(struct hmac_sha1_ctx *ctx,
+                                         const void *data, unsigned int len);
+void hmac_sha1_final(uint8_t *dst, struct hmac_sha1_ctx *ctx);
+
+#endif /* _USUAL_HMAC_H_ */