1 /*
\r * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska H
\9agskolan
\r * (Royal Institute of Technology, Stockholm, Sweden).
\r * All rights reserved.
\r *
\r * Redistribution and use in source and binary forms, with or without
\r * modification, are permitted provided that the following conditions
\r * are met:
\r *
\r * 1. Redistributions of source code must retain the above copyright
\r * notice, this list of conditions and the following disclaimer.
\r *
\r * 2. Redistributions in binary form must reproduce the above copyright
\r * notice, this list of conditions and the following disclaimer in the
\r * documentation and/or other materials provided with the distribution.
\r *
\r * 3. All advertising materials mentioning features or use of this software
\r * must display the following acknowledgement:
\r * This product includes software developed by the Kungliga Tekniska
\r * H
\9agskolan and its contributors.
\r *
\r * 4. Neither the name of the Institute nor the names of its contributors
\r * may be used to endorse or promote products derived from this software
\r * without specific prior written permission.
\r *
\r * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
\r * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
\r * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
\r * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
\r * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
\r * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
\r * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
\r * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
\r * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
\r * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
\r * SUCH DAMAGE.
\r */
\r\r#ifdef HAVE_CONFIG_H
\r#include "config.h"
\r\rRCSID("$Id: rnd_keys.c,v 1.2 2001/12/04 02:06:31 rjs3 Exp $");
\r#endif
\r\r#include <des.h>
\r#include <des_locl.h>
\r#ifdef KRB5
\r#include <krb5-types.h>
\r#elif defined(KRB4)
\r#include <ktypes.h>
\r#endif
\r\r#include <string.h>
\r\r#ifdef TIME_WITH_SYS_TIME
\r#include <sys/time.h>
\r#include <time.h>
\r#elif defined(HAVE_SYS_TIME_H)
\r#include <sys/time.h>
\r#else
\r#include <time.h>
\r#endif
\r\r#ifdef HAVE_SYS_TYPES_H
\r#include <sys/types.h>
\r#endif
\r\r#ifdef HAVE_UNISTD_H
\r#include <unistd.h>
\r#endif
\r#ifdef HAVE_IO_H
\r#include <io.h>
\r#endif
\r\r#ifdef HAVE_SIGNAL_H
\r#include <signal.h>
\r#endif
\r#ifdef HAVE_FCNTL_H
\r#include <fcntl.h>
\r#endif
\r\r#ifdef HAVE_WINSOCK_H
\r#include <winsock.h>
\r#endif
\r\r/*
\r * Generate "random" data by checksumming a file.
\r *
\r * Returns -1 if there were any problems with permissions or I/O
\r * errors.
\r */
\rstatic
\rint
\rsumFile (const char *name, int len, void *res)
\r{
\r u_int32_t sum[2];
\r u_int32_t buf[1024*2];
\r int fd, i;
\r\r fd = open (name, 0);
\r if (fd < 0)
\r return -1;
\r\r while (len > 0)
\r {
\r int n = read(fd, buf, sizeof(buf));
\r if (n < 0)
\r {
\r close(fd);
\r return n;
\r }
\r for (i = 0; i < (n/sizeof(buf[0])); i++)
\r {
\r sum[0] += buf[i];
\r i++;
\r sum[1] += buf[i];
\r }
\r len -= n;
\r }
\r close (fd);
\r memcpy (res, &sum, sizeof(sum));
\r return 0;
\r}
\r\r#if 0
\rstatic
\rint
\rmd5sumFile (const char *name, int len, int32_t sum[4])
\r{
\r int32_t buf[1024*2];
\r int fd, cnt;
\r struct md5 md5;
\r\r fd = open (name, 0);
\r if (fd < 0)
\r return -1;
\r\r md5_init(&md5);
\r while (len > 0)
\r {
\r int n = read(fd, buf, sizeof(buf));
\r if (n < 0)
\r {
\r close(fd);
\r return n;
\r }
\r md5_update(&md5, buf, n);
\r len -= n;
\r }
\r md5_finito(&md5, (unsigned char *)sum);
\r close (fd);
\r return 0;
\r}
\r#endif
\r\r/*
\r * Create a sequence of random 64 bit blocks.
\r * The sequence is indexed with a long long and
\r * based on an initial des key used as a seed.
\r */
\rstatic des_key_schedule sequence_seed;
\rstatic u_int32_t sequence_index[2];
\r\r/*
\r * Random number generator based on ideas from truerand in cryptolib
\r * as described on page 424 in Applied Cryptography 2 ed. by Bruce
\r * Schneier.
\r */
\r\rstatic volatile int counter;
\rstatic volatile unsigned char *gdata; /* Global data */
\rstatic volatile int igdata; /* Index into global data */
\rstatic int gsize;
\r\r#if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__)
\r/* Visual C++ 4.0 (Windows95/NT) */
\r\rstatic
\rRETSIGTYPE
\rsigALRM(int sig)
\r{
\r if (igdata < gsize)
\r gdata[igdata++] ^= counter & 0xff;
\r\r#ifndef HAVE_SIGACTION
\r signal(SIGALRM, sigALRM); /* Reinstall SysV signal handler */
\r#endif
\r SIGRETURN(0);
\r}
\r\r#endif
\r\r#if !defined(HAVE_RANDOM) && defined(HAVE_RAND)
\r#ifndef srandom
\r#define srandom srand
\r#endif
\r#ifndef random
\r#define random rand
\r#endif
\r#endif
\r\rstatic void
\rdes_not_rand_data(unsigned char *data, int size)
\r{
\r int i;
\r\r srandom (time (NULL));
\r\r for(i = 0; i < size; ++i)
\r data[i] ^= random() % 0x100;
\r}
\r\r#if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__)
\r\r#ifndef HAVE_SETITIMER
\rstatic void
\rpacemaker(struct timeval *tv)
\r{
\r fd_set fds;
\r pid_t pid;
\r pid = getppid();
\r while(1){
\r FD_ZERO(&fds);
\r FD_SET(0, &fds);
\r select(1, &fds, NULL, NULL, tv);
\r kill(pid, SIGALRM);
\r }
\r}
\r#endif
\r\r/*
\r * Generate size bytes of "random" data using timed interrupts.
\r * It takes about 40ms/byte random data.
\r * It's not neccessary to be root to run it.
\r */
\rvoid
\rdes_rand_data(unsigned char *data, int size)
\r{
\r struct itimerval tv, otv;
\r#ifdef HAVE_SIGACTION
\r struct sigaction sa, osa;
\r#else
\r RETSIGTYPE (*osa)(int);
\r#endif
\r int i, j;
\r#ifndef HAVE_SETITIMER
\r pid_t pid;
\r#endif
\r char *rnd_devices[] = {"/dev/random",
\r "/dev/srandom",
\r "/dev/urandom",
\r NULL};
\r char **p;
\r\r for(p = rnd_devices; *p; p++) {
\r int fd = open(*p, O_RDONLY | O_NDELAY);
\r \r if(fd >= 0 && read(fd, data, size) == size) {
\r close(fd);
\r return;
\r }
\r close(fd);
\r }
\r\r /* Paranoia? Initialize data from /dev/mem if we can read it. */
\r if (size >= 8)
\r sumFile("/dev/mem", (1024*1024*2), data);
\r\r gdata = data;
\r gsize = size;
\r igdata = 0;
\r\r#ifdef HAVE_SIGACTION
\r /* Setup signal handler */
\r sa.sa_handler = sigALRM;
\r sa.sa_flags = 0;
\r sigemptyset(&sa.sa_mask);
\r sigaction(SIGALRM, &sa, &osa);
\r#else
\r osa = signal(SIGALRM, sigALRM);
\r#endif
\r \r /* Start timer */
\r tv.it_value.tv_sec = 0;
\r tv.it_value.tv_usec = 10 * 1000; /* 10 ms */
\r tv.it_interval = tv.it_value;
\r#ifdef HAVE_SETITIMER
\r setitimer(ITIMER_REAL, &tv, &otv);
\r#else
\r pid = fork();
\r if(pid == -1){
\r des_not_rand_data(data, size);
\r return;
\r }
\r if(pid == 0)
\r pacemaker(&tv.it_interval);
\r#endif
\r\r for(i = 0; i < 4; i++) {
\r for (igdata = 0; igdata < size;) /* igdata++ in sigALRM */
\r counter++;
\r for (j = 0; j < size; j++) /* Only use 2 bits each lap */
\r gdata[j] = (gdata[j]>>2) | (gdata[j]<<6);
\r }
\r#ifdef HAVE_SETITIMER
\r setitimer(ITIMER_REAL, &otv, 0);
\r#else
\r kill(pid, SIGKILL);
\r waitpid(pid, NULL, 0);
\r#endif
\r#ifdef HAVE_SIGACTION
\r sigaction(SIGALRM, &osa, 0);
\r#else
\r signal(SIGALRM, osa != SIG_ERR ? osa : SIG_DFL);
\r#endif
\r}
\r#else
\rvoid
\rdes_rand_data(unsigned char *p, int s)
\r{
\r des_not_rand_data (p, s);
\r}
\r#endif
\r\rvoid
\rdes_generate_random_block(des_cblock *block)
\r{
\r des_rand_data((unsigned char *)block, sizeof(*block));
\r}
\r\r/*
\r * Generate a "random" DES key.
\r */
\rvoid
\rdes_rand_data_key(des_cblock *key)
\r{
\r unsigned char data[8];
\r des_key_schedule sched;
\r do {
\r des_rand_data(data, sizeof(data));
\r des_rand_data((unsigned char*)key, sizeof(des_cblock));
\r des_set_odd_parity(key);
\r des_key_sched(key, sched);
\r des_ecb_encrypt(&data, key, sched, DES_ENCRYPT);
\r memset(&data, 0, sizeof(data));
\r memset(&sched, 0, sizeof(sched));
\r des_set_odd_parity(key);
\r } while(des_is_weak_key(key));
\r}
\r\r/*
\r * Generate "random" data by checksumming /dev/mem
\r *
\r * It's neccessary to be root to run it. Returns -1 if there were any
\r * problems with permissions.
\r */
\rint
\rdes_mem_rand8(unsigned char *data)
\r{
\r return 1;
\r}
\r\r/*
\r * In case the generator does not get initialized use this as fallback.
\r */
\rstatic int initialized;
\r\rstatic void
\rdo_initialize(void)
\r{
\r des_cblock default_seed;
\r do {
\r des_generate_random_block(&default_seed);
\r des_set_odd_parity(&default_seed);
\r } while (des_is_weak_key(&default_seed));
\r des_init_random_number_generator(&default_seed);
\r}
\r\r#define zero_long_long(ll) do { ll[0] = ll[1] = 0; } while (0)
\r\r#define incr_long_long(ll) do { if (++ll[0] == 0) ++ll[1]; } while (0)
\r\r#define set_sequence_number(ll) \
\rmemcpy((char *)sequence_index, (ll), sizeof(sequence_index));
\r\r/*
\r * Set the sequnce number to this value (a long long).
\r */
\rvoid
\rdes_set_sequence_number(unsigned char *ll)
\r{
\r set_sequence_number(ll);
\r}
\r\r/*
\r * Set the generator seed and reset the sequence number to 0.
\r */
\rvoid
\rdes_set_random_generator_seed(des_cblock *seed)
\r{
\r des_key_sched(seed, sequence_seed);
\r zero_long_long(sequence_index);
\r initialized = 1;
\r}
\r\r/*
\r * Generate a sequence of random des keys
\r * using the random block sequence, fixup
\r * parity and skip weak keys.
\r */
\rint
\rdes_new_random_key(des_cblock *key)
\r{
\r if (!initialized)
\r do_initialize();
\r\r do {
\r des_ecb_encrypt((des_cblock *) sequence_index,
\r key,
\r sequence_seed,
\r DES_ENCRYPT);
\r incr_long_long(sequence_index);
\r /* random key must have odd parity and not be weak */
\r des_set_odd_parity(key);
\r } while (des_is_weak_key(key));
\r return(0);
\r}
\r\r/*
\r * des_init_random_number_generator:
\r *
\r * Initialize the sequence of random 64 bit blocks. The input seed
\r * can be a secret key since it should be well hidden and is also not
\r * kept.
\r *
\r */
\rvoid
\rdes_init_random_number_generator(des_cblock *seed)
\r{
\r struct timeval now;
\r des_cblock uniq;
\r des_cblock new_key;
\r\r gettimeofday(&now, (struct timezone *)0);
\r des_generate_random_block(&uniq);
\r\r /* Pick a unique random key from the shared sequence. */
\r des_set_random_generator_seed(seed);
\r set_sequence_number((unsigned char *)&uniq);
\r des_new_random_key(&new_key);
\r\r /* Select a new nonshared sequence, */
\r des_set_random_generator_seed(&new_key);
\r\r /* and use the current time to pick a key for the new sequence. */
\r set_sequence_number((unsigned char *)&now);
\r des_new_random_key(&new_key);
\r des_set_random_generator_seed(&new_key);
\r}
\r\r/* This is for backwards compatibility. */
\rvoid
\rdes_random_key(des_cblock ret)
\r{
\r des_new_random_key((des_cblock *)ret);
\r}
\r\r#ifdef TESTRUN
\rint
\rmain()
\r{
\r unsigned char data[8];
\r int i;
\r\r while (1)
\r {
\r if (sumFile("/dev/mem", (1024*1024*8), data) != 0)
\r { perror("sumFile"); exit(1); }
\r for (i = 0; i < 8; i++)
\r printf("%02x", data[i]);
\r printf("\n");
\r }
\r}
\r#endif
\r\r#ifdef TESTRUN2
\rint
\rmain()
\r{
\r des_cblock data;
\r int i;
\r\r while (1)
\r {
\r do_initialize();
\r des_random_key(data);
\r for (i = 0; i < 8; i++)
\r printf("%02x", data[i]);
\r printf("\n");
\r }
\r}
\r#endif
\r