Implement support for remembering CCS messages

This commit implements support for remembering out-of-order
CCS messages. Specifically, a flag is set whenever a CCS message
is read which remains until the end of a flight, and when a
CCS message is expected and a CCS message has been seen in the
current flight, a synthesized CCS record is created.
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 41292a5..6a44145 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -3069,6 +3069,9 @@
     /* The next incoming flight will start with this msg_seq */
     ssl->handshake->in_flight_start_seq = ssl->handshake->in_msg_seq;
 
+    /* We don't want to remember CCS's across flight boundaries. */
+    ssl->handshake->seen_ccs = 0;
+
     /* Cancel timer */
     ssl_set_timer( ssl, 0 );
 
@@ -4138,15 +4141,6 @@
         }
 #endif
 
-        /* Drop unexpected ChangeCipherSpec messages */
-        if( ssl->in_msgtype == MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC &&
-            ssl->state != MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC &&
-            ssl->state != MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC )
-        {
-            MBEDTLS_SSL_DEBUG_MSG( 1, ( "dropping unexpected ChangeCipherSpec" ) );
-            return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
-        }
-
         /* Drop unexpected ApplicationData records,
          * except at the beginning of renegotiations */
         if( ssl->in_msgtype == MBEDTLS_SSL_MSG_APPLICATION_DATA &&
@@ -4390,16 +4384,75 @@
 
 static int ssl_load_buffered_message( mbedtls_ssl_context *ssl )
 {
-    /* No buffering support so far. */
-    ((void) ssl );
-    return( -1 );
+    mbedtls_ssl_handshake_params * const hs = ssl->handshake;
+    int ret = 0;
+
+    MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> ssl_load_buffered_messsage" ) );
+
+    if( hs == NULL )
+        return( -1 );
+
+    if( ssl->state == MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC ||
+        ssl->state == MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC )
+    {
+        /* Check if we have seen a ChangeCipherSpec before.
+         * If yes, synthesize a CCS record. */
+        if( ! hs->seen_ccs )
+        {
+            MBEDTLS_SSL_DEBUG_MSG( 2, ( "CCS not seen in the current flight" ) );
+            ret = -1;
+            goto exit;
+        }
+
+        MBEDTLS_SSL_DEBUG_MSG( 2, ( "Inject buffered CCS message" ) );
+        ssl->in_msgtype = MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC;
+        ssl->in_msglen = 1;
+        ssl->in_msg[0] = 1;
+
+        /* As long as they are equal, the exact value doesn't matter. */
+        ssl->in_left            = 0;
+        ssl->next_record_offset = 0;
+
+        hs->seen_ccs = 0;
+        goto exit;
+    }
+    ret = -1;
+
+exit:
+
+    MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= ssl_load_buffered_message" ) );
+    return( ret );
 }
 
 static int ssl_buffer_message( mbedtls_ssl_context *ssl )
 {
-    /* No buffering support so far. */
-    ((void) ssl );
-    return( 0 );
+    int ret = 0;
+    mbedtls_ssl_handshake_params * const hs = ssl->handshake;
+
+    if( hs == NULL )
+        return( 0 );
+
+    MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> ssl_buffer_message" ) );
+
+    switch( ssl->in_msgtype )
+    {
+        case MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC:
+            MBEDTLS_SSL_DEBUG_MSG( 2, ( "Remember CCS message" ) );
+            hs->seen_ccs = 1;
+            break;
+
+        case MBEDTLS_SSL_MSG_HANDSHAKE:
+            /* No support for buffering handshake messages so far. */
+            break;
+
+        default:
+            break;
+    }
+
+exit:
+
+    MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= ssl_buffer_message" ) );
+    return( ret );
 }
 #endif /* MBEDTLS_SSL_PROTO_DTLS */
 
@@ -4649,6 +4702,24 @@
         }
     }
 
+#if defined(MBEDTLS_SSL_PROTO_DTLS)
+    /* Drop unexpected ChangeCipherSpec messages */
+    if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
+        ssl->in_msgtype == MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC  &&
+        ssl->state != MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC    &&
+        ssl->state != MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC )
+    {
+        if( ssl->handshake == NULL )
+        {
+            MBEDTLS_SSL_DEBUG_MSG( 1, ( "dropping ChangeCipherSpec outside handshake" ) );
+            return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
+        }
+
+        MBEDTLS_SSL_DEBUG_MSG( 1, ( "received out-of-order ChangeCipherSpec - remember" ) );
+        return( MBEDTLS_ERR_SSL_EARLY_MESSAGE );
+    }
+#endif
+
     if( ssl->in_msgtype == MBEDTLS_SSL_MSG_ALERT )
     {
         if( ssl->in_msglen != 2 )