summaryrefslogtreecommitdiffstats
path: root/src/lib-otp/otp-hash.c
blob: c1b6f72c2bcf1a3ba295c863060353a034329d9a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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);
}