Move handshake message handling functions from ssl_both.c to client/server.
authorjsing <jsing@openbsd.org>
Sat, 1 Oct 2022 16:23:15 +0000 (16:23 +0000)
committerjsing <jsing@openbsd.org>
Sat, 1 Oct 2022 16:23:15 +0000 (16:23 +0000)
Currently, ssl_both.c contains several functions that are used by both the
legacy client and legacy server. This interwines the client and server,
making it harder to make progressive changes. While it does deduplicate
some code, it also ends up with code that is conditioned on s->server and
forces the caller to pass in SSL3_ST_* values.

Move these functions from ssl_both.c into ssl_clnt.c and ssl_srvr.c,
renaming as appropriate and removing the s->server conditionals. Also move
the client and server function prototypes from ssl_locl.h into the .c
files, making them static in the process.

ok tb@

lib/libssl/ssl_both.c
lib/libssl/ssl_clnt.c
lib/libssl/ssl_locl.h
lib/libssl/ssl_srvr.c

index cfd3238..801b5be 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_both.c,v 1.42 2022/02/05 14:54:10 jsing Exp $ */
+/* $OpenBSD: ssl_both.c,v 1.43 2022/10/01 16:23:15 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -161,152 +161,6 @@ ssl3_do_write(SSL *s, int type)
        return (0);
 }
 
-int
-ssl3_send_finished(SSL *s, int state_a, int state_b)
-{
-       CBB cbb, finished;
-
-       memset(&cbb, 0, sizeof(cbb));
-
-       if (s->s3->hs.state == state_a) {
-               if (!tls12_derive_finished(s))
-                       goto err;
-
-               /* Copy finished so we can use it for renegotiation checks. */
-               if (!s->server) {
-                       memcpy(s->s3->previous_client_finished,
-                           s->s3->hs.finished, s->s3->hs.finished_len);
-                       s->s3->previous_client_finished_len =
-                           s->s3->hs.finished_len;
-               } else {
-                       memcpy(s->s3->previous_server_finished,
-                           s->s3->hs.finished, s->s3->hs.finished_len);
-                       s->s3->previous_server_finished_len =
-                           s->s3->hs.finished_len;
-               }
-
-               if (!ssl3_handshake_msg_start(s, &cbb, &finished,
-                   SSL3_MT_FINISHED))
-                        goto err;
-               if (!CBB_add_bytes(&finished, s->s3->hs.finished,
-                   s->s3->hs.finished_len))
-                       goto err;
-               if (!ssl3_handshake_msg_finish(s, &cbb))
-                       goto err;
-
-               s->s3->hs.state = state_b;
-       }
-
-       return (ssl3_handshake_write(s));
-
- err:
-       CBB_cleanup(&cbb);
-
-       return (-1);
-}
-
-int
-ssl3_get_finished(SSL *s, int a, int b)
-{
-       int al, md_len, ret;
-       CBS cbs;
-
-       /* should actually be 36+4 :-) */
-       if ((ret = ssl3_get_message(s, a, b, SSL3_MT_FINISHED, 64)) <= 0)
-               return ret;
-
-       /* If this occurs, we have missed a message */
-       if (!s->s3->change_cipher_spec) {
-               al = SSL_AD_UNEXPECTED_MESSAGE;
-               SSLerror(s, SSL_R_GOT_A_FIN_BEFORE_A_CCS);
-               goto fatal_err;
-       }
-       s->s3->change_cipher_spec = 0;
-
-       md_len = TLS1_FINISH_MAC_LENGTH;
-
-       if (s->internal->init_num < 0) {
-               al = SSL_AD_DECODE_ERROR;
-               SSLerror(s, SSL_R_BAD_DIGEST_LENGTH);
-               goto fatal_err;
-       }
-
-       CBS_init(&cbs, s->internal->init_msg, s->internal->init_num);
-
-       if (s->s3->hs.peer_finished_len != md_len ||
-           CBS_len(&cbs) != md_len) {
-               al = SSL_AD_DECODE_ERROR;
-               SSLerror(s, SSL_R_BAD_DIGEST_LENGTH);
-               goto fatal_err;
-       }
-
-       if (!CBS_mem_equal(&cbs, s->s3->hs.peer_finished, CBS_len(&cbs))) {
-               al = SSL_AD_DECRYPT_ERROR;
-               SSLerror(s, SSL_R_DIGEST_CHECK_FAILED);
-               goto fatal_err;
-       }
-
-       /* Copy finished so we can use it for renegotiation checks. */
-       OPENSSL_assert(md_len <= EVP_MAX_MD_SIZE);
-       if (s->server) {
-               memcpy(s->s3->previous_client_finished,
-                   s->s3->hs.peer_finished, md_len);
-               s->s3->previous_client_finished_len = md_len;
-       } else {
-               memcpy(s->s3->previous_server_finished,
-                   s->s3->hs.peer_finished, md_len);
-               s->s3->previous_server_finished_len = md_len;
-       }
-
-       return (1);
- fatal_err:
-       ssl3_send_alert(s, SSL3_AL_FATAL, al);
-       return (0);
-}
-
-int
-ssl3_send_change_cipher_spec(SSL *s, int a, int b)
-{
-       size_t outlen;
-       CBB cbb;
-
-       memset(&cbb, 0, sizeof(cbb));
-
-       if (s->s3->hs.state == a) {
-               if (!CBB_init_fixed(&cbb, s->internal->init_buf->data,
-                   s->internal->init_buf->length))
-                       goto err;
-               if (!CBB_add_u8(&cbb, SSL3_MT_CCS))
-                       goto err;
-               if (!CBB_finish(&cbb, NULL, &outlen))
-                       goto err;
-
-               if (outlen > INT_MAX)
-                       goto err;
-
-               s->internal->init_num = (int)outlen;
-               s->internal->init_off = 0;
-
-               if (SSL_is_dtls(s)) {
-                       s->d1->handshake_write_seq =
-                           s->d1->next_handshake_write_seq;
-                       dtls1_set_message_header_int(s, SSL3_MT_CCS, 0,
-                           s->d1->handshake_write_seq, 0, 0);
-                       dtls1_buffer_message(s, 1);
-               }
-
-               s->s3->hs.state = b;
-       }
-
-       /* SSL3_ST_CW_CHANGE_B */
-       return ssl3_record_write(s, SSL3_RT_CHANGE_CIPHER_SPEC);
-
- err:
-       CBB_cleanup(&cbb);
-
-       return -1;
-}
-
 static int
 ssl3_add_cert(CBB *cbb, X509 *x)
 {
index 0e50285..8b2f209 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_clnt.c,v 1.153 2022/08/17 07:39:19 jsing Exp $ */
+/* $OpenBSD: ssl_clnt.c,v 1.154 2022/10/01 16:23:15 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
 
 static int ca_dn_cmp(const X509_NAME * const *a, const X509_NAME * const *b);
 
+static int ssl3_send_client_hello(SSL *s);
+static int ssl3_get_dtls_hello_verify(SSL *s);
+static int ssl3_get_server_hello(SSL *s);
+static int ssl3_get_certificate_request(SSL *s);
+static int ssl3_get_new_session_ticket(SSL *s);
+static int ssl3_get_cert_status(SSL *s);
+static int ssl3_get_server_done(SSL *s);
+static int ssl3_send_client_verify(SSL *s);
+static int ssl3_send_client_certificate(SSL *s);
+static int ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey);
+static int ssl3_send_client_key_exchange(SSL *s);
+static int ssl3_get_server_key_exchange(SSL *s);
+static int ssl3_get_server_certificate(SSL *s);
+static int ssl3_check_cert_and_algorithm(SSL *s);
+static int ssl3_check_finished(SSL *s);
+static int ssl3_send_client_change_cipher_spec(SSL *s);
+static int ssl3_send_client_finished(SSL *s);
+static int ssl3_get_server_finished(SSL *s);
+
 int
 ssl3_connect(SSL *s)
 {
@@ -469,8 +488,7 @@ ssl3_connect(SSL *s)
                case SSL3_ST_CW_CHANGE_B:
                        if (SSL_is_dtls(s) && !s->internal->hit)
                                dtls1_start_timer(s);
-                       ret = ssl3_send_change_cipher_spec(s,
-                           SSL3_ST_CW_CHANGE_A, SSL3_ST_CW_CHANGE_B);
+                       ret = ssl3_send_client_change_cipher_spec(s);
                        if (ret <= 0)
                                goto end;
 
@@ -492,8 +510,7 @@ ssl3_connect(SSL *s)
                case SSL3_ST_CW_FINISHED_B:
                        if (SSL_is_dtls(s) && !s->internal->hit)
                                dtls1_start_timer(s);
-                       ret = ssl3_send_finished(s, SSL3_ST_CW_FINISHED_A,
-                           SSL3_ST_CW_FINISHED_B);
+                       ret = ssl3_send_client_finished(s);
                        if (ret <= 0)
                                goto end;
                        if (!SSL_is_dtls(s))
@@ -539,8 +556,7 @@ ssl3_connect(SSL *s)
                                s->d1->change_cipher_spec_ok = 1;
                        else
                                s->s3->flags |= SSL3_FLAGS_CCS_OK;
-                       ret = ssl3_get_finished(s, SSL3_ST_CR_FINISHED_A,
-                           SSL3_ST_CR_FINISHED_B);
+                       ret = ssl3_get_server_finished(s);
                        if (ret <= 0)
                                goto end;
                        if (SSL_is_dtls(s))
@@ -640,7 +656,7 @@ ssl3_connect(SSL *s)
        return (ret);
 }
 
-int
+static int
 ssl3_send_client_hello(SSL *s)
 {
        CBB cbb, client_hello, session_id, cookie, cipher_suites;
@@ -752,7 +768,7 @@ ssl3_send_client_hello(SSL *s)
        return (-1);
 }
 
-int
+static int
 ssl3_get_dtls_hello_verify(SSL *s)
 {
        CBS hello_verify_request, cookie;
@@ -813,7 +829,7 @@ ssl3_get_dtls_hello_verify(SSL *s)
        return -1;
 }
 
-int
+static int
 ssl3_get_server_hello(SSL *s)
 {
        CBS cbs, server_random, session_id;
@@ -1083,7 +1099,7 @@ ssl3_get_server_hello(SSL *s)
        return (-1);
 }
 
-int
+static int
 ssl3_get_server_certificate(SSL *s)
 {
        CBS cbs, cert_list, cert_data;
@@ -1280,7 +1296,7 @@ ssl3_get_server_kex_ecdhe(SSL *s, CBS *cbs)
        return 0;
 }
 
-int
+static int
 ssl3_get_server_key_exchange(SSL *s)
 {
        CBS cbs, signature;
@@ -1428,7 +1444,7 @@ ssl3_get_server_key_exchange(SSL *s)
        return (-1);
 }
 
-int
+static int
 ssl3_get_certificate_request(SSL *s)
 {
        CBS cert_request, cert_types, rdn_list;
@@ -1572,7 +1588,7 @@ ca_dn_cmp(const X509_NAME * const *a, const X509_NAME * const *b)
        return (X509_NAME_cmp(*a, *b));
 }
 
-int
+static int
 ssl3_get_new_session_ticket(SSL *s)
 {
        uint32_t lifetime_hint;
@@ -1647,7 +1663,7 @@ ssl3_get_new_session_ticket(SSL *s)
        return (-1);
 }
 
-int
+static int
 ssl3_get_cert_status(SSL *s)
 {
        CBS cert_status, response;
@@ -1748,7 +1764,7 @@ ssl3_get_cert_status(SSL *s)
        return (-1);
 }
 
-int
+static int
 ssl3_get_server_done(SSL *s)
 {
        int ret;
@@ -2024,7 +2040,7 @@ ssl3_send_client_kex_gost(SSL *s, CBB *cbb)
        return ret;
 }
 
-int
+static int
 ssl3_send_client_key_exchange(SSL *s)
 {
        unsigned long alg_k;
@@ -2283,7 +2299,7 @@ ssl3_send_client_verify_gost(SSL *s, EVP_PKEY *pkey, CBB *cert_verify)
 }
 #endif
 
-int
+static int
 ssl3_send_client_verify(SSL *s)
 {
        const struct ssl_sigalg *sigalg;
@@ -2345,7 +2361,7 @@ ssl3_send_client_verify(SSL *s)
        return (-1);
 }
 
-int
+static int
 ssl3_send_client_certificate(SSL *s)
 {
        EVP_PKEY *pkey = NULL;
@@ -2423,7 +2439,7 @@ ssl3_send_client_certificate(SSL *s)
 
 #define has_bits(i,m)  (((i)&(m)) == (m))
 
-int
+static int
 ssl3_check_cert_and_algorithm(SSL *s)
 {
        long alg_k, alg_a;
@@ -2481,7 +2497,7 @@ ssl3_check_cert_and_algorithm(SSL *s)
  * session tickets we have to check the next message to be sure.
  */
 
-int
+static int
 ssl3_check_finished(SSL *s)
 {
        int ret;
@@ -2503,7 +2519,7 @@ ssl3_check_finished(SSL *s)
        return (1);
 }
 
-int
+static int
 ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey)
 {
        int     i = 0;
@@ -2521,3 +2537,137 @@ ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey)
                i = s->ctx->internal->client_cert_cb(s, px509, ppkey);
        return (i);
 }
