tech-kern archive

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

getrandom and getentropy



[please followup to tech-userlevel@ to keep discussion in one place]

Traditionally NetBSD has implemented three ways to get unpredictable
independent uniform random bytes out of the kernel's entropy pool:

- /dev/urandom -- never blocks, uses whatever is in the entropy pool,
  returns short reads only if interrupted

- /dev/random -- may block, may be truncated to 16 bytes or otherwise
  short reads; if system has partial entropy, blocks until full
  entropy; traditionally also sometimes blocks randomly at other times
  according to the obsolete `entropy depletion' semantics

- sysctl kern.arandom -- never blocks, uses whatever is in the entropy
  pool like /dev/urandom, returns up to 256 bytes.  (There's also
  kern.urandom, but it only provides 32 bits at a time and to my
  knowledge has never been documented.)

I propose that we additionally adopt getrandom and getentropy, two C
APIs the world is converging on.  For getentropy, this can be a simple
userland wrapper in libc; for getrandom, this requires a new path into
the kernel, and I propose that we adopt getrandom as both a system
call and a public libc stub, like Linux/glibc and FreeBSD both did.

The getentropy and getrandom APIs, described below, may not be
perfect, and for several years I was reluctant to adopt them, but I've
come to the conclusion that adopting them is a good idea, for reasons
below, and that there's no clearly better alternative ways to achieve
the same desiderata.


DETAILS

The usage model for getentropy, from OpenBSD [1], is:

	if (getentropy(buf, buflen) == -1)
		err(...);

The semantics is essentially the same as sysctl kern.arandom: never
blocks, uses whatever is in the entropy pool like /dev/urandom,
returns up to 256 bytes (fails with EIO if buflen>256).  Once the
system entropy pool is seeded, this is good, although it provides no
way to wait until the entropy pool is seeded.

getrandom, from Linux [2], has three usage models:

	/*
	 * Recommended default usage -- may block once at boot time;
	 * otherwise never blocks.  Limited only to 33554431 bytes.
	 */
	if (getrandom(buf, buflen, 0) == -1)
		err(...);

	/*
	 * Never blocks, like /dev/urandom, sysctl kern.arandom, and
	 * getentropy.  Limited only to 33554431 bytes.  Called
	 * GRND_INSECURE because it is safe only after you have
	 * already verified the system entropy pool is seeded -- e.g.,
	 * by calling getrandom(0,0,0) once or reading a single byte
	 * from /dev/random; if you _don't_ wait for the pool to be
	 * seeded, then it can ruin the security of the entropy pool
	 * for _everyone_.
	 */
	if (getrandom(buf, buflen, GRND_INSECURE) == -1)
		err(...);

	/*
	 * May block, may return very short reads, like /dev/random;
	 * limited to 512 bytes.  NOT RECOMMENDED -- provided only for
	 * source compatibility with applications written for Linux,
	 * FreeBSD, or Solaris.
	 */
	nread = getrandom(buf, buflen, GRND_RANDOM);
	if (nread == -1)
		err(...);
	if ((size_t)nread < buflen)
		/* start over and try to read some more, maybe */

I drafted a man page for getrandom that I think is considerably
clearer than the Linux manual; you can read it here:
<https://www.NetBSD.org/~riastradh/tmp/20200430/getrandom.html> (the
mdoc source is also in the attached patch).

The option GRND_NONBLOCK may also be bitwise-OR'd into the third
argument to make it fail with EAGAIN rather than blocking in the event
it would have blocked.  For example, `if (getrandom(0,0,GRND_NONBLOCK)
== -1 && errno == EAGAIN) ...' serves to test whether the entropy pool
is not yet seeded.


RATIONALE

### Why adopt anything?  Why not leave things as they are?

1. getrandom and getentropy are easier to use than /dev/u?random or a
   sysctl, and a lot of software has already adopted them to make
   getting cryptographic key material much simpler than it was in the
   past.  They serve a real need in practice.

2. Like sysctl kern.arandom, getrandom and getentropy don't need /dev
   to be populated (so they can be used in an empty chroot) and don't
   require opening and closing a file descriptor.

   I see very little value in using the file system to control access
   to the random number generator -- after all, the process can always
   use sysctl kern.arandom.  I see much greater value in making it
   more easily accessible to applications to reduce the temptation to
   use garbage like srand(time()).

3. getrandom and getentropy are more portable than sysctl
   kern.arandom:

   . Linux/glibc [2], FreeBSD [3], and Solaris (Oracle [4] and illumos
     [5]) all support getrandom.

   . glibc [6], OpenBSD [1], FreeBSD [7], and Oracle Solaris [4] (not
     sure about illumos) all support getentropy.

   Only NetBSD still supports kern.arandom, to my knowledge; it came
   from OpenBSD but they have replaced it altogether by getentropy.

4. Many developers these days expect the semantics that the
   cryptographic random number generator does not return anything
   until the system is seeded.  For example, this is the contract
   provided by Rust's OsRng [8].

   Although a never-blocking API like sysctl kern.arandom may
   technically function as a drop-in replacement, it doesn't provide
   the same security contract and may lead people to unpleasant
   surprises like <https://factorable.net>.

   Although an _often_ blocking API like the traditional /dev/random
   semantics may technically function as a drop-in replacement, it
   leads to excessive blocking in programs that don't need it even for
   security, which was the bane of Rust builds for a long time -- for
   a _system_, you only need to wait for the entropy pool to be seeded
   once, but each Rust process was reading from /dev/random on its own
   independently and blocking over and over again.  (That's no longer
   relevant in NetBSD-current, but there are lots of systems that
   still provide the traditional /dev/random block-often behaviour so
   application developers might still reasonably prefer
   getrandom(p,n,0) over reading from /dev/random.)

### Why bother with getentropy?  Why not just getrandom?

The alias getentropy(p,n) := getrandom(p,n,GRND_INSECURE), or
getentropy(p,n) := sysctl(kern.arandom,p,n), costs very little to
support, and makes it easier for application developers to reduce
#ifdef clutter like the rat's nest that is OpenSSL rand_unix.c which
for many years had code to call kern.arandom that seems to have been
never actually reached until yesterday.

### Why a new getrandom system call?  Why not a userland libc wrapper?

While getentropy(p,n) is essentially the same as
sysctl(kern.arandom,p,n), the semantics of getrandom(p,n,0) (and the
slightly silly semantics of getrandom(p,n,GRND_RANDOM)) requires a
potentially blocking code path that is currently available to userland
only via /dev/random.

In other words, we have no path accessible from userland to
simultaneously to satisfy desiderata (2) and (4).  So if we want to
satisfy them, we need a new path into the kernel.

### Why not invent our own interface?

Since desiderata (2) and (4) cannot be both satisfied by anything we
have in tree, I considered various alternatives to adopting getrandom
wholesale:

- a better system call
  => We could just add, say, getrandom_wait(p,n) to be like
     getrandom(p,n,0), and then implement getrandom as a userland libc
     wrapper around either getrandom_wait OR sysctl(kern.arandom).

     But no applications are likely to use getrandom_wait, and this
     would take more userland logic to implement; it's not clear that
     there's much value to having a NetBSD-specific system call that
     serves no other purpose than to implement part of the getrandom
     API.

- a better sysctl
  => We could just add, say, sysctl(kern.blockingrandom,p,n) to be
     like getrandom(p,n,0), and then implement getrandom as a userland
     libc wrapper around either sysctl(kern.blockingrandom) OR
     sysctl(kern.arandom).

     But it's usually rude for reads from sysctls to block, and it's
     not clear to me why a sysctl is any better than a system call.

- better names for the existing system call semantics
  => We could adopt, say:
     . NETBSD_GRND_DEFAULT <=> Linux 0
     . NETBSD_GRND_URANDOM <=> Linux GRND_INSECURE (/dev/urandom semantics)
     . NETBSD_GRND_LEGACY <=> Linux GRND_RANDOM
     Then we could just compile everything in pkgsrc with the
     compatibility definitions
       CPPFLAGS+= -DGRND_INSECURE=NETBSD_GRND_URANDOM.
     It's not clear to me that cosmetic changes like this are worth
     much, since application developers will just go for the de facto
     standard and ignore our NetBSDisms, so in the end we would bring
     greater maintenance burden on ourselves.

