arc4random.c revision 56698
150476Speter/* $FreeBSD: head/lib/libc/gen/arc4random.c 56698 2000-01-27 23:07:25Z jasone $ */
226628Sache
326628Sache/*
426628Sache * Arc4 random number generator for OpenBSD.
526628Sache * Copyright 1996 David Mazieres <dm@lcs.mit.edu>.
626628Sache *
726628Sache * Modification and redistribution in source and binary forms is
826628Sache * permitted provided that due credit is given to the author and the
926628Sache * OpenBSD project (for instance by leaving this copyright notice
1026628Sache * intact).
1126628Sache */
1226628Sache
1326628Sache/*
1426628Sache * This code is derived from section 17.1 of Applied Cryptography,
1526628Sache * second edition, which describes a stream cipher allegedly
1626628Sache * compatible with RSA Labs "RC4" cipher (the actual description of
1726628Sache * which is a trade secret).  The same algorithm is used as a stream
1826628Sache * cipher called "arcfour" in Tatu Ylonen's ssh package.
1926628Sache *
2026628Sache * Here the stream cipher has been modified always to include the time
2126628Sache * when initializing the state.  That makes it impossible to
2226628Sache * regenerate the same random sequence twice, so this can't be used
2326628Sache * for encryption, but will generate good random numbers.
2426628Sache *
2526628Sache * RC4 is a registered trademark of RSA Laboratories.
2626628Sache */
2726628Sache
2826628Sache#include <stdlib.h>
2926628Sache#include <fcntl.h>
3026628Sache#include <unistd.h>
3126628Sache#include <sys/types.h>
3226628Sache#include <sys/time.h>
3326628Sache
3426628Sachestruct arc4_stream {
3526628Sache	u_int8_t i;
3626628Sache	u_int8_t j;
3726628Sache	u_int8_t s[256];
3826628Sache};
3926628Sache
4026628Sachestatic int rs_initialized;
4126628Sachestatic struct arc4_stream rs;
4226628Sache
4326628Sachestatic inline void
4426628Sachearc4_init(as)
4526628Sache	struct arc4_stream *as;
4626628Sache{
4726628Sache	int     n;
4826628Sache
4926628Sache	for (n = 0; n < 256; n++)
5026628Sache		as->s[n] = n;
5126628Sache	as->i = 0;
5226628Sache	as->j = 0;
5326628Sache}
5426628Sache
5526628Sachestatic inline void
5626628Sachearc4_addrandom(as, dat, datlen)
5726628Sache	struct arc4_stream *as;
5826628Sache	u_char *dat;
5926628Sache	int     datlen;
6026628Sache{
6126628Sache	int     n;
6226628Sache	u_int8_t si;
6326628Sache
6426628Sache	as->i--;
6526628Sache	for (n = 0; n < 256; n++) {
6626628Sache		as->i = (as->i + 1);
6726628Sache		si = as->s[as->i];
6826628Sache		as->j = (as->j + si + dat[n % datlen]);
6926628Sache		as->s[as->i] = as->s[as->j];
7026628Sache		as->s[as->j] = si;
7126628Sache	}
7226628Sache}
7326628Sache
7426628Sachestatic void
7526628Sachearc4_stir(as)
7626628Sache	struct arc4_stream *as;
7726628Sache{
7826628Sache	int     fd;
7926628Sache	struct {
8026628Sache		struct timeval tv;
8126628Sache		pid_t pid;
8226628Sache		u_int8_t rnd[128 - sizeof(struct timeval) - sizeof(pid_t)];
8326628Sache	}       rdat;
8426628Sache
8526628Sache	gettimeofday(&rdat.tv, NULL);
8626628Sache	rdat.pid = getpid();
8756698Sjasone	fd = _open("/dev/urandom", O_RDONLY, 0);
8826628Sache	if (fd >= 0) {
8956698Sjasone		(void) _read(fd, rdat.rnd, sizeof(rdat.rnd));
9056698Sjasone		_close(fd);
9126628Sache	}
9226628Sache	/* fd < 0?  Ah, what the heck. We'll just take whatever was on the
9326628Sache	 * stack... */
9426628Sache
9526628Sache	arc4_addrandom(as, (void *) &rdat, sizeof(rdat));
9626628Sache}
9726628Sache
9826628Sachestatic inline u_int8_t
9926628Sachearc4_getbyte(as)
10026628Sache	struct arc4_stream *as;
10126628Sache{
10226628Sache	u_int8_t si, sj;
10326628Sache
10426628Sache	as->i = (as->i + 1);
10526628Sache	si = as->s[as->i];
10626628Sache	as->j = (as->j + si);
10726628Sache	sj = as->s[as->j];
10826628Sache	as->s[as->i] = sj;
10926628Sache	as->s[as->j] = si;
11026628Sache	return (as->s[(si + sj) & 0xff]);
11126628Sache}
11226628Sache
11326628Sachestatic inline u_int32_t
11426628Sachearc4_getword(as)
11526628Sache	struct arc4_stream *as;
11626628Sache{
11726628Sache	u_int32_t val;
11826628Sache	val = arc4_getbyte(as) << 24;
11926628Sache	val |= arc4_getbyte(as) << 16;
12026628Sache	val |= arc4_getbyte(as) << 8;
12126628Sache	val |= arc4_getbyte(as);
12226628Sache	return val;
12326628Sache}
12426628Sache
12526628Sachevoid
12626628Sachearc4random_stir()
12726628Sache{
12826628Sache	if (!rs_initialized) {
12926628Sache		arc4_init(&rs);
13026628Sache		rs_initialized = 1;
13126628Sache	}
13226628Sache	arc4_stir(&rs);
13326628Sache}
13426628Sache
13526628Sachevoid
13626628Sachearc4random_addrandom(dat, datlen)
13726628Sache	u_char *dat;
13826628Sache	int     datlen;
13926628Sache{
14026628Sache	if (!rs_initialized)
14126628Sache		arc4random_stir();
14226628Sache	arc4_addrandom(&rs, dat, datlen);
14326628Sache}
14426628Sache
14526628Sacheu_int32_t
14626628Sachearc4random()
14726628Sache{
14826628Sache	if (!rs_initialized)
14926628Sache		arc4random_stir();
15026628Sache	return arc4_getword(&rs);
15126628Sache}
15226628Sache
15326628Sache#if 0
15426628Sache/*-------- Test code for i386 --------*/
15526628Sache#include <stdio.h>
15626628Sache#include <machine/pctr.h>
15726628Sacheint
15826628Sachemain(int argc, char **argv)
15926628Sache{
16026628Sache	const int iter = 1000000;
16126628Sache	int     i;
16226628Sache	pctrval v;
16326628Sache
16426628Sache	v = rdtsc();
16526628Sache	for (i = 0; i < iter; i++)
16626628Sache		arc4random();
16726628Sache	v = rdtsc() - v;
16826628Sache	v /= iter;
16926628Sache
17026628Sache	printf("%qd cycles\n", v);
17126628Sache}
17226628Sache#endif
173