+
+static int
+ssl3_send_client_change_cipher_spec(SSL *s)
+{
+       size_t outlen;
+       CBB cbb;
+
+       memset(&cbb, 0, sizeof(cbb));
+
+       if (s->s3->hs.state == SSL3_ST_CW_CHANGE_A) {
+               if (!CBB_init_fixed(&cbb, s->internal->init_buf->data,
+                   s->internal->init_buf->length))
+                       goto err;
+               if (!CBB_add_u8(&cbb, SSL3_MT_CCS))
+                       goto err;
+               if (!CBB_finish(&cbb, NULL, &outlen))
+                       goto err;
+
+               if (outlen > INT_MAX)
+                       goto err;
+
+               s->internal->init_num = (int)outlen;
+               s->internal->init_off = 0;
+
+               if (SSL_is_dtls(s)) {
+                       s->d1->handshake_write_seq =
+                           s->d1->next_handshake_write_seq;
+                       dtls1_set_message_header_int(s, SSL3_MT_CCS, 0,
+                           s->d1->handshake_write_seq, 0, 0);
+                       dtls1_buffer_message(s, 1);
+               }
+
+               s->s3->hs.state = SSL3_ST_CW_CHANGE_B;
+       }
+
+       /* SSL3_ST_CW_CHANGE_B */
+       return ssl3_record_write(s, SSL3_RT_CHANGE_CIPHER_SPEC);
+
+ err:
+       CBB_cleanup(&cbb);
+
+       return -1;
+}
+
+static int
+ssl3_send_client_finished(SSL *s)
+{
+       CBB cbb, finished;
+
+       memset(&cbb, 0, sizeof(cbb));
+
+       if (s->s3->hs.state == SSL3_ST_CW_FINISHED_A) {
+               if (!tls12_derive_finished(s))
+                       goto err;
+
+               /* Copy finished so we can use it for renegotiation checks. */
+               memcpy(s->s3->previous_client_finished,
+                   s->s3->hs.finished, s->s3->hs.finished_len);
+               s->s3->previous_client_finished_len =
+                   s->s3->hs.finished_len;
+
+               if (!ssl3_handshake_msg_start(s, &cbb, &finished,
+                   SSL3_MT_FINISHED))
+                        goto err;
+               if (!CBB_add_bytes(&finished, s->s3->hs.finished,
+                   s->s3->hs.finished_len))
+                       goto err;
+               if (!ssl3_handshake_msg_finish(s, &cbb))
+                       goto err;
+
+               s->s3->hs.state = SSL3_ST_CW_FINISHED_B;
+       }
+
+       return (ssl3_handshake_write(s));
+
+ err:
+       CBB_cleanup(&cbb);
+
+       return (-1);
+}
+
+static int
+ssl3_get_server_finished(SSL *s)
+{
+       int al, md_len, ret;
+       CBS cbs;
+
+       /* should actually be 36+4 :-) */
+       if ((ret = ssl3_get_message(s, SSL3_ST_CR_FINISHED_A,
+           SSL3_ST_CR_FINISHED_B, SSL3_MT_FINISHED, 64)) <= 0)
+               return ret;
+
+       /* If this occurs, we have missed a message */
+       if (!s->s3->change_cipher_spec) {
+               al = SSL_AD_UNEXPECTED_MESSAGE;
+               SSLerror(s, SSL_R_GOT_A_FIN_BEFORE_A_CCS);
+               goto fatal_err;
+       }
+       s->s3->change_cipher_spec = 0;
+
+       md_len = TLS1_FINISH_MAC_LENGTH;
+
+       if (s->internal->init_num < 0) {
+               al = SSL_AD_DECODE_ERROR;
+               SSLerror(s, SSL_R_BAD_DIGEST_LENGTH);
+               goto fatal_err;
+       }
+
+       CBS_init(&cbs, s->internal->init_msg, s->internal->init_num);
+
+       if (s->s3->hs.peer_finished_len != md_len ||
+           CBS_len(&cbs) != md_len) {
+               al = SSL_AD_DECODE_ERROR;
+               SSLerror(s, SSL_R_BAD_DIGEST_LENGTH);
+               goto fatal_err;
+       }
+
+       if (!CBS_mem_equal(&cbs, s->s3->hs.peer_finished, CBS_len(&cbs))) {
+               al = SSL_AD_DECRYPT_ERROR;
+               SSLerror(s, SSL_R_DIGEST_CHECK_FAILED);
+               goto fatal_err;
+       }
+
+       /* Copy finished so we can use it for renegotiation checks. */
+       OPENSSL_assert(md_len <= EVP_MAX_MD_SIZE);
+       memcpy(s->s3->previous_server_finished,
+           s->s3->hs.peer_finished, md_len);
+       s->s3->previous_server_finished_len = md_len;
+
+       return (1);
+ fatal_err:
+       ssl3_send_alert(s, SSL3_AL_FATAL, al);
+       return (0);
+}
index a6fc6ea..a8d5308 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_locl.h,v 1.425 2022/09/10 15:29:33 jsing Exp $ */
+/* $OpenBSD: ssl_locl.h,v 1.426 2022/10/01 16:23:15 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -1357,16 +1357,10 @@ int ssl_verify_alarm_type(long type);
 int SSL_SESSION_ticket(SSL_SESSION *ss, unsigned char **out, size_t *out_len);
 
 const SSL_CIPHER *ssl3_get_cipher_by_char(const unsigned char *p);
-int ssl3_send_server_certificate(SSL *s);
-int ssl3_send_newsession_ticket(SSL *s);
-int ssl3_send_cert_status(SSL *s);
-int ssl3_get_finished(SSL *s, int state_a, int state_b);
-int ssl3_send_change_cipher_spec(SSL *s, int state_a, int state_b);
 int ssl3_do_write(SSL *s, int type);
 int ssl3_send_alert(SSL *s, int level, int desc);
 int ssl3_get_req_cert_types(SSL *s, CBB *cbb);
 int ssl3_get_message(SSL *s, int st1, int stn, int mt, long max);
-int ssl3_send_finished(SSL *s, int state_a, int state_b);
 int ssl3_num_ciphers(void);
 const SSL_CIPHER *ssl3_get_cipher(unsigned int u);
 const SSL_CIPHER *ssl3_get_cipher_by_id(unsigned int id);
@@ -1424,35 +1418,6 @@ int ssl_server_legacy_first_packet(SSL *s);
 int ssl3_write_pending(SSL *s, int type, const unsigned char *buf,
     unsigned int len);
 
-/* some client-only functions */
-int ssl3_send_client_hello(SSL *s);
-int ssl3_get_dtls_hello_verify(SSL *s);
-int ssl3_get_server_hello(SSL *s);
-int ssl3_get_certificate_request(SSL *s);
-int ssl3_get_new_session_ticket(SSL *s);
-int ssl3_get_cert_status(SSL *s);
-int ssl3_get_server_done(SSL *s);
-int ssl3_send_client_verify(SSL *s);
-int ssl3_send_client_certificate(SSL *s);
-int ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey);
-int ssl3_send_client_key_exchange(SSL *s);
-int ssl3_get_server_key_exchange(SSL *s);
-int ssl3_get_server_certificate(SSL *s);
-int ssl3_check_cert_and_algorithm(SSL *s);
-int ssl3_check_finished(SSL *s);
-
-/* some server-only functions */
-int ssl3_get_client_hello(SSL *s);
-int ssl3_send_dtls_hello_verify_request(SSL *s);
-int ssl3_send_server_hello(SSL *s);
-int ssl3_send_hello_request(SSL *s);
-int ssl3_send_server_key_exchange(SSL *s);
-int ssl3_send_certificate_request(SSL *s);
-int ssl3_send_server_done(SSL *s);
-int ssl3_get_client_certificate(SSL *s);
-int ssl3_get_client_key_exchange(SSL *s);
-int ssl3_get_cert_verify(SSL *s);
-
 int ssl_kex_generate_dhe(DH *dh, DH *dh_params);
 int ssl_kex_generate_dhe_params_auto(DH *dh, size_t key_len);
 int ssl_kex_params_dhe(DH *dh, CBB *cbb);
