Submitted By: Robert Connolly <robert at linuxfromscratch dot org> (ashes)
Date: 2007-06-06
Initial Package Version: 2.4.34.5
Upstream Status: Rejected - http://lkml.org/lkml/2003/10/16/26
Origin: http://frandom.sourceforge.net/
Description: This patch adds the frandom and erandom devices.

mknod /dev/frandom c 235 11
mknod /dev/erandom c 235 12

diff -Naur linux-2.4.34.5.orig/Documentation/Configure.help linux-2.4.34.5/Documentation/Configure.help
--- linux-2.4.34.5.orig/Documentation/Configure.help	2007-06-06 19:20:53.000000000 +0000
+++ linux-2.4.34.5/Documentation/Configure.help	2007-06-06 23:44:39.000000000 +0000
@@ -18507,6 +18507,17 @@
   input/output character sets. Say Y here for the UTF-8 encoding of
   the Unicode/ISO9646 universal character set.
 
+Fast random data generator suite (/dev/frandom and /dev/erandom)
+CONFIG_FRANDOM
+  Fast random data/number generator support in kernel. This random
+  generator is 10-50 times faster than /dev/urandom, and saves kernel
+  entropy.
+
+  If unsure, say Y unless you're tight on kernel size. This module is
+  small and harmless otherwise.
+
+  If you choose M, the sysctl interface will be disabled.
+
 Virtual terminal
 CONFIG_VT
   If you say Y here, you will get support for terminal devices with
diff -Naur linux-2.4.34.5.orig/arch/i386/defconfig linux-2.4.34.5/arch/i386/defconfig
--- linux-2.4.34.5.orig/arch/i386/defconfig	2007-06-06 19:20:53.000000000 +0000
+++ linux-2.4.34.5/arch/i386/defconfig	2007-06-06 23:48:52.000000000 +0000
@@ -580,6 +580,7 @@
 #
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
+CONFIG_FRANDOM=y
 CONFIG_SERIAL=y
 # CONFIG_SERIAL_CONSOLE is not set
 # CONFIG_SERIAL_EXTENDED is not set
diff -Naur linux-2.4.34.5.orig/drivers/char/Config.in linux-2.4.34.5/drivers/char/Config.in
--- linux-2.4.34.5.orig/drivers/char/Config.in	2007-06-06 19:20:53.000000000 +0000
+++ linux-2.4.34.5/drivers/char/Config.in	2007-06-06 23:44:39.000000000 +0000
@@ -25,6 +25,7 @@
       tristate '   Dual serial port support' CONFIG_DUALSP_SERIAL
    fi
 fi
+tristate 'Fast random data generator suite (/dev/frandom and /dev/erandom)' CONFIG_FRANDOM
 dep_mbool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED $CONFIG_SERIAL
 if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then
    bool '  Support more than 4 serial ports' CONFIG_SERIAL_MANY_PORTS
diff -Naur linux-2.4.34.5.orig/drivers/char/Makefile linux-2.4.34.5/drivers/char/Makefile
--- linux-2.4.34.5.orig/drivers/char/Makefile	2007-06-06 19:20:53.000000000 +0000
+++ linux-2.4.34.5/drivers/char/Makefile	2007-06-06 23:44:58.000000000 +0000
@@ -25,7 +25,7 @@
 			misc.o pty.o random.o selection.o serial.o \
 			sonypi.o tty_io.o tty_ioctl.o generic_serial.o \
 			au1000_gpio.o vac-serial.o hp_psaux.o nvram.o \
-			scx200.o fetchop.o
+			scx200.o fetchop.o frandom.o
 
 mod-subdirs	:=	joystick ftape drm drm-4.0 pcmcia
 
@@ -334,6 +334,8 @@
   obj-y += ipmi/ipmi.o
 endif
 
+obj-$(CONFIG_FRANDOM) += frandom.o
+
 include $(TOPDIR)/Rules.make
 
 fastdep:
