Merge pull request #5707 from yuhaoth/pr/add-tls13-write-hello-retry-request

TLS1.3: Add  HelloRetryRequest Write
diff --git a/library/ssl_debug_helpers.h b/library/ssl_debug_helpers.h
index 29b64dc..9f1df73 100644
--- a/library/ssl_debug_helpers.h
+++ b/library/ssl_debug_helpers.h
@@ -41,6 +41,8 @@
 
 const char *mbedtls_ssl_sig_alg_to_str( uint16_t in );
 
+const char *mbedtls_ssl_named_group_to_str( uint16_t in );
+
 #endif /* MBEDTLS_DEBUG_C */
 
 #endif /* SSL_DEBUG_HELPERS_H */
diff --git a/library/ssl_misc.h b/library/ssl_misc.h
index c2c3cd2..38453eb 100644
--- a/library/ssl_misc.h
+++ b/library/ssl_misc.h
@@ -585,17 +585,14 @@
      */
 #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
     int tls13_kex_modes; /*!< key exchange modes for TLS 1.3 */
-#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
 
-#if defined(MBEDTLS_SSL_CLI_C)
-    /** Number of Hello Retry Request messages received from the server.  */
+    /** Number of HelloRetryRequest messages received/sent from/to the server. */
     int hello_retry_request_count;
-#endif /* MBEDTLS_SSL_CLI_C */
-
 #if defined(MBEDTLS_SSL_SRV_C)
     /** selected_group of key_share extension in HelloRetryRequest message. */
     uint16_t hrr_selected_group;
 #endif /* MBEDTLS_SSL_SRV_C */
+#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */
 
 #if defined(MBEDTLS_SSL_PROTO_TLS1_2) && \
     defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED)
@@ -1634,7 +1631,8 @@
 #endif /* MBEDTLS_SSL_PROTO_TLS1_2 && MBEDTLS_SSL_PROTO_TLS1_3 */
 
 #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
-
+extern const uint8_t mbedtls_ssl_tls13_hello_retry_request_magic[
+                        MBEDTLS_SERVER_HELLO_RANDOM_LEN ];
 int mbedtls_ssl_tls13_process_finished_message( mbedtls_ssl_context *ssl );
 int mbedtls_ssl_tls13_write_finished_message( mbedtls_ssl_context *ssl );
 void mbedtls_ssl_tls13_handshake_wrapup( mbedtls_ssl_context *ssl );