index acdcb15..821006a 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_srvr.c,v 1.149 2022/08/17 07:39:19 jsing Exp $ */
+/* $OpenBSD: ssl_srvr.c,v 1.150 2022/10/01 16:23:15 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
  * OTHERWISE.
  */
 
+#include <limits.h>
 #include <stdio.h>
 
 #include <openssl/bn.h>
 #include "ssl_sigalgs.h"
 #include "ssl_tlsext.h"
 
+static int ssl3_get_client_hello(SSL *s);
+static int ssl3_send_dtls_hello_verify_request(SSL *s);
+static int ssl3_send_server_hello(SSL *s);
+static int ssl3_send_hello_request(SSL *s);
+static int ssl3_send_server_certificate(SSL *s);
+static int ssl3_send_server_key_exchange(SSL *s);
+static int ssl3_send_certificate_request(SSL *s);
+static int ssl3_send_server_done(SSL *s);
+static int ssl3_get_client_certificate(SSL *s);
+static int ssl3_get_client_key_exchange(SSL *s);
+static int ssl3_get_cert_verify(SSL *s);
+static int ssl3_send_newsession_ticket(SSL *s);
+static int ssl3_send_cert_status(SSL *s);
+static int ssl3_send_server_change_cipher_spec(SSL *s);
+static int ssl3_send_server_finished(SSL *s);
+static int ssl3_get_client_finished(SSL *s);
+
 int
 ssl3_accept(SSL *s)
 {
@@ -605,8 +623,7 @@ ssl3_accept(SSL *s)
                                s->d1->change_cipher_spec_ok = 1;
                        else
                                s->s3->flags |= SSL3_FLAGS_CCS_OK;
-                       ret = ssl3_get_finished(s, SSL3_ST_SR_FINISHED_A,
-                           SSL3_ST_SR_FINISHED_B);
+                       ret = ssl3_get_client_finished(s);
                        if (ret <= 0)
                                goto end;
                        if (SSL_is_dtls(s))
@@ -640,8 +657,7 @@ ssl3_accept(SSL *s)
 
                case SSL3_ST_SW_CHANGE_A:
                case SSL3_ST_SW_CHANGE_B:
-                       ret = ssl3_send_change_cipher_spec(s,
-                           SSL3_ST_SW_CHANGE_A, SSL3_ST_SW_CHANGE_B);
+                       ret = ssl3_send_server_change_cipher_spec(s);
                        if (ret <= 0)
                                goto end;
                        s->s3->hs.state = SSL3_ST_SW_FINISHED_A;
@@ -660,8 +676,7 @@ ssl3_accept(SSL *s)
 
                case SSL3_ST_SW_FINISHED_A:
                case SSL3_ST_SW_FINISHED_B:
-                       ret = ssl3_send_finished(s, SSL3_ST_SW_FINISHED_A,
-                           SSL3_ST_SW_FINISHED_B);
+                       ret = ssl3_send_server_finished(s);
                        if (ret <= 0)
                                goto end;
                        s->s3->hs.state = SSL3_ST_SW_FLUSH;
@@ -748,7 +763,7 @@ ssl3_accept(SSL *s)
        return (ret);
 }
 
