Merge branch 'padding' into development
diff --git a/ChangeLog b/ChangeLog
index e0c1ec9..505d90a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -17,6 +17,7 @@
    * Support for truncated_hmac extension (RFC 6066)
    * Support for zeros-and-length (ANSI X.923) padding, one-and-zeros
      (ISO/IEC 7816-4) padding and zero padding in the cipher layer
+   * Support for session tickets (RFC 5077)
 
 Changes
    * Introduced separate SSL Ciphersuites module that is based on
diff --git a/include/polarssl/config.h b/include/polarssl/config.h
index 6cabebb..6fa95c4 100644
--- a/include/polarssl/config.h
+++ b/include/polarssl/config.h
@@ -529,6 +529,18 @@
 #define POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO
 
 /**
+ * \def POLARSSL_SSL_SESSION_TICKETS
+ *
+ * Enable support for RFC 5077 session tickets in SSL
+ *
+ * Requires: POLARSSL_AES_C
+ *           POLARSSL_SHA256_C
+ *
+ * Comment this macro to disable support for SSL session tickets
+ */
+#define POLARSSL_SSL_SESSION_TICKETS
+
+/**
  * \def POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION
  *
  * If set, the X509 parser will not break-off when parsing an X509 certificate
diff --git a/include/polarssl/error.h b/include/polarssl/error.h
index f8c23e6..48de009 100644
--- a/include/polarssl/error.h
+++ b/include/polarssl/error.h
@@ -84,7 +84,7 @@
  * ECP      4   4 (Started from top)
  * MD       5   4
  * CIPHER   6   5
- * SSL      6   2 (Started from top)
+ * SSL      6   4 (Started from top)
  * SSL      7   31
  *
  * Module dependent error code (5 bits 0x.08.-0x.F8.)
diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h
index a463460..f45d00e 100644
--- a/include/polarssl/ssl.h
+++ b/include/polarssl/ssl.h
@@ -35,6 +35,7 @@
 #include "sha1.h"
 #include "sha256.h"
 #include "sha512.h"
+#include "aes.h"
 
 #include "ssl_ciphersuites.h"
 
@@ -107,6 +108,8 @@
 #define POLARSSL_ERR_SSL_HW_ACCEL_FALLTHROUGH              -0x6F80  /**< Hardware acceleration function skipped / left alone data */
 #define POLARSSL_ERR_SSL_COMPRESSION_FAILED                -0x6F00  /**< Processing of the compression / decompression failed */
 #define POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION           -0x6E80  /**< Handshake protocol not within min/max boundaries */
+#define POLARSSL_ERR_SSL_BAD_HS_NEW_SESSION_TICKET         -0x6E00  /**< Processing of the NewSessionTicket handshake message failed. */
+
 
 /*
  * Various constants
@@ -152,6 +155,9 @@
 #define SSL_TRUNC_HMAC_ENABLED          1
 #define SSL_TRUNCATED_HMAC_LEN          10  /* 80 bits, rfc 6066 section 7 */
 
+#define SSL_SESSION_TICKETS_DISABLED     0
+#define SSL_SESSION_TICKETS_ENABLED      1
+
 /*
  * Size of the input / output buffer.
  * Note: the RFC defines the default size of SSL / TLS messages. If you
@@ -239,6 +245,7 @@
 #define SSL_HS_HELLO_REQUEST            0
 #define SSL_HS_CLIENT_HELLO             1
 #define SSL_HS_SERVER_HELLO             2
+#define SSL_HS_NEW_SESSION_TICKET       4
 #define SSL_HS_CERTIFICATE             11
 #define SSL_HS_SERVER_KEY_EXCHANGE     12
 #define SSL_HS_CERTIFICATE_REQUEST     13
@@ -262,6 +269,8 @@
 
 #define TLS_EXT_SIG_ALG                     13
 
+#define TLS_EXT_SESSION_TICKET              35
+
 #define TLS_EXT_RENEGOTIATION_INFO      0xFF01
 
 /*
@@ -311,7 +320,8 @@
     SSL_SERVER_FINISHED,
     SSL_FLUSH_BUFFERS,
     SSL_HANDSHAKE_WRAPUP,
-    SSL_HANDSHAKE_OVER
+    SSL_HANDSHAKE_OVER,
+    SSL_SERVER_NEW_SESSION_TICKET,
 }
 ssl_states;
 
@@ -319,6 +329,9 @@
 typedef struct _ssl_context ssl_context;
 typedef struct _ssl_transform ssl_transform;
 typedef struct _ssl_handshake_params ssl_handshake_params;
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+typedef struct _ssl_ticket_keys ssl_ticket_keys;
+#endif
 
 /*
  * This structure is used for storing current session data.
@@ -338,6 +351,12 @@
     x509_cert *peer_cert;       /*!< peer X.509 cert chain */
 #endif /* POLARSSL_X509_PARSE_C */
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+    unsigned char *ticket;      /*!< RFC 5077 session ticket */
+    size_t ticket_len;          /*!< session ticket length   */
+    uint32_t ticket_lifetime;   /*!< ticket lifetime hint    */
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
     unsigned char mfl_code;     /*!< MaxFragmentLength negotiated by peer */
     int trunc_hmac;             /*!< flag for truncated hmac activation   */
 };
@@ -428,8 +447,25 @@
     int resume;                         /*!<  session resume indicator*/
     int max_major_ver;                  /*!< max. major version client*/
     int max_minor_ver;                  /*!< max. minor version client*/
