Skip to content

Commit e349c68

Browse files
trial decryption for ECH
1 parent eecb8cc commit e349c68

6 files changed

Lines changed: 129 additions & 9 deletions

File tree

src/internal.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8024,7 +8024,8 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
80248024
ssl->options.disallowEncThenMac = ctx->disallowEncThenMac;
80258025
#endif
80268026
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
8027-
ssl->options.disableECH = ctx->disableECH;
8027+
ssl->options.disableECH = ctx->disableECH;
8028+
ssl->options.enableEchTrialDecrypt = ctx->enableEchTrialDecrypt;
80288029
#endif
80298030

80308031
/* default alert state (none) */

src/ssl_ech.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,15 @@ void wolfSSL_CTX_SetEchEnable(WOLFSSL_CTX* ctx, byte enable)
244244
}
245245
}
246246

247+
/* disabled (default) -> only decrypt the ClientHello with configs that have a
248+
* matching configId
249+
* enabled -> try to decrypt the inner ClientHello with all configs */
250+
void wolfSSL_CTX_SetEchEnableTrialDecrypt(WOLFSSL_CTX* ctx, byte enable)
251+
{
252+
if (ctx != NULL)
253+
ctx->enableEchTrialDecrypt = (enable != 0);
254+
}
255+
247256
/* set the ech config from base64 for our client ssl object, base64 is the
248257
* format ech configs are sent using dns records */
249258
int wolfSSL_SetEchConfigsBase64(WOLFSSL* ssl, const char* echConfigs64,
@@ -480,6 +489,15 @@ void wolfSSL_SetEchEnable(WOLFSSL* ssl, byte enable)
480489
}
481490
}
482491

492+
/* disabled (default) -> only decrypt the ClientHello with configs that have a
493+
* matching configId
494+
* enabled -> try to decrypt the inner ClientHello with all configs */
495+
void wolfSSL_SetEchEnableTrialDecrypt(WOLFSSL* ssl, byte enable)
496+
{
497+
if (ssl != NULL)
498+
ssl->options.enableEchTrialDecrypt = (enable != 0);
499+
}
500+
483501
/* Walk the ECHConfigExtension list and check for mandatory extensions.
484502
* Returns:
485503
* 0 if all extensions are known/optional,

src/tls.c

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14513,23 +14513,26 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
1451314513
if (echConfig->configId == ech->configId) {
1451414514
ret = TLSX_ExtractEch(ech, echConfig, aadCopy, ech->aadLen,
1451514515
ssl->heap);
14516-
break;
14516+
if (ret == 0)
14517+
break;
1451714518
}
1451814519
echConfig = echConfig->next;
1451914520
}
14520-
/* otherwise, try to decrypt with all configs */
14521-
if (echConfig == NULL || ret != 0) {
14521+
/* otherwise, try to decrypt with all configs (trial decryption) */
14522+
if (echConfig == NULL && ssl->options.enableEchTrialDecrypt) {
1452214523
echConfig = ssl->ctx->echConfigs;
1452314524
while (echConfig != NULL) {
14524-
ret = TLSX_ExtractEch(ech, echConfig, aadCopy, ech->aadLen,
14525-
ssl->heap);
14526-
if (ret == 0)
14527-
break;
14525+
if (echConfig->configId != ech->configId) {
14526+
ret = TLSX_ExtractEch(ech, echConfig, aadCopy, ech->aadLen,
14527+
ssl->heap);
14528+
if (ret == 0)
14529+
break;
14530+
}
1452814531
echConfig = echConfig->next;
1452914532
}
1453014533
}
1453114534
/* if we failed to extract/expand */
14532-
if (ret != 0) {
14535+
if (ret != 0 || echConfig == NULL) {
1453314536
WOLFSSL_MSG("ECH rejected");
1453414537

1453514538
if (ssl->options.echAccepted == 1) {

tests/api.c

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15080,6 +15080,16 @@ static int test_ech_server_ctx_ready(WOLFSSL_CTX* ctx)
1508015080
return TEST_SUCCESS;
1508115081
}
1508215082

15083+
/* Server ctx_ready callback: generate ECH config and opt into trial
15084+
* decryption at the CTX level so the SSL inherits it on creation */
15085+
static int test_ech_server_ctx_ready_trial_decrypt(WOLFSSL_CTX* ctx)
15086+
{
15087+
int ret = test_ech_server_ctx_ready(ctx);
15088+
if (ret == TEST_SUCCESS)
15089+
wolfSSL_CTX_SetEchEnableTrialDecrypt(ctx, 1);
15090+
return ret;
15091+
}
15092+
1508315093
/* Server ssl_ready callback: set SNI */
1508415094
static int test_ech_server_ssl_ready(WOLFSSL* ssl)
1508515095
{
@@ -15687,6 +15697,84 @@ static int test_wolfSSL_Tls13_ECH_new_config(void)
1568715697
return EXPECT_RESULT();
1568815698
}
1568915699

15700+
/* Test trial decryption for ECH: server has a single config, client receives a
15701+
* copy of it but its configId is overwritten so it cannot match the server's */
15702+
static int test_wolfSSL_Tls13_ECH_trial_decrypt(void)
15703+
{
15704+
EXPECT_DECLS;
15705+
test_ssl_memio_ctx test_ctx;
15706+
15707+
/* --- CTX-enabled, SSL-disabled override: ECH rejected --- */
15708+
15709+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
15710+
15711+
test_ctx.s_cb.method = wolfTLSv1_3_server_method;
15712+
test_ctx.c_cb.method = wolfTLSv1_3_client_method;
15713+
15714+
/* *_trial_decrypt sets enableEchTrialDecrypt to 1 - overriding the default
15715+
* value of 0 */
15716+
test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready_trial_decrypt;
15717+
test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready;
15718+
test_ctx.c_cb.ssl_ready = test_ech_client_ssl_ready;
15719+
15720+
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
15721+
15722+
/* SSL inherited the CTX setting */
15723+
ExpectIntEQ(test_ctx.s_ctx->enableEchTrialDecrypt, 1);
15724+
ExpectIntEQ(test_ctx.s_ssl->options.enableEchTrialDecrypt, 1);
15725+
15726+
/* override on the SSL */
15727+
wolfSSL_SetEchEnableTrialDecrypt(test_ctx.s_ssl, 0);
15728+
ExpectIntEQ(test_ctx.s_ssl->options.enableEchTrialDecrypt, 0);
15729+
15730+
/* alter the client's configId so it does not match the server's configId */
15731+
ExpectNotNull(test_ctx.c_ssl->echConfigs);
15732+
ExpectNotNull(test_ctx.s_ctx->echConfigs);
15733+
if (EXPECT_SUCCESS()) {
15734+
test_ctx.c_ssl->echConfigs->configId =
15735+
(byte)(test_ctx.s_ctx->echConfigs->configId ^ 0x01);
15736+
}
15737+
15738+
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
15739+
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
15740+
ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, 0),
15741+
WC_NO_ERR_TRACE(ECH_REQUIRED_E));
15742+
15743+
test_ssl_memio_cleanup(&test_ctx);
15744+
15745+
/* --- trial decryption opted in on the SSL: ECH accepted --- */
15746+
15747+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
15748+
15749+
test_ctx.s_cb.method = wolfTLSv1_3_server_method;
15750+
test_ctx.c_cb.method = wolfTLSv1_3_client_method;
15751+
15752+
test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready;
15753+
test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready;
15754+
test_ctx.c_cb.ssl_ready = test_ech_client_ssl_ready;
15755+
15756+
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
15757+
15758+
/* opt into trial decryption on the SSL */
15759+
wolfSSL_SetEchEnableTrialDecrypt(test_ctx.s_ssl, 1);
15760+
ExpectIntEQ(test_ctx.s_ssl->options.enableEchTrialDecrypt, 1);
15761+
15762+
/* alter the client's configId so it does not match the server's configId */
15763+
ExpectNotNull(test_ctx.c_ssl->echConfigs);
15764+
ExpectNotNull(test_ctx.s_ctx->echConfigs);
15765+
if (EXPECT_SUCCESS()) {
15766+
test_ctx.c_ssl->echConfigs->configId =
15767+
(byte)(test_ctx.s_ctx->echConfigs->configId ^ 0x01);
15768+
}
15769+
15770+
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
15771+
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 1);
15772+
15773+
test_ssl_memio_cleanup(&test_ctx);
15774+
15775+
return EXPECT_RESULT();
15776+
}
15777+
1569015778
/* Test GREASE ECH:
1569115779
* 1. client sends GREASE ECH extension but server has no ECH configs so it
1569215780
* ignores it, handshake succeeds normally
@@ -40600,6 +40688,7 @@ TEST_CASE testCases[] = {
4060040688
TEST_DECL(test_wolfSSL_Tls13_ECH_retry_configs_bad),
4060140689
TEST_DECL(test_wolfSSL_Tls13_ECH_retry_configs_auth_fail),
4060240690
TEST_DECL(test_wolfSSL_Tls13_ECH_new_config),
40691+
TEST_DECL(test_wolfSSL_Tls13_ECH_trial_decrypt),
4060340692
TEST_DECL(test_wolfSSL_Tls13_ECH_GREASE),
4060440693
TEST_DECL(test_wolfSSL_Tls13_ECH_disable_conn),
4060540694
TEST_DECL(test_wolfSSL_Tls13_ECH_long_SNI),

wolfssl/internal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4044,6 +4044,8 @@ struct WOLFSSL_CTX {
40444044
#endif
40454045
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
40464046
byte disableECH:1;
4047+
byte enableEchTrialDecrypt:1; /* Trial decryption of the
4048+
inner hello */
40474049
#endif
40484050
word16 minProto:1; /* sets min to min available */
40494051
word16 maxProto:1; /* sets max to max available */
@@ -5251,6 +5253,8 @@ struct Options {
52515253
word16 disableECH:1; /* Did the user disable ech */
52525254
word16 echProcessingInner:1; /* Processing the inner hello */
52535255
word16 echRetryConfigsAccepted:1;
5256+
word16 enableEchTrialDecrypt:1; /* Trial decryption of the
5257+
inner hello */
52545258
#endif
52555259
#ifdef WOLFSSL_SEND_HRR_COOKIE
52565260
word16 cookieGood:1;

wolfssl/ssl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,9 @@ WOLFSSL_API int wolfSSL_CTX_GetEchConfigs(WOLFSSL_CTX* ctx, byte* output,
12431243

12441244
WOLFSSL_API void wolfSSL_CTX_SetEchEnable(WOLFSSL_CTX* ctx, byte enable);
12451245

1246+
WOLFSSL_API void wolfSSL_CTX_SetEchEnableTrialDecrypt(WOLFSSL_CTX* ctx,
1247+
byte enable);
1248+
12461249
WOLFSSL_API int wolfSSL_SetEchConfigsBase64(WOLFSSL* ssl,
12471250
const char* echConfigs64, word32 echConfigs64Len);
12481251

@@ -1256,6 +1259,8 @@ WOLFSSL_API int wolfSSL_GetEchRetryConfigs(WOLFSSL* ssl, byte* echConfigs,
12561259
word32* echConfigsLen);
12571260

12581261
WOLFSSL_API void wolfSSL_SetEchEnable(WOLFSSL* ssl, byte enable);
1262+
1263+
WOLFSSL_API void wolfSSL_SetEchEnableTrialDecrypt(WOLFSSL* ssl, byte enable);
12591264
#endif /* WOLFSSL_TLS13 && HAVE_ECH */
12601265

12611266
#ifdef HAVE_POLY1305

0 commit comments

Comments
 (0)