Merge pull request #6680 from valeriosetti/issue6599

Allow isolation of EC J-PAKE password when used in TLS
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index 3f48377..7751560 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -3899,6 +3899,23 @@
 int mbedtls_ssl_set_hs_ecjpake_password( mbedtls_ssl_context *ssl,
                                          const unsigned char *pw,
                                          size_t pw_len );
+
+/**
+ * \brief          Set the EC J-PAKE opaque password for current handshake.
+ *
+ * \note           The key must remain valid until the handshake is over.
+ *
+ * \note           The SSL context needs to be already set up. The right place
+ *                 to call this function is between \c mbedtls_ssl_setup() or
+ *                 \c mbedtls_ssl_reset() and \c mbedtls_ssl_handshake().
+ *
+ * \param ssl      SSL context
+ * \param pwd      EC J-PAKE opaque password
+ *
+ * \return         0 on success, or a negative error code.
+ */
+int mbedtls_ssl_set_hs_ecjpake_password_opaque( mbedtls_ssl_context *ssl,
+                                                mbedtls_svc_key_id_t pwd );
 #endif /*MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 
 #if defined(MBEDTLS_SSL_ALPN)
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 9bb9dc2..b757613 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -1863,27 +1863,55 @@
 #endif
 
 #if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
-/*
- * Set EC J-PAKE password for current handshake
- */
-#if defined(MBEDTLS_USE_PSA_CRYPTO)
-int mbedtls_ssl_set_hs_ecjpake_password( mbedtls_ssl_context *ssl,
-                                         const unsigned char *pw,
-                                         size_t pw_len )
-{
-    psa_pake_cipher_suite_t cipher_suite = psa_pake_cipher_suite_init();
-    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
-    psa_pake_role_t psa_role;
-    psa_status_t status;
 
-    if( ssl->handshake == NULL || ssl->conf == NULL )
-        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+static psa_status_t mbedtls_ssl_set_hs_ecjpake_password_common(
+                                                mbedtls_ssl_context *ssl,
+                                                mbedtls_svc_key_id_t pwd )
+{
+    psa_status_t status;
+    psa_pake_role_t psa_role;
+    psa_pake_cipher_suite_t cipher_suite = psa_pake_cipher_suite_init();
+
+    psa_pake_cs_set_algorithm( &cipher_suite, PSA_ALG_JPAKE );
+    psa_pake_cs_set_primitive( &cipher_suite,
+                               PSA_PAKE_PRIMITIVE( PSA_PAKE_PRIMITIVE_TYPE_ECC,
+                                                   PSA_ECC_FAMILY_SECP_R1,
+                                                   256) );
+    psa_pake_cs_set_hash( &cipher_suite, PSA_ALG_SHA_256 );
+
+    status = psa_pake_setup( &ssl->handshake->psa_pake_ctx, &cipher_suite );
+    if( status != PSA_SUCCESS )
+        return status;
 
     if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER )
         psa_role = PSA_PAKE_ROLE_SERVER;
     else
         psa_role = PSA_PAKE_ROLE_CLIENT;
 
+    status = psa_pake_set_role( &ssl->handshake->psa_pake_ctx, psa_role );
+    if( status != PSA_SUCCESS )
+        return status;
+
+    status = psa_pake_set_password_key( &ssl->handshake->psa_pake_ctx, pwd );
+    if( status != PSA_SUCCESS )
+        return status;
+
+    ssl->handshake->psa_pake_ctx_is_ok = 1;
+
+    return ( PSA_SUCCESS );
+}
+
+int mbedtls_ssl_set_hs_ecjpake_password( mbedtls_ssl_context *ssl,
+                                         const unsigned char *pw,
+                                         size_t pw_len )
+{
+    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+    psa_status_t status;
+
+    if( ssl->handshake == NULL || ssl->conf == NULL )
+        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
     /* Empty password is not valid  */
     if( ( pw == NULL) || ( pw_len == 0 ) )
         return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
@@ -1897,21 +1925,8 @@
     if( status != PSA_SUCCESS )
         return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
 
-    psa_pake_cs_set_algorithm( &cipher_suite, PSA_ALG_JPAKE );
-    psa_pake_cs_set_primitive( &cipher_suite,
-                               PSA_PAKE_PRIMITIVE( PSA_PAKE_PRIMITIVE_TYPE_ECC,
-                                                   PSA_ECC_FAMILY_SECP_R1,
-                                                   256) );
-    psa_pake_cs_set_hash( &cipher_suite, PSA_ALG_SHA_256 );
-
-    status = psa_pake_setup( &ssl->handshake->psa_pake_ctx, &cipher_suite );
-    if( status != PSA_SUCCESS )
-    {
-        psa_destroy_key( ssl->handshake->psa_pake_password );
-        return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
-    }
-
-    status = psa_pake_set_role( &ssl->handshake->psa_pake_ctx, psa_role );
+    status = mbedtls_ssl_set_hs_ecjpake_password_common( ssl,
+                                        ssl->handshake->psa_pake_password );
     if( status != PSA_SUCCESS )
     {
         psa_destroy_key( ssl->handshake->psa_pake_password );
@@ -1919,17 +1934,27 @@
         return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
     }
 
-    psa_pake_set_password_key( &ssl->handshake->psa_pake_ctx,
-                                ssl->handshake->psa_pake_password );
+    return( 0 );
+}
+
+int mbedtls_ssl_set_hs_ecjpake_password_opaque( mbedtls_ssl_context *ssl,
+                                                mbedtls_svc_key_id_t pwd )
+{
+    psa_status_t status;
+
+    if( ssl->handshake == NULL || ssl->conf == NULL )
+        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+    if( mbedtls_svc_key_id_is_null( pwd ) )
+        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+    status = mbedtls_ssl_set_hs_ecjpake_password_common( ssl, pwd );
     if( status != PSA_SUCCESS )
     {
-        psa_destroy_key( ssl->handshake->psa_pake_password );
         psa_pake_abort( &ssl->handshake->psa_pake_ctx );
         return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
     }
 
-    ssl->handshake->psa_pake_ctx_is_ok = 1;
-
     return( 0 );
 }
 #else /* MBEDTLS_USE_PSA_CRYPTO */
