Merge branch 'psa_cipher_integration' into development-psa-proposed
diff --git a/include/mbedtls/ecdsa.h b/include/mbedtls/ecdsa.h
index 4057828..5245c6e 100644
--- a/include/mbedtls/ecdsa.h
+++ b/include/mbedtls/ecdsa.h
@@ -35,25 +35,30 @@
 #include "ecp.h"
 #include "md.h"
 
-/*
- * RFC-4492 page 20:
+/**
+ * \brief           Maximum ECDSA signature size for a given curve bit size
  *
+ * \param bits      Curve size in bits
+ * \return          Maximum signature size in bytes
+ *
+ * \note            This macro returns a compile-time constant if its argument
+ *                  is one. It may evaluate its argument multiple times.
+ */
+/*
  *     Ecdsa-Sig-Value ::= SEQUENCE {
  *         r       INTEGER,
  *         s       INTEGER
  *     }
  *
- * Size is at most
- *    1 (tag) + 1 (len) + 1 (initial 0) + ECP_MAX_BYTES for each of r and s,
- *    twice that + 1 (tag) + 2 (len) for the sequence
- * (assuming ECP_MAX_BYTES is less than 126 for r and s,
- * and less than 124 (total len <= 255) for the sequence)
+ * For each of r and s, the value (V) may include an extra initial "0" bit.
  */
-#if MBEDTLS_ECP_MAX_BYTES > 124
-#error "MBEDTLS_ECP_MAX_BYTES bigger than expected, please fix MBEDTLS_ECDSA_MAX_LEN"
-#endif
+#define MBEDTLS_ECDSA_MAX_SIG_LEN( bits )                               \
+    ( /*T,L of SEQUENCE*/ ( ( bits ) >= 61 * 8 ? 3 : 2 ) +              \
+      /*T,L of r,s*/        2 * ( ( ( bits ) >= 127 * 8 ? 3 : 2 ) +     \
+      /*V of r,s*/                ( ( bits ) + 8 ) / 8 ) )
+
 /** The maximal size of an ECDSA signature in Bytes. */
-#define MBEDTLS_ECDSA_MAX_LEN  ( 3 + 2 * ( 3 + MBEDTLS_ECP_MAX_BYTES ) )
+#define MBEDTLS_ECDSA_MAX_LEN  MBEDTLS_ECDSA_MAX_SIG_LEN( MBEDTLS_ECP_MAX_BITS )
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/include/mbedtls/pk.h b/include/mbedtls/pk.h
index df3a03c..862065e 100644
--- a/include/mbedtls/pk.h
+++ b/include/mbedtls/pk.h
@@ -45,6 +45,10 @@
 #include "ecdsa.h"
 #endif
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#include "psa/crypto.h"
+#endif
+
 #if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
     !defined(inline) && !defined(__cplusplus)
 #define inline __inline
@@ -83,6 +87,7 @@
     MBEDTLS_PK_ECDSA,
     MBEDTLS_PK_RSA_ALT,
     MBEDTLS_PK_RSASSA_PSS,
+    MBEDTLS_PK_OPAQUE,
 } mbedtls_pk_type_t;
 
 /**
@@ -203,6 +208,11 @@
 
 /**
  * \brief           Free a mbedtls_pk_context
+ *
+ * \note            For contexts that have been set up with
+ *                  mbedtls_pk_setup_opaque(), this does not free the underlying
+ *                  key slot and you still need to call psa_destroy_key()
+ *                  independently if you want to destroy that key.
  */
 void mbedtls_pk_free( mbedtls_pk_context *ctx );
 
@@ -234,6 +244,38 @@
  */
 int mbedtls_pk_setup( mbedtls_pk_context *ctx, const mbedtls_pk_info_t *info );
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+/**
+ * \brief           Initialize a PK context to wrap a PSA key slot.
+ *
+ * \note            This function replaces mbedtls_pk_setup() for contexts
+ *                  that wrap a (possibly opaque) PSA key slot instead of
+ *                  storing and manipulating the key material directly.
+ *
+ * \param ctx       The context to initialize. It must be empty (type NONE).
+ * \param key       The PSA key slot to wrap, which must hold an ECC key pair
+ *                  (see notes below).
+ *
+ * \note            The wrapped key slot must remain valid as long as the
+ *                  wrapping PK context is in use, that is at least between
+ *                  the point this function is called and the point
+ *                  mbedtls_pk_free() is called on this context. The wrapped
+ *                  key slot might then be independently used or destroyed.
+ *
+ * \note            This function is currently only available for ECC key
+ *                  pairs (that is, ECC keys containing private key material).
+ *                  Support for other key types may be added later.
+ *
+ * \return          \c 0 on success.
+ * \return          #MBEDTLS_ERR_PK_BAD_INPUT_DATA on invalid input
+ *                  (context already used, invalid key slot).
+ * \return          #MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE if the key is not an
+ *                  ECC key pair.
+ * \return          #MBEDTLS_ERR_PK_ALLOC_FAILED on allocation failure.
+ */
+int mbedtls_pk_setup_opaque( mbedtls_pk_context *ctx, const psa_key_slot_t key );
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
 #if defined(MBEDTLS_PK_RSA_ALT_SUPPORT)
 /**
  * \brief           Initialize an RSA-alt context
@@ -480,7 +522,11 @@
  * \param pub       Context holding a public key.
  * \param prv       Context holding a private (and public) key.
  *
- * \return          0 on success or MBEDTLS_ERR_PK_BAD_INPUT_DATA
+ * \return          \c 0 on success (keys were checked and match each other).
+ * \return          #MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE if the keys could not
+ *                  be checked - in that case they may or may not match.
+ * \return          #MBEDTLS_ERR_PK_BAD_INPUT_DATA if a context is invalid.
+ * \return          Another non-zero value if the keys do not match.
  */
 int mbedtls_pk_check_pair( const mbedtls_pk_context *pub, const mbedtls_pk_context *prv );
 
@@ -694,6 +740,31 @@
 int mbedtls_pk_load_file( const char *path, unsigned char **buf, size_t *n );
 #endif
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+/**
+ * \brief           Turn an EC key into an Opaque one
+ *
+ * \warning         This is a temporary utility function for tests. It might
+ *                  change or be removed at any time without notice.
+ *
+ * \note            Only ECDSA keys are supported so far. Signing with the
+ *                  specified hash is the only allowed use of that key.
+ *
+ * \param pk        Input: the EC key to transfer to a PSA key slot.
+ *                  Output: a PK context wrapping that PSA key slot.
+ * \param slot      Output: the chosen slot for storing the key.
+ *                  It's the caller's responsibility to destroy that slot
+ *                  after calling mbedtls_pk_free() on the PK context.
+ * \param hash_alg  The hash algorithm to allow for use with that key.
+ *
+ * \return          \c 0 if successful.
+ * \return          An Mbed TLS error code otherwise.
+ */
+int mbedtls_pk_wrap_as_opaque( mbedtls_pk_context *pk,
+                               psa_key_slot_t *slot,
+                               psa_algorithm_t hash_alg );
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/mbedtls/pk_internal.h b/include/mbedtls/pk_internal.h
index 48b7a5f..fc9ba13 100644
--- a/include/mbedtls/pk_internal.h
+++ b/include/mbedtls/pk_internal.h
@@ -135,4 +135,8 @@
 extern const mbedtls_pk_info_t mbedtls_rsa_alt_info;
 #endif
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+extern const mbedtls_pk_info_t mbedtls_pk_opaque_info;
+#endif
+
 #endif /* MBEDTLS_PK_WRAP_H */
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index 55b206f..f7c9d93 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -65,6 +65,10 @@
 #include "platform_time.h"
 #endif
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#include "psa/crypto.h"
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
 /*
  * SSL Error codes
  */
@@ -923,19 +927,37 @@
 #endif
 
 #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
-    unsigned char *psk;             /*!< pre-shared key. This field should
-                                         only be set via
-                                         mbedtls_ssl_conf_psk() */
-    size_t         psk_len;         /*!< length of the pre-shared key. This
-                                         field should only be set via
-                                         mbedtls_ssl_conf_psk() */
-    unsigned char *psk_identity;    /*!< identity for PSK negotiation. This
-                                         field should only be set via
-                                         mbedtls_ssl_conf_psk() */
-    size_t         psk_identity_len;/*!< length of identity. This field should
-                                         only be set via
-                                         mbedtls_ssl_conf_psk() */
-#endif
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    psa_key_slot_t psk_opaque; /*!< PSA key slot holding opaque PSK.
+                                *   This field should only be set via
+                                *   mbedtls_ssl_conf_psk_opaque().
+                                *   If either no PSK or a raw PSK have
+                                *   been configured, this has value \c 0. */
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
+    unsigned char *psk;      /*!< The raw pre-shared key. This field should
+                              *   only be set via mbedtls_ssl_conf_psk().
+                              *   If either no PSK or an opaque PSK
+                              *   have been configured, this has value NULL. */
+    size_t         psk_len;  /*!< The length of the raw pre-shared key.
+                              *   This field should only be set via
+                              *   mbedtls_ssl_conf_psk().
+                              *   Its value is non-zero if and only if
+                              *   \c psk is not \c NULL. */
+
+    unsigned char *psk_identity;    /*!< The PSK identity for PSK negotiation.
+                                     *   This field should only be set via
+                                     *   mbedtls_ssl_conf_psk().
+                                     *   This is set if and only if either
+                                     *   \c psk or \c psk_opaque are set. */
+    size_t         psk_identity_len;/*!< The length of PSK identity.
+                                     *   This field should only be set via
+                                     *   mbedtls_ssl_conf_psk().
+                                     *   Its value is non-zero if and only if
+                                     *   \c psk is not \c NULL or \c psk_opaque
+                                     *   is not \c 0. */
+#endif /* MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */
 
 #if defined(MBEDTLS_SSL_ALPN)
     const char **alpn_list;         /*!< ordered list of protocols          */
