Commit message: random: Hash continuous areas in the csprng pool.

* cipher/random.c (mix_pool): Store the first hash at the end of the
pool.
--

This fixes a long standing bug (since 1998) in Libgcrypt and GnuPG.
An attacker who obtains 580 bytes of the random number from the
standard RNG can trivially predict the next 20 bytes of output.

This bug does not affect the default generation of
keys because running gpg for key creation creates at most 2 keys from
the pool: For a single 4096 bit RSA key 512 byte of random are
required and thus for the second key (encryption subkey), 20 bytes
could be predicted from the the first key.  However, the security of
an OpenPGP key depends on the primary key (which was generated first)
and thus the 20 predictable bytes should not be a problem.  For the
default key length of 2048 bit nothing will be predictable.

For the former default of DSA+Elgamal key it is complicate to give an
answer: For 2048 bit keys a pool of 30 non-secret candidate primes of
about 300 bits each are first created.  This reads at least 1140 bytes
from the pool and thus parts could be predicted.  At some point a 256
bit secret is read from the pool; which in the worst case might be
partly predictable.

The bug was found and reported by Felix Dörre and Vladimir Klebanov,
Karlsruhe Institute of Technology.  A paper describing the problem in
detail will shortly be published.

CVE-id: CVE-2016-6313
Signed-off-by: Werner Koch <wk@gnupg.org>
CVE: CVE-2016-6313
CWE: CWE-200
Vulnerability description: The mixing functions in the random number generator in Libgcrypt before 1.5.6, 1.6.x before 1.6.6, and 1.7.x before 1.7.3 and GnuPG before 1.4.21 make it easier for attackers to obtain the values of 160 bits by leveraging knowledge of the previous 4640 bits.

Project: gnupg
File: random.c

randomize_buffer( byte *buffer, size_t length, int level )
{
    char *p = get_random_bits( length*8, level, 1 );
    memcpy( buffer, p, length );
    xfree(p);
}

Project: gnupg
File: random.c

read_random_source( int requester, size_t length, int level )
{
    static int (*fnc)(void (*)(const void*, size_t, int), int,
						    size_t, int) = NULL;
    if( !fnc ) {
	if( !is_initialized )
	    initialize();
	fnc = getfnc_gather_random();
	if( !fnc ) {
	    faked_rng = 1;
	    fnc = gather_faked;
	}
	if( !requester && !length && !level )
	    return; /* init only */
    }
    if( (*fnc)( add_randomness, requester, length, level ) < 0 )
	log_fatal("No way to gather entropy for the RNG\n");
}

Project: gnupg
File: random.c

gather_faked( void (*add)(const void*, size_t, int), int requester,
	      size_t length, int level )
{
    static int initialized=0;
    size_t n;
    char *buffer, *p;

    if( !initialized ) {
	log_info(_("WARNING: using insecure random number generator!!\n"));
	tty_printf(_("The random number generator is only a kludge to let\n"
		   "it run - it is in no way a strong RNG!\n\n"
		   "DON'T USE ANY DATA GENERATED BY THIS PROGRAM!!\n\n"));
	initialized=1;
#ifdef HAVE_RAND
	srand(make_timestamp()*getpid());
#else
	srandom(make_timestamp()*getpid());
#endif
    }

    p = buffer = xmalloc( length );
    n = length;
#ifdef HAVE_RAND
    while( n-- )
	*p++ = ((unsigned)(1 + (int) (256.0*rand()/(RAND_MAX+1.0)))-1);
#else
    while( n-- )
	*p++ = ((unsigned)(1 + (int) (256.0*random()/(RAND_MAX+1.0)))-1);
#endif
    add_randomness( buffer, length, requester );
    xfree(buffer);
    return 0; /* okay */
}

Project: gnupg
File: random.c

randomize_mpi (MPI mpi, size_t nbits, int level)
{
  unsigned char *buffer;

  buffer = get_random_bits (nbits, level, mpi_is_secure (mpi));
  mpi_set_buffer (mpi, buffer, (nbits+7)/8, 0);
  xfree (buffer);
}

Project: gnupg
File: random.c

add_randomness( const void *buffer, size_t length, int source )
{
    const byte *p = buffer;

    if( !is_initialized )
	initialize();
    rndstats.addbytes += length;
    rndstats.naddbytes++;
    while( length-- ) {
	rndpool[pool_writepos++] ^= *p++;
	if( pool_writepos >= POOLSIZE ) {
	    if( source > 1 )
		pool_filled = 1;
	    pool_writepos = 0;
	    mix_pool(rndpool); rndstats.mixrnd++;
	    just_mixed = !length;
	}
    }
}

Project: gnupg
File: random.c

lock_seed_file (int fd, const char *fname, int for_write)
{
#if LOCK_SEED_FILE
  struct flock lck;
  struct timeval tv;
  int backoff=0;

  if (no_seed_file_locking)
    return 0;

  /* We take a lock on the entire file. */
  memset (&lck, 0, sizeof lck);
  lck.l_type = for_write? F_WRLCK : F_RDLCK;
  lck.l_whence = SEEK_SET;

  while (fcntl (fd, F_SETLK, &lck) == -1)
    {
      if (errno != EAGAIN && errno != EACCES)
        {
          log_info (_("can't lock `%s': %s\n"), fname, strerror (errno));
          return -1;
        }

      if (backoff > 2) /* Show the first message after ~3.75 seconds. */
        log_info( _("waiting for lock on `%s'...\n"), fname);

      tv.tv_sec = backoff;
      tv.tv_usec = 250000;
      select (0, NULL, NULL, NULL, &tv);
      if (backoff < 10)
        backoff++ ;
    }
#endif /*LOCK_SEED_FILE*/
  return 0;
}

Project: gnupg
File: random.c

random_dump_stats()
{
    fprintf(stderr,
	    "random usage: poolsize=%d mixed=%lu polls=%lu/%lu added=%lu/%lu\n"
	    "              outmix=%lu getlvl1=%lu/%lu getlvl2=%lu/%lu\n",
	POOLSIZE, rndstats.mixrnd, rndstats.slowpolls, rndstats.fastpolls,
		  rndstats.naddbytes, rndstats.addbytes,
	rndstats.mixkey, rndstats.ngetbytes1, rndstats.getbytes1,
		    rndstats.ngetbytes2, rndstats.getbytes2 );
}

Project: gnupg
File: random.c

mix_pool(byte *pool)
{
    char *hashbuf = pool + POOLSIZE;
    char *p, *pend;
    int i, n;
    RMD160_CONTEXT md;

    rmd160_init( &md );
#if DIGESTLEN != 20
#error must have a digest length of 20 for ripe-md-160
#endif
    /* pool -> pool' */
    pend = pool + POOLSIZE;
    memcpy(hashbuf, pend - DIGESTLEN, DIGESTLEN );
    memcpy(hashbuf+DIGESTLEN, pool, BLOCKLEN-DIGESTLEN);
    rmd160_mixblock( &md, hashbuf);
    memcpy(pool, hashbuf, DIGESTLEN);

    /* Loop for the remaining iterations.  */
    p = pool;
    for( n=1; n < POOLBLOCKS; n++ ) {
	if( p + BLOCKLEN < pend )
	    memcpy(hashbuf, p, BLOCKLEN);
	else {
	    char *pp = p;
	    for(i=0; i < BLOCKLEN; i++ ) {
		if( pp >= pend )
		    pp = pool;
		hashbuf[i] = *pp++;
	    }
	}

	rmd160_mixblock( &md, hashbuf);
        p += DIGESTLEN;
	memcpy(p, hashbuf, DIGESTLEN);
    }
    burn_stack (384); /* for the rmd160_mixblock() */
}

Project: gnupg
File: random.c

getfnc_fast_random_poll (void))( void (*)(const void*, size_t, int), int)
{
#ifdef USE_RNDW32
  return rndw32_gather_random_fast;
#endif
  return NULL;
}

