tech-kern archive

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

a working patch to _allow_ non-hardware-RNG entropy sources



As may be obvious to some by now my original proof-of-concept patch was
not exactly complete or even near correct.

This is the actual patch I've been testing (though it's collected from
places in a cvs-examine emacs buffer so may be incomplete).

I've restored some of the code that does actual entropy estimation using
the value and time deltas as before -- I think I've got it correct and
it seems to work as before.

This patch is designed to restore the admin's the ability to choose
non-hardware-RNGs as sources of entropy bits.

After all, this is a "unix" system, and, to quote Eric Allman, "Unix
gives you just enough rope to hang yourself -- and then a couple of more
feet, just to be sure."

The intent of course is also to mostly leave all this rope coiled neatly
in the corner and not to change the default behaviour of the system.  I
don't quite do that yet though -- I do leave RND_TYPE_{ENV,POWER,SKEW}
devices without NO_ESTIMATE by default.  This is probably still a point
of contention, but I'll propose it as a requirement for older hardware.
Perhaps this choice could be an options(4) option and port maintainers
could choose to enable it in their GENERIC, etc. kernels or not.  In the
end though lots of old hardware won't have environmental sensors or any
power events to use, and I'm not sure clock skew is actually a viable
source.

I've tested it on real hardware and with Xen dom0 and domU kernels.

I successfully use the following for Xen domUs that boot and run from
read-only ISOs:

	rndctl=YES	rndctl_flags="-t disk; -t vm"

I think in the case where your local network is not easily accessed by
adversaries you can also safely add "; -t net" to the above, assuming
your network interface driver has calls to rnd_attach_source() and
rnd_add_uint32().

This patch is against 2021-03-10 sources.

Index: kern/kern_clock.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/sys/kern/kern_clock.c,v
retrieving revision 1.144
diff -u -r1.144 kern_clock.c
--- kern/kern_clock.c	16 Jan 2021 02:20:00 -0000	1.144
+++ kern/kern_clock.c	9 Apr 2021 18:16:52 -0000
@@ -173,7 +173,7 @@
 	if (ci != ci->ci_package1st)
 		return;

-	/* Take a sample and enter it into the pool.  */
+	/* Take a sample (timestamp only) and enter it into the pool.  */
 	rnd_add_uint32(&C->source, 0);

 	/*
@@ -283,12 +283,12 @@

 	rndsource_setcb(&hardclockrnd.source, clockrnd_get, &hardclockrnd);
 	rnd_attach_source(&hardclockrnd.source, "hardclock", RND_TYPE_SKEW,
-	    RND_FLAG_COLLECT_TIME|RND_FLAG_HASCB);
+	    RND_FLAG_COLLECT_TIME|RND_FLAG_ESTIMATE_TIME|RND_FLAG_HASCB);
 	if (stathz) {
 		rndsource_setcb(&statclockrnd.source, clockrnd_get,
 		    &statclockrnd);
 		rnd_attach_source(&statclockrnd.source, "statclock",
-		    RND_TYPE_SKEW, RND_FLAG_COLLECT_TIME|RND_FLAG_HASCB);
+		    RND_TYPE_SKEW, RND_FLAG_COLLECT_TIME|RND_FLAG_ESTIMATE_TIME|RND_FLAG_HASCB);
 	}
 }

Index: kern/kern_entropy.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/sys/kern/kern_entropy.c,v
retrieving revision 1.30
diff -u -r1.30 kern_entropy.c
--- kern/kern_entropy.c	12 Feb 2021 19:48:26 -0000	1.30
+++ kern/kern_entropy.c	9 Apr 2021 18:08:30 -0000
@@ -1308,6 +1308,8 @@
 		KASSERT(E->stage >= ENTROPY_WARM);
 		printf("entropy: pid %d (%s) blocking due to lack of entropy\n",
 		       curproc->p_pid, curproc->p_comm);
+		uprintf("entropy: pid %d (%s) blocking due to lack of entropy\n",
+		       curproc->p_pid, curproc->p_comm);

 		if (ISSET(flags, ENTROPY_SIG)) {
 			error = cv_wait_sig(&E->cv, &E->lock);
@@ -1512,6 +1514,7 @@
     void *getarg)
 {

+	/* XXX why the heck doesn't this set RND_FLAG_HACB itself???? */
 	rs->get = get;
 	rs->getarg = getarg;
 }
