Skip to content

Commit f8f1e93

Browse files
authored
Merge pull request #10534 from SparkiDev/tls13_psk_id_fix
TLSv1.3 PSK binders: always use id protection
2 parents 2d186b3 + 089f1f7 commit f8f1e93

5 files changed

Lines changed: 199 additions & 23 deletions

File tree

src/tls13.c

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
* WOLFSSL_PSK_ONE_ID: Single PSK identity per connect default: off
5858
* WOLFSSL_PSK_MULTI_ID_PER_CS: Multiple PSK IDs per cipher suite default: off
5959
* WOLFSSL_PRIORITIZE_PSK: Prioritize PSK over ciphersuite order default: off
60-
* WOLFSSL_PSK_ID_PROTECTION: Enable PSK identity protection default: off
6160
*
6261
* TLS 1.3 Session Tickets:
6362
* WOLFSSL_TICKET_HAVE_ID: Session tickets include ID default: off
@@ -6544,20 +6543,8 @@ static int DoPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 inputSz,
65446543
}
65456544

65466545
if (current == NULL) {
6547-
#ifdef WOLFSSL_PSK_ID_PROTECTION
6548-
#ifndef NO_CERTS
6549-
if (ssl->buffers.certChainCnt != 0) {
6550-
ret = 0;
6551-
goto cleanup;
6552-
}
6553-
#endif
6554-
WOLFSSL_ERROR_VERBOSE(BAD_BINDER);
6555-
ret = BAD_BINDER;
6556-
goto cleanup;
6557-
#else
65586546
ret = 0;
65596547
goto cleanup;
6560-
#endif
65616548
}
65626549

65636550
*first = (current == ext->data);
@@ -6664,6 +6651,20 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz,
66646651
}
66656652
#endif
66666653