Project: gnupg
File: random.c

getfnc_gather_random (void))(void (*)(const void*, size_t, int), int,
                        size_t, int)
{
#ifdef USE_ALL_RANDOM_MODULES
  static int (*fnc)(void (*)(const void*, size_t, int), int, size_t, int);

  if (fnc)
    return fnc;
# ifdef USE_RNDLINUX
  if ( !access (NAME_OF_DEV_RANDOM, R_OK)
       && !access (NAME_OF_DEV_URANDOM, R_OK))
    {
      fnc = rndlinux_gather_random;
      return fnc;
    }
# endif
# ifdef USE_RNDEGD
  if ( rndegd_connect_socket (1) != -1 )
    {
      fnc = rndegd_gather_random;
      return fnc;
    }
# endif
# ifdef USE_RNDUNIX
  fnc = rndunix_gather_random;
  return fnc;
# endif

  log_fatal (_("no entropy gathering module detected\n"));

#else
# ifdef USE_RNDLINUX
  return rndlinux_gather_random;
# endif
# ifdef USE_RNDUNIX
  return rndunix_gather_random;
# endif
# ifdef USE_RNDVMS
  return rndvms_gather_random;
# endif
# ifdef USE_RNDEGD
  return rndegd_gather_random;
# endif
# ifdef USE_RNDW32
  return rndw32_gather_random;
# endif
# ifdef USE_RNDRISCOS
  return rndriscos_gather_random;
# endif
#endif
  return NULL;
}

Project: gnupg
File: random.c

burn_stack (int bytes)
{
    char buf[128];

    wipememory(buf,sizeof buf);
    bytes -= sizeof buf;
    if (bytes > 0)
        burn_stack (bytes);
}

Project: gnupg
File: random.c

update_random_seed_file()
{
    ulong *sp, *dp;
    int fd, i;

    if( !seed_file_name || !is_initialized || !pool_filled )
	return;
    if( !allow_seed_file_update ) {
	log_info(_("note: random_seed file not updated\n"));
	return;
    }


    /* copy the entropy pool to a scratch pool and mix both of them */
    for(i=0,dp=(ulong*)keypool, sp=(ulong*)rndpool;
				    i < POOLWORDS; i++, dp++, sp++ ) {
	*dp = *sp + ADD_VALUE;
    }
    mix_pool(rndpool); rndstats.mixrnd++;
    mix_pool(keypool); rndstats.mixkey++;

#if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__)
    fd = open( seed_file_name, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,
							S_IRUSR|S_IWUSR );
#else
# if LOCK_SEED_FILE
    fd = open( seed_file_name, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR );
# else
#  ifdef __VMS
    /* Open the seed file for exclusive write access, but allow other
     * readers.  Loop until success.  Complain after a few failures.  */
    {
        int backoff = 0;

        while ((fd = open( seed_file_name,
                           O_WRONLY|O_CREAT,
                           S_IRUSR|S_IWUSR,
                           "shr=get")) == -1 )
        {
          if ((errno != EVMSERR) || (vaxc$errno != RMS$_FLK))
            {
              /* Some unexpected open failure. */
              log_info (_("can't lock `%s': %s\n"),
                        seed_file_name, strerror (errno));
              return;
            }

          if (backoff > 2) /* Show the first message after ~3.75 seconds. */
            log_info( _("waiting for lock on `%s'...\n"), seed_file_name);

          wait_vms( backoff+ 0.25);
          if (backoff < 10)
            backoff++ ;
        }
    }
#  else /* !def __VMS */
    fd = open( seed_file_name, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR );
#  endif /* !def __VMS */
# endif
#endif
    if( fd == -1 ) {
	log_info(_("can't create `%s': %s\n"), seed_file_name, strerror(errno));
	return;
    }

    if (lock_seed_file (fd, seed_file_name, 1))
      {
        close (fd);
        return;
      }
#if LOCK_SEED_FILE
    if (ftruncate (fd, 0))
      {
	log_info(_("can't write `%s': %s\n"), seed_file_name, strerror(errno));
        close (fd);
        return;
      }
#endif /*LOCK_SEED_FILE*/

    do {
	i = write( fd, keypool, POOLSIZE );
    } while( i == -1 && errno == EINTR );
    if( i != POOLSIZE ) {
	log_info(_("can't write `%s': %s\n"), seed_file_name, strerror(errno) );
    }
    if( close(fd) )
	log_info(_("can't close `%s': %s\n"), seed_file_name, strerror(errno) );
}

Project: gnupg
File: random.c

fast_random_poll()
{
    static int (*fnc)( void (*)(const void*, size_t, int), int) = NULL;
    static int initialized = 0;

    rndstats.fastpolls++;
    if( !initialized ) {
	if( !is_initialized )
	    initialize();
	initialized = 1;
	fnc = getfnc_fast_random_poll();
    }
    if( fnc ) {
	(*fnc)( add_randomness, 1 );
	return;
    }

    /* fall back to the generic function */
#if defined(HAVE_GETHRTIME) && !defined(HAVE_BROKEN_GETHRTIME)
    {	hrtime_t tv;
        /* On some Solaris and HPUX system gethrtime raises an SIGILL, but we
         * checked this with configure */
	tv = gethrtime();
	add_randomness( &tv, sizeof(tv), 1 );
    }
#elif defined (HAVE_GETTIMEOFDAY)
    {	struct timeval tv;
	if( gettimeofday( &tv, NULL ) )
	    BUG();
	add_randomness( &tv.tv_sec, sizeof(tv.tv_sec), 1 );
	add_randomness( &tv.tv_usec, sizeof(tv.tv_usec), 1 );
    }
#elif defined (HAVE_CLOCK_GETTIME)
    {	struct timespec tv;
	if( clock_gettime( CLOCK_REALTIME, &tv ) == -1 )
	    BUG();
	add_randomness( &tv.tv_sec, sizeof(tv.tv_sec), 1 );
	add_randomness( &tv.tv_nsec, sizeof(tv.tv_nsec), 1 );
    }
#elif defined (HAVE_TIMES)
    {	struct tms buf;
        if( times( &buf ) == -1 )
	    BUG();
	add_randomness( &buf, sizeof buf, 1 );
    }
#endif
#ifdef HAVE_GETRUSAGE
#ifndef RUSAGE_SELF
#ifdef __GCC__
#warning There is no RUSAGE_SELF on this system
#endif
#else
    {	struct rusage buf;
        /* QNX/Neutrino does return ENOSYS - so we just ignore it and
         * add whatever is in buf.  In a chroot environment it might not
         * work at all (i.e. because /proc/ is not accessible), so we better
         * ignore all error codes and hope for the best
         */
        getrusage( RUSAGE_SELF, &buf );

	add_randomness( &buf, sizeof buf, 1 );
	wipememory( &buf, sizeof buf );
    }
#endif
#endif
    /* time and clock are available on all systems - so
     * we better do it just in case one of the above functions
     * didn't work */
    {	time_t x = time(NULL);
	add_randomness( &x, sizeof(x), 1 );
    }
    {	clock_t x = clock();
	add_randomness( &x, sizeof(x), 1 );
    }
}

Project: gnupg
File: random.c

random_poll()
{
    rndstats.slowpolls++;
    read_random_source( 2, POOLSIZE/5, 1 );
}

Project: gnupg
File: random.c

quick_random_gen( int onoff )
{
    int last;

    read_random_source(0,0,0); /* init */
    last = quick_test;
    if( onoff != -1 )
	quick_test = onoff;
    return faked_rng? 1 : last;
}

Project: gnupg
File: random.c

