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