Merge c4a5ad3f126b6bc27b92d296c32565623b80015f into cac9589a97fb9d07a115b42fa10523bc226b5180

This commit is contained in:
v1rtu3x 2026-10-22 09:25:20 +00:00 committed by GitHub
commit 764ccd67bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 305 additions and 59 deletions

7
ChangeLog.d/10956.txt Normal file
View File

@ -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.

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}
@ -1230,9 +1273,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;
@ -1241,22 +1281,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;
}

View File

@ -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,

View File

@ -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 */
}

View File

@ -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 */
}

View File

@ -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"