1 /* $MirOS: src/sys/crypto/arc4random.c,v 1.8 2014/03/13 06:09:48 tg Exp $ */
2 
3 /*-
4  * Copyright © 2010, 2014
5  *	Thorsten Glaser <tg@mirbsd.org>
6  *
7  * Provided that these terms and disclaimer and all copyright notices
8  * are retained or reproduced in an accompanying document, permission
9  * is granted to deal in this work without restriction, including un‐
10  * limited rights to use, publicly perform, distribute, sell, modify,
11  * merge, give away, or sublicence.
12  *
13  * This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
14  * the utmost extent permitted by applicable law, neither express nor
15  * implied; without malicious intent or gross negligence. In no event
16  * may a licensor, author or contributor be held liable for indirect,
17  * direct, other damage, loss, or other issues arising in any way out
18  * of dealing in the work, even if advised of the possibility of such
19  * damage or existence of a defect, except proven that it results out
20  * of said person’s immediate fault when using the work as intended.
21  *-
22  * Always working arc4random(9) implementation; the arcfour state has
23  * been initialised at kernel compile time by newvers.sh for us. Once
24  * the random device has attached, arc4random_reinit() will be called
25  * from a timeout, or when enough bytes are consumed, and restir with
26  * entropy from both the rndpool and the lopool.
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/timeout.h>
33 #include <crypto/randimpl.h>
34 
35 /* import from random.c */
36 extern struct timeout arc4random_timeout;
37 extern struct arcfour_status lopool_collapse;
38 
39 /* import from vers.c generated by conf/newvers.sh */
40 extern struct arcfour_status initial_arc4random;
41 
42 size_t arc4random_count = 0;
43 
44 /*
45  * 8 MiB seems like a sane limit to protect against reading
46  * enough stream cipher output to try and attack the key
47  */
48 #ifndef ARC4RANDOM_MAXBYTES
49 #define ARC4RANDOM_MAXBYTES	(8 * 1048576)
50 #endif
51 
52 u_int32_t
arc4random(void)53 arc4random(void)
54 {
55 	uint32_t v;
56 	int s;
57 
58 	s = splhigh();
59 
60 	rndstats.arc4_reads += 4;
61 	if (arc4random_count >= ARC4RANDOM_MAXBYTES - 4)
62 		arc4random_reinit(NULL);
63 	arc4random_count += 4;
64 
65 	/* skip an output byte randomly for whitening */
66 	if ((arcfour_byte(&lopool_collapse) & 1))
67 		(void)arcfour_byte(&initial_arc4random);
68 
69 	v = ((uint32_t)arcfour_byte(&initial_arc4random) << 24) |
70 	    ((uint32_t)arcfour_byte(&initial_arc4random) << 16) |
71 	    ((uint32_t)arcfour_byte(&initial_arc4random) << 8) |
72 	    ((uint32_t)arcfour_byte(&initial_arc4random));
73 
74 	splx(s);
75 	return (v);
76 }
77 
78 void
arc4random_buf(void * buf_,size_t len)79 arc4random_buf(void *buf_, size_t len)
80 {
81 	size_t n;
82 	uint8_t *buf = (uint8_t *)buf_;
83 	int s;
84 
85 	if (!len)
86 		return;
87 	s = splhigh();
88 	rndstats.arc4_reads += len;
89 	goto into_the_loop;
90 
91 	/* operate in chunks of 256 output bytes to give interrupts a chance */
92 	while (len) {
93 		s = splhigh();
94  into_the_loop:
95 		/* skip a few output bytes randomly for whitening */
96 		n = arcfour_byte(&lopool_collapse) & 3;
97 		while (n--)
98 			(void)arcfour_byte(&initial_arc4random);
99 
100 		n = min(len, 256);
101 		len -= n;
102 
103 		/*
104 		 * using 256 here, not n, to speed up; the difference
105 		 * is not worth mentioning, and it’s also more secure
106 		 */
107 		if (arc4random_count >= (ARC4RANDOM_MAXBYTES - 256))
108 			arc4random_reinit(NULL);
109 		arc4random_count += n;
110 
111 		while (n--)
112 			*buf++ = arcfour_byte(&initial_arc4random);
113 
114 		splx(s);
115 	}
116 }
117 
118 /*
119  * Stir the arcfour state used by arc4random(9). This function is
120  * called via a periodic timeout (approximately every 8½ minutes)
121  * as well as from a consumer when approx. 8 MiB were used up.
122  */
123 void
arc4random_reinit(void * arg __unused)124 arc4random_reinit(void *arg __unused)
125 {
126 	size_t n;
127 	struct {
128 		uint8_t chars[192];
129 		struct timeval tv;
130 	} buf;
131 	int s;
132 
133 	s = splhigh();
134 
135 	if (!rnd_attached) {
136 		RNDEBUG(RD_ALWAYS, "rnd: reinit before attach\n");
137 		goto out;
138 	}
139 
140 	/* if we were called directly instead of from timeout */
141 	timeout_del(&arc4random_timeout);
142 
143 	/* half the poolbits, minimum 8, maximum 128 bytes */
144 	n = max(8, min(128, (random_state.entropy_count / 16)));
145 	rndstats.arc4_stirs += n;
146 
147 	RNDEBUG(RD_ARC4RANDOM, "rnd: reinit at %lu with %lu bytes\n",
148 	    (u_long)arc4random_count, (u_long)n);
149 	get_random_bytes(buf.chars, n);
150 
151 	while (n < 192)
152 		buf.chars[n++] = arcfour_byte(&lopool_collapse);
153 	buf.tv.tv_sec = mono_time.tv_sec;
154 	buf.tv.tv_usec = mono_time.tv_usec;
155 	/* carry over */
156 	n = arcfour_byte(&initial_arc4random) & 7;
157 	/* re-key */
158 	arcfour_ksa(&initial_arc4random, (void *)&buf, sizeof(buf));
159 	arc4random_count = 0;
160 	++rndstats.arc4_nstirs;
161 
162 	/*
163 	 * skip early keystream for security,
164 	 * plus a random amount for whitening
165 	 */
166 	n += 256 * 12 +
167 	    (arcfour_byte(&lopool_collapse) & 7) +
168 	    (arcfour_byte(&initial_arc4random) & 7);
169 	while (n--)
170 		(void)arcfour_byte(&initial_arc4random);
171 
172 	/* contribute our (lopool,rndpool,arc4pool) result */
173 	n = ((uint32_t)arcfour_byte(&initial_arc4random) << 24) |
174 	    ((uint32_t)arcfour_byte(&initial_arc4random) << 16) |
175 	    ((uint32_t)arcfour_byte(&initial_arc4random) << 8) |
176 	    ((uint32_t)arcfour_byte(&initial_arc4random));
177 	enqueue_randomness(RND_SRC_LPC, (int)n);
178 	n = (arcfour_byte(&initial_arc4random) & 3) +
179 	    (arcfour_byte(&lopool_collapse) & 3);
180 	while (n--)
181 		(void)arcfour_byte(&initial_arc4random);
182 
183 	/* zero out temp. key buffer */
184 	bzero(&buf, sizeof(buf));
185 
186 	/* call us again in approx. 8½ minutes, minus [0‥32[ seconds */
187 	timeout_add(&arc4random_timeout,
188 	    (hz << 9) - arc4random_uniform(hz << 5));
189 
190  out:
191 	splx(s);
192 }
193