Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / heimdal / lib / gssapi / wrap.c
1 /*
2  * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met: 
9  *
10  * 1. Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer. 
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright 
14  *    notice, this list of conditions and the following disclaimer in the 
15  *    documentation and/or other materials provided with the distribution. 
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors 
18  *    may be used to endorse or promote products derived from this software 
19  *    without specific prior written permission. 
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
31  * SUCH DAMAGE. 
32  */
33
34 #include "gssapi_locl.h"
35
36 RCSID("$Id: wrap.c,v 1.20 2002/09/03 17:33:36 joda Exp $");
37
38 OM_uint32
39 gss_krb5_get_localkey(const gss_ctx_id_t context_handle,
40                        krb5_keyblock **key)
41 {
42     krb5_keyblock *skey;
43
44     krb5_auth_con_getlocalsubkey(gssapi_krb5_context,
45                                  context_handle->auth_context, 
46                                  &skey);
47     if(skey == NULL)
48         krb5_auth_con_getremotesubkey(gssapi_krb5_context,
49                                       context_handle->auth_context, 
50                                       &skey);
51     if(skey == NULL)
52         krb5_auth_con_getkey(gssapi_krb5_context,
53                              context_handle->auth_context, 
54                              &skey);
55     if(skey == NULL)
56         return GSS_S_FAILURE;
57     *key = skey;
58     return 0;
59 }
60
61 static OM_uint32
62 sub_wrap_size (
63             OM_uint32 req_output_size,
64             OM_uint32 * max_input_size,
65             int blocksize,
66             int extrasize
67            )
68 {
69   size_t len, total_len, padlength;
70   padlength = blocksize - (req_output_size % blocksize);
71   len = req_output_size + 8 + padlength + extrasize;
72   gssapi_krb5_encap_length(len, &len, &total_len);
73   *max_input_size = (OM_uint32)total_len;
74   return GSS_S_COMPLETE;
75 }
76
77 OM_uint32
78 gss_wrap_size_limit (
79             OM_uint32 * minor_status,
80             const gss_ctx_id_t context_handle,
81             int conf_req_flag,
82             gss_qop_t qop_req,
83             OM_uint32 req_output_size,
84             OM_uint32 * max_input_size
85            )
86 {
87   krb5_keyblock *key;
88   OM_uint32 ret;
89   krb5_keytype keytype;
90
91   ret = gss_krb5_get_localkey(context_handle, &key);
92   if (ret) {
93       gssapi_krb5_set_error_string ();
94       *minor_status = ret;
95       return GSS_S_FAILURE;
96   }
97   krb5_enctype_to_keytype (gssapi_krb5_context, key->keytype, &keytype);
98
99   switch (keytype) {
100   case KEYTYPE_DES :
101       ret = sub_wrap_size(req_output_size, max_input_size, 8, 22);
102       break;
103   case KEYTYPE_DES3 :
104       ret = sub_wrap_size(req_output_size, max_input_size, 8, 34);
105       break;
106   default :
107       *minor_status = KRB5_PROG_ETYPE_NOSUPP;
108       ret = GSS_S_FAILURE;
109       break;
110   }
111   krb5_free_keyblock (gssapi_krb5_context, key);
112   return ret;
113 }
114
115 static OM_uint32
116 wrap_des
117            (OM_uint32 * minor_status,
118             const gss_ctx_id_t context_handle,
119             int conf_req_flag,
120             gss_qop_t qop_req,
121             const gss_buffer_t input_message_buffer,
122             int * conf_state,
123             gss_buffer_t output_message_buffer,
124             krb5_keyblock *key
125            )
126 {
127   u_char *p;
128   MD5_CTX md5;
129   u_char hash[16];
130   des_key_schedule schedule;
131   des_cblock deskey;
132   des_cblock zero;
133   int i;
134   int32_t seq_number;
135   size_t len, total_len, padlength, datalen;
136
137   padlength = 8 - (input_message_buffer->length % 8);
138   datalen = input_message_buffer->length + padlength + 8;
139   len = datalen + 22;
140   gssapi_krb5_encap_length (len, &len, &total_len);
141
142   output_message_buffer->length = total_len;
143   output_message_buffer->value  = malloc (total_len);
144   if (output_message_buffer->value == NULL)
145     return GSS_S_FAILURE;
146
147   p = gssapi_krb5_make_header(output_message_buffer->value,
148                               len,
149                               "\x02\x01"); /* TOK_ID */
150
151   /* SGN_ALG */
152   memcpy (p, "\x00\x00", 2);
153   p += 2;
154   /* SEAL_ALG */
155   if(conf_req_flag)
156       memcpy (p, "\x00\x00", 2);
157   else
158       memcpy (p, "\xff\xff", 2);
159   p += 2;
160   /* Filler */
161   memcpy (p, "\xff\xff", 2);
162   p += 2;
163
164   /* fill in later */
165   memset (p, 0, 16);
166   p += 16;
167
168   /* confounder + data + pad */
169   krb5_generate_random_block(p, 8);
170   memcpy (p + 8, input_message_buffer->value,
171           input_message_buffer->length);
172   memset (p + 8 + input_message_buffer->length, padlength, padlength);
173
174   /* checksum */
175   MD5_Init (&md5);
176   MD5_Update (&md5, p - 24, 8);
177   MD5_Update (&md5, p, datalen);
178   MD5_Final (hash, &md5);
179
180   memset (&zero, 0, sizeof(zero));
181   memcpy (&deskey, key->keyvalue.data, sizeof(deskey));
182   des_set_key (&deskey, schedule);
183   des_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash),
184                  schedule, &zero);
185   memcpy (p - 8, hash, 8);
186
187   /* sequence number */
188   krb5_auth_con_getlocalseqnumber (gssapi_krb5_context,
189                                context_handle->auth_context,
190                                &seq_number);
191
192   p -= 16;
193   p[0] = (seq_number >> 0)  & 0xFF;
194   p[1] = (seq_number >> 8)  & 0xFF;
195   p[2] = (seq_number >> 16) & 0xFF;
196   p[3] = (seq_number >> 24) & 0xFF;
197   memset (p + 4,
198           (context_handle->more_flags & LOCAL) ? 0 : 0xFF,
199           4);
200
201   des_set_key (&deskey, schedule);
202   des_cbc_encrypt ((void *)p, (void *)p, 8,
203                    schedule, (des_cblock *)(p + 8), DES_ENCRYPT);
204
205   krb5_auth_con_setlocalseqnumber (gssapi_krb5_context,
206                                context_handle->auth_context,
207                                ++seq_number);
208
209   /* encrypt the data */
210   p += 16;
211
212   if(conf_req_flag) {
213       memcpy (&deskey, key->keyvalue.data, sizeof(deskey));
214
215       for (i = 0; i < sizeof(deskey); ++i)
216           deskey[i] ^= 0xf0;
217       des_set_key (&deskey, schedule);
218       memset (&zero, 0, sizeof(zero));
219       des_cbc_encrypt ((void *)p,
220                        (void *)p,
221                        datalen,
222                        schedule,
223                        &zero,
224                        DES_ENCRYPT);
225       
226       memset (deskey, 0, sizeof(deskey));
227       memset (schedule, 0, sizeof(schedule));
228   }
229   if(conf_state != NULL)
230       *conf_state = conf_req_flag;
231   return GSS_S_COMPLETE;
232 }
233
234 static OM_uint32
235 wrap_des3
236            (OM_uint32 * minor_status,
237             const gss_ctx_id_t context_handle,
238             int conf_req_flag,
239             gss_qop_t qop_req,
240             const gss_buffer_t input_message_buffer,
241             int * conf_state,
242             gss_buffer_t output_message_buffer,
243             krb5_keyblock *key
244            )
245 {
246   u_char *p;
247   u_char seq[8];
248   int32_t seq_number;
249   size_t len, total_len, padlength, datalen;
250   u_int32_t ret;
251   krb5_crypto crypto;
252   Checksum cksum;
253   krb5_data encdata;
254
255   padlength = 8 - (input_message_buffer->length % 8);
256   datalen = input_message_buffer->length + padlength + 8;
257   len = datalen + 34;
258   gssapi_krb5_encap_length (len, &len, &total_len);
259
260   output_message_buffer->length = total_len;
261   output_message_buffer->value  = malloc (total_len);
262   if (output_message_buffer->value == NULL)
263     return GSS_S_FAILURE;
264
265   p = gssapi_krb5_make_header(output_message_buffer->value,
266                               len,
267                               "\x02\x01"); /* TOK_ID */
268
269   /* SGN_ALG */
270   memcpy (p, "\x04\x00", 2);    /* HMAC SHA1 DES3-KD */
271   p += 2;
272   /* SEAL_ALG */
273   if(conf_req_flag)
274       memcpy (p, "\x02\x00", 2); /* DES3-KD */
275   else
276       memcpy (p, "\xff\xff", 2);
277   p += 2;
278   /* Filler */
279   memcpy (p, "\xff\xff", 2);
280   p += 2;
281
282   /* calculate checksum (the above + confounder + data + pad) */
283
284   memcpy (p + 20, p - 8, 8);
285   krb5_generate_random_block(p + 28, 8);
286   memcpy (p + 28 + 8, input_message_buffer->value,
287           input_message_buffer->length);
288   memset (p + 28 + 8 + input_message_buffer->length, padlength, padlength);
289
290   ret = krb5_crypto_init(gssapi_krb5_context, key, 0, &crypto);
291   if (ret) {
292       gssapi_krb5_set_error_string ();
293       free (output_message_buffer->value);
294       *minor_status = ret;
295       return GSS_S_FAILURE;
296   }
297
298   ret = krb5_create_checksum (gssapi_krb5_context,
299                               crypto,
300                               KRB5_KU_USAGE_SIGN,
301                               0,
302                               p + 20,
303                               datalen + 8,
304                               &cksum);
305   krb5_crypto_destroy (gssapi_krb5_context, crypto);
306   if (ret) {
307       gssapi_krb5_set_error_string ();
308       free (output_message_buffer->value);
309       *minor_status = ret;
310       return GSS_S_FAILURE;
311   }
312
313   /* zero out SND_SEQ + SGN_CKSUM in case */
314   memset (p, 0, 28);
315
316   memcpy (p + 8, cksum.checksum.data, cksum.checksum.length);
317   free_Checksum (&cksum);
318
319   /* sequence number */
320   krb5_auth_con_getlocalseqnumber (gssapi_krb5_context,
321                                context_handle->auth_context,
322                                &seq_number);
323
324   seq[0] = (seq_number >> 0)  & 0xFF;
325   seq[1] = (seq_number >> 8)  & 0xFF;
326   seq[2] = (seq_number >> 16) & 0xFF;
327   seq[3] = (seq_number >> 24) & 0xFF;
328   memset (seq + 4,
329           (context_handle->more_flags & LOCAL) ? 0 : 0xFF,
330           4);
331
332
333   ret = krb5_crypto_init(gssapi_krb5_context, key, ETYPE_DES3_CBC_NONE,
334                          &crypto);
335   if (ret) {
336       free (output_message_buffer->value);
337       *minor_status = ret;
338       return GSS_S_FAILURE;
339   }
340
341   {
342       des_cblock ivec;
343
344       memcpy (&ivec, p + 8, 8);
345       ret = krb5_encrypt_ivec (gssapi_krb5_context,
346                                crypto,
347                                KRB5_KU_USAGE_SEQ,
348                                seq, 8, &encdata,
349                                &ivec);
350   }
351   krb5_crypto_destroy (gssapi_krb5_context, crypto);
352   if (ret) {
353       gssapi_krb5_set_error_string ();
354       free (output_message_buffer->value);
355       *minor_status = ret;
356       return GSS_S_FAILURE;
357   }
358   
359   assert (encdata.length == 8);
360
361   memcpy (p, encdata.data, encdata.length);
362   krb5_data_free (&encdata);
363
364   krb5_auth_con_setlocalseqnumber (gssapi_krb5_context,
365                                context_handle->auth_context,
366                                ++seq_number);
367
368   /* encrypt the data */
369   p += 28;
370
371   if(conf_req_flag) {
372       krb5_data tmp;
373
374       ret = krb5_crypto_init(gssapi_krb5_context, key,
375                              ETYPE_DES3_CBC_NONE, &crypto);
376       if (ret) {
377           gssapi_krb5_set_error_string ();
378           free (output_message_buffer->value);
379           *minor_status = ret;
380           return GSS_S_FAILURE;
381       }
382       ret = krb5_encrypt(gssapi_krb5_context, crypto, KRB5_KU_USAGE_SEAL,
383                          p, datalen, &tmp);
384       krb5_crypto_destroy(gssapi_krb5_context, crypto);
385       if (ret) {
386           gssapi_krb5_set_error_string ();
387           free (output_message_buffer->value);
388           *minor_status = ret;
389           return GSS_S_FAILURE;
390       }
391       assert (tmp.length == datalen);
392
393       memcpy (p, tmp.data, datalen);
394       krb5_data_free(&tmp);
395   }
396   if(conf_state != NULL)
397       *conf_state = conf_req_flag;
398   return GSS_S_COMPLETE;
399 }
400
401 OM_uint32 gss_wrap
402            (OM_uint32 * minor_status,
403             const gss_ctx_id_t context_handle,
404             int conf_req_flag,
405             gss_qop_t qop_req,
406             const gss_buffer_t input_message_buffer,
407             int * conf_state,
408             gss_buffer_t output_message_buffer
409            )
410 {
411   krb5_keyblock *key;
412   OM_uint32 ret;
413   krb5_keytype keytype;
414
415   ret = gss_krb5_get_localkey(context_handle, &key);
416   if (ret) {
417       gssapi_krb5_set_error_string ();
418       *minor_status = ret;
419       return GSS_S_FAILURE;
420   }
421   krb5_enctype_to_keytype (gssapi_krb5_context, key->keytype, &keytype);
422
423   switch (keytype) {
424   case KEYTYPE_DES :
425       ret = wrap_des (minor_status, context_handle, conf_req_flag,
426                       qop_req, input_message_buffer, conf_state,
427                       output_message_buffer, key);
428       break;
429   case KEYTYPE_DES3 :
430       ret = wrap_des3 (minor_status, context_handle, conf_req_flag,
431                        qop_req, input_message_buffer, conf_state,
432                        output_message_buffer, key);
433       break;
434   default :
435       *minor_status = KRB5_PROG_ETYPE_NOSUPP;
436       ret = GSS_S_FAILURE;
437       break;
438   }
439   krb5_free_keyblock (gssapi_krb5_context, key);
440   return ret;
441 }