Commit 4ff65f77 authored by Matt Caswell's avatar Matt Caswell
Browse files

Add support for client side parsing of the PSK extension



Requires a refactor of the ServerHello parsing, so that we parse first and
then subsequently process. This is because the resumption information is
held in the extensions block which is parsed last - but we need to know that
information earlier.

Reviewed-by: default avatarRich Salz <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/2259)
parent fc24f0bf
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -2326,6 +2326,7 @@ int ERR_load_SSL_strings(void);
# define SSL_F_TLS_PARSE_CTOS_RENEGOTIATE                 464
# define SSL_F_TLS_PARSE_CTOS_USE_SRTP                    465
# define SSL_F_TLS_PARSE_STOC_KEY_SHARE                   445
# define SSL_F_TLS_PARSE_STOC_PSK                         502
# define SSL_F_TLS_PARSE_STOC_RENEGOTIATE                 448
# define SSL_F_TLS_PARSE_STOC_USE_SRTP                    446
# define SSL_F_TLS_POST_PROCESS_CLIENT_HELLO              378
+2 −0
Original line number Diff line number Diff line
@@ -373,6 +373,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     "tls_parse_ctos_renegotiate"},
    {ERR_FUNC(SSL_F_TLS_PARSE_CTOS_USE_SRTP), "tls_parse_ctos_use_srtp"},
    {ERR_FUNC(SSL_F_TLS_PARSE_STOC_KEY_SHARE), "tls_parse_stoc_key_share"},
    {ERR_FUNC(SSL_F_TLS_PARSE_STOC_PSK), "tls_parse_stoc_psk"},
    {ERR_FUNC(SSL_F_TLS_PARSE_STOC_RENEGOTIATE),
     "tls_parse_stoc_renegotiate"},
    {ERR_FUNC(SSL_F_TLS_PARSE_STOC_USE_SRTP), "tls_parse_stoc_use_srtp"},
@@ -454,6 +455,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
    {ERR_REASON(SSL_R_BAD_PACKET_LENGTH), "bad packet length"},
    {ERR_REASON(SSL_R_BAD_PROTOCOL_VERSION_NUMBER),
     "bad protocol version number"},
    {ERR_REASON(SSL_R_BAD_PSK_IDENTITY), "bad psk identity"},
    {ERR_REASON(SSL_R_BAD_RECORD_TYPE), "bad record type"},
    {ERR_REASON(SSL_R_BAD_RSA_ENCRYPT), "bad rsa encrypt"},
    {ERR_REASON(SSL_R_BAD_SIGNATURE), "bad signature"},
+1 −1
Original line number Diff line number Diff line
@@ -277,7 +277,7 @@ static const EXTENSION_DEFINITION ext_defs[] = {
        TLSEXT_TYPE_psk,
        EXT_CLIENT_HELLO | EXT_TLS1_3_SERVER_HELLO | EXT_TLS_IMPLEMENTATION_ONLY
        | EXT_TLS1_3_ONLY,
        NULL, NULL, NULL, NULL, tls_construct_ctos_psk, NULL
        NULL, NULL, tls_parse_stoc_psk, NULL, tls_construct_ctos_psk, NULL
    }
};

+23 −0
Original line number Diff line number Diff line
@@ -1279,3 +1279,26 @@ int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,

    return 1;
}