@@ -1942,6 +1967,10 @@
     if( ssl->handshake == NULL || ssl->conf == NULL )
         return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
 
+    /* Empty password is not valid  */
+    if( ( pw == NULL) || ( pw_len == 0 ) )
+        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
     if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER )
         role = MBEDTLS_ECJPAKE_SERVER;
     else
@@ -3996,7 +4025,15 @@
 #if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
     psa_pake_abort( &handshake->psa_pake_ctx );
-    psa_destroy_key( handshake->psa_pake_password );
+    /*
+     * Opaque keys are not stored in the handshake's data and it's the user
+     * responsibility to destroy them. Clear ones, instead, are created by
+     * the TLS library and should be destroyed at the same level
+     */
+    if( ! mbedtls_svc_key_id_is_null( handshake->psa_pake_password ) )
+    {
+        psa_destroy_key( handshake->psa_pake_password );
+    }
     handshake->psa_pake_password = MBEDTLS_SVC_KEY_ID_INIT;
 #else
     mbedtls_ecjpake_free( &handshake->ecjpake_ctx );
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index 6aa295d..02ee7cf 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -68,6 +68,7 @@
 #define DFL_PSK_OPAQUE          0
 #define DFL_PSK_IDENTITY        "Client_identity"
 #define DFL_ECJPAKE_PW          NULL
+#define DFL_ECJPAKE_PW_OPAQUE   0
 #define DFL_EC_MAX_OPS          -1
 #define DFL_FORCE_CIPHER        0
 #define DFL_TLS1_3_KEX_MODES    MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_ALL
@@ -318,11 +319,17 @@
 #endif
 
 #if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
 #define USAGE_ECJPAKE \
-    "    ecjpake_pw=%%s       default: none (disabled)\n"
-#else
+    "    ecjpake_pw=%%s           default: none (disabled)\n"   \
+    "    ecjpake_pw_opaque=%%d    default: 0 (disabled)\n"
+#else /* MBEDTLS_USE_PSA_CRYPTO */
+#define USAGE_ECJPAKE \
+    "    ecjpake_pw=%%s           default: none (disabled)\n"
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+#else /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 #define USAGE_ECJPAKE ""
-#endif
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 
 #if defined(MBEDTLS_ECP_RESTARTABLE)
 #define USAGE_ECRESTART \
