xref: /freebsd/sys/libkern/arc4random.c (revision 2b50ce65be6c97b3507d45275133df39e95752e8)
1ee3fd601SDan Moschuk /*-
2ee3fd601SDan Moschuk  * THE BEER-WARE LICENSE
3ee3fd601SDan Moschuk  *
4ee3fd601SDan Moschuk  * <dan@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5ee3fd601SDan Moschuk  * can do whatever you want with this stuff.  If we meet some day, and you
6ee3fd601SDan Moschuk  * think this stuff is worth it, you can buy me a beer in return.
7ee3fd601SDan Moschuk  *
8ee3fd601SDan Moschuk  * Dan Moschuk
9ee3fd601SDan Moschuk  */
10ee3fd601SDan Moschuk 
11ab0de15bSDavid E. O'Brien #include <sys/cdefs.h>
12ab0de15bSDavid E. O'Brien __FBSDID("$FreeBSD$");
13ab0de15bSDavid E. O'Brien 
14bf3191e9SMark Murray #include <sys/types.h>
152f823fa3SMike Silbersack #include <sys/param.h>
162f823fa3SMike Silbersack #include <sys/kernel.h>
17bf3191e9SMark Murray #include <sys/random.h>
18ee3fd601SDan Moschuk #include <sys/libkern.h>
192f823fa3SMike Silbersack #include <sys/lock.h>
202f823fa3SMike Silbersack #include <sys/mutex.h>
213a7810bcSMike Silbersack #include <sys/time.h>
22d65b1670SDan Moschuk 
232c38619bSPoul-Henning Kamp #define	ARC4_RESEED_BYTES 65536
243a7810bcSMike Silbersack #define	ARC4_RESEED_SECONDS 300
252c38619bSPoul-Henning Kamp #define	ARC4_KEYBYTES (256 / 8)
26ee3fd601SDan Moschuk 
27*2b50ce65SAndrey A. Chernov int arc4rand_iniseed_state = ARC4_ENTR_NONE;
28*2b50ce65SAndrey A. Chernov 
29ee3fd601SDan Moschuk static u_int8_t arc4_i, arc4_j;
30d65b1670SDan Moschuk static int arc4_numruns = 0;
31ee3fd601SDan Moschuk static u_int8_t arc4_sbox[256];
322c38619bSPoul-Henning Kamp static time_t arc4_t_reseed;
332f823fa3SMike Silbersack static struct mtx arc4_mtx;
343a7810bcSMike Silbersack 
353a7810bcSMike Silbersack static u_int8_t arc4_randbyte(void);
36ee3fd601SDan Moschuk 
37ee3fd601SDan Moschuk static __inline void
38ee3fd601SDan Moschuk arc4_swap(u_int8_t *a, u_int8_t *b)
39ee3fd601SDan Moschuk {
40ee3fd601SDan Moschuk 	u_int8_t c;
41ee3fd601SDan Moschuk 
42ee3fd601SDan Moschuk 	c = *a;
43ee3fd601SDan Moschuk 	*a = *b;
44ee3fd601SDan Moschuk 	*b = c;
45ee3fd601SDan Moschuk }
46ee3fd601SDan Moschuk 
47ee3fd601SDan Moschuk /*
48d65b1670SDan Moschuk  * Stir our S-box.
49d65b1670SDan Moschuk  */
50d65b1670SDan Moschuk static void
51d65b1670SDan Moschuk arc4_randomstir (void)
52d65b1670SDan Moschuk {
53d65b1670SDan Moschuk 	u_int8_t key[256];
54d65b1670SDan Moschuk 	int r, n;
552c38619bSPoul-Henning Kamp 	struct timeval tv_now;
56d65b1670SDan Moschuk 
5760f8e3afSBruce Evans 	/*
5860f8e3afSBruce Evans 	 * XXX read_random() returns unsafe numbers if the entropy
5960f8e3afSBruce Evans 	 * device is not loaded -- MarkM.
604cb1e539SMark Murray 	 */
613a7810bcSMike Silbersack 	r = read_random(key, ARC4_KEYBYTES);
622f823fa3SMike Silbersack 	getmicrouptime(&tv_now);
632f823fa3SMike Silbersack 	mtx_lock(&arc4_mtx);
6460f8e3afSBruce Evans 	/* If r == 0 || -1, just use what was on the stack. */
652c38619bSPoul-Henning Kamp 	if (r > 0) {
66d65b1670SDan Moschuk 		for (n = r; n < sizeof(key); n++)
67d65b1670SDan Moschuk 			key[n] = key[n % r];
68e6082d19SDan Moschuk 	}
69d65b1670SDan Moschuk 
702c38619bSPoul-Henning Kamp 	for (n = 0; n < 256; n++) {
71d65b1670SDan Moschuk 		arc4_j = (arc4_j + arc4_sbox[n] + key[n]) % 256;
72d65b1670SDan Moschuk 		arc4_swap(&arc4_sbox[n], &arc4_sbox[arc4_j]);
73d65b1670SDan Moschuk 	}
74b834665cSAndrey A. Chernov 	arc4_i = arc4_j = 0;
753a7810bcSMike Silbersack 
763a7810bcSMike Silbersack 	/* Reset for next reseed cycle. */
772c38619bSPoul-Henning Kamp 	arc4_t_reseed = tv_now.tv_sec + ARC4_RESEED_SECONDS;
783a7810bcSMike Silbersack 	arc4_numruns = 0;
792f823fa3SMike Silbersack 
802f823fa3SMike Silbersack 	/*
81fff6495eSAndrey A. Chernov 	 * Throw away the first N words of output, as suggested in the
822f823fa3SMike Silbersack 	 * paper "Weaknesses in the Key Scheduling Algorithm of RC4"
83fff6495eSAndrey A. Chernov 	 * by Fluher, Mantin, and Shamir.  (N = 256 in our case.)
842f823fa3SMike Silbersack 	 */
85fff6495eSAndrey A. Chernov 	for (n = 0; n < 256*4; n++)
86fff6495eSAndrey A. Chernov 		arc4_randbyte();
872f823fa3SMike Silbersack 	mtx_unlock(&arc4_mtx);
88d65b1670SDan Moschuk }
89d65b1670SDan Moschuk 
90d65b1670SDan Moschuk /*
91ee3fd601SDan Moschuk  * Initialize our S-box to its beginning defaults.
92ee3fd601SDan Moschuk  */
93ee3fd601SDan Moschuk static void
94ee3fd601SDan Moschuk arc4_init(void)
95ee3fd601SDan Moschuk {
96ee3fd601SDan Moschuk 	int n;
97ee3fd601SDan Moschuk 
982f823fa3SMike Silbersack 	mtx_init(&arc4_mtx, "arc4_mtx", NULL, MTX_DEF);
99ee3fd601SDan Moschuk 	arc4_i = arc4_j = 0;
100ee3fd601SDan Moschuk 	for (n = 0; n < 256; n++)
101d65b1670SDan Moschuk 		arc4_sbox[n] = (u_int8_t) n;
102d65b1670SDan Moschuk 
1032f823fa3SMike Silbersack 	arc4_t_reseed = 0;
104ee3fd601SDan Moschuk }
105ee3fd601SDan Moschuk 
1062f823fa3SMike Silbersack SYSINIT(arc4_init, SI_SUB_LOCK, SI_ORDER_ANY, arc4_init, NULL);
1072f823fa3SMike Silbersack 
108ee3fd601SDan Moschuk /*
109ee3fd601SDan Moschuk  * Generate a random byte.
110ee3fd601SDan Moschuk  */
111ee3fd601SDan Moschuk static u_int8_t
112ee3fd601SDan Moschuk arc4_randbyte(void)
113ee3fd601SDan Moschuk {
114ee3fd601SDan Moschuk 	u_int8_t arc4_t;
115ee3fd601SDan Moschuk 
116ee3fd601SDan Moschuk 	arc4_i = (arc4_i + 1) % 256;
117ee3fd601SDan Moschuk 	arc4_j = (arc4_j + arc4_sbox[arc4_i]) % 256;
118ee3fd601SDan Moschuk 
119ee3fd601SDan Moschuk 	arc4_swap(&arc4_sbox[arc4_i], &arc4_sbox[arc4_j]);
120ee3fd601SDan Moschuk 
121ee3fd601SDan Moschuk 	arc4_t = (arc4_sbox[arc4_i] + arc4_sbox[arc4_j]) % 256;
122ee3fd601SDan Moschuk 	return arc4_sbox[arc4_t];
123ee3fd601SDan Moschuk }
124ee3fd601SDan Moschuk 
1252f823fa3SMike Silbersack /*
1262f823fa3SMike Silbersack  * MPSAFE
1272f823fa3SMike Silbersack  */
1282c38619bSPoul-Henning Kamp void
1292c38619bSPoul-Henning Kamp arc4rand(void *ptr, u_int len, int reseed)
130ee3fd601SDan Moschuk {
1312c38619bSPoul-Henning Kamp 	u_char *p;
1322c38619bSPoul-Henning Kamp 	struct timeval tv;
133ee3fd601SDan Moschuk 
1342c38619bSPoul-Henning Kamp 	getmicrouptime(&tv);
135*2b50ce65SAndrey A. Chernov 	if (atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_HAVE,
136*2b50ce65SAndrey A. Chernov 	    ARC4_ENTR_SEED) || reseed ||
1372c38619bSPoul-Henning Kamp 	   (arc4_numruns > ARC4_RESEED_BYTES) ||
1382c38619bSPoul-Henning Kamp 	   (tv.tv_sec > arc4_t_reseed))
139d65b1670SDan Moschuk 		arc4_randomstir();
1402c38619bSPoul-Henning Kamp 
1412f823fa3SMike Silbersack 	mtx_lock(&arc4_mtx);
1422f823fa3SMike Silbersack 	arc4_numruns += len;
1432c38619bSPoul-Henning Kamp 	p = ptr;
1442c38619bSPoul-Henning Kamp 	while (len--)
1452c38619bSPoul-Henning Kamp 		*p++ = arc4_randbyte();
1462f823fa3SMike Silbersack 	mtx_unlock(&arc4_mtx);
147d65b1670SDan Moschuk }
148ee3fd601SDan Moschuk 
1492c38619bSPoul-Henning Kamp uint32_t
1502c38619bSPoul-Henning Kamp arc4random(void)
1512c38619bSPoul-Henning Kamp {
1522c38619bSPoul-Henning Kamp 	uint32_t ret;
153ee3fd601SDan Moschuk 
1542c38619bSPoul-Henning Kamp 	arc4rand(&ret, sizeof ret, 0);
155ee3fd601SDan Moschuk 	return ret;
156ee3fd601SDan Moschuk }
157