|
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 |
}
|