-int
+static int
 ssl3_send_hello_request(SSL *s)
 {
        CBB cbb, hello;
@@ -774,7 +789,7 @@ ssl3_send_hello_request(SSL *s)
        return (-1);
 }
 
-int
+static int
 ssl3_get_client_hello(SSL *s)
 {
        CBS cbs, client_random, session_id, cookie, cipher_suites;
@@ -1167,7 +1182,7 @@ ssl3_get_client_hello(SSL *s)
        return (ret);
 }
 
-int
+static int
 ssl3_send_dtls_hello_verify_request(SSL *s)
 {
        CBB cbb, verify, cookie;
@@ -1211,7 +1226,7 @@ ssl3_send_dtls_hello_verify_request(SSL *s)
        return (-1);
 }
 
-int
+static int
 ssl3_send_server_hello(SSL *s)
 {
        CBB cbb, server_hello, session_id;
@@ -1290,7 +1305,7 @@ ssl3_send_server_hello(SSL *s)
        return (-1);
 }
 
-int
+static int
 ssl3_send_server_done(SSL *s)
 {
        CBB cbb, done;
@@ -1413,7 +1428,7 @@ ssl3_send_server_kex_ecdhe(SSL *s, CBB *cbb)
        return 0;
 }
 
-int
+static int
 ssl3_send_server_key_exchange(SSL *s)
 {
        CBB cbb, cbb_params, cbb_signature, server_kex;
@@ -1552,7 +1567,7 @@ ssl3_send_server_key_exchange(SSL *s)
        return (-1);
 }
 