+
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+    int new_session_ticket;             /*!< use NewSessionTicket?    */
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
 };
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+/*
+ * Parameters needed to secure session tickets
+ */
+struct _ssl_ticket_keys
+{
+    unsigned char key_name[16];     /*!< name to quickly discard bad tickets */
+    aes_context enc;                /*!< encryption context                  */
+    aes_context dec;                /*!< decryption context                  */
+    unsigned char mac_key[16];      /*!< authentication key                  */
+};
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
 struct _ssl_context
 {
     /*
@@ -538,6 +574,13 @@
     const char *peer_cn;                /*!<  expected peer CN        */
 #endif /* POLARSSL_X509_PARSE_C */
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+    /*
+     * Support for generating and checking session tickets
+     */
+    ssl_ticket_keys *ticket_keys;       /*!<  keys for ticket encryption */
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
     /*
      * User settings
      */
@@ -549,6 +592,7 @@
     int allow_legacy_renegotiation;     /*!<  allow legacy renegotiation     */
     const int *ciphersuite_list[4];     /*!<  allowed ciphersuites / version */
     int trunc_hmac;                     /*!<  negotiate truncated hmac?      */
+    int session_tickets;                /*!<  use session tickets?    */
 
 #if defined(POLARSSL_DHM_C)
     mpi dhm_P;                          /*!<  prime modulus for DHM   */
@@ -655,6 +699,9 @@
  *
  * \param ssl      SSL context
  * \param endpoint must be SSL_IS_CLIENT or SSL_IS_SERVER
+ *
+ * \note           This function should be called right after ssl_init() since
+ *                 some other ssl_set_foo() functions depend on it.
  */
 void ssl_set_endpoint( ssl_context *ssl, int endpoint );
 
@@ -774,15 +821,17 @@
  * \brief          Request resumption of session (client-side only)
  *                 Session data is copied from presented session structure.
  *
- *                 Warning: session.peer_cert is cleared by the SSL/TLS layer on
- *                 connection shutdown, so do not cache the pointer! Either set
- *                 it to NULL or make a full copy of the certificate when
- *                 storing the session for use in this function.
- *
  * \param ssl      SSL context
  * \param session  session context
+ *
+ * \return         0 if successful,
+ *                 POLARSSL_ERR_SSL_MALLOC_FAILED if memory allocation failed,
+ *                 POLARSSL_ERR_SSL_BAD_INPUT_DATA if used server-side or
+ *                 arguments are otherwise invalid
+ *
+ * \sa             ssl_get_session()
  */
-void ssl_set_session( ssl_context *ssl, const ssl_session *session );
+int ssl_set_session( ssl_context *ssl, const ssl_session *session );
 
 /**
  * \brief               Set the list of allowed ciphersuites
@@ -998,6 +1047,26 @@
  */
 int ssl_set_truncated_hmac( ssl_context *ssl, int truncate );
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+/**
+ * \brief          Enable / Disable session tickets
+ *                 (Default: SSL_SESSION_TICKETS_ENABLED on client,
+ *                           SSL_SESSION_TICKETS_DISABLED on server)
+ *
+ * \note           On server, ssl_set_rng() must be called before this function
+ *                 to allow generating the ticket encryption and
+ *                 authentication keys.
+ *
+ * \param ssl      SSL context
+ * \param use_tickets   Enable or disable (SSL_SESSION_TICKETS_ENABLED or
+ *                                         SSL_SESSION_TICKETS_DISABLED)
+ *
+ * \return         O if successful,
+ *                 or a specific error code (server only).
+ */
+int ssl_set_session_tickets( ssl_context *ssl, int use_tickets );
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
 /**
  * \brief          Enable / Disable renegotiation support for connection when
  *                 initiated by peer
@@ -1101,6 +1170,24 @@
 #endif /* POLARSSL_X509_PARSE_C */
 
 /**
+ * \brief          Save session in order to resume it later (client-side only)
+ *                 Session data is copied to presented session structure.
+ *
+ * \warning        Currently, peer certificate is lost in the operation.
+ *
+ * \param ssl      SSL context
+ * \param session  session context
+ *
+ * \return         0 if successful,
+ *                 POLARSSL_ERR_SSL_MALLOC_FAILED if memory allocation failed,
+ *                 POLARSSL_ERR_SSL_BAD_INPUT_DATA if used server-side or
+ *                 arguments are otherwise invalid
+ *
+ * \sa             ssl_set_session()
+ */
+int ssl_get_session( const ssl_context *ssl, ssl_session *session );
+
+/**
  * \brief          Perform the SSL handshake
  *
  * \param ssl      SSL context
diff --git a/library/error.c b/library/error.c
index 560c54c..94d8dc1 100644
--- a/library/error.c
+++ b/library/error.c
@@ -369,6 +369,8 @@
             snprintf( buf, buflen, "SSL - Processing of the compression / decompression failed" );
         if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION) )
             snprintf( buf, buflen, "SSL - Handshake protocol not within min/max boundaries" );
+        if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_NEW_SESSION_TICKET) )
+            snprintf( buf, buflen, "SSL - Processing of the NewSessionTicket handshake message failed" );
 #endif /* POLARSSL_SSL_TLS_C */
 
 #if defined(POLARSSL_X509_PARSE_C)
diff --git a/library/ssl_cli.c b/library/ssl_cli.c
index 877d6cd..ac72832 100644
--- a/library/ssl_cli.c
+++ b/library/ssl_cli.c
@@ -30,6 +30,13 @@
 #include "polarssl/debug.h"
 #include "polarssl/ssl.h"
 
+#if defined(POLARSSL_MEMORY_C)
+#include "polarssl/memory.h"
+#else
+#define polarssl_malloc     malloc
+#define polarssl_free       free
+#endif
+
 #include <stdlib.h>
 #include <stdio.h>
 
@@ -315,6 +322,43 @@
     *olen = 4;
 }
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+static void ssl_write_session_ticket_ext( ssl_context *ssl,
+                                          unsigned char *buf, size_t *olen )
+{
+    unsigned char *p = buf;
+    size_t tlen = ssl->session_negotiate->ticket_len;
+
+    if( ssl->session_tickets == SSL_SESSION_TICKETS_DISABLED )
+    {
+        *olen = 0;
+        return;
+    }
+
+    SSL_DEBUG_MSG( 3, ( "client hello, adding session ticket extension" ) );
+
+    *p++ = (unsigned char)( ( TLS_EXT_SESSION_TICKET >> 8 ) & 0xFF );
+    *p++ = (unsigned char)( ( TLS_EXT_SESSION_TICKET      ) & 0xFF );
+
+    *p++ = (unsigned char)( ( tlen >> 8 ) & 0xFF );
+    *p++ = (unsigned char)( ( tlen      ) & 0xFF );
+
+    *olen = 4;
+
+    if( ssl->session_negotiate->ticket == NULL ||
+        ssl->session_negotiate->ticket_len == 0 )
+    {
+        return;
+    }
+
+    SSL_DEBUG_MSG( 3, ( "sending session ticket of length %d", tlen ) );
+
+    memcpy( p, ssl->session_negotiate->ticket, tlen );
+
+    *olen += tlen;
+}
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
 static int ssl_write_client_hello( ssl_context *ssl )
 {
     int ret;
@@ -395,7 +439,27 @@
 
     if( ssl->renegotiation != SSL_INITIAL_HANDSHAKE || n < 16 || n > 32 ||
         ssl->handshake->resume == 0 )
+    {
         n = 0;
+    }
+
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+    /*
+     * RFC 5077 section 3.4: "When presenting a ticket, the client MAY
+     * generate and include a Session ID in the TLS ClientHello."
+     */
+    if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE &&
+        ssl->session_negotiate->ticket != NULL &&
+        ssl->session_negotiate->ticket_len != 0 )
+    {
+        ret = ssl->f_rng( ssl->p_rng, ssl->session_negotiate->id, 32 );
+
+        if( ret != 0 )
+            return( ret );
+
+        ssl->session_negotiate->length = n = 32;
+    }
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
 
     *p++ = (unsigned char) n;
 