- a randomfd(flags) system call to create a file descriptor
  => Example:
	if ((fd = randomfd(RANDFD_CLOEXEC|RANDFD_BLOCKONCE)) == -1)
		err(1, "randomfd");
	... kevent(..., fd) ...
	if ((nread = read(fd,p,n)) == -1)
		err(1, "read");
	if ((size_t)nread < n)
		errx(1, "truncated");
	...
  => This would fail desideratum (1) -- it is not really less work to
     use such an API than it is to read sysctl kern.arandom or to read
     from /dev/u?random.
  => This would satisfy part of desideratum (2) -- no need for /dev.
     But it would still require creating a file descriptor, which
     might fail in the event of rlimit exhaustion, failing the other
     part of desideratum (2).
  => Like all the other options here, this would fail desideratum (3):
     if we invent a new API there would be nobody using it at first,
     and we would have to justify a reason to use it.
  => This would provide the nice property that you can multiplex a
     wait for the pool to be seeded with other kinds of blocking using
     select/poll/kqueue.  But it's not clear that the value of that is
     worth the trouble of inventing a new standard and getting anyone
     to adopt it.  After all, you could always just fork a subprocess
     and wait for it to complete using, say, kevent or a pipe hack to
     multiplex it with other I/O.

### Why were you initially reluctant to adopt them and what changed?

- It wasn't clear that the world would adopt getentropy, but it's
  widespread now and the cost to adding a trivial libc stub is small.

- I found the Linux documentation for getrandom difficult to follow,
  and I suspect most other people do too.  It is hard to see what
  configuration of flags give you the semantics you want, and with
  eight different choices for the three separate flags it seemed like
  a good way to have lots of people shoot themselves in the foot.

  However, after implementing the semantics and distilling it, I
  realized that it cleanly breaks down into only three usage models
  (with an nonblocking option -- meaning when it would block, it
  returns EAGAIN/EWOULDBLOCK instead), two of which are reasonable
  (flags=0 and flags=GRND_INSECURE), and one of which we didn't
  already have a pathway for.

  So the cost of adopting a silly operation (flags=GRND_RANDOM)
  strikes me as quite small in exchange for the benefit of source
  compatibility, and I tried to address the confusing API by writing
  short usage guidelines with clear examples in the man page
  <https://www.netbsd.org/~riastradh/tmp/20200430/getrandom.html> for
  what the flags argument can be.  If in doubt, use flags=0.

- In the past, I was reluctant to adopt the operation `block once
  early at boot and never again', because that invites application
  developers to write logic that never blocks during development and
  testing, but may then block as soon as you deploy something out in
  the field.

  Although the sometimes-blocks-for-no-obvious-reason semantics of
  /dev/random is frustrating in some contexts (like gpg), it ensures
  that the blocking path will generally be exercised, and I reasoned
  that it's better to have an API that keeps code paths exercised than
  to provide an easy API that sounds good at face value but will blow
  up in the field when you're not there to deal with it.

  But on reflection I realized that really this is just a matter of
  fault injection, and it's not necessary for the kernel to
  obstinately insist on doing fault injection for you to keep your
  code exercised.  (This is the same reason I switched the NetBSD
  kernel to cease doing `entropy depletion' by default, but left in a
  sysctl knob kern.entropy.depletion to turn it on for testing
  purposes so you don't have to write `blocking' injection logic
  yourself.)


IMPLEMENTATION

The attached patches implement getrandom and getentropy separately.
nia@ wrote the getentropy patch (probably needs a set list update too,
and could use an automatic test); I wrote the getrandom patch.
Feedback welcome!


[1] `getentropy -- get entropy', OpenBSD 6.6 Manual Pages, System
    Calls Manual, 2019-09-28.
    https://man.openbsd.org/OpenBSD-6.6/man2/getentropy.2

[2] `getrandom -- obtain a series of random bytes', Linux Programmer's
    Manual, 2017-09-15.
    http://man7.org/linux/man-pages/man2/getrandom.2.html

[3] `getrandom -- get random data', FreeBSD 12.1-RELEASE Manual Pages,
    BSD System Calls Manual, 2018-02-24.
    https://www.freebsd.org/cgi/man.cgi?query=getrandom&apropos=0&sektion=0&manpath=FreeBSD+12.1-RELEASE+and+Ports&arch=default&format=html

[4] Darran Moffat, `Solaris new system calls: getentropy(2) and
    getrandom(2)', Oracle Solaris Blog, 2015-07-07.
    https://blogs.oracle.com/solaris/solaris-new-system-calls%3a-getentropy2-and-getrandom2-v2    

[5] `getrandom -- get random numbers', illumos Manual Pages, System
    Calls, 2018-11-06.
    https://illumos.org/man/2/getrandom

[6] `getentropy -- fill a buffer with random bytes', Linux
    Programmer's Manual, 2017-09-15.
    http://man7.org/linux/man-pages/man3/getentropy.3.html

[7] `getentropy -- get entropy', FreeBSD 12.1-RELEASE Manual Pages,
    BSD Library Functions Manual, 2018-02-24.
    https://www.freebsd.org/cgi/man.cgi?query=getentropy&sektion=3&apropos=0&manpath=FreeBSD+12.1-RELEASE+and+Ports    

[8] rust-random manual, struct rand::rngs::OsRng, 2020-05-01.
    https://rust-random.github.io/rand/rand/rngs/struct.OsRng.html
>From c56cbc75071e66f014528d40e0e8c48c395f494d Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Mon, 13 Jan 2020 22:26:24 +0000
Subject: [PATCH 1/2] New system call getrandom() compatible with Linux.

Three ways to call:

getrandom(p, n, 0)              May block.  Returns up to n bytes at
                                p, guaranteeing >=256 bytes even if
                                interrupted after blocking.  Can be
                                used as getrandom(NULL,0,0) to serve
                                as `entropy barrier': return only
                                after system is seeded.

getrandom(p, n, GRND_INSECURE)  Never blocks.  Guarantees >=256 bytes
                                even if interrupted.  Equivalent to
                                /dev/urandom.  Safe only after
                                successful getrandom(..., 0),
                                getrandom(..., GRND_RANDOM), or read
                                from /dev/random.

getrandom(p, n, GRND_RANDOM)    May block.  Returns up to n bytes at
                                p, but no guarantees about how many.
                                Equivalent to /dev/random.  Legacy.

Can also use flags|GRND_NONBLOCK to fail with EWOULDBLOCK/EAGAIN
instead of blocking.  (The combination GRND_INSECURE|GRND_NONBLOCK is
the same as GRND_INSECURE, since GRND_INSECURE never blocks anyway.
The combination GRND_INSECURE|GRND_RANDOM is nonsensical and fails with
EINVAL.)
---
 distrib/sets/lists/comp/mi                  |   3 +
 distrib/sets/lists/debug/mi                 |   1 +
 distrib/sets/lists/tests/mi                 |   1 +
 lib/libc/sys/Makefile.inc                   |   7 +-
 lib/libc/sys/getrandom.2                    | 252 ++++++++++++++++++++
 sys/dev/random.c                            | 186 ++-------------
 sys/kern/files.kern                         |   1 +
 sys/kern/kern_entropy.c                     |  14 +-
 sys/kern/sys_getrandom.c                    | 244 +++++++++++++++++++
 sys/kern/syscalls.master                    |   3 +-
 sys/rump/librump/rumpkern/Makefile.rumpkern |   1 +
 sys/sys/Makefile                            |   2 +-
 sys/sys/entropy.h                           |   5 +-
 sys/sys/random.h                            |  69 ++++++
 tests/lib/libc/sys/Makefile                 |   1 +
 tests/lib/libc/sys/t_getrandom.c            | 170 +++++++++++++
 16 files changed, 788 insertions(+), 172 deletions(-)
 create mode 100644 lib/libc/sys/getrandom.2
 create mode 100644 sys/kern/sys_getrandom.c
 create mode 100644 sys/sys/random.h
 create mode 100644 tests/lib/libc/sys/t_getrandom.c

diff --git a/distrib/sets/lists/comp/mi b/distrib/sets/lists/comp/mi
index 6dfb4f6e3189..732eb5355300 100644
--- a/distrib/sets/lists/comp/mi
+++ b/distrib/sets/lists/comp/mi
@@ -3131,6 +3131,7 @@
 ./usr/include/sys/quotactl.h			comp-c-include
 ./usr/include/sys/radioio.h			comp-c-include
 ./usr/include/sys/radixtree.h			comp-c-include
+./usr/include/sys/random.h			comp-c-include
 ./usr/include/sys/ras.h				comp-c-include
 ./usr/include/sys/rb.h				comp-obsolete		obsolete
 ./usr/include/sys/rbtree.h			comp-c-include
@@ -12514,6 +12515,7 @@
 ./usr/share/man/html2/getpid.html		comp-c-htmlman		html
 ./usr/share/man/html2/getppid.html		comp-c-htmlman		html
 ./usr/share/man/html2/getpriority.html		comp-c-htmlman		html