-int
+static int
 ssl3_send_certificate_request(SSL *s)
 {
        CBB cbb, cert_request, cert_types, sigalgs, cert_auth, dn;
@@ -1868,7 +1883,7 @@ ssl3_get_client_kex_gost(SSL *s, CBS *cbs)
        return 0;
 }
 
-int
+static int
 ssl3_get_client_key_exchange(SSL *s)
 {
        unsigned long alg_k;
@@ -1919,7 +1934,7 @@ ssl3_get_client_key_exchange(SSL *s)
        return (-1);
 }
 
-int
+static int
 ssl3_get_cert_verify(SSL *s)
 {
        CBS cbs, signature;
@@ -2153,7 +2168,7 @@ ssl3_get_cert_verify(SSL *s)
        return (ret);
 }
 
-int
+static int
 ssl3_get_client_certificate(SSL *s)
 {
        CBS cbs, cert_list, cert_data;
@@ -2271,7 +2286,7 @@ ssl3_get_client_certificate(SSL *s)
        return (ret);
 }
 
-int
+static int
 ssl3_send_server_certificate(SSL *s)
 {
        CBB cbb, server_cert;
@@ -2310,7 +2325,7 @@ ssl3_send_server_certificate(SSL *s)
 }
 
 /* send a new session ticket (not necessarily for a new session) */