@@ -488,6 +552,11 @@
     ssl_write_truncated_hmac_ext( ssl, p + 2 + ext_len, &olen );
     ext_len += olen;
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+    ssl_write_session_ticket_ext( ssl, p + 2 + ext_len, &olen );
+    ext_len += olen;
+#endif
+
     SSL_DEBUG_MSG( 3, ( "client hello, total extension length: %d",
                    ext_len ) );
 
@@ -587,6 +656,25 @@
     return( 0 );
 }
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+static int ssl_parse_session_ticket_ext( ssl_context *ssl,
+                                         const unsigned char *buf,
+                                         size_t len )
+{
+    if( ssl->session_tickets == SSL_SESSION_TICKETS_DISABLED ||
+        len != 0 )
+    {
+        return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO );
+    }
+
+    ((void) buf);
+
+    ssl->handshake->new_session_ticket = 1;
+
+    return( 0 );
+}
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
 static int ssl_parse_server_hello( ssl_context *ssl )
 {
     uint32_t t;
@@ -825,6 +913,19 @@
 
             break;
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+        case TLS_EXT_SESSION_TICKET:
+            SSL_DEBUG_MSG( 3, ( "found session_ticket extension" ) );
+
+            if( ( ret = ssl_parse_session_ticket_ext( ssl,
+                            ext + 4, ext_size ) ) != 0 )
+            {
+                return( ret );
+            }
+
+            break;
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
         default:
             SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)",
                            ext_id ) );
@@ -1834,6 +1935,100 @@
           !POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED &&
           !POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED */
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+static int ssl_parse_new_session_ticket( ssl_context *ssl )
+{
+    int ret;
+    uint32_t lifetime;
+    size_t ticket_len;
+    unsigned char *ticket;
+
+    SSL_DEBUG_MSG( 2, ( "=> parse new session ticket" ) );
+
+    if( ( ret = ssl_read_record( ssl ) ) != 0 )
+    {
+        SSL_DEBUG_RET( 1, "ssl_read_record", ret );
+        return( ret );
+    }
+
+    if( ssl->in_msgtype != SSL_MSG_HANDSHAKE )
+    {
+        SSL_DEBUG_MSG( 1, ( "bad new session ticket message" ) );
+        return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE );
+    }
+
+    /*
+     * struct {
+     *     uint32 ticket_lifetime_hint;
+     *     opaque ticket<0..2^16-1>;
+     * } NewSessionTicket;
+     *
+     * 0  .  0   handshake message type
+     * 1  .  3   handshake message length
+     * 4  .  7   ticket_lifetime_hint
+     * 8  .  9   ticket_len (n)
+     * 10 .  9+n ticket content
+     */
+    if( ssl->in_msg[0] != SSL_HS_NEW_SESSION_TICKET ||
+        ssl->in_hslen < 10 )
+    {
+        SSL_DEBUG_MSG( 1, ( "bad new session ticket message" ) );
+        return( POLARSSL_ERR_SSL_BAD_HS_NEW_SESSION_TICKET );
+    }
+
+    lifetime = ( ssl->in_msg[4] << 24 ) | ( ssl->in_msg[5] << 16 ) |
+               ( ssl->in_msg[6] <<  8 ) | ( ssl->in_msg[7]       );
+
+    ticket_len = ( ssl->in_msg[8] << 8 ) | ( ssl->in_msg[9] );
+
+    if( ticket_len + 10 != ssl->in_hslen )
+    {
+        SSL_DEBUG_MSG( 1, ( "bad new session ticket message" ) );
+        return( POLARSSL_ERR_SSL_BAD_HS_NEW_SESSION_TICKET );
+    }
+
+    SSL_DEBUG_MSG( 3, ( "ticket length: %d", ticket_len ) );
+
+    /* We're not waiting for a NewSessionTicket message any more */
+    ssl->handshake->new_session_ticket = 0;
+
+    /*
+     * Zero-length ticket means the server changed his mind and doesn't want
+     * to send a ticket after all, so just forget it
+     */
+    if( ticket_len == 0)
+        return( 0 );
+
+    polarssl_free( ssl->session_negotiate->ticket );
+    ssl->session_negotiate->ticket = NULL;
+    ssl->session_negotiate->ticket_len = 0;
+
+    if( ( ticket = polarssl_malloc( ticket_len ) ) == NULL )
+    {
+        SSL_DEBUG_MSG( 1, ( "ticket malloc failed" ) );
+        return( POLARSSL_ERR_SSL_MALLOC_FAILED );
+    }
+
+    memcpy( ticket, ssl->in_msg + 10, ticket_len );
+
+    ssl->session_negotiate->ticket = ticket;
+    ssl->session_negotiate->ticket_len = ticket_len;
+    ssl->session_negotiate->ticket_lifetime = lifetime;
+
+    /*
+     * RFC 5077 section 3.4:
+     * "If the client receives a session ticket from the server, then it
+     * discards any Session ID that was sent in the ServerHello."
+     */
+    SSL_DEBUG_MSG( 3, ( "ticket in use, discarding session id" ) );
+    ssl->session_negotiate->length = 0;
+
+    SSL_DEBUG_MSG( 2, ( "<= parse new session ticket" ) );
+
+    return( 0 );
+}
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
 /*
  * SSL handshake -- client side -- single step
  */
@@ -1917,11 +2112,17 @@
            break;
 
        /*
-        *  <==   ChangeCipherSpec
+        *  <==   ( NewSessionTicket )
+        *        ChangeCipherSpec
         *        Finished
         */
        case SSL_SERVER_CHANGE_CIPHER_SPEC:
-           ret = ssl_parse_change_cipher_spec( ssl );
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+           if( ssl->handshake->new_session_ticket != 0 )
+               ret = ssl_parse_new_session_ticket( ssl );
+           else
+#endif
+               ret = ssl_parse_change_cipher_spec( ssl );
            break;
 
        case SSL_SERVER_FINISHED:
diff --git a/library/ssl_srv.c b/library/ssl_srv.c
index 6cbc5f4..0dbcdb5 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -33,6 +33,13 @@
 #include "polarssl/ecp.h"
 #endif
 
+#if defined(POLARSSL_MEMORY_C)
+#include "polarssl/memory.h"
+#else
+#define polarssl_malloc     malloc
+#define polarssl_free       free
+#endif
+
 #include <stdlib.h>
 #include <stdio.h>
 
