crypto/digest: common framework for digests
authorMarko Kreen <markokr@gmail.com>
Mon, 29 Oct 2012 06:33:35 +0000 (08:33 +0200)
committerMarko Kreen <markokr@gmail.com>
Sat, 22 Dec 2012 22:40:44 +0000 (00:40 +0200)
The digests will be easier to use (and test) if they share
common wrapper API.

Makefile
doc/mainpage.dox
test/test_crypto.c
usual/crypto/digest.c [new file with mode: 0644]
usual/crypto/digest.h [new file with mode: 0644]
usual/crypto/hmac.c
usual/crypto/hmac.h
usual/crypto/md5.c
usual/crypto/sha1.c

index 9efdbc42d6a58b5f0ad0d30c98bd44eb1cdfabf5..10765252f420978bfd208474e02751980280332f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -18,6 +18,7 @@ libusual_la_SOURCES = usual/config.h.in \
        usual/cbtree.h usual/cbtree.c \
        usual/cfparser.h usual/cfparser.c \
        usual/crc32.h usual/crc32.c \
+       usual/crypto/digest.h usual/crypto/digest.c \
        usual/crypto/hmac.h usual/crypto/hmac.c \
        usual/crypto/md5.h usual/crypto/md5.c \
        usual/crypto/sha1.h usual/crypto/sha1.c \
index d2eac13bb1b6d0e8b0145de374ffd1316c161972..f12b3f460307dc59dde2054e9f8c777db319418a 100644 (file)
@@ -63,7 +63,8 @@
  * <tr><td>  <usual/pgutil.h>        </td><td>  PostgreSQL data formats   </td></tr>
  * <tr><td>  <usual/utf8.h>          </td><td>  Low-level UTF8 handling   </td></tr>
  * <tr><th colspan=2>  Cryptography  </th></tr>
- * <tr><td>  <usual/crypto/hmac.h>   </td><td>  HMAC-SHA1 </td></tr>
+ * <tr><td>  <usual/crypto/digest.h> </td><td>  Common API for cryptographic message digests </td></tr>
+ * <tr><td>  <usual/crypto/hmac.h>   </td><td>  HMAC with digest </td></tr>
  * <tr><td>  <usual/crypto/md5.h>    </td><td>  MD5 hash  </td></tr>
  * <tr><td>  <usual/crypto/sha1.h>   </td><td>  SHA1 hash  </td></tr>
  * <tr><th colspan=2>  Memory Allocation  </th></tr>
index a3517d22558ccf7fe5b22c3e42fe82bf22cfcb71..789dbd63d94dbe8fe81578d463e348d3ef49dda0 100644 (file)
@@ -9,6 +9,8 @@
 #include <usual/crypto/hmac.h>
 #include <usual/crypto/md5.h>
 #include <usual/crypto/sha1.h>
+#include <usual/crypto/digest.h>
+#include <usual/cxalloc.h>
 
 static const char *mkhex(const uint8_t *src, int len)
 {
@@ -23,33 +25,44 @@ static const char *mkhex(const uint8_t *src, int len)
        return buf;
 }
 
-/*
- * MD5
- */
-
-static const char *run_md5(const char *str)
+static const char *run_hash(const char *str, const struct DigestInfo *impl)
 {
-       struct md5_ctx ctx[1];
-       uint8_t res[MD5_DIGEST_LENGTH];
-       uint8_t res2[MD5_DIGEST_LENGTH];
+       struct DigestContext *ctx;
+       uint8_t res[512];
+       uint8_t res2[512];
        int i, len = strlen(str), step;
+       int reslen;
+
+       ctx = digest_new(impl, USUAL_ALLOC);
+       if (!ctx)
+               return "NOMEM";
+       reslen = digest_result_len(ctx);
 
-       md5_reset(ctx);
-       md5_update(ctx, str, len);
-       md5_final(ctx, res);
+       digest_update(ctx, str, len);
+       digest_final(ctx, res);
 
-       md5_reset(ctx);
+       digest_reset(ctx);
        step = 3;
        for (i = 0; i < len; i += step)
-               md5_update(ctx, str+i,
-                          (i + step <= len)
-                          ? (step) : (len - i));
-       md5_final(ctx, res2);
+               digest_update(ctx, str+i,
+                             (i + step <= len) ? (step) : (len - i));
+       digest_final(ctx, res2);
 
-       if (memcmp(res, res2, MD5_DIGEST_LENGTH) != 0)
+       digest_free(ctx);
+
+       if (memcmp(res, res2, reslen) != 0)
                return "FAIL";
        
-       return mkhex(res, MD5_DIGEST_LENGTH);
+       return mkhex(res, reslen);
+}
+
+/*
+ * MD5
+ */
+
+static const char *run_md5(const char *str)
+{
+       return run_hash(str, digest_MD5());
 }
 
 static void test_md5(void *ptr)
