#include "crypto_int.h"
static krb5_key
find_cached_dkey(struct derived_key *list, const krb5_data *constant)
{
for (; list; list = list->next) {
if (data_eq(list->constant, *constant)) {
krb5_k_reference_key(NULL, list->dkey);
return list->dkey;
}
}
return NULL;
}
static krb5_error_code
add_cached_dkey(krb5_key key, const krb5_data *constant,
const krb5_keyblock *dkeyblock, krb5_key *cached_dkey)
{
krb5_key dkey;
krb5_error_code ret;
struct derived_key *dkent = NULL;
char *data = NULL;
dkent = malloc(sizeof(*dkent));
if (dkent == NULL)
goto cleanup;
data = k5memdup(constant->data, constant->length, &ret);
if (data == NULL)
goto cleanup;
ret = krb5_k_create_key(NULL, dkeyblock, &dkey);
if (ret != 0)
goto cleanup;
dkent->dkey = dkey;
dkent->constant.data = data;
dkent->constant.length = constant->length;
dkent->next = key->derived;
key->derived = dkent;
krb5_k_reference_key(NULL, dkey);
*cached_dkey = dkey;
return 0;
cleanup:
free(dkent);
free(data);
return ENOMEM;
}
krb5_error_code
krb5int_derive_random(const struct krb5_enc_provider *enc,
const struct krb5_hash_provider *hash,
krb5_key inkey, krb5_data *outrnd,
const krb5_data *in_constant, enum deriv_alg alg)
{
krb5_data empty = empty_data();
switch (alg) {
case DERIVE_RFC3961:
return k5_derive_random_rfc3961(enc, inkey, in_constant, outrnd);
case DERIVE_SP800_108_CMAC:
return k5_sp800_108_feedback_cmac(enc, inkey, in_constant, outrnd);
case DERIVE_SP800_108_HMAC:
return k5_sp800_108_counter_hmac(hash, inkey, in_constant, &empty,
outrnd);
default:
return EINVAL;
}
}
krb5_error_code
krb5int_derive_keyblock(const struct krb5_enc_provider *enc,
const struct krb5_hash_provider *hash,
krb5_key inkey, krb5_keyblock *outkey,
const krb5_data *in_constant, enum deriv_alg alg)
{
krb5_error_code ret;
krb5_data rawkey = empty_data();
ret = alloc_data(&rawkey, enc->keybytes);
if (ret)
goto cleanup;
ret = krb5int_derive_random(enc, hash, inkey, &rawkey, in_constant, alg);
if (ret)
goto cleanup;
ret = krb5_c_random_to_key(NULL, inkey->keyblock.enctype, &rawkey, outkey);
cleanup:
zapfree(rawkey.data, enc->keybytes);
return ret;
}
krb5_error_code
krb5int_derive_key(const struct krb5_enc_provider *enc,
const struct krb5_hash_provider *hash,
krb5_key inkey, krb5_key *outkey,
const krb5_data *in_constant, enum deriv_alg alg)
{
krb5_keyblock keyblock;
krb5_error_code ret;
krb5_key dkey;
*outkey = NULL;
dkey = find_cached_dkey(inkey->derived, in_constant);
if (dkey != NULL) {
*outkey = dkey;
return 0;
}
keyblock.length = enc->keylength;
keyblock.contents = malloc(keyblock.length);
keyblock.enctype = inkey->keyblock.enctype;
if (keyblock.contents == NULL)
return ENOMEM;
ret = krb5int_derive_keyblock(enc, hash, inkey, &keyblock, in_constant,
alg);
if (ret)
goto cleanup;
ret = add_cached_dkey(inkey, in_constant, &keyblock, &dkey);
if (ret != 0)
goto cleanup;
*outkey = dkey;
cleanup:
zapfree(keyblock.contents, keyblock.length);
return ret;
}