@@ -40,6 +47,262 @@
 #include <time.h>
 #endif
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+/*
+ * Serialize a session in the following format:
+ *  0   .   n-1     session structure, n = sizeof(ssl_session)
+ *  n   .   n+2     peer_cert length = m (0 if no certificate)
+ *  n+3 .   n+2+m   peer cert ASN.1
+ *
+ *  Assumes ticket is NULL (always true on server side).
+ */
+static void ssl_save_session( const ssl_session *session,
+                              unsigned char *buf, size_t *olen )
+{
+    unsigned char *p = buf;
+#if defined(POLARSSL_X509_PARSE_C)
+    size_t cert_len;
+#endif /* POLARSSL_X509_PARSE_C */
+
+    memcpy( p, session, sizeof( ssl_session ) );
+    p += sizeof( ssl_session );
+
+#if defined(POLARSSL_X509_PARSE_C)
+    ((ssl_session *) buf)->peer_cert = NULL;
+
+    if( session->peer_cert == NULL )
+        cert_len = 0;
+    else
+        cert_len = session->peer_cert->raw.len;
+
+    *p++ = (unsigned char)( cert_len >> 16 & 0xFF );
+    *p++ = (unsigned char)( cert_len >>  8 & 0xFF );
+    *p++ = (unsigned char)( cert_len       & 0xFF );
+
+    if( session->peer_cert != NULL )
+        memcpy( p, session->peer_cert->raw.p, cert_len );
+
+    p += cert_len;
+#endif /* POLARSSL_X509_PARSE_C */
+
+    *olen = p - buf;
+}
+
+/*
+ * Unserialise session, see ssl_save_session()
+ */
+static int ssl_load_session( ssl_session *session,
+                             const unsigned char *buf, size_t len )
+{
+    int ret;
+    const unsigned char *p = buf;
+    const unsigned char * const end = buf + len;
+#if defined(POLARSSL_X509_PARSE_C)
+    size_t cert_len;
+#endif /* POLARSSL_X509_PARSE_C */
+
+    if( p + sizeof( ssl_session ) > end )
+        return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+
+    memcpy( session, p, sizeof( ssl_session ) );
+    p += sizeof( ssl_session );
+
+#if defined(POLARSSL_X509_PARSE_C)
+    if( p + 3 > end )
+        return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+
+    cert_len = ( p[0] << 16 ) | ( p[1] << 8 ) | p[2];
+    p += 3;
+
+    if( cert_len == 0 )
+    {
+        session->peer_cert = NULL;
+    }
+    else
+    {
+        if( p + cert_len > end )
+            return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+
+        session->peer_cert = polarssl_malloc( cert_len );
+
+        if( session->peer_cert == NULL )
+            return( POLARSSL_ERR_SSL_MALLOC_FAILED );
+
+        memset( session->peer_cert, 0, sizeof( x509_cert ) );
+
+        if( ( ret = x509parse_crt( session->peer_cert, p, cert_len ) ) != 0 )
+        {
+            polarssl_free( session->peer_cert );
+            free( session->peer_cert );
+            session->peer_cert = NULL;
+            return( ret );
+        }
+
+        p += cert_len;
+    }
+#endif /* POLARSSL_X509_PARSE_C */
+
+    if( p != end )
+        return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+
+    return( 0 );
+}
+
+/*
+ * Create session ticket, secured as recommended in RFC 5077 section 4:
+ *
+ *    struct {
+ *        opaque key_name[16];
+ *        opaque iv[16];
+ *        opaque encrypted_state<0..2^16-1>;
+ *        opaque mac[32];
+ *    } ticket;
+ *
+ * (the internal state structure differs, however).
+ */
+static int ssl_write_ticket( ssl_context *ssl, size_t *tlen )
+{
+    int ret;
+    unsigned char * const start = ssl->out_msg + 10;
+    unsigned char *p = start;
+    unsigned char *state;
+    unsigned char iv[16];
+    size_t clear_len, enc_len, pad_len, i;
+
+    if( ssl->ticket_keys == NULL )
+        return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+
+    /* Write key name */
+    memcpy( p, ssl->ticket_keys->key_name, 16 );
+    p += 16;
+
+    /* Generate and write IV (with a copy for aes_crypt) */
+    if( ( ret = ssl->f_rng( ssl->p_rng, p, 16 ) ) != 0 )
+        return( ret );
+    memcpy( iv, p, 16 );
+    p += 16;
+
+    /* Dump session state */
+    state = p + 2;
+    ssl_save_session( ssl->session_negotiate, state, &clear_len );
+    SSL_DEBUG_BUF( 3, "session ticket cleartext", state, clear_len );
+
+    /* Apply PKCS padding */
+    pad_len = 16 - clear_len % 16;
+    enc_len = clear_len + pad_len;
+    for( i = clear_len; i < enc_len; i++ )
+        state[i] = (unsigned char) pad_len;
+
+    /* Encrypt */
+    if( ( ret = aes_crypt_cbc( &ssl->ticket_keys->enc, AES_ENCRYPT,
+                               enc_len, iv, state, state ) ) != 0 )
+    {
+        return( ret );
+    }
+
+    /* Write length */
+    *p++ = (unsigned char)( ( enc_len >> 8 ) & 0xFF );
+    *p++ = (unsigned char)( ( enc_len      ) & 0xFF );
+    p = state + enc_len;
+
+    /* Compute and write MAC( key_name + iv + enc_state_len + enc_state ) */
+    sha256_hmac( ssl->ticket_keys->mac_key, 16, start, p - start, p, 0 );
+    p += 32;
+
+    *tlen = p - start;
+
+    SSL_DEBUG_BUF( 3, "session ticket structure", start, *tlen );
+
+    return( 0 );
+}
+
+/*
+ * Load session ticket (see ssl_write_ticket for structure)
+ */
+static int ssl_parse_ticket( ssl_context *ssl,
+                             unsigned char *buf,
+                             size_t len )
+{
+    int ret;
+    ssl_session session;
+    unsigned char *key_name = buf;
+    unsigned char *iv = buf + 16;
+    unsigned char *enc_len_p = iv + 16;
+    unsigned char *ticket = enc_len_p + 2;
+    unsigned char *mac;
+    unsigned char computed_mac[16];
+    size_t enc_len, clear_len, i;
+    unsigned char pad_len;
+
+    SSL_DEBUG_BUF( 3, "session ticket structure", buf, len );
+
+    if( len < 34 || ssl->ticket_keys == NULL )
+        return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+
+    enc_len = ( enc_len_p[0] << 8 ) | enc_len_p[1];
+    mac = ticket + enc_len;
+
+    if( len != enc_len + 66 )
+        return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+
+    /* Check name */
+    if( memcmp( key_name, ssl->ticket_keys->key_name, 16 ) != 0 )
+        return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+
+    /* Check mac */
+    sha256_hmac( ssl->ticket_keys->mac_key, 16, buf, len - 32,
+                 computed_mac, 0 );
+    ret = 0;
+    for( i = 0; i < 32; i++ )
+        if( mac[i] != computed_mac[i] )
+            ret = POLARSSL_ERR_SSL_INVALID_MAC;
+    if( ret != 0 )
+        return( ret );
+
+    /* Decrypt */
+    if( ( ret = aes_crypt_cbc( &ssl->ticket_keys->dec, AES_DECRYPT,
+                               enc_len, iv, ticket, ticket ) ) != 0 )
+    {
+        return( ret );
+    }
+
+    /* Check PKCS padding */
+    pad_len = ticket[enc_len - 1];
+
+    ret = 0;
+    for( i = 2; i < pad_len; i++ )
+        if( ticket[enc_len - i] != pad_len )
+            ret = POLARSSL_ERR_SSL_BAD_INPUT_DATA;
+    if( ret != 0 )
+        return( ret );
+
+    clear_len = enc_len - pad_len;
+
+    SSL_DEBUG_BUF( 3, "session ticket cleartext", ticket, clear_len );
+
+    /* Actually load session */
+    if( ( ret = ssl_load_session( &session, ticket, clear_len ) ) != 0 )
+    {
+        SSL_DEBUG_MSG( 1, ( "failed to parse ticket content" ) );
+        memset( &session, 0, sizeof( ssl_session ) );
+        return( ret );
+    }
+
+    /*
+     * Keep the session ID sent by the client, since we MUST send it back to
+     * inform him we're accepting the ticket  (RFC 5077 section 3.4)
+     */
+    session.length = ssl->session_negotiate->length;
+    memcpy( &session.id, ssl->session_negotiate->id, session.length );
+
+    ssl_session_free( ssl->session_negotiate );
+    memcpy( ssl->session_negotiate, &session, sizeof( ssl_session ) );
+    memset( &session, 0, sizeof( ssl_session ) );
+
+    return( 0 );
+}
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
 static int ssl_parse_servername_ext( ssl_context *ssl,
                                      const unsigned char *buf,
                                      size_t len )
@@ -323,6 +586,50 @@
     return( 0 );
 }
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+static int ssl_parse_session_ticket_ext( ssl_context *ssl,
+                                         unsigned char *buf,
+                                         size_t len )
+{
+    int ret;
+
+    if( ssl->session_tickets == SSL_SESSION_TICKETS_DISABLED )
+        return( 0 );
+
+    /* Remember the client asked us to send a new ticket */
+    ssl->handshake->new_session_ticket = 1;
+
+    SSL_DEBUG_MSG( 3, ( "ticket length: %d", len ) );
+
+    if( len == 0 )
+        return( 0 );
+
+    if( ssl->renegotiation != SSL_INITIAL_HANDSHAKE )
+    {
+        SSL_DEBUG_MSG( 3, ( "ticket rejected: renegotiating" ) );
+        return( 0 );
+    }
+
+    /*
+     * Failures are ok: just ignore the ticket and proceed.
+     */
+    if( ( ret = ssl_parse_ticket( ssl, buf, len ) ) != 0 )
+    {
+        SSL_DEBUG_RET( 1, "ssl_parse_ticket", ret );
+        return( 0 );
+    }
+
+    SSL_DEBUG_MSG( 3, ( "session successfully restored from ticket" ) );
+
+    ssl->handshake->resume = 1;
+
+    /* Don't send a new ticket after all, this one is OK */
+    ssl->handshake->new_session_ticket = 0;
+
+    return( 0 );
+}
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
 #if defined(POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO)
 static int ssl_parse_client_hello_v2( ssl_context *ssl )
 {
@@ -610,7 +917,7 @@
 
     n = ( buf[3] << 8 ) | buf[4];
 
-    if( n < 45 || n > 512 )
+    if( n < 45 || n > 2048 )
     {
         SSL_DEBUG_MSG( 1, ( "bad client hello message" ) );
         return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO );
@@ -873,6 +1180,16 @@
                 return( ret );
             break;
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+        case TLS_EXT_SESSION_TICKET:
+            SSL_DEBUG_MSG( 3, ( "found session ticket extension" ) );
+
+            ret = ssl_parse_session_ticket_ext( ssl, ext + 4, ext_size );
+            if( ret != 0 )
+                return( ret );
+            break;
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
         default:
             SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)",
                            ext_id ) );
@@ -1005,6 +1322,31 @@
     *olen = 4;
 }
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+static void ssl_write_session_ticket_ext( ssl_context *ssl,
+                                          unsigned char *buf,
+                                          size_t *olen )
+{
+    unsigned char *p = buf;
+
+    if( ssl->handshake->new_session_ticket == 0 )
+    {
+        *olen = 0;
+        return;
+    }
+
+    SSL_DEBUG_MSG( 3, ( "server hello, adding session ticket extension" ) );
+
+    *p++ = (unsigned char)( ( TLS_EXT_SESSION_TICKET >> 8 ) & 0xFF );
+    *p++ = (unsigned char)( ( TLS_EXT_SESSION_TICKET      ) & 0xFF );
+
+    *p++ = 0x00;
+    *p++ = 0x00;
+
+    *olen = 4;
+}
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
 static void ssl_write_renegotiation_ext( ssl_context *ssl,
                                          unsigned char *buf,
                                          size_t *olen )
@@ -1111,36 +1453,52 @@
     SSL_DEBUG_BUF( 3, "server hello, random bytes", buf + 6, 32 );
 
     /*
-     *    38  .  38     session id length
-     *    39  . 38+n    session id
-     *   39+n . 40+n    chosen ciphersuite
-     *   41+n . 41+n    chosen compression alg.
-     *   42+n . 43+n    extensions length
-     *   44+n . 43+n+m  extensions
+     * Resume is 0  by default, see ssl_handshake_init().
+     * It may be already set to 1 by ssl_parse_session_ticket_ext().
+     * If not, try looking up session ID in our cache.
      */
-    ssl->session_negotiate->length = n = 32;
-    *p++ = (unsigned char) ssl->session_negotiate->length;
+    if( ssl->handshake->resume == 0 &&
+        ssl->renegotiation == SSL_INITIAL_HANDSHAKE &&
+        ssl->session_negotiate->length != 0 &&
+        ssl->f_get_cache != NULL &&
+        ssl->f_get_cache( ssl->p_get_cache, ssl->session_negotiate ) == 0 )
+    {
+        ssl->handshake->resume = 1;
+    }
 
-    if( ssl->renegotiation != SSL_INITIAL_HANDSHAKE ||
-        ssl->f_get_cache == NULL ||
-        ssl->f_get_cache( ssl->p_get_cache, ssl->session_negotiate ) != 0 )
+    if( ssl->handshake->resume == 0 )
     {
         /*
-         * Not found, create a new session id
+         * New session, create a new session id,
+         * unless we're about to issue a session ticket
          */
-        ssl->handshake->resume = 0;
         ssl->state++;
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+        if( ssl->handshake->new_session_ticket == 0 )
+        {
+            ssl->session_negotiate->length = n = 32;
+            if( ( ret = ssl->f_rng( ssl->p_rng, ssl->session_negotiate->id,
+                                    n ) ) != 0 )
+                return( ret );
+        }
+        else
+        {
+            ssl->session_negotiate->length = 0;
+            memset( ssl->session_negotiate->id, 0, 32 );
+        }
+#else
+        ssl->session_negotiate->length = n = 32;
         if( ( ret = ssl->f_rng( ssl->p_rng, ssl->session_negotiate->id,
                                 n ) ) != 0 )
             return( ret );
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
     }
     else
     {
         /*
-         * Found a matching session, resuming it
+         * Resuming a session
          */
-        ssl->handshake->resume = 1;
         ssl->state = SSL_SERVER_CHANGE_CIPHER_SPEC;
 
         if( ( ret = ssl_derive_keys( ssl ) ) != 0 )
@@ -1150,6 +1508,15 @@
         }
     }
 
