gnutls-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] session ticket support


From: Daiki Ueno
Subject: [PATCH] session ticket support
Date: Wed, 15 Jul 2009 05:11:45 +0900
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.0.96 (gnu/linux)

Hi,

The attached is an experimental patch which adds support for RFC5077
SessionTicket extension to GnuTLS.  I would appreciate any comment.

Some notes:

- I added gnutls_ext_register2, since the send_params callback of
  gnutls_ext_register is not currently able to send empty extension
  data.

- The interface is not flexible and there are many limitations; for
  example, there is no control whether to accept/reject tickets on
  reception.

- For the example usage of the interface, please check
  tests/session_ticket.c.

diff --git a/ChangeLog b/ChangeLog
index cf84af7..4ded5d5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,43 @@
+2009-07-15  Daiki Ueno  <address@hidden>
+
+       * doc/TODO: Remove item about session_ticket extension since it is
+       now supported.
+
+       * tests/Makefile.am (ctests): Add session_ticket.c.
+       * tests/session_ticket.c: New test.
+
+       * lib/libgnutls.map: Export gnutls_ext_register2,
+       gnutls_session_ticket_enable_client,
+       gnutls_session_ticket_enable_server.
+
+       * lib/gnutls_session_pack.c (pack_certificate_auth_info)
+       (pack_srp_auth_info, pack_anon_auth_info, pack_psk_auth_info):
+       Allocate memory for session_ticket.
+       (pack_security_parameters): Pack session_ticket.
+       (unpack_security_parameters): Unpack session_ticket.
+
+       * lib/gnutls_handshake.c (_gnutls_read_client_hello): Handle the
+       case where the server is resumed by session_ticket.
+       (_gnutls_recv_handshake): Handle NewSessionTicket handshake.
+       (_gnutls_handshake_common): Exchange NewSessionTicket handshake.
+
+       * lib/ext_session_ticket.h, lib/ext_session_ticket.c: New file.
+
+       * lib/gnutls_extensions.c (gnutls_ext_register2): New function.
+       (_gnutls_ext_init): Register session_ticket extension.
+
+       * lib/m4/hooks.m4: Add session_ticket extension checks.
+       * lib/Makefile.am (SESSION_TICKET_COBJECTS): Add ext_session_ticket.c.
+
+       * lib/gnutls_int.h: Define constants for session_ticket extension.
+       Define new struct session_ticket_server_params_st.
+       (tls_ext_st, internals_st): Add fields for session_ticket extension.
+
+       * lib/includes/gnutls/gnutls.h.in: Add prototype for
+       gnutls_ext_register2, gnutls_session_ticket_enable_client, and
+       gnutls_session_ticket_enable_server.  Define new handshake
+       GNUTLS_HANDSHAKE_NEW_SESSION_TICKET.
+
 2009-06-09  Simon Josefsson <address@hidden>
 
        * gl/m4/gnulib-comp.m4, gl/m4/version-etc.m4,
diff --git a/doc/TODO b/doc/TODO
index 86f144b..d561d57 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -50,7 +50,6 @@ Current list:
   be able to execve a new process that take over the current
   living socket (using the fcntl close-on-exec flag) and
   continue the TLS session as well.
-- Implement draft-salowey-tls-ticket-05, useful for (e.g.) EAP-FAST.
 - Reduce memory footprint
   - Inside gnutls_global_init, the library allocates about 64 kb of
     memory in almost 4000 calls to malloc. On my desktop, there are 22
diff --git a/lib/Makefile.am b/lib/Makefile.am
index ab1def4..bbe9db2 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -63,6 +63,8 @@ PSK_COBJECTS = auth_psk.c auth_psk_passwd.c gnutls_psk.c      
\
 
 OPRFI_COBJECTS = ext_oprfi.c
 
+SESSION_TICKET_COBJECTS = ext_session_ticket.c
+
 COBJECTS = gnutls_record.c gnutls_compress.c debug.c gnutls_cipher.c   \
        gnutls_buffers.c gnutls_handshake.c gnutls_num.c                \
        gnutls_errors.c gnutls_algorithms.c gnutls_dh.c gnutls_kx.c     \
@@ -97,12 +99,14 @@ HFILES = debug.h gnutls_compress.h gnutls_cipher.h 
gnutls_buffers.h \
        gnutls_rsa_export.h ext_server_name.h auth_dh_common.h          \
        ext_srp.h gnutls_srp.h auth_srp.h auth_srp_passwd.h             \
        gnutls_helper.h auth_psk.h auth_psk_passwd.h                    \
-       gnutls_supplemental.h ext_oprfi.h crypto.h random.h
+       gnutls_supplemental.h ext_oprfi.h crypto.h random.h             \
+       ext_session_ticket.h
 
 # Separate so we can create the documentation
 
 libgnutls_la_SOURCES = $(HFILES) $(COBJECTS) $(SRP_COBJECTS)   \
-       $(PSK_COBJECTS) gnutls.asn pkix.asn libgnutls.map
+       $(PSK_COBJECTS) $(SESSION_TICKET_COBJECTS) \
+       gnutls.asn pkix.asn libgnutls.map
 
 libgnutls_la_LDFLAGS = -no-undefined \
        -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
