Add support for using DRNG random number generator
authorDmitry Kasatkin <dmitry.kasatkin@intel.com>
Tue, 18 Dec 2012 12:56:48 +0000 (14:56 +0200)
committerWerner Koch <wk@gnupg.org>
Tue, 18 Dec 2012 18:33:04 +0000 (19:33 +0100)
* configure.ac: Add option --disable-drng-support.
(ENABLE_DRNG_SUPPORT): New.
* random/rndhw.c (USE_DRNG): New.
(rdrand_long, rdrand_nlong, poll_drng): New.
(_gcry_rndhw_poll_fast, _gcry_rndhw_poll_slow): Call poll function.
* src/g10lib.h (HWF_INTEL_RDRAND): New.
* src/global.c (hwflist): Add "intel-rdrand".
* src/hwfeatures.c (detect_x86_64_gnuc) [ENABLE_DRNG_SUPPORT]: Detect
RDRAND.
(detect_ia32_gnuc) [ENABLE_DRNG_SUPPORT]: Detect RDRAND.
--

This patch provides support for using Digital Random Number Generator (DRNG)
engine, which is available on the latest Intel's CPUs. DRNG engine is
accesible via new the RDRAND instruction.

This patch adds the following:
- support for disabling using of rdrand instruction
- checking for RDRAND instruction support using cpuid
- RDRAND usage implementation

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
ChangeLog and editorial changes by wk.

configure.ac
random/rndhw.c
src/g10lib.h
src/global.c
src/hwfeatures.c

index ff07dda..7d162a2 100644 (file)
@@ -544,6 +544,18 @@ if test x"$aesnisupport" = xyes ; then
             [Enable support for Intel AES-NI instructions.])
 fi
 
+# Implementation of the --disable-drng-support switch.
+AC_MSG_CHECKING([whether DRNG support is requested])
+AC_ARG_ENABLE(drng-support,
+              AC_HELP_STRING([--disable-drng-support],
+                 [Disable support for the Intel DRNG (RDRAND instruction)]),
+             drngsupport=$enableval,drngsupport=yes)
+AC_MSG_RESULT($drngsupport)
+if test x"$drngsupport" = xyes ; then
+  AC_DEFINE(ENABLE_DRNG_SUPPORT, 1,
+            [Enable support for Intel DRNG (RDRAND instruction).])
+fi
+
 # Implementation of the --disable-O-flag-munging switch.
 AC_MSG_CHECKING([whether a -O flag munging is requested])
 AC_ARG_ENABLE([O-flag-munging],
@@ -1304,6 +1316,7 @@ echo "
         Using linux capabilities:  $use_capabilities
         Try using Padlock crypto:  $padlocksupport
         Try using AES-NI crypto:   $aesnisupport
+        Try using DRNG (RDRAND):   $drngsupport
 "
 
 if test "$print_egd_notice" = "yes"; then
index 775d90f..cbb28d1 100644 (file)
@@ -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.
  *
 # 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);
index f1af399..5e99c46 100644 (file)
@@ -151,6 +151,7 @@ int _gcry_log_verbosity( int level );
 #define HWF_PADLOCK_MMUL 8
 
 #define HWF_INTEL_AESNI  256
+#define HWF_INTEL_RDRAND 512
 
 
 unsigned int _gcry_get_hw_features (void);
index f280a7b..2428e21 100644 (file)
@@ -66,6 +66,7 @@ static struct
     { HWF_PADLOCK_SHA, "padlock-sha" },
     { HWF_PADLOCK_MMUL,"padlock-mmul"},
     { HWF_INTEL_AESNI, "intel-aesni" },
+    { HWF_INTEL_RDRAND,"intel-rdrand" },
     { 0, NULL}
   };
 
index 82c435b..e89c825 100644 (file)
@@ -134,6 +134,20 @@ detect_x86_64_gnuc (void)
      : "%eax", "%ebx", "%ecx", "%edx", "cc"
      );
 #endif /*#ifdef ENABLE_AESNI_SUPPORT*/
+#ifdef ENABLE_DRNG_SUPPORT
+  asm volatile
+    ("movl $1, %%eax\n\t"           /* Get CPU info and feature flags.  */
+     "cpuid\n"
+     "testl $0x40000000, %%ecx\n\t" /* Test bit 30.  */
+     "jz .Lno_rdrand%=\n\t"         /* No RDRAND support.  */
+     "orl $512, %0\n"               /* Set our HWF_INTEL_RDRAND bit.  */
+
+     ".Lno_rdrand%=:\n"
+     : "+r" (hw_features)
+     :
+     : "%eax", "%ebx", "%ecx", "%edx", "cc"
+     );
+#endif /* #ifdef ENABLE_DRNG_SUPPORT */
 
 }
 #endif /* __x86_64__ && __GNUC__ */
@@ -267,6 +281,22 @@ detect_ia32_gnuc (void)
      : "%eax", "%ecx", "%edx", "cc"
      );
 #endif /*ENABLE_AESNI_SUPPORT*/
+#ifdef ENABLE_DRNG_SUPPORT
+  asm volatile
+    ("pushl %%ebx\n\t"         /* Save GOT register.  */
+     "movl $1, %%eax\n\t"           /* Get CPU info and feature flags.  */
+     "cpuid\n"
+     "popl %%ebx\n\t"          /* Restore GOT register. */
+     "testl $0x40000000, %%ecx\n\t" /* Test bit 30.  */
+     "jz .Lno_rdrand%=\n\t"         /* No RDRAND support.  */
+     "orl $512, %0\n"               /* Set our HWF_INTEL_RDRAND bit.  */
+
+     ".Lno_rdrand%=:\n"
+     : "+r" (hw_features)
+     :
+     : "%eax", "%ecx", "%edx", "cc"
+     );
+#endif /*ENABLE_DRNG_SUPPORT*/
 
 }
 #endif /* __i386__ && SIZEOF_UNSIGNED_LONG == 4 && __GNUC__ */