#include "crypto_int.h"
krb5_crypto_iov *
krb5int_c_locate_iov(krb5_crypto_iov *data, size_t num_data,
krb5_cryptotype type)
{
size_t i;
krb5_crypto_iov *iov = NULL;
if (data == NULL)
return NULL;
for (i = 0; i < num_data; i++) {
if (data[i].flags == type) {
if (iov == NULL)
iov = &data[i];
else
return NULL;
}
}
return iov;
}
krb5_error_code
krb5int_c_iov_decrypt_stream(const struct krb5_keytypes *ktp, krb5_key key,
krb5_keyusage keyusage, const krb5_data *ivec,
krb5_crypto_iov *data, size_t num_data)
{
krb5_error_code ret;
unsigned int header_len, trailer_len;
krb5_crypto_iov *iov;
krb5_crypto_iov *stream;
size_t i, j;
int got_data = 0;
stream = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_STREAM);
assert(stream != NULL);
header_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER);
trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER);
if (stream->data.length < header_len + trailer_len)
return KRB5_BAD_MSIZE;
iov = calloc(num_data + 2, sizeof(krb5_crypto_iov));
if (iov == NULL)
return ENOMEM;
i = 0;
iov[i].flags = KRB5_CRYPTO_TYPE_HEADER;
iov[i].data = make_data(stream->data.data, header_len);
i++;
for (j = 0; j < num_data; j++) {
if (data[j].flags == KRB5_CRYPTO_TYPE_DATA) {
if (got_data) {
free(iov);
return KRB5_BAD_MSIZE;
}
got_data++;
data[j].data.data = stream->data.data + header_len;
data[j].data.length = stream->data.length - header_len
- trailer_len;
}
if (data[j].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY ||
data[j].flags == KRB5_CRYPTO_TYPE_DATA)
iov[i++] = data[j];
}
iov[i].flags = KRB5_CRYPTO_TYPE_PADDING;
iov[i].data = empty_data();
i++;
iov[i].flags = KRB5_CRYPTO_TYPE_TRAILER;
iov[i].data = make_data(stream->data.data + stream->data.length -
trailer_len, trailer_len);
i++;
assert(i <= num_data + 2);
ret = ktp->decrypt(ktp, key, keyusage, ivec, iov, i);
free(iov);
return ret;
}
unsigned int
krb5int_c_padding_length(const struct krb5_keytypes *ktp, size_t data_length)
{
unsigned int header, padding;
header = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER);
data_length += header;
padding = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_PADDING);
if (padding == 0 || (data_length % padding) == 0)
return 0;
else
return padding - (data_length % padding);
}
static size_t
next_iov_to_process(struct iov_cursor *cursor, size_t ind)
{
const krb5_crypto_iov *iov;
for (; ind < cursor->iov_count; ind++) {
iov = &cursor->iov[ind];
if (cursor->signing ? SIGN_IOV(iov) : ENCRYPT_IOV(iov))
break;
}
return ind;
}
void
k5_iov_cursor_init(struct iov_cursor *cursor, const krb5_crypto_iov *iov,
size_t count, size_t block_size, krb5_boolean signing)
{
cursor->iov = iov;
cursor->iov_count = count;
cursor->block_size = block_size;
cursor->signing = signing;
cursor->in_iov = next_iov_to_process(cursor, 0);
cursor->out_iov = cursor->in_iov;
cursor->in_pos = cursor->out_pos = 0;
}
krb5_boolean
k5_iov_cursor_get(struct iov_cursor *cursor, unsigned char *block)
{
size_t nbytes, bsz = cursor->block_size, remain = cursor->block_size;
const krb5_crypto_iov *iov;
remain = cursor->block_size;
while (remain > 0 && cursor->in_iov < cursor->iov_count) {
iov = &cursor->iov[cursor->in_iov];
nbytes = iov->data.length - cursor->in_pos;
if (nbytes > remain)
nbytes = remain;
memcpy(block + bsz - remain, iov->data.data + cursor->in_pos, nbytes);
cursor->in_pos += nbytes;
remain -= nbytes;
if (cursor->in_pos == iov->data.length) {
cursor->in_iov = next_iov_to_process(cursor, cursor->in_iov + 1);
cursor->in_pos = 0;
}
}
if (remain == bsz)
return FALSE;
if (remain > 0)
memset(block + bsz - remain, 0, remain);
return TRUE;
}
void
k5_iov_cursor_put(struct iov_cursor *cursor, unsigned char *block)
{
size_t nbytes, bsz = cursor->block_size, remain = cursor->block_size;
const krb5_crypto_iov *iov;
remain = cursor->block_size;
while (remain > 0 && cursor->out_iov < cursor->iov_count) {
iov = &cursor->iov[cursor->out_iov];
nbytes = iov->data.length - cursor->out_pos;
if (nbytes > remain)
nbytes = remain;
memcpy(iov->data.data + cursor->out_pos, block + bsz - remain, nbytes);
cursor->out_pos += nbytes;
remain -= nbytes;
if (cursor->out_pos == iov->data.length) {
cursor->out_iov = next_iov_to_process(cursor, cursor->out_iov + 1);
cursor->out_pos = 0;
}
}
}