1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 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 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
|
/* rndhw.c - Access to the external random daemon
* Copyright (C) 2007 Free Software Foundation, Inc.
* Copyright (C) 2012 Dmitry Kasatkin
*
* This file is part of Libgcrypt.
*
* Libgcrypt is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* Libgcrypt 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include "types.h"
#include "g10lib.h"
#include "rand-internal.h"
#undef USE_PADLOCK
#ifdef ENABLE_PADLOCK_SUPPORT
# ifdef HAVE_GCC_ATTRIBUTE_ALIGNED
# if (defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4) || defined(__x86_64__)
# define USE_PADLOCK 1
# endif
# endif
#endif /*ENABLE_PADLOCK_SUPPORT*/
#undef USE_DRNG
#ifdef ENABLE_DRNG_SUPPORT
# ifdef HAVE_GCC_ATTRIBUTE_ALIGNED
# if (defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4) || defined(__x86_64__)
# define USE_DRNG 1
# endif
# endif
#endif /*ENABLE_RDRAND_SUPPORT*/
typedef void (*add_fn_t)(const void*, size_t, enum random_origins);
/* Keep track on whether the RNG has problems. */
static volatile int rng_failed;
#ifdef USE_PADLOCK
static size_t
poll_padlock (void (*add)(const void*, size_t, enum random_origins),
enum random_origins origin, int fast)
{
volatile char buffer[64+8] __attribute__ ((aligned (8)));
volatile char *p;
unsigned int nbytes, status;
/* Peter Gutmann's cryptlib tests again whether the RNG is enabled
but we don't do so. We would have to do this also for our AES
implementation and that is definitely too time consuming. There
would be a race condition anyway. Thus we assume that the OS
does not change the Padlock initialization while a user process
is running. */
p = buffer;
nbytes = 0;
while (nbytes < 64)
{
#if defined(__x86_64__) && SIZEOF_VOID_P == 8
asm volatile
("movq %1, %%rdi\n\t" /* Set buffer. */
"xorq %%rdx, %%rdx\n\t" /* Request up to 8 bytes. */
".byte 0x0f, 0xa7, 0xc0\n\t" /* XSTORE RNG. */
: "=a" (status)
: "g" (p)
: "%rdx", "%rdi", "cc", "memory"
);
#else
asm volatile
("movl %1, %%edi\n\t" /* Set buffer. */
"xorl %%edx, %%edx\n\t" /* Request up to 8 bytes. */
".byte 0x0f, 0xa7, 0xc0\n\t" /* XSTORE RNG. */
: "=a" (status)
: "g" (p)
: "%edx", "%edi", "cc", "memory"
);
#endif
if ((status & (1<<6)) /* RNG still enabled. */
&& !(status & (1<<13)) /* von Neumann corrector is enabled. */
&& !(status & (1<<14)) /* String filter is disabled. */
&& !(status & 0x1c00) /* BIAS voltage at default. */
&& (!(status & 0x1f) || (status & 0x1f) == 8) /* Sanity check. */
)
{
nbytes += (status & 0x1f);
if (fast)
break; /* Don't get into the loop with the fast flag set. */
p += (status & 0x1f);
}
else
{
/* If there was an error we need to break the loop and
record that there is something wrong with the padlock
RNG. */
rng_failed = 1;
break;
}
}
if (nbytes)
{
(*add) ((void*)buffer, nbytes, origin);
wipememory (buffer, nbytes);
}
return nbytes;
}
#endif /*USE_PADLOCK*/
#ifdef USE_DRNG
# define RDRAND_RETRY_LOOPS 10
# define RDRAND_INT ".byte 0x0f,0xc7,0xf0"
# if defined(__x86_64__) && SIZEOF_UNSIGNED_LONG == 8
# define RDRAND_LONG ".byte 0x48,0x0f,0xc7,0xf0"
# else
# define RDRAND_LONG RDRAND_INT
# endif
static inline int
rdrand_long (volatile unsigned long *v)
{
int ok;
asm volatile ("1: " RDRAND_LONG "\n\t"
"jc 2f\n\t"
"decl %0\n\t"
"jnz 1b\n\t"
"2:"
: "=r" (ok), "=a" (*v)
: "0" (RDRAND_RETRY_LOOPS)
: "cc", "memory");
return ok;
}
static inline int
rdrand_nlong (volatile unsigned long *v, int count)
{
while (count--)
if (!rdrand_long(v++))
return 0;
return 1;
}
static size_t
poll_drng (add_fn_t add, enum random_origins origin, int fast)
{
volatile unsigned long buffer[8] __attribute__ ((aligned (8)));
unsigned int nbytes = sizeof (buffer);
(void)fast;
if (!rdrand_nlong (buffer, DIM(buffer)))
return 0;
(*add)((void *)buffer, nbytes, origin);
return nbytes;
}
#endif /*USE_DRNG*/
int
_gcry_rndhw_failed_p (void)
{
return rng_failed;
}
/* Try to read random from a hardware RNG if a fast one is
available. */
void
_gcry_rndhw_poll_fast (void (*add)(const void*, size_t, enum random_origins),
enum random_origins origin)
{
(void)add;
(void)origin;
#ifdef USE_DRNG
if ((_gcry_get_hw_features () & HWF_INTEL_RDRAND))
poll_drng (add, origin, 1);
#endif
#ifdef USE_PADLOCK
if ((_gcry_get_hw_features () & HWF_PADLOCK_RNG))
poll_padlock (add, origin, 1);
#endif
}
/* Read 64 bytes from a hardware RNG and return the number of bytes
actually read. However hardware source is let account only
for up to 50% (or 25% for RDRAND) of the requested bytes. */
size_t
_gcry_rndhw_poll_slow (void (*add)(const void*, size_t, enum random_origins),
enum random_origins origin, size_t req_length)
{
size_t nbytes = 0;
(void)add;
(void)origin;
req_length /= 2; /* Up to 50%. */
#ifdef USE_DRNG
if ((_gcry_get_hw_features () & HWF_INTEL_RDRAND))
{
req_length /= 2; /* Up to 25%. */
nbytes += poll_drng (add, origin, 0);
}
#endif
#ifdef USE_PADLOCK
if ((_gcry_get_hw_features () & HWF_PADLOCK_RNG))
nbytes += poll_padlock (add, origin, 0);
#endif
if (nbytes > req_length)
nbytes = req_length;
return nbytes;
}
|