diff -Naur linux-2.4.34.5.orig/drivers/char/frandom.c linux-2.4.34.5/drivers/char/frandom.c
--- linux-2.4.34.5.orig/drivers/char/frandom.c	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.4.34.5/drivers/char/frandom.c	2007-06-06 23:44:39.000000000 +0000
@@ -0,0 +1,362 @@
+/*
+** frandom.c
+**      Fast pseudo-random generator 
+**
+**      (c) Copyright 2003 Eli Billauer
+**      http://www.billauer.co.il
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** Usage: mknod /dev/frandom c 235 11
+**        mknod /dev/erandom c 235 12
+**        insmod frandom
+**
+** This code is highly based upon the examples given in the book "Linux
+** Device Drivers" by Alessandro Rubini and Jonathan Corbet, published
+** by O'Reilly & Associates.
+** O'Reilly's release of this book on the web for free is highly
+** appreciated.
+**
+*/
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h> 
+#include <linux/fs.h> 
+#include <linux/errno.h>
+#include <linux/types.h> 
+#include <linux/random.h>
+
+#include <asm/uaccess.h>
+
+#if (LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,0))
+#include <linux/moduleparam.h>
+#endif
+
+#define INTERNAL_SEED 0
+#define EXTERNAL_SEED 1
+
+#define FRANDOM_MAJOR 235
+#define FRANDOM_MINOR 11 
+#define ERANDOM_MINOR 12 
+
+static struct file_operations frandom_fops; /* Values assigned below */
+
+static int erandom_seeded = 0; /* Internal flag */
+
+static int frandom_major = FRANDOM_MAJOR;
+static int frandom_minor = FRANDOM_MINOR;
+static int erandom_minor = ERANDOM_MINOR;
+static int frandom_bufsize = 256;
+static int frandom_chunklimit = 0; /* =0 means unlimited */
+
+MODULE_DESCRIPTION("Fast pseudo-random number generator");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Eli Billauer");
+MODULE_PARM(frandom_major,"i");
+MODULE_PARM_DESC(frandom_major,"Major number of /dev/frandom and /dev/erandom");
+MODULE_PARM(frandom_minor,"i");
+MODULE_PARM_DESC(frandom_minor,"Minor number of /dev/frandom");
+MODULE_PARM(erandom_minor,"i");
+MODULE_PARM_DESC(erandom_minor,"Minor number of /dev/erandom");
+MODULE_PARM(frandom_bufsize,"i");
+MODULE_PARM_DESC(frandom_bufsize,"Internal buffer size in bytes. Default is 256. Must be >= 256");
+MODULE_PARM(frandom_chunklimit,"i");
+MODULE_PARM_DESC(frandom_chunklimit,"Limit for read() blocks size. 0 (default) is unlimited, otherwise must be >= 256");
+
+struct frandom_state
+{
+	struct semaphore sem; /* Semaphore on the state structure */
+
+	u8 S[256]; /* The state array */
+	u8 i;        
+	u8 j;
+
+	char *buf;
+};
+
+static struct frandom_state *erandom_state;
+
+static inline void swap_byte(u8 *a, u8 *b)
+{
+	u8 swapByte; 
+  
+	swapByte = *a; 
+	*a = *b;      
+	*b = swapByte;
+}
+
+static void init_rand_state(struct frandom_state *state, int seedflag);
+
+void erandom_get_random_bytes(char *buf, size_t count)
+{
+	struct frandom_state *state = erandom_state;
+	int k;
+
+	unsigned int i;
+	unsigned int j;
+	u8 *S;
+  
+	/* If we fail to get the semaphore, we revert to external random data.
+	   Since semaphore blocking is expected to be very rare, and interrupts
+	   during these rare and very short periods of time even less frequent,
+	   we take the better-safe-than-sorry approach, and fill the buffer
+	   some expensive random data, in case the caller wasn't aware of this
+	   possibility, and expects random data anyhow.
+	*/
+
+	if (down_interruptible(&state->sem)) {
+		get_random_bytes(buf, count);
+		return;
+	}
+
+	/* We seed erandom as late as possible, hoping that the kernel's main
+	   RNG is already restored in the boot sequence (not critical, but
+	   better.
+	*/
+	
+	if (!erandom_seeded) {
+		erandom_seeded = 1;
+		init_rand_state(state, EXTERNAL_SEED);
+		printk(KERN_INFO "frandom: Seeded global generator now (used by erandom)\n");
+	}
+
+	i = state->i;     
+	j = state->j;
+	S = state->S;  
+
+	for (k=0; k<count; k++) {
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		swap_byte(&S[i], &S[j]);
+		*buf++ = S[(S[i] + S[j]) & 0xff];
+	}
+ 
+	state->i = i;     
+	state->j = j;
+
+	up(&state->sem);
+}
+
+static void init_rand_state(struct frandom_state *state, int seedflag)
+{
+	unsigned int i, j, k;
+	u8 *S;
+	u8 *seed = state->buf;
+
+	if (seedflag == INTERNAL_SEED)
+		erandom_get_random_bytes(seed, 256);
+	else
+		get_random_bytes(seed, 256);
+
+	S = state->S;
+	for (i=0; i<256; i++)
+		*S++=i;
+
+	j=0;
+	S = state->S;
+
+	for (i=0; i<256; i++) {
+		j = (j + S[i] + *seed++) & 0xff;
+		swap_byte(&S[i], &S[j]);
+	}
+
+	/* It's considered good practice to discard the first 256 bytes
+	   generated. So we do it:
+	*/
+
+	i=0; j=0;
+	for (k=0; k<256; k++) {
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		swap_byte(&S[i], &S[j]);
+	}
+
+	state->i = i; /* Save state */
+	state->j = j;
+}
+
+static int frandom_open(struct inode *inode, struct file *filp)
+{
+  
+	struct frandom_state *state;
+
+	int num =MINOR(kdev_t_to_nr(inode->i_rdev));
+	if ((num != frandom_minor) && (num != erandom_minor)) return -ENODEV;
+  
+	state = kmalloc(sizeof(struct frandom_state), GFP_KERNEL);
+	if (!state)
+		return -ENOMEM;
+
+	state->buf = kmalloc(frandom_bufsize, GFP_KERNEL);
+	if (!state->buf) {
+		kfree(state);
+		return -ENOMEM;
+	}
+
+	sema_init(&state->sem, 1); /* Init semaphore as a mutex */
+
+	if (num == frandom_minor)
+		init_rand_state(state, EXTERNAL_SEED);
+	else
+		init_rand_state(state, INTERNAL_SEED);
+
+	filp->private_data = state;
+
+#if (LINUX_VERSION_CODE<KERNEL_VERSION(2,4,0))
+	MOD_INC_USE_COUNT; 
+#endif
+  
+	return 0; /* Success */
+}
+
+static int frandom_release(struct inode *inode, struct file *filp)
+{
+
+	struct frandom_state *state = filp->private_data;
+
+	kfree(state->buf);
+	kfree(state);
+  
+#if (LINUX_VERSION_CODE<KERNEL_VERSION(2,4,0))
+	MOD_DEC_USE_COUNT;
+#endif
+
+	return 0;
+}
+
+static ssize_t frandom_read(struct file *filp, char *buf, size_t count,
+			    loff_t *f_pos)
+{
+	struct frandom_state *state = filp->private_data;
+	ssize_t ret;
+	int dobytes, k;
+	char *localbuf;
+
+	unsigned int i;
+	unsigned int j;
+	u8 *S;
+  
+	if (down_interruptible(&state->sem))
+		return -ERESTARTSYS;
+  
+	if ((frandom_chunklimit > 0) && (count > frandom_chunklimit))
+		count = frandom_chunklimit;
+
+	ret = count; /* It's either everything or an error... */
+  
+	i = state->i;     
+	j = state->j;
+	S = state->S;  
+
+	while (count) {
+		if (count > frandom_bufsize)
+			dobytes = frandom_bufsize;
+		else
+			dobytes = count;
+
+		localbuf = state->buf;
+
+		for (k=0; k<dobytes; k++) {
+			i = (i + 1) & 0xff;
+			j = (j + S[i]) & 0xff;
+			swap_byte(&S[i], &S[j]);
+			*localbuf++ = S[(S[i] + S[j]) & 0xff];
+		}
+ 
+		if (copy_to_user(buf, state->buf, dobytes)) {
+			ret = -EFAULT;
+			goto out;
+		}
+
+		buf += dobytes;
+		count -= dobytes;
+	}
+
+ out:
+	state->i = i;     
+	state->j = j;
+
+	up(&state->sem);
+	return ret;
+}
+
+static struct file_operations frandom_fops = {
+	read:       frandom_read,
+	open:       frandom_open,
+	release:    frandom_release,
+};
+
+static void frandom_cleanup_module(void) {
+	kfree(erandom_state->buf);
+	kfree(erandom_state);
+ 
+	unregister_chrdev(frandom_major, "frandom");
+}
+
+
+static int frandom_init_module(void)
+{
+	int result;
+
+	/* The buffer size MUST be at least 256 bytes, because we assume that
+	   minimal length in init_rand_state().
+	*/       
+	if (frandom_bufsize < 256) {
+		printk(KERN_ERR "frandom: Refused to load because frandom_bufsize=%d < 256\n",frandom_bufsize);
+		return -EINVAL;
+	}
+	if ((frandom_chunklimit != 0) && (frandom_chunklimit < 256)) {
+		printk(KERN_ERR "frandom: Refused to load because frandom_chunklimit=%d < 256 and != 0\n",frandom_chunklimit);
+		return -EINVAL;
+	}
+
+	erandom_state = kmalloc(sizeof(struct frandom_state), GFP_KERNEL);
+	if (!erandom_state)
+		return -ENOMEM;
+
+	/* This specific buffer is only used for seeding, so we need
+	   256 bytes exactly */
+	erandom_state->buf = kmalloc(256, GFP_KERNEL);
+	if (!erandom_state->buf) {
+		kfree(erandom_state);
+		return -ENOMEM;
+	}
+
+	sema_init(&erandom_state->sem, 1); /* Init semaphore as a mutex */
+
+	erandom_seeded = 0;
+
+#ifdef SET_MODULE_OWNER
+	SET_MODULE_OWNER(&frandom_fops);
+#endif
+	/*
+	 * Register your major, and accept a dynamic number. This is the
+	 * first thing to do, in order to avoid releasing other module's
+	 * fops in frandom_cleanup_module()
+	 */
+	result = register_chrdev(frandom_major, "frandom", &frandom_fops);
+	if (result < 0) {
+		printk(KERN_WARNING "frandom: can't get major %d\n",frandom_major);
+
+		kfree(erandom_state->buf);
+		kfree(erandom_state);
+	
+		return result;
+	}
+	if (frandom_major == 0) frandom_major = result; /* dynamic */
+    
+	return 0; /* succeed */
+}
+
+module_init(frandom_init_module);
+module_exit(frandom_cleanup_module);
+
+EXPORT_SYMBOL(erandom_get_random_bytes);
diff -Naur linux-2.4.34.5.orig/drivers/char/random.c linux-2.4.34.5/drivers/char/random.c
--- linux-2.4.34.5.orig/drivers/char/random.c	2007-06-06 19:20:53.000000000 +0000
+++ linux-2.4.34.5/drivers/char/random.c	2007-06-06 23:44:39.000000000 +0000
@@ -1875,6 +1875,60 @@
 	return 1;
 }
 
