Merge pull request #5532 from ronald-cron-arm/tls13_and_use_psa_crypto

Make TLS 1.3 compatible with MBEDTLS_USE_PSA_CRYPTO
diff --git a/ChangeLog.d/use-psa-ecdhe-curve.txt b/ChangeLog.d/use-psa-ecdhe-curve.txt
new file mode 100644
index 0000000..cc432bd
--- /dev/null
+++ b/ChangeLog.d/use-psa-ecdhe-curve.txt
@@ -0,0 +1,7 @@
+Bugfix
+   * Fix a bug in (D)TLS curve negotiation: when MBEDTLS_USE_PSA_CRYPTO was
+     enabled and an ECDHE-ECDSA or ECDHE-RSA key exchange was used, the
+     client would fail to check that the curve selected by the server for
+     ECDHE was indeed one that was offered. As a result, the client would
+     accept any curve that it supported, even if that curve was not allowed
+     according to its configuration.
diff --git a/include/mbedtls/psa_util.h b/include/mbedtls/psa_util.h
index c54c035..e38e2e3 100644
--- a/include/mbedtls/psa_util.h
+++ b/include/mbedtls/psa_util.h
@@ -258,85 +258,24 @@
     return( -1 );
 }
 
-#define MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH 1
+#define MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH \
+    PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE( PSA_VENDOR_ECC_MAX_CURVE_BITS )
 
-#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED)
-#if MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH < ( 2 * ( ( 192 + 7 ) / 8 ) + 1 )
-#undef MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH
-#define MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH ( 2 * ( ( 192 + 7 ) / 8 ) + 1 )
-#endif
-#endif /* MBEDTLS_ECP_DP_SECP192R1_ENABLED */
-
-#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED)
-#if MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH < ( 2 * ( ( 224 + 7 ) / 8 ) + 1 )
-#undef MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH
-#define MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH ( 2 * ( ( 224 + 7 ) / 8 ) + 1 )
-#endif
-#endif /* MBEDTLS_ECP_DP_SECP224R1_ENABLED */
-
-#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED)
-#if MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH < ( 2 * ( ( 256 + 7 ) / 8 ) + 1 )
-#undef MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH
-#define MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH ( 2 * ( ( 256 + 7 ) / 8 ) + 1 )
-#endif
-#endif /* MBEDTLS_ECP_DP_SECP256R1_ENABLED */
-
-#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED)
-#if MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH < ( 2 * ( ( 384 + 7 ) / 8 ) + 1 )
-#undef MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH
-#define MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH ( 2 * ( ( 384 + 7 ) / 8 ) + 1 )
-#endif
-#endif /* MBEDTLS_ECP_DP_SECP384R1_ENABLED */
-
-#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED)
-#if MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH < ( 2 * ( ( 521 + 7 ) / 8 ) + 1 )
-#undef MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH
-#define MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH ( 2 * ( ( 521 + 7 ) / 8 ) + 1 )
-#endif
-#endif /* MBEDTLS_ECP_DP_SECP521R1_ENABLED */
-
-#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED)
-#if MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH < ( 2 * ( ( 192 + 7 ) / 8 ) + 1 )
-#undef MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH
-#define MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH ( 2 * ( ( 192 + 7 ) / 8 ) + 1 )
-#endif
-#endif /* MBEDTLS_ECP_DP_SECP192K1_ENABLED */
-
-#if defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED)
-#if MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH < ( 2 * ( ( 224 + 7 ) / 8 ) + 1 )
-#undef MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH
-#define MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH ( 2 * ( ( 224 + 7 ) / 8 ) + 1 )
-#endif
-#endif /* MBEDTLS_ECP_DP_SECP224K1_ENABLED */
-
-#if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED)
-#if MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH < ( 2 * ( ( 256 + 7 ) / 8 ) + 1 )
-#undef MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH
-#define MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH ( 2 * ( ( 256 + 7 ) / 8 ) + 1 )
-#endif
-#endif /* MBEDTLS_ECP_DP_SECP256K1_ENABLED */
-
-#if defined(MBEDTLS_ECP_DP_BP256R1_ENABLED)
-#if MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH < ( 2 * ( ( 256 + 7 ) / 8 ) + 1 )
-#undef MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH
-#define MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH ( 2 * ( ( 256 + 7 ) / 8 ) + 1 )
-#endif
-#endif /* MBEDTLS_ECP_DP_BP256R1_ENABLED */
-
-#if defined(MBEDTLS_ECP_DP_BP384R1_ENABLED)
-#if MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH < ( 2 * ( ( 384 + 7 ) / 8 ) + 1 )
-#undef MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH
-#define MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH ( 2 * ( ( 384 + 7 ) / 8 ) + 1 )
-#endif
-#endif /* MBEDTLS_ECP_DP_BP384R1_ENABLED */
-
-#if defined(MBEDTLS_ECP_DP_BP512R1_ENABLED)
-#if MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH < ( 2 * ( ( 512 + 7 ) / 8 ) + 1 )
-#undef MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH
-#define MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH ( 2 * ( ( 512 + 7 ) / 8 ) + 1 )
-#endif
-#endif /* MBEDTLS_ECP_DP_BP512R1_ENABLED */
-
+/* This function transforms an ECC group identifier from
+ * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
+ * into a PSA ECC group identifier. */
+#if defined(MBEDTLS_ECP_C)
+static inline psa_key_type_t mbedtls_psa_parse_tls_ecc_group(
+    uint16_t tls_ecc_grp_reg_id, size_t *bits )
+{
+    const mbedtls_ecp_curve_info *curve_info =
+        mbedtls_ecp_curve_info_from_tls_id( tls_ecc_grp_reg_id );
+    if( curve_info == NULL )
+        return( 0 );
+    return( PSA_KEY_TYPE_ECC_KEY_PAIR(
+                mbedtls_ecc_group_to_psa( curve_info->grp_id, bits ) ) );
+}
+#endif /* MBEDTLS_ECP_C */
 
 /* Translations for PK layer */
 
@@ -366,63 +305,6 @@
     }
 }
 
-/* Translations for ECC */
-
-/* This function transforms an ECC group identifier from
- * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
- * into a PSA ECC group identifier. */
-#if defined(MBEDTLS_ECP_C)
-static inline psa_key_type_t mbedtls_psa_parse_tls_ecc_group(
-    uint16_t tls_ecc_grp_reg_id, size_t *bits )
-{
-    const mbedtls_ecp_curve_info *curve_info =
-        mbedtls_ecp_curve_info_from_tls_id( tls_ecc_grp_reg_id );
-    if( curve_info == NULL )
-        return( 0 );
-    return( PSA_KEY_TYPE_ECC_KEY_PAIR(
-                mbedtls_ecc_group_to_psa( curve_info->grp_id, bits ) ) );
-}
-#endif /* MBEDTLS_ECP_C */
-
-/* This function takes a buffer holding an EC public key
- * exported through psa_export_public_key(), and converts
- * it into an ECPoint structure to be put into a ClientKeyExchange
- * message in an ECDHE exchange.
- *
- * Both the present and the foreseeable future format of EC public keys
- * used by PSA have the ECPoint structure contained in the exported key
- * as a subbuffer, and the function merely selects this subbuffer instead
- * of making a copy.
- */
-static inline int mbedtls_psa_tls_psa_ec_to_ecpoint( unsigned char *src,
-                                                     size_t srclen,
-                                                     unsigned char **dst,
-                                                     size_t *dstlen )
-{
-    *dst = src;
-    *dstlen = srclen;
-    return( 0 );
-}
-
-/* This function takes a buffer holding an ECPoint structure
- * (as contained in a TLS ServerKeyExchange message for ECDHE
- * exchanges) and converts it into a format that the PSA key
- * agreement API understands.
- */
-static inline int mbedtls_psa_tls_ecpoint_to_psa_ec( unsigned char const *src,
-                                                     size_t srclen,
-                                                     unsigned char *dst,
-                                                     size_t dstlen,
-                                                     size_t *olen )
-{
-    if( srclen > dstlen )
-        return( MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL );
-
-    memcpy( dst, src, srclen );
-    *olen = srclen;
-    return( 0 );
-}
-
 #endif /* MBEDTLS_USE_PSA_CRYPTO */
 
 /* Expose whatever RNG the PSA subsystem uses to applications using the
diff --git a/library/aes.c b/library/aes.c
index 4afc3c4..d2b05e2 100644
--- a/library/aes.c
+++ b/library/aes.c
@@ -971,6 +971,7 @@
                            unsigned char output[16] )
 {
     AES_VALIDATE_RET( ctx != NULL );
+    AES_VALIDATE_RET( ctx->rk != NULL );
     AES_VALIDATE_RET( input != NULL );
     AES_VALIDATE_RET( output != NULL );
     AES_VALIDATE_RET( mode == MBEDTLS_AES_ENCRYPT ||
diff --git a/library/ssl_cli.c b/library/ssl_cli.c
index 825034a..694473f 100644
--- a/library/ssl_cli.c
+++ b/library/ssl_cli.c
@@ -2334,12 +2334,7 @@
 
     MBEDTLS_SSL_DEBUG_MSG( 2, ( "ECDH curve: %s", curve_info->name ) );
 
-#if defined(MBEDTLS_ECP_C)
     if( mbedtls_ssl_check_curve( ssl, grp_id ) != 0 )
-#else
-    if( ssl->handshake->ecdh_ctx.grp.nbits < 163 ||
-        ssl->handshake->ecdh_ctx.grp.nbits > 521 )
-#endif
         return( -1 );
 
     MBEDTLS_SSL_DEBUG_ECDH( 3, &ssl->handshake->ecdh_ctx,
@@ -2366,9 +2361,16 @@
     mbedtls_ssl_handshake_params *handshake = ssl->handshake;
 
     /*
-     * Parse ECC group
+     * struct {
+     *     ECParameters curve_params;
+     *     ECPoint      public;
+     * } ServerECDHParams;
+     *
+     *  1       curve_type (must be "named_curve")
+     *  2..3    NamedCurve
+     *  4       ECPoint.len
+     *  5+      ECPoint contents
      */
