xref: /freebsd/lib/libc/gen/arc4random.c (revision 60ce8b0e072a330acbb910ebba88d3dcf3c2f494)
17f3dea24SPeter Wemm /* $FreeBSD$ */
283a03b38SAndrey A. Chernov 
383a03b38SAndrey A. Chernov /*
483a03b38SAndrey A. Chernov  * Arc4 random number generator for OpenBSD.
583a03b38SAndrey A. Chernov  * Copyright 1996 David Mazieres <dm@lcs.mit.edu>.
683a03b38SAndrey A. Chernov  *
783a03b38SAndrey A. Chernov  * Modification and redistribution in source and binary forms is
883a03b38SAndrey A. Chernov  * permitted provided that due credit is given to the author and the
983a03b38SAndrey A. Chernov  * OpenBSD project (for instance by leaving this copyright notice
1083a03b38SAndrey A. Chernov  * intact).
1183a03b38SAndrey A. Chernov  */
1283a03b38SAndrey A. Chernov 
1383a03b38SAndrey A. Chernov /*
1483a03b38SAndrey A. Chernov  * This code is derived from section 17.1 of Applied Cryptography,
1583a03b38SAndrey A. Chernov  * second edition, which describes a stream cipher allegedly
1683a03b38SAndrey A. Chernov  * compatible with RSA Labs "RC4" cipher (the actual description of
1783a03b38SAndrey A. Chernov  * which is a trade secret).  The same algorithm is used as a stream
1883a03b38SAndrey A. Chernov  * cipher called "arcfour" in Tatu Ylonen's ssh package.
1983a03b38SAndrey A. Chernov  *
2083a03b38SAndrey A. Chernov  * Here the stream cipher has been modified always to include the time
2183a03b38SAndrey A. Chernov  * when initializing the state.  That makes it impossible to
2283a03b38SAndrey A. Chernov  * regenerate the same random sequence twice, so this can't be used
2383a03b38SAndrey A. Chernov  * for encryption, but will generate good random numbers.
2483a03b38SAndrey A. Chernov  *
2583a03b38SAndrey A. Chernov  * RC4 is a registered trademark of RSA Laboratories.
2683a03b38SAndrey A. Chernov  */
2783a03b38SAndrey A. Chernov 
28333fc21eSDavid E. O'Brien #include <sys/cdefs.h>
29333fc21eSDavid E. O'Brien __FBSDID("$FreeBSD$");
30333fc21eSDavid E. O'Brien 
31d201fe46SDaniel Eischen #include "namespace.h"
32333fc21eSDavid E. O'Brien #include <sys/types.h>
33333fc21eSDavid E. O'Brien #include <sys/time.h>
3483a03b38SAndrey A. Chernov #include <stdlib.h>
3583a03b38SAndrey A. Chernov #include <fcntl.h>
3683a03b38SAndrey A. Chernov #include <unistd.h>
37d201fe46SDaniel Eischen #include "un-namespace.h"
3883a03b38SAndrey A. Chernov 
3983a03b38SAndrey A. Chernov struct arc4_stream {
4083a03b38SAndrey A. Chernov 	u_int8_t i;
4183a03b38SAndrey A. Chernov 	u_int8_t j;
4283a03b38SAndrey A. Chernov 	u_int8_t s[256];
4383a03b38SAndrey A. Chernov };
4483a03b38SAndrey A. Chernov 
4583a03b38SAndrey A. Chernov static int rs_initialized;
4683a03b38SAndrey A. Chernov static struct arc4_stream rs;
4783a03b38SAndrey A. Chernov 
4860ce8b0eSDavid Schultz static inline u_int8_t arc4_getbyte(struct arc4_stream *);
4960ce8b0eSDavid Schultz 
5083a03b38SAndrey A. Chernov static inline void
5183a03b38SAndrey A. Chernov arc4_init(as)
5283a03b38SAndrey A. Chernov 	struct arc4_stream *as;
5383a03b38SAndrey A. Chernov {
5483a03b38SAndrey A. Chernov 	int     n;
5583a03b38SAndrey A. Chernov 
5683a03b38SAndrey A. Chernov 	for (n = 0; n < 256; n++)
5783a03b38SAndrey A. Chernov 		as->s[n] = n;
5883a03b38SAndrey A. Chernov 	as->i = 0;
5983a03b38SAndrey A. Chernov 	as->j = 0;
6083a03b38SAndrey A. Chernov }
6183a03b38SAndrey A. Chernov 
6283a03b38SAndrey A. Chernov static inline void
6383a03b38SAndrey A. Chernov arc4_addrandom(as, dat, datlen)
6483a03b38SAndrey A. Chernov 	struct arc4_stream *as;
6583a03b38SAndrey A. Chernov 	u_char *dat;
6683a03b38SAndrey A. Chernov 	int     datlen;
6783a03b38SAndrey A. Chernov {
6883a03b38SAndrey A. Chernov 	int     n;
6983a03b38SAndrey A. Chernov 	u_int8_t si;
7083a03b38SAndrey A. Chernov 
7183a03b38SAndrey A. Chernov 	as->i--;
7283a03b38SAndrey A. Chernov 	for (n = 0; n < 256; n++) {
7383a03b38SAndrey A. Chernov 		as->i = (as->i + 1);
7483a03b38SAndrey A. Chernov 		si = as->s[as->i];
7583a03b38SAndrey A. Chernov 		as->j = (as->j + si + dat[n % datlen]);
7683a03b38SAndrey A. Chernov 		as->s[as->i] = as->s[as->j];
7783a03b38SAndrey A. Chernov 		as->s[as->j] = si;
7883a03b38SAndrey A. Chernov 	}
7983a03b38SAndrey A. Chernov }
8083a03b38SAndrey A. Chernov 
8183a03b38SAndrey A. Chernov static void
8283a03b38SAndrey A. Chernov arc4_stir(as)
8383a03b38SAndrey A. Chernov 	struct arc4_stream *as;
8483a03b38SAndrey A. Chernov {
8560ce8b0eSDavid Schultz 	int     fd, n;
8683a03b38SAndrey A. Chernov 	struct {
8783a03b38SAndrey A. Chernov 		struct timeval tv;
8883a03b38SAndrey A. Chernov 		pid_t pid;
8983a03b38SAndrey A. Chernov 		u_int8_t rnd[128 - sizeof(struct timeval) - sizeof(pid_t)];
9083a03b38SAndrey A. Chernov 	}       rdat;
9183a03b38SAndrey A. Chernov 
9283a03b38SAndrey A. Chernov 	gettimeofday(&rdat.tv, NULL);
9383a03b38SAndrey A. Chernov 	rdat.pid = getpid();
949233c4d9SJason Evans 	fd = _open("/dev/urandom", O_RDONLY, 0);
9583a03b38SAndrey A. Chernov 	if (fd >= 0) {
969233c4d9SJason Evans 		(void) _read(fd, rdat.rnd, sizeof(rdat.rnd));
979233c4d9SJason Evans 		_close(fd);
9883a03b38SAndrey A. Chernov 	}
9983a03b38SAndrey A. Chernov 	/* fd < 0?  Ah, what the heck. We'll just take whatever was on the
10083a03b38SAndrey A. Chernov 	 * stack... */
10183a03b38SAndrey A. Chernov 
10283a03b38SAndrey A. Chernov 	arc4_addrandom(as, (void *) &rdat, sizeof(rdat));
10360ce8b0eSDavid Schultz 
10460ce8b0eSDavid Schultz 	/*
10560ce8b0eSDavid Schultz 	 * Throw away the first N bytes of output, as suggested in the
10660ce8b0eSDavid Schultz 	 * paper "Weaknesses in the Key Scheduling Algorithm of RC4"
10760ce8b0eSDavid Schultz 	 * by Fluher, Mantin, and Shamir.  N=1024 is based on
10860ce8b0eSDavid Schultz 	 * suggestions in the paper "(Not So) Random Shuffles of RC4"
10960ce8b0eSDavid Schultz 	 * by Ilya Mironov.
11060ce8b0eSDavid Schultz 	 */
11160ce8b0eSDavid Schultz 	for (n = 0; n < 1024; n++)
11260ce8b0eSDavid Schultz 		arc4_getbyte(as);
11383a03b38SAndrey A. Chernov }
11483a03b38SAndrey A. Chernov 
11583a03b38SAndrey A. Chernov static inline u_int8_t
11683a03b38SAndrey A. Chernov arc4_getbyte(as)
11783a03b38SAndrey A. Chernov 	struct arc4_stream *as;
11883a03b38SAndrey A. Chernov {
11983a03b38SAndrey A. Chernov 	u_int8_t si, sj;
12083a03b38SAndrey A. Chernov 
12183a03b38SAndrey A. Chernov 	as->i = (as->i + 1);
12283a03b38SAndrey A. Chernov 	si = as->s[as->i];
12383a03b38SAndrey A. Chernov 	as->j = (as->j + si);
12483a03b38SAndrey A. Chernov 	sj = as->s[as->j];
12583a03b38SAndrey A. Chernov 	as->s[as->i] = sj;
12683a03b38SAndrey A. Chernov 	as->s[as->j] = si;
12783a03b38SAndrey A. Chernov 	return (as->s[(si + sj) & 0xff]);
12883a03b38SAndrey A. Chernov }
12983a03b38SAndrey A. Chernov 
13083a03b38SAndrey A. Chernov static inline u_int32_t
13183a03b38SAndrey A. Chernov arc4_getword(as)
13283a03b38SAndrey A. Chernov 	struct arc4_stream *as;
13383a03b38SAndrey A. Chernov {
13483a03b38SAndrey A. Chernov 	u_int32_t val;
13583a03b38SAndrey A. Chernov 	val = arc4_getbyte(as) << 24;
13683a03b38SAndrey A. Chernov 	val |= arc4_getbyte(as) << 16;
13783a03b38SAndrey A. Chernov 	val |= arc4_getbyte(as) << 8;
13883a03b38SAndrey A. Chernov 	val |= arc4_getbyte(as);
13983a03b38SAndrey A. Chernov 	return val;
14083a03b38SAndrey A. Chernov }
14183a03b38SAndrey A. Chernov 
14283a03b38SAndrey A. Chernov void
14383a03b38SAndrey A. Chernov arc4random_stir()
14483a03b38SAndrey A. Chernov {
14583a03b38SAndrey A. Chernov 	if (!rs_initialized) {
14683a03b38SAndrey A. Chernov 		arc4_init(&rs);
14783a03b38SAndrey A. Chernov 		rs_initialized = 1;
14883a03b38SAndrey A. Chernov 	}
14983a03b38SAndrey A. Chernov 	arc4_stir(&rs);
15083a03b38SAndrey A. Chernov }
15183a03b38SAndrey A. Chernov 
15283a03b38SAndrey A. Chernov void
15383a03b38SAndrey A. Chernov arc4random_addrandom(dat, datlen)
15483a03b38SAndrey A. Chernov 	u_char *dat;
15583a03b38SAndrey A. Chernov 	int     datlen;
15683a03b38SAndrey A. Chernov {
15783a03b38SAndrey A. Chernov 	if (!rs_initialized)
15883a03b38SAndrey A. Chernov 		arc4random_stir();
15983a03b38SAndrey A. Chernov 	arc4_addrandom(&rs, dat, datlen);
16083a03b38SAndrey A. Chernov }
16183a03b38SAndrey A. Chernov 
16283a03b38SAndrey A. Chernov u_int32_t
16383a03b38SAndrey A. Chernov arc4random()
16483a03b38SAndrey A. Chernov {
16583a03b38SAndrey A. Chernov 	if (!rs_initialized)
16683a03b38SAndrey A. Chernov 		arc4random_stir();
16783a03b38SAndrey A. Chernov 	return arc4_getword(&rs);
16883a03b38SAndrey A. Chernov }
16983a03b38SAndrey A. Chernov 
17083a03b38SAndrey A. Chernov #if 0
17183a03b38SAndrey A. Chernov /*-------- Test code for i386 --------*/
17283a03b38SAndrey A. Chernov #include <stdio.h>
17383a03b38SAndrey A. Chernov #include <machine/pctr.h>
17483a03b38SAndrey A. Chernov int
17583a03b38SAndrey A. Chernov main(int argc, char **argv)
17683a03b38SAndrey A. Chernov {
17783a03b38SAndrey A. Chernov 	const int iter = 1000000;
17883a03b38SAndrey A. Chernov 	int     i;
17983a03b38SAndrey A. Chernov 	pctrval v;
18083a03b38SAndrey A. Chernov 
18183a03b38SAndrey A. Chernov 	v = rdtsc();
18283a03b38SAndrey A. Chernov 	for (i = 0; i < iter; i++)
18383a03b38SAndrey A. Chernov 		arc4random();
18483a03b38SAndrey A. Chernov 	v = rdtsc() - v;
18583a03b38SAndrey A. Chernov 	v /= iter;
18683a03b38SAndrey A. Chernov 
18783a03b38SAndrey A. Chernov 	printf("%qd cycles\n", v);
18883a03b38SAndrey A. Chernov }
18983a03b38SAndrey A. Chernov #endif
190