+#ifdef CONFIG_FRANDOM
+/* We don't really want to create a header file for frandom
+   at this stage, so here's the prototype: */
+
+void erandom_get_random_bytes(char *buf, size_t count);
+
+static int proc_do_erandom(ctl_table *table, int write, struct file *filp,
+			void *buffer, size_t *lenp)
+{
+	ctl_table	fake_table;
+	unsigned char	buf[64], random[16], *p;
+	int i;
+	
+	erandom_get_random_bytes(random, 16);
+	
+	p=buf;
+	
+	for (i=0; i<16; i++) {
+     		sprintf(p, "%02x", random[i]);
+		p+=2;
+	}
+
+	fake_table.data = buf;
+	fake_table.maxlen = sizeof(buf);
+
+	return proc_dostring(&fake_table, write, filp, buffer, lenp);
+}
+
+
+static int erandom_strategy(ctl_table *table, int *name, int nlen,
+			 void *oldval, size_t *oldlenp,
+			 void *newval, size_t newlen, void **context)
+{
+	unsigned char	random[16];
+	unsigned int	len;
+
+	if (!oldval || !oldlenp)
+		return 1;
+
+	erandom_get_random_bytes(random, 16);
+
+	if (get_user(len, oldlenp))
+		return -EFAULT;
+	if (len) {
+		if (len > 16)
+			len = 16;
+		if (copy_to_user(oldval, random, len) ||
+		    put_user(len, oldlenp))
+			return -EFAULT;
+	}
+	return 1;
+}
+#endif
+
 ctl_table random_table[] = {
 	{RANDOM_POOLSIZE, "poolsize",
 	 &sysctl_poolsize, sizeof(int), 0644, NULL,
@@ -1893,6 +1947,11 @@
 	{RANDOM_BOOT_ID, "boot_id",
 	 &sysctl_bootid, 16, 0444, NULL,
 	 &proc_do_uuid, &uuid_strategy},
+#ifdef CONFIG_FRANDOM
+	{RANDOM_ERANDOM, "erandom",
+	 NULL, 16, 0444, NULL,
+	 &proc_do_erandom, &erandom_strategy},
+#endif
 	{RANDOM_UUID, "uuid",
 	 NULL, 16, 0444, NULL,
 	 &proc_do_uuid, &uuid_strategy},
diff -Naur linux-2.4.34.5.orig/include/linux/sysctl.h linux-2.4.34.5/include/linux/sysctl.h
--- linux-2.4.34.5.orig/include/linux/sysctl.h	2007-06-06 19:20:53.000000000 +0000
+++ linux-2.4.34.5/include/linux/sysctl.h	2007-06-06 23:44:39.000000000 +0000
@@ -193,7 +193,8 @@
 	RANDOM_READ_THRESH=3,
 	RANDOM_WRITE_THRESH=4,
 	RANDOM_BOOT_ID=5,
-	RANDOM_UUID=6
+	RANDOM_UUID=6,
+	RANDOM_ERANDOM=7
 };
 
 /* /proc/sys/bus/isa */