@@ -1535,13 +1538,38 @@
 	/*
 	 * Apply some standard flags:
 	 *
-	 * - We do not bother with network devices by default, for
-	 *   hysterical raisins (perhaps: because it is often the case
-	 *   that an adversary can influence network packet timings).
+	 * - We do not bother at all with network devices and VM events by
+	 *   default, for hysterical raisins (perhaps: because it is often the
+	 *   case that an adversary can influence or observe network packet
+	 *   timings or paging activity and thus possibly infer what entropy
+	 *   bits have been collected from these devices).
+	 *
+	 * - We also do not trust anything but environmental sensors, power
+	 *   events, clock skews, and of course hardware RNG devices for
+	 *   counting or supplying entropy bits by default, though they are
+	 *   allowed to contribute to the runtime entropy pool.  We could
+	 *   probably trust tape, disk, and tty devices, but there is some
+	 *   controversy about these, perhaps because an adversary may also be
+	 *   able to observe, or perhaps even influence, their activity well
+	 *   enough to predict their entropy bits.  (All told though the more
+	 *   devices, and device types, the less predictable the whole system's
+	 *   entropy pool will be as it gets more difficult to simultaneously
+	 *   influence and/or observe the whole collection of sources.)
 	 */
 	switch (type) {
+	case RND_TYPE_SKEW:
+	case RND_TYPE_ENV:
+	case RND_TYPE_POWER:
+	case RND_TYPE_RNG:
+		/* these devices are trusted to both collect and
+		 * "estimate"(count) entropy by default */
+		break;
 	case RND_TYPE_NET:
+	case RND_TYPE_VM:
 		flags |= RND_FLAG_NO_COLLECT;
+		/* FALLTHRU */
+	default:
+		flags |= RND_FLAG_NO_ESTIMATE;
 		break;
 	}

@@ -1577,6 +1605,16 @@
 	KASSERT(i == __arraycount(extra));
 	entropy_enter(extra, sizeof extra, 0);
 	explicit_memset(extra, 0, sizeof extra);
+
+	aprint_verbose("entropy: %s attached as an entropy source (", rs->name);
+	if (!(flags & RND_FLAG_NO_COLLECT)) {
+		printf("collecting");
+		if (flags & RND_FLAG_NO_ESTIMATE)
+			printf(" without estimation");
+	} else {
+		printf("off");
+	}
+	printf(")\n");
 }

 /*
@@ -1610,6 +1648,8 @@

 	/* Free the per-CPU data.  */
 	percpu_free(rs->state, sizeof(struct rndsource_cpu));