diff --git a/lib/ext_session_ticket.c b/lib/ext_session_ticket.c
new file mode 100644
index 0000000..4451c01
--- /dev/null
+++ b/lib/ext_session_ticket.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2009 Free Software Foundation
+ *
+ * Author: Daiki Ueno
+ *
+ * This file is part of GNUTLS.
+ *
+ * The GNUTLS library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA
+ *
+ */
+
+#include <gnutls_int.h>
+#include <gnutls_errors.h>
+#include <gnutls_datum.h>
+#include <ext_session_ticket.h>
+
+#ifdef ENABLE_SESSION_TICKET
+
+#define KEY_NAME_SIZE SESSION_TICKET_KEY_NAME_SIZE
+#define KEY_SIZE SESSION_TICKET_KEY_SIZE
+#define IV_SIZE SESSION_TICKET_IV_SIZE
+#define MAC_SECRET_SIZE SESSION_TICKET_MAC_SECRET_SIZE
+
+#define MAC_SIZE 32
+
+struct ticket {
+  opaque key_name[KEY_NAME_SIZE];
+  opaque IV[IV_SIZE];
+  opaque *encrypted_state;
+  uint16_t encrypted_state_len;
+  opaque mac[MAC_SIZE];
+};
+
+static int
+digest_ticket (const gnutls_datum_t *key, struct ticket *ticket, opaque 
*digest)
+{
+  digest_hd_st digest_hd;
+  uint16_t length16;
+  int ret;
+
+  ret = _gnutls_hmac_init (&digest_hd, GNUTLS_MAC_SHA256, key->data,
+                          key->size);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+  _gnutls_hmac (&digest_hd, ticket->key_name, KEY_NAME_SIZE);
+  _gnutls_hmac (&digest_hd, ticket->IV, IV_SIZE);
+  length16 = _gnutls_conv_uint16 (ticket->encrypted_state_len);
+  _gnutls_hmac (&digest_hd, &length16, 2);
+  _gnutls_hmac (&digest_hd, ticket->encrypted_state,
+               ticket->encrypted_state_len);
+  _gnutls_hmac_deinit (&digest_hd, digest);
+
+  return 0;
+}
+
+static int
+decrypt_ticket (gnutls_session_t session, struct ticket *ticket)
+{
+  cipher_hd_st cipher_hd;
+  gnutls_datum_t key, IV, mac_secret;
+  opaque final[32], *state;
+  uint16_t state_len;
+  time_t timestamp = time (0);
+  int ret;
+
+  /* Check the integrity of ticket using HMAC-SHA-256. */
+  mac_secret.data = session->security_parameters.extensions.
+    session_ticket_server_params.mac_secret;
+  mac_secret.size = MAC_SECRET_SIZE;
+  ret = digest_ticket (&mac_secret, ticket, final);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  if (memcmp (ticket->mac, final, MAC_SIZE))
+    {
+      gnutls_assert ();
+      return GNUTLS_E_DECRYPTION_FAILED;
+    }
+
+  /* Decrypt encrypted_state using 128-bit AES in CBC mode. */
+  key.data = session->security_parameters.extensions.
+    session_ticket_server_params.key;
+  key.size = KEY_SIZE;
+  IV.data = ticket->IV;
+  IV.size = IV_SIZE;
+  ret = _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+  ret = _gnutls_cipher_decrypt (&cipher_hd, ticket->encrypted_state,
+                               ticket->encrypted_state_len);
+  _gnutls_cipher_deinit (&cipher_hd);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  /* Unpack security parameters. */
+  state = ticket->encrypted_state;
+  state_len = ticket->encrypted_state_len;
+
+  DECR_LEN (state_len, 2);
+  session->internals.resumed_security_parameters.version =
+    _gnutls_version_get (state[0], state[1]);
+  state += 2;
+
+  DECR_LEN (state_len, 2);
+  memcpy (session->internals.resumed_security_parameters.
+         current_cipher_suite.suite,
+         state, 2);
+  state += 2;
+
+  DECR_LEN (state_len, 1);
+  session->internals.resumed_security_parameters.write_compression_algorithm =
+    session->internals.resumed_security_parameters.read_compression_algorithm =
+    _gnutls_compression_get_id (*state);
+  state += 1;
+
+  DECR_LEN (state_len, GNUTLS_MASTER_SIZE);
+  memcpy (session->internals.resumed_security_parameters.master_secret,
+         state, GNUTLS_MASTER_SIZE);
+  state += GNUTLS_MASTER_SIZE;
+
+  DECR_LEN (state_len, 1);
+  switch (*state++)            /* ClientAuthenticationType */
+    {
+    case 0:                    /* anonymous */
+      break;
+    case 1:                    /* certificate_based */
+      {
+       uint32_t length24 = 0;
+
+       DECR_LEN (state_len, 3);
+       length24 |= *state++;
+       length24 << 8;
+       length24 |= *state++;
+       length24 << 8;
+       length24 |= *state++;
+       
+       /* FIXME: implement */
+
+       DECR_LEN (state_len, length24);
+       state += length24;
+      }
+      break;
+    case 2:                    /* psk */
+      {
+       uint16_t length16;
+
+       DECR_LEN (state_len, 2);
+       length16 = _gnutls_read_uint16 (state);
+
+       /* FIXME: implement */
+
+       DECR_LEN (state_len, length16);
+       state += length16;
+      }
+      break;
+    }
+
+  DECR_LEN (state_len, 4);
+  session->internals.resumed_security_parameters.timestamp =
+    _gnutls_read_uint32 (state);
+
+  if (timestamp - session->internals.resumed_security_parameters.timestamp >
+      session->internals.expire_time
+      || session->internals.resumed_security_parameters.timestamp > timestamp)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_EXPIRED;
+    }
+
+  session->internals.resumed_security_parameters.entity =
+    GNUTLS_SERVER;
+
+  session->internals.resumed_security_parameters.write_bulk_cipher_algorithm =
+    session->internals.resumed_security_parameters.read_bulk_cipher_algorithm =
+    _gnutls_cipher_suite_get_cipher_algo
+    (&session->internals.resumed_security_parameters.current_cipher_suite);
+
+  session->internals.resumed_security_parameters.write_mac_algorithm =
+    session->internals.resumed_security_parameters.read_mac_algorithm =
+    _gnutls_cipher_suite_get_mac_algo
+    (&session->internals.resumed_security_parameters.current_cipher_suite);
+
+  session->internals.resumed_security_parameters.kx_algorithm =
+    _gnutls_cipher_suite_get_kx_algo
+    (&session->internals.resumed_security_parameters.current_cipher_suite);
+
+  session->internals.resumed = RESUME_TRUE;
+
+  return 0;
+}
+
+static int
+encrypt_ticket (gnutls_session_t session, struct ticket *ticket)
+{
+  cipher_hd_st cipher_hd;
+  gnutls_datum_t key, IV, mac_secret;
+  opaque *state, *p;
+  uint16_t state_len = 2 + 2 + 1 + GNUTLS_MASTER_SIZE + 1 + 4;
+  int blocksize;
+  int ret;
+
+  blocksize = _gnutls_cipher_get_block_size (GNUTLS_CIPHER_AES_128_CBC);
+  state_len = ((state_len + blocksize - 1) / blocksize) * blocksize;
+  state = gnutls_malloc (state_len);
+  if (!state)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_MEMORY_ERROR;
+    }
+  memset (state, 0, state_len);
+
+  /* Pack security parameters. */
+  p = state;
+  *p++ = _gnutls_version_get_major (session->security_parameters.version);
+  *p++ = _gnutls_version_get_minor (session->security_parameters.version);
+
+  memcpy (p, session->security_parameters.current_cipher_suite.suite, 2);
+  p += 2;
+
+  *p++ = _gnutls_compression_get_num (session->security_parameters.
+                                     read_compression_algorithm);
+
+  memcpy (p, session->security_parameters.master_secret, GNUTLS_MASTER_SIZE);
+  p += GNUTLS_MASTER_SIZE;
+
+  /* FIXME: more authentication type support. */
+  *p++ = 0;                    /* anonymous */
+  _gnutls_write_uint32 (session->security_parameters.timestamp, p);
+
+  /* Encrypt state using 128-bit AES in CBC mode. */
+  key.data = session->security_parameters.extensions.
+    session_ticket_server_params.key;
+  key.size = KEY_SIZE;
+  IV.data = session->security_parameters.extensions.
+    session_ticket_server_params.IV;
+  IV.size = IV_SIZE;
+  ret = _gnutls_cipher_init (&cipher_hd, GNUTLS_CIPHER_AES_128_CBC, &key, &IV);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      gnutls_free (state);
+      return ret;
+    }
+
+  ret = _gnutls_cipher_encrypt (&cipher_hd, state, state_len);
+  _gnutls_cipher_deinit (&cipher_hd);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      gnutls_free (state);
+      return ret;
+    }
+
+  /* Fill the ticket structure to compute MAC. */
+  memcpy (ticket->key_name,
+         session->security_parameters.extensions.
+         session_ticket_server_params.key_name,
+         KEY_NAME_SIZE);
+  memcpy (ticket->IV, IV.data, IV.size);
+  ticket->encrypted_state_len = state_len;
+  ticket->encrypted_state = state;
+
+  mac_secret.data = session->security_parameters.extensions.
+    session_ticket_server_params.mac_secret;
+  mac_secret.size = MAC_SECRET_SIZE;
+  ret = digest_ticket (&mac_secret, ticket, ticket->mac);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      gnutls_free (state);
+      return ret;
+    }
+
+  return 0;
+}
+
+int
+_gnutls_session_ticket_recv_params (gnutls_session_t session,
+                                   const opaque * data, size_t data_size)
+{
+  if (!session->internals.session_ticket_enable)
+    return 0;
+    
+  if (session->security_parameters.entity == GNUTLS_SERVER)
+    {
+      struct ticket ticket;
+      const opaque *encrypted_state;
+      int ret;
+
+      if (data_size == 0)
+       {
+         session->internals.session_ticket_renew = 1;
+         return 0;
+       }
+
+      DECR_LEN (data_size, KEY_NAME_SIZE);
+      memcpy (ticket.key_name, data, KEY_NAME_SIZE);
+      data += KEY_NAME_SIZE;
+
+      /* If the key name of the ticket does not match what we
+        hold, issue a new ticket. */
+      if (memcmp (ticket.key_name,
+                 session->security_parameters.extensions.
+                 session_ticket_server_params.key_name,
+                 KEY_NAME_SIZE))
+       {
+         session->internals.session_ticket_renew = 1;
+         return 0;
+       }
+
+      DECR_LEN (data_size, IV_SIZE);
+      memcpy (ticket.IV, data, IV_SIZE);
+      data += IV_SIZE;
+
+      DECR_LEN (data_size, 2);
+      ticket.encrypted_state_len = _gnutls_read_uint16 (data);
+      data += 2;
+
+      encrypted_state = data;
+
+      DECR_LEN (data_size, ticket.encrypted_state_len);
+      data += ticket.encrypted_state_len;
+
+      DECR_LEN (data_size, MAC_SIZE);
+      memcpy (ticket.mac, data, MAC_SIZE);
+         
+      ticket.encrypted_state = gnutls_malloc (ticket.encrypted_state_len);
+      if (!ticket.encrypted_state)
+       {
+         gnutls_assert ();
+         return GNUTLS_E_MEMORY_ERROR;
+       }
+      memcpy (ticket.encrypted_state, encrypted_state,
+             ticket.encrypted_state_len);
+
+      ret = decrypt_ticket (session, &ticket);
+      gnutls_free (ticket.encrypted_state);
+      if (ret < 0)
+       {
+         gnutls_assert ();
+         return ret;
+       }
+    }
+
+  return 0;
+}
+
+/* This function returns a positive number if we send the extension
+   data even if it is empty, zero if we do not want to send it, and a
+   negative number on failure.
+ */
+int
+_gnutls_session_ticket_send_params (gnutls_session_t session,
+                                   opaque * data, size_t *_data_size)
+{
+  size_t data_size = *_data_size;
+
+  if (!session->internals.session_ticket_enable)
+    return 0;
+
+  if (session->security_parameters.entity == GNUTLS_SERVER)
+    {
+      if (session->internals.session_ticket_renew)
+       {
+         *_data_size = 0;
+         return 1;
+       }
+    }
+  else
+    {
+      if (session->internals.resumed_security_parameters.extensions.
+         session_ticket_len > 0)
+       {
+         DECR_LENGTH_RET (data_size,
+                          session->internals.resumed_security_parameters.
+                          extensions.session_ticket_len,
+                          GNUTLS_E_SHORT_MEMORY_BUFFER);
+         memcpy (data,
+                 session->internals.resumed_security_parameters.extensions.
+                 session_ticket,
+                 session->internals.resumed_security_parameters.extensions.
+                 session_ticket_len);
+         *_data_size = session->internals.resumed_security_parameters.
+           extensions.session_ticket_len;
+         return 1;
+       }
+      else
+       {
+         session->internals.session_ticket_renew = 1;
+         *_data_size = 0;
+         return 1;
+       }
+    }
+  return 0;
+}
+
+int
+gnutls_session_ticket_enable_client (gnutls_session_t session)
+{
+  session->internals.session_ticket_enable = 1;
+  return 0;
+}
+
+int
+gnutls_session_ticket_enable_server (gnutls_session_t session,
+                                    const void *key, size_t keylen)
+{
+  int pos, ret;
+
+  if (keylen < KEY_SIZE + MAC_SECRET_SIZE)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INTERNAL_ERROR;
+    }
+    
+  ret = _gnutls_rnd (GNUTLS_RND_RANDOM,
+                    session->security_parameters.extensions.
+                    session_ticket_server_params.IV,
+                    IV_SIZE);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  memcpy (session->security_parameters.extensions.
+         session_ticket_server_params.key,
+         key, KEY_SIZE);
+
+  memcpy (session->security_parameters.extensions.
+         session_ticket_server_params.mac_secret,
+         &key[KEY_SIZE], MAC_SECRET_SIZE);
+
+  session->internals.session_ticket_enable = 1;
+  return 0;
+}
+
+int
+_gnutls_send_new_session_ticket (gnutls_session_t session, int again)
+{
+  uint8_t *data = NULL, *p;
+  int data_size;
+  int ret;
+  struct ticket ticket;
+  uint16_t ticket_len;
+
+  ret = encrypt_ticket (session, &ticket);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  ticket_len = KEY_NAME_SIZE + IV_SIZE + 2 + ticket.encrypted_state_len
+    + MAC_SIZE;
+
+  data = gnutls_malloc (4 + 2 + ticket_len);
+  if (!data)
+    {
+      gnutls_assert ();
+      gnutls_free (ticket.encrypted_state);
+      return GNUTLS_E_MEMORY_ERROR;
+    }
+
+  p = data;
+  /* FIXME: ticket lifetime is fixed to 10 days, which should be
+     customizable. */
+  _gnutls_write_uint32 (864000, p);
+  p += 4;
+
+  _gnutls_write_uint16 (ticket_len, p);
+  p += 2;
+
+  memcpy (p, ticket.key_name, KEY_NAME_SIZE);
+  p += KEY_NAME_SIZE;
+
+  memcpy (p, ticket.IV, IV_SIZE);
+  p += IV_SIZE;
+
+  _gnutls_write_uint16 (ticket.encrypted_state_len, p);
+  p += 2;
+
+  memcpy (p, ticket.encrypted_state, ticket.encrypted_state_len);
+  gnutls_free (ticket.encrypted_state);
+  p += ticket.encrypted_state_len;
+
+  memcpy (p, ticket.mac, MAC_SIZE);
+  p += MAC_SIZE;
+
+  ret = _gnutls_send_handshake (session, data, p - data,
+                               GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
+  gnutls_free (data);
+
+  return ret;
+}
+
+int
+_gnutls_recv_new_session_ticket (gnutls_session_t session)
+{
+  uint8_t *data = NULL, *p;
+  int data_size;
+  uint32_t lifetime_hint;
+  uint16_t ticket_len;
+  int ret;
+
+  ret = _gnutls_recv_handshake (session, &data, &data_size,
+                               GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      return ret;
+    }
+
+  p = data;
+  DECR_LENGTH_COM (data_size, 4, goto error);
+  lifetime_hint = _gnutls_read_uint32 (p);
+  p += 4;
+
+  DECR_LENGTH_COM (data_size, 2, goto error);
+  ticket_len = _gnutls_read_uint16 (p);
+  p += 2;
+
+  DECR_LENGTH_COM (data_size, ticket_len, goto error);
+  session->security_parameters.extensions.session_ticket =
+    gnutls_malloc (ticket_len);
+  if (!session->security_parameters.extensions.session_ticket)
+    {
+      gnutls_assert ();
+      gnutls_free (data);
+      return GNUTLS_E_MEMORY_ERROR;
+    }
+  memcpy (session->security_parameters.extensions.session_ticket,
+         p, ticket_len);
+  gnutls_free (data);
+  session->security_parameters.extensions.session_ticket_len = ticket_len;
+  return 0;
+
+ error:
+  gnutls_free (data);
+  return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
+}
+
+#endif
diff --git a/lib/ext_session_ticket.h b/lib/ext_session_ticket.h
new file mode 100644
index 0000000..59ed78f
--- /dev/null
+++ b/lib/ext_session_ticket.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2009 Free Software Foundation
+ *
+ * Author: Daiki Ueno
+ *
+ * This file is part of GNUTLS.
+ *
+ * The GNUTLS library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA
+ *
+ */
+
+#ifdef ENABLE_SESSION_TICKET
+
+int _gnutls_session_ticket_recv_params (gnutls_session_t session,
+                                       const opaque * data, size_t data_size);
+int _gnutls_session_ticket_send_params (gnutls_session_t session,
+                                       opaque * data, size_t *data_size);
+
+int _gnutls_send_new_session_ticket (gnutls_session_t session, int again);
+int _gnutls_recv_new_session_ticket (gnutls_session_t session);
+
+#endif
diff --git a/lib/gnutls_constate.c b/lib/gnutls_constate.c
index 521e55f..9afd897 100644
--- a/lib/gnutls_constate.c
+++ b/lib/gnutls_constate.c
@@ -486,9 +486,12 @@ _gnutls_read_connection_state_init (gnutls_session_t 
session)
     }
   else
     {                          /* RESUME_TRUE */
+      opaque *session_ticket =
+       session->security_parameters.extensions.session_ticket;
       _gnutls_cpy_read_security_parameters (&session->security_parameters,
                                            &session->internals.
                                            resumed_security_parameters);
+      session->security_parameters.extensions.session_ticket = session_ticket;
     }
 
 
