2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * lib/crypto/aead.c
2N/A *
2N/A * Copyright 2008 by the Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A */
2N/A
2N/A#include "k5-int.h"
2N/A#include "etypes.h"
2N/A#include "cksumtypes.h"
2N/A#include "dk.h"
2N/A#include "aead.h"
2N/A
2N/Akrb5_crypto_iov *
2N/Akrb5int_c_locate_iov(krb5_crypto_iov *data, size_t num_data,
2N/A krb5_cryptotype type)
2N/A{
2N/A size_t i;
2N/A krb5_crypto_iov *iov = NULL;
2N/A
2N/A if (data == NULL)
2N/A return NULL;
2N/A
2N/A for (i = 0; i < num_data; i++) {
2N/A if (data[i].flags == type) {
2N/A if (iov == NULL)
2N/A iov = &data[i];
2N/A else
2N/A return NULL; /* can't appear twice */
2N/A }
2N/A }
2N/A
2N/A return iov;
2N/A}
2N/A
2N/A#ifdef DEBUG_IOV
2N/Astatic void
2N/Adump_block(const char *tag,
2N/A size_t i,
2N/A size_t j,
2N/A unsigned char *block,
2N/A size_t block_size)
2N/A{
2N/A size_t k;
2N/A
2N/A printf("[%s: %d.%d] ", tag, i, j);
2N/A
2N/A for (k = 0; k < block_size; k++)
2N/A printf("%02x ", block[k] & 0xFF);
2N/A
2N/A printf("\n");
2N/A}
2N/A#endif
2N/A
2N/Astatic int
2N/Aprocess_block_p(const krb5_crypto_iov *data,
2N/A size_t num_data,
2N/A struct iov_block_state *iov_state,
2N/A size_t i)
2N/A{
2N/A const krb5_crypto_iov *iov = &data[i];
2N/A int process_block;
2N/A
2N/A switch (iov->flags) {
2N/A case KRB5_CRYPTO_TYPE_SIGN_ONLY:
2N/A process_block = iov_state->include_sign_only;
2N/A break;
2N/A case KRB5_CRYPTO_TYPE_PADDING:
2N/A process_block = (iov_state->pad_to_boundary == 0);
2N/A break;
2N/A case KRB5_CRYPTO_TYPE_HEADER:
2N/A process_block = (iov_state->ignore_header == 0);
2N/A break;
2N/A case KRB5_CRYPTO_TYPE_DATA:
2N/A process_block = 1;
2N/A break;
2N/A default:
2N/A process_block = 0;
2N/A break;
2N/A }
2N/A
2N/A return process_block;
2N/A}
2N/A
2N/A/*
2N/A * Returns TRUE if, having reached the end of the current buffer,
2N/A * we should pad the rest of the block with zeros.
2N/A */
2N/Astatic int
2N/Apad_to_boundary_p(const krb5_crypto_iov *data,
2N/A size_t num_data,
2N/A struct iov_block_state *iov_state,
2N/A size_t i,
2N/A size_t j)
2N/A{
2N/A /* If the pad_to_boundary flag is unset, return FALSE */
2N/A if (iov_state->pad_to_boundary == 0)
2N/A return 0;
2N/A
2N/A /* If we haven't got any data, we need to get some */
2N/A if (j == 0)
2N/A return 0;
2N/A
2N/A /* No boundary between adjacent buffers marked for processing */
2N/A if (data[iov_state->iov_pos].flags == data[i].flags)
2N/A return 0;
2N/A
2N/A return 1;
2N/A}
2N/A
2N/Akrb5_boolean
2N/Akrb5int_c_iov_get_block(unsigned char *block,
2N/A size_t block_size,
2N/A const krb5_crypto_iov *data,
2N/A size_t num_data,
2N/A struct iov_block_state *iov_state)
2N/A{
2N/A size_t i, j = 0;
2N/A
2N/A for (i = iov_state->iov_pos; i < num_data; i++) {
2N/A const krb5_crypto_iov *iov = &data[i];
2N/A size_t nbytes;
2N/A
2N/A if (!process_block_p(data, num_data, iov_state, i))
2N/A continue;
2N/A
2N/A if (pad_to_boundary_p(data, num_data, iov_state, i, j))
2N/A break;
2N/A
2N/A iov_state->iov_pos = i;
2N/A
2N/A nbytes = iov->data.length - iov_state->data_pos;
2N/A if (nbytes > block_size - j)
2N/A nbytes = block_size - j;
2N/A
2N/A memcpy(block + j, iov->data.data + iov_state->data_pos, nbytes);
2N/A
2N/A iov_state->data_pos += nbytes;
2N/A j += nbytes;
2N/A
2N/A assert(j <= block_size);
2N/A
2N/A if (j == block_size)
2N/A break;
2N/A
2N/A assert(iov_state->data_pos == iov->data.length);
2N/A
2N/A iov_state->data_pos = 0;
2N/A }
2N/A
2N/A iov_state->iov_pos = i;
2N/A if (i == num_data)
2N/A return FALSE;
2N/A
2N/A if (j != block_size)
2N/A memset(block + j, 0, block_size - j);
2N/A
2N/A#ifdef DEBUG_IOV
2N/A dump_block("get_block", i, j, block, block_size);
2N/A#endif
2N/A
2N/A return TRUE;
2N/A}
2N/A
2N/Akrb5_boolean
2N/Akrb5int_c_iov_put_block(const krb5_crypto_iov *data,
2N/A size_t num_data,
2N/A unsigned char *block,
2N/A size_t block_size,
2N/A struct iov_block_state *iov_state)
2N/A{
2N/A size_t i, j = 0;
2N/A
2N/A for (i = iov_state->iov_pos; i < num_data; i++) {
2N/A const krb5_crypto_iov *iov = &data[i];
2N/A size_t nbytes;
2N/A
2N/A if (!process_block_p(data, num_data, iov_state, i))
2N/A continue;
2N/A
2N/A if (pad_to_boundary_p(data, num_data, iov_state, i, j))
2N/A break;
2N/A
2N/A iov_state->iov_pos = i;
2N/A
2N/A nbytes = iov->data.length - iov_state->data_pos;
2N/A if (nbytes > block_size - j)
2N/A nbytes = block_size - j;
2N/A
2N/A memcpy(iov->data.data + iov_state->data_pos, block + j, nbytes);
2N/A
2N/A iov_state->data_pos += nbytes;
2N/A j += nbytes;
2N/A
2N/A assert(j <= block_size);
2N/A
2N/A if (j == block_size)
2N/A break;
2N/A
2N/A assert(iov_state->data_pos == iov->data.length);
2N/A
2N/A iov_state->data_pos = 0;
2N/A }
2N/A
2N/A iov_state->iov_pos = i;
2N/A
2N/A#ifdef DEBUG_IOV
2N/A dump_block("put_block", i, j, block, block_size);
2N/A#endif
2N/A
2N/A return (iov_state->iov_pos < num_data);
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5int_c_iov_decrypt_stream(const struct krb5_keytypes *ktp, krb5_key key,
2N/A krb5_keyusage keyusage, const krb5_data *ivec,
2N/A krb5_crypto_iov *data, size_t num_data)
2N/A{
2N/A krb5_error_code ret;
2N/A unsigned int header_len, trailer_len;
2N/A krb5_crypto_iov *iov;
2N/A krb5_crypto_iov *stream;
2N/A size_t i, j;
2N/A int got_data = 0;
2N/A
2N/A stream = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_STREAM);
2N/A assert(stream != NULL);
2N/A
2N/A header_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER);
2N/A trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER);
2N/A
2N/A if (stream->data.length < header_len + trailer_len)
2N/A return KRB5_BAD_MSIZE;
2N/A
2N/A iov = calloc(num_data + 2, sizeof(krb5_crypto_iov));
2N/A if (iov == NULL)
2N/A return ENOMEM;
2N/A
2N/A i = 0;
2N/A
2N/A iov[i].flags = KRB5_CRYPTO_TYPE_HEADER; /* takes place of STREAM */
2N/A iov[i].data = make_data(stream->data.data, header_len);
2N/A i++;
2N/A
2N/A for (j = 0; j < num_data; j++) {
2N/A if (data[j].flags == KRB5_CRYPTO_TYPE_DATA) {
2N/A if (got_data) {
2N/A free(iov);
2N/A return KRB5_BAD_MSIZE;
2N/A }
2N/A
2N/A got_data++;
2N/A
2N/A data[j].data.data = stream->data.data + header_len;
2N/A data[j].data.length = stream->data.length - header_len
2N/A - trailer_len;
2N/A }
2N/A if (data[j].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY ||
2N/A data[j].flags == KRB5_CRYPTO_TYPE_DATA)
2N/A iov[i++] = data[j];
2N/A }
2N/A
2N/A /* Use empty padding since tokens don't indicate the padding length. */
2N/A iov[i].flags = KRB5_CRYPTO_TYPE_PADDING;
2N/A iov[i].data = empty_data();
2N/A i++;
2N/A
2N/A iov[i].flags = KRB5_CRYPTO_TYPE_TRAILER;
2N/A iov[i].data = make_data(stream->data.data + stream->data.length -
2N/A trailer_len, trailer_len);
2N/A i++;
2N/A
2N/A assert(i <= num_data + 2);
2N/A
2N/A ret = ktp->decrypt(ktp, key, keyusage, ivec, iov, i);
2N/A free(iov);
2N/A return ret;
2N/A}
2N/A
2N/Aunsigned int
2N/Akrb5int_c_padding_length(const struct krb5_keytypes *ktp, size_t data_length)
2N/A{
2N/A unsigned int header, padding;
2N/A
2N/A /*
2N/A * Add in the header length since the header is encrypted along with the
2N/A * data. (arcfour violates this assumption since not all of the header is
2N/A * encrypted, but that's okay since it has no padding. If there is ever an
2N/A * enctype using a similar token format and a block cipher, we will have to
2N/A * move this logic into an enctype-dependent function.)
2N/A */
2N/A header = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER);
2N/A data_length += header;
2N/A
2N/A padding = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_PADDING);
2N/A if (padding == 0 || (data_length % padding) == 0)
2N/A return 0;
2N/A else
2N/A return padding - (data_length % padding);
2N/A}