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 \
* <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>
#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)
{
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)
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)
* 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)
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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
+
/*
- * 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);
+}
+
#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_ */
*/
#include <usual/crypto/md5.h>
+#include <usual/crypto/digest.h>
#include <usual/endian.h>
#include <usual/bits.h>
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;
+}
+
#include <usual/crypto/sha1.h>
+#include <usual/crypto/digest.h>
#include <usual/endian.h>
#include <usual/bits.h>
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;
+}
+