diff --git a/library/ssl_tls13_client.c b/library/ssl_tls13_client.c
index 59e42c8..320c5b4 100644
--- a/library/ssl_tls13_client.c
+++ b/library/ssl_tls13_client.c
@@ -750,11 +750,6 @@
                                     const unsigned char *buf,
                                     const unsigned char *end )
 {
-    static const unsigned char magic_hrr_string[MBEDTLS_SERVER_HELLO_RANDOM_LEN] =
-        { 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11,
-          0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
-          0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E,
-          0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33 ,0x9C };
 
     /* Check whether this message is a HelloRetryRequest ( HRR ) message.
      *
@@ -771,9 +766,11 @@
      * } ServerHello;
      *
      */
-    MBEDTLS_SSL_CHK_BUF_READ_PTR( buf, end, 2 + sizeof( magic_hrr_string ) );
+    MBEDTLS_SSL_CHK_BUF_READ_PTR( buf, end,
+                    2 + sizeof( mbedtls_ssl_tls13_hello_retry_request_magic ) );
 
-    if( memcmp( buf + 2, magic_hrr_string, sizeof( magic_hrr_string ) ) == 0 )
+    if( memcmp( buf + 2, mbedtls_ssl_tls13_hello_retry_request_magic,
+                sizeof( mbedtls_ssl_tls13_hello_retry_request_magic ) ) == 0 )
     {
         return( SSL_SERVER_HELLO_COORDINATE_HRR );
     }
diff --git a/library/ssl_tls13_generic.c b/library/ssl_tls13_generic.c
index f5d791f..06ee46b 100644
--- a/library/ssl_tls13_generic.c
+++ b/library/ssl_tls13_generic.c
@@ -34,6 +34,13 @@
 #include "ssl_tls13_keys.h"
 #include "ssl_debug_helpers.h"
 
+const uint8_t mbedtls_ssl_tls13_hello_retry_request_magic[
+                MBEDTLS_SERVER_HELLO_RANDOM_LEN ] =
+                    { 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11,
+                      0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
+                      0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E,
+                      0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C };
+
 int mbedtls_ssl_tls13_fetch_handshake_msg( mbedtls_ssl_context *ssl,
                                            unsigned hs_type,
                                            unsigned char **buf,
diff --git a/library/ssl_tls13_server.c b/library/ssl_tls13_server.c
index 775443c..46a6a49 100644
--- a/library/ssl_tls13_server.c
+++ b/library/ssl_tls13_server.c
@@ -106,9 +106,9 @@
  *       NamedGroup named_group_list<2..2^16-1>;
  *   } NamedGroupList;
  */
-static int ssl_tls13_parse_supported_groups_ext(
-                mbedtls_ssl_context *ssl,
-                const unsigned char *buf, const unsigned char *end )
+static int ssl_tls13_parse_supported_groups_ext( mbedtls_ssl_context *ssl,
+                                                 const unsigned char *buf,
+                                                 const unsigned char *end )
 {
     const unsigned char *p = buf;
     size_t named_group_list_len;
@@ -129,7 +129,10 @@
         named_group = MBEDTLS_GET_UINT16_BE( p, 0 );
         p += 2;
 
-        MBEDTLS_SSL_DEBUG_MSG( 2, ( "got named group: %d", named_group ) );
+        MBEDTLS_SSL_DEBUG_MSG( 2,
+                               ( "got named group: %s(%04x)",
+                                 mbedtls_ssl_named_group_to_str( named_group ),
+                                 named_group ) );
 
         if( ! mbedtls_ssl_named_group_is_offered( ssl, named_group ) ||
             ! mbedtls_ssl_named_group_is_supported( named_group ) ||
@@ -138,9 +141,11 @@
             continue;
         }
 
-        MBEDTLS_SSL_DEBUG_MSG(
-                2, ( "add named group (%04x) into received list.",
-                     named_group ) );
+        MBEDTLS_SSL_DEBUG_MSG( 2,
+                               ( "add named group %s(%04x) into received list.",
+                                 mbedtls_ssl_named_group_to_str( named_group ),
+                                 named_group ) );
+
         ssl->handshake->hrr_selected_group = named_group;
     }
 
@@ -162,8 +167,7 @@
  *    does not match a group supported by the server. A HelloRetryRequest will
  *    be needed.
  *  - A negative value for fatal errors.
-*/
-
+ */
 static int ssl_tls13_parse_key_shares_ext( mbedtls_ssl_context *ssl,
                                            const unsigned char *buf,
                                            const unsigned char *end )
@@ -171,8 +175,7 @@
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     unsigned char const *p = buf;
     unsigned char const *client_shares_end;
-    size_t client_shares_len, key_exchange_len;
-    int match_found = 0;
+    size_t client_shares_len;
 
     /* From RFC 8446:
      *
@@ -196,9 +199,11 @@
      * dismiss it and send a HelloRetryRequest message.
      */
 
-    for( ; p < client_shares_end; p += key_exchange_len )
+    while( p < client_shares_end )
     {
         uint16_t group;
+        size_t key_exchange_len;
+        const unsigned char *key_exchange;
 
         /*
          * struct {
@@ -208,19 +213,18 @@
          */
         MBEDTLS_SSL_CHK_BUF_READ_PTR( p, client_shares_end, 4 );
         group = MBEDTLS_GET_UINT16_BE( p, 0 );
-        p += 2;
-        key_exchange_len = MBEDTLS_GET_UINT16_BE( p, 0 );
-        p += 2;
+        key_exchange_len = MBEDTLS_GET_UINT16_BE( p, 2 );
+        p += 4;
+        key_exchange = p;
         MBEDTLS_SSL_CHK_BUF_READ_PTR( p, client_shares_end, key_exchange_len );
+        p += key_exchange_len;
 
         /* Continue parsing even if we have already found a match,
          * for input validation purposes.
          */
-        if( match_found == 1 )
-            continue;
-
         if( ! mbedtls_ssl_named_group_is_offered( ssl, group ) ||
-            ! mbedtls_ssl_named_group_is_supported( group ) )
+            ! mbedtls_ssl_named_group_is_supported( group ) ||
+            ssl->handshake->offered_group_id != 0 )
         {
             continue;
         }
@@ -230,16 +234,14 @@
          */
         if( mbedtls_ssl_tls13_named_group_is_ecdhe( group ) )
         {
-            const mbedtls_ecp_curve_info *curve_info =
-                    mbedtls_ecp_curve_info_from_tls_id( group );
-            ((void) curve_info);
-            MBEDTLS_SSL_DEBUG_MSG( 2, ( "ECDH curve: %s", curve_info->name ) );
+            MBEDTLS_SSL_DEBUG_MSG( 2, ( "ECDH group: %s (%04x)",
+                                        mbedtls_ssl_named_group_to_str( group ),
+                                        group ) );
             ret = mbedtls_ssl_tls13_read_public_ecdhe_share(
-                    ssl, p - 2, key_exchange_len + 2 );
+                      ssl, key_exchange - 2, key_exchange_len + 2 );
             if( ret != 0 )
                 return( ret );
 
-            match_found = 1;
         }
         else
         {
@@ -251,7 +253,8 @@
         ssl->handshake->offered_group_id = group;
     }
 
