SSL asynchronous signature: basic test of recovery after error

Add test cases where the server goes through an async operation which
fails, then the server makes a successful connection.
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index 28f6367..2a4c833 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -203,7 +203,8 @@
     "    async_private_delay1=%%d  Asynchronous delay for key_file or preloaded key\n" \
     "    async_private_delay2=%%d  Asynchronous delay for key_file2\n" \
     "                              default: -1 (not asynchronous)\n" \
-    "    async_private_error=%%d   Async callback error injection (default=0=none, 1=start, 2=cancel, 3=resume, 4=pk)"
+    "    async_private_error=%%d   Async callback error injection (default=0=none,\n" \
+    "                              1=start, 2=cancel, 3=resume, 4=pk, negative=first time only)"
 #else
 #define USAGE_SSL_ASYNC ""
 #endif /* MBEDTLS_SSL_ASYNC_PRIVATE_C */
@@ -1238,7 +1239,8 @@
         else if( strcmp( p, "async_private_error" ) == 0 )
         {
             int n = atoi( q );
-            if( n < 0 || n > SSL_ASYNC_INJECT_ERROR_MAX )
+            if( n < -SSL_ASYNC_INJECT_ERROR_MAX ||
+                n > SSL_ASYNC_INJECT_ERROR_MAX )
             {
                 ret = 2;
                 goto usage;
@@ -2152,7 +2154,9 @@
 #if defined(MBEDTLS_SSL_ASYNC_PRIVATE_C)
     if( opt.async_private_delay1 >= 0 || opt.async_private_delay2 >= 0 )
     {
-        ssl_async_keys.inject_error = opt.async_private_error;
+        ssl_async_keys.inject_error = ( opt.async_private_error < 0 ?
+                                        - opt.async_private_error :
+                                        opt.async_private_error );
         ssl_async_keys.f_rng = mbedtls_ctr_drbg_random;
         ssl_async_keys.p_rng = &ctr_drbg;
         mbedtls_ssl_conf_async_private_cb( &conf,
@@ -2338,10 +2342,10 @@
         ret = mbedtls_ssl_handshake( &ssl );
 #if defined(MBEDTLS_SSL_ASYNC_PRIVATE_C)
         if( ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS &&
-            opt.async_private_error == SSL_ASYNC_INJECT_ERROR_CANCEL )
+            ssl_async_keys.inject_error == SSL_ASYNC_INJECT_ERROR_CANCEL )
         {
             mbedtls_printf( " cancelling on injected error\n" );
-            goto reset;
+            break;
         }
 #endif /* MBEDTLS_SSL_ASYNC_PRIVATE_C */
     }
@@ -2371,6 +2375,11 @@
         }
 #endif
 
+#if defined(MBEDTLS_SSL_ASYNC_PRIVATE_C)
+        if( opt.async_private_error < 0 )
+            /* Injected error only the first time round, to test reset */
+            ssl_async_keys.inject_error = SSL_ASYNC_INJECT_ERROR_NONE;
+#endif
         goto reset;
     }
     else /* ret == 0 */
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 5f23cd1..1ec1a14 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -3738,6 +3738,56 @@
             -s "! mbedtls_ssl_handshake returned"
 
 requires_config_enabled MBEDTLS_SSL_ASYNC_PRIVATE_C
+run_test    "SSL async private: cancel after start then operate correctly" \
+            "$P_SRV async_private_delay1=1 async_private_delay2=1 async_private_error=-2" \
+            "$P_CLI; [ \$? -eq 1 ] && $P_CLI" \
+            0 \
+            -s "Async cancel" \
+            -s "! mbedtls_ssl_handshake returned" \
+            -s "Async resume" \
+            -s "Successful connection"
+
+requires_config_enabled MBEDTLS_SSL_ASYNC_PRIVATE_C
+run_test    "SSL async private: error in resume then operate correctly" \
+            "$P_SRV async_private_delay1=1 async_private_delay2=1 async_private_error=-3" \
+            "$P_CLI; [ \$? -eq 1 ] && $P_CLI" \
+            0 \
+            -s "! mbedtls_ssl_handshake returned" \
+            -s "Async resume" \
+            -s "Successful connection"
+
+# key1: ECDSA, key2: RSA; use key1 through async, then key2 directly
+requires_config_enabled MBEDTLS_SSL_ASYNC_PRIVATE_C
+run_test    "SSL async private: cancel after start then fall back to transparent key" \
+            "$P_SRV key_file=data_files/server5.key crt_file=data_files/server5.crt \
+             key_file2=data_files/server2.key crt_file2=data_files/server2.crt \
+             async_private_delay1=1 async_private_error=-2" \
+            "$P_CLI force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256;
+             [ \$? -eq 1 ] &&
+             $P_CLI force_ciphersuite=TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256" \
+            0 \
+            -S "Async resume" \
+            -s "Async cancel" \
+            -s "! mbedtls_ssl_handshake returned" \
+            -s "Async sign callback: no key matches this certificate." \
+            -s "Successful connection"
+
+# key1: ECDSA, key2: RSA; use key1 through async, then key2 directly
+requires_config_enabled MBEDTLS_SSL_ASYNC_PRIVATE_C
+run_test    "SSL async private: error in resume then fall back to transparent key" \
+            "$P_SRV key_file=data_files/server5.key crt_file=data_files/server5.crt \
+             key_file2=data_files/server2.key crt_file2=data_files/server2.crt \
+             async_private_delay1=1 async_private_error=-3" \
+            "$P_CLI force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256;
+             [ \$? -eq 1 ] &&
+             $P_CLI force_ciphersuite=TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256" \
+            0 \
+            -s "Async resume" \
+            -s "! mbedtls_ssl_handshake returned" \
+            -s "Async sign callback: no key matches this certificate." \
+            -s "Successful connection"
+
+requires_config_enabled MBEDTLS_SSL_ASYNC_PRIVATE_C
 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION
 run_test    "SSL async private: renegotiation: client-initiated" \
             "$P_SRV async_private_delay1=1 async_private_delay2=1