Mercurial > dropbear
comparison fortuna.c @ 143:5d99163f7e32 libtomcrypt-orig
import of libtomcrypt 0.99
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Sun, 19 Dec 2004 11:34:45 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
15:6362d3854bb4 | 143:5d99163f7e32 |
---|---|
1 /* LibTomCrypt, modular cryptographic library -- Tom St Denis | |
2 * | |
3 * LibTomCrypt is a library that provides various cryptographic | |
4 * algorithms in a highly modular and flexible manner. | |
5 * | |
6 * The library is free for all purposes without any express | |
7 * guarantee it works. | |
8 * | |
9 * Tom St Denis, [email protected], http://libtomcrypt.org | |
10 */ | |
11 | |
12 /* Implementation of Fortuna by Tom St Denis | |
13 | |
14 We deviate slightly here for reasons of simplicity [and to fit in the API]. First all "sources" | |
15 in the AddEntropy function are fixed to 0. Second since no reliable timer is provided | |
16 we reseed automatically when len(pool0) >= 64 or every FORTUNA_WD calls to the read function */ | |
17 | |
18 #include "mycrypt.h" | |
19 | |
20 #ifdef FORTUNA | |
21 | |
22 /* requries SHA256 and AES */ | |
23 #if !(defined(RIJNDAEL) && defined(SHA256)) | |
24 #error FORTUNA requires SHA256 and RIJNDAEL (AES) | |
25 #endif | |
26 | |
27 #ifndef FORTUNA_POOLS | |
28 #warning FORTUNA_POOLS was not previously defined (old headers?) | |
29 #define FORTUNA_POOLS 32 | |
30 #endif | |
31 | |
32 #if FORTUNA_POOLS < 4 || FORTUNA_POOLS > 32 | |
33 #error FORTUNA_POOLS must be in [4..32] | |
34 #endif | |
35 | |
36 const struct _prng_descriptor fortuna_desc = { | |
37 "fortuna", 1024, | |
38 &fortuna_start, | |
39 &fortuna_add_entropy, | |
40 &fortuna_ready, | |
41 &fortuna_read, | |
42 &fortuna_done, | |
43 &fortuna_export, | |
44 &fortuna_import, | |
45 &fortuna_test | |
46 }; | |
47 | |
48 /* update the IV */ | |
49 static void fortuna_update_iv(prng_state *prng) | |
50 { | |
51 int x; | |
52 unsigned char *IV; | |
53 /* update IV */ | |
54 IV = prng->fortuna.IV; | |
55 for (x = 0; x < 16; x++) { | |
56 IV[x] = (IV[x] + 1) & 255; | |
57 if (IV[x] != 0) break; | |
58 } | |
59 } | |
60 | |
61 /* reseed the PRNG */ | |
62 static int fortuna_reseed(prng_state *prng) | |
63 { | |
64 unsigned char tmp[MAXBLOCKSIZE]; | |
65 hash_state md; | |
66 int err, x; | |
67 | |
68 ++prng->fortuna.reset_cnt; | |
69 | |
70 /* new K == SHA256(K || s) where s == SHA256(P0) || SHA256(P1) ... */ | |
71 sha256_init(&md); | |
72 if ((err = sha256_process(&md, prng->fortuna.K, 32)) != CRYPT_OK) { | |
73 return err; | |
74 } | |
75 | |
76 for (x = 0; x < FORTUNA_POOLS; x++) { | |
77 if (x == 0 || ((prng->fortuna.reset_cnt >> (x-1)) & 1) == 0) { | |
78 /* terminate this hash */ | |
79 if ((err = sha256_done(&prng->fortuna.pool[x], tmp)) != CRYPT_OK) { | |
80 return err; | |
81 } | |
82 /* add it to the string */ | |
83 if ((err = sha256_process(&md, tmp, 32)) != CRYPT_OK) { | |
84 return err; | |
85 } | |
86 /* reset this pool */ | |
87 sha256_init(&prng->fortuna.pool[x]); | |
88 } else { | |
89 break; | |
90 } | |
91 } | |
92 | |
93 /* finish key */ | |
94 if ((err = sha256_done(&md, prng->fortuna.K)) != CRYPT_OK) { | |
95 return err; | |
96 } | |
97 if ((err = rijndael_setup(prng->fortuna.K, 32, 0, &prng->fortuna.skey)) != CRYPT_OK) { | |
98 return err; | |
99 } | |
100 fortuna_update_iv(prng); | |
101 | |
102 /* reset pool len */ | |
103 prng->fortuna.pool0_len = 0; | |
104 prng->fortuna.wd = 0; | |
105 | |
106 | |
107 #ifdef CLEAN_STACK | |
108 zeromem(&md, sizeof(md)); | |
109 zeromem(tmp, sizeof(tmp)); | |
110 #endif | |
111 | |
112 return CRYPT_OK; | |
113 } | |
114 | |
115 int fortuna_start(prng_state *prng) | |
116 { | |
117 int err, x; | |
118 | |
119 _ARGCHK(prng != NULL); | |
120 | |
121 /* initialize the pools */ | |
122 for (x = 0; x < FORTUNA_POOLS; x++) { | |
123 sha256_init(&prng->fortuna.pool[x]); | |
124 } | |
125 prng->fortuna.pool_idx = prng->fortuna.pool0_len = prng->fortuna.reset_cnt = | |
126 prng->fortuna.wd = 0; | |
127 | |
128 /* reset bufs */ | |
129 zeromem(prng->fortuna.K, 32); | |
130 if ((err = rijndael_setup(prng->fortuna.K, 32, 0, &prng->fortuna.skey)) != CRYPT_OK) { | |
131 return err; | |
132 } | |
133 zeromem(prng->fortuna.IV, 16); | |
134 | |
135 return CRYPT_OK; | |
136 } | |
137 | |
138 int fortuna_add_entropy(const unsigned char *buf, unsigned long len, prng_state *prng) | |
139 { | |
140 unsigned char tmp[2]; | |
141 int err; | |
142 | |
143 _ARGCHK(buf != NULL); | |
144 _ARGCHK(prng != NULL); | |
145 | |
146 /* ensure len <= 32 */ | |
147 if (len > 32) { | |
148 return CRYPT_INVALID_ARG; | |
149 } | |
150 | |
151 /* add s || length(buf) || buf to pool[pool_idx] */ | |
152 tmp[0] = 0; | |
153 tmp[1] = len; | |
154 if ((err = sha256_process(&prng->fortuna.pool[prng->fortuna.pool_idx], tmp, 2)) != CRYPT_OK) { | |
155 return err; | |
156 } | |
157 if ((err = sha256_process(&prng->fortuna.pool[prng->fortuna.pool_idx], buf, len)) != CRYPT_OK) { | |
158 return err; | |
159 } | |
160 if (prng->fortuna.pool_idx == 0) { | |
161 prng->fortuna.pool0_len += len; | |
162 } | |
163 if (++(prng->fortuna.pool_idx) == FORTUNA_POOLS) { | |
164 prng->fortuna.pool_idx = 0; | |
165 } | |
166 | |
167 return CRYPT_OK; | |
168 } | |
169 | |
170 int fortuna_ready(prng_state *prng) | |
171 { | |
172 return fortuna_reseed(prng); | |
173 } | |
174 | |
175 unsigned long fortuna_read(unsigned char *dst, unsigned long len, prng_state *prng) | |
176 { | |
177 unsigned char tmp[16]; | |
178 int err; | |
179 unsigned long tlen; | |
180 | |
181 _ARGCHK(dst != NULL); | |
182 _ARGCHK(prng != NULL); | |
183 | |
184 /* do we have to reseed? */ | |
185 if (++prng->fortuna.wd == FORTUNA_WD || prng->fortuna.pool0_len >= 64) { | |
186 if ((err = fortuna_reseed(prng)) != CRYPT_OK) { | |
187 return 0; | |
188 } | |
189 } | |
190 | |
191 /* now generate the blocks required */ | |
192 tlen = len; | |
193 | |
194 /* handle whole blocks without the extra memcpy */ | |
195 while (len >= 16) { | |
196 /* encrypt the IV and store it */ | |
197 rijndael_ecb_encrypt(prng->fortuna.IV, dst, &prng->fortuna.skey); | |
198 dst += 16; | |
199 len -= 16; | |
200 fortuna_update_iv(prng); | |
201 } | |
202 | |
203 /* left over bytes? */ | |
204 if (len > 0) { | |
205 rijndael_ecb_encrypt(prng->fortuna.IV, tmp, &prng->fortuna.skey); | |
206 XMEMCPY(dst, tmp, len); | |
207 fortuna_update_iv(prng); | |
208 } | |
209 | |
210 /* generate new key */ | |
211 rijndael_ecb_encrypt(prng->fortuna.IV, prng->fortuna.K , &prng->fortuna.skey); fortuna_update_iv(prng); | |
212 rijndael_ecb_encrypt(prng->fortuna.IV, prng->fortuna.K+16, &prng->fortuna.skey); fortuna_update_iv(prng); | |
213 if ((err = rijndael_setup(prng->fortuna.K, 32, 0, &prng->fortuna.skey)) != CRYPT_OK) { | |
214 return 0; | |
215 } | |
216 | |
217 #ifdef CLEAN_STACK | |
218 zeromem(tmp, sizeof(tmp)); | |
219 #endif | |
220 return tlen; | |
221 } | |
222 | |
223 int fortuna_done(prng_state *prng) | |
224 { | |
225 int err, x; | |
226 unsigned char tmp[32]; | |
227 | |
228 _ARGCHK(prng != NULL); | |
229 | |
230 /* terminate all the hashes */ | |
231 for (x = 0; x < FORTUNA_POOLS; x++) { | |
232 if ((err = sha256_done(&(prng->fortuna.pool[x]), tmp)) != CRYPT_OK) { | |
233 return err; | |
234 } | |
235 } | |
236 /* call cipher done when we invent one ;-) */ | |
237 | |
238 #ifdef CLEAN_STACK | |
239 zeromem(tmp, sizeof(tmp)); | |
240 #endif | |
241 | |
242 return CRYPT_OK; | |
243 } | |
244 | |
245 int fortuna_export(unsigned char *out, unsigned long *outlen, prng_state *prng) | |
246 { | |
247 int x, err; | |
248 hash_state *md; | |
249 | |
250 _ARGCHK(out != NULL); | |
251 _ARGCHK(outlen != NULL); | |
252 _ARGCHK(prng != NULL); | |
253 | |
254 /* we'll write bytes for s&g's */ | |
255 if (*outlen < 32*FORTUNA_POOLS) { | |
256 return CRYPT_BUFFER_OVERFLOW; | |
257 } | |
258 | |
259 md = XMALLOC(sizeof(hash_state)); | |
260 if (md == NULL) { | |
261 return CRYPT_MEM; | |
262 } | |
263 | |
264 /* to emit the state we copy each pool, terminate it then hash it again so | |
265 * an attacker who sees the state can't determine the current state of the PRNG | |
266 */ | |
267 for (x = 0; x < FORTUNA_POOLS; x++) { | |
268 /* copy the PRNG */ | |
269 XMEMCPY(md, &(prng->fortuna.pool[x]), sizeof(*md)); | |
270 | |
271 /* terminate it */ | |
272 if ((err = sha256_done(md, out+x*32)) != CRYPT_OK) { | |
273 goto __ERR; | |
274 } | |
275 | |
276 /* now hash it */ | |
277 if ((err = sha256_init(md)) != CRYPT_OK) { | |
278 goto __ERR; | |
279 } | |
280 if ((err = sha256_process(md, out+x*32, 32)) != CRYPT_OK) { | |
281 goto __ERR; | |
282 } | |
283 if ((err = sha256_done(md, out+x*32)) != CRYPT_OK) { | |
284 goto __ERR; | |
285 } | |
286 } | |
287 *outlen = 32*FORTUNA_POOLS; | |
288 err = CRYPT_OK; | |
289 | |
290 __ERR: | |
291 #ifdef CLEAN_STACK | |
292 zeromem(md, sizeof(*md)); | |
293 #endif | |
294 XFREE(md); | |
295 return err; | |
296 } | |
297 | |
298 int fortuna_import(const unsigned char *in, unsigned long inlen, prng_state *prng) | |
299 { | |
300 int err, x; | |
301 | |
302 _ARGCHK(in != NULL); | |
303 _ARGCHK(prng != NULL); | |
304 | |
305 if (inlen != 32*FORTUNA_POOLS) { | |
306 return CRYPT_INVALID_ARG; | |
307 } | |
308 | |
309 if ((err = fortuna_start(prng)) != CRYPT_OK) { | |
310 return err; | |
311 } | |
312 for (x = 0; x < FORTUNA_POOLS; x++) { | |
313 if ((err = fortuna_add_entropy(in+x*32, 32, prng)) != CRYPT_OK) { | |
314 return err; | |
315 } | |
316 } | |
317 return err; | |
318 } | |
319 | |
320 int fortuna_test(void) | |
321 { | |
322 #ifndef LTC_TEST | |
323 return CRYPT_NOP; | |
324 #else | |
325 int err; | |
326 | |
327 if ((err = sha256_test()) != CRYPT_OK) { | |
328 return err; | |
329 } | |
330 return rijndael_test(); | |
331 #endif | |
332 } | |
333 | |
334 #endif | |
335 |