Merge pull request #5624 from superna9999/5312-tls-server-ecdh

TLS ECDH 3b: server-side static ECDH (1.2)
diff --git a/include/mbedtls/pk.h b/include/mbedtls/pk.h
index 324612a..a0d4694 100644
--- a/include/mbedtls/pk.h
+++ b/include/mbedtls/pk.h
@@ -923,7 +923,8 @@
  *                  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.
+ *                  specified hash & ECDH key agreement derivation operation
+ *                  are the only allowed use of that key.
  *
  * \param pk        Input: the EC key to import to a PSA key.
  *                  Output: a PK context wrapping that PSA key.
diff --git a/library/pk.c b/library/pk.c
index 7f4d5fe..3b42799 100644
--- a/library/pk.c
+++ b/library/pk.c
@@ -735,8 +735,10 @@
     /* prepare the key attributes */
     psa_set_key_type( &attributes, key_type );
     psa_set_key_bits( &attributes, bits );
-    psa_set_key_usage_flags( &attributes, PSA_KEY_USAGE_SIGN_HASH );
+    psa_set_key_usage_flags( &attributes, PSA_KEY_USAGE_SIGN_HASH |
+                                          PSA_KEY_USAGE_DERIVE);
     psa_set_key_algorithm( &attributes, PSA_ALG_ECDSA(hash_alg) );
+    psa_set_key_enrollment_algorithm( &attributes, PSA_ALG_ECDH );
 
     /* import private key into PSA */
     if( PSA_SUCCESS != psa_import_key( &attributes, d, d_len, key ) )
diff --git a/library/ssl_misc.h b/library/ssl_misc.h
index 193f310..254627f 100644
--- a/library/ssl_misc.h
+++ b/library/ssl_misc.h
@@ -644,8 +644,9 @@
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3)
     psa_key_type_t ecdh_psa_type;
-    uint16_t ecdh_bits;
+    size_t ecdh_bits;
     mbedtls_svc_key_id_t ecdh_psa_privkey;
+    uint8_t ecdh_psa_privkey_is_external;
     unsigned char ecdh_psa_peerkey[MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH];
     size_t ecdh_psa_peerkey_len;
 #endif /* MBEDTLS_USE_PSA_CRYPTO || MBEDTLS_SSL_PROTO_TLS1_3 */
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 94cb776..32b9799 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -3204,7 +3204,8 @@
 
 #if defined(MBEDTLS_ECDH_C) && \
     ( defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3) )
-    psa_destroy_key( handshake->ecdh_psa_privkey );
+    if( handshake->ecdh_psa_privkey_is_external == 0 )
+        psa_destroy_key( handshake->ecdh_psa_privkey );
 #endif /* MBEDTLS_ECDH_C && MBEDTLS_USE_PSA_CRYPTO */
 
 #if defined(MBEDTLS_SSL_PROTO_TLS1_3)
diff --git a/library/ssl_tls12_client.c b/library/ssl_tls12_client.c
index 734d3a2..7771d38 100644
--- a/library/ssl_tls12_client.c
+++ b/library/ssl_tls12_client.c
@@ -1861,9 +1861,7 @@
     {
         return( MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE );
     }
-    if( ecdh_bits > 0xffff )
-        return( MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER );
-    handshake->ecdh_bits = (uint16_t) ecdh_bits;
+    handshake->ecdh_bits = ecdh_bits;
 
     /* Keep a copy of the peer's public key */
     ecpoint_len = *(*p)++;
diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c
index 486632e..9ecfdd2 100644
--- a/library/ssl_tls12_server.c
+++ b/library/ssl_tls12_server.c
@@ -2848,7 +2848,102 @@
 }
 #endif /* MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED */
 
-#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \
+#if defined(MBEDTLS_USE_PSA_CRYPTO) &&                      \
+        ( defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \
+          defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) )
+static int ssl_get_ecdh_params_from_cert( mbedtls_ssl_context *ssl )
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    unsigned char buf[
+        PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS)];
+    psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
+    size_t ecdh_bits = 0;
+    size_t key_len;
+    mbedtls_pk_context *pk;
+    mbedtls_ecp_keypair *key;
+
+    pk = mbedtls_ssl_own_key( ssl );
+
+    if( pk == NULL )
+        return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
+
+    switch( mbedtls_pk_get_type( pk ) )
+    {
+    case MBEDTLS_PK_OPAQUE:
+        if( ! mbedtls_pk_can_do( pk, MBEDTLS_PK_ECKEY ) )
+            return( MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH );
+
+        ssl->handshake->ecdh_psa_privkey =
+                *( (mbedtls_svc_key_id_t*) pk->pk_ctx );
+
+        /* Key should not be destroyed in the TLS library */
+        ssl->handshake->ecdh_psa_privkey_is_external = 1;
+
+        status = psa_get_key_attributes( ssl->handshake->ecdh_psa_privkey,
+                                         &key_attributes );
+        if( status != PSA_SUCCESS)
+        {
+            ssl->handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT;
+            return( psa_ssl_status_to_mbedtls( status ) );
+        }
+
+        ssl->handshake->ecdh_psa_type = psa_get_key_type( &key_attributes );
+        ssl->handshake->ecdh_bits = psa_get_key_bits( &key_attributes );
+
+        psa_reset_key_attributes( &key_attributes );
+
+        ret = 0;
+        break;
+    case MBEDTLS_PK_ECKEY:
+    case MBEDTLS_PK_ECKEY_DH:
+    case MBEDTLS_PK_ECDSA:
+        key = mbedtls_pk_ec( *pk );
+        if( key == NULL )
+            return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
+
+        /* Convert EC group to PSA key type. */
+        if( ( ssl->handshake->ecdh_psa_type =
+                    mbedtls_ecc_group_to_psa( key->grp.id,
+                                              &ecdh_bits ) ) == 0 )
+        {
+            return( MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE );
+        }
+
+        ssl->handshake->ecdh_bits = ecdh_bits;
+
+        key_attributes = psa_key_attributes_init();
+        psa_set_key_usage_flags( &key_attributes, PSA_KEY_USAGE_DERIVE );
+        psa_set_key_algorithm( &key_attributes, PSA_ALG_ECDH );
+        psa_set_key_type( &key_attributes,
+                PSA_KEY_TYPE_ECC_KEY_PAIR( ssl->handshake->ecdh_psa_type ) );
+        psa_set_key_bits( &key_attributes, ssl->handshake->ecdh_bits );
+
+        key_len = PSA_BITS_TO_BYTES( key->grp.pbits );
+        ret = mbedtls_ecp_write_key( key, buf, key_len );
+        if( ret != 0 )
+            goto cleanup;
+
+        status = psa_import_key( &key_attributes, buf, key_len,
+                                 &ssl->handshake->ecdh_psa_privkey );
+        if( status != PSA_SUCCESS )
+        {
+            ret = psa_ssl_status_to_mbedtls( status );
+            goto cleanup;
+        }
+
+        ret = 0;
+        break;
+    default:
+            ret = MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH;
+    }
+
+cleanup:
+    mbedtls_platform_zeroize( buf, sizeof( buf ) );
+
+    return( ret );
+}
+#elif defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \
     defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED)
 static int ssl_get_ecdh_params_from_cert( mbedtls_ssl_context *ssl )
 {
@@ -3085,12 +3180,12 @@
             handshake->ecdh_psa_type = mbedtls_psa_parse_tls_ecc_group(
                         (*curve)->tls_id, &ecdh_bits );
 
-            if( handshake->ecdh_psa_type == 0 || ecdh_bits > 0xffff )
+            if( handshake->ecdh_psa_type == 0 )
             {
                 MBEDTLS_SSL_DEBUG_MSG( 1, ( "Invalid ecc group parse." ) );
                 return( MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER );
             }
-            handshake->ecdh_bits = (uint16_t) ecdh_bits;
+            handshake->ecdh_bits = ecdh_bits;
 
             key_attributes = psa_key_attributes_init();
             psa_set_key_usage_flags( &key_attributes, PSA_KEY_USAGE_DERIVE );
@@ -3832,9 +3927,13 @@
 #endif /* MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED */
 #if defined(MBEDTLS_USE_PSA_CRYPTO) &&                           \
         ( defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) ||     \
-          defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) )
+          defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) ||   \
+          defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) ||      \
+          defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) )
     if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_RSA ||
-        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA )
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA ||
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDH_RSA ||
+        ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA )
     {
         size_t data_len = (size_t)( *p++ );
         size_t buf_len = (size_t)( end - p );
@@ -3872,25 +3971,31 @@
         {
             ret = psa_ssl_status_to_mbedtls( status );
             MBEDTLS_SSL_DEBUG_RET( 1, "psa_raw_key_agreement", ret );
-            (void) psa_destroy_key( handshake->ecdh_psa_privkey );
+            if( handshake->ecdh_psa_privkey_is_external == 0 )
+                (void) psa_destroy_key( handshake->ecdh_psa_privkey );
             handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT;
             return( ret );
         }
 
-        status = psa_destroy_key( handshake->ecdh_psa_privkey );
-
-        if( status != PSA_SUCCESS )
+        if( handshake->ecdh_psa_privkey_is_external == 0 )
         {
-            ret = psa_ssl_status_to_mbedtls( status );
-            MBEDTLS_SSL_DEBUG_RET( 1, "psa_destroy_key", ret );
-            return( ret );
+            status = psa_destroy_key( handshake->ecdh_psa_privkey );
+
+            if( status != PSA_SUCCESS )
+            {
+                ret = psa_ssl_status_to_mbedtls( status );
+                MBEDTLS_SSL_DEBUG_RET( 1, "psa_destroy_key", ret );
+                return( ret );
+            }
         }
         handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT;
     }
     else
 #endif /* MBEDTLS_USE_PSA_CRYPTO &&
             ( MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED ||
-              MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED ) */
+              MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED ||
+              MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED ||
+              MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED ) */
 #if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) ||                     \
     defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) ||                   \
     defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) ||                      \
diff --git a/library/ssl_tls13_client.c b/library/ssl_tls13_client.c
index a9b9b03..8e1112d 100644
--- a/library/ssl_tls13_client.c
+++ b/library/ssl_tls13_client.c
@@ -229,9 +229,7 @@
         mbedtls_psa_parse_tls_ecc_group( named_group, &ecdh_bits ) ) == 0 )
             return( MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE );
 
-    if( ecdh_bits > 0xffff )
-        return( MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER );
-    ssl->handshake->ecdh_bits = (uint16_t) ecdh_bits;
+    ssl->handshake->ecdh_bits = ecdh_bits;
 
     key_attributes = psa_key_attributes_init();
     psa_set_key_usage_flags( &key_attributes, PSA_KEY_USAGE_DERIVE );
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 71a3904..70dac3f 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -1583,6 +1583,24 @@
             -S "error" \
             -C "error"
 
+requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
+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 server authentication (ECDH-)" \
+            "$P_SRV force_version=tls12 auth_mode=required key_opaque=1\
+             crt_file=data_files/server5.ku-ka.crt\
+             key_file=data_files/server5.key" \
+            "$P_CLI" \
+            0 \
+            -c "Verifying peer X.509 certificate... ok" \
+            -c "Ciphersuite is TLS-ECDH-" \
+            -s "key types: Opaque, none" \
+            -s "Ciphersuite is TLS-ECDH-" \
+            -S "error" \
+            -C "error"
+
 # Test using an opaque private key for client/server authentication
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
 requires_config_enabled MBEDTLS_USE_PSA_CRYPTO