@@ -70,27 +83,7 @@ end:;
 
 static const char *run_sha1(const char *str)
 {
-       struct sha1_ctx ctx[1];
-       uint8_t res[SHA1_DIGEST_LENGTH];
-       uint8_t res2[SHA1_DIGEST_LENGTH];
-       int i, len = strlen(str), step;
-
-       sha1_reset(ctx);
-       sha1_update(ctx, str, len);
-       sha1_final(ctx, res);
-
-       sha1_reset(ctx);
-       step = 3;
-       for (i = 0; i < len; i += step)
-               sha1_update(ctx, str+i,
-                           (i + step <= len)
-                           ? (step) : (len - i));
-       sha1_final(ctx, res2);
-
-       if (memcmp(res, res2, SHA1_DIGEST_LENGTH) != 0)
-               return "FAIL";
-
-       return mkhex(res, SHA1_DIGEST_LENGTH);
+       return run_hash(str, digest_SHA1());
 }
 
 static void test_sha1(void *ptr)
@@ -110,31 +103,27 @@ end:;
  * HMAC
  */
 
-static const char *run_hmac_sha1(const char *key, const char *str)
+static const char *run_hmac(const char *key, const char *str, const struct DigestInfo *impl)
 {
-       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;
+       struct HMAC *ctx;
+       uint8_t res[512];
+       int len = strlen(str);
+       int reslen;
 
-       /* Compute HMAC all at once */
-       hmac_sha1_reset(ctx, (void *) key, strlen(key));
-       hmac_sha1_update(ctx, str, len);
-       hmac_sha1_final(ctx, monolithic_res);
+       ctx = hmac_new(impl, key, strlen(key), USUAL_ALLOC);
+       if (!ctx)
+               return "NOMEM";
+       reslen = hmac_result_len(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(ctx, incremental_res);
+       hmac_update(ctx, str, len);
+       hmac_final(ctx, res);
 
-       if (memcmp(monolithic_res, incremental_res, SHA1_DIGEST_LENGTH) != 0)
-               return "FAIL";
+       return mkhex(res, reslen);
+}
 
-       return mkhex(monolithic_res, SHA1_DIGEST_LENGTH);
+static const char *run_hmac_sha1(const char *key, const char *str)
+{
+       return run_hmac(key, str, digest_SHA1());
 }
 
 static void test_hmac(void *ptr)
diff --git a/usual/crypto/digest.c b/usual/crypto/digest.c
new file mode 100644 (file)
index 0000000..4c1ce17
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Common API for cryptographic digests.
+ *
+ * Copyright (c) 2012  Marko Kreen
+ *
+ * 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 <usual/crypto/digest.h>
+
+#include <string.h>
+
+struct DigestContext {
+       const struct DigestInfo *impl;
+       CxMem *cx;
+       uint64_t state[1];
+};
+
+struct DigestContext *digest_new(const struct DigestInfo *impl, CxMem *cx)
+{
+       struct DigestContext *ctx;
+       unsigned alloc;
+       
+       alloc = offsetof(struct DigestContext, state) + impl->state_len;
+       ctx = cx_alloc(cx, alloc);
+       if (!ctx)
+               return NULL;
+
+       ctx->impl = impl;
+       ctx->cx = cx;
+       impl->init(ctx->state);
+       return ctx;
+}
+
+void digest_update(struct DigestContext *ctx, const void *data, size_t len)
+{
+       ctx->impl->update(ctx->state, data, len);
+}
+
+void digest_final(struct DigestContext *ctx, uint8_t *res)
+{
+       ctx->impl->final(ctx->state, res);
+}
+
+void digest_reset(struct DigestContext *ctx)
+{
+       ctx->impl->init(ctx->state);
+}
+
+void digest_free(struct DigestContext *ctx)
+{
+       CxMem *cx =  ctx->cx;
+       unsigned alloc = offsetof(struct DigestContext, state) + ctx->impl->state_len;
+
+       memset(ctx, 0, alloc);
+       cx_free(cx, ctx);
+}
+
+unsigned digest_block_len(struct DigestContext *ctx)
+{
+       return ctx->impl->block_len;
+}
+
+unsigned digest_result_len(struct DigestContext *ctx)
+{
+       return ctx->impl->result_len;
+}
+
diff --git a/usual/crypto/digest.h b/usual/crypto/digest.h
new file mode 100644 (file)
index 0000000..d13cf2b
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Common API for cryptographic digests.
+ *
+ * Copyright (c) 2012  Marko Kreen
+ *
+ * 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.
+ */
+
+/**
+ * @file
+ *
+ * Common API for cryptographic digests.
+ */
+
+#ifndef _USUAL_CRYPTO_DIGEST_H_
+#define _USUAL_CRYPTO_DIGEST_H_
+
+#include <usual/cxalloc.h>
+
+typedef void (DigestInitFunc)(void *ctx);
+typedef void (DigestUpdateFunc)(void *ctx, const void *, unsigned);
+typedef void (DigestFinalFunc)(void *ctx, uint8_t *);
+
+/**
+ * Algoright info.
+ */
+struct DigestInfo {
+       DigestInitFunc *init;
+       DigestUpdateFunc *update;
+       DigestFinalFunc *final;
+       short state_len;
+       short result_len;
+       short block_len;
+};
+
+/**
+ * Algoright instance.
+ */
+struct DigestContext;
+
+/**
+ * Allocate and initialize new algorithm instance.
+ */
+struct DigestContext *digest_new(const struct DigestInfo *impl, CxMem *cx);
+
+/** Hash more data */
+void digest_update(struct DigestContext *ctx, const void *data, size_t len);
+
+/**
+ * Get final result.
+ *
+ * To re-use same instance, digest_reset() must be called first.
+ */
+void digest_final(struct DigestContext *ctx, uint8_t *res);
+
+/**
+ * Prepares instance for new data.
+ */
+void digest_reset(struct DigestContext *ctx);
+
+/**
+ * Free instance.
+ */
+void digest_free(struct DigestContext *ctx);
+
+/**
+ * Hash function block length in bytes.
+ */
+unsigned digest_block_len(struct DigestContext *ctx);
+
+/**
+ * Hash function result length in bytes.
+ */
+unsigned digest_result_len(struct DigestContext *ctx);
+
+/*
+ * Declare algorithm info's here instead per-also headers
+ * to avoid unnecessary dependencies.
+ */
+
+/** MD5 message digest */
+const struct DigestInfo *digest_MD5(void);
+
+/** SHA1 message digest */
+const struct DigestInfo *digest_SHA1(void);
+
+#endif
+
index adad90bd29db73e35352d8151e5450786415ba5f..587ea4b23a3b67804d5dafb7a8ce1f54c5f95c8d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * HMAC-SHA1 implementation based on OpenBSD hmac.c
+ * HMAC implementation based on OpenBSD hmac.c
  *
  * Copyright (c) 2012 Daniel Farina
  *
  */
 
 #include <usual/crypto/hmac.h>
-#include <usual/crypto/sha1.h>
 
 #include <string.h>
 
-/* Clean HMAC-SHA1 state */
-void
-hmac_sha1_reset(struct hmac_sha1_ctx *ctx,
-               const uint8_t *key, unsigned int key_len)
+
+struct HMAC {
+       struct DigestContext *hash;
+       CxMem *cx;
+       uint8_t *ipad;
+       uint8_t *opad;
+};
+
+struct HMAC *hmac_new(const struct DigestInfo *impl,
+                     const void *key, unsigned int key_len,
+                     CxMem *cx)
 {
-       uint8_t k_ipad[SHA1_BLOCK_SIZE];
-       int i;
-
-       if (key_len > SHA1_BLOCK_SIZE) {
-               sha1_reset(&ctx->ctx);
-               sha1_update(&ctx->ctx, key, key_len);
-               sha1_final(&ctx->ctx, ctx->key);
-               ctx->key_len = SHA1_DIGEST_LENGTH;
+       struct DigestContext *hash;
+       struct HMAC *hmac;
+       unsigned bs = impl->block_len;
+       unsigned i;
+
+       /* load hash */
+       hash = digest_new(impl, cx);
+       if (!hash)
+               return NULL;
+
+       /* struct setup */
+       hmac = cx_alloc0(cx, sizeof(struct HMAC) + 2*bs);
+       if (!hmac) {
+               digest_free(hash);
+               return NULL;
+       }
+       hmac->hash = hash;
+       hmac->cx = cx;
+       hmac->ipad = (uint8_t *)(hmac + 1);
+       hmac->opad = hmac->ipad + bs;
+
+       /* copy key to pads */
+       if (key_len > bs) {
+               digest_update(hash, key, key_len);
+               digest_final(hash, hmac->ipad);
+               digest_reset(hash);
+               memcpy(hmac->opad, hmac->ipad, digest_result_len(hash));
        } else {
-               memcpy(ctx->key, key, key_len);
-               ctx->key_len = key_len;
+               memcpy(hmac->ipad, key, key_len);
+               memcpy(hmac->opad, key, key_len);
        }
 
-       memset(k_ipad, 0, sizeof k_ipad);
-       memcpy(k_ipad, ctx->key, ctx->key_len);
+       /* calculate pads */
+       for (i = 0; i < bs; i++) {
+               hmac->ipad[i] ^= 0x36;
+               hmac->opad[i] ^= 0x5c;
+       }
 
-       for (i = 0; i < SHA1_BLOCK_SIZE; i += 1)
-               k_ipad[i] ^= 0x36;
+       /* prepare for user data */
+       digest_update(hmac->hash, hmac->ipad, bs);
+       return hmac;
+}
 
-       sha1_reset(&ctx->ctx);
-       sha1_update(&ctx->ctx, k_ipad, SHA1_BLOCK_SIZE);
+/* Clean HMAC state */
+void hmac_reset(struct HMAC *ctx)
+{
+       unsigned bs = digest_block_len(ctx->hash);
 
-       /*
-        * Seen in OpenBSD source, presumably to prevent key leakage through
-        * uninitialized memory.
-        */
-       memset(k_ipad, 0, sizeof k_ipad);
+       digest_reset(ctx->hash);
+       digest_update(ctx->hash, ctx->ipad, bs);
 }
 
 
-/* Update HMAC-SHA1 state with more data */
-void
-hmac_sha1_update(struct hmac_sha1_ctx *ctx,
-                const void *data, unsigned int len)
+/* Update HMAC state with more data */
+void hmac_update(struct HMAC *ctx, const void *data, unsigned int len)
 {
-       sha1_update(&ctx->ctx, data, len);
+       digest_update(ctx->hash, data, len);
 }
 
 
-/* Get final HMAC-SHA1 result */
-void hmac_sha1_final(struct hmac_sha1_ctx *ctx, uint8_t *dst)
+/* Get final HMAC result */
+void hmac_final(struct HMAC *ctx, uint8_t *dst)
 {
-       uint8_t k_opad[SHA1_BLOCK_SIZE];
-       int i;
-
-       sha1_final(&ctx->ctx, dst);
-
-       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(&ctx->ctx, dst);
-
-       /*
-        * Seen in OpenBSD source, presumably to prevent key leakage through
-        * uninitialized memory.
-        */
-       memset(k_opad, 0, sizeof k_opad);
+       unsigned bs = digest_block_len(ctx->hash);
+       unsigned rs = digest_result_len(ctx->hash);
+
+       digest_final(ctx->hash, dst);
+
+       digest_reset(ctx->hash);
+       digest_update(ctx->hash, ctx->opad, bs);
+       digest_update(ctx->hash, dst, rs);
+       digest_final(ctx->hash, dst);
 }
+
+unsigned hmac_block_len(struct HMAC *ctx)
+{
+       return digest_block_len(ctx->hash);
+}
+
+unsigned hmac_result_len(struct HMAC *ctx)
+{
+       return digest_result_len(ctx->hash);
+}
+
index 000aeade1fb42bf845f9422d5cd1a6c445b9d268..d202a5b109051b8a44f32ea0321486fa7b893243 100644 (file)
 #ifndef _USUAL_CRYPTO_HMAC_H_
 #define _USUAL_CRYPTO_HMAC_H_
 
-#include <usual/base.h>
+#include <usual/crypto/digest.h>
 
-#include <usual/crypto/sha1.h>
+/** HMAC Context */
+struct HMAC;
 
-/** HMAC-SHA1 Context */
-struct hmac_sha1_ctx {
-       struct  sha1_ctx        ctx;
-       uint8_t                 key[SHA1_BLOCK_SIZE];
-       unsigned int            key_len;
-};
+/** Create context with key */
+struct HMAC *hmac_new(const struct DigestInfo *impl,
+                     const void *key, unsigned int key_len,
+                     CxMem *cx);
 
-/** Initialize context with new key */
-void hmac_sha1_reset(struct hmac_sha1_ctx *ctx, const uint8_t *key, unsigned int key_len);
+/** Initialize context */
+void hmac_reset(struct HMAC *ctx);
 
 /** Hash more data */
-void hmac_sha1_update(struct hmac_sha1_ctx *ctx, const void *data, unsigned int len);
+void hmac_update(struct HMAC *ctx, const void *data, unsigned int len);
 
 /** Get final result */
-void hmac_sha1_final(struct hmac_sha1_ctx *ctx, uint8_t *dst);
+void hmac_final(struct HMAC *ctx, uint8_t *dst);
+
+unsigned hmac_block_len(struct HMAC *ctx);
+unsigned hmac_result_len(struct HMAC *ctx);
 
 #endif /* _USUAL_HMAC_H_ */
index 4a1b60bd2a64c3daad9802818b77121b2eaca75c..ea12a3ac9cafda22f9b33d6e20cda4ba0eef2951 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include <usual/crypto/md5.h>
+#include <usual/crypto/digest.h>
 
 #include <usual/endian.h>
 #include <usual/bits.h>
@@ -193,3 +194,21 @@ void md5_final(struct md5_ctx *ctx, uint8_t *dst)
        le32enc(dst + 12, ctx->d);
 }
 
+/*
+ * DigestInfo
+ */
+
+static const struct DigestInfo md5 = {
+       (DigestInitFunc *)md5_reset,
+       (DigestUpdateFunc *)md5_update,
+       (DigestFinalFunc *)md5_final,
+       sizeof(struct md5_ctx),
+       MD5_DIGEST_LENGTH,
+       MD5_BLOCK_LENGTH
+};
+
+const struct DigestInfo *digest_MD5(void)
+{
+       return &md5;
+}
+
index b0a60866dd136fb4d7e69f93d02285d753071a71..375f2d5e6401838b7c56dd92f33684059f764f7d 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <usual/crypto/sha1.h>
 
+#include <usual/crypto/digest.h>
 #include <usual/endian.h>
 #include <usual/bits.h>
 
@@ -143,3 +144,21 @@ void sha1_final(struct sha1_ctx *ctx, uint8_t *dst)
        be32enc(dst + 4*4, ctx->e);
 }
 
+/*
+ * DigestInfo
+ */
+
+static const struct DigestInfo sha1_info = {
+       (DigestInitFunc *)sha1_reset,
+       (DigestUpdateFunc *)sha1_update,
+       (DigestFinalFunc *)sha1_final,
+       sizeof(struct sha1_ctx),
+       SHA1_DIGEST_LENGTH,
+       SHA1_BLOCK_SIZE
+};
+
+const struct DigestInfo *digest_SHA1(void)
+{
+       return &sha1_info;
+}
+