#include "k5-int.h"
#include "dk.h"
#define K5CLENGTH 5
void
krb5_dk_encrypt_length(const struct krb5_enc_provider *enc,
const struct krb5_hash_provider *hash,
size_t inputlen, size_t *length)
{
size_t blocksize, hashsize;
blocksize = enc->block_size;
hashsize = hash->hashsize;
*length = krb5_roundup(blocksize+inputlen, blocksize) + hashsize;
}
krb5_error_code
krb5_dk_encrypt(krb5_context context,
const struct krb5_enc_provider *enc,
const struct krb5_hash_provider *hash,
const krb5_keyblock *key, krb5_keyusage usage,
const krb5_data *ivec, const krb5_data *input,
krb5_data *output)
{
size_t blocksize, plainlen, enclen;
krb5_error_code ret;
krb5_data d1, d2;
unsigned char *plaintext = NULL, *cn;
krb5_keyblock *derived_encr_key = NULL;
krb5_keyblock *derived_hmac_key = NULL;
KRB5_LOG0(KRB5_INFO, "krb5_dk_encrypt() start");
ret = init_derived_keydata(context, enc,
(krb5_keyblock *)key,
usage,
&derived_encr_key,
&derived_hmac_key);
if (ret)
return (ret);
blocksize = enc->block_size;
plainlen = krb5_roundup(blocksize+input->length, blocksize);
krb5_dk_encrypt_length(enc, hash, input->length, &enclen);
if (output->length < enclen)
return(KRB5_BAD_MSIZE);
if ((plaintext = (unsigned char *) MALLOC(plainlen)) == NULL) {
return(ENOMEM);
}
d1.length = blocksize;
d1.data = (char *) plaintext;
if ((ret = krb5_c_random_make_octets(context, &d1)))
goto cleanup;
(void) memcpy(plaintext+blocksize, input->data, input->length);
(void) memset(plaintext+blocksize+input->length, 0,
plainlen - (blocksize+input->length));
d1.length = plainlen;
d1.data = (char *) plaintext;
d2.length = plainlen;
d2.data = output->data;
if ((ret = ((*(enc->encrypt))(context, derived_encr_key,
ivec, &d1, &d2))))
goto cleanup;
if (ivec != NULL && ivec->length == blocksize)
cn = (unsigned char *) d2.data + d2.length - blocksize;
else
cn = NULL;
d2.length = enclen - plainlen;
d2.data = output->data+plainlen;
output->length = enclen;
#ifdef _KERNEL
if ((ret = krb5_hmac(context, derived_hmac_key, &d1, &d2))) {
(void) memset(d2.data, 0, d2.length);
goto cleanup;
}
#else
if ((ret = krb5_hmac(context, hash, derived_hmac_key,
1, &d1, &d2))) {
(void) memset(d2.data, 0, d2.length);
goto cleanup;
}
#endif
if (cn != NULL)
(void) memcpy(ivec->data, cn, blocksize);
cleanup:
FREE(plaintext, plainlen);
KRB5_LOG(KRB5_INFO, "krb5_dk_encrypt() end, ret=%d\n", ret);
return(ret);
}
void
krb5int_aes_encrypt_length(enc, hash, inputlen, length)
const struct krb5_enc_provider *enc;
const struct krb5_hash_provider *hash;
size_t inputlen;
size_t *length;
{
size_t blocksize, hashsize;
blocksize = enc->block_size;
hashsize = 96 / 8;
*length = blocksize+inputlen + hashsize;
}
static krb5_error_code
trunc_hmac (krb5_context context,
const struct krb5_hash_provider *hash,
const krb5_keyblock *ki, int num,
const krb5_data *input, krb5_data *output)
{
size_t hashsize;
krb5_error_code ret;
char buff[256];
krb5_data tmphash;
hashsize = hash->hashsize;
if (hashsize < output->length)
return (KRB5_CRYPTO_INTERNAL);
tmphash.length = hashsize;
tmphash.data = buff;
#ifdef _KERNEL
ret = krb5_hmac(context, ki, input, &tmphash);
#else
ret = krb5_hmac(context, hash, ki, num, input, &tmphash);
#endif
if (ret)
(void) memset(output->data, 0, output->length);
else
(void) memcpy(output->data, tmphash.data, output->length);
(void) memset(buff, 0, sizeof(buff));
return (ret);
}
krb5_error_code
krb5int_aes_dk_encrypt(krb5_context context,
const struct krb5_enc_provider *enc,
const struct krb5_hash_provider *hash,
const krb5_keyblock *key,
krb5_keyusage usage,
const krb5_data *ivec,
const krb5_data *input,
krb5_data *output)
{
size_t blocksize, plainlen, enclen;
krb5_error_code ret;
krb5_data d1, d2;
unsigned char *plaintext, *cn;
krb5_keyblock *derived_encr_key = NULL;
krb5_keyblock *derived_hmac_key = NULL;
ret = init_derived_keydata(context, enc,
(krb5_keyblock *)key,
usage,
&derived_encr_key,
&derived_hmac_key);
if (ret)
return (ret);
blocksize = enc->block_size;
plainlen = blocksize+input->length;
krb5int_aes_encrypt_length(enc, hash, input->length, &enclen);
if (output->length < enclen)
return(KRB5_BAD_MSIZE);
if ((plaintext = (unsigned char *) MALLOC(plainlen)) == NULL) {
return(ENOMEM);
}
d1.length = blocksize;
d1.data = (char *)plaintext;
if ((ret = krb5_c_random_make_octets(context, &d1)))
goto cleanup;
(void) memcpy(plaintext+blocksize, input->data, input->length);
if (plainlen != blocksize + input->length) {
ret = KRB5_BAD_KEYSIZE;
goto cleanup;
}
d1.length = plainlen;
d1.data = (char *)plaintext;
d2.length = plainlen;
d2.data = output->data;
if ((ret = ((*(enc->encrypt))(context, derived_encr_key, ivec, &d1, &d2))))
goto cleanup;
if (ivec != NULL && ivec->length == blocksize) {
int nblocks = (d2.length + blocksize - 1) / blocksize;
cn = (uchar_t *) d2.data + blocksize * (nblocks - 2);
} else {
cn = NULL;
}
d2.length = enclen - plainlen;
d2.data = output->data+plainlen;
if (d2.length != 96 / 8)
goto cleanup;
if ((ret = trunc_hmac(context, hash, derived_hmac_key, 1, &d1, &d2))) {
(void) memset(d2.data, 0, d2.length);
goto cleanup;
}
output->length = enclen;
if (cn != NULL) {
(void) memcpy(ivec->data, cn, blocksize);
}
cleanup:
(void) memset(plaintext, 0, plainlen);
FREE(plaintext, plainlen);
return(ret);
}