5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2002 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; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
21 * Created: Sun Mar 9 00:09:18 1997
23 * The original RNG was based on Secure Shell's random number generator
24 * by Tatu Ylönen and was used as reference when programming this RNG.
25 * This RNG has been rewritten twice since the creation.
28 #include "silcincludes.h"
32 extern pid_t getsid (pid_t __pid);
36 extern pid_t getpgid (pid_t __pid);
41 /*#define SILC_RNG_DEBUG*/
43 /* Number of states to fetch data from pool. */
44 #define SILC_RNG_STATE_NUM 4
46 /* Byte size of the random data pool. */
47 #define SILC_RNG_POOLSIZE 1024
49 static SilcUInt32 silc_rng_get_position(SilcRng rng);
50 static void silc_rng_stir_pool(SilcRng rng);
51 static void silc_rng_xor(SilcRng rng, SilcUInt32 val, unsigned int pos);
52 static void silc_rng_exec_command(SilcRng rng, char *command);
53 static void silc_rng_get_hard_noise(SilcRng rng);
54 static void silc_rng_get_medium_noise(SilcRng rng);
55 static void silc_rng_get_soft_noise(SilcRng rng);
58 SILC SilcRng State context.
60 This object is used by the random number generator to provide
61 variable points where the actual random number is fetched from
62 the random pool. This provides that the data is not fetched always
63 from the same point of the pool. Short description of the fields
69 The index for the random pool buffer. Lowest and current
72 SilcRngStateContext *next
74 Pointer to the next state. If this is the last state this
75 will point to the first state thus providing circular list.
78 typedef struct SilcRngStateContext {
81 struct SilcRngStateContext *next;
85 SILC Random Number Generator object.
87 This object holds random pool which is used to generate the random
88 numbers used by various routines needing cryptographically strong
89 random numbers. Following short descriptions of the fields.
93 The random pool. This buffer holds the random data. This is
94 frequently stirred thus providing ever changing randomnes.
98 Key used in stirring the random pool. The pool is encrypted
99 with SHA1 hash function in CFB (Cipher Feedback) mode.
101 SilcSilcRngState state
103 State object that is used to get the next position for the
104 random pool. This position is used to fetch data from pool
105 or to save the data to the pool. The state changes everytime
110 Hash object (SHA1) used to make the CFB encryption to the
111 random pool. This is allocated when RNG object is allocated and
112 free'd when RNG object is free'd.
116 Threshold to indicate when it is required to acquire more
117 noise from the environment. More soft noise is acquired after
118 64 bits of output and hard noise every 160 bits of output.
121 struct SilcRngStruct {
122 unsigned char pool[SILC_RNG_POOLSIZE];
123 unsigned char key[64];
131 /* Allocates new RNG object. */
133 SilcRng silc_rng_alloc(void)
137 SILC_LOG_DEBUG(("Allocating new RNG object"));
139 new = silc_calloc(1, sizeof(*new));
140 new->fd_devurandom = -1;
142 memset(new->pool, 0, sizeof(new->pool));
143 memset(new->key, 0, sizeof(new->key));
145 if (!silc_hash_alloc("sha1", &new->sha1)) {
147 SILC_LOG_ERROR(("Could not allocate sha1 hash, probably not registered"));
151 new->devrandom = strdup("/dev/random");
156 /* Free's RNG object. */
158 void silc_rng_free(SilcRng rng)
161 memset(rng->pool, 0, sizeof(rng->pool));
162 memset(rng->key, 0, sizeof(rng->key));
163 silc_hash_free(rng->sha1);
164 silc_free(rng->devrandom);
166 if (rng->fd_devurandom != -1)
167 close(rng->fd_devurandom);
173 /* Initializes random number generator by getting noise from environment.
174 The environmental noise is our so called seed. One should not call
175 this function more than once. */
177 void silc_rng_init(SilcRng rng)
180 SilcRngState first, next;
184 SILC_LOG_DEBUG(("Initializing RNG object"));
186 /* Initialize the states for the RNG. */
187 rng->state = silc_calloc(1, sizeof(*rng->state));
190 rng->state->next = NULL;
192 for (i = SILC_RNG_STATE_NUM - 1; i >= 1; i--) {
193 next = silc_calloc(1, sizeof(*rng->state));
195 (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM));
197 (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM)) + 8;
198 next->next = rng->state;
204 memset(rng->pool, 0, sizeof(rng->pool));
206 /* Get noise from various environmental sources */
207 silc_rng_get_soft_noise(rng);
208 silc_rng_get_medium_noise(rng);
209 silc_rng_get_hard_noise(rng);
210 silc_rng_get_soft_noise(rng);
211 silc_free(rng->devrandom);
212 rng->devrandom = strdup("/dev/urandom");
215 /* This function gets 'soft' noise from environment. */
217 static void silc_rng_get_soft_noise(SilcRng rng)
224 pos = silc_rng_get_position(rng);
226 silc_rng_xor(rng, clock(), 0);
229 silc_rng_xor(rng, getpid(), 1);
231 silc_rng_xor(rng, getpgid(getpid()) << 8, 2);
232 silc_rng_xor(rng, getpgid(getpid()) << 8, 3);
234 silc_rng_xor(rng, getgid(), 4);
237 silc_rng_xor(rng, getpgrp(), 5);
240 silc_rng_xor(rng, getsid(getpid()) << 16, 6);
242 silc_rng_xor(rng, times(&ptime), 7);
243 silc_rng_xor(rng, ptime.tms_utime, 8);
244 silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), pos++);
245 silc_rng_xor(rng, (ptime.tms_stime + ptime.tms_cutime), pos++);
246 silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), pos++);
247 silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_stime), pos++);
248 silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_cstime), pos++);
249 silc_rng_xor(rng, (ptime.tms_utime ^ ptime.tms_stime), pos++);
250 silc_rng_xor(rng, (ptime.tms_stime ^ ptime.tms_cutime), pos++);
251 silc_rng_xor(rng, (ptime.tms_cutime + ptime.tms_stime), pos++);
252 silc_rng_xor(rng, (ptime.tms_stime << 8), pos++);
254 silc_rng_xor(rng, clock() << 4, pos++);
257 silc_rng_xor(rng, getpgid(getpid()) << 8, pos++);
260 silc_rng_xor(rng, getpgrp(), pos++);
263 silc_rng_xor(rng, getsid(getpid()) << 16, pos++);
265 silc_rng_xor(rng, times(&ptime), pos++);
266 silc_rng_xor(rng, ptime.tms_utime, pos++);
268 silc_rng_xor(rng, getpgrp(), pos++);
272 #ifdef SILC_RNG_DEBUG
273 SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
276 /* Stir random pool */
277 silc_rng_stir_pool(rng);
280 /* This function gets noise from different commands */
282 static void silc_rng_get_medium_noise(SilcRng rng)
284 silc_rng_exec_command(rng, "ps -leaww 2> /dev/null");
285 silc_rng_exec_command(rng, "ls -afiln ~ 2> /dev/null");
286 silc_rng_exec_command(rng, "ls -afiln /proc 2> /dev/null");
287 silc_rng_exec_command(rng, "ps -axww 2> /dev/null");
289 #ifdef SILC_RNG_DEBUG
290 SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
294 /* This function gets 'hard' noise from environment. This tries to
295 get the noise from /dev/random if available. */
297 static void silc_rng_get_hard_noise(SilcRng rng)
300 unsigned char buf[32];
303 /* Get noise from /dev/[u]random if available */
304 fd = open(rng->devrandom, O_RDONLY);
308 fcntl(fd, F_SETFL, O_NONBLOCK);
310 for (i = 0; i < 2; i++) {
311 len = read(fd, buf, sizeof(buf));
314 silc_rng_add_noise(rng, buf, len);
317 #ifdef SILC_RNG_DEBUG
318 SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
323 memset(buf, 0, sizeof(buf));
327 /* Execs command and gets noise from its output */
329 static void silc_rng_exec_command(SilcRng rng, char *command)
332 unsigned char buf[1024];
338 fd = popen(command, "r");
342 /* Get data as much as we can get into the buffer */
343 for (i = 0; i < sizeof(buf); i++) {
355 /* Add the buffer into random pool */
356 silc_rng_add_noise(rng, buf, i);
357 memset(buf, 0, sizeof(buf));
361 /* This function adds the contents of the buffer as noise into random
362 pool. After adding the noise the pool is stirred. */
364 void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, SilcUInt32 len)
368 pos = silc_rng_get_position(rng);
370 /* Add the buffer one by one into the pool */
371 for(i = 0; i < len; i++, buffer++) {
372 if(pos >= SILC_RNG_POOLSIZE)
374 rng->pool[pos++] ^= *buffer;
377 /* Stir random pool */
378 silc_rng_stir_pool(rng);
381 /* XOR's data into the pool */
383 static void silc_rng_xor(SilcRng rng, SilcUInt32 val, unsigned int pos)
386 rng->pool[pos] ^= val + val;
389 /* This function stirs the random pool by encrypting buffer in CFB
390 (cipher feedback) mode with SHA1 algorithm. */
392 static void silc_rng_stir_pool(SilcRng rng)
398 memcpy(iv, &rng->pool[16], sizeof(iv));
401 for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
402 rng->sha1->hash->transform(iv, rng->key);
403 iv[0] = rng->pool[i] ^= iv[0];
404 iv[1] = rng->pool[i + 1] ^= iv[1];
405 iv[2] = rng->pool[i + 2] ^= iv[2];
406 iv[3] = rng->pool[i + 3] ^= iv[3];
407 iv[4] = rng->pool[i + 4] ^= iv[4];
411 memcpy(rng->key, &rng->pool[silc_rng_get_position(rng)], sizeof(rng->key));
413 /* Second CFB pass */
414 for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
415 rng->sha1->hash->transform(iv, rng->key);
416 iv[0] = rng->pool[i] ^= iv[0];
417 iv[1] = rng->pool[i + 1] ^= iv[1];
418 iv[2] = rng->pool[i + 2] ^= iv[2];
419 iv[3] = rng->pool[i + 3] ^= iv[3];
420 iv[4] = rng->pool[i + 4] ^= iv[4];
423 memset(iv, 0, sizeof(iv));
426 /* Returns next position where data is fetched from the pool or
429 static SilcUInt32 silc_rng_get_position(SilcRng rng)
434 next = rng->state->next;
436 pos = rng->state->pos++;
437 if ((next->low != 0 && pos >= next->low) || (pos >= SILC_RNG_POOLSIZE))
438 rng->state->pos = rng->state->low;
440 #ifdef SILC_RNG_DEBUG
441 fprintf(stderr, "state: %p: low: %lu, pos: %lu\n",
442 rng->state, rng->state->low, rng->state->pos);
450 /* Returns random byte. */
452 SilcUInt8 silc_rng_get_byte(SilcRng rng)
456 /* Get more soft noise after 64 bits threshold */
457 if (rng->threshold >= 8)
458 silc_rng_get_soft_noise(rng);
460 /* Get hard noise after 160 bits threshold, zero the threshold. */
461 if (rng->threshold >= 20) {
463 silc_rng_get_hard_noise(rng);
466 return rng->pool[silc_rng_get_position(rng)];
469 /* Return random byte as fast as possible. Reads from /dev/urandom if
470 available. If not then return from normal RNG (not so fast). */
472 SilcUInt8 silc_rng_get_byte_fast(SilcRng rng)
475 unsigned char buf[1];
477 if (rng->fd_devurandom == -1) {
478 rng->fd_devurandom = open("/dev/urandom", O_RDONLY);
480 return silc_rng_get_byte(rng);
481 fcntl(rng->fd_devurandom, F_SETFL, O_NONBLOCK);
484 if (read(rng->fd_devurandom, buf, sizeof(buf)) < 0)
485 return silc_rng_get_byte(rnf);
489 return silc_rng_get_byte(rng);
493 /* Returns 16 bit random number */
495 SilcUInt16 silc_rng_get_rn16(SilcRng rng)
500 rn[0] = silc_rng_get_byte(rng);
501 rn[1] = silc_rng_get_byte(rng);
502 SILC_GET16_MSB(num, rn);
507 /* Returns 32 bit random number */
509 SilcUInt32 silc_rng_get_rn32(SilcRng rng)
514 rn[0] = silc_rng_get_byte(rng);
515 rn[1] = silc_rng_get_byte(rng);
516 rn[2] = silc_rng_get_byte(rng);
517 rn[3] = silc_rng_get_byte(rng);
518 SILC_GET32_MSB(num, rn);
523 /* Returns random number string. Returned string is in HEX format. */
525 unsigned char *silc_rng_get_rn_string(SilcRng rng, SilcUInt32 len)
528 unsigned char *string;
530 string = silc_calloc((len * 2 + 1), sizeof(unsigned char));
532 for (i = 0; i < len; i++)
533 sprintf(string + 2 * i, "%02x", silc_rng_get_byte(rng));
538 /* Returns random number binary data. */
540 unsigned char *silc_rng_get_rn_data(SilcRng rng, SilcUInt32 len)
545 data = silc_calloc(len + 1, sizeof(*data));
547 for (i = 0; i < len; i++)
548 data[i] = silc_rng_get_byte(rng);
553 /* Global RNG. This is global RNG that application can initialize so
554 that any part of code anywhere can use RNG without having to allocate
555 new RNG object everytime. If this is not initialized then these routines
556 will fail. Note: currently in SILC applications always initialize this. */
558 SilcRng global_rng = NULL;
560 /* Initialize global RNG. If `rng' is provided it is set as the global
561 RNG object (it can be allocated by the application for example). */
563 bool silc_rng_global_init(SilcRng rng)
568 global_rng = silc_rng_alloc();
573 /* Uninitialize global RNG */
575 bool silc_rng_global_uninit(void)
578 silc_rng_free(global_rng);
585 /* These are analogous to the functions above. */
587 SilcUInt8 silc_rng_global_get_byte(void)
589 return global_rng ? silc_rng_get_byte(global_rng) : 0;
592 /* Return random byte as fast as possible. Reads from /dev/urandom if
593 available. If not then return from normal RNG (not so fast). */
595 SilcUInt8 silc_rng_global_get_byte_fast(void)
597 return global_rng ? silc_rng_get_byte_fast(global_rng) : 0;
600 SilcUInt16 silc_rng_global_get_rn16(void)
602 return global_rng ? silc_rng_get_rn16(global_rng) : 0;
605 SilcUInt32 silc_rng_global_get_rn32(void)
607 return global_rng ? silc_rng_get_rn32(global_rng) : 0;
610 unsigned char *silc_rng_global_get_rn_string(SilcUInt32 len)
612 return global_rng ? silc_rng_get_rn_string(global_rng, len) : NULL;
615 unsigned char *silc_rng_global_get_rn_data(SilcUInt32 len)
617 return global_rng ? silc_rng_get_rn_data(global_rng, len) : NULL;
620 void silc_rng_global_add_noise(unsigned char *buffer, SilcUInt32 len)
623 silc_rng_add_noise(global_rng, buffer, len);