int tls_parse_stoc_psk(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al)
{
#ifndef OPENSSL_NO_TLS1_3
    unsigned int identity;

    if (!PACKET_get_net_2(pkt, &identity) || PACKET_remaining(pkt) != 0) {
        *al = SSL_AD_HANDSHAKE_FAILURE;
        SSLerr(SSL_F_TLS_PARSE_STOC_PSK, SSL_R_LENGTH_MISMATCH);
        return 0;
    }

    if (s->session->ext.tick_identity != (int)identity) {
        *al = SSL_AD_HANDSHAKE_FAILURE;
        SSLerr(SSL_F_TLS_PARSE_STOC_PSK, SSL_R_BAD_PSK_IDENTITY);
        return 0;
    }

    s->hit = 1;
#endif

    return 1;
}
+80 −64
Original line number Diff line number Diff line
@@ -1130,6 +1130,7 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
        goto f_err;
    }

    /* We do this immediately so we know what format the ServerHello is in */
    protverr = ssl_choose_client_version(s, sversion);
    if (protverr != 0) {
        al = SSL_AD_PROTOCOL_VERSION;
@@ -1145,8 +1146,6 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
        goto f_err;
    }

    s->hit = 0;

    /* Get the session-id. */
    if (!SSL_IS_TLS13(s)) {
        if (!PACKET_get_length_prefixed_1(pkt, &session_id)) {
@@ -1173,19 +1172,52 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
        goto f_err;
    }

    if (!SSL_IS_TLS13(s)) {
        if (!PACKET_get_1(pkt, &compression)) {
            SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_LENGTH_MISMATCH);
            al = SSL_AD_DECODE_ERROR;
            goto f_err;
        }
    } else {
        compression = 0;
    }

    /* TLS extensions */
    if (PACKET_remaining(pkt) == 0) {
        PACKET_null_init(&extpkt);
    } else if (!PACKET_as_length_prefixed_2(pkt, &extpkt)) {
        al = SSL_AD_DECODE_ERROR;
        SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_BAD_LENGTH);
        goto f_err;
    }

    context = SSL_IS_TLS13(s) ? EXT_TLS1_3_SERVER_HELLO
                              : EXT_TLS1_2_SERVER_HELLO;
    if (!tls_collect_extensions(s, &extpkt, context, &extensions, &al))
        goto f_err;

    s->hit = 0;

    if (SSL_IS_TLS13(s)) {
        /* This will set s->hit if we are resuming */
        if (!tls_parse_extension(s, TLSEXT_IDX_psk,
                                 EXT_TLS1_3_SERVER_HELLO,
                                 extensions, NULL, 0, &al))
            goto f_err;
    } else {
        /*
     * Check if we can resume the session based on external pre-shared secret.
     * EAP-FAST (RFC 4851) supports two types of session resumption.
         * Check if we can resume the session based on external pre-shared
         * secret. EAP-FAST (RFC 4851) supports two types of session resumption.
         * Resumption based on server-side state works with session IDs.
         * Resumption based on pre-shared Protected Access Credentials (PACs)
         * works by overriding the SessionTicket extension at the application
     * layer, and does not send a session ID. (We do not know whether EAP-FAST
     * servers would honour the session ID.) Therefore, the session ID alone
     * is not a reliable indicator of session resumption, so we first check if
     * we can resume, and later peek at the next handshake message to see if the
     * server wants to resume.
         * layer, and does not send a session ID. (We do not know whether
         * EAP-FAST servers would honour the session ID.) Therefore, the session
         * ID alone is not a reliable indicator of session resumption, so we
         * first check if we can resume, and later peek at the next handshake
         * message to see if the server wants to resume.
         */
    if (s->version >= TLS1_VERSION && !SSL_IS_TLS13(s)
        if (s->version >= TLS1_VERSION
                && s->ext.session_secret_cb != NULL && s->session->ext.tick) {
            const SSL_CIPHER *pref_cipher = NULL;
            /*
@@ -1209,9 +1241,14 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
            }
        }

    if (session_id_len != 0 && session_id_len == s->session->session_id_length
        if (session_id_len != 0
                && session_id_len == s->session->session_id_length
                && memcmp(PACKET_data(&session_id), s->session->session_id,
                  session_id_len) == 0) {
                          session_id_len) == 0)
            s->hit = 1;
    }

    if (s->hit) {
        if (s->sid_ctx_length != s->session->sid_ctx_length
                || memcmp(s->session->sid_ctx, s->sid_ctx, s->sid_ctx_length)) {
            /* actually a client application bug */
@@ -1220,16 +1257,18 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
                   SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT);
            goto f_err;
        }
        s->hit = 1;
    } else {
        /*
         * If we were trying for session-id reuse but the server
         * didn't echo the ID, make a new SSL_SESSION.
         * didn't resume, make a new SSL_SESSION.
         * In the case of EAP-FAST and PAC, we do not send a session ID,
         * so the PAC-based session secret is always preserved. It'll be
         * overwritten if the server refuses resumption.
         */
        if (s->session->session_id_length > 0) {
        if (s->session->session_id_length > 0
                || (SSL_IS_TLS13(s)
                    && s->session->ext.tick_identity
                       != TLSEXT_PSK_BAD_IDENTITY)) {
            s->ctx->stats.sess_miss++;
            if (!ssl_get_new_session(s, 0)) {
                goto f_err;
@@ -1299,17 +1338,6 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
        goto f_err;
    }
    s->s3->tmp.new_cipher = c;
    /* lets get the compression algorithm */
    /* COMPRESSION */
    if (!SSL_IS_TLS13(s)) {
        if (!PACKET_get_1(pkt, &compression)) {
            SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_LENGTH_MISMATCH);
            al = SSL_AD_DECODE_ERROR;
            goto f_err;
        }
    } else {
        compression = 0;
    }

#ifdef OPENSSL_NO_COMP
    if (compression != 0) {
@@ -1353,19 +1381,7 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
    }
#endif

    /* TLS extensions */
    if (PACKET_remaining(pkt) == 0) {
        PACKET_null_init(&extpkt);
    } else if (!PACKET_as_length_prefixed_2(pkt, &extpkt)) {
        al = SSL_AD_DECODE_ERROR;
        SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_BAD_LENGTH);
        goto f_err;
    }

    context = SSL_IS_TLS13(s) ? EXT_TLS1_3_SERVER_HELLO
                              : EXT_TLS1_2_SERVER_HELLO;
    if (!tls_collect_extensions(s, &extpkt, context, &extensions, &al)
            || !tls_parse_all_extensions(s, context, extensions, NULL, 0, &al))
    if (!tls_parse_all_extensions(s, context, extensions, NULL, 0, &al))
        goto f_err;

#ifndef OPENSSL_NO_SCTP
Loading