NetBSD/dist/dhcp/dst/prandom.c
drochner ae6fdb5ce4 rather than adding braces in the code, define the offending macro
to "(void)0". This is what similar code in bind9 (up to 9.5) does.
(native NetBSD code usually does "do { } while (/* CONSTCOND */ 0)"
in this case)
Anyway, I've checked the code, didn't find ambiguities due to
the empty statements, so the whole thing is harmless.
2010-01-26 19:11:00 +00:00

863 lines
24 KiB
C

#ifndef LINT
static const char rcsid[] = "$Header: /cvsroot/src/dist/dhcp/dst/Attic/prandom.c,v 1.6 2010/01/26 19:11:00 drochner Exp $";
#endif
/*
* 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 "minires/minires.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 structues like
* stat and time wrap over blocks. This adds certain uncertanty 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 dictatates how many bits we shift to the right before using a
*/
#ifndef DST_SHIFT
#define DST_SHIFT 9
#endif
/*
* An initalizer 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
/*
* these two data structure are used to process input data into digests,
*
* The first structure is containts 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 controlls 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 vlaues
*/
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 freqently 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, tb_i = 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));
tb_i += 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 successfull 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];
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);
name = files[f_cnt++];
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;
}
else if (i > 0)
my_digest(work, buf, (unsigned)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 adde 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 varialbe in the work sturcture 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);
tmp = tp;
for (cnt = 0, i = hash->curr; i < size; i += hash->step, cnt++)
*(tp++) = input[i];
/* calcutate 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 initalized
* 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 expecialy 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));
n += 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))
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 directores 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;
int start =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
*/
start = work->filled;
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
* finaly 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 ammounts 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)
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 */
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;
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++) {
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 initaly set using the strong random function above
* the HMAC key is selected by the same methold 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;
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 randome 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.
* minimaly this operation will modify at least 1 bit,
* amortized 2 bits
*/
for (n = 0; n < DST_NUMBER_OF_COUNTERS; n++)
i = (int) counter[n]++;
i = dst_sign_data(SIG_MODE_ALL, my_key, NULL,
(u_char *) counter, hb_size,
semi_old, sizeof(semi_old));
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);
}