Submitted By: Robert Connolly (ashes) Date: 2005-04-17 Initial Package Version: 2.4.30 Upstream Status: Not submitted Origin: http://frandom.sourceforge.net/ Description: This patch adds the he frandom, and erandom devices, and a sysctl interface for urandom. mknod /dev/frandom c 235 11 mknod /dev/erandom c 235 12 Also see: http://www.linuxfromscratch.org/hlfs/ http://www.linuxfromscratch.org/hints/downloads/files/entropy.txt diff -Naur linux-2.4.30.orig/Documentation/Configure.help linux-2.4.30/Documentation/Configure.help --- linux-2.4.30.orig/Documentation/Configure.help 2005-04-04 01:42:19.000000000 +0000 +++ linux-2.4.30/Documentation/Configure.help 2005-04-17 03:51:25.000000000 +0000 @@ -4642,6 +4642,13 @@ building a kernel for install/rescue disks or your system is very limited in memory. +Sysctl_Urandom support +CONFIG_SYSCTL_URANDOM + If you say Y here, hooks will be enabled to access get_random_bytes() + from sysctl kernel.random.urandom. This is a non-blocking interface. + The main advantage to this is to be able to access urandom through + a chroot. + # Choice: kcore Kernel core (/proc/kcore) format CONFIG_KCORE_ELF @@ -18491,6 +18498,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.30.orig/arch/i386/config.in linux-2.4.30/arch/i386/config.in --- linux-2.4.30.orig/arch/i386/config.in 2004-11-17 11:54:21.000000000 +0000 +++ linux-2.4.30/arch/i386/config.in 2005-04-17 03:57:58.000000000 +0000 @@ -320,6 +320,9 @@ bool 'System V IPC' CONFIG_SYSVIPC bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT bool 'Sysctl support' CONFIG_SYSCTL +if [ "$CONFIG_SYSCTL" = "y" ]; then + bool 'Sysctl Urandom support' CONFIG_SYSCTL_URANDOM +fi if [ "$CONFIG_PROC_FS" = "y" ]; then choice 'Kernel core (/proc/kcore) format' \ "ELF CONFIG_KCORE_ELF \ diff -Naur linux-2.4.30.orig/arch/i386/defconfig linux-2.4.30/arch/i386/defconfig --- linux-2.4.30.orig/arch/i386/defconfig 2005-01-19 14:09:25.000000000 +0000 +++ linux-2.4.30/arch/i386/defconfig 2005-04-17 03:52:21.000000000 +0000 @@ -113,6 +113,7 @@ CONFIG_SYSVIPC=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y +CONFIG_SYSCTL_URANDOM=y CONFIG_KCORE_ELF=y # CONFIG_KCORE_AOUT is not set CONFIG_BINFMT_AOUT=y @@ -580,6 +581,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.30.orig/drivers/char/Config.in linux-2.4.30/drivers/char/Config.in --- linux-2.4.30.orig/drivers/char/Config.in 2004-08-07 23:26:04.000000000 +0000 +++ linux-2.4.30/drivers/char/Config.in 2005-04-17 03:44:32.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.30.orig/drivers/char/Makefile linux-2.4.30/drivers/char/Makefile --- linux-2.4.30.orig/drivers/char/Makefile 2004-08-07 23:26:04.000000000 +0000 +++ linux-2.4.30/drivers/char/Makefile 2005-04-17 03:44:32.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.30.orig/drivers/char/frandom.c linux-2.4.30/drivers/char/frandom.c --- linux-2.4.30.orig/drivers/char/frandom.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.4.30/drivers/char/frandom.c 2005-04-17 03:44:33.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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#if (LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,0)) +#include +#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; ki = 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(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_CODEprivate_data; + + kfree(state->buf); + kfree(state); + +#if (LINUX_VERSION_CODEprivate_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; kbuf, 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.30.orig/drivers/char/random.c linux-2.4.30/drivers/char/random.c --- linux-2.4.30.orig/drivers/char/random.c 2005-01-19 14:09:48.000000000 +0000 +++ linux-2.4.30/drivers/char/random.c 2005-04-17 04:17:37.000000000 +0000 @@ -1875,6 +1875,107 @@ return 1; } +#ifdef CONFIG_SYSCTL_URANDOM +static int proc_do_urandom(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; + + 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 urandom_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; + + 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 + +#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 +1994,16 @@ {RANDOM_BOOT_ID, "boot_id", &sysctl_bootid, 16, 0444, NULL, &proc_do_uuid, &uuid_strategy}, +#ifdef CONFIG_SYSCTL_URANDOM + {RANDOM_URANDOM, "urandom", + NULL, 16, 0444, NULL, + &proc_do_urandom, &urandom_strategy}, +#endif +#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.30.orig/include/linux/sysctl.h linux-2.4.30/include/linux/sysctl.h --- linux-2.4.30.orig/include/linux/sysctl.h 2005-04-04 01:42:20.000000000 +0000 +++ linux-2.4.30/include/linux/sysctl.h 2005-04-17 04:14:48.000000000 +0000 @@ -185,6 +185,8 @@ }; /* /proc/sys/kernel/random */ +#define SYSCTL_URANDOM +#define SYSCTL_ERANDOM enum { RANDOM_POOLSIZE=1, @@ -192,7 +194,9 @@ RANDOM_READ_THRESH=3, RANDOM_WRITE_THRESH=4, RANDOM_BOOT_ID=5, - RANDOM_UUID=6 + RANDOM_UUID=6, + RANDOM_URANDOM=7, + RANDOM_ERANDOM=8 }; /* /proc/sys/bus/isa */