@@ -492,6 +499,9 @@
     const char *psk;            /* the pre-shared key                       */
     const char *psk_identity;   /* the pre-shared key identity              */
     const char *ecjpake_pw;     /* the EC J-PAKE password                   */
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    int ecjpake_pw_opaque;      /* set to 1 to use the opaque method for setting the password */
+#endif
     int ec_max_ops;             /* EC consecutive operations limit          */
     int force_ciphersuite[2];   /* protocol/ciphersuite to use, or all      */
 #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
@@ -824,6 +834,10 @@
         MBEDTLS_TLS_SRTP_UNSET
     };
 #endif /* MBEDTLS_SSL_DTLS_SRTP */
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) && \
+    defined(MBEDTLS_USE_PSA_CRYPTO)
+    mbedtls_svc_key_id_t ecjpake_pw_slot = MBEDTLS_SVC_KEY_ID_INIT; /* ecjpake password key slot */
+#endif /* MBEDTLS_USE_PSA_CRYPTO && MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 
 #if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
     mbedtls_memory_buffer_alloc_init( alloc_buf, sizeof(alloc_buf) );
@@ -919,6 +933,9 @@
 #endif
     opt.psk_identity        = DFL_PSK_IDENTITY;
     opt.ecjpake_pw          = DFL_ECJPAKE_PW;
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    opt.ecjpake_pw_opaque   = DFL_ECJPAKE_PW_OPAQUE;
+#endif
     opt.ec_max_ops          = DFL_EC_MAX_OPS;
     opt.force_ciphersuite[0]= DFL_FORCE_CIPHER;
 #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
@@ -1094,6 +1111,10 @@
             opt.psk_identity = q;
         else if( strcmp( p, "ecjpake_pw" ) == 0 )
             opt.ecjpake_pw = q;
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        else if( strcmp( p, "ecjpake_pw_opaque" ) == 0 )
+            opt.ecjpake_pw_opaque = atoi( q );
+#endif
         else if( strcmp( p, "ec_max_ops" ) == 0 )
             opt.ec_max_ops = atoi( q );
         else if( strcmp( p, "force_ciphersuite" ) == 0 )
@@ -2166,16 +2187,46 @@
 #if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
     if( opt.ecjpake_pw != DFL_ECJPAKE_PW )
     {
-        if( ( ret = mbedtls_ssl_set_hs_ecjpake_password( &ssl,
-                        (const unsigned char *) opt.ecjpake_pw,
-                                        strlen( opt.ecjpake_pw ) ) ) != 0 )
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        if ( opt.ecjpake_pw_opaque != DFL_ECJPAKE_PW_OPAQUE )
         {
-            mbedtls_printf( " failed\n  ! mbedtls_ssl_set_hs_ecjpake_password returned %d\n\n",
-                            ret );
-            goto exit;
+            psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+
+            psa_set_key_usage_flags( &attributes, PSA_KEY_USAGE_DERIVE );
+            psa_set_key_algorithm( &attributes, PSA_ALG_JPAKE );
+            psa_set_key_type( &attributes, PSA_KEY_TYPE_PASSWORD );
+
+            status = psa_import_key( &attributes,
+                                (const unsigned char *) opt.ecjpake_pw,
+                                strlen( opt.ecjpake_pw ),
+                                &ecjpake_pw_slot );
+            if( status != PSA_SUCCESS )
+            {
+                mbedtls_printf( " failed\n  ! psa_import_key returned %d\n\n",
+                            status );
+                goto exit;
+            }
+            if( ( ret = mbedtls_ssl_set_hs_ecjpake_password_opaque( &ssl,
+                                        ecjpake_pw_slot ) ) != 0 )
+            {
+                mbedtls_printf( " failed\n  ! mbedtls_ssl_set_hs_ecjpake_password_opaque returned %d\n\n", ret );
+                goto exit;
+            }
+            mbedtls_printf( "using opaque password\n");
+        }
+        else
+#endif  /* MBEDTLS_USE_PSA_CRYPTO */
+        {
+            if( ( ret = mbedtls_ssl_set_hs_ecjpake_password( &ssl,
+                                        (const unsigned char *) opt.ecjpake_pw,
+                                        strlen( opt.ecjpake_pw ) ) ) != 0 )
+            {
+                mbedtls_printf( " failed\n  ! mbedtls_ssl_set_hs_ecjpake_password returned %d\n\n", ret );
+                goto exit;
+            }
         }
     }