-int
+static int
 ssl3_send_newsession_ticket(SSL *s)
 {
        CBB cbb, session_ticket, ticket;
@@ -2443,7 +2458,7 @@ ssl3_send_newsession_ticket(SSL *s)
        return (-1);
 }
 
-int
+static int
 ssl3_send_cert_status(SSL *s)
 {
        CBB cbb, certstatus, ocspresp;
@@ -2475,3 +2490,136 @@ ssl3_send_cert_status(SSL *s)
 
        return (-1);
 }
+
+static int
+ssl3_send_server_change_cipher_spec(SSL *s)
+{
+       size_t outlen;
+       CBB cbb;
+
+       memset(&cbb, 0, sizeof(cbb));
+
+       if (s->s3->hs.state == SSL3_ST_SW_CHANGE_A) {
+               if (!CBB_init_fixed(&cbb, s->internal->init_buf->data,
+                   s->internal->init_buf->length))
+                       goto err;
+               if (!CBB_add_u8(&cbb, SSL3_MT_CCS))
+                       goto err;
+               if (!CBB_finish(&cbb, NULL, &outlen))
+                       goto err;
+
+               if (outlen > INT_MAX)
+                       goto err;
+
+               s->internal->init_num = (int)outlen;
+               s->internal->init_off = 0;
+
+               if (SSL_is_dtls(s)) {
+                       s->d1->handshake_write_seq =
+                           s->d1->next_handshake_write_seq;
+                       dtls1_set_message_header_int(s, SSL3_MT_CCS, 0,
+                           s->d1->handshake_write_seq, 0, 0);
+                       dtls1_buffer_message(s, 1);
+               }
+
+               s->s3->hs.state = SSL3_ST_SW_CHANGE_B;
+       }
+
+       /* SSL3_ST_SW_CHANGE_B */
+       return ssl3_record_write(s, SSL3_RT_CHANGE_CIPHER_SPEC);
+
+ err:
+       CBB_cleanup(&cbb);
+
+       return -1;
+}
+
+static int
+ssl3_get_client_finished(SSL *s)
+{
+       int al, md_len, ret;
+       CBS cbs;
+
+       /* should actually be 36+4 :-) */
+       if ((ret = ssl3_get_message(s, SSL3_ST_SR_FINISHED_A,
+           SSL3_ST_SR_FINISHED_B, SSL3_MT_FINISHED, 64)) <= 0)
+               return ret;
+
+       /* If this occurs, we have missed a message */
+       if (!s->s3->change_cipher_spec) {
+               al = SSL_AD_UNEXPECTED_MESSAGE;
+               SSLerror(s, SSL_R_GOT_A_FIN_BEFORE_A_CCS);
+               goto fatal_err;
+       }
+       s->s3->change_cipher_spec = 0;
+
+       md_len = TLS1_FINISH_MAC_LENGTH;
+
+       if (s->internal->init_num < 0) {
+               al = SSL_AD_DECODE_ERROR;
+               SSLerror(s, SSL_R_BAD_DIGEST_LENGTH);
+               goto fatal_err;
+       }
+
+       CBS_init(&cbs, s->internal->init_msg, s->internal->init_num);
+
+       if (s->s3->hs.peer_finished_len != md_len ||
+           CBS_len(&cbs) != md_len) {
+               al = SSL_AD_DECODE_ERROR;
+               SSLerror(s, SSL_R_BAD_DIGEST_LENGTH);
+               goto fatal_err;
+       }
+
+       if (!CBS_mem_equal(&cbs, s->s3->hs.peer_finished, CBS_len(&cbs))) {
+               al = SSL_AD_DECRYPT_ERROR;
+               SSLerror(s, SSL_R_DIGEST_CHECK_FAILED);
+               goto fatal_err;
+       }
+
+       /* Copy finished so we can use it for renegotiation checks. */
+       OPENSSL_assert(md_len <= EVP_MAX_MD_SIZE);
+       memcpy(s->s3->previous_client_finished,
+           s->s3->hs.peer_finished, md_len);
+       s->s3->previous_client_finished_len = md_len;
+
+       return (1);
+ fatal_err:
+       ssl3_send_alert(s, SSL3_AL_FATAL, al);
+       return (0);
+}
+
+static int
+ssl3_send_server_finished(SSL *s)
+{
+       CBB cbb, finished;
+
+       memset(&cbb, 0, sizeof(cbb));
+
+       if (s->s3->hs.state == SSL3_ST_SW_FINISHED_A) {
+               if (!tls12_derive_finished(s))
+                       goto err;
+
+               /* Copy finished so we can use it for renegotiation checks. */
+               memcpy(s->s3->previous_server_finished,
+                   s->s3->hs.finished, s->s3->hs.finished_len);
+               s->s3->previous_server_finished_len = s->s3->hs.finished_len;
+
+               if (!ssl3_handshake_msg_start(s, &cbb, &finished,
+                   SSL3_MT_FINISHED))
+                        goto err;
+               if (!CBB_add_bytes(&finished, s->s3->hs.finished,
+                   s->s3->hs.finished_len))
+                       goto err;
+               if (!ssl3_handshake_msg_finish(s, &cbb))
+                       goto err;
+
+               s->s3->hs.state = SSL3_ST_SW_FINISHED_B;
+       }
+
+       return (ssl3_handshake_write(s));
+
+ err:
+       CBB_cleanup(&cbb);
+
+       return (-1);
+}