5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2000 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
22 * Created: Sun Mar 9 00:09:18 1997
24 * This RNG is based on Secure Shell's random number generator.
26 /* XXX: Some operations block resulting slow initialization.
27 * XXX: I have some pending changes to make this better. */
29 #include "silcincludes.h"
32 /* #define SILC_RNG_DEBUG */
34 static unsigned int silc_rng_get_position(SilcRng rng);
35 static void silc_rng_stir_pool(SilcRng rng);
36 static void silc_rng_xor(SilcRng rng, unsigned int val, unsigned int pos);
37 static void silc_rng_add_noise(SilcRng rng, unsigned char *buffer,
39 static void silc_rng_exec_command(SilcRng rng, char *command);
40 static void silc_rng_get_hard_noise(SilcRng rng);
41 static void silc_rng_get_medium_noise(SilcRng rng);
42 static void silc_rng_get_soft_noise(SilcRng rng);
45 SILC SilcRng State context.
47 This object is used by the random number generator to provide
48 variable points where the actual random number is fetched from
49 the random pool. This provides that the data is not fetched always
50 from the same point of the pool. Short description of the fields
56 The index for the random pool buffer. Lowest and current
59 SilcRngStateContext *next
61 Pointer to the next state. If this is the last state this
62 will point to the first state thus providing circular list.
65 typedef struct SilcRngStateContext {
68 struct SilcRngStateContext *next;
72 SILC Random Number Generator object.
74 This object holds random pool which is used to generate the random
75 numbers used by various routines needing cryptographically strong
76 random numbers. Following short descriptions of the fields.
80 The random pool. This buffer holds the random data. This is
81 frequently stirred thus providing ever changing randomnes.
85 Key used in stirring the random pool. The pool is encrypted
86 with SHA1 hash function in CFB (Cipher Feedback) mode.
88 SilcSilcRngState state
90 State object that is used to get the next position for the
91 random pool. This position is used to fetch data from pool
92 or to save the data to the pool. The state changes everytime
97 Hash object (SHA1) used to make the CFB encryption to the
98 random pool. This is allocated when RNG object is allocated and
99 free'd when RNG object is free'd.
102 typedef struct SilcRngObjectStruct {
103 unsigned char pool[SILC_RNG_POOLSIZE];
104 unsigned char key[64];
109 /* Allocates new RNG object. */
111 SilcRng silc_rng_alloc()
115 SILC_LOG_DEBUG(("Allocating new RNG object"));
117 new = silc_calloc(1, sizeof(*new));
119 memset(new->pool, 0, sizeof(new->pool));
120 memset(new->key, 0, sizeof(new->key));
122 silc_hash_alloc("sha1", &new->sha1);
127 /* Free's RNG object. */
129 void silc_rng_free(SilcRng rng)
132 memset(rng->pool, 0, sizeof(rng->pool));
133 memset(rng->key, 0, sizeof(rng->key));
134 silc_free(rng->sha1);
139 /* Initializes random number generator by getting noise from environment.
140 The environmental noise is our so called seed. One should not call
141 this function more than once. */
143 void silc_rng_init(SilcRng rng)
146 SilcRngState first, next;
150 SILC_LOG_DEBUG(("Initializing RNG object"));
152 /* Initialize the states for the RNG. */
153 rng->state = silc_calloc(1, sizeof(*rng->state));
156 rng->state->next = NULL;
158 for (i = SILC_RNG_STATE_NUM - 1; i >= 1; i--) {
159 next = silc_calloc(1, sizeof(*rng->state));
161 (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM));
163 (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM)) + 8;
164 next->next = rng->state;
170 memset(rng->pool, 0, sizeof(rng->pool));
172 /* Get noise from various environmental sources */
173 silc_rng_get_soft_noise(rng);
174 silc_rng_get_medium_noise(rng);
175 silc_rng_get_hard_noise(rng);
178 /* This function gets 'soft' noise from environment. */
180 static void silc_rng_get_soft_noise(SilcRng rng)
184 silc_rng_xor(rng, clock(), 0);
185 silc_rng_xor(rng, getpid(), 1);
186 silc_rng_xor(rng, getpgid(getpid() << 8), 2);
187 silc_rng_xor(rng, getpgid(getpid() << 8), 3);
188 silc_rng_xor(rng, getgid(), 4);
189 silc_rng_xor(rng, getpgrp(), 5);
190 silc_rng_xor(rng, getsid(getpid() << 16), 6);
191 silc_rng_xor(rng, times(&ptime), 7);
192 silc_rng_xor(rng, ptime.tms_utime, 8);
193 silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), 9);
194 silc_rng_xor(rng, (ptime.tms_stime + ptime.tms_cutime), 10);
195 silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), 11);
196 silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_stime), 12);
197 silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_cstime), 13);
198 silc_rng_xor(rng, (ptime.tms_utime ^ ptime.tms_stime), 14);
199 silc_rng_xor(rng, (ptime.tms_stime ^ ptime.tms_cutime), 15);
200 silc_rng_xor(rng, (ptime.tms_cutime + ptime.tms_stime), 16);
201 silc_rng_xor(rng, (ptime.tms_stime << 8), 17);
202 silc_rng_xor(rng, clock() << 4, 18);
203 silc_rng_xor(rng, getpgid(getpid() << 8), 19);
204 silc_rng_xor(rng, getpgrp(), 20);
205 silc_rng_xor(rng, getsid(getpid() << 16), 21);
206 silc_rng_xor(rng, times(&ptime), 22);
207 silc_rng_xor(rng, ptime.tms_utime, 23);
208 silc_rng_xor(rng, getpgrp(), 24);
210 /* Stir random pool */
211 silc_rng_stir_pool(rng);
214 /* This function gets noise from different commands */
216 static void silc_rng_get_medium_noise(SilcRng rng)
218 silc_rng_exec_command(rng, "ps -lefaww 2> /dev/null");
219 silc_rng_exec_command(rng, "ls -afiln 2> /dev/null");
220 silc_rng_exec_command(rng, "ps -asww 2> /dev/null");
221 silc_rng_exec_command(rng, "ls -afiln /proc 2> /dev/null");
223 silc_rng_exec_command(rng, "ps -ef 2> /dev/null");
224 silc_rng_exec_command(rng, "ls -alin /dev 2> /dev/null");
228 /* This function gets 'hard' noise from environment. This tries to
229 get the noise from /dev/random if available. */
231 static void silc_rng_get_hard_noise(SilcRng rng)
236 /* Get noise from /dev/random if available */
237 fd = open("/dev/random", O_RDONLY);
241 fcntl(fd, F_SETFL, O_NONBLOCK);
243 for (i = 0; i < 8; i++) {
244 len = read(fd, buf, sizeof(buf));
247 silc_rng_add_noise(rng, buf, len);
252 memset(buf, 0, sizeof(buf));
255 /* Execs command and gets noise from its output */
257 static void silc_rng_exec_command(SilcRng rng, char *command)
265 fd = popen(command, "r");
269 /* Get data as much as we can get into the buffer */
270 for (i = 0; i < sizeof(buf); i++) {
282 /* Add the buffer into random pool */
283 silc_rng_add_noise(rng, buf, strlen(buf));
284 memset(buf, 0, sizeof(buf));
287 /* This function adds the contents of the buffer as noise into random
288 pool. After adding the noise the pool is stirred. */
290 static void silc_rng_add_noise(SilcRng rng, unsigned char *buffer,
295 pos = silc_rng_get_position(rng);
297 /* Add the buffer one by one into the pool */
298 for(i = 0; i < len; i++, buffer++) {
299 if(pos >= SILC_RNG_POOLSIZE)
301 rng->pool[pos++] ^= *buffer;
304 /* Stir random pool */
305 silc_rng_stir_pool(rng);
308 /* XOR's data into the pool */
310 static void silc_rng_xor(SilcRng rng, unsigned int val, unsigned int pos)
313 rng->pool[pos] ^= val + val;
316 /* This function stirs the random pool by encrypting buffer in CFB
317 (cipher feedback) mode with SHA1 algorithm. */
319 static void silc_rng_stir_pool(SilcRng rng)
325 memcpy(iv, &rng->pool[SILC_RNG_POOLSIZE - 256], sizeof(iv));
328 for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
329 rng->sha1->hash->transform(iv, rng->key);
330 iv[0] = rng->pool[i] ^= iv[0];
331 iv[1] = rng->pool[i + 1] ^= iv[1];
332 iv[2] = rng->pool[i + 2] ^= iv[2];
333 iv[3] = rng->pool[i + 3] ^= iv[3];
334 iv[4] = rng->pool[i + 4] ^= iv[4];
338 memcpy(rng->key, &rng->pool[silc_rng_get_position(rng)], sizeof(rng->key));
340 /* Second CFB pass */
341 for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
342 rng->sha1->hash->transform(iv, rng->key);
343 iv[0] = rng->pool[i] ^= iv[0];
344 iv[1] = rng->pool[i + 1] ^= iv[1];
345 iv[2] = rng->pool[i + 2] ^= iv[2];
346 iv[3] = rng->pool[i + 3] ^= iv[3];
347 iv[4] = rng->pool[i + 4] ^= iv[4];
350 memset(iv, 0, sizeof(iv));
353 /* Returns next position where data is fetched from the pool or
356 static unsigned int silc_rng_get_position(SilcRng rng)
361 next = rng->state->next;
363 pos = rng->state->pos++;
364 if ((next->low != 0 && pos >= next->low) || (pos >= SILC_RNG_POOLSIZE))
365 rng->state->pos = rng->state->low;
367 #ifdef SILC_RNG_DEBUG
368 fprintf(stderr, "state: %p: low: %d, pos: %d\n",
369 rng->state, rng->state->low, rng->state->pos);
377 /* returns random byte. Every two byte is from pools low or high state. */
379 unsigned char silc_rng_get_byte(SilcRng rng)
381 return rng->pool[silc_rng_get_position(rng)];
384 /* Returns 16 bit random number */
386 unsigned short silc_rng_get_rn16(SilcRng rng)
391 rn[0] = silc_rng_get_byte(rng);
392 rn[1] = silc_rng_get_byte(rng);
393 SILC_GET16_MSB(num, rn);
398 /* Returns 32 bit random number */
400 unsigned int silc_rng_get_rn32(SilcRng rng)
405 rn[0] = silc_rng_get_byte(rng);
406 rn[1] = silc_rng_get_byte(rng);
407 rn[2] = silc_rng_get_byte(rng);
408 rn[3] = silc_rng_get_byte(rng);
409 SILC_GET32_MSB(num, rn);
414 /* Returns random number string. Returned string is in HEX format. */
416 unsigned char *silc_rng_get_rn_string(SilcRng rng, unsigned int len)
419 unsigned char *string;
421 string = silc_calloc((len * 2 + 1), sizeof(unsigned char));
423 for (i = 0; i < len; i++)
424 sprintf(string + 2 * i, "%02x", silc_rng_get_byte(rng));
429 /* Returns random number binary data. */
431 unsigned char *silc_rng_get_rn_data(SilcRng rng, unsigned int len)
436 data = silc_calloc(len + 1, sizeof(*data));
438 for (i = 0; i < len; i++)
439 data[i] = silc_rng_get_byte(rng);
444 /* Global RNG. This is global RNG that application can initialize so
445 that any part of code anywhere can use RNG without having to allocate
446 new RNG object everytime. If this is not initialized then these routines
447 will fail. Note: currently in SILC applications always initialize this. */
449 SilcRng global_rng = NULL;
451 /* Initialize global RNG. If `rng' is provided it is set as the global
452 RNG object (it can be allocated by the application for example). */
454 int silc_rng_global_init(SilcRng rng)
459 global_rng = silc_rng_alloc();
464 /* Uninitialize global RNG */
466 int silc_rng_global_uninit()
469 silc_rng_free(global_rng);
476 /* These are analogous to the functions above. */
478 unsigned char silc_rng_global_get_byte()
480 return global_rng ? silc_rng_get_byte(global_rng) : 0;
483 unsigned short silc_rng_global_get_rn16()
485 return global_rng ? silc_rng_get_rn16(global_rng) : 0;
488 unsigned int silc_rng_global_get_rn32()
490 return global_rng ? silc_rng_get_rn32(global_rng) : 0;
493 unsigned char *silc_rng_global_get_rn_string(unsigned int len)
495 return global_rng ? silc_rng_get_rn_string(global_rng, len) : NULL;
498 unsigned char *silc_rng_global_get_rn_data(unsigned int len)
500 return global_rng ? silc_rng_get_rn_data(global_rng, len) : NULL;