randomness.c 4.43 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1 2
/* randomness.c
 *
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 *
 * $Id$ */

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 1998 Niels Mller
 *
 * 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.
 *
 * 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 GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Niels Möller's avatar
Niels Möller committed
24 25 26
 */

#include "randomness.h"
27

28 29 30
#include "werror.h"

#include "crypto.h"
31
#include "xalloc.h"
Niels Möller's avatar
Niels Möller committed
32

33 34 35 36 37 38 39 40 41 42
#include <errno.h>

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>

#include <string.h>

43 44 45 46 47 48
#define CLASS_DEFINE
#include "randomness.h.x"
#undef CLASS_DEFINE

#include "randomness.c.x"

Niels Möller's avatar
Niels Möller committed
49
/* Random */
50 51 52 53 54 55 56 57 58 59 60
/* CLASS:
   (class
     (name poor_random)
     (super randomness)
     (vars
       (hash object hash_instance)
       (pos simple UINT32)
       (buffer space UINT8)))
*/

static void do_poor_random(struct randomness *r, UINT32 length, UINT8 *dst)
Niels Möller's avatar
Niels Möller committed
61
{
62
  CAST(poor_random, self, r);
Niels Möller's avatar
Niels Möller committed
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

  while(length)
    {
      UINT32 available = self->hash->hash_size - self->pos;
      UINT32 to_copy;
      
      if (!available)
	{
	  time_t now = time(NULL); /* To avoid cycles */
	  HASH_UPDATE(self->hash, sizeof(now), (UINT8 *) &now);
	  HASH_UPDATE(self->hash, self->hash->hash_size,
		      self->buffer);
	  HASH_DIGEST(self->hash, self->buffer);

	  available = self->hash->hash_size;
	  self->pos = 0;
	}
      to_copy = MIN(available, length);

      memcpy(dst, self->buffer + self->pos, to_copy);
      length -= to_copy;
      dst += to_copy;
      self->pos += to_copy;
    }
}

struct randomness *make_poor_random(struct hash_algorithm *hash,
				    struct lsh_string *init)
{
92
  NEW(poor_random, self);
93 94 95
  time_t now = time(NULL); 
  pid_t pid = getpid();
  
Niels Möller's avatar
Niels Möller committed
96
  self->super.random = do_poor_random;
97 98
  self->super.quality = 0;
  
Niels Möller's avatar
Niels Möller committed
99
  self->hash = MAKE_HASH(hash);
100 101
  self->buffer = lsh_space_alloc(hash->hash_size);
  
Niels Möller's avatar
Niels Möller committed
102
  HASH_UPDATE(self->hash, sizeof(now), (UINT8 *) &now);
103
  HASH_UPDATE(self->hash, sizeof(pid), (UINT8 *) &pid);
Niels Möller's avatar
Niels Möller committed
104
  
105 106 107 108 109 110 111
  if (init)
    {
      HASH_UPDATE(self->hash, init->length, init->data);
      lsh_string_free(init);
    }
  HASH_DIGEST(self->hash, self->buffer);

Niels Möller's avatar
Niels Möller committed
112 113 114 115
  self->pos = 0;

  return &self->super;
}
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198

/* CLASS:
   (class
     (name device_random)
     (super randomness)
     (vars
       (fd . int)))
*/

static void do_device_random(struct randomness *r, UINT32 length, UINT8 *dst)
{
  CAST(device_random, self, r);

  while(length)
    {
      int n = read(self->fd, dst, length);

      if (!n)
	fatal("do_device_random: EOF on random source.\n");

      if (n<0)
	switch(errno)
	  {
	  case EINTR:
	    break;
	  default:
	    fatal("Read from random device failed (errno = %d): %s\n",
		  errno, strerror(errno));
	  }
      else
	{
	  length -= n;
	  dst += n;
	}
    }
}

/* NOTE: In most cases, blocking while waiting for more entropy to
 * arrive is not acceptable. So use /dev/urandom, not /dev/random. The
 * alternative is to read a smaller seed from /dev/random at startup,
 * and use an internal pseudorandom generator. That
 * would be friendlier to other applications, but would not buy as
 * more security, as /dev/urandom should degenerate to a fairly strong
 * pseudorandom generator when it runs out of entropy. */

struct randomness *make_device_random(const char *device)
{
  int fd = open(device, O_RDONLY);

  if (fd < 0)
    {
      werror("make_device_random: Failed to open '%s' (errno = %d): %s\n",
	     device, errno, strerror(errno));
      return NULL;
    }
  else
    {
      NEW(device_random, self);
      
      self->super.random = do_device_random;

      /* The quality depends on the used device. */
      self->super.quality = 0;
      self->fd = fd;
      
      return &self->super;
    }
}

struct randomness *make_reasonably_random(void)
{
  struct randomness *r = make_device_random("/dev/urandom");

  if (r)
    r->quality = 1;
  else
    {
      werror("Warning: Falling back to an insecure pseudorandom generator.\n");
      r = make_poor_random(&sha_algorithm, NULL);
    }

  return r;
}