Tizen 2.0 Release
[external/libgnutls26.git] / libextra / gnutls_ia.c
1 /*
2  * Copyright (C) 2005, 2006, 2008, 2009, 2010 Free Software Foundation,
3  * Inc.
4  *
5  * Author: Simon Josefsson
6  *
7  * This file is part of GnuTLS-EXTRA.
8  *
9  * GnuTLS-extra is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * GnuTLS-extra is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22
23 #include "gnutls_int.h"
24 #include "gnutls_record.h"
25 #include "gnutls_errors.h"
26 #include "gnutls_num.h"
27 #include "gnutls_state.h"
28 #include <gnutls/extra.h>
29 #include <ext_inner_application.h>
30
31 #define CHECKSUM_SIZE 12
32
33 struct gnutls_ia_client_credentials_st
34 {
35   gnutls_ia_avp_func avp_func;
36   void *avp_ptr;
37 };
38
39 struct gnutls_ia_server_credentials_st
40 {
41   gnutls_ia_avp_func avp_func;
42   void *avp_ptr;
43 };
44
45 static const char server_finished_label[] = "server phase finished";
46 static const char client_finished_label[] = "client phase finished";
47 static const char inner_permutation_label[] = "inner secret permutation";
48 static const char challenge_label[] = "inner application challenge";
49
50 /*
51  * The TLS/IA packet is the InnerApplication token, described as
52  * follows in draft-funk-tls-inner-application-extension-01.txt:
53  *
54  * enum {
55  *   application_payload(0), intermediate_phase_finished(1),
56  *   final_phase_finished(2), (255)
57  * } InnerApplicationType;
58  *
59  * struct {
60  *   InnerApplicationType msg_type;
61  *   uint24 length;
62  *   select (InnerApplicationType) {
63  *     case application_payload:           ApplicationPayload;
64  *     case intermediate_phase_finished:   IntermediatePhaseFinished;
65  *     case final_phase_finished:          FinalPhaseFinished;
66  *   } body;
67  * } InnerApplication;
68  *
69  */
70
71 /* Send TLS/IA data.  If data==NULL && sizeofdata==NULL, then the last
72    send was interrupted for some reason, and then we try to send it
73    again.  Returns the number of bytes sent, or an error code.  If
74    this return E_AGAIN and E_INTERRUPTED, call this function again
75    with data==NULL&&sizeofdata=0NULL until it returns successfully. */
76 static ssize_t
77 _gnutls_send_inner_application (gnutls_session_t session,
78                                 gnutls_ia_apptype_t msg_type,
79                                 const char *data, size_t sizeofdata)
80 {
81   opaque *p = NULL;
82   size_t plen = 0;
83   ssize_t len;
84
85   if (data != NULL)
86     {
87       plen = sizeofdata + 4;
88       p = gnutls_malloc (plen);
89       if (!p)
90         {
91           gnutls_assert ();
92           return GNUTLS_E_MEMORY_ERROR;
93         }
94
95       *(unsigned char *) p = (unsigned char) (msg_type & 0xFF);
96       _gnutls_write_uint24 (sizeofdata, p + 1);
97       memcpy (p + 4, data, sizeofdata);
98     }
99
100   len =
101     _gnutls_send_int (session, GNUTLS_INNER_APPLICATION, -1,
102                       EPOCH_WRITE_CURRENT, p, plen, MBUFFER_FLUSH);
103
104   if (p)
105     gnutls_free (p);
106
107   return len;
108 }
109
110 /* Receive TLS/IA data.  Store received TLS/IA message type in
111    *MSG_TYPE, and the data in DATA of max SIZEOFDATA size.  Return the
112    number of bytes read, or an error code. */
113 static ssize_t
114 _gnutls_recv_inner_application (gnutls_session_t session,
115                                 gnutls_ia_apptype_t * msg_type,
116                                 opaque * data, size_t sizeofdata)
117 {
118   ssize_t len;
119   uint32_t len24;
120   opaque pkt[4];
121
122   len = _gnutls_recv_int (session, GNUTLS_INNER_APPLICATION, -1, pkt, 4);
123   if (len != 4)
124     {
125       gnutls_assert ();
126       return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
127     }
128
129   *msg_type = pkt[0];
130   len24 = _gnutls_read_uint24 (&pkt[1]);
131
132   if (*msg_type != GNUTLS_IA_APPLICATION_PAYLOAD && len24 != CHECKSUM_SIZE)
133     {
134       gnutls_assert ();
135       return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
136     }
137
138   if (sizeofdata < len24)
139     {
140       /* XXX push back pkt to IA buffer? */
141       gnutls_assert ();
142       return GNUTLS_E_SHORT_MEMORY_BUFFER;
143     }
144
145   if (len24 > 0)
146     {
147       uint32_t tmplen = len24;
148
149       len24 = _gnutls_recv_int (session, GNUTLS_INNER_APPLICATION, -1,
150                                 data, tmplen);
151       if (len24 != tmplen)
152         {
153           gnutls_assert ();
154           /* XXX Correct? */
155           return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
156         }
157     }
158
159   return len24;
160 }
161
162 /* Apply the TLS PRF using the TLS/IA inner secret as keying material,
163    where the seed is the client random concatenated with the server
164    random concatenated EXTRA of EXTRA_SIZE length (which can be NULL/0
165    respectively).  LABEL and LABEL_SIZE is used as the label.  The
166    result is placed in pre-allocated OUT of OUTSIZE length. */
167 static int
168 _gnutls_ia_prf (gnutls_session_t session,
169                 size_t label_size,
170                 const char *label,
171                 size_t extra_size,
172                 const char *extra, size_t outsize, opaque * out)
173 {
174   int ret;
175   opaque *seed;
176   size_t seedsize = 2 * GNUTLS_RANDOM_SIZE + extra_size;
177   extension_priv_data_t epriv;
178   ia_ext_st *priv;
179
180   ret =
181     _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_INNER_APPLICATION,
182                                   &epriv);
183   if (ret < 0)
184     {
185       gnutls_assert ();
186       return ret;
187     }
188   priv = epriv.ptr;
189
190   seed = gnutls_malloc (seedsize);
191   if (!seed)
192     {
193       gnutls_assert ();
194       return GNUTLS_E_MEMORY_ERROR;
195     }
196
197   memcpy (seed, session->security_parameters.server_random,
198           GNUTLS_RANDOM_SIZE);
199   memcpy (seed + GNUTLS_RANDOM_SIZE,
200           session->security_parameters.client_random, GNUTLS_RANDOM_SIZE);
201   memcpy (seed + 2 * GNUTLS_RANDOM_SIZE, extra, extra_size);
202
203   ret = _gnutls_PRF (session, priv->inner_secret,
204                      GNUTLS_MASTER_SIZE,
205                      label, label_size, seed, seedsize, outsize, out);
206
207   gnutls_free (seed);
208
209   return ret;
210 }
211
212 /**
213  * gnutls_ia_permute_inner_secret:
214  * @session: is a #gnutls_session_t structure.
215  * @session_keys_size: Size of generated session keys (0 if none).
216  * @session_keys: Generated session keys, used to permute inner secret
217  *                (NULL if none).
218  *
219  * Permute the inner secret using the generated session keys.
220  *
221  * This can be called in the TLS/IA AVP callback to mix any generated
222  * session keys with the TLS/IA inner secret.
223  *
224  * Return value: Return zero on success, or a negative error code.
225  **/
226 int
227 gnutls_ia_permute_inner_secret (gnutls_session_t session,
228                                 size_t session_keys_size,
229                                 const char *session_keys)
230 {
231   extension_priv_data_t epriv;
232   ia_ext_st *priv;
233   int ret;
234
235   ret =
236     _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_INNER_APPLICATION,
237                                   &epriv);
238   if (ret < 0)
239     {
240       gnutls_assert ();
241       return ret;
242     }
243   priv = epriv.ptr;
244
245   return _gnutls_ia_prf (session,
246                          sizeof (inner_permutation_label) - 1,
247                          inner_permutation_label,
248                          session_keys_size,
249                          session_keys,
250                          GNUTLS_RANDOM_SIZE, priv->inner_secret);
251 }
252
253 /**
254  * gnutls_ia_generate_challenge:
255  * @session: is a #gnutls_session_t structure.
256  * @buffer_size: size of output buffer.
257  * @buffer: pre-allocated buffer to contain @buffer_size bytes of output.
258  *
259  * Generate an application challenge that the client cannot control or
260  * predict, based on the TLS/IA inner secret.
261  *
262  * Return value: Returns 0 on success, or an negative error code.
263  **/
264 int
265 gnutls_ia_generate_challenge (gnutls_session_t session,
266                               size_t buffer_size, char *buffer)
267 {
268   return _gnutls_ia_prf (session,
269                          sizeof (challenge_label) - 1,
270                          challenge_label, 0, NULL, buffer_size, buffer);
271 }
272
273 /**
274  * gnutls_ia_extract_inner_secret:
275  * @session: is a #gnutls_session_t structure.
276  * @buffer: pre-allocated buffer to hold 48 bytes of inner secret.
277  *
278  * Copy the 48 bytes large inner secret into the specified buffer
279  *
280  * This function is typically used after the TLS/IA handshake has
281  * concluded.  The TLS/IA inner secret can be used as input to a PRF
282  * to derive session keys.  Do not use the inner secret directly as a
283  * session key, because for a resumed session that does not include an
284  * application phase, the inner secret will be identical to the inner
285  * secret in the original session.  It is important to include, for
286  * example, the client and server randomness when deriving a sesssion
287  * key from the inner secret.
288  **/
289 void
290 gnutls_ia_extract_inner_secret (gnutls_session_t session, char *buffer)
291 {
292   extension_priv_data_t epriv;
293   ia_ext_st *priv;
294   int ret;
295
296   ret =
297     _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_INNER_APPLICATION,
298                                   &epriv);
299   if (ret < 0)
300     {
301       gnutls_assert ();
302       return;
303     }
304   priv = epriv.ptr;
305
306   memcpy (buffer, priv->inner_secret, GNUTLS_MASTER_SIZE);
307 }
308
309 /**
310  * gnutls_ia_endphase_send:
311  * @session: is a #gnutls_session_t structure.
312  * @final_p: Set iff this should signal the final phase.
313  *
314  * Send a TLS/IA end phase message.
315  *
316  * In the client, this should only be used to acknowledge an end phase
317  * message sent by the server.
318  *
319  * In the server, this can be called instead of gnutls_ia_send() if
320  * the server wishes to end an application phase.
321  *
322  * Return value: Return 0 on success, or an error code.
323  **/
324 int
325 gnutls_ia_endphase_send (gnutls_session_t session, int final_p)
326 {
327   opaque local_checksum[CHECKSUM_SIZE];
328   int client = session->security_parameters.entity == GNUTLS_CLIENT;
329   const char *label = client ? client_finished_label : server_finished_label;
330   int size_of_label = client ? sizeof (client_finished_label) :
331     sizeof (server_finished_label);
332   ssize_t len;
333   int ret;
334   extension_priv_data_t epriv;
335   ia_ext_st *priv;
336
337   ret =
338     _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_INNER_APPLICATION,
339                                   &epriv);
340   if (ret < 0)
341     {
342       gnutls_assert ();
343       return ret;
344     }
345   priv = epriv.ptr;
346
347   ret = _gnutls_PRF (session, priv->inner_secret,
348                      GNUTLS_MASTER_SIZE, label, size_of_label - 1,
349                      /* XXX specification unclear on seed. */
350                      "", 0, CHECKSUM_SIZE, local_checksum);
351   if (ret < 0)
352     return ret;
353
354   len = _gnutls_send_inner_application
355     (session,
356      final_p ? GNUTLS_IA_FINAL_PHASE_FINISHED :
357      GNUTLS_IA_INTERMEDIATE_PHASE_FINISHED, local_checksum, CHECKSUM_SIZE);
358
359   /* XXX  Instead of calling this function over and over...?
360    * while (len == GNUTLS_E_AGAIN || len == GNUTLS_E_INTERRUPTED)
361    *  len = _gnutls_io_write_flush(session);
362    */
363
364   if (len < 0)
365     {
366       gnutls_assert ();
367       return len;
368     }
369
370   return 0;
371 }
372
373 /**
374  * gnutls_ia_verify_endphase:
375  * @session: is a #gnutls_session_t structure.
376  * @checksum: 12-byte checksum data, received from gnutls_ia_recv().
377  *
378  * Verify TLS/IA end phase checksum data.  If verification fails, the
379  * %GNUTLS_A_INNER_APPLICATION_VERIFICATION alert is sent to the other
380  * sie.
381  *
382  * This function is called when gnutls_ia_recv() return
383  * %GNUTLS_E_WARNING_IA_IPHF_RECEIVED or
384  * %GNUTLS_E_WARNING_IA_FPHF_RECEIVED.
385  *
386  * Return value: Return 0 on successful verification, or an error
387  * code.  If the checksum verification of the end phase message fails,
388  * %GNUTLS_E_IA_VERIFY_FAILED is returned.
389  **/
390 int
391 gnutls_ia_verify_endphase (gnutls_session_t session, const char *checksum)
392 {
393   char local_checksum[CHECKSUM_SIZE];
394   int client = session->security_parameters.entity == GNUTLS_CLIENT;
395   const char *label = client ? server_finished_label : client_finished_label;
396   int size_of_label = client ? sizeof (server_finished_label) :
397     sizeof (client_finished_label);
398   int ret;
399   extension_priv_data_t epriv;
400   ia_ext_st *priv;
401
402   ret =
403     _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_INNER_APPLICATION,
404                                   &epriv);
405   if (ret < 0)
406     {
407       gnutls_assert ();
408       return ret;
409     }
410   priv = epriv.ptr;
411
412   ret = _gnutls_PRF (session, priv->inner_secret,
413                      GNUTLS_MASTER_SIZE,
414                      label, size_of_label - 1,
415                      "", 0, CHECKSUM_SIZE, local_checksum);
416   if (ret < 0)
417     {
418       gnutls_assert ();
419       return ret;
420     }
421
422   if (memcmp (local_checksum, checksum, CHECKSUM_SIZE) != 0)
423     {
424       ret = gnutls_alert_send (session, GNUTLS_AL_FATAL,
425                                GNUTLS_A_INNER_APPLICATION_VERIFICATION);
426       if (ret < 0)
427         {
428           gnutls_assert ();
429           return ret;
430         }
431
432       return GNUTLS_E_IA_VERIFY_FAILED;
433     }
434
435   return 0;
436 }
437
438 /**
439  * gnutls_ia_send:
440  * @session: is a #gnutls_session_t structure.
441  * @data: contains the data to send
442  * @sizeofdata: is the length of the data
443  *
444  * Send TLS/IA application payload data.  This function has the
445  * similar semantics with send().  The only difference is that it
446  * accepts a GnuTLS session, and uses different error codes.
447  *
448  * The TLS/IA protocol is synchronous, so you cannot send more than
449  * one packet at a time.  The client always send the first packet.
450  *
451  * To finish an application phase in the server, use
452  * gnutls_ia_endphase_send().  The client cannot end an application
453  * phase unilaterally; rather, a client is required to respond with an
454  * endphase of its own if gnutls_ia_recv indicates that the server has
455  * sent one.
456  *
457  * If the EINTR is returned by the internal push function (the default
458  * is send()} then %GNUTLS_E_INTERRUPTED will be returned.  If
459  * %GNUTLS_E_INTERRUPTED or %GNUTLS_E_AGAIN is returned, you must call
460  * this function again, with the same parameters; alternatively you
461  * could provide a %NULL pointer for data, and 0 for size.
462  *
463  * Returns: The number of bytes sent, or a negative error code.
464  **/
465 ssize_t
466 gnutls_ia_send (gnutls_session_t session, const char *data, size_t sizeofdata)
467 {
468   ssize_t len;
469
470   len = _gnutls_send_inner_application (session,
471                                         GNUTLS_IA_APPLICATION_PAYLOAD,
472                                         data, sizeofdata);
473
474   return len;
475 }
476
477 /**
478  * gnutls_ia_recv:
479  * @session: is a #gnutls_session_t structure.
480  * @data: the buffer that the data will be read into, must hold >= 12 bytes.
481  * @sizeofdata: the number of requested bytes, must be >= 12.
482  *
483  * Receive TLS/IA data.  This function has the similar semantics with
484  * recv().  The only difference is that it accepts a GnuTLS session,
485  * and uses different error codes.
486  *
487  * If the server attempt to finish an application phase, this function
488  * will return %GNUTLS_E_WARNING_IA_IPHF_RECEIVED or
489  * %GNUTLS_E_WARNING_IA_FPHF_RECEIVED.  The caller should then invoke
490  * gnutls_ia_verify_endphase(), and if it runs the client side, also
491  * send an endphase message of its own using gnutls_ia_endphase_send.
492  *
493  * If EINTR is returned by the internal push function (the default is
494  * @code{recv()}) then GNUTLS_E_INTERRUPTED will be returned.  If
495  * GNUTLS_E_INTERRUPTED or GNUTLS_E_AGAIN is returned, you must call
496  * this function again, with the same parameters; alternatively you
497  * could provide a NULL pointer for data, and 0 for size.
498  *
499  * Returns: The number of bytes received.  A negative error code is
500  * returned in case of an error.  The
501  * %GNUTLS_E_WARNING_IA_IPHF_RECEIVED and
502  * %GNUTLS_E_WARNING_IA_FPHF_RECEIVED errors are returned when an
503  * application phase finished message has been sent by the server.
504  **/
505 ssize_t
506 gnutls_ia_recv (gnutls_session_t session, char *data, size_t sizeofdata)
507 {
508   gnutls_ia_apptype_t msg_type = 0;
509   ssize_t len;
510
511   len = _gnutls_recv_inner_application (session, &msg_type, data, sizeofdata);
512
513   if (msg_type == GNUTLS_IA_INTERMEDIATE_PHASE_FINISHED)
514     return GNUTLS_E_WARNING_IA_IPHF_RECEIVED;
515   else if (msg_type == GNUTLS_IA_FINAL_PHASE_FINISHED)
516     return GNUTLS_E_WARNING_IA_FPHF_RECEIVED;
517
518   return len;
519 }
520
521 /* XXX rewrite the following two functions as state machines, to
522    handle EAGAIN/EINTERRUPTED?  just add more problems to callers,
523    though.  */
524
525 static int
526 _gnutls_ia_client_handshake (gnutls_session_t session)
527 {
528   char *buf = NULL;
529   size_t buflen = 0;
530   char tmp[1024];               /* XXX */
531   ssize_t len;
532   int ret;
533   const struct gnutls_ia_client_credentials_st *cred =
534     _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL);
535
536   if (cred == NULL)
537     return GNUTLS_E_INTERNAL_ERROR;
538
539   while (1)
540     {
541       char *avp;
542       size_t avplen;
543
544       ret = cred->avp_func (session, cred->avp_ptr,
545                             buf, buflen, &avp, &avplen);
546       if (ret)
547         {
548           int tmpret;
549           tmpret = gnutls_alert_send (session, GNUTLS_AL_FATAL,
550                                       GNUTLS_A_INNER_APPLICATION_FAILURE);
551           if (tmpret < 0)
552             gnutls_assert ();
553           return ret;
554         }
555
556       len = gnutls_ia_send (session, avp, avplen);
557       gnutls_free (avp);
558       if (len < 0)
559         return len;
560
561       len = gnutls_ia_recv (session, tmp, sizeof (tmp));
562       if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED ||
563           len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED)
564         {
565           ret = gnutls_ia_verify_endphase (session, tmp);
566           if (ret < 0)
567             return ret;
568
569           ret = gnutls_ia_endphase_send
570             (session, len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED);
571           if (ret < 0)
572             return ret;
573         }
574
575       if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED)
576         {
577           buf = NULL;
578           buflen = 0;
579           continue;
580         }
581       else if (len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED)
582         break;
583
584       if (len < 0)
585         return len;
586
587       buflen = len;
588       buf = tmp;
589     }
590
591   return 0;
592 }
593
594 static int
595 _gnutls_ia_server_handshake (gnutls_session_t session)
596 {
597   gnutls_ia_apptype_t msg_type;
598   ssize_t len;
599   char buf[1024];
600   int ret;
601   const struct gnutls_ia_server_credentials_st *cred =
602     _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL);
603
604   if (cred == NULL)
605     return GNUTLS_E_INTERNAL_ERROR;
606
607   do
608     {
609       char *avp;
610       size_t avplen;
611
612       len = gnutls_ia_recv (session, buf, sizeof (buf));
613       if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED ||
614           len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED)
615         {
616           ret = gnutls_ia_verify_endphase (session, buf);
617           if (ret < 0)
618             return ret;
619         }
620
621       if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED)
622         continue;
623       else if (len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED)
624         break;
625
626       if (len < 0)
627         return len;
628
629       avp = NULL;
630       avplen = 0;
631
632       ret = cred->avp_func (session, cred->avp_ptr, buf, len, &avp, &avplen);
633       if (ret < 0)
634         {
635           int tmpret;
636           tmpret = gnutls_alert_send (session, GNUTLS_AL_FATAL,
637                                       GNUTLS_A_INNER_APPLICATION_FAILURE);
638           if (tmpret < 0)
639             gnutls_assert ();
640           return ret;
641         }
642
643       msg_type = ret;
644
645       if (msg_type != GNUTLS_IA_APPLICATION_PAYLOAD)
646         {
647           ret = gnutls_ia_endphase_send (session, msg_type ==
648                                          GNUTLS_IA_FINAL_PHASE_FINISHED);
649           if (ret < 0)
650             return ret;
651         }
652       else
653         {
654           len = gnutls_ia_send (session, avp, avplen);
655           gnutls_free (avp);
656           if (len < 0)
657             return len;
658         }
659     }
660   while (1);
661
662   return 0;
663 }
664
665 /**
666  * gnutls_ia_handshake_p:
667  * @session: is a #gnutls_session_t structure.
668  *
669  * Predicate to be used after gnutls_handshake() to decide whether to
670  * invoke gnutls_ia_handshake().  Usable by both clients and servers.
671  *
672  * Return value: non-zero if TLS/IA handshake is expected, zero
673  *   otherwise.
674  **/
675 int
676 gnutls_ia_handshake_p (gnutls_session_t session)
677 {
678   extension_priv_data_t epriv;
679   ia_ext_st *priv;
680   int ret;
681
682   ret =
683     _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_SERVER_NAME,
684                                   &epriv);
685   if (ret < 0)
686     {
687       gnutls_assert ();
688       return ret;
689     }
690   priv = epriv.ptr;
691
692   /* Either local side or peer doesn't do TLS/IA: don't do IA */
693
694   if (!(priv->flags & IA_ENABLE) || !(priv->flags & IA_PEER_ENABLE))
695     return 0;
696
697   /* Not resuming or we don't allow skipping on resumption locally: do IA */
698
699   if (!(priv->flags & IA_ALLOW_SKIP) || !gnutls_session_is_resumed (session))
700     return 1;
701
702   /* If we're resuming and we and the peer both allow skipping on resumption: 
703    * don't do IA */
704
705   return !(priv->flags & IA_PEER_ALLOW_SKIP);
706 }
707
708
709 /**
710  * gnutls_ia_handshake:
711  * @session: is a #gnutls_session_t structure.
712  *
713  * Perform a TLS/IA handshake.  This should be called after
714  * gnutls_handshake() iff gnutls_ia_handshake_p().
715  *
716  * Returns: On success, %GNUTLS_E_SUCCESS (zero) is returned,
717  *   otherwise an error code is returned.
718  **/
719 int
720 gnutls_ia_handshake (gnutls_session_t session)
721 {
722   int ret;
723
724   if (session->security_parameters.entity == GNUTLS_CLIENT)
725     ret = _gnutls_ia_client_handshake (session);
726   else
727     ret = _gnutls_ia_server_handshake (session);
728
729   return ret;
730 }
731
732 /**
733  * gnutls_ia_allocate_client_credentials:
734  * @sc: is a pointer to a #gnutls_ia_server_credentials_t structure.
735  *
736  * This structure is complex enough to manipulate directly thus this
737  * helper function is provided in order to allocate it.
738  *
739  * Adding this credential to a session will enable TLS/IA, and will
740  * require an Application Phase after the TLS handshake (if the server
741  * support TLS/IA).  Use gnutls_ia_enable() to toggle the TLS/IA mode.
742  *
743  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
744  *   an error code is returned.
745  **/
746 int
747 gnutls_ia_allocate_client_credentials (gnutls_ia_client_credentials_t * sc)
748 {
749   *sc = gnutls_calloc (1, sizeof (**sc));
750
751   if (*sc == NULL)
752     return GNUTLS_E_MEMORY_ERROR;
753
754   return 0;
755 }
756
757 /**
758  * gnutls_ia_free_client_credentials:
759  * @sc: is a #gnutls_ia_client_credentials_t structure.
760  *
761  * This structure is complex enough to manipulate directly thus this
762  * helper function is provided in order to free (deallocate) it.
763  *
764  **/
765 void
766 gnutls_ia_free_client_credentials (gnutls_ia_client_credentials_t sc)
767 {
768   gnutls_free (sc);
769 }
770
771 /**
772  * gnutls_ia_set_client_avp_function:
773  * @cred: is a #gnutls_ia_client_credentials_t structure.
774  * @avp_func: is the callback function
775  *
776  * Set the TLS/IA AVP callback handler used for the session.
777  *
778  * The AVP callback is called to process AVPs received from the
779  * server, and to get a new AVP to send to the server.
780  *
781  * The callback's function form is:
782  * int (*avp_func) (gnutls_session_t session, void *ptr,
783  *                  const char *last, size_t lastlen,
784  *                  char **next, size_t *nextlen);
785  *
786  * The @session parameter is the #gnutls_session_t structure
787  * corresponding to the current session.  The @ptr parameter is the
788  * application hook pointer, set through
789  * gnutls_ia_set_client_avp_ptr().  The AVP received from the server
790  * is present in @last of @lastlen size, which will be %NULL on the
791  * first invocation.  The newly allocated output AVP to send to the
792  * server should be placed in *@next of *@nextlen size.
793  *
794  * The callback may invoke gnutls_ia_permute_inner_secret() to mix any
795  * generated session keys with the TLS/IA inner secret.
796  *
797  * Return 0 (%GNUTLS_IA_APPLICATION_PAYLOAD) on success, or a negative
798  * error code to abort the TLS/IA handshake.
799  *
800  * Note that the callback must use allocate the @next parameter using
801  * gnutls_malloc(), because it is released via gnutls_free() by the
802  * TLS/IA handshake function.
803  *
804  **/
805 void
806 gnutls_ia_set_client_avp_function (gnutls_ia_client_credentials_t cred,
807                                    gnutls_ia_avp_func avp_func)
808 {
809   cred->avp_func = avp_func;
810 }
811
812 /**
813  * gnutls_ia_set_client_avp_ptr:
814  * @cred: is a #gnutls_ia_client_credentials_t structure.
815  * @ptr: is the pointer
816  *
817  * Sets the pointer that will be provided to the TLS/IA callback
818  * function as the first argument.
819  **/
820 void
821 gnutls_ia_set_client_avp_ptr (gnutls_ia_client_credentials_t cred, void *ptr)
822 {
823   cred->avp_ptr = ptr;
824 }
825
826 /**
827  * gnutls_ia_get_client_avp_ptr:
828  * @cred: is a #gnutls_ia_client_credentials_t structure.
829  *
830  * Returns the pointer that will be provided to the TLS/IA callback
831  * function as the first argument.
832  *
833  * Returns: The client callback data pointer.
834  **/
835 void *
836 gnutls_ia_get_client_avp_ptr (gnutls_ia_client_credentials_t cred)
837 {
838   return cred->avp_ptr;
839 }
840
841 /**
842  * gnutls_ia_allocate_server_credentials:
843  * @sc: is a pointer to a #gnutls_ia_server_credentials_t structure.
844  *
845  * This structure is complex enough to manipulate directly thus this
846  * helper function is provided in order to allocate it.
847  *
848  * Adding this credential to a session will enable TLS/IA, and will
849  * require an Application Phase after the TLS handshake (if the client
850  * support TLS/IA).  Use gnutls_ia_enable() to toggle the TLS/IA mode.
851  *
852  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
853  *   an error code is returned.
854  **/
855 int
856 gnutls_ia_allocate_server_credentials (gnutls_ia_server_credentials_t * sc)
857 {
858   *sc = gnutls_calloc (1, sizeof (**sc));
859
860   if (*sc == NULL)
861     return GNUTLS_E_MEMORY_ERROR;
862
863   return 0;
864 }
865
866 /**
867  * gnutls_ia_free_server_credentials:
868  * @sc: is a #gnutls_ia_server_credentials_t structure.
869  *
870  * This structure is complex enough to manipulate directly thus this
871  * helper function is provided in order to free (deallocate) it.
872  *
873  **/
874 void
875 gnutls_ia_free_server_credentials (gnutls_ia_server_credentials_t sc)
876 {
877   gnutls_free (sc);
878 }
879
880 /**
881  * gnutls_ia_set_server_credentials_function:
882  * @cred: is a #gnutls_ia_server_credentials_t structure.
883  * @func: is the callback function
884  *
885  * Set the TLS/IA AVP callback handler used for the session.
886  *
887  * The callback's function form is:
888  * int (*avp_func) (gnutls_session_t session, void *ptr,
889  *                  const char *last, size_t lastlen,
890  *                  char **next, size_t *nextlen);
891  *
892  * The @session parameter is the #gnutls_session_t structure
893  * corresponding to the current session.  The @ptr parameter is the
894  * application hook pointer, set through
895  * gnutls_ia_set_server_avp_ptr().  The AVP received from the client
896  * is present in @last of @lastlen size.  The newly allocated output
897  * AVP to send to the client should be placed in *@next of *@nextlen
898  * size.
899  *
900  * The AVP callback is called to process incoming AVPs from the
901  * client, and to get a new AVP to send to the client.  It can also be
902  * used to instruct the TLS/IA handshake to do go into the
903  * Intermediate or Final phases.  It return a negative error code, or
904  * a #gnutls_ia_apptype_t message type.
905  *
906  * The callback may invoke gnutls_ia_permute_inner_secret() to mix any
907  * generated session keys with the TLS/IA inner secret.
908  *
909  * Specifically, return %GNUTLS_IA_APPLICATION_PAYLOAD (0) to send
910  * another AVP to the client, return
911  * %GNUTLS_IA_INTERMEDIATE_PHASE_FINISHED (1) to indicate that an
912  * IntermediatePhaseFinished message should be sent, and return
913  * %GNUTLS_IA_FINAL_PHASE_FINISHED (2) to indicate that an
914  * FinalPhaseFinished message should be sent.  In the last two cases,
915  * the contents of the @next and @nextlen parameter is not used.
916  *
917  * Note that the callback must use allocate the @next parameter using
918  * gnutls_malloc(), because it is released via gnutls_free() by the
919  * TLS/IA handshake function.
920  **/
921 void
922 gnutls_ia_set_server_avp_function (gnutls_ia_server_credentials_t cred,
923                                    gnutls_ia_avp_func avp_func)
924 {
925   cred->avp_func = avp_func;
926 }
927
928 /**
929  * gnutls_ia_set_server_avp_ptr:
930  * @cred: is a #gnutls_ia_client_credentials_t structure.
931  * @ptr: is the pointer
932  *
933  * Sets the pointer that will be provided to the TLS/IA callback
934  * function as the first argument.
935  **/
936 void
937 gnutls_ia_set_server_avp_ptr (gnutls_ia_server_credentials_t cred, void *ptr)
938 {
939   cred->avp_ptr = ptr;
940 }
941
942 /**
943  * gnutls_ia_get_server_avp_ptr:
944  * @cred: is a #gnutls_ia_client_credentials_t structure.
945  *
946  * Returns the pointer that will be provided to the TLS/IA callback
947  * function as the first argument.
948  *
949  * Returns: The server callback data pointer.
950  **/
951 void *
952 gnutls_ia_get_server_avp_ptr (gnutls_ia_server_credentials_t cred)
953 {
954   return cred->avp_ptr;
955 }
956
957 /**
958  * gnutls_ia_enable:
959  * @session: is a #gnutls_session_t structure.
960  * @allow_skip_on_resume: non-zero if local party allows one to skip the
961  *                        TLS/IA application phases for a resumed session.
962  *
963  * Specify whether we must advertise support for the TLS/IA extension
964  * during the handshake.
965  *
966  * At the client side, we always advertise TLS/IA if gnutls_ia_enable
967  * was called before the handshake; at the server side, we also
968  * require that the client has advertised that it wants to run TLS/IA
969  * before including the advertisement, as required by the protocol.
970  *
971  * Similarly, at the client side we always advertise that we allow
972  * TLS/IA to be skipped for resumed sessions if @allow_skip_on_resume
973  * is non-zero; at the server side, we also require that the session
974  * is indeed resumable and that the client has also advertised that it
975  * allows TLS/IA to be skipped for resumed sessions.
976  *
977  * After the TLS handshake, call gnutls_ia_handshake_p() to find out
978  * whether both parties agreed to do a TLS/IA handshake, before
979  * calling gnutls_ia_handshake() or one of the lower level gnutls_ia_*
980  * functions.
981  **/
982 void
983 gnutls_ia_enable (gnutls_session_t session, int allow_skip_on_resume)
984 {
985   extension_priv_data_t epriv;
986   ia_ext_st *priv;
987
988   priv = gnutls_calloc (1, sizeof (*priv));
989   if (priv == NULL)
990     {
991       gnutls_assert ();
992       return;
993     }
994
995   epriv.ptr = priv;
996
997   priv->flags |= IA_ENABLE;
998   if (allow_skip_on_resume)
999     priv->flags |= IA_ALLOW_SKIP;
1000
1001   _gnutls_ext_set_session_data (session, GNUTLS_EXTENSION_INNER_APPLICATION,
1002                                 epriv);
1003
1004 }