+    /*
+     *    38  .  38     session id length
+     *    39  . 38+n    session id
+     *   39+n . 40+n    chosen ciphersuite
+     *   41+n . 41+n    chosen compression alg.
+     *   42+n . 43+n    extensions length
+     *   44+n . 43+n+m  extensions
+     */
+    *p++ = (unsigned char) ssl->session_negotiate->length;
     memcpy( p, ssl->session_negotiate->id, ssl->session_negotiate->length );
     p += ssl->session_negotiate->length;
 
@@ -1179,6 +1546,11 @@
     ssl_write_truncated_hmac_ext( ssl, p + 2 + ext_len, &olen );
     ext_len += olen;
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+    ssl_write_session_ticket_ext( ssl, p + 2 + ext_len, &olen );
+    ext_len += olen;
+#endif
+
     SSL_DEBUG_MSG( 3, ( "server hello, total extension length: %d", ext_len ) );
 
     *p++ = (unsigned char)( ( ext_len >> 8 ) & 0xFF );
@@ -2114,6 +2486,58 @@
           !POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED &&
           !POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED */
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+static int ssl_write_new_session_ticket( ssl_context *ssl )
+{
+    int ret;
+    size_t tlen;
+
+    SSL_DEBUG_MSG( 2, ( "=> write new session ticket" ) );
+
+    ssl->out_msgtype = SSL_MSG_HANDSHAKE;
+    ssl->out_msg[0]  = SSL_HS_NEW_SESSION_TICKET;
+
+    /*
+     * struct {
+     *     uint32 ticket_lifetime_hint;
+     *     opaque ticket<0..2^16-1>;
+     * } NewSessionTicket;
+     *
+     * 4  .  7   ticket_lifetime_hint (0 = unspecified)
+     * 8  .  9   ticket_len (n)
+     * 10 .  9+n ticket content
+     */
+    ssl->out_msg[4] = 0x00;
+    ssl->out_msg[5] = 0x00;
+    ssl->out_msg[6] = 0x00;
+    ssl->out_msg[7] = 0x00;
+
+    if( ( ret = ssl_write_ticket( ssl, &tlen ) ) != 0 )
+    {
+        SSL_DEBUG_RET( 1, "ssl_write_ticket", ret );
+        tlen = 0;
+    }
+
+    ssl->out_msg[8] = (unsigned char)( ( tlen >> 8 ) & 0xFF );
+    ssl->out_msg[9] = (unsigned char)( ( tlen      ) & 0xFF );
+
+    ssl->out_msglen = 10 + tlen;
+
+    if( ( ret = ssl_write_record( ssl ) ) != 0 )
+    {
+        SSL_DEBUG_RET( 1, "ssl_write_record", ret );
+        return( ret );
+    }
+
+    /* No need to remember writing a NewSessionTicket any more */
+    ssl->handshake->new_session_ticket = 0;
+
+    SSL_DEBUG_MSG( 2, ( "<= write new session ticket" ) );
+
+    return( 0 );
+}
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
 /*
  * SSL handshake -- server side -- single step
  */
@@ -2197,11 +2621,17 @@
             break;
 
         /*
-         *  ==>   ChangeCipherSpec
+         *  ==> ( NewSessionTicket )
+         *        ChangeCipherSpec
          *        Finished
          */
         case SSL_SERVER_CHANGE_CIPHER_SPEC:
-            ret = ssl_write_change_cipher_spec( ssl );
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+            if( ssl->handshake->new_session_ticket != 0 )
+                ret = ssl_write_new_session_ticket( ssl );
+            else
+#endif
+                ret = ssl_write_change_cipher_spec( ssl );
             break;
 
         case SSL_SERVER_FINISHED:
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index b9fca44..2585d6e 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -76,6 +76,44 @@
     4096,                   /* SSL_MAX_FRAG_LEN_4096 */
 };
 
+static int ssl_session_copy( ssl_session *dst, const ssl_session *src )
+{
+    int ret;
+
+    ssl_session_free( dst );
+    memcpy( dst, src, sizeof( ssl_session ) );
+
+#if defined(POLARSSL_X509_PARSE_C)
+    if( src->peer_cert != NULL )
+    {
+        if( ( dst->peer_cert = polarssl_malloc( sizeof(x509_cert) ) ) == NULL )
+            return( POLARSSL_ERR_SSL_MALLOC_FAILED );
+
+        memset( dst->peer_cert, 0, sizeof(x509_cert) );
+
+        if( ( ret = x509parse_crt( dst->peer_cert, src->peer_cert->raw.p,
+                                   src->peer_cert->raw.len ) != 0 ) )
+        {
+            polarssl_free( dst->peer_cert );
+            dst->peer_cert = NULL;
+            return( ret );
+        }
+    }
+#endif /* POLARSSL_X509_PARSE_C */
+
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+    if( src->ticket != NULL )
+    {
+        if( ( dst->ticket = polarssl_malloc( src->ticket_len ) ) == NULL )
+            return( POLARSSL_ERR_SSL_MALLOC_FAILED );
+
+        memcpy( dst->ticket, src->ticket, src->ticket_len );
+    }
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
+    return( 0 );
+}
+
 #if defined(POLARSSL_SSL_HW_RECORD_ACCEL)
 int (*ssl_hw_record_init)(ssl_context *ssl,
                        const unsigned char *key_enc, const unsigned char *key_dec,
@@ -2539,6 +2577,8 @@
 
 void ssl_handshake_wrapup( ssl_context *ssl )
 {
+    int resume = ssl->handshake->resume;
+
     SSL_DEBUG_MSG( 3, ( "=> handshake wrapup" ) );
 
     /*
@@ -2570,9 +2610,13 @@
     /*
      * Add cache entry
      */
-    if( ssl->f_set_cache != NULL )
+    if( ssl->f_set_cache != NULL &&
+        ssl->session->length != 0 &&
+        resume == 0 )
+    {
         if( ssl->f_set_cache( ssl->p_set_cache, ssl->session ) != 0 )
             SSL_DEBUG_MSG( 1, ( "cache did not store session" ) );
+    }
 
     ssl->state++;
 
@@ -2930,12 +2974,50 @@
     return( 0 );
 }
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+/*
+ * Allocate and initialize ticket keys
+ */
+static int ssl_ticket_keys_init( ssl_context *ssl )
+{
+    int ret;
+    ssl_ticket_keys *tkeys;
+    unsigned char buf[16];
+
+    if( ssl->ticket_keys != NULL )
+        return( 0 );
+
+    if( ( tkeys = polarssl_malloc( sizeof( ssl_ticket_keys ) ) ) == NULL )
+        return( POLARSSL_ERR_SSL_MALLOC_FAILED );
+
+    if( ( ret = ssl->f_rng( ssl->p_rng, tkeys->key_name, 16 ) ) != 0 )
+        return( ret );
+
+    if( ( ret = ssl->f_rng( ssl->p_rng, buf, 16 ) ) != 0 ||
+        ( ret = aes_setkey_enc( &tkeys->enc, buf, 128 ) ) != 0 ||
+        ( ret = aes_setkey_dec( &tkeys->dec, buf, 128 ) ) != 0 )
+    {
+            return( ret );
+    }
+
+    if( ( ret = ssl->f_rng( ssl->p_rng, tkeys->mac_key, 16 ) ) != 0 )
+        return( ret );
+
+    ssl->ticket_keys = tkeys;
+
+    return( 0 );
+}
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
 /*
  * SSL set accessors
  */
 void ssl_set_endpoint( ssl_context *ssl, int endpoint )
 {
     ssl->endpoint   = endpoint;
+
+    if( endpoint == SSL_IS_CLIENT )
+        ssl->session_tickets = SSL_SESSION_TICKETS_ENABLED;
 }
 
 void ssl_set_authmode( ssl_context *ssl, int authmode )
@@ -2989,10 +3071,24 @@
     ssl->p_set_cache = p_set_cache;
 }
 