-#endif
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 
 #if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED)
     if( opt.context_crt_cb == 1 )
@@ -3276,6 +3327,31 @@
 #endif /* MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED &&
           MBEDTLS_USE_PSA_CRYPTO */
 
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) && \
+    defined(MBEDTLS_USE_PSA_CRYPTO)
+    /*
+     * In case opaque keys it's the user responsibility to keep the key valid
+     * for the duration of the handshake and destroy it at the end
+     */
+    if( ( opt.ecjpake_pw_opaque != DFL_ECJPAKE_PW_OPAQUE ) )
+    {
+        psa_key_attributes_t check_attributes = PSA_KEY_ATTRIBUTES_INIT;
+
+        /* Verify that the key is still valid before destroying it */
+        if( psa_get_key_attributes( ecjpake_pw_slot, &check_attributes ) !=
+                PSA_SUCCESS )
+        {
+            if( ret == 0 )
+                ret = 1;
+            mbedtls_printf( "The EC J-PAKE password key has unexpectedly been already destroyed\n" );
+        }
+        else
+        {
+            psa_destroy_key( ecjpake_pw_slot );
+        }
+    }
+#endif  /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED && MBEDTLS_USE_PSA_CRYPTO */
+
 #if defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3)
     const char* message = mbedtls_test_helper_is_psa_leaking();
     if( message )
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index 00624b5..802beb2 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -98,6 +98,7 @@
 #define DFL_PSK_LIST_OPAQUE     0
 #define DFL_PSK_IDENTITY        "Client_identity"
 #define DFL_ECJPAKE_PW          NULL
+#define DFL_ECJPAKE_PW_OPAQUE   0
 #define DFL_PSK_LIST            NULL
 #define DFL_FORCE_CIPHER        0
 #define DFL_TLS1_3_KEX_MODES    MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_ALL
@@ -419,11 +420,17 @@
 #endif
 
 #if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
 #define USAGE_ECJPAKE \
-    "    ecjpake_pw=%%s       default: none (disabled)\n"
-#else
+    "    ecjpake_pw=%%s           default: none (disabled)\n"   \
+    "    ecjpake_pw_opaque=%%d    default: 0 (disabled)\n"
+#else /* MBEDTLS_USE_PSA_CRYPTO */
+#define USAGE_ECJPAKE \
+    "    ecjpake_pw=%%s           default: none (disabled)\n"
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+#else /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 #define USAGE_ECJPAKE ""
-#endif
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 
 #if defined(MBEDTLS_SSL_EARLY_DATA)
 #define USAGE_EARLY_DATA \
@@ -631,6 +638,9 @@
     const char *psk_identity;   /* the pre-shared key identity              */
     char *psk_list;             /* list of PSK id/key pairs for callback    */
     const char *ecjpake_pw;     /* the EC J-PAKE password                   */
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    int ecjpake_pw_opaque;      /* set to 1 to use the opaque method for setting the password */
+#endif
     int force_ciphersuite[2];   /* protocol/ciphersuite to use, or all      */
 #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
     int tls13_kex_modes;        /* supported TLS 1.3 key exchange modes     */
@@ -1517,6 +1527,10 @@
     unsigned char *context_buf = NULL;
     size_t context_buf_len = 0;
 #endif
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) && \
+    defined(MBEDTLS_USE_PSA_CRYPTO)
+    mbedtls_svc_key_id_t ecjpake_pw_slot = MBEDTLS_SVC_KEY_ID_INIT; /* ecjpake password key slot */
+#endif /* MBEDTLS_USE_PSA_CRYPTO && MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 
 #if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED)
     uint16_t sig_alg_list[SIG_ALG_LIST_SIZE];
