diff options
Diffstat (limited to 'random/rndhw.c')
-rw-r--r-- | random/rndhw.c | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/random/rndhw.c b/random/rndhw.c index 775d90f0..cbb28d1a 100644 --- a/random/rndhw.c +++ b/random/rndhw.c @@ -1,5 +1,6 @@ /* 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. * @@ -34,6 +35,16 @@ # 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; @@ -109,6 +120,55 @@ poll_padlock (void (*add)(const void*, size_t, enum random_origins), #endif /*USE_PADLOCK*/ +#ifdef USE_DRNG +# define RDRAND_RETRY_LOOPS 10 +# define RDRAND_INT ".byte 0x0f,0xc7,0xf0" +# ifdef __x86_64__ +# define RDRAND_LONG ".byte 0x48,0x0f,0xc7,0xf0" +# else +# define RDRAND_LONG RDRAND_INT +# endif +static inline int +rdrand_long (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)); + return ok; +} + + +static inline int +rdrand_nlong (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 char buffer[64] __attribute__ ((aligned (8))); + unsigned int nbytes = sizeof (buffer); + + (void)fast; + + if (!rdrand_nlong ((unsigned long *)buffer, sizeof(buffer)/sizeof(long))) + return 0; + (*add)((void *)buffer, nbytes, origin); + return nbytes; +} +#endif /*USE_DRNG*/ + + int _gcry_rndhw_failed_p (void) { @@ -125,6 +185,10 @@ _gcry_rndhw_poll_fast (void (*add)(const void*, size_t, enum random_origins), (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); @@ -143,6 +207,10 @@ _gcry_rndhw_poll_slow (void (*add)(const void*, size_t, enum random_origins), (void)add; (void)origin; +#ifdef USE_DRNG + if ((_gcry_get_hw_features () & HWF_INTEL_RDRAND)) + nbytes += poll_drng (add, origin, 0); +#endif #ifdef USE_PADLOCK if ((_gcry_get_hw_features () & HWF_PADLOCK_RNG)) nbytes += poll_padlock (add, origin, 0); |