#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(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, keybytes, keylength, plainlen, enclen;
krb5_error_code ret;
unsigned char constantdata[K5CLENGTH];
krb5_data d1, d2;
unsigned char *plaintext, *kedata, *kidata, *cn;
krb5_keyblock ke, ki;
blocksize = enc->block_size;
keybytes = enc->keybytes;
keylength = enc->keylength;
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 ((kedata = (unsigned char *) malloc(keylength)) == NULL)
return(ENOMEM);
if ((kidata = (unsigned char *) malloc(keylength)) == NULL) {
free(kedata);
return(ENOMEM);
}
if ((plaintext = (unsigned char *) malloc(plainlen)) == NULL) {
free(kidata);
free(kedata);
return(ENOMEM);
}
ke.contents = kedata;
ke.length = keylength;
ki.contents = kidata;
ki.length = keylength;
d1.data = constantdata;
d1.length = K5CLENGTH;
d1.data[0] = (usage>>24)&0xff;
d1.data[1] = (usage>>16)&0xff;
d1.data[2] = (usage>>8)&0xff;
d1.data[3] = usage&0xff;
d1.data[4] = (char) 0xAA;
if ((ret = krb5_derive_key(enc, key, &ke, &d1)))
goto cleanup;
d1.data[4] = 0x55;
if ((ret = krb5_derive_key(enc, key, &ki, &d1)))
goto cleanup;
d1.length = blocksize;
d1.data = plaintext;
if ((ret = krb5_c_random_make_octets( 0, &d1)))
goto cleanup;
memcpy(plaintext+blocksize, input->data, input->length);
memset(plaintext+blocksize+input->length, 0,
plainlen - (blocksize+input->length));
d1.length = plainlen;
d1.data = plaintext;
d2.length = plainlen;
d2.data = output->data;
if ((ret = ((*(enc->encrypt))(&ke, ivec, &d1, &d2))))
goto cleanup;
if (ivec != NULL && ivec->length == blocksize)
cn = d2.data + d2.length - blocksize;
else
cn = NULL;
d2.length = enclen - plainlen;
d2.data = output->data+plainlen;
output->length = enclen;
if ((ret = krb5_hmac(hash, &ki, 1, &d1, &d2))) {
memset(d2.data, 0, d2.length);
goto cleanup;
}
if (cn != NULL)
memcpy(ivec->data, cn, blocksize);
cleanup:
memset(kedata, 0, keylength);
memset(kidata, 0, keylength);
memset(plaintext, 0, plainlen);
free(plaintext);
free(kidata);
free(kedata);
return(ret);
}
void
krb5int_aes_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 = 96 / 8;
*length = blocksize+inputlen + hashsize;
}
static krb5_error_code
trunc_hmac (const struct krb5_hash_provider *hash,
const krb5_keyblock *ki, unsigned int num,
const krb5_data *input, const krb5_data *output)
{
size_t hashsize;
krb5_data tmp;
krb5_error_code ret;
hashsize = hash->hashsize;
if (hashsize < output->length)
return KRB5_CRYPTO_INTERNAL;
tmp.length = hashsize;
tmp.data = malloc(hashsize);
if (tmp.data == NULL)
return errno;
ret = krb5_hmac(hash, ki, num, input, &tmp);
if (ret == 0)
memcpy(output->data, tmp.data, output->length);
memset(tmp.data, 0, hashsize);
free(tmp.data);
return ret;
}
krb5_error_code
krb5int_aes_dk_encrypt(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, keybytes, keylength, plainlen, enclen;
krb5_error_code ret;
unsigned char constantdata[K5CLENGTH];
krb5_data d1, d2;
unsigned char *plaintext, *kedata, *kidata, *cn;
krb5_keyblock ke, ki;
blocksize = enc->block_size;
keybytes = enc->keybytes;
keylength = enc->keylength;
plainlen = blocksize+input->length;
krb5int_aes_encrypt_length(enc, hash, input->length, &enclen);
if (output->length < enclen)
return(KRB5_BAD_MSIZE);
if ((kedata = (unsigned char *) malloc(keylength)) == NULL)
return(ENOMEM);
if ((kidata = (unsigned char *) malloc(keylength)) == NULL) {
free(kedata);
return(ENOMEM);
}
if ((plaintext = (unsigned char *) malloc(plainlen)) == NULL) {
free(kidata);
free(kedata);
return(ENOMEM);
}
ke.contents = kedata;
ke.length = keylength;
ki.contents = kidata;
ki.length = keylength;
d1.data = constantdata;
d1.length = K5CLENGTH;
d1.data[0] = (usage>>24)&0xff;
d1.data[1] = (usage>>16)&0xff;
d1.data[2] = (usage>>8)&0xff;
d1.data[3] = usage&0xff;
d1.data[4] = (char) 0xAA;
if ((ret = krb5_derive_key(enc, key, &ke, &d1)))
goto cleanup;
d1.data[4] = 0x55;
if ((ret = krb5_derive_key(enc, key, &ki, &d1)))
goto cleanup;
d1.length = blocksize;
d1.data = plaintext;
if ((ret = krb5_c_random_make_octets( 0, &d1)))
goto cleanup;
memcpy(plaintext+blocksize, input->data, input->length);
if (plainlen != blocksize + input->length)
abort();
d1.length = plainlen;
d1.data = plaintext;
d2.length = plainlen;
d2.data = output->data;
if ((ret = ((*(enc->encrypt))(&ke, ivec, &d1, &d2))))
goto cleanup;
if (ivec != NULL && ivec->length == blocksize) {
int nblocks = (d2.length + blocksize - 1) / blocksize;
cn = d2.data + blocksize * (nblocks - 2);
} else
cn = NULL;
d2.length = enclen - plainlen;
d2.data = output->data+plainlen;
if (d2.length != 96 / 8)
abort();
if ((ret = trunc_hmac(hash, &ki, 1, &d1, &d2))) {
memset(d2.data, 0, d2.length);
goto cleanup;
}
output->length = enclen;
if (cn != NULL) {
memcpy(ivec->data, cn, blocksize);
#if 0
{
int i;
printf("\n%s: output:", __func__);
for (i = 0; i < output->length; i++) {
if (i % 16 == 0)
printf("\n%s: ", __func__);
printf(" %02x", i[(unsigned char *)output->data]);
}
printf("\n%s: outputIV:", __func__);
for (i = 0; i < ivec->length; i++) {
if (i % 16 == 0)
printf("\n%s: ", __func__);
printf(" %02x", i[(unsigned char *)ivec->data]);
}
printf("\n"); fflush(stdout);
}
#endif
}
cleanup:
memset(kedata, 0, keylength);
memset(kidata, 0, keylength);
memset(plaintext, 0, plainlen);
free(plaintext);
free(kidata);
free(kedata);
return(ret);
}