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"
33 extern pid_t getsid (pid_t __pid);
37 extern pid_t getpgid (pid_t __pid);
42 /*#define SILC_RNG_DEBUG*/
44 /* Number of states to fetch data from pool. */
45 #define SILC_RNG_STATE_NUM 4
47 /* Byte size of the random data pool. */
48 #define SILC_RNG_POOLSIZE 1024
50 static uint32 silc_rng_get_position(SilcRng rng);
51 static void silc_rng_stir_pool(SilcRng rng);
52 static void silc_rng_xor(SilcRng rng, uint32 val, unsigned int pos);
53 static void silc_rng_exec_command(SilcRng rng, char *command);
54 static void silc_rng_get_hard_noise(SilcRng rng);
55 static void silc_rng_get_medium_noise(SilcRng rng);
56 static void silc_rng_get_soft_noise(SilcRng rng);
59 SILC SilcRng State context.
61 This object is used by the random number generator to provide
62 variable points where the actual random number is fetched from
63 the random pool. This provides that the data is not fetched always
64 from the same point of the pool. Short description of the fields
70 The index for the random pool buffer. Lowest and current
73 SilcRngStateContext *next
75 Pointer to the next state. If this is the last state this
76 will point to the first state thus providing circular list.
79 typedef struct SilcRngStateContext {
82 struct SilcRngStateContext *next;
86 SILC Random Number Generator object.
88 This object holds random pool which is used to generate the random
89 numbers used by various routines needing cryptographically strong
90 random numbers. Following short descriptions of the fields.
94 The random pool. This buffer holds the random data. This is
95 frequently stirred thus providing ever changing randomnes.
99 Key used in stirring the random pool. The pool is encrypted
100 with SHA1 hash function in CFB (Cipher Feedback) mode.
102 SilcSilcRngState state
104 State object that is used to get the next position for the
105 random pool. This position is used to fetch data from pool
106 or to save the data to the pool. The state changes everytime
111 Hash object (SHA1) used to make the CFB encryption to the
112 random pool. This is allocated when RNG object is allocated and
113 free'd when RNG object is free'd.
117 Threshhold to indicate when it is required to acquire more
118 noise from the environment. More soft noise is acquired after
119 64 bits of output and hard noise every 160 bits of output.
122 typedef struct SilcRngObjectStruct {
123 unsigned char pool[SILC_RNG_POOLSIZE];
124 unsigned char key[64];
132 /* Allocates new RNG object. */
134 SilcRng silc_rng_alloc()
138 SILC_LOG_DEBUG(("Allocating new RNG object"));
140 new = silc_calloc(1, sizeof(*new));
141 new->fd_devurandom = -1;
143 memset(new->pool, 0, sizeof(new->pool));
144 memset(new->key, 0, sizeof(new->key));
146 if (!silc_hash_alloc("sha1", &new->sha1)) {
148 SILC_LOG_ERROR(("Could not allocate sha1 hash, probably not registered"));
152 new->devrandom = strdup("/dev/random");
157 /* Free's RNG object. */
159 void silc_rng_free(SilcRng rng)
162 memset(rng->pool, 0, sizeof(rng->pool));
163 memset(rng->key, 0, sizeof(rng->key));
164 silc_hash_free(rng->sha1);
165 silc_free(rng->devrandom);
167 if (rng->fd_devurandom != -1)
168 close(rng->fd_devurandom);
174 /* Initializes random number generator by getting noise from environment.
175 The environmental noise is our so called seed. One should not call
176 this function more than once. */
178 void silc_rng_init(SilcRng rng)
181 SilcRngState first, next;
185 SILC_LOG_DEBUG(("Initializing RNG object"));
187 /* Initialize the states for the RNG. */
188 rng->state = silc_calloc(1, sizeof(*rng->state));
191 rng->state->next = NULL;
193 for (i = SILC_RNG_STATE_NUM - 1; i >= 1; i--) {
194 next = silc_calloc(1, sizeof(*rng->state));
196 (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM));
198 (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM)) + 8;
199 next->next = rng->state;
205 memset(rng->pool, 0, sizeof(rng->pool));
207 /* Get noise from various environmental sources */
208 silc_rng_get_soft_noise(rng);
209 silc_rng_get_medium_noise(rng);
210 silc_rng_get_hard_noise(rng);
211 silc_rng_get_soft_noise(rng);
212 silc_free(rng->devrandom);
213 rng->devrandom = strdup("/dev/urandom");
216 /* This function gets 'soft' noise from environment. */
218 static void silc_rng_get_soft_noise(SilcRng rng)
225 pos = silc_rng_get_position(rng);
227 silc_rng_xor(rng, clock(), 0);
230 silc_rng_xor(rng, getpid(), 1);
232 silc_rng_xor(rng, getpgid(getpid()) << 8, 2);
233 silc_rng_xor(rng, getpgid(getpid()) << 8, 3);
235 silc_rng_xor(rng, getgid(), 4);
238 silc_rng_xor(rng, getpgrp(), 5);
241 silc_rng_xor(rng, getsid(getpid()) << 16, 6);
243 silc_rng_xor(rng, times(&ptime), 7);
244 silc_rng_xor(rng, ptime.tms_utime, 8);
245 silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), pos++);
246 silc_rng_xor(rng, (ptime.tms_stime + ptime.tms_cutime), pos++);
247 silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), pos++);
248 silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_stime), pos++);
249 silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_cstime), pos++);
250 silc_rng_xor(rng, (ptime.tms_utime ^ ptime.tms_stime), pos++);
251 silc_rng_xor(rng, (ptime.tms_stime ^ ptime.tms_cutime), pos++);
252 silc_rng_xor(rng, (ptime.tms_cutime + ptime.tms_stime), pos++);
253 silc_rng_xor(rng, (ptime.tms_stime << 8), pos++);
255 silc_rng_xor(rng, clock() << 4, pos++);
258 silc_rng_xor(rng, getpgid(getpid()) << 8, pos++);
261 silc_rng_xor(rng, getpgrp(), pos++);
264 silc_rng_xor(rng, getsid(getpid()) << 16, pos++);
266 silc_rng_xor(rng, times(&ptime), pos++);
267 silc_rng_xor(rng, ptime.tms_utime, pos++);
269 silc_rng_xor(rng, getpgrp(), pos++);
273 #ifdef SILC_RNG_DEBUG
274 SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
277 /* Stir random pool */
278 silc_rng_stir_pool(rng);
281 /* This function gets noise from different commands */
283 static void silc_rng_get_medium_noise(SilcRng rng)
285 silc_rng_exec_command(rng, "ps -leaww 2> /dev/null");
286 silc_rng_exec_command(rng, "ls -afiln ~ 2> /dev/null");
287 silc_rng_exec_command(rng, "ls -afiln /proc 2> /dev/null");
288 silc_rng_exec_command(rng, "ps -axww 2> /dev/null");
290 #ifdef SILC_RNG_DEBUG
291 SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
295 /* This function gets 'hard' noise from environment. This tries to
296 get the noise from /dev/random if available. */
298 static void silc_rng_get_hard_noise(SilcRng rng)
301 unsigned char buf[32];
304 /* Get noise from /dev/[u]random if available */
305 fd = open(rng->devrandom, O_RDONLY);
309 fcntl(fd, F_SETFL, O_NONBLOCK);
311 for (i = 0; i < 2; i++) {
312 len = read(fd, buf, sizeof(buf));
315 silc_rng_add_noise(rng, buf, len);
318 #ifdef SILC_RNG_DEBUG
319 SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
324 memset(buf, 0, sizeof(buf));
328 /* Execs command and gets noise from its output */
330 static void silc_rng_exec_command(SilcRng rng, char *command)
333 unsigned char buf[1024];
339 fd = popen(command, "r");
343 /* Get data as much as we can get into the buffer */
344 for (i = 0; i < sizeof(buf); i++) {
356 /* Add the buffer into random pool */
357 silc_rng_add_noise(rng, buf, i);
358 memset(buf, 0, sizeof(buf));
362 /* This function adds the contents of the buffer as noise into random
363 pool. After adding the noise the pool is stirred. */
365 void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, uint32 len)
369 pos = silc_rng_get_position(rng);
371 /* Add the buffer one by one into the pool */
372 for(i = 0; i < len; i++, buffer++) {
373 if(pos >= SILC_RNG_POOLSIZE)
375 rng->pool[pos++] ^= *buffer;
378 /* Stir random pool */
379 silc_rng_stir_pool(rng);
382 /* XOR's data into the pool */
384 static void silc_rng_xor(SilcRng rng, uint32 val, unsigned int pos)
387 rng->pool[pos] ^= val + val;
390 /* This function stirs the random pool by encrypting buffer in CFB
391 (cipher feedback) mode with SHA1 algorithm. */
393 static void silc_rng_stir_pool(SilcRng rng)
399 memcpy(iv, &rng->pool[16], sizeof(iv));
402 for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
403 rng->sha1->hash->transform(iv, rng->key);
404 iv[0] = rng->pool[i] ^= iv[0];
405 iv[1] = rng->pool[i + 1] ^= iv[1];
406 iv[2] = rng->pool[i + 2] ^= iv[2];
407 iv[3] = rng->pool[i + 3] ^= iv[3];
408 iv[4] = rng->pool[i + 4] ^= iv[4];
412 memcpy(rng->key, &rng->pool[silc_rng_get_position(rng)], sizeof(rng->key));
414 /* Second CFB pass */
415 for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
416 rng->sha1->hash->transform(iv, rng->key);
417 iv[0] = rng->pool[i] ^= iv[0];
418 iv[1] = rng->pool[i + 1] ^= iv[1];
419 iv[2] = rng->pool[i + 2] ^= iv[2];
420 iv[3] = rng->pool[i + 3] ^= iv[3];
421 iv[4] = rng->pool[i + 4] ^= iv[4];
424 memset(iv, 0, sizeof(iv));
427 /* Returns next position where data is fetched from the pool or
430 static uint32 silc_rng_get_position(SilcRng rng)
435 next = rng->state->next;
437 pos = rng->state->pos++;
438 if ((next->low != 0 && pos >= next->low) || (pos >= SILC_RNG_POOLSIZE))
439 rng->state->pos = rng->state->low;
441 #ifdef SILC_RNG_DEBUG
442 fprintf(stderr, "state: %p: low: %lu, pos: %lu\n",
443 rng->state, rng->state->low, rng->state->pos);
451 /* Returns random byte. */
453 unsigned char silc_rng_get_byte(SilcRng rng)
457 /* Get more soft noise after 64 bits threshhold */
458 if (rng->threshhold >= 8)
459 silc_rng_get_soft_noise(rng);
461 /* Get hard noise after 160 bits threshhold, zero the threshhold. */
462 if (rng->threshhold >= 20) {
464 silc_rng_get_hard_noise(rng);
467 return rng->pool[silc_rng_get_position(rng)];
470 /* Returns 16 bit random number */
472 uint16 silc_rng_get_rn16(SilcRng rng)
477 rn[0] = silc_rng_get_byte(rng);
478 rn[1] = silc_rng_get_byte(rng);
479 SILC_GET16_MSB(num, rn);
484 /* Returns 32 bit random number */
486 uint32 silc_rng_get_rn32(SilcRng rng)
491 rn[0] = silc_rng_get_byte(rng);
492 rn[1] = silc_rng_get_byte(rng);
493 rn[2] = silc_rng_get_byte(rng);
494 rn[3] = silc_rng_get_byte(rng);
495 SILC_GET32_MSB(num, rn);
500 /* Returns random number string. Returned string is in HEX format. */
502 unsigned char *silc_rng_get_rn_string(SilcRng rng, uint32 len)
505 unsigned char *string;
507 string = silc_calloc((len * 2 + 1), sizeof(unsigned char));
509 for (i = 0; i < len; i++)
510 sprintf(string + 2 * i, "%02x", silc_rng_get_byte(rng));
515 /* Returns random number binary data. */
517 unsigned char *silc_rng_get_rn_data(SilcRng rng, uint32 len)
522 data = silc_calloc(len + 1, sizeof(*data));
524 for (i = 0; i < len; i++)
525 data[i] = silc_rng_get_byte(rng);
530 /* Global RNG. This is global RNG that application can initialize so
531 that any part of code anywhere can use RNG without having to allocate
532 new RNG object everytime. If this is not initialized then these routines
533 will fail. Note: currently in SILC applications always initialize this. */
535 SilcRng global_rng = NULL;
537 /* Initialize global RNG. If `rng' is provided it is set as the global
538 RNG object (it can be allocated by the application for example). */
540 int silc_rng_global_init(SilcRng rng)
545 global_rng = silc_rng_alloc();
550 /* Uninitialize global RNG */
552 int silc_rng_global_uninit()
555 silc_rng_free(global_rng);
562 /* These are analogous to the functions above. */
564 unsigned char silc_rng_global_get_byte()
566 return global_rng ? silc_rng_get_byte(global_rng) : 0;
569 /* Return random byte as fast as possible. Reads from /dev/urandom if
570 available. If not then return from normal RNG (not so fast). */
572 unsigned char silc_rng_global_get_byte_fast()
575 unsigned char buf[1];
580 if (global_rng->fd_devurandom == -1) {
581 global_rng->fd_devurandom = open("/dev/urandom", O_RDONLY);
583 return silc_rng_global_get_byte();
584 fcntl(global_rng->fd_devurandom, F_SETFL, O_NONBLOCK);
587 if (read(global_rng->fd_devurandom, buf, sizeof(buf)) < 0)
588 return silc_rng_global_get_byte();
592 return silc_rng_global_get_byte();
596 uint16 silc_rng_global_get_rn16()
598 return global_rng ? silc_rng_get_rn16(global_rng) : 0;
601 uint32 silc_rng_global_get_rn32()
603 return global_rng ? silc_rng_get_rn32(global_rng) : 0;
606 unsigned char *silc_rng_global_get_rn_string(uint32 len)
608 return global_rng ? silc_rng_get_rn_string(global_rng, len) : NULL;
611 unsigned char *silc_rng_global_get_rn_data(uint32 len)
613 return global_rng ? silc_rng_get_rn_data(global_rng, len) : NULL;
616 void silc_rng_global_add_noise(unsigned char *buffer, uint32 len)
619 silc_rng_add_noise(global_rng, buffer, len);