1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* lib/crypto/krb/aead.c */ 3 /* 4 * Copyright 2008 by the Massachusetts Institute of Technology. 5 * All Rights Reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 */ 26 27 #include "crypto_int.h" 28 29 krb5_crypto_iov * 30 krb5int_c_locate_iov(krb5_crypto_iov *data, size_t num_data, 31 krb5_cryptotype type) 32 { 33 size_t i; 34 krb5_crypto_iov *iov = NULL; 35 36 if (data == NULL) 37 return NULL; 38 39 for (i = 0; i < num_data; i++) { 40 if (data[i].flags == type) { 41 if (iov == NULL) 42 iov = &data[i]; 43 else 44 return NULL; /* can't appear twice */ 45 } 46 } 47 48 return iov; 49 } 50 51 krb5_error_code 52 krb5int_c_iov_decrypt_stream(const struct krb5_keytypes *ktp, krb5_key key, 53 krb5_keyusage keyusage, const krb5_data *ivec, 54 krb5_crypto_iov *data, size_t num_data) 55 { 56 krb5_error_code ret; 57 unsigned int header_len, trailer_len; 58 krb5_crypto_iov *iov; 59 krb5_crypto_iov *stream; 60 size_t i, j; 61 int got_data = 0; 62 63 stream = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_STREAM); 64 assert(stream != NULL); 65 66 header_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER); 67 trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER); 68 69 if (stream->data.length < header_len + trailer_len) 70 return KRB5_BAD_MSIZE; 71 72 iov = calloc(num_data + 2, sizeof(krb5_crypto_iov)); 73 if (iov == NULL) 74 return ENOMEM; 75 76 i = 0; 77 78 iov[i].flags = KRB5_CRYPTO_TYPE_HEADER; /* takes place of STREAM */ 79 iov[i].data = make_data(stream->data.data, header_len); 80 i++; 81 82 for (j = 0; j < num_data; j++) { 83 if (data[j].flags == KRB5_CRYPTO_TYPE_DATA) { 84 if (got_data) { 85 free(iov); 86 return KRB5_BAD_MSIZE; 87 } 88 89 got_data++; 90 91 data[j].data.data = stream->data.data + header_len; 92 data[j].data.length = stream->data.length - header_len 93 - trailer_len; 94 } 95 if (data[j].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY || 96 data[j].flags == KRB5_CRYPTO_TYPE_DATA) 97 iov[i++] = data[j]; 98 } 99 100 /* Use empty padding since tokens don't indicate the padding length. */ 101 iov[i].flags = KRB5_CRYPTO_TYPE_PADDING; 102 iov[i].data = empty_data(); 103 i++; 104 105 iov[i].flags = KRB5_CRYPTO_TYPE_TRAILER; 106 iov[i].data = make_data(stream->data.data + stream->data.length - 107 trailer_len, trailer_len); 108 i++; 109 110 assert(i <= num_data + 2); 111 112 ret = ktp->decrypt(ktp, key, keyusage, ivec, iov, i); 113 free(iov); 114 return ret; 115 } 116 117 unsigned int 118 krb5int_c_padding_length(const struct krb5_keytypes *ktp, size_t data_length) 119 { 120 unsigned int header, padding; 121 122 /* 123 * Add in the header length since the header is encrypted along with the 124 * data. (arcfour violates this assumption since not all of the header is 125 * encrypted, but that's okay since it has no padding. If there is ever an 126 * enctype using a similar token format and a block cipher, we will have to 127 * move this logic into an enctype-dependent function.) 128 */ 129 header = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER); 130 data_length += header; 131 132 padding = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_PADDING); 133 if (padding == 0 || (data_length % padding) == 0) 134 return 0; 135 else 136 return padding - (data_length % padding); 137 } 138 139 /* Return the next iov (starting from ind) which cursor should process, or 140 * cursor->iov_count if there are none remaining. */ 141 static size_t 142 next_iov_to_process(struct iov_cursor *cursor, size_t ind) 143 { 144 const krb5_crypto_iov *iov; 145 146 for (; ind < cursor->iov_count; ind++) { 147 iov = &cursor->iov[ind]; 148 if (cursor->signing ? SIGN_IOV(iov) : ENCRYPT_IOV(iov)) 149 break; 150 } 151 return ind; 152 } 153 154 void 155 k5_iov_cursor_init(struct iov_cursor *cursor, const krb5_crypto_iov *iov, 156 size_t count, size_t block_size, krb5_boolean signing) 157 { 158 cursor->iov = iov; 159 cursor->iov_count = count; 160 cursor->block_size = block_size; 161 cursor->signing = signing; 162 cursor->in_iov = next_iov_to_process(cursor, 0); 163 cursor->out_iov = cursor->in_iov; 164 cursor->in_pos = cursor->out_pos = 0; 165 } 166 167 /* Fetch one block from cursor's input position. */ 168 krb5_boolean 169 k5_iov_cursor_get(struct iov_cursor *cursor, unsigned char *block) 170 { 171 size_t nbytes, bsz = cursor->block_size, remain = cursor->block_size; 172 const krb5_crypto_iov *iov; 173 174 remain = cursor->block_size; 175 while (remain > 0 && cursor->in_iov < cursor->iov_count) { 176 iov = &cursor->iov[cursor->in_iov]; 177 178 nbytes = iov->data.length - cursor->in_pos; 179 if (nbytes > remain) 180 nbytes = remain; 181 182 memcpy(block + bsz - remain, iov->data.data + cursor->in_pos, nbytes); 183 cursor->in_pos += nbytes; 184 remain -= nbytes; 185 186 if (cursor->in_pos == iov->data.length) { 187 cursor->in_iov = next_iov_to_process(cursor, cursor->in_iov + 1); 188 cursor->in_pos = 0; 189 } 190 } 191 192 if (remain == bsz) 193 return FALSE; 194 if (remain > 0) 195 memset(block + bsz - remain, 0, remain); 196 return TRUE; 197 } 198 199 /* Write a block to a cursor's output position. */ 200 void 201 k5_iov_cursor_put(struct iov_cursor *cursor, unsigned char *block) 202 { 203 size_t nbytes, bsz = cursor->block_size, remain = cursor->block_size; 204 const krb5_crypto_iov *iov; 205 206 remain = cursor->block_size; 207 while (remain > 0 && cursor->out_iov < cursor->iov_count) { 208 iov = &cursor->iov[cursor->out_iov]; 209 210 nbytes = iov->data.length - cursor->out_pos; 211 if (nbytes > remain) 212 nbytes = remain; 213 214 memcpy(iov->data.data + cursor->out_pos, block + bsz - remain, nbytes); 215 cursor->out_pos += nbytes; 216 remain -= nbytes; 217 218 if (cursor->out_pos == iov->data.length) { 219 cursor->out_iov = next_iov_to_process(cursor, cursor->out_iov + 1); 220 cursor->out_pos = 0; 221 } 222 } 223 } 224