Added SILC Server library.
[silc.git] / lib / silccrypt / silcrng.c
index 9da70dd93f9449a03398dfba3fbf2f3147b8ced0..d32b708b1613dbee7a617df2d0ea86bc7b22b404 100644 (file)
@@ -2,15 +2,14 @@
 
   silcrng.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2003 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 /*
  * Created: Sun Mar  9 00:09:18 1997
  *
- * This RNG is based on Secure Shell's random number generator.
+ * The original RNG was based on Secure Shell's random number generator
+ * by Tatu Ylönen and was used as reference when programming this RNG.
+ * This RNG has been rewritten twice since the creation.
  */
-/* XXX: Some operations block resulting slow initialization.
- * XXX: I have some pending changes to make this better. */
 
-#include "silcincludes.h"
+#include "silc.h"
+
+#ifndef WIN32
+#ifdef HAVE_GETSID
+extern pid_t getsid (pid_t __pid);
+#endif
+
+#ifdef HAVE_GETPGID
+extern pid_t getpgid (pid_t __pid);
+#endif
+#endif
 
 #undef SILC_RNG_DEBUG
-/* #define SILC_RNG_DEBUG */
+/*#define SILC_RNG_DEBUG*/
+
+/* Number of states to fetch data from pool. */
+#define SILC_RNG_STATE_NUM 4
+
+/* Byte size of the random data pool. */
+#define SILC_RNG_POOLSIZE (20 * 48)
 
-/* 
+static SilcUInt32 silc_rng_get_position(SilcRng rng);
+static void silc_rng_stir_pool(SilcRng rng);
+static void silc_rng_xor(SilcRng rng, SilcUInt32 val, unsigned int pos);
+static void silc_rng_exec_command(SilcRng rng, char *command);
+static void silc_rng_get_hard_noise(SilcRng rng);
+static void silc_rng_get_medium_noise(SilcRng rng);
+static void silc_rng_get_soft_noise(SilcRng rng);
+
+/*
    SILC SilcRng State context.
 
    This object is used by the random number generator to provide
@@ -40,8 +63,8 @@
    from the same point of the pool. Short description of the fields
    following.
 
-   unsigned int low
-   unsigned int pos
+   SilcUInt32 low
+   SilcUInt32 pos
 
        The index for the random pool buffer. Lowest and current
        positions.
 
 */
 typedef struct SilcRngStateContext {
-  unsigned int low;
-  unsigned int pos;
+  SilcUInt32 low;
+  SilcUInt32 pos;
   struct SilcRngStateContext *next;
 } *SilcRngState;
 
-/* 
-   SILC Random Number Generator object. 
+/*
+   SILC Random Number Generator object.
 
    This object holds random pool which is used to generate the random
    numbers used by various routines needing cryptographically strong
@@ -88,28 +111,44 @@ typedef struct SilcRngStateContext {
        random pool. This is allocated when RNG object is allocated and
        free'd when RNG object is free'd.
 
+   SilcUInt8 threshold
+
+       Threshold to indicate when it is required to acquire more
+       noise from the environment.  More soft noise is acquired after
+       64 bits of output and hard noise every 160 bits of output.
+
 */
-typedef struct SilcRngObjectStruct {
+struct SilcRngStruct {
   unsigned char pool[SILC_RNG_POOLSIZE];
   unsigned char key[64];
   SilcRngState state;
   SilcHash sha1;
-} SilcRngObject;
+  SilcUInt8 threshold;
+  char *devrandom;
+  int fd_devurandom;
+};
 
 /* Allocates new RNG object. */
 
-SilcRng silc_rng_alloc()
+SilcRng silc_rng_alloc(void)
 {
   SilcRng new;
 
   SILC_LOG_DEBUG(("Allocating new RNG object"));
 
   new = silc_calloc(1, sizeof(*new));
+  new->fd_devurandom = -1;
 
   memset(new->pool, 0, sizeof(new->pool));
   memset(new->key, 0, sizeof(new->key));
   new->state = NULL;
-  silc_hash_alloc("sha1", &new->sha1);
+  if (!silc_hash_alloc("sha1", &new->sha1)) {
+    silc_free(new);
+    SILC_LOG_ERROR(("Could not allocate sha1 hash, probably not registered"));
+    return NULL;
+  }
+
+  new->devrandom = strdup("/dev/random");
 
   return new;
 }
