More state machine reorg
authorMatt Caswell <matt@openssl.org>
Fri, 11 Sep 2015 10:23:20 +0000 (11:23 +0100)
committerMatt Caswell <matt@openssl.org>
Fri, 30 Oct 2015 08:38:18 +0000 (08:38 +0000)
Move some function definitions around within the state machine to make sure
they are in the correct files. Also create a statem_locl.h header for stuff
entirely local to the state machine code and move various definitions into
it.

Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
ssl/Makefile
ssl/ssl_locl.h
ssl/statem/statem.c
ssl/statem/statem.h
ssl/statem/statem_clnt.c
ssl/statem/statem_dtls.c
ssl/statem/statem_lib.c
ssl/statem/statem_locl.h [new file with mode: 0644]
ssl/statem/statem_srvr.c

index 9f1fe8d..0865631 100644 (file)
@@ -682,6 +682,7 @@ statem/statem.o: ../include/openssl/symhacks.h ../include/openssl/tls1.h
 statem/statem.o: ../include/openssl/x509.h ../include/openssl/x509_vfy.h
 statem/statem.o: statem/../packet_locl.h statem/../record/record.h
 statem/statem.o: statem/../ssl_locl.h statem/../statem/statem.h statem/statem.c
+statem/statem.o: statem/statem_locl.h
 statem/statem_clnt.o: ../e_os.h ../include/openssl/asn1.h
 statem/statem_clnt.o: ../include/openssl/bio.h ../include/openssl/bn.h
 statem/statem_clnt.o: ../include/openssl/buffer.h ../include/openssl/comp.h
@@ -706,7 +707,7 @@ statem/statem_clnt.o: ../include/openssl/symhacks.h ../include/openssl/tls1.h
 statem/statem_clnt.o: ../include/openssl/x509.h ../include/openssl/x509_vfy.h
 statem/statem_clnt.o: statem/../packet_locl.h statem/../record/record.h
 statem/statem_clnt.o: statem/../ssl_locl.h statem/../statem/statem.h
-statem/statem_clnt.o: statem/statem_clnt.c
+statem/statem_clnt.o: statem/statem_clnt.c statem/statem_locl.h
 statem/statem_dtls.o: ../e_os.h ../include/openssl/asn1.h
 statem/statem_dtls.o: ../include/openssl/bio.h ../include/openssl/bn.h
 statem/statem_dtls.o: ../include/openssl/buffer.h ../include/openssl/comp.h
@@ -730,7 +731,7 @@ statem/statem_dtls.o: ../include/openssl/symhacks.h ../include/openssl/tls1.h
 statem/statem_dtls.o: ../include/openssl/x509.h ../include/openssl/x509_vfy.h
 statem/statem_dtls.o: statem/../packet_locl.h statem/../record/record.h
 statem/statem_dtls.o: statem/../ssl_locl.h statem/../statem/statem.h
-statem/statem_dtls.o: statem/statem_dtls.c
+statem/statem_dtls.o: statem/statem_dtls.c statem/statem_locl.h
 statem/statem_lib.o: ../e_os.h ../include/openssl/asn1.h
 statem/statem_lib.o: ../include/openssl/bio.h ../include/openssl/bn.h
 statem/statem_lib.o: ../include/openssl/buffer.h ../include/openssl/comp.h
@@ -754,7 +755,7 @@ statem/statem_lib.o: ../include/openssl/symhacks.h ../include/openssl/tls1.h
 statem/statem_lib.o: ../include/openssl/x509.h ../include/openssl/x509_vfy.h
 statem/statem_lib.o: statem/../packet_locl.h statem/../record/record.h
 statem/statem_lib.o: statem/../ssl_locl.h statem/../statem/statem.h
-statem/statem_lib.o: statem/statem_lib.c
+statem/statem_lib.o: statem/statem_lib.c statem/statem_locl.h
 statem/statem_srvr.o: ../e_os.h ../include/internal/constant_time_locl.h
 statem/statem_srvr.o: ../include/openssl/asn1.h ../include/openssl/bio.h
 statem/statem_srvr.o: ../include/openssl/bn.h ../include/openssl/buffer.h
@@ -779,7 +780,7 @@ statem/statem_srvr.o: ../include/openssl/symhacks.h ../include/openssl/tls1.h
 statem/statem_srvr.o: ../include/openssl/x509.h ../include/openssl/x509_vfy.h
 statem/statem_srvr.o: statem/../packet_locl.h statem/../record/record.h
 statem/statem_srvr.o: statem/../ssl_locl.h statem/../statem/statem.h
-statem/statem_srvr.o: statem/statem_srvr.c
+statem/statem_srvr.o: statem/statem_locl.h statem/statem_srvr.c
 t1_enc.o: ../e_os.h ../include/openssl/asn1.h ../include/openssl/bio.h
 t1_enc.o: ../include/openssl/bn.h ../include/openssl/buffer.h
 t1_enc.o: ../include/openssl/comp.h ../include/openssl/crypto.h
index 25afff8..d30663f 100644 (file)
@@ -1914,15 +1914,7 @@ __owur int ssl_generate_master_secret(SSL *s, unsigned char *pms, size_t pmslen,
 __owur const SSL_CIPHER *ssl3_get_cipher_by_char(const unsigned char *p);
 __owur int ssl3_put_cipher_by_char(const SSL_CIPHER *c, unsigned char *p);
 void ssl3_init_finished_mac(SSL *s);
-__owur int tls_construct_server_certificate(SSL *s);
-__owur int tls_construct_new_session_ticket(SSL *s);
-__owur int tls_construct_cert_status(SSL *s);
-__owur enum MSG_PROCESS_RETURN tls_process_change_cipher_spec(SSL *s,
-                                                              PACKET *pkt);
-__owur enum MSG_PROCESS_RETURN tls_process_finished(SSL *s, PACKET *pkt);
 __owur int ssl3_setup_key_block(SSL *s);
-__owur int tls_construct_change_cipher_spec(SSL *s);
-__owur int dtls_construct_change_cipher_spec(SSL *s);
 __owur int ssl3_change_cipher_state(SSL *s, int which);
 void ssl3_cleanup_key_block(SSL *s);
 __owur int ssl3_do_write(SSL *s, int type);
@@ -1930,12 +1922,6 @@ int ssl3_send_alert(SSL *s, int level, int desc);
 __owur int ssl3_generate_master_secret(SSL *s, unsigned char *out,
                                 unsigned char *p, int len);
 __owur int ssl3_get_req_cert_type(SSL *s, unsigned char *p);
-__owur long ssl3_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok);
-__owur int tls_get_message_header(SSL *s, int *mt);
-__owur int tls_get_message_body(SSL *s, unsigned long *len);
-__owur int tls_construct_finished(SSL *s, const char *sender, int slen);
-__owur enum WORK_STATE tls_finish_handshake(SSL *s, enum WORK_STATE wst);
-__owur enum WORK_STATE dtls_wait_for_dry(SSL *s);
 __owur int ssl3_num_ciphers(void);
 __owur const SSL_CIPHER *ssl3_get_cipher(unsigned int u);
 int ssl3_renegotiate(SSL *ssl);
@@ -2007,54 +1993,6 @@ __owur unsigned int dtls1_link_min_mtu(void);
 void dtls1_hm_fragment_free(hm_fragment *frag);
 __owur int dtls1_query_mtu(SSL *s);
 
-/* some client-only functions */
-__owur int tls_construct_client_hello(SSL *s);
-__owur enum MSG_PROCESS_RETURN tls_process_server_hello(SSL *s,
-                                                        PACKET *pkt);
-__owur enum MSG_PROCESS_RETURN tls_process_certificate_request(SSL *s,
-                                                               PACKET *pkt);
-__owur enum MSG_PROCESS_RETURN tls_process_new_session_ticket(SSL *s,
-                                                              PACKET *pkt);
-__owur enum MSG_PROCESS_RETURN tls_process_cert_status(SSL *s, PACKET *pkt);
-__owur enum MSG_PROCESS_RETURN tls_process_server_done(SSL *s, PACKET *pkt);
-__owur int tls_construct_client_verify(SSL *s);
-__owur enum WORK_STATE tls_prepare_client_certificate(SSL *s,
-                                                      enum WORK_STATE wst);
-__owur int tls_construct_client_certificate(SSL *s);
-__owur int ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey);
-__owur int tls_construct_client_key_exchange(SSL *s);
-__owur int tls_client_key_exchange_post_work(SSL *s);
-__owur enum MSG_PROCESS_RETURN tls_process_key_exchange(SSL *s,
-                                                        PACKET *pkt);
-__owur enum MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s,
-                                                              PACKET *pkt);
-__owur int ssl3_check_cert_and_algorithm(SSL *s);
-#  ifndef OPENSSL_NO_NEXTPROTONEG
-__owur int tls_construct_next_proto(SSL *s);
-#  endif
-__owur enum MSG_PROCESS_RETURN dtls_process_hello_verify(SSL *s, PACKET *pkt);
-
-/* some server-only functions */
-__owur enum MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt);
-__owur enum WORK_STATE tls_post_process_client_hello(SSL *s,
-                                                     enum WORK_STATE wst);
-__owur int tls_construct_server_hello(SSL *s);
-__owur int tls_construct_hello_request(SSL *s);
-__owur int dtls_construct_hello_verify_request(SSL *s);
-__owur int tls_construct_server_key_exchange(SSL *s);
-__owur int tls_construct_certificate_request(SSL *s);
-__owur int tls_construct_server_done(SSL *s);
-__owur enum MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s,
-                                                              PACKET *pkt);
-__owur enum MSG_PROCESS_RETURN tls_process_client_key_exchange(SSL *s,
-                                                               PACKET *pkt);
-__owur enum WORK_STATE tls_post_process_client_key_exchange(SSL *s,
-    enum WORK_STATE wst);
-__owur enum MSG_PROCESS_RETURN tls_process_cert_verify(SSL *s, PACKET *pkt);
-#  ifndef OPENSSL_NO_NEXTPROTONEG
-__owur enum MSG_PROCESS_RETURN tls_process_next_proto(SSL *s, PACKET *pkt);
-#  endif
-
 __owur int tls1_new(SSL *s);
 void tls1_free(SSL *s);
 void tls1_clear(SSL *s);
@@ -2067,7 +2005,6 @@ void dtls1_clear(SSL *s);
 long dtls1_ctrl(SSL *s, int cmd, long larg, void *parg);
 __owur int dtls1_shutdown(SSL *s);
 
-__owur int dtls_get_message(SSL *s, int *mt, unsigned long *len);
 __owur int dtls1_dispatch_alert(SSL *s);
 
 __owur int ssl_init_wbio_buffer(SSL *s, int push);
index 7e4f524..f0b3260 100644 (file)
@@ -58,6 +58,7 @@
 
 #include <openssl/rand.h>
 #include "../ssl_locl.h"
+#include "statem_locl.h"
 
 /*
  * This file implements the SSL/TLS/DTLS state machines.
@@ -83,7 +84,7 @@
  * | Message flow state machine                |            |                 |
  * |                                           |            |                 |
  * | -------------------- -------------------- | Transition | Handshake state |
- * | | MSG_FLOW_READING    | | MSG_FLOW_WRITING    | | Event      | machine         |
+ * | | MSG_FLOW_READING | | MSG_FLOW_WRITING | | Event      | machine         |
  * | | sub-state        | | sub-state        | |----------->|                 |
  * | | machine for      | | machine for      | |            |                 |
  * | | reading messages | | writing messages | |            |                 |
@@ -108,27 +109,6 @@ static void init_read_state_machine(SSL *s);
 static enum SUB_STATE_RETURN read_state_machine(SSL *s);
 static void init_write_state_machine(SSL *s);
 static enum SUB_STATE_RETURN write_state_machine(SSL *s);
-static inline int cert_req_allowed(SSL *s);
-static inline int key_exchange_skip_allowed(SSL *s);
-static int client_read_transition(SSL *s, int mt);
-static enum WRITE_TRAN client_write_transition(SSL *s);
-static enum WORK_STATE client_pre_work(SSL *s, enum WORK_STATE wst);
-static enum WORK_STATE client_post_work(SSL *s, enum WORK_STATE wst);
-static int client_construct_message(SSL *s);
-static unsigned long client_max_message_size(SSL *s);
-static enum MSG_PROCESS_RETURN client_process_message(SSL *s, PACKET *pkt);
-static enum WORK_STATE client_post_process_message(SSL *s, enum WORK_STATE wst);
-static int server_read_transition(SSL *s, int mt);
-static inline int send_server_key_exchange(SSL *s);
-static inline int send_certificate_request(SSL *s);
-static enum WRITE_TRAN server_write_transition(SSL *s);
-static enum WORK_STATE server_pre_work(SSL *s, enum WORK_STATE wst);
-static enum WORK_STATE server_post_work(SSL *s, enum WORK_STATE wst);
-static int server_construct_message(SSL *s);
-static unsigned long server_max_message_size(SSL *s);
-static enum MSG_PROCESS_RETURN server_process_message(SSL *s, PACKET *pkt);
-static enum WORK_STATE server_post_process_message(SSL *s, enum WORK_STATE wst);
-
 
 enum HANDSHAKE_STATE SSL_state(const SSL *ssl)
 {
@@ -818,7 +798,7 @@ static enum SUB_STATE_RETURN write_state_machine(SSL *s)
 /*
  * Flush the write BIO
  */
-static int statem_flush(SSL *s)
+int statem_flush(SSL *s)
 {
     s->rwstate = SSL_WRITING;
     if (BIO_flush(s->wbio) <= 0) {
@@ -867,7 +847,6 @@ int statem_app_data_allowed(SSL *s)
     return 0;
 }
 
-
 #ifndef OPENSSL_NO_SCTP
 /*
  * Set flag used by SCTP to determine whether we are in the read sock state
@@ -890,1352 +869,3 @@ int statem_in_sctp_read_sock(SSL *s)
     return s->statem.in_sctp_read_sock;
 }
 #endif
-
-/*
- * Is a CertificateRequest message allowed at the moment or not?
- *
- *  Return values are:
- *  1: Yes
- *  0: No
- */
-static inline int cert_req_allowed(SSL *s)
-{
-    /* TLS does not like anon-DH with client cert */
-    if (s->version > SSL3_VERSION
-            && (s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL))
-        return 0;
-
-    return 1;
-}
-
-/*
- * Are we allowed to skip the ServerKeyExchange message?
- *
- *  Return values are:
- *  1: Yes
- *  0: No
- */
-static inline int key_exchange_skip_allowed(SSL *s)
-{
-    long alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
-
-    /*
-     * Can't skip server key exchange if this is an ephemeral
-     * ciphersuite.
-     */
-    if (alg_k & (SSL_kDHE | SSL_kECDHE)) {
-        return 0;
-    }
-
-    return 1;
-}
-
-/*
- * client_read_transition() encapsulates the logic for the allowed handshake
- * state transitions when the client is reading messages from the server. The
- * message type that the server has sent is provided in |mt|. The current state
- * is in |s->statem.hand_state|.
- *
- *  Return values are:
- *  1: Success (transition allowed)
- *  0: Error (transition not allowed)
- */
-static int client_read_transition(SSL *s, int mt)
-{
-    STATEM *st = &s->statem;
-
-    switch(st->hand_state) {
-    case TLS_ST_CW_CLNT_HELLO:
-        if (mt == SSL3_MT_SERVER_HELLO) {
-            st->hand_state = TLS_ST_CR_SRVR_HELLO;
-            return 1;
-        }
-
-        if (SSL_IS_DTLS(s)) {
-            if (mt == DTLS1_MT_HELLO_VERIFY_REQUEST) {
-                st->hand_state = DTLS_ST_CR_HELLO_VERIFY_REQUEST;
-                return 1;
-            }
-        }
-        break;
-
-    case TLS_ST_CR_SRVR_HELLO:
-        if (s->hit) {
-            if (s->tlsext_ticket_expected) {
-                if (mt == SSL3_MT_NEWSESSION_TICKET) {
-                    st->hand_state = TLS_ST_CR_SESSION_TICKET;
-                    return 1;
-                }
-            } else if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
-                st->hand_state = TLS_ST_CR_CHANGE;
-                return 1;
-            }
-        } else {
-            if (SSL_IS_DTLS(s) && mt == DTLS1_MT_HELLO_VERIFY_REQUEST) {
-                st->hand_state = DTLS_ST_CR_HELLO_VERIFY_REQUEST;
-                return 1;
-            } else if (!(s->s3->tmp.new_cipher->algorithm_auth
-                        & (SSL_aNULL | SSL_aSRP | SSL_aPSK))) {
-                if (mt == SSL3_MT_CERTIFICATE) {
-                    st->hand_state = TLS_ST_CR_CERT;
-                    return 1;
-                }
-            } else {
-                if (mt == SSL3_MT_SERVER_KEY_EXCHANGE) {
-                    st->hand_state = TLS_ST_CR_KEY_EXCH;
-                    return 1;
-                } else if (key_exchange_skip_allowed(s)) {
-                    if (mt == SSL3_MT_CERTIFICATE_REQUEST
-                            && cert_req_allowed(s)) {
-                        st->hand_state = TLS_ST_CR_CERT_REQ;
-                        return 1;
-                    } else if (mt == SSL3_MT_SERVER_DONE) {
-                        st->hand_state = TLS_ST_CR_SRVR_DONE;
-                        return 1;
-                    }
-                }
-            }
-        }
-        break;
-
-    case TLS_ST_CR_CERT:
-        if (s->tlsext_status_expected) {
-            if (mt == SSL3_MT_CERTIFICATE_STATUS) {
-                st->hand_state = TLS_ST_CR_CERT_STATUS;
-                return 1;
-            }
-        } else {
-            if (mt == SSL3_MT_SERVER_KEY_EXCHANGE) {
-                st->hand_state = TLS_ST_CR_KEY_EXCH;
-                return 1;
-            } else if (key_exchange_skip_allowed(s)) {
-                if (mt == SSL3_MT_CERTIFICATE_REQUEST && cert_req_allowed(s)) {
-                    st->hand_state = TLS_ST_CR_CERT_REQ;
-                    return 1;
-                } else if (mt == SSL3_MT_SERVER_DONE) {
-                    st->hand_state = TLS_ST_CR_SRVR_DONE;
-                    return 1;
-                }
-            }
-        }
-        break;
-
-    case TLS_ST_CR_CERT_STATUS:
-        if (mt == SSL3_MT_SERVER_KEY_EXCHANGE) {
-            st->hand_state = TLS_ST_CR_KEY_EXCH;
-            return 1;
-        } else if (key_exchange_skip_allowed(s)) {
-            if (mt == SSL3_MT_CERTIFICATE_REQUEST && cert_req_allowed(s)) {
-                st->hand_state = TLS_ST_CR_CERT_REQ;
-                return 1;
-            } else if (mt == SSL3_MT_SERVER_DONE) {
-                st->hand_state = TLS_ST_CR_SRVR_DONE;
-                return 1;
-            }
-        }
-        break;
-
-    case TLS_ST_CR_KEY_EXCH:
-        if (mt == SSL3_MT_CERTIFICATE_REQUEST && cert_req_allowed(s)) {
-            st->hand_state = TLS_ST_CR_CERT_REQ;
-            return 1;
-        } else if (mt == SSL3_MT_SERVER_DONE) {
-            st->hand_state = TLS_ST_CR_SRVR_DONE;
-            return 1;
-        }
-        break;
-
-    case TLS_ST_CR_CERT_REQ:
-        if (mt == SSL3_MT_SERVER_DONE) {
-            st->hand_state = TLS_ST_CR_SRVR_DONE;
-            return 1;
-        }
-        break;
-
-    case TLS_ST_CW_FINISHED:
-        if (mt == SSL3_MT_NEWSESSION_TICKET && s->tlsext_ticket_expected) {
-            st->hand_state = TLS_ST_CR_SESSION_TICKET;
-            return 1;
-        } else if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
-            st->hand_state = TLS_ST_CR_CHANGE;
-            return 1;
-        }
-        break;
-
-    case TLS_ST_CR_SESSION_TICKET:
-        if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
-            st->hand_state = TLS_ST_CR_CHANGE;
-            return 1;
-        }
-        break;
-
-    case TLS_ST_CR_CHANGE:
-        if (mt == SSL3_MT_FINISHED) {
-            st->hand_state = TLS_ST_CR_FINISHED;
-            return 1;
-        }
-        break;
-
-    default:
-        break;
-    }
-
-    /* No valid transition found */
-    return 0;
-}
-
-/*
- * client_write_transition() works out what handshake state to move to next
- * when the client is writing messages to be sent to the server.
- */
-static enum WRITE_TRAN client_write_transition(SSL *s)
-{
-    STATEM *st = &s->statem;
-
-    switch(st->hand_state) {
-        case TLS_ST_OK:
-            /* Renegotiation - fall through */
-        case TLS_ST_BEFORE:
-            st->hand_state = TLS_ST_CW_CLNT_HELLO;
-            return WRITE_TRAN_CONTINUE;
-
-        case TLS_ST_CW_CLNT_HELLO:
-            /*
-             * No transition at the end of writing because we don't know what
-             * we will be sent
-             */
-            return WRITE_TRAN_FINISHED;
-
-        case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
-            st->hand_state = TLS_ST_CW_CLNT_HELLO;
-            return WRITE_TRAN_CONTINUE;
-
-        case TLS_ST_CR_SRVR_DONE:
-            if (s->s3->tmp.cert_req)
-                st->hand_state = TLS_ST_CW_CERT;
-            else
-                st->hand_state = TLS_ST_CW_KEY_EXCH;
-            return WRITE_TRAN_CONTINUE;
-
-        case TLS_ST_CW_CERT:
-            st->hand_state = TLS_ST_CW_KEY_EXCH;
-            return WRITE_TRAN_CONTINUE;
-
-        case TLS_ST_CW_KEY_EXCH:
-            /*
-             * For TLS, cert_req is set to 2, so a cert chain of nothing is
-             * sent, but no verify packet is sent
-             */
-            /*
-             * XXX: For now, we do not support client authentication in ECDH
-             * cipher suites with ECDH (rather than ECDSA) certificates. We
-             * need to skip the certificate verify message when client's
-             * ECDH public key is sent inside the client certificate.
-             */
-            if (s->s3->tmp.cert_req == 1) {
-                st->hand_state = TLS_ST_CW_CERT_VRFY;
-            } else {
-                st->hand_state = TLS_ST_CW_CHANGE;
-            }
-            if (s->s3->flags & TLS1_FLAGS_SKIP_CERT_VERIFY) {
-                st->hand_state = TLS_ST_CW_CHANGE;
-            }
-            return WRITE_TRAN_CONTINUE;
-
-        case TLS_ST_CW_CERT_VRFY:
-            st->hand_state = TLS_ST_CW_CHANGE;
-            return WRITE_TRAN_CONTINUE;
-
-        case TLS_ST_CW_CHANGE:
-#if defined(OPENSSL_NO_NEXTPROTONEG)
-            st->hand_state = TLS_ST_CW_FINISHED;
-#else
-            if (!SSL_IS_DTLS(s) && s->s3->next_proto_neg_seen)
-                st->hand_state = TLS_ST_CW_NEXT_PROTO;
-            else
-                st->hand_state = TLS_ST_CW_FINISHED;
-#endif
-            return WRITE_TRAN_CONTINUE;
-
-#if !defined(OPENSSL_NO_NEXTPROTONEG)
-        case TLS_ST_CW_NEXT_PROTO:
-            st->hand_state = TLS_ST_CW_FINISHED;
-            return WRITE_TRAN_CONTINUE;
-#endif
-
-        case TLS_ST_CW_FINISHED:
-            if (s->hit) {
-                st->hand_state = TLS_ST_OK;
-                statem_set_in_init(s, 0);
-                return WRITE_TRAN_CONTINUE;
-            } else {
-                return WRITE_TRAN_FINISHED;
-            }
-
-        case TLS_ST_CR_FINISHED:
-            if (s->hit) {
-                st->hand_state = TLS_ST_CW_CHANGE;
-                return WRITE_TRAN_CONTINUE;
-            } else {
-                st->hand_state = TLS_ST_OK;
-                statem_set_in_init(s, 0);
-                return WRITE_TRAN_CONTINUE;
-            }
-
-        default:
-            /* Shouldn't happen */
-            return WRITE_TRAN_ERROR;
-    }
-}
-
-/*
- * Perform any pre work that needs to be done prior to sending a message from
- * the client to the server.
- */
-static enum WORK_STATE client_pre_work(SSL *s, enum WORK_STATE wst)
-{
-    STATEM *st = &s->statem;
-
-    switch(st->hand_state) {
-    case TLS_ST_CW_CLNT_HELLO:
-        s->shutdown = 0;
-        if (SSL_IS_DTLS(s)) {
-            /* every DTLS ClientHello resets Finished MAC */
-            ssl3_init_finished_mac(s);
-        }
-        break;
-
-    case TLS_ST_CW_CERT:
-        return tls_prepare_client_certificate(s, wst);
-
-    case TLS_ST_CW_CHANGE:
-        if (SSL_IS_DTLS(s)) {
-            if (s->hit) {
-                /*
-                 * We're into the last flight so we don't retransmit these
-                 * messages unless we need to.
-                 */
-                st->use_timer = 0;
-            }
-#ifndef OPENSSL_NO_SCTP
-            if (BIO_dgram_is_sctp(SSL_get_wbio(s)))
-                return dtls_wait_for_dry(s);
-#endif
-        }
-        return WORK_FINISHED_CONTINUE;
-
-    case TLS_ST_OK:
-        return tls_finish_handshake(s, wst);
-
-    default:
-        /* No pre work to be done */
-        break;
-    }
-
-    return WORK_FINISHED_CONTINUE;
-}
-
-/*
- * Perform any work that needs to be done after sending a message from the
- * client to the server.
- */
-static enum WORK_STATE client_post_work(SSL *s, enum WORK_STATE wst)
-{
-    STATEM *st = &s->statem;
-
-    s->init_num = 0;
-
-    switch(st->hand_state) {
-    case TLS_ST_CW_CLNT_HELLO:
-        if (SSL_IS_DTLS(s) && s->d1->cookie_len > 0 && statem_flush(s) != 1)
-            return WORK_MORE_A;
-#ifndef OPENSSL_NO_SCTP
-        /* Disable buffering for SCTP */
-        if (!SSL_IS_DTLS(s) || !BIO_dgram_is_sctp(SSL_get_wbio(s))) {
-#endif
-            /*
-             * turn on buffering for the next lot of output
-             */
-            if (s->bbio != s->wbio)
-                s->wbio = BIO_push(s->bbio, s->wbio);
-#ifndef OPENSSL_NO_SCTP
-            }
-#endif
-        if (SSL_IS_DTLS(s)) {
-            /* Treat the next message as the first packet */
-            s->first_packet = 1;
-        }
-        break;
-
-    case TLS_ST_CW_KEY_EXCH:
-        if (tls_client_key_exchange_post_work(s) == 0)
-            return WORK_ERROR;
-        break;
-
-    case TLS_ST_CW_CHANGE:
-        s->session->cipher = s->s3->tmp.new_cipher;
-#ifdef OPENSSL_NO_COMP
-        s->session->compress_meth = 0;
-#else
-        if (s->s3->tmp.new_compression == NULL)
-            s->session->compress_meth = 0;
-        else
-            s->session->compress_meth = s->s3->tmp.new_compression->id;
-#endif
-        if (!s->method->ssl3_enc->setup_key_block(s))
-            return WORK_ERROR;
-
-        if (!s->method->ssl3_enc->change_cipher_state(s,
-                                                      SSL3_CHANGE_CIPHER_CLIENT_WRITE))
-            return WORK_ERROR;
-
-        if (SSL_IS_DTLS(s)) {
-#ifndef OPENSSL_NO_SCTP
-            if (s->hit) {
-                /*
-                 * Change to new shared key of SCTP-Auth, will be ignored if
-                 * no SCTP used.
-                 */
-                BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY,
-                         0, NULL);
-            }
-#endif
-
-            dtls1_reset_seq_numbers(s, SSL3_CC_WRITE);
-        }
-        break;
-
-    case TLS_ST_CW_FINISHED:
-#ifndef OPENSSL_NO_SCTP
-        if (wst == WORK_MORE_A && SSL_IS_DTLS(s) && s->hit == 0) {
-            /*
-             * Change to new shared key of SCTP-Auth, will be ignored if
-             * no SCTP used.
-             */
-            BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY,
-                     0, NULL);
-        }
-#endif
-        if (statem_flush(s) != 1)
-            return WORK_MORE_B;
-
-        if (s->hit && tls_finish_handshake(s, WORK_MORE_A) != 1)
-                return WORK_ERROR;
-        break;
-
-    default:
-        /* No post work to be done */
-        break;
-    }
-
-    return WORK_FINISHED_CONTINUE;
-}
-
-/*
- * Construct a message to be sent from the client to the server.
- *
- * Valid return values are:
- *   1: Success
- *   0: Error
- */
-static int client_construct_message(SSL *s)
-{
-    STATEM *st = &s->statem;
-
-    switch(st->hand_state) {
-    case TLS_ST_CW_CLNT_HELLO:
-        return tls_construct_client_hello(s);
-
-    case TLS_ST_CW_CERT:
-        return tls_construct_client_certificate(s);
-
-    case TLS_ST_CW_KEY_EXCH:
-        return tls_construct_client_key_exchange(s);
-
-    case TLS_ST_CW_CERT_VRFY:
-        return tls_construct_client_verify(s);
-
-    case TLS_ST_CW_CHANGE:
-        if (SSL_IS_DTLS(s))
-            return dtls_construct_change_cipher_spec(s);
-        else
-            return tls_construct_change_cipher_spec(s);
-
-#if !defined(OPENSSL_NO_NEXTPROTONEG)
-    case TLS_ST_CW_NEXT_PROTO:
-        return tls_construct_next_proto(s);
-#endif
-    case TLS_ST_CW_FINISHED:
-        return tls_construct_finished(s,
-                                      s->method->
-                                      ssl3_enc->client_finished_label,
-                                      s->method->
-                                      ssl3_enc->client_finished_label_len);
-
-    default:
-        /* Shouldn't happen */
-        break;
-    }
-
-    return 0;
-}
-
-/* The spec allows for a longer length than this, but we limit it */
-#define HELLO_VERIFY_REQUEST_MAX_LENGTH 258
-#define SERVER_HELLO_MAX_LENGTH         20000
-#define SERVER_KEY_EXCH_MAX_LENGTH      102400
-#define SERVER_HELLO_DONE_MAX_LENGTH    0
-#define CCS_MAX_LENGTH                  1
-/* Max should actually be 36 but we are generous */
-#define FINISHED_MAX_LENGTH             64
-
-/*
- * Returns the maximum allowed length for the current message that we are
- * reading. Excludes the message header.
- */
-static unsigned long client_max_message_size(SSL *s)
-{
-    STATEM *st = &s->statem;
-
-    switch(st->hand_state) {
-        case TLS_ST_CR_SRVR_HELLO:
-            return SERVER_HELLO_MAX_LENGTH;
-
-        case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
-            return HELLO_VERIFY_REQUEST_MAX_LENGTH;
-
-        case TLS_ST_CR_CERT:
-            return s->max_cert_list;
-
-        case TLS_ST_CR_CERT_STATUS:
-            return SSL3_RT_MAX_PLAIN_LENGTH;
-
-        case TLS_ST_CR_KEY_EXCH:
-            return SERVER_KEY_EXCH_MAX_LENGTH;
-
-        case TLS_ST_CR_CERT_REQ:
-            return SSL3_RT_MAX_PLAIN_LENGTH;
-
-        case TLS_ST_CR_SRVR_DONE:
-            return SERVER_HELLO_DONE_MAX_LENGTH;
-
-        case TLS_ST_CR_CHANGE:
-            return CCS_MAX_LENGTH;
-
-        case TLS_ST_CR_SESSION_TICKET:
-            return SSL3_RT_MAX_PLAIN_LENGTH;
-
-        case TLS_ST_CR_FINISHED:
-            return FINISHED_MAX_LENGTH;
-
-        default:
-            /* Shouldn't happen */
-            break;
-    }
-
-    return 0;
-}
-
-/*
- * Process a message that the client has been received from the server.
- */
-static enum MSG_PROCESS_RETURN client_process_message(SSL *s, PACKET *pkt)
-{
-    STATEM *st = &s->statem;
-
-    switch(st->hand_state) {
-        case TLS_ST_CR_SRVR_HELLO:
-            return tls_process_server_hello(s, pkt);
-
-        case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
-            return dtls_process_hello_verify(s, pkt);
-
-        case TLS_ST_CR_CERT:
-            return tls_process_server_certificate(s, pkt);
-
-        case TLS_ST_CR_CERT_STATUS:
-            return tls_process_cert_status(s, pkt);
-
-        case TLS_ST_CR_KEY_EXCH:
-            return tls_process_key_exchange(s, pkt);
-
-        case TLS_ST_CR_CERT_REQ:
-            return tls_process_certificate_request(s, pkt);
-
-        case TLS_ST_CR_SRVR_DONE:
-            return tls_process_server_done(s, pkt);
-
-        case TLS_ST_CR_CHANGE:
-            return tls_process_change_cipher_spec(s, pkt);
-
-        case TLS_ST_CR_SESSION_TICKET:
-            return tls_process_new_session_ticket(s, pkt);
-
-        case TLS_ST_CR_FINISHED:
-            return tls_process_finished(s, pkt);
-
-        default:
-            /* Shouldn't happen */
-            break;
-    }
-
-    return MSG_PROCESS_ERROR;
-}
-
-/*
- * Perform any further processing required following the receipt of a message
- * from the server
- */
-static enum WORK_STATE client_post_process_message(SSL *s, enum WORK_STATE wst)
-{
-    STATEM *st = &s->statem;
-
-    switch(st->hand_state) {
-#ifndef OPENSSL_NO_SCTP
-    case TLS_ST_CR_SRVR_DONE:
-        /* We only get here if we are using SCTP and we are renegotiating */
-        if (BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s))) {
-            s->s3->in_read_app_data = 2;
-            s->rwstate = SSL_READING;
-            BIO_clear_retry_flags(SSL_get_rbio(s));
-            BIO_set_retry_read(SSL_get_rbio(s));
-            statem_set_sctp_read_sock(s, 1);
-            return WORK_MORE_A;
-        }
-        statem_set_sctp_read_sock(s, 0);
-        return WORK_FINISHED_STOP;
-#endif
-
-    case TLS_ST_CR_FINISHED:
-        if (!s->hit)
-            return tls_finish_handshake(s, wst);
-        else
-            return WORK_FINISHED_STOP;
-    default:
-        break;
-    }
-
-    /* Shouldn't happen */
-    return WORK_ERROR;
-}
-
-
-/*
- * server_read_transition() encapsulates the logic for the allowed handshake
- * state transitions when the server is reading messages from the client. The
- * message type that the client has sent is provided in |mt|. The current state
- * is in |s->statem.hand_state|.
- *
- *  Valid return values are:
- *  1: Success (transition allowed)
- *  0: Error (transition not allowed)
- */
-static int server_read_transition(SSL *s, int mt)
-{
-    STATEM *st = &s->statem;
-
-    switch(st->hand_state) {
-    case TLS_ST_BEFORE:
-    case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
-        if (mt == SSL3_MT_CLIENT_HELLO) {
-            st->hand_state = TLS_ST_SR_CLNT_HELLO;
-            return 1;
-        }
-        break;
-
-    case TLS_ST_SW_SRVR_DONE:
-        /*
-         * If we get a CKE message after a ServerDone then either
-         * 1) We didn't request a Certificate
-         * OR
-         * 2) If we did request one then
-         *      a) We allow no Certificate to be returned
-         *      AND
-         *      b) We are running SSL3 (in TLS1.0+ the client must return a 0
-         *         list if we requested a certificate)
-         */
-        if (mt == SSL3_MT_CLIENT_KEY_EXCHANGE
-                && (!s->s3->tmp.cert_request
-                    || (!((s->verify_mode & SSL_VERIFY_PEER) &&
-                          (s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT))
-                        && (s->version == SSL3_VERSION)))) {
-            st->hand_state = TLS_ST_SR_KEY_EXCH;
-            return 1;
-        } else if (s->s3->tmp.cert_request) {
-            if (mt == SSL3_MT_CERTIFICATE) {
-                st->hand_state = TLS_ST_SR_CERT;
-                return 1;
-            } 
-        }
-        break;
-
-    case TLS_ST_SR_CERT:
-        if (mt == SSL3_MT_CLIENT_KEY_EXCHANGE) {
-            st->hand_state = TLS_ST_SR_KEY_EXCH;
-            return 1;
-        }
-        break;
-
-    case TLS_ST_SR_KEY_EXCH:
-        /*
-         * We should only process a CertificateVerify message if we have
-         * received a Certificate from the client. If so then |s->session->peer|
-         * will be non NULL. In some instances a CertificateVerify message is
-         * not required even if the peer has sent a Certificate (e.g. such as in
-         * the case of static DH). In that case |s->no_cert_verify| should be
-         * set.
-         */
-        if (s->session->peer == NULL || s->no_cert_verify) {
-            if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
-                /*
-                 * For the ECDH ciphersuites when the client sends its ECDH
-                 * pub key in a certificate, the CertificateVerify message is
-                 * not sent. Also for GOST ciphersuites when the client uses
-                 * its key from the certificate for key exchange.
-                 */
-                st->hand_state = TLS_ST_SR_CHANGE;
-                return 1;
-            }
-        } else {
-            if (mt == SSL3_MT_CERTIFICATE_VERIFY) {
-                st->hand_state = TLS_ST_SR_CERT_VRFY;
-                return 1;
-            }
-        }
-        break;
-
-    case TLS_ST_SR_CERT_VRFY:
-        if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
-            st->hand_state = TLS_ST_SR_CHANGE;
-            return 1;
-        }
-        break;
-
-    case TLS_ST_SR_CHANGE:
-#ifndef OPENSSL_NO_NEXTPROTONEG
-        if (s->s3->next_proto_neg_seen) {
-            if (mt == SSL3_MT_NEXT_PROTO) {
-                st->hand_state = TLS_ST_SR_NEXT_PROTO;
-                return 1;
-            }
-        } else {
-#endif
-            if (mt == SSL3_MT_FINISHED) {
-                st->hand_state = TLS_ST_SR_FINISHED;
-                return 1;
-            }
-#ifndef OPENSSL_NO_NEXTPROTONEG
-        }
-#endif
-        break;
-
-#ifndef OPENSSL_NO_NEXTPROTONEG
-    case TLS_ST_SR_NEXT_PROTO:
-        if (mt == SSL3_MT_FINISHED) {
-            st->hand_state = TLS_ST_SR_FINISHED;
-            return 1;
-        }
-        break;
-#endif
-
-    case TLS_ST_SW_FINISHED:
-        if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
-            st->hand_state = TLS_ST_SR_CHANGE;
-            return 1;
-        }
-        break;
-
-    default:
-        break;
-    }
-
-    /* No valid transition found */
-    return 0;
-}
-
-/*
- * Should we send a ServerKeyExchange message?
- *
- * Valid return values are:
- *   1: Yes
- *   0: No
- */
-static inline int send_server_key_exchange(SSL *s)
-{
-    unsigned long alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
-
-    /*
-     * only send a ServerKeyExchange if DH, fortezza or RSA but we have a
-     * sign only certificate PSK: may send PSK identity hints For
-     * ECC ciphersuites, we send a serverKeyExchange message only if
-     * the cipher suite is either ECDH-anon or ECDHE. In other cases,
-     * the server certificate contains the server's public key for
-     * key exchange.
-     */
-    if (   (alg_k & SSL_kDHE)
-        || (alg_k & SSL_kECDHE)
-        || ((alg_k & SSL_kRSA)
-            && (s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey == NULL
-                || (SSL_C_IS_EXPORT(s->s3->tmp.new_cipher)
-                    && EVP_PKEY_size(s->cert->pkeys
-                                     [SSL_PKEY_RSA_ENC].privatekey) *
-                    8 > SSL_C_EXPORT_PKEYLENGTH(s->s3->tmp.new_cipher)
-                   )
-               )
-           )
-        /*
-         * PSK: send ServerKeyExchange if PSK identity hint if
-         * provided
-         */
-#ifndef OPENSSL_NO_PSK
-        /* Only send SKE if we have identity hint for plain PSK */
-        || ((alg_k & (SSL_kPSK | SSL_kRSAPSK))
-            && s->cert->psk_identity_hint)
-        /* For other PSK always send SKE */
-        || (alg_k & (SSL_PSK & (SSL_kDHEPSK | SSL_kECDHEPSK)))
-#endif
-#ifndef OPENSSL_NO_SRP
-        /* SRP: send ServerKeyExchange */
-        || (alg_k & SSL_kSRP)
-#endif
-       ) {
-        return 1;
-    }
-
-    return 0;
-}
-
-/*
- * Should we send a CertificateRequest message?
- *
- * Valid return values are:
- *   1: Yes
- *   0: No
- */
-static inline int send_certificate_request(SSL *s)
-{
-    if (
-           /* don't request cert unless asked for it: */
-           s->verify_mode & SSL_VERIFY_PEER
-           /*
-            * if SSL_VERIFY_CLIENT_ONCE is set, don't request cert
-            * during re-negotiation:
-            */
-           && ((s->session->peer == NULL) ||
-               !(s->verify_mode & SSL_VERIFY_CLIENT_ONCE))
-           /*
-            * never request cert in anonymous ciphersuites (see
-            * section "Certificate request" in SSL 3 drafts and in
-            * RFC 2246):
-            */
-           && (!(s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL)
-           /*
-            * ... except when the application insists on
-            * verification (against the specs, but s3_clnt.c accepts
-            * this for SSL 3)
-            */
-               || (s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT))
-           /* don't request certificate for SRP auth */
-           && !(s->s3->tmp.new_cipher->algorithm_auth & SSL_aSRP)
-           /*
-            * With normal PSK Certificates and Certificate Requests
-            * are omitted
-            */
-           && !(s->s3->tmp.new_cipher->algorithm_mkey & SSL_PSK)) {
-        return 1;
-    }
-
-    return 0;
-}
-
-/*
- * server_write_transition() works out what handshake state to move to next
- * when the server is writing messages to be sent to the client.
- */
-static enum WRITE_TRAN server_write_transition(SSL *s)
-{
-    STATEM *st = &s->statem;
-
-    switch(st->hand_state) {
-        case TLS_ST_BEFORE:
-            /* Just go straight to trying to read from the client */;
-            return WRITE_TRAN_FINISHED;
-
-        case TLS_ST_OK:
-            /* We must be trying to renegotiate */
-            st->hand_state = TLS_ST_SW_HELLO_REQ;
-            return WRITE_TRAN_CONTINUE;
-
-        case TLS_ST_SW_HELLO_REQ:
-            st->hand_state = TLS_ST_OK;
-            statem_set_in_init(s, 0);
-            return WRITE_TRAN_CONTINUE;
-
-        case TLS_ST_SR_CLNT_HELLO:
-            if (SSL_IS_DTLS(s) && !s->d1->cookie_verified
-                    && (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE))
-                st->hand_state = DTLS_ST_SW_HELLO_VERIFY_REQUEST;
-            else
-                st->hand_state = TLS_ST_SW_SRVR_HELLO;
-            return WRITE_TRAN_CONTINUE;
-
-        case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
-            return WRITE_TRAN_FINISHED;
-
-        case TLS_ST_SW_SRVR_HELLO:
-            if (s->hit) {
-                if (s->tlsext_ticket_expected)
-                    st->hand_state = TLS_ST_SW_SESSION_TICKET;
-                else
-                    st->hand_state = TLS_ST_SW_CHANGE;
-            } else {
-                /* Check if it is anon DH or anon ECDH, */
-                /* normal PSK or SRP */
-                if (!(s->s3->tmp.new_cipher->algorithm_auth &
-                     (SSL_aNULL | SSL_aSRP | SSL_aPSK))) {
-                    st->hand_state = TLS_ST_SW_CERT;
-                } else if (send_server_key_exchange(s)) {
-                    st->hand_state = TLS_ST_SW_KEY_EXCH;
-                } else if (send_certificate_request(s)) {
-                    st->hand_state = TLS_ST_SW_CERT_REQ;
-                } else {
-                    st->hand_state = TLS_ST_SW_SRVR_DONE;
-                }
-            }
-            return WRITE_TRAN_CONTINUE;
-
-        case TLS_ST_SW_CERT:
-            if (s->tlsext_status_expected) {
-                st->hand_state = TLS_ST_SW_CERT_STATUS;
-                return WRITE_TRAN_CONTINUE;
-            }
-            /* Fall through */
-
-        case TLS_ST_SW_CERT_STATUS:
-            if (send_server_key_exchange(s)) {
-                st->hand_state = TLS_ST_SW_KEY_EXCH;
-                return WRITE_TRAN_CONTINUE;
-            }
-            /* Fall through */
-
-        case TLS_ST_SW_KEY_EXCH:
-            if (send_certificate_request(s)) {
-                st->hand_state = TLS_ST_SW_CERT_REQ;
-                return WRITE_TRAN_CONTINUE;
-            }
-            /* Fall through */
-
-        case TLS_ST_SW_CERT_REQ:
-            st->hand_state = TLS_ST_SW_SRVR_DONE;
-            return WRITE_TRAN_CONTINUE;
-
-        case TLS_ST_SW_SRVR_DONE:
-            return WRITE_TRAN_FINISHED;
-
-        case TLS_ST_SR_FINISHED:
-            if (s->hit) {
-                st->hand_state = TLS_ST_OK;
-                statem_set_in_init(s, 0);
-                return WRITE_TRAN_CONTINUE;
-            } else if (s->tlsext_ticket_expected) {
-                st->hand_state = TLS_ST_SW_SESSION_TICKET;
-            } else {
-                st->hand_state = TLS_ST_SW_CHANGE;
-            }
-            return WRITE_TRAN_CONTINUE;
-
-        case TLS_ST_SW_SESSION_TICKET:
-            st->hand_state = TLS_ST_SW_CHANGE;
-            return WRITE_TRAN_CONTINUE;
-
-        case TLS_ST_SW_CHANGE:
-            st->hand_state = TLS_ST_SW_FINISHED;
-            return WRITE_TRAN_CONTINUE;
-
-        case TLS_ST_SW_FINISHED:
-            if (s->hit) {
-                return WRITE_TRAN_FINISHED;
-            }
-            st->hand_state = TLS_ST_OK;
-            statem_set_in_init(s, 0);
-            return WRITE_TRAN_CONTINUE;
-
-        default:
-            /* Shouldn't happen */
-            return WRITE_TRAN_ERROR;
-    }
-}
-
-/*
- * Perform any pre work that needs to be done prior to sending a message from
- * the server to the client.
- */
-static enum WORK_STATE server_pre_work(SSL *s, enum WORK_STATE wst)
-{
-    STATEM *st = &s->statem;
-
-    switch(st->hand_state) {
-    case TLS_ST_SW_HELLO_REQ:
-        s->shutdown = 0;
-        if (SSL_IS_DTLS(s))
-            dtls1_clear_record_buffer(s);
-        break;
-
-    case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
-        s->shutdown = 0;
-        if (SSL_IS_DTLS(s)) {
-            dtls1_clear_record_buffer(s);
-            /* We don't buffer this message so don't use the timer */
-            st->use_timer = 0;
-        }
-        break;
-
-    case TLS_ST_SW_SRVR_HELLO:
-        if (SSL_IS_DTLS(s)) {
-            /*
-             * Messages we write from now on should be bufferred and
-             * retransmitted if necessary, so we need to use the timer now
-             */
-            st->use_timer = 1;
-        }
-        break;
-
-    case TLS_ST_SW_SRVR_DONE:
-#ifndef OPENSSL_NO_SCTP
-        if (SSL_IS_DTLS(s) && BIO_dgram_is_sctp(SSL_get_wbio(s)))
-            return dtls_wait_for_dry(s);
-#endif
-        return WORK_FINISHED_CONTINUE;
-
-    case TLS_ST_SW_SESSION_TICKET:
-        if (SSL_IS_DTLS(s)) {
-            /*
-             * We're into the last flight. We don't retransmit the last flight
-             * unless we need to, so we don't use the timer
-             */
-            st->use_timer = 0;
-        }
-        break;
-
-    case TLS_ST_SW_CHANGE:
-        s->session->cipher = s->s3->tmp.new_cipher;
-        if (!s->method->ssl3_enc->setup_key_block(s)) {
-            statem_set_error(s);
-            return WORK_ERROR;
-        }
-        if (SSL_IS_DTLS(s)) {
-            /*
-             * We're into the last flight. We don't retransmit the last flight
-             * unless we need to, so we don't use the timer. This might have
-             * already been set to 0 if we sent a NewSessionTicket message,
-             * but we'll set it again here in case we didn't.
-             */
-            st->use_timer = 0;
-        }
-        return WORK_FINISHED_CONTINUE;
-
-    case TLS_ST_OK:
-        return tls_finish_handshake(s, wst);
-
-    default:
-        /* No pre work to be done */
-        break;
-    }
-
-    return WORK_FINISHED_CONTINUE;
-}
-
-/*
- * Perform any work that needs to be done after sending a message from the
- * server to the client.
- */
-static enum WORK_STATE server_post_work(SSL *s, enum WORK_STATE wst)
-{
-    STATEM *st = &s->statem;
-
-    s->init_num = 0;
-
-    switch(st->hand_state) {
-    case TLS_ST_SW_HELLO_REQ:
-        if (statem_flush(s) != 1)
-            return WORK_MORE_A;
-        ssl3_init_finished_mac(s);
-        break;
-
-    case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
-        if (statem_flush(s) != 1)
-            return WORK_MORE_A;
-        /* HelloVerifyRequest resets Finished MAC */
-        if (s->version != DTLS1_BAD_VER)
-            ssl3_init_finished_mac(s);
-        /*
-         * The next message should be another ClientHello which we need to
-         * treat like it was the first packet
-         */
-        s->first_packet = 1;
-        break;
-
-    case TLS_ST_SW_SRVR_HELLO:
-#ifndef OPENSSL_NO_SCTP
-        if (SSL_IS_DTLS(s) && s->hit) {
-            unsigned char sctpauthkey[64];
-            char labelbuffer[sizeof(DTLS1_SCTP_AUTH_LABEL)];
-
-            /*
-             * Add new shared key for SCTP-Auth, will be ignored if no
-             * SCTP used.
-             */
-            snprintf((char *)labelbuffer, sizeof(DTLS1_SCTP_AUTH_LABEL),
-                     DTLS1_SCTP_AUTH_LABEL);
-
-            if (SSL_export_keying_material(s, sctpauthkey,
-                    sizeof(sctpauthkey), labelbuffer,
-                    sizeof(labelbuffer), NULL, 0, 0) <= 0) {
-                statem_set_error(s);
-                return WORK_ERROR;
-            }
-
-            BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY,
-                     sizeof(sctpauthkey), sctpauthkey);
-        }
-#endif
-        break;
-
-    case TLS_ST_SW_CHANGE:
-#ifndef OPENSSL_NO_SCTP
-        if (SSL_IS_DTLS(s) && !s->hit) {
-            /*
-             * Change to new shared key of SCTP-Auth, will be ignored if
-             * no SCTP used.
-             */
-            BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY,
-                     0, NULL);
-        }
-#endif
-        if (!s->method->ssl3_enc->change_cipher_state(s,
-                SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
-            statem_set_error(s);
-            return WORK_ERROR;
-        }
-
-        if (SSL_IS_DTLS(s))
-            dtls1_reset_seq_numbers(s, SSL3_CC_WRITE);
-        break;
-
-    case TLS_ST_SW_SRVR_DONE:
-        if (statem_flush(s) != 1)
-            return WORK_MORE_A;
-        break;
-
-    case TLS_ST_SW_FINISHED:
-        if (statem_flush(s) != 1)
-            return WORK_MORE_A;
-#ifndef OPENSSL_NO_SCTP
-        if (SSL_IS_DTLS(s) && s->hit) {
-            /*
-             * Change to new shared key of SCTP-Auth, will be ignored if
-             * no SCTP used.
-             */
-            BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY,
-                     0, NULL);
-        }
-#endif
-        break;
-
-    default:
-        /* No post work to be done */
-        break;
-    }
-
-    return WORK_FINISHED_CONTINUE;
-}
-
-/*
- * Construct a message to be sent from the server to the client.
- *
- * Valid return values are:
- *   1: Success
- *   0: Error
- */
-static int server_construct_message(SSL *s)
-{
-    STATEM *st = &s->statem;
-
-    switch(st->hand_state) {
-    case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
-        return dtls_construct_hello_verify_request(s);
-
-    case TLS_ST_SW_HELLO_REQ:
-        return tls_construct_hello_request(s);
-
-    case TLS_ST_SW_SRVR_HELLO:
-        return tls_construct_server_hello(s);
-
-    case TLS_ST_SW_CERT:
-        return tls_construct_server_certificate(s);
-
-    case TLS_ST_SW_KEY_EXCH:
-        return tls_construct_server_key_exchange(s);
-
-    case TLS_ST_SW_CERT_REQ:
-        return tls_construct_certificate_request(s);
-
-    case TLS_ST_SW_SRVR_DONE:
-        return tls_construct_server_done(s);
-
-    case TLS_ST_SW_SESSION_TICKET:
-        return tls_construct_new_session_ticket(s);
-
-    case TLS_ST_SW_CERT_STATUS:
-        return tls_construct_cert_status(s);
-
-    case TLS_ST_SW_CHANGE:
-        if (SSL_IS_DTLS(s))
-            return dtls_construct_change_cipher_spec(s);
-        else
-            return tls_construct_change_cipher_spec(s);
-
-    case TLS_ST_SW_FINISHED:
-        return tls_construct_finished(s,
-                                      s->method->
-                                      ssl3_enc->server_finished_label,
-                                      s->method->
-                                      ssl3_enc->server_finished_label_len);
-
-    default:
-        /* Shouldn't happen */
-        break;
-    }
-
-    return 0;
-}
-
-#define CLIENT_KEY_EXCH_MAX_LENGTH      2048
-#define NEXT_PROTO_MAX_LENGTH           514
-
-/*
- * Returns the maximum allowed length for the current message that we are
- * reading. Excludes the message header.
- */
-static unsigned long server_max_message_size(SSL *s)
-{
-    STATEM *st = &s->statem;
-
-    switch(st->hand_state) {
-    case TLS_ST_SR_CLNT_HELLO:
-        return SSL3_RT_MAX_PLAIN_LENGTH;
-
-    case TLS_ST_SR_CERT:
-        return s->max_cert_list;
-
-    case TLS_ST_SR_KEY_EXCH:
-        return CLIENT_KEY_EXCH_MAX_LENGTH;
-
-    case TLS_ST_SR_CERT_VRFY:
-        return SSL3_RT_MAX_PLAIN_LENGTH;
-
-#ifndef OPENSSL_NO_NEXTPROTONEG
-    case TLS_ST_SR_NEXT_PROTO:
-        return NEXT_PROTO_MAX_LENGTH;
-#endif
-
-    case TLS_ST_SR_CHANGE:
-        return CCS_MAX_LENGTH;
-
-    case TLS_ST_SR_FINISHED:
-        return FINISHED_MAX_LENGTH;
-
-    default:
-        /* Shouldn't happen */
-        break;
-    }
-
-    return 0;
-}
-
-/*
- * Process a message that the server has received from the client.
- */
-static enum MSG_PROCESS_RETURN  server_process_message(SSL *s, PACKET *pkt)
-{
-    STATEM *st = &s->statem;
-
-    switch(st->hand_state) {
-    case TLS_ST_SR_CLNT_HELLO:
-        return tls_process_client_hello(s, pkt);
-
-    case TLS_ST_SR_CERT:
-        return tls_process_client_certificate(s, pkt);
-
-    case TLS_ST_SR_KEY_EXCH:
-        return tls_process_client_key_exchange(s, pkt);
-
-    case TLS_ST_SR_CERT_VRFY:
-        return tls_process_cert_verify(s, pkt);
-
-#ifndef OPENSSL_NO_NEXTPROTONEG
-    case TLS_ST_SR_NEXT_PROTO:
-        return tls_process_next_proto(s, pkt);
-#endif
-
-    case TLS_ST_SR_CHANGE:
-        return tls_process_change_cipher_spec(s, pkt);
-
-    case TLS_ST_SR_FINISHED:
-        return tls_process_finished(s, pkt);
-
-    default:
-        /* Shouldn't happen */
-        break;
-    }
-
-    return MSG_PROCESS_ERROR;
-}
-
-/*
- * Perform any further processing required following the receipt of a message
- * from the client
- */
-static enum WORK_STATE server_post_process_message(SSL *s, enum WORK_STATE wst)
-{
-    STATEM *st = &s->statem;
-
-    switch(st->hand_state) {
-    case TLS_ST_SR_CLNT_HELLO:
-        return tls_post_process_client_hello(s, wst);
-
-    case TLS_ST_SR_KEY_EXCH:
-        return tls_post_process_client_key_exchange(s, wst);
-
-    case TLS_ST_SR_CERT_VRFY:
-#ifndef OPENSSL_NO_SCTP
-        if (    /* Is this SCTP? */
-                BIO_dgram_is_sctp(SSL_get_wbio(s))
-                /* Are we renegotiating? */
-                && s->renegotiate
-                && BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s))) {
-            s->s3->in_read_app_data = 2;
-            s->rwstate = SSL_READING;
-            BIO_clear_retry_flags(SSL_get_rbio(s));
-            BIO_set_retry_read(SSL_get_rbio(s));
-            statem_set_sctp_read_sock(s, 1);
-            return WORK_MORE_A;
-        } else {
-            statem_set_sctp_read_sock(s, 0);
-        }
-#endif
-        return WORK_FINISHED_CONTINUE;
-
-
-    case TLS_ST_SR_FINISHED:
-        if (s->hit)
-            return tls_finish_handshake(s, wst);
-        else
-            return WORK_FINISHED_STOP;
-    default:
-        break;
-    }
-
-    /* Shouldn't happen */
-    return WORK_ERROR;
-}
index b6256f9..873ed0e 100644 (file)
@@ -86,14 +86,6 @@ enum WRITE_TRAN {
     WRITE_TRAN_FINISHED
 };
 
-/* Message processing return codes */
-enum MSG_PROCESS_RETURN {
-    MSG_PROCESS_ERROR,
-    MSG_PROCESS_FINISHED_READING,
-    MSG_PROCESS_CONTINUE_PROCESSING,
-    MSG_PROCESS_CONTINUE_READING
-};
-
 /* Message flow states */
 enum MSG_FLOW_STATE {
     /* No handshake in progress */
index 3ff4276..c68eecf 100644 (file)
 
 #include <stdio.h>
 #include "../ssl_locl.h"
+#include "statem_locl.h"
 #include <openssl/buffer.h>
 #include <openssl/rand.h>
 #include <openssl/objects.h>
 # include <openssl/engine.h>
 #endif
 
+static inline int cert_req_allowed(SSL *s);
+static inline int key_exchange_skip_allowed(SSL *s);
 static int ssl_set_version(SSL *s);
 static int ca_dn_cmp(const X509_NAME *const *a, const X509_NAME *const *b);
 static int ssl_cipher_list_to_bytes(SSL *s, STACK_OF(SSL_CIPHER) *sk,
                                     unsigned char *p);
 
+
+/*
+ * Is a CertificateRequest message allowed at the moment or not?
+ *
+ *  Return values are:
+ *  1: Yes
+ *  0: No
+ */
+static inline int cert_req_allowed(SSL *s)
+{
+    /* TLS does not like anon-DH with client cert */
+    if (s->version > SSL3_VERSION
+            && (s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL))
+        return 0;
+
+    return 1;
+}
+
+/*
+ * Are we allowed to skip the ServerKeyExchange message?
+ *
+ *  Return values are:
+ *  1: Yes
+ *  0: No
+ */
+static inline int key_exchange_skip_allowed(SSL *s)
+{
+    long alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
+
+    /*
+     * Can't skip server key exchange if this is an ephemeral
+     * ciphersuite.
+     */
+    if (alg_k & (SSL_kDHE | SSL_kECDHE)) {
+        return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * client_read_transition() encapsulates the logic for the allowed handshake
+ * state transitions when the client is reading messages from the server. The
+ * message type that the server has sent is provided in |mt|. The current state
+ * is in |s->statem.hand_state|.
+ *
+ *  Return values are:
+ *  1: Success (transition allowed)
+ *  0: Error (transition not allowed)
+ */
+int client_read_transition(SSL *s, int mt)
+{
+    STATEM *st = &s->statem;
+
+    switch(st->hand_state) {
+    case TLS_ST_CW_CLNT_HELLO:
+        if (mt == SSL3_MT_SERVER_HELLO) {
+            st->hand_state = TLS_ST_CR_SRVR_HELLO;
+            return 1;
+        }
+
+        if (SSL_IS_DTLS(s)) {
+            if (mt == DTLS1_MT_HELLO_VERIFY_REQUEST) {
+                st->hand_state = DTLS_ST_CR_HELLO_VERIFY_REQUEST;
+                return 1;
+            }
+        }
+        break;
+
+    case TLS_ST_CR_SRVR_HELLO:
+        if (s->hit) {
+            if (s->tlsext_ticket_expected) {
+                if (mt == SSL3_MT_NEWSESSION_TICKET) {
+                    st->hand_state = TLS_ST_CR_SESSION_TICKET;
+                    return 1;
+                }
+            } else if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
+                st->hand_state = TLS_ST_CR_CHANGE;
+                return 1;
+            }
+        } else {
+            if (SSL_IS_DTLS(s) && mt == DTLS1_MT_HELLO_VERIFY_REQUEST) {
+                st->hand_state = DTLS_ST_CR_HELLO_VERIFY_REQUEST;
+                return 1;
+            } else if (!(s->s3->tmp.new_cipher->algorithm_auth
+                        & (SSL_aNULL | SSL_aSRP | SSL_aPSK))) {
+                if (mt == SSL3_MT_CERTIFICATE) {
+                    st->hand_state = TLS_ST_CR_CERT;
+                    return 1;
+                }
+            } else {
+                if (mt == SSL3_MT_SERVER_KEY_EXCHANGE) {
+                    st->hand_state = TLS_ST_CR_KEY_EXCH;
+                    return 1;
+                } else if (key_exchange_skip_allowed(s)) {
+                    if (mt == SSL3_MT_CERTIFICATE_REQUEST
+                            && cert_req_allowed(s)) {
+                        st->hand_state = TLS_ST_CR_CERT_REQ;
+                        return 1;
+                    } else if (mt == SSL3_MT_SERVER_DONE) {
+                        st->hand_state = TLS_ST_CR_SRVR_DONE;
+                        return 1;
+                    }
+                }
+            }
+        }
+        break;
+
+    case TLS_ST_CR_CERT:
+        if (s->tlsext_status_expected) {
+            if (mt == SSL3_MT_CERTIFICATE_STATUS) {
+                st->hand_state = TLS_ST_CR_CERT_STATUS;
+                return 1;
+            }
+        } else {
+            if (mt == SSL3_MT_SERVER_KEY_EXCHANGE) {
+                st->hand_state = TLS_ST_CR_KEY_EXCH;
+                return 1;
+            } else if (key_exchange_skip_allowed(s)) {
+                if (mt == SSL3_MT_CERTIFICATE_REQUEST && cert_req_allowed(s)) {
+                    st->hand_state = TLS_ST_CR_CERT_REQ;
+                    return 1;
+                } else if (mt == SSL3_MT_SERVER_DONE) {
+                    st->hand_state = TLS_ST_CR_SRVR_DONE;
+                    return 1;
+                }
+            }
+        }
+        break;
+
+    case TLS_ST_CR_CERT_STATUS:
+        if (mt == SSL3_MT_SERVER_KEY_EXCHANGE) {
+            st->hand_state = TLS_ST_CR_KEY_EXCH;
+            return 1;
+        } else if (key_exchange_skip_allowed(s)) {
+            if (mt == SSL3_MT_CERTIFICATE_REQUEST && cert_req_allowed(s)) {
+                st->hand_state = TLS_ST_CR_CERT_REQ;
+                return 1;
+            } else if (mt == SSL3_MT_SERVER_DONE) {
+                st->hand_state = TLS_ST_CR_SRVR_DONE;
+                return 1;
+            }
+        }
+        break;
+
+    case TLS_ST_CR_KEY_EXCH:
+        if (mt == SSL3_MT_CERTIFICATE_REQUEST && cert_req_allowed(s)) {
+            st->hand_state = TLS_ST_CR_CERT_REQ;
+            return 1;
+        } else if (mt == SSL3_MT_SERVER_DONE) {
+            st->hand_state = TLS_ST_CR_SRVR_DONE;
+            return 1;
+        }
+        break;
+
+    case TLS_ST_CR_CERT_REQ:
+        if (mt == SSL3_MT_SERVER_DONE) {
+            st->hand_state = TLS_ST_CR_SRVR_DONE;
+            return 1;
+        }
+        break;
+
+    case TLS_ST_CW_FINISHED:
+        if (mt == SSL3_MT_NEWSESSION_TICKET && s->tlsext_ticket_expected) {
+            st->hand_state = TLS_ST_CR_SESSION_TICKET;
+            return 1;
+        } else if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
+            st->hand_state = TLS_ST_CR_CHANGE;
+            return 1;
+        }
+        break;
+
+    case TLS_ST_CR_SESSION_TICKET:
+        if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
+            st->hand_state = TLS_ST_CR_CHANGE;
+            return 1;
+        }
+        break;
+
+    case TLS_ST_CR_CHANGE:
+        if (mt == SSL3_MT_FINISHED) {
+            st->hand_state = TLS_ST_CR_FINISHED;
+            return 1;
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    /* No valid transition found */
+    return 0;
+}
+
+/*
+ * client_write_transition() works out what handshake state to move to next
+ * when the client is writing messages to be sent to the server.
+ */
+enum WRITE_TRAN client_write_transition(SSL *s)
+{
+    STATEM *st = &s->statem;
+
+    switch(st->hand_state) {
+        case TLS_ST_OK:
+            /* Renegotiation - fall through */
+        case TLS_ST_BEFORE:
+            st->hand_state = TLS_ST_CW_CLNT_HELLO;
+            return WRITE_TRAN_CONTINUE;
+
+        case TLS_ST_CW_CLNT_HELLO:
+            /*
+             * No transition at the end of writing because we don't know what
+             * we will be sent
+             */
+            return WRITE_TRAN_FINISHED;
+
+        case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
+            st->hand_state = TLS_ST_CW_CLNT_HELLO;
+            return WRITE_TRAN_CONTINUE;
+
+        case TLS_ST_CR_SRVR_DONE:
+            if (s->s3->tmp.cert_req)
+                st->hand_state = TLS_ST_CW_CERT;
+            else
+                st->hand_state = TLS_ST_CW_KEY_EXCH;
+            return WRITE_TRAN_CONTINUE;
+
+        case TLS_ST_CW_CERT:
+            st->hand_state = TLS_ST_CW_KEY_EXCH;
+            return WRITE_TRAN_CONTINUE;
+
+        case TLS_ST_CW_KEY_EXCH:
+            /*
+             * For TLS, cert_req is set to 2, so a cert chain of nothing is
+             * sent, but no verify packet is sent
+             */
+            /*
+             * XXX: For now, we do not support client authentication in ECDH
+             * cipher suites with ECDH (rather than ECDSA) certificates. We
+             * need to skip the certificate verify message when client's
+             * ECDH public key is sent inside the client certificate.
+             */
+            if (s->s3->tmp.cert_req == 1) {
+                st->hand_state = TLS_ST_CW_CERT_VRFY;
+            } else {
+                st->hand_state = TLS_ST_CW_CHANGE;
+            }
+            if (s->s3->flags & TLS1_FLAGS_SKIP_CERT_VERIFY) {
+                st->hand_state = TLS_ST_CW_CHANGE;
+            }
+            return WRITE_TRAN_CONTINUE;
+
+        case TLS_ST_CW_CERT_VRFY:
+            st->hand_state = TLS_ST_CW_CHANGE;
+            return WRITE_TRAN_CONTINUE;
+
+        case TLS_ST_CW_CHANGE:
+#if defined(OPENSSL_NO_NEXTPROTONEG)
+            st->hand_state = TLS_ST_CW_FINISHED;
+#else
+            if (!SSL_IS_DTLS(s) && s->s3->next_proto_neg_seen)
+                st->hand_state = TLS_ST_CW_NEXT_PROTO;
+            else
+                st->hand_state = TLS_ST_CW_FINISHED;
+#endif
+            return WRITE_TRAN_CONTINUE;
+
+#if !defined(OPENSSL_NO_NEXTPROTONEG)
+        case TLS_ST_CW_NEXT_PROTO:
+            st->hand_state = TLS_ST_CW_FINISHED;
+            return WRITE_TRAN_CONTINUE;
+#endif
+
+        case TLS_ST_CW_FINISHED:
+            if (s->hit) {
+                st->hand_state = TLS_ST_OK;
+                statem_set_in_init(s, 0);
+                return WRITE_TRAN_CONTINUE;
+            } else {
+                return WRITE_TRAN_FINISHED;
+            }
+
+        case TLS_ST_CR_FINISHED:
+            if (s->hit) {
+                st->hand_state = TLS_ST_CW_CHANGE;
+                return WRITE_TRAN_CONTINUE;
+            } else {
+                st->hand_state = TLS_ST_OK;
+                statem_set_in_init(s, 0);
+                return WRITE_TRAN_CONTINUE;
+            }
+
+        default:
+            /* Shouldn't happen */
+            return WRITE_TRAN_ERROR;
+    }
+}
+
+/*
+ * Perform any pre work that needs to be done prior to sending a message from
+ * the client to the server.
+ */
+enum WORK_STATE client_pre_work(SSL *s, enum WORK_STATE wst)
+{
+    STATEM *st = &s->statem;
+
+    switch(st->hand_state) {
+    case TLS_ST_CW_CLNT_HELLO:
+        s->shutdown = 0;
+        if (SSL_IS_DTLS(s)) {
+            /* every DTLS ClientHello resets Finished MAC */
+            ssl3_init_finished_mac(s);
+        }
+        break;
+
+    case TLS_ST_CW_CERT:
+        return tls_prepare_client_certificate(s, wst);
+
+    case TLS_ST_CW_CHANGE:
+        if (SSL_IS_DTLS(s)) {
+            if (s->hit) {
+                /*
+                 * We're into the last flight so we don't retransmit these
+                 * messages unless we need to.
+                 */
+                st->use_timer = 0;
+            }
+#ifndef OPENSSL_NO_SCTP
+            if (BIO_dgram_is_sctp(SSL_get_wbio(s)))
+                return dtls_wait_for_dry(s);
+#endif
+        }
+        return WORK_FINISHED_CONTINUE;
+
+    case TLS_ST_OK:
+        return tls_finish_handshake(s, wst);
+
+    default:
+        /* No pre work to be done */
+        break;
+    }
+
+    return WORK_FINISHED_CONTINUE;
+}
+
+/*
+ * Perform any work that needs to be done after sending a message from the
+ * client to the server.
+ */
+enum WORK_STATE client_post_work(SSL *s, enum WORK_STATE wst)
+{
+    STATEM *st = &s->statem;
+
+    s->init_num = 0;
+
+    switch(st->hand_state) {
+    case TLS_ST_CW_CLNT_HELLO:
+        if (SSL_IS_DTLS(s) && s->d1->cookie_len > 0 && statem_flush(s) != 1)
+            return WORK_MORE_A;
+#ifndef OPENSSL_NO_SCTP
+        /* Disable buffering for SCTP */
+        if (!SSL_IS_DTLS(s) || !BIO_dgram_is_sctp(SSL_get_wbio(s))) {
+#endif
+            /*
+             * turn on buffering for the next lot of output
+             */
+            if (s->bbio != s->wbio)
+                s->wbio = BIO_push(s->bbio, s->wbio);
+#ifndef OPENSSL_NO_SCTP
+            }
+#endif
+        if (SSL_IS_DTLS(s)) {
+            /* Treat the next message as the first packet */
+            s->first_packet = 1;
+        }
+        break;
+
+    case TLS_ST_CW_KEY_EXCH:
+        if (tls_client_key_exchange_post_work(s) == 0)
+            return WORK_ERROR;
+        break;
+
+    case TLS_ST_CW_CHANGE:
+        s->session->cipher = s->s3->tmp.new_cipher;
+#ifdef OPENSSL_NO_COMP
+        s->session->compress_meth = 0;
+#else
+        if (s->s3->tmp.new_compression == NULL)
+            s->session->compress_meth = 0;
+        else
+            s->session->compress_meth = s->s3->tmp.new_compression->id;
+#endif
+        if (!s->method->ssl3_enc->setup_key_block(s))
+            return WORK_ERROR;
+
+        if (!s->method->ssl3_enc->change_cipher_state(s,
+                                                      SSL3_CHANGE_CIPHER_CLIENT_WRITE))
+            return WORK_ERROR;
+
+        if (SSL_IS_DTLS(s)) {
+#ifndef OPENSSL_NO_SCTP
+            if (s->hit) {
+                /*
+                 * Change to new shared key of SCTP-Auth, will be ignored if
+                 * no SCTP used.
+                 */
+                BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY,
+                         0, NULL);
+            }
+#endif
+
+            dtls1_reset_seq_numbers(s, SSL3_CC_WRITE);
+        }
+        break;
+
+    case TLS_ST_CW_FINISHED:
+#ifndef OPENSSL_NO_SCTP
+        if (wst == WORK_MORE_A && SSL_IS_DTLS(s) && s->hit == 0) {
+            /*
+             * Change to new shared key of SCTP-Auth, will be ignored if
+             * no SCTP used.
+             */
+            BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY,
+                     0, NULL);
+        }
+#endif
+        if (statem_flush(s) != 1)
+            return WORK_MORE_B;
+
+        if (s->hit && tls_finish_handshake(s, WORK_MORE_A) != 1)
+                return WORK_ERROR;
+        break;
+
+    default:
+        /* No post work to be done */
+        break;
+    }
+
+    return WORK_FINISHED_CONTINUE;
+}
+
+/*
+ * Construct a message to be sent from the client to the server.
+ *
+ * Valid return values are:
+ *   1: Success
+ *   0: Error
+ */
+int client_construct_message(SSL *s)
+{
+    STATEM *st = &s->statem;
+
+    switch(st->hand_state) {
+    case TLS_ST_CW_CLNT_HELLO:
+        return tls_construct_client_hello(s);
+
+    case TLS_ST_CW_CERT:
+        return tls_construct_client_certificate(s);
+
+    case TLS_ST_CW_KEY_EXCH:
+        return tls_construct_client_key_exchange(s);
+
+    case TLS_ST_CW_CERT_VRFY:
+        return tls_construct_client_verify(s);
+
+    case TLS_ST_CW_CHANGE:
+        if (SSL_IS_DTLS(s))
+            return dtls_construct_change_cipher_spec(s);
+        else
+            return tls_construct_change_cipher_spec(s);
+
+#if !defined(OPENSSL_NO_NEXTPROTONEG)
+    case TLS_ST_CW_NEXT_PROTO:
+        return tls_construct_next_proto(s);
+#endif
+    case TLS_ST_CW_FINISHED:
+        return tls_construct_finished(s,
+                                      s->method->
+                                      ssl3_enc->client_finished_label,
+                                      s->method->
+                                      ssl3_enc->client_finished_label_len);
+
+    default:
+        /* Shouldn't happen */
+        break;
+    }
+
+    return 0;
+}
+
+/*
+ * Returns the maximum allowed length for the current message that we are
+ * reading. Excludes the message header.
+ */
+unsigned long client_max_message_size(SSL *s)
+{
+    STATEM *st = &s->statem;
+
+    switch(st->hand_state) {
+        case TLS_ST_CR_SRVR_HELLO:
+            return SERVER_HELLO_MAX_LENGTH;
+
+        case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
+            return HELLO_VERIFY_REQUEST_MAX_LENGTH;
+
+        case TLS_ST_CR_CERT:
+            return s->max_cert_list;
+
+        case TLS_ST_CR_CERT_STATUS:
+            return SSL3_RT_MAX_PLAIN_LENGTH;
+
+        case TLS_ST_CR_KEY_EXCH:
+            return SERVER_KEY_EXCH_MAX_LENGTH;
+
+        case TLS_ST_CR_CERT_REQ:
+            return SSL3_RT_MAX_PLAIN_LENGTH;
+
+        case TLS_ST_CR_SRVR_DONE:
+            return SERVER_HELLO_DONE_MAX_LENGTH;
+
+        case TLS_ST_CR_CHANGE:
+            return CCS_MAX_LENGTH;
+
+        case TLS_ST_CR_SESSION_TICKET:
+            return SSL3_RT_MAX_PLAIN_LENGTH;
+
+        case TLS_ST_CR_FINISHED:
+            return FINISHED_MAX_LENGTH;
+
+        default:
+            /* Shouldn't happen */
+            break;
+    }
+
+    return 0;
+}
+
+/*
+ * Process a message that the client has been received from the server.
+ */
+enum MSG_PROCESS_RETURN client_process_message(SSL *s, PACKET *pkt)
+{
+    STATEM *st = &s->statem;
+
+    switch(st->hand_state) {
+        case TLS_ST_CR_SRVR_HELLO:
+            return tls_process_server_hello(s, pkt);
+
+        case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
+            return dtls_process_hello_verify(s, pkt);
+
+        case TLS_ST_CR_CERT:
+            return tls_process_server_certificate(s, pkt);
+
+        case TLS_ST_CR_CERT_STATUS:
+            return tls_process_cert_status(s, pkt);
+
+        case TLS_ST_CR_KEY_EXCH:
+            return tls_process_key_exchange(s, pkt);
+
+        case TLS_ST_CR_CERT_REQ:
+            return tls_process_certificate_request(s, pkt);
+
+        case TLS_ST_CR_SRVR_DONE:
+            return tls_process_server_done(s, pkt);
+
+        case TLS_ST_CR_CHANGE:
+            return tls_process_change_cipher_spec(s, pkt);
+
+        case TLS_ST_CR_SESSION_TICKET:
+            return tls_process_new_session_ticket(s, pkt);
+
+        case TLS_ST_CR_FINISHED:
+            return tls_process_finished(s, pkt);
+
+        default:
+            /* Shouldn't happen */
+            break;
+    }
+
+    return MSG_PROCESS_ERROR;
+}
+
+/*
+ * Perform any further processing required following the receipt of a message
+ * from the server
+ */
+enum WORK_STATE client_post_process_message(SSL *s, enum WORK_STATE wst)
+{
+    STATEM *st = &s->statem;
+
+    switch(st->hand_state) {
+#ifndef OPENSSL_NO_SCTP
+    case TLS_ST_CR_SRVR_DONE:
+        /* We only get here if we are using SCTP and we are renegotiating */
+        if (BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s))) {
+            s->s3->in_read_app_data = 2;
+            s->rwstate = SSL_READING;
+            BIO_clear_retry_flags(SSL_get_rbio(s));
+            BIO_set_retry_read(SSL_get_rbio(s));
+            statem_set_sctp_read_sock(s, 1);
+            return WORK_MORE_A;
+        }
+        statem_set_sctp_read_sock(s, 0);
+        return WORK_FINISHED_STOP;
+#endif
+
+    case TLS_ST_CR_FINISHED:
+        if (!s->hit)
+            return tls_finish_handshake(s, wst);
+        else
+            return WORK_FINISHED_STOP;
+    default:
+        break;
+    }
+
+    /* Shouldn't happen */
+    return WORK_ERROR;
+}
+
 /*
  * Work out what version we should be using for the initial ClientHello if
  * the version is currently set to (D)TLS_ANY_VERSION.
index e13ed8d..34fd8b2 100644 (file)
 #include <string.h>
 #include <stdio.h>
 #include "../ssl_locl.h"
+#include "statem_locl.h"
 #include <openssl/buffer.h>
 #include <openssl/rand.h>
 #include <openssl/objects.h>
index 75d151e..0f548d4 100644 (file)
 #include <string.h>
 #include <stdio.h>
 #include "../ssl_locl.h"
+#include "statem_locl.h"
 #include <openssl/buffer.h>
 #include <openssl/rand.h>
 #include <openssl/objects.h>
diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h
new file mode 100644 (file)
index 0000000..65787bd
--- /dev/null
@@ -0,0 +1,181 @@
+/* ssl/statem/statem_locl.h */
+/* ====================================================================
+ * Copyright (c) 1998-2015 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+/*****************************************************************************
+ *                                                                           *
+ * The following definitions are PRIVATE to the state machine. They should   *
+ * NOT be used outside of the state machine.                                 *
+ *                                                                           *
+ *****************************************************************************/
+
+/* Max message length definitions */
+
+/* The spec allows for a longer length than this, but we limit it */
+#define HELLO_VERIFY_REQUEST_MAX_LENGTH 258
+#define SERVER_HELLO_MAX_LENGTH         20000
+#define SERVER_KEY_EXCH_MAX_LENGTH      102400
+#define SERVER_HELLO_DONE_MAX_LENGTH    0
+#define CCS_MAX_LENGTH                  1
+/* Max should actually be 36 but we are generous */
+#define FINISHED_MAX_LENGTH             64
+
+/* Message processing return codes */
+enum MSG_PROCESS_RETURN {
+    /* Something bad happened */
+    MSG_PROCESS_ERROR,
+    /* We've finished reading - swap to writing */
+    MSG_PROCESS_FINISHED_READING,
+    /*
+     * We've completed the main processing of this message but there is some
+     * post processing to be done.
+     */
+    MSG_PROCESS_CONTINUE_PROCESSING,
+    /* We've finished this message - read the next message */
+    MSG_PROCESS_CONTINUE_READING
+};
+
+/* Flush the write BIO */
+int statem_flush(SSL *s);
+
+/*
+ * TLS/DTLS client state machine functions
+ */
+int client_read_transition(SSL *s, int mt);
+enum WRITE_TRAN client_write_transition(SSL *s);
+enum WORK_STATE client_pre_work(SSL *s, enum WORK_STATE wst);
+enum WORK_STATE client_post_work(SSL *s, enum WORK_STATE wst);
+int client_construct_message(SSL *s);
+unsigned long client_max_message_size(SSL *s);
+enum MSG_PROCESS_RETURN client_process_message(SSL *s, PACKET *pkt);
+enum WORK_STATE client_post_process_message(SSL *s, enum WORK_STATE wst);
+
+/*
+ * TLS/DTLS server state machine functions
+ */
+int server_read_transition(SSL *s, int mt);
+enum WRITE_TRAN server_write_transition(SSL *s);
+enum WORK_STATE server_pre_work(SSL *s, enum WORK_STATE wst);
+enum WORK_STATE server_post_work(SSL *s, enum WORK_STATE wst);
+int server_construct_message(SSL *s);
+unsigned long server_max_message_size(SSL *s);
+enum MSG_PROCESS_RETURN server_process_message(SSL *s, PACKET *pkt);
+enum WORK_STATE server_post_process_message(SSL *s, enum WORK_STATE wst);
+
+/* Functions for getting new message data */
+__owur int tls_get_message_header(SSL *s, int *mt);
+__owur int tls_get_message_body(SSL *s, unsigned long *len);
+__owur int dtls_get_message(SSL *s, int *mt, unsigned long *len);
+
+/* Message construction and processing functions */
+__owur enum MSG_PROCESS_RETURN tls_process_change_cipher_spec(SSL *s,
+                                                              PACKET *pkt);
+__owur enum MSG_PROCESS_RETURN tls_process_finished(SSL *s, PACKET *pkt);
+__owur int tls_construct_change_cipher_spec(SSL *s);
+__owur int dtls_construct_change_cipher_spec(SSL *s);
+
+__owur int tls_construct_finished(SSL *s, const char *sender, int slen);
+__owur enum WORK_STATE tls_finish_handshake(SSL *s, enum WORK_STATE wst);
+__owur enum WORK_STATE dtls_wait_for_dry(SSL *s);
+
+/* some client-only functions */
+__owur int tls_construct_client_hello(SSL *s);
+__owur enum MSG_PROCESS_RETURN tls_process_server_hello(SSL *s,
+                                                        PACKET *pkt);
+__owur enum MSG_PROCESS_RETURN tls_process_certificate_request(SSL *s,
+                                                               PACKET *pkt);
+__owur enum MSG_PROCESS_RETURN tls_process_new_session_ticket(SSL *s,
+                                                              PACKET *pkt);
+__owur enum MSG_PROCESS_RETURN tls_process_cert_status(SSL *s, PACKET *pkt);
+__owur enum MSG_PROCESS_RETURN tls_process_server_done(SSL *s, PACKET *pkt);
+__owur int tls_construct_client_verify(SSL *s);
+__owur enum WORK_STATE tls_prepare_client_certificate(SSL *s,
+                                                      enum WORK_STATE wst);
+__owur int tls_construct_client_certificate(SSL *s);
+__owur int ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey);
+__owur int tls_construct_client_key_exchange(SSL *s);
+__owur int tls_client_key_exchange_post_work(SSL *s);
+__owur int tls_construct_cert_status(SSL *s);
+__owur enum MSG_PROCESS_RETURN tls_process_key_exchange(SSL *s,
+                                                        PACKET *pkt);
+__owur enum MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s,
+                                                              PACKET *pkt);
+__owur int ssl3_check_cert_and_algorithm(SSL *s);
+#  ifndef OPENSSL_NO_NEXTPROTONEG
+__owur int tls_construct_next_proto(SSL *s);
+#  endif
+__owur enum MSG_PROCESS_RETURN dtls_process_hello_verify(SSL *s, PACKET *pkt);
+
+/* some server-only functions */
+__owur enum MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt);
+__owur enum WORK_STATE tls_post_process_client_hello(SSL *s,
+                                                     enum WORK_STATE wst);
+__owur int tls_construct_server_hello(SSL *s);
+__owur int tls_construct_hello_request(SSL *s);
+__owur int dtls_construct_hello_verify_request(SSL *s);
+__owur int tls_construct_server_certificate(SSL *s);
+__owur int tls_construct_server_key_exchange(SSL *s);
+__owur int tls_construct_certificate_request(SSL *s);
+__owur int tls_construct_server_done(SSL *s);
+__owur enum MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s,
+                                                              PACKET *pkt);
+__owur enum MSG_PROCESS_RETURN tls_process_client_key_exchange(SSL *s,
+                                                               PACKET *pkt);
+__owur enum WORK_STATE tls_post_process_client_key_exchange(SSL *s,
+    enum WORK_STATE wst);
+__owur enum MSG_PROCESS_RETURN tls_process_cert_verify(SSL *s, PACKET *pkt);
+#  ifndef OPENSSL_NO_NEXTPROTONEG
+__owur enum MSG_PROCESS_RETURN tls_process_next_proto(SSL *s, PACKET *pkt);
+#  endif
+__owur int tls_construct_new_session_ticket(SSL *s);
index 5f85a8c..f0b79f0 100644 (file)
 
 #include <stdio.h>
 #include "../ssl_locl.h"
