tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Lightweight support for instruction RNGs



I was playing with code for a RDRAND/RDSEED entropy source and it
just felt like -- much like opencrypto is poorly suited for crypto
via unprivileged CPU instructions -- our rndsource interface is
a little too heavy for CPU RNGs implemented as instructions.

I came up with the attached, which mixes in entropy from a new
"cpu_rng" each time samples are added to the global rndpool.

On the downside, this means we don't get statistics for this source.

On the upside, the cost of the stats-keeping vastly exceeds the cost
of the entropy generation, at least for the Intel implementation; I'm
less sure about VIA.

Another downside is that you can't turn this source on and off; but
on the upside, it's never used _except_ when samples from other sources
are being mixed in at the same time, so that should not be a cause for
security concern.

Needless to say, the real benefit here is that we get up to 64 bits
of additional entropy along with every other sample, without paying
any additional locking or synchronization overhead.

I've tested the RDRAND code.  The RDSEED code is almost identical but
I don't have a CPU with RDSEED handy.  The VIA code is lifted almost
verbatim from the existing PadLock driver, but I no longer have a VIA
board to test with -- I'd appreciate help with that.

If this looks like a good idea, before committing it I'll make
cpu_rng_init a do a little more work -- specifically, an entropy
test is probably in order.

Thor
Index: arch/amd64/include/Makefile
===================================================================
RCS file: /Volumes/NB/repo/src/sys/arch/amd64/include/Makefile,v
retrieving revision 1.18
diff -p -u -r1.18 Makefile
--- arch/amd64/include/Makefile	23 Jul 2014 18:19:43 -0000	1.18
+++ arch/amd64/include/Makefile	19 Dec 2015 22:17:36 -0000
@@ -4,7 +4,7 @@ INCSDIR= /usr/include/amd64
 
 INCS=	ansi.h aout_machdep.h asm.h \
 	bootinfo.h bswap.h byte_swap.h \
-	cdefs.h cpu.h \
+	cdefs.h cpu.h cpu_rng.h\
 	disklabel.h \
 	elf_machdep.h endian.h endian_machdep.h \
 	float.h fpu.h frame.h frame_regs.h \
Index: arch/amd64/include/types.h
===================================================================
RCS file: /Volumes/NB/repo/src/sys/arch/amd64/include/types.h,v
retrieving revision 1.48
diff -p -u -r1.48 types.h
--- arch/amd64/include/types.h	27 Aug 2015 12:30:50 -0000	1.48
+++ arch/amd64/include/types.h	19 Dec 2015 21:39:37 -0000
@@ -93,6 +93,7 @@ typedef	unsigned char		__cpu_simple_lock
 #define	__HAVE_TLS_VARIANT_II
 #define	__HAVE_COMMON___TLS_GET_ADDR
 #define	__HAVE_INTR_CONTROL
+#define __HAVE_CPU_RNG
 
 #ifdef _KERNEL_OPT
 #define	__HAVE_RAS
Index: arch/i386/include/Makefile
===================================================================
RCS file: /Volumes/NB/repo/src/sys/arch/i386/include/Makefile,v
retrieving revision 1.43
diff -p -u -r1.43 Makefile
--- arch/i386/include/Makefile	23 Jul 2014 18:19:44 -0000	1.43
+++ arch/i386/include/Makefile	19 Dec 2015 22:16:58 -0000
@@ -4,7 +4,7 @@ INCSDIR= /usr/include/i386
 
 INCS=	ansi.h aout_machdep.h apmvar.h asm.h \
 	bioscall.h bootinfo.h bswap.h byte_swap.h \
-	cdefs.h cpu.h cputypes.h \
+	cdefs.h cpu.h cpu_rng.h cputypes.h \
 	disklabel.h \
 	elf_machdep.h endian.h endian_machdep.h \
 	fenv.h float.h frame.h freebsd_machdep.h \
Index: arch/i386/include/types.h
===================================================================
RCS file: /Volumes/NB/repo/src/sys/arch/i386/include/types.h,v
retrieving revision 1.83
diff -p -u -r1.83 types.h
--- arch/i386/include/types.h	27 Aug 2015 12:30:51 -0000	1.83
+++ arch/i386/include/types.h	19 Dec 2015 21:40:33 -0000
@@ -109,6 +109,8 @@ typedef	unsigned char		__cpu_simple_lock
 #define	__HAVE_SYSCALL_INTERN
 #define	__HAVE_MINIMAL_EMUL
 #define	__HAVE_OLD_DISKLABEL
