[Cryptography] OpenSSL CSPRNG work

Theodore Ts'o tytso at mit.edu
Sat Jul 1 20:40:11 EDT 2017


On Sat, Jul 01, 2017 at 07:10:10PM +0000, Salz, Rich via cryptography wrote:
> > Again, I humbly request that, whatever clever userspace machinery you
> > devise, please disable it completely by default on any system with
> > getrandom() / getentropy() / etc. (Key words are "by default". Sure, provide
> > APIs for enabling whatever you want... But by default, please just use the
> > system's provided mechanisms.)
> 
> This is unlikely to happen *as the default* because of DoS concerns;
> see Colm's posts and tweets on this. It will be possible to make
> RAND_bytes() do nothing but call a function you specify, but that
> will have to be enabled at configuration time.  Most likely, the O/S
> will be used to seed/reseed an AES-CTR DRBG implementation.

If you mean DoS concerns against the kernel, on any non-prehistoric
(3.13+) Linux kernel, you're not going to "drain the entropy" on the
/dev/random pool.  (Which first of all, makes no sense because you
should be using /dev/urandom, where entropy accounting doesn't matter,
and secondly because /dev/urandom is only periodically reseeding the
CRNG from the entropy pool.)

As I mentioned previously, using a n1-standard-1 GCE VM, it takes 2.9
microseconds to generate 32 bytes worth of randomness (executing
getrandom in a tight loop), and on pre-ChaCha20 CRNG kernels
(pre-4.9), it takes 5.5 microseconds.

On my T470 laptop (with a 2.8GHz Core i7-7600U Kirby Lake CPU),
running Linux 4.11.1 and libbsd 0.8.3, getrandom(2) takes 0.379
microseconds, versus 0.137 microseconds for arc4random_buf(3).
Arc4random_buf() is is a userspace ChaCha20 RNG originally from
OpenBSD.  Of the 0.379 vs 0.137 microsecond difference, from measuring
the time to execute getpid(2), only 0.042 microseconds can be
attributable to the system call overhead.  What's the rest of the
difference?  I suspect it's either locking overhead or a more
optimized Chacha20 implementation.

So yes, a userspace CRNG can certainly be faster, and if you don't
mind the hair of dealing with process-level state (e.g., dealing
fork(2), making sure it doesn't get written out to swap, or written
out to a core dump, etc.), there are certainly valid reasons to use a
userspace RNG.  But the cost of using getrandom(2) directly really
isn't as great as some people are making it out to be.

For these reasons, for a less-than-clueful applications programmer,
I'd personally recommend getrandom(2) over trying to implement their
own CRNG in userspace, and probably getting wrong.  I'm sure, though,
the OpenSSL devs are far more talented than your average application
programmer!  :-)

Cheers,

					- Ted

/*
 * bench-getrandom.c
 *
 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
 *
 * This file may be redistributed under the terms of the GNU Public
 * License Version 2.
 *
 * The init_resource_track() and print_resource_track() functions are
 * adapted from in e2fsck/util.c from e2fsprogs.
 *
 * Compile via: (do NOT enable any optimizations)
 *
 * cc -o /tmp/bench-getrandom /home/tytso/src/bench-getrandom.c -lbsd -Wall
 * 
 */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <bsd/stdlib.h>

#ifndef __NR_getrandom
#ifdef __i386__
#define __NR_getrandom	355
#elif __x86_64__
#define __NR_getrandom 318
#else
#error No getrandom syscall number!
#endif
#endif

struct resource_track {
	struct timeval time_start;
	struct timeval user_start;
	struct timeval system_start;
};

struct resource_track	global_rtrack;

void init_resource_track(struct resource_track *track)
{
	struct rusage r;

	gettimeofday(&track->time_start, 0);
	getrusage(RUSAGE_SELF, &r);
	track->user_start = r.ru_utime;
	track->system_start = r.ru_stime;
}

static __inline__ float timeval_subtract(struct timeval *tv1,
					 struct timeval *tv2)
{
	return ((tv1->tv_sec - tv2->tv_sec) +
		((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000);
}

static void print_resource_track(struct resource_track *track)
{
	struct rusage r;
	struct timeval time_end;

	gettimeofday(&time_end, 0);
	getrusage(RUSAGE_SELF, &r);

	printf("Elapsed time: %6.3f/%6.3f/%6.3f\n",
	       timeval_subtract(&time_end, &track->time_start),
	       timeval_subtract(&r.ru_utime, &track->user_start),
	       timeval_subtract(&r.ru_stime, &track->system_start));
}


int main(int argc, char **argv)
{
	int i;
	unsigned char buf[32];
	struct resource_track track;

	init_resource_track(&track);
	for (i=0; i < 1000000; i++) {
		(void) syscall(__NR_getrandom, buf, sizeof(buf), 0);
	}
	print_resource_track(&track);
	init_resource_track(&track);
	for (i=0; i < 1000000; i++) {
		arc4random_buf(buf, sizeof(buf));
	}
	print_resource_track(&track);
	init_resource_track(&track);
	for (i=0; i < 1000000; i++) {
		syscall(SYS_getpid);
	}
	print_resource_track(&track);
	exit(0);
}


More information about the cryptography mailing list