@@ -668,9 +671,12 @@ _gnutls_write_connection_state_init (gnutls_session_t 
session)
     }
   else
     {                          /* RESUME_TRUE */
+      opaque *session_ticket =
+       session->security_parameters.extensions.session_ticket;
       _gnutls_cpy_write_security_parameters (&session->security_parameters,
                                             &session->internals.
                                             resumed_security_parameters);
+      session->security_parameters.extensions.session_ticket = session_ticket;
     }
 
   rc = _gnutls_set_write_keys (session);
diff --git a/lib/gnutls_extensions.c b/lib/gnutls_extensions.c
index 31769d2..4e397f8 100644
--- a/lib/gnutls_extensions.c
+++ b/lib/gnutls_extensions.c
@@ -35,6 +35,7 @@
 #include <ext_server_name.h>
 #include <ext_oprfi.h>
 #include <ext_srp.h>
+#include <ext_session_ticket.h>
 #include <gnutls_num.h>
 
 typedef struct
@@ -44,6 +45,7 @@ typedef struct
   gnutls_ext_parse_type_t parse_type;
   gnutls_ext_recv_func recv_func;
   gnutls_ext_send_func send_func;
+  gnutls_ext_send_func2 send_func2;
 } gnutls_extension_entry;
 
 static size_t extfunc_size = 0;
