Skip to content
Snippets Groups Projects
Select Git revision
  • 4c658fbfae00580593b9d82a25aee182f313773a
  • master default
  • wip-slh-dsa-sha2-128s
  • master-updates
  • release-3.10-fixes
  • getopt-prototype
  • fix-bcrypt-warning
  • refactor-hmac
  • wip-use-alignas
  • trim-sha3-context
  • fix-gitlab-ci
  • check-fat-emulate
  • delete-digest_func-size
  • slh-dsa-shake-128f-nettle
  • slh-dsa-shake-128s-nettle
  • slh-dsa-shake-128s
  • delete-openpgp
  • ppc64-sha512
  • delete-md5-compat
  • cleanup-hmac-tests
  • ppc64-sha256
  • nettle_3.10.2_release_20250626
  • nettle_3.10.1_release_20241230
  • nettle_3.10_release_20240616
  • nettle_3.10rc2
  • nettle_3.10rc1
  • nettle_3.9.1_release_20230601
  • nettle_3.9_release_20230514
  • nettle_3.8.1_release_20220727
  • nettle_3.8_release_20220602
  • nettle_3.7.3_release_20210606
  • nettle_3.7.2_release_20210321
  • nettle_3.7.1_release_20210217
  • nettle_3.7_release_20210104
  • nettle_3.7rc1
  • nettle_3.6_release_20200429
  • nettle_3.6rc3
  • nettle_3.6rc2
  • nettle_3.6rc1
  • nettle_3.5.1_release_20190627
  • nettle_3.5_release_20190626
41 results

desCode.h

