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 and was used as reference when programming this RNG.
26 * This RNG has been rewritten twice since the creation.
29 #include "silcincludes.h"
32 extern __pid_t getsid (__pid_t __pid);
36 extern __pid_t getpgid (__pid_t __pid);
40 /*#define SILC_RNG_DEBUG*/
42 /* Number of states to fetch data from pool. */
43 #define SILC_RNG_STATE_NUM 4
45 /* Byte size of the random data pool. */
46 #define SILC_RNG_POOLSIZE 1024
48 static uint32 silc_rng_get_position(SilcRng rng);
49 static void silc_rng_stir_pool(SilcRng rng);
50 static void silc_rng_xor(SilcRng rng, uint32 val, unsigned int pos);
51 static void silc_rng_exec_command(SilcRng rng, char *command);
52 static void silc_rng_get_hard_noise(SilcRng rng);
53 static void silc_rng_get_medium_noise(SilcRng rng);
54 static void silc_rng_get_soft_noise(SilcRng rng);
57 SILC SilcRng State context.
59 This object is used by the random number generator to provide
60 variable points where the actual random number is fetched from
61 the random pool. This provides that the data is not fetched always
62 from the same point of the pool. Short description of the fields
68 The index for the random pool buffer. Lowest and current
71 SilcRngStateContext *next
73 Pointer to the next state. If this is the last state this
74 will point to the first state thus providing circular list.
77 typedef struct SilcRngStateContext {
80 struct SilcRngStateContext *next;
84 SILC Random Number Generator object.
86 This object holds random pool which is used to generate the random
87 numbers used by various routines needing cryptographically strong
88 random numbers. Following short descriptions of the fields.
92 The random pool. This buffer holds the random data. This is
93 frequently stirred thus providing ever changing randomnes.
97 Key used in stirring the random pool. The pool is encrypted
98 with SHA1 hash function in CFB (Cipher Feedback) mode.
100 SilcSilcRngState state
102 State object that is used to get the next position for the
103 random pool. This position is used to fetch data from pool
104 or to save the data to the pool. The state changes everytime
109 Hash object (SHA1) used to make the CFB encryption to the
110 random pool. This is allocated when RNG object is allocated and
111 free'd when RNG object is free'd.
115 Threshhold to indicate when it is required to acquire more
116 noise from the environment. More soft noise is acquired after
117 64 bits of output and hard noise every 160 bits of output.
120 typedef struct SilcRngObjectStruct {
121 unsigned char pool[SILC_RNG_POOLSIZE];
122 unsigned char key[64];
130 /* Allocates new RNG object. */
132 SilcRng silc_rng_alloc()
136 SILC_LOG_DEBUG(("Allocating new RNG object"));
138 new = silc_calloc(1, sizeof(*new));
139 new->fd_devurandom = -1;
141 memset(new->pool, 0, sizeof(new->pool));
142 memset(new->key, 0, sizeof(new->key));
144 silc_hash_alloc("sha1", &new->sha1);
146 new->devrandom = strdup("/dev/random");
151 /* Free's RNG object. */
153 void silc_rng_free(SilcRng rng)
156 memset(rng->pool, 0, sizeof(rng->pool));
157 memset(rng->key, 0, sizeof(rng->key));
158 silc_hash_free(rng->sha1);
159 silc_free(rng->devrandom);
161 if (rng->fd_devurandom != -1)
162 close(rng->fd_devurandom);
168 /* Initializes random number generator by getting noise from environment.
169 The environmental noise is our so called seed. One should not call
170 this function more than once. */
172 void silc_rng_init(SilcRng rng)
175 SilcRngState first, next;
179 SILC_LOG_DEBUG(("Initializing RNG object"));
181 /* Initialize the states for the RNG. */
182 rng->state = silc_calloc(1, sizeof(*rng->state));
185 rng->state->next = NULL;
187 for (i = SILC_RNG_STATE_NUM - 1; i >= 1; i--) {
188 next = silc_calloc(1, sizeof(*rng->state));
190 (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM));
192 (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM)) + 8;
193 next->next = rng->state;
199 memset(rng->pool, 0, sizeof(rng->pool));
201 /* Get noise from various environmental sources */
202 silc_rng_get_soft_noise(rng);
203 silc_rng_get_medium_noise(rng);
204 silc_rng_get_hard_noise(rng);
205 silc_rng_get_soft_noise(rng);
206 silc_free(rng->devrandom);
207 rng->devrandom = strdup("/dev/urandom");
210 /* This function gets 'soft' noise from environment. */
212 static void silc_rng_get_soft_noise(SilcRng rng)
219 pos = silc_rng_get_position(rng);
221 silc_rng_xor(rng, clock(), 0);
224 silc_rng_xor(rng, getpid(), 1);
226 silc_rng_xor(rng, getpgid(getpid()) << 8, 2);
227 silc_rng_xor(rng, getpgid(getpid()) << 8, 3);
229 silc_rng_xor(rng, getgid(), 4);
232 silc_rng_xor(rng, getpgrp(), 5);
235 silc_rng_xor(rng, getsid(getpid()) << 16, 6);
237 silc_rng_xor(rng, times(&ptime), 7);
238 silc_rng_xor(rng, ptime.tms_utime, 8);
239 silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), pos++);
240 silc_rng_xor(rng, (ptime.tms_stime + ptime.tms_cutime), pos++);
241 silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), pos++);
242 silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_stime), pos++);
243 silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_cstime), pos++);
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_cutime + ptime.tms_stime), pos++);
247 silc_rng_xor(rng, (ptime.tms_stime << 8), pos++);
249 silc_rng_xor(rng, clock() << 4, pos++);
252 silc_rng_xor(rng, getpgid(getpid()) << 8, pos++);
255 silc_rng_xor(rng, getpgrp(), pos++);
258 silc_rng_xor(rng, getsid(getpid()) << 16, pos++);
260 silc_rng_xor(rng, times(&ptime), pos++);
261 silc_rng_xor(rng, ptime.tms_utime, pos++);
263 silc_rng_xor(rng, getpgrp(), pos++);
267 #ifdef SILC_RNG_DEBUG
268 SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
271 /* Stir random pool */
272 silc_rng_stir_pool(rng);
275 /* This function gets noise from different commands */
277 static void silc_rng_get_medium_noise(SilcRng rng)
279 silc_rng_exec_command(rng, "ps -leaww 2> /dev/null");
280 silc_rng_exec_command(rng, "ls -afiln ~ 2> /dev/null");
281 silc_rng_exec_command(rng, "ls -afiln /proc 2> /dev/null");
282 silc_rng_exec_command(rng, "ps -axww 2> /dev/null");
284 #ifdef SILC_RNG_DEBUG
285 SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
289 /* This function gets 'hard' noise from environment. This tries to
290 get the noise from /dev/random if available. */
292 static void silc_rng_get_hard_noise(SilcRng rng)
295 unsigned char buf[32];
298 /* Get noise from /dev/[u]random if available */
299 fd = open(rng->devrandom, O_RDONLY);
303 fcntl(fd, F_SETFL, O_NONBLOCK);
305 for (i = 0; i < 2; i++) {
306 len = read(fd, buf, sizeof(buf));
309 silc_rng_add_noise(rng, buf, len);
312 #ifdef SILC_RNG_DEBUG
313 SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
318 memset(buf, 0, sizeof(buf));
322 /* Execs command and gets noise from its output */
324 static void silc_rng_exec_command(SilcRng rng, char *command)
327 unsigned char buf[1024];
333 fd = popen(command, "r");
337 /* Get data as much as we can get into the buffer */
338 for (i = 0; i < sizeof(buf); i++) {
350 /* Add the buffer into random pool */
351 silc_rng_add_noise(rng, buf, strlen(buf));
352 memset(buf, 0, sizeof(buf));
356 /* This function adds the contents of the buffer as noise into random
357 pool. After adding the noise the pool is stirred. */
359 void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, uint32 len)
363 pos = silc_rng_get_position(rng);
365 /* Add the buffer one by one into the pool */
366 for(i = 0; i < len; i++, buffer++) {
367 if(pos >= SILC_RNG_POOLSIZE)
369 rng->pool[pos++] ^= *buffer;
372 /* Stir random pool */
373 silc_rng_stir_pool(rng);
376 /* XOR's data into the pool */
378 static void silc_rng_xor(SilcRng rng, uint32 val, unsigned int pos)
381 rng->pool[pos] ^= val + val;
384 /* This function stirs the random pool by encrypting buffer in CFB
385 (cipher feedback) mode with SHA1 algorithm. */
387 static void silc_rng_stir_pool(SilcRng rng)
393 memcpy(iv, &rng->pool[16], sizeof(iv));
396 for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
397 rng->sha1->hash->transform(iv, rng->key);
398 iv[0] = rng->pool[i] ^= iv[0];
399 iv[1] = rng->pool[i + 1] ^= iv[1];
400 iv[2] = rng->pool[i + 2] ^= iv[2];
401 iv[3] = rng->pool[i + 3] ^= iv[3];
402 iv[4] = rng->pool[i + 4] ^= iv[4];
406 memcpy(rng->key, &rng->pool[silc_rng_get_position(rng)], sizeof(rng->key));
408 /* Second CFB pass */
409 for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
410 rng->sha1->hash->transform(iv, rng->key);
411 iv[0] = rng->pool[i] ^= iv[0];
412 iv[1] = rng->pool[i + 1] ^= iv[1];
413 iv[2] = rng->pool[i + 2] ^= iv[2];
414 iv[3] = rng->pool[i + 3] ^= iv[3];
415 iv[4] = rng->pool[i + 4] ^= iv[4];
418 memset(iv, 0, sizeof(iv));
421 /* Returns next position where data is fetched from the pool or
424 static uint32 silc_rng_get_position(SilcRng rng)
429 next = rng->state->next;
431 pos = rng->state->pos++;
432 if ((next->low != 0 && pos >= next->low) || (pos >= SILC_RNG_POOLSIZE))
433 rng->state->pos = rng->state->low;
435 #ifdef SILC_RNG_DEBUG
436 fprintf(stderr, "state: %p: low: %lu, pos: %lu\n",
437 rng->state, rng->state->low, rng->state->pos);
445 /* Returns random byte. */
447 unsigned char silc_rng_get_byte(SilcRng rng)
451 /* Get more soft noise after 64 bits threshhold */
452 if (rng->threshhold >= 8)
453 silc_rng_get_soft_noise(rng);
455 /* Get hard noise after 160 bits threshhold, zero the threshhold. */
456 if (rng->threshhold >= 20) {
458 silc_rng_get_hard_noise(rng);
461 return rng->pool[silc_rng_get_position(rng)];
464 /* Returns 16 bit random number */
466 uint16 silc_rng_get_rn16(SilcRng rng)
471 rn[0] = silc_rng_get_byte(rng);
472 rn[1] = silc_rng_get_byte(rng);
473 SILC_GET16_MSB(num, rn);
478 /* Returns 32 bit random number */
480 uint32 silc_rng_get_rn32(SilcRng rng)
485 rn[0] = silc_rng_get_byte(rng);
486 rn[1] = silc_rng_get_byte(rng);
487 rn[2] = silc_rng_get_byte(rng);
488 rn[3] = silc_rng_get_byte(rng);
489 SILC_GET32_MSB(num, rn);
494 /* Returns random number string. Returned string is in HEX format. */
496 unsigned char *silc_rng_get_rn_string(SilcRng rng, uint32 len)
499 unsigned char *string;
501 string = silc_calloc((len * 2 + 1), sizeof(unsigned char));
503 for (i = 0; i < len; i++)
504 sprintf(string + 2 * i, "%02x", silc_rng_get_byte(rng));
509 /* Returns random number binary data. */
511 unsigned char *silc_rng_get_rn_data(SilcRng rng, uint32 len)
516 data = silc_calloc(len + 1, sizeof(*data));
518 for (i = 0; i < len; i++)
519 data[i] = silc_rng_get_byte(rng);
524 /* Global RNG. This is global RNG that application can initialize so
525 that any part of code anywhere can use RNG without having to allocate
526 new RNG object everytime. If this is not initialized then these routines
527 will fail. Note: currently in SILC applications always initialize this. */
529 SilcRng global_rng = NULL;
531 /* Initialize global RNG. If `rng' is provided it is set as the global
532 RNG object (it can be allocated by the application for example). */
534 int silc_rng_global_init(SilcRng rng)
539 global_rng = silc_rng_alloc();
544 /* Uninitialize global RNG */
546 int silc_rng_global_uninit()
549 silc_rng_free(global_rng);
556 /* These are analogous to the functions above. */
558 unsigned char silc_rng_global_get_byte()
560 return global_rng ? silc_rng_get_byte(global_rng) : 0;
563 /* Return random byte as fast as possible. Reads from /dev/urandom if
564 available. If not then return from normal RNG (not so fast). */
566 unsigned char silc_rng_global_get_byte_fast()
569 unsigned char buf[1];
574 if (global_rng->fd_devurandom == -1) {
575 global_rng->fd_devurandom = open("/dev/urandom", O_RDONLY);
577 return silc_rng_global_get_byte();
578 fcntl(global_rng->fd_devurandom, F_SETFL, O_NONBLOCK);
581 if (read(global_rng->fd_devurandom, buf, sizeof(buf)) < 0)
582 return silc_rng_global_get_byte();
586 return silc_rng_global_get_byte();
590 uint16 silc_rng_global_get_rn16()
592 return global_rng ? silc_rng_get_rn16(global_rng) : 0;
595 uint32 silc_rng_global_get_rn32()
597 return global_rng ? silc_rng_get_rn32(global_rng) : 0;
600 unsigned char *silc_rng_global_get_rn_string(uint32 len)
602 return global_rng ? silc_rng_get_rn_string(global_rng, len) : NULL;
605 unsigned char *silc_rng_global_get_rn_data(uint32 len)
607 return global_rng ? silc_rng_get_rn_data(global_rng, len) : NULL;
610 void silc_rng_global_add_noise(unsigned char *buffer, uint32 len)
613 silc_rng_add_noise(global_rng, buffer, len);