5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2001 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 * The original RNG was based on Secure Shell's random number generator
25 * by Tatu Ylönen. This RNG has been rewritten twice since the creation.
28 #include "silcincludes.h"
31 /*#define SILC_RNG_DEBUG*/
33 /* Number of states to fetch data from pool. */
34 #define SILC_RNG_STATE_NUM 4
36 /* Byte size of the random data pool. */
37 #define SILC_RNG_POOLSIZE 1024
39 static uint32 silc_rng_get_position(SilcRng rng);
40 static void silc_rng_stir_pool(SilcRng rng);
41 static void silc_rng_xor(SilcRng rng, uint32 val, unsigned int pos);
42 static void silc_rng_exec_command(SilcRng rng, char *command);
43 static void silc_rng_get_hard_noise(SilcRng rng);
44 static void silc_rng_get_medium_noise(SilcRng rng);
45 static void silc_rng_get_soft_noise(SilcRng rng);
48 SILC SilcRng State context.
50 This object is used by the random number generator to provide
51 variable points where the actual random number is fetched from
52 the random pool. This provides that the data is not fetched always
53 from the same point of the pool. Short description of the fields
59 The index for the random pool buffer. Lowest and current
62 SilcRngStateContext *next
64 Pointer to the next state. If this is the last state this
65 will point to the first state thus providing circular list.
68 typedef struct SilcRngStateContext {
71 struct SilcRngStateContext *next;
75 SILC Random Number Generator object.
77 This object holds random pool which is used to generate the random
78 numbers used by various routines needing cryptographically strong
79 random numbers. Following short descriptions of the fields.
83 The random pool. This buffer holds the random data. This is
84 frequently stirred thus providing ever changing randomnes.
88 Key used in stirring the random pool. The pool is encrypted
89 with SHA1 hash function in CFB (Cipher Feedback) mode.
91 SilcSilcRngState state
93 State object that is used to get the next position for the
94 random pool. This position is used to fetch data from pool
95 or to save the data to the pool. The state changes everytime
100 Hash object (SHA1) used to make the CFB encryption to the
101 random pool. This is allocated when RNG object is allocated and
102 free'd when RNG object is free'd.
106 Threshhold to indicate when it is required to acquire more
107 noise from the environment. More soft noise is acquired after
108 64 bits of output and hard noise every 160 bits of output.
111 typedef struct SilcRngObjectStruct {
112 unsigned char pool[SILC_RNG_POOLSIZE];
113 unsigned char key[64];
119 /* Allocates new RNG object. */
121 SilcRng silc_rng_alloc()
125 SILC_LOG_DEBUG(("Allocating new RNG object"));
127 new = silc_calloc(1, sizeof(*new));
129 memset(new->pool, 0, sizeof(new->pool));
130 memset(new->key, 0, sizeof(new->key));
132 silc_hash_alloc("sha1", &new->sha1);
137 /* Free's RNG object. */
139 void silc_rng_free(SilcRng rng)
142 memset(rng->pool, 0, sizeof(rng->pool));
143 memset(rng->key, 0, sizeof(rng->key));
144 silc_free(rng->sha1);
149 /* Initializes random number generator by getting noise from environment.
150 The environmental noise is our so called seed. One should not call
151 this function more than once. */
153 void silc_rng_init(SilcRng rng)
156 SilcRngState first, next;
160 SILC_LOG_DEBUG(("Initializing RNG object"));
162 /* Initialize the states for the RNG. */
163 rng->state = silc_calloc(1, sizeof(*rng->state));
166 rng->state->next = NULL;
168 for (i = SILC_RNG_STATE_NUM - 1; i >= 1; i--) {
169 next = silc_calloc(1, sizeof(*rng->state));
171 (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM));
173 (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM)) + 8;
174 next->next = rng->state;
180 memset(rng->pool, 0, sizeof(rng->pool));
182 /* Get noise from various environmental sources */
183 silc_rng_get_soft_noise(rng);
184 silc_rng_get_medium_noise(rng);
185 silc_rng_get_hard_noise(rng);
186 silc_rng_get_soft_noise(rng);
189 /* This function gets 'soft' noise from environment. */
191 static void silc_rng_get_soft_noise(SilcRng rng)
196 pos = silc_rng_get_position(rng);
198 silc_rng_xor(rng, clock(), 0);
200 silc_rng_xor(rng, getpid(), 1);
202 silc_rng_xor(rng, getpgid(getpid() << 8), 2);
203 silc_rng_xor(rng, getpgid(getpid() << 8), 3);
205 silc_rng_xor(rng, getgid(), 4);
208 silc_rng_xor(rng, getpgrp(), 5);
211 silc_rng_xor(rng, getsid(getpid() << 16), 6);
213 silc_rng_xor(rng, times(&ptime), 7);
214 silc_rng_xor(rng, ptime.tms_utime, 8);
215 silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), pos++);
216 silc_rng_xor(rng, (ptime.tms_stime + ptime.tms_cutime), pos++);
217 silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), pos++);
218 silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_stime), pos++);
219 silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_cstime), pos++);
220 silc_rng_xor(rng, (ptime.tms_utime ^ ptime.tms_stime), pos++);
221 silc_rng_xor(rng, (ptime.tms_stime ^ ptime.tms_cutime), pos++);
222 silc_rng_xor(rng, (ptime.tms_cutime + ptime.tms_stime), pos++);
223 silc_rng_xor(rng, (ptime.tms_stime << 8), pos++);
224 silc_rng_xor(rng, clock() << 4, pos++);
226 silc_rng_xor(rng, getpgid(getpid() << 8), pos++);
229 silc_rng_xor(rng, getpgrp(), pos++);
232 silc_rng_xor(rng, getsid(getpid() << 16), pos++);
234 silc_rng_xor(rng, times(&ptime), pos++);
235 silc_rng_xor(rng, ptime.tms_utime, pos++);
237 silc_rng_xor(rng, getpgrp(), pos++);
240 #ifdef SILC_RNG_DEBUG
241 SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
244 /* Stir random pool */
245 silc_rng_stir_pool(rng);
248 /* This function gets noise from different commands */
250 static void silc_rng_get_medium_noise(SilcRng rng)
252 silc_rng_exec_command(rng, "ps -leaww 2> /dev/null");
253 silc_rng_exec_command(rng, "ls -afiln ~ 2> /dev/null");
254 silc_rng_exec_command(rng, "ls -afiln /proc 2> /dev/null");
255 silc_rng_exec_command(rng, "ps -axww 2> /dev/null");
257 #ifdef SILC_RNG_DEBUG
258 SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
262 /* This function gets 'hard' noise from environment. This tries to
263 get the noise from /dev/random if available. */
265 static void silc_rng_get_hard_noise(SilcRng rng)
270 /* Get noise from /dev/random if available */
271 fd = open("/dev/random", O_RDONLY);
275 fcntl(fd, F_SETFL, O_NONBLOCK);
277 for (i = 0; i < 2; i++) {
278 len = read(fd, buf, sizeof(buf));
281 silc_rng_add_noise(rng, buf, len);
284 #ifdef SILC_RNG_DEBUG
285 SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
290 memset(buf, 0, sizeof(buf));
293 /* Execs command and gets noise from its output */
295 static void silc_rng_exec_command(SilcRng rng, char *command)
303 fd = popen(command, "r");
307 /* Get data as much as we can get into the buffer */
308 for (i = 0; i < sizeof(buf); i++) {
320 /* Add the buffer into random pool */
321 silc_rng_add_noise(rng, buf, strlen(buf));
322 memset(buf, 0, sizeof(buf));
325 /* This function adds the contents of the buffer as noise into random
326 pool. After adding the noise the pool is stirred. */
328 void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, uint32 len)
332 pos = silc_rng_get_position(rng);
334 /* Add the buffer one by one into the pool */
335 for(i = 0; i < len; i++, buffer++) {
336 if(pos >= SILC_RNG_POOLSIZE)
338 rng->pool[pos++] ^= *buffer;
341 /* Stir random pool */
342 silc_rng_stir_pool(rng);
345 /* XOR's data into the pool */
347 static void silc_rng_xor(SilcRng rng, uint32 val, unsigned int pos)
350 rng->pool[pos] ^= val + val;
353 /* This function stirs the random pool by encrypting buffer in CFB
354 (cipher feedback) mode with SHA1 algorithm. */
356 static void silc_rng_stir_pool(SilcRng rng)
362 memcpy(iv, &rng->pool[SILC_RNG_POOLSIZE - 256], sizeof(iv));
365 for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
366 rng->sha1->hash->transform(iv, rng->key);
367 iv[0] = rng->pool[i] ^= iv[0];
368 iv[1] = rng->pool[i + 1] ^= iv[1];
369 iv[2] = rng->pool[i + 2] ^= iv[2];
370 iv[3] = rng->pool[i + 3] ^= iv[3];
371 iv[4] = rng->pool[i + 4] ^= iv[4];
375 memcpy(rng->key, &rng->pool[silc_rng_get_position(rng)], sizeof(rng->key));
377 /* Second CFB pass */
378 for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
379 rng->sha1->hash->transform(iv, rng->key);
380 iv[0] = rng->pool[i] ^= iv[0];
381 iv[1] = rng->pool[i + 1] ^= iv[1];
382 iv[2] = rng->pool[i + 2] ^= iv[2];
383 iv[3] = rng->pool[i + 3] ^= iv[3];
384 iv[4] = rng->pool[i + 4] ^= iv[4];
387 memset(iv, 0, sizeof(iv));
390 /* Returns next position where data is fetched from the pool or
393 static uint32 silc_rng_get_position(SilcRng rng)
398 next = rng->state->next;
400 pos = rng->state->pos++;
401 if ((next->low != 0 && pos >= next->low) || (pos >= SILC_RNG_POOLSIZE))
402 rng->state->pos = rng->state->low;
404 #ifdef SILC_RNG_DEBUG
405 fprintf(stderr, "state: %p: low: %lu, pos: %lu\n",
406 rng->state, rng->state->low, rng->state->pos);
414 /* Returns random byte. */
416 unsigned char silc_rng_get_byte(SilcRng rng)
420 /* Get more soft noise after 64 bits threshhold */
421 if (rng->threshhold >= 8)
422 silc_rng_get_soft_noise(rng);
424 /* Get hard noise after 160 bits threshhold, zero the threshhold. */
425 if (rng->threshhold >= 20) {
427 silc_rng_get_hard_noise(rng);
430 return rng->pool[silc_rng_get_position(rng)];
433 /* Returns 16 bit random number */
435 uint16 silc_rng_get_rn16(SilcRng rng)
440 rn[0] = silc_rng_get_byte(rng);
441 rn[1] = silc_rng_get_byte(rng);
442 SILC_GET16_MSB(num, rn);
447 /* Returns 32 bit random number */
449 uint32 silc_rng_get_rn32(SilcRng rng)
454 rn[0] = silc_rng_get_byte(rng);
455 rn[1] = silc_rng_get_byte(rng);
456 rn[2] = silc_rng_get_byte(rng);
457 rn[3] = silc_rng_get_byte(rng);
458 SILC_GET32_MSB(num, rn);
463 /* Returns random number string. Returned string is in HEX format. */
465 unsigned char *silc_rng_get_rn_string(SilcRng rng, uint32 len)
468 unsigned char *string;
470 string = silc_calloc((len * 2 + 1), sizeof(unsigned char));
472 for (i = 0; i < len; i++)
473 sprintf(string + 2 * i, "%02x", silc_rng_get_byte(rng));
478 /* Returns random number binary data. */
480 unsigned char *silc_rng_get_rn_data(SilcRng rng, uint32 len)
485 data = silc_calloc(len + 1, sizeof(*data));
487 for (i = 0; i < len; i++)
488 data[i] = silc_rng_get_byte(rng);
493 /* Global RNG. This is global RNG that application can initialize so
494 that any part of code anywhere can use RNG without having to allocate
495 new RNG object everytime. If this is not initialized then these routines
496 will fail. Note: currently in SILC applications always initialize this. */
498 SilcRng global_rng = NULL;
500 /* Initialize global RNG. If `rng' is provided it is set as the global
501 RNG object (it can be allocated by the application for example). */
503 int silc_rng_global_init(SilcRng rng)
508 global_rng = silc_rng_alloc();
513 /* Uninitialize global RNG */
515 int silc_rng_global_uninit()
518 silc_rng_free(global_rng);
525 /* These are analogous to the functions above. */
527 unsigned char silc_rng_global_get_byte()
529 return global_rng ? silc_rng_get_byte(global_rng) : 0;
532 uint16 silc_rng_global_get_rn16()
534 return global_rng ? silc_rng_get_rn16(global_rng) : 0;
537 uint32 silc_rng_global_get_rn32()
539 return global_rng ? silc_rng_get_rn32(global_rng) : 0;
542 unsigned char *silc_rng_global_get_rn_string(uint32 len)
544 return global_rng ? silc_rng_get_rn_string(global_rng, len) : NULL;
547 unsigned char *silc_rng_global_get_rn_data(uint32 len)
549 return global_rng ? silc_rng_get_rn_data(global_rng, len) : NULL;
552 void silc_rng_global_add_noise(unsigned char *buffer, uint32 len)
555 silc_rng_add_noise(global_rng, buffer, len);