-
     if( end - *p < 4 )
         return( MBEDTLS_ERR_SSL_DECODE_ERROR );
 
@@ -2381,6 +2383,15 @@
     tls_id <<= 8;
     tls_id |= *(*p)++;
 
+    /* Check it's a curve we offered */
+    if( mbedtls_ssl_check_curve_tls_id( ssl, tls_id ) != 0 )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 2,
+            ( "bad server key exchange message (ECDHE curve): %u",
+              (unsigned) tls_id ) );
+        return( MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE );
+    }
+
     /* Convert EC group to PSA key type. */
     if( ( handshake->ecdh_psa_type =
           mbedtls_psa_parse_tls_ecc_group( tls_id, &ecdh_bits ) ) == 0 )
@@ -2391,24 +2402,18 @@
         return( MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER );
     handshake->ecdh_bits = (uint16_t) ecdh_bits;
 
-    /*
-     * Put peer's ECDH public key in the format understood by PSA.
-     */
-
+    /* Keep a copy of the peer's public key */
     ecpoint_len = *(*p)++;
     if( (size_t)( end - *p ) < ecpoint_len )
         return( MBEDTLS_ERR_SSL_DECODE_ERROR );
 
-    if( mbedtls_psa_tls_ecpoint_to_psa_ec(
-                                    *p, ecpoint_len,
-                                    handshake->ecdh_psa_peerkey,
-                                    sizeof( handshake->ecdh_psa_peerkey ),
-                                    &handshake->ecdh_psa_peerkey_len ) != 0 )
-    {
-        return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
-    }
+    if( ecpoint_len > sizeof( handshake->ecdh_psa_peerkey ) )
+        return( MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE );
 
+    memcpy( handshake->ecdh_psa_peerkey, *p, ecpoint_len );
+    handshake->ecdh_psa_peerkey_len = ecpoint_len;
     *p += ecpoint_len;