@@ -119,14 +158,28 @@ SilcRng silc_rng_alloc()
 void silc_rng_free(SilcRng rng)
 {
   if (rng) {
+    SilcRngState t, n;
+
     memset(rng->pool, 0, sizeof(rng->pool));
     memset(rng->key, 0, sizeof(rng->key));
-    silc_free(rng->sha1);
+    silc_hash_free(rng->sha1);
+    silc_free(rng->devrandom);
+
+    if (rng->fd_devurandom != -1)
+      close(rng->fd_devurandom);
+
+    for (t = rng->state->next; t != rng->state; ) {
+      n = t->next;
+      silc_free(t);
+      t = n;
+    }
+    silc_free(rng->state);
+
     silc_free(rng);
   }
 }
 
-/* Initializes random number generator by getting noise from environment. 
+/* Initializes random number generator by getting noise from environment.
    The environmental noise is our so called seed. One should not call
    this function more than once. */
 
@@ -147,7 +200,7 @@ void silc_rng_init(SilcRng rng)
   first = rng->state;
   for (i = SILC_RNG_STATE_NUM - 1; i >= 1; i--) {
     next = silc_calloc(1, sizeof(*rng->state));
-    next->low = 
+    next->low =
       (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM));
     next->pos =
       (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM)) + 8;
@@ -163,39 +216,97 @@ void silc_rng_init(SilcRng rng)
   silc_rng_get_soft_noise(rng);
   silc_rng_get_medium_noise(rng);
   silc_rng_get_hard_noise(rng);
+  silc_rng_get_soft_noise(rng);
+  silc_free(rng->devrandom);
+  rng->devrandom = strdup("/dev/urandom");
 }
 
 /* This function gets 'soft' noise from environment. */
 