-    if( match_found == 0 )
+
+    if( ssl->handshake->offered_group_id == 0 )
     {
         MBEDTLS_SSL_DEBUG_MSG( 1, ( "no matching key share" ) );
         return( SSL_TLS1_3_PARSE_KEY_SHARES_EXT_NO_MATCH );
@@ -388,6 +391,7 @@
     const unsigned char *cipher_suites_end;
     size_t extensions_len;
     const unsigned char *extensions_end;
+    int hrr_required = 0;
 
     const mbedtls_ssl_ciphersuite_t* ciphersuite_info;
 
@@ -435,9 +439,9 @@
      */
     ssl->tls_version = MBEDTLS_SSL_VERSION_TLS1_3;
 
-    /* ---
-     *  Random random;
-     * ---
+    /* ...
+     * Random random;
+     * ...
      * with Random defined as:
      * opaque Random[32];
      */
@@ -447,9 +451,9 @@
     memcpy( &ssl->handshake->randbytes[0], p, MBEDTLS_CLIENT_HELLO_RANDOM_LEN );
     p += MBEDTLS_CLIENT_HELLO_RANDOM_LEN;
 
-    /* ---
+    /* ...
      * opaque legacy_session_id<0..32>;
-     * ---
+     * ...
      */
     legacy_session_id_len = p[0];
     p++;
@@ -480,9 +484,9 @@
      */
     MBEDTLS_SSL_CHK_BUF_READ_PTR( p, end, cipher_suites_len + 2 + 2 );
 
-   /* ---
+   /* ...
     * CipherSuite cipher_suites<2..2^16-2>;
-    * ---
+    * ...
     * with CipherSuite defined as:
     * uint8 CipherSuite[2];
     */
@@ -504,10 +508,12 @@
          * Check whether this ciphersuite is valid and offered.
          */
         if( ( mbedtls_ssl_validate_ciphersuite(
-            ssl, ciphersuite_info, ssl->tls_version,
-            ssl->tls_version ) != 0 ) ||
-            !mbedtls_ssl_tls13_cipher_suite_is_offered( ssl, cipher_suite ) )
+                ssl, ciphersuite_info, ssl->tls_version,
+                ssl->tls_version ) != 0 ) ||
+            ! mbedtls_ssl_tls13_cipher_suite_is_offered( ssl, cipher_suite ) )
+        {
             continue;
+        }
 
         ssl->session_negotiate->ciphersuite = cipher_suite;
         ssl->handshake->ciphersuite_info = ciphersuite_info;
@@ -517,7 +523,7 @@
 
     }
 
-    if( !ciphersuite_match )
+    if( ! ciphersuite_match )
     {
         MBEDTLS_SSL_PEND_FATAL_ALERT( MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE,
                                       MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE );
@@ -528,6 +534,7 @@
                                 ciphersuite_info->name ) );
 
     p = cipher_suites + cipher_suites_len;
+
     /* ...
      * opaque legacy_compression_methods<1..2^8-1>;
      * ...
@@ -541,9 +548,9 @@
     }
     p += 2;
 
-    /* ---
+    /* ...
      * Extension extensions<8..2^16-1>;
-     * ---
+     * ...
      * with Extension defined as:
      * struct {
      *    ExtensionType extension_type;
@@ -583,8 +590,8 @@
                  * indicates the named groups which the client supports,
                  * ordered from most preferred to least preferred.
                  */
-                ret = ssl_tls13_parse_supported_groups_ext( ssl, p,
-                            extension_data_end );
+                ret = ssl_tls13_parse_supported_groups_ext(
+                          ssl, p, extension_data_end );
                 if( ret != 0 )
                 {
                     MBEDTLS_SSL_DEBUG_RET( 1,
@@ -607,15 +614,20 @@
                  * contains the endpoint's cryptographic parameters for
                  * ECDHE/DHE key establishment methods.
                  */
-                ret = ssl_tls13_parse_key_shares_ext( ssl, p, extension_data_end );
+                ret = ssl_tls13_parse_key_shares_ext(
+                          ssl, p, extension_data_end );
                 if( ret == SSL_TLS1_3_PARSE_KEY_SHARES_EXT_NO_MATCH )
                 {
                     MBEDTLS_SSL_DEBUG_MSG( 2, ( "HRR needed " ) );
-                    ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE;
+                    hrr_required = 1;
                 }
 
-                if( ret != 0 )
+                if( ret < 0 )
+                {
+                    MBEDTLS_SSL_DEBUG_RET(
+                        1, "ssl_tls13_parse_key_shares_ext", ret );
                     return( ret );
+                }
 
                 ssl->handshake->extensions_present |= MBEDTLS_SSL_EXT_KEY_SHARE;
                 break;
@@ -625,7 +637,7 @@
                 MBEDTLS_SSL_DEBUG_MSG( 3, ( "found supported versions extension" ) );
 
                 ret = ssl_tls13_parse_supported_versions_ext(
-                        ssl, p, extension_data_end );
+                          ssl, p, extension_data_end );
                 if( ret != 0 )
                 {
                     MBEDTLS_SSL_DEBUG_RET( 1,
@@ -639,8 +651,8 @@
             case MBEDTLS_TLS_EXT_SIG_ALG:
                 MBEDTLS_SSL_DEBUG_MSG( 3, ( "found signature_algorithms extension" ) );
 
-                ret = mbedtls_ssl_tls13_parse_sig_alg_ext( ssl, p,
-                                                           extension_data_end );
+                ret = mbedtls_ssl_tls13_parse_sig_alg_ext(
+                          ssl, p, extension_data_end );
                 if( ret != 0 )
                 {
                     MBEDTLS_SSL_DEBUG_MSG( 1,
@@ -687,9 +699,11 @@
         return( MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER );
     }
 
-    return( 0 );
+    return( hrr_required ? SSL_CLIENT_HELLO_HRR_REQUIRED : SSL_CLIENT_HELLO_OK );
 }
 
+/* Update the handshake state machine */
+
 static int ssl_tls13_postprocess_client_hello( mbedtls_ssl_context* ssl )
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
@@ -716,6 +730,8 @@
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     unsigned char* buf = NULL;
     size_t buflen = 0;
+    int parse_client_hello_ret;
+
     MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> parse client hello" ) );
 
     MBEDTLS_SSL_PROC_CHK( mbedtls_ssl_tls13_fetch_handshake_msg(
@@ -724,8 +740,18 @@
 
     MBEDTLS_SSL_PROC_CHK_NEG( ssl_tls13_parse_client_hello( ssl, buf,
                                                             buf + buflen ) );
+    parse_client_hello_ret = ret; /* Store return value of parse_client_hello,
+                                   * only SSL_CLIENT_HELLO_OK or
+                                   * SSL_CLIENT_HELLO_HRR_REQUIRED at this
+                                   * stage as negative error codes are handled
+                                   * by MBEDTLS_SSL_PROC_CHK_NEG. */
+
     MBEDTLS_SSL_PROC_CHK( ssl_tls13_postprocess_client_hello( ssl ) );
-    mbedtls_ssl_handshake_set_state( ssl, MBEDTLS_SSL_SERVER_HELLO );
+
+    if( parse_client_hello_ret == SSL_CLIENT_HELLO_OK )
+        mbedtls_ssl_handshake_set_state( ssl, MBEDTLS_SSL_SERVER_HELLO );
+    else
+        mbedtls_ssl_handshake_set_state( ssl, MBEDTLS_SSL_HELLO_RETRY_REQUEST );
 
 cleanup:
 
@@ -894,6 +920,7 @@
     if( ret != 0 )
         return( ret );
     p += key_exchange_length;
+
     MBEDTLS_PUT_UINT16_BE( key_exchange_length, server_share + 2, 0 );
 
     MBEDTLS_PUT_UINT16_BE( p - server_share, buf, 2 );
@@ -903,6 +930,68 @@
     return( 0 );
 }
 
+static int ssl_tls13_write_hrr_key_share_ext( mbedtls_ssl_context *ssl,
+                                              unsigned char *buf,
+                                              unsigned char *end,
+                                              size_t *out_len )
+{
+    uint16_t selected_group = ssl->handshake->hrr_selected_group;
+    /* key_share Extension
+     *
+     *  struct {
+     *    select (Handshake.msg_type) {
+     *      ...
+     *      case hello_retry_request:
+     *          NamedGroup selected_group;
+     *      ...
+     *    };
+     * } KeyShare;
+     */
+
+    *out_len = 0;
+
+    /*
+     * For a pure PSK key exchange, there is no group to agree upon. The purpose
+     * of the HRR is then to transmit a cookie to force the client to demonstrate
+     * reachability at their apparent network address (primarily useful for DTLS).
+     */
+    if( ! mbedtls_ssl_tls13_some_ephemeral_enabled( ssl ) )
+        return( 0 );
+
+    /* We should only send the key_share extension if the client's initial
+     * key share was not acceptable. */
+    if( ssl->handshake->offered_group_id != 0 )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 4, ( "Skip key_share extension in HRR" ) );
+        return( 0 );
+    }
+
+    if( selected_group == 0 )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 1, ( "no matching named group found" ) );
+        return( MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE );
+    }
+
+    /* Check if we have enough space:
+     * - extension_type         (2 bytes)
+     * - extension_data_length  (2 bytes)
+     * - selected_group         (2 bytes)
+     */
+    MBEDTLS_SSL_CHK_BUF_READ_PTR( buf, end, 6 );
+
+    MBEDTLS_PUT_UINT16_BE( MBEDTLS_TLS_EXT_KEY_SHARE, buf, 0 );
+    MBEDTLS_PUT_UINT16_BE( 2, buf, 2 );
+    MBEDTLS_PUT_UINT16_BE( selected_group, buf, 4 );
+
+    MBEDTLS_SSL_DEBUG_MSG( 3,
+        ( "HRR selected_group: %s (%x)",
+            mbedtls_ssl_named_group_to_str( selected_group ),
+            selected_group ) );
+
+    *out_len = 6;
+
+    return( 0 );
+}
 
 /*
  * Structure of ServerHello message:
@@ -919,12 +1008,13 @@
 static int ssl_tls13_write_server_hello_body( mbedtls_ssl_context *ssl,
                                               unsigned char *buf,
                                               unsigned char *end,
-                                              size_t *out_len )
+                                              size_t *out_len,
+                                              int is_hrr )
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     unsigned char *p = buf;
     unsigned char *p_extensions_len;
-    size_t output_len;               /* Length of buffer used by function */
+    size_t output_len;
 
     *out_len = 0;
 
