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 static uint32 silc_rng_get_position(SilcRng rng);
34 static void silc_rng_stir_pool(SilcRng rng);
35 static void silc_rng_xor(SilcRng rng, uint32 val, unsigned int pos);
36 static void silc_rng_exec_command(SilcRng rng, char *command);
37 static void silc_rng_get_hard_noise(SilcRng rng);
38 static void silc_rng_get_medium_noise(SilcRng rng);
39 static void silc_rng_get_soft_noise(SilcRng rng);
42 SILC SilcRng State context.
44 This object is used by the random number generator to provide
45 variable points where the actual random number is fetched from
46 the random pool. This provides that the data is not fetched always
47 from the same point of the pool. Short description of the fields
53 The index for the random pool buffer. Lowest and current
56 SilcRngStateContext *next
58 Pointer to the next state. If this is the last state this
59 will point to the first state thus providing circular list.
62 typedef struct SilcRngStateContext {
65 struct SilcRngStateContext *next;
69 SILC Random Number Generator object.
71 This object holds random pool which is used to generate the random
72 numbers used by various routines needing cryptographically strong
73 random numbers. Following short descriptions of the fields.
77 The random pool. This buffer holds the random data. This is
78 frequently stirred thus providing ever changing randomnes.
82 Key used in stirring the random pool. The pool is encrypted
83 with SHA1 hash function in CFB (Cipher Feedback) mode.
85 SilcSilcRngState state
87 State object that is used to get the next position for the
88 random pool. This position is used to fetch data from pool
89 or to save the data to the pool. The state changes everytime
94 Hash object (SHA1) used to make the CFB encryption to the
95 random pool. This is allocated when RNG object is allocated and
96 free'd when RNG object is free'd.
100 Threshold to indicate when it is required to acquire more
101 noise from the environment. More soft noise is acquired after
102 64 bits of output and hard noise every 160 bits of output.
105 typedef struct SilcRngObjectStruct {
106 unsigned char pool[SILC_RNG_POOLSIZE];
107 unsigned char key[64];
113 /* Allocates new RNG object. */
115 SilcRng silc_rng_alloc()
119 SILC_LOG_DEBUG(("Allocating new RNG object"));
121 new = silc_calloc(1, sizeof(*new));
123 memset(new->pool, 0, sizeof(new->pool));
124 memset(new->key, 0, sizeof(new->key));
126 silc_hash_alloc("sha1", &new->sha1);
131 /* Free's RNG object. */
133 void silc_rng_free(SilcRng rng)
136 memset(rng->pool, 0, sizeof(rng->pool));
137 memset(rng->key, 0, sizeof(rng->key));
138 silc_free(rng->sha1);
143 /* Initializes random number generator by getting noise from environment.
144 The environmental noise is our so called seed. One should not call
145 this function more than once. */
147 void silc_rng_init(SilcRng rng)
150 SilcRngState first, next;
154 SILC_LOG_DEBUG(("Initializing RNG object"));
156 /* Initialize the states for the RNG. */
157 rng->state = silc_calloc(1, sizeof(*rng->state));
160 rng->state->next = NULL;
162 for (i = SILC_RNG_STATE_NUM - 1; i >= 1; i--) {
163 next = silc_calloc(1, sizeof(*rng->state));
165 (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM));
167 (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM)) + 8;
168 next->next = rng->state;
174 memset(rng->pool, 0, sizeof(rng->pool));
176 /* Get noise from various environmental sources */
177 silc_rng_get_soft_noise(rng);
178 silc_rng_get_medium_noise(rng);
179 silc_rng_get_hard_noise(rng);
180 silc_rng_get_soft_noise(rng);
183 /* This function gets 'soft' noise from environment. */
185 static void silc_rng_get_soft_noise(SilcRng rng)
190 pos = silc_rng_get_position(rng);
192 silc_rng_xor(rng, clock(), 0);
194 silc_rng_xor(rng, getpid(), 1);
196 silc_rng_xor(rng, getpgid(getpid() << 8), 2);
197 silc_rng_xor(rng, getpgid(getpid() << 8), 3);
199 silc_rng_xor(rng, getgid(), 4);
202 silc_rng_xor(rng, getpgrp(), 5);
205 silc_rng_xor(rng, getsid(getpid() << 16), 6);
207 silc_rng_xor(rng, times(&ptime), 7);
208 silc_rng_xor(rng, ptime.tms_utime, 8);
209 silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), pos++);
210 silc_rng_xor(rng, (ptime.tms_stime + ptime.tms_cutime), pos++);
211 silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), pos++);
212 silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_stime), pos++);
213 silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_cstime), pos++);
214 silc_rng_xor(rng, (ptime.tms_utime ^ ptime.tms_stime), pos++);
215 silc_rng_xor(rng, (ptime.tms_stime ^ ptime.tms_cutime), pos++);
216 silc_rng_xor(rng, (ptime.tms_cutime + ptime.tms_stime), pos++);
217 silc_rng_xor(rng, (ptime.tms_stime << 8), pos++);
218 silc_rng_xor(rng, clock() << 4, pos++);
220 silc_rng_xor(rng, getpgid(getpid() << 8), pos++);
223 silc_rng_xor(rng, getpgrp(), pos++);
226 silc_rng_xor(rng, getsid(getpid() << 16), pos++);
228 silc_rng_xor(rng, times(&ptime), pos++);
229 silc_rng_xor(rng, ptime.tms_utime, pos++);
231 silc_rng_xor(rng, getpgrp(), pos++);
234 #ifdef SILC_RNG_DEBUG
235 SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
238 /* Stir random pool */
239 silc_rng_stir_pool(rng);
242 /* This function gets noise from different commands */
244 static void silc_rng_get_medium_noise(SilcRng rng)
246 silc_rng_exec_command(rng, "ps -leaww 2> /dev/null");
247 silc_rng_exec_command(rng, "ls -afiln ~ 2> /dev/null");
248 silc_rng_exec_command(rng, "ls -afiln /proc 2> /dev/null");
249 silc_rng_exec_command(rng, "ps -axww 2> /dev/null");
251 #ifdef SILC_RNG_DEBUG
252 SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
256 /* This function gets 'hard' noise from environment. This tries to
257 get the noise from /dev/random if available. */
259 static void silc_rng_get_hard_noise(SilcRng rng)
264 /* Get noise from /dev/random if available */
265 fd = open("/dev/random", O_RDONLY);
269 fcntl(fd, F_SETFL, O_NONBLOCK);
271 for (i = 0; i < 2; i++) {
272 len = read(fd, buf, sizeof(buf));
275 silc_rng_add_noise(rng, buf, len);
278 #ifdef SILC_RNG_DEBUG
279 SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
284 memset(buf, 0, sizeof(buf));
287 /* Execs command and gets noise from its output */
289 static void silc_rng_exec_command(SilcRng rng, char *command)
297 fd = popen(command, "r");
301 /* Get data as much as we can get into the buffer */
302 for (i = 0; i < sizeof(buf); i++) {
314 /* Add the buffer into random pool */
315 silc_rng_add_noise(rng, buf, strlen(buf));
316 memset(buf, 0, sizeof(buf));
319 /* This function adds the contents of the buffer as noise into random
320 pool. After adding the noise the pool is stirred. */
322 void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, uint32 len)
326 pos = silc_rng_get_position(rng);
328 /* Add the buffer one by one into the pool */
329 for(i = 0; i < len; i++, buffer++) {
330 if(pos >= SILC_RNG_POOLSIZE)
332 rng->pool[pos++] ^= *buffer;
335 /* Stir random pool */
336 silc_rng_stir_pool(rng);
339 /* XOR's data into the pool */
341 static void silc_rng_xor(SilcRng rng, uint32 val, unsigned int pos)
344 rng->pool[pos] ^= val + val;
347 /* This function stirs the random pool by encrypting buffer in CFB
348 (cipher feedback) mode with SHA1 algorithm. */
350 static void silc_rng_stir_pool(SilcRng rng)
356 memcpy(iv, &rng->pool[SILC_RNG_POOLSIZE - 256], sizeof(iv));
359 for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
360 rng->sha1->hash->transform(iv, rng->key);
361 iv[0] = rng->pool[i] ^= iv[0];
362 iv[1] = rng->pool[i + 1] ^= iv[1];
363 iv[2] = rng->pool[i + 2] ^= iv[2];
364 iv[3] = rng->pool[i + 3] ^= iv[3];
365 iv[4] = rng->pool[i + 4] ^= iv[4];
369 memcpy(rng->key, &rng->pool[silc_rng_get_position(rng)], sizeof(rng->key));
371 /* Second CFB pass */
372 for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
373 rng->sha1->hash->transform(iv, rng->key);
374 iv[0] = rng->pool[i] ^= iv[0];
375 iv[1] = rng->pool[i + 1] ^= iv[1];
376 iv[2] = rng->pool[i + 2] ^= iv[2];
377 iv[3] = rng->pool[i + 3] ^= iv[3];
378 iv[4] = rng->pool[i + 4] ^= iv[4];
381 memset(iv, 0, sizeof(iv));
384 /* Returns next position where data is fetched from the pool or
387 static uint32 silc_rng_get_position(SilcRng rng)
392 next = rng->state->next;
394 pos = rng->state->pos++;
395 if ((next->low != 0 && pos >= next->low) || (pos >= SILC_RNG_POOLSIZE))
396 rng->state->pos = rng->state->low;
398 #ifdef SILC_RNG_DEBUG
399 fprintf(stderr, "state: %p: low: %lu, pos: %lu\n",
400 rng->state, rng->state->low, rng->state->pos);
408 /* Returns random byte. */
410 unsigned char silc_rng_get_byte(SilcRng rng)
414 /* Get more soft noise after 64 bits threshold */
415 if (rng->threshold >= 8)
416 silc_rng_get_soft_noise(rng);
418 /* Get hard noise after 160 bits threshold, zero the threshold. */
419 if (rng->threshold >= 20) {
421 silc_rng_get_hard_noise(rng);
424 return rng->pool[silc_rng_get_position(rng)];
427 /* Returns 16 bit random number */
429 uint16 silc_rng_get_rn16(SilcRng rng)
434 rn[0] = silc_rng_get_byte(rng);
435 rn[1] = silc_rng_get_byte(rng);
436 SILC_GET16_MSB(num, rn);
441 /* Returns 32 bit random number */
443 uint32 silc_rng_get_rn32(SilcRng rng)
448 rn[0] = silc_rng_get_byte(rng);
449 rn[1] = silc_rng_get_byte(rng);
450 rn[2] = silc_rng_get_byte(rng);
451 rn[3] = silc_rng_get_byte(rng);
452 SILC_GET32_MSB(num, rn);
457 /* Returns random number string. Returned string is in HEX format. */
459 unsigned char *silc_rng_get_rn_string(SilcRng rng, uint32 len)
462 unsigned char *string;
464 string = silc_calloc((len * 2 + 1), sizeof(unsigned char));
466 for (i = 0; i < len; i++)
467 sprintf(string + 2 * i, "%02x", silc_rng_get_byte(rng));
472 /* Returns random number binary data. */
474 unsigned char *silc_rng_get_rn_data(SilcRng rng, uint32 len)
479 data = silc_calloc(len + 1, sizeof(*data));
481 for (i = 0; i < len; i++)
482 data[i] = silc_rng_get_byte(rng);
487 /* Global RNG. This is global RNG that application can initialize so
488 that any part of code anywhere can use RNG without having to allocate
489 new RNG object everytime. If this is not initialized then these routines
490 will fail. Note: currently in SILC applications always initialize this. */
492 SilcRng global_rng = NULL;
494 /* Initialize global RNG. If `rng' is provided it is set as the global
495 RNG object (it can be allocated by the application for example). */
497 int silc_rng_global_init(SilcRng rng)
502 global_rng = silc_rng_alloc();
507 /* Uninitialize global RNG */
509 int silc_rng_global_uninit()
512 silc_rng_free(global_rng);
519 /* These are analogous to the functions above. */
521 unsigned char silc_rng_global_get_byte()
523 return global_rng ? silc_rng_get_byte(global_rng) : 0;
526 uint16 silc_rng_global_get_rn16()
528 return global_rng ? silc_rng_get_rn16(global_rng) : 0;
531 uint32 silc_rng_global_get_rn32()
533 return global_rng ? silc_rng_get_rn32(global_rng) : 0;
536 unsigned char *silc_rng_global_get_rn_string(uint32 len)
538 return global_rng ? silc_rng_get_rn_string(global_rng, len) : NULL;
541 unsigned char *silc_rng_global_get_rn_data(uint32 len)
543 return global_rng ? silc_rng_get_rn_data(global_rng, len) : NULL;
546 void silc_rng_global_add_noise(unsigned char *buffer, uint32 len)
549 silc_rng_add_noise(global_rng, buffer, len);