diff options
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | random/Makefile | 31 | ||||
-rw-r--r-- | random/TODO | 11 | ||||
-rw-r--r-- | random/gnupg-bithelp.h | 41 | ||||
-rw-r--r-- | random/gnupg-glue.h | 40 | ||||
-rw-r--r-- | random/gnupg-random.c | 810 | ||||
-rw-r--r-- | random/gnupg-random.h | 47 | ||||
-rw-r--r-- | random/gnupg-rmd.h | 38 | ||||
-rw-r--r-- | random/gnupg-rmd160.c | 656 | ||||
-rw-r--r-- | random/mig-decls.h | 47 | ||||
-rw-r--r-- | random/mig-mutate.h | 25 | ||||
-rw-r--r-- | random/random.c | 634 | ||||
-rw-r--r-- | random/random.h | 32 |
13 files changed, 2414 insertions, 1 deletions
@@ -37,7 +37,8 @@ prog-subdirs = auth proc exec init term \ login daemons boot console \ hostmux usermux ftpfs trans \ console-client utils sutils \ - benchmarks fstests + benchmarks fstests \ + random \ ifeq ($(HAVE_SUN_RPC),yes) prog-subdirs += nfs nfsd diff --git a/random/Makefile b/random/Makefile new file mode 100644 index 00000000..6291da0c --- /dev/null +++ b/random/Makefile @@ -0,0 +1,31 @@ +# +# Copyright (C) 1994,95,96,97,99,2000,2001 Free Software Foundation, Inc. +# +# 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, or (at +# your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +dir := random +makemode := server + +CFLAGS += -D__HURD__ + +target = random +SRCS = random.c gnupg-random.c gnupg-rmd160.c +OBJS = $(SRCS:.c=.o) startup_notifyServer.o +LCLHDRS = gnupg-random.h gnupg-rmd.h gnupg-bithelp.h random.h +HURDLIBS = trivfs ports fshelp ihash shouldbeinlibc +OTHERLIBS = -lpthread +MIGSFLAGS = -DSEQNOS -imacros $(srcdir)/mig-mutate.h + +include ../Makeconf diff --git a/random/TODO b/random/TODO new file mode 100644 index 00000000..9cc57ab9 --- /dev/null +++ b/random/TODO @@ -0,0 +1,11 @@ +* read_poll uses random_poll until the pool is filled. This is ian + issue at first initialization, as this requries POOLSIZE good random (level 1 from + gather_random) even in level 0 and 1. + For now, the code is only applied to level 2. Eventually, readable_pool + should be fixed to return 0 if initialization is not done yet and not enough bytes + are available. Otherwise it enters an infinite loop. + +* Permissions? + +* Off by one error in gather_random/io_write? I can only get GATHERBUFSIZE - 1 + bytes from it. diff --git a/random/gnupg-bithelp.h b/random/gnupg-bithelp.h new file mode 100644 index 00000000..188db168 --- /dev/null +++ b/random/gnupg-bithelp.h @@ -0,0 +1,41 @@ +/* bithelp.h - Some bit manipulation helpers + * Copyright (C) 1999 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#ifndef G10_BITHELP_H +#define G10_BITHELP_H + + +/**************** + * Rotate a 32 bit integer by n bytes + */ +#if defined(__GNUC__) && defined(__i386__) +static inline u32 +rol( u32 x, int n) +{ + __asm__("roll %%cl,%0" + :"=r" (x) + :"0" (x),"c" (n)); + return x; +} +#else + #define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) ) +#endif + + +#endif /*G10_BITHELP_H*/ diff --git a/random/gnupg-glue.h b/random/gnupg-glue.h new file mode 100644 index 00000000..cbf0a103 --- /dev/null +++ b/random/gnupg-glue.h @@ -0,0 +1,40 @@ +#ifndef __GNUPG_GLUE_H__ +#define __GNUPG_GLUE_H__ + +#include <sys/types.h> +#include <random.h> + +#define SIZEOF_UNSIGNED_LONG 4 +typedef unsigned int u32; +typedef unsigned char byte; + +/* GnuPG's config.h */ +#define HAVE_GETTIMEOFDAY 1 +#define HAVE_GETRUSAGE 1 +#define HAVE_RAND 1 + +/* GnuPG's memory.h */ +#define m_alloc malloc +#define m_alloc_secure malloc +#define m_alloc_clear(x) calloc(x, 1) +#define m_alloc_secure_clear(x) calloc(x, 1) +#define m_free free +#define m_strdup strdup + +/* GnuPG's dynaload.h */ +#define dynload_getfnc_fast_random_poll() (0) +#define dynload_getfnc_gather_random() &gather_random +int +gather_random( void (*add)(const void*, size_t, int), int requester, + size_t length, int level ); + +/* GnuPG's miscellaneous stuff. */ +#define BUG() assert(0) +#define _(x) x +#define make_timestamp() time(0) +#define tty_printf printf +#define log_info(format, args...) printf(format , ## args) +#define log_fatal(format, args...) { printf(format , ## args) ; exit(2); } +#define DIM(v) (sizeof(v)/sizeof((v)[0])) + +#endif /* __GNUPG_GLUE_H__ */ diff --git a/random/gnupg-random.c b/random/gnupg-random.c new file mode 100644 index 00000000..8f308621 --- /dev/null +++ b/random/gnupg-random.c @@ -0,0 +1,810 @@ +/* random.c - random number generator + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + + +/**************** + * This random number generator is modelled after the one described + * in Peter Gutmann's Paper: "Software Generation of Practically + * Strong Random Numbers". + */ + +#ifndef __HURD__ +#include <config.h> +#else +#include "gnupg-glue.h" +#endif +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#ifdef HAVE_GETHRTIME + #include <sys/times.h> +#endif +#ifdef HAVE_GETTIMEOFDAY + #include <sys/times.h> +#endif +#ifdef HAVE_GETRUSAGE + #include <sys/resource.h> +#endif +#ifdef __MINGW32__ + #include <process.h> +#endif +#ifndef __HURD__ +#include "util.h" +#endif +#ifndef __HURD__ +#include "rmd.h" +#include "ttyio.h" +#include "i18n.h" +#include "random.h" +#include "rand-internal.h" +#include "dynload.h" +#else +#include "gnupg-rmd.h" +#include "gnupg-random.h" +#endif + +#ifndef RAND_MAX /* for SunOS */ + #define RAND_MAX 32767 +#endif + + +#if SIZEOF_UNSIGNED_LONG == 8 + #define ADD_VALUE 0xa5a5a5a5a5a5a5a5 +#elif SIZEOF_UNSIGNED_LONG == 4 + #define ADD_VALUE 0xa5a5a5a5 +#else + #error weird size for an unsigned long +#endif + +#define BLOCKLEN 64 /* hash this amount of bytes */ +#define DIGESTLEN 20 /* into a digest of this length (rmd160) */ +/* poolblocks is the number of digests which make up the pool + * and poolsize must be a multiple of the digest length + * to make the AND operations faster, the size should also be + * a multiple of ulong + */ +#define POOLBLOCKS 30 +#define POOLSIZE (POOLBLOCKS*DIGESTLEN) +#if (POOLSIZE % SIZEOF_UNSIGNED_LONG) + #error Please make sure that poolsize is a multiple of ulong +#endif +#define POOLWORDS (POOLSIZE / SIZEOF_UNSIGNED_LONG) + + +static int is_initialized; +#define MASK_LEVEL(a) do {if( a > 2 ) a = 2; else if( a < 0 ) a = 0; } while(0) +static char *rndpool; /* allocated size is POOLSIZE+BLOCKLEN */ +static char *keypool; /* allocated size is POOLSIZE+BLOCKLEN */ +static size_t pool_readpos; +static size_t pool_writepos; +static int pool_filled; +static int pool_balance; +static int just_mixed; +static int did_initial_extra_seeding; +static char *seed_file_name; +static int allow_seed_file_update; + +static int secure_alloc; +static int quick_test; +static int faked_rng; + + +#ifndef __HURD__ +static void read_pool( byte *buffer, size_t length, int level ); +#else +int read_pool( byte *buffer, size_t length, int level ); +#endif +static void add_randomness( const void *buffer, size_t length, int source ); +static void random_poll(void); +#ifndef __HURD__ +static void read_random_source( int requester, size_t length, int level); +#else +static int read_random_source( int requester, size_t length, int level); +#endif +static int gather_faked( void (*add)(const void*, size_t, int), int requester, + size_t length, int level ); + +static struct { + ulong mixrnd; + ulong mixkey; + ulong slowpolls; + ulong fastpolls; + ulong getbytes1; + ulong ngetbytes1; + ulong getbytes2; + ulong ngetbytes2; + ulong addbytes; + ulong naddbytes; +} rndstats; + +static void +initialize(void) +{ + /* The data buffer is allocated somewhat larger, so that + * we can use this extra space (which is allocated in secure memory) + * as a temporary hash buffer */ + rndpool = secure_alloc ? m_alloc_secure_clear(POOLSIZE+BLOCKLEN) + : m_alloc_clear(POOLSIZE+BLOCKLEN); + keypool = secure_alloc ? m_alloc_secure_clear(POOLSIZE+BLOCKLEN) + : m_alloc_clear(POOLSIZE+BLOCKLEN); + is_initialized = 1; +#ifndef __HURD__ + cipher_modules_constructor(); +#endif +} + +static void +burn_stack (int bytes) +{ + char buf[128]; + + memset (buf, 0, sizeof buf); + bytes -= sizeof buf; + if (bytes > 0) + burn_stack (bytes); +} + +void +random_dump_stats() +{ + fprintf(stderr, + "random usage: poolsize=%d mixed=%lu polls=%lu/%lu added=%lu/%lu\n" + " outmix=%lu getlvl1=%lu/%lu getlvl2=%lu/%lu\n", + POOLSIZE, rndstats.mixrnd, rndstats.slowpolls, rndstats.fastpolls, + rndstats.naddbytes, rndstats.addbytes, + rndstats.mixkey, rndstats.ngetbytes1, rndstats.getbytes1, + rndstats.ngetbytes2, rndstats.getbytes2 ); +} + +void +secure_random_alloc() +{ + secure_alloc = 1; +} + + +int +quick_random_gen( int onoff ) +{ + int last; + + read_random_source(0,0,0); /* init */ + last = quick_test; + if( onoff != -1 ) + quick_test = onoff; + return faked_rng? 1 : last; +} + + +/**************** + * Fill the buffer with LENGTH bytes of cryptographically strong + * random bytes. level 0 is not very strong, 1 is strong enough + * for most usage, 2 is good for key generation stuff but may be very slow. + */ +void +randomize_buffer( byte *buffer, size_t length, int level ) +{ + char *p = get_random_bits( length*8, level, 1 ); + memcpy( buffer, p, length ); + m_free(p); +} + + +int +random_is_faked() +{ + if( !is_initialized ) + initialize(); + return faked_rng || quick_test; +} + +/**************** + * Return a pointer to a randomized buffer of level 0 and LENGTH bits + * caller must free the buffer. + * Note: The returned value is rounded up to bytes. + */ +byte * +get_random_bits( size_t nbits, int level, int secure ) +{ + byte *buf, *p; + size_t nbytes = (nbits+7)/8; + + if( quick_test && level > 1 ) + level = 1; + MASK_LEVEL(level); + if( level == 1 ) { + rndstats.getbytes1 += nbytes; + rndstats.ngetbytes1++; + } + else if( level >= 2 ) { + rndstats.getbytes2 += nbytes; + rndstats.ngetbytes2++; + } + + buf = secure && secure_alloc ? m_alloc_secure( nbytes ) : m_alloc( nbytes ); + for( p = buf; nbytes > 0; ) { + size_t n = nbytes > POOLSIZE? POOLSIZE : nbytes; +#ifdef __HURD__ + n = +#endif + read_pool( p, n, level ); + nbytes -= n; + p += n; + + } + return buf; +} + + +/**************** + * Mix the pool + */ +static void +mix_pool(byte *pool) +{ + char *hashbuf = pool + POOLSIZE; + char *p, *pend; + int i, n; + RMD160_CONTEXT md; + + rmd160_init( &md ); + #if DIGESTLEN != 20 + #error must have a digest length of 20 for ripe-md-160 + #endif + /* loop over the pool */ + pend = pool + POOLSIZE; + memcpy(hashbuf, pend - DIGESTLEN, DIGESTLEN ); + memcpy(hashbuf+DIGESTLEN, pool, BLOCKLEN-DIGESTLEN); + rmd160_mixblock( &md, hashbuf); + memcpy(pool, hashbuf, 20 ); + + p = pool; + for( n=1; n < POOLBLOCKS; n++ ) { + memcpy(hashbuf, p, DIGESTLEN ); + + p += DIGESTLEN; + if( p+DIGESTLEN+BLOCKLEN < pend ) + memcpy(hashbuf+DIGESTLEN, p+DIGESTLEN, BLOCKLEN-DIGESTLEN); + else { + char *pp = p+DIGESTLEN; + for(i=DIGESTLEN; i < BLOCKLEN; i++ ) { + if( pp >= pend ) + pp = pool; + hashbuf[i] = *pp++; + } + } + + rmd160_mixblock( &md, hashbuf); + memcpy(p, hashbuf, 20 ); + } + burn_stack (200); /* for the rmd160_mixblock() */ +} + + +void +set_random_seed_file( const char *name ) +{ + if( seed_file_name ) + BUG(); + seed_file_name = m_strdup( name ); +} + +/**************** + * Read in a seed form the random_seed file + * and return true if this was successful + */ +static int +read_seed_file() +{ + int fd; + struct stat sb; + unsigned char buffer[POOLSIZE]; + int n; + + if( !seed_file_name ) + return 0; + + #ifdef HAVE_DOSISH_SYSTEM + fd = open( seed_file_name, O_RDONLY | O_BINARY ); + #else + fd = open( seed_file_name, O_RDONLY ); + #endif + if( fd == -1 && errno == ENOENT) { + allow_seed_file_update = 1; + return 0; + } + + if( fd == -1 ) { + log_info(_("can't open `%s': %s\n"), seed_file_name, strerror(errno) ); + return 0; + } + if( fstat( fd, &sb ) ) { + log_info(_("can't stat `%s': %s\n"), seed_file_name, strerror(errno) ); + close(fd); + return 0; + } + if( !S_ISREG(sb.st_mode) ) { + log_info(_("`%s' is not a regular file - ignored\n"), seed_file_name ); + close(fd); + return 0; + } + if( !sb.st_size ) { + log_info(_("note: random_seed file is empty\n") ); + close(fd); + allow_seed_file_update = 1; + return 0; + } + if( sb.st_size != POOLSIZE ) { + log_info(_("warning: invalid size of random_seed file - not used\n") ); + close(fd); + return 0; + } + do { + n = read( fd, buffer, POOLSIZE ); + } while( n == -1 && errno == EINTR ); + if( n != POOLSIZE ) { + log_fatal(_("can't read `%s': %s\n"), seed_file_name,strerror(errno) ); + close(fd); + return 0; + } + + close(fd); + + add_randomness( buffer, POOLSIZE, 0 ); + /* add some minor entropy to the pool now (this will also force a mixing) */ + { pid_t x = getpid(); + add_randomness( &x, sizeof(x), 0 ); + } + { time_t x = time(NULL); + add_randomness( &x, sizeof(x), 0 ); + } + { clock_t x = clock(); + add_randomness( &x, sizeof(x), 0 ); + } + /* And read a few bytes from our entropy source. By using + * a level of 0 this will not block and might not return anything + * with some entropy drivers, however the rndlinux driver will use + * /dev/urandom and return some stuff - Do not read to much as we + * want to be friendly to the scare system entropy resource. */ + read_random_source( 0, 16, 0 ); + + allow_seed_file_update = 1; + return 1; +} + +void +update_random_seed_file() +{ + ulong *sp, *dp; + int fd, i; + + if( !seed_file_name || !is_initialized || !pool_filled ) + return; + if( !allow_seed_file_update ) { + log_info(_("note: random_seed file not updated\n")); + return; + } + + + /* copy the entropy pool to a scratch pool and mix both of them */ + for(i=0,dp=(ulong*)keypool, sp=(ulong*)rndpool; + i < POOLWORDS; i++, dp++, sp++ ) { + *dp = *sp + ADD_VALUE; + } + mix_pool(rndpool); rndstats.mixrnd++; + mix_pool(keypool); rndstats.mixkey++; + + #ifdef HAVE_DOSISH_SYSTEM + fd = open( seed_file_name, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, + S_IRUSR|S_IWUSR ); + #else + fd = open( seed_file_name, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR ); + #endif + if( fd == -1 ) { + log_info(_("can't create `%s': %s\n"), seed_file_name, strerror(errno) ); + return; + } + do { + i = write( fd, keypool, POOLSIZE ); + } while( i == -1 && errno == EINTR ); + if( i != POOLSIZE ) { + log_info(_("can't write `%s': %s\n"), seed_file_name, strerror(errno) ); + } + if( close(fd) ) + log_info(_("can't close `%s': %s\n"), seed_file_name, strerror(errno) ); +} + +#ifdef __HURD__ +int readable_pool( size_t length, int level ) +{ + size_t needed = 0; + size_t my_balance = pool_balance; + size_t available = (gatherwpos - gatherrpos + GATHERBUFSIZE) % GATHERBUFSIZE; + + if (length > POOLSIZE) + length = POOLSIZE; + + if (level < 2) + return length; + + if( !pool_filled ) { + if( read_seed_file() ) + pool_filled = 1; + } + + if (!did_initial_extra_seeding) + { + /* Take account for initial extra seeding. */ + needed = length; + if (needed < POOLSIZE/2) + needed = POOLSIZE/2; + my_balance = needed; + + if (!pool_filled && pool_writepos + needed < POOLSIZE) + { + /* If the pool is not filled yet, we couldn't read the seed + file. Too bad. We will now have to take account for so many + random_poll()s as fit into the remaining pool. */ + + needed += (POOLSIZE - pool_writepos + needed + POOLSIZE/5 - 1) / (POOLSIZE/5); + } + } + else + { + if (!pool_filled) + needed += (POOLSIZE - pool_writepos + needed + POOLSIZE/5 - 1) / (POOLSIZE/5); + } + + /* NEEDED contains the bytes needed for initialization, MY_BALANCE the resulting + available bytes. */ + if (available < needed) + return 0; + return available + my_balance - needed; +} +#endif + +#ifndef __HURD__ +static void +#else +int +#endif +read_pool( byte *buffer, size_t length, int level ) +{ + int i; + ulong *sp, *dp; + + if( length > POOLSIZE ) { +#ifndef __HURD__ + log_fatal(_("too many random bits requested; the limit is %d\n"), + POOLSIZE*8-1 ); +#else + length = POOLSIZE; +#endif + } + + if( !pool_filled ) { + if( read_seed_file() ) + pool_filled = 1; + } + + /* For level 2 quality (key generation) we alwas make + * sure that the pool has been seeded enough initially */ + if( level == 2 && !did_initial_extra_seeding ) { + size_t needed; + + pool_balance = 0; + needed = length - pool_balance; + if( needed < POOLSIZE/2 ) + needed = POOLSIZE/2; + else if( needed > POOLSIZE ) + BUG(); +#ifdef __HURD__ + needed = +#endif + read_random_source( 3, needed, 2 ); +#ifdef __HURD__ + if (! needed) + return 0; + /* XXX This will succeed with needed < POOLSIZE/2 even. But + erroring out will waste the random we already got. */ +#endif + pool_balance += needed; + did_initial_extra_seeding=1; + } + + /* for level 2 make sure that there is enough random in the pool */ + if( level == 2 && pool_balance < length ) { + size_t needed; + + if( pool_balance < 0 ) + pool_balance = 0; + needed = length - pool_balance; + if( needed > POOLSIZE ) + BUG(); +#ifdef __HURD__ + needed = +#endif + read_random_source( 3, needed, 2 ); + pool_balance += needed; + } + +#ifdef __HURD__ + /* XXX This makes level 0 and 1 worse than needed at first start up. */ + if (level == 2) +#endif + /* make sure the pool is filled */ + while( !pool_filled ) + random_poll(); + + /* do always a fast random poll */ + fast_random_poll(); + + if( !level ) { /* no need for cryptographic strong random */ + /* create a new pool */ + for(i=0,dp=(ulong*)keypool, sp=(ulong*)rndpool; + i < POOLWORDS; i++, dp++, sp++ ) + *dp = *sp + ADD_VALUE; + /* must mix both pools */ + mix_pool(rndpool); rndstats.mixrnd++; + mix_pool(keypool); rndstats.mixkey++; + memcpy( buffer, keypool, length ); + return length; + } + else { +#ifdef __HURD__ + int amount; +#endif + /* mix the pool (if add_randomness() didn't it) */ + if( !just_mixed ) { + mix_pool(rndpool); + rndstats.mixrnd++; + } + /* create a new pool */ + for(i=0,dp=(ulong*)keypool, sp=(ulong*)rndpool; + i < POOLWORDS; i++, dp++, sp++ ) + *dp = *sp + ADD_VALUE; + /* and mix both pools */ + mix_pool(rndpool); rndstats.mixrnd++; + mix_pool(keypool); rndstats.mixkey++; + /* read the required data + * we use a readpoiter to read from a different postion each + * time */ +#ifdef __HURD__ + if (level == 2 && length > pool_balance) + length = pool_balance; + amount = length; +#endif + while( length-- ) { + *buffer++ = keypool[pool_readpos++]; + if( pool_readpos >= POOLSIZE ) + pool_readpos = 0; + pool_balance--; + } + if( pool_balance < 0 ) + pool_balance = 0; + /* and clear the keypool */ + memset( keypool, 0, POOLSIZE ); +#ifdef __HURD__ + return amount; +#endif + } +} + + +/**************** + * Add LENGTH bytes of randomness from buffer to the pool. + * source may be used to specify the randomness source. + * Source is: + * 0 - used ony for initialization + * 1 - fast random poll function + * 2 - normal poll function + * 3 - used when level 2 random quality has been requested + * to do an extra pool seed. + */ +static void +add_randomness( const void *buffer, size_t length, int source ) +{ + const byte *p = buffer; + + if( !is_initialized ) + initialize(); + rndstats.addbytes += length; + rndstats.naddbytes++; + while( length-- ) { + rndpool[pool_writepos++] = *p++; + if( pool_writepos >= POOLSIZE ) { + if( source > 1 ) + pool_filled = 1; + pool_writepos = 0; + mix_pool(rndpool); rndstats.mixrnd++; + just_mixed = !length; + } + } +} + + + +static void +random_poll() +{ + rndstats.slowpolls++; + read_random_source( 2, POOLSIZE/5, 1 ); +} + + +void +fast_random_poll() +{ + static void (*fnc)( void (*)(const void*, size_t, int), int) = NULL; + static int initialized = 0; + + rndstats.fastpolls++; + if( !initialized ) { + if( !is_initialized ) + initialize(); + initialized = 1; + fnc = dynload_getfnc_fast_random_poll(); + } + if( fnc ) { + (*fnc)( add_randomness, 1 ); + return; + } + + /* fall back to the generic function */ + #if HAVE_GETHRTIME + { hrtime_t tv; + tv = gethrtime(); + add_randomness( &tv, sizeof(tv), 1 ); + } + #elif HAVE_GETTIMEOFDAY + { struct timeval tv; + if( gettimeofday( &tv, NULL ) ) + BUG(); + add_randomness( &tv.tv_sec, sizeof(tv.tv_sec), 1 ); + add_randomness( &tv.tv_usec, sizeof(tv.tv_usec), 1 ); + } + #elif HAVE_CLOCK_GETTIME + { struct timespec tv; + if( clock_gettime( CLOCK_REALTIME, &tv ) == -1 ) + BUG(); + add_randomness( &tv.tv_sec, sizeof(tv.tv_sec), 1 ); + add_randomness( &tv.tv_nsec, sizeof(tv.tv_nsec), 1 ); + } + #else /* use times */ + #ifndef HAVE_DOSISH_SYSTEM + { struct tms buf; + times( &buf ); + add_randomness( &buf, sizeof buf, 1 ); + } + #endif + #endif + #ifdef HAVE_GETRUSAGE + #ifndef RUSAGE_SELF + #ifdef __GCC__ + #warning There is no RUSAGE_SELF on this system + #endif + #else + { struct rusage buf; + /* QNX/Neutrino does return ENOSYS - so we just ignore it and + * add whatever is in buf. In a chroot environment it might not + * work at all (i.e. because /proc/ is not accessible), so we better + * ognore all error codes and hope for the best + */ + getrusage( RUSAGE_SELF, &buf ); + + add_randomness( &buf, sizeof buf, 1 ); + memset( &buf, 0, sizeof buf ); + } + #endif + #endif + /* time and clock are availabe on all systems - so + * we better do it just in case one of the above functions + * didn't work */ + { time_t x = time(NULL); + add_randomness( &x, sizeof(x), 1 ); + } + { clock_t x = clock(); + add_randomness( &x, sizeof(x), 1 ); + } +} + + +#ifndef __HURD__ +static void +#else +static int +#endif +read_random_source( int requester, size_t length, int level ) +{ + static int (*fnc)(void (*)(const void*, size_t, int), int, + size_t, int) = NULL; +#ifdef __HURD__ + int got; +#endif + if( !fnc ) { + if( !is_initialized ) + initialize(); + fnc = dynload_getfnc_gather_random(); + if( !fnc ) { + faked_rng = 1; + fnc = gather_faked; + } + if( !requester && !length && !level ) +#ifndef __HURD__ + return; /* init only */ +#else + return 0; +#endif + } +#ifndef __HURD__ + if( (*fnc)( add_randomness, requester, length, level ) < 0 ) + log_fatal("No way to gather entropy for the RNG\n"); +#else + got = (*fnc)( add_randomness, requester, length, level ); + if (got < 0) + log_fatal("No way to gather entropy for the RNG\n"); + return got; +#endif +} + + +static int +gather_faked( void (*add)(const void*, size_t, int), int requester, + size_t length, int level ) +{ + static int initialized=0; + size_t n; + char *buffer, *p; + + if( !initialized ) { + log_info(_("WARNING: using insecure random number generator!!\n")); + tty_printf(_("The random number generator is only a kludge to let\n" + "it run - it is in no way a strong RNG!\n\n" + "DON'T USE ANY DATA GENERATED BY THIS PROGRAM!!\n\n")); + initialized=1; + #ifdef HAVE_RAND + srand(make_timestamp()*getpid()); + #else + srandom(make_timestamp()*getpid()); + #endif + } + printf("WAITING FOR %i bytes.\n", length); + p = buffer = m_alloc( length ); + n = length; + #ifdef HAVE_RAND + while( n-- ) + *p++ = ((unsigned)(1 + (int) (256.0*rand()/(RAND_MAX+1.0)))-1); + #else + while( n-- ) + *p++ = ((unsigned)(1 + (int) (256.0*random()/(RAND_MAX+1.0)))-1); + #endif + add_randomness( buffer, length, requester ); + m_free(buffer); + return 0; /* okay */ +} + diff --git a/random/gnupg-random.h b/random/gnupg-random.h new file mode 100644 index 00000000..ee18febc --- /dev/null +++ b/random/gnupg-random.h @@ -0,0 +1,47 @@ +/* random.h - random functions + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#ifndef G10_RANDOM_H +#define G10_RANDOM_H + +#ifndef __HURD__ +#include "types.h" +#else +#include "gnupg-glue.h" +int read_pool (byte *, size_t, int); +int readable_pool (size_t, int); +#endif + +/*-- random.c --*/ +void random_dump_stats(void); +void secure_random_alloc(void); +void set_random_seed_file(const char *); +void update_random_seed_file(void); +int quick_random_gen( int onoff ); +int random_is_faked(void); +void randomize_buffer( byte *buffer, size_t length, int level ); +byte *get_random_bits( size_t nbits, int level, int secure ); +void fast_random_poll( void ); + +/*-- rndw32.c --*/ +#ifdef USE_STATIC_RNDW32 +void rndw32_set_dll_name( const char *name ); +#endif + +#endif /*G10_RANDOM_H*/ diff --git a/random/gnupg-rmd.h b/random/gnupg-rmd.h new file mode 100644 index 00000000..2446fc7d --- /dev/null +++ b/random/gnupg-rmd.h @@ -0,0 +1,38 @@ +/* rmd.h - RIPE-MD hash functions + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#ifndef G10_RMD_H +#define G10_RMD_H + +#ifdef __HURD__ +#include "gnupg-glue.h" +#endif + +/* we need this here because random.c must have direct access */ +typedef struct { + u32 h0,h1,h2,h3,h4; + u32 nblocks; + byte buf[64]; + int count; +} RMD160_CONTEXT; + +void rmd160_init( RMD160_CONTEXT *hd ); +void rmd160_mixblock( RMD160_CONTEXT *hd, char *buffer ); + +#endif /*G10_RMD_H*/ diff --git a/random/gnupg-rmd160.c b/random/gnupg-rmd160.c new file mode 100644 index 00000000..66107685 --- /dev/null +++ b/random/gnupg-rmd160.c @@ -0,0 +1,656 @@ +/* rmd160.c - RIPE-MD160 + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG 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. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef __HURD__ +#include <config.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#ifndef __HURD__ +#include "util.h" +#include "memory.h" +#include "rmd.h" +#include "cipher.h" /* only used for the rmd160_hash_buffer() prototype */ +#include "dynload.h" + +#include "bithelp.h" +#else +#include "gnupg-rmd.h" +#include "gnupg-bithelp.h" +#endif + + +/********************************* + * RIPEMD-160 is not patented, see (as of 25.10.97) + * http://www.esat.kuleuven.ac.be/~bosselae/ripemd160.html + * Note that the code uses Little Endian byteorder, which is good for + * 386 etc, but we must add some conversion when used on a big endian box. + * + * + * Pseudo-code for RIPEMD-160 + * + * RIPEMD-160 is an iterative hash function that operates on 32-bit words. + * The round function takes as input a 5-word chaining variable and a 16-word + * message block and maps this to a new chaining variable. All operations are + * defined on 32-bit words. Padding is identical to that of MD4. + * + * + * RIPEMD-160: definitions + * + * + * nonlinear functions at bit level: exor, mux, -, mux, - + * + * f(j, x, y, z) = x XOR y XOR z (0 <= j <= 15) + * f(j, x, y, z) = (x AND y) OR (NOT(x) AND z) (16 <= j <= 31) + * f(j, x, y, z) = (x OR NOT(y)) XOR z (32 <= j <= 47) + * f(j, x, y, z) = (x AND z) OR (y AND NOT(z)) (48 <= j <= 63) + * f(j, x, y, z) = x XOR (y OR NOT(z)) (64 <= j <= 79) + * + * + * added constants (hexadecimal) + * + * K(j) = 0x00000000 (0 <= j <= 15) + * K(j) = 0x5A827999 (16 <= j <= 31) int(2**30 x sqrt(2)) + * K(j) = 0x6ED9EBA1 (32 <= j <= 47) int(2**30 x sqrt(3)) + * K(j) = 0x8F1BBCDC (48 <= j <= 63) int(2**30 x sqrt(5)) + * K(j) = 0xA953FD4E (64 <= j <= 79) int(2**30 x sqrt(7)) + * K'(j) = 0x50A28BE6 (0 <= j <= 15) int(2**30 x cbrt(2)) + * K'(j) = 0x5C4DD124 (16 <= j <= 31) int(2**30 x cbrt(3)) + * K'(j) = 0x6D703EF3 (32 <= j <= 47) int(2**30 x cbrt(5)) + * K'(j) = 0x7A6D76E9 (48 <= j <= 63) int(2**30 x cbrt(7)) + * K'(j) = 0x00000000 (64 <= j <= 79) + * + * + * selection of message word + * + * r(j) = j (0 <= j <= 15) + * r(16..31) = 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8 + * r(32..47) = 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12 + * r(48..63) = 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2 + * r(64..79) = 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 + * r0(0..15) = 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12 + * r0(16..31)= 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2 + * r0(32..47)= 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13 + * r0(48..63)= 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14 + * r0(64..79)= 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 + * + * + * amount for rotate left (rol) + * + * s(0..15) = 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8 + * s(16..31) = 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12 + * s(32..47) = 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5 + * s(48..63) = 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12 + * s(64..79) = 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 + * s'(0..15) = 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6 + * s'(16..31)= 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11 + * s'(32..47)= 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5 + * s'(48..63)= 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8 + * s'(64..79)= 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 + * + * + * initial value (hexadecimal) + * + * h0 = 0x67452301; h1 = 0xEFCDAB89; h2 = 0x98BADCFE; h3 = 0x10325476; + * h4 = 0xC3D2E1F0; + * + * + * RIPEMD-160: pseudo-code + * + * It is assumed that the message after padding consists of t 16-word blocks + * that will be denoted with X[i][j], with 0 <= i <= t-1 and 0 <= j <= 15. + * The symbol [+] denotes addition modulo 2**32 and rol_s denotes cyclic left + * shift (rotate) over s positions. + * + * + * for i := 0 to t-1 { + * A := h0; B := h1; C := h2; D = h3; E = h4; + * A' := h0; B' := h1; C' := h2; D' = h3; E' = h4; + * for j := 0 to 79 { + * T := rol_s(j)(A [+] f(j, B, C, D) [+] X[i][r(j)] [+] K(j)) [+] E; + * A := E; E := D; D := rol_10(C); C := B; B := T; + * T := rol_s'(j)(A' [+] f(79-j, B', C', D') [+] X[i][r'(j)] + [+] K'(j)) [+] E'; + * A' := E'; E' := D'; D' := rol_10(C'); C' := B'; B' := T; + * } + * T := h1 [+] C [+] D'; h1 := h2 [+] D [+] E'; h2 := h3 [+] E [+] A'; + * h3 := h4 [+] A [+] B'; h4 := h0 [+] B [+] C'; h0 := T; + * } + */ + +/* Some examples: + * "" 9c1185a5c5e9fc54612808977ee8f548b2258d31 + * "a" 0bdc9d2d256b3ee9daae347be6f4dc835a467ffe + * "abc" 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc + * "message digest" 5d0689ef49d2fae572b881b123a85ffa21595f36 + * "a...z" f71c27109c692c1b56bbdceb5b9d2865b3708dbc + * "abcdbcde...nopq" 12a053384a9c0c88e405a06c27dcf49ada62eb2b + * "A...Za...z0...9" b0e20b6e3116640286ed3a87a5713079b21f5189 + * 8 times "1234567890" 9b752e45573d4b39f4dbd3323cab82bf63326bfb + * 1 million times "a" 52783243c1697bdbe16d37f97f68f08325dc1528 + */ + +static void +burn_stack (int bytes) +{ + char buf[150]; + + memset (buf, 0, sizeof buf); + bytes -= sizeof buf; + if (bytes > 0) + burn_stack (bytes); +} + + + +void +rmd160_init( RMD160_CONTEXT *hd ) +{ + hd->h0 = 0x67452301; + hd->h1 = 0xEFCDAB89; + hd->h2 = 0x98BADCFE; + hd->h3 = 0x10325476; + hd->h4 = 0xC3D2E1F0; + hd->nblocks = 0; + hd->count = 0; +} + + + +/**************** + * Transform the message X which consists of 16 32-bit-words + */ +static void +transform( RMD160_CONTEXT *hd, byte *data ) +{ + u32 a,b,c,d,e,aa,bb,cc,dd,ee,t; + #ifdef BIG_ENDIAN_HOST + u32 x[16]; + { int i; + byte *p2, *p1; + for(i=0, p1=data, p2=(byte*)x; i < 16; i++, p2 += 4 ) { + p2[3] = *p1++; + p2[2] = *p1++; + p2[1] = *p1++; + p2[0] = *p1++; + } + } + #else + #if 0 + u32 *x =(u32*)data; + #else + /* this version is better because it is always aligned; + * The performance penalty on a 586-100 is about 6% which + * is acceptable - because the data is more local it might + * also be possible that this is faster on some machines. + * This function (when compiled with -02 on gcc 2.7.2) + * executes on a 586-100 (39.73 bogomips) at about 1900kb/sec; + * [measured with a 4MB data and "gpgm --print-md rmd160"] */ + u32 x[16]; + memcpy( x, data, 64 ); + #endif + #endif + + +#define K0 0x00000000 +#define K1 0x5A827999 +#define K2 0x6ED9EBA1 +#define K3 0x8F1BBCDC +#define K4 0xA953FD4E +#define KK0 0x50A28BE6 +#define KK1 0x5C4DD124 +#define KK2 0x6D703EF3 +#define KK3 0x7A6D76E9 +#define KK4 0x00000000 +#define F0(x,y,z) ( (x) ^ (y) ^ (z) ) +#define F1(x,y,z) ( ((x) & (y)) | (~(x) & (z)) ) +#define F2(x,y,z) ( ((x) | ~(y)) ^ (z) ) +#define F3(x,y,z) ( ((x) & (z)) | ((y) & ~(z)) ) +#define F4(x,y,z) ( (x) ^ ((y) | ~(z)) ) +#define R(a,b,c,d,e,f,k,r,s) do { t = a + f(b,c,d) + k + x[r]; \ + a = rol(t,s) + e; \ + c = rol(c,10); \ + } while(0) + + /* left lane */ + a = hd->h0; + b = hd->h1; + c = hd->h2; + d = hd->h3; + e = hd->h4; + R( a, b, c, d, e, F0, K0, 0, 11 ); + R( e, a, b, c, d, F0, K0, 1, 14 ); + R( d, e, a, b, c, F0, K0, 2, 15 ); + R( c, d, e, a, b, F0, K0, 3, 12 ); + R( b, c, d, e, a, F0, K0, 4, 5 ); + R( a, b, c, d, e, F0, K0, 5, 8 ); + R( e, a, b, c, d, F0, K0, 6, 7 ); + R( d, e, a, b, c, F0, K0, 7, 9 ); + R( c, d, e, a, b, F0, K0, 8, 11 ); + R( b, c, d, e, a, F0, K0, 9, 13 ); + R( a, b, c, d, e, F0, K0, 10, 14 ); + R( e, a, b, c, d, F0, K0, 11, 15 ); + R( d, e, a, b, c, F0, K0, 12, 6 ); + R( c, d, e, a, b, F0, K0, 13, 7 ); + R( b, c, d, e, a, F0, K0, 14, 9 ); + R( a, b, c, d, e, F0, K0, 15, 8 ); + R( e, a, b, c, d, F1, K1, 7, 7 ); + R( d, e, a, b, c, F1, K1, 4, 6 ); + R( c, d, e, a, b, F1, K1, 13, 8 ); + R( b, c, d, e, a, F1, K1, 1, 13 ); + R( a, b, c, d, e, F1, K1, 10, 11 ); + R( e, a, b, c, d, F1, K1, 6, 9 ); + R( d, e, a, b, c, F1, K1, 15, 7 ); + R( c, d, e, a, b, F1, K1, 3, 15 ); + R( b, c, d, e, a, F1, K1, 12, 7 ); + R( a, b, c, d, e, F1, K1, 0, 12 ); + R( e, a, b, c, d, F1, K1, 9, 15 ); + R( d, e, a, b, c, F1, K1, 5, 9 ); + R( c, d, e, a, b, F1, K1, 2, 11 ); + R( b, c, d, e, a, F1, K1, 14, 7 ); + R( a, b, c, d, e, F1, K1, 11, 13 ); + R( e, a, b, c, d, F1, K1, 8, 12 ); + R( d, e, a, b, c, F2, K2, 3, 11 ); + R( c, d, e, a, b, F2, K2, 10, 13 ); + R( b, c, d, e, a, F2, K2, 14, 6 ); + R( a, b, c, d, e, F2, K2, 4, 7 ); + R( e, a, b, c, d, F2, K2, 9, 14 ); + R( d, e, a, b, c, F2, K2, 15, 9 ); + R( c, d, e, a, b, F2, K2, 8, 13 ); + R( b, c, d, e, a, F2, K2, 1, 15 ); + R( a, b, c, d, e, F2, K2, 2, 14 ); + R( e, a, b, c, d, F2, K2, 7, 8 ); + R( d, e, a, b, c, F2, K2, 0, 13 ); + R( c, d, e, a, b, F2, K2, 6, 6 ); + R( b, c, d, e, a, F2, K2, 13, 5 ); + R( a, b, c, d, e, F2, K2, 11, 12 ); + R( e, a, b, c, d, F2, K2, 5, 7 ); + R( d, e, a, b, c, F2, K2, 12, 5 ); + R( c, d, e, a, b, F3, K3, 1, 11 ); + R( b, c, d, e, a, F3, K3, 9, 12 ); + R( a, b, c, d, e, F3, K3, 11, 14 ); + R( e, a, b, c, d, F3, K3, 10, 15 ); + R( d, e, a, b, c, F3, K3, 0, 14 ); + R( c, d, e, a, b, F3, K3, 8, 15 ); + R( b, c, d, e, a, F3, K3, 12, 9 ); + R( a, b, c, d, e, F3, K3, 4, 8 ); + R( e, a, b, c, d, F3, K3, 13, 9 ); + R( d, e, a, b, c, F3, K3, 3, 14 ); + R( c, d, e, a, b, F3, K3, 7, 5 ); + R( b, c, d, e, a, F3, K3, 15, 6 ); + R( a, b, c, d, e, F3, K3, 14, 8 ); + R( e, a, b, c, d, F3, K3, 5, 6 ); + R( d, e, a, b, c, F3, K3, 6, 5 ); + R( c, d, e, a, b, F3, K3, 2, 12 ); + R( b, c, d, e, a, F4, K4, 4, 9 ); + R( a, b, c, d, e, F4, K4, 0, 15 ); + R( e, a, b, c, d, F4, K4, 5, 5 ); + R( d, e, a, b, c, F4, K4, 9, 11 ); + R( c, d, e, a, b, F4, K4, 7, 6 ); + R( b, c, d, e, a, F4, K4, 12, 8 ); + R( a, b, c, d, e, F4, K4, 2, 13 ); + R( e, a, b, c, d, F4, K4, 10, 12 ); + R( d, e, a, b, c, F4, K4, 14, 5 ); + R( c, d, e, a, b, F4, K4, 1, 12 ); + R( b, c, d, e, a, F4, K4, 3, 13 ); + R( a, b, c, d, e, F4, K4, 8, 14 ); + R( e, a, b, c, d, F4, K4, 11, 11 ); + R( d, e, a, b, c, F4, K4, 6, 8 ); + R( c, d, e, a, b, F4, K4, 15, 5 ); + R( b, c, d, e, a, F4, K4, 13, 6 ); + + aa = a; bb = b; cc = c; dd = d; ee = e; + + /* right lane */ + a = hd->h0; + b = hd->h1; + c = hd->h2; + d = hd->h3; + e = hd->h4; + R( a, b, c, d, e, F4, KK0, 5, 8); + R( e, a, b, c, d, F4, KK0, 14, 9); + R( d, e, a, b, c, F4, KK0, 7, 9); + R( c, d, e, a, b, F4, KK0, 0, 11); + R( b, c, d, e, a, F4, KK0, 9, 13); + R( a, b, c, d, e, F4, KK0, 2, 15); + R( e, a, b, c, d, F4, KK0, 11, 15); + R( d, e, a, b, c, F4, KK0, 4, 5); + R( c, d, e, a, b, F4, KK0, 13, 7); + R( b, c, d, e, a, F4, KK0, 6, 7); + R( a, b, c, d, e, F4, KK0, 15, 8); + R( e, a, b, c, d, F4, KK0, 8, 11); + R( d, e, a, b, c, F4, KK0, 1, 14); + R( c, d, e, a, b, F4, KK0, 10, 14); + R( b, c, d, e, a, F4, KK0, 3, 12); + R( a, b, c, d, e, F4, KK0, 12, 6); + R( e, a, b, c, d, F3, KK1, 6, 9); + R( d, e, a, b, c, F3, KK1, 11, 13); + R( c, d, e, a, b, F3, KK1, 3, 15); + R( b, c, d, e, a, F3, KK1, 7, 7); + R( a, b, c, d, e, F3, KK1, 0, 12); + R( e, a, b, c, d, F3, KK1, 13, 8); + R( d, e, a, b, c, F3, KK1, 5, 9); + R( c, d, e, a, b, F3, KK1, 10, 11); + R( b, c, d, e, a, F3, KK1, 14, 7); + R( a, b, c, d, e, F3, KK1, 15, 7); + R( e, a, b, c, d, F3, KK1, 8, 12); + R( d, e, a, b, c, F3, KK1, 12, 7); + R( c, d, e, a, b, F3, KK1, 4, 6); + R( b, c, d, e, a, F3, KK1, 9, 15); + R( a, b, c, d, e, F3, KK1, 1, 13); + R( e, a, b, c, d, F3, KK1, 2, 11); + R( d, e, a, b, c, F2, KK2, 15, 9); + R( c, d, e, a, b, F2, KK2, 5, 7); + R( b, c, d, e, a, F2, KK2, 1, 15); + R( a, b, c, d, e, F2, KK2, 3, 11); + R( e, a, b, c, d, F2, KK2, 7, 8); + R( d, e, a, b, c, F2, KK2, 14, 6); + R( c, d, e, a, b, F2, KK2, 6, 6); + R( b, c, d, e, a, F2, KK2, 9, 14); + R( a, b, c, d, e, F2, KK2, 11, 12); + R( e, a, b, c, d, F2, KK2, 8, 13); + R( d, e, a, b, c, F2, KK2, 12, 5); + R( c, d, e, a, b, F2, KK2, 2, 14); + R( b, c, d, e, a, F2, KK2, 10, 13); + R( a, b, c, d, e, F2, KK2, 0, 13); + R( e, a, b, c, d, F2, KK2, 4, 7); + R( d, e, a, b, c, F2, KK2, 13, 5); + R( c, d, e, a, b, F1, KK3, 8, 15); + R( b, c, d, e, a, F1, KK3, 6, 5); + R( a, b, c, d, e, F1, KK3, 4, 8); + R( e, a, b, c, d, F1, KK3, 1, 11); + R( d, e, a, b, c, F1, KK3, 3, 14); + R( c, d, e, a, b, F1, KK3, 11, 14); + R( b, c, d, e, a, F1, KK3, 15, 6); + R( a, b, c, d, e, F1, KK3, 0, 14); + R( e, a, b, c, d, F1, KK3, 5, 6); + R( d, e, a, b, c, F1, KK3, 12, 9); + R( c, d, e, a, b, F1, KK3, 2, 12); + R( b, c, d, e, a, F1, KK3, 13, 9); + R( a, b, c, d, e, F1, KK3, 9, 12); + R( e, a, b, c, d, F1, KK3, 7, 5); + R( d, e, a, b, c, F1, KK3, 10, 15); + R( c, d, e, a, b, F1, KK3, 14, 8); + R( b, c, d, e, a, F0, KK4, 12, 8); + R( a, b, c, d, e, F0, KK4, 15, 5); + R( e, a, b, c, d, F0, KK4, 10, 12); + R( d, e, a, b, c, F0, KK4, 4, 9); + R( c, d, e, a, b, F0, KK4, 1, 12); + R( b, c, d, e, a, F0, KK4, 5, 5); + R( a, b, c, d, e, F0, KK4, 8, 14); + R( e, a, b, c, d, F0, KK4, 7, 6); + R( d, e, a, b, c, F0, KK4, 6, 8); + R( c, d, e, a, b, F0, KK4, 2, 13); + R( b, c, d, e, a, F0, KK4, 13, 6); + R( a, b, c, d, e, F0, KK4, 14, 5); + R( e, a, b, c, d, F0, KK4, 0, 15); + R( d, e, a, b, c, F0, KK4, 3, 13); + R( c, d, e, a, b, F0, KK4, 9, 11); + R( b, c, d, e, a, F0, KK4, 11, 11); + + + t = hd->h1 + d + cc; + hd->h1 = hd->h2 + e + dd; + hd->h2 = hd->h3 + a + ee; + hd->h3 = hd->h4 + b + aa; + hd->h4 = hd->h0 + c + bb; + hd->h0 = t; +} + + +/* Update the message digest with the contents + * of INBUF with length INLEN. + */ +static void +rmd160_write( RMD160_CONTEXT *hd, byte *inbuf, size_t inlen) +{ + if( hd->count == 64 ) { /* flush the buffer */ + transform( hd, hd->buf ); + burn_stack (108+5*sizeof(void*)); + hd->count = 0; + hd->nblocks++; + } + if( !inbuf ) + return; + if( hd->count ) { + for( ; inlen && hd->count < 64; inlen-- ) + hd->buf[hd->count++] = *inbuf++; + rmd160_write( hd, NULL, 0 ); + if( !inlen ) + return; + } + + while( inlen >= 64 ) { + transform( hd, inbuf ); + hd->count = 0; + hd->nblocks++; + inlen -= 64; + inbuf += 64; + } + burn_stack (108+5*sizeof(void*)); + for( ; inlen && hd->count < 64; inlen-- ) + hd->buf[hd->count++] = *inbuf++; +} + +/**************** + * Apply the rmd160 transform function on the buffer which must have + * a length 64 bytes. Do not use this function together with the + * other functions, use rmd160_init to initialize internal variables. + * Returns: 16 bytes in buffer with the mixed contentes of buffer. + */ +void +rmd160_mixblock( RMD160_CONTEXT *hd, char *buffer ) +{ + char *p = buffer; + transform( hd, buffer ); + #define X(a) do { *(u32*)p = hd->h##a ; p += 4; } while(0) + X(0); + X(1); + X(2); + X(3); + X(4); + #undef X +} + + +/* The routine terminates the computation + */ + +static void +rmd160_final( RMD160_CONTEXT *hd ) +{ + u32 t, msb, lsb; + byte *p; + + rmd160_write(hd, NULL, 0); /* flush */; + + t = hd->nblocks; + /* multiply by 64 to make a byte count */ + lsb = t << 6; + msb = t >> 26; + /* add the count */ + t = lsb; + if( (lsb += hd->count) < t ) + msb++; + /* multiply by 8 to make a bit count */ + t = lsb; + lsb <<= 3; + msb <<= 3; + msb |= t >> 29; + + if( hd->count < 56 ) { /* enough room */ + hd->buf[hd->count++] = 0x80; /* pad */ + while( hd->count < 56 ) + hd->buf[hd->count++] = 0; /* pad */ + } + else { /* need one extra block */ + hd->buf[hd->count++] = 0x80; /* pad character */ + while( hd->count < 64 ) + hd->buf[hd->count++] = 0; + rmd160_write(hd, NULL, 0); /* flush */; + memset(hd->buf, 0, 56 ); /* fill next block with zeroes */ + } + /* append the 64 bit count */ + hd->buf[56] = lsb ; + hd->buf[57] = lsb >> 8; + hd->buf[58] = lsb >> 16; + hd->buf[59] = lsb >> 24; + hd->buf[60] = msb ; + hd->buf[61] = msb >> 8; + hd->buf[62] = msb >> 16; + hd->buf[63] = msb >> 24; + transform( hd, hd->buf ); + burn_stack (108+5*sizeof(void*)); + + p = hd->buf; + #ifdef BIG_ENDIAN_HOST + #define X(a) do { *p++ = hd->h##a ; *p++ = hd->h##a >> 8; \ + *p++ = hd->h##a >> 16; *p++ = hd->h##a >> 24; } while(0) + #else /* little endian */ + #define X(a) do { *(u32*)p = hd->h##a ; p += 4; } while(0) + #endif + X(0); + X(1); + X(2); + X(3); + X(4); + #undef X +} + +static byte * +rmd160_read( RMD160_CONTEXT *hd ) +{ + return hd->buf; +} + + + +/**************** + * Shortcut functions which puts the hash value of the supplied buffer + * into outbuf which must have a size of 20 bytes. + */ +void +rmd160_hash_buffer( char *outbuf, const char *buffer, size_t length ) +{ + RMD160_CONTEXT hd; + + rmd160_init( &hd ); + rmd160_write( &hd, (byte*)buffer, length ); + rmd160_final( &hd ); + memcpy( outbuf, hd.buf, 20 ); +} + + +/**************** + * Return some information about the algorithm. We need algo here to + * distinguish different flavors of the algorithm. + * Returns: A pointer to string describing the algorithm or NULL if + * the ALGO is invalid. + */ +static const char * +rmd160_get_info( int algo, size_t *contextsize, + byte **r_asnoid, int *r_asnlen, int *r_mdlen, + void (**r_init)( void *c ), + void (**r_write)( void *c, byte *buf, size_t nbytes ), + void (**r_final)( void *c ), + byte *(**r_read)( void *c ) + ) +{ + static byte asn[15] = /* Object ID is 1.3.36.3.2.1 */ + { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, + 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; + + if( algo != 3 ) + return NULL; + + *contextsize = sizeof(RMD160_CONTEXT); + *r_asnoid = asn; + *r_asnlen = DIM(asn); + *r_mdlen = 20; + *(void (**)(RMD160_CONTEXT *))r_init = rmd160_init; + *(void (**)(RMD160_CONTEXT *, byte*, size_t))r_write = rmd160_write; + *(void (**)(RMD160_CONTEXT *))r_final = rmd160_final; + *(byte *(**)(RMD160_CONTEXT *))r_read = rmd160_read; + + return "RIPEMD160"; +} + + +#ifndef IS_MODULE +static +#endif +const char * const gnupgext_version = "RMD160 ($Revision: 1.17.2.4 $)"; + +static struct { + int class; + int version; + int value; + void (*func)(void); +} func_table[] = { + { 10, 1, 0, (void(*)(void))rmd160_get_info }, + { 11, 1, 3 }, +}; + + +#ifndef IS_MODULE +static +#endif +void * +gnupgext_enum_func( int what, int *sequence, int *class, int *vers ) +{ + void *ret; + int i = *sequence; + + do { + if( i >= DIM(func_table) || i < 0 ) { + return NULL; + } + *class = func_table[i].class; + *vers = func_table[i].version; + switch( *class ) { + case 11: + case 21: + case 31: + ret = &func_table[i].value; + break; + default: + ret = func_table[i].func; + break; + } + i++; + } while( what && what != *class ); + + *sequence = i; + return ret; +} + + + +#ifndef __HURD__ +#ifndef IS_MODULE +void +rmd160_constructor(void) +{ + register_internal_cipher_extension( gnupgext_version, gnupgext_enum_func ); +} +#endif +#endif diff --git a/random/mig-decls.h b/random/mig-decls.h new file mode 100644 index 00000000..87b7eb2a --- /dev/null +++ b/random/mig-decls.h @@ -0,0 +1,47 @@ +/* + Copyright (C) 2014 Free Software Foundation, Inc. + Written by Justus Winter. + + This file is part of the GNU Hurd. + + The GNU Hurd 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, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef __RANDOM_MIG_DECLS_H__ +#define __RANDOM_MIG_DECLS_H__ + +#include <hurd/ports.h> + +typedef struct port_info *port_info_t; + +extern struct trivfs_control *fsys; +extern struct port_class *shutdown_notify_class; + +/* Called by server stub functions. */ + +static inline struct port_info * __attribute__ ((unused)) +begin_using_startup_port (mach_port_t port) +{ + return ports_lookup_port (fsys->pi.bucket, + handle, + shutdown_notify_class); +} + +static inline void __attribute__ ((unused)) +end_using_startup (struct port_info *p) +{ + if (p) + ports_port_deref (p); +} + +#endif /* __RANDOM_MIG_DECLS_H__ */ diff --git a/random/mig-mutate.h b/random/mig-mutate.h new file mode 100644 index 00000000..dab89e56 --- /dev/null +++ b/random/mig-mutate.h @@ -0,0 +1,25 @@ +/* + Copyright (C) 2014 Free Software Foundation, Inc. + Written by Justus Winter. + + This file is part of the GNU Hurd. + + The GNU Hurd 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, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */ + +#define STARTUP_INTRAN \ + port_info_t begin_using_startup_port (mach_port_t) +#define STARTUP_DESTRUCTOR \ + end_using_startup (port_info_t) +#define STARTUP_IMPORTS \ + import "mig-decls.h"; diff --git a/random/random.c b/random/random.c new file mode 100644 index 00000000..6eea3631 --- /dev/null +++ b/random/random.c @@ -0,0 +1,634 @@ +/* random.c - A single-file translator providing random data + Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc. + + 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, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#define _GNU_SOURCE 1 + +#include <hurd/trivfs.h> +#include <hurd/startup.h> +#include <stdio.h> +#include <stdlib.h> +#include <argp.h> +#include <argz.h> +#include <error.h> +#include <string.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <pthread.h> +#include <assert.h> + +#include <version.h> + +#include "random.h" +#include "gnupg-random.h" + +/* Our control port. */ +struct trivfs_control *fsys; + +int read_blocked; /* For read and select. */ +pthread_cond_t wait; /* For read and select. */ +pthread_cond_t select_alert; /* For read and select. */ + + +/* The quality of randomness we provide. + 0: Very weak randomness based on time() and getrusage(). + No external random data is used. + 1: Pseudo random numbers based on all available real random + numbers. + 2: Strong random numbers with a somewhat guaranteed entropy. +*/ +#define DEFAULT_LEVEL 2 +static int level = DEFAULT_LEVEL; + +/* Name of file to use as seed. */ +static char *seed_file; + +/* The random bytes we collected. */ +char gatherbuf[GATHERBUFSIZE]; + +/* The current positions in gatherbuf[]. */ +int gatherrpos; +int gatherwpos; + +/* XXX Yuk Yuk. */ +#define POOLSIZE 600 + +/* Take up to length bytes from gather_random if available. If + nothing is available, sleep until something becomes available. + Must be called with global_lock held. */ +int +gather_random( void (*add)(const void*, size_t, int), int requester, + size_t length, int level ) +{ + int avail = (gatherwpos - gatherrpos + GATHERBUFSIZE) % GATHERBUFSIZE; + int first = GATHERBUFSIZE - gatherrpos; + int second = length - first; + + /* If level is zero, we should not block and not add anything + to the pool. */ + if( !level ) + return 0; + + /* io_read() should guarantee that there is always data available. */ + if (level == 2) + assert (avail); + + if (length > avail) + length = avail; + + if (first > length) + first = length; + (*add) (&gatherbuf[gatherrpos], first, requester); + gatherrpos = (gatherrpos + first) % GATHERBUFSIZE; + if (second > 0) + { + (*add) (&gatherbuf[gatherrpos], second, requester); + gatherrpos += second; + } + return length; +} + + +const char *argp_program_version = STANDARD_HURD_VERSION (random); + +/* This lock protects the GnuPG code. */ +static pthread_mutex_t global_lock; + +/* Trivfs hooks. */ +int trivfs_fstype = FSTYPE_MISC; +int trivfs_fsid = 0; + +int trivfs_allow_open = O_READ | O_WRITE; + +int trivfs_support_read = 1; +int trivfs_support_write = 1; +int trivfs_support_exec = 0; + +void +trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st) +{ + /* Mark the node as a read-only plain file. */ + st->st_mode &= ~S_IFMT; + st->st_mode |= (S_IFCHR); + st->st_size = 0; +} + +error_t +trivfs_goaway (struct trivfs_control *cntl, int flags) +{ + update_random_seed_file (); + exit (0); +} + +/* Read data from an IO object. If offset is -1, read from the object + maintained file pointer. If the object is not seekable, offset is + ignored. The amount desired to be read is in AMOUNT. */ +error_t +trivfs_S_io_read (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + data_t *data, mach_msg_type_number_t *data_len, + loff_t offs, mach_msg_type_number_t amount) +{ + /* Deny access if they have bad credentials. */ + if (! cred) + return EOPNOTSUPP; + else if (! (cred->po->openmodes & O_READ)) + return EBADF; + + pthread_mutex_lock (&global_lock); + + if (amount > 0) + { + mach_msg_type_number_t new_amount; + while (readable_pool (amount, level) == 0) + { + if (cred->po->openmodes & O_NONBLOCK) + { + pthread_mutex_unlock (&global_lock); + return EWOULDBLOCK; + } + read_blocked = 1; + if (pthread_hurd_cond_wait_np (&wait, &global_lock)) + { + pthread_mutex_unlock (&global_lock); + return EINTR; + } + /* See term/users.c for possible race? */ + } + + /* Possibly allocate a new buffer. */ + if (*data_len < amount) + { + *data = mmap (0, amount, PROT_READ|PROT_WRITE, + MAP_ANON, 0, 0); + if (*data == MAP_FAILED) + { + pthread_mutex_unlock (&global_lock); + return errno; + } + } + + new_amount = read_pool ((byte *) *data, amount, level); + + if (new_amount < amount) + munmap (*data + round_page (new_amount), + round_page(amount) - round_page (new_amount)); + amount = new_amount; + } + *data_len = amount; + + /* Set atime, see term/users.c */ + + pthread_mutex_unlock (&global_lock); + + return 0; +} + +/* Write data to an IO object. If offset is -1, write at the object + maintained file pointer. If the object is not seekable, offset is + ignored. The amount successfully written is returned in amount. A + given user should not have more than one outstanding io_write on an + object at a time; servers implement congestion control by delaying + responses to io_write. Servers may drop data (returning ENOBUFS) + if they receive more than one write when not prepared for it. */ +error_t +trivfs_S_io_write (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t replytype, + data_t data, + mach_msg_type_number_t datalen, + loff_t offset, + mach_msg_type_number_t *amount) +{ + int i = 0; + /* Deny access if they have bad credentials. */ + if (! cred) + return EOPNOTSUPP; + else if (! (cred->po->openmodes & O_WRITE)) + return EBADF; + + pthread_mutex_lock (&global_lock); + + while (i < datalen) + { + gatherbuf[gatherwpos] = data[i++]; + gatherwpos = (gatherwpos + 1) % GATHERBUFSIZE; + if (gatherrpos == gatherwpos) + /* Overrun. */ + gatherrpos = (gatherrpos + 1) % GATHERBUFSIZE; + } + *amount = datalen; + + if (datalen > 0 && read_blocked) + { + read_blocked = 0; + pthread_cond_broadcast (&wait); + pthread_cond_broadcast (&select_alert); + } + + pthread_mutex_unlock (&global_lock); + return 0; +} + +/* Tell how much data can be read from the object without blocking for + a "long time" (this should be the same meaning of "long time" used + by the nonblocking flag. */ +kern_return_t +trivfs_S_io_readable (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t replytype, + mach_msg_type_number_t *amount) +{ + /* Deny access if they have bad credentials. */ + if (! cred) + return EOPNOTSUPP; + else if (! (cred->po->openmodes & O_READ)) + return EBADF; + + pthread_mutex_lock (&global_lock); + + /* XXX: Before initialization, the amount depends on the amount we + want to read. Assume some medium value. */ + *amount = readable_pool (POOLSIZE/2, level); + + pthread_mutex_unlock (&global_lock); + + return 0; +} + +/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG. + Block until one of the indicated types of i/o can be done "quickly", and + return the types that are then available. ID_TAG is returned as passed; it + is just for the convenience of the user in matching up reply messages with + specific requests sent. */ +error_t +trivfs_S_io_select (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + int *type) +{ + if (!cred) + return EOPNOTSUPP; + + /* We only deal with SELECT_READ and SELECT_WRITE here. */ + if (*type & ~(SELECT_READ | SELECT_WRITE)) + return EINVAL; + + if (*type == 0) + return 0; + + pthread_mutex_lock (&global_lock); + + while (1) + { + /* XXX Before initialization, readable_pool depends on length. */ + int avail = readable_pool (POOLSIZE/2, level); + + if (avail != 0 || *type & SELECT_WRITE) + { + *type = (avail ? SELECT_READ : 0) | (*type & SELECT_WRITE); + pthread_mutex_unlock (&global_lock); + return 0; + } + + ports_interrupt_self_on_port_death (cred, reply); + read_blocked = 1; + + if (pthread_hurd_cond_wait_np (&select_alert, &global_lock)) + { + *type = 0; + pthread_mutex_unlock (&global_lock); + return EINTR; + } + } +} + + +/* Change current read/write offset */ +error_t +trivfs_S_io_seek (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + loff_t offs, int whence, loff_t *new_offs) +{ + if (! cred) + return EOPNOTSUPP; + + /* Not seekable. */ + return ESPIPE; +} + +/* Change the size of the file. If the size increases, new blocks are + zero-filled. After successful return, it is safe to reference mapped + areas of the file up to NEW_SIZE. */ +error_t +trivfs_S_file_set_size (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + loff_t size) +{ + if (!cred) + return EOPNOTSUPP; + + return size == 0 ? 0 : EINVAL; +} + +/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and + O_NONBLOCK bits for the IO object. In addition, io_get_openmodes + will tell you which of O_READ, O_WRITE, and O_EXEC the object can + be used for. The O_ASYNC bit affects icky async I/O; good async + I/O is done through io_async which is orthogonal to these calls. */ +error_t +trivfs_S_io_set_all_openmodes(struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + int mode) +{ + if (!cred) + return EOPNOTSUPP; + + return 0; +} + +error_t +trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + int bits) +{ + if (!cred) + return EOPNOTSUPP; + + return 0; +} + +error_t +trivfs_S_io_get_owner (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + pid_t *owner) +{ + if (!cred) + return EOPNOTSUPP; + + *owner = 0; + return 0; +} + +error_t +trivfs_S_io_mod_owner (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + pid_t owner) +{ + if (!cred) + return EOPNOTSUPP; + + return EINVAL; +} + +/* Return objects mapping the data underlying this memory object. If + the object can be read then memobjrd will be provided; if the + object can be written then memobjwr will be provided. For objects + where read data and write data are the same, these objects will be + equal, otherwise they will be disjoint. Servers are permitted to + implement io_map but not io_map_cntl. Some objects do not provide + mapping; they will set none of the ports and return an error. Such + objects can still be accessed by io_read and io_write. */ +error_t +trivfs_S_io_map(struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + mach_port_t *rdobj, + mach_msg_type_name_t *rdtype, + mach_port_t *wrobj, + mach_msg_type_name_t *wrtype) +{ + if (!cred) + return EOPNOTSUPP; + + return EINVAL; +} + + +int +random_demuxer (mach_msg_header_t *inp, + mach_msg_header_t *outp) +{ + extern int startup_notify_server (mach_msg_header_t *, mach_msg_header_t *); + + return (trivfs_demuxer (inp, outp) + || startup_notify_server (inp, outp)); +} + + +/* Options processing. We accept the same options on the command line + and from fsys_set_options. */ + +static const struct argp_option options[] = +{ + {"weak", 'w', 0, 0, "Output weak pseudo random data"}, + {"fast", 'f', 0, 0, "Output cheap random data fast"}, + {"secure", 's', 0, 0, "Output cryptographically secure random"}, + {"seed-file", 'S', "FILE", 0, "Use FILE to remember the seed"}, + {0} +}; + +static error_t +parse_opt (int opt, char *arg, struct argp_state *state) +{ + switch (opt) + { + default: + return ARGP_ERR_UNKNOWN; + case ARGP_KEY_INIT: + case ARGP_KEY_SUCCESS: + case ARGP_KEY_ERROR: + break; + + case 'w': + { + level = 0; + break; + } + case 'f': + { + level = 1; + break; + } + case 's': + { + level = 2; + break; + } + case 'S': + { + seed_file = strdup (arg); + set_random_seed_file (arg); + } + } + return 0; +} + +/* This will be called from libtrivfs to help construct the answer + to an fsys_get_options RPC. */ +error_t +trivfs_append_args (struct trivfs_control *fsys, + char **argz, size_t *argz_len) +{ + error_t err = 0; + char *opt; + + pthread_mutex_lock (&global_lock); + switch (level) + { + case 0: + { + opt = "--weak"; + break; + } + case 1: + { + opt = "--fast"; + break; + } + default: + { + opt = "--secure"; + break; + } + } + if (level != DEFAULT_LEVEL) + err = argz_add (argz, argz_len, opt); + + if (!err && seed_file) + { + if (asprintf (&opt, "--seed-file=%s", seed_file) < 0) + err = ENOMEM; + else + { + err = argz_add (argz, argz_len, opt); + free (opt); + } + } + pthread_mutex_unlock (&global_lock); + + return err; +} + +static struct argp random_argp = +{ options, parse_opt, 0, + "A translator providing random output." }; + +/* Setting this variable makes libtrivfs use our argp to + parse options passed in an fsys_set_options RPC. */ +struct argp *trivfs_runtime_argp = &random_argp; + +struct port_class *shutdown_notify_class; + +/* The system is going down; destroy all the extant port rights. That + will cause net channels and such to close promptly. */ +error_t +S_startup_dosync (struct port_info *inpi) +{ + if (!inpi) + return EOPNOTSUPP; + + update_random_seed_file (); + return 0; +} + +void +sigterm_handler (int signo) +{ + update_random_seed_file (); + signal (SIGTERM, SIG_DFL); + raise (SIGTERM); +} + +void +arrange_shutdown_notification () +{ + error_t err; + mach_port_t initport, notify; + process_t procserver; + struct port_info *pi; + + shutdown_notify_class = ports_create_class (0, 0); + + signal (SIGTERM, sigterm_handler); + + /* Arrange to get notified when the system goes down, + but if we fail for some reason, just silently give up. No big deal. */ + + err = ports_create_port (shutdown_notify_class, fsys->pi.bucket, + sizeof (struct port_info), &pi); + if (err) + return; + + procserver = getproc (); + if (!procserver) + return; + + err = proc_getmsgport (procserver, 1, &initport); + mach_port_deallocate (mach_task_self (), procserver); + if (err) + return; + + notify = ports_get_send_right (pi); + ports_port_deref (pi); + startup_request_notification (initport, notify, + MACH_MSG_TYPE_MAKE_SEND, + program_invocation_short_name); + mach_port_deallocate (mach_task_self (), notify); + mach_port_deallocate (mach_task_self (), initport); +} + + +int +main (int argc, char **argv) +{ + error_t err; + mach_port_t bootstrap; + + /* Initialize the lock that will protect everything. + We must do this before argp_parse, because parse_opt (above) will + use the lock. */ + pthread_mutex_init (&global_lock, NULL); + + /* The conditions are used to implement proper read/select + behaviour. */ + pthread_cond_init (&wait, NULL); + pthread_cond_init (&select_alert, NULL); + + /* We use the same argp for options available at startup + as for options we'll accept in an fsys_set_options RPC. */ + argp_parse (&random_argp, argc, argv, 0, 0, 0); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + if (bootstrap == MACH_PORT_NULL) + error (1, 0, "Must be started as a translator"); + + /* Reply to our parent */ + err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys); + mach_port_deallocate (mach_task_self (), bootstrap); + if (err) + error (3, err, "trivfs_startup"); + + arrange_shutdown_notification (); + + /* Launch. */ + ports_manage_port_operations_multithread (fsys->pi.bucket, random_demuxer, + 10 * 1000, /* idle thread */ + 10 * 60 * 1000, /* idle server */ + 0); + return 0; +} diff --git a/random/random.h b/random/random.h new file mode 100644 index 00000000..a38a4177 --- /dev/null +++ b/random/random.h @@ -0,0 +1,32 @@ +/* random.c - A single-file translator providing random data + Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc. + + 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, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef __RANDOM_H__ +#define __RANDOM_H__ + +/* How many random bytes to gather at most. + XXX: Should be at least POOLSIZE. */ +#define GATHERBUFSIZE 32768 + +/* The random bytes we collected. */ +extern char gatherbuf[GATHERBUFSIZE]; + +/* The current positions in gatherbuf[]. */ +extern int gatherrpos; +extern int gatherwpos; + +#endif |