6654+
if (!*usingPSK) {
6655+
#ifndef NO_CERTS
6656+
if (ssl->buffers.certificate == NULL
6657+
#ifdef WOLFSSL_CERT_SETUP_CB
6658+
&& ssl->ctx->certSetupCb == NULL
6659+
#endif
6660+
)
6661+
#endif
6662+
{
6663+
WOLFSSL_ERROR_VERBOSE(BAD_BINDER);
6664+
return BAD_BINDER;
6665+
}
6666+
}
6667+
66676668
if (*usingPSK) {
66686669
/* While verifying the selected PSK, we updated the
66696670
* handshake hash up to the binder bytes in the PSK extensions.
@@ -6834,14 +6835,16 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz,
68346835
TLSX_Remove(&ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK, ssl->heap);
68356836
ssl->options.certWithExternPsk = 0;
68366837
#endif
6837-
#ifdef WOLFSSL_PSK_ID_PROTECTION
68386838
#ifndef NO_CERTS
6839-
if (ssl->buffers.certChainCnt != 0)
6839+
if (ssl->buffers.certificate != NULL
6840+
#ifdef WOLFSSL_CERT_SETUP_CB
6841+
|| ssl->ctx->certSetupCb != NULL
6842+
#endif
6843+
)
68406844
return 0;
68416845
#endif
68426846
WOLFSSL_ERROR_VERBOSE(BAD_BINDER);
68436847
return BAD_BINDER;
6844-
#endif
68456848
}
68466849

68476850
WOLFSSL_LEAVE("CheckPreSharedKeys", ret);

tests/api/test_tls13.c

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1775,6 +1775,162 @@ int test_tls13_bad_psk_binder(void)
17751775
}
17761776

17771777

1778+
#if defined(WOLFSSL_TLS13) && \
1779+
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD) && \
1780+
!defined(NO_PSK)
1781+
static unsigned int test_tls13_psk_no_cert_client_cb(WOLFSSL* ssl,
1782+
const char* hint, char* identity, unsigned int id_max_len,
1783+
unsigned char* key, unsigned int key_max_len)
1784+
{
1785+
(void)ssl;
1786+
(void)hint;
1787+
(void)key_max_len;
1788+
1789+
/* Offer a PSK so the client sends a pre_shared_key extension. */
1790+
XSTRNCPY(identity, "Client_identity", id_max_len);
1791+
key[0] = 0x20;
1792+
return 1;
1793+
}
1794+
1795+
static unsigned int test_tls13_psk_no_cert_server_cb(WOLFSSL* ssl,
1796+
const char* id, unsigned char* key, unsigned int key_max_len)
1797+
{
1798+
(void)ssl;
1799+
(void)id;
1800+
(void)key;
1801+
(void)key_max_len;
1802+
1803+
/* Reject every identity so the server finds no matching PSK. */
1804+
return 0;
1805+
}
1806+
#endif
1807+
1808+
/* When no offered PSK matches and the server has no certificate to fall back
1809+
* to, the server must abort the handshake with BAD_BINDER rather than silently
1810+
* continuing. This covers both configurations:
1811+
* - NO_CERTS defined: the certificate fall-back branch is compiled out.
1812+
* - certificates compiled in but none loaded: ssl->buffers.certificate is
1813+
* NULL, so the runtime check takes the same abort path.
1814+
* The contexts are built by hand (no certificate loaded) so the test exercises
1815+
* whichever branch the build provides.
1816+
* When certificates are compiled in, a second connection sets a certificate
1817+
* and key against the server context and verifies the opposite branch: the
1818+
* non-matching PSK is ignored and the handshake falls back to a full
1819+
* certificate handshake instead of aborting. */
1820+
int test_tls13_psk_no_cert_bad_binder(void)
1821+
{
1822+
EXPECT_DECLS;
1823+
#if defined(WOLFSSL_TLS13) && \
1824+
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD) && \
1825+
!defined(NO_PSK)
1826+
WOLFSSL_CTX *ctx_c = NULL;
1827+
WOLFSSL_CTX *ctx_s = NULL;
1828+
WOLFSSL *ssl_c = NULL;
1829+
WOLFSSL *ssl_s = NULL;
1830+
struct test_memio_ctx test_ctx;
1831+
WOLFSSL_ALERT_HISTORY h;
1832+
1833+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
1834+
1835+
/* Don't use test_memio_setup(): it loads a default server certificate,
1836+
* which would let the server fall back to a certificate handshake. Build
1837+
* the contexts by hand so the server has no certificate loaded. */
1838+
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
1839+
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
1840+
if (ctx_c != NULL) {
1841+
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
1842+
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
1843+
}
1844+
if (ctx_s != NULL) {
1845+
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
1846+
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
1847+
}
1848+
1849+
/* Set the PSK callbacks on the contexts, not the SSL objects: with
1850+
* certificates compiled in, creating a server-side SSL object without a
1851+
* certificate and key fails (NO_PRIVATE_KEY) unless ctx->havePSK is
1852+
* already set when wolfSSL_new() is called. */
1853+
wolfSSL_CTX_set_psk_client_callback(ctx_c,
1854+
test_tls13_psk_no_cert_client_cb);
1855+
wolfSSL_CTX_set_psk_server_callback(ctx_s,
1856+
test_tls13_psk_no_cert_server_cb);
1857+
1858+
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
1859+
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
1860+
if (ssl_c != NULL) {
1861+
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
1862+
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
1863+
}
1864+
if (ssl_s != NULL) {
1865+
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
1866+
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
1867+
}
1868+
1869+
/* Confirm the precondition: the server really has no certificate. */
1870+
#ifndef NO_CERTS
1871+
if (ssl_s != NULL) {
1872+
ExpectNull(ssl_s->buffers.certificate);
1873+
}
1874+
#endif
1875+
1876+
/* Client sends ClientHello (with PSK) and waits for the response. */
1877+
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
1878+
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
1879+
WOLFSSL_ERROR_WANT_READ);
1880+
1881+
/* Server processes ClientHello: no PSK matches and no certificate is
1882+
* available, so it must abort with BAD_BINDER. */
1883+
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
1884+
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
1885+
WC_NO_ERR_TRACE(BAD_BINDER));
1886+
1887+
/* Client reads the server's alert: BAD_BINDER maps to a fatal
1888+
* illegal_parameter alert (see TranslateErrorToAlert). */
1889+
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
1890+
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
1891+
WC_NO_ERR_TRACE(FATAL_ERROR));
1892+
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
1893+
ExpectIntEQ(h.last_rx.code, illegal_parameter);
1894+
ExpectIntEQ(h.last_rx.level, alert_fatal);
1895+
1896+
wolfSSL_free(ssl_c);
1897+
ssl_c = NULL;
1898+
wolfSSL_CTX_free(ctx_c);
1899+
ctx_c = NULL;
1900+
wolfSSL_free(ssl_s);
1901+
ssl_s = NULL;
1902+
wolfSSL_CTX_free(ctx_s);
1903+
ctx_s = NULL;
1904+
1905+
#ifndef NO_CERTS
1906+
/* Conversely, with a certificate and key set against the server context,
1907+
* a non-matching PSK must not leak the mismatch: the server ignores the
1908+
* PSK and falls back to a full certificate handshake. test_memio_setup()
1909+
* loads the default CA, server certificate and key. */
1910+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
1911+
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
1912+
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
1913+
1914+
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_psk_no_cert_client_cb);
1915+
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_psk_no_cert_server_cb);
1916+
1917+
/* Confirm the precondition: the server has a certificate this time. */
1918+
if (ssl_s != NULL) {
1919+
ExpectNotNull(ssl_s->buffers.certificate);
1920+
}
1921+
1922+
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
1923+
1924+
wolfSSL_free(ssl_c);
1925+
wolfSSL_CTX_free(ctx_c);
1926+
wolfSSL_free(ssl_s);
1927+
wolfSSL_CTX_free(ctx_s);
1928+
#endif /* !NO_CERTS */
1929+
#endif
1930+
return EXPECT_RESULT();
1931+
}
1932+
1933+
17781934
#if defined(HAVE_RPK) && !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \
17791935
!defined(NO_WOLFSSL_SERVER)
17801936