@@ -1675,6 +1689,9 @@
     opt.psk_identity        = DFL_PSK_IDENTITY;
     opt.psk_list            = DFL_PSK_LIST;
     opt.ecjpake_pw          = DFL_ECJPAKE_PW;
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    opt.ecjpake_pw_opaque   = DFL_ECJPAKE_PW_OPAQUE;
+#endif
     opt.force_ciphersuite[0]= DFL_FORCE_CIPHER;
 #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
     opt.tls13_kex_modes     = DFL_TLS1_3_KEX_MODES;
@@ -1879,6 +1896,10 @@
             opt.psk_list = q;
         else if( strcmp( p, "ecjpake_pw" ) == 0 )
             opt.ecjpake_pw = q;
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        else if( strcmp( p, "ecjpake_pw_opaque" ) == 0 )
+            opt.ecjpake_pw_opaque = atoi( q );
+#endif
         else if( strcmp( p, "force_ciphersuite" ) == 0 )
         {
             opt.force_ciphersuite[0] = mbedtls_ssl_get_ciphersuite_id( q );
@@ -3528,15 +3549,46 @@
 #if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
     if( opt.ecjpake_pw != DFL_ECJPAKE_PW )
     {
-        if( ( ret = mbedtls_ssl_set_hs_ecjpake_password( &ssl,
-                        (const unsigned char *) opt.ecjpake_pw,
-                                        strlen( opt.ecjpake_pw ) ) ) != 0 )
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        if ( opt.ecjpake_pw_opaque != DFL_ECJPAKE_PW_OPAQUE )
         {
-            mbedtls_printf( " failed\n  ! mbedtls_ssl_set_hs_ecjpake_password returned %d\n\n", ret );
-            goto exit;
+            psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+
+            psa_set_key_usage_flags( &attributes, PSA_KEY_USAGE_DERIVE );
+            psa_set_key_algorithm( &attributes, PSA_ALG_JPAKE );
+            psa_set_key_type( &attributes, PSA_KEY_TYPE_PASSWORD );
+
+            status = psa_import_key( &attributes,
+                                (const unsigned char *) opt.ecjpake_pw,
+                                strlen( opt.ecjpake_pw ),
+                                &ecjpake_pw_slot );
+            if( status != PSA_SUCCESS )
+            {
+                mbedtls_printf( " failed\n  ! psa_import_key returned %d\n\n",
+                            status );
+                goto exit;
+            }
+            if( ( ret = mbedtls_ssl_set_hs_ecjpake_password_opaque( &ssl,
+                                        ecjpake_pw_slot ) ) != 0 )
+            {
+                mbedtls_printf( " failed\n  ! mbedtls_ssl_set_hs_ecjpake_password_opaque returned %d\n\n", ret );
+                goto exit;
+            }
+            mbedtls_printf( "using opaque password\n");
+        }
+        else
+#endif  /* MBEDTLS_USE_PSA_CRYPTO */
+        {
+            if( ( ret = mbedtls_ssl_set_hs_ecjpake_password( &ssl,
+                                        (const unsigned char *) opt.ecjpake_pw,
+                                        strlen( opt.ecjpake_pw ) ) ) != 0 )
+            {
+                mbedtls_printf( " failed\n  ! mbedtls_ssl_set_hs_ecjpake_password returned %d\n\n", ret );
+                goto exit;
+            }
         }
     }
-#endif
+#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 
 #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
 #if defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED)
@@ -4422,6 +4474,31 @@
 #endif /* MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED &&
           MBEDTLS_USE_PSA_CRYPTO */
 
+#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) && \
+    defined(MBEDTLS_USE_PSA_CRYPTO)
+    /*
+     * In case opaque keys it's the user responsibility to keep the key valid
+     * for the duration of the handshake and destroy it at the end
+     */
+    if( ( opt.ecjpake_pw_opaque != DFL_ECJPAKE_PW_OPAQUE ) )
+    {
+        psa_key_attributes_t check_attributes = PSA_KEY_ATTRIBUTES_INIT;
+
+        /* Verify that the key is still valid before destroying it */
+        if( psa_get_key_attributes( ecjpake_pw_slot, &check_attributes ) !=
+                PSA_SUCCESS )
+        {
+            if( ret == 0 )
+                ret = 1;
+            mbedtls_printf( "The EC J-PAKE password key has unexpectedly been already destroyed\n" );
+        }
+        else
+        {
+            psa_destroy_key( ecjpake_pw_slot );
+        }
+    }
+#endif  /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED && MBEDTLS_USE_PSA_CRYPTO */
+
 #if defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3)
     const char* message = mbedtls_test_helper_is_psa_leaking();
     if( message )
diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh
index db46b03..cc630ce 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -1457,10 +1457,14 @@
     make -C programs ssl/ssl_server2 ssl/ssl_client2
     make -C programs test/udp_proxy test/query_compile_time_config
 
-    msg "test: server w/o USE_PSA - client w/ USE_PSA"
-    P_SRV=../s2_no_use_psa tests/ssl-opt.sh -f ECJPAKE
-    msg "test: client w/o USE_PSA - server w/ USE_PSA"
-    P_CLI=../c2_no_use_psa tests/ssl-opt.sh -f ECJPAKE
+    msg "test: server w/o USE_PSA - client w/ USE_PSA, text password"
+    P_SRV=../s2_no_use_psa tests/ssl-opt.sh -f "ECJPAKE: working, TLS"
+    msg "test: server w/o USE_PSA - client w/ USE_PSA, opaque password"
+    P_SRV=../s2_no_use_psa tests/ssl-opt.sh -f "ECJPAKE: opaque password client only, working, TLS"
+    msg "test: client w/o USE_PSA - server w/ USE_PSA, text password"
+    P_CLI=../c2_no_use_psa tests/ssl-opt.sh -f "ECJPAKE: working, TLS"
+    msg "test: client w/o USE_PSA - server w/ USE_PSA, opaque password"
+    P_CLI=../c2_no_use_psa tests/ssl-opt.sh -f "ECJPAKE: opaque password server only, working, TLS"
 
     rm s2_no_use_psa c2_no_use_psa
 }
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 1fe8bae..ea57b25 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -7986,6 +7986,8 @@
             -C "found ecjpake_kkpp extension" \
             -s "SSL - The handshake negotiation failed"
 
+# Note: if the name of this test is changed, then please adjust the corresponding
+#       filtering label in "test_tls1_2_ecjpake_compatibility" (in "all.sh")
 requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
 run_test    "ECJPAKE: working, TLS" \
@@ -8004,6 +8006,73 @@
             -S "SSL - The handshake negotiation failed" \
             -S "SSL - Verification of the message MAC failed"
 
+requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
+run_test    "ECJPAKE: opaque password client+server, working, TLS" \
+            "$P_SRV debug_level=3 ecjpake_pw=bla ecjpake_pw_opaque=1" \
+            "$P_CLI debug_level=3 ecjpake_pw=bla ecjpake_pw_opaque=1\
+             force_ciphersuite=TLS-ECJPAKE-WITH-AES-128-CCM-8" \
+            0 \
+            -c "add ciphersuite: c0ff" \
+            -c "adding ecjpake_kkpp extension" \
+            -c "using opaque password" \
+            -s "using opaque password" \
+            -C "re-using cached ecjpake parameters" \
+            -s "found ecjpake kkpp extension" \
+            -S "skip ecjpake kkpp extension" \
+            -S "ciphersuite mismatch: ecjpake not configured" \
+            -s "server hello, ecjpake kkpp extension" \
+            -c "found ecjpake_kkpp extension" \
+            -S "SSL - The handshake negotiation failed" \
+            -S "SSL - Verification of the message MAC failed"
+
+# Note: if the name of this test is changed, then please adjust the corresponding
+#       filtering label in "test_tls1_2_ecjpake_compatibility" (in "all.sh")
+requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
+run_test    "ECJPAKE: opaque password client only, working, TLS" \
+            "$P_SRV debug_level=3 ecjpake_pw=bla" \
+            "$P_CLI debug_level=3 ecjpake_pw=bla ecjpake_pw_opaque=1\
+             force_ciphersuite=TLS-ECJPAKE-WITH-AES-128-CCM-8" \
+            0 \
+            -c "add ciphersuite: c0ff" \
+            -c "adding ecjpake_kkpp extension" \
+            -c "using opaque password" \
+            -S "using opaque password" \
+            -C "re-using cached ecjpake parameters" \
+            -s "found ecjpake kkpp extension" \
+            -S "skip ecjpake kkpp extension" \
+            -S "ciphersuite mismatch: ecjpake not configured" \
+            -s "server hello, ecjpake kkpp extension" \
+            -c "found ecjpake_kkpp extension" \
+            -S "SSL - The handshake negotiation failed" \
+            -S "SSL - Verification of the message MAC failed"
+
+# Note: if the name of this test is changed, then please adjust the corresponding
+#       filtering label in "test_tls1_2_ecjpake_compatibility" (in "all.sh")
+requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
+run_test    "ECJPAKE: opaque password server only, working, TLS" \
+            "$P_SRV debug_level=3 ecjpake_pw=bla ecjpake_pw_opaque=1" \
+            "$P_CLI debug_level=3 ecjpake_pw=bla\
+             force_ciphersuite=TLS-ECJPAKE-WITH-AES-128-CCM-8" \
+            0 \
+            -c "add ciphersuite: c0ff" \
+            -c "adding ecjpake_kkpp extension" \
+            -C "using opaque password" \
+            -s "using opaque password" \
+            -C "re-using cached ecjpake parameters" \
+            -s "found ecjpake kkpp extension" \
+            -S "skip ecjpake kkpp extension" \
+            -S "ciphersuite mismatch: ecjpake not configured" \
+            -s "server hello, ecjpake kkpp extension" \
+            -c "found ecjpake_kkpp extension" \
+            -S "SSL - The handshake negotiation failed" \
+            -S "SSL - Verification of the message MAC failed"
+
 server_needs_more_time 1
 requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
@@ -8015,6 +8084,20 @@
             -C "re-using cached ecjpake parameters" \
             -s "SSL - Verification of the message MAC failed"
 
+server_needs_more_time 1
+requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
+run_test    "ECJPAKE_OPAQUE_PW: opaque password mismatch, TLS" \
+            "$P_SRV debug_level=3 ecjpake_pw=bla ecjpake_pw_opaque=1" \
+            "$P_CLI debug_level=3 ecjpake_pw=bad ecjpake_pw_opaque=1 \
+             force_ciphersuite=TLS-ECJPAKE-WITH-AES-128-CCM-8" \
+            1 \
+            -c "using opaque password" \
+            -s "using opaque password" \
+            -C "re-using cached ecjpake parameters" \
+            -s "SSL - Verification of the message MAC failed"
+
 requires_config_enabled MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
 run_test    "ECJPAKE: working, DTLS" \
diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data
index a7f0501..1b5e44b 100644
--- a/tests/suites/test_suite_ssl.data
+++ b/tests/suites/test_suite_ssl.data
@@ -3539,3 +3539,11 @@
 
 TLS 1.3 srv Certificate msg - wrong vector lengths
 tls13_server_certificate_msg_invalid_vector_len
+
+EC-JPAKE set password
+depends_on:MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
+ssl_ecjpake_set_password:0
+
+EC-JPAKE set opaque password
+depends_on:MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED:MBEDTLS_USE_PSA_CRYPTO
+ssl_ecjpake_set_password:1
diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function
index 674e649..95fa8ef 100644
--- a/tests/suites/test_suite_ssl.function
+++ b/tests/suites/test_suite_ssl.function
@@ -2582,6 +2582,21 @@
     return( 0 );
 }
 #endif /* MBEDTLS_TEST_HOOKS */
+
+#define ECJPAKE_TEST_PWD        "bla"
+
+#if defined( MBEDTLS_USE_PSA_CRYPTO )
+#define ECJPAKE_TEST_SET_PASSWORD( exp_ret_val )                          \
+    ret = ( use_opaque_arg ) ?                                            \
+        mbedtls_ssl_set_hs_ecjpake_password_opaque( &ssl, pwd_slot ) :    \
+        mbedtls_ssl_set_hs_ecjpake_password( &ssl, pwd_string, pwd_len ); \
+    TEST_EQUAL( ret, exp_ret_val )
+#else
+#define ECJPAKE_TEST_SET_PASSWORD( exp_ret_val )                \
+    ret = mbedtls_ssl_set_hs_ecjpake_password( &ssl,            \
+                                        pwd_string, pwd_len );  \
+    TEST_EQUAL( ret, exp_ret_val )
+#endif
 /* END_HEADER */
 
 /* BEGIN_DEPENDENCIES
@@ -5997,3 +6012,85 @@
     USE_PSA_DONE( );
 }
 /* END_CASE */
+
+/* BEGIN_CASE depends_on:MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
+void ssl_ecjpake_set_password( int use_opaque_arg )
+{
+    mbedtls_ssl_context ssl;
+    mbedtls_ssl_config conf;
+#if defined( MBEDTLS_USE_PSA_CRYPTO )
+    mbedtls_svc_key_id_t pwd_slot = MBEDTLS_SVC_KEY_ID_INIT;
+#else   /* MBEDTLS_USE_PSA_CRYPTO */
+    (void) use_opaque_arg;
+#endif  /* MBEDTLS_USE_PSA_CRYPTO */
+    unsigned char pwd_string[ sizeof(ECJPAKE_TEST_PWD) ] = "";
+    size_t pwd_len = 0;
+    int ret;
+
+    USE_PSA_INIT( );
+
+    mbedtls_ssl_init( &ssl );
+
+    /* test with uninitalized SSL context */
+    ECJPAKE_TEST_SET_PASSWORD( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+    mbedtls_ssl_config_init( &conf );
+
+    TEST_EQUAL( mbedtls_ssl_config_defaults( &conf,
+                                              MBEDTLS_SSL_IS_CLIENT,
+                                              MBEDTLS_SSL_TRANSPORT_STREAM,
+                                              MBEDTLS_SSL_PRESET_DEFAULT ), 0 );
+
+    TEST_EQUAL( mbedtls_ssl_setup( &ssl, &conf ), 0 );
+
+    /* test with empty password or unitialized password key (depending on use_opaque_arg) */
+    ECJPAKE_TEST_SET_PASSWORD( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+    pwd_len = strlen( ECJPAKE_TEST_PWD );
+    memcpy( pwd_string, ECJPAKE_TEST_PWD, pwd_len );
+
+#if defined( MBEDTLS_USE_PSA_CRYPTO )
+    if( use_opaque_arg )
+    {
+        psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+        psa_key_attributes_t check_attributes = PSA_KEY_ATTRIBUTES_INIT;
+
+        /* First try with an invalid usage */
+        psa_set_key_usage_flags( &attributes, PSA_KEY_USAGE_SIGN_HASH );
+        psa_set_key_algorithm( &attributes, PSA_ALG_JPAKE );
+        psa_set_key_type( &attributes, PSA_KEY_TYPE_PASSWORD );
+
+        PSA_ASSERT( psa_import_key( &attributes, pwd_string,
+                    pwd_len, &pwd_slot ) );
+
+        ECJPAKE_TEST_SET_PASSWORD( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
+
+        /* check that the opaque key is still valid after failure */
+        TEST_EQUAL( psa_get_key_attributes( pwd_slot, &check_attributes ),
+                        PSA_SUCCESS );
+
+        psa_destroy_key( pwd_slot );
+
+        /* Then set the correct usage */
+        psa_set_key_usage_flags( &attributes, PSA_KEY_USAGE_DERIVE );
+
+        PSA_ASSERT( psa_import_key( &attributes, pwd_string,
+                    pwd_len, &pwd_slot ) );
+    }
+#endif  /* MBEDTLS_USE_PSA_CRYPTO */
+
+    /* final check which should work without errors */
+    ECJPAKE_TEST_SET_PASSWORD( 0 );
+
+#if defined( MBEDTLS_USE_PSA_CRYPTO )
+    if( use_opaque_arg )
+    {
+        psa_destroy_key( pwd_slot );
+    }
+#endif  /* MBEDTLS_USE_PSA_CRYPTO */
+    mbedtls_ssl_free( &ssl );
+    mbedtls_ssl_config_free( &conf );
+
+    USE_PSA_DONE( );
+}
+/* END_CASE */