/*
 * lib/crypto/des/random_key.c
 *
 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
 * Copyright 1996 by Lehman Brothers, Inc.
 * All Rights Reserved.
 *
 * Export of this software from the United States of America may
 *   require a specific license from the United States Government.
 *   It is the responsibility of any person or organization contemplating
 *   export to obtain such a license before exporting.
 * 
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
 * distribute this software and its documentation for any purpose and
 * without fee is hereby granted, 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 M.I.T. or Lehman Brothers not be used in advertising or
 * publicity pertaining to distribution of the software without
 * specific, written prior permission.  M.I.T. and Lehman Brothers
 * make no representations about the suitability of this software for
 * any purpose.  It is provided "as is" without express or implied
 * warranty.
 */

#include "k5-int.h"
#include "des_int.h"

static void mit_des_generate_random_key
	PROTOTYPE((mit_des_random_state * state, krb5_keyblock * randkey));


/*
        generate a random encryption key, allocating storage for it and
        filling in the keyblock address in *keyblock
 */

krb5_error_code
mit_des_random_key (eblock, state, keyblock)
    const krb5_encrypt_block * eblock;
    krb5_pointer state;
    krb5_keyblock ** keyblock;
{
    krb5_keyblock *randkey;
    int keysize = ((mit_des_random_state *)state)->eblock.crypto_entry->keysize;

    if (eblock == NULL)
	/* We are being called from the random number initialization routine */
	eblock = &((mit_des_random_state *)state)->eblock;

    if (!(randkey = (krb5_keyblock *)malloc(sizeof(*randkey))))
	return ENOMEM;
    if (!(randkey->contents = (krb5_octet *)malloc(keysize))) {
	krb5_xfree(randkey);
	return ENOMEM;
    }
    randkey->magic = KV5M_KEYBLOCK;
    randkey->length = keysize;
    randkey->enctype = eblock->crypto_entry->proto_enctype;

    do {
	mit_des_generate_random_key(state, randkey);
	mit_des_fixup_keyblock_parity(randkey);
    } while (mit_des_is_weak_keyblock(randkey));

    *keyblock = randkey;
    return 0;
}

static mit_des_cblock zero_ivec = { 0, 0, 0, 0, 0, 0, 0, 0 };

static void
mit_des_generate_random_key(state, randkey)
    mit_des_random_state * state;
    krb5_keyblock * randkey;
{
    krb5_encrypt_block *eblock = &state->eblock;
    int i;

    (* state->eblock.crypto_entry->encrypt_func)
	(state->sequence.data /*in*/, randkey->contents /*out*/,
	 state->sequence.length, eblock, zero_ivec);
    if (state->sequence.length > sizeof(mit_des_cblock))
	(* state->eblock.crypto_entry->encrypt_func)
	    (randkey->contents /*in*/, randkey->contents /*out*/,
	     randkey->length, eblock,
	     randkey->contents + randkey->length - sizeof(mit_des_cblock));

    /* Increment the sequence number, with wraparound (LSB) */
    for (i = 0; i < state->sequence.length; i++) {
	state->sequence.data[i] = (state->sequence.data[i] + 1) & 0xff;
	if (state->sequence.data[i])
	    break;
    }
}