read_pool( byte *buffer, size_t length, int level )
{
    int i;
    ulong *sp, *dp;

    if( length > POOLSIZE ) {
	log_bug("too many random bits requested\n");
    }

    if( !pool_filled ) {
	if( read_seed_file() )
	    pool_filled = 1;
    }

    /* For level 2 quality (key generation) we alwas make
     * sure that the pool has been seeded enough initially */
    if( level == 2 && !did_initial_extra_seeding ) {
	size_t needed;

	pool_balance = 0;
	needed = length - pool_balance;
	if( needed < POOLSIZE/2 )
	    needed = POOLSIZE/2;
	else if( needed > POOLSIZE )
	    BUG();
	read_random_source( 3, needed, 2 );
	pool_balance += needed;
	did_initial_extra_seeding=1;
    }

    /* for level 2 make sure that there is enough random in the pool */
    if( level == 2 && pool_balance < length ) {
	size_t needed;

	if( pool_balance < 0 )
	    pool_balance = 0;
	needed = length - pool_balance;
	if( needed > POOLSIZE )
	    BUG();
	read_random_source( 3, needed, 2 );
	pool_balance += needed;
    }

    /* make sure the pool is filled */
    while( !pool_filled )
	random_poll();

    /* do always a fast random poll */
    fast_random_poll();

    if( !level ) { /* no need for cryptographic strong random */
	/* create a new pool */
	for(i=0,dp=(ulong*)keypool, sp=(ulong*)rndpool;
				    i < POOLWORDS; i++, dp++, sp++ )
	    *dp = *sp + ADD_VALUE;
	/* must mix both pools */
	mix_pool(rndpool); rndstats.mixrnd++;
	mix_pool(keypool); rndstats.mixkey++;
	memcpy( buffer, keypool, length );
    }
    else {
	/* mix the pool (if add_randomness() didn't it) */
	if( !just_mixed ) {
	    mix_pool(rndpool);
	    rndstats.mixrnd++;
	}
	/* create a new pool */
	for(i=0,dp=(ulong*)keypool, sp=(ulong*)rndpool;
				    i < POOLWORDS; i++, dp++, sp++ )
	    *dp = *sp + ADD_VALUE;
	/* and mix both pools */
	mix_pool(rndpool); rndstats.mixrnd++;
	mix_pool(keypool); rndstats.mixkey++;
	/* read the required data
	 * we use a readpoiter to read from a different postion each
	 * time */
	while( length-- ) {
	    *buffer++ = keypool[pool_readpos++];
	    if( pool_readpos >= POOLSIZE )
		pool_readpos = 0;
	    pool_balance--;
	}
	if( pool_balance < 0 )
	    pool_balance = 0;
	/* and clear the keypool */
	wipememory(keypool, POOLSIZE);
    }
}

Project: gnupg
File: random.c

initialize(void)
{
    /* The data buffer is allocated somewhat larger, so that
     * we can use this extra space (which is allocated in secure memory)
     * as a temporary hash buffer */
    rndpool = secure_alloc ? xmalloc_secure_clear(POOLSIZE+BLOCKLEN)
			   : xmalloc_clear(POOLSIZE+BLOCKLEN);
    keypool = secure_alloc ? xmalloc_secure_clear(POOLSIZE+BLOCKLEN)
			   : xmalloc_clear(POOLSIZE+BLOCKLEN);
    is_initialized = 1;
}

Project: gnupg
File: random.c

random_disable_locking ()
{
  no_seed_file_locking = 1;
}

Project: gnupg
File: random.c

get_random_bits( size_t nbits, int level, int secure )
{
    byte *buf, *p;
    size_t nbytes = (nbits+7)/8;

    if( quick_test && level > 1 )
	level = 1;
    MASK_LEVEL(level);
    if( level == 1 ) {
	rndstats.getbytes1 += nbytes;
	rndstats.ngetbytes1++;
    }
    else if( level >= 2 ) {
	rndstats.getbytes2 += nbytes;
	rndstats.ngetbytes2++;
    }

    buf = secure && secure_alloc ? xmalloc_secure( nbytes ) : xmalloc( nbytes );
    for( p = buf; nbytes > 0; ) {
	size_t n = nbytes > POOLSIZE? POOLSIZE : nbytes;
	read_pool( p, n, level );
	nbytes -= n;
	p += n;
    }
    return buf;
}

