xref: /freebsd/crypto/krb5/src/lib/crypto/krb/aead.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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