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 *
krb5int_c_locate_iov(krb5_crypto_iov * data,size_t num_data,krb5_cryptotype type)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
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)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
krb5int_c_padding_length(const struct krb5_keytypes * ktp,size_t data_length)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
next_iov_to_process(struct iov_cursor * cursor,size_t ind)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
k5_iov_cursor_init(struct iov_cursor * cursor,const krb5_crypto_iov * iov,size_t count,size_t block_size,krb5_boolean signing)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
k5_iov_cursor_get(struct iov_cursor * cursor,unsigned char * block)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
k5_iov_cursor_put(struct iov_cursor * cursor,unsigned char * block)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