@@ -197,7 +199,7 @@ _gnutls_gen_extensions (gnutls_session_t session, opaque * 
data,
   int size;
   uint16_t pos = 0;
   opaque *sdata;
-  int sdata_size;
+  size_t sdata_size;
   size_t i;
 
   if (data_size < 2)
@@ -221,12 +223,18 @@ _gnutls_gen_extensions (gnutls_session_t session, opaque 
* data,
     {
       gnutls_extension_entry *p = &extfunc[i];
 
-      if (p->send_func == NULL)
+      if (p->send_func == NULL && p->send_func2 == NULL)
        continue;
 
-      size = p->send_func (session, sdata, sdata_size);
+      if (p->send_func2)
+       size = p->send_func2 (session, sdata, &sdata_size);
+      else
+       size = p->send_func (session, sdata, sdata_size);
       if (size > 0)
        {
+         if (p->send_func2)
+           size = sdata_size;
+
          if (data_size < pos + (size_t) size + 4)
            {
              gnutls_assert ();
@@ -324,6 +332,16 @@ _gnutls_ext_init (void)
     return ret;
 #endif
 
+#ifdef ENABLE_SESSION_TICKET
+  ret = gnutls_ext_register2 (GNUTLS_EXTENSION_SESSION_TICKET,
+                             "SESSION_TICKET",
+                             GNUTLS_EXT_TLS,
+                             _gnutls_session_ticket_recv_params,
+                             _gnutls_session_ticket_send_params);
+  if (ret != GNUTLS_E_SUCCESS)
+    return ret;
+#endif
+
   return GNUTLS_E_SUCCESS;
 }
 
@@ -371,6 +389,50 @@ gnutls_ext_register (int type,
   extfunc[extfunc_size].parse_type = parse_type;
   extfunc[extfunc_size].recv_func = recv_func;
   extfunc[extfunc_size].send_func = send_func;
+  extfunc[extfunc_size].send_func2 = NULL;
+
+  extfunc_size++;
+
+  return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ext_register2 - Register a handler for a TLS extension
+ * @type: the 16-bit integer referring to the extension type
+ * @name: human printable name of the extension used for debugging
+ * @parse_type: either #GNUTLS_EXT_TLS or %GNUTLS_EXT_APPLICATION.
+ * @recv_func: a function to receive extension data
+ * @send_func: a function to send extension data
+ *
+ * This function is used to register a new TLS extension handler.  The
+ * only difference is that the type of the 5th argument send_func is
+ * gnutls_ext_send_func2 instead of gnutls_ext_send_func.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, or an error code.
+ **/
+int
+gnutls_ext_register2 (int type,
+                    const char *name,
+                    gnutls_ext_parse_type_t parse_type,
+                    gnutls_ext_recv_func recv_func,
+                    gnutls_ext_send_func2 send_func)
+{
+  gnutls_extension_entry *p;
+
+  p = gnutls_realloc (extfunc, sizeof (*extfunc) * (extfunc_size + 1));
+  if (!p)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_MEMORY_ERROR;
+    }
+  extfunc = p;
+
+  extfunc[extfunc_size].type = type;
+  extfunc[extfunc_size].name = name;
+  extfunc[extfunc_size].parse_type = parse_type;
+  extfunc[extfunc_size].recv_func = recv_func;
+  extfunc[extfunc_size].send_func = NULL;
+  extfunc[extfunc_size].send_func2 = send_func;
 
   extfunc_size++;
 
diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c
index 1ad4f52..03364c9 100644
--- a/lib/gnutls_handshake.c
+++ b/lib/gnutls_handshake.c
@@ -354,7 +354,7 @@ _gnutls_read_client_hello (gnutls_session_t session, opaque 
* data,
   gnutls_protocol_t adv_version;
   int neg_version;
   int len = datalen;
-  opaque rnd[GNUTLS_RANDOM_SIZE], *suite_ptr, *comp_ptr;
+  opaque rnd[GNUTLS_RANDOM_SIZE], *suite_ptr, *comp_ptr, *session_id;
 
   if (session->internals.v2_hello != 0)
     {                          /* version 2.0 */
@@ -399,7 +399,8 @@ _gnutls_read_client_hello (gnutls_session_t session, opaque 
* data,
     }
   DECR_LEN (len, session_id_len);
 
-  ret = _gnutls_server_restore_session (session, &data[pos], session_id_len);
+  session_id = &data[pos];
+  ret = _gnutls_server_restore_session (session, session_id, session_id_len);
   pos += session_id_len;
 
   if (ret == 0)
@@ -467,6 +468,24 @@ _gnutls_read_client_hello (gnutls_session_t session, 
opaque * data,
          gnutls_assert ();
          return ret;
        }
+
+      /* resumed by session_ticket extension */
+      if (session->internals.resumed == RESUME_TRUE)
+       {
+         /* to indicate the client that the current session is resumed */
+         memcpy (session->internals.resumed_security_parameters.session_id,
+                 session_id, session_id_len);
+         session->internals.resumed_security_parameters.session_id_size =
+           session_id_len;
+
+         session->internals.resumed_security_parameters.max_record_recv_size =
+           session->security_parameters.max_record_recv_size;
+         session->internals.resumed_security_parameters.max_record_send_size =
+           session->security_parameters.max_record_send_size;
+
+         resume_copy_required_values (session);
+         return 0;
+       }
     }
 
   /* select an appropriate cipher suite
@@ -1291,6 +1310,7 @@ _gnutls_recv_handshake (gnutls_session_t session, uint8_t 
** data,
     case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST:
     case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY:
     case GNUTLS_HANDSHAKE_SUPPLEMENTAL:
+    case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET:
       ret = length32;
       break;
     default:
@@ -2684,6 +2704,12 @@ _gnutls_handshake_common (gnutls_session_t session)
       ret = _gnutls_recv_handshake_final (session, TRUE);
       IMED_RET ("recv handshake final", ret);
 
+      if (session->internals.session_ticket_renew)
+       {
+         ret = _gnutls_send_new_session_ticket (session);
+         IMED_RET ("send handshake new session ticket", ret);
+       }
+
       ret = _gnutls_send_handshake_final (session, FALSE);
       IMED_RET ("send handshake final", ret);
     }
@@ -2693,6 +2719,12 @@ _gnutls_handshake_common (gnutls_session_t session)
       ret = _gnutls_send_handshake_final (session, TRUE);
       IMED_RET ("send handshake final 2", ret);
 
+      if (session->internals.session_ticket_renew)
+       {
+         ret = _gnutls_recv_new_session_ticket (session);
+         IMED_RET ("recv handshake new session ticket", ret);
+       }
+
       ret = _gnutls_recv_handshake_final (session, FALSE);
       IMED_RET ("recv handshake final 2", ret);
     }
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 6d6ab5d..0d9624c 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -87,6 +87,12 @@ typedef struct
 #define MAX_LOG_SIZE 1024      /* maximum size of log message */
 #define MAX_SRP_USERNAME 128
 #define MAX_SERVER_NAME_SIZE 128
+#define MAX_SESSION_TICKET_SIZE 65535
+
+#define SESSION_TICKET_KEY_NAME_SIZE 16
+#define SESSION_TICKET_KEY_SIZE 16
+#define SESSION_TICKET_IV_SIZE 16
+#define SESSION_TICKET_MAC_SECRET_SIZE 32
 
 /* we can receive up to MAX_EXT_TYPES extensions.
  */
@@ -171,6 +177,7 @@ typedef enum extensions_t
   GNUTLS_EXTENSION_OPAQUE_PRF_INPUT = ENABLE_OPRFI,
 #endif
   GNUTLS_EXTENSION_SRP = 12,
+  GNUTLS_EXTENSION_SESSION_TICKET = 35,
   GNUTLS_EXTENSION_INNER_APPLICATION = 37703
 } extensions_t;
 
@@ -284,6 +291,14 @@ typedef struct
 
 typedef struct
 {
+  opaque key_name[SESSION_TICKET_KEY_NAME_SIZE];
+  opaque key[SESSION_TICKET_KEY_SIZE];
+  opaque IV[SESSION_TICKET_IV_SIZE];
+  opaque mac_secret[SESSION_TICKET_MAC_SECRET_SIZE];
+} session_ticket_server_params_st;
+
+typedef struct
+{
   server_name_st server_names[MAX_SERVER_NAME_EXTENSIONS];
   /* limit server_name extensions */
   unsigned server_names_size;
@@ -304,6 +319,10 @@ typedef struct
   uint16_t oprfi_client_len;
   opaque *oprfi_server;
   uint16_t oprfi_server_len;
+
+  opaque *session_ticket;
+  uint16_t session_ticket_len;
+  session_ticket_server_params_st session_ticket_server_params;
 } tls_ext_st;
 
 /* auth_info_t structures now MAY contain malloced 
@@ -673,6 +692,8 @@ typedef struct
    */
   uint16_t srp_prime_bits;
 
+  int session_ticket_enable, session_ticket_renew;
+
   /* If you add anything here, check _gnutls_handshake_internal_state_clear().
    */
 } internals_st;
diff --git a/lib/gnutls_session_pack.c b/lib/gnutls_session_pack.c
index 115f17e..dc56b0a 100644
--- a/lib/gnutls_session_pack.c
+++ b/lib/gnutls_session_pack.c
@@ -280,7 +280,8 @@ pack_certificate_auth_info (gnutls_session_t session,
   /* calculate the size and allocate the data.
    */
   packed_session->data =
-    gnutls_malloc (packed_session->size + MAX_SEC_PARAMS);
+    gnutls_malloc (packed_session->size + MAX_SEC_PARAMS + 2 +
+                  session->security_parameters.extensions.session_ticket_len);
 
   if (packed_session->data == NULL)
     {
@@ -513,7 +514,8 @@ pack_srp_auth_info (gnutls_session_t session, 
gnutls_datum_t * packed_session)
   /* calculate the size and allocate the data.
    */
   packed_session->data =
-    gnutls_malloc (packed_session->size + MAX_SEC_PARAMS);
+    gnutls_malloc (packed_session->size + MAX_SEC_PARAMS + 2 +
+                  session->security_parameters.extensions.session_ticket_len);
 
   if (packed_session->data == NULL)
     {
@@ -618,7 +620,8 @@ pack_anon_auth_info (gnutls_session_t session,
   /* calculate the size and allocate the data.
    */
   packed_session->data =
-    gnutls_malloc (packed_session->size + MAX_SEC_PARAMS);
+    gnutls_malloc (packed_session->size + MAX_SEC_PARAMS + 2 +
+                  session->security_parameters.extensions.session_ticket_len);
 
   if (packed_session->data == NULL)
     {
@@ -781,7 +784,8 @@ pack_psk_auth_info (gnutls_session_t session, 
gnutls_datum_t * packed_session)
   /* calculate the size and allocate the data.
    */
   packed_session->data =
-    gnutls_malloc (packed_session->size + MAX_SEC_PARAMS);
+    gnutls_malloc (packed_session->size + MAX_SEC_PARAMS + 2 +
+                  session->security_parameters.extensions.session_ticket_len);
 
   if (packed_session->data == NULL)
     {
@@ -977,9 +981,11 @@ error:
  *      2 bytes the size of the first name 
  *      x bytes the first name (MAX_SERVER_NAME_SIZE)
  *       and so on...
+ *      2 bytes the session ticket size
+ *      x bytes the session ticket (MAX_SESSION_TICKET_SIZE)
  *
  *           --------------------
- *                MAX: 
7+MAX_SRP_USERNAME+MAX_SERVER_NAME_EXTENSIONS*(3+MAX_SERVER_NAME_SIZE)
+ *                MAX: 
7+MAX_SRP_USERNAME+MAX_SERVER_NAME_EXTENSIONS*(3+MAX_SERVER_NAME_SIZE)+MAX_SESSION_TICKET_SIZE
  */
 static int
 pack_security_parameters (gnutls_session_t session,
@@ -1077,6 +1083,15 @@ pack_security_parameters (gnutls_session_t session,
        session->security_parameters.extensions.server_names[i].name_length;
     }
 
+  _gnutls_write_uint16 (session->security_parameters.extensions.
+                       session_ticket_len,
+                       &packed_session->data[pos]);
+  pos += 2;
+  memcpy (&packed_session->data[pos],
+         session->security_parameters.extensions.session_ticket,
+         session->security_parameters.extensions.session_ticket_len);
+  pos += session->security_parameters.extensions.session_ticket_len;
+  
   /* write the total size */
   _gnutls_write_uint32 (pos - init - 4, &packed_session->data[init]);
   packed_session->size += pos - init;
@@ -1109,7 +1124,7 @@ unpack_security_parameters (gnutls_session_t session,
     return GNUTLS_E_INVALID_REQUEST;
 
   /* a simple check for integrity */
-  if (pack_size > MAX_SEC_PARAMS)
+  if (pack_size > MAX_SEC_PARAMS + 2 + MAX_SESSION_TICKET_SIZE)
     {
       gnutls_assert ();
       return GNUTLS_E_INVALID_REQUEST;
@@ -1212,5 +1227,18 @@ unpack_security_parameters (gnutls_session_t session,
        session->internals.resumed_security_parameters.extensions.
        server_names[i].name_length;
     }
+
+  session->internals.resumed_security_parameters.extensions.
+    session_ticket_len = _gnutls_read_uint16 (&packed_session->data[pos]);
+  pos += 2;
+  session->internals.resumed_security_parameters.extensions.session_ticket =
+    gnutls_malloc (session->internals.resumed_security_parameters.extensions.
+                  session_ticket_len);
+  memcpy (session->internals.resumed_security_parameters.extensions.
+         session_ticket,
+         &packed_session->data[pos],
+         session->internals.resumed_security_parameters.extensions.
+         session_ticket_len);
+
   return 0;
 }
diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c
index 3dd440e..08bc43f 100644
--- a/lib/gnutls_state.c
+++ b/lib/gnutls_state.c
@@ -433,6 +433,10 @@ gnutls_deinit (gnutls_session_t session)
       gnutls_free (session->internals.srp_password);
     }
 
+  gnutls_free (session->security_parameters.extensions.session_ticket);
+  gnutls_free (session->internals.resumed_security_parameters.extensions.
+              session_ticket);
+
   memset (session, 0, sizeof (struct gnutls_session_int));
   gnutls_free (session);
 }
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index af27b8d..67a3eb7 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -221,6 +221,7 @@ extern "C" {
   { GNUTLS_HANDSHAKE_HELLO_REQUEST = 0,
     GNUTLS_HANDSHAKE_CLIENT_HELLO = 1,
     GNUTLS_HANDSHAKE_SERVER_HELLO = 2,
+    GNUTLS_HANDSHAKE_NEW_SESSION_TICKET = 4,
     GNUTLS_HANDSHAKE_CERTIFICATE_PKT = 11,
     GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE = 12,
     GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST = 13,
@@ -493,6 +494,8 @@ extern "C" {
                                       const unsigned char *data, size_t len);
   typedef int (*gnutls_ext_send_func) (gnutls_session_t session,
                                       unsigned char *data, size_t len);
+  typedef int (*gnutls_ext_send_func2) (gnutls_session_t session,
+                                       unsigned char *data, size_t *len);
 
   /* This flag indicates for an extension whether
    * it is useful to application level or TLS level only.
@@ -512,6 +515,12 @@ extern "C" {
                           gnutls_ext_recv_func recv_func,
                           gnutls_ext_send_func send_func);
 
+  int gnutls_ext_register2 (int type,
+                           const char *name,
+                           gnutls_ext_parse_type_t parse_type,
+                           gnutls_ext_recv_func recv_func,
+                           gnutls_ext_send_func2 send_func);
+
   typedef enum
   {
     GNUTLS_NAME_DNS = 1
@@ -554,6 +563,11 @@ extern "C" {
   const char *gnutls_supplemental_get_name
   (gnutls_supplemental_data_format_type_t type);
 
+  /* SessionTicket, RFC 5077. */
+  int gnutls_session_ticket_enable_client (gnutls_session_t);
+  int gnutls_session_ticket_enable_server (gnutls_session_t,
+                                          const void *, size_t);
+
 /* functions to set priority of cipher suites 
  */
   int gnutls_cipher_set_priority (gnutls_session_t session, const int *list);
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index 73dc6aa..d4cd02a 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -139,6 +139,7 @@ GNUTLS_1_4
     gnutls_error_is_fatal;
     gnutls_error_to_alert;
     gnutls_ext_register;
+    gnutls_ext_register2;
     gnutls_fingerprint;
     gnutls_free;
     gnutls_global_deinit;
@@ -565,6 +566,8 @@ GNUTLS_2_8
     gnutls_x509_crt_get_verify_algorithm;
     gnutls_x509_crt_set_crq_extensions;
     gnutls_x509_crt_verify_hash;
+    gnutls_session_ticket_enable_client;
+    gnutls_session_ticket_enable_server;
 } GNUTLS_1_4;
 
 GNUTLS_PRIVATE {
diff --git a/lib/m4/hooks.m4 b/lib/m4/hooks.m4
index 7ee3e50..9d7700e 100644
--- a/lib/m4/hooks.m4
+++ b/lib/m4/hooks.m4
@@ -214,6 +214,20 @@ AC_DEFUN([LIBGNUTLS_HOOKS],
   fi
   AM_CONDITIONAL(ENABLE_OPENPGP, test "$ac_enable_openpgp" = "yes")
 
+  AC_MSG_CHECKING([whether to disable SessionTicket extension support])
+  AC_ARG_ENABLE(session-ticket,
+    AS_HELP_STRING([--disable-session-ticket],
+                   [disable the SessionTicket extension support]),
+    ac_session_ticket=no)
+  if test x$ac_session_ticket != xno; then
+   AC_MSG_RESULT(no)
+   AC_DEFINE([ENABLE_SESSION_TICKET], 1, [enable SessionTicket extension])
+  else
+   ac_full=0
+   AC_MSG_RESULT(yes)
+  fi
+  AM_CONDITIONAL(ENABLE_SESSION_TICKET, test "$ac_enable_session_ticket" != 
"no")
+
   # For storing integers in pointers without warnings
   # 
http://developer.gnome.org/doc/API/2.0/glib/glib-Type-Conversion-Macros.html#desc
   AC_CHECK_SIZEOF(void *)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 6fa092e..66c8a61 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -84,6 +84,10 @@ if ENABLE_OPRFI
 ctests +=  oprfi
 endif
 
+if ENABLE_SESSION_TICKET
+ctests += session_ticket
+endif
+
 check_PROGRAMS = $(ctests)
 TESTS = $(ctests)
 
diff --git a/tests/session_ticket.c b/tests/session_ticket.c
new file mode 100644
index 0000000..1b47d87
--- /dev/null
+++ b/tests/session_ticket.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation
+ *
+ * Author: Daiki Ueno
+ *
+ * This file is part of GNUTLS.
+ *
+ * GNUTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GNUTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNUTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/* Based on resume.c. */
+/* Parts copied from GnuTLS example programs. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+
+#include "tcp.c"
+
+#include "utils.h"
+
+/* A very basic TLS client, with anonymous authentication.
+ */
+
+#define MAX_BUF 1024
+#define MSG "Hello TLS"
+
+static void
+client (void)
+{
+  int ret, sd, ii;
+  gnutls_session_t session;
+  char buffer[MAX_BUF + 1];
+  gnutls_anon_client_credentials_t anoncred;
+  /* Need to enable anonymous KX specifically. */
+  const int kx_prio[] = { GNUTLS_KX_ANON_DH, 0 };
+
+  /* variables used in session resuming
+   */
+  int t;
+  gnutls_datum session_data;
+
+  gnutls_global_init ();
+
+  gnutls_anon_allocate_client_credentials (&anoncred);
+
+  for (t = 0; t < 2; t++)
+    {                          /* connect 2 times to the server */
+
+      /* connect to the peer
+       */
+      sd = tcp_connect ();
+
+      /* Initialize TLS session
+       */
+      gnutls_init (&session, GNUTLS_CLIENT);
+
+      /* Use default priorities */
+      gnutls_set_default_priority (session);
+      gnutls_kx_set_priority (session, kx_prio);
+
+      /* put the anonymous credentials to the current session
+       */
+      gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
+
+      gnutls_session_ticket_enable_client (session);
+
+      if (t > 0)
+       {
+         /* if this is not the first time we connect */
+         gnutls_session_set_data (session, session_data.data,
+                                  session_data.size);
+         gnutls_free (session_data.data);
+       }
+
+      gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sd);
+
+      /* Perform the TLS handshake
+       */
+      ret = gnutls_handshake (session);
+
+      if (ret < 0)
+       {
+         fail ("client: Handshake failed\n");
+         gnutls_perror (ret);
+         goto end;
+       }
+      else
+       {
+         success ("client: Handshake was completed\n");
+       }
+
+      if (t == 0)
+       {                       /* the first time we connect */
+         /* get the session data size */
+         ret = gnutls_session_get_data2 (session, &session_data);
+         if (ret < 0)
+           fail ("Getting resume data failed\n");
+       }
+      else
+       {                       /* the second time we connect */
+
+         /* check if we actually resumed the previous session */
+         if (gnutls_session_is_resumed (session) != 0)
+           {
+             success ("- Previous session was resumed\n");
+           }
+         else
+           {
+             success ("*** Previous session was NOT resumed\n");
+           }
+       }
+
+      gnutls_record_send (session, MSG, strlen (MSG));
+
+      ret = gnutls_record_recv (session, buffer, MAX_BUF);
+      if (ret == 0)
+       {
+         success ("client: Peer has closed the TLS connection\n");
+         goto end;
+       }
+      else if (ret < 0)
+       {
+         fail ("client: Error: %s\n", gnutls_strerror (ret));
+         goto end;
+       }
+
+      if (debug)
+       {
+         printf ("- Received %d bytes: ", ret);
+         for (ii = 0; ii < ret; ii++)
+           {
+             fputc (buffer[ii], stdout);
+           }
+         fputs ("\n", stdout);
+       }
+
+      gnutls_bye (session, GNUTLS_SHUT_RDWR);
+
+    end:
+
+      tcp_close (sd);
+
+      gnutls_deinit (session);
+    }
+
+  gnutls_anon_free_client_credentials (anoncred);
+}
+
+/* This is a sample TLS 1.0 echo server, for anonymous authentication only.
+ */
+
+#define SA struct sockaddr
+#define MAX_BUF 1024
+#define PORT 5556              /* listen to 5556 port */
+#define DH_BITS 1024
+
+/* These are global */
+gnutls_anon_server_credentials_t anoncred;
+
+static gnutls_session_t
+initialize_tls_session (void)
+{
+  gnutls_session_t session;
+  const int kx_prio[] = { GNUTLS_KX_ANON_DH, 0 };
+  static const unsigned char st_key[48] =
+    "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL";
+
+  gnutls_init (&session, GNUTLS_SERVER);
+
+  /* avoid calling all the priority functions, since the defaults
+   * are adequate.
+   */
+  gnutls_set_default_priority (session);
+  gnutls_kx_set_priority (session, kx_prio);
+
+  gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
+
+  gnutls_dh_set_prime_bits (session, DH_BITS);
+
+  gnutls_session_ticket_enable_server (session, st_key, sizeof(st_key));
+
+  return session;
+}
+
+static gnutls_dh_params_t dh_params;
+
+static int
+generate_dh_params (void)
+{
+  const gnutls_datum_t p3 = { (char*) pkcs3, strlen (pkcs3) };
+  /* Generate Diffie-Hellman parameters - for use with DHE
+   * kx algorithms. These should be discarded and regenerated
+   * once a day, once a week or once a month. Depending on the
+   * security requirements.
+   */
+  gnutls_dh_params_init (&dh_params);
+  return gnutls_dh_params_import_pkcs3 (dh_params, &p3, GNUTLS_X509_FMT_PEM);
+}
+
+int err, listen_sd, i;
+int sd, ret;
+struct sockaddr_in sa_serv;
+struct sockaddr_in sa_cli;
+int client_len;
+char topbuf[512];
+gnutls_session_t session;
+char buffer[MAX_BUF + 1];
+int optval = 1;
+
+static void
+global_start (void)
+{
+  /* Socket operations
+   */
+  listen_sd = socket (AF_INET, SOCK_STREAM, 0);
+  if (err == -1)
+    {
+      perror ("socket");
+      fail ("server: socket failed\n");
+      return;
+    }
+
+  memset (&sa_serv, '\0', sizeof (sa_serv));
+  sa_serv.sin_family = AF_INET;
+  sa_serv.sin_addr.s_addr = INADDR_ANY;
+  sa_serv.sin_port = htons (PORT);     /* Server Port number */
+
+  setsockopt (listen_sd, SOL_SOCKET, SO_REUSEADDR, (void *) &optval, sizeof 
(int));
+
+  err = bind (listen_sd, (SA *) & sa_serv, sizeof (sa_serv));
+  if (err == -1)
+    {
+      perror ("bind");
+      fail ("server: bind failed\n");
+      return;
+    }
+
+  err = listen (listen_sd, 1024);
+  if (err == -1)
+    {
+      perror ("listen");
+      fail ("server: listen failed\n");
+      return;
+    }
+
+  success ("server: ready. Listening to port '%d'.\n", PORT);
+}
+
+static void
+global_stop (void)
+{
+  success ("global stop\n");
+
+  gnutls_anon_free_server_credentials (anoncred);
+
+  gnutls_dh_params_deinit (dh_params);
+
+  gnutls_global_deinit ();
+}
+
+static void
+server (void)
+{
+  size_t t;
+
+  /* this must be called once in the program, it is mostly for the server.
+   */
+  gnutls_global_init ();
+
+  gnutls_anon_allocate_server_credentials (&anoncred);
+
+  success ("Launched, generating DH parameters...\n");
+
+  generate_dh_params ();
+
+  gnutls_anon_set_server_dh_params (anoncred, dh_params);
+
+  for (t = 0; t < 2; t++)
+    {
+      client_len = sizeof (sa_cli);
+
+      session = initialize_tls_session ();
+
+      sd = accept (listen_sd, (SA *) & sa_cli, &client_len);
+
+      success ("server: connection from %s, port %d\n",
+              inet_ntop (AF_INET, &sa_cli.sin_addr, topbuf,
+                         sizeof (topbuf)), ntohs (sa_cli.sin_port));
+
+      gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sd);
+      ret = gnutls_handshake (session);
+      if (ret < 0)
+       {
+         close (sd);
+         gnutls_deinit (session);
+         fail ("server: Handshake has failed (%s)\n\n",
+               gnutls_strerror (ret));
+         return;
+       }
+      success ("server: Handshake was completed\n");
+
+      /* see the Getting peer's information example */
+      /* print_info(session); */
+
+      i = 0;
+      for (;;)
+       {
+         memset (buffer, 0, MAX_BUF + 1);
+         ret = gnutls_record_recv (session, buffer, MAX_BUF);
+
+         if (ret == 0)
+           {
+             success ("server: Peer has closed the GNUTLS connection\n");
+             break;
+           }
+         else if (ret < 0)
+           {
+             fail ("server: Received corrupted data(%d). Closing...\n", ret);
+             break;
+           }
+         else if (ret > 0)
+           {
+             /* echo data back to the client
+              */
+             gnutls_record_send (session, buffer, strlen (buffer));
+           }
+       }
+      /* do not wait for the peer to close the connection.
+       */
+      gnutls_bye (session, GNUTLS_SHUT_WR);
+
+      close (sd);
+
+      gnutls_deinit (session);
+    }
+
+  close (listen_sd);
+
+  success ("server: finished\n");
+}
+
+void
+doit (void)
+{
+  pid_t child;
+
+  global_start ();
+  if (error_count)
+    return;
+
+  child = fork ();
+  if (child < 0)
+    {
+      perror ("fork");
+      fail ("fork");
+      return;
+    }
+
+  if (child)
+    {
+      int status;
+      /* parent */
+      server ();
+      wait (&status);
+    }
+  else
+    client ();
+
+  global_stop ();
+}
Regards,
-- 
Daiki Ueno

reply via email to

[Prev in Thread] Current Thread [Next in Thread]