@@ -2057,68 +2079,146 @@
 
 #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
 /**
- * \brief          Set the Pre Shared Key (PSK) and the expected identity name
+ * \brief          Configure a pre-shared key (PSK) and identity
+ *                 to be used in PSK-based ciphersuites.
  *
  * \note           This is mainly useful for clients. Servers will usually
  *                 want to use \c mbedtls_ssl_conf_psk_cb() instead.
  *
- * \note           Currently clients can only register one pre-shared key.
- *                 In other words, the servers' identity hint is ignored.
+ * \warning        Currently, clients can only register a single pre-shared key.
+ *                 Calling this function or mbedtls_ssl_conf_psk_opaque() more
+ *                 than once will overwrite values configured in previous calls.
  *                 Support for setting multiple PSKs on clients and selecting
- *                 one based on the identity hint is not a planned feature but
- *                 feedback is welcomed.
+ *                 one based on the identity hint is not a planned feature,
+ *                 but feedback is welcomed.
  *
- * \param conf     SSL configuration
- * \param psk      pointer to the pre-shared key
- * \param psk_len  pre-shared key length
- * \param psk_identity      pointer to the pre-shared key identity
- * \param psk_identity_len  identity key length
+ * \param conf     The SSL configuration to register the PSK with.
+ * \param psk      The pointer to the pre-shared key to use.
+ * \param psk_len  The length of the pre-shared key in bytes.
+ * \param psk_identity      The pointer to the pre-shared key identity.
+ * \param psk_identity_len  The length of the pre-shared key identity
+ *                          in bytes.
  *
- * \return         0 if successful or MBEDTLS_ERR_SSL_ALLOC_FAILED
+ * \note           The PSK and its identity are copied internally and
+ *                 hence need not be preserved by the caller for the lifetime
+ *                 of the SSL configuration.
+ *
+ * \return         \c 0 if successful.
+ * \return         An \c MBEDTLS_ERR_SSL_XXX error code on failure.
  */
 int mbedtls_ssl_conf_psk( mbedtls_ssl_config *conf,
                 const unsigned char *psk, size_t psk_len,
                 const unsigned char *psk_identity, size_t psk_identity_len );
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+/**
+ * \brief          Configure an opaque pre-shared key (PSK) and identity
+ *                 to be used in PSK-based ciphersuites.
+ *
+ * \note           This is mainly useful for clients. Servers will usually
+ *                 want to use \c mbedtls_ssl_conf_psk_cb() instead.
+ *
+ * \warning        Currently, clients can only register a single pre-shared key.
+ *                 Calling this function or mbedtls_ssl_conf_psk() more than
+ *                 once will overwrite values configured in previous calls.
+ *                 Support for setting multiple PSKs on clients and selecting
+ *                 one based on the identity hint is not a planned feature,
+ *                 but feedback is welcomed.
+ *
+ * \param conf     The SSL configuration to register the PSK with.
+ * \param psk      The identifier of the key slot holding the PSK.
+ *                 Until \p conf is destroyed or this function is successfully
+ *                 called again, the key slot \p psk must be populated with a
+ *                 key of type #PSA_ALG_CATEGORY_KEY_DERIVATION whose policy
+ *                 allows its use for the key derivation algorithm applied
+ *                 in the handshake.
+ * \param psk_identity      The pointer to the pre-shared key identity.
+ * \param psk_identity_len  The length of the pre-shared key identity
+ *                          in bytes.
+ *
+ * \note           The PSK identity hint is copied internally and hence need
+ *                 not be preserved by the caller for the lifetime of the
+ *                 SSL configuration.
+ *
+ * \return         \c 0 if successful.
+ * \return         An \c MBEDTLS_ERR_SSL_XXX error code on failure.
+ */
+int mbedtls_ssl_conf_psk_opaque( mbedtls_ssl_config *conf,
+                                 psa_key_slot_t psk,
+                                 const unsigned char *psk_identity,
+                                 size_t psk_identity_len );
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
 
 /**
- * \brief          Set the Pre Shared Key (PSK) for the current handshake
+ * \brief          Set the pre-shared Key (PSK) for the current handshake.
  *
  * \note           This should only be called inside the PSK callback,
- *                 ie the function passed to \c mbedtls_ssl_conf_psk_cb().
+ *                 i.e. the function passed to \c mbedtls_ssl_conf_psk_cb().
  *
- * \param ssl      SSL context
- * \param psk      pointer to the pre-shared key
- * \param psk_len  pre-shared key length
+ * \param ssl      The SSL context to configure a PSK for.
+ * \param psk      The pointer to the pre-shared key.
+ * \param psk_len  The length of the pre-shared key in bytes.
  *
- * \return         0 if successful or MBEDTLS_ERR_SSL_ALLOC_FAILED
+ * \return         \c 0 if successful.
+ * \return         An \c MBEDTLS_ERR_SSL_XXX error code on failure.
  */
 int mbedtls_ssl_set_hs_psk( mbedtls_ssl_context *ssl,
                             const unsigned char *psk, size_t psk_len );
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+/**
+ * \brief          Set an opaque pre-shared Key (PSK) for the current handshake.
+ *
+ * \note           This should only be called inside the PSK callback,
+ *                 i.e. the function passed to \c mbedtls_ssl_conf_psk_cb().
+ *
+ * \param ssl      The SSL context to configure a PSK for.
+ * \param psk      The identifier of the key slot holding the PSK.
+ *                 For the duration of the current handshake, the key slot
+ *                 must be populated with a key of type
+ *                 #PSA_ALG_CATEGORY_KEY_DERIVATION whose policy allows its
+ *                 use for the key derivation algorithm
+ *                 applied in the handshake.
+  *
+ * \return         \c 0 if successful.
+ * \return         An \c MBEDTLS_ERR_SSL_XXX error code on failure.
+ */
+int mbedtls_ssl_set_hs_psk_opaque( mbedtls_ssl_context *ssl,
+                                   psa_key_slot_t psk );
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
 /**
  * \brief          Set the PSK callback (server-side only).
  *
  *                 If set, the PSK callback is called for each
- *                 handshake where a PSK ciphersuite was negotiated.
+ *                 handshake where a PSK-based ciphersuite was negotiated.
  *                 The caller provides the identity received and wants to
  *                 receive the actual PSK data and length.
  *
- *                 The callback has the following parameters: (void *parameter,
- *                 mbedtls_ssl_context *ssl, const unsigned char *psk_identity,
- *                 size_t identity_len)
+ *                 The callback has the following parameters:
+ *                 - \c void*: The opaque pointer \p p_psk.
+ *                 - \c mbedtls_ssl_context*: The SSL context to which
+ *                                            the operation applies.
+ *                 - \c const unsigned char*: The PSK identity
+ *                                            selected by the client.
+ *                 - \c size_t: The length of the PSK identity
+ *                              selected by the client.
+ *
  *                 If a valid PSK identity is found, the callback should use
- *                 \c mbedtls_ssl_set_hs_psk() on the ssl context to set the
- *                 correct PSK and return 0.
+ *                 \c mbedtls_ssl_set_hs_psk() or
+ *                 \c mbedtls_ssl_set_hs_psk_opaque()
+ *                 on the SSL context to set the correct PSK and return \c 0.
  *                 Any other return value will result in a denied PSK identity.
  *
  * \note           If you set a PSK callback using this function, then you
  *                 don't need to set a PSK key and identity using
  *                 \c mbedtls_ssl_conf_psk().
  *
- * \param conf     SSL configuration
- * \param f_psk    PSK identity function
- * \param p_psk    PSK identity parameter
+ * \param conf     The SSL configuration to register the callback with.
+ * \param f_psk    The callback for selecting and setting the PSK based
+ *                 in the PSK identity chosen by the client.
+ * \param p_psk    A pointer to an opaque structure to be passed to
+ *                 the callback, for example a PSK store.
  */
 void mbedtls_ssl_conf_psk_cb( mbedtls_ssl_config *conf,
                      int (*f_psk)(void *, mbedtls_ssl_context *, const unsigned char *,
diff --git a/include/mbedtls/ssl_internal.h b/include/mbedtls/ssl_internal.h
index 97abb9f..318d13f 100644
--- a/include/mbedtls/ssl_internal.h
+++ b/include/mbedtls/ssl_internal.h
@@ -283,9 +283,12 @@
     const mbedtls_ecp_curve_info **curves;      /*!<  Supported elliptic curves */
 #endif
 #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    psa_key_slot_t psk_opaque;          /*!< Opaque PSK from the callback   */
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
     unsigned char *psk;                 /*!<  PSK from the callback         */
     size_t psk_len;                     /*!<  Length of PSK from callback   */
-#endif
+#endif /* MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */
 #if defined(MBEDTLS_X509_CRT_PARSE_C)
     mbedtls_ssl_key_cert *key_cert;     /*!< chosen key/cert pair (server)  */
 #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
diff --git a/library/pk.c b/library/pk.c
index e0e8dba..989ed09 100644
--- a/library/pk.c
+++ b/library/pk.c
@@ -41,6 +41,10 @@
 #include "mbedtls/ecdsa.h"
 #endif
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#include "mbedtls/psa_util.h"
+#endif
+
 #include <limits.h>
 #include <stdint.h>
 
@@ -139,6 +143,38 @@
     return( 0 );
 }
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+/*
+ * Initialise a PSA-wrapping context
+ */
+int mbedtls_pk_setup_opaque( mbedtls_pk_context *ctx, const psa_key_slot_t key )
+{
+    const mbedtls_pk_info_t * const info = &mbedtls_pk_opaque_info;
+    psa_key_slot_t *pk_ctx;
+    psa_key_type_t type;
+
+    if( ctx == NULL || ctx->pk_info != NULL )
+        return( MBEDTLS_ERR_PK_BAD_INPUT_DATA );
+
+    if( PSA_SUCCESS != psa_get_key_information( key, &type, NULL ) )
+        return( MBEDTLS_ERR_PK_BAD_INPUT_DATA );
+
+    /* Current implementation of can_do() relies on this. */
+    if( ! PSA_KEY_TYPE_IS_ECC_KEYPAIR( type ) )
+        return( MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE) ;
+
+    if( ( ctx->pk_ctx = info->ctx_alloc_func() ) == NULL )
+        return( MBEDTLS_ERR_PK_ALLOC_FAILED );
+
+    ctx->pk_info = info;
+
+    pk_ctx = (psa_key_slot_t *) ctx->pk_ctx;
+    *pk_ctx = key;
+
+    return( 0 );
+}
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
 #if defined(MBEDTLS_PK_RSA_ALT_SUPPORT)
 /*
  * Initialize an RSA-alt context
@@ -433,12 +469,14 @@
 int mbedtls_pk_check_pair( const mbedtls_pk_context *pub, const mbedtls_pk_context *prv )
 {
     if( pub == NULL || pub->pk_info == NULL ||
-        prv == NULL || prv->pk_info == NULL ||
-        prv->pk_info->check_pair_func == NULL )
+        prv == NULL || prv->pk_info == NULL )
     {
         return( MBEDTLS_ERR_PK_BAD_INPUT_DATA );
     }
 
+    if( prv->pk_info->check_pair_func == NULL )
+        return( MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE );
+
     if( prv->pk_info->type == MBEDTLS_PK_RSA_ALT )
     {
         if( pub->pk_info->type != MBEDTLS_PK_RSA )
@@ -501,4 +539,65 @@
     return( ctx->pk_info->type );
 }
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+/*
+ * Load the key to a PSA key slot,
+ * then turn the PK context into a wrapper for that key slot.
+ *
+ * Currently only works for EC private keys.
+ */
+int mbedtls_pk_wrap_as_opaque( mbedtls_pk_context *pk,
+                               psa_key_slot_t *slot,
+                               psa_algorithm_t hash_alg )
+{
+#if !defined(MBEDTLS_ECP_C)
+    return( MBEDTLS_ERR_PK_TYPE_MISMATCH );
+#else
+    psa_key_slot_t key;
+    const mbedtls_ecp_keypair *ec;
+    unsigned char d[MBEDTLS_ECP_MAX_BYTES];
+    size_t d_len;
+    psa_ecc_curve_t curve_id;
+    psa_key_type_t key_type;
+    psa_key_policy_t policy;
+    int ret;
+
+    /* export the private key material in the format PSA wants */
+    if( mbedtls_pk_get_type( pk ) != MBEDTLS_PK_ECKEY )
+        return( MBEDTLS_ERR_PK_TYPE_MISMATCH );
+
+    ec = mbedtls_pk_ec( *pk );
+    d_len = ( ec->grp.nbits + 7 ) / 8;
+    if( ( ret = mbedtls_mpi_write_binary( &ec->d, d, d_len ) ) != 0 )
+        return( ret );
+
+    curve_id = mbedtls_ecp_curve_info_from_grp_id( ec->grp.id )->tls_id;
+
+    /* find a free key slot */
+    if( PSA_SUCCESS != mbedtls_psa_get_free_key_slot( &key ) )
+        return( MBEDTLS_ERR_PK_HW_ACCEL_FAILED );
+
+    /* set policy */
+    psa_key_policy_init( &policy );
+    psa_key_policy_set_usage( &policy, PSA_KEY_USAGE_SIGN,
+                                       PSA_ALG_ECDSA(hash_alg) );
+    if( PSA_SUCCESS != psa_set_key_policy( key, &policy ) )
+        return( MBEDTLS_ERR_PK_HW_ACCEL_FAILED );
+
+    /* import private key in slot */
+    key_type = PSA_KEY_TYPE_ECC_KEYPAIR(curve_id);
+    if( PSA_SUCCESS != psa_import_key( key, key_type, d, d_len ) )
+        return( MBEDTLS_ERR_PK_HW_ACCEL_FAILED );
+
+    /* remember slot number to be destroyed later by caller */
+    *slot = key;
+
+    /* make PK context wrap the key slot */
+    mbedtls_pk_free( pk );
+    mbedtls_pk_init( pk );
+
+    return( mbedtls_pk_setup_opaque( pk, key ) );
+#endif /* MBEDTLS_ECP_C */
+}
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
 #endif /* MBEDTLS_PK_C */
diff --git a/library/pk_wrap.c b/library/pk_wrap.c
index 87806be..3af17d3 100644
--- a/library/pk_wrap.c
+++ b/library/pk_wrap.c
@@ -41,10 +41,18 @@
 #include "mbedtls/ecdsa.h"
 #endif
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#include "mbedtls/asn1write.h"
+#endif
+
 #if defined(MBEDTLS_PK_RSA_ALT_SUPPORT)
 #include "mbedtls/platform_util.h"
 #endif
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#include "mbedtls/psa_util.h"
+#endif
+
 #if defined(MBEDTLS_PLATFORM_C)
 #include "mbedtls/platform.h"
 #else
@@ -716,4 +724,182 @@
 
 #endif /* MBEDTLS_PK_RSA_ALT_SUPPORT */
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+
+static void *pk_opaque_alloc_wrap( void )
+{
+    void *ctx = mbedtls_calloc( 1, sizeof( psa_key_slot_t ) );
+
+    /* no _init() function to call, an calloc() already zeroized */
+
+    return( ctx );
+}
+
+static void pk_opaque_free_wrap( void *ctx )
+{
+    mbedtls_platform_zeroize( ctx, sizeof( psa_key_slot_t ) );
+    mbedtls_free( ctx );
+}
+
+static size_t pk_opaque_get_bitlen( const void *ctx )
+{
+    const psa_key_slot_t *key = (const psa_key_slot_t *) ctx;
+    size_t bits;
+
+    if( PSA_SUCCESS != psa_get_key_information( *key, NULL, &bits ) )
+        return( 0 );
+
+    return( bits );
+}
+
+static int pk_opaque_can_do( mbedtls_pk_type_t type )
+{
+    /* For now opaque PSA keys can only wrap ECC keypairs,
+     * as checked by setup_psa().
+     * Also, ECKEY_DH does not really make sense with the current API. */
+    return( type == MBEDTLS_PK_ECKEY ||
+            type == MBEDTLS_PK_ECDSA );
+}
+
+/*
+ * Simultaneously convert and move raw MPI from the beginning of a buffer
+ * to an ASN.1 MPI at the end of the buffer.
+ * See also mbedtls_asn1_write_mpi().
+ *
+ * p: pointer to the end of the output buffer
+ * start: start of the output buffer, and also of the mpi to write at the end
+ * n_len: length of the mpi to read from start
+ */
+static int asn1_write_mpibuf( unsigned char **p, unsigned char *start,
+                              size_t n_len )
+{
+    int ret;
+    size_t len = 0;
+
+    if( (size_t)( *p - start ) < n_len )
+        return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
+
+    len = n_len;
+    *p -= len;
+    memmove( *p, start, len );
+
+    /* ASN.1 DER encoding requires minimal length, so skip leading 0s.
+     * Neither r nor s should be 0, but as a failsafe measure, still detect
+     * that rather than overflowing the buffer in case of a PSA error. */
+    while( len > 0 && **p == 0x00 )
+    {
+        ++(*p);
+        --len;
+    }
+
+    /* this is only reached if the signature was invalid */
+    if( len == 0 )
+        return( MBEDTLS_ERR_PK_HW_ACCEL_FAILED );
+
+    /* if the msb is 1, ASN.1 requires that we prepend a 0.
+     * Neither r nor s can be 0, so we can assume len > 0 at all times. */
+    if( **p & 0x80 )
+    {
+        if( *p - start < 1 )
+            return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
+
+        *--(*p) = 0x00;
+        len += 1;
+    }
+
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( p, start, len ) );
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( p, start,
+                                                MBEDTLS_ASN1_INTEGER ) );
+
+    return( (int) len );
+}
+
+/* Transcode signature from PSA format to ASN.1 sequence.
+ * See ecdsa_signature_to_asn1 in ecdsa.c, but with byte buffers instead of
+ * MPIs, and in-place.
+ *
+ * [in/out] sig: the signature pre- and post-transcoding
+ * [in/out] sig_len: signature length pre- and post-transcoding
+ * [int] buf_len: the available size the in/out buffer
+ */
+static int pk_ecdsa_sig_asn1_from_psa( unsigned char *sig, size_t *sig_len,
+                                       size_t buf_len )
+{
+    int ret;
+    size_t len = 0;
+    const size_t rs_len = *sig_len / 2;
+    unsigned char *p = sig + buf_len;
+
+    MBEDTLS_ASN1_CHK_ADD( len, asn1_write_mpibuf( &p, sig + rs_len, rs_len ) );
+    MBEDTLS_ASN1_CHK_ADD( len, asn1_write_mpibuf( &p, sig, rs_len ) );
+
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &p, sig, len ) );
+    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &p, sig,
+                          MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) );
+
+    memmove( sig, p, len );
+    *sig_len = len;
+
+    return( 0 );
+}
+
+static int pk_opaque_sign_wrap( void *ctx, mbedtls_md_type_t md_alg,
+                   const unsigned char *hash, size_t hash_len,
+                   unsigned char *sig, size_t *sig_len,
+                   int (*f_rng)(void *, unsigned char *, size_t), void *p_rng )
+{
+    const psa_key_slot_t *key = (const psa_key_slot_t *) ctx;
+    psa_algorithm_t alg = PSA_ALG_ECDSA( mbedtls_psa_translate_md( md_alg ) );
+    size_t bits, buf_len;
+    psa_status_t status;
+
+    /* PSA has its own RNG */
+    (void) f_rng;
+    (void) p_rng;
+
+    /* PSA needs an output buffer of known size, but our API doesn't provide
+     * that information. Assume that the buffer is large enough for a
+     * maximal-length signature with that key (otherwise the application is
+     * buggy anyway). */
+    status = psa_get_key_information( *key, NULL, &bits );
+    if( status != PSA_SUCCESS )
+        return( mbedtls_psa_err_translate_pk( status ) );
+
+    buf_len = MBEDTLS_ECDSA_MAX_SIG_LEN( bits );
+
+    /* make the signature */
+    status = psa_asymmetric_sign( *key, alg, hash, hash_len,
+                                        sig, buf_len, sig_len );
+    if( status != PSA_SUCCESS )
+        return( mbedtls_psa_err_translate_pk( status ) );
+
+    /* transcode it to ASN.1 sequence */
+    return( pk_ecdsa_sig_asn1_from_psa( sig, sig_len, buf_len ) );
+}
+
+const mbedtls_pk_info_t mbedtls_pk_opaque_info = {
+    MBEDTLS_PK_OPAQUE,
+    "Opaque",
+    pk_opaque_get_bitlen,
+    pk_opaque_can_do,
+    NULL, /* verify - will be done later */
+    pk_opaque_sign_wrap,
+#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE)
+    NULL, /* restartable verify - not relevant */
+    NULL, /* restartable sign - not relevant */
+#endif
+    NULL, /* decrypt - will be done later */
+    NULL, /* encrypt - will be done later */
+    NULL, /* check_pair - could be done later or left NULL */
+    pk_opaque_alloc_wrap,
+    pk_opaque_free_wrap,
+#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE)
+    NULL, /* restart alloc - not relevant */
+    NULL, /* restart free - not relevant */
+#endif
+    NULL, /* debug - could be done later, or even left NULL */
+};
+
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
 #endif /* MBEDTLS_PK_C */
diff --git a/library/pkwrite.c b/library/pkwrite.c
index 8eabd88..d34714b 100644
--- a/library/pkwrite.c
+++ b/library/pkwrite.c
@@ -46,6 +46,9 @@
 #include "mbedtls/pem.h"
 #endif
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#include "psa/crypto.h"
+#endif
 #if defined(MBEDTLS_PLATFORM_C)
 #include "mbedtls/platform.h"
 #else
@@ -161,6 +164,28 @@
         MBEDTLS_ASN1_CHK_ADD( len, pk_write_ec_pubkey( p, start, mbedtls_pk_ec( *key ) ) );
     else
 #endif
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_OPAQUE )
+    {
+        size_t buffer_size;
+        psa_key_slot_t* key_slot = (psa_key_slot_t*) key->pk_ctx;
+
+        if ( *p < start )
+            return( MBEDTLS_ERR_PK_BAD_INPUT_DATA );
+
+        buffer_size = (size_t)( *p - start );
+        if ( psa_export_public_key( *key_slot, start, buffer_size, &len )
+             != PSA_SUCCESS )
+        {
+            return( MBEDTLS_ERR_PK_BAD_INPUT_DATA );
+        }
+        else
+        {
+            memmove( *p - len, start, len );
+        }
+    }
+    else
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
         return( MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE );
 
     return( (int) len );
@@ -177,6 +202,10 @@
 
     MBEDTLS_ASN1_CHK_ADD( len, mbedtls_pk_write_pubkey( &c, buf, key ) );
 
+    if( mbedtls_pk_get_type( key ) == MBEDTLS_PK_OPAQUE )
+    {
+        return( (int) len );
+    }
     if( c - buf < 1 )
         return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
 
diff --git a/library/ssl_cli.c b/library/ssl_cli.c
index ff576f3..cd25dca 100644
--- a/library/ssl_cli.c
+++ b/library/ssl_cli.c
@@ -51,6 +51,44 @@
 #include "mbedtls/platform_util.h"
 #endif
 
+#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
+static int ssl_conf_has_static_psk( mbedtls_ssl_config const *conf )
+{
+    if( conf->psk_identity     == NULL ||
+        conf->psk_identity_len == 0     )
+    {
+        return( 0 );
+    }
+
+    if( conf->psk != NULL && conf->psk_len != 0 )
+        return( 1 );
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    if( conf->psk_opaque != 0 )
+        return( 1 );
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
+    return( 0 );
+}
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+static int ssl_conf_has_static_raw_psk( mbedtls_ssl_config const *conf )
+{
+    if( conf->psk_identity     == NULL ||
+        conf->psk_identity_len == 0     )
+    {
+        return( 0 );
+    }
+
+    if( conf->psk != NULL && conf->psk_len != 0 )
+        return( 1 );
+
+    return( 0 );
+}
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
+#endif /* MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */
+
 #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
 static void ssl_write_hostname_ext( mbedtls_ssl_context *ssl,
                                     unsigned char *buf,
@@ -754,6 +792,15 @@
         return( 1 );
 #endif
 
+    /* Don't suggest PSK-based ciphersuite if no PSK is available. */
+#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
+    if( mbedtls_ssl_ciphersuite_uses_psk( suite_info ) &&
+        ssl_conf_has_static_psk( ssl->conf ) == 0 )
+    {
+        return( 1 );
+    }
+#endif /* MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */
+
     return( 0 );
 }
 
@@ -3007,10 +3054,12 @@
         /*
          * opaque psk_identity<0..2^16-1>;
          */
-        if( ssl->conf->psk == NULL || ssl->conf->psk_identity == NULL )
+        if( ssl_conf_has_static_psk( ssl->conf ) == 0 )
         {
-            MBEDTLS_SSL_DEBUG_MSG( 1, ( "got no private key for PSK" ) );
-            return( MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED );
+            /* We don't offer PSK suites if we don't have a PSK,
+             * and we check that the server's choice is among the
+             * ciphersuites we offered, so this should never happen. */
+            return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
         }
 
         i = 4;
@@ -3039,6 +3088,12 @@
 #if defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED)
         if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK )
         {
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+            /* Opaque PSKs are currently only supported for PSK-only suites. */
+            if( ssl_conf_has_static_raw_psk( ssl->conf ) == 0 )
+                return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE );
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
             if( ( ret = ssl_write_encrypted_pms( ssl, i, &n, 2 ) ) != 0 )
                 return( ret );
         }
@@ -3047,6 +3102,12 @@
 #if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED)
         if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK )
         {
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+            /* Opaque PSKs are currently only supported for PSK-only suites. */
+            if( ssl_conf_has_static_raw_psk( ssl->conf ) == 0 )
+                return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE );
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
             /*
              * ClientDiffieHellmanPublic public (DHM send G^X mod P)
              */
@@ -3077,6 +3138,12 @@
 #if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED)
         if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK )
         {
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+            /* Opaque PSKs are currently only supported for PSK-only suites. */
+            if( ssl_conf_has_static_raw_psk( ssl->conf ) == 0 )
+                return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE );
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
             /*
              * ClientECDiffieHellmanPublic public;
              */
@@ -3098,6 +3165,17 @@
             return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
         }
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO) &&          \
+    defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED)
+        if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK &&
+            ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_3 &&
+            ssl_conf_has_static_raw_psk( ssl->conf ) == 0 )
+        {
+            MBEDTLS_SSL_DEBUG_MSG( 1, ( "skip PMS generation for opaque PSK" ) );
+        }
+        else
+#endif /* MBEDTLS_USE_PSA_CRYPTO &&
+          MBEDTLS_KEY_EXCHANGE_PSK_ENABLED */
         if( ( ret = mbedtls_ssl_psk_derive_premaster( ssl,
                         ciphersuite_info->key_exchange ) ) != 0 )
         {
diff --git a/library/ssl_srv.c b/library/ssl_srv.c
index 36ca0d6..4d99f88 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -149,6 +149,48 @@
 }
 #endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */
 
+#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
+static int ssl_conf_has_psk_or_cb( mbedtls_ssl_config const *conf )
+{
+    if( conf->f_psk != NULL )
+        return( 1 );
+
+    if( conf->psk_identity_len == 0 || conf->psk_identity == NULL )
+        return( 0 );
+
+    if( conf->psk != NULL && conf->psk_len != 0 )
+        return( 1 );
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    if( conf->psk_opaque != 0 )
+        return( 1 );
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
+    return( 0 );
+}
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+static int ssl_use_opaque_psk( mbedtls_ssl_context const *ssl )
+{
+    if( ssl->conf->f_psk != NULL )
+    {
+        /* If we've used a callback to select the PSK,
+         * the static configuration is irrelevant. */
+
+        if( ssl->handshake->psk_opaque != 0 )
+            return( 1 );
+
+        return( 0 );
+    }
+
+    if( ssl->conf->psk_opaque != 0 )
+        return( 1 );
+
+    return( 0 );
+}
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+#endif /* MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */
+
 static int ssl_parse_renegotiation_info( mbedtls_ssl_context *ssl,
                                          const unsigned char *buf,
                                          size_t len )
@@ -867,9 +909,7 @@
     /* If the ciphersuite requires a pre-shared key and we don't
      * have one, skip it now rather than failing later */
     if( mbedtls_ssl_ciphersuite_uses_psk( suite_info ) &&
-        ssl->conf->f_psk == NULL &&
-        ( ssl->conf->psk == NULL || ssl->conf->psk_identity == NULL ||
-          ssl->conf->psk_identity_len == 0 || ssl->conf->psk_len == 0 ) )
+        ssl_conf_has_psk_or_cb( ssl->conf ) == 0 )
     {
         MBEDTLS_SSL_DEBUG_MSG( 3, ( "ciphersuite mismatch: no pre-shared key" ) );
         return( 0 );
@@ -3648,9 +3688,7 @@
     int ret = 0;
     size_t n;
 
-    if( ssl->conf->f_psk == NULL &&
-        ( ssl->conf->psk == NULL || ssl->conf->psk_identity == NULL ||
-          ssl->conf->psk_identity_len == 0 || ssl->conf->psk_len == 0 ) )
+    if( ssl_conf_has_psk_or_cb( ssl->conf ) == 0 )
     {
         MBEDTLS_SSL_DEBUG_MSG( 1, ( "got no pre-shared key" ) );
         return( MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED );
@@ -3828,6 +3866,13 @@
             return( MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE );
         }
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        /* For opaque PSKs, we perform the PSK-to-MS derivation atomatically
+         * and skip the intermediate PMS. */
+        if( ssl_use_opaque_psk( ssl ) == 1 )
+            MBEDTLS_SSL_DEBUG_MSG( 1, ( "skip PMS generation for opaque PSK" ) );
+        else
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
         if( ( ret = mbedtls_ssl_psk_derive_premaster( ssl,
                         ciphersuite_info->key_exchange ) ) != 0 )
         {
@@ -3859,6 +3904,12 @@
             return( ret );
         }
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        /* Opaque PSKs are currently only supported for PSK-only. */
+        if( ssl_use_opaque_psk( ssl ) == 1 )
+            return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE );
+#endif
+
         if( ( ret = ssl_parse_encrypted_pms( ssl, p, end, 2 ) ) != 0 )
         {
             MBEDTLS_SSL_DEBUG_RET( 1, ( "ssl_parse_encrypted_pms" ), ret );
@@ -3888,6 +3939,12 @@
             return( ret );
         }
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        /* Opaque PSKs are currently only supported for PSK-only. */
+        if( ssl_use_opaque_psk( ssl ) == 1 )
+            return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE );
+#endif
+
         if( p != end )
         {
             MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad client key exchange" ) );
@@ -3919,6 +3976,12 @@
             return( MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP );
         }
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        /* Opaque PSKs are currently only supported for PSK-only. */
+        if( ssl_use_opaque_psk( ssl ) == 1 )
+            return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE );
+#endif
+
         MBEDTLS_SSL_DEBUG_ECP( 3, "ECDH: Qp ", &ssl->handshake->ecdh_ctx.Qp );
 
         if( ( ret = mbedtls_ssl_psk_derive_premaster( ssl,
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index e6a4222..d14434a 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -607,6 +607,28 @@
 #endif
 #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */
 
+#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED) && \
+    defined(MBEDTLS_USE_PSA_CRYPTO)
+static int ssl_use_opaque_psk( mbedtls_ssl_context const *ssl )
+{
+    if( ssl->conf->f_psk != NULL )
+    {
+        /* If we've used a callback to select the PSK,
+         * the static configuration is irrelevant. */
+        if( ssl->handshake->psk_opaque != 0 )
+            return( 1 );
+
+        return( 0 );
+    }
+
+    if( ssl->conf->psk_opaque != 0 )
+        return( 1 );
+
+    return( 0 );
+}
+#endif /* MBEDTLS_USE_PSA_CRYPTO &&
+          MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */
+
 int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
 {
     int ret = 0;
@@ -625,6 +647,14 @@
     const mbedtls_cipher_info_t *cipher_info;
     const mbedtls_md_info_t *md_info;
 
+    /* cf. RFC 5246, Section 8.1:
+     * "The master secret is always exactly 48 bytes in length." */
+    size_t const master_secret_len = 48;
+
+#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET)
+    unsigned char session_hash[48];
+#endif /* MBEDTLS_SSL_EXTENDED_MASTER_SECRET */
+
     mbedtls_ssl_session *session = ssl->session_negotiate;
     mbedtls_ssl_transform *transform = ssl->transform_negotiate;
     mbedtls_ssl_handshake_params *handshake = ssl->handshake;
@@ -704,68 +734,127 @@
      * TLSv1+:
      *   master = PRF( premaster, "master secret", randbytes )[0..47]
      */
-    if( handshake->resume == 0 )
+    if( handshake->resume != 0 )
     {
-        MBEDTLS_SSL_DEBUG_BUF( 3, "premaster secret", handshake->premaster,
-                       handshake->pmslen );
+        MBEDTLS_SSL_DEBUG_MSG( 3, ( "no premaster (session resumed)" ) );
+    }
+    else
+    {
+        /* The label for the KDF used for key expansion.
+         * This is either "master secret" or "extended master secret"
+         * depending on whether the Extended Master Secret extension
+         * is used. */
+        char const *lbl = "master secret";
+
+        /* The salt for the KDF used for key expansion.
+         * - If the Extended Master Secret extension is not used,
+         *   this is ClientHello.Random + ServerHello.Random
+         *   (see Sect. 8.1 in RFC 5246).
+         * - If the Extended Master Secret extension is used,
+         *   this is the transcript of the handshake so far.
+         *   (see Sect. 4 in RFC 7627). */
+        unsigned char const *salt = handshake->randbytes;
+        size_t salt_len = 64;
+
+#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET)
+        const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
+            ssl->transform_negotiate->ciphersuite_info;
+        mbedtls_md_type_t const md_type = ciphersuite_info->mac;
+#endif /* MBEDTLS_SSL_EXTENDED_MASTER_SECRET */
 
 #if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET)
         if( ssl->handshake->extended_ms == MBEDTLS_SSL_EXTENDED_MS_ENABLED )
         {
-            unsigned char session_hash[48];
-            size_t hash_len;
-
             MBEDTLS_SSL_DEBUG_MSG( 3, ( "using extended master secret" ) );
 
+            lbl  = "extended master secret";
+            salt = session_hash;
             ssl->handshake->calc_verify( ssl, session_hash );
-
 #if defined(MBEDTLS_SSL_PROTO_TLS1_2)
             if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_3 )
             {
 #if defined(MBEDTLS_SHA512_C)
-                if( ssl->transform_negotiate->ciphersuite_info->mac ==
-                    MBEDTLS_MD_SHA384 )
-                {
-                    hash_len = 48;
-                }
+                if( md_type == MBEDTLS_MD_SHA384 )
+                    salt_len = 48;
                 else
-#endif
-                    hash_len = 32;
+#endif /* MBEDTLS_SHA512_C */
+                    salt_len = 32;
             }
             else
 #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */
-                hash_len = 36;
+                salt_len = 36;
 
-            MBEDTLS_SSL_DEBUG_BUF( 3, "session hash", session_hash, hash_len );
+            MBEDTLS_SSL_DEBUG_BUF( 3, "session hash", session_hash, salt_len );
+        }
+#endif /* MBEDTLS_SSL_EXTENDED_MS_ENABLED */
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO) &&          \
+    defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED)
+        if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK &&
+            ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_3 &&
+            ssl_use_opaque_psk( ssl ) == 1 )
+        {
+            /* Perform PSK-to-MS expansion in a single step. */
+            psa_status_t status;
+            psa_algorithm_t alg;
+            psa_crypto_generator_t generator = PSA_CRYPTO_GENERATOR_INIT;
+            psa_key_slot_t psk;
+
+            MBEDTLS_SSL_DEBUG_MSG( 2, ( "perform PSA-based PSK-to-MS expansion" ) );
+
+            psk = ssl->conf->psk_opaque;
+            if( ssl->handshake->psk_opaque != 0 )
+                psk = ssl->handshake->psk_opaque;
+
+            if( md_type == MBEDTLS_MD_SHA384 )
+                alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_384);
+            else
+                alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256);
+
+            status = psa_key_derivation( &generator, psk, alg,
+                                         salt, salt_len,
+                                         (unsigned char const *) lbl,
+                                         (size_t) strlen( lbl ),
+                                         master_secret_len );
+            if( status != PSA_SUCCESS )
+            {
+                psa_generator_abort( &generator );
+                return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
+            }
+
+            status = psa_generator_read( &generator, session->master,
+                                         master_secret_len );
+            if( status != PSA_SUCCESS )
+            {
+                psa_generator_abort( &generator );
+                return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
+            }
+
+            status = psa_generator_abort( &generator );
+            if( status != PSA_SUCCESS )
+                return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
+        }
+        else
+#endif
+        {
             ret = handshake->tls_prf( handshake->premaster, handshake->pmslen,
-                                      "extended master secret",
-                                      session_hash, hash_len,
-                                      session->master, 48 );
+                                      lbl, salt, salt_len,
+                                      session->master,
+                                      master_secret_len );
             if( ret != 0 )
             {
                 MBEDTLS_SSL_DEBUG_RET( 1, "prf", ret );
                 return( ret );
             }
 
-        }
-        else
-#endif
-        ret = handshake->tls_prf( handshake->premaster, handshake->pmslen,
-                                  "master secret",
-                                  handshake->randbytes, 64,
-                                  session->master, 48 );
-        if( ret != 0 )
-        {
-            MBEDTLS_SSL_DEBUG_RET( 1, "prf", ret );
-            return( ret );
-        }
+            MBEDTLS_SSL_DEBUG_BUF( 3, "premaster secret",
+                                   handshake->premaster,
+                                   handshake->pmslen );
 
-        mbedtls_platform_zeroize( handshake->premaster,
-                                  sizeof(handshake->premaster) );
+            mbedtls_platform_zeroize( handshake->premaster,
+                                      sizeof(handshake->premaster) );
+        }
     }
-    else
-        MBEDTLS_SSL_DEBUG_MSG( 3, ( "no premaster (session resumed)" ) );
 
     /*
      * Swap the client and server random values.
@@ -7403,23 +7492,23 @@
 #endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
 
 #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
-int mbedtls_ssl_conf_psk( mbedtls_ssl_config *conf,
-                const unsigned char *psk, size_t psk_len,
-                const unsigned char *psk_identity, size_t psk_identity_len )
+
+static void ssl_conf_remove_psk( mbedtls_ssl_config *conf )
 {
-    if( psk == NULL || psk_identity == NULL )
-        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
-
-    if( psk_len > MBEDTLS_PSK_MAX_LEN )
-        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
-
-    /* Identity len will be encoded on two bytes */
-    if( ( psk_identity_len >> 16 ) != 0 ||
-        psk_identity_len > MBEDTLS_SSL_OUT_CONTENT_LEN )
+    /* Remove reference to existing PSK, if any. */
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    if( conf->psk_opaque != 0 )
     {
-        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+        /* The maintenance of the PSK key slot is the
+         * user's responsibility. */
+        conf->psk_opaque = 0;
     }
-
+    /* This and the following branch should never
+     * be taken simultaenously as we maintain the
+     * invariant that raw and opaque PSKs are never
+     * configured simultaneously. As a safeguard,
+     * though, `else` is omitted here. */
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
     if( conf->psk != NULL )
     {
         mbedtls_platform_zeroize( conf->psk, conf->psk_len );
@@ -7428,32 +7517,84 @@
         conf->psk = NULL;
         conf->psk_len = 0;
     }
+
+    /* Remove reference to PSK identity, if any. */
     if( conf->psk_identity != NULL )
     {
         mbedtls_free( conf->psk_identity );
         conf->psk_identity = NULL;
         conf->psk_identity_len = 0;
     }
+}
 
-    if( ( conf->psk = mbedtls_calloc( 1, psk_len ) ) == NULL ||
-        ( conf->psk_identity = mbedtls_calloc( 1, psk_identity_len ) ) == NULL )
+/* This function assumes that PSK identity in the SSL config is unset.
+ * It checks that the provided identity is well-formed and attempts
+ * to make a copy of it in the SSL config.
+ * On failure, the PSK identity in the config remains unset. */
+static int ssl_conf_set_psk_identity( mbedtls_ssl_config *conf,
+                                      unsigned char const *psk_identity,
+                                      size_t psk_identity_len )
+{
+    /* Identity len will be encoded on two bytes */
+    if( psk_identity               == NULL ||
+        ( psk_identity_len >> 16 ) != 0    ||
+        psk_identity_len > MBEDTLS_SSL_OUT_CONTENT_LEN )
     {
-        mbedtls_free( conf->psk );
-        mbedtls_free( conf->psk_identity );
-        conf->psk = NULL;
-        conf->psk_identity = NULL;
-        return( MBEDTLS_ERR_SSL_ALLOC_FAILED );
+        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
     }
 
-    conf->psk_len = psk_len;
-    conf->psk_identity_len = psk_identity_len;
+    conf->psk_identity = mbedtls_calloc( 1, psk_identity_len );
+    if( conf->psk_identity == NULL )
+        return( MBEDTLS_ERR_SSL_ALLOC_FAILED );
 
-    memcpy( conf->psk, psk, conf->psk_len );
+    conf->psk_identity_len = psk_identity_len;
     memcpy( conf->psk_identity, psk_identity, conf->psk_identity_len );
 
     return( 0 );
 }
 