tests/api/test_tls13.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ int test_tls13_post_handshake_auth_no_ext(void);
6363
int test_tls13_post_handshake_auth_late_allow(void);
6464
int test_tls13_downgrade_sentinel(void);
6565
int test_tls13_serverhello_bad_cipher_suites(void);
66+
int test_tls13_psk_no_cert_bad_binder(void);
6667
int test_tls13_cert_with_extern_psk_apis(void);
6768
int test_tls13_cert_with_extern_psk_handshake(void);
6869
int test_tls13_cert_with_extern_psk_requires_key_share(void);
@@ -122,6 +123,7 @@ int test_tls13_AEAD_limit_KU_aes128_ccm_8_sha256(void);
122123
TEST_DECL_GROUP("tls13", test_tls13_post_handshake_auth_late_allow), \
123124
TEST_DECL_GROUP("tls13", test_tls13_downgrade_sentinel), \
124125
TEST_DECL_GROUP("tls13", test_tls13_serverhello_bad_cipher_suites), \
126+
TEST_DECL_GROUP("tls13", test_tls13_psk_no_cert_bad_binder), \
125127
TEST_DECL_GROUP("tls13", test_tls13_cert_with_extern_psk_apis), \
126128
TEST_DECL_GROUP("tls13", test_tls13_cert_with_extern_psk_handshake), \
127129
TEST_DECL_GROUP("tls13", test_tls13_cert_with_extern_psk_requires_key_share), \

tests/utils.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
#include <tests/utils.h>
2424
#include <wolfssl/wolfcrypt/error-crypt.h>
2525

26-
#ifdef HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES
26+
#ifdef HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD
2727

2828
/* This set of memio functions allows for more fine tuned control of the TLS
2929
* connection operations. For new tests, try to use ssl_memio first. */
@@ -784,7 +784,7 @@ int test_memio_setup(struct test_memio_ctx *ctx,
784784
method_s, NULL, 0, NULL, 0, NULL, 0);
785785
}
786786

787-
#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES */
787+
#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD */
788788

789789
#if !defined(NO_FILESYSTEM) && defined(OPENSSL_EXTRA) && \
790790
defined(DEBUG_UNIT_TEST_CERTS)

tests/utils.h

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,26 @@ extern char tmpDirName[16];
3232
extern const char* currentTestName;
3333
#endif
3434

35-
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \
36-
(!defined(NO_RSA) || defined(HAVE_RPK)) && \
37-
!defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) && \
38-
(!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13))
35+
/* Base dependencies for the manual memio test harness. The harness itself does
36+
* not require certificate support, so cert-less tests (e.g. PSK-only) can use
37+
* it through this narrower macro. */
38+
#if !defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) && \
39+
(!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13)) && defined(NO_CERTS)
40+
#define HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_NO_CERTS
41+
#define HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD
42+
#endif
43+
44+
/* Full dependencies: the base harness plus certificate support. Most memio
45+
* tests set up a certificate-based handshake and must use this macro. */
46+
#if !defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) && \
47+
(!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13)) && \
48+
!defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \
49+
(!defined(NO_RSA) || defined(HAVE_RPK))
3950
#define HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES
51+
#define HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD
52+
#endif
53+
54+
#ifdef HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD
4055
#define TEST_MEMIO_BUF_SZ (64 * 1024)
4156
#define TEST_MEMIO_MAX_MSGS 32
4257

@@ -85,7 +100,7 @@ int test_memio_move_message(struct test_memio_ctx *ctx, int client,
85100
int test_memio_drop_message(struct test_memio_ctx *ctx, int client, int msg_pos);
86101
int test_memio_modify_message_len(struct test_memio_ctx *ctx, int client, int msg_pos, int new_len);
87102
int test_memio_remove_from_buffer(struct test_memio_ctx *ctx, int client, int off, int sz);
88-
#endif
103+
#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD */
89104

90105
/* Shared TLS server/client thread bodies, defined in tests/api.c. The
91106
* definitions are gated on ENABLE_TLS_CALLBACK_TEST (a composite condition

0 commit comments

Comments
 (0)