aboutsummaryrefslogtreecommitdiff
path: root/src/lib/crypto/krb/yarrow/yarrow.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/crypto/krb/yarrow/yarrow.c')
-rw-r--r--src/lib/crypto/krb/yarrow/yarrow.c958
1 files changed, 958 insertions, 0 deletions
diff --git a/src/lib/crypto/krb/yarrow/yarrow.c b/src/lib/crypto/krb/yarrow/yarrow.c
new file mode 100644
index 0000000..ff25fa9
--- /dev/null
+++ b/src/lib/crypto/krb/yarrow/yarrow.c
@@ -0,0 +1,958 @@
+/* -*- Mode: C; c-file-style: "bsd" -*- */
+
+/*
+ * Yarrow - Cryptographic Pseudo-Random Number Generator
+ * Copyright (c) 2000 Zero-Knowledge Systems, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Zero-Knowledge Systems,
+ * Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Zero-Knowledge Systems, Inc. makes no representations
+ * about the suitability of this software for any purpose. It is
+ * provided "as is" without express or implied warranty.
+ *
+ * See the accompanying LICENSE file for more information.
+ */
+
+#include "k5-int.h"
+
+#include <string.h>
+#include <limits.h>
+#ifdef _WIN32
+#include "port-sockets.h"
+#else
+# include <unistd.h>
+# include <netinet/in.h>
+#endif
+#if !defined(YARROW_NO_MATHLIB)
+#include <math.h>
+#endif
+
+#define YARROW_IMPL
+#include "yarrow.h"
+#include "yhash.h"
+#include "ycipher.h"
+#include "ylock.h"
+#include "ystate.h"
+#include "yexcep.h"
+
+#if defined( YARROW_DEBUG ) || defined( YARROW_TRACE )
+# include <stdio.h>
+#endif
+
+#if defined( YARROW_TRACE )
+extern int yarrow_verbose;
+#define TRACE( x ) do { if (yarrow_verbose) { x } } while (0)
+#else
+#define TRACE( x )
+#endif
+
+#if defined(macintosh)
+# define make_big_endian32(x) (x)
+#else
+# define make_big_endian32(x) htonl(x)
+#endif
+
+#if defined( YARROW_DEBUG )
+static void hex_print(FILE* f, const char* var, void* data, size_t size);
+#endif
+
+static void block_increment( void* block, const int sz );
+#if defined( YARROW_SAVE_STATE )
+static int Yarrow_Load_State( Yarrow_CTX *y );
+static int Yarrow_Save_State( Yarrow_CTX *y );
+#endif
+
+static int yarrow_gate_locked(Yarrow_CTX* y);
+
+static const byte zero_block[CIPHER_BLOCK_SIZE] = { 0, };
+
+static const char* const yarrow_str_error[] = {
+ "ok",
+ "failed",
+ "failed: uninitialized",
+ "failed: already initialized",
+ "failed: no driver",
+ "failed: can't open driver",
+ "failed: invalid source id",
+ "failed: no more source ids available",
+ "failed: invalid argument",
+ "failed: insufficient privileges",
+ "failed: out of memory",
+ "failed: resource exhausted",
+ "failed: not enough entropy to generate output",
+ "failed: locking error",
+ "failed: no state to load",
+ "failed: state load or save failed",
+ "failed: not implemented"
+};
+
+/* calculate limits after initialization */
+
+static void krb5int_yarrow_init_Limits(Yarrow_CTX* y)
+{
+ double tmp1, tmp2, limit;
+ /* max number of gates between reseeds -> exceed this, do forced reseed */
+
+ /* #oututs <= min(2^n, 2^(k/3).Pg) */
+
+ /* => #gates <= min(2^n/Pg, 2^(k/3)) */
+
+ tmp1 = POW_CIPHER_BLOCK_SIZE / y->Pg;
+ tmp2 = POW_CIPHER_KEY_SIZE;
+ limit = min(tmp1, tmp2);
+ if (limit < COUNTER_MAX)
+ {
+ y->gates_limit = limit;
+ }
+ else
+ {
+ y->gates_limit = COUNTER_MAX;
+ }
+}
+
+static int yarrow_reseed_locked( Yarrow_CTX* y, int pool );
+
+/* if the program was forked, the child must not operate on the same
+ PRNG state */
+#ifdef YARROW_DETECT_FORK
+
+static int
+yarrow_input_locked( Yarrow_CTX* y, unsigned source_id,
+ const void *sample,
+ size_t size, size_t entropy_bits );
+
+static int Yarrow_detect_fork(Yarrow_CTX *y)
+{
+ pid_t newpid;
+ EXCEP_DECL;
+
+ /* this does not work for multi-threaded apps if threads have different
+ * pids */
+ newpid = getpid();
+ if ( y->pid != newpid )
+ {
+ /* we input the pid twice, so it will get into the fast pool at least once
+ * Then we reseed. This doesn't really increase entropy, but does make the
+ * streams distinct assuming we already have good entropy*/
+ y->pid = newpid;
+ TRY (yarrow_input_locked (y, 0, &newpid,
+ sizeof (newpid), 0));
+ TRY (yarrow_input_locked (y, 0, &newpid,
+ sizeof (newpid), 0));
+ TRY (yarrow_reseed_locked (y, YARROW_FAST_POOL));
+ }
+
+ CATCH:
+ EXCEP_RET;
+}
+
+#else
+
+#define Yarrow_detect_fork(x) (YARROW_OK)
+
+#endif
+
+static void Yarrow_Make_Seeded( Yarrow_CTX* y )
+{
+ TRACE( printf( "SEEDED," ); );
+ y->seeded = 1;
+
+ /* now we are seeded switch to _THRESH values */
+
+ y->slow_thresh = YARROW_SLOW_THRESH;
+ y->fast_thresh = YARROW_FAST_THRESH;
+ y->slow_k_of_n_thresh = YARROW_K_OF_N_THRESH;
+}
+
+YARROW_DLL
+int krb5int_yarrow_init(Yarrow_CTX* y, const char *filename)
+{
+ EXCEP_DECL;
+ int locked = 0;
+
+ if (!y) { THROW( YARROW_BAD_ARG ); }
+ TRY( LOCK() );
+ locked = 1;
+
+ y->seeded = 0;
+ y->saved = 0;
+
+#if defined( YARROW_DETECT_FORK )
+ y->pid = getpid();
+#endif
+
+ y->entropyfile = filename;
+ y->num_sources = 0;
+ mem_zero(y->C, sizeof(y->C));
+ HASH_Init(&y->pool[YARROW_FAST_POOL]);
+ HASH_Init(&y->pool[YARROW_SLOW_POOL]);
+
+ mem_zero(y->K, sizeof(y->K));
+
+ mem_zero(&y->cipher, sizeof(y->cipher));
+
+ TRY (krb5int_yarrow_cipher_init(&y->cipher, y->K));
+ y->out_left = 0;
+ y->out_count = 0;
+ y->gate_count = 0;
+ y->Pg = YARROW_OUTPUTS_PER_GATE;
+ y->Pt[YARROW_FAST_POOL] = YARROW_FAST_PT;
+ y->Pt[YARROW_SLOW_POOL] = YARROW_SLOW_PT;
+ y->slow_k_of_n = 0;
+
+ /* start with INIT_THRESH values, after seeded, switch to THRESH values */
+
+ y->slow_thresh = YARROW_SLOW_INIT_THRESH;
+ y->fast_thresh = YARROW_FAST_INIT_THRESH;
+ y->slow_k_of_n_thresh = YARROW_K_OF_N_INIT_THRESH;
+
+ krb5int_yarrow_init_Limits(y);
+
+#if defined( YARROW_SAVE_STATE )
+ if ( y->entropyfile != NULL )
+ {
+ int ret = Yarrow_Load_State( y );
+ if ( ret != YARROW_OK && ret != YARROW_NO_STATE )
+ {
+ THROW( ret );
+ }
+
+ /* if load suceeded then write new state back immediately
+ */
+
+ /* Also check that it's not already saved, because the reseed in
+ * Yarrow_Load_State may trigger a save
+ */
+
+ if ( ret == YARROW_OK && !y->saved )
+ {
+ TRY( Yarrow_Save_State( y ) );
+ }
+ }
+#endif
+
+ if ( !y->seeded )
+ {
+ THROW( YARROW_NOT_SEEDED );
+ }
+
+ CATCH:
+ if ( locked ) { TRY( UNLOCK() ); }
+ EXCEP_RET;
+}
+
+static
+int yarrow_input_maybe_locking( Yarrow_CTX* y, unsigned source_id,
+ const void* sample,
+ size_t size, size_t entropy_bits,
+ int do_lock )
+{
+ EXCEP_DECL;
+ int ret;
+ int locked = 0;
+ Source* source;
+ size_t new_entropy;
+ size_t estimate;
+
+ if (do_lock) {
+ TRY( LOCK() );
+ locked = 1;
+ }
+ k5_assert_locked(&krb5int_yarrow_lock);
+
+ if (!y) { THROW( YARROW_BAD_ARG ); }
+
+ if (source_id >= y->num_sources) { THROW( YARROW_BAD_SOURCE ); }
+
+ source = &y->source[source_id];
+
+ if(source->pool != YARROW_FAST_POOL && source->pool != YARROW_SLOW_POOL)
+ {
+ THROW( YARROW_BAD_SOURCE );
+ }
+
+ /* hash in the sample */
+
+ HASH_Update(&y->pool[source->pool], (const void*)sample, size);
+
+ /* only update entropy estimate if pool is not full */
+
+ if ( (source->pool == YARROW_FAST_POOL &&
+ source->entropy[source->pool] < y->fast_thresh) ||
+ (source->pool == YARROW_SLOW_POOL &&
+ source->entropy[source->pool] < y->slow_thresh) )
+ {
+ new_entropy = min(entropy_bits, size * 8 * YARROW_ENTROPY_MULTIPLIER);
+ if (source->estimator)
+ {
+ estimate = source->estimator(sample, size);
+ new_entropy = min(new_entropy, estimate);
+ }
+ source->entropy[source->pool] += new_entropy;
+ if ( source->entropy[source->pool] > YARROW_POOL_SIZE )
+ {
+ source->entropy[source->pool] = YARROW_POOL_SIZE;
+ }
+
+ if (source->pool == YARROW_FAST_POOL)
+ {
+ if (source->entropy[YARROW_FAST_POOL] >= y->fast_thresh)
+ {
+ ret = yarrow_reseed_locked(y, YARROW_FAST_POOL);
+ if ( ret != YARROW_OK && ret != YARROW_NOT_SEEDED )
+ {
+ THROW( ret );
+ }
+ }
+ }
+ else
+ {
+ if (!source->reached_slow_thresh &&
+ source->entropy[YARROW_SLOW_POOL] >= y->slow_thresh)
+ {
+ source->reached_slow_thresh = 1;
+ y->slow_k_of_n++;
+ if (y->slow_k_of_n >= y->slow_k_of_n_thresh)
+ {
+ y->slow_k_of_n = 0;
+ ret = yarrow_reseed_locked(y, YARROW_SLOW_POOL);
+ if ( ret != YARROW_OK && ret != YARROW_NOT_SEEDED )
+ {
+ THROW( ret );
+ }
+ }
+ }
+ }
+ }
+
+ /* put samples in alternate pools */
+
+ source->pool = (source->pool + 1) % 2;
+
+ CATCH:
+ if ( locked ) { TRY( UNLOCK() ); }
+ EXCEP_RET;
+}
+
+YARROW_DLL
+int krb5int_yarrow_input( Yarrow_CTX* y, unsigned source_id,
+ const void* sample,
+ size_t size, size_t entropy_bits )
+{
+ return yarrow_input_maybe_locking(y, source_id, sample, size,
+ entropy_bits, 1);
+}
+
+static int
+yarrow_input_locked( Yarrow_CTX* y, unsigned source_id,
+ const void *sample,
+ size_t size, size_t entropy_bits )
+{
+ return yarrow_input_maybe_locking(y, source_id, sample, size,
+ entropy_bits, 0);
+}
+
+YARROW_DLL
+int krb5int_yarrow_new_source(Yarrow_CTX* y, unsigned* source_id)
+{
+ EXCEP_DECL;
+ int locked = 0;
+ Source* source;
+
+ if (!y) { THROW( YARROW_BAD_ARG ); }
+
+ TRY( LOCK() );
+ locked = 1;
+
+ if (y->num_sources + 1 > YARROW_MAX_SOURCES)
+ {
+ THROW( YARROW_TOO_MANY_SOURCES );
+ }
+
+ *source_id = y->num_sources;
+
+ source = &y->source[*source_id];
+
+ source->pool = YARROW_FAST_POOL;
+ source->entropy[YARROW_FAST_POOL] = 0;
+ source->entropy[YARROW_SLOW_POOL] = 0;
+ source->reached_slow_thresh = 0;
+ source->estimator = 0;
+
+ y->num_sources++;
+CATCH:
+ if ( locked ) { TRY( UNLOCK() ); }
+ EXCEP_RET;
+}
+
+int krb5int_yarrow_register_source_estimator(Yarrow_CTX* y, unsigned source_id,
+ estimator_fn* fptr)
+{
+ EXCEP_DECL;
+ Source* source;
+
+ if (!y) { THROW( YARROW_BAD_ARG ); }
+ if (source_id >= y->num_sources) { THROW( YARROW_BAD_SOURCE ); }
+
+ source = &y->source[source_id];
+
+ source->estimator = fptr;
+
+ CATCH:
+ EXCEP_RET;
+}
+
+static int krb5int_yarrow_output_Block( Yarrow_CTX* y, void* out )
+{
+ EXCEP_DECL;
+
+ if (!y || !out) { THROW( YARROW_BAD_ARG ); }
+
+ TRACE( printf( "OUT," ); );
+
+ /* perform a gate function after Pg outputs */
+
+ y->out_count++;
+ if (y->out_count >= y->Pg)
+ {
+ y->out_count = 0;
+ TRY( yarrow_gate_locked( y ) );
+
+ /* require new seed after reaching gates_limit */
+
+ y->gate_count++;
+ if ( y->gate_count >= y->gates_limit )
+ {
+ y->gate_count = 0;
+
+ /* not defined whether to do slow or fast reseed */
+
+ TRACE( printf( "OUTPUT LIMIT REACHED," ); );
+
+ TRY( yarrow_reseed_locked( y, YARROW_SLOW_POOL ) );
+ }
+ }
+
+ /* C <- (C + 1) mod 2^n */
+
+ block_increment( y->C, CIPHER_BLOCK_SIZE );
+
+ /* R <- E_k(C) */
+
+ TRY ( krb5int_yarrow_cipher_encrypt_block ( &y->cipher, y->C, out ));
+
+#if defined(YARROW_DEBUG)
+ printf("===\n");
+ hex_print( stdout, "output: C", y->C, CIPHER_BLOCK_SIZE );
+ hex_print( stdout, "output: K", y->K, CIPHER_KEY_SIZE );
+ hex_print( stdout, "output: O", out, CIPHER_BLOCK_SIZE );
+#endif
+ CATCH:
+ EXCEP_RET;
+}
+
+YARROW_DLL
+int krb5int_yarrow_status( Yarrow_CTX* y, int *num_sources, unsigned *source_id,
+ size_t *entropy_bits, size_t *entropy_max )
+{
+ EXCEP_DECL;
+ int num = y->slow_k_of_n_thresh;
+ int source = -1;
+ int emax = y->slow_thresh;
+ size_t entropy = 0;
+ unsigned i;
+
+ if (!y) { THROW( YARROW_BAD_ARG ); }
+ TRY( Yarrow_detect_fork( y ) );
+
+ if (num_sources) { *num_sources = num; }
+ if (source_id) { *source_id = -1; }
+ if (entropy_bits) { *entropy_bits = 0; }
+ if (entropy_max) { *entropy_max = emax; }
+
+ if (y->seeded)
+ {
+ if (num_sources) { *num_sources = 0; }
+ if (entropy_bits) { *entropy_bits = emax; }
+ THROW( YARROW_OK );
+ }
+
+ for (i = 0; i < y->num_sources; i++)
+ {
+ if (y->source[i].entropy[YARROW_SLOW_POOL] >= y->slow_thresh)
+ {
+ num--;
+ }
+ else if (y->source[i].entropy[YARROW_SLOW_POOL] > entropy)
+ {
+ source = i;
+ entropy = y->source[i].entropy[YARROW_SLOW_POOL];
+ }
+ }
+
+ if (num_sources) { *num_sources = num; }
+ if (source_id) { *source_id = source; }
+ if (entropy_bits) { *entropy_bits = entropy; }
+ THROW( YARROW_NOT_SEEDED );
+
+ CATCH:
+ EXCEP_RET;
+}
+
+static int yarrow_output_locked(Yarrow_CTX*, void*, size_t);
+
+YARROW_DLL
+int krb5int_yarrow_output( Yarrow_CTX* y, void* out, size_t size )
+{
+ EXCEP_DECL;
+ TRY( LOCK() );
+ TRY( yarrow_output_locked(y, out, size));
+CATCH:
+ UNLOCK();
+ EXCEP_RET;
+}
+
+static
+int yarrow_output_locked( Yarrow_CTX* y, void* out, size_t size )
+{
+ EXCEP_DECL;
+ size_t left;
+ char* outp;
+ size_t use;
+
+ if (!y || !out) { THROW( YARROW_BAD_ARG ); }
+ TRY( Yarrow_detect_fork( y ) );
+
+ if (!y->seeded) { THROW( YARROW_NOT_SEEDED ); }
+
+ left = size;
+ outp = out;
+
+ if (y->out_left > 0)
+ {
+ use = min(left, y->out_left);
+ mem_copy(outp, y->out + CIPHER_BLOCK_SIZE - y->out_left, use);
+ left -= use;
+ y->out_left -= use;
+ outp += use;
+ }
+
+ for ( ;
+ left >= CIPHER_BLOCK_SIZE;
+ left -= CIPHER_BLOCK_SIZE, outp += CIPHER_BLOCK_SIZE)
+ {
+ TRY( krb5int_yarrow_output_Block(y, outp) );
+ }
+
+ if (left > 0)
+ {
+ TRY( krb5int_yarrow_output_Block(y, y->out) );
+ mem_copy(outp, y->out, left);
+ y->out_left = CIPHER_BLOCK_SIZE - left;
+ }
+
+ CATCH:
+ EXCEP_RET;
+}
+
+static int yarrow_gate_locked(Yarrow_CTX* y)
+{
+ EXCEP_DECL;
+ byte new_K[CIPHER_KEY_SIZE];
+
+ if (!y) { THROW( YARROW_BAD_ARG ); }
+
+ TRACE( printf( "GATE[" ); );
+
+ /* K <- Next k bits of PRNG output */
+
+ TRY( yarrow_output_locked(y, new_K, CIPHER_KEY_SIZE) );
+ mem_copy(y->K, new_K, CIPHER_KEY_SIZE);
+
+ /* need to resetup the key schedule as the key has changed */
+
+ TRY (krb5int_yarrow_cipher_init(&y->cipher, y->K));
+
+ CATCH:
+ TRACE( printf( "]," ); );
+ mem_zero(new_K, sizeof(new_K));
+ EXCEP_RET;
+}
+
+int krb5int_yarrow_gate(Yarrow_CTX* y)
+{
+ EXCEP_DECL;
+ byte new_K[CIPHER_KEY_SIZE];
+
+ if (!y) { THROW( YARROW_BAD_ARG ); }
+
+ TRACE( printf( "GATE[" ); );
+
+ /* K <- Next k bits of PRNG output */
+
+ TRY( krb5int_yarrow_output(y, new_K, CIPHER_KEY_SIZE) );
+ mem_copy(y->K, new_K, CIPHER_KEY_SIZE);
+
+ /* need to resetup the key schedule as the key has changed */
+
+ TRY (krb5int_yarrow_cipher_init(&y->cipher, y->K));
+
+ CATCH:
+ TRACE( printf( "]," ); );
+ mem_zero(new_K, sizeof(new_K));
+ EXCEP_RET;
+}
+
+#if defined( YARROW_SAVE_STATE )
+static int Yarrow_Load_State( Yarrow_CTX *y )
+{
+ EXCEP_DECL;
+ Yarrow_STATE state;
+
+ if ( !y ) { THROW( YARROW_BAD_ARG ); }
+
+ if ( y->entropyfile )
+ {
+ TRY( STATE_Load(y->entropyfile, &state) );
+ TRACE( printf( "LOAD STATE," ); );
+
+#if defined( YARROW_DEBUG )
+ hex_print( stderr, "state.load", state.seed, sizeof(state.seed));
+#endif
+
+ /* what to do here is not defined by the Yarrow paper */
+ /* this is a place holder until we get some clarification */
+
+ HASH_Update( &y->pool[YARROW_FAST_POOL],
+ state.seed, sizeof(state.seed) );
+
+ Yarrow_Make_Seeded( y );
+
+ TRY( krb5int_yarrow_reseed(y, YARROW_FAST_POOL) );
+ }
+ CATCH:
+ mem_zero(state.seed, sizeof(state.seed));
+ EXCEP_RET;
+}
+
+static int Yarrow_Save_State( Yarrow_CTX *y )
+{
+ EXCEP_DECL;
+ Yarrow_STATE state;
+
+ if ( !y ) { THROW( YARROW_BAD_ARG ); }
+
+ if ( y->entropyfile && y->seeded )
+ {
+ TRACE( printf( "SAVE STATE[" ); );
+ TRY( krb5int_yarrow_output( y, state.seed, sizeof(state.seed) ) );
+ TRY( STATE_Save(y->entropyfile, &state) );
+ }
+ y->saved = 1;
+# if defined(YARROW_DEBUG)
+ hex_print(stdout, "state.save", state.seed, sizeof(state.seed));
+# endif
+
+ CATCH:
+ TRACE( printf( "]," ); );
+ mem_zero(state.seed, sizeof(state.seed));
+ EXCEP_RET;
+}
+
+#endif
+
+static int yarrow_reseed_locked(Yarrow_CTX* y, int pool)
+{
+ EXCEP_DECL;
+ HASH_CTX* fast_pool;
+ HASH_CTX* slow_pool;
+ byte digest[HASH_DIGEST_SIZE];
+ HASH_CTX hash;
+ byte v_0[HASH_DIGEST_SIZE];
+ byte v_i[HASH_DIGEST_SIZE];
+ krb5_ui_4 big_endian_int32;
+ COUNTER i;
+
+ k5_assert_locked(&krb5int_yarrow_lock);
+ if (!y) { THROW( YARROW_BAD_ARG ); }
+ fast_pool = &y->pool[YARROW_FAST_POOL];
+ slow_pool = &y->pool[YARROW_SLOW_POOL];
+ if( pool != YARROW_FAST_POOL && pool != YARROW_SLOW_POOL )
+ {
+ THROW( YARROW_BAD_ARG );
+ }
+
+ TRACE( printf( "%s RESEED,",
+ pool == YARROW_SLOW_POOL ? "SLOW" : "FAST" ); );
+
+ if (pool == YARROW_SLOW_POOL)
+ {
+ /* SLOW RESEED */
+
+ /* feed hash of slow pool into the fast pool */
+
+
+ HASH_Final(slow_pool, digest);
+
+ /* Each pool contains the running hash of all inputs fed into it
+ * since it was last used to carry out a reseed -- this implies
+ * that the pool must be reinitialized after a reseed
+ */
+
+ HASH_Init(slow_pool); /* reinitialize slow pool */
+ HASH_Update(fast_pool, digest, sizeof(digest));
+
+ if (y->seeded == 0)
+ {
+ Yarrow_Make_Seeded( y );
+ }
+ }
+
+ /* step 1. v_0 <- hash of all inputs into fast pool */
+
+ HASH_Final(fast_pool, &v_0);
+ HASH_Init(fast_pool); /* reinitialize fast pool */
+
+ /* v_i <- v_0 */
+
+ mem_copy( v_i, v_0, sizeof(v_0) );
+
+ /* step 2. v_i = h(v_{i-1}|v_0|i) for i = 1,..,Pt */
+
+ /* note: this code has to work for Pt = 0 also */
+
+ for ( i = 0; i < y->Pt[pool]; i++ )
+ {
+ HASH_Init(&hash);
+ HASH_Update(&hash, v_i, sizeof(v_i));
+ HASH_Update(&hash, v_0, sizeof(v_0));
+ big_endian_int32 = make_big_endian32(0); /* MS word */
+ HASH_Update(&hash, &big_endian_int32, sizeof(krb5_ui_4));
+ big_endian_int32 = make_big_endian32(i & 0xFFFFFFFF); /* LS word */
+ HASH_Update(&hash, &big_endian_int32, sizeof(krb5_ui_4));
+ HASH_Final(&hash, &v_i);
+ }
+
+ /* step3. K = h'(h(v_Pt|K)) */
+
+ /* t = h(v_Pt|K) */
+
+ HASH_Init(&hash);
+ HASH_Update(&hash, v_i, sizeof(v_i));
+ HASH_Update(&hash, y->K, sizeof(y->K));
+ HASH_Final(&hash, v_i);
+
+#if defined(YARROW_DEBUG)
+ hex_print(stdout, "old K", y->K, sizeof(y->K));
+#endif
+ /* K <- h'(t) */
+
+ TRY( krb5int_yarrow_stretch(v_i, HASH_DIGEST_SIZE, y->K, CIPHER_KEY_SIZE) );
+
+ /* need to resetup the key schedule as the key has changed */
+
+ TRY(krb5int_yarrow_cipher_init(&y->cipher, y->K));
+
+#if defined(YARROW_DEBUG)
+ hex_print(stdout, "new K", y->K, sizeof(y->K));
+#endif
+
+ /* step 4. C <- E_k(0) */
+
+#if defined(YARROW_DEBUG)
+ hex_print(stdout, "old C", y->C, sizeof(y->C));
+#endif
+ TRY (krb5int_yarrow_cipher_encrypt_block (&y->cipher, zero_block, y->C));
+#if defined(YARROW_DEBUG)
+ hex_print(stdout, "new C", y->C, sizeof(y->C));
+#endif
+
+ /* discard part output from previous key */
+
+ y->out_left = 0;
+
+ /* step 5. Reset all entropy estimate accumulators of the entropy
+ * accumulator to zero
+ */
+
+ for (i = 0; i < y->num_sources; i++)
+ {
+ y->source[i].entropy[pool] = 0;
+ if (pool == YARROW_SLOW_POOL)
+ {
+ /* if this is a slow reseed, reset the fast pool entropy
+ * accumulator also
+ */
+ y->source[i].entropy[YARROW_FAST_POOL] = 0;
+ y->source[i].reached_slow_thresh = 0;
+ }
+ }
+
+ /* step 7. If a seed file is in use, the next 2k bits of output
+ * are written to the seed file
+ */
+
+#if defined( YARROW_SAVE_STATE )
+ if ( y->seeded && y->entropyfile )
+ {
+ TRY( Yarrow_Save_State( y ) );
+ }
+#endif
+
+ CATCH:
+ /* step 6. Wipe the memory of all intermediate values
+ *
+ */
+
+ mem_zero( digest, sizeof(digest) );
+ mem_zero( &hash, sizeof(hash) );
+ mem_zero( v_0, sizeof(v_0) );
+ mem_zero( v_i, sizeof(v_i) );
+
+ EXCEP_RET;
+}
+int krb5int_yarrow_reseed(Yarrow_CTX* y, int pool)
+{
+ int r;
+ LOCK();
+ r = yarrow_reseed_locked(y, pool);
+ UNLOCK();
+ return r;
+}
+
+int krb5int_yarrow_stretch(const byte* m, size_t size, byte* out, size_t out_size)
+{
+ EXCEP_DECL;
+ const byte* s_i;
+ byte* outp;
+ int left;
+ unsigned int use;
+ HASH_CTX hash, save;
+ byte digest[HASH_DIGEST_SIZE];
+
+ if (m == NULL || size == 0 || out == NULL || out_size == 0)
+ {
+ THROW( YARROW_BAD_ARG );
+ }
+
+ /*
+ * s_0 = m
+ * s_1 = h(s_0 | ... | s_{i-1})
+ *
+ * h'(m, k) = first k bits of (s_0 | s_1 | ...)
+ *
+ */
+
+ outp = out;
+ left = out_size;
+
+ use = min(out_size, size);
+ mem_copy(outp, m, use); /* get k bits or as many as available */
+
+ s_i = (const byte*)m; /* pointer to s0 = m */
+ outp += use;
+ left -= use;
+
+ HASH_Init(&hash);
+ for ( ;
+ left > 0;
+ left -= HASH_DIGEST_SIZE)
+ {
+ HASH_Update(&hash, s_i, use);
+
+ /* have to save hash state to one side as HASH_final changes state */
+
+ mem_copy(&save, &hash, sizeof(hash));
+ HASH_Final(&hash, digest);
+
+ use = min(HASH_DIGEST_SIZE, left);
+ mem_copy(outp, digest, use);
+
+ /* put state back for next time */
+
+ mem_copy(&hash, &save, sizeof(hash));
+
+ s_i = outp; /* retain pointer to s_i */
+ outp += use;
+ }
+
+ CATCH:
+ mem_zero(&hash, sizeof(hash));
+ mem_zero(digest, sizeof(digest));
+
+ EXCEP_RET;
+}
+
+static void block_increment(void* block, const int sz)
+{
+ byte* b = block;
+ int i;
+
+ for (i = sz-1; (++b[i]) == 0 && i > 0; i--)
+ {
+ ; /* nothing */
+ }
+}
+
+YARROW_DLL
+int krb5int_yarrow_final(Yarrow_CTX* y)
+{
+ EXCEP_DECL;
+ int locked = 0;
+
+ if (!y) { THROW( YARROW_BAD_ARG ); }
+ TRY( LOCK() );
+ locked = 1;
+
+#if defined( YARROW_SAVE_STATE )
+ if ( y->seeded && y->entropyfile )
+ {
+ TRY( Yarrow_Save_State( y ) );
+ }
+#endif
+
+ CATCH:
+ if ( y )
+ {
+ krb5int_yarrow_cipher_final(&y->cipher);
+ mem_zero( y, sizeof(Yarrow_CTX) );
+ }
+ if ( locked ) { TRY( UNLOCK() ); }
+ EXCEP_RET;
+}
+
+YARROW_DLL
+const char* krb5int_yarrow_str_error( int err )
+{
+ err = 1-err;
+ if ( err < 0 || err >= sizeof( yarrow_str_error ) / sizeof( char* ) )
+ {
+ err = 1-YARROW_FAIL;
+ }
+ return yarrow_str_error[ err ];
+}
+
+#if defined(YARROW_DEBUG)
+static void hex_print(FILE* f, const char* var, void* data, size_t size)
+{
+ const char* conv = "0123456789abcdef";
+ size_t i;
+ char* p = (char*) data;
+ char c, d;
+
+ fprintf(f, var);
+ fprintf(f, " = ");
+ for (i = 0; i < size; i++)
+ {
+ c = conv[(p[i] >> 4) & 0xf];
+ d = conv[p[i] & 0xf];
+ fprintf(f, "%c%c", c, d);
+ }
+ fprintf(f, "\n");
+}
+#endif