@@ -945,8 +1035,16 @@
      * opaque Random[MBEDTLS_SERVER_HELLO_RANDOM_LEN];
      */
     MBEDTLS_SSL_CHK_BUF_PTR( p, end, MBEDTLS_SERVER_HELLO_RANDOM_LEN );
-    memcpy( p, &ssl->handshake->randbytes[MBEDTLS_CLIENT_HELLO_RANDOM_LEN],
-               MBEDTLS_SERVER_HELLO_RANDOM_LEN );
+    if( is_hrr )
+    {
+        memcpy( p, mbedtls_ssl_tls13_hello_retry_request_magic,
+                MBEDTLS_SERVER_HELLO_RANDOM_LEN );
+    }
+    else
+    {
+        memcpy( p, &ssl->handshake->randbytes[MBEDTLS_CLIENT_HELLO_RANDOM_LEN],
+                MBEDTLS_SERVER_HELLO_RANDOM_LEN );
+    }
     MBEDTLS_SSL_DEBUG_BUF( 3, "server hello, random bytes",
                            p, MBEDTLS_SERVER_HELLO_RANDOM_LEN );
     p += MBEDTLS_SERVER_HELLO_RANDOM_LEN;
@@ -1012,7 +1110,10 @@
 
     if( mbedtls_ssl_conf_tls13_some_ephemeral_enabled( ssl ) )
     {
-        ret = ssl_tls13_write_key_share_ext( ssl, p, end, &output_len );
+        if( is_hrr )
+            ret = ssl_tls13_write_hrr_key_share_ext( ssl, p, end, &output_len );
+        else
+            ret = ssl_tls13_write_key_share_ext( ssl, p, end, &output_len );
         if( ret != 0 )
             return( ret );
         p += output_len;
@@ -1065,7 +1166,8 @@
 
     MBEDTLS_SSL_PROC_CHK( ssl_tls13_write_server_hello_body( ssl, buf,
                                                              buf + buf_len,
-                                                             &msg_len ) );
+                                                             &msg_len,
+                                                             0 ) );
 
     mbedtls_ssl_add_hs_msg_to_checksum(
         ssl, MBEDTLS_SSL_HS_SERVER_HELLO, buf, msg_len );
