1 /* $OpenBSD: arc4random.c,v 1.41 2014/07/12 13:24:54 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 1996, David Mazieres <dm@uun.org> 5 * Copyright (c) 2008, Damien Miller <djm@openbsd.org> 6 * Copyright (c) 2013, Markus Friedl <markus@openbsd.org> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 #include "config.h" 21 22 /* 23 * ChaCha based random number generator for OpenBSD. 24 */ 25 26 #include <fcntl.h> 27 #include <limits.h> 28 #include <signal.h> 29 #ifdef HAVE_STDINT_H 30 #include <stdint.h> 31 #endif 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/time.h> 38 #ifndef UB_ON_WINDOWS 39 #include <sys/mman.h> 40 #endif 41 42 #define KEYSTREAM_ONLY 43 #include "chacha_private.h" 44 45 #define arc4_min(a, b) ((a) < (b) ? (a) : (b)) 46 #ifdef __GNUC__ 47 #define inline __inline 48 #else /* !__GNUC__ */ 49 #define inline 50 #endif /* !__GNUC__ */ 51 #ifndef MAP_ANON 52 #define MAP_ANON MAP_ANONYMOUS 53 #endif 54 55 #define KEYSZ 32 56 #define IVSZ 8 57 #define BLOCKSZ 64 58 #define RSBUFSZ (16*BLOCKSZ) 59 60 /* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */ 61 static struct { 62 size_t rs_have; /* valid bytes at end of rs_buf */ 63 size_t rs_count; /* bytes till reseed */ 64 } *rs; 65 66 /* Preserved in fork children. */ 67 static struct { 68 chacha_ctx rs_chacha; /* chacha context for random keystream */ 69 u_char rs_buf[RSBUFSZ]; /* keystream blocks */ 70 } *rsx; 71 72 static inline void _rs_rekey(u_char *dat, size_t datlen); 73 74 /* 75 * Basic sanity checking; wish we could do better. 76 */ 77 static int 78 fallback_gotdata(char *buf, size_t len) 79 { 80 char any_set = 0; 81 size_t i; 82 83 for (i = 0; i < len; ++i) 84 any_set |= buf[i]; 85 if (any_set == 0) 86 return -1; 87 return 0; 88 } 89 90 /* fallback for getentropy in case libc returns failure */ 91 static int 92 fallback_getentropy_urandom(void *buf, size_t len) 93 { 94 size_t i; 95 int fd, flags; 96 int save_errno = errno; 97 98 start: 99 100 flags = O_RDONLY; 101 #ifdef O_NOFOLLOW 102 flags |= O_NOFOLLOW; 103 #endif 104 #ifdef O_CLOEXEC 105 flags |= O_CLOEXEC; 106 #endif 107 fd = open("/dev/urandom", flags, 0); 108 if (fd == -1) { 109 if (errno == EINTR) 110 goto start; 111 goto nodevrandom; 112 } 113 #ifndef O_CLOEXEC 114 # ifdef HAVE_FCNTL 115 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 116 # endif 117 #endif 118 for (i = 0; i < len; ) { 119 size_t wanted = len - i; 120 ssize_t ret = read(fd, (char*)buf + i, wanted); 121 122 if (ret == -1) { 123 if (errno == EAGAIN || errno == EINTR) 124 continue; 125 close(fd); 126 goto nodevrandom; 127 } 128 i += ret; 129 } 130 close(fd); 131 if (fallback_gotdata(buf, len) == 0) { 132 errno = save_errno; 133 return 0; /* satisfied */ 134 } 135 nodevrandom: 136 errno = EIO; 137 return -1; 138 } 139 140 static inline void 141 _rs_init(u_char *buf, size_t n) 142 { 143 if (n < KEYSZ + IVSZ) 144 return; 145 146 if (rs == NULL) { 147 #ifndef UB_ON_WINDOWS 148 if ((rs = mmap(NULL, sizeof(*rs), PROT_READ|PROT_WRITE, 149 MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) 150 abort(); 151 #ifdef MAP_INHERIT_ZERO 152 if (minherit(rs, sizeof(*rs), MAP_INHERIT_ZERO) == -1) 153 abort(); 154 #endif 155 #else /* WINDOWS */ 156 rs = malloc(sizeof(*rs)); 157 if(!rs) 158 abort(); 159 #endif 160 } 161 if (rsx == NULL) { 162 #ifndef UB_ON_WINDOWS 163 if ((rsx = mmap(NULL, sizeof(*rsx), PROT_READ|PROT_WRITE, 164 MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) 165 abort(); 166 #else /* WINDOWS */ 167 rsx = malloc(sizeof(*rsx)); 168 if(!rsx) 169 abort(); 170 #endif 171 } 172 173 chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8, 0); 174 chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ); 175 } 176 177 static void 178 _rs_stir(void) 179 { 180 u_char rnd[KEYSZ + IVSZ]; 181 182 if (getentropy(rnd, sizeof rnd) == -1) { 183 if(errno != ENOSYS || 184 fallback_getentropy_urandom(rnd, sizeof rnd) == -1) { 185 #ifdef SIGKILL 186 raise(SIGKILL); 187 #else 188 exit(9); /* windows */ 189 #endif 190 } 191 } 192 193 if (!rs) 194 _rs_init(rnd, sizeof(rnd)); 195 else 196 _rs_rekey(rnd, sizeof(rnd)); 197 explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */ 198 199 /* invalidate rs_buf */ 200 rs->rs_have = 0; 201 memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf)); 202 203 rs->rs_count = 1600000; 204 } 205 206 static inline void 207 _rs_stir_if_needed(size_t len) 208 { 209 #ifndef MAP_INHERIT_ZERO 210 static pid_t _rs_pid = 0; 211 pid_t pid = getpid(); 212 213 /* If a system lacks MAP_INHERIT_ZERO, resort to getpid() */ 214 if (_rs_pid == 0 || _rs_pid != pid) { 215 _rs_pid = pid; 216 if (rs) 217 rs->rs_count = 0; 218 } 219 #endif 220 if (!rs || rs->rs_count <= len) 221 _rs_stir(); 222 if (rs->rs_count <= len) 223 rs->rs_count = 0; 224 else 225 rs->rs_count -= len; 226 } 227 228 static inline void 229 _rs_rekey(u_char *dat, size_t datlen) 230 { 231 #ifndef KEYSTREAM_ONLY 232 memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf)); 233 #endif 234 /* fill rs_buf with the keystream */ 235 chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf, 236 rsx->rs_buf, sizeof(rsx->rs_buf)); 237 /* mix in optional user provided data */ 238 if (dat) { 239 size_t i, m; 240 241 m = arc4_min(datlen, KEYSZ + IVSZ); 242 for (i = 0; i < m; i++) 243 rsx->rs_buf[i] ^= dat[i]; 244 } 245 /* immediately reinit for backtracking resistance */ 246 _rs_init(rsx->rs_buf, KEYSZ + IVSZ); 247 memset(rsx->rs_buf, 0, KEYSZ + IVSZ); 248 rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ; 249 } 250 251 static inline void 252 _rs_random_buf(void *_buf, size_t n) 253 { 254 u_char *buf = (u_char *)_buf; 255 u_char *keystream; 256 size_t m; 257 258 _rs_stir_if_needed(n); 259 while (n > 0) { 260 if (rs->rs_have > 0) { 261 m = arc4_min(n, rs->rs_have); 262 keystream = rsx->rs_buf + sizeof(rsx->rs_buf) 263 - rs->rs_have; 264 memcpy(buf, keystream, m); 265 memset(keystream, 0, m); 266 buf += m; 267 n -= m; 268 rs->rs_have -= m; 269 } 270 if (rs->rs_have == 0) 271 _rs_rekey(NULL, 0); 272 } 273 } 274 275 static inline void 276 _rs_random_u32(uint32_t *val) 277 { 278 u_char *keystream; 279 _rs_stir_if_needed(sizeof(*val)); 280 if (rs->rs_have < sizeof(*val)) 281 _rs_rekey(NULL, 0); 282 keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have; 283 memcpy(val, keystream, sizeof(*val)); 284 memset(keystream, 0, sizeof(*val)); 285 rs->rs_have -= sizeof(*val); 286 } 287 288 uint32_t 289 arc4random(void) 290 { 291 uint32_t val; 292 293 _ARC4_LOCK(); 294 _rs_random_u32(&val); 295 _ARC4_UNLOCK(); 296 return val; 297 } 298 299 void 300 arc4random_buf(void *buf, size_t n) 301 { 302 _ARC4_LOCK(); 303 _rs_random_buf(buf, n); 304 _ARC4_UNLOCK(); 305 } 306