-void silc_rng_get_soft_noise(SilcRng rng)
+static void silc_rng_get_soft_noise(SilcRng rng)
 {
+#ifndef SILC_WIN32
   struct tms ptime;
-  
+#endif
+  SilcUInt32 pos;
+#ifdef HAVE_GETRUSAGE
+  struct rusage r;
+#endif
+
+  pos = silc_rng_get_position(rng);
+
   silc_rng_xor(rng, clock(), 0);
+#ifndef SILC_WIN32
+#ifdef HAVE_GETPID
   silc_rng_xor(rng, getpid(), 1);
-  silc_rng_xor(rng, getpgid(getpid() << 8), 2);
-  silc_rng_xor(rng, getpgid(getpid() << 8), 3);
+#ifdef HAVE_GETPGID
+  silc_rng_xor(rng, getpgid(getpid()) << 8, 2);
+  silc_rng_xor(rng, getpgid(getpid()) << 8, 3);
+#endif
   silc_rng_xor(rng, getgid(), 4);
+#endif
+#ifdef HAVE_GETPGRP
   silc_rng_xor(rng, getpgrp(), 5);
-  silc_rng_xor(rng, getsid(getpid() << 16), 6);
+#endif
+#ifdef HAVE_GETSID
+  silc_rng_xor(rng, getsid(getpid()) << 16, 6);
+#endif
   silc_rng_xor(rng, times(&ptime), 7);
   silc_rng_xor(rng, ptime.tms_utime, 8);
-  silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), 9);
-  silc_rng_xor(rng, (ptime.tms_stime + ptime.tms_cutime), 10);
-  silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), 11);
-  silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_stime), 12);
-  silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_cstime), 13);
-  silc_rng_xor(rng, (ptime.tms_utime ^ ptime.tms_stime), 14);
-  silc_rng_xor(rng, (ptime.tms_stime ^ ptime.tms_cutime), 15);
-  silc_rng_xor(rng, (ptime.tms_cutime + ptime.tms_stime), 16);
-  silc_rng_xor(rng, (ptime.tms_stime << 8), 17);
-  silc_rng_xor(rng, clock() << 4, 18);
-  silc_rng_xor(rng, getpgid(getpid() << 8), 19);
-  silc_rng_xor(rng, getpgrp(), 20);
-  silc_rng_xor(rng, getsid(getpid() << 16), 21);
-  silc_rng_xor(rng, times(&ptime), 22);
-  silc_rng_xor(rng, ptime.tms_utime, 23);
-  silc_rng_xor(rng, getpgrp(), 24);
+  silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), pos++);
+  silc_rng_xor(rng, (ptime.tms_stime + ptime.tms_cutime), pos++);
+  silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), pos++);
+  silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_stime), pos++);
+  silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_cstime), pos++);
+  silc_rng_xor(rng, (ptime.tms_utime ^ ptime.tms_stime), pos++);
+  silc_rng_xor(rng, (ptime.tms_stime ^ ptime.tms_cutime), pos++);
+  silc_rng_xor(rng, (ptime.tms_cutime + ptime.tms_stime), pos++);
+  silc_rng_xor(rng, (ptime.tms_stime << 8), pos++);
+#endif
+  silc_rng_xor(rng, clock() << 4, pos++);
+#ifndef SILC_WIN32
+#ifdef HAVE_GETPGID
+  silc_rng_xor(rng, getpgid(getpid()) << 8, pos++);
+#endif
+#ifdef HAVE_GETPGRP
+  silc_rng_xor(rng, getpgrp(), pos++);
+#endif
+#ifdef HAVE_SETSID
+  silc_rng_xor(rng, getsid(getpid()) << 16, pos++);
+#endif
+  silc_rng_xor(rng, times(&ptime), pos++);
+  silc_rng_xor(rng, ptime.tms_utime, pos++);
+#ifdef HAVE_GETPGRP
+  silc_rng_xor(rng, getpgrp(), pos++);
+#endif
+#endif
+#ifdef HAVE_GETRUSAGE
+  getrusage(RUSAGE_SELF, &r);
+  silc_rng_xor(rng, (r.ru_utime.tv_sec + r.ru_utime.tv_usec), pos++);
+  silc_rng_xor(rng, (r.ru_utime.tv_sec ^ r.ru_utime.tv_usec), pos++);
+  silc_rng_xor(rng, (r.ru_stime.tv_sec + r.ru_stime.tv_usec), pos++);
+  silc_rng_xor(rng, (r.ru_stime.tv_sec ^ r.ru_stime.tv_usec), pos++);
+  silc_rng_xor(rng, (r.ru_maxrss + r.ru_ixrss), pos++);
+  silc_rng_xor(rng, (r.ru_maxrss ^ r.ru_ixrss), pos++);
+  silc_rng_xor(rng, (r.ru_idrss + r.ru_idrss), pos++);
+  silc_rng_xor(rng, (r.ru_idrss ^ r.ru_idrss), pos++);
+  silc_rng_xor(rng, (r.ru_idrss << 16), pos++);
+  silc_rng_xor(rng, (r.ru_minflt + r.ru_majflt), pos++);
+  silc_rng_xor(rng, (r.ru_minflt ^ r.ru_majflt), pos++);
+  silc_rng_xor(rng, (r.ru_nswap + r.ru_oublock + r.ru_inblock), pos++);
+  silc_rng_xor(rng, (r.ru_nswap << 8), pos++);
+  silc_rng_xor(rng, (r.ru_inblock + r.ru_oublock), pos++);
+  silc_rng_xor(rng, (r.ru_inblock ^ r.ru_oublock), pos++);
+  silc_rng_xor(rng, (r.ru_msgsnd ^ r.ru_msgrcv), pos++);
+  silc_rng_xor(rng, (r.ru_nsignals + r.ru_msgsnd + r.ru_msgrcv), pos++);
+  silc_rng_xor(rng, (r.ru_nsignals << 16), pos++);
+  silc_rng_xor(rng, (r.ru_nvcsw + r.ru_nivcsw), pos++);
+  silc_rng_xor(rng, (r.ru_nvcsw ^ r.ru_nivcsw), pos++);
+#endif
+  
+#ifdef SILC_RNG_DEBUG
+  SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
+#endif
 
   /* Stir random pool */
   silc_rng_stir_pool(rng);
@@ -203,84 +314,94 @@ void silc_rng_get_soft_noise(SilcRng rng)
 
 /* This function gets noise from different commands */
 
-void silc_rng_get_medium_noise(SilcRng rng)
+static void silc_rng_get_medium_noise(SilcRng rng)
 {
-  silc_rng_exec_command(rng, "ps -lefaww 2> /dev/null");
-  silc_rng_exec_command(rng, "ls -afiln 2> /dev/null");
-  silc_rng_exec_command(rng, "ps -asww 2> /dev/null");
+  /* If getrusage is available, there is no need for shell commands */
+#ifdef HAVE_GETRUSAGE
+  return;
+#endif
+  silc_rng_exec_command(rng, "ps -leaww 2> /dev/null");
+  silc_rng_exec_command(rng, "ls -afiln ~ 2> /dev/null");
   silc_rng_exec_command(rng, "ls -afiln /proc 2> /dev/null");
-  /*
-  silc_rng_exec_command(rng, "ps -ef 2> /dev/null");
-  silc_rng_exec_command(rng, "ls -alin /dev 2> /dev/null");
-  */
+  silc_rng_exec_command(rng, "ps -axww 2> /dev/null");
+
+#ifdef SILC_RNG_DEBUG
+  SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
+#endif
 }
 
 /* This function gets 'hard' noise from environment. This tries to
    get the noise from /dev/random if available. */
 
-void silc_rng_get_hard_noise(SilcRng rng)
+static void silc_rng_get_hard_noise(SilcRng rng)
 {
-  char buf[32];
+#ifndef SILC_WIN32
+  unsigned char buf[32];
   int fd, len, i;
-  
-  /* Get noise from /dev/random if available */
-  fd = open("/dev/random", O_RDONLY);
+
+  /* Get noise from /dev/[u]random if available */
+  fd = open(rng->devrandom, O_RDONLY);
   if (fd < 0)
     return;
 
   fcntl(fd, F_SETFL, O_NONBLOCK);
 
-  for (i = 0; i < 8; i++) {
+  for (i = 0; i < 2; i++) {
     len = read(fd, buf, sizeof(buf));
     if (len <= 0)
       goto out;
     silc_rng_add_noise(rng, buf, len);
   }
 
+#ifdef SILC_RNG_DEBUG
+  SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
+#endif
+
  out:
   close(fd);
   memset(buf, 0, sizeof(buf));
+#endif
 }
 
 /* Execs command and gets noise from its output */
 
-void silc_rng_exec_command(SilcRng rng, char *command)
+static void silc_rng_exec_command(SilcRng rng, char *command)
 {
-  char buf[2048];
+#ifndef SILC_WIN32
+  unsigned char buf[1024];
   FILE *fd;
   int i;
   int c;
-  
+
   /* Open process */
   fd = popen(command, "r");
   if (!fd)
     return;
-  
+
   /* Get data as much as we can get into the buffer */
   for (i = 0; i < sizeof(buf); i++) {
     c = fgetc(fd);
-    if (c == EOF) {
-      if (!i)
-       return;
-      break; 
-    }
+    if (c == EOF)
+      break;
     buf[i] = c;
   }
-  
+
   pclose(fd);
-  
-  /* Add the buffer into random pool */
-  silc_rng_add_noise(rng, buf, strlen(buf));
-  memset(buf, 0, sizeof(buf));
+
+  if (i != 0) {
+    /* Add the buffer into random pool */
+    silc_rng_add_noise(rng, buf, i);
+    memset(buf, 0, sizeof(buf));
+  }
+#endif
 }
 
-/* This function adds the contents of the buffer as noise into random 
+/* This function adds the contents of the buffer as noise into random
    pool. After adding the noise the pool is stirred. */
 
-void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, 
-                       unsigned int len)
+void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, SilcUInt32 len)
 {
-  unsigned int i, pos;
+  SilcUInt32 i, pos;
 
   pos = silc_rng_get_position(rng);
 
@@ -297,44 +418,81 @@ void silc_rng_add_noise(SilcRng rng, unsigned char *buffer,
 
 /* XOR's data into the pool */
 
-void silc_rng_xor(SilcRng rng, unsigned int val, unsigned int pos)
+static void silc_rng_xor(SilcRng rng, SilcUInt32 val, unsigned int pos)
 {
-  assert(rng != NULL);
-  rng->pool[pos] ^= val + val;
+  SilcUInt32 tmp;
+
+  SILC_GET32_MSB(tmp, &rng->pool[pos]);
+  val ^= tmp + val;
+  SILC_PUT32_MSB(val, &rng->pool[pos]);
 }
 
-/* This function stirs the random pool by encrypting buffer in CFB 
+/* This function stirs the random pool by encrypting buffer in CFB
    (cipher feedback) mode with SHA1 algorithm. */
 
-void silc_rng_stir_pool(SilcRng rng)
+static void silc_rng_stir_pool(SilcRng rng)
 {
   int i;
-  unsigned long iv[5];
+  SilcUInt32 iv[5], tmp;
 
   /* Get the IV */
-  memcpy(iv, &rng->pool[SILC_RNG_POOLSIZE - 256], sizeof(iv));
+  SILC_GET32_MSB(iv[0], &rng->pool[16     ]);
+  SILC_GET32_MSB(iv[1], &rng->pool[16 +  4]);
+  SILC_GET32_MSB(iv[2], &rng->pool[16 +  8]);
+  SILC_GET32_MSB(iv[3], &rng->pool[16 + 12]);
+  SILC_GET32_MSB(iv[4], &rng->pool[16 + 16]);
 
   /* First CFB pass */
-  for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
-    rng->sha1->hash->transform(iv, rng->key);
-    iv[0] = rng->pool[i] ^= iv[0];
-    iv[1] = rng->pool[i + 1] ^= iv[1];
-    iv[2] = rng->pool[i + 2] ^= iv[2];
-    iv[3] = rng->pool[i + 3] ^= iv[3];
-    iv[4] = rng->pool[i + 4] ^= iv[4];
+  for (i = 0; i < SILC_RNG_POOLSIZE; i += 20) {
+    silc_hash_transform(rng->sha1, iv, rng->key);
+
+    SILC_GET32_MSB(tmp, &rng->pool[i]);
+    iv[0] ^= tmp;
+    SILC_PUT32_MSB(iv[0], &rng->pool[i]);
+
+    SILC_GET32_MSB(tmp, &rng->pool[i + 4]);
+    iv[1] ^= tmp;
+    SILC_PUT32_MSB(iv[1], &rng->pool[i + 4]);
+
+    SILC_GET32_MSB(tmp, &rng->pool[i + 8]);
+    iv[2] ^= tmp;
+    SILC_PUT32_MSB(iv[2], &rng->pool[i + 8]);
+
+    SILC_GET32_MSB(tmp, &rng->pool[i + 12]);
+    iv[3] ^= tmp;
+    SILC_PUT32_MSB(iv[3], &rng->pool[i + 12]);
+
+    SILC_GET32_MSB(tmp, &rng->pool[i + 16]);
+    iv[4] ^= tmp;
+    SILC_PUT32_MSB(iv[4], &rng->pool[i + 16]);
   }
 
   /* Get new key */
   memcpy(rng->key, &rng->pool[silc_rng_get_position(rng)], sizeof(rng->key));
 
   /* Second CFB pass */
-  for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
-    rng->sha1->hash->transform(iv, rng->key);
-    iv[0] = rng->pool[i] ^= iv[0];
-    iv[1] = rng->pool[i + 1] ^= iv[1];
-    iv[2] = rng->pool[i + 2] ^= iv[2];
-    iv[3] = rng->pool[i + 3] ^= iv[3];
-    iv[4] = rng->pool[i + 4] ^= iv[4];
+  for (i = 0; i < SILC_RNG_POOLSIZE; i += 20) {
+    silc_hash_transform(rng->sha1, iv, rng->key);
+
+    SILC_GET32_MSB(tmp, &rng->pool[i]);
+    iv[0] ^= tmp;
+    SILC_PUT32_MSB(iv[0], &rng->pool[i]);
+
+    SILC_GET32_MSB(tmp, &rng->pool[i + 4]);
+    iv[1] ^= tmp;
+    SILC_PUT32_MSB(iv[1], &rng->pool[i + 4]);
+
+    SILC_GET32_MSB(tmp, &rng->pool[i + 8]);
+    iv[2] ^= tmp;
+    SILC_PUT32_MSB(iv[2], &rng->pool[i + 8]);
+
+    SILC_GET32_MSB(tmp, &rng->pool[i + 12]);
+    iv[3] ^= tmp;
+    SILC_PUT32_MSB(iv[3], &rng->pool[i + 12]);
+
+    SILC_GET32_MSB(tmp, &rng->pool[i + 16]);
+    iv[4] ^= tmp;
+    SILC_PUT32_MSB(iv[4], &rng->pool[i + 16]);
   }
 
   memset(iv, 0, sizeof(iv));
@@ -343,10 +501,10 @@ void silc_rng_stir_pool(SilcRng rng)
 /* Returns next position where data is fetched from the pool or
    put to the pool. */
 
-unsigned int silc_rng_get_position(SilcRng rng)
+static SilcUInt32 silc_rng_get_position(SilcRng rng)
 {
   SilcRngState next;
-  unsigned int pos;
+  SilcUInt32 pos;
 
   next = rng->state->next;
 
@@ -355,7 +513,7 @@ unsigned int silc_rng_get_position(SilcRng rng)
     rng->state->pos = rng->state->low;
 
 #ifdef SILC_RNG_DEBUG
-    fprintf(stderr, "state: %p: low: %d, pos: %d\n", 
+    fprintf(stderr, "state: %p: low: %lu, pos: %lu\n",
            rng->state, rng->state->low, rng->state->pos);
 #endif
 
@@ -364,19 +522,58 @@ unsigned int silc_rng_get_position(SilcRng rng)
   return pos;
 }
 
-/* returns random byte. Every two byte is from pools low or high state. */
+/* Returns random byte. */
+
+SilcUInt8 silc_rng_get_byte(SilcRng rng)
+{
+  SilcUInt8 byte;
+
+  rng->threshold++;
+
+  /* Get more soft noise after 64 bits threshold */
+  if (rng->threshold >= 8)
+    silc_rng_get_soft_noise(rng);
+
+  /* Get hard noise after 160 bits threshold, zero the threshold. */
+  if (rng->threshold >= 20) {
+    rng->threshold = 0;
+    silc_rng_get_hard_noise(rng);
+  }
+
+  do byte = rng->pool[silc_rng_get_position(rng)]; while (byte == 0x00);
+  return byte;
+}
+
+/* Return random byte as fast as possible. Reads from /dev/urandom if
+   available. If not then return from normal RNG (not so fast). */
 
-unsigned char silc_rng_get_byte(SilcRng rng)
+SilcUInt8 silc_rng_get_byte_fast(SilcRng rng)
 {
-  return rng->pool[silc_rng_get_position(rng)];
+#ifndef SILC_WIN32
+  unsigned char buf[1];
+
+  if (rng->fd_devurandom == -1) {
+    rng->fd_devurandom = open("/dev/urandom", O_RDONLY);
+    if (rng->fd_devurandom < 0)
+      return silc_rng_get_byte(rng);
+    fcntl(rng->fd_devurandom, F_SETFL, O_NONBLOCK);
+  }
+
+  if (read(rng->fd_devurandom, buf, sizeof(buf)) < 0)
+    return silc_rng_get_byte(rng);
+
+  return buf[0] != 0x00 ? buf[0] : silc_rng_get_byte(rng);
+#else
+  return silc_rng_get_byte(rng);
+#endif
 }
 
 /* Returns 16 bit random number */
 
-unsigned short silc_rng_get_rn16(SilcRng rng)
+SilcUInt16 silc_rng_get_rn16(SilcRng rng)
 {
   unsigned char rn[2];
-  unsigned short num;
+  SilcUInt16 num;
 
   rn[0] = silc_rng_get_byte(rng);
   rn[1] = silc_rng_get_byte(rng);
@@ -387,10 +584,10 @@ unsigned short silc_rng_get_rn16(SilcRng rng)
 
 /* Returns 32 bit random number */
 
-unsigned int silc_rng_get_rn32(SilcRng rng)
+SilcUInt32 silc_rng_get_rn32(SilcRng rng)
 {
   unsigned char rn[4];
-  unsigned short num;
+  SilcUInt32 num;
 
   rn[0] = silc_rng_get_byte(rng);
   rn[1] = silc_rng_get_byte(rng);
@@ -401,9 +598,9 @@ unsigned int silc_rng_get_rn32(SilcRng rng)
   return num;
 }
 
-/* Returns random number string. Returned string is in HEX format. */
+/* Returns non-zero random number string. Returned string is in HEX format. */
 
-unsigned char *silc_rng_get_rn_string(SilcRng rng, unsigned int len)
+unsigned char *silc_rng_get_rn_string(SilcRng rng, SilcUInt32 len)
 {
   int i;
   unsigned char *string;
@@ -416,9 +613,9 @@ unsigned char *silc_rng_get_rn_string(SilcRng rng, unsigned int len)
   return string;
 }
 
-/* Returns random number binary data. */
+/* Returns non-zero random number binary data. */
 
-unsigned char *silc_rng_get_rn_data(SilcRng rng, unsigned int len)
+unsigned char *silc_rng_get_rn_data(SilcRng rng, SilcUInt32 len)
 {
   int i;
   unsigned char *data;
@@ -430,3 +627,79 @@ unsigned char *silc_rng_get_rn_data(SilcRng rng, unsigned int len)
 
   return data;
 }
+
+/* Global RNG. This is global RNG that application can initialize so
+   that any part of code anywhere can use RNG without having to allocate
+   new RNG object everytime.  If this is not initialized then these routines
+   will fail.  Note: currently in SILC applications always initialize this. */
+
+SilcRng global_rng = NULL;
+
+/* Initialize global RNG. If `rng' is provided it is set as the global
+   RNG object (it can be allocated by the application for example). */
+
+SilcBool silc_rng_global_init(SilcRng rng)
+{
+  if (rng) {
+    global_rng = rng;
+    return TRUE;
+  }
+
+  global_rng = silc_rng_alloc();
+  silc_rng_init(global_rng);
+
+  return TRUE;
+}
+
+/* Uninitialize global RNG */
+
+SilcBool silc_rng_global_uninit(void)
+{
+  if (global_rng) {
+    silc_rng_free(global_rng);
+    global_rng = NULL;
+  }
+
+  return TRUE;
+}
+
+/* These are analogous to the functions above. */
+
+SilcUInt8 silc_rng_global_get_byte(void)
+{
+  return global_rng ? silc_rng_get_byte(global_rng) : 0;
+}
+
+/* Return random byte as fast as possible. Reads from /dev/urandom if
+   available. If not then return from normal RNG (not so fast). */
+
+SilcUInt8 silc_rng_global_get_byte_fast(void)
+{
+  return global_rng ? silc_rng_get_byte_fast(global_rng) : 0;
+}
+
+SilcUInt16 silc_rng_global_get_rn16(void)
+{
+  return global_rng ? silc_rng_get_rn16(global_rng) : 0;
+}
+
+SilcUInt32 silc_rng_global_get_rn32(void)
+{
+  return global_rng ? silc_rng_get_rn32(global_rng) : 0;
+}
+
+unsigned char *silc_rng_global_get_rn_string(SilcUInt32 len)
+{
+  return global_rng ? silc_rng_get_rn_string(global_rng, len) : NULL;
+}
+
+unsigned char *silc_rng_global_get_rn_data(SilcUInt32 len)
+{
+  return global_rng ? silc_rng_get_rn_data(global_rng, len) : NULL;
+}
+
+void silc_rng_global_add_noise(unsigned char *buffer, SilcUInt32 len)
+{
+  if (global_rng)
+    silc_rng_add_noise(global_rng, buffer, len);
+}