sys_getrandom.c revision 1.2 1 1.2 riastrad /* $NetBSD: sys_getrandom.c,v 1.2 2021/12/28 13:22:43 riastradh Exp $ */
2 1.1 riastrad
3 1.1 riastrad /*-
4 1.1 riastrad * Copyright (c) 2020 The NetBSD Foundation, Inc.
5 1.1 riastrad * All rights reserved.
6 1.1 riastrad *
7 1.1 riastrad * This code is derived from software contributed to The NetBSD Foundation
8 1.1 riastrad * by Taylor R. Campbell.
9 1.1 riastrad *
10 1.1 riastrad * Redistribution and use in source and binary forms, with or without
11 1.1 riastrad * modification, are permitted provided that the following conditions
12 1.1 riastrad * are met:
13 1.1 riastrad * 1. Redistributions of source code must retain the above copyright
14 1.1 riastrad * notice, this list of conditions and the following disclaimer.
15 1.1 riastrad * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 riastrad * notice, this list of conditions and the following disclaimer in the
17 1.1 riastrad * documentation and/or other materials provided with the distribution.
18 1.1 riastrad *
19 1.1 riastrad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 riastrad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 riastrad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 riastrad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 riastrad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 riastrad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 riastrad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 riastrad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 riastrad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 riastrad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 riastrad * POSSIBILITY OF SUCH DAMAGE.
30 1.1 riastrad */
31 1.1 riastrad
32 1.1 riastrad /*
33 1.1 riastrad * getrandom() system call
34 1.1 riastrad */
35 1.1 riastrad
36 1.1 riastrad #include <sys/cdefs.h>
37 1.2 riastrad __KERNEL_RCSID(0, "$NetBSD: sys_getrandom.c,v 1.2 2021/12/28 13:22:43 riastradh Exp $");
38 1.1 riastrad
39 1.1 riastrad #include <sys/types.h>
40 1.1 riastrad #include <sys/param.h>
41 1.1 riastrad
42 1.1 riastrad #include <sys/atomic.h>
43 1.1 riastrad #include <sys/cprng.h>
44 1.1 riastrad #include <sys/entropy.h>
45 1.1 riastrad #include <sys/kmem.h>
46 1.1 riastrad #include <sys/lwp.h>
47 1.1 riastrad #include <sys/proc.h>
48 1.1 riastrad #include <sys/random.h>
49 1.1 riastrad #include <sys/sched.h>
50 1.1 riastrad #include <sys/signalvar.h>
51 1.1 riastrad #include <sys/syscallargs.h>
52 1.1 riastrad #include <sys/uio.h>
53 1.1 riastrad
54 1.1 riastrad #include <crypto/nist_hash_drbg/nist_hash_drbg.h>
55 1.1 riastrad
56 1.1 riastrad #define RANDOM_BUFSIZE 512
57 1.1 riastrad
58 1.1 riastrad int
59 1.1 riastrad dogetrandom(struct uio *uio, unsigned int flags)
60 1.1 riastrad {
61 1.1 riastrad uint8_t seed[NIST_HASH_DRBG_SEEDLEN_BYTES] = {0};
62 1.1 riastrad struct nist_hash_drbg drbg;
63 1.1 riastrad uint8_t *buf;
64 1.1 riastrad int extractflags = 0;
65 1.1 riastrad int error;
66 1.1 riastrad
67 1.1 riastrad KASSERT((flags & ~(GRND_RANDOM|GRND_INSECURE|GRND_NONBLOCK)) == 0);
68 1.1 riastrad KASSERT((flags & (GRND_RANDOM|GRND_INSECURE)) !=
69 1.1 riastrad (GRND_RANDOM|GRND_INSECURE));
70 1.1 riastrad
71 1.1 riastrad /* Get a buffer for transfers. */
72 1.1 riastrad buf = kmem_alloc(RANDOM_BUFSIZE, KM_SLEEP);
73 1.1 riastrad
74 1.1 riastrad /*
75 1.1 riastrad * Fast path: for short reads other than from /dev/random, if
76 1.1 riastrad * seeded or if INSECURE, just draw from per-CPU cprng_strong.
77 1.1 riastrad */
78 1.1 riastrad if (uio->uio_resid <= RANDOM_BUFSIZE &&
79 1.1 riastrad !ISSET(flags, GRND_RANDOM) &&
80 1.1 riastrad (entropy_ready() || ISSET(flags, GRND_INSECURE))) {
81 1.1 riastrad /* Generate data and transfer it out. */
82 1.1 riastrad cprng_strong(user_cprng, buf, uio->uio_resid, 0);
83 1.1 riastrad error = uiomove(buf, uio->uio_resid, uio);
84 1.1 riastrad goto out;
85 1.1 riastrad }
86 1.1 riastrad
87 1.1 riastrad /*
88 1.1 riastrad * Try to get a seed from the entropy pool. Fail if we would
89 1.1 riastrad * block. If GRND_INSECURE, always return something even if it
90 1.1 riastrad * is partial entropy; if !GRND_INSECURE, set ENTROPY_HARDFAIL
91 1.1 riastrad * in order to tell entropy_extract not to bother drawing
92 1.1 riastrad * anything from a partial pool if we can't get full entropy.
93 1.1 riastrad */
94 1.1 riastrad if (!ISSET(flags, GRND_NONBLOCK) && !ISSET(flags, GRND_INSECURE))
95 1.1 riastrad extractflags |= ENTROPY_WAIT|ENTROPY_SIG;
96 1.1 riastrad if (!ISSET(flags, GRND_INSECURE))
97 1.1 riastrad extractflags |= ENTROPY_HARDFAIL;
98 1.1 riastrad error = entropy_extract(seed, sizeof seed, extractflags);
99 1.1 riastrad if (error && !ISSET(flags, GRND_INSECURE))
100 1.1 riastrad goto out;
101 1.1 riastrad
102 1.1 riastrad /* Instantiate the DRBG. */
103 1.1 riastrad if (nist_hash_drbg_instantiate(&drbg, seed, sizeof seed, NULL, 0,
104 1.1 riastrad NULL, 0))
105 1.1 riastrad panic("nist_hash_drbg_instantiate");
106 1.1 riastrad
107 1.1 riastrad /* Promptly zero the seed. */
108 1.1 riastrad explicit_memset(seed, 0, sizeof seed);
109 1.1 riastrad
110 1.1 riastrad /* Generate data. */
111 1.1 riastrad error = 0;
112 1.1 riastrad while (uio->uio_resid) {
113 1.1 riastrad size_t n = MIN(uio->uio_resid, RANDOM_BUFSIZE);
114 1.1 riastrad
115 1.1 riastrad /*
116 1.1 riastrad * Clamp /dev/random output to the entropy capacity and
117 1.1 riastrad * seed size. Programs can't rely on long reads.
118 1.1 riastrad */
119 1.1 riastrad if (ISSET(flags, GRND_RANDOM)) {
120 1.1 riastrad n = MIN(n, ENTROPY_CAPACITY);
121 1.1 riastrad n = MIN(n, sizeof seed);
122 1.1 riastrad /*
123 1.1 riastrad * Guarantee never to return more than one
124 1.1 riastrad * buffer in this case to minimize bookkeeping.
125 1.1 riastrad */
126 1.1 riastrad CTASSERT(ENTROPY_CAPACITY <= RANDOM_BUFSIZE);
127 1.1 riastrad CTASSERT(sizeof seed <= RANDOM_BUFSIZE);
128 1.1 riastrad }
129 1.1 riastrad
130 1.1 riastrad /*
131 1.1 riastrad * Try to generate a block of data, but if we've hit
132 1.1 riastrad * the DRBG reseed interval, reseed.
133 1.1 riastrad */
134 1.1 riastrad if (nist_hash_drbg_generate(&drbg, buf, n, NULL, 0)) {
135 1.1 riastrad /*
136 1.1 riastrad * Get a fresh seed without blocking -- we have
137 1.1 riastrad * already generated some output so it is not
138 1.1 riastrad * useful to block. This can fail only if the
139 1.1 riastrad * request is obscenely large, so it is OK for
140 1.1 riastrad * either /dev/random or /dev/urandom to fail:
141 1.1 riastrad * we make no promises about gigabyte-sized
142 1.1 riastrad * reads happening all at once.
143 1.1 riastrad */
144 1.1 riastrad error = entropy_extract(seed, sizeof seed,
145 1.1 riastrad ENTROPY_HARDFAIL);
146 1.1 riastrad if (error)
147 1.1 riastrad break;
148 1.1 riastrad
149 1.1 riastrad /* Reseed and try again. */
150 1.1 riastrad if (nist_hash_drbg_reseed(&drbg, seed, sizeof seed,
151 1.1 riastrad NULL, 0))
152 1.1 riastrad panic("nist_hash_drbg_reseed");
153 1.1 riastrad
154 1.1 riastrad /* Promptly zero the seed. */
155 1.1 riastrad explicit_memset(seed, 0, sizeof seed);
156 1.1 riastrad
157 1.1 riastrad /* If it fails now, that's a bug. */
158 1.1 riastrad if (nist_hash_drbg_generate(&drbg, buf, n, NULL, 0))
159 1.1 riastrad panic("nist_hash_drbg_generate");
160 1.1 riastrad }
161 1.1 riastrad
162 1.1 riastrad /* Transfer n bytes out. */
163 1.1 riastrad error = uiomove(buf, n, uio);
164 1.1 riastrad if (error)
165 1.1 riastrad break;
166 1.1 riastrad
167 1.1 riastrad /*
168 1.1 riastrad * If this is /dev/random, stop here, return what we
169 1.1 riastrad * have, and force the next read to reseed. Programs
170 1.1 riastrad * can't rely on /dev/random for long reads.
171 1.1 riastrad */
172 1.1 riastrad if (ISSET(flags, GRND_RANDOM)) {
173 1.1 riastrad error = 0;
174 1.1 riastrad break;
175 1.1 riastrad }
176 1.1 riastrad
177 1.2 riastrad /* Now's a good time to yield if needed. */
178 1.2 riastrad preempt_point();
179 1.1 riastrad
180 1.1 riastrad /* Check for interruption after at least 256 bytes. */
181 1.1 riastrad CTASSERT(RANDOM_BUFSIZE >= 256);
182 1.1 riastrad if (__predict_false(curlwp->l_flag & LW_PENDSIG) &&
183 1.1 riastrad sigispending(curlwp, 0)) {
184 1.1 riastrad error = EINTR;
185 1.1 riastrad break;
186 1.1 riastrad }
187 1.1 riastrad }
188 1.1 riastrad
189 1.1 riastrad out: /* Zero the buffer and free it. */
190 1.1 riastrad explicit_memset(buf, 0, RANDOM_BUFSIZE);
191 1.1 riastrad kmem_free(buf, RANDOM_BUFSIZE);
192 1.1 riastrad
193 1.1 riastrad return error;
194 1.1 riastrad }
195 1.1 riastrad
196 1.1 riastrad int
197 1.1 riastrad sys_getrandom(struct lwp *l, const struct sys_getrandom_args *uap,
198 1.1 riastrad register_t *retval)
199 1.1 riastrad {
200 1.1 riastrad /* {
201 1.1 riastrad syscallarg(void *) buf;
202 1.1 riastrad syscallarg(size_t) buflen;
203 1.1 riastrad syscallarg(unsigned) flags;
204 1.1 riastrad } */
205 1.1 riastrad void *buf = SCARG(uap, buf);
206 1.1 riastrad size_t buflen = SCARG(uap, buflen);
207 1.1 riastrad int flags = SCARG(uap, flags);
208 1.1 riastrad int error;
209 1.1 riastrad
210 1.1 riastrad /* Set up an iov and uio to read into the user's buffer. */
211 1.1 riastrad struct iovec iov = { .iov_base = buf, .iov_len = buflen };
212 1.1 riastrad struct uio uio = {
213 1.1 riastrad .uio_iov = &iov,
214 1.1 riastrad .uio_iovcnt = 1,
215 1.1 riastrad .uio_offset = 0,
216 1.1 riastrad .uio_resid = buflen,
217 1.1 riastrad .uio_rw = UIO_READ,
218 1.1 riastrad .uio_vmspace = curproc->p_vmspace,
219 1.1 riastrad };
220 1.1 riastrad
221 1.1 riastrad /* Validate the flags. */
222 1.1 riastrad if (flags & ~(GRND_RANDOM|GRND_INSECURE|GRND_NONBLOCK)) {
223 1.1 riastrad /* Unknown flags. */
224 1.1 riastrad error = EINVAL;
225 1.1 riastrad goto out;
226 1.1 riastrad }
227 1.1 riastrad if ((flags & (GRND_RANDOM|GRND_INSECURE)) ==
228 1.1 riastrad (GRND_RANDOM|GRND_INSECURE)) {
229 1.1 riastrad /* Nonsensical combination. */
230 1.1 riastrad error = EINVAL;
231 1.1 riastrad goto out;
232 1.1 riastrad }
233 1.1 riastrad
234 1.1 riastrad /* Do it. */
235 1.1 riastrad error = dogetrandom(&uio, flags);
236 1.1 riastrad
237 1.1 riastrad out: /*
238 1.1 riastrad * If we transferred anything, return the number of bytes
239 1.1 riastrad * transferred and suppress error; otherwise return the error.
240 1.1 riastrad */
241 1.1 riastrad *retval = buflen - uio.uio_resid;
242 1.1 riastrad if (*retval)
243 1.1 riastrad error = 0;
244 1.1 riastrad return error;
245 1.1 riastrad }
246