Session Resumption in TLS 1.2 in an ongoing session (iec-62351-3 / RFC5246)

Hello,

I am trying to test a device’s conformance to IEC62351 which defines some rules about TLS implementations, in particular im wondering if it’s possible to:

Use mbedtls on a TLS server to accept a session resumption when a client sends a ClientHello message with a session ID in an ongoing TLS session.

Also RFC 5246 says this about resumption:

The ClientHello message includes a variable-length session
identifier. If not empty, the value identifies a session between the
same client and server whose security parameters the client wishes to
reuse. The session identifier MAY be from an earlier connection,
this connection, or from another currently active connection.

so it should be possible to resume in an ongoing session but:

I already have a working implementation of a TLS 1.2 server using mbedtls 2.28, but if a client sends a clienthello with a session ID in an ongoing session, the server always responds with a renegotiation by default.

Taking a look at the library code i tried to change the function:

static void ssl_handle_id_based_session_resumption(mbedtls_ssl_context *ssl)

found in the file ssl_srv.c in mbedtls 2.28,

and removed a check which skipped resumption if a client hello was received during a session, this does not work properly however because the server closes the connection after sending the finished message due to a MBEDTLS_SSL_ALERT_MSG_DECRYPT_ERROR.

Im wondering if there is anyway to allow resumption in this manner using mbedtls or if im doing something wrong? If you require further information please let me know and i will try to add as much as i can!

Just replying to my own post in case anyone will ever google this question, the change we made to the library did actually work and is compliant with IEC-62351.
Im gonna leave as much info as i can because to be honest trying to find information about this topic was very hard (there was in fact no information available at all actually) and it seems like a good idea to spare some poor engineer in the future some time.

This only applies to mbedtls 2.28 but im sure a similiar fix can be applied to later versions.
Basically just modify the function ssl_handle_id_based_session_resumption() in ssl_srv.c like this:

static void ssl_handle_id_based_session_resumption(mbedtls_ssl_context *ssl)

{
int ret;
mbedtls_ssl_session session_tmp;
mbedtls_ssl_session * const session = ssl->session_negotiate;

// Resume is 0  by default, see ssl_handshake_init().
//It may be already set to 1 by ssl_parse_session_ticket_ext().
if (ssl->handshake->resume == 1) {
    return;
}
if (session->id_len == 0) {
    return;
}
#if defined(MBEDTLS_SSL_RENEGOTIATION)
    if (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE) {
        if (ssl->session != NULL &&
            ssl->session->id_len == session->id_len &&
            memcmp(ssl->session->id, session->id, session->id_len) == 0 &&
            mbedtls_ssl_session_copy(session, ssl->session) == 0) {
            printf("resuming existing session\n");
            ssl->handshake->resume = 1;
            ssl->secure_renegotiation = MBEDTLS_SSL_SECURE_RENEGOTIATION;
            return;
        }
        return;
    }
#endif

if (ssl->conf->f_get_cache == NULL) {
    return;
}

mbedtls_ssl_session_init(&session_tmp);

session_tmp.id_len = session->id_len;
memcpy(session_tmp.id, session->id, session->id_len);

ret = ssl->conf->f_get_cache(ssl->conf->p_cache,
                             &session_tmp);
if (ret != 0) {
    goto exit;
}

if (session->ciphersuite != session_tmp.ciphersuite ||
    session->compression != session_tmp.compression) {
    // Mismatch between cached and negotiated session
    goto exit;
}

// Move semantics
mbedtls_ssl_session_free(session);
*session = session_tmp;
memset(&session_tmp, 0, sizeof(session_tmp));

MBEDTLS_SSL_DEBUG_MSG(3, ("session successfully restored from cache"));
ssl->handshake->resume = 1;

exit:

mbedtls_ssl_session_free(&session_tmp);


}

we decided to go with this version but you can also just remove the if statement and it also does seem to work, so something like this:

#if (0)
    if (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE) {
        if (ssl->session != NULL &&
            ssl->session->id_len == session->id_len &&
            memcmp(ssl->session->id, session->id, session->id_len) == 0 &&
            mbedtls_ssl_session_copy(session, ssl->session) == 0) {
            printf("resuming existing session\n");
            ssl->handshake->resume = 1;
            ssl->secure_renegotiation = MBEDTLS_SSL_SECURE_RENEGOTIATION;
            return;
        }
        return;
    }
#endif

It’s a bit of a hacky solution but it manages to pass certification tests and does not break renegotiation or normal resumption for mbedtls 2.28.

If you are on later versions of mbedtls you might have to work a bit harder because they renamed some files and moved some functions around.
To test this behaviour i suggest you write an openssl client using openssllib and perform a “resumption” using the function SSL_renegotiate_abbreviated(), which simulates the behaviour of the test cases from the standard and seems be compliant to the RFC aswell.

Good luck, hope this is useful to someone out there!