+./usr/share/man/html2/getrandom.html		comp-c-htmlman		html
 ./usr/share/man/html2/getrlimit.html		comp-c-htmlman		html
 ./usr/share/man/html2/getrusage.html		comp-c-htmlman		html
 ./usr/share/man/html2/getsid.html		comp-c-htmlman		html
@@ -20426,6 +20428,7 @@
 ./usr/share/man/man2/getpid.2			comp-c-man		.man
 ./usr/share/man/man2/getppid.2			comp-c-man		.man
 ./usr/share/man/man2/getpriority.2		comp-c-man		.man
+./usr/share/man/man2/getrandom.2		comp-c-man		.man
 ./usr/share/man/man2/getrlimit.2		comp-c-man		.man
 ./usr/share/man/man2/getrusage.2		comp-c-man		.man
 ./usr/share/man/man2/getsid.2			comp-c-man		.man
diff --git a/distrib/sets/lists/debug/mi b/distrib/sets/lists/debug/mi
index 30c9d3b8de79..d4651b47445b 100644
--- a/distrib/sets/lists/debug/mi
+++ b/distrib/sets/lists/debug/mi
@@ -2127,6 +2127,7 @@
 ./usr/libdata/debug/usr/tests/lib/libc/sys/t_getitimer.debug		tests-lib-debug		debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/lib/libc/sys/t_getlogin.debug		tests-lib-debug		debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/lib/libc/sys/t_getpid.debug		tests-lib-debug		debug,atf,compattestfile
+./usr/libdata/debug/usr/tests/lib/libc/sys/t_getrandom.debug		tests-lib-debug		debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/lib/libc/sys/t_getrusage.debug		tests-lib-debug		debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/lib/libc/sys/t_getsid.debug		tests-lib-debug		debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/lib/libc/sys/t_getsockname.debug		tests-lib-debug		debug,atf,compattestfile
diff --git a/distrib/sets/lists/tests/mi b/distrib/sets/lists/tests/mi
index 9c5aa8d313e0..324f4975f125 100644
--- a/distrib/sets/lists/tests/mi
+++ b/distrib/sets/lists/tests/mi
@@ -3125,6 +3125,7 @@
 ./usr/tests/lib/libc/sys/t_getitimer		tests-lib-tests		compattestfile,atf
 ./usr/tests/lib/libc/sys/t_getlogin		tests-lib-tests		compattestfile,atf
 ./usr/tests/lib/libc/sys/t_getpid		tests-lib-tests		compattestfile,atf
+./usr/tests/lib/libc/sys/t_getrandom		tests-lib-tests		compattestfile,atf
 ./usr/tests/lib/libc/sys/t_getrusage		tests-lib-tests		compattestfile,atf
 ./usr/tests/lib/libc/sys/t_getsid		tests-lib-tests		compattestfile,atf
 ./usr/tests/lib/libc/sys/t_getsockname		tests-lib-tests		compattestfile,atf
diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc
index 7eddf7e3fc19..d7faaefef29a 100644
--- a/lib/libc/sys/Makefile.inc
+++ b/lib/libc/sys/Makefile.inc
@@ -110,7 +110,7 @@ ASM=	access.S acct.S \
 		__fstatvfs190.S fstatat.S  __futimes50.S futimens.S \
 	__getcwd.S __getdents30.S __getfh30.S __getvfsstat90.S getgroups.S\
 		__getitimer50.S __getlogin.S getpeername.S getpgid.S getpgrp.S \
-		getpriority.S getrlimit.S __getrusage50.S getsid.S \
+		getpriority.S getrandom.S getrlimit.S __getrusage50.S getsid.S \
 		getsockname.S getsockopt.S getsockopt2.S __gettimeofday50.S \
 	ioctl.S \
 	kqueue.S kqueue1.S ktrace.S \
@@ -250,8 +250,9 @@ MAN+=	accept.2 access.2 acct.2 adjtime.2 bind.2 brk.2 chdir.2 \
 	flock.2 fork.2 fsync.2 getcontext.2 getdents.2 \
 	getfh.2 getvfsstat.2 getgid.2 getgroups.2 \
 	getitimer.2 getlogin.2 getpeername.2 getpgrp.2 getpid.2 \
-	getpriority.2 getrlimit.2 getrusage.2 getsid.2 getsockname.2 \
-	getsockopt.2 gettimeofday.2 getuid.2 intro.2 ioctl.2 issetugid.2 \
+	getpriority.2 getrandom.2 getrlimit.2 getrusage.2 getsid.2 \
+	getsockname.2 getsockopt.2 gettimeofday.2 getuid.2\
+	intro.2 ioctl.2 issetugid.2 \
 	kill.2 kqueue.2 ktrace.2 _ksem.2 \
 	lfs_bmapv.2 lfs_markv.2 lfs_segclean.2 lfs_segwait.2 \
 	link.2 listen.2 lseek.2 \
diff --git a/lib/libc/sys/getrandom.2 b/lib/libc/sys/getrandom.2
new file mode 100644
index 000000000000..fcd1d41d431c
--- /dev/null
+++ b/lib/libc/sys/getrandom.2
@@ -0,0 +1,252 @@
+.\"	$NetBSD$
+.\"
+.\" Copyright (c) 2020 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Taylor R. Campbell.
+.\"
+.\" 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.
+.\"
+.Dd January 13, 2020
+.Dt GETRANDOM 2
+.Os
+.Sh NAME
+.Nm getrandom
+.Nd random number generation from system entropy
+.Sh LIBRARY
+.Lb libc
+.Sh SYNOPSIS
+.In sys/random.h
+.Ft ssize_t
+.Fn getrandom "void *buf" "size_t buflen" "unsigned int flags"
+.Sh DESCRIPTION
+The
+.Nm
+function fills
+.Fa buf
+with up to
+.Fa buflen
+independent uniform random bytes derived from the system's entropy
+pool.
+.Pp
+The function may block until the system has full entropy, meaning that
+the system has observed enough noise from physical processes that an
+adversary cannot predict what state it is in:
+.Bl -bullet -compact
+.It
+When the system has only partial entropy, the output of
+.Fn getrandom
+may be predictable.
+.It
+When the system has full entropy, the output is fit for use as
+cryptographic key material.
+.El
+.Pp
+The
+.Fa flags
+argument may be:
+.Bl -tag -offset abcd -width GRND_INSECURE
+.It Li 0
+Block until the system entropy pool has full entropy; then generate
+arbitrarily much data.
+.Em Recommended .
+.Pp
+If interrupted by a signal, may fail with
+.Er EINTR
+or return a short read.
+If successful, guaranteed to return at least 256 bytes even if
+interrupted.
+.It Dv GRND_INSECURE
+Do not block; instead fill
+.Fa buf
+with output derived from whatever is in the system entropy pool so
+far.
+Equivalent to reading from
+.Pa /dev/urandom ;
+see
+.Xr rnd 4 .
+.Pp
+If interrupted by a signal, may fail with
+.Er EINTR
+or return a short read.
+If successful, guaranteed to return at least 256 bytes even if
+interrupted.
+.Pp
+Despite the name, this is secure as long as you only do it
+.Em after
+at least one successful call without
+.Dv GRND_INSECURE ,
+such as
+.Li "getrandom(..., 0)"
+or
+.Li "getrandom(..., GRND_RANDOM)" ,
+or after reading at least one byte from
+.Pa /dev/random .
+.Pp
+.Sy WARNING :
+If you use
+.Dv GRND_INSECURE
+.Em before
+the system has full entropy. the output may enable an adversary to
+search the possible states of the entropy pool by brute force, and
+thereby reduce its entropy to zero.
+Thus, incautious use of
+.Dv GRND_INSECURE
+can ruin the security of the whole system.
+.It Dv GRND_RANDOM
+Block until the system entropy pool has full entropy; then generate a
+small amount of data.
+Equivalent to reading from
+.Pa /dev/random ;
+see
+.Xr rnd 4 .
+This is provided mainly for source compatibility with Linux; there is
+essentially no reason to ever use it.
+.El
+.Pp
+The flag
+.Dv GNRD_NONBLOCK
+may also be included with bitwise-OR, in which case if
+.Fn getrandom
+would have blocked without
+.Dv GRND_NONBLOCK ,
+it returns
+.Er EAGAIN
+instead.
+.Pp
+Adding
+.Dv GRND_NONBLOCK
+to
+.Dv GRND_INSECURE
+has no effect; the combination
+.Dv GRND_INSECURE Ns Li "|" Ns Li GRND_NONBLOCK
+is equivalent to
+.Dv GRND_INSECURE ,
+since
+.Dv GRND_INSECURE
+never blocks.
+The combination
+.Dv GRND_INSECURE Ns Li "|" Ns Li GRND_RANDOM
+is nonsensical and fails with
+.Er EINVAL .
+.Sh RETURN VALUES
+If successful,
+.Fn getrandom
+returns the number of bytes stored in
+.Fa buf .
+Otherwise,
+.Fn getrandom
+returns \-1 and sets
+.Va errno .
+.Sh EXAMPLES
+.Sy Recommended usage .
+Generate a key for cryptography:
+.Bd -literal
+	uint8_t secretkey[32];
+
+	if (getrandom(secretkey, sizeof secretkey, 0) == -1)
+		err(EXIT_FAILURE, "getrandom");
+	crypto_secretbox_xsalsa20poly1305(..., secretkey);
+.Ed
+.Pp
+Other idioms for illustration:
+.Bl -bullet
+.It
+Wait for entropy once, and then generate many keys without waiting:
+.Bd -literal
+	struct { uint8_t key[32]; } user[100];
+
+	if (getrandom(NULL, 0, 0) == -1)
+		err(EXIT_FAILURE, "getrandom");
+	for (i = 0; i < 100; i++)
+		getrandom(user[i].key, sizeof user[i].key,
+		    GRND_INSECURE);
+.Ed
+.It
+Twiddle thumbs while waiting for entropy:
+.Bd -literal
+	uint8_t secretkey[32];
+
+	while (getrandom(secretkey, sizeof secretkey, GRND_NONBLOCK)
+	    == -1) {
+		if (errno != EAGAIN)
+			err(EXIT_FAILURE, "getrandom");
+		twiddle_thumbs();
+	}
+	crypto_secretbox_xsalsa20poly1305(..., secretkey);
+.Ed
+.El
+.Pp
+(No examples of
+.Dv GRND_RANDOM
+because it is not useful.)
+.Sh ERRORS
+.Bl -tag -width Er
+.It Bq Er EAGAIN
+The
+.Dv GRND_NONBLOCK
+flag was specified, and the system entropy pool does not have full
+entropy.
+.It Bq Er EINTR
+The
+.Dv GRND_NONBLOCK
+flag was
+.Em not
+specified, the system entropy pool does not have full entropy, and the
+process was interrupted by a signal while waiting.
+.It Bq Er EINVAL
+.Fa flags
+contains an unrecognized flag or a nonsensical combination of flags.
+.It Bq Er EFAULT
+.Fa buf
+points outside the allocated address space.
+.El
+.Sh SEE ALSO
+.Xr rnd 4
+.Sh HISTORY
+The
+.Nm
+system call first appeared in Linux 3.17, and was added to
+.Nx 10.0 .
+.Sh AUTHORS
+The
+.Nx
+implementation of
+.Nm
+and this man page were written by
+.An Taylor R Campbell Aq Mt riastradh%NetBSD.org@localhost .
+.Sh BUGS
+There is no way to multiplex waiting for
+.Fn getrandom
+with other I/O in
+.Xr select 2 ,
+.Xr poll 2 ,
+or
+.Xr kqueue 2 .
+Instead, you can wait for a read from
+.Pa /dev/random ;
+see
+.Xr rnd 4 .
+.Pp
+.Dv GRND_RANDOM
+is a little silly.
diff --git a/sys/dev/random.c b/sys/dev/random.c
index 35000bc22a40..2dbc56f56345 100644
--- a/sys/dev/random.c
+++ b/sys/dev/random.c
@@ -58,16 +58,15 @@ __KERNEL_RCSID(0, "$NetBSD$");
 #include <sys/event.h>
 #include <sys/fcntl.h>
 #include <sys/kauth.h>