+int mbedtls_ssl_conf_psk( mbedtls_ssl_config *conf,
+                const unsigned char *psk, size_t psk_len,
+                const unsigned char *psk_identity, size_t psk_identity_len )
+{
+    int ret;
+    /* Remove opaque/raw PSK + PSK Identity */
+    ssl_conf_remove_psk( conf );
+
+    /* Check and set raw PSK */
+    if( psk == NULL || psk_len > MBEDTLS_PSK_MAX_LEN )
+        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+    if( ( conf->psk = mbedtls_calloc( 1, psk_len ) ) == NULL )
+        return( MBEDTLS_ERR_SSL_ALLOC_FAILED );
+    conf->psk_len = psk_len;
+    memcpy( conf->psk, psk, conf->psk_len );
+
+    /* Check and set PSK Identity */
+    ret = ssl_conf_set_psk_identity( conf, psk_identity, psk_identity_len );
+    if( ret != 0 )
+        ssl_conf_remove_psk( conf );
+
+    return( ret );
+}
+
+static void ssl_remove_psk( mbedtls_ssl_context *ssl )
+{
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    if( ssl->handshake->psk_opaque != 0 )
+    {
+        ssl->handshake->psk_opaque = 0;
+    }
+    else
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+    if( ssl->handshake->psk != NULL )
+    {
+        mbedtls_platform_zeroize( ssl->handshake->psk,
+                                  ssl->handshake->psk_len );
+        mbedtls_free( ssl->handshake->psk );
+        ssl->handshake->psk_len = 0;
+    }
+}
+
 int mbedtls_ssl_set_hs_psk( mbedtls_ssl_context *ssl,
                             const unsigned char *psk, size_t psk_len )
 {
@@ -7463,13 +7604,7 @@
     if( psk_len > MBEDTLS_PSK_MAX_LEN )
         return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
 
-    if( ssl->handshake->psk != NULL )
-    {
-        mbedtls_platform_zeroize( ssl->handshake->psk,
-                                  ssl->handshake->psk_len );
-        mbedtls_free( ssl->handshake->psk );
-        ssl->handshake->psk_len = 0;
-    }
+    ssl_remove_psk( ssl );
 
     if( ( ssl->handshake->psk = mbedtls_calloc( 1, psk_len ) ) == NULL )
         return( MBEDTLS_ERR_SSL_ALLOC_FAILED );
@@ -7480,6 +7615,42 @@
     return( 0 );
 }
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+int mbedtls_ssl_conf_psk_opaque( mbedtls_ssl_config *conf,
+                                 psa_key_slot_t psk_slot,
+                                 const unsigned char *psk_identity,
+                                 size_t psk_identity_len )
+{
+    int ret;
+    /* Clear opaque/raw PSK + PSK Identity, if present. */
+    ssl_conf_remove_psk( conf );
+
+    /* Check and set opaque PSK */
+    if( psk_slot == 0 )
+        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+    conf->psk_opaque = psk_slot;
+
+    /* Check and set PSK Identity */
+    ret = ssl_conf_set_psk_identity( conf, psk_identity,
+                                     psk_identity_len );
+    if( ret != 0 )
+        ssl_conf_remove_psk( conf );
+
+    return( ret );
+}
+
+int mbedtls_ssl_set_hs_psk_opaque( mbedtls_ssl_context *ssl,
+                                   psa_key_slot_t psk_slot )
+{
+    if( psk_slot == 0 || ssl->handshake == NULL )
+        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+    ssl_remove_psk( ssl );
+    ssl->handshake->psk_opaque = psk_slot;
+    return( 0 );
+}
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
 void mbedtls_ssl_conf_psk_cb( mbedtls_ssl_config *conf,
                      int (*f_psk)(void *, mbedtls_ssl_context *, const unsigned char *,
                      size_t),
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index 87b9ab1..a98a3a2 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -61,6 +61,7 @@
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
 #include "psa/crypto.h"
+#include "mbedtls/psa_util.h"
 #endif
 
 #include <stdio.h>
@@ -84,7 +85,9 @@
 #define DFL_CA_PATH             ""
 #define DFL_CRT_FILE            ""
 #define DFL_KEY_FILE            ""
+#define DFL_KEY_OPAQUE          0
 #define DFL_PSK                 ""
+#define DFL_PSK_OPAQUE          0
 #define DFL_PSK_IDENTITY        "Client_identity"
 #define DFL_ECJPAKE_PW          NULL
 #define DFL_EC_MAX_OPS          -1
@@ -134,14 +137,36 @@
 #define USAGE_IO \
     "    No file operations available (MBEDTLS_FS_IO not defined)\n"
 #endif /* MBEDTLS_FS_IO */
-#else
+#else /* MBEDTLS_X509_CRT_PARSE_C */
 #define USAGE_IO ""
 #endif /* MBEDTLS_X509_CRT_PARSE_C */
+#if defined(MBEDTLS_USE_PSA_CRYPTO) && defined(MBEDTLS_X509_CRT_PARSE_C)
+#define USAGE_KEY_OPAQUE \
+    "    key_opaque=%%d       Handle your private key as if it were opaque\n" \
+    "                        default: 0 (disabled)\n"
+#else
+#define USAGE_KEY_OPAQUE ""
+#endif
 
 #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
-#define USAGE_PSK                                                   \
+#define USAGE_PSK_RAW                                               \
     "    psk=%%s              default: \"\" (in hex, without 0x)\n" \
     "    psk_identity=%%s     default: \"Client_identity\"\n"
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#define USAGE_PSK_SLOT                          \
+    "    psk_opaque=%%d       default: 0 (don't use opaque static PSK)\n"     \
+    "                          Enable this to store the PSK configured through command line\n" \
+    "                          parameter `psk` in a PSA-based key slot.\n" \
+    "                          Note: Currently only supported in conjunction with\n"                  \
+    "                          the use of min_version to force TLS 1.2 and force_ciphersuite \n"      \
+    "                          to force a particular PSK-only ciphersuite.\n"                         \
+    "                          Note: This is to test integration of PSA-based opaque PSKs with\n"     \
+    "                          Mbed TLS only. Production systems are likely to configure Mbed TLS\n"  \
+    "                          with prepopulated key slots instead of importing raw key material.\n"
+#else
+#define USAGE_PSK_SLOT ""
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+#define USAGE_PSK USAGE_PSK_RAW USAGE_PSK_SLOT
 #else
 #define USAGE_PSK ""
 #endif /* MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */
@@ -283,6 +308,7 @@
     "    auth_mode=%%s        default: (library default: none)\n" \
     "                        options: none, optional, required\n" \
     USAGE_IO                                                \
+    USAGE_KEY_OPAQUE                                        \
     "\n"                                                    \
     USAGE_PSK                                               \
     USAGE_ECJPAKE                                           \
@@ -337,6 +363,10 @@
     const char *ca_path;        /* the path with the CA certificate(s) reside */
     const char *crt_file;       /* the file with the client certificate     */
     const char *key_file;       /* the file with the client key             */
+    int key_opaque;             /* handle private key as if it were opaque  */
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    int psk_opaque;
+#endif
     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                   */
@@ -540,6 +570,13 @@
 
     const char *pers = "ssl_client2";
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    psa_key_slot_t slot = 0;
+    psa_algorithm_t alg = 0;
+    psa_key_policy_t policy;
+    psa_status_t status;
+#endif
+
 #if defined(MBEDTLS_X509_CRT_PARSE_C)
     mbedtls_x509_crt_profile crt_profile_for_test = mbedtls_x509_crt_profile_default;
 #endif
@@ -556,12 +593,12 @@
     mbedtls_x509_crt cacert;
     mbedtls_x509_crt clicert;
     mbedtls_pk_context pkey;
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    psa_key_slot_t key_slot = 0; /* invalid key slot */
+#endif
 #endif
     char *p, *q;
     const int *list;
-#if defined(MBEDTLS_USE_PSA_CRYPTO)
-    psa_status_t status;
-#endif
 
     /*
      * Make sure memory references are valid.
@@ -627,7 +664,11 @@
     opt.ca_path             = DFL_CA_PATH;
     opt.crt_file            = DFL_CRT_FILE;
     opt.key_file            = DFL_KEY_FILE;
+    opt.key_opaque          = DFL_KEY_OPAQUE;
     opt.psk                 = DFL_PSK;
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    opt.psk_opaque          = DFL_PSK_OPAQUE;
+#endif
     opt.psk_identity        = DFL_PSK_IDENTITY;
     opt.ecjpake_pw          = DFL_ECJPAKE_PW;
     opt.ec_max_ops          = DFL_EC_MAX_OPS;
@@ -726,8 +767,16 @@
             opt.crt_file = q;
         else if( strcmp( p, "key_file" ) == 0 )
             opt.key_file = q;
+#if defined(MBEDTLS_USE_PSA_CRYPTO) && defined(MBEDTLS_X509_CRT_PARSE_C)
+        else if( strcmp( p, "key_opaque" ) == 0 )
+            opt.key_opaque = atoi( q );
+#endif
         else if( strcmp( p, "psk" ) == 0 )
             opt.psk = q;
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        else if( strcmp( p, "psk_opaque" ) == 0 )
+            opt.psk_opaque = atoi( q );
+#endif
         else if( strcmp( p, "psk_identity" ) == 0 )
             opt.psk_identity = q;
         else if( strcmp( p, "ecjpake_pw" ) == 0 )
@@ -1012,57 +1061,6 @@
     mbedtls_debug_set_threshold( opt.debug_level );
 #endif
 
-    if( opt.force_ciphersuite[0] > 0 )
-    {
-        const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
-        ciphersuite_info =
-            mbedtls_ssl_ciphersuite_from_id( opt.force_ciphersuite[0] );
-
-        if( opt.max_version != -1 &&
-            ciphersuite_info->min_minor_ver > opt.max_version )
-        {
-            mbedtls_printf( "forced ciphersuite not allowed with this protocol version\n" );
-            ret = 2;
-            goto usage;
-        }
-        if( opt.min_version != -1 &&
-            ciphersuite_info->max_minor_ver < opt.min_version )
-        {
-            mbedtls_printf( "forced ciphersuite not allowed with this protocol version\n" );
-            ret = 2;
-            goto usage;
-        }
-
-        /* If the server selects a version that's not supported by
-         * this suite, then there will be no common ciphersuite... */
-        if( opt.max_version == -1 ||
-            opt.max_version > ciphersuite_info->max_minor_ver )
-        {
-            opt.max_version = ciphersuite_info->max_minor_ver;
-        }
-        if( opt.min_version < ciphersuite_info->min_minor_ver )
-        {
-            opt.min_version = ciphersuite_info->min_minor_ver;
-            /* DTLS starts with TLS 1.1 */
-            if( opt.transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
-                opt.min_version < MBEDTLS_SSL_MINOR_VERSION_2 )
-                opt.min_version = MBEDTLS_SSL_MINOR_VERSION_2;
-        }
-
-        /* Enable RC4 if needed and not explicitly disabled */
-        if( ciphersuite_info->cipher == MBEDTLS_CIPHER_ARC4_128 )
-        {
-            if( opt.arc4 == MBEDTLS_SSL_ARC4_DISABLED )
-            {
-                mbedtls_printf( "forced RC4 ciphersuite with RC4 disabled\n" );
-                ret = 2;
-                goto usage;
-            }
-
-            opt.arc4 = MBEDTLS_SSL_ARC4_ENABLED;
-        }
-    }
-
 #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
     /*
      * Unhexify the pre-shared key if any is given
@@ -1113,6 +1111,101 @@
     }
 #endif /* MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */
 
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    if( opt.psk_opaque != 0 )
+    {
+        if( opt.psk == NULL )
+        {
+            mbedtls_printf( "psk_opaque set but no psk to be imported specified.\n" );
+            ret = 2;
+            goto usage;
+        }
+
+        if( opt.force_ciphersuite[0] <= 0 )
+        {
+            mbedtls_printf( "opaque PSKs are only supported in conjunction with forcing TLS 1.2 and a PSK-only ciphersuite through the 'force_ciphersuite' option.\n" );
+            ret = 2;
+            goto usage;
+        }
+    }
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
+    if( opt.force_ciphersuite[0] > 0 )
+    {
+        const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
+        ciphersuite_info =
+            mbedtls_ssl_ciphersuite_from_id( opt.force_ciphersuite[0] );
+
+        if( opt.max_version != -1 &&
+            ciphersuite_info->min_minor_ver > opt.max_version )
+        {
+            mbedtls_printf( "forced ciphersuite not allowed with this protocol version\n" );
+            ret = 2;
+            goto usage;
+        }
+        if( opt.min_version != -1 &&
+            ciphersuite_info->max_minor_ver < opt.min_version )
+        {
+            mbedtls_printf( "forced ciphersuite not allowed with this protocol version\n" );
+            ret = 2;
+            goto usage;
+        }
+
+        /* If the server selects a version that's not supported by
+         * this suite, then there will be no common ciphersuite... */
+        if( opt.max_version == -1 ||
+            opt.max_version > ciphersuite_info->max_minor_ver )
+        {
+            opt.max_version = ciphersuite_info->max_minor_ver;
+        }
+        if( opt.min_version < ciphersuite_info->min_minor_ver )
+        {
+            opt.min_version = ciphersuite_info->min_minor_ver;
+            /* DTLS starts with TLS 1.1 */
+            if( opt.transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
+                opt.min_version < MBEDTLS_SSL_MINOR_VERSION_2 )
+                opt.min_version = MBEDTLS_SSL_MINOR_VERSION_2;
+        }
+
+        /* Enable RC4 if needed and not explicitly disabled */
+        if( ciphersuite_info->cipher == MBEDTLS_CIPHER_ARC4_128 )
+        {
+            if( opt.arc4 == MBEDTLS_SSL_ARC4_DISABLED )
+            {
+                mbedtls_printf( "forced RC4 ciphersuite with RC4 disabled\n" );
+                ret = 2;
+                goto usage;
+            }
+
+            opt.arc4 = MBEDTLS_SSL_ARC4_ENABLED;
+        }
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        if( opt.psk_opaque != 0 )
+        {
+            /* Ensure that the chosen ciphersuite is PSK-only; we must know
+             * the ciphersuite in advance to set the correct policy for the
+             * PSK key slot. This limitation might go away in the future. */
+            if( ciphersuite_info->key_exchange != MBEDTLS_KEY_EXCHANGE_PSK ||
+                opt.min_version != MBEDTLS_SSL_MINOR_VERSION_3 )
+            {
+                mbedtls_printf( "opaque PSKs are only supported in conjunction with forcing TLS 1.2 and a PSK-only ciphersuite through the 'force_ciphersuite' option.\n" );
+                ret = 2;
+                goto usage;
+            }
+
+            /* Determine KDF algorithm the opaque PSK will be used in. */
+#if defined(MBEDTLS_SHA512_C)
+            if( ciphersuite_info->mac == MBEDTLS_MD_SHA384 )
+                alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_384);
+            else
+#endif /* MBEDTLS_SHA512_C */
+                alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256);
+        }
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+    }
+
 #if defined(MBEDTLS_ECP_C)
     if( opt.curves != NULL )
     {
@@ -1309,7 +1402,20 @@
         goto exit;
     }
 
-    mbedtls_printf( " ok\n" );
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    if( opt.key_opaque != 0 )
+    {
+        if( ( ret = mbedtls_pk_wrap_as_opaque( &pkey, &key_slot,
+                                               PSA_ALG_SHA_256 ) ) != 0 )
+        {
+            mbedtls_printf( " failed\n  !  "
+                            "mbedtls_pk_wrap_as_opaque returned -0x%x\n\n", -ret );
+            goto exit;
+        }
+    }
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
+    mbedtls_printf( " ok (key type: %s)\n", mbedtls_pk_get_name( &pkey ) );
 #endif /* MBEDTLS_X509_CRT_PARSE_C */
 
     /*
@@ -1484,6 +1590,45 @@
 #endif
 
 #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    if( opt.psk_opaque != 0 )
+    {
+        /* The algorithm has already been determined earlier. */
+        status = mbedtls_psa_get_free_key_slot( &slot );
+        if( status != PSA_SUCCESS )
+        {
+            ret = MBEDTLS_ERR_SSL_HW_ACCEL_FAILED;
+            goto exit;
+        }
+
+        psa_key_policy_init( &policy );
+        psa_key_policy_set_usage( &policy, PSA_KEY_USAGE_DERIVE, alg );
+
+        status = psa_set_key_policy( slot, &policy );
+        if( status != PSA_SUCCESS )
+        {
+            ret = MBEDTLS_ERR_SSL_HW_ACCEL_FAILED;
+            goto exit;
+        }
+
+        status = psa_import_key( slot, PSA_KEY_TYPE_DERIVE, psk, psk_len );
+        if( status != PSA_SUCCESS )
+        {
+            ret = MBEDTLS_ERR_SSL_HW_ACCEL_FAILED;
+            goto exit;
+        }
+
+        if( ( ret = mbedtls_ssl_conf_psk_opaque( &conf, slot,
+                                  (const unsigned char *) opt.psk_identity,
+                                  strlen( opt.psk_identity ) ) ) != 0 )
+        {
+            mbedtls_printf( " failed\n  ! mbedtls_ssl_conf_psk_opaque returned %d\n\n",
+                            ret );
+            goto exit;
+        }
+    }
+    else
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
     if( ( ret = mbedtls_ssl_conf_psk( &conf, psk, psk_len,
                              (const unsigned char *) opt.psk_identity,
                              strlen( opt.psk_identity ) ) ) != 0 )
@@ -1492,7 +1637,7 @@
                         ret );
         goto exit;
     }
