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 \
#define str_check(a, b) tt_str_op(a, ==, b)
+#include <usual/hmac.h>
#include <usual/md5.h>
#include <usual/sha1.h>
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.
*/
struct testcase_t crypto_tests[] = {
{ "md5", test_md5 },
{ "sha1", test_sha1 },
+ { "hmac", test_hmac },
END_OF_TESTCASES
};
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */