#include "k5-int.h"
#include "enc_provider.h"
#include <assert.h>
#include "k5-thread.h"
#include "yarrow.h"
static Yarrow_CTX y_ctx;
static int init_error;
#define yarrow_lock krb5int_yarrow_lock
k5_mutex_t yarrow_lock = K5_MUTEX_PARTIAL_INITIALIZER;
static size_t
entropy_estimate (unsigned int randsource, size_t length)
{
switch (randsource) {
case KRB5_C_RANDSOURCE_OLDAPI:
return (4*length);
case KRB5_C_RANDSOURCE_OSRAND:
return (8*length);
case KRB5_C_RANDSOURCE_TRUSTEDPARTY:
return (4*length);
case KRB5_C_RANDSOURCE_TIMING:return (2);
case KRB5_C_RANDSOURCE_EXTERNAL_PROTOCOL:
return (0);
default:
abort();
}
return (0);
}
int krb5int_prng_init(void)
{
unsigned i;
int yerr;
yerr = k5_mutex_finish_init(&yarrow_lock);
if (yerr)
return yerr;
yerr = krb5int_yarrow_init (&y_ctx, NULL);
if ((yerr != YARROW_OK) && (yerr != YARROW_NOT_SEEDED))
return KRB5_CRYPTO_INTERNAL;
for (i=0; i < KRB5_C_RANDSOURCE_MAX; i++ ) {
unsigned source_id;
if (krb5int_yarrow_new_source (&y_ctx, &source_id) != YARROW_OK )
return KRB5_CRYPTO_INTERNAL;
assert (source_id == i);
}
return 0;
}
krb5_error_code KRB5_CALLCONV
krb5_c_random_add_entropy (krb5_context context, unsigned int randsource,
const krb5_data *data)
{
int yerr;
yerr = krb5int_crypto_init();
if (yerr)
return yerr;
yerr = krb5int_yarrow_input (&y_ctx, randsource,
data->data, data->length,
entropy_estimate (randsource, data->length));
if (yerr != YARROW_OK)
return (KRB5_CRYPTO_INTERNAL);
return (0);
}
krb5_error_code KRB5_CALLCONV
krb5_c_random_seed (krb5_context context, krb5_data *data)
{
return krb5_c_random_add_entropy (context, KRB5_C_RANDSOURCE_OLDAPI, data);
}
krb5_error_code KRB5_CALLCONV
krb5_c_random_make_octets(krb5_context context, krb5_data *data)
{
int yerr;
yerr = krb5int_yarrow_output (&y_ctx, data->data, data->length);
if (yerr == YARROW_NOT_SEEDED) {
yerr = krb5int_yarrow_reseed (&y_ctx, YARROW_SLOW_POOL);
if (yerr == YARROW_OK)
yerr = krb5int_yarrow_output (&y_ctx, data->data, data->length);
}
if ( yerr != YARROW_OK)
return (KRB5_CRYPTO_INTERNAL);
return(0);
}
void krb5int_prng_cleanup (void)
{
krb5int_yarrow_final (&y_ctx);
k5_mutex_destroy(&yarrow_lock);
}
#if defined(_WIN32)
krb5_error_code KRB5_CALLCONV
krb5_c_random_os_entropy (krb5_context context, int strong, int *success)
{
if (success)
*success = 0;
return 0;
}
#else
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
static int
read_entropy_from_device (krb5_context context, const char *device)
{
krb5_data data;
struct stat sb;
int fd;
unsigned char buf[YARROW_SLOW_THRESH/8];
int left;
fd = open (device, O_RDONLY);
if (fd == -1)
return 0;
if (fstat (fd, &sb) == -1)
return 0;
if (S_ISREG(sb.st_mode)) {
close(fd);
return 0;
}
for (left = sizeof (buf); left > 0;) {
ssize_t count;
count = read (fd, &buf, (unsigned) left);
if (count <= 0) {
close(fd);
return 0;
}
left -= count;
}
close (fd);
data.length = sizeof (buf);
data.data = ( char * ) buf;
if ( krb5_c_random_add_entropy (context, KRB5_C_RANDSOURCE_OSRAND,
&data) != 0) {
return 0;
}
return 1;
}
krb5_error_code KRB5_CALLCONV
krb5_c_random_os_entropy (krb5_context context,
int strong, int *success)
{
int unused;
int *oursuccess = success?success:&unused;
*oursuccess = 0;
if (strong) {
if (read_entropy_from_device (context, "/dev/random"))
*oursuccess = 1;
}
if (read_entropy_from_device (context, "/dev/urandom"))
*oursuccess = 1;
return 0;
}
#endif