+
+	aprint_verbose("entropy: %s detached as an entropy source\n", rs->name);
 }

 /*
@@ -1741,35 +1781,176 @@
 }

 /*
+ * Use the timing/value of the event to estimate the entropy gathered.
+ * If all the differentials (first, second, and third) are non-zero, return
+ * non-zero.  If any of these are zero, return zero.
+ */
+static inline uint32_t
+rnd_delta_estimate(rnd_delta_t *d, uint32_t v, uint32_t delta)
+{
+	uint32_t delta2, delta3;
+
+	d->insamples++;
+
+	/*
+	 * Calculate the second and third order differentials
+	 */
+	if (delta > (uint32_t)d->dx)
+		delta2 = delta - (uint32_t)d->dx;
+	else
+		delta2 = (uint32_t)d->dx - delta;
+
+	if (delta2 > (uint32_t)d->d2x)
+		delta3 = delta2 - (uint32_t)d->d2x;
+	else
+		delta3 = (uint32_t)d->d2x - delta2;
+
+	d->x = v;
+	d->dx = delta;
+	d->d2x = delta2;
+
+	/*
+	 * If any delta is 0, we got no entropy.  If all are non-zero, we
+	 * might have something.
+	 */
+	if (delta == 0 || delta2 == 0 || delta3 == 0)
+		return 0;
+
+	d->outbits++;
+
+	return 1;
+}
+
+/*
+ * Delta estimator for 32-bit timestamps.
+ * Timestaps generally increase, but may wrap around to 0.
+ * If t decreases, it is assumed that wrap-around occurred (once).
+ */
+static inline uint32_t
+rnd_dt_estimate(krndsource_t *rs, uint32_t t)
+{
+	uint32_t delta;
+	uint32_t ret;
+	rnd_delta_t *d = &rs->time_delta;
+
+	if (t < (uint32_t)d->x) {
+		delta = UINT32_MAX - (uint32_t)d->x + t;
+	} else {
+		delta = t - (uint32_t)d->x;
+	}
+
+	ret = rnd_delta_estimate(d, t, delta);
+
+	KASSERT(d->x == t);
+	KASSERT(d->dx == delta);
+
+	return ret;
+}
+
+/*
+ * Delta estimator for arbitrary unsigned 32 bit values.
+ */
+static inline uint32_t
+rnd_dv_estimate(krndsource_t *rs, uint32_t v)
+{
+	uint32_t delta;
+	uint32_t ret;
+	rnd_delta_t *d = &rs->value_delta;
+
+	if (v >= (uint32_t)d->x) {
+		delta = v - (uint32_t)d->x;
+	} else {
+		delta = (uint32_t)d->x - v;
+	}
+
+	ret = rnd_delta_estimate(d, v, delta);
+
+	KASSERT(d->x == v);
+	KASSERT(d->dx == delta);
+
+	return ret;
+}
+
+static inline uint32_t
+rnd_estimate_entropy(krndsource_t *rs, uint32_t ts, uint32_t val)
+{
+	uint32_t entropy = 0, dt_est, dv_est;
+
+	dt_est = rnd_dt_estimate(rs, ts);
+	dv_est = rnd_dv_estimate(rs, val);
+
+	if (!(rs->flags & RND_FLAG_NO_ESTIMATE)) {
+
+		if (rs->flags & RND_FLAG_ESTIMATE_TIME) {
+			entropy += dt_est;
+		}
+
+		if (rs->flags & RND_FLAG_ESTIMATE_VALUE) {
+			entropy += dv_est;
+		}
+	}
+
+	return entropy;
+}
+
+/*
  * rnd_add_uint32(rs, value)
  *
- *	Enter 32 bits of data from an entropy source into the pool.
+ *	Enter a 32 bits of data from an entropy source into the pool.
  *
- *	If rs is NULL, may not be called from interrupt context.
+ *	The number of entropy bits for the value is determined by
+ *	rnd_estimate_entropy() based on either the time delta and/or the value's
+ *	delta from the last time or value data was added from the same source.
  *
- *	If rs is non-NULL, may be called from any context.  May drop
- *	data if called from interrupt context.
+ *	If rs is NULL, this may not be called from an interrupt context.
+ *
+ *	If rs is non-NULL, this may be called from any context.  It may ignore
+ *	the data if called from interrupt context.
  */
 void
 rnd_add_uint32(struct krndsource *rs, uint32_t value)
 {
+	u_int32_t ts;
+	u_int32_t entropy = 0;
+
+	if (rs->flags & RND_FLAG_NO_COLLECT)
+		return;

-	rnd_add_data(rs, &value, sizeof value, 0);
+	/*
+	 * Sample the counter as soon as possible to avoid
+	 * entropy overestimation.
+	 */
+	ts = entropy_timer();
+
+	/*
+	 * If we are estimating entropy on this source,
+	 * calculate the possible bits of entropy.
+	 */
+
+	if ((rs->flags & RND_FLAG_NO_ESTIMATE) == 0) {
+		entropy = rnd_estimate_entropy(rs, ts, value);
+	}
+
+	rnd_add_data(rs, &value, sizeof value, entropy);
 }

+#if 0
+/* xxx Unused */
 void
 _rnd_add_uint32(struct krndsource *rs, uint32_t value)
 {

-	rnd_add_data(rs, &value, sizeof value, 0);
+	rnd_add_data(rs, &value, sizeof value, 1);
 }

+/* xxx Unused */
 void
 _rnd_add_uint64(struct krndsource *rs, uint64_t value)
 {

-	rnd_add_data(rs, &value, sizeof value, 0);
+	rnd_add_data(rs, &value, sizeof value, 1);
 }
