lua-arc4random

Cryptographically secure PRNG for Lua
Log | Files | Refs | README

arc4random.c (4553B)


      1 /*	$OpenBSD: arc4random.c,v 1.49 2014/07/20 20:51:13 bcook 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 
     21 /*
     22  * ChaCha based random number generator for OpenBSD.
     23  */
     24 
     25 #include <fcntl.h>
     26 #include <limits.h>
     27 #include <signal.h>
     28 #include <stdint.h>
     29 #include <stdlib.h>
     30 #include <string.h>
     31 #include <unistd.h>
     32 #include <sys/types.h>
     33 #include <sys/param.h>
     34 #include <sys/time.h>
     35 
     36 #define KEYSTREAM_ONLY
     37 #include "chacha_private.h"
     38 
     39 #define min(a, b) ((a) < (b) ? (a) : (b))
     40 #ifdef __GNUC__
     41 #define inline __inline
     42 #else				/* !__GNUC__ */
     43 #define inline
     44 #endif				/* !__GNUC__ */
     45 
     46 #define KEYSZ	32
     47 #define IVSZ	8
     48 #define BLOCKSZ	64
     49 #define RSBUFSZ	(16*BLOCKSZ)
     50 
     51 /* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */
     52 static struct _rs {
     53 	size_t		rs_have;	/* valid bytes at end of rs_buf */
     54 	size_t		rs_count;	/* bytes till reseed */
     55 } *rs;
     56 
     57 /* Maybe be preserved in fork children, if _rs_allocate() decides. */
     58 static struct _rsx {
     59 	chacha_ctx	rs_chacha;	/* chacha context for random keystream */
     60 	u_char		rs_buf[RSBUFSZ];	/* keystream blocks */
     61 } *rsx;
     62 
     63 static inline int _rs_allocate(struct _rs **, struct _rsx **);
     64 static inline void _rs_forkdetect(void);
     65 #include "arc4random.h"
     66 
     67 static inline void _rs_rekey(u_char *dat, size_t datlen);
     68 
     69 static inline void
     70 _rs_init(u_char *buf, size_t n)
     71 {
     72 	if (n < KEYSZ + IVSZ)
     73 		return;
     74 
     75 	if (rs == NULL) {
     76 		if (_rs_allocate(&rs, &rsx) == -1)
     77 			abort();
     78 	}
     79 
     80 	chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8, 0);
     81 	chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);
     82 }
     83 
     84 static void
     85 _rs_stir(void)
     86 {
     87 	u_char rnd[KEYSZ + IVSZ];
     88 
     89 	if (getentropy(rnd, sizeof rnd) == -1)
     90 		_getentropy_fail();
     91 
     92 	if (!rs)
     93 		_rs_init(rnd, sizeof(rnd));
     94 	else
     95 		_rs_rekey(rnd, sizeof(rnd));
     96 	explicit_bzero(rnd, sizeof(rnd));	/* discard source seed */
     97 
     98 	/* invalidate rs_buf */
     99 	rs->rs_have = 0;
    100 	memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
    101 
    102 	rs->rs_count = 1600000;
    103 }
    104 
    105 static inline void
    106 _rs_stir_if_needed(size_t len)
    107 {
    108 	_rs_forkdetect();
    109 	if (!rs || rs->rs_count <= len)
    110 		_rs_stir();
    111 	if (rs->rs_count <= len)
    112 		rs->rs_count = 0;
    113 	else
    114 		rs->rs_count -= len;
    115 }
    116 
    117 static inline void
    118 _rs_rekey(u_char *dat, size_t datlen)
    119 {
    120 #ifndef KEYSTREAM_ONLY
    121 	memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
    122 #endif
    123 	/* fill rs_buf with the keystream */
    124 	chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf,
    125 	    rsx->rs_buf, sizeof(rsx->rs_buf));
    126 	/* mix in optional user provided data */
    127 	if (dat) {
    128 		size_t i, m;
    129 
    130 		m = min(datlen, KEYSZ + IVSZ);
    131 		for (i = 0; i < m; i++)
    132 			rsx->rs_buf[i] ^= dat[i];
    133 	}
    134 	/* immediately reinit for backtracking resistance */
    135 	_rs_init(rsx->rs_buf, KEYSZ + IVSZ);
    136 	memset(rsx->rs_buf, 0, KEYSZ + IVSZ);
    137 	rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ;
    138 }
    139 
    140 static inline void
    141 _rs_random_buf(void *_buf, size_t n)
    142 {
    143 	u_char *buf = (u_char *)_buf;
    144 	u_char *keystream;
    145 	size_t m;
    146 
    147 	_rs_stir_if_needed(n);
    148 	while (n > 0) {
    149 		if (rs->rs_have > 0) {
    150 			m = min(n, rs->rs_have);
    151 			keystream = rsx->rs_buf + sizeof(rsx->rs_buf)
    152 			    - rs->rs_have;
    153 			memcpy(buf, keystream, m);
    154 			memset(keystream, 0, m);
    155 			buf += m;
    156 			n -= m;
    157 			rs->rs_have -= m;
    158 		}
    159 		if (rs->rs_have == 0)
    160 			_rs_rekey(NULL, 0);
    161 	}
    162 }
    163 
    164 static inline void
    165 _rs_random_u32(uint32_t *val)
    166 {
    167 	u_char *keystream;
    168 
    169 	_rs_stir_if_needed(sizeof(*val));
    170 	if (rs->rs_have < sizeof(*val))
    171 		_rs_rekey(NULL, 0);
    172 	keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have;
    173 	memcpy(val, keystream, sizeof(*val));
    174 	memset(keystream, 0, sizeof(*val));
    175 	rs->rs_have -= sizeof(*val);
    176 }
    177 
    178 uint32_t
    179 arc4random(void)
    180 {
    181 	uint32_t val;
    182 
    183 	_ARC4_LOCK();
    184 	_rs_random_u32(&val);
    185 	_ARC4_UNLOCK();
    186 	return val;
    187 }
    188 
    189 void
    190 arc4random_buf(void *buf, size_t n)
    191 {
    192 	_ARC4_LOCK();
    193 	_rs_random_buf(buf, n);
    194 	_ARC4_UNLOCK();
    195 }