SSL asynchronous signature: first implementation

Implement SSL asynchronous private operation for the case of a
signature operation in a server.

This is a first implementation. It is functional, but the code is not
clean, with heavy reliance on goto.
diff --git a/library/ssl_srv.c b/library/ssl_srv.c
index 84c8e1e..ac9e6a5 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -2843,6 +2843,17 @@
 
     MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> write server key exchange" ) );
 
+#if defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED) && \
+    defined(MBEDTLS_SSL_ASYNC_PRIVATE_C)
+    if( ssl->handshake->out_async_start != NULL )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 2, ( "resuming signature operation" ) );
+        p = ssl->handshake->out_async_start;
+        goto async_resume;
+    }
+#endif /* defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED) &&
+          defined(MBEDTLS_SSL_ASYNC_PRIVATE_C) */
+
     /*
      *
      * Part 1: Extract static ECDH parameters and abort
@@ -3169,12 +3180,6 @@
         /*
          * 3.3: Compute and add the signature
          */
-        if( mbedtls_ssl_own_key( ssl ) == NULL )
-        {
-            MBEDTLS_SSL_DEBUG_MSG( 1, ( "got no private key" ) );
-            return( MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED );
-        }
-
 #if defined(MBEDTLS_SSL_PROTO_TLS1_2)
         if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_3 )
         {
@@ -3199,6 +3204,55 @@
         }
 #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */
 
+#if defined(MBEDTLS_SSL_ASYNC_PRIVATE_C)
+        if( ssl->conf->f_async_sign_start != NULL )
+        {
+            size_t sig_max_len = ( ssl->out_buf + MBEDTLS_SSL_MAX_CONTENT_LEN
+                                   - ( p + 2 ) );
+            ret = ssl->conf->f_async_sign_start(
+                ssl->conf->p_async_connection_ctx,
+                &ssl->handshake->p_async_operation_ctx,
+                mbedtls_ssl_own_cert( ssl ),
+                md_alg, hash, hashlen );
+            switch( ret )
+            {
+            case MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH:
+                /* act as if f_async_sign was null */
+                break;
+            case 0:
+            async_resume:
+                ret = ssl->conf->f_async_resume(
+                    ssl->conf->p_async_connection_ctx,
+                    ssl->handshake->p_async_operation_ctx,
+                    p + 2, &signature_len, sig_max_len );
+                if( ret != MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS )
+                {
+                    ssl->handshake->p_async_operation_ctx = NULL;
+                    if( ret != 0 )
+                    {
+                        MBEDTLS_SSL_DEBUG_RET( 1, "f_async_resume", ret );
+                        return( ret );
+                    }
+                    goto have_signature;
+                }
+                /* FALLTHROUGH */
+            case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
+                ssl->handshake->out_async_start = p;
+                MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= write server key exchange (pending)" ) );
+                return( MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS );
+            default:
+                MBEDTLS_SSL_DEBUG_RET( 1, "f_async_sign", ret );
+                return( ret );
+            }
+        }
+#endif /* MBEDTLS_SSL_ASYNC_PRIVATE_C */
+
+        if( mbedtls_ssl_own_key( ssl ) == NULL )
+        {
+            MBEDTLS_SSL_DEBUG_MSG( 1, ( "got no private key" ) );
+            return( MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED );
+        }
+
         if( ( ret = mbedtls_pk_sign( mbedtls_ssl_own_key( ssl ), md_alg, hash, hashlen,
                         p + 2 , &signature_len, ssl->conf->f_rng, ssl->conf->p_rng ) ) != 0 )
         {
@@ -3206,6 +3260,7 @@
             return( ret );
         }
 
+    have_signature:
         *(p++) = (unsigned char)( signature_len >> 8 );
         *(p++) = (unsigned char)( signature_len      );