+#include "statem_locl.h"
 #include "internal/constant_time_locl.h"
 #include <openssl/buffer.h>
 #include <openssl/rand.h>
@@ -169,6 +170,726 @@ static STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s,
                                                       STACK_OF(SSL_CIPHER) **skp,
                                                       int sslv2format, int *al);
 
+/*
+ * server_read_transition() encapsulates the logic for the allowed handshake
+ * state transitions when the server is reading messages from the client. The
+ * message type that the client has sent is provided in |mt|. The current state
+ * is in |s->statem.hand_state|.
+ *
+ *  Valid return values are:
+ *  1: Success (transition allowed)
+ *  0: Error (transition not allowed)
+ */
+int server_read_transition(SSL *s, int mt)
+{
+    STATEM *st = &s->statem;
+
+    switch(st->hand_state) {
+    case TLS_ST_BEFORE:
+    case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
+        if (mt == SSL3_MT_CLIENT_HELLO) {
+            st->hand_state = TLS_ST_SR_CLNT_HELLO;
+            return 1;
+        }
+        break;
+
+    case TLS_ST_SW_SRVR_DONE:
+        /*
+         * If we get a CKE message after a ServerDone then either
+         * 1) We didn't request a Certificate
+         * OR
+         * 2) If we did request one then
+         *      a) We allow no Certificate to be returned
+         *      AND
+         *      b) We are running SSL3 (in TLS1.0+ the client must return a 0
+         *         list if we requested a certificate)
+         */
+        if (mt == SSL3_MT_CLIENT_KEY_EXCHANGE
+                && (!s->s3->tmp.cert_request
+                    || (!((s->verify_mode & SSL_VERIFY_PEER) &&
+                          (s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT))
+                        && (s->version == SSL3_VERSION)))) {
+            st->hand_state = TLS_ST_SR_KEY_EXCH;
+            return 1;
+        } else if (s->s3->tmp.cert_request) {
+            if (mt == SSL3_MT_CERTIFICATE) {
+                st->hand_state = TLS_ST_SR_CERT;
+                return 1;
+            } 
+        }
+        break;
+
+    case TLS_ST_SR_CERT:
+        if (mt == SSL3_MT_CLIENT_KEY_EXCHANGE) {
+            st->hand_state = TLS_ST_SR_KEY_EXCH;
+            return 1;
+        }
+        break;
+
+    case TLS_ST_SR_KEY_EXCH:
+        /*
+         * We should only process a CertificateVerify message if we have
+         * received a Certificate from the client. If so then |s->session->peer|
+         * will be non NULL. In some instances a CertificateVerify message is
+         * not required even if the peer has sent a Certificate (e.g. such as in
+         * the case of static DH). In that case |s->no_cert_verify| should be
+         * set.
+         */
+        if (s->session->peer == NULL || s->no_cert_verify) {
+            if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
+                /*
+                 * For the ECDH ciphersuites when the client sends its ECDH
+                 * pub key in a certificate, the CertificateVerify message is
+                 * not sent. Also for GOST ciphersuites when the client uses
+                 * its key from the certificate for key exchange.
+                 */
+                st->hand_state = TLS_ST_SR_CHANGE;
+                return 1;
+            }
+        } else {
+            if (mt == SSL3_MT_CERTIFICATE_VERIFY) {
+                st->hand_state = TLS_ST_SR_CERT_VRFY;
+                return 1;
+            }
+        }
+        break;
+
+    case TLS_ST_SR_CERT_VRFY:
+        if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
+            st->hand_state = TLS_ST_SR_CHANGE;
+            return 1;
+        }
+        break;
+
+    case TLS_ST_SR_CHANGE:
+#ifndef OPENSSL_NO_NEXTPROTONEG
+        if (s->s3->next_proto_neg_seen) {
+            if (mt == SSL3_MT_NEXT_PROTO) {
+                st->hand_state = TLS_ST_SR_NEXT_PROTO;
+                return 1;
+            }
+        } else {
+#endif
+            if (mt == SSL3_MT_FINISHED) {
+                st->hand_state = TLS_ST_SR_FINISHED;
+                return 1;
+            }
+#ifndef OPENSSL_NO_NEXTPROTONEG
+        }
+#endif
+        break;
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+    case TLS_ST_SR_NEXT_PROTO:
+        if (mt == SSL3_MT_FINISHED) {
+            st->hand_state = TLS_ST_SR_FINISHED;
+            return 1;
+        }
+        break;
+#endif
+
+    case TLS_ST_SW_FINISHED:
+        if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
+            st->hand_state = TLS_ST_SR_CHANGE;
+            return 1;
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    /* No valid transition found */
+    return 0;
+}
+
+/*
+ * Should we send a ServerKeyExchange message?
+ *
+ * Valid return values are:
+ *   1: Yes
+ *   0: No
+ */
+static inline int send_server_key_exchange(SSL *s)
+{
+    unsigned long alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
+
+    /*
+     * only send a ServerKeyExchange if DH, fortezza or RSA but we have a
+     * sign only certificate PSK: may send PSK identity hints For
+     * ECC ciphersuites, we send a serverKeyExchange message only if
+     * the cipher suite is either ECDH-anon or ECDHE. In other cases,
+     * the server certificate contains the server's public key for
+     * key exchange.
+     */
+    if (   (alg_k & SSL_kDHE)
+        || (alg_k & SSL_kECDHE)
+        || ((alg_k & SSL_kRSA)
+            && (s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey == NULL
+                || (SSL_C_IS_EXPORT(s->s3->tmp.new_cipher)
+                    && EVP_PKEY_size(s->cert->pkeys
+                                     [SSL_PKEY_RSA_ENC].privatekey) *
+                    8 > SSL_C_EXPORT_PKEYLENGTH(s->s3->tmp.new_cipher)
+                   )
+               )
+           )
+        /*
+         * PSK: send ServerKeyExchange if PSK identity hint if
+         * provided
+         */
+#ifndef OPENSSL_NO_PSK
+        /* Only send SKE if we have identity hint for plain PSK */
+        || ((alg_k & (SSL_kPSK | SSL_kRSAPSK))
+            && s->cert->psk_identity_hint)
+        /* For other PSK always send SKE */
+        || (alg_k & (SSL_PSK & (SSL_kDHEPSK | SSL_kECDHEPSK)))
+#endif
+#ifndef OPENSSL_NO_SRP
+        /* SRP: send ServerKeyExchange */
+        || (alg_k & SSL_kSRP)
+#endif
+       ) {
+        return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * Should we send a CertificateRequest message?
+ *
+ * Valid return values are:
+ *   1: Yes
+ *   0: No
+ */
+static inline int send_certificate_request(SSL *s)
+{
+    if (
+           /* don't request cert unless asked for it: */
+           s->verify_mode & SSL_VERIFY_PEER
+           /*
+            * if SSL_VERIFY_CLIENT_ONCE is set, don't request cert
+            * during re-negotiation:
+            */
+           && ((s->session->peer == NULL) ||
+               !(s->verify_mode & SSL_VERIFY_CLIENT_ONCE))
+           /*
+            * never request cert in anonymous ciphersuites (see
+            * section "Certificate request" in SSL 3 drafts and in
+            * RFC 2246):
+            */
+           && (!(s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL)
+           /*
+            * ... except when the application insists on
+            * verification (against the specs, but s3_clnt.c accepts
+            * this for SSL 3)
+            */
+               || (s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT))
+           /* don't request certificate for SRP auth */
+           && !(s->s3->tmp.new_cipher->algorithm_auth & SSL_aSRP)
+           /*
+            * With normal PSK Certificates and Certificate Requests
+            * are omitted
+            */
+           && !(s->s3->tmp.new_cipher->algorithm_mkey & SSL_PSK)) {
+        return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * server_write_transition() works out what handshake state to move to next
+ * when the server is writing messages to be sent to the client.
+ */
+enum WRITE_TRAN server_write_transition(SSL *s)
+{
+    STATEM *st = &s->statem;
+
+    switch(st->hand_state) {
+        case TLS_ST_BEFORE:
+            /* Just go straight to trying to read from the client */;
+            return WRITE_TRAN_FINISHED;
+
+        case TLS_ST_OK:
+            /* We must be trying to renegotiate */
+            st->hand_state = TLS_ST_SW_HELLO_REQ;
+            return WRITE_TRAN_CONTINUE;
+
+        case TLS_ST_SW_HELLO_REQ:
+            st->hand_state = TLS_ST_OK;
+            statem_set_in_init(s, 0);
+            return WRITE_TRAN_CONTINUE;
+
+        case TLS_ST_SR_CLNT_HELLO:
+            if (SSL_IS_DTLS(s) && !s->d1->cookie_verified
+                    && (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE))
+                st->hand_state = DTLS_ST_SW_HELLO_VERIFY_REQUEST;
+            else
+                st->hand_state = TLS_ST_SW_SRVR_HELLO;
+            return WRITE_TRAN_CONTINUE;
+
+        case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
+            return WRITE_TRAN_FINISHED;
+
+        case TLS_ST_SW_SRVR_HELLO:
+            if (s->hit) {
+                if (s->tlsext_ticket_expected)
+                    st->hand_state = TLS_ST_SW_SESSION_TICKET;
+                else
+                    st->hand_state = TLS_ST_SW_CHANGE;
+            } else {
+                /* Check if it is anon DH or anon ECDH, */
+                /* normal PSK or SRP */
+                if (!(s->s3->tmp.new_cipher->algorithm_auth &
+                     (SSL_aNULL | SSL_aSRP | SSL_aPSK))) {
+                    st->hand_state = TLS_ST_SW_CERT;
+                } else if (send_server_key_exchange(s)) {
+                    st->hand_state = TLS_ST_SW_KEY_EXCH;
+                } else if (send_certificate_request(s)) {
+                    st->hand_state = TLS_ST_SW_CERT_REQ;
+                } else {
+                    st->hand_state = TLS_ST_SW_SRVR_DONE;
+                }
+            }
+            return WRITE_TRAN_CONTINUE;
+
+        case TLS_ST_SW_CERT:
+            if (s->tlsext_status_expected) {
+                st->hand_state = TLS_ST_SW_CERT_STATUS;
+                return WRITE_TRAN_CONTINUE;
+            }
+            /* Fall through */
+
+        case TLS_ST_SW_CERT_STATUS:
+            if (send_server_key_exchange(s)) {
+                st->hand_state = TLS_ST_SW_KEY_EXCH;
+                return WRITE_TRAN_CONTINUE;
+            }
+            /* Fall through */
+
+        case TLS_ST_SW_KEY_EXCH:
+            if (send_certificate_request(s)) {
+                st->hand_state = TLS_ST_SW_CERT_REQ;
+                return WRITE_TRAN_CONTINUE;
+            }
+            /* Fall through */
+
+        case TLS_ST_SW_CERT_REQ:
+            st->hand_state = TLS_ST_SW_SRVR_DONE;
+            return WRITE_TRAN_CONTINUE;
+
+        case TLS_ST_SW_SRVR_DONE:
+            return WRITE_TRAN_FINISHED;
+
+        case TLS_ST_SR_FINISHED:
+            if (s->hit) {
+                st->hand_state = TLS_ST_OK;
+                statem_set_in_init(s, 0);
+                return WRITE_TRAN_CONTINUE;
+            } else if (s->tlsext_ticket_expected) {
+                st->hand_state = TLS_ST_SW_SESSION_TICKET;
+            } else {
+                st->hand_state = TLS_ST_SW_CHANGE;
+            }
+            return WRITE_TRAN_CONTINUE;
+
+        case TLS_ST_SW_SESSION_TICKET:
+            st->hand_state = TLS_ST_SW_CHANGE;
+            return WRITE_TRAN_CONTINUE;
+
+        case TLS_ST_SW_CHANGE:
+            st->hand_state = TLS_ST_SW_FINISHED;
+            return WRITE_TRAN_CONTINUE;
+
+        case TLS_ST_SW_FINISHED:
+            if (s->hit) {
+                return WRITE_TRAN_FINISHED;
+            }
+            st->hand_state = TLS_ST_OK;
+            statem_set_in_init(s, 0);
+            return WRITE_TRAN_CONTINUE;
+
+        default:
+            /* Shouldn't happen */
+            return WRITE_TRAN_ERROR;
+    }
+}
+
+/*
+ * Perform any pre work that needs to be done prior to sending a message from
+ * the server to the client.
+ */
+enum WORK_STATE server_pre_work(SSL *s, enum WORK_STATE wst)
+{
+    STATEM *st = &s->statem;
+
+    switch(st->hand_state) {
+    case TLS_ST_SW_HELLO_REQ:
+        s->shutdown = 0;
+        if (SSL_IS_DTLS(s))
+            dtls1_clear_record_buffer(s);
+        break;
+
+    case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
+        s->shutdown = 0;
+        if (SSL_IS_DTLS(s)) {
+            dtls1_clear_record_buffer(s);
+            /* We don't buffer this message so don't use the timer */
+            st->use_timer = 0;
+        }
+        break;
+
+    case TLS_ST_SW_SRVR_HELLO:
+        if (SSL_IS_DTLS(s)) {
+            /*
+             * Messages we write from now on should be bufferred and
+             * retransmitted if necessary, so we need to use the timer now
+             */
+            st->use_timer = 1;
+        }
+        break;
+
+    case TLS_ST_SW_SRVR_DONE:
+#ifndef OPENSSL_NO_SCTP
+        if (SSL_IS_DTLS(s) && BIO_dgram_is_sctp(SSL_get_wbio(s)))
+            return dtls_wait_for_dry(s);
+#endif
+        return WORK_FINISHED_CONTINUE;
+
+    case TLS_ST_SW_SESSION_TICKET:
+        if (SSL_IS_DTLS(s)) {
+            /*
+             * We're into the last flight. We don't retransmit the last flight
+             * unless we need to, so we don't use the timer
+             */
+            st->use_timer = 0;
+        }
+        break;
+
+    case TLS_ST_SW_CHANGE:
+        s->session->cipher = s->s3->tmp.new_cipher;
+        if (!s->method->ssl3_enc->setup_key_block(s)) {
+            statem_set_error(s);
+            return WORK_ERROR;
+        }
+        if (SSL_IS_DTLS(s)) {
+            /*
+             * We're into the last flight. We don't retransmit the last flight
+             * unless we need to, so we don't use the timer. This might have
+             * already been set to 0 if we sent a NewSessionTicket message,
+             * but we'll set it again here in case we didn't.
+             */
+            st->use_timer = 0;
+        }
+        return WORK_FINISHED_CONTINUE;
+
+    case TLS_ST_OK:
+        return tls_finish_handshake(s, wst);
+
+    default:
+        /* No pre work to be done */
+        break;
+    }
+
+    return WORK_FINISHED_CONTINUE;
+}
+
+/*
+ * Perform any work that needs to be done after sending a message from the
+ * server to the client.
+ */
+enum WORK_STATE server_post_work(SSL *s, enum WORK_STATE wst)
+{
+    STATEM *st = &s->statem;
+
+    s->init_num = 0;
+
+    switch(st->hand_state) {
+    case TLS_ST_SW_HELLO_REQ:
+        if (statem_flush(s) != 1)
+            return WORK_MORE_A;
+        ssl3_init_finished_mac(s);
+        break;
+
+    case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
+        if (statem_flush(s) != 1)
+            return WORK_MORE_A;
+        /* HelloVerifyRequest resets Finished MAC */
+        if (s->version != DTLS1_BAD_VER)
+            ssl3_init_finished_mac(s);
+        /*
+         * The next message should be another ClientHello which we need to
+         * treat like it was the first packet
+         */
+        s->first_packet = 1;
+        break;
+
+    case TLS_ST_SW_SRVR_HELLO:
+#ifndef OPENSSL_NO_SCTP
+        if (SSL_IS_DTLS(s) && s->hit) {
+            unsigned char sctpauthkey[64];
+            char labelbuffer[sizeof(DTLS1_SCTP_AUTH_LABEL)];
+
+            /*
+             * Add new shared key for SCTP-Auth, will be ignored if no
+             * SCTP used.
+             */
+            snprintf((char *)labelbuffer, sizeof(DTLS1_SCTP_AUTH_LABEL),
+                     DTLS1_SCTP_AUTH_LABEL);
+
+            if (SSL_export_keying_material(s, sctpauthkey,
+                    sizeof(sctpauthkey), labelbuffer,
+                    sizeof(labelbuffer), NULL, 0, 0) <= 0) {
+                statem_set_error(s);
+                return WORK_ERROR;
+            }
+
+            BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY,
+                     sizeof(sctpauthkey), sctpauthkey);
+        }
+#endif
+        break;
+
+    case TLS_ST_SW_CHANGE:
+#ifndef OPENSSL_NO_SCTP
+        if (SSL_IS_DTLS(s) && !s->hit) {
+            /*
+             * Change to new shared key of SCTP-Auth, will be ignored if
+             * no SCTP used.
+             */
+            BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY,
+                     0, NULL);
+        }
+#endif
+        if (!s->method->ssl3_enc->change_cipher_state(s,
+                SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
+            statem_set_error(s);
+            return WORK_ERROR;
+        }
+
+        if (SSL_IS_DTLS(s))
+            dtls1_reset_seq_numbers(s, SSL3_CC_WRITE);
+        break;
+
+    case TLS_ST_SW_SRVR_DONE:
+        if (statem_flush(s) != 1)
+            return WORK_MORE_A;
+        break;
+
+    case TLS_ST_SW_FINISHED:
+        if (statem_flush(s) != 1)
+            return WORK_MORE_A;
+#ifndef OPENSSL_NO_SCTP
+        if (SSL_IS_DTLS(s) && s->hit) {
+            /*
+             * Change to new shared key of SCTP-Auth, will be ignored if
+             * no SCTP used.
+             */
+            BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY,
+                     0, NULL);
+        }
+#endif
+        break;
+
+    default:
+        /* No post work to be done */
+        break;
+    }
+
+    return WORK_FINISHED_CONTINUE;
+}
+
+/*
+ * Construct a message to be sent from the server to the client.
+ *
+ * Valid return values are:
+ *   1: Success
+ *   0: Error
+ */
+int server_construct_message(SSL *s)
+{
+    STATEM *st = &s->statem;
+
+    switch(st->hand_state) {
+    case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
+        return dtls_construct_hello_verify_request(s);
+
+    case TLS_ST_SW_HELLO_REQ:
+        return tls_construct_hello_request(s);
+
+    case TLS_ST_SW_SRVR_HELLO:
+        return tls_construct_server_hello(s);
+
+    case TLS_ST_SW_CERT:
+        return tls_construct_server_certificate(s);
+
+    case TLS_ST_SW_KEY_EXCH:
+        return tls_construct_server_key_exchange(s);
+
+    case TLS_ST_SW_CERT_REQ:
+        return tls_construct_certificate_request(s);
+
+    case TLS_ST_SW_SRVR_DONE:
+        return tls_construct_server_done(s);
+
+    case TLS_ST_SW_SESSION_TICKET:
+        return tls_construct_new_session_ticket(s);
+
+    case TLS_ST_SW_CERT_STATUS:
+        return tls_construct_cert_status(s);
+
+    case TLS_ST_SW_CHANGE:
+        if (SSL_IS_DTLS(s))
+            return dtls_construct_change_cipher_spec(s);
+        else
+            return tls_construct_change_cipher_spec(s);
+
+    case TLS_ST_SW_FINISHED:
+        return tls_construct_finished(s,
+                                      s->method->
+                                      ssl3_enc->server_finished_label,
+                                      s->method->
+                                      ssl3_enc->server_finished_label_len);
+
+    default:
+        /* Shouldn't happen */
+        break;
+    }
+
+    return 0;
+}
+
+#define CLIENT_KEY_EXCH_MAX_LENGTH      2048
+#define NEXT_PROTO_MAX_LENGTH           514
+
+/*
+ * Returns the maximum allowed length for the current message that we are
+ * reading. Excludes the message header.
+ */
+unsigned long server_max_message_size(SSL *s)
+{
+    STATEM *st = &s->statem;
+
+    switch(st->hand_state) {
+    case TLS_ST_SR_CLNT_HELLO:
+        return SSL3_RT_MAX_PLAIN_LENGTH;
+
+    case TLS_ST_SR_CERT:
+        return s->max_cert_list;
+
+    case TLS_ST_SR_KEY_EXCH:
+        return CLIENT_KEY_EXCH_MAX_LENGTH;
+
+    case TLS_ST_SR_CERT_VRFY:
+        return SSL3_RT_MAX_PLAIN_LENGTH;
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+    case TLS_ST_SR_NEXT_PROTO:
+        return NEXT_PROTO_MAX_LENGTH;
+#endif
+
+    case TLS_ST_SR_CHANGE:
+        return CCS_MAX_LENGTH;
+
+    case TLS_ST_SR_FINISHED:
+        return FINISHED_MAX_LENGTH;
+
+    default:
+        /* Shouldn't happen */
+        break;
+    }
+
+    return 0;
+}
+
+/*
+ * Process a message that the server has received from the client.
+ */
+enum MSG_PROCESS_RETURN server_process_message(SSL *s, PACKET *pkt)
+{
+    STATEM *st = &s->statem;
+
+    switch(st->hand_state) {
+    case TLS_ST_SR_CLNT_HELLO:
+        return tls_process_client_hello(s, pkt);
+
+    case TLS_ST_SR_CERT:
+        return tls_process_client_certificate(s, pkt);
+
+    case TLS_ST_SR_KEY_EXCH:
+        return tls_process_client_key_exchange(s, pkt);
+
+    case TLS_ST_SR_CERT_VRFY:
+        return tls_process_cert_verify(s, pkt);
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+    case TLS_ST_SR_NEXT_PROTO:
+        return tls_process_next_proto(s, pkt);
+#endif
+
+    case TLS_ST_SR_CHANGE:
+        return tls_process_change_cipher_spec(s, pkt);
+
+    case TLS_ST_SR_FINISHED:
+        return tls_process_finished(s, pkt);
+
+    default:
+        /* Shouldn't happen */
+        break;
+    }
+
+    return MSG_PROCESS_ERROR;
+}
+
+/*
+ * Perform any further processing required following the receipt of a message
+ * from the client
+ */
+enum WORK_STATE server_post_process_message(SSL *s, enum WORK_STATE wst)
+{
+    STATEM *st = &s->statem;
+
+    switch(st->hand_state) {
+    case TLS_ST_SR_CLNT_HELLO:
+        return tls_post_process_client_hello(s, wst);
+
+    case TLS_ST_SR_KEY_EXCH:
+        return tls_post_process_client_key_exchange(s, wst);
+
+    case TLS_ST_SR_CERT_VRFY:
+#ifndef OPENSSL_NO_SCTP
+        if (    /* Is this SCTP? */
+                BIO_dgram_is_sctp(SSL_get_wbio(s))
+                /* Are we renegotiating? */
+                && s->renegotiate
+                && BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s))) {
+            s->s3->in_read_app_data = 2;
+            s->rwstate = SSL_READING;
+            BIO_clear_retry_flags(SSL_get_rbio(s));
+            BIO_set_retry_read(SSL_get_rbio(s));
+            statem_set_sctp_read_sock(s, 1);
+            return WORK_MORE_A;
+        } else {
+            statem_set_sctp_read_sock(s, 0);
+        }
+#endif
+        return WORK_FINISHED_CONTINUE;
+
+
+    case TLS_ST_SR_FINISHED:
+        if (s->hit)
+            return tls_finish_handshake(s, wst);
+        else
+            return WORK_FINISHED_STOP;
+    default:
+        break;
+    }
+
+    /* Shouldn't happen */
+    return WORK_ERROR;
+}
+
 #ifndef OPENSSL_NO_SRP
 static int ssl_check_srp_ext_ClientHello(SSL *s, int *al)
 {