summaryrefslogtreecommitdiff
path: root/dst/prandom.c
diff options
context:
space:
mode:
Diffstat (limited to 'dst/prandom.c')
-rw-r--r--dst/prandom.c976
1 files changed, 0 insertions, 976 deletions
diff --git a/dst/prandom.c b/dst/prandom.c
deleted file mode 100644
index 7a24d332..00000000
--- a/dst/prandom.c
+++ /dev/null
@@ -1,976 +0,0 @@
-/*
- * Portions Copyright (c) 2012-2014 by Internet Systems Consortium, Inc. ("ISC")
- * Portions Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
- * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
- *
- * Permission to use, copy modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS
- * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
- * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
- * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OR PERFORMANCE OF THE SOFTWARE.
- */
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <time.h>
-#include <dirent.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#include <netinet/in.h>
-#include <sys/socket.h>
-#define NEED_PRAND_CONF
-
-#include "cdefs.h"
-#include "osdep.h"
-#include "dst_internal.h"
-#include "arpa/nameser.h"
-
-
-#ifndef DST_NUM_HASHES
-#define DST_NUM_HASHES 4
-#endif
-#ifndef DST_NUMBER_OF_COUNTERS
-#define DST_NUMBER_OF_COUNTERS 5 /* 32 * 5 == 160 == SHA(1) > MD5 */
-#endif
-
-/*
- * the constant below is a prime number to make fixed data structures like
- * stat and time wrap over blocks. This adds certain randomness to what is
- * in each digested block.
- * The prime number 2879 has the special property that when
- * divided by 2,4 and 6 the result is also a prime numbers
- */
-
-#ifndef DST_RANDOM_BLOCK_SIZE
-#define DST_RANDOM_BLOCK_SIZE 2879
-#endif
-
-/*
- * This constant dictates how many bits we shift to the right before using a
- */
-#ifndef DST_SHIFT
-#define DST_SHIFT 9
-#endif
-
-/*
- * An initializer that is as bad as any other with half the bits set
- */
-#ifndef DST_RANDOM_PATTERN
-#define DST_RANDOM_PATTERN 0x8765CA93
-#endif
-/*
- * things must have changed in the last 3600 seconds to be used
- */
-#define MAX_OLD 3600
-
-/*
- * Define a single set of configuration for prand stuff. A superset
- * works okay (failed commands return no data, missing directories
- * are skipped, and so on.
- */
-static const char *cmds[] = {
- "/usr/bin/netstat -an 2>&1",
- "/usr/sbin/netstat -an 2>&1",
- "/usr/etc/netstat -an 2>&1",
- "/bin/netstat -an 2>&1",
- "/usr/ucb/netstat -an 2>&1",
-
- /* AIX */
- "/bin/ps -ef 2>&1",
- "/bin/df 2>&1",
- "/usr/bin/uptime 2>&1",
- "/usr/bin/printenv 2>&1",
- "/usr/bin/netstat -s 2>&1",
- "/usr/bin/w 2>&1",
- /* Tru64 */
- "/usr/bin/dig com. soa +ti=1 +retry=0 2>&1",
- "/usr/sbin/arp -an 2>&1",
- "/usr/ucb/uptime 2>&1",
- "/bin/iostat 2>&1",
- /* BSD */
- "/bin/ps -axlw 2>&1",
- "/usr/sbin/iostat 2>&1",
- "/usr/sbin/vmstat 2>&1",
- /* FreeBSD */
- "/usr/bin/vmstat 2>&1",
- "/usr/bin/w 2>&1",
- /* HP/UX */
- "/usr/bin/ps -ef 2>&1",
- /* IRIX */
- "/usr/etc/arp -a 2>&1",
- "/usr/bsd/uptime 2>&1",
- "/usr/bin/printenv 2>&1",
- "/usr/bsd/w 2>&1",
- /* Linux */
- "/sbin/arp -an 2>&1",
- "/usr/bin/vmstat 2>&1",
- /* NetBSD */
- /* OpenBSD */
- /* QNX */
- "/bin/ps -a 2>&1",
- "/bin/sin 2>&1",
- "/bin/sin fds 2>&1",
- "/bin/sin memory 2>&1",
- /* Solaris */
- "/usr/ucb/uptime 2>&1",
- "/usr/ucb/netstat -an 2>&1",
-
- "/usr/bin/netstat -an 2>&1",
- "/usr/sbin/netstat -an 2>&1",
- "/usr/etc/netstat -an 2>&1",
- "/bin/netstat -an 2>&1",
- "/usr/ucb/netstat -an 2>&1",
- NULL
-};
-
-static const char *dirs[] = {
- "/tmp",
- "/var/tmp",
- ".",
- "/",
- "/var/spool",
- "/var/adm",
- "/dev",
- "/var/spool/mail",
- "/var/mail",
- "/home",
- "/usr/home",
- NULL
-};
-
-static const char *files[] = {
- "/var/adm/messages",
- "/var/adm/wtmp",
- "/var/adm/lastlog",
- "/var/log/messages",
- "/var/log/wtmp",
- "/var/log/lastlog",
- "/proc/stat",
- "/proc/rtc",
- "/proc/meminfo",
- "/proc/interrupts",
- "/proc/self/status",
- "/proc/ipstats",
- "/proc/dumper",
- "/proc/self/as",
- NULL
-};
-
-/*
- * these two data structure are used to process input data into digests,
- *
- * The first structure contains a pointer to a DST HMAC key
- * the variables accompanying are used for
- * step : select every step byte from input data for the hash
- * block: number of data elements going into each hash
- * digested: number of data elements digested so far
- * curr: offset into the next input data for the first byte.
- */
-typedef struct hash {
- DST_KEY *key;
- void *ctx;
- int digested, block, step, curr;
-} prand_hash;
-
-/*
- * This data structure controls number of hashes and keeps track of
- * overall progress in generating correct number of bytes of output.
- * output : array to store the output data in
- * needed : how many bytes of output are needed
- * filled : number of bytes in output so far.
- * bytes : total number of bytes processed by this structure
- * file_digest : the HMAC key used to digest files.
- */
-typedef struct work {
- unsigned needed, filled, bytes;
- u_char *output;
- prand_hash *hash[DST_NUM_HASHES];
- DST_KEY *file_digest;
-} dst_work;
-
-
-/*
- * forward function declarations
- */
-static int get_dev_random(u_char *output, unsigned size);
-static int do_time(dst_work *work);
-static int do_ls(dst_work *work);
-static int unix_cmd(dst_work *work);
-static int digest_file(dst_work *work);
-
-static void force_hash(dst_work *work, prand_hash *hash);
-static int do_hash(dst_work *work, prand_hash *hash, const u_char *input,
- unsigned size);
-static int my_digest(dst_work *tmp, const u_char *input, unsigned size);
-static prand_hash *get_hmac_key(int step, int block);
-
-static unsigned own_random(dst_work *work);
-
-
-/*
- * variables used in the quick random number generator
- */
-static u_int32_t ran_val = DST_RANDOM_PATTERN;
-static u_int32_t ran_cnt = (DST_RANDOM_PATTERN >> 10);
-
-/*
- * setting the quick_random generator to particular values or if both
- * input parameters are 0 then set it to initial values
- */
-
-void
-dst_s_quick_random_set(u_int32_t val, u_int32_t cnt)
-{
- ran_val = (val == 0) ? DST_RANDOM_PATTERN : val;
- ran_cnt = (cnt == 0) ? (DST_RANDOM_PATTERN >> 10) : cnt;
-}
-
-/*
- * this is a quick and random number generator that seems to generate quite
- * good distribution of data
- */
-u_int32_t
-dst_s_quick_random(int inc)
-{
- ran_val = ((ran_val >> 13) ^ (ran_val << 19)) ^
- ((ran_val >> 7) ^ (ran_val << 25));
- if (inc > 0) /* only increasing values accepted */
- ran_cnt += inc;
- ran_val += ran_cnt++;
- return (ran_val);
-}
-
-/*
- * get_dev_random: Function to read /dev/random reliably
- * this function returns how many bytes where read from the device.
- * port_after.h should set the control variable HAVE_DEV_RANDOM
- */
-static int
-get_dev_random(u_char *output, unsigned size)
-{
-#ifdef HAVE_DEV_RANDOM
- struct stat st;
- int n = 0, fd = -1, s;
-
- s = stat("/dev/random", &st);
- if (s == 0 && S_ISCHR(st.st_mode)) {
- if ((fd = open("/dev/random", O_RDONLY | O_NONBLOCK)) != -1) {
- if ((n = read(fd, output, size)) < 0)
- n = 0;
- close(fd);
- }
- return (n);
- }
-#endif
- return (0);
-}
-
-/*
- * Portable way of getting the time values if gettimeofday is missing
- * then compile with -DMISSING_GETTIMEOFDAY time() is POSIX compliant but
- * gettimeofday() is not.
- * Time of day is predictable, we are looking for the randomness that comes
- * the last few bits in the microseconds in the timer are hard to predict when
- * this is invoked at the end of other operations
- */
-struct timeval *mtime;
-static int
-do_time(dst_work *work)
-{
- int cnt = 0;
- static u_char tmp[sizeof(struct timeval) + sizeof(struct timezone)];
- struct timezone *zone;
-
- zone = (struct timezone *) tmp;
- mtime = (struct timeval *)(tmp + sizeof(struct timezone));
- gettimeofday(mtime, zone);
- cnt = sizeof(tmp);
- my_digest(work, tmp, sizeof(tmp));
-
- return (cnt);
-}
-
-/*
- * this function simulates the ls command, but it uses stat which gives more
- * information and is harder to guess
- * Each call to this function will visit the next directory on the list of
- * directories, in a circular manner.
- * return value is the number of bytes added to the temp buffer
- *
- * do_ls() does not visit subdirectories
- * if attacker has access to machine it can guess most of the values seen
- * thus it is important to only visit directories that are frequently updated
- * Attacker that has access to the network can see network traffic
- * when NFS mounted directories are accessed and know exactly the data used
- * but may not know exactly in what order data is used.
- * Returns the number of bytes that where returned in stat structures
- */
-static int
-do_ls(dst_work *work)
-{
- struct dir_info {
- uid_t uid;
- gid_t gid;
- off_t size;
- time_t atime, mtime, ctime;
- };
- static struct dir_info dir_info;
- struct stat buf;
- struct dirent *entry;
- static int i = 0;
- static unsigned long d_round = 0;
- struct timeval tv;
- int n = 0, out = 0;
- unsigned dir_len;
-
- char file_name[1024];
- u_char tmp_buff[1024];
- DIR *dir = NULL;
-
- if (dirs[i] == NULL) /* if at the end of the list start over */
- i = 0;
- if (stat(dirs[i++], &buf)) /* directory does not exist */
- return (0);
-
- gettimeofday(&tv,NULL);
- if (d_round == 0)
- d_round = tv.tv_sec - MAX_OLD;
- else if (i==1) /* if starting a new round cut what we accept */
- d_round += (tv.tv_sec - d_round)/2;
-
- if (buf.st_atime < d_round)
- return (0);
-
- EREPORT(("do_ls i %d filled %4d in_temp %4d\n",
- i-1, work->filled, work->in_temp));
- memcpy(tmp_buff, &buf, sizeof(buf));
-
-
- if ((dir = opendir(dirs[i-1])) == NULL)/* open it for read */
- return (0);
- strcpy(file_name, dirs[i-1]);
- dir_len = strlen(file_name);
- file_name[dir_len++] = '/';
- while ((entry = readdir(dir))) {
- unsigned len = strlen(entry->d_name);
- out += len;
- if (my_digest(work, (u_char *)entry->d_name, len))
- break;
-
- memcpy(&file_name[dir_len], entry->d_name, len);
- file_name[dir_len + len] = 0x0;
- /* for all entries in dir get the stats */
- if (stat(file_name, &buf) == 0) {
- n++; /* count successful stat calls */
- /* copy non static fields */
- dir_info.uid += buf.st_uid;
- dir_info.gid += buf.st_gid;
- dir_info.size += buf.st_size;
- dir_info.atime += buf.st_atime;
- dir_info.mtime += buf.st_mtime;
- dir_info.ctime += buf.st_ctime;
- out += sizeof(dir_info);
- if(my_digest(work, (u_char *)&dir_info,
- sizeof(dir_info)))
- break;
- }
- }
- closedir(dir); /* done */
- out += do_time(work); /* add a time stamp */
- return (out);
-}
-
-
-/*
- * unix_cmd()
- * this function executes the a command from the cmds[] list of unix commands
- * configured in the prand_conf.h file
- * return value is the number of bytes added to the randomness temp buffer
- *
- * it returns the number of bytes that where read in
- * if more data is needed at the end time is added to the data.
- * This function maintains a state to selects the next command to run
- * returns the number of bytes read in from the command
- */
-static int
-unix_cmd(dst_work *work)
-{
- static int cmd_index = 0;
- int cnt = 0, n;
- FILE *pipe;
- u_char buffer[4096];
-
- if (cmds[cmd_index] == NULL)
- cmd_index = 0;
- EREPORT(("unix_cmd() i %d filled %4d in_temp %4d\n",
- cmd_index, work->filled, work->in_temp));
- pipe = popen(cmds[cmd_index++], "r"); /* execute the command */
-
- while ((n = fread(buffer, sizeof(char), sizeof(buffer), pipe)) > 0) {
- cnt += n; /* process the output */
- if (my_digest(work, buffer, (unsigned)n))
- break;
- /* this adds some randomness to the output */
- cnt += do_time(work);
- }
- while ((n = fread(buffer, sizeof(char), sizeof(buffer), pipe)) > 0)
- ; /* drain the pipe */
- pclose(pipe);
- return (cnt); /* read how many bytes where read in */
-}
-
-/*
- * digest_file() This function will read a file and run hash over it
- * input is a file name
- */
-static int
-digest_file(dst_work *work)
-{
- static int f_cnt = 0;
- static unsigned long f_round = 0;
- FILE *fp;
- void *ctx;
- const char *name;
- int no, i;
- struct stat st;
- struct timeval tv;
- u_char buf[1024];
-
- name = files[f_cnt++];
- if (f_round == 0 || files[f_cnt] == NULL || work->file_digest == NULL)
- if (gettimeofday(&tv, NULL)) /* only do this if needed */
- return (0);
- if (f_round == 0) /* first time called set to one hour ago */
- f_round = (tv.tv_sec - MAX_OLD);
- if (files[f_cnt] == NULL) { /* end of list of files */
- if(f_cnt <= 1) /* list is too short */
- return (0);
- f_cnt = 0; /* start again on list */
- f_round += (tv.tv_sec - f_round)/2; /* set new cutoff */
- work->file_digest = dst_free_key(work->file_digest);
- }
- if (work->file_digest == NULL) {
- work->file_digest = dst_buffer_to_key("", KEY_HMAC_MD5, 0, 0,
- (u_char *)&tv, sizeof(tv));
- if (work->file_digest == NULL)
- return (0);
- }
- if (access(name, R_OK) || stat(name, &st))
- return (0); /* no such file or not allowed to read it */
- if (strncmp(name, "/proc/", 6) && st.st_mtime < f_round)
- return(0); /* file has not changed recently enough */
- if (dst_sign_data(SIG_MODE_INIT, work->file_digest, &ctx,
- NULL, 0, NULL, 0)) {
- work->file_digest = dst_free_key(work->file_digest);
- return (0);
- }
- if ((fp = fopen(name, "r")) == NULL)
- return (0);
- for (no = 0; (i = fread(buf, sizeof(*buf), sizeof(buf), fp)) > 0;
- no += i)
- dst_sign_data(SIG_MODE_UPDATE, work->file_digest, &ctx,
- buf, (unsigned)i, NULL, 0);
-
- fclose(fp);
- if (no >= 64) {
- i = dst_sign_data(SIG_MODE_FINAL, work->file_digest, &ctx,
- NULL, 0, &work->output[work->filled],
- DST_HASH_SIZE);
- if (i > 0)
- work->filled += i;
- }
-
- my_digest(work, (const u_char *)name, strlen(name));
- return (no + strlen(name));
-}
-
-/*
- * function to perform the FINAL and INIT operation on a hash if allowed
- */
-static void
-force_hash(dst_work *work, prand_hash *hash)
-{
- int i = 0;
-
- /*
- * if more than half a block then add data to output
- * otherwise add the digest to the next hash
- */
- if ((hash->digested * 2) > hash->block) {
- i = dst_sign_data(SIG_MODE_FINAL, hash->key, &hash->ctx,
- NULL, 0, &work->output[work->filled],
- DST_HASH_SIZE);
-
- hash->digested = 0;
- dst_sign_data(SIG_MODE_INIT, hash->key, &hash->ctx,
- NULL, 0, NULL, 0);
- if (i > 0)
- work->filled += i;
- }
- return;
-}
-
-/*
- * This function takes the input data does the selection of data specified
- * by the hash control block.
- * The step variable in the work structure determines which 1/step bytes
- * are used,
- *
- */
-static int
-do_hash(dst_work *work, prand_hash *hash, const u_char *input, unsigned size)
-{
- const u_char *tmp = input;
- u_char *tp, *abuf = (u_char *)0;
- int i, n;
- unsigned needed, avail, dig, cnt = size;
- unsigned tmp_size = 0;
-
- if (cnt <= 0 || input == NULL)
- return (0);
-
- if (hash->step > 1) { /* if using subset of input data */
- tmp_size = size / hash->step + 2;
- abuf = tp = malloc(tmp_size);
- /* no good return code but at least don't step on things */
- if (tp == NULL) {
- return (0);
- }
- tmp = tp;
- for (cnt = 0, i = hash->curr; i < size; i += hash->step, cnt++)
- *(tp++) = input[i];
- /* calculate the starting point in the next input set */
- hash->curr = (hash->step - (i - size)) % hash->step;
- }
- /* digest the data in block sizes */
- for (n = 0; n < cnt; n += needed) {
- avail = (cnt - n);
- needed = hash->block - hash->digested;
- dig = (avail < needed) ? avail : needed;
- dst_sign_data(SIG_MODE_UPDATE, hash->key, &hash->ctx,
- &tmp[n], dig, NULL, 0);
- hash->digested += dig;
- if (hash->digested >= hash->block)
- force_hash(work, hash);
- if (work->needed < work->filled) {
- if (abuf)
- SAFE_FREE2(abuf, tmp_size);
- return (1);
- }
- }
- if (tmp_size > 0)
- SAFE_FREE2(abuf, tmp_size);
- return (0);
-}
-
-/*
- * Copy data from INPUT for length SIZE into the work-block TMP.
- * If we fill the work-block, digest it; then,
- * if work-block needs more data, keep filling with the rest of the input.
- */
-static int
-my_digest(dst_work *work, const u_char *input, unsigned size)
-{
-
- int i, full = 0;
- static unsigned counter;
-
- counter += size;
- /* first do each one of the hashes */
- for (i = 0; i < DST_NUM_HASHES && full == 0; i++)
- full = do_hash(work, work->hash[i], input, size) +
- do_hash(work, work->hash[i], (u_char *) &counter,
- sizeof(counter));
-/*
- * if enough data has be generated do final operation on all hashes
- * that have enough date for that
- */
- for (i = 0; full && (i < DST_NUM_HASHES); i++)
- force_hash(work, work->hash[i]);
-
- return (full);
-}
-
-/*
- * this function gets some semi random data and sets that as an HMAC key
- * If we get a valid key this function returns that key initialized
- * otherwise it returns NULL;
- */
-static prand_hash *
-get_hmac_key(int step, int block)
-{
-
- u_char *buff;
- int temp = 0, n = 0;
- unsigned size = 70;
- DST_KEY *new_key = NULL;
- prand_hash *new = NULL;
-
- /* use key that is larger than digest algorithms (64) for key size */
- buff = malloc(size);
- if (buff == NULL)
- return (NULL);
- /* do not memset the allocated memory to get random bytes there */
- /* time of day is somewhat random especially in the last bytes */
- gettimeofday((struct timeval *) &buff[n], NULL);
- n += sizeof(struct timeval);
-
-/* get some semi random stuff in here stir it with micro seconds */
- if (n < size) {
- temp = dst_s_quick_random((int) buff[n - 1]);
- memcpy(&buff[n], &temp, sizeof(temp));
- n += sizeof(temp);
- }
-/* get the pid of this process and its parent */
- if (n < size) {
- temp = (int) getpid();
- memcpy(&buff[n], &temp, sizeof(temp));
- n += sizeof(temp);
- }
- if (n < size) {
- temp = (int) getppid();
- memcpy(&buff[n], &temp, sizeof(temp));
- n += sizeof(temp);
- }
-/* get the user ID */
- if (n < size) {
- temp = (int) getuid();
- memcpy(&buff[n], &temp, sizeof(temp));
- n += sizeof(temp);
- }
-#ifndef GET_HOST_ID_MISSING
- if (n < size) {
- temp = (int) gethostid();
- memcpy(&buff[n], &temp, sizeof(temp));
- n += sizeof(temp);
- }
-#endif
-/* get some more random data */
- if (n < size) {
- temp = dst_s_quick_random((int) buff[n - 1]);
- memcpy(&buff[n], &temp, sizeof(temp));
- }
-/* covert this into a HMAC key */
- new_key = dst_buffer_to_key("", KEY_HMAC_MD5, 0, 0, buff, size);
- SAFE_FREE(buff);
-
-/* get the control structure */
- if ((new = malloc(sizeof(prand_hash))) == NULL)
- return (NULL);
- new->digested = new->curr = 0;
- new->step = step;
- new->block = block;
- new->key = new_key;
- if (dst_sign_data(SIG_MODE_INIT, new_key, &new->ctx, NULL, 0, NULL, 0)) {
- SAFE_FREE(new);
- return (NULL);
- }
-
- return (new);
-}
-
-/*
- * own_random()
- * This function goes out and from various sources tries to generate enough
- * semi random data that a hash function can generate a random data.
- * This function will iterate between the two main random source sources,
- * information from programs and directories in random order.
- * This function return the number of bytes added to the random output buffer.
- */
-static unsigned
-own_random(dst_work *work)
-{
- int dir = 0, b;
- int bytes, n, cmd = 0, dig = 0;
-/*
- * now get the initial seed to put into the quick random function from
- * the address of the work structure
- */
- bytes = (int) getpid();
-/*
- * proceed while needed
- */
- while (work->filled < work->needed) {
- EREPORT(("own_random r %08x b %6d t %6d f %6d\n",
- ran_val, bytes, work->in_temp, work->filled));
-/* pick a random number in the range of 0..7 based on that random number
- * perform some operations that yield random data
- */
- n = (dst_s_quick_random(bytes) >> DST_SHIFT) & 0x07;
- switch (n) {
- case 0:
- case 3:
- if (sizeof(cmds) > 2 *sizeof(*cmds)) {
- b = unix_cmd(work);
- cmd += b;
- }
- break;
-
- case 1:
- case 7:
- if (sizeof(dirs) > 2 *sizeof(*dirs)) {
- b = do_ls(work);
- dir += b;
- }
- break;
-
- case 4:
- case 5:
- /* retry getting data from /dev/random */
- b = get_dev_random(&work->output[work->filled],
- work->needed - work->filled);
- if (b > 0)
- work->filled += b;
- break;
-
- case 6:
- if (sizeof(files) > 2 * sizeof(*files)) {
- b = digest_file(work);
- dig += b;
- }
- break;
-
- case 2:
- default: /* to make sure we make some progress */
- work->output[work->filled++] = 0xff &
- dst_s_quick_random(bytes);
- b = 1;
- break;
- }
- if (b > 0)
- bytes += b;
- }
- return (work->filled);
-}
-
-
-/*
- * dst_s_random() This function will return the requested number of bytes
- * of randomness to the caller it will use the best available sources of
- * randomness.
- * The current order is to use /dev/random, precalculated randomness, and
- * finally use some system calls and programs to generate semi random data
- * that is then digested to generate randomness.
- * This function is thread safe as each thread uses its own context, but
- * concurrent treads will affect each other as they update shared state
- * information.
- * It is strongly recommended that this function be called requesting a size
- * that is not a multiple of the output of the hash function used.
- *
- * If /dev/random is not available this function is not suitable to generate
- * large amounts of data, rather it is suitable to seed a pseudo-random
- * generator
- * Returns the number of bytes put in the output buffer
- */
-int
-dst_s_random(u_char *output, unsigned size)
-{
- int n = 0, i;
- unsigned s;
- static u_char old_unused[DST_HASH_SIZE * DST_NUM_HASHES];
- static unsigned unused = 0;
-
- if (size <= 0 || output == NULL)
- return (0);
-
- if (size >= 2048)
- return (-1);
- /*
- * Read from /dev/random
- */
- n = get_dev_random(output, size);
- /*
- * If old data is available and needed use it
- */
- if (n < size && unused > 0) {
- unsigned need = size - n;
- if (unused <= need) {
- memcpy(output, old_unused, unused);
- n += unused;
- unused = 0;
- } else {
- memcpy(output, old_unused, need);
- n += need;
- unused -= need;
- memcpy(old_unused, &old_unused[need], unused);
- }
- }
- /*
- * If we need more use the simulated randomness here.
- */
- if (n < size) {
- dst_work *my_work = (dst_work *) malloc(sizeof(dst_work));
- if (my_work == NULL)
- return (n);
- my_work->needed = size - n;
- my_work->filled = 0;
- my_work->output = (u_char *) malloc(my_work->needed +
- DST_HASH_SIZE *
- DST_NUM_HASHES);
- my_work->file_digest = NULL;
- if (my_work->output == NULL) {
- SAFE_FREE(my_work);
- return (n);
- }
- memset(my_work->output, 0x0, my_work->needed);
-/* allocate upto 4 different HMAC hash functions out of order */
-#if DST_NUM_HASHES >= 3
- my_work->hash[2] = get_hmac_key(3, DST_RANDOM_BLOCK_SIZE / 2);
-#endif
-#if DST_NUM_HASHES >= 2
- my_work->hash[1] = get_hmac_key(7, DST_RANDOM_BLOCK_SIZE / 6);
-#endif
-#if DST_NUM_HASHES >= 4
- my_work->hash[3] = get_hmac_key(5, DST_RANDOM_BLOCK_SIZE / 4);
-#endif
- my_work->hash[0] = get_hmac_key(1, DST_RANDOM_BLOCK_SIZE);
- if (my_work->hash[0] == NULL) { /* if failure bail out */
- for (i = 1; i < DST_NUM_HASHES; i++) {
- if (my_work->hash[i] != NULL) {
- dst_free_key(my_work->hash[i]->key);
- SAFE_FREE(my_work->hash[i]);
- }
- }
- SAFE_FREE(my_work->output);
- SAFE_FREE(my_work);
- return (n);
- }
- s = own_random(my_work);
-/* if more generated than needed store it for future use */
- if (s >= my_work->needed) {
- EREPORT(("dst_s_random(): More than needed %d >= %d\n",
- s, my_work->needed));
- memcpy(&output[n], my_work->output, my_work->needed);
- n += my_work->needed;
- /* saving unused data for next time */
- unused = s - my_work->needed;
- if (unused > sizeof(old_unused)) {
- unused = sizeof(old_unused);
- }
- memcpy(old_unused, &my_work->output[my_work->needed],
- unused);
- } else {
- /* XXXX This should not happen */
- EREPORT(("Not enough %d >= %d\n", s, my_work->needed));
- memcpy(&output[n], my_work->output, s);
- n += my_work->needed;
- }
-
-/* delete the allocated work area */
- for (i = 0; i < DST_NUM_HASHES; i++) {
- if (my_work->hash[i] != NULL) {
- dst_free_key(my_work->hash[i]->key);
- SAFE_FREE(my_work->hash[i]);
- }
- }
- SAFE_FREE(my_work->output);
- SAFE_FREE(my_work);
- }
- return (n);
-}
-
-/*
- * A random number generator that is fast and strong
- * this random number generator is based on HASHing data,
- * the input to the digest function is a collection of <NUMBER_OF_COUNTERS>
- * counters that is incremented between digest operations
- * each increment operation amortizes to 2 bits changed in that value
- * for 5 counters thus the input will amortize to have 10 bits changed
- * The counters are initially set using the strong random function above
- * the HMAC key is selected by the same method as the HMAC keys for the
- * strong random function.
- * Each set of counters is used for 2^25 operations
- *
- * returns the number of bytes written to the output buffer
- * or negative number in case of error
- */
-int
-dst_s_semi_random(u_char *output, unsigned size)
-{
- static u_int32_t counter[DST_NUMBER_OF_COUNTERS];
- static u_char semi_old[DST_HASH_SIZE];
- static int semi_loc = 0, cnt = 0;
- static unsigned hb_size = 0;
- static DST_KEY *my_key = NULL;
- prand_hash *hash;
- unsigned out = 0;
- unsigned i;
- int n, res;
-
- if (output == NULL || size <= 0)
- return (-2);
-
-/* check if we need a new key */
- if (my_key == NULL || cnt > (1 << 25)) { /* get HMAC KEY */
- if (my_key)
- my_key->dk_func->destroy(my_key);
- if ((hash = get_hmac_key(1, DST_RANDOM_BLOCK_SIZE)) == NULL)
- return (0);
- my_key = hash->key;
-/* check if the key works stir the new key using some old random data */
- hb_size = dst_sign_data(SIG_MODE_ALL, my_key, NULL,
- (u_char *) counter, sizeof(counter),
- semi_old, sizeof(semi_old));
- if (hb_size <= 0) {
- EREPORT(("dst_s_semi_random() Sign of alg %d failed %d\n",
- my_key->dk_alg, hb_size));
- return (-1);
- }
-/* new set the counters to random values */
- dst_s_random((u_char *) counter, sizeof(counter));
- cnt = 0;
- }
-/* if old data around use it first */
- if (semi_loc < hb_size) {
- if (size <= hb_size - semi_loc) { /* need less */
- memcpy(output, &semi_old[semi_loc], size);
- semi_loc += size;
- return (size); /* DONE */
- } else {
- out = hb_size - semi_loc;
- memcpy(output, &semi_old[semi_loc], out);
- semi_loc += out;
- }
- }
-/* generate more random stuff */
- while (out < size) {
- /*
- * modify at least one bit by incrementing at least one counter
- * based on the last bit of the last counter updated update
- * the next one.
- * minimally this operation will modify at least 1 bit,
- * amortized 2 bits
- */
- for (n = 0; n < DST_NUMBER_OF_COUNTERS; n++)
- i = (int) counter[n]++;
-
- res = dst_sign_data(SIG_MODE_ALL, my_key, NULL,
- (u_char *) counter, hb_size,
- semi_old, sizeof(semi_old));
- if (res < 0) {
- return res;
- }
- i = (unsigned) res;
- if (i != hb_size)
- EREPORT(("HMAC SIGNATURE FAILURE %d\n", i));
- cnt++;
- if (size - out < i) /* Not all data is needed */
- semi_loc = i = size - out;
- memcpy(&output[out], semi_old, i);
- out += i;
- }
- return (out);
-}