1254219Scy/*-
2254219Scy * THE BEER-WARE LICENSE
3254219Scy *
4254219Scy * <dan@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5254219Scy * can do whatever you want with this stuff.  If we meet some day, and you
6254219Scy * think this stuff is worth it, you can buy me a beer in return.
7254219Scy *
8254219Scy * Dan Moschuk
9254219Scy */
10254219Scy#if !defined(SOLARIS2) && !defined(__osf__)
11254219Scy# include <sys/cdefs.h>
12254219Scy#endif
13254219Scy
14254219Scy#include <sys/types.h>
15254219Scy#include <sys/param.h>
16254219Scy#ifdef __FreeBSD__
17254219Scy# include <sys/kernel.h>
18254219Scy#endif
19254219Scy#if !defined(__osf__)
20254219Scy# include <sys/random.h>
21254219Scy#endif
22254219Scy#ifdef __FreeBSD__
23254219Scy# include <sys/libkern.h>
24254219Scy#endif
25254219Scy#include <sys/lock.h>
26254219Scy#ifndef __osf__
27254219Scy# include <sys/mutex.h>
28254219Scy#endif
29254219Scy#include <sys/time.h>
30254219Scy
31254219Scy#if defined(SOLARIS2) && (SOLARIS2 < 9)
32254219Scy# include <netinet/in_systm.h>
33254219Scy#endif
34254219Scy#include <sys/socket.h>
35254219Scy#include <net/if.h>
36254219Scy#ifdef __osf__
37254219Scy# include <net/route.h>
38254219Scy#endif
39254219Scy#include <netinet/in.h>
40254219Scy#include <netinet/ip.h>
41254219Scy#include "netinet/ip_compat.h"
42254219Scy#ifdef HAS_SYS_MD5_H
43254219Scy# include <sys/md5.h>
44254219Scy#else
45254219Scy# include "md5.h"
46254219Scy#endif
47254219Scy
48254219Scy#ifdef NEED_LOCAL_RAND
49254219Scy#if !defined(__GNUC__)
50254219Scy# define __inline
51254219Scy#endif
52254219Scy
53254219Scy#define	ARC4_RESEED_BYTES 65536
54254219Scy#define	ARC4_RESEED_SECONDS 300
55254219Scy#define	ARC4_KEYBYTES (256 / 8)
56254219Scy
57254219Scystatic u_int8_t arc4_i, arc4_j;
58254219Scystatic int arc4_numruns = 0;
59254219Scystatic u_int8_t arc4_sbox[256];
60254219Scystatic time_t arc4_t_reseed;
61254219Scystatic ipfmutex_t arc4_mtx;
62254219Scystatic MD5_CTX md5ctx;
63254219Scy
64254219Scystatic u_int8_t arc4_randbyte(void);
65254219Scystatic int ipf_read_random(void *dest, int length);
66254219Scy
67254219Scystatic __inline void
68254219Scyarc4_swap(u_int8_t *a, u_int8_t *b)
69254219Scy{
70254219Scy	u_int8_t c;
71254219Scy
72254219Scy	c = *a;
73254219Scy	*a = *b;
74254219Scy	*b = c;
75254219Scy}
76254219Scy
77254219Scy/*
78254219Scy * Stir our S-box.
79254219Scy */
80254219Scystatic void
81254219Scyarc4_randomstir (void)
82254219Scy{
83254219Scy	u_int8_t key[256];
84254219Scy	int r, n;
85254219Scy	struct timeval tv_now;
86254219Scy
87254219Scy	/*
88254219Scy	 * XXX read_random() returns unsafe numbers if the entropy
89254219Scy	 * device is not loaded -- MarkM.
90254219Scy	 */
91254219Scy	r = ipf_read_random(key, ARC4_KEYBYTES);
92254219Scy	GETKTIME(&tv_now);
93254219Scy	MUTEX_ENTER(&arc4_mtx);
94254219Scy	/* If r == 0 || -1, just use what was on the stack. */
95254219Scy	if (r > 0) {
96254219Scy		for (n = r; n < sizeof(key); n++)
97254219Scy			key[n] = key[n % r];
98254219Scy	}
99254219Scy
100254219Scy	for (n = 0; n < 256; n++) {
101254219Scy		arc4_j = (arc4_j + arc4_sbox[n] + key[n]) % 256;
102254219Scy		arc4_swap(&arc4_sbox[n], &arc4_sbox[arc4_j]);
103254219Scy	}
104254219Scy
105254219Scy	/* Reset for next reseed cycle. */
106254219Scy	arc4_t_reseed = tv_now.tv_sec + ARC4_RESEED_SECONDS;
107254219Scy	arc4_numruns = 0;
108254219Scy
109254219Scy	/*
110254219Scy	 * Throw away the first N words of output, as suggested in the
111254219Scy	 * paper "Weaknesses in the Key Scheduling Algorithm of RC4"
112322012Scy	 * by Fluher, Mantin, and Shamir.  (N = 768 in our case.)
113254219Scy	 */
114322012Scy	for (n = 0; n < 768*4; n++)
115254219Scy		arc4_randbyte();
116254219Scy	MUTEX_EXIT(&arc4_mtx);
117254219Scy}
118254219Scy
119254219Scy/*
120254219Scy * Initialize our S-box to its beginning defaults.
121254219Scy */
122254219Scystatic void
123254219Scyarc4_init(void)
124254219Scy{
125254219Scy	int n;
126254219Scy
127254219Scy	MD5Init(&md5ctx);
128254219Scy
129254219Scy	MUTEX_INIT(&arc4_mtx, "arc4_mtx");
130254219Scy	arc4_i = arc4_j = 0;
131254219Scy	for (n = 0; n < 256; n++)
132254219Scy		arc4_sbox[n] = (u_int8_t) n;
133254219Scy
134254219Scy	arc4_t_reseed = 0;
135254219Scy}
136254219Scy
137254219Scy
138254219Scy/*
139254219Scy * Generate a random byte.
140254219Scy */
141254219Scystatic u_int8_t
142254219Scyarc4_randbyte(void)
143254219Scy{
144254219Scy	u_int8_t arc4_t;
145254219Scy
146254219Scy	arc4_i = (arc4_i + 1) % 256;
147254219Scy	arc4_j = (arc4_j + arc4_sbox[arc4_i]) % 256;
148254219Scy
149254219Scy	arc4_swap(&arc4_sbox[arc4_i], &arc4_sbox[arc4_j]);
150254219Scy
151254219Scy	arc4_t = (arc4_sbox[arc4_i] + arc4_sbox[arc4_j]) % 256;
152254219Scy	return arc4_sbox[arc4_t];
153254219Scy}
154254219Scy
155254219Scy/*
156254219Scy * MPSAFE
157254219Scy */
158254219Scyvoid
159254219Scyarc4rand(void *ptr, u_int len, int reseed)
160254219Scy{
161254219Scy	u_int8_t *p;
162254219Scy	struct timeval tv;
163254219Scy
164254219Scy	GETKTIME(&tv);
165254219Scy	if (reseed ||
166254219Scy	   (arc4_numruns > ARC4_RESEED_BYTES) ||
167254219Scy	   (tv.tv_sec > arc4_t_reseed))
168254219Scy		arc4_randomstir();
169254219Scy
170254219Scy	MUTEX_ENTER(&arc4_mtx);
171254219Scy	arc4_numruns += len;
172254219Scy	p = ptr;
173254219Scy	while (len--)
174254219Scy		*p++ = arc4_randbyte();
175254219Scy	MUTEX_EXIT(&arc4_mtx);
176254219Scy}
177254219Scy
178254219Scyuint32_t
179254219Scyipf_random(void)
180254219Scy{
181254219Scy	uint32_t ret;
182254219Scy
183254219Scy	arc4rand(&ret, sizeof ret, 0);
184254219Scy	return ret;
185254219Scy}
186254219Scy
187254219Scy
188254219Scystatic u_char pot[ARC4_RESEED_BYTES];
189254219Scystatic u_char *pothead = pot, *pottail = pot;
190254219Scystatic int inpot = 0;
191254219Scy
192254219Scy/*
193254219Scy * This is not very strong, and this is understood, but the aim isn't to
194254219Scy * be cryptographically strong - it is just to make up something that is
195254219Scy * pseudo random.
196254219Scy */
197254219Scyvoid
198254219Scyipf_rand_push(void *src, int length)
199254219Scy{
200254219Scy	static int arc4_inited = 0;
201254219Scy	u_char *nsrc;
202254219Scy	int mylen;
203254219Scy
204254219Scy	if (arc4_inited == 0) {
205254219Scy		arc4_init();
206254219Scy		arc4_inited = 1;
207254219Scy	}
208254219Scy
209254219Scy	if (length < 64) {
210254219Scy		MD5Update(&md5ctx, src, length);
211254219Scy		return;
212254219Scy	}
213254219Scy
214254219Scy	nsrc = src;
215254219Scy	mylen = length;
216254219Scy
217254219Scy#if defined(_SYS_MD5_H) && defined(SOLARIS2)
218254219Scy# define	buf	buf_un.buf8
219254219Scy#endif
220254219Scy	MUTEX_ENTER(&arc4_mtx);
221254219Scy	while ((mylen > 64)  && (sizeof(pot) - inpot > sizeof(md5ctx.buf))) {
222254219Scy		MD5Update(&md5ctx, nsrc, 64);
223254219Scy		mylen -= 64;
224254219Scy		nsrc += 64;
225254219Scy		if (pottail + sizeof(md5ctx.buf) > pot + sizeof(pot)) {
226254219Scy			int left, numbytes;
227254219Scy
228254219Scy			numbytes = pot + sizeof(pot) - pottail;
229254219Scy			bcopy(md5ctx.buf, pottail, numbytes);
230254219Scy			left = sizeof(md5ctx.buf) - numbytes;
231254219Scy			pottail = pot;
232254219Scy			bcopy(md5ctx.buf + sizeof(md5ctx.buf) - left,
233254219Scy			      pottail, left);
234254219Scy			pottail += left;
235254219Scy		} else {
236254219Scy			bcopy(md5ctx.buf, pottail, sizeof(md5ctx.buf));
237254219Scy			pottail += sizeof(md5ctx.buf);
238254219Scy		}
239254219Scy		inpot += 64;
240254219Scy	}
241254219Scy	MUTEX_EXIT(&arc4_mtx);
242254219Scy#if defined(_SYS_MD5_H) && defined(SOLARIS2)
243254219Scy# undef buf
244254219Scy#endif
245254219Scy}
246254219Scy
247254219Scy
248254219Scystatic int
249254219Scyipf_read_random(void *dest, int length)
250254219Scy{
251254219Scy	if (length > inpot)
252254219Scy		return 0;
253254219Scy
254254219Scy	MUTEX_ENTER(&arc4_mtx);
255254219Scy	if (pothead + length > pot + sizeof(pot)) {
256254219Scy		int left, numbytes;
257254219Scy
258254219Scy		left = length;
259254219Scy		numbytes = pot + sizeof(pot) - pothead;
260254219Scy		bcopy(pothead, dest, numbytes);
261254219Scy		left -= numbytes;
262254219Scy		pothead = pot;
263254219Scy		bcopy(pothead, dest + length - left, left);
264254219Scy		pothead += left;
265254219Scy	} else {
266254219Scy		bcopy(pothead, dest, length);
267254219Scy		pothead += length;
268254219Scy	}
269254219Scy	inpot -= length;
270254219Scy	if (inpot == 0)
271254219Scy		pothead = pottail = pot;
272254219Scy	MUTEX_EXIT(&arc4_mtx);
273254219Scy
274254219Scy	return length;
275254219Scy}
276254219Scy
277254219Scy#endif /* NEED_LOCAL_RAND */
278