import cyrus-sasl-2.1.23
[cyrus-sasl.git] / mac / libdes / src / rnd_keys.c
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