ssl_tls13_client: Add downgrade attack protection

Signed-off-by: Ronald Cron <ronald.cron@arm.com>
diff --git a/library/ssl_tls13_client.c b/library/ssl_tls13_client.c
index b05d2f2..6427259 100644
--- a/library/ssl_tls13_client.c
+++ b/library/ssl_tls13_client.c
@@ -711,6 +711,7 @@
 /*
  * Functions for parsing and processing Server Hello
  */
+
 /**
  * \brief Detect if the ServerHello contains a supported_versions extension
  *        or not.
@@ -793,6 +794,36 @@
 }
 
 /* Returns a negative value on failure, and otherwise
+ * - 1 if the last eight bytes of the ServerHello random bytes indicate that
+ *     the server is TLS 1.3 capable but negotiating TLS 1.2 or below.
+ * - 0 otherwise
+ */
+static int ssl_tls13_is_downgrade_negotiation( mbedtls_ssl_context *ssl,
+                                               const unsigned char *buf,
+                                               const unsigned char *end )
+{
+    /* First seven bytes of the magic downgrade strings, see RFC 8446 4.1.3 */
+    static const unsigned char magic_downgrade_string[] =
+        { 0x44, 0x4F, 0x57, 0x4E, 0x47, 0x52, 0x44 };
+    const unsigned char *last_eight_bytes_of_random;
+    unsigned char last_byte_of_random;
+
+    MBEDTLS_SSL_CHK_BUF_READ_PTR( buf, end, MBEDTLS_SERVER_HELLO_RANDOM_LEN + 2 );
+    last_eight_bytes_of_random = buf + 2 + MBEDTLS_SERVER_HELLO_RANDOM_LEN - 8;
+
+    if( memcmp( last_eight_bytes_of_random,
+                magic_downgrade_string,
+                sizeof( magic_downgrade_string ) ) == 0 )
+    {
+        last_byte_of_random = last_eight_bytes_of_random[7];
+        return( last_byte_of_random == 0 ||
+                last_byte_of_random == 1    );
+    }
+
+    return( 0 );
+}
+
+/* Returns a negative value on failure, and otherwise
  * - SSL_SERVER_HELLO_COORDINATE_HELLO or
  * - SSL_SERVER_HELLO_COORDINATE_HRR
  * to indicate which message is expected and to be parsed next.
@@ -846,20 +877,27 @@
                                               size_t *buf_len )
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    const unsigned char *end;
 
     MBEDTLS_SSL_PROC_CHK( mbedtls_ssl_tls13_fetch_handshake_msg( ssl,
                                              MBEDTLS_SSL_HS_SERVER_HELLO,
                                              buf, buf_len ) );
+    end = *buf + *buf_len;
 
     MBEDTLS_SSL_PROC_CHK_NEG( ssl_tls13_is_supported_versions_ext_present(
-                                  ssl, *buf, *buf + *buf_len ) );
+                                  ssl, *buf, end ) );
     if( ret == 0 )
     {
-        /* If the supported versions extension is not present but we were
-         * expecting it, abort the handshake. Otherwise, switch to TLS 1.2
-         * handshake.
+        MBEDTLS_SSL_PROC_CHK_NEG(
+            ssl_tls13_is_downgrade_negotiation( ssl, *buf, end ) );
+
+        /* If the server is negotiating TLS 1.2 or below and:
+         * . we did not propose TLS 1.2 or
+         * . the server responded it is TLS 1.3 capable but negotiating a lower
+         *   version of the protocol and thus we are under downgrade attack
+         * abort the handshake with an "illegal parameter" alert.
          */
-        if( ssl->handshake->min_tls_version > MBEDTLS_SSL_VERSION_TLS1_2 )
+        if( ssl->handshake->min_tls_version > MBEDTLS_SSL_VERSION_TLS1_2 || ret )
         {
             MBEDTLS_SSL_PEND_FATAL_ALERT( MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER,
                                           MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER );
@@ -881,7 +919,7 @@
         return( SSL_SERVER_HELLO_COORDINATE_TLS1_2 );
     }
 
-    ret = ssl_server_hello_is_hrr( ssl, *buf, *buf + *buf_len );
+    ret = ssl_server_hello_is_hrr( ssl, *buf, end );
     switch( ret )
     {
         case SSL_SERVER_HELLO_COORDINATE_HELLO: