summaryrefslogtreecommitdiffstats
path: root/src/lib-otp/otp-hash.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-otp/otp-hash.c')
-rw-r--r--src/lib-otp/otp-hash.c165
1 files changed, 165 insertions, 0 deletions
diff --git a/src/lib-otp/otp-hash.c b/src/lib-otp/otp-hash.c
new file mode 100644
index 0000000..c1b6f72
--- /dev/null
+++ b/src/lib-otp/otp-hash.c
@@ -0,0 +1,165 @@
+/*
+ * OTP hash generation.
+ *
+ * Copyright (c) 2006 Andrey Panin <pazke@donpac.ru>
+ *
+ * This software is released under the MIT license.
+ */
+
+#include "lib.h"
+#include "md4.h"
+#include "md5.h"
+#include "sha1.h"
+
+#include "otp.h"
+
+struct digest {
+ const char *name;
+ void (*init)(void *ctx);
+ void (*update)(void *ctx, const void *data, const size_t size);
+ void (*final)(void *ctx, void *res);
+ void (*otp_final)(void *ctx, void *res);
+};
+
+struct digest_context {
+ const struct digest *digest;
+ union {
+ struct md4_context md4_ctx;
+ struct md5_context md5_ctx;
+ struct sha1_ctxt sha1_ctx;
+ } ctx;
+};
+
+static void md4_fold(struct md4_context *ctx, void *res)
+{
+ uint32_t tmp[4], *p = res;
+
+ md4_final(ctx, (unsigned char *) tmp);
+
+ *p++ = tmp[0] ^ tmp[2];
+ *p = tmp[1] ^ tmp[3];
+}
+
+static void md5_fold(struct md5_context *ctx, void *res)
+{
+ uint32_t tmp[4], *p = res;
+
+ md5_final(ctx, (unsigned char *) tmp);
+
+ *p++ = tmp[0] ^ tmp[2];
+ *p = tmp[1] ^ tmp[3];
+}
+
+/*
+ * Sometimes I simply can't look at code generated by gcc.
+ */
+static inline uint32_t swab_uint32(uint32_t val)
+{
+#if defined(__GNUC__) && defined(__i386__)
+ asm("xchgb %b0, %h0\n"
+ "rorl $16, %0\n"
+ "xchgb %b0, %h0\n"
+ :"=q" (val)
+ : "0" (val));
+#else
+ val = ((val & 0xff) << 24) | ((val & 0xff00) << 8) |
+ ((val & 0xff0000) >> 8) | ((val >> 24) & 0xff);
+#endif
+ return val;
+}
+
+static void sha1_fold(struct sha1_ctxt *ctx, void *res)
+{
+ uint32_t tmp[5], *p = res;
+
+ sha1_result(ctx, tmp);
+
+ *p++ = swab_uint32(tmp[0] ^ tmp[2] ^ tmp[4]);
+ *p = swab_uint32(tmp[1] ^ tmp[3]);
+}
+
+
+#define F_INIT(name) ((void (*)(void *)) (name))
+#define F_UPDATE(name) ((void (*)(void *, const void *, size_t)) (name))
+#define F_FINAL(name) ((void (*)(void *, void *)) (name))
+#define F_FOLD(name) ((void (*)(void *, void *)) (name))
+
+static const struct digest digests[] = {
+ { "md4", F_INIT(md4_init), F_UPDATE(md4_update), F_FINAL(md4_final), F_FOLD(md4_fold) },
+ { "md5", F_INIT(md5_init), F_UPDATE(md5_update), F_FINAL(md5_final), F_FOLD(md5_fold) },
+ { "sha1", F_INIT(sha1_init), F_UPDATE(sha1_loop), F_FINAL(sha1_result), F_FOLD(sha1_fold) },
+};
+
+#undef F_INIT
+#undef F_UPDATE
+#undef F_FINAL
+#undef F_FOLD
+
+const char *digest_name(unsigned int algo)
+{
+ i_assert(algo < N_ELEMENTS(digests));
+
+ return digests[algo].name;
+}
+
+int digest_find(const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; i < N_ELEMENTS(digests); i++)
+ if (strcmp(name, digests[i].name) == 0)
+ return i;
+
+ return -1;
+}
+
+void digest_init(struct digest_context *ctx, const unsigned int algo)
+{
+ i_assert(algo < N_ELEMENTS(digests));
+
+ ctx->digest = digests + algo;
+ ctx->digest->init(&ctx->ctx);
+}
+
+void digest_update(struct digest_context *ctx, const void *data,
+ const size_t size)
+{
+ ctx->digest->update(&ctx->ctx, data, size);
+}
+
+void digest_final(struct digest_context *ctx, unsigned char *result)
+{
+ ctx->digest->final(&ctx->ctx, result);
+}
+
+void digest_otp_final(struct digest_context *ctx, unsigned char *result)
+{
+ ctx->digest->otp_final(&ctx->ctx, result);
+}
+
+void otp_hash(unsigned int algo, const char *seed, const char *passphrase,
+ unsigned int step, unsigned char *result)
+{
+ struct digest_context ctx;
+
+ digest_init(&ctx, algo);
+ digest_update(&ctx, seed, strlen(seed));
+ digest_update(&ctx, passphrase, strlen(passphrase));
+ digest_otp_final(&ctx, result);
+
+ for (unsigned int i = 0; i < step; i++) {
+ digest_init(&ctx, algo);
+ digest_update(&ctx, result, OTP_HASH_SIZE);
+ digest_otp_final(&ctx, result);
+ }
+}
+
+void otp_next_hash(unsigned int algo, const unsigned char *prev,
+ unsigned char *result)
+{
+ struct digest_context ctx;
+
+ digest_init(&ctx, algo);
+ digest_update(&ctx, prev, OTP_HASH_SIZE);
+ digest_otp_final(&ctx, result);
+}