otp-hash.c revision 4081894c16e50f37639d9826ebca4394b398c35e
/*
* OTP hash generaion.
*
* 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(name) ((void *) (name))
static const struct digest digests[] = {
{ "md4", F(md4_init), F(md4_update), F(md4_final), F(md4_fold) },
{ "md5", F(md5_init), F(md5_update), F(md5_final), F(md5_fold) },
{ "sha1", F(sha1_init), F(sha1_loop), F(sha1_result), F(sha1_fold) },
};
#define N_DIGESTS (int)(sizeof(digests) / sizeof(digests[0]))
#undef F
const char *digest_name(unsigned int algo)
{
i_assert(algo < N_DIGESTS);
return digests[algo].name;
}
int digest_find(const char *name)
{
int i;
for (i = 0; i < N_DIGESTS; i++)
if (strcmp(name, digests[i].name) == 0)
return i;
return -1;
}
int digest_init(struct digest_context *ctx, const unsigned int algo)
{
i_assert(algo < N_DIGESTS);
ctx->digest = digests + algo;
ctx->digest->init((void *) &ctx->ctx);
return 0;
}
void digest_update(struct digest_context *ctx, const void *data,
const size_t size)
{
ctx->digest->update((void *) &ctx->ctx, data, size);
}
void digest_final(struct digest_context *ctx, unsigned char *result)
{
ctx->digest->final((void *) &ctx->ctx, result);
}
void digest_otp_final(struct digest_context *ctx, unsigned char *result)
{
ctx->digest->otp_final((void *) &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);
while (step-- > 0) {
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);
}