+#include <sys/kmem.h>
 #include <sys/lwp.h>
 #include <sys/poll.h>
-#include <sys/pool.h>
+#include <sys/random.h>
 #include <sys/rnd.h>
 #include <sys/rndsource.h>
 #include <sys/signalvar.h>
 #include <sys/systm.h>
 
-#include <crypto/nist_hash_drbg/nist_hash_drbg.h>
-
 #include "ioconf.h"
 
 static dev_type_open(random_open);
@@ -94,7 +93,6 @@ const struct cdevsw rnd_cdevsw = {
 };
 
 #define	RANDOM_BUFSIZE	512	/* XXX pulled from arse */
-static pool_cache_t random_buf_pc __read_mostly;
 
 /* Entropy source for writes to /dev/random and /dev/urandom */
 static krndsource_t	user_rndsource;
@@ -103,8 +101,6 @@ void
 rndattach(int num)
 {
 
-	random_buf_pc = pool_cache_init(RANDOM_BUFSIZE, 0, 0, 0,
-	    "randombuf", NULL, IPL_NONE, NULL, NULL, NULL);
 	rnd_attach_source(&user_rndsource, "/dev/random", RND_TYPE_UNKNOWN,
 	    RND_FLAG_COLLECT_VALUE);
 }
@@ -211,161 +207,30 @@ random_kqfilter(dev_t dev, struct knote *kn)
 static int
 random_read(dev_t dev, struct uio *uio, int flags)
 {
-	uint8_t seed[NIST_HASH_DRBG_SEEDLEN_BYTES] = {0};
-	struct nist_hash_drbg drbg;
-	uint8_t *buf;
-	int extractflags;
-	bool interruptible;
-	int error;
-
-	/* Get a buffer for transfers.  */
-	buf = pool_cache_get(random_buf_pc, PR_WAITOK);
-
-	/*
-	 * If it's a short read from /dev/urandom, just generate the
-	 * output directly with per-CPU cprng_strong.
-	 */
-	if (minor(dev) == RND_DEV_URANDOM &&
-	    uio->uio_resid <= RANDOM_BUFSIZE) {
-		/* Generate data and transfer it out.  */
-		cprng_strong(user_cprng, buf, uio->uio_resid, 0);
-		error = uiomove(buf, uio->uio_resid, uio);
-		goto out;
-	}
-
-	/*
-	 * If we're doing a blocking read from /dev/random, wait
-	 * interruptibly.  Otherwise, don't wait.
-	 */
-	if (minor(dev) == RND_DEV_RANDOM && !ISSET(flags, FNONBLOCK))
-		extractflags = ENTROPY_WAIT|ENTROPY_SIG;
-	else
-		extractflags = 0;
-
-	/*
-	 * Query the entropy pool.  For /dev/random, stop here if this
-	 * fails.  For /dev/urandom, go on either way --
-	 * entropy_extract will always fill the buffer with what we
-	 * have from the global pool.
-	 */
-	error = entropy_extract(seed, sizeof seed, extractflags);
-	if (minor(dev) == RND_DEV_RANDOM && error)
-		goto out;
-
-	/* Instantiate the DRBG.  */
-	if (nist_hash_drbg_instantiate(&drbg, seed, sizeof seed, NULL, 0,
-		NULL, 0))
-		panic("nist_hash_drbg_instantiate");
-
-	/* Promptly zero the seed.  */
-	explicit_memset(seed, 0, sizeof seed);
-
-	/*
-	 * Generate data.  Assume no error until failure.  No
-	 * interruption at this point until we've generated at least
-	 * one block of output.
-	 */
-	error = 0;
-	interruptible = false;
-	while (uio->uio_resid) {
-		size_t n = uio->uio_resid;
-
-		/* No more than one buffer's worth.  */
-		n = MIN(n, RANDOM_BUFSIZE);
-
-		/*
-		 * If we're `depleting' and this is /dev/random, clamp
-		 * to the smaller of the entropy capacity or the seed.
-		 */
-		if (__predict_false(atomic_load_relaxed(&entropy_depletion)) &&
-		    minor(dev) == RND_DEV_RANDOM) {
-			n = MIN(n, ENTROPY_CAPACITY);
-			n = MIN(n, sizeof seed);
-			/*
-			 * Guarantee never to return more than one
-			 * buffer in this case to minimize bookkeeping.
-			 */
-			CTASSERT(ENTROPY_CAPACITY <= RANDOM_BUFSIZE);
-			CTASSERT(sizeof seed <= RANDOM_BUFSIZE);
-		}
-
-		/* Yield if requested.  */
-		if (curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD)
-			preempt();
-
-		/*
-		 * Allow interruption, but only after providing a
-		 * minimum number of bytes.
-		 */
-		CTASSERT(RANDOM_BUFSIZE >= 256);
-		/* Check for interruption.  */
-		if (__predict_false(curlwp->l_flag & LW_PENDSIG) &&
-		    interruptible && sigispending(curlwp, 0)) {
-			error = EINTR; /* XXX ERESTART? */
-			break;
-		}
-
-		/*
-		 * Try to generate a block of data, but if we've hit
-		 * the DRBG reseed interval, reseed.
-		 */
-		if (nist_hash_drbg_generate(&drbg, buf, n, NULL, 0)) {
-			/*
-			 * Get a fresh seed without blocking -- we have
-			 * already generated some output so it is not
-			 * useful to block.  This can fail only if the
-			 * request is obscenely large, so it is OK for
-			 * either /dev/random or /dev/urandom to fail:
-			 * we make no promises about gigabyte-sized
-			 * reads happening all at once.
-			 */
-			error = entropy_extract(seed, sizeof seed, 0);
-			if (error)
-				break;
-
-			/* Reseed and try again.  */
-			if (nist_hash_drbg_reseed(&drbg, seed, sizeof seed,
-				NULL, 0))
-				panic("nist_hash_drbg_reseed");
-
-			/* Promptly zero the seed.  */
-			explicit_memset(seed, 0, sizeof seed);
-
-			/* If it fails now, that's a bug.  */
-			if (nist_hash_drbg_generate(&drbg, buf, n, NULL, 0))
-				panic("nist_hash_drbg_generate");
-		}
-
-		/* Transfer n bytes out.  */
-		error = uiomove(buf, n, uio);
-		if (error)
-			break;
-
-		/*
-		 * If we're `depleting' and this is /dev/random, stop
-		 * here, return what we have, and force the next read
-		 * to reseed.  Could grab more from the pool if
-		 * possible without blocking, but that's more
-		 * work.
-		 */
-		if (__predict_false(atomic_load_relaxed(&entropy_depletion)) &&
-		    minor(dev) == RND_DEV_RANDOM) {
-			error = 0;
-			break;
-		}
+	int gflags;
 
+	/* Set the appropriate GRND_* flags.  */
+	switch (minor(dev)) {
+	case RND_DEV_RANDOM:
+		gflags = GRND_RANDOM;
+		break;
+	case RND_DEV_URANDOM:
 		/*
-		 * We have generated one block of output, so it is
-		 * reasonable to allow interruption after this point.
+		 * Misnomer from Linux -- it's only insecure if you do
+		 * it before reading at least one byte of /dev/random.
 		 */
-		interruptible = true;
+		gflags = GRND_INSECURE;
+		break;
+	default:
+		return ENXIO;
 	}
 
-out:	/* Zero the buffer and return it to the pool cache.  */
-	explicit_memset(buf, 0, RANDOM_BUFSIZE);
-	pool_cache_put(random_buf_pc, buf);
+	/* Set GRND_NONBLOCK if user requested FNONBLOCK.  */
+	if (flags & FNONBLOCK)
+		gflags |= GRND_NONBLOCK;
 
-	return error;
+	/* Defer to getrandom.  */
+	return dogetrandom(uio, gflags);
 }
 
 /*
@@ -403,14 +268,11 @@ random_write(dev_t dev, struct uio *uio, int flags)
 		privileged = true;
 
 	/* Get a buffer for transfers.  */
-	buf = pool_cache_get(random_buf_pc, PR_WAITOK);
+	buf = kmem_alloc(RANDOM_BUFSIZE, KM_SLEEP);
 
 	/* Consume data.  */
 	while (uio->uio_resid) {
-		size_t n = uio->uio_resid;
-
-		/* No more than one buffer's worth in one step.  */
-		n = MIN(uio->uio_resid, RANDOM_BUFSIZE);
+		size_t n = MIN(uio->uio_resid, RANDOM_BUFSIZE);
 
 		/* Yield if requested.  */
 		if (curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD)
@@ -430,8 +292,8 @@ random_write(dev_t dev, struct uio *uio, int flags)
 		rnd_add_data(&user_rndsource, buf, n, privileged ? n*NBBY : 0);
 	}
 
-	/* Zero the buffer and return it to the pool cache.  */
+	/* Zero the buffer and free it.  */
 	explicit_memset(buf, 0, RANDOM_BUFSIZE);
-	pool_cache_put(random_buf_pc, buf);
+	kmem_free(buf, RANDOM_BUFSIZE);
 	return error;
 }
diff --git a/sys/kern/files.kern b/sys/kern/files.kern
index 4d18f896e3d0..8e06d0f9d49d 100644
--- a/sys/kern/files.kern
+++ b/sys/kern/files.kern
@@ -153,6 +153,7 @@ file	kern/subr_xcall.c		kern
 file	kern/sys_aio.c			aio
 file	kern/sys_descrip.c		kern
 file	kern/sys_generic.c		kern
+file	kern/sys_getrandom.c		kern
 file	kern/sys_module.c		kern
 file	kern/sys_mqueue.c		mqueue
 file	kern/sys_lwp.c			kern
diff --git a/sys/kern/kern_entropy.c b/sys/kern/kern_entropy.c
index 0628870c48b4..11f12af197fa 100644
--- a/sys/kern/kern_entropy.c
+++ b/sys/kern/kern_entropy.c
@@ -1173,6 +1173,8 @@ sysctl_entropy_consolidate(SYSCTLFN_ARGS)
  *
  *		ENTROPY_WAIT	Wait for entropy if not available yet.
  *		ENTROPY_SIG	Allow interruption by a signal during wait.
+ *		ENTROPY_NOPARTIAL Either fill the buffer with full entropy,
+ *				or fail without filling it at all.
  *
  *	Return zero on success, or error on failure:
  *
@@ -1234,9 +1236,15 @@ entropy_extract(void *buf, size_t len, int flags)
 		}
 	}
 
-	/* Count failure -- but fill the buffer nevertheless.  */
-	if (error)
+	/*
+	 * Count failure -- but fill the buffer nevertheless, unless
+	 * the caller specified ENTROPY_NOPARTIAL.
+	 */
+	if (error) {
+		if (ISSET(flags, ENTROPY_NOPARTIAL))
+			goto out;
 		entropy_extract_fail_evcnt.ev_count++;
+	}
 
 	/*
 	 * Report a warning if we have never yet reached full entropy.
@@ -1266,7 +1274,7 @@ entropy_extract(void *buf, size_t len, int flags)
 		entropy_deplete_evcnt.ev_count++;
 	}
 
-	/* Release the global lock and return the error.  */
+out:	/* Release the global lock and return the error.  */
 	if (E->stage >= ENTROPY_WARM)
 		mutex_exit(&E->lock);
 	return error;