@@ -1277,6 +1379,71 @@
 #endif /* MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED */
 
 /*
+ * Handler for MBEDTLS_SSL_HELLO_RETRY_REQUEST
+ */
+
+static int ssl_tls13_write_hello_retry_request_coordinate(
+                                                    mbedtls_ssl_context *ssl )
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    if( ssl->handshake->hello_retry_request_count > 0 )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 1, ( "Too many HRRs" ) );
+        MBEDTLS_SSL_PEND_FATAL_ALERT( MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE,
+                                      MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE );
+        return( MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE );
+    }
+
+    /*
+     * Create stateless transcript hash for HRR
+     */
+    MBEDTLS_SSL_DEBUG_MSG( 4, ( "Reset transcript for HRR" ) );
+    ret = mbedtls_ssl_reset_transcript_for_hrr( ssl );
+    if( ret != 0 )
+    {
+        MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_reset_transcript_for_hrr", ret );
+        return( ret );
+    }
+    mbedtls_ssl_session_reset_msg_layer( ssl, 0 );
+
+    return( 0 );
+}
+
+static int ssl_tls13_write_hello_retry_request( mbedtls_ssl_context *ssl )
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    unsigned char *buf;
+    size_t buf_len, msg_len;
+
+    MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> write hello retry request" ) );
+
+    MBEDTLS_SSL_PROC_CHK( ssl_tls13_write_hello_retry_request_coordinate( ssl ) );
+
+    MBEDTLS_SSL_PROC_CHK( mbedtls_ssl_start_handshake_msg(
+                              ssl, MBEDTLS_SSL_HS_SERVER_HELLO,
+                              &buf, &buf_len ) );
+
+    MBEDTLS_SSL_PROC_CHK( ssl_tls13_write_server_hello_body( ssl, buf,
+                                                             buf + buf_len,
+                                                             &msg_len,
+                                                             1 ) );
+    mbedtls_ssl_add_hs_msg_to_checksum(
+        ssl, MBEDTLS_SSL_HS_SERVER_HELLO, buf, msg_len );
+
+
+    MBEDTLS_SSL_PROC_CHK( mbedtls_ssl_finish_handshake_msg( ssl, buf_len,
+                                                            msg_len ) );
+
+    ssl->handshake->hello_retry_request_count++;
+
+    mbedtls_ssl_handshake_set_state( ssl, MBEDTLS_SSL_CLIENT_HELLO );
+
+cleanup:
+    MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= write hello retry request" ) );
+    return( ret );
+}
+
+/*
  * TLS 1.3 State Machine -- server side
  */
 int mbedtls_ssl_tls13_handshake_server_step( mbedtls_ssl_context *ssl )
