Blame random/rndhw.c

Packit 0680ba
/* rndhw.c  - Access to the external random daemon
Packit 0680ba
 * Copyright (C) 2007  Free Software Foundation, Inc.
Packit 0680ba
 * Copyright (C) 2012  Dmitry Kasatkin
Packit 0680ba
 *
Packit 0680ba
 * This file is part of Libgcrypt.
Packit 0680ba
 *
Packit 0680ba
 * Libgcrypt is free software; you can redistribute it and/or modify
Packit 0680ba
 * it under the terms of the GNU Lesser General Public License as
Packit 0680ba
 * published by the Free Software Foundation; either version 2.1 of
Packit 0680ba
 * the License, or (at your option) any later version.
Packit 0680ba
 *
Packit 0680ba
 * Libgcrypt is distributed in the hope that it will be useful,
Packit 0680ba
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 0680ba
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 0680ba
 * GNU Lesser General Public License for more details.
Packit 0680ba
 *
Packit 0680ba
 * You should have received a copy of the GNU Lesser General Public
Packit 0680ba
 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
Packit 0680ba
 */
Packit 0680ba
Packit 0680ba
#include <config.h>
Packit 0680ba
#include <stdio.h>
Packit 0680ba
#include <stdlib.h>
Packit 0680ba
Packit 0680ba
#include "types.h"
Packit 0680ba
#include "g10lib.h"
Packit 0680ba
#include "rand-internal.h"
Packit 0680ba
Packit 0680ba
#undef USE_PADLOCK
Packit 0680ba
#ifdef ENABLE_PADLOCK_SUPPORT
Packit 0680ba
# ifdef HAVE_GCC_ATTRIBUTE_ALIGNED
Packit 0680ba
#  if (defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4) || defined(__x86_64__)
Packit 0680ba
#   define USE_PADLOCK 1
Packit 0680ba
#  endif
Packit 0680ba
# endif
Packit 0680ba
#endif /*ENABLE_PADLOCK_SUPPORT*/
Packit 0680ba
Packit 0680ba
#undef USE_DRNG
Packit 0680ba
#ifdef ENABLE_DRNG_SUPPORT
Packit 0680ba
# ifdef HAVE_GCC_ATTRIBUTE_ALIGNED
Packit 0680ba
#  if (defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4) || defined(__x86_64__)
Packit 0680ba
#   define USE_DRNG 1
Packit 0680ba
#  endif
Packit 0680ba
# endif
Packit 0680ba
#endif /*ENABLE_RDRAND_SUPPORT*/
Packit 0680ba
Packit 0680ba
typedef void (*add_fn_t)(const void*, size_t, enum random_origins);
Packit 0680ba
Packit 0680ba
/* Keep track on whether the RNG has problems.  */
Packit 0680ba
static volatile int rng_failed;
Packit 0680ba
Packit 0680ba
Packit 0680ba
#ifdef USE_PADLOCK
Packit 0680ba
static size_t
Packit 0680ba
poll_padlock (void (*add)(const void*, size_t, enum random_origins),
Packit 0680ba
              enum random_origins origin, int fast)
Packit 0680ba
{
Packit 0680ba
  volatile char buffer[64+8] __attribute__ ((aligned (8)));
Packit 0680ba
  volatile char *p;
Packit 0680ba
  unsigned int nbytes, status;
Packit 0680ba
Packit 0680ba
  /* Peter Gutmann's cryptlib tests again whether the RNG is enabled
Packit 0680ba
     but we don't do so.  We would have to do this also for our AES
Packit 0680ba
     implementaion and that is definitely too time consuming.  There
Packit 0680ba
     would be a race condition anyway.  Thus we assume that the OS
Packit 0680ba
     does not change the Padlock initialization while a user process
Packit 0680ba
     is running.  */
Packit 0680ba
  p = buffer;
Packit 0680ba
  nbytes = 0;
Packit 0680ba
  while (nbytes < 64)
Packit 0680ba
    {
Packit 0680ba
#if defined(__x86_64__) && defined(__LP64__)
Packit 0680ba
      asm volatile
Packit 0680ba
        ("movq %1, %%rdi\n\t"         /* Set buffer.  */
Packit 0680ba
         "xorq %%rdx, %%rdx\n\t"      /* Request up to 8 bytes.  */
Packit 0680ba
         ".byte 0x0f, 0xa7, 0xc0\n\t" /* XSTORE RNG. */
Packit 0680ba
         : "=a" (status)
Packit 0680ba
         : "g" (p)
Packit 0680ba
         : "%rdx", "%rdi", "cc"
Packit 0680ba
         );
Packit 0680ba
#else
Packit 0680ba
      asm volatile
Packit 0680ba
        ("movl %1, %%edi\n\t"         /* Set buffer.  */
Packit 0680ba
         "xorl %%edx, %%edx\n\t"      /* Request up to 8 bytes.  */
Packit 0680ba
         ".byte 0x0f, 0xa7, 0xc0\n\t" /* XSTORE RNG. */
Packit 0680ba
         : "=a" (status)
Packit 0680ba
         : "g" (p)
Packit 0680ba
         : "%edx", "%edi", "cc"
Packit 0680ba
         );
Packit 0680ba
#endif
Packit 0680ba
      if ((status & (1<<6))         /* RNG still enabled.  */
Packit 0680ba
          && !(status & (1<<13))    /* von Neumann corrector is enabled.  */
Packit 0680ba
          && !(status & (1<<14))    /* String filter is disabled.  */
Packit 0680ba
          && !(status & 0x1c00)     /* BIAS voltage at default.  */
Packit 0680ba
          && (!(status & 0x1f) || (status & 0x1f) == 8) /* Sanity check.  */
Packit 0680ba
          )
Packit 0680ba
        {
Packit 0680ba
          nbytes += (status & 0x1f);
Packit 0680ba
          if (fast)
Packit 0680ba
            break; /* Don't get into the loop with the fast flag set.  */
Packit 0680ba
          p += (status & 0x1f);
Packit 0680ba
        }
Packit 0680ba
      else
Packit 0680ba
        {
Packit 0680ba
          /* If there was an error we need to break the loop and
Packit 0680ba
             record that there is something wrong with the padlock
Packit 0680ba
             RNG.  */
Packit 0680ba
          rng_failed = 1;
Packit 0680ba
          break;
Packit 0680ba
        }
Packit 0680ba
    }
Packit 0680ba
Packit 0680ba
  if (nbytes)
Packit 0680ba
    {
Packit 0680ba
      (*add) ((void*)buffer, nbytes, origin);
Packit 0680ba
      wipememory (buffer, nbytes);
Packit 0680ba
    }
Packit 0680ba
  return nbytes;
Packit 0680ba
}
Packit 0680ba
#endif /*USE_PADLOCK*/
Packit 0680ba
Packit 0680ba
Packit 0680ba
#ifdef USE_DRNG
Packit 0680ba
# define RDRAND_RETRY_LOOPS	10
Packit 0680ba
# define RDRAND_INT	".byte 0x0f,0xc7,0xf0"
Packit 0680ba
# if defined(__x86_64__) && defined(__LP64__)
Packit 0680ba
#  define RDRAND_LONG	".byte 0x48,0x0f,0xc7,0xf0"
Packit 0680ba
# else
Packit 0680ba
#  define RDRAND_LONG	RDRAND_INT
Packit 0680ba
# endif
Packit 0680ba
static inline int
Packit 0680ba
rdrand_long (unsigned long *v)
Packit 0680ba
{
Packit 0680ba
  int ok;
Packit 0680ba
  asm volatile ("1: " RDRAND_LONG "\n\t"
Packit 0680ba
                "jc 2f\n\t"
Packit 0680ba
                "decl %0\n\t"
Packit 0680ba
                "jnz 1b\n\t"
Packit 0680ba
                "2:"
Packit 0680ba
                : "=r" (ok), "=a" (*v)
Packit 0680ba
                : "0" (RDRAND_RETRY_LOOPS)
Packit 0680ba
                : "cc");
Packit 0680ba
  return ok;
Packit 0680ba
}
Packit 0680ba
Packit 0680ba
Packit 0680ba
static inline int
Packit 0680ba
rdrand_nlong (unsigned long *v, int count)
Packit 0680ba
{
Packit 0680ba
  while (count--)
Packit 0680ba
    if (!rdrand_long(v++))
Packit 0680ba
      return 0;
Packit 0680ba
  return 1;
Packit 0680ba
}
Packit 0680ba
Packit 0680ba
Packit 0680ba
static size_t
Packit 0680ba
poll_drng (add_fn_t add, enum random_origins origin, int fast)
Packit 0680ba
{
Packit 0680ba
  volatile char buffer[64] __attribute__ ((aligned (8)));
Packit 0680ba
  unsigned int nbytes = sizeof (buffer);
Packit 0680ba
Packit 0680ba
  (void)fast;
Packit 0680ba
Packit 0680ba
  if (!rdrand_nlong ((unsigned long *)buffer, sizeof(buffer)/sizeof(long)))
Packit 0680ba
    return 0;
Packit 0680ba
  (*add)((void *)buffer, nbytes, origin);
Packit 0680ba
  return nbytes;
Packit 0680ba
}
Packit 0680ba
#endif /*USE_DRNG*/
Packit 0680ba
Packit 0680ba
Packit 0680ba
int
Packit 0680ba
_gcry_rndhw_failed_p (void)
Packit 0680ba
{
Packit 0680ba
  return rng_failed;
Packit 0680ba
}
Packit 0680ba
Packit 0680ba
Packit 0680ba
/* Try to read random from a hardware RNG if a fast one is
Packit 0680ba
   available.  */
