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 );