Project: gnupg
File: random.c

set_random_seed_file( const char *name )
{
    if( seed_file_name )
	BUG();
    seed_file_name = xstrdup( name );
}

Project: gnupg
File: random.c

read_seed_file(void)
{
    int fd;
    struct stat sb;
    unsigned char buffer[POOLSIZE];
    int n;

    if( !seed_file_name )
	return 0;

#if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__)
    fd = open( seed_file_name, O_RDONLY | O_BINARY );
#elif defined( __VMS)
    /* We're only reading, so allow others to do anything. */
    fd = open( seed_file_name, O_RDONLY, 0777, "shr=get,put,upd" );
#else
    fd = open( seed_file_name, O_RDONLY );
#endif
    if( fd == -1 && errno == ENOENT) {
	allow_seed_file_update = 1;
	return 0;
    }

    if( fd == -1 ) {
	log_info(_("can't open `%s': %s\n"), seed_file_name, strerror(errno) );
	return 0;
    }
    if (lock_seed_file (fd, seed_file_name, 0))
      {
        close (fd);
        return 0;
      }

    if( fstat( fd, &sb ) ) {
	log_info(_("can't stat `%s': %s\n"), seed_file_name, strerror(errno) );
	close(fd);
	return 0;
    }
    if( !S_ISREG(sb.st_mode) ) {
	log_info(_("`%s' is not a regular file - ignored\n"), seed_file_name );
	close(fd);
	return 0;
    }
    if( !sb.st_size ) {
	log_info(_("note: random_seed file is empty\n") );
	close(fd);
	allow_seed_file_update = 1;
	return 0;
    }
    if( sb.st_size != POOLSIZE ) {
	log_info(_("WARNING: invalid size of random_seed file - not used\n") );
	close(fd);
	return 0;
    }

    do {
	n = read( fd, buffer, POOLSIZE );
    } while( n == -1 && errno == EINTR );
    /* The N==0, ENOENT, and N!=POOLSIZE cases may happen if another
       process is updating the file.  For consistency we use the same
       recovery strategy as with the pre-read checks.  */
    if (!n) {
        log_info(_("note: random_seed file is empty\n") );
        allow_seed_file_update = 1;
        close(fd);
        return 0;
    }
    else if( n == -1 && errno == ENOENT) {
        /* On a Unix system that should never happen.  However, I can
           imagine this error code on non-inode based systems.  */
	log_info(_("can't read `%s': %s\n"), seed_file_name, strerror(errno));
        allow_seed_file_update = 1;
	close(fd);
	return 0;
    }
    else if( n == -1 ) {
        /* A real read error.  */
	log_fatal(_("can't read `%s': %s\n"), seed_file_name,strerror(errno) );
	close(fd);
	return 0;
    }
    else if ( n != POOLSIZE ) {
        log_info(_("WARNING: invalid size of random_seed file - not used\n") );
	close(fd);
	return 0;
    }

    close(fd);

    add_randomness( buffer, POOLSIZE, 0 );
    /* add some minor entropy to the pool now (this will also force a mixing) */
    {	pid_t x = getpid();
	add_randomness( &x, sizeof(x), 0 );
    }
    {	time_t x = time(NULL);
	add_randomness( &x, sizeof(x), 0 );
    }
    {	clock_t x = clock();
	add_randomness( &x, sizeof(x), 0 );
    }
    /* And read a few bytes from our entropy source.  By using
     * a level of 0 this will not block and might not return anything
     * with some entropy drivers, however the rndlinux driver will use
     * /dev/urandom and return some stuff - Do not read too much as we
     * want to be friendly to the scarce system entropy resource. */
    read_random_source( 0, 16, 0 );

    allow_seed_file_update = 1;
    return 1;
}

Project: gnupg
File: random.c

secure_randoxmalloc()
{
    secure_alloc = 1;
}

Project: gnupg
File: random.c

random_is_faked()
{
    if( !is_initialized )
	initialize();
    return faked_rng || quick_test;
}