Packit 0680ba
void
Packit 0680ba
_gcry_rndhw_poll_fast (void (*add)(const void*, size_t, enum random_origins),
Packit 0680ba
                       enum random_origins origin)
Packit 0680ba
{
Packit 0680ba
  (void)add;
Packit 0680ba
  (void)origin;
Packit 0680ba
Packit 0680ba
#ifdef USE_DRNG
Packit 0680ba
  if ((_gcry_get_hw_features () & HWF_INTEL_RDRAND))
Packit 0680ba
    poll_drng (add, origin, 1);
Packit 0680ba
#endif
Packit 0680ba
#ifdef USE_PADLOCK
Packit 0680ba
  if ((_gcry_get_hw_features () & HWF_PADLOCK_RNG))
Packit 0680ba
    poll_padlock (add, origin, 1);
Packit 0680ba
#endif
Packit 0680ba
}
Packit 0680ba
Packit 0680ba
Packit 0680ba
/* Read 64 bytes from a hardware RNG and return the number of bytes
Packit 0680ba
   actually read.  */
Packit 0680ba
size_t
Packit 0680ba
_gcry_rndhw_poll_slow (void (*add)(const void*, size_t, enum random_origins),
Packit 0680ba
                       enum random_origins origin)
Packit 0680ba
{
Packit 0680ba
  size_t nbytes = 0;
Packit 0680ba
Packit 0680ba
  (void)add;
Packit 0680ba
  (void)origin;
Packit 0680ba
Packit 0680ba
#ifdef USE_DRNG
Packit 0680ba
  if ((_gcry_get_hw_features () & HWF_INTEL_RDRAND))
Packit 0680ba
    nbytes += poll_drng (add, origin, 0);
Packit 0680ba
#endif
Packit 0680ba
#ifdef USE_PADLOCK
Packit 0680ba
  if ((_gcry_get_hw_features () & HWF_PADLOCK_RNG))
Packit 0680ba
    nbytes += poll_padlock (add, origin, 0);
Packit 0680ba
#endif
Packit 0680ba
Packit 0680ba
  return nbytes;
Packit 0680ba
}