Merge branch 'opaque_psk_implementation' 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/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/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index ff625fb..a98a3a2 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -85,6 +85,7 @@
 #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"
@@ -136,9 +137,16 @@
 #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_RAW                                               \
@@ -300,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                                           \
@@ -354,6 +363,7 @@
     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
@@ -583,6 +593,9 @@
     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;
@@ -651,6 +664,7 @@
     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;
@@ -753,6 +767,10 @@
             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)
@@ -1384,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 */
 
     /*
@@ -2230,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 );
diff --git a/scripts/config.pl b/scripts/config.pl
index 833b6d3..dda30bb 100755
--- a/scripts/config.pl
+++ b/scripts/config.pl
@@ -100,7 +100,6 @@
 MBEDTLS_PKCS11_C
 MBEDTLS_NO_UDBL_DIVISION
 MBEDTLS_NO_64BIT_MULTIPLICATION
-MBEDTLS_PSA_CRYPTO_SPM
 MBEDTLS_USE_PSA_CRYPTO
 _ALT\s*$
 );
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 129b91c..d3a4338 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -865,6 +865,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 current time in ServerHello
 requires_config_enabled MBEDTLS_HAVE_TIME
 run_test    "ServerHello contains gmt_unix_time" \
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,