+#define __HAVE_CPU_RNG
+
 #if defined(_KERNEL)
 /*
  * Processors < i586 do not have cmpxchg8b, and we compile for i486
Index: arch/x86/include/Makefile
===================================================================
RCS file: /Volumes/NB/repo/src/sys/arch/x86/include/Makefile,v
retrieving revision 1.19
diff -p -u -r1.19 Makefile
--- arch/x86/include/Makefile	11 Feb 2014 20:17:16 -0000	1.19
+++ arch/x86/include/Makefile	19 Dec 2015 21:46:23 -0000
@@ -7,6 +7,7 @@ INCS=	aout_machdep.h \
 	cacheinfo.h \
 	cpu.h \
 	cpu_extended_state.h \
+	cpu_rng.h \
 	cpu_ucode.h \
 	cputypes.h \
 	cpuvar.h \
Index: arch/x86/include/cpu_rng.h
===================================================================
RCS file: arch/x86/include/cpu_rng.h
diff -N arch/x86/include/cpu_rng.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ arch/x86/include/cpu_rng.h	19 Dec 2015 23:40:42 -0000
@@ -0,0 +1,187 @@
+/* $NetBSD: $ */
+
+#ifndef _X86_CPURNG_H_
+#define _X86_CPURNG_H_
+
+/*-
+ * Copyright (c) 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Thor Lancelot Simon.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2003 Jason Wright
+ * Copyright (c) 2003, 2004 Theo de Raadt
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/cpu.h>
+
+#include <x86/specialreg.h>
+
+#include <machine/cpufunc.h>
+#include <machine/cpuvar.h>
+
+enum { CPU_RNG_NONE = 0,
+       CPU_RNG_RDRAND,
+       CPU_RNG_RDSEED,
+       CPU_RNG_VIA } cpu_rng_mode = CPU_RNG_NONE;
+
+typedef uint64_t cpu_rng_t;
+
+static inline void
+cpu_rng_init(void)
+{
+	if (cpu_feature[5] & CPUID_SEF_RDSEED) {
+		cpu_rng_mode = CPU_RNG_RDSEED;
+		aprint_normal("cpu_rng: RDSEED\n");
+	} else
+
+	if (cpu_feature[1] & CPUID2_RDRAND) {
+		cpu_rng_mode = CPU_RNG_RDRAND;
+		aprint_normal("cpu_rng: RDRAND\n");
+	} else
+
+	if (cpu_feature[4] & CPUID_VIA_HAS_RNG) {
+		cpu_rng_mode = CPU_RNG_VIA;
+		aprint_normal("cpu_rng: VIA\n");
+	}
+}
+
+static inline size_t
+cpu_rng_rdseed(cpu_rng_t *out)
+{
+	uint8_t rndsts;
+#ifndef __x86_64__
+	uint32_t outword[2] = out;
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		__asm __volatile("rdseed %0; setc %1":"=r"(outword + i),
+				 "=qm"(rndsts));
+		if (rndsts != 1) return 0;
+	}
+#else
+	__asm __volatile("rdseed %0; setc %1":"=r"(out),
+			 "=qm"(rndsts));
+	if (rndsts != 1) return 0;
+#endif
+	return sizeof(*out) * NBBY;
+}
+
+static inline size_t
+cpu_rng_rdrand(cpu_rng_t *out)
+{
+	uint8_t rndsts;
+#ifndef __x86_64__
+	uint32_t outword[2] = out;
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		__asm __volatile("rdrand %0; setc %1":"=r"(outword + i),
+				 "=qm"(rndsts));
+		if (rndsts != 1) return 0;
+	}
+#else
+	__asm __volatile("rdrand %0; setc %1":"=r"(out),
+			 "=qm"(rndsts));
+	if (rndsts != 1) return 0;
+#endif
+	return sizeof(*out) * NBBY;
+}
+
+static inline size_t
+cpu_rng_via(cpu_rng_t *out)
+{
+	uint32_t creg0, len = sizeof(*out);
+	uint32_t buffer[len / 4 + 1]; /* CPU goes 3 bytes beyond */
+        uint32_t eax, ecx, edi; /* XXX write-only, but necessary it seems */
+
+	/*
+	 * Sadly, we have to monkey with the coprocessor enable and fault
+	 * registers, which are really for the FPU, in order to read
+	 * from the RNG.
+	 *
+	 * Don't remove CR0_TS from the call below -- comments in the Linux
+	 * driver indicate that the xstorerng instruction can generate
+	 * spurious DNA faults though no FPU or SIMD state is changed
+	 * even if such a fault is generated.
+	 *
+	 */
+	kpreempt_disable();
+	x86_disable_intr();
+	creg0 = rcr0();
+	lcr0(creg0 & ~(CR0_EM|CR0_TS)); /* Permit access to SIMD/FPU path */
+	/*
+	 * Collect the random data from the C3 RNG into our buffer.
+	 * We turn on maximum whitening (is this actually desirable
+	 * if we will feed the data to SHA1?) (%edx[0,1] = "11").
+	 */
+	__asm __volatile("rep xstorerng"
+			 : "=a" (eax), "=c" (ecx), "=D" (edi)
+			 : "d" (3), "D" (buffer), "c" (len)
+			 : "memory", "cc");
+	/* Put CR0 back how it was */
+	lcr0(creg0);
+	memcpy(out, buffer, len);
+	explicit_memset(buffer, 0, sizeof(buffer));
+	x86_enable_intr();
+	kpreempt_enable();
+
+	return len * NBBY;
+}
+
+static inline size_t
+cpu_rng(cpu_rng_t *out)
+{
+	switch (cpu_rng_mode) {
+	    case CPU_RNG_NONE:
+		return 0;
+	    case CPU_RNG_RDSEED:
+		return cpu_rng_rdseed(out);
+	    case CPU_RNG_RDRAND:
+		return cpu_rng_rdrand(out);
+	    case CPU_RNG_VIA:
+		return cpu_rng_via(out);
+	    default:
+		panic("cpu_rng: unknown mode %d", (int)cpu_rng_mode);
+	}
+}
+#endif
Index: kern/kern_rndpool.c
===================================================================
RCS file: /Volumes/NB/repo/src/sys/kern/kern_rndpool.c,v
retrieving revision 1.16
diff -p -u -r1.16 kern_rndpool.c
--- kern/kern_rndpool.c	21 Apr 2015 04:41:36 -0000	1.16
+++ kern/kern_rndpool.c	19 Dec 2015 23:45:19 -0000
@@ -40,6 +40,10 @@ __KERNEL_RCSID(0, "$NetBSD: kern_rndpool
 
 #include <dev/rnd_private.h>
 
+#ifdef __HAVE_CPU_RNG
+#include <machine/cpu_rng.h>
+#endif
+
 /*
  * The random pool "taps"
  */
@@ -62,6 +66,11 @@ rndpool_init(rndpool_t *rp)
 	rp->stats.poolsize = RND_POOLWORDS;
 	rp->stats.threshold = RND_ENTROPY_THRESHOLD;
 	rp->stats.maxentropy = RND_POOLBITS;
+
+#ifdef __HAVE_CPU_RNG
+	cpu_rng_init();
+#endif
+
 }
 
 u_int32_t
@@ -168,6 +177,12 @@ rndpool_add_data(rndpool_t *rp,
 {
 	u_int32_t val;
 	const u_int8_t * buf;
+#ifdef __HAVE_CPU_RNG
+	cpu_rng_t extra;
+	size_t extra_entropy;
+
+	KASSERT(sizeof(extra) % sizeof(val) == 0);
+#endif
 
 	buf = p;
 
@@ -191,6 +206,23 @@ rndpool_add_data(rndpool_t *rp,
 		rndpool_add_one_word(rp, val);
 	}
 
+#ifdef __HAVE_CPU_RNG
+	extra_entropy = cpu_rng(&extra);
+	if ((extra_entropy)) {
+		buf = (uint8_t *)&extra;
+		len = sizeof(extra);
+
+		for (; len > 3; len -= 4) {
+			(void)memcpy(&val, buf, 4);
+			rndpool_add_one_word(rp, val);
+			buf += 4;
+		}
+	}
+
+	rp->stats.curentropy += extra_entropy;
+	rp->stats.added += extra_entropy;
+#endif
+
 	rp->stats.curentropy += entropy;
 	rp->stats.added += entropy;
 


Home | Main Index | Thread Index | Old Index