+
     return( 0 );
 }
 #endif /* MBEDTLS_USE_PSA_CRYPTO &&
@@ -3373,11 +3378,6 @@
 
         mbedtls_ssl_handshake_params *handshake = ssl->handshake;
 
-        unsigned char own_pubkey[MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH];
-        size_t own_pubkey_len;
-        unsigned char *own_pubkey_ecpoint;
-        size_t own_pubkey_ecpoint_len;
-
         header_len = 4;
 
         MBEDTLS_SSL_DEBUG_MSG( 1, ( "Perform PSA-based ECDH computation." ) );
@@ -3405,27 +3405,22 @@
         if( status != PSA_SUCCESS )
             return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
 
-        /* Export the public part of the ECDH private key from PSA
-         * and convert it to ECPoint format used in ClientKeyExchange. */
+        /* Export the public part of the ECDH private key from PSA.
+         * The export format is an ECPoint structure as expected by TLS,
+         * but we just need to add a length byte before that. */
+        unsigned char *own_pubkey = ssl->out_msg + header_len + 1;
+        unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN;
+        size_t own_pubkey_max_len = (size_t)( end - own_pubkey );
+        size_t own_pubkey_len;
+
         status = psa_export_public_key( handshake->ecdh_psa_privkey,
-                                        own_pubkey, sizeof( own_pubkey ),
+                                        own_pubkey, own_pubkey_max_len,
                                         &own_pubkey_len );
         if( status != PSA_SUCCESS )
             return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
 
-        if( mbedtls_psa_tls_psa_ec_to_ecpoint( own_pubkey,
-                                               own_pubkey_len,
-                                               &own_pubkey_ecpoint,
-                                               &own_pubkey_ecpoint_len ) != 0 )
-        {
-            return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
-        }
-
-        /* Copy ECPoint structure to outgoing message buffer. */
-        ssl->out_msg[header_len] = (unsigned char) own_pubkey_ecpoint_len;
-        memcpy( ssl->out_msg + header_len + 1,
-                own_pubkey_ecpoint, own_pubkey_ecpoint_len );
-        content_len = own_pubkey_ecpoint_len + 1;
+        ssl->out_msg[header_len] = (unsigned char) own_pubkey_len;
+        content_len = own_pubkey_len + 1;
 
         /* The ECDH secret is the premaster secret used for key derivation. */
 
diff --git a/library/ssl_misc.h b/library/ssl_misc.h
index 8cb9576..4d753c8 100644
--- a/library/ssl_misc.h
+++ b/library/ssl_misc.h
@@ -1314,6 +1314,7 @@
 unsigned char mbedtls_ssl_hash_from_md_alg( int md );
 int mbedtls_ssl_set_calc_verify_md( mbedtls_ssl_context *ssl, int md );
 
+int mbedtls_ssl_check_curve_tls_id( const mbedtls_ssl_context *ssl, uint16_t tls_id );
 #if defined(MBEDTLS_ECP_C)
 int mbedtls_ssl_check_curve( const mbedtls_ssl_context *ssl, mbedtls_ecp_group_id grp_id );
 #endif
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 72493cf..ae6f635 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -7216,18 +7216,16 @@
     }
 }
 
-#if defined(MBEDTLS_ECP_C)
 /*
  * Check if a curve proposed by the peer is in our list.
  * Return 0 if we're willing to use it, -1 otherwise.
  */
-int mbedtls_ssl_check_curve( const mbedtls_ssl_context *ssl, mbedtls_ecp_group_id grp_id )
+int mbedtls_ssl_check_curve_tls_id( const mbedtls_ssl_context *ssl, uint16_t tls_id )
 {
     const uint16_t *group_list = mbedtls_ssl_get_groups( ssl );
 
     if( group_list == NULL )
         return( -1 );
-    uint16_t tls_id = mbedtls_ecp_curve_info_from_grp_id(grp_id)->tls_id;
 
     for( ; *group_list != 0; group_list++ )
     {
@@ -7237,6 +7235,16 @@
 
     return( -1 );
 }
+
+#if defined(MBEDTLS_ECP_C)
+/*
+ * Same as mbedtls_ssl_check_curve_tls_id() but with a mbedtls_ecp_group_id.
+ */
+int mbedtls_ssl_check_curve( const mbedtls_ssl_context *ssl, mbedtls_ecp_group_id grp_id )
+{
+    uint16_t tls_id = mbedtls_ecp_curve_info_from_grp_id( grp_id )->tls_id;
+    return mbedtls_ssl_check_curve_tls_id( ssl, tls_id );
+}
 #endif /* MBEDTLS_ECP_C */
 
 #if defined(MBEDTLS_X509_CRT_PARSE_C)
diff --git a/programs/fuzz/common.c b/programs/fuzz/common.c
index e12ee3b..4d58402 100644
--- a/programs/fuzz/common.c
+++ b/programs/fuzz/common.c
@@ -60,8 +60,14 @@
     size_t i;
 
 #if defined(MBEDTLS_CTR_DRBG_C)
-    //use mbedtls_ctr_drbg_random to find bugs in it
-    ret = mbedtls_ctr_drbg_random(p_rng, output, output_len);
+    //mbedtls_ctr_drbg_random requires a valid mbedtls_ctr_drbg_context in p_rng
+    if( p_rng != NULL ) {
+        //use mbedtls_ctr_drbg_random to find bugs in it
+        ret = mbedtls_ctr_drbg_random(p_rng, output, output_len);
+    } else {
+        //fall through to pseudo-random
+        ret = 0;
+    }
 #else
     (void) p_rng;
     ret = 0;
diff --git a/programs/fuzz/fuzz_dtlsserver.c b/programs/fuzz/fuzz_dtlsserver.c
index 9a6e894..1aa757c 100644
--- a/programs/fuzz/fuzz_dtlsserver.c
+++ b/programs/fuzz/fuzz_dtlsserver.c
@@ -44,6 +44,13 @@
     unsigned char buf[4096];
     fuzzBufferOffset_t biomemfuzz;
 
+    mbedtls_ctr_drbg_init( &ctr_drbg );
+    mbedtls_entropy_init( &entropy );
+
+    if( mbedtls_ctr_drbg_seed( &ctr_drbg, dummy_entropy, &entropy,
+                               ( const unsigned char * ) pers, strlen( pers ) ) != 0 )
+        goto exit;
+
     if (initialized == 0) {
 #if defined(MBEDTLS_X509_CRT_PARSE_C) && defined(MBEDTLS_PEM_PARSE_C)
         mbedtls_x509_crt_init( &srvcert );
@@ -56,7 +63,7 @@
             return 1;
         if (mbedtls_pk_parse_key( &pkey, (const unsigned char *) mbedtls_test_srv_key,
                                  mbedtls_test_srv_key_len, NULL, 0,
-                                 dummy_random, NULL ) != 0)
+                                 dummy_random, &ctr_drbg ) != 0)
             return 1;
 #endif
         dummy_init();
@@ -65,15 +72,8 @@
     }
     mbedtls_ssl_init( &ssl );
     mbedtls_ssl_config_init( &conf );
-    mbedtls_ctr_drbg_init( &ctr_drbg );
-    mbedtls_entropy_init( &entropy );
     mbedtls_ssl_cookie_init( &cookie_ctx );
 
-    if( mbedtls_ctr_drbg_seed( &ctr_drbg, dummy_entropy, &entropy,
-                              (const unsigned char *) pers, strlen( pers ) ) != 0 )
-        goto exit;
-
-
     if( mbedtls_ssl_config_defaults( &conf,
                                     MBEDTLS_SSL_IS_SERVER,
                                     MBEDTLS_SSL_TRANSPORT_DATAGRAM,
diff --git a/programs/fuzz/fuzz_privkey.c b/programs/fuzz/fuzz_privkey.c
index b9a160e..e8e1d44 100644
--- a/programs/fuzz/fuzz_privkey.c
+++ b/programs/fuzz/fuzz_privkey.c
@@ -2,26 +2,41 @@
 
 #include <stdint.h>
 #include <stdlib.h>
+#include <string.h>
 #include "mbedtls/pk.h"
+#include "mbedtls/entropy.h"
+#include "mbedtls/ctr_drbg.h"
 #include "common.h"
 
 //4 Kb should be enough for every bug ;-)
 #define MAX_LEN 0x1000
 
+#if defined(MBEDTLS_PK_PARSE_C) && defined(MBEDTLS_CTR_DRBG_C)
+const char *pers = "fuzz_privkey";
+#endif // MBEDTLS_PK_PARSE_C && MBEDTLS_CTR_DRBG_C
 
 int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
-#ifdef MBEDTLS_PK_PARSE_C
+#if defined(MBEDTLS_PK_PARSE_C) && defined(MBEDTLS_CTR_DRBG_C)
     int ret;
     mbedtls_pk_context pk;
+    mbedtls_ctr_drbg_context ctr_drbg;
+    mbedtls_entropy_context entropy;
 
     if (Size > MAX_LEN) {
         //only work on small inputs
         Size = MAX_LEN;
     }
 
+    mbedtls_ctr_drbg_init( &ctr_drbg );
+    mbedtls_entropy_init( &entropy );
+
+    if( mbedtls_ctr_drbg_seed( &ctr_drbg, dummy_entropy, &entropy,
+                               ( const unsigned char * ) pers, strlen( pers ) ) != 0 )
+        return 1;
+
     mbedtls_pk_init( &pk );
     ret = mbedtls_pk_parse_key( &pk, Data, Size, NULL, 0,
-                                dummy_random, NULL );
+                                dummy_random, &ctr_drbg );
     if (ret == 0) {
 #if defined(MBEDTLS_RSA_C)
         if( mbedtls_pk_get_type( &pk ) == MBEDTLS_PK_RSA )
@@ -73,7 +88,7 @@
 #else
     (void) Data;
     (void) Size;
-#endif //MBEDTLS_PK_PARSE_C
+#endif // MBEDTLS_PK_PARSE_C && MBEDTLS_CTR_DRBG_C
 
     return 0;
 }
diff --git a/programs/fuzz/fuzz_server.c b/programs/fuzz/fuzz_server.c
index e6eb5a7..3d11d47 100644
--- a/programs/fuzz/fuzz_server.c
+++ b/programs/fuzz/fuzz_server.c
@@ -55,13 +55,14 @@
     }
     options = Data[Size - 1];
 
-    if (initialized == 0) {
-        mbedtls_ctr_drbg_init( &ctr_drbg );
-        mbedtls_entropy_init( &entropy );
+    mbedtls_ctr_drbg_init( &ctr_drbg );
+    mbedtls_entropy_init( &entropy );
 
-        if( mbedtls_ctr_drbg_seed( &ctr_drbg, dummy_entropy, &entropy,
-                                  (const unsigned char *) pers, strlen( pers ) ) != 0 )
-            return 1;
+    if( mbedtls_ctr_drbg_seed( &ctr_drbg, dummy_entropy, &entropy,
+                               ( const unsigned char * ) pers, strlen( pers ) ) != 0 )
+        return 1;
+
+    if (initialized == 0) {
 
 #if defined(MBEDTLS_X509_CRT_PARSE_C) && defined(MBEDTLS_PEM_PARSE_C)
         mbedtls_x509_crt_init( &srvcert );
diff --git a/tests/scripts/analyze_outcomes.py b/tests/scripts/analyze_outcomes.py
index 73f16bd..d06a059 100755
--- a/tests/scripts/analyze_outcomes.py
+++ b/tests/scripts/analyze_outcomes.py
@@ -7,7 +7,6 @@
 """
 
 import argparse
-import re
 import sys
 import traceback
 
@@ -51,29 +50,9 @@
         """
         return len(self.successes) + len(self.failures)
 
-class TestDescriptions(check_test_cases.TestDescriptionExplorer):
-    """Collect the available test cases."""
-
-    def __init__(self):
-        super().__init__()
-        self.descriptions = set()
-
-    def process_test_case(self, _per_file_state,
-                          file_name, _line_number, description):
-        """Record an available test case."""
-        base_name = re.sub(r'\.[^.]*$', '', re.sub(r'.*/', '', file_name))
-        key = ';'.join([base_name, description.decode('utf-8')])
-        self.descriptions.add(key)
-
-def collect_available_test_cases():
-    """Collect the available test cases."""
-    explorer = TestDescriptions()
-    explorer.walk_all()
-    return sorted(explorer.descriptions)
-
 def analyze_coverage(results, outcomes):
     """Check that all available test cases are executed at least once."""
-    available = collect_available_test_cases()
+    available = check_test_cases.collect_available_test_cases()
     for key in available:
         hits = outcomes[key].hits() if key in outcomes else 0
         if hits == 0:
diff --git a/tests/scripts/check_test_cases.py b/tests/scripts/check_test_cases.py
index f9ae36c..d84ed04 100755
--- a/tests/scripts/check_test_cases.py
+++ b/tests/scripts/check_test_cases.py
@@ -137,6 +137,26 @@
                                                             '*.sh')):
                 self.walk_ssl_opt_sh(ssl_opt_file_name)
 
+class TestDescriptions(TestDescriptionExplorer):
+    """Collect the available test cases."""
+
+    def __init__(self):
+        super().__init__()
+        self.descriptions = set()
+
+    def process_test_case(self, _per_file_state,
+                          file_name, _line_number, description):
+        """Record an available test case."""
+        base_name = re.sub(r'\.[^.]*$', '', re.sub(r'.*/', '', file_name))
+        key = ';'.join([base_name, description.decode('utf-8')])
+        self.descriptions.add(key)
+
+def collect_available_test_cases():
+    """Collect the available test cases."""
+    explorer = TestDescriptions()
+    explorer.walk_all()
+    return sorted(explorer.descriptions)
+
 class DescriptionChecker(TestDescriptionExplorer):
     """Check all test case descriptions.
 
@@ -176,6 +196,9 @@
 
 def main():
     parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('--list-all',
+                        action='store_true',
+                        help='List all test cases, without doing checks')
     parser.add_argument('--quiet', '-q',
                         action='store_true',
                         help='Hide warnings')
@@ -183,6 +206,10 @@
                         action='store_false', dest='quiet',
                         help='Show warnings (default: on; undoes --quiet)')
     options = parser.parse_args()
+    if options.list_all:
+        descriptions = collect_available_test_cases()
+        sys.stdout.write('\n'.join(descriptions + ['']))
+        return
     results = Results(options)
     checker = DescriptionChecker(results)
     checker.walk_all()
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 08a1fe7..ddf831f 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -543,16 +543,18 @@
 
 # record_outcome <outcome> [<failure-reason>]
 # The test name must be in $NAME.
+# Use $TEST_SUITE_NAME as the test suite name if set.
 record_outcome() {
     echo "$1"
     if [ -n "$MBEDTLS_TEST_OUTCOME_FILE" ]; then
         printf '%s;%s;%s;%s;%s;%s\n' \
                "$MBEDTLS_TEST_PLATFORM" "$MBEDTLS_TEST_CONFIGURATION" \
-               "ssl-opt" "$NAME" \
+               "${TEST_SUITE_NAME:-ssl-opt}" "$NAME" \
                "$1" "${2-}" \
                >>"$MBEDTLS_TEST_OUTCOME_FILE"
     fi
 }
+unset TEST_SUITE_NAME
 
 # True if the presence of the given pattern in a log definitely indicates
 # that the test has failed. False if the presence is inconclusive.
@@ -9273,10 +9275,13 @@
             -c "tls13 client state: MBEDTLS_SSL_CLIENT_HELLO" \
             -c "HTTP/1.0 200 OK"
 
-for i in $(ls opt-testcases/*.sh)
+for i in opt-testcases/*.sh
 do
-    . $i
+    TEST_SUITE_NAME=${i##*/}
+    TEST_SUITE_NAME=${TEST_SUITE_NAME%.*}
+    . "$i"
 done
+unset TEST_SUITE_NAME
 
 requires_openssl_tls1_3
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3