-void ssl_set_session( ssl_context *ssl, const ssl_session *session )
+int ssl_set_session( ssl_context *ssl, const ssl_session *session )
 {
-    memcpy( ssl->session_negotiate, session, sizeof(ssl_session) );
+    int ret;
+
+    if( ssl == NULL ||
+        session == NULL ||
+        ssl->session_negotiate == NULL ||
+        ssl->endpoint != SSL_IS_CLIENT )
+    {
+        return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+    }
+
+    if( ( ret = ssl_session_copy( ssl->session_negotiate, session ) ) != 0 )
+        return( ret );
+
     ssl->handshake->resume = 1;
+
+    return( 0 );
 }
 
 void ssl_set_ciphersuites( ssl_context *ssl, const int *ciphersuites )
@@ -3169,6 +3265,21 @@
     ssl->allow_legacy_renegotiation = allow_legacy;
 }
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+int ssl_set_session_tickets( ssl_context *ssl, int use_tickets )
+{
+    ssl->session_tickets = use_tickets;
+
+    if( ssl->endpoint == SSL_IS_CLIENT )
+        return( 0 );
+
+    if( ssl->f_rng == NULL )
+        return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+
+    return( ssl_ticket_keys_init( ssl ) );
+}
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
 /*
  * SSL get accessors
  */
@@ -3222,6 +3333,19 @@
 }
 #endif /* POLARSSL_X509_PARSE_C */
 
+int ssl_get_session( const ssl_context *ssl, ssl_session *dst )
+{
+    if( ssl == NULL ||
+        dst == NULL ||
+        ssl->session == NULL ||
+        ssl->endpoint != SSL_IS_CLIENT )
+    {
+        return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+    }
+
+    return( ssl_session_copy( dst, ssl->session ) );
+}
+
 /*
  * Perform a single step of the SSL handshake
  */
@@ -3540,6 +3664,10 @@
     }
 #endif
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+    polarssl_free( session->ticket );
+#endif
+
     memset( session, 0, sizeof( ssl_session ) );
 }
 
@@ -3590,6 +3718,10 @@
         polarssl_free( ssl->session );
     }
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+    polarssl_free( ssl->ticket_keys );
+#endif
+
     if ( ssl->hostname != NULL)
     {
         memset( ssl->hostname, 0, ssl->hostname_len );
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index 05dfdc7..291795f 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -59,6 +59,8 @@
 #define DFL_AUTH_MODE           SSL_VERIFY_OPTIONAL
 #define DFL_MFL_CODE            SSL_MAX_FRAG_LEN_NONE
 #define DFL_TRUNC_HMAC          0
+#define DFL_RECONNECT           0
+#define DFL_TICKETS             SSL_SESSION_TICKETS_ENABLED
 
 #define LONG_HEADER "User-agent: blah-blah-blah-blah-blah-blah-blah-blah-"   \
     "-01--blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-" \
@@ -96,6 +98,8 @@
     int auth_mode;              /* verify mode for connection               */
     unsigned char mfl_code;     /* code for maximum fragment length         */
     int trunc_hmac;             /* negotiate truncated hmac or not          */
+    int reconnect;              /* attempt to resume session                */
+    int tickets;                /* enable / disable session tickets         */
 } opt;
 
 static void my_debug( void *ctx, int level, const char *str )
@@ -174,6 +178,13 @@
 #define USAGE_PSK ""
 #endif /* POLARSSL_KEY_EXCHANGE_PSK_ENABLED */
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+#define USAGE_TICKETS                                       \
+    "    tickets=%%d          default: 1 (enabled)\n"
+#else
+#define USAGE_TICKETS ""
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
 #define USAGE \
     "\n usage: ssl_client2 param=<>...\n"                   \
     "\n acceptable parameters:\n"                           \
@@ -184,6 +195,8 @@
     "    request_page=%%s     default: \".\"\n"             \
     "    renegotiation=%%d    default: 1 (enabled)\n"       \
     "    allow_legacy=%%d     default: 0 (disabled)\n"      \
+    "    reconnect=%%d        default: 0 (disabled)\n"      \
+    USAGE_TICKETS                                           \
     "\n"                                                    \
     "    min_version=%%s      default: \"\" (ssl3)\n"       \
     "    max_version=%%s      default: \"\" (tls1_2)\n"     \
@@ -226,6 +239,7 @@
     entropy_context entropy;
     ctr_drbg_context ctr_drbg;
     ssl_context ssl;
+    ssl_session saved_session;
 #if defined(POLARSSL_X509_PARSE_C)
     x509_cert cacert;
     x509_cert clicert;
@@ -239,6 +253,7 @@
      */
     server_fd = 0;
     memset( &ssl, 0, sizeof( ssl_context ) );
+    memset( &saved_session, 0, sizeof( ssl_session ) );
 #if defined(POLARSSL_X509_PARSE_C)
     memset( &cacert, 0, sizeof( x509_cert ) );
     memset( &clicert, 0, sizeof( x509_cert ) );
@@ -285,6 +300,8 @@
     opt.auth_mode           = DFL_AUTH_MODE;
     opt.mfl_code            = DFL_MFL_CODE;
     opt.trunc_hmac          = DFL_TRUNC_HMAC;
+    opt.reconnect           = DFL_RECONNECT;
+    opt.tickets             = DFL_TICKETS;
 
     for( i = 1; i < argc; i++ )
     {
@@ -345,6 +362,18 @@
             if( opt.allow_legacy < 0 || opt.allow_legacy > 1 )
                 goto usage;
         }
+        else if( strcmp( p, "reconnect" ) == 0 )
+        {
+            opt.reconnect = atoi( q );
+            if( opt.reconnect < 0 || opt.reconnect > 2 )
+                goto usage;
+        }
+        else if( strcmp( p, "tickets" ) == 0 )
+        {
+            opt.tickets = atoi( q );
+            if( opt.tickets < 0 || opt.tickets > 2 )
+                goto usage;
+        }
         else if( strcmp( p, "min_version" ) == 0 )
         {
             if( strcmp( q, "ssl3" ) == 0 )
@@ -652,6 +681,10 @@
     ssl_set_bio( &ssl, net_recv, &server_fd,
                        net_send, &server_fd );
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+    ssl_set_session_tickets( &ssl, opt.tickets );
+#endif
+
     if( opt.force_ciphersuite[0] != DFL_FORCE_CIPHER )
         ssl_set_ciphersuites( &ssl, opt.force_ciphersuite );
 
@@ -693,6 +726,20 @@
     printf( " ok\n    [ Ciphersuite is %s ]\n",
             ssl_get_ciphersuite( &ssl ) );
 
+    if( opt.reconnect != 0 )
+    {
+        printf("  . Saving session for reuse..." );
+        fflush( stdout );
+
+        if( ( ret = ssl_get_session( &ssl, &saved_session ) ) != 0 )
+        {
+            printf( " failed\n  ! ssl_get_session returned -0x%x\n\n", -ret );
+            goto exit;
+        }
+
+        printf( " ok\n" );
+    }
+
 #if defined(POLARSSL_X509_PARSE_C)
     /*
      * 5. Verify the server certificate
@@ -732,6 +779,7 @@
     /*
      * 6. Write the GET request
      */
+send_request:
     printf( "  > Write to server:" );
     fflush( stdout );
 
@@ -789,6 +837,43 @@
 
     ssl_close_notify( &ssl );
 
+    if( opt.reconnect != 0 )
+    {
+        --opt.reconnect;
+
+        printf( "  . Reconnecting with saved session..." );
+        fflush( stdout );
+
+        if( ( ret = ssl_session_reset( &ssl ) ) != 0 )
+        {
+            printf( " failed\n  ! ssl_session_reset returned -0x%x\n\n", -ret );
+            goto exit;
+        }
+
+        ssl_set_session( &ssl, &saved_session );
+
+        if( ( ret = net_connect( &server_fd, opt.server_name,
+                        opt.server_port ) ) != 0 )
+        {
+            printf( " failed\n  ! net_connect returned -0x%x\n\n", -ret );
+            goto exit;
+        }
+
+        while( ( ret = ssl_handshake( &ssl ) ) != 0 )
+        {
+            if( ret != POLARSSL_ERR_NET_WANT_READ &&
+                ret != POLARSSL_ERR_NET_WANT_WRITE )
+            {
+                printf( " failed\n  ! ssl_handshake returned -0x%x\n\n", -ret );
+                goto exit;
+            }
+        }
+
+        printf( " ok\n" );
+
+        goto send_request;
+    }
+
 exit:
 
 #ifdef POLARSSL_ERROR_C
@@ -807,6 +892,7 @@
     x509_free( &cacert );
     rsa_free( &rsa );
 #endif
+    ssl_session_free( &saved_session );
     ssl_free( &ssl );
 
     memset( &ssl, 0, sizeof( ssl ) );
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index 57ff85c..aca0db5 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -69,6 +69,7 @@
 #define DFL_MAX_VERSION         -1
 #define DFL_AUTH_MODE           SSL_VERIFY_OPTIONAL
 #define DFL_MFL_CODE            SSL_MAX_FRAG_LEN_NONE
+#define DFL_TICKETS             SSL_SESSION_TICKETS_ENABLED
 
 #define LONG_RESPONSE "<p>01-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n" \
     "02-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n"  \
@@ -105,6 +106,7 @@
     int max_version;            /* maximum protocol version accepted        */
     int auth_mode;              /* verify mode for connection               */
     unsigned char mfl_code;     /* code for maximum fragment length         */
+    int tickets;                /* enable / disable session tickets         */
 } opt;
 
 static void my_debug( void *ctx, int level, const char *str )
@@ -144,6 +146,13 @@
 #define USAGE_PSK ""
 #endif /* POLARSSL_KEY_EXCHANGE_PSK_ENABLED */
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+#define USAGE_TICKETS                                       \
+    "    tickets=%%d          default: 1 (enabled)\n"
+#else
+#define USAGE_TICKETS ""
+#endif /* POLARSSL_SSL_SESSION_TICKETS */
+
 #define USAGE \
     "\n usage: ssl_server2 param=<>...\n"                   \
     "\n acceptable parameters:\n"                           \
@@ -152,6 +161,7 @@
     USAGE_IO                                                \
     "    request_page=%%s     default: \".\"\n"             \
     "    renegotiation=%%d    default: 1 (enabled)\n"       \
+    USAGE_TICKETS                                           \
     "    allow_legacy=%%d     default: 0 (disabled)\n"      \
     "    min_version=%%s      default: \"ssl3\"\n"          \
     "    max_version=%%s      default: \"tls1_2\"\n"        \
@@ -265,6 +275,7 @@
     opt.max_version         = DFL_MAX_VERSION;
     opt.auth_mode           = DFL_AUTH_MODE;
     opt.mfl_code            = DFL_MFL_CODE;
+    opt.tickets             = DFL_TICKETS;
 
     for( i = 1; i < argc; i++ )
     {
@@ -396,6 +407,12 @@
             else
                 goto usage;
         }
+        else if( strcmp( p, "tickets" ) == 0 )
+        {
+            opt.tickets = atoi( q );
+            if( opt.tickets < 0 || opt.tickets > 1 )
+                goto usage;
+        }
         else
             goto usage;
     }
@@ -611,6 +628,10 @@
                                  ssl_cache_set, &cache );
 #endif
 
+#if defined(POLARSSL_SSL_SESSION_TICKETS)
+    ssl_set_session_tickets( &ssl, opt.tickets );
+#endif
+
     if( opt.force_ciphersuite[0] != DFL_FORCE_CIPHER )
         ssl_set_ciphersuites( &ssl, opt.force_ciphersuite );