diff --git a/sys/kern/sys_getrandom.c b/sys/kern/sys_getrandom.c
new file mode 100644
index 000000000000..b02bada23e0e
--- /dev/null
+++ b/sys/kern/sys_getrandom.c
@@ -0,0 +1,244 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2020 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Taylor R. Campbell.
+ *
+ * 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.
+ */
+
+/*
+ * getrandom() system call
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <sys/atomic.h>
+#include <sys/cprng.h>
+#include <sys/entropy.h>
+#include <sys/kmem.h>
+#include <sys/lwp.h>
+#include <sys/proc.h>
+#include <sys/random.h>
+#include <sys/sched.h>
+#include <sys/signalvar.h>
+#include <sys/syscallargs.h>
+#include <sys/uio.h>
+
+#include <crypto/nist_hash_drbg/nist_hash_drbg.h>
+
+#define	RANDOM_BUFSIZE	512
+
+int
+dogetrandom(struct uio *uio, unsigned int flags)
+{
+	uint8_t seed[NIST_HASH_DRBG_SEEDLEN_BYTES] = {0};
+	struct nist_hash_drbg drbg;
+	void *buf;
+	int eflags = 0;
+	int error;
+
+	KASSERT((flags & ~(GRND_RANDOM|GRND_INSECURE|GRND_NONBLOCK)) == 0);
+	KASSERT((flags & (GRND_RANDOM|GRND_INSECURE)) !=
+	    (GRND_RANDOM|GRND_INSECURE));
+
+	/* Allocate a buffer.  */
+	buf = kmem_alloc(RANDOM_BUFSIZE, KM_SLEEP);
+
+	/*
+	 * Fast path: for short reads, if INSECURE, or if we have
+	 * entropy and we're not doing depletion, just return it from
+	 * the per-CPU cprng_strong.
+	 */
+	if (uio->uio_resid <= RANDOM_BUFSIZE &&
+	    (ISSET(flags, GRND_INSECURE) ||
+		(__predict_true(!atomic_load_relaxed(&entropy_depletion)) &&
+		    __predict_true(entropy_epoch() != (unsigned)-1)))) {
+		cprng_strong(user_cprng, buf, uio->uio_resid, 0);
+		error = uiomove(buf, uio->uio_resid, uio);
+		goto out;
+	}
+
+	/*
+	 * Try to get a seed from the entropy pool.  Fail if we would
+	 * block.  If GRND_INSECURE, always return something even if it
+	 * is partial entropy; if !GRND_INSECURE, set ENTROPY_NOPARTIAL
+	 * in order to tell entropy_extract not to bother drawing
+	 * anything from a partial pool if we can't get full entropy.
+	 */
+	if (!ISSET(flags, GRND_NONBLOCK) && !ISSET(flags, GRND_INSECURE))
+		eflags |= ENTROPY_WAIT|ENTROPY_SIG;
+	if (!ISSET(flags, GRND_INSECURE))
+		eflags |= ENTROPY_NOPARTIAL;
+	error = entropy_extract(seed, sizeof seed, eflags);
+	if (error && !ISSET(flags, GRND_INSECURE))
+		goto out;
+
+	/* Instantiate the DRBG and promptly zero the seed.  */
+	if (nist_hash_drbg_instantiate(&drbg, seed, sizeof seed, NULL, 0,
+		NULL, 0))
+		panic("nist_hash_drbg_instantiate");
+	explicit_memset(seed, 0, sizeof seed);
+
+	/* Generate data.  */
+	error = 0;
+	while (uio->uio_resid) {
+		size_t n = MIN(uio->uio_resid, RANDOM_BUFSIZE);
+
+		/*
+		 * If we're `depleting' and this is /dev/random, clamp
+		 * to the smaller of the entropy capacity or the seed.
+		 */
+		if (__predict_false(atomic_load_relaxed(&entropy_depletion)) &&
+		    ISSET(flags, GRND_RANDOM)) {
+			n = MIN(n, ENTROPY_CAPACITY);
+			n = MIN(n, sizeof seed);
+			/*
+			 * Guarantee never to return more than one
+			 * buffer in this case to minimize bookkeeping.
+			 */
+			CTASSERT(ENTROPY_CAPACITY <= RANDOM_BUFSIZE);
+			CTASSERT(sizeof seed <= RANDOM_BUFSIZE);
+		}
+
+		/*
+		 * Try to generate a block of data, but if we've hit
+		 * the DRBG reseed interval, reseed.
+		 */
+		if (nist_hash_drbg_generate(&drbg, buf, n, NULL, 0)) {
+			/*
+			 * Get a fresh seed without blocking -- we have
+			 * already generated some output so it is not
+			 * useful to block.  This can fail only if the
+			 * request is obscenely large, so it is OK for
+			 * either /dev/random or /dev/urandom to fail:
+			 * we make no promises about gigabyte-sized
+			 * reads happening all at once.
+			 */
+			error = entropy_extract(seed, sizeof seed,
+			    ENTROPY_NOPARTIAL);
+			if (error)
+				break;
+
+			/* Reseed, promptly zero seed, and try again.  */
+			if (nist_hash_drbg_reseed(&drbg, seed, sizeof seed,
+				NULL, 0))
+				panic("nist_hash_drbg_reseed");
+			explicit_memset(seed, 0, sizeof seed);
+
+			/* If it fails now, that's a bug.  */
+			if (nist_hash_drbg_generate(&drbg, buf, n, NULL, 0))
+				panic("nist_hash_drbg_generate");
+		}
+
+		/* Transfer n bytes out.  */
+		error = uiomove(buf, n, uio);
+		if (error)
+			break;
+
+		/*
+		 * If we're `depleting' and this is /dev/random, stop
+		 * here, return what we have, and force the next read
+		 * to reseed.  Could grab more from the pool if
+		 * possible without blocking, but that's more work.
+		 */
+		if (__predict_false(atomic_load_relaxed(&entropy_depletion)) &&
+		    ISSET(flags, GRND_RANDOM)) {
+			error = 0;
+			break;
+		}
+
+		/* Yield if requested.  */
+		if (curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD)
+			preempt();
+
+		/* Check for interruption after at least one transfer.  */
+		if (__predict_false(curlwp->l_flag & LW_PENDSIG) &&
+		    sigispending(curlwp, 0)) {
+			error = EINTR;
+			break;
+		}
+	}
+
+out:	/* Zero and free the buffer, and we're done -- return any error.  */
+	explicit_memset(buf, 0, RANDOM_BUFSIZE);
+	kmem_free(buf, RANDOM_BUFSIZE);
+	return error;
+}
+
+int
+sys_getrandom(struct lwp *l, const struct sys_getrandom_args *uap,
+    register_t *retval)
+{
+	/* {
+		syscallarg(void *)	buf;
+		syscallarg(size_t)	buflen;
+		syscallarg(unsigned)	flags;
+	} */
+	void *buf = SCARG(uap, buf);
+	size_t buflen = SCARG(uap, buflen);
+	int flags = SCARG(uap, flags);
+	int error;
+
+	/* Set up an iov and uio to read into the user's buffer.  */
+	struct iovec iov = { .iov_base = buf, .iov_len = buflen };
+	struct uio uio = {
+		.uio_iov = &iov,
+		.uio_iovcnt = 1,
+		.uio_offset = 0,
+		.uio_resid = buflen,
+		.uio_rw = UIO_READ,
+		.uio_vmspace = curproc->p_vmspace,
+	};
+
+	/* Validate the flags.  */
+	if (flags & ~(GRND_RANDOM|GRND_INSECURE|GRND_NONBLOCK)) {
+		/* Unknown flags.  */
+		error = EINVAL;
+		goto out;
+	}
+	if ((flags & (GRND_RANDOM|GRND_INSECURE)) ==
+	    (GRND_RANDOM|GRND_INSECURE)) {
+		/* Nonsensical combination.  */
+		error = EINVAL;
+		goto out;
+	}
+
+	/* Do it.  */
+	error = dogetrandom(&uio, flags);
+
+out:	/*
+	 * If we transferred anything, return the number of bytes
+	 * transferred and suppress error; otherwise return the error.
+	 */
+	*retval = buflen - uio.uio_resid;
+	if (*retval)
+		error = 0;
+	return error;
+}
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
index f86f232e2e69..e708f307821d 100644
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -215,7 +215,8 @@
 89	COMPAT_43 MODULAR compat_43	\
 		{ int|sys||getdtablesize(void); } ogetdtablesize
 90	STD	RUMP	{ int|sys||dup2(int from, int to); }
-91	UNIMPL		getdopt
+91	STD	RUMP	{ ssize_t|sys||getrandom(void *buf, size_t buflen, \
+			    unsigned int flags); }
 92	STD	RUMP	{ int|sys||fcntl(int fd, int cmd, ... void *arg); }
 93	COMPAT_50 MODULAR compat_50 RUMP	\
 		{ int|sys||select(int nd, fd_set *in, fd_set *ou, \
diff --git a/sys/rump/librump/rumpkern/Makefile.rumpkern b/sys/rump/librump/rumpkern/Makefile.rumpkern
index d6d54ee7187d..7b84049d8ac9 100644
--- a/sys/rump/librump/rumpkern/Makefile.rumpkern
+++ b/sys/rump/librump/rumpkern/Makefile.rumpkern
@@ -129,6 +129,7 @@ SRCS+=	init_sysctl_base.c	\
 	subr_xcall.c		\
 	sys_descrip.c		\
 	sys_generic.c		\
+	sys_getrandom.c		\
 	sys_module.c		\
 	sys_pipe.c		\
 	sys_select.c		\
diff --git a/sys/sys/Makefile b/sys/sys/Makefile
index baa0ffc5d783..572e584ea16b 100644
--- a/sys/sys/Makefile
+++ b/sys/sys/Makefile
@@ -33,7 +33,7 @@ INCS=	acct.h agpio.h aio.h ansi.h aout_mids.h ataio.h atomic.h \
 	param.h pcu.h pipe.h pmf.h poll.h pool.h power.h proc.h \
 	protosw.h pset.h psref.h ptrace.h ptree.h \
 	queue.h quota.h quotactl.h \
-	radixtree.h ras.h rbtree.h reboot.h radioio.h resource.h \
+	radioio.h radixtree.h random.h ras.h rbtree.h reboot.h resource.h \
 	resourcevar.h rmd160.h rnd.h rndio.h rwlock.h \
 	scanio.h sched.h scsiio.h sdt.h select.h selinfo.h sem.h semaphore.h \
 	sha1.h sha2.h sha3.h shm.h siginfo.h signal.h signalvar.h sigtypes.h \
diff --git a/sys/sys/entropy.h b/sys/sys/entropy.h
index cabff68e6394..de561bcca9a7 100644
--- a/sys/sys/entropy.h
+++ b/sys/sys/entropy.h
@@ -44,8 +44,9 @@ struct knote;
 
 #define	ENTROPY_CAPACITY	ENTPOOL_CAPACITY	/* bytes */
 
-#define	ENTROPY_WAIT	0x01
-#define	ENTROPY_SIG	0x02
+#define	ENTROPY_WAIT		0x01
+#define	ENTROPY_SIG		0x02
+#define	ENTROPY_NOPARTIAL	0x04
 
 void	entropy_bootrequest(void);
 unsigned entropy_epoch(void);
diff --git a/sys/sys/random.h b/sys/sys/random.h
new file mode 100644
index 000000000000..ceac8c722725
--- /dev/null
+++ b/sys/sys/random.h
@@ -0,0 +1,69 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2020 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Taylor R. Campbell.
+ *
+ * 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.
+ */
+
+#ifndef	_SYS_RANDOM_H
+#define	_SYS_RANDOM_H
+
+#include <sys/cdefs.h>
+
+#include <machine/ansi.h>	/* _BSD_SIZE_T_ */
+
+#define	GRND_NONBLOCK	(1u << 0)
+#define	GRND_RANDOM	(1u << 1)
+#define	GRND_INSECURE	(1u << 2)
+
+#ifdef _KERNEL
+
+struct uio;
+
+int	dogetrandom(struct uio *, unsigned int);
+
+#endif	/* _KERNEL */
+
+#ifndef _KERNEL
+__BEGIN_DECLS
+
+#ifdef	_BSD_SIZE_T_
+typedef	_BSD_SIZE_T_	size_t;
+#undef	_BSD_SIZE_T_
+#endif
+
+#ifdef	_BSD_SSIZE_T_
+typedef	_BSD_SSIZE_T_	ssize_t;
+#undef	_BSD_SSIZE_T_
+#endif
+
+ssize_t	getrandom(void *, size_t, unsigned int);
+
+__END_DECLS
+#endif	/* !_KERNEL */
+
+#endif	/* _SYS_RANDOM_H */
diff --git a/tests/lib/libc/sys/Makefile b/tests/lib/libc/sys/Makefile
index e72e8ee64920..8d04563266b1 100644
--- a/tests/lib/libc/sys/Makefile
+++ b/tests/lib/libc/sys/Makefile
@@ -23,6 +23,7 @@ TESTS_C+=		t_getgroups
 TESTS_C+=		t_getitimer
 TESTS_C+=		t_getlogin
 TESTS_C+=		t_getpid
+TESTS_C+=		t_getrandom
 TESTS_C+=		t_getrusage
 TESTS_C+=		t_getsid
 TESTS_C+=		t_getsockname
diff --git a/tests/lib/libc/sys/t_getrandom.c b/tests/lib/libc/sys/t_getrandom.c
new file mode 100644
index 000000000000..9a50f97babe8
--- /dev/null
+++ b/tests/lib/libc/sys/t_getrandom.c
@@ -0,0 +1,170 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2020 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Taylor R. Campbell.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD$");
+
+#include <sys/random.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+
+static uint8_t buf[65536];
+static uint8_t zero24[24];
+
+static void
+alarm_handler(int signo)
+{
+}
+
+ATF_TC(getrandom);
+ATF_TC_HEAD(getrandom, tc)
+{
+
+	atf_tc_set_md_var(tc, "descr", "getrandom(2)");
+}
+
+/*
+ * Probability of spurious failure is 1/2^192 for each of the memcmps.
+ * As long as there are fewer than 2^64 of them, the probability of
+ * spurious failure is at most 1/2^128, which is low enough that we
+ * don't care about it.
+ */
+
+ATF_TC_BODY(getrandom, tc)
+{
+	ssize_t n;
+
+	ATF_REQUIRE(signal(SIGALRM, &alarm_handler) != SIG_ERR);
+
+	/* default */
+	alarm(1);
+	memset(buf, 0, sizeof buf);
+	n = getrandom(buf, sizeof buf, 0);
+	if (n == -1) {
+		ATF_CHECK_EQ(errno, EINTR);
+	} else {
+		ATF_CHECK_EQ((size_t)n, sizeof buf);
+		ATF_CHECK(memcmp(buf, zero24, 24) != 0);
+		ATF_CHECK(memcmp(buf + sizeof buf - 24, zero24, 24) != 0);
+	}
+	alarm(0);
+
+	/* default, nonblocking */
+	memset(buf, 0, sizeof buf);
+	n = getrandom(buf, sizeof buf, GRND_NONBLOCK);
+	if (n == -1) {
+		ATF_CHECK_EQ(errno, EAGAIN);
+	} else {
+		ATF_CHECK_EQ((size_t)n, sizeof buf);
+		ATF_CHECK(memcmp(buf, zero24, 24) != 0);
+		ATF_CHECK(memcmp(buf + sizeof buf - 24, zero24, 24) != 0);
+	}
+
+	/* insecure */
+	memset(buf, 0, sizeof buf);
+	n = getrandom(buf, sizeof buf, GRND_INSECURE);
+	ATF_CHECK(n != -1);
+	ATF_CHECK_EQ((size_t)n, sizeof buf);
+	ATF_CHECK(memcmp(buf, zero24, 24) != 0);
+	ATF_CHECK(memcmp(buf + sizeof buf - 24, zero24, 24) != 0);
+
+	/* insecure, nonblocking -- same as mere insecure */
+	memset(buf, 0, sizeof buf);
+	n = getrandom(buf, sizeof buf, GRND_INSECURE|GRND_NONBLOCK);
+	ATF_CHECK(n != -1);
+	ATF_CHECK_EQ((size_t)n, sizeof buf);
+	ATF_CHECK(memcmp(buf, zero24, 24) != 0);
+	ATF_CHECK(memcmp(buf + sizeof buf - 24, zero24, 24) != 0);
+
+	/* `random' (hokey) */
+	alarm(1);
+	memset(buf, 0, sizeof buf);
+	n = getrandom(buf, sizeof buf, GRND_RANDOM);
+	if (n == -1) {
+		ATF_CHECK_EQ(errno, EINTR);
+	} else {
+		ATF_CHECK(n != 0);
+		ATF_CHECK((size_t)n <= sizeof buf);
+		if ((size_t)n >= 24) {
+			ATF_CHECK(memcmp(buf, zero24, 24) != 0);
+			ATF_CHECK(memcmp(buf + n - 24, zero24, 24) != 0);
+		}
+	}
+	alarm(0);
+
+	/* `random' (hokey), nonblocking */
+	memset(buf, 0, sizeof buf);
+	n = getrandom(buf, sizeof buf, GRND_RANDOM|GRND_NONBLOCK);
+	if (n == -1) {
+		ATF_CHECK_EQ(errno, EAGAIN);
+	} else {
+		ATF_CHECK(n != 0);
+		ATF_CHECK((size_t)n <= sizeof buf);
+		if ((size_t)n >= 24) {
+			ATF_CHECK(memcmp(buf, zero24, 24) != 0);
+			ATF_CHECK(memcmp(buf + n - 24, zero24, 24) != 0);
+		}
+	}
+
+	/* random and insecure -- nonsensical */
+	n = getrandom(buf, sizeof buf, GRND_RANDOM|GRND_INSECURE);
+	ATF_CHECK_EQ(n, -1);
+	ATF_CHECK_EQ(errno, EINVAL);
+
+	/* random and insecure, nonblocking -- nonsensical */
+	n = getrandom(buf, sizeof buf,
+	    GRND_RANDOM|GRND_INSECURE|GRND_NONBLOCK);
+	ATF_CHECK_EQ(n, -1);
+	ATF_CHECK_EQ(errno, EINVAL);
+
+	/* invalid flags */
+	__CTASSERT(~(GRND_RANDOM|GRND_INSECURE|GRND_NONBLOCK));
+	n = getrandom(buf, sizeof buf,
+	    ~(GRND_RANDOM|GRND_INSECURE|GRND_NONBLOCK));
+	ATF_CHECK_EQ(n, -1);
+	ATF_CHECK_EQ(errno, EINVAL);
+
+	/* unmapped */
+	n = getrandom(NULL, sizeof buf, GRND_INSECURE|GRND_NONBLOCK);
+	ATF_CHECK_EQ(n, -1);
+	ATF_CHECK_EQ(errno, EFAULT);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+	ATF_TP_ADD_TC(tp, getrandom);
+
+	return atf_no_error();
+}

>From a5a46411df3719e43f53f601a4a216ca5c12313e Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Tue, 14 Jan 2020 02:33:12 +0000
Subject: [PATCH 2/2] Simplify /dev/random and getrandom(GRND_RANDOM)
 semantics.

Just clamp to 32-byte reads irrespective of whether we're doing
entropy depletion.  You shouldn't do bulk reads out of /dev/random.
---
 sys/kern/sys_getrandom.c | 24 +++++++++++-------------
 1 file changed, 11 insertions(+), 13 deletions(-)

diff --git a/sys/kern/sys_getrandom.c b/sys/kern/sys_getrandom.c
index b02bada23e0e..5d4887cff821 100644
--- a/sys/kern/sys_getrandom.c
+++ b/sys/kern/sys_getrandom.c
@@ -72,11 +72,12 @@ dogetrandom(struct uio *uio, unsigned int flags)
 	buf = kmem_alloc(RANDOM_BUFSIZE, KM_SLEEP);
 
 	/*
-	 * Fast path: for short reads, if INSECURE, or if we have
-	 * entropy and we're not doing depletion, just return it from
-	 * the per-CPU cprng_strong.
+	 * Fast path, for short reads other than from /dev/random: if
+	 * INSECURE, or if we have entropy and we're not doing
+	 * `depletion', just return it from the per-CPU cprng_strong.
 	 */
 	if (uio->uio_resid <= RANDOM_BUFSIZE &&
+	    !ISSET(flags, GRND_RANDOM) &&
 	    (ISSET(flags, GRND_INSECURE) ||
 		(__predict_true(!atomic_load_relaxed(&entropy_depletion)) &&
 		    __predict_true(entropy_epoch() != (unsigned)-1)))) {
@@ -112,11 +113,10 @@ dogetrandom(struct uio *uio, unsigned int flags)
 		size_t n = MIN(uio->uio_resid, RANDOM_BUFSIZE);
 
 		/*
-		 * If we're `depleting' and this is /dev/random, clamp
-		 * to the smaller of the entropy capacity or the seed.
+		 * If this is /dev/random, clamp to the smaller of the
+		 * entropy capacity or the seed.
 		 */
-		if (__predict_false(atomic_load_relaxed(&entropy_depletion)) &&
-		    ISSET(flags, GRND_RANDOM)) {
+		if (ISSET(flags, GRND_RANDOM)) {
 			n = MIN(n, ENTROPY_CAPACITY);
 			n = MIN(n, sizeof seed);
 			/*
@@ -163,13 +163,11 @@ dogetrandom(struct uio *uio, unsigned int flags)
 			break;
 
 		/*
-		 * If we're `depleting' and this is /dev/random, stop
-		 * here, return what we have, and force the next read
-		 * to reseed.  Could grab more from the pool if
-		 * possible without blocking, but that's more work.
+		 * If this is /dev/random, stop here and return what we
+		 * have, and force the next read to reseed.  You're not
+		 * supposed to draw lots of data from /dev/random.
 		 */
-		if (__predict_false(atomic_load_relaxed(&entropy_depletion)) &&
-		    ISSET(flags, GRND_RANDOM)) {
+		if (ISSET(flags, GRND_RANDOM)) {
 			error = 0;
 			break;
 		}
Index: lib/libc/gen/Makefile.inc
===================================================================
RCS file: /cvsroot/src/lib/libc/gen/Makefile.inc,v
retrieving revision 1.201
diff -u -r1.201 Makefile.inc
--- lib/libc/gen/Makefile.inc	22 Apr 2020 23:32:25 -0000	1.201
+++ lib/libc/gen/Makefile.inc	1 May 2020 15:20:52 -0000
@@ -15,7 +15,7 @@
 	errc.c errlist.c errno.c execl.c execle.c execlp.c execv.c execvp.c \
 	exect.c extattr.c fmtcheck.c fmtmsg.c fnmatch.c fstab.c ftok.c \
 	fts.c ftw.c getbsize.c getcap.c getcwd.c \
-	getdevmajor.c getdomainname.c getgrent.c \
+	getdevmajor.c getdomainname.c getentropy.c getgrent.c \
 	getgrouplist.c getgroupmembership.c gethostname.c \
 	getloadavg.c getlogin.c getmntinfo.c \
 	getnetgrent.c getpagesize.c \
Index: lib/libc/gen/getentropy.3
===================================================================
RCS file: lib/libc/gen/getentropy.3
diff -N lib/libc/gen/getentropy.3
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ lib/libc/gen/getentropy.3	1 May 2020 15:20:52 -0000
@@ -0,0 +1,93 @@
+.\"	$NetBSD$ $
+.\"
+.\" Copyright (c) 2020 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Nia Alarie.
+.\"
+.\" 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.
+.\"
+.Dd May 1, 2020
+.Dt GETENTROPY 3
+.Os
+.Sh NAME
+.Nm getentropy
+.Nd fill a buffer with high quality random data
+.Sh LIBRARY
+.Lb libc
+.Sh SYNOPSIS
+.In unistd.h
+.Ft int
+.Fn getentropy "void *buf" "size_t buflen"
+.Sh DESCRIPTION
+.Pp
+The
+.Fn getentropy
+function fills a buffer with high quality random data, suitable for seeding
+cryptographically secure psuedorandom number generators.
+.Pp
+.Fn getentropy
+is only intended for seeding random number generators and is not intended
+for use by regular code which simply needs secure random data.  For this
+purpose, please use
+.Xr arc4random 3 .
+.Pp
+The maximum value for
+.Li buflen
+is 256 bytes.
+.Sh IMPLEMENTATION NOTES
+.Fn getentropy
+reads from the
+.Xr sysctl 7
+variable
+.Li kern.arandom .
+.Sh RETURN VALUES
+The
+.Fn getentropy
+function returns 0 on success, and -1 if an error occurred.
+.Sh ERRORS
+.Fn getentropy
+will succeed unless:
+.Bl -tag -width Er
+.It Bq Er EFAULT
+The
+.Fa buf
+argument points to an invalid memory address.
+.It Bq Er EIO
+Too many bytes were requested.
+.Sh SEE ALSO
+.Xr arc4random 3 ,
+.Xr rnd 4
+.Sh STANDARDS
+The
+.Fn getentropy
+function is non-standard.
+.Sh HISTORY
+The
+.Fn getentropy
+function first appeared in
+.Ox 5.6 ,
+then in
+.Fx 12.0 ,
+and
+.Nx 10 .
Index: lib/libc/gen/getentropy.c
===================================================================
RCS file: lib/libc/gen/getentropy.c
diff -N lib/libc/gen/getentropy.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ lib/libc/gen/getentropy.c	1 May 2020 15:20:52 -0000
@@ -0,0 +1,63 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2020 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nia Alarie.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD$");
+
+#include "namespace.h"
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#include <errno.h>
+#include <unistd.h>
+
+#ifdef __weak_alias
+__weak_alias(getentropy,_getentropy)
+#endif
+
+int
+getentropy(void *buf, size_t buflen)
+{
+	const int name[2] = { CTL_KERN, KERN_ARND };
+
+	if (buf == NULL && buflen > 0) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (buflen > 256) {
+		errno = EIO;
+		return -1;
+	}
+
+	return sysctl(name, 2, buf, &buflen, NULL, 0);
+}
Index: lib/libc/include/namespace.h
===================================================================
RCS file: /cvsroot/src/lib/libc/include/namespace.h,v
retrieving revision 1.198
diff -u -r1.198 namespace.h
--- lib/libc/include/namespace.h	18 Apr 2020 23:55:50 -0000	1.198
+++ lib/libc/include/namespace.h	1 May 2020 15:20:53 -0000
@@ -362,6 +362,7 @@
 #define getdevmajor		_getdevmajor
 #define getdiskbyname		_getdiskbyname
 #define getdomainname		_getdomainname
+#define getentropy		_getentropy
 #define getfsent		_getfsent
 #define getfsfile		_getfsfile
 #define getfsspec		_getfsspec
Index: include/unistd.h
===================================================================
RCS file: /cvsroot/src/include/unistd.h,v
retrieving revision 1.156
diff -u -r1.156 unistd.h
--- include/unistd.h	31 Mar 2020 16:50:31 -0000	1.156
+++ include/unistd.h	1 May 2020 15:20:53 -0000
@@ -338,6 +338,7 @@
 int	 fdiscard(int, off_t, off_t);
 int	 fsync_range(int, int, off_t, off_t);
 int	 getdomainname(char *, size_t);
+int	 getentropy(void *, size_t);
 int	 getgrouplist(const char *, gid_t, gid_t *, int *);
 int	 getgroupmembership(const char *, gid_t, gid_t *, int, int *);
 mode_t	 getmode(const void *, mode_t);


Home | Main Index | Thread Index | Old Index