+#endif

 /*
  * rnd_add_data(rs, buf, len, entropybits)
Index: kern/subr_autoconf.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/sys/kern/subr_autoconf.c,v
retrieving revision 1.277
diff -u -r1.277 subr_autoconf.c
--- kern/subr_autoconf.c	27 Jan 2021 04:54:08 -0000	1.277
+++ kern/subr_autoconf.c	8 Apr 2021 21:38:59 -0000
@@ -358,7 +358,7 @@
 	TAILQ_INSERT_TAIL(&allcftables, &initcftable, ct_list);

 	rnd_attach_source(&rnd_autoconf_source, "autoconf", RND_TYPE_UNKNOWN,
-	    RND_FLAG_COLLECT_TIME);
+	    RND_FLAG_COLLECT_TIME|RND_FLAG_ESTIMATE_TIME|RND_FLAG_NO_ESTIMATE);

 	config_initialized = true;
 }
@@ -1429,7 +1429,8 @@
 	xunit = number(&num[sizeof(num)], myunit);
 	lunit = &num[sizeof(num)] - xunit;
 	if (lname + lunit > sizeof(dev->dv_xname))
-		panic("config_devalloc: device name too long");
+		panic("config_devalloc: device name too long for %s",
+		      cf->cf_atname);

 	dvl = device_getlock(dev);

@@ -1467,6 +1468,14 @@
 	if (dev->dv_cfdriver->cd_attrs != NULL)
 		config_add_attrib_dict(dev);

+#if 0
+	aprint_debug_dev(dev, "config_devalloc: setup completed for %s unit %d as %s (dev = %p)\n",
+			 dev->dv_cfdriver->cd_name,
+			 dev->dv_unit,
+			 dev->dv_xname,
+			 dev);
+#endif
+
 	return dev;
 }

Index: kern/subr_prf.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/sys/kern/subr_prf.c,v
retrieving revision 1.186
diff -u -r1.186 subr_prf.c
--- kern/subr_prf.c	10 Mar 2021 13:27:51 -0000	1.186
+++ kern/subr_prf.c	8 Apr 2021 21:39:41 -0000
@@ -151,7 +151,7 @@
 	mutex_init(&kprintf_mtx, MUTEX_DEFAULT, IPL_HIGH);
 #ifdef RND_PRINTF
 	rnd_attach_source(&rnd_printf_source, "printf", RND_TYPE_UNKNOWN,
-	    RND_FLAG_COLLECT_TIME|RND_FLAG_COLLECT_VALUE);
+	    RND_FLAG_DEFAULT);
 #endif
 	kprintf_inited = true;
 }
Index: rump/librump/rumpkern/hyperentropy.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/sys/rump/librump/rumpkern/hyperentropy.c,v
retrieving revision 1.17
diff -u -r1.17 hyperentropy.c
--- rump/librump/rumpkern/hyperentropy.c	30 Apr 2020 03:41:20 -0000	1.17
+++ rump/librump/rumpkern/hyperentropy.c	8 Apr 2021 22:16:39 -0000
@@ -64,6 +64,6 @@
 {

 	rndsource_setcb(&rndsrc, &feedrandom, NULL);
-	rnd_attach_source(&rndsrc, "rump_hyperent", RND_TYPE_VM,
+	rnd_attach_source(&rndsrc, "rump_hyperent", RND_TYPE_VM, /* XXX this seems like a re-use of a type! */
 	    RND_FLAG_COLLECT_VALUE|RND_FLAG_HASCB);
 }
Index: sys/rndio.h
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/sys/sys/rndio.h,v
retrieving revision 1.2
diff -u -r1.2 rndio.h
--- sys/rndio.h	6 Sep 2015 06:01:02 -0000	1.2
+++ sys/rndio.h	9 Apr 2021 18:01:03 -0000
@@ -91,8 +91,29 @@
 #define RND_FLAG_ESTIMATE_TIME	0x00004000	/* estimate entropy on time */
 #define RND_FLAG_ESTIMATE_VALUE	0x00008000	/* estimate entropy on value */
 #define	RND_FLAG_HASENABLE	0x00010000	/* has enable/disable fns */