@@ -1295,16 +1462,22 @@
         /* start state */
         case MBEDTLS_SSL_HELLO_REQUEST:
             mbedtls_ssl_handshake_set_state( ssl, MBEDTLS_SSL_CLIENT_HELLO );
-
             ret = 0;
             break;
 
         case MBEDTLS_SSL_CLIENT_HELLO:
-
             ret = ssl_tls13_process_client_hello( ssl );
             if( ret != 0 )
                 MBEDTLS_SSL_DEBUG_RET( 1, "ssl_tls13_process_client_hello", ret );
+            break;
 
+        case MBEDTLS_SSL_HELLO_RETRY_REQUEST:
+            ret = ssl_tls13_write_hello_retry_request( ssl );
+            if( ret != 0 )
+            {
+                MBEDTLS_SSL_DEBUG_RET( 1, "ssl_tls13_write_hello_retry_request", ret );
+                return( ret );
+            }
             break;
 
         case MBEDTLS_SSL_SERVER_HELLO:
diff --git a/scripts/generate_ssl_debug_helpers.py b/scripts/generate_ssl_debug_helpers.py
index 42e4fc8..4be6fd6 100755
--- a/scripts/generate_ssl_debug_helpers.py
+++ b/scripts/generate_ssl_debug_helpers.py
@@ -234,6 +234,7 @@
                            prototype=self._prototype)
         return body
 
+
 class SignatureAlgorithmDefinition:
     """
         Generate helper functions for signature algorithms.
@@ -267,6 +268,7 @@
 
     def span(self):
         return self._definitions[0].span()
+
     def __str__(self):
         """
             Generate function for translating value to string
