API to get ECH connection status

This commit is contained in:
sebastian-carpenter
2026-05-11 11:09:42 -06:00
parent e349c68784
commit 335714d658
4 changed files with 153 additions and 33 deletions
+56
View File
@@ -498,6 +498,62 @@ void wolfSSL_SetEchEnableTrialDecrypt(WOLFSSL* ssl, byte enable)
ssl->options.enableEchTrialDecrypt = (enable != 0);
}
/* Return the status of the ECH connection. Possible return values:
* WOLFSSL_ECH_STATUS_NOT_OFFERED:
* server - client did not send ECH or it is not setup
* client - ECH is not setup
* pending - connection has not been initiated
*
* WOLFSSL_ECH_STATUS_GREASE:
* client - GREASE ECH extension sent
*
* WOLFSSL_ECH_STATUS_REJECTED:
* server - ECH was not accepted (decryption of inner ClientHello failed)
* client - ECH was offered but the server rejected it
*
* In both cases the connection fell back to the outer transcript.
*
* WOLFSSL_ECH_STATUS_ACCEPTED:
* Decryption of the inner ClientHello was successful and the inner
* transcript was used.
*
* Returns BAD_FUNC_ARG if ssl is NULL. */
int wolfSSL_GetEchStatus(const WOLFSSL* ssl)
{
if (ssl == NULL)
return BAD_FUNC_ARG;
if (ssl->options.disableECH)
return WOLFSSL_ECH_STATUS_NOT_OFFERED;
if (ssl->options.echAccepted)
return WOLFSSL_ECH_STATUS_ACCEPTED;
if (ssl->options.side == WOLFSSL_SERVER_END) {
TLSX* echX = TLSX_Find(ssl->extensions, TLSX_ECH);
WOLFSSL_ECH* ech;
if (echX == NULL || echX->data == NULL)
return WOLFSSL_ECH_STATUS_NOT_OFFERED;
/* state stays at ECH_WRITE_NONE and innerClientHello stays NULL when
* the client did not send an ECH extension */
ech = (WOLFSSL_ECH*)echX->data;
if (ech->state == ECH_WRITE_NONE && ech->innerClientHello == NULL)
return WOLFSSL_ECH_STATUS_NOT_OFFERED;
return WOLFSSL_ECH_STATUS_REJECTED;
}
/* client */
if (ssl->options.connectState < CLIENT_HELLO_SENT)
return WOLFSSL_ECH_STATUS_NOT_OFFERED;
if (ssl->echConfigs == NULL)
return WOLFSSL_ECH_STATUS_GREASE;
return WOLFSSL_ECH_STATUS_REJECTED;
}
/* Walk the ECHConfigExtension list and check for mandatory extensions.
* Returns:
* 0 if all extensions are known/optional,
+3 -8
View File
@@ -14551,20 +14551,15 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
}
}
else {
WOLFSSL_MSG("ECH accepted");
ssl->options.echAccepted = 1;
ret = TLSX_ECH_CheckInnerPadding(ssl, ech);
if (ret == 0) {
/* expand EchOuterExtensions if present.
* Also, if it exists, copy sessionID from outer hello */
ret = TLSX_ECH_ExpandOuterExtensions(ssl, ech, ssl->heap);
}
if (ret == 0){
WOLFSSL_MSG("ECH accepted");
ssl->options.echAccepted = 1;
}
else {
WOLFSSL_MSG("ECH rejected");
}
}
if (ret != 0) {
XFREE(ech->innerClientHello, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
+88 -25
View File
@@ -14976,7 +14976,7 @@ static int test_wolfSSL_ECH_conn_ex(method_provider serverMeth,
/* connect like normal */
ExpectIntEQ(wolfSSL_set_fd(ssl, sockfd), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_connect(ssl), WOLFSSL_SUCCESS);
ExpectIntEQ(ssl->options.echAccepted, 1);
ExpectIntEQ(wolfSSL_GetEchStatus(ssl), WOLFSSL_ECH_STATUS_ACCEPTED);
ExpectIntEQ(wolfSSL_write(ssl, privateName, privateNameLen),
privateNameLen);
ExpectIntGT((replyLen = wolfSSL_read(ssl, reply, sizeof(reply))), 0);
@@ -15137,7 +15137,10 @@ static int test_wolfSSL_Tls13_ECH_all_algos_ex(void)
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 1);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_ACCEPTED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_ACCEPTED);
if (echCbTestKemID != 0 && echCbTestKdfID != 0 && echCbTestAeadID != 0) {
TLSX* echX = TLSX_Find(test_ctx.c_ssl->extensions, TLSX_ECH);
@@ -15250,7 +15253,10 @@ static int test_wolfSSL_Tls13_ECH_no_private_name(void)
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 1);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_ACCEPTED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_ACCEPTED);
test_ssl_memio_cleanup(&test_ctx);
@@ -15270,7 +15276,10 @@ static int test_wolfSSL_Tls13_ECH_no_private_name(void)
echCbTestConfigsLen), WOLFSSL_SUCCESS);
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_NOT_OFFERED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_NOT_OFFERED);
test_ssl_memio_cleanup(&test_ctx);
@@ -15289,7 +15298,10 @@ static int test_wolfSSL_Tls13_ECH_no_private_name(void)
echCbTestConfigsLen), WOLFSSL_SUCCESS);
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_NOT_OFFERED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_NOT_OFFERED);
test_ssl_memio_cleanup(&test_ctx);
@@ -15348,7 +15360,10 @@ static int test_wolfSSL_Tls13_ECH_bad_configs_ex(int hrr, int sniCb)
}
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
test_ssl_memio_cleanup(&test_ctx);
@@ -15384,7 +15399,12 @@ static int test_wolfSSL_Tls13_ECH_bad_configs_ex(int hrr, int sniCb)
}
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
/* server decrypts inner successfully but rejects SNI, thus the client does
* not receive the acceptance signal */
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_ACCEPTED);
test_ssl_memio_cleanup(&test_ctx);
@@ -15445,7 +15465,10 @@ static int test_wolfSSL_Tls13_ECH_retry_configs_ex(int hrr)
/* ECH must fail and retry configs must be present */
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, 0),
WC_NO_ERR_TRACE(ECH_REQUIRED_E));
@@ -15483,7 +15506,10 @@ static int test_wolfSSL_Tls13_ECH_retry_configs_ex(int hrr)
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 1);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_ACCEPTED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_ACCEPTED);
wolfSSL_CTX_free(test_ctx.s_ctx);
test_ctx.s_ctx = NULL;
@@ -15605,7 +15631,10 @@ static int test_wolfSSL_Tls13_ECH_retry_configs_bad(void)
/* bad retry configs are discarded - failure must be ECH_REQUIRED_E,
* not a retry-config parse error */
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, 0),
WC_NO_ERR_TRACE(ECH_REQUIRED_E));
@@ -15690,7 +15719,10 @@ static int test_wolfSSL_Tls13_ECH_new_config(void)
WOLFSSL_SUCCESS);
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 1);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_ACCEPTED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_ACCEPTED);
test_ssl_memio_cleanup(&test_ctx);
@@ -15736,7 +15768,10 @@ static int test_wolfSSL_Tls13_ECH_trial_decrypt(void)
}
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, 0),
WC_NO_ERR_TRACE(ECH_REQUIRED_E));
@@ -15768,7 +15803,10 @@ static int test_wolfSSL_Tls13_ECH_trial_decrypt(void)
}
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 1);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_ACCEPTED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_ACCEPTED);
test_ssl_memio_cleanup(&test_ctx);
@@ -15810,9 +15848,11 @@ static int test_wolfSSL_Tls13_ECH_GREASE(void)
/* handshake should succeed - server ignores the GREASE ECH extension */
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
/* ECH should NOT be accepted since this was GREASE */
ExpectIntEQ(test_ctx.s_ssl->options.echAccepted, 0);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
/* server has no configs and client did not offer real ECH */
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_NOT_OFFERED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_GREASE);
/* verify no ECH configs are received */
ExpectNull(test_ctx.c_ssl->echConfigs);
/* retry configs must not be saved */
@@ -15848,9 +15888,12 @@ static int test_wolfSSL_Tls13_ECH_GREASE(void)
/* handshake should succeed - server responds to the GREASE ECH extension */
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
/* ECH should NOT be accepted since this was GREASE */
ExpectIntEQ(test_ctx.s_ssl->options.echAccepted, 0);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
/* server was unable to decrypt the ECH extension's payload
* client never offered real ECH */
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_GREASE);
/* verify no ECH configs are received */
ExpectNull(test_ctx.c_ssl->echConfigs);
/* retry configs must not be saved */
@@ -15895,6 +15938,8 @@ static int test_wolfSSL_Tls13_ECH_disable_conn_ex(int enableServer,
* normally but ECH is not accepted */
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
TEST_SUCCESS);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_NOT_OFFERED);
}
else if (!enableServer) {
/* client sends ECH but server can't process it: server has no ECH
@@ -15902,8 +15947,11 @@ static int test_wolfSSL_Tls13_ECH_disable_conn_ex(int enableServer,
* rejection and aborts the handshake */
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
TEST_SUCCESS);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
}
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_NOT_OFFERED);
test_ssl_memio_cleanup(&test_ctx);
@@ -16008,7 +16056,10 @@ static int test_wolfSSL_Tls13_ECH_HRR_rejection(void)
/* Handshake must fail: client aborts with ech_required */
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_NOT_OFFERED);
/* hsHashesEch must have been freed by the HRR rejection code path */
ExpectNull(test_ctx.c_ssl->hsHashesEch);
ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, 0),
@@ -16046,7 +16097,10 @@ static int test_wolfSSL_Tls13_ECH_ch2_no_ech(void)
/* server must have committed to ECH acceptance in the HRR */
ExpectIntEQ(test_ctx.s_ssl->options.serverState,
SERVER_HELLO_RETRY_REQUEST_COMPLETE);
ExpectIntEQ(test_ctx.s_ssl->options.echAccepted, 1);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_ACCEPTED);
/* disable ECH on the client so CH2 omits the ECH extension entirely */
wolfSSL_SetEchEnable(test_ctx.c_ssl, 0);
@@ -16088,7 +16142,10 @@ static int test_wolfSSL_Tls13_ECH_ch2_decrypt_error(void)
ExpectIntEQ(test_ctx.s_ssl->options.serverState,
SERVER_HELLO_RETRY_REQUEST_COMPLETE);
ExpectIntEQ(test_ctx.s_ssl->options.echAccepted, 1);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_ACCEPTED);
if (EXPECT_SUCCESS()) {
/* Client reads HRR and writes CH2 into s_buff */
@@ -16166,7 +16223,10 @@ static int test_wolfSSL_Tls13_ECH_rejected_cert_valid_ex(const char* publicName,
/* client sends ECH but server can't process it, however it is possible to
* fall back to the outer handshake */
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_NOT_OFFERED);
if (validName) {
/* the server should see the handshake as successful
@@ -16244,7 +16304,10 @@ static int test_wolfSSL_Tls13_ECH_rejected_empty_client_cert(void)
publicName, (word16)XSTRLEN(publicName)), WOLFSSL_SUCCESS);
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl),
WOLFSSL_ECH_STATUS_REJECTED);
ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl),
WOLFSSL_ECH_STATUS_NOT_OFFERED);
/* Server cert is valid for public_name, cert check passes, ech_required
* is sent on the client side. */
+6
View File
@@ -1261,6 +1261,12 @@ WOLFSSL_API int wolfSSL_GetEchRetryConfigs(WOLFSSL* ssl, byte* echConfigs,
WOLFSSL_API void wolfSSL_SetEchEnable(WOLFSSL* ssl, byte enable);
WOLFSSL_API void wolfSSL_SetEchEnableTrialDecrypt(WOLFSSL* ssl, byte enable);
#define WOLFSSL_ECH_STATUS_NOT_OFFERED 0
#define WOLFSSL_ECH_STATUS_GREASE 1
#define WOLFSSL_ECH_STATUS_REJECTED 2
#define WOLFSSL_ECH_STATUS_ACCEPTED 3
WOLFSSL_API int wolfSSL_GetEchStatus(const WOLFSSL* ssl);
#endif /* WOLFSSL_TLS13 && HAVE_ECH */
#ifdef HAVE_POLY1305