Blame
  • unix_random.c 10.22 KiB
    /* unix_random.c
     *
     * Randomness polling on unix, using yarrow and ideas from Peter
     * Gutmann's cryptlib. */
    
    /* lsh, an implementation of the ssh protocol
     *
     * Copyright (C) 2000, 2001 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     */
    
    #if HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #include <assert.h>
    #include <string.h>
    
    #include <fcntl.h>
    
    #if HAVE_UNISTD_H
    #include <unistd.h>
    #endif
    
    #include <sys/types.h>
    #include <sys/time.h> /* Must be included before sys/resource.h */
    #include <sys/resource.h>
    #include <sys/stat.h>
    
    #include "nettle/yarrow.h"
    
    #include "randomness.h"
    
    #include "crypto.h"
    #include "environ.h"
    #include "format.h"
    #include "lock_file.h"
    #include "lsh_string.h"
    #include "reaper.h"
    #include "xalloc.h"
    #include "werror.h"
    
    #include "unix_random.c.x"
    
    /* GABA:
       (class
         (name unix_random)
         (super randomness)
         (vars
           (seed_file_fd . int)
           (lock object lsh_file_lock_info)
           (yarrow . "struct yarrow256_ctx")
           (sources array "struct yarrow_source" RANDOM_NSOURCES)
    
           ; For the SOURCE_TRIVIA, count the number of invocations per second
           (previous_time . long)
           (time_count . unsigned)
    
           ; For SOURCE_DEVICE
           (device_fd . int)
           (device_last_read . time_t)))
    */
    
    static struct unix_random *the_random = NULL;
    
    static int
    write_seed_file(struct yarrow256_ctx *ctx,
    	       int fd)
    {
      if (lseek(fd, 0, SEEK_SET) < 0)
        {
          werror("Seeking to beginning of seed file failed!? %e\n", errno);
          return 0;
        }
    
      if (!write_raw(fd, YARROW256_SEED_FILE_SIZE, ctx->seed_file))
        {
          werror("Overwriting seed file failed: %e\n", errno);
          return 0;
        }
    
      return 1;
    }
    
    static struct lsh_string *
    read_seed_file(int fd)
    {
      struct lsh_string *seed;
      
      if (lseek(fd, 0, SEEK_SET) < 0)
        {
          werror("Seeking to beginning of seed file failed!? %e\n", errno);
          return NULL;
        }
    
      seed = io_read_file_raw(fd, YARROW256_SEED_FILE_SIZE + 1);
      if (!seed)
        werror("Couldn't read seed file %e\n", errno);
      return seed;
    }
    
    static int
    read_initial_seed_file(struct yarrow256_ctx *ctx,
    		       int fd)
    {
      struct lsh_string *seed;
      struct stat sbuf;
    
      if (fstat(fd, &sbuf) < 0)
        {
          werror("Couldn't stat seed file %e\n", errno);
          return 0;
        }
    
      /* Check permissions */
      if (sbuf.st_uid != getuid())
        {
          werror("Seed file not owned by you!\n");
          return 0;
        }
    
      if (sbuf.st_mode & (S_IRWXG | S_IRWXO))
        {
          werror("Too permissive permissions on seed-file.\n");
          return 0;
        }
      
      seed = read_seed_file(fd);
      if (!seed)
        return 0;
      
      if (lsh_string_length(seed) < YARROW256_SEED_FILE_SIZE)
        {
          werror("Seed file too short\n");
          lsh_string_free(seed);
          return 0;
        }
      
      yarrow256_seed(ctx, STRING_LD(seed));
      lsh_string_free(seed);
    
      assert(yarrow256_is_seeded(ctx));
    
      if (lseek(fd, 0, SEEK_SET) < 0)
        {
          werror("Seeking to beginning of seed file failed!? %e\n", errno);
          return 0;
        }
    
      return 1;
    }
    
    static void
    update_seed_file(struct unix_random *self)
    {
      struct resource *lock;
      verbose("Overwriting seed file.\n");
    
      lock = LSH_FILE_LOCK(self->lock, 0);
      if (!lock)
        {
          werror("Failed to lock seed file, so not overwriting it now.\n");
        }
      else
        {
          struct lsh_string *s = read_seed_file(self->seed_file_fd);
    
          write_seed_file(&self->yarrow, self->seed_file_fd);
          KILL_RESOURCE(lock);
    
          /* Mix in the old seed file, it might have picked up
           * some randomness. */
          if (s)
    	{
    	  yarrow256_update(&self->yarrow, RANDOM_SOURCE_NEW_SEED,
    			   0, STRING_LD(s));
    	  lsh_string_free(s);
    	}
        }
    }
    
    static int
    do_trivia_source(struct unix_random *self, int init)
    {
      struct {
        struct timeval now;
    #if HAVE_GETRUSAGE
        struct rusage rusage;
    #endif
        unsigned count;
        pid_t pid;
      } event;
      
      unsigned entropy = 0;
    
      if (gettimeofday(&event.now, NULL) < 0)
        fatal("gettimeofday failed %e\n", errno);
    #if HAVE_GETRUSAGE
      if (getrusage(RUSAGE_SELF, &event.rusage) < 0)
        fatal("getrusage failed %e\n", errno);
    #endif
    
      event.count = 0;
      if (init)
        {
          self->time_count = 0;
        }
      else
        {
          event.count = self->time_count++;
          
          if (event.now.tv_sec != self->previous_time)
    	{
    	  /* Count one bit of entropy if we either have more than two
    	   * invocations in one second, or more than two seconds
    	   * between invocations. */
    	  if ( (self->time_count > 2)
    	       || ( (event.now.tv_sec - self->previous_time) > 2) )
    	    entropy++;
    
    	  self->time_count = 0;
    	}
        }
      self->previous_time = event.now.tv_sec;
      event.pid = getpid();
    
      return yarrow256_update(&self->yarrow, RANDOM_SOURCE_TRIVIA, entropy,
    			  sizeof(event), (const uint8_t *) &event);
    }
    
    #define DEVICE_READ_SIZE 10
    static int
    do_device_source(struct unix_random *self, int init)
    {
      time_t now = time(NULL);
      
      if (init)
        {
          self->device_fd = open("/dev/urandom", O_RDONLY);
          if (self->device_fd < 0)
    	return 0;
    
          self->device_last_read = now;
        }
    
      if ( (self->device_fd > 0)
           && (init || ( (now - self->device_last_read) > 60)))
        {
          /* More than a minute since we last read the device */
          uint8_t buf[DEVICE_READ_SIZE];
          uint32_t done;
    
          /* FIXME: Use lsh_string_read instead? */
          for (done = 0; done < sizeof(buf) ;)
    	{
    	  int res;
    	  do
    	    res = read(self->device_fd, buf + done, sizeof(buf) - done);
    	  while (res < 0 && errno == EINTR);
    
    	  if (res <= 0)
    	    {
    	      if (res < 0)
    		werror("Failed to read /dev/urandom %e\n", errno);
    	      else
    		werror("Failed to read /dev/urandom: end of filee\n");
    
    	      return 0;
    	    }
    
    	  done += res;
    	}
          self->device_last_read = now;
          
          return yarrow256_update(&self->yarrow, RANDOM_SOURCE_DEVICE,
    			      10, /* Estimate 10 bits of entropy */
    			      sizeof(buf), buf);
        }
      return 0;
    }
    #undef DEVICE_READ_SIZE
    
    
    static void
    do_unix_random(struct randomness *s,
    	       uint32_t length,
    	       uint8_t *dst)
    {
      CAST(unix_random, self, s);
    
      /* First do a "fast" poll. */
      int trivia_reseed = do_trivia_source(self, 0);
      int device_reseed = do_device_source(self, 0);
    
      if (trivia_reseed || device_reseed)
        update_seed_file(self);
    
      /* Ok, generate some output */
      yarrow256_random(&self->yarrow, length, dst);
    }
    
    static void
    do_unix_random_add(struct randomness *s,
    		   enum random_source_type type,
    		   uint32_t length,
    		   const uint8_t *data)
    {
      CAST(unix_random, self, s);
    
      unsigned entropy;
      
      switch(type)
        {
        case RANDOM_SOURCE_SECRET:
          /* Count one bit of entropy per character in a password or
           * key */
          entropy = length;
          break;
        case RANDOM_SOURCE_REMOTE:
          /* Count one bit of entropy if we have two bytes of padding. */
          entropy = (length >= 2);
          break;
    
        default:
          fatal("Internal error\n");
        }
    
      if (yarrow256_update(&self->yarrow, type,
    		       entropy,
    		       length, data))
        update_seed_file(self);
    }
    
    struct randomness *
    random_init(struct lsh_string *seed_file_name)
    {
      assert(!the_random);
    
      trace("random_init\n");
      {
        NEW(unix_random, self);
        struct resource *lock;
    
        self->super.quality = RANDOM_GOOD;
        self->super.random = do_unix_random;
        self->super.add = do_unix_random_add;
    
        yarrow256_init(&self->yarrow, RANDOM_NSOURCES, self->sources);
        
        verbose("Reading seed-file `%S'\n", seed_file_name);
        
        self->lock
          = make_lsh_file_lock_info(ssh_format("%lS.lock", seed_file_name));
    
        trace("random_init, locking seed file...\n");
        
        lock = LSH_FILE_LOCK(self->lock, 5);
    
        if (!lock)
          {
    	werror("Could not lock seed-file `%S'\n", seed_file_name);
    	KILL(self);
    	return NULL;
          }
    
        trace("random_init, seed file locked successfully.\n");
        
        self->seed_file_fd = open(lsh_get_cstring(seed_file_name), O_RDWR);
        if (self->seed_file_fd < 0)
          {
    	werror("No seed file. Please create one by running\n");
    	werror("lsh-make-seed -o \"%S\".\n", seed_file_name);
    
    	KILL_RESOURCE(lock);
    	KILL(self);
    	return NULL;
          }
    
        trace("random_init, reading seed file...\n");
        
        if (!read_initial_seed_file(&self->yarrow, self->seed_file_fd))
          {
    	KILL_RESOURCE(lock);
    	KILL(self);
    	return NULL;
          }
    
        trace("random_init, seed file read successfully.\n");
        
        assert(yarrow256_is_seeded(&self->yarrow));
        
        /* Ok, now yarrow is initialized. */
        do_device_source(self, 1);
        do_trivia_source(self, 1);
    
        /* Mix that data in before generating any output. */
        yarrow256_force_reseed(&self->yarrow);
    
        /* Overwrite seed file. */
        if (!write_seed_file(&self->yarrow, self->seed_file_fd))
          {
    	KILL_RESOURCE(lock);
    	KILL(self);
    	return NULL;
          }
    
        trace("random_init: All done, releasing lock.\n");
    
        KILL_RESOURCE(lock);
        
        the_random = self;
        return &self->super;
      }
    }
    
    struct randomness *
    make_user_random(const char *home)
    {
      struct randomness *r;
      struct lsh_string *file_name;
      const char *env_name;
    
      env_name = getenv(ENV_SEED_FILE);
      if (env_name)
        file_name = make_string(env_name);
      else
        {
          if (!home)
    	{
    	  werror("Please set HOME in your environment.\n");
    	  return NULL;
    	}
    
          file_name = ssh_format("%lz/.lsh/yarrow-seed-file", home);
        }
      r = random_init(file_name);
      
      lsh_string_free(file_name);
    
      return r;
    }
    
    struct randomness *
    make_system_random(void)
    {
      struct randomness *r;
      struct lsh_string *file_name;
      
      const char *env_name;
    
      env_name = getenv(ENV_SEED_FILE);
    
      /* FIXME: What's a proper place for this? */
      file_name = make_string(env_name ? env_name
    			  : "/var/spool/lsh/yarrow-seed-file");
    
      r = random_init(file_name);
      
      lsh_string_free(file_name);
    
      return r;
    }