diff --git a/ChangeLog.d/10956.txt b/ChangeLog.d/10956.txt new file mode 100644 index 0000000000..461e9fb163 --- /dev/null +++ b/ChangeLog.d/10956.txt @@ -0,0 +1,7 @@ +Bugfix + * Fix TLS 1.3 handshakes using opaque pre-shared keys configured through + mbedtls_ssl_conf_psk_opaque(). The client computed an incorrect PSK + binder, the server rejected the PSK identity, and the early key + schedule exported the key with psa_export_key(), which fails for + non-exportable keys. Opaque PSKs are now consumed directly via + psa_key_derivation_input_key(). Fixes #10956. diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 1f44fc71a7..341a689c59 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2008,7 +2008,22 @@ int mbedtls_ssl_set_hs_psk(mbedtls_ssl_context *ssl, #if defined(MBEDTLS_SSL_PROTO_TLS1_2) if (ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_2) { - if (ssl->handshake->ciphersuite_info->mac == MBEDTLS_MD_SHA384) { + /* + * The ciphersuite is not always negotiated when the handshake PSK + * is set: with TLS 1.3 session resumption (e.g. with early data), + * the resumption PSK is imported while writing the ClientHello, + * before the ServerHello. Fall back to the ciphersuite pinned by + * the ticket in that case. + */ + const mbedtls_ssl_ciphersuite_t *psk_ciphersuite_info = + ssl->handshake->ciphersuite_info; + if (psk_ciphersuite_info == NULL && ssl->session_negotiate != NULL) { + psk_ciphersuite_info = + mbedtls_ssl_ciphersuite_from_id( + ssl->session_negotiate->ciphersuite); + } + if (psk_ciphersuite_info != NULL && + psk_ciphersuite_info->mac == 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); @@ -2020,6 +2035,7 @@ int mbedtls_ssl_set_hs_psk(mbedtls_ssl_context *ssl, #if defined(MBEDTLS_SSL_PROTO_TLS1_3) if (ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_3) { alg = PSA_ALG_HKDF_EXTRACT(PSA_ALG_ANY_HASH); + psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT); } diff --git a/library/ssl_tls13_client.c b/library/ssl_tls13_client.c index e8b6063f32..59c4e46474 100644 --- a/library/ssl_tls13_client.c +++ b/library/ssl_tls13_client.c @@ -1084,6 +1084,7 @@ static int ssl_tls13_parse_server_pre_shared_key_ext(mbedtls_ssl_context *ssl, const unsigned char *psk; size_t psk_len; psa_algorithm_t hash_alg; + int psk_is_opaque = 0; MBEDTLS_SSL_CHK_BUF_READ_PTR(buf, end, 2); selected_identity = MBEDTLS_GET_UINT16_BE(buf, 0); @@ -1106,6 +1107,8 @@ static int ssl_tls13_parse_server_pre_shared_key_ext(mbedtls_ssl_context *ssl, #endif if (mbedtls_ssl_conf_has_static_psk(ssl->conf)) { ret = ssl_tls13_psk_get_psk(ssl, &hash_alg, &psk, &psk_len); + psk_is_opaque = + !mbedtls_svc_key_id_is_null(ssl->conf->psk_opaque); } else { MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); return MBEDTLS_ERR_SSL_INTERNAL_ERROR; @@ -1124,10 +1127,20 @@ static int ssl_tls13_parse_server_pre_shared_key_ext(mbedtls_ssl_context *ssl, return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; } - ret = mbedtls_ssl_set_hs_psk(ssl, psk, psk_len); - if (ret != 0) { - MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_set_hs_psk", ret); - return ret; + if (psk_is_opaque) { + ret = mbedtls_ssl_set_hs_psk_opaque( + ssl, ssl->conf->psk_opaque); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET( + 1, "mbedtls_ssl_set_hs_psk_opaque", ret); + return ret; + } + } else { + ret = mbedtls_ssl_set_hs_psk(ssl, psk, psk_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_set_hs_psk", ret); + return ret; + } } return 0; diff --git a/library/ssl_tls13_keys.c b/library/ssl_tls13_keys.c index cc4ed17ea0..5674774082 100644 --- a/library/ssl_tls13_keys.c +++ b/library/ssl_tls13_keys.c @@ -329,10 +329,11 @@ int mbedtls_ssl_tls13_derive_secret( } -int mbedtls_ssl_tls13_evolve_secret( +static int ssl_tls13_evolve_secret_internal( psa_algorithm_t hash_alg, const unsigned char *secret_old, const unsigned char *input, size_t input_len, + mbedtls_svc_key_id_t input_key, unsigned char *secret_new) { int ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; @@ -342,7 +343,7 @@ int mbedtls_ssl_tls13_evolve_secret( unsigned char tmp_secret[PSA_MAC_MAX_SIZE] = { 0 }; const unsigned char all_zeroes_input[MBEDTLS_TLS1_3_MD_MAX_SIZE] = { 0 }; const unsigned char *l_input = NULL; - size_t l_input_len; + size_t l_input_len = 0; psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT; @@ -370,12 +371,14 @@ int mbedtls_ssl_tls13_evolve_secret( ret = 0; - if (input != NULL && input_len != 0) { - l_input = input; - l_input_len = input_len; - } else { - l_input = all_zeroes_input; - l_input_len = hlen; + if (mbedtls_svc_key_id_is_null(input_key)) { + if (input != NULL && input_len != 0) { + l_input = input; + l_input_len = input_len; + } else { + l_input = all_zeroes_input; + l_input_len = hlen; + } } status = psa_key_derivation_setup(&operation, @@ -394,9 +397,18 @@ int mbedtls_ssl_tls13_evolve_secret( goto cleanup; } - status = psa_key_derivation_input_bytes(&operation, - PSA_KEY_DERIVATION_INPUT_SECRET, - l_input, l_input_len); + if (!mbedtls_svc_key_id_is_null(input_key)) { + status = psa_key_derivation_input_key( + &operation, + PSA_KEY_DERIVATION_INPUT_SECRET, + input_key); + } else { + status = psa_key_derivation_input_bytes( + &operation, + PSA_KEY_DERIVATION_INPUT_SECRET, + l_input, + l_input_len); + } if (status != PSA_SUCCESS) { goto cleanup; @@ -418,6 +430,21 @@ cleanup: return ret; } +int mbedtls_ssl_tls13_evolve_secret( + psa_algorithm_t hash_alg, + const unsigned char *secret_old, + const unsigned char *input, size_t input_len, + unsigned char *secret_new) +{ + return ssl_tls13_evolve_secret_internal( + hash_alg, + secret_old, + input, + input_len, + MBEDTLS_SVC_KEY_ID_INIT, + secret_new); +} + int mbedtls_ssl_tls13_derive_early_secrets( psa_algorithm_t hash_alg, unsigned char const *early_secret, @@ -841,11 +868,7 @@ int mbedtls_ssl_tls13_create_psk_binder(mbedtls_ssl_context *ssl, unsigned char early_secret[PSA_MAC_MAX_SIZE]; size_t const hash_len = PSA_HASH_LENGTH(hash_alg); size_t actual_len; - -#if !defined(MBEDTLS_DEBUG_C) - ssl = NULL; /* make sure we don't use it except for debug */ - ((void) ssl); -#endif + mbedtls_svc_key_id_t psk_opaque = MBEDTLS_SVC_KEY_ID_INIT; /* We should never call this function with an unknown hash, * but add an assertion anyway. */ @@ -853,6 +876,24 @@ int mbedtls_ssl_tls13_create_psk_binder(mbedtls_ssl_context *ssl, return MBEDTLS_ERR_SSL_INTERNAL_ERROR; } + /* + * External PSKs may be configured as opaque PSA keys. + * + * Do not use a configured external opaque PSK for resumption binders: + * resumption PSKs are supplied explicitly through psk/psk_len. + */ +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED) + /* + * Only fetch the opaque key when no raw PSK bytes were supplied: + * raw (exportable) PSKs take the byte path below, which works + * whatever hash the negotiated ciphersuite requires. + */ + if ((psk == NULL || psk_len == 0) && ssl != NULL && + psk_type == MBEDTLS_SSL_TLS1_3_PSK_EXTERNAL) { + psk_opaque = mbedtls_ssl_get_opaque_psk(ssl); + } +#endif + /* * 0 * | @@ -864,12 +905,14 @@ int mbedtls_ssl_tls13_create_psk_binder(mbedtls_ssl_context *ssl, * v */ - ret = mbedtls_ssl_tls13_evolve_secret(hash_alg, - NULL, /* Old secret */ - psk, psk_len, /* Input */ - early_secret); + ret = ssl_tls13_evolve_secret_internal( + hash_alg, + NULL, /* Old secret */ + psk, psk_len, /* Raw input, if used */ + psk_opaque, /* Opaque PSA input, if used */ + early_secret); if (ret != 0) { - MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_evolve_secret", ret); + MBEDTLS_SSL_DEBUG_RET(1, "ssl_tls13_evolve_secret_internal", ret); goto exit; } @@ -1232,9 +1275,6 @@ int mbedtls_ssl_tls13_key_schedule_stage_early(mbedtls_ssl_context *ssl) int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; psa_algorithm_t hash_alg; mbedtls_ssl_handshake_params *handshake = ssl->handshake; - unsigned char *psk = NULL; - size_t psk_len = 0; - if (handshake->ciphersuite_info == NULL) { MBEDTLS_SSL_DEBUG_MSG(1, ("cipher suite info not found")); return MBEDTLS_ERR_SSL_INTERNAL_ERROR; @@ -1243,22 +1283,50 @@ int mbedtls_ssl_tls13_key_schedule_stage_early(mbedtls_ssl_context *ssl) hash_alg = mbedtls_md_psa_alg_from_type((mbedtls_md_type_t) handshake->ciphersuite_info->mac); #if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) if (mbedtls_ssl_tls13_key_exchange_mode_with_psk(ssl)) { - ret = mbedtls_ssl_tls13_export_handshake_psk(ssl, &psk, &psk_len); - if (ret != 0) { - MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_export_handshake_psk", - ret); - return ret; - } - } -#endif + /* + * In TLS 1.3 the handshake PSK is always stored as a PSA key. + * Exportable PSKs (in particular raw PSKs imported by + * mbedtls_ssl_set_hs_psk()) take the byte path, which works + * with whatever hash the negotiated ciphersuite requires. + * Non-exportable opaque PSKs are consumed directly by HKDF, + * without ever exporting them to raw bytes. + */ + unsigned char *psk = NULL; + size_t psk_len = 0; + mbedtls_svc_key_id_t psk_opaque = MBEDTLS_SVC_KEY_ID_INIT; + psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; - ret = mbedtls_ssl_tls13_evolve_secret(hash_alg, NULL, psk, psk_len, - handshake->tls13_master_secrets.early); -#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) - mbedtls_free((void *) psk); + status = psa_get_key_attributes(handshake->psk_opaque, + &key_attributes); + if (status == PSA_SUCCESS && + (psa_get_key_usage_flags(&key_attributes) & + PSA_KEY_USAGE_EXPORT)) { + ret = mbedtls_ssl_tls13_export_handshake_psk(ssl, &psk, &psk_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_export_handshake_psk", + ret); + return ret; + } + } else { + psk_opaque = handshake->psk_opaque; + } + + ret = ssl_tls13_evolve_secret_internal( + hash_alg, + NULL, /* Old secret */ + psk, psk_len, /* Raw input, if used */ + psk_opaque, /* Opaque PSA input, if used */ + handshake->tls13_master_secrets.early); + mbedtls_free((void *) psk); + } else #endif + { + ret = mbedtls_ssl_tls13_evolve_secret(hash_alg, NULL, NULL, 0, + handshake->tls13_master_secrets.early); + } if (ret != 0) { - MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_evolve_secret", ret); + MBEDTLS_SSL_DEBUG_RET(1, "TLS 1.3: evolve early secret", ret); return ret; } diff --git a/library/ssl_tls13_server.c b/library/ssl_tls13_server.c index 38f2fe9370..baa311f9c9 100644 --- a/library/ssl_tls13_server.c +++ b/library/ssl_tls13_server.c @@ -381,10 +381,20 @@ static int ssl_tls13_offered_psks_check_identity_match( identity_len == ssl->conf->psk_identity_len && mbedtls_ct_memcmp(ssl->conf->psk_identity, identity, identity_len) == 0) { - ret = mbedtls_ssl_set_hs_psk(ssl, ssl->conf->psk, ssl->conf->psk_len); - if (ret != 0) { - MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_set_hs_psk", ret); - return ret; + if (!mbedtls_svc_key_id_is_null(ssl->conf->psk_opaque)) { + ret = mbedtls_ssl_set_hs_psk_opaque(ssl, ssl->conf->psk_opaque); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_set_hs_psk_opaque", + ret); + return ret; + } + } else { + ret = mbedtls_ssl_set_hs_psk(ssl, ssl->conf->psk, + ssl->conf->psk_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_set_hs_psk", ret); + return ret; + } } return SSL_TLS1_3_PSK_IDENTITY_MATCH; } @@ -426,9 +436,36 @@ static int ssl_tls13_offered_psks_check_binder_match( return ret; } - ret = mbedtls_ssl_tls13_export_handshake_psk(ssl, &psk, &psk_len); - if (ret != 0) { - return ret; + if (psk_type == MBEDTLS_SSL_TLS1_3_PSK_EXTERNAL) { + /* + * External PSKs may be opaque and non-exportable, in which case + * mbedtls_ssl_tls13_create_psk_binder() consumes the key directly + * via psa_key_derivation_input_key(). Exportable PSKs (in + * particular raw PSKs imported by mbedtls_ssl_set_hs_psk()) take + * the byte path instead, which works with whatever hash the + * negotiated ciphersuite requires. + */ + psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + status = psa_get_key_attributes(ssl->handshake->psk_opaque, + &key_attributes); + if (status == PSA_SUCCESS && + (psa_get_key_usage_flags(&key_attributes) & + PSA_KEY_USAGE_EXPORT)) { + ret = mbedtls_ssl_tls13_export_handshake_psk(ssl, &psk, &psk_len); + if (ret != 0) { + return ret; + } + } else { + psk = NULL; + psk_len = 0; + } + } else { + ret = mbedtls_ssl_tls13_export_handshake_psk(ssl, &psk, &psk_len); + if (ret != 0) { + return ret; + } } ret = mbedtls_ssl_tls13_create_psk_binder(ssl, psk_hash_alg, diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index ad87e225da..94a207d8bd 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -1521,13 +1521,36 @@ usage: #if defined(MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED) if (opt.psk_opaque != 0) { - /* Determine KDF algorithm the opaque PSK will be used in. */ + /* + * Determine the KDF algorithm the opaque PSK will be used in. + * + * TLS 1.3 consumes the PSK directly as the secret input to + * HKDF-Extract. Keep the key non-exportable: DERIVE permission + * is sufficient. + */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (ciphersuite_info->min_tls_version == + MBEDTLS_SSL_VERSION_TLS1_3) { #if defined(PSA_WANT_ALG_SHA_384) - if (ciphersuite_info->mac == MBEDTLS_MD_SHA384) { - alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_384); - } else + if (ciphersuite_info->mac == MBEDTLS_MD_SHA384) { + alg = PSA_ALG_HKDF_EXTRACT(PSA_ALG_SHA_384); + } else #endif /* PSA_WANT_ALG_SHA_384 */ - alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256); + { + alg = PSA_ALG_HKDF_EXTRACT(PSA_ALG_SHA_256); + } + } else +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + { +#if defined(PSA_WANT_ALG_SHA_384) + if (ciphersuite_info->mac == MBEDTLS_MD_SHA384) { + alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_384); + } else +#endif /* PSA_WANT_ALG_SHA_384 */ + { + alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256); + } + } } #endif /* MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED */ } diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index b5cdc817b8..43ec140ff2 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -2377,13 +2377,36 @@ usage: #if defined(MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED) if (opt.psk_opaque != 0 || opt.psk_list_opaque != 0) { - /* Determine KDF algorithm the opaque PSK will be used in. */ + /* + * Determine the KDF algorithm the opaque PSK will be used in. + * + * TLS 1.3 consumes the PSK directly as the secret input to + * HKDF-Extract. Keep the key non-exportable: DERIVE + * permission is sufficient. + */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (ciphersuite_info->min_tls_version == + MBEDTLS_SSL_VERSION_TLS1_3) { #if defined(PSA_WANT_ALG_SHA_384) - if (ciphersuite_info->mac == MBEDTLS_MD_SHA384) { - alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_384); - } else + if (ciphersuite_info->mac == MBEDTLS_MD_SHA384) { + alg = PSA_ALG_HKDF_EXTRACT(PSA_ALG_SHA_384); + } else #endif /* PSA_WANT_ALG_SHA_384 */ - alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256); + { + alg = PSA_ALG_HKDF_EXTRACT(PSA_ALG_SHA_256); + } + } else +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + { +#if defined(PSA_WANT_ALG_SHA_384) + if (ciphersuite_info->mac == MBEDTLS_MD_SHA384) { + alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_384); + } else +#endif /* PSA_WANT_ALG_SHA_384 */ + { + alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256); + } + } } #endif /* MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED */ } diff --git a/tests/opt-testcases/tls13-opaque-psk.sh b/tests/opt-testcases/tls13-opaque-psk.sh new file mode 100644 index 0000000000..32185ad744 --- /dev/null +++ b/tests/opt-testcases/tls13-opaque-psk.sh @@ -0,0 +1,59 @@ +# Regression tests for TLS 1.3 with opaque pre-shared keys (#10956). +# +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +# + +# 10956: TLS 1.3 opaque PSK regression tests. +# The sample programs import the opaque PSK with PSA_KEY_USAGE_DERIVE only +# (no PSA_KEY_USAGE_EXPORT), so these tests fail if the TLS 1.3 key +# schedule or PSK binder calculation tries to export the key instead of +# using psa_key_derivation_input_key(). +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3 +requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED +run_test "TLS 1.3: PSK: opaque PSK both sides" \ + "$P_SRV force_version=tls13 tls13_kex_modes=psk debug_level=4 \ + force_ciphersuite=TLS1-3-AES-128-GCM-SHA256 \ + psk=00112233445566778899aabbccddeeff \ + psk_identity=Client_identity psk_opaque=1" \ + "$P_CLI force_version=tls13 tls13_kex_modes=psk debug_level=4 \ + force_ciphersuite=TLS1-3-AES-128-GCM-SHA256 \ + psk=00112233445566778899aabbccddeeff \ + psk_identity=Client_identity psk_opaque=1" \ + 0 \ + -c "Protocol is TLSv1.3" \ + -s "Pre shared key found" + +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3 +requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3 +requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED +run_test "TLS 1.3: PSK: opaque client, raw server" \ + "$P_SRV force_version=tls13 tls13_kex_modes=psk debug_level=4 \ + force_ciphersuite=TLS1-3-AES-128-GCM-SHA256 \ + psk=00112233445566778899aabbccddeeff \ + psk_identity=Client_identity" \ + "$P_CLI force_version=tls13 tls13_kex_modes=psk debug_level=4 \ + force_ciphersuite=TLS1-3-AES-128-GCM-SHA256 \ + psk=00112233445566778899aabbccddeeff \ + psk_identity=Client_identity psk_opaque=1" \ + 0 \ + -c "Protocol is TLSv1.3" \ + -s "Pre shared key found" + +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3 +requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3 +requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED +run_test "TLS 1.3: PSK: raw client, opaque server" \ + "$P_SRV force_version=tls13 tls13_kex_modes=psk debug_level=4 \ + force_ciphersuite=TLS1-3-AES-128-GCM-SHA256 \ + psk=00112233445566778899aabbccddeeff \ + psk_identity=Client_identity psk_opaque=1" \ + "$P_CLI force_version=tls13 tls13_kex_modes=psk debug_level=4 \ + force_ciphersuite=TLS1-3-AES-128-GCM-SHA256 \ + psk=00112233445566778899aabbccddeeff \ + psk_identity=Client_identity" \ + 0 \ + -c "Protocol is TLSv1.3" \ + -s "Pre shared key found"