-#endif
+#endif /* MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */
 
     if( opt.min_version != DFL_MIN_VERSION )
         mbedtls_ssl_conf_min_version( &conf, MBEDTLS_SSL_MAJOR_VERSION_3,
@@ -2116,6 +2261,9 @@
     mbedtls_x509_crt_free( &clicert );
     mbedtls_x509_crt_free( &cacert );
     mbedtls_pk_free( &pkey );
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    psa_destroy_key( key_slot );
+#endif
 #endif
     mbedtls_ssl_session_free( &saved_session );
     mbedtls_ssl_free( &ssl );
@@ -2123,6 +2271,26 @@
     mbedtls_ctr_drbg_free( &ctr_drbg );
     mbedtls_entropy_free( &entropy );
 
+#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED) && \
+    defined(MBEDTLS_USE_PSA_CRYPTO)
+    if( opt.psk_opaque != 0 )
+    {
+        /* This is ok even if the slot hasn't been
+         * initialized (we might have jumed here
+         * immediately because of bad cmd line params,
+         * for example). */
+        status = psa_destroy_key( slot );
+        if( status != PSA_SUCCESS )
+        {
+            mbedtls_printf( "Failed to destroy key slot %u - error was %d",
+                            (unsigned) slot, (int) status );
+            if( ret == 0 )
+                ret = MBEDTLS_ERR_SSL_HW_ACCEL_FAILED;
+        }
+    }
+#endif /* MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED &&
+          MBEDTLS_USE_PSA_CRYPTO */
+
 #if defined(_WIN32)
     mbedtls_printf( "  + Press Enter to exit this program.\n" );
     fflush( stdout ); getchar();
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index 1c6ccae..534a3f3 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -62,6 +62,7 @@
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
 #include "psa/crypto.h"
+#include "mbedtls/psa_util.h"
 #endif
 
 #include <stdio.h>
@@ -123,6 +124,8 @@
 #define DFL_ASYNC_PRIVATE_DELAY2 ( -1 )
 #define DFL_ASYNC_PRIVATE_ERROR  ( 0 )
 #define DFL_PSK                 ""
+#define DFL_PSK_OPAQUE          0
+#define DFL_PSK_LIST_OPAQUE     0
 #define DFL_PSK_IDENTITY        "Client_identity"
 #define DFL_ECJPAKE_PW          NULL
 #define DFL_PSK_LIST            NULL
@@ -224,9 +227,36 @@
 #endif /* MBEDTLS_SSL_ASYNC_PRIVATE */
 
 #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
-#define USAGE_PSK                                                   \
+#define USAGE_PSK_RAW                                               \
     "    psk=%%s              default: \"\" (in hex, without 0x)\n" \
-    "    psk_identity=%%s     default: \"Client_identity\"\n"
+    "    psk_identity=%%s     default: \"Client_identity\"\n"       \
+    "    psk_list=%%s         default: \"\"\n"                          \
+    "                          A list of (PSK identity, PSK value) pairs in (hex format, without 0x)\n" \
+    "                          id1,psk1[,id2,psk2[,...]]\n"
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#define USAGE_PSK_SLOT                          \
+    "    psk_opaque=%%d       default: 0 (don't use opaque static PSK)\n"     \
+    "                          Enable this to store the PSK configured through command line\n" \
+    "                          parameter `psk` in a PSA-based key slot.\n" \
+    "                          Note: Currently only supported in conjunction with\n"                  \
+    "                          the use of min_version to force TLS 1.2 and force_ciphersuite \n"      \
+    "                          to force a particular PSK-only ciphersuite.\n"                         \
+    "                          Note: This is to test integration of PSA-based opaque PSKs with\n"     \
+    "                          Mbed TLS only. Production systems are likely to configure Mbed TLS\n"  \
+    "                          with prepopulated key slots instead of importing raw key material.\n" \
+    "    psk_list_opaque=%%d  default: 0 (don't use opaque dynamic PSKs)\n"     \
+    "                          Enable this to store the list of dynamically chosen PSKs configured\n" \
+    "                          through the command line parameter `psk_list` in PSA-based key slots.\n" \
+    "                          Note: Currently only supported in conjunction with\n" \
+    "                          the use of min_version to force TLS 1.2 and force_ciphersuite \n" \
+    "                          to force a particular PSK-only ciphersuite.\n" \
+    "                          Note: This is to test integration of PSA-based opaque PSKs with\n" \
+    "                          Mbed TLS only. Production systems are likely to configure Mbed TLS\n" \
+    "                          with prepopulated key slots instead of importing raw key material.\n"
+#else
+#define USAGE_PSK_SLOT ""
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+#define USAGE_PSK USAGE_PSK_RAW USAGE_PSK_SLOT
 #else
 #define USAGE_PSK ""
 #endif /* MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */
@@ -453,6 +483,10 @@
     int async_private_delay1;   /* number of times f_async_resume needs to be called for key 1, or -1 for no async */
     int async_private_delay2;   /* number of times f_async_resume needs to be called for key 2, or -1 for no async */
     int async_private_error;    /* inject error in async private callback */
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    int psk_opaque;
+    int psk_list_opaque;
+#endif
     const char *psk;            /* the pre-shared key                       */
     const char *psk_identity;   /* the pre-shared key identity              */
     char *psk_list;             /* list of PSK id/key pairs for callback    */
@@ -771,22 +805,39 @@
     const char *name;
     size_t key_len;
     unsigned char key[MBEDTLS_PSK_MAX_LEN];
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    psa_key_slot_t slot;
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
     psk_entry *next;
 };
 
 /*
  * Free a list of psk_entry's
  */
-void psk_free( psk_entry *head )
+int psk_free( psk_entry *head )
 {
     psk_entry *next;
 
     while( head != NULL )
     {
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        psa_status_t status;
+        psa_key_slot_t const slot = head->slot;
+
+        if( slot != 0 )
+        {
+            status = psa_destroy_key( slot );
+            if( status != PSA_SUCCESS )
+                return( status );
+        }
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
         next = head->next;
         mbedtls_free( head );
         head = next;
     }
+
+    return( 0 );
 }
 
 /*
@@ -844,6 +895,11 @@
         if( name_len == strlen( cur->name ) &&
             memcmp( name, cur->name, name_len ) == 0 )
         {
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+            if( cur->slot != 0 )
+                return( mbedtls_ssl_set_hs_psk_opaque( ssl, cur->slot ) );
+            else
+#endif
             return( mbedtls_ssl_set_hs_psk( ssl, cur->key, cur->key_len ) );
         }
 
@@ -1174,12 +1230,46 @@
     return( 0 );
 }
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+static psa_status_t psa_setup_psk_key_slot( psa_key_slot_t slot,
+                                            psa_algorithm_t alg,
+                                            unsigned char *psk,
+                                            size_t psk_len )
+{
+    psa_status_t status;
+    psa_key_policy_t policy;
+
+    psa_key_policy_init( &policy );
+    psa_key_policy_set_usage( &policy, PSA_KEY_USAGE_DERIVE, alg );
+
+    status = psa_set_key_policy( slot, &policy );
+    if( status != PSA_SUCCESS )
+    {
+        fprintf( stderr, "POLICY\n" );
+        return( status );
+    }
+
+    status = psa_import_key( slot, PSA_KEY_TYPE_DERIVE, psk, psk_len );
+    if( status != PSA_SUCCESS )
+    {
+        fprintf( stderr, "IMPORT\n" );
+        return( status );
+    }
+
+    return( PSA_SUCCESS );
+}
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
 int main( int argc, char *argv[] )
 {
     int ret = 0, len, written, frags, exchanges_left;
     int version_suites[4][2];
     unsigned char* buf = 0;
 #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    psa_algorithm_t alg = 0;
+    psa_key_slot_t psk_slot = 0;
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
     unsigned char psk[MBEDTLS_PSK_MAX_LEN];
     size_t psk_len = 0;
     psk_entry *psk_info = NULL;
@@ -1342,6 +1432,10 @@
     opt.async_private_delay2 = DFL_ASYNC_PRIVATE_DELAY2;
     opt.async_private_error = DFL_ASYNC_PRIVATE_ERROR;
     opt.psk                 = DFL_PSK;
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    opt.psk_opaque          = DFL_PSK_OPAQUE;
+    opt.psk_list_opaque     = DFL_PSK_LIST_OPAQUE;
+#endif
     opt.psk_identity        = DFL_PSK_IDENTITY;
     opt.psk_list            = DFL_PSK_LIST;
     opt.ecjpake_pw          = DFL_ECJPAKE_PW;
@@ -1470,6 +1564,12 @@
 #endif /* MBEDTLS_SSL_ASYNC_PRIVATE */
         else if( strcmp( p, "psk" ) == 0 )
             opt.psk = q;
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        else if( strcmp( p, "psk_opaque" ) == 0 )
+            opt.psk_opaque = atoi( q );
+        else if( strcmp( p, "psk_list_opaque" ) == 0 )
+            opt.psk_list_opaque = atoi( q );
+#endif
         else if( strcmp( p, "psk_identity" ) == 0 )
             opt.psk_identity = q;
         else if( strcmp( p, "psk_list" ) == 0 )
@@ -1779,6 +1879,42 @@
         goto exit;
     }
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+    if( opt.psk_opaque != 0 )
+    {
+        if( strlen( opt.psk ) == 0 )
+        {
+            mbedtls_printf( "psk_opaque set but no psk to be imported specified.\n" );
+            ret = 2;
+            goto usage;
+        }
+
+        if( opt.force_ciphersuite[0] <= 0 )
+        {
+            mbedtls_printf( "opaque PSKs are only supported in conjunction with forcing TLS 1.2 and a PSK-only ciphersuite through the 'force_ciphersuite' option.\n" );
+            ret = 2;
+            goto usage;
+        }
+    }
+
+    if( opt.psk_list_opaque != 0 )
+    {
+        if( opt.psk_list == NULL )
+        {
+            mbedtls_printf( "psk_slot set but no psk to be imported specified.\n" );
+            ret = 2;
+            goto usage;
+        }
+
+        if( opt.force_ciphersuite[0] <= 0 )
+        {
+            mbedtls_printf( "opaque PSKs are only supported in conjunction with forcing TLS 1.2 and a PSK-only ciphersuite through the 'force_ciphersuite' option.\n" );
+            ret = 2;
+            goto usage;
+        }
+    }
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
     if( opt.force_ciphersuite[0] > 0 )
     {
         const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
@@ -1828,6 +1964,30 @@
 
             opt.arc4 = MBEDTLS_SSL_ARC4_ENABLED;
         }
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        if( opt.psk_opaque != 0 || opt.psk_list_opaque != 0 )
+        {
+            /* Ensure that the chosen ciphersuite is PSK-only; we must know
+             * the ciphersuite in advance to set the correct policy for the
+             * PSK key slot. This limitation might go away in the future. */
+            if( ciphersuite_info->key_exchange != MBEDTLS_KEY_EXCHANGE_PSK ||
+                opt.min_version != MBEDTLS_SSL_MINOR_VERSION_3 )
+            {
+                mbedtls_printf( "opaque PSKs are only supported in conjunction with forcing TLS 1.2 and a PSK-only ciphersuite through the 'force_ciphersuite' option.\n" );
+                ret = 2;
+                goto usage;
+            }
+
+            /* Determine KDF algorithm the opaque PSK will be used in. */
+#if defined(MBEDTLS_SHA512_C)
+            if( ciphersuite_info->mac == MBEDTLS_MD_SHA384 )
+                alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_384);
+            else
+#endif /* MBEDTLS_SHA512_C */
+                alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256);
+        }
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
     }
 
     if( opt.version_suites != NULL )
@@ -2501,12 +2661,42 @@
 #endif
 
 #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
+
     if( strlen( opt.psk ) != 0 && strlen( opt.psk_identity ) != 0 )
     {
-        ret = mbedtls_ssl_conf_psk( &conf, psk, psk_len,
-                           (const unsigned char *) opt.psk_identity,
-                           strlen( opt.psk_identity ) );
-        if( ret != 0 )
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        if( opt.psk_opaque != 0 )
+        {
+            status = mbedtls_psa_get_free_key_slot( &psk_slot );
+            if( status != PSA_SUCCESS )
+            {
+                fprintf( stderr, "ALLOC FAIL\n" );
+                ret = MBEDTLS_ERR_SSL_HW_ACCEL_FAILED;
+                goto exit;
+            }
+
+            /* The algorithm has already been determined earlier. */
+            status = psa_setup_psk_key_slot( psk_slot, alg, psk, psk_len );
+            if( status != PSA_SUCCESS )
+            {
+                fprintf( stderr, "SETUP FAIL\n" );
+                ret = MBEDTLS_ERR_SSL_HW_ACCEL_FAILED;
+                goto exit;
+            }
+            if( ( ret = mbedtls_ssl_conf_psk_opaque( &conf, psk_slot,
+                             (const unsigned char *) opt.psk_identity,
+                             strlen( opt.psk_identity ) ) ) != 0 )
+            {
+                mbedtls_printf( " failed\n  ! mbedtls_ssl_conf_psk_opaque returned %d\n\n",
+                                ret );
+                goto exit;
+            }
+        }
+        else
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+        if( ( ret = mbedtls_ssl_conf_psk( &conf, psk, psk_len,
+                                     (const unsigned char *) opt.psk_identity,
+                                     strlen( opt.psk_identity ) ) ) != 0 )
         {
             mbedtls_printf( "  failed\n  mbedtls_ssl_conf_psk returned -0x%04X\n\n", - ret );
             goto exit;
@@ -2514,7 +2704,34 @@
     }
 
     if( opt.psk_list != NULL )
+    {
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+        if( opt.psk_list_opaque != 0 )
+        {
+            psk_entry *cur_psk;
+            for( cur_psk = psk_info; cur_psk != NULL; cur_psk = cur_psk->next )
+            {
+                status = mbedtls_psa_get_free_key_slot( &cur_psk->slot );
+                if( status != PSA_SUCCESS )
+                {
+                    ret = MBEDTLS_ERR_SSL_HW_ACCEL_FAILED;
+                    goto exit;
+                }
+
+                status = psa_setup_psk_key_slot( cur_psk->slot, alg,
+                                                 cur_psk->key,
+                                                 cur_psk->key_len );
+                if( status != PSA_SUCCESS )
+                {
+                    ret = MBEDTLS_ERR_SSL_HW_ACCEL_FAILED;
+                    goto exit;
+                }
+            }
+        }
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
         mbedtls_ssl_conf_psk_cb( &conf, psk_callback, psk_info );
+    }
 #endif
 
 #if defined(MBEDTLS_DHM_C)
@@ -3143,12 +3360,31 @@
     sni_free( sni_info );
 #endif
 #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
-    psk_free( psk_info );
+    if( ( ret = psk_free( psk_info ) ) != 0 )
+        mbedtls_printf( "Failed to list of opaque PSKs - error was %d\n", ret );
 #endif
 #if defined(MBEDTLS_DHM_C) && defined(MBEDTLS_FS_IO)
     mbedtls_dhm_free( &dhm );
 #endif
 
+#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED) && \
+    defined(MBEDTLS_USE_PSA_CRYPTO)
+    if( opt.psk_opaque != 0 )
+    {
+        /* This is ok even if the slot hasn't been
+         * initialized (we might have jumed here
+         * immediately because of bad cmd line params,
+         * for example). */
+        status = psa_destroy_key( psk_slot );
+        if( status != PSA_SUCCESS )
+        {
+            mbedtls_printf( "Failed to destroy key slot %u - error was %d",
+                            (unsigned) psk_slot, (int) status );
+        }
+    }
+#endif /* MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED &&
+          MBEDTLS_USE_PSA_CRYPTO */
+
     mbedtls_ssl_free( &ssl );
     mbedtls_ssl_config_free( &conf );
     mbedtls_ctr_drbg_free( &ctr_drbg );
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index bae70e8..2ccecc4 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -900,6 +900,21 @@
             -s "Protocol is DTLSv1.2" \
             -s "Ciphersuite is TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256"
 
+# Test using an opaque private key for client authentication
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+requires_config_enabled MBEDTLS_X509_CRT_PARSE_C
+requires_config_enabled MBEDTLS_ECDSA_C
+requires_config_enabled MBEDTLS_SHA256_C
+run_test    "Opaque key for client authentication" \
+            "$P_SRV auth_mode=required" \
+            "$P_CLI key_opaque=1 crt_file=data_files/server5.crt \
+             key_file=data_files/server5.key" \
+            0 \
+            -c "key type: Opaque" \
+            -s "Verifying peer X.509 certificate... ok" \
+            -S "error" \
+            -C "error"
+
 # Test ciphersuites which we expect to be fully supported by PSA Crypto
 # and check that we don't fall back to Mbed TLS' internal crypto primitives.
 run_test_psa TLS-ECDHE-ECDSA-WITH-AES-128-CCM
@@ -3877,6 +3892,240 @@
             -S "SSL - Unknown identity received" \
             -S "SSL - Verification of the message MAC failed"
 
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: opaque psk on client, no callback" \
+            "$P_SRV extended_ms=0 debug_level=1 psk=abc123 psk_identity=foo" \
+            "$P_CLI extended_ms=0 debug_level=1 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA \
+            psk_identity=foo psk=abc123 psk_opaque=1" \
+            0 \
+            -c "skip PMS generation for opaque PSK"\
+            -S "skip PMS generation for opaque PSK"\
+            -C "using extended master secret"\
+            -S "using extended master secret"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: opaque psk on client, no callback, SHA-384" \
+            "$P_SRV extended_ms=0 debug_level=1 psk=abc123 psk_identity=foo" \
+            "$P_CLI extended_ms=0 debug_level=1 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-256-CBC-SHA384 \
+            psk_identity=foo psk=abc123 psk_opaque=1" \
+            0 \
+            -c "skip PMS generation for opaque PSK"\
+            -S "skip PMS generation for opaque PSK"\
+            -C "using extended master secret"\
+            -S "using extended master secret"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: opaque psk on client, no callback, EMS" \
+            "$P_SRV extended_ms=1 debug_level=3 psk=abc123 psk_identity=foo" \
+            "$P_CLI extended_ms=1 debug_level=3 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA \
+            psk_identity=foo psk=abc123 psk_opaque=1" \
+            0 \
+            -c "skip PMS generation for opaque PSK"\
+            -S "skip PMS generation for opaque PSK"\
+            -c "using extended master secret"\
+            -s "using extended master secret"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: opaque psk on client, no callback, SHA-384, EMS" \
+            "$P_SRV extended_ms=1 debug_level=3 psk=abc123 psk_identity=foo" \
+            "$P_CLI extended_ms=1 debug_level=3 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-256-CBC-SHA384 \
+            psk_identity=foo psk=abc123 psk_opaque=1" \
+            0 \
+            -c "skip PMS generation for opaque PSK"\
+            -S "skip PMS generation for opaque PSK"\
+            -c "using extended master secret"\
+            -s "using extended master secret"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: raw psk on client, static opaque on server, no callback" \
+            "$P_SRV extended_ms=0 debug_level=1 psk=abc123 psk_identity=foo psk_opaque=1 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA" \
+            "$P_CLI extended_ms=0 debug_level=1 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA \
+            psk_identity=foo psk=abc123" \
+            0 \
+            -C "skip PMS generation for opaque PSK"\
+            -s "skip PMS generation for opaque PSK"\
+            -C "using extended master secret"\
+            -S "using extended master secret"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: raw psk on client, static opaque on server, no callback, SHA-384" \
+            "$P_SRV extended_ms=0 debug_level=1 psk=abc123 psk_identity=foo psk_opaque=1 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-256-CBC-SHA384" \
+            "$P_CLI extended_ms=0 debug_level=1 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-256-CBC-SHA384 \
+            psk_identity=foo psk=abc123" \
+            0 \
+            -C "skip PMS generation for opaque PSK"\
+            -s "skip PMS generation for opaque PSK"\
+            -C "using extended master secret"\
+            -S "using extended master secret"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: raw psk on client, static opaque on server, no callback, EMS" \
+            "$P_SRV debug_level=3 psk=abc123 psk_identity=foo psk_opaque=1 min_version=tls1_2 \
+            force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA extended_ms=1" \
+            "$P_CLI debug_level=3 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA \
+            psk_identity=foo psk=abc123 extended_ms=1" \
+            0 \
+            -c "using extended master secret"\
+            -s "using extended master secret"\
+            -C "skip PMS generation for opaque PSK"\
+            -s "skip PMS generation for opaque PSK"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: raw psk on client, static opaque on server, no callback, EMS, SHA384" \
+            "$P_SRV debug_level=3 psk=abc123 psk_identity=foo psk_opaque=1 min_version=tls1_2 \
+            force_ciphersuite=TLS-PSK-WITH-AES-256-CBC-SHA384 extended_ms=1" \
+            "$P_CLI debug_level=3 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-256-CBC-SHA384 \
+            psk_identity=foo psk=abc123 extended_ms=1" \
+            0 \
+            -c "using extended master secret"\
+            -s "using extended master secret"\
+            -C "skip PMS generation for opaque PSK"\
+            -s "skip PMS generation for opaque PSK"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: raw psk on client, no static PSK on server, opaque PSK from callback" \
+            "$P_SRV extended_ms=0 debug_level=3 psk_list=abc,dead,def,beef psk_list_opaque=1 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA" \
+            "$P_CLI extended_ms=0 debug_level=3 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA \
+            psk_identity=def psk=beef" \
+            0 \
+            -C "skip PMS generation for opaque PSK"\
+            -s "skip PMS generation for opaque PSK"\
+            -C "using extended master secret"\
+            -S "using extended master secret"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: raw psk on client, no static PSK on server, opaque PSK from callback, SHA-384" \
+            "$P_SRV extended_ms=0 debug_level=3 psk_list=abc,dead,def,beef psk_list_opaque=1 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-256-CBC-SHA384" \
+            "$P_CLI extended_ms=0 debug_level=3 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-256-CBC-SHA384 \
+            psk_identity=def psk=beef" \
+            0 \
+            -C "skip PMS generation for opaque PSK"\
+            -s "skip PMS generation for opaque PSK"\
+            -C "using extended master secret"\
+            -S "using extended master secret"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: raw psk on client, no static PSK on server, opaque PSK from callback, EMS" \
+            "$P_SRV debug_level=3 psk_list=abc,dead,def,beef psk_list_opaque=1 min_version=tls1_2 \
+            force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA extended_ms=1" \
+            "$P_CLI debug_level=3 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA \
+            psk_identity=abc psk=dead extended_ms=1" \
+            0 \
+            -c "using extended master secret"\
+            -s "using extended master secret"\
+            -C "skip PMS generation for opaque PSK"\
+            -s "skip PMS generation for opaque PSK"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: raw psk on client, no static PSK on server, opaque PSK from callback, EMS, SHA384" \
+            "$P_SRV debug_level=3 psk_list=abc,dead,def,beef psk_list_opaque=1 min_version=tls1_2 \
+            force_ciphersuite=TLS-PSK-WITH-AES-256-CBC-SHA384 extended_ms=1" \
+            "$P_CLI debug_level=3 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-256-CBC-SHA384 \
+            psk_identity=abc psk=dead extended_ms=1" \
+            0 \
+            -c "using extended master secret"\
+            -s "using extended master secret"\
+            -C "skip PMS generation for opaque PSK"\
+            -s "skip PMS generation for opaque PSK"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: raw psk on client, mismatching static raw PSK on server, opaque PSK from callback" \
+            "$P_SRV extended_ms=0 psk_identity=foo psk=abc123 debug_level=3 psk_list=abc,dead,def,beef psk_list_opaque=1 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA" \
+            "$P_CLI extended_ms=0 debug_level=3 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA \
+            psk_identity=def psk=beef" \
+            0 \
+            -C "skip PMS generation for opaque PSK"\
+            -s "skip PMS generation for opaque PSK"\
+            -C "using extended master secret"\
+            -S "using extended master secret"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: raw psk on client, mismatching static opaque PSK on server, opaque PSK from callback" \
+            "$P_SRV extended_ms=0 psk_opaque=1 psk_identity=foo psk=abc123 debug_level=3 psk_list=abc,dead,def,beef psk_list_opaque=1 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA" \
+            "$P_CLI extended_ms=0 debug_level=3 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA \
+            psk_identity=def psk=beef" \
+            0 \
+            -C "skip PMS generation for opaque PSK"\
+            -s "skip PMS generation for opaque PSK"\
+            -C "using extended master secret"\
+            -S "using extended master secret"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: raw psk on client, mismatching static opaque PSK on server, raw PSK from callback" \
+            "$P_SRV extended_ms=0 psk_opaque=1 psk_identity=foo psk=abc123 debug_level=3 psk_list=abc,dead,def,beef min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA" \
+            "$P_CLI extended_ms=0 debug_level=3 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA \
+            psk_identity=def psk=beef" \
+            0 \
+            -C "skip PMS generation for opaque PSK"\
+            -C "using extended master secret"\
+            -S "using extended master secret"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: raw psk on client, id-matching but wrong raw PSK on server, opaque PSK from callback" \
+            "$P_SRV extended_ms=0 psk_opaque=1 psk_identity=def psk=abc123 debug_level=3 psk_list=abc,dead,def,beef min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA" \
+            "$P_CLI extended_ms=0 debug_level=3 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA \
+            psk_identity=def psk=beef" \
+            0 \
+            -C "skip PMS generation for opaque PSK"\
+            -C "using extended master secret"\
+            -S "using extended master secret"\
+            -S "SSL - None of the common ciphersuites is usable" \
+            -S "SSL - Unknown identity received" \
+            -S "SSL - Verification of the message MAC failed"
+
+requires_config_enabled MBEDTLS_USE_PSA_CRYPTO
+run_test    "PSK callback: raw psk on client, matching opaque PSK on server, wrong opaque PSK from callback" \
+            "$P_SRV extended_ms=0 psk_opaque=1 psk_identity=def psk=beef debug_level=3 psk_list=abc,dead,def,abc123 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA" \
+            "$P_CLI extended_ms=0 debug_level=3 min_version=tls1_2 force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA \
+            psk_identity=def psk=beef" \
+            1 \
+            -s "SSL - Verification of the message MAC failed"
+
 run_test    "PSK callback: no psk, no callback" \
             "$P_SRV" \
             "$P_CLI force_ciphersuite=TLS-PSK-WITH-AES-128-CBC-SHA \
diff --git a/tests/suites/test_suite_pk.data b/tests/suites/test_suite_pk.data
index 478cde7..0497502 100644
--- a/tests/suites/test_suite_pk.data
+++ b/tests/suites/test_suite_pk.data
@@ -14,6 +14,9 @@
 depends_on:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP192R1_ENABLED
 pk_utils:MBEDTLS_PK_ECDSA:192:24:"ECDSA"
 
+PK PSA utilities: setup/free, info functions, unsupported operations
+pk_psa_utils:
+
 RSA verify test vector #1 (good)
 depends_on:MBEDTLS_SHA1_C:MBEDTLS_PKCS1_V15
 pk_rsa_verify_test_vec:"206ef4bf396c6087f8229ef196fd35f37ccb8de5efcdb238f20d556668f114257a11fbe038464a67830378e62ae9791453953dac1dbd7921837ba98e84e856eb80ed9487e656d0b20c28c8ba5e35db1abbed83ed1c7720a97701f709e3547a4bfcabca9c89c57ad15c3996577a0ae36d7c7b699035242f37954646c1cd5c08ac":MBEDTLS_MD_SHA1:1024:16:"e28a13548525e5f36dccb24ecb7cc332cc689dfd64012604c9c7816d72a16c3f5fcdc0e86e7c03280b1c69b586ce0cd8aec722cc73a5d3b730310bf7dfebdc77ce5d94bbc369dc18a2f7b07bd505ab0f82224aef09fdc1e5063234255e0b3c40a52e9e8ae60898eb88a766bdd788fe9493d8fd86bcdd2884d5c06216c65469e5":16:"3":"5abc01f5de25b70867ff0c24e222c61f53c88daf42586fddcd56f3c4588f074be3c328056c063388688b6385a8167957c6e5355a510e005b8a851d69c96b36ec6036644078210e5d7d326f96365ee0648882921492bc7b753eb9c26cdbab37555f210df2ca6fec1b25b463d38b81c0dcea202022b04af5da58aa03d77be949b7":0
@@ -185,3 +188,6 @@
 ECDSA restartable sign/verify: ECKEY, max_ops=250
 depends_on:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C
 pk_sign_verify_restart:MBEDTLS_PK_ECKEY:MBEDTLS_ECP_DP_SECP256R1:"C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721":"60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6":"7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299":MBEDTLS_MD_SHA256:"test":"3045022100f1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d383670220019f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083":250:2:64
+
+PSA wrapped sign
+pk_psa_sign:
diff --git a/tests/suites/test_suite_pk.function b/tests/suites/test_suite_pk.function
index 4813f71..37cf5c5 100644
--- a/tests/suites/test_suite_pk.function
+++ b/tests/suites/test_suite_pk.function
@@ -62,6 +62,45 @@
     return( ((const mbedtls_rsa_context *) ctx)->len );
 }
 #endif /* MBEDTLS_RSA_C */
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+
+#include "mbedtls/psa_util.h"
+
+#define PK_PSA_INVALID_SLOT 0 /* guaranteed invalid */
+
+/*
+ * Generate a key in a free key slot and return this key slot,
+ * or PK_PSA_INVALID_SLOT if no slot was available.
+ * The key uses NIST P-256 and is usable for signing with SHA-256.
+ */
+psa_key_slot_t pk_psa_genkey( void )
+{
+    psa_key_slot_t key;
+
+    const int curve = PSA_ECC_CURVE_SECP256R1;
+    const psa_key_type_t type = PSA_KEY_TYPE_ECC_KEYPAIR(curve);
+    const size_t bits = 256;
+    psa_key_policy_t policy;
+
+    /* find a free key slot */
+    if( PSA_SUCCESS != mbedtls_psa_get_free_key_slot( &key ) )
+        return( PK_PSA_INVALID_SLOT );
+
+    /* set up policy on key slot */
+    psa_key_policy_init( &policy );
+    psa_key_policy_set_usage( &policy, PSA_KEY_USAGE_SIGN,
+                                      PSA_ALG_ECDSA(PSA_ALG_SHA_256) );
+    if( PSA_SUCCESS != psa_set_key_policy( key, &policy ) )
+        return( PK_PSA_INVALID_SLOT );
+
+    /* generate key */
+    if( PSA_SUCCESS != psa_generate_key( key, type, bits, NULL, 0 ) )
+        return( PK_PSA_INVALID_SLOT );
+
+    return( key );
+}
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
 /* END_HEADER */
 
 /* BEGIN_DEPENDENCIES
@@ -69,6 +108,77 @@
  * END_DEPENDENCIES
  */
 
+/* BEGIN_CASE depends_on:MBEDTLS_USE_PSA_CRYPTO:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED */
+void pk_psa_utils(  )
+{
+    mbedtls_pk_context pk, pk2;
+    psa_key_slot_t key;
+
+    const char * const name = "Opaque";
+    const size_t bitlen = 256; /* harcoded in genkey() */
+
+    mbedtls_md_type_t md_alg = MBEDTLS_MD_NONE;
+    unsigned char b1[1], b2[1];
+    size_t len;
+    mbedtls_pk_debug_item dbg;
+
+    mbedtls_pk_init( &pk );
+    mbedtls_pk_init( &pk2 );
+
+    TEST_ASSERT( mbedtls_pk_setup_opaque( &pk, 0 ) ==
+                 MBEDTLS_ERR_PK_BAD_INPUT_DATA );
+
+    mbedtls_pk_free( &pk );
+    mbedtls_pk_init( &pk );
+
+    key = pk_psa_genkey();
+    TEST_ASSERT( key != 0 );
+
+    TEST_ASSERT( mbedtls_pk_setup_opaque( &pk, key ) == 0 );
+
+    TEST_ASSERT( mbedtls_pk_get_type( &pk ) == MBEDTLS_PK_OPAQUE );
+    TEST_ASSERT( strcmp( mbedtls_pk_get_name( &pk), name ) == 0 );
+
+    TEST_ASSERT( mbedtls_pk_get_bitlen( &pk ) == bitlen );
+    TEST_ASSERT( mbedtls_pk_get_len( &pk ) == bitlen / 8 );
+
+    TEST_ASSERT( mbedtls_pk_can_do( &pk, MBEDTLS_PK_ECKEY ) == 1 );
+    TEST_ASSERT( mbedtls_pk_can_do( &pk, MBEDTLS_PK_ECDSA ) == 1 );
+    TEST_ASSERT( mbedtls_pk_can_do( &pk, MBEDTLS_PK_RSA ) == 0 );
+
+    /* unsupported operations: verify, decrypt, encrypt */
+    TEST_ASSERT( mbedtls_pk_verify( &pk, md_alg,
+                                    b1, sizeof( b1), b2, sizeof( b2 ) )
+                 == MBEDTLS_ERR_PK_TYPE_MISMATCH );
+    TEST_ASSERT( mbedtls_pk_decrypt( &pk, b1, sizeof( b1 ),
+                                     b2, &len, sizeof( b2 ),
+                                     NULL, NULL )
+                 == MBEDTLS_ERR_PK_TYPE_MISMATCH );
+    TEST_ASSERT( mbedtls_pk_encrypt( &pk, b1, sizeof( b1 ),
+                                     b2, &len, sizeof( b2 ),
+                                     NULL, NULL )
+                 == MBEDTLS_ERR_PK_TYPE_MISMATCH );
+
+    /* unsupported functions: check_pair, debug */
+    TEST_ASSERT( mbedtls_pk_setup( &pk2,
+                 mbedtls_pk_info_from_type( MBEDTLS_PK_ECKEY ) ) == 0 );
+    TEST_ASSERT( mbedtls_pk_check_pair( &pk, &pk2 )
+                 == MBEDTLS_ERR_PK_TYPE_MISMATCH );
+    TEST_ASSERT( mbedtls_pk_debug( &pk, &dbg )
+                 == MBEDTLS_ERR_PK_TYPE_MISMATCH );
+
+    /* test that freeing the context does not destroy the key */
+    mbedtls_pk_free( &pk );
+    TEST_ASSERT( PSA_SUCCESS == psa_get_key_information( key, NULL, NULL ) );
+    TEST_ASSERT( PSA_SUCCESS == psa_destroy_key( key ) );
+
+exit:
+    mbedtls_pk_free( &pk ); /* redundant except upon error */
+    mbedtls_pk_free( &pk2 );
+}
+/* END_CASE */
+
+
 /* BEGIN_CASE */
 void pk_utils( int type, int size, int len, char * name )
 {
@@ -662,3 +772,53 @@
     mbedtls_pk_free( &rsa ); mbedtls_pk_free( &alt );
 }
 /* END_CASE */
+
+/* BEGIN_CASE depends_on:MBEDTLS_SHA256_C:MBEDTLS_USE_PSA_CRYPTO:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED */
+void pk_psa_sign(  )
+{
+    mbedtls_pk_context pk;
+    psa_key_slot_t key;
+    unsigned char hash[50], sig[100], pkey[100];
+    size_t sig_len, klen = 0;
+
+    /*
+     * This tests making signatures with a wrapped PSA key:
+     * - generate a fresh PSA key
+     * - wrap it in a PK context and make a signature this way
+     * - extract the public key
+     * - parse it to a PK context and verify the signature this way
+     */
+
+    mbedtls_pk_init( &pk );
+
+    memset( hash, 0x2a, sizeof hash );
+    memset( sig, 0, sizeof sig );
+    memset( pkey, 0, sizeof pkey );
+
+    key = pk_psa_genkey();
+    TEST_ASSERT( key != 0 );
+
+    TEST_ASSERT( mbedtls_pk_setup_opaque( &pk, key ) == 0 );
+
+    TEST_ASSERT( mbedtls_pk_sign( &pk, MBEDTLS_MD_SHA256,
+                 hash, sizeof hash, sig, &sig_len,
+                 NULL, NULL ) == 0 );
+
+    mbedtls_pk_free( &pk );
+
+    TEST_ASSERT( PSA_SUCCESS == psa_export_public_key(
+                                key, pkey, sizeof( pkey ), &klen ) );
+    TEST_ASSERT( PSA_SUCCESS == psa_destroy_key( key ) );
+
+    mbedtls_pk_init( &pk );
+
+    TEST_ASSERT( mbedtls_pk_parse_public_key( &pk, pkey, klen ) == 0 );
+
+
+    TEST_ASSERT( mbedtls_pk_verify( &pk, MBEDTLS_MD_SHA256,
+                            hash, sizeof hash, sig, sig_len ) == 0 );
+
+exit:
+    mbedtls_pk_free( &pk );
+}
+/* END_CASE */
diff --git a/tests/suites/test_suite_x509write.data b/tests/suites/test_suite_x509write.data
index 5b54d85..4096425 100644
--- a/tests/suites/test_suite_x509write.data
+++ b/tests/suites/test_suite_x509write.data
@@ -42,6 +42,10 @@
 depends_on:MBEDTLS_SHA1_C:MBEDTLS_ECDSA_C:MBEDTLS_ECDSA_DETERMINISTIC:MBEDTLS_ECP_DP_SECP256R1_ENABLED
 x509_csr_check:"data_files/server5.key":"data_files/server5.req.ku.sha1":MBEDTLS_MD_SHA1:MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_NON_REPUDIATION:0
 
+Certificate Request check opaque Server5 ECDSA, key_usage
+depends_on:MBEDTLS_SHA256_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED
+x509_csr_check_opaque:"data_files/server5.key":MBEDTLS_MD_SHA256:MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_NON_REPUDIATION:0
+
 Certificate write check Server1 SHA1
 depends_on:MBEDTLS_SHA1_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_MD5_C
 x509_crt_check:"data_files/server1.key":"":"C=NL,O=PolarSSL,CN=PolarSSL Server 1":"data_files/test-ca.key":"PolarSSLTest":"C=NL,O=PolarSSL,CN=PolarSSL Test CA":"1":"20110212144406":"20210212144406":MBEDTLS_MD_SHA1:0:0:1:-1:"data_files/server1.crt":0
diff --git a/tests/suites/test_suite_x509write.function b/tests/suites/test_suite_x509write.function
index c00b1ac..bf43a80 100644
--- a/tests/suites/test_suite_x509write.function
+++ b/tests/suites/test_suite_x509write.function
@@ -5,6 +5,11 @@
 #include "mbedtls/pem.h"
 #include "mbedtls/oid.h"
 #include "mbedtls/rsa.h"
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#include "psa/crypto.h"
+#include "mbedtls/psa_util.h"
+#endif
+
 
 #if defined(MBEDTLS_RSA_C)
 int mbedtls_rsa_decrypt_func( void *ctx, int mode, size_t *olen,
@@ -28,6 +33,34 @@
 }
 #endif /* MBEDTLS_RSA_C */
 
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+static int x509_crt_verifycsr( const unsigned char *buf, size_t buflen )
+{
+    unsigned char hash[MBEDTLS_MD_MAX_SIZE];
+    const mbedtls_md_info_t *md_info;
+    mbedtls_x509_csr csr;
+
+    if( mbedtls_x509_csr_parse( &csr, buf, buflen ) != 0 )
+        return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
+
+    md_info = mbedtls_md_info_from_type( csr.sig_md );
+    if( mbedtls_md( md_info, csr.cri.p, csr.cri.len, hash ) != 0 )
+    {
+        /* Note: this can't happen except after an internal error */
+        return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
+    }
+
+    if( mbedtls_pk_verify_ext( csr.sig_pk, csr.sig_opts, &csr.pk,
+                       csr.sig_md, hash, mbedtls_md_get_size( md_info ),
+                       csr.sig.p, csr.sig.len ) != 0 )
+    {
+        return( MBEDTLS_ERR_X509_CERT_VERIFY_FAILED );
+    }
+
+    return( 0 );
+}
+#endif /* MBEDTLS_USE_PSA_CRYPTO */
+
 /* END_HEADER */
 
 /* BEGIN_DEPENDENCIES
@@ -95,6 +128,53 @@
 }
 /* END_CASE */
 
+/* BEGIN_CASE depends_on:MBEDTLS_PEM_WRITE_C:MBEDTLS_X509_CSR_WRITE_C:MBEDTLS_USE_PSA_CRYPTO */
+void x509_csr_check_opaque( char *key_file, int md_type, int key_usage,
+                                 int cert_type )
+{
+    mbedtls_pk_context key;
+    psa_key_slot_t slot;
+    psa_algorithm_t md_alg_psa;
+    mbedtls_x509write_csr req;
+    unsigned char buf[4096];
+    int ret;
+    size_t pem_len = 0;
+    const char *subject_name = "C=NL,O=PolarSSL,CN=PolarSSL Server 1";
+    rnd_pseudo_info rnd_info;
+
+    psa_crypto_init();
+    memset( &rnd_info, 0x2a, sizeof( rnd_pseudo_info ) );
+
+    md_alg_psa = mbedtls_psa_translate_md( (mbedtls_md_type_t) md_type );
+    TEST_ASSERT( md_alg_psa != MBEDTLS_MD_NONE );
+
+    mbedtls_pk_init( &key );
+    TEST_ASSERT( mbedtls_pk_parse_keyfile( &key, key_file, NULL ) == 0 );
+    TEST_ASSERT( mbedtls_pk_wrap_as_opaque( &key, &slot, md_alg_psa ) == 0 );
+
+    mbedtls_x509write_csr_init( &req );
+    mbedtls_x509write_csr_set_md_alg( &req, md_type );
+    mbedtls_x509write_csr_set_key( &req, &key );
+    TEST_ASSERT( mbedtls_x509write_csr_set_subject_name( &req, subject_name ) == 0 );
+    if( key_usage != 0 )
+        TEST_ASSERT( mbedtls_x509write_csr_set_key_usage( &req, key_usage ) == 0 );
+    if( cert_type != 0 )
+        TEST_ASSERT( mbedtls_x509write_csr_set_ns_cert_type( &req, cert_type ) == 0 );
+
+    ret = mbedtls_x509write_csr_pem( &req, buf, sizeof( buf ) - 1,
+                             rnd_pseudo_rand, &rnd_info );
+    TEST_ASSERT( ret == 0 );
+
+    pem_len = strlen( (char *) buf );
+    buf[pem_len] = '\0';
+    TEST_ASSERT( x509_crt_verifycsr( buf, pem_len + 1 ) == 0 );
+
+exit:
+    mbedtls_x509write_csr_free( &req );
+    mbedtls_pk_free( &key );
+}
+/* END_CASE */
+
 /* BEGIN_CASE depends_on:MBEDTLS_PEM_WRITE_C:MBEDTLS_X509_CRT_WRITE_C:MBEDTLS_SHA1_C */
 void x509_crt_check( char *subject_key_file, char *subject_pwd,
                      char *subject_name, char *issuer_key_file,