-#define RND_FLAG_DEFAULT	(RND_FLAG_COLLECT_VALUE|RND_FLAG_COLLECT_TIME|\
-				 RND_FLAG_ESTIMATE_TIME)
+#define RND_FLAG_DEFAULT	(RND_FLAG_COLLECT_VALUE|RND_FLAG_ESTIMATE_VALUE| \
+				 RND_FLAG_COLLECT_TIME|RND_FLAG_ESTIMATE_TIME)
+/*
+ * N.B.:  It would appear from the above value that by default all devices using
+ * RND_FLAG_DEFAULT will be enabled directly to collect _and_ estimate(count)
+ * entropy based on both deltas in values they submit, and the time delta
+ * between submissions.  HOWEVER this is moderated by a switch in
+ * kern_entropy.c:rnd_attach_source() which will add either the NO_COLLECT
+ * and/or the NO_ESTIMATE flag depending on what type the device is.
+ *
+ * By default only RND_TYPE_SKEW, RND_TYPE_ENV, RND_TYPE_POWER, and RND_TYPE_RNG
+ * will avoid both of these flags being set.
+ *
+ * Network devices will be entirely disabled (from both colleciton and
+ * estimating) as they can possibly be easily influenced externally.
+ *
+ * All other devices will be given the NO_ESTIMATE flag such that they are not
+ * used to estimate(count) entropy by default.
+ *
+ * In any case either or both of the RND_FLAG_NO_* flags can be turned off at
+ * runtime by the RNDCTL ioctl on rnd(4), i.e. by rndctl(8) such that entropy
+ * collection and estimation can be enabled on a per-device or per-type basis.
+ */

 #define	RND_TYPE_UNKNOWN	0	/* unknown source */
 #define	RND_TYPE_DISK		1	/* source is physical disk */
Index: sys/rndsource.h
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/sys/sys/rndsource.h,v
retrieving revision 1.7
diff -u -r1.7 rndsource.h
--- sys/rndsource.h	30 Apr 2020 03:28:19 -0000	1.7
+++ sys/rndsource.h	8 Apr 2021 18:15:01 -0000
@@ -45,8 +45,6 @@

 /*
  * struct rnd_delta_estimator
- *
- *	Unused.  Preserved for ABI compatibility.
  */
 typedef struct rnd_delta_estimator {
 	uint64_t	x;
@@ -68,8 +66,8 @@
 struct krndsource {
 	LIST_ENTRY(krndsource) list;	/* the linked list */
         char            name[16];       /* device name */
-	rnd_delta_t	time_delta;	/* unused */
-	rnd_delta_t	value_delta;	/* unused */
+	rnd_delta_t	time_delta;	/* */
+	rnd_delta_t	value_delta;	/* */
         uint32_t        total;          /* number of bits added while cold */
         uint32_t        type;           /* type, RND_TYPE_* */
         uint32_t        flags;          /* flags, RND_FLAG_* */
@@ -89,8 +87,10 @@
 	    uint32_t);
 void	rnd_detach_source(struct krndsource *);

+#if 0
 void	_rnd_add_uint32(struct krndsource *, uint32_t); /* legacy */
 void	_rnd_add_uint64(struct krndsource *, uint64_t); /* legacy */
+#endif

 void	rnd_add_uint32(struct krndsource *, uint32_t);
 void	rnd_add_data(struct krndsource *, const void *, uint32_t, uint32_t);
Index: uvm/uvm_page.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/sys/uvm/uvm_page.c,v
retrieving revision 1.250
diff -u -r1.250 uvm_page.c
--- uvm/uvm_page.c	20 Dec 2020 11:11:34 -0000	1.250
+++ uvm/uvm_page.c	8 Apr 2021 21:41:20 -0000
@@ -983,8 +983,7 @@
 	 * Attach RNG source for this CPU's VM events
 	 */
         rnd_attach_source(&ucpu->rs, ci->ci_data.cpu_name, RND_TYPE_VM,
-	    RND_FLAG_COLLECT_TIME|RND_FLAG_COLLECT_VALUE|
-	    RND_FLAG_ESTIMATE_VALUE);
+	    RND_FLAG_DEFAULT);
 }

 /*

--
					Greg A. Woods <gwoods%acm.org@localhost>

Kelowna, BC     +1 250 762-7675           RoboHack <woods%robohack.ca@localhost>
Planix, Inc. <woods%planix.com@localhost>     Avoncote Farms <woods%avoncote.ca@localhost>

Attachment: pgpdRLQHnu8h9.pgp
Description: OpenPGP Digital Signature



Home | Main Index | Thread Index | Old Index