#include "k5-int.h"
#include "dk.h"
#define K5CLENGTH 5
void
krb5_dk_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;
(*(enc->block_size))(&blocksize);
(*(hash->hash_size))(&hashsize);
*length = krb5_roundup(blocksize+inputlen, blocksize) + hashsize;
}
krb5_error_code
krb5_dk_encrypt(enc, hash, key, usage, ivec, input, output)
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;
(*(enc->block_size))(&blocksize);
(*(enc->keysize))(&keybytes, &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(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;
(*(enc->block_size))(&blocksize);
hashsize = 96 / 8;
*length = blocksize+inputlen + hashsize;
}
static krb5_error_code
trunc_hmac (const struct krb5_hash_provider *hash,
const krb5_keyblock *ki, int num,
const krb5_data *input, const krb5_data *output)
{
size_t hashsize;
krb5_data tmp;
krb5_error_code ret;
(hash->hash_size)(&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(enc, hash, key, usage, ivec, input, output)
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;
(*(enc->block_size))(&blocksize);
(*(enc->keysize))(&keybytes, &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)
cn = d2.data + d2.length - blocksize;
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);
cleanup:
memset(kedata, 0, keylength);
memset(kidata, 0, keylength);
memset(plaintext, 0, plainlen);
free(plaintext);
free(kidata);
free(kedata);
return(ret);
}
#ifdef ATHENA_DES3_KLUDGE
void
krb5_marc_dk_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;
(*(enc->block_size))(&blocksize);
(*(hash->hash_size))(&hashsize);
*length = krb5_roundup(blocksize+4+inputlen, blocksize) + hashsize;
}
krb5_error_code
krb5_marc_dk_encrypt(enc, hash, key, usage, ivec, input, output)
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;
(*(enc->block_size))(&blocksize);
(*(enc->keysize))(&keybytes, &keylength);
plainlen = krb5_roundup(blocksize+4+input->length, blocksize);
krb5_marc_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] = 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;
(plaintext+blocksize)[0] = (input->length>>24)&0xff;
(plaintext+blocksize)[1] = (input->length>>16)&0xff;
(plaintext+blocksize)[2] = (input->length>>8)&0xff;
(plaintext+blocksize)[3] = input->length&0xff;
memcpy(plaintext+blocksize+4, input->data, input->length);
memset(plaintext+blocksize+4+input->length, 0,
plainlen - (blocksize+4+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);
}
#endif