@@ -277,7 +279,7 @@
             translation_table.append(
                 '\tcase {}:\n\t    return "{}";'.format(name,
                                                         name[len('MBEDTLS_TLS1_3_SIG_'):].lower())
-                )
+            )
 
         body = textwrap.dedent('''\
             const char *mbedtls_ssl_sig_alg_to_str( uint16_t in )
@@ -292,6 +294,65 @@
         body = body.format(translation_table='\n'.join(translation_table))
         return body
 
+
+class NamedGroupDefinition:
+    """
+        Generate helper functions for named group
+
+        It generates translation function from named group define to string.
+        Named group definition looks like:
+        #define MBEDTLS_SSL_IANA_TLS_GROUP_[ upper case named group ] [ value(hex) ]
+
+        Known limitation:
+        - the definitions SHOULD exist in same macro blocks.
+    """
+
+    @classmethod
+    def extract(cls, source_code, start=0, end=-1):
+        named_group_pattern = re.compile(r'#define\s+(?P<name>MBEDTLS_SSL_IANA_TLS_GROUP_\w+)\s+' +
+                                         r'(?P<value>0[xX][0-9a-fA-F]+)$',
+                                         re.MULTILINE | re.DOTALL)
+        matches = list(named_group_pattern.finditer(source_code, start, end))
+        if matches:
+            yield NamedGroupDefinition(source_code, definitions=matches)
+
+    def __init__(self, source_code, definitions=None):
+        if definitions is None:
+            definitions = []
+        assert isinstance(definitions, list) and definitions
+        self._definitions = definitions
+        self._source = source_code
+
+    def __repr__(self):
+        return 'NamedGroup({})'.format(self._definitions[0].span())
+
+    def span(self):
+        return self._definitions[0].span()
+
+    def __str__(self):
+        """
+            Generate function for translating value to string
+        """
+        translation_table = []
+        for m in self._definitions:
+            name = m.groupdict()['name']
+            iana_name = name[len('MBEDTLS_SSL_IANA_TLS_GROUP_'):].lower()
+            translation_table.append('\tcase {}:\n\t    return "{}";'.format(name, iana_name))
+
+        body = textwrap.dedent('''\
+            const char *mbedtls_ssl_named_group_to_str( uint16_t in )
+            {{
+                switch( in )
+                {{
+            {translation_table}
+                }};
+
+                return "UNKOWN";
+            }}''')
+        body = body.format(translation_table='\n'.join(translation_table))
+        return body
+
+
 OUTPUT_C_TEMPLATE = '''\
 /* Automatically generated by generate_ssl_debug_helpers.py. DO NOT EDIT. */
 
@@ -335,14 +396,16 @@
     """
         Generate functions of debug helps
     """
-    mbedtls_root = os.path.abspath(mbedtls_root or build_tree.guess_mbedtls_root())
+    mbedtls_root = os.path.abspath(
+        mbedtls_root or build_tree.guess_mbedtls_root())
     with open(os.path.join(mbedtls_root, 'include/mbedtls/ssl.h')) as f:
         source_code = remove_c_comments(f.read())
 
     definitions = dict()
     for start, instance in preprocess_c_source_code(source_code,
                                                     EnumDefinition,
-                                                    SignatureAlgorithmDefinition):
+                                                    SignatureAlgorithmDefinition,
+                                                    NamedGroupDefinition):
         if start in definitions:
             continue
         if isinstance(instance, EnumDefinition):
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 754d669..c9ec7b1 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -11299,6 +11299,25 @@
             -s "=> parse client hello" \
             -s "<= parse client hello"
 
+
+requires_config_enabled MBEDTLS_DEBUG_C
+requires_config_enabled MBEDTLS_SSL_CLI_C
+requires_config_enabled MBEDTLS_SSL_SRV_C
+requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3
+run_test "TLS 1.3: server: HRR check - mbedtls" \
+         "$P_SRV debug_level=4 force_version=tls13 curves=secp384r1" \
+         "$P_CLI debug_level=4 force_version=tls13 curves=secp256r1,secp384r1" \
+         1 \
+        -s "tls13 server state: MBEDTLS_SSL_CLIENT_HELLO" \
+        -s "tls13 server state: MBEDTLS_SSL_SERVER_HELLO" \
+        -s "tls13 server state: MBEDTLS_SSL_ENCRYPTED_EXTENSIONS" \
+        -s "tls13 server state: MBEDTLS_SSL_HELLO_RETRY_REQUEST" \
+        -c "client state: MBEDTLS_SSL_ENCRYPTED_EXTENSIONS" \
+        -s "selected_group: secp384r1" \
+        -s "SSL - The requested feature is not available" \
+        -s "=> write hello retry request" \
+        -s "<= write hello retry request"
+
 for i in opt-testcases/*.sh
 do
     TEST_SUITE_NAME=${i##*/}