diff --git a/ChangeLog b/ChangeLog index 5bda25cbd..99a4b89f4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,14 @@ mbed TLS ChangeLog (Sorted per branch, date) = mbed TLS 2.x.x branch released xxxx-xx-xx +Security + * Make mbedtls_ecdh_get_params return an error if the second key + belongs to a different group from the first. Before, if an application + passed keys that belonged to different group, the first key's data was + interpreted according to the second group, which could lead to either + an error or a meaningless output from mbedtls_ecdh_get_params. In the + latter case, this could expose at most 5 bits of the private key. + Features * Add support for draft-05 of the Connection ID extension, as specified in https://tools.ietf.org/html/draft-ietf-tls-dtls-connection-id-05. @@ -12,14 +20,10 @@ Features changed its IP or port. The feature is enabled at compile-time by setting MBEDTLS_SSL_DTLS_CONNECTION_ID (disabled by default), and at run-time through the new APIs mbedtls_ssl_conf_cid() and mbedtls_ssl_set_cid(). - -Security - * Make mbedtls_ecdh_get_params return an error if the second key - belongs to a different group from the first. Before, if an application - passed keys that belonged to different group, the first key's data was - interpreted according to the second group, which could lead to either - an error or a meaningless output from mbedtls_ecdh_get_params. In the - latter case, this could expose at most 5 bits of the private key. + * Add new API functions mbedtls_ssl_session_save() and + mbedtls_ssl_session_load() to allow serializing a session, for example to + store it in non-volatile storage, and later using it for TLS session + resumption. Bugfix * Server's RSA certificate in certs.c was SHA-1 signed. In the default diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 71277108e..d435a694b 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -810,6 +810,14 @@ typedef void mbedtls_ssl_async_cancel_t( mbedtls_ssl_context *ssl ); /* * This structure is used for storing current session data. + * + * Note: when changing this definition, we need to check and update: + * - in tests/suites/test_suite_ssl.function: + * ssl_populate_session() and ssl_serialize_session_save_load() + * - in library/ssl_tls.c: + * mbedtls_ssl_session_init() and mbedtls_ssl_session_free() + * mbedtls_ssl_session_save() and ssl_session_load() + * ssl_session_copy() */ struct mbedtls_ssl_session { @@ -2145,6 +2153,87 @@ void mbedtls_ssl_conf_session_cache( mbedtls_ssl_config *conf, int mbedtls_ssl_set_session( mbedtls_ssl_context *ssl, const mbedtls_ssl_session *session ); #endif /* MBEDTLS_SSL_CLI_C */ +/** + * \brief Load serialized session data into a session structure. + * On client, this can be used for loading saved sessions + * before resuming them with mbedstls_ssl_set_session(). + * On server, this can be used for alternative implementations + * of session cache or session tickets. + * + * \warning If a peer certificate chain is associated with the session, + * the serialized state will only contain the peer's + * end-entity certificate and the result of the chain + * verification (unless verification was disabled), but not + * the rest of the chain. + * + * \see mbedtls_ssl_session_save() + * \see mbedtls_ssl_set_session() + * + * \param session The session structure to be populated. It must have been + * initialised with mbedtls_ssl_session_init() but not + * populated yet. + * \param buf The buffer holding the serialized session data. It must be a + * readable buffer of at least \p len bytes. + * \param len The size of the serialized data in bytes. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_SSL_ALLOC_FAILED if memory allocation failed. + * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if input data is invalid. + * \return Another negative value for other kinds of errors (for + * example, unsupported features in the embedded certificate). + */ +int mbedtls_ssl_session_load( mbedtls_ssl_session *session, + const unsigned char *buf, + size_t len ); + +/** + * \brief Save session structure as serialized data in a buffer. + * On client, this can be used for saving session data, + * potentially in non-volatile storage, for resuming later. + * On server, this can be used for alternative implementations + * of session cache or session tickets. + * + * \see mbedtls_ssl_session_load() + * \see mbedtls_ssl_get_session_pointer() + * + * \param session The session structure to be saved. + * \param buf The buffer to write the serialized data to. It must be a + * writeable buffer of at least \p len bytes, or may be \c + * NULL if \p len is \c 0. + * \param buf_len The number of bytes available for writing in \p buf. + * \param olen The size in bytes of the data that has been or would have + * been written. It must point to a valid \c size_t. + * + * \note \p olen is updated to the correct value regardless of + * whether \p buf_len was large enough. This makes it possible + * to determine the necessary size by calling this function + * with \p buf set to \c NULL and \p buf_len to \c 0. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL if \p buf is too small. + */ +int mbedtls_ssl_session_save( const mbedtls_ssl_session *session, + unsigned char *buf, + size_t buf_len, + size_t *olen ); + +/** + * \brief Get a pointer to the current session structure, for example + * to serialize it. + * + * \warning Ownership of the session remains with the SSL context, and + * the returned pointer is only guaranteed to be valid until + * the next API call operating on the same \p ssl context. + * + * \see mbedtls_ssl_session_save() + * + * \param ssl The SSL context. + * + * \return A pointer to the current session if successful. + * \return \c NULL if no session is active. + */ +const mbedtls_ssl_session *mbedtls_ssl_get_session_pointer( const mbedtls_ssl_context *ssl ); + /** * \brief Set the list of allowed ciphersuites and the preference * order. First in the list has the highest preference. diff --git a/library/ssl_ticket.c b/library/ssl_ticket.c index 8492c19a8..5fe693c8f 100644 --- a/library/ssl_ticket.c +++ b/library/ssl_ticket.c @@ -156,115 +156,6 @@ int mbedtls_ssl_ticket_setup( mbedtls_ssl_ticket_context *ctx, return( 0 ); } -/* - * Serialize a session in the following format: - * 0 . n-1 session structure, n = sizeof(mbedtls_ssl_session) - * n . n+2 peer_cert length = m (0 if no certificate) - * n+3 . n+2+m peer cert ASN.1 - */ -static int ssl_save_session( const mbedtls_ssl_session *session, - unsigned char *buf, size_t buf_len, - size_t *olen ) -{ - unsigned char *p = buf; - size_t left = buf_len; -#if defined(MBEDTLS_X509_CRT_PARSE_C) - size_t cert_len; -#endif /* MBEDTLS_X509_CRT_PARSE_C */ - - if( left < sizeof( mbedtls_ssl_session ) ) - return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL ); - - memcpy( p, session, sizeof( mbedtls_ssl_session ) ); - p += sizeof( mbedtls_ssl_session ); - left -= sizeof( mbedtls_ssl_session ); - -#if defined(MBEDTLS_X509_CRT_PARSE_C) - if( session->peer_cert == NULL ) - cert_len = 0; - else - cert_len = session->peer_cert->raw.len; - - if( left < 3 + cert_len ) - return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL ); - - *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 /* MBEDTLS_X509_CRT_PARSE_C */ - - *olen = p - buf; - - return( 0 ); -} - -/* - * Unserialise session, see ssl_save_session() - */ -static int ssl_load_session( mbedtls_ssl_session *session, - const unsigned char *buf, size_t len ) -{ - const unsigned char *p = buf; - const unsigned char * const end = buf + len; -#if defined(MBEDTLS_X509_CRT_PARSE_C) - size_t cert_len; -#endif /* MBEDTLS_X509_CRT_PARSE_C */ - - if( sizeof( mbedtls_ssl_session ) > (size_t)( end - p ) ) - return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); - - memcpy( session, p, sizeof( mbedtls_ssl_session ) ); - p += sizeof( mbedtls_ssl_session ); - -#if defined(MBEDTLS_X509_CRT_PARSE_C) - if( 3 > (size_t)( end - p ) ) - return( MBEDTLS_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 - { - int ret; - - if( cert_len > (size_t)( end - p ) ) - return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); - - session->peer_cert = mbedtls_calloc( 1, sizeof( mbedtls_x509_crt ) ); - - if( session->peer_cert == NULL ) - return( MBEDTLS_ERR_SSL_ALLOC_FAILED ); - - mbedtls_x509_crt_init( session->peer_cert ); - - if( ( ret = mbedtls_x509_crt_parse_der( session->peer_cert, - p, cert_len ) ) != 0 ) - { - mbedtls_x509_crt_free( session->peer_cert ); - mbedtls_free( session->peer_cert ); - session->peer_cert = NULL; - return( ret ); - } - - p += cert_len; - } -#endif /* MBEDTLS_X509_CRT_PARSE_C */ - - if( p != end ) - return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); - - return( 0 ); -} - /* * Create session ticket, with the following structure: * @@ -323,8 +214,9 @@ int mbedtls_ssl_ticket_write( void *p_ticket, goto cleanup; /* Dump session state */ - if( ( ret = ssl_save_session( session, - state, end - state, &clear_len ) ) != 0 || + if( ( ret = mbedtls_ssl_session_save( session, + state, end - state, + &clear_len ) ) != 0 || (unsigned long) clear_len > 65535 ) { goto cleanup; @@ -441,7 +333,7 @@ int mbedtls_ssl_ticket_parse( void *p_ticket, } /* Actually load session */ - if( ( ret = ssl_load_session( session, ticket, clear_len ) ) != 0 ) + if( ( ret = mbedtls_ssl_session_load( session, ticket, clear_len ) ) != 0 ) goto cleanup; #if defined(MBEDTLS_HAVE_TIME) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 81bed8412..4ad49f19f 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -8767,6 +8767,221 @@ int mbedtls_ssl_get_session( const mbedtls_ssl_context *ssl, mbedtls_ssl_session } #endif /* MBEDTLS_SSL_CLI_C */ +const mbedtls_ssl_session *mbedtls_ssl_get_session_pointer( const mbedtls_ssl_context *ssl ) +{ + if( ssl == NULL ) + return( NULL ); + + return( ssl->session ); +} + +/* + * Serialize a session in the following format: + * (in the presentation language of TLS, RFC 8446 section 3) + * + * opaque session_struct[n]; // n = sizeof(mbedtls_ssl_session) + * opaque peer_cert<0..2^24-1>; // 0 means no peer cert + * opaque ticket<0..2^24-1>; // 0 means no ticket + * + * Only the peer's certificate is saved, not the whole chain. + */ +int mbedtls_ssl_session_save( const mbedtls_ssl_session *session, + unsigned char *buf, + size_t buf_len, + size_t *olen ) +{ + unsigned char *p = buf; + size_t used = 0; +#if defined(MBEDTLS_X509_CRT_PARSE_C) + size_t cert_len; +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + + /* + * Shallow copy of the session structure + */ + used += sizeof( mbedtls_ssl_session ); + + if( used <= buf_len ) + { + memcpy( p, session, sizeof( mbedtls_ssl_session ) ); + p += sizeof( mbedtls_ssl_session ); + } + + /* + * Copy of the peer's end-entity certificate + */ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + if( session->peer_cert == NULL ) + cert_len = 0; + else + cert_len = session->peer_cert->raw.len; + + used += 3 + cert_len; + + if( used <= buf_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 /* MBEDTLS_X509_CRT_PARSE_C */ + + /* + * Copy of the session ticket if any + */ +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) + used += 3 + session->ticket_len; + + if( used <= buf_len ) + { + *p++ = (unsigned char)( ( session->ticket_len >> 16 ) & 0xFF ); + *p++ = (unsigned char)( ( session->ticket_len >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( session->ticket_len ) & 0xFF ); + + if( session->ticket != NULL ) + { + memcpy( p, session->ticket, session->ticket_len ); + p += session->ticket_len; + } + } +#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */ + + /* Done */ + *olen = used; + + if( used > buf_len ) + return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL ); + + return( 0 ); +} + +/* + * Unserialize session, see mbedtls_ssl_session_save(). + * + * This internal version is wrapped by a public function that cleans up in + * case of error. + */ +static int ssl_session_load( mbedtls_ssl_session *session, + const unsigned char *buf, + size_t len ) +{ + const unsigned char *p = buf; + const unsigned char * const end = buf + len; +#if defined(MBEDTLS_X509_CRT_PARSE_C) + size_t cert_len; +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + + /* + * Shallow session structure + */ + if( sizeof( mbedtls_ssl_session ) > (size_t)( end - p ) ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + + memcpy( session, p, sizeof( mbedtls_ssl_session ) ); + p += sizeof( mbedtls_ssl_session ); + + /* Immediately clear invalid pointer values that have been read, in case + * we exit early before we replaced them with valid ones. */ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + session->peer_cert = NULL; +#endif +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) + session->ticket = NULL; +#endif + + /* + * Peer certificate + */ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + if( 3 > (size_t)( end - p ) ) + return( MBEDTLS_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 + { + int ret; + + if( cert_len > (size_t)( end - p ) ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + + session->peer_cert = mbedtls_calloc( 1, sizeof( mbedtls_x509_crt ) ); + + if( session->peer_cert == NULL ) + return( MBEDTLS_ERR_SSL_ALLOC_FAILED ); + + mbedtls_x509_crt_init( session->peer_cert ); + + if( ( ret = mbedtls_x509_crt_parse_der( session->peer_cert, + p, cert_len ) ) != 0 ) + { + mbedtls_x509_crt_free( session->peer_cert ); + mbedtls_free( session->peer_cert ); + session->peer_cert = NULL; + return( ret ); + } + + p += cert_len; + } +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + + /* + * Session ticket + */ +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) + if( 3 > (size_t)( end - p ) ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + + session->ticket_len = ( p[0] << 16 ) | ( p[1] << 8 ) | p[2]; + p += 3; + + if( session->ticket_len != 0 ) + { + if( session->ticket_len > (size_t)( end - p ) ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + + session->ticket = mbedtls_calloc( 1, session->ticket_len ); + if( session->ticket == NULL ) + return( MBEDTLS_ERR_SSL_ALLOC_FAILED ); + + memcpy( session->ticket, p, session->ticket_len ); + p += session->ticket_len; + } +#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */ + + /* Done, should have consumed entire buffer */ + if( p != end ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + + return( 0 ); +} + +/* + * Unserialize session: public wrapper for error cleaning + */ +int mbedtls_ssl_session_load( mbedtls_ssl_session *session, + const unsigned char *buf, + size_t len ) +{ + int ret = ssl_session_load( session, buf, len ); + + if( ret != 0 ) + mbedtls_ssl_session_free( session ); + + return( ret ); +} + /* * Perform a single step of the SSL handshake */ diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index 5ac3985a2..38c94be60 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -36,6 +36,8 @@ #define mbedtls_fprintf fprintf #define mbedtls_snprintf snprintf #define mbedtls_exit exit +#define mbedtls_calloc calloc +#define mbedtls_free free #define MBEDTLS_EXIT_SUCCESS EXIT_SUCCESS #define MBEDTLS_EXIT_FAILURE EXIT_FAILURE #endif @@ -103,6 +105,7 @@ int main( void ) #define DFL_DHMLEN -1 #define DFL_RECONNECT 0 #define DFL_RECO_DELAY 0 +#define DFL_RECO_MODE 1 #define DFL_CID_ENABLED 0 #define DFL_CID_VALUE "" #define DFL_CID_ENABLED_RENEGO -1 @@ -309,8 +312,11 @@ int main( void ) " allow_legacy=%%d default: (library default: no)\n" \ USAGE_RENEGO \ " exchanges=%%d default: 1\n" \ - " reconnect=%%d default: 0 (disabled)\n" \ + " reconnect=%%d number of reconnections using session resumption\n" \ + " default: 0 (disabled)\n" \ " reco_delay=%%d default: 0 seconds\n" \ + " reco_mode=%%d 0: copy session, 1: serialize session\n" \ + " default: 1\n" \ " reconnect_hard=%%d default: 0 (disabled)\n" \ USAGE_TICKETS \ USAGE_MAX_FRAG_LEN \ @@ -392,6 +398,7 @@ struct options int dhmlen; /* minimum DHM params len in bits */ int reconnect; /* attempt to resume session */ int reco_delay; /* delay in seconds before resuming session */ + int reco_mode; /* how to keep the session around */ int reconnect_hard; /* unexpectedly reconnect from the same port */ int tickets; /* enable / disable session tickets */ const char *curves; /* list of supported elliptic curves */ @@ -716,6 +723,8 @@ int main( int argc, char *argv[] ) mbedtls_ssl_context ssl; mbedtls_ssl_config conf; mbedtls_ssl_session saved_session; + unsigned char *session_data = NULL; + size_t session_data_len = 0; #if defined(MBEDTLS_TIMING_C) mbedtls_timing_delay_context timer; #endif @@ -805,6 +814,7 @@ int main( int argc, char *argv[] ) opt.dhmlen = DFL_DHMLEN; opt.reconnect = DFL_RECONNECT; opt.reco_delay = DFL_RECO_DELAY; + opt.reco_mode = DFL_RECO_MODE; opt.reconnect_hard = DFL_RECONNECT_HARD; opt.tickets = DFL_TICKETS; opt.alpn_string = DFL_ALPN_STRING; @@ -971,6 +981,12 @@ int main( int argc, char *argv[] ) if( opt.reco_delay < 0 ) goto usage; } + else if( strcmp( p, "reco_mode" ) == 0 ) + { + opt.reco_mode = atoi( q ); + if( opt.reco_mode < 0 ) + goto usage; + } else if( strcmp( p, "reconnect_hard" ) == 0 ) { opt.reconnect_hard = atoi( q ); @@ -1863,14 +1879,55 @@ int main( int argc, char *argv[] ) mbedtls_printf(" . Saving session for reuse..." ); fflush( stdout ); - if( ( ret = mbedtls_ssl_get_session( &ssl, &saved_session ) ) != 0 ) + if( opt.reco_mode == 1 ) { - mbedtls_printf( " failed\n ! mbedtls_ssl_get_session returned -0x%x\n\n", - -ret ); - goto exit; + /* free any previously saved data */ + if( session_data != NULL ) + { + mbedtls_platform_zeroize( session_data, session_data_len ); + mbedtls_free( session_data ); + session_data = NULL; + } + + /* get size of the buffer needed */ + mbedtls_ssl_session_save( mbedtls_ssl_get_session_pointer( &ssl ), + NULL, 0, &session_data_len ); + session_data = mbedtls_calloc( 1, session_data_len ); + if( session_data == NULL ) + { + mbedtls_printf( " failed\n ! alloc %u bytes for session data\n", + (unsigned) session_data_len ); + ret = MBEDTLS_ERR_SSL_ALLOC_FAILED; + goto exit; + } + + /* actually save session data */ + if( ( ret = mbedtls_ssl_session_save( mbedtls_ssl_get_session_pointer( &ssl ), + session_data, session_data_len, + &session_data_len ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_session_saved returned -0x%04x\n\n", + -ret ); + goto exit; + } + } + else + { + if( ( ret = mbedtls_ssl_get_session( &ssl, &saved_session ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_get_session returned -0x%x\n\n", + -ret ); + goto exit; + } } mbedtls_printf( " ok\n" ); + + if( opt.reco_mode == 1 ) + { + mbedtls_printf( " [ Saved %u bytes of session data]\n", + (unsigned) session_data_len ); + } } #if defined(MBEDTLS_X509_CRT_PARSE_C) @@ -2306,10 +2363,22 @@ reconnect: goto exit; } + if( opt.reco_mode == 1 ) + { + if( ( ret = mbedtls_ssl_session_load( &saved_session, + session_data, + session_data_len ) ) != 0 ) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_session_load returned -0x%x\n\n", + -ret ); + goto exit; + } + } + if( ( ret = mbedtls_ssl_set_session( &ssl, &saved_session ) ) != 0 ) { - mbedtls_printf( " failed\n ! mbedtls_ssl_conf_session returned %d\n\n", - ret ); + mbedtls_printf( " failed\n ! mbedtls_ssl_set_session returned -0x%x\n\n", + -ret ); goto exit; } @@ -2376,6 +2445,9 @@ exit: mbedtls_ssl_config_free( &conf ); mbedtls_ctr_drbg_free( &ctr_drbg ); mbedtls_entropy_free( &entropy ); + if( session_data != NULL ) + mbedtls_platform_zeroize( session_data, session_data_len ); + mbedtls_free( session_data ); #if defined(_WIN32) mbedtls_printf( " + Press Enter to exit this program.\n" ); diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 2ea177c30..d6683b41e 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -2071,6 +2071,20 @@ run_test "Session resume using tickets: timeout" \ -S "a session has been resumed" \ -C "a session has been resumed" +run_test "Session resume using tickets: session copy" \ + "$P_SRV debug_level=3 tickets=1 cache_max=0" \ + "$P_CLI debug_level=3 tickets=1 reconnect=1 reco_mode=0" \ + 0 \ + -c "client hello, adding session ticket extension" \ + -s "found session ticket extension" \ + -s "server hello, adding session ticket extension" \ + -c "found session_ticket extension" \ + -c "parse new session ticket" \ + -S "session successfully restored from cache" \ + -s "session successfully restored from ticket" \ + -s "a session has been resumed" \ + -c "a session has been resumed" + run_test "Session resume using tickets: openssl server" \ "$O_SRV" \ "$P_CLI debug_level=3 tickets=1 reconnect=1" \ @@ -2136,6 +2150,20 @@ run_test "Session resume using tickets, DTLS: timeout" \ -S "a session has been resumed" \ -C "a session has been resumed" +run_test "Session resume using tickets, DTLS: session copy" \ + "$P_SRV debug_level=3 dtls=1 tickets=1 cache_max=0" \ + "$P_CLI debug_level=3 dtls=1 tickets=1 reconnect=1 reco_mode=0" \ + 0 \ + -c "client hello, adding session ticket extension" \ + -s "found session ticket extension" \ + -s "server hello, adding session ticket extension" \ + -c "found session_ticket extension" \ + -c "parse new session ticket" \ + -S "session successfully restored from cache" \ + -s "session successfully restored from ticket" \ + -s "a session has been resumed" \ + -c "a session has been resumed" + run_test "Session resume using tickets, DTLS: openssl server" \ "$O_SRV -dtls1" \ "$P_CLI dtls=1 debug_level=3 tickets=1 reconnect=1" \ @@ -2232,6 +2260,15 @@ run_test "Session resume using cache: no timeout" \ -s "a session has been resumed" \ -c "a session has been resumed" +run_test "Session resume using cache: session copy" \ + "$P_SRV debug_level=3 tickets=0" \ + "$P_CLI debug_level=3 tickets=0 reconnect=1 reco_mode=0" \ + 0 \ + -s "session successfully restored from cache" \ + -S "session successfully restored from ticket" \ + -s "a session has been resumed" \ + -c "a session has been resumed" + run_test "Session resume using cache: openssl client" \ "$P_SRV debug_level=3 tickets=0" \ "( $O_CLI -sess_out $SESSION; \ @@ -2327,6 +2364,15 @@ run_test "Session resume using cache, DTLS: no timeout" \ -s "a session has been resumed" \ -c "a session has been resumed" +run_test "Session resume using cache, DTLS: session copy" \ + "$P_SRV dtls=1 debug_level=3 tickets=0" \ + "$P_CLI dtls=1 debug_level=3 tickets=0 reconnect=1 reco_mode=0" \ + 0 \ + -s "session successfully restored from cache" \ + -S "session successfully restored from ticket" \ + -s "a session has been resumed" \ + -c "a session has been resumed" + run_test "Session resume using cache, DTLS: openssl client" \ "$P_SRV dtls=1 debug_level=3 tickets=0" \ "( $O_CLI -dtls1 -sess_out $SESSION; \ diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data index 5b2025fd8..aca26a9cc 100644 --- a/tests/suites/test_suite_ssl.data +++ b/tests/suites/test_suite_ssl.data @@ -8713,3 +8713,95 @@ ssl_crypt_record_small:MBEDTLS_CIPHER_NULL:MBEDTLS_MD_MD5:0:1:MBEDTLS_SSL_MINOR_ Record crypt, little space, NULL cipher, SSL3, MD5, short tag, EtM depends_on:MBEDTLS_CIPHER_NULL_CIPHER:MBEDTLS_SSL_PROTO_SSL3:MBEDTLS_MD5_C:MBEDTLS_SSL_ENCRYPT_THEN_MAC ssl_crypt_record_small:MBEDTLS_CIPHER_NULL:MBEDTLS_MD_MD5:1:1:MBEDTLS_SSL_MINOR_VERSION_0:0:0 + +Session serialization, save-load: no ticket, no cert +ssl_serialize_session_save_load:0:"" + +Session serialization, save-load: small ticket, no cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C +ssl_serialize_session_save_load:42:"" + +Session serialization, save-load: large ticket, no cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C +ssl_serialize_session_save_load:1023:"" + +Session serialization, save-load: no ticket, cert +depends_on:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO +ssl_serialize_session_save_load:0:"data_files/server5.crt" + +Session serialization, save-load: small ticket, cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO +ssl_serialize_session_save_load:42:"data_files/server5.crt" + +Session serialization, save-load: large ticket, cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO +ssl_serialize_session_save_load:1023:"data_files/server5.crt" + +Session serialization, load-save: no ticket, no cert +ssl_serialize_session_load_save:0:"" + +Session serialization, load-save: small ticket, no cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C +ssl_serialize_session_load_save:42:"" + +Session serialization, load-save: large ticket, no cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C +ssl_serialize_session_load_save:1023:"" + +Session serialization, load-save: no ticket, cert +depends_on:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO +ssl_serialize_session_load_save:0:"data_files/server5.crt" + +Session serialization, load-save: small ticket, cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO +ssl_serialize_session_load_save:42:"data_files/server5.crt" + +Session serialization, load-save: large ticket, cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO +ssl_serialize_session_load_save:1023:"data_files/server5.crt" + +Session serialization, save buffer size: no ticket, no cert +ssl_serialize_session_save_buf_size:0:"" + +Session serialization, save buffer size: small ticket, no cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C +ssl_serialize_session_save_buf_size:42:"" + +Session serialization, save buffer size: large ticket, no cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C +ssl_serialize_session_save_buf_size:1023:"" + +Session serialization, save buffer size: no ticket, cert +depends_on:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO +ssl_serialize_session_save_buf_size:0:"data_files/server5.crt" + +Session serialization, save buffer size: small ticket, cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO +ssl_serialize_session_save_buf_size:42:"data_files/server5.crt" + +Session serialization, save buffer size: large ticket, cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO +ssl_serialize_session_save_buf_size:1023:"data_files/server5.crt" + +Session serialization, load buffer size: no ticket, no cert +ssl_serialize_session_load_buf_size:0:"" + +Session serialization, load buffer size: small ticket, no cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C +ssl_serialize_session_load_buf_size:42:"" + +Session serialization, load buffer size: large ticket, no cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C +ssl_serialize_session_load_buf_size:1023:"" + +Session serialization, load buffer size: no ticket, cert +depends_on:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO +ssl_serialize_session_load_buf_size:0:"data_files/server5.crt" + +Session serialization, load buffer size: small ticket, cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO +ssl_serialize_session_load_buf_size:42:"data_files/server5.crt" + +Session serialization, load buffer size: large ticket, cert +depends_on:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_X509_USE_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_FS_IO +ssl_serialize_session_load_buf_size:1023:"data_files/server5.crt" diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index d599b6d99..b13f882c3 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -266,6 +266,68 @@ cleanup: return( ret ); } +/* + * Populate a session structure for serialization tests. + * Choose dummy values, mostly non-0 to distinguish from the init default. + */ +static int ssl_populate_session( mbedtls_ssl_session *session, + int ticket_len, + const char *crt_file ) +{ +#if defined(MBEDTLS_HAVE_TIME) + session->start = mbedtls_time( NULL ) - 42; +#endif + session->ciphersuite = 0xabcd; + session->compression = 1; + session->id_len = sizeof( session->id ); + memset( session->id, 66, session->id_len ); + memset( session->master, 17, sizeof( session->master ) ); + +#if defined(MBEDTLS_X509_CRT_PARSE_C) && defined(MBEDTLS_FS_IO) + if( strlen( crt_file ) != 0 ) + { + int ret; + + session->peer_cert = mbedtls_calloc( 1, sizeof( *session->peer_cert ) ); + if( session->peer_cert == NULL ) + return( -1 ); + + ret = mbedtls_x509_crt_parse_file( session->peer_cert, crt_file ); + if( ret != 0 ) + return( ret ); + } +#else + (void) crt_file; +#endif + session->verify_result = 0xdeadbeef; + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) + if( ticket_len != 0 ) + { + session->ticket = mbedtls_calloc( 1, ticket_len ); + if( session->ticket == NULL ) + return( -1 ); + memset( session->ticket, 33, ticket_len ); + } + session->ticket_len = ticket_len; + session->ticket_lifetime = 86401; +#else + (void) ticket_len; +#endif + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + session->mfl_code = 1; +#endif +#if defined(MBEDTLS_SSL_TRUNCATED_HMAC) + session->trunc_hmac = 1; +#endif +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + session->encrypt_then_mac = 1; +#endif + + return( 0 ); +} + /* END_HEADER */ /* BEGIN_DEPENDENCIES @@ -575,3 +637,217 @@ exit: mbedtls_free( buf ); } /* END_CASE */ + +/* BEGIN_CASE */ +void ssl_serialize_session_save_load( int ticket_len, char *crt_file ) +{ + mbedtls_ssl_session original, restored; + unsigned char *buf = NULL; + size_t len; + + /* + * Test that a save-load pair is the identity + */ + + mbedtls_ssl_session_init( &original ); + mbedtls_ssl_session_init( &restored ); + + /* Prepare a dummy session to work on */ + TEST_ASSERT( ssl_populate_session( &original, ticket_len, crt_file ) == 0 ); + + /* Serialize it */ + TEST_ASSERT( mbedtls_ssl_session_save( &original, NULL, 0, &len ) + == MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL ); + TEST_ASSERT( ( buf = mbedtls_calloc( 1, len ) ) != NULL ); + TEST_ASSERT( mbedtls_ssl_session_save( &original, buf, len, &len ) + == 0 ); + + /* Restore session from serialized data */ + TEST_ASSERT( mbedtls_ssl_session_load( &restored, buf, len) == 0 ); + + /* + * Make sure both session structures are identical + */ +#if defined(MBEDTLS_HAVE_TIME) + TEST_ASSERT( original.start == restored.start ); +#endif + TEST_ASSERT( original.ciphersuite == restored.ciphersuite ); + TEST_ASSERT( original.compression == restored.compression ); + TEST_ASSERT( original.id_len == restored.id_len ); + TEST_ASSERT( memcmp( original.id, + restored.id, sizeof( original.id ) ) == 0 ); + TEST_ASSERT( memcmp( original.master, + restored.master, sizeof( original.master ) ) == 0 ); + +#if defined(MBEDTLS_X509_CRT_PARSE_C) + TEST_ASSERT( ( original.peer_cert == NULL ) == + ( restored.peer_cert == NULL ) ); + if( original.peer_cert != NULL ) + { + TEST_ASSERT( original.peer_cert->raw.len == + restored.peer_cert->raw.len ); + TEST_ASSERT( memcmp( original.peer_cert->raw.p, + restored.peer_cert->raw.p, + original.peer_cert->raw.len ) == 0 ); + } +#endif + TEST_ASSERT( original.verify_result == restored.verify_result ); + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) + TEST_ASSERT( original.ticket_len == restored.ticket_len ); + if( original.ticket_len != 0 ) + { + TEST_ASSERT( original.ticket != NULL ); + TEST_ASSERT( restored.ticket != NULL ); + TEST_ASSERT( memcmp( original.ticket, + restored.ticket, original.ticket_len ) == 0 ); + } + TEST_ASSERT( original.ticket_lifetime == restored.ticket_lifetime ); +#endif + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + TEST_ASSERT( original.mfl_code == restored.mfl_code ); +#endif + +#if defined(MBEDTLS_SSL_TRUNCATED_HMAC) + TEST_ASSERT( original.trunc_hmac == restored.trunc_hmac ); +#endif + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + TEST_ASSERT( original.encrypt_then_mac == restored.encrypt_then_mac ); +#endif + +exit: + mbedtls_ssl_session_free( &original ); + mbedtls_ssl_session_free( &restored ); + mbedtls_free( buf ); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_SEE_FUTURE_PR */ +void ssl_serialize_session_load_save( int ticket_len, char *crt_file ) +{ + mbedtls_ssl_session session; + unsigned char *buf1 = NULL, *buf2 = NULL; + size_t len0, len1, len2; + + /* + * Test that a load-save pair is the identity + */ + + mbedtls_ssl_session_init( &session ); + + /* Prepare a dummy session to work on */ + TEST_ASSERT( ssl_populate_session( &session, ticket_len, crt_file ) == 0 ); + + /* Get desired buffer size for serializing */ + TEST_ASSERT( mbedtls_ssl_session_save( &session, NULL, 0, &len0 ) + == MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL ); + + /* Allocate first buffer */ + buf1 = mbedtls_calloc( 1, len0 ); + TEST_ASSERT( buf1 != NULL ); + + /* Serialize to buffer and free live session */ + TEST_ASSERT( mbedtls_ssl_session_save( &session, buf1, len0, &len1 ) + == 0 ); + TEST_ASSERT( len0 == len1 ); + mbedtls_ssl_session_free( &session ); + + /* Restore session from serialized data */ + TEST_ASSERT( mbedtls_ssl_session_load( &session, buf1, len1 ) == 0 ); + + /* Allocate second buffer and serialize to it */ + buf2 = mbedtls_calloc( 1, len0 ); + TEST_ASSERT( buf2 != NULL ); + TEST_ASSERT( mbedtls_ssl_session_save( &session, buf2, len0, &len2 ) + == 0 ); + + /* Make sure both serialized versions are identical */ + TEST_ASSERT( len1 == len2 ); + TEST_ASSERT( memcmp( buf1, buf2, len1 ) == 0 ); + +exit: + mbedtls_ssl_session_free( &session ); + mbedtls_free( buf1 ); + mbedtls_free( buf2 ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void ssl_serialize_session_save_buf_size( int ticket_len, char *crt_file ) +{ + mbedtls_ssl_session session; + unsigned char *buf = NULL; + size_t good_len, bad_len, test_len; + + /* + * Test that session_save() fails cleanly on small buffers + */ + + mbedtls_ssl_session_init( &session ); + + /* Prepare dummy session and get serialized size */ + TEST_ASSERT( ssl_populate_session( &session, ticket_len, crt_file ) == 0 ); + TEST_ASSERT( mbedtls_ssl_session_save( &session, NULL, 0, &good_len ) + == MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL ); + + /* Try all possible bad lengths */ + for( bad_len = 1; bad_len < good_len; bad_len++ ) + { + /* Allocate exact size so that asan/valgrind can detect any overwrite */ + mbedtls_free( buf ); + TEST_ASSERT( ( buf = mbedtls_calloc( 1, bad_len ) ) != NULL ); + TEST_ASSERT( mbedtls_ssl_session_save( &session, buf, bad_len, + &test_len ) + == MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL ); + TEST_ASSERT( test_len == good_len ); + } + +exit: + mbedtls_ssl_session_free( &session ); + mbedtls_free( buf ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void ssl_serialize_session_load_buf_size( int ticket_len, char *crt_file ) +{ + mbedtls_ssl_session session; + unsigned char *good_buf = NULL, *bad_buf = NULL; + size_t good_len, bad_len; + + /* + * Test that session_load() fails cleanly on small buffers + */ + + mbedtls_ssl_session_init( &session ); + + /* Prepare serialized session data */ + TEST_ASSERT( ssl_populate_session( &session, ticket_len, crt_file ) == 0 ); + TEST_ASSERT( mbedtls_ssl_session_save( &session, NULL, 0, &good_len ) + == MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL ); + TEST_ASSERT( ( good_buf = mbedtls_calloc( 1, good_len ) ) != NULL ); + TEST_ASSERT( mbedtls_ssl_session_save( &session, good_buf, good_len, + &good_len ) == 0 ); + mbedtls_ssl_session_free( &session ); + + /* Try all possible bad lengths */ + for( bad_len = 0; bad_len < good_len; bad_len++ ) + { + /* Allocate exact size so that asan/valgrind can detect any overread */ + mbedtls_free( bad_buf ); + bad_buf = mbedtls_calloc( 1, bad_len ? bad_len : 1 ); + TEST_ASSERT( bad_buf != NULL ); + memcpy( bad_buf, good_buf, bad_len ); + + TEST_ASSERT( mbedtls_ssl_session_load( &session, bad_buf, bad_len ) + == MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + } + +exit: + mbedtls_ssl_session_free( &session ); + mbedtls_free( good_buf ); + mbedtls_free( bad_buf ); +} +/* END_CASE */