updates
[silc.git] / lib / silccrypt / silcrng.c
index 7b01e552acdc989cc47eb7bdcab9cab01f6c5e71..7bed81e138e9997fd34e502a42ce24e969f516a4 100644 (file)
  * Created: Sun Mar  9 00:09:18 1997
  *
  * The original RNG was based on Secure Shell's random number generator
- * by Tatu Ylönen.  This RNG has been rewritten twice since the creation.
+ * by Tatu Ylönen and was used as reference when programming this RNG.  
+ * This RNG has been rewritten twice since the creation.
  */
 
 #include "silcincludes.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*/
 
+/* 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 1024
+
 static uint32 silc_rng_get_position(SilcRng rng);
 static void silc_rng_stir_pool(SilcRng rng);
 static void silc_rng_xor(SilcRng rng, uint32 val, unsigned int pos);
@@ -95,9 +112,9 @@ typedef struct SilcRngStateContext {
        random pool. This is allocated when RNG object is allocated and
        free'd when RNG object is free'd.
 
-   uint8 threshold
+   uint8 threshhold
 
-       Threshold to indicate when it is required to acquire more
+       Threshhold 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.
 
@@ -107,7 +124,9 @@ typedef struct SilcRngObjectStruct {
   unsigned char key[64];
   SilcRngState state;
   SilcHash sha1;
-  uint8 threshold;
+  uint8 threshhold;
+  char *devrandom;
+  int fd_devurandom;
 } SilcRngObject;
 
 /* Allocates new RNG object. */
@@ -119,12 +138,15 @@ SilcRng silc_rng_alloc()
   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);
 
+  new->devrandom = strdup("/dev/random");
+
   return new;
 }
 
@@ -135,7 +157,12 @@ void silc_rng_free(SilcRng rng)
   if (rng) {
     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);
+
     silc_free(rng);
   }
 }
@@ -178,23 +205,28 @@ void silc_rng_init(SilcRng 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. */
 
 static void silc_rng_get_soft_noise(SilcRng rng)
 {
+#ifndef SILC_WIN32
   struct tms ptime;
+#endif
   uint32 pos;
   
   pos = silc_rng_get_position(rng);
 
   silc_rng_xor(rng, clock(), 0);
+#ifndef SILC_WIN32
 #ifdef HAVE_GETPID
   silc_rng_xor(rng, getpid(), 1);
 #ifdef HAVE_GETPGID
-  silc_rng_xor(rng, getpgid(getpid() << 8), 2);
-  silc_rng_xor(rng, getpgid(getpid() << 8), 3);
+  silc_rng_xor(rng, getpgid(getpid()) << 8, 2);
+  silc_rng_xor(rng, getpgid(getpid()) << 8, 3);
 #endif
   silc_rng_xor(rng, getgid(), 4);
 #endif
@@ -202,7 +234,7 @@ static void silc_rng_get_soft_noise(SilcRng rng)
   silc_rng_xor(rng, getpgrp(), 5);
 #endif
 #ifdef HAVE_GETSID
-  silc_rng_xor(rng, getsid(getpid() << 16), 6);
+  silc_rng_xor(rng, getsid(getpid()) << 16, 6);
 #endif
   silc_rng_xor(rng, times(&ptime), 7);
   silc_rng_xor(rng, ptime.tms_utime, 8);
@@ -215,21 +247,24 @@ static void silc_rng_get_soft_noise(SilcRng rng)
   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++);
+  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++);
+  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 SILC_RNG_DEBUG
   SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
@@ -243,10 +278,10 @@ static void silc_rng_get_soft_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 -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 -asww 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));
@@ -258,11 +293,12 @@ static void silc_rng_get_medium_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;
 
@@ -282,13 +318,15 @@ static void silc_rng_get_hard_noise(SilcRng rng)
  out:
   close(fd);
   memset(buf, 0, sizeof(buf));
+#endif
 }
 
 /* Execs command and gets noise from its output */
 
 static void silc_rng_exec_command(SilcRng rng, char *command)
 {
-  char buf[1024];
+#ifndef SILC_WIN32
+  unsigned char buf[1024];
   FILE *fd;
   int i;
   int c;
@@ -312,8 +350,9 @@ static void silc_rng_exec_command(SilcRng rng, char *command)
   pclose(fd);
   
   /* Add the buffer into random pool */
-  silc_rng_add_noise(rng, buf, strlen(buf));
+  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 
@@ -353,7 +392,7 @@ static void silc_rng_stir_pool(SilcRng rng)
   uint32 iv[5];
 
   /* Get the IV */
-  memcpy(iv, &rng->pool[SILC_RNG_POOLSIZE - 256], sizeof(iv));
+  memcpy(iv, &rng->pool[16], sizeof(iv));
 
   /* First CFB pass */
   for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
@@ -409,15 +448,15 @@ static uint32 silc_rng_get_position(SilcRng rng)
 
 unsigned char silc_rng_get_byte(SilcRng rng)
 {
-  rng->threshold++;
+  rng->threshhold++;
 
-  /* Get more soft noise after 64 bits threshold */
-  if (rng->threshold >= 8)
+  /* Get more soft noise after 64 bits threshhold */
+  if (rng->threshhold >= 8)
     silc_rng_get_soft_noise(rng);
 
-  /* Get hard noise after 160 bits threshold, zero the threshold. */
-  if (rng->threshold >= 20) {
-    rng->threshold = 0;
+  /* Get hard noise after 160 bits threshhold, zero the threshhold. */
+  if (rng->threshhold >= 20) {
+    rng->threshhold = 0;
     silc_rng_get_hard_noise(rng);
   }
 
@@ -523,6 +562,33 @@ unsigned char silc_rng_global_get_byte()
   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). */
+
+unsigned char silc_rng_global_get_byte_fast()
+{
+#ifndef SILC_WIN32
+  unsigned char buf[1];
+
+  if (!global_rng)
+    return 0;
+
+  if (global_rng->fd_devurandom == -1) {
+    global_rng->fd_devurandom = open("/dev/urandom", O_RDONLY);
+    if (global_rng < 0)
+      return silc_rng_global_get_byte();
+    fcntl(global_rng->fd_devurandom, F_SETFL, O_NONBLOCK);
+  }
+
+  if (read(global_rng->fd_devurandom, buf, sizeof(buf)) < 0)
+    return silc_rng_global_get_byte();
+
+  return buf[0];
+#else
+  return silc_rng_global_get_byte();
+#endif
+}
+
 uint16 silc_rng_global_get_rn16()
 {
   return global_rng ? silc_rng_get_rn16(global_rng) : 0;