-
Notifications
You must be signed in to change notification settings - Fork 970
TLS ECH keylogging #10259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
TLS ECH keylogging #10259
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -13845,6 +13845,42 @@ static int TLSX_ECH_GetSize(WOLFSSL_ECH* ech, byte msgType) | |||||||||||
| return (int)size; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| #ifdef HAVE_SECRET_CALLBACK | ||||||||||||
| /* log ECH_SECRET and ECH_CONFIG | ||||||||||||
| * returns 0 on success, TLS13_SECRET_CB_E otherwise */ | ||||||||||||
| static int EchWriteKeyLog(WOLFSSL* ssl, const byte* secret, word32 secretSz, | ||||||||||||
| const byte* config, word32 configSz) | ||||||||||||
| { | ||||||||||||
| int ret = 0; | ||||||||||||
| if (ssl->tls13SecretCb != NULL) { | ||||||||||||
| ret = ssl->tls13SecretCb(ssl, ECH_SECRET, secret, (int)secretSz, | ||||||||||||
| ssl->tls13SecretCtx); | ||||||||||||
| if (ret == 0) { | ||||||||||||
| ret = ssl->tls13SecretCb(ssl, ECH_CONFIG, config, (int)configSz, | ||||||||||||
| ssl->tls13SecretCtx); | ||||||||||||
| } | ||||||||||||
| if (ret != 0) { | ||||||||||||
| WOLFSSL_ERROR_VERBOSE(TLS13_SECRET_CB_E); | ||||||||||||
| ret = TLS13_SECRET_CB_E; | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| #ifdef OPENSSL_EXTRA | ||||||||||||
| if (ret == 0 && ssl->tls13KeyLogCb != NULL) { | ||||||||||||
| ret = ssl->tls13KeyLogCb(ssl, ECH_SECRET, secret, (int)secretSz, NULL); | ||||||||||||
| if (ret == 0) { | ||||||||||||
| ret = ssl->tls13KeyLogCb(ssl, ECH_CONFIG, config, (int)configSz, | ||||||||||||
| NULL); | ||||||||||||
| } | ||||||||||||
| if (ret != 0) { | ||||||||||||
| WOLFSSL_ERROR_VERBOSE(TLS13_SECRET_CB_E); | ||||||||||||
| ret = TLS13_SECRET_CB_E; | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| #endif /* OPENSSL_EXTRA */ | ||||||||||||
| return ret; | ||||||||||||
| } | ||||||||||||
| #endif /* HAVE_SECRET_CALLBACK */ | ||||||||||||
|
|
||||||||||||
| /* rough check that inner hello fields do not exceed length of decrypted | ||||||||||||
| * information. Additionally, this function will check that all padding bytes | ||||||||||||
| * are zero and decrease the innerHelloLen accordingly if so. | ||||||||||||
|
|
@@ -14259,15 +14295,15 @@ static int TLSX_ECH_ExpandOuterExtensions(WOLFSSL* ssl, WOLFSSL_ECH* ech, | |||||||||||
| /* return status after attempting to open the hpke encrypted ech extension, if | ||||||||||||
| * successful the inner client hello will be stored in | ||||||||||||
| * ech->innerClientHelloLen */ | ||||||||||||
| static int TLSX_ExtractEch(WOLFSSL_ECH* ech, WOLFSSL_EchConfig* echConfig, | ||||||||||||
| byte* aad, word32 aadLen, void* heap) | ||||||||||||
| static int TLSX_ExtractEch(WOLFSSL* ssl, WOLFSSL_ECH* ech, | ||||||||||||
| WOLFSSL_EchConfig* echConfig, byte* aad, word32 aadLen) | ||||||||||||
| { | ||||||||||||
| int ret = 0; | ||||||||||||
| int i; | ||||||||||||
| word32 rawConfigLen = 0; | ||||||||||||
| byte* info = NULL; | ||||||||||||
| word32 infoLen = 0; | ||||||||||||
| if (ech == NULL || echConfig == NULL || aad == NULL) | ||||||||||||
| if (ssl == NULL || ech == NULL || echConfig == NULL || aad == NULL) | ||||||||||||
| return BAD_FUNC_ARG; | ||||||||||||
| /* verify the kem and key len */ | ||||||||||||
| if (wc_HpkeKemGetEncLen(echConfig->kemId) != ech->encLen) | ||||||||||||
|
|
@@ -14284,13 +14320,14 @@ static int TLSX_ExtractEch(WOLFSSL_ECH* ech, WOLFSSL_EchConfig* echConfig, | |||||||||||
| } | ||||||||||||
| /* check if hpke already exists, may if HelloRetryRequest */ | ||||||||||||
| if (ech->hpke == NULL) { | ||||||||||||
| ech->hpke = (Hpke*)XMALLOC(sizeof(Hpke), heap, DYNAMIC_TYPE_TMP_BUFFER); | ||||||||||||
| ech->hpke = (Hpke*)XMALLOC(sizeof(Hpke), ssl->heap, | ||||||||||||
| DYNAMIC_TYPE_TMP_BUFFER); | ||||||||||||
| if (ech->hpke == NULL) | ||||||||||||
| ret = MEMORY_E; | ||||||||||||
| /* init the hpke struct */ | ||||||||||||
| if (ret == 0) { | ||||||||||||
| ret = wc_HpkeInit(ech->hpke, echConfig->kemId, | ||||||||||||
| ech->cipherSuite.kdfId, ech->cipherSuite.aeadId, heap); | ||||||||||||
| ech->cipherSuite.kdfId, ech->cipherSuite.aeadId, ssl->heap); | ||||||||||||
| } | ||||||||||||
| if (ret == 0) { | ||||||||||||
| /* allocate hpkeContext */ | ||||||||||||
|
|
@@ -14308,7 +14345,7 @@ static int TLSX_ExtractEch(WOLFSSL_ECH* ech, WOLFSSL_EchConfig* echConfig, | |||||||||||
| /* create info */ | ||||||||||||
| if (ret == 0) { | ||||||||||||
| infoLen = TLS_INFO_CONST_STRING_SZ + 1 + rawConfigLen; | ||||||||||||
| info = (byte*)XMALLOC(infoLen, heap, DYNAMIC_TYPE_TMP_BUFFER); | ||||||||||||
| info = (byte*)XMALLOC(infoLen, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); | ||||||||||||
|
|
||||||||||||
| if (info == NULL) | ||||||||||||
| ret = MEMORY_E; | ||||||||||||
|
|
@@ -14319,6 +14356,16 @@ static int TLSX_ExtractEch(WOLFSSL_ECH* ech, WOLFSSL_EchConfig* echConfig, | |||||||||||
| TLS_INFO_CONST_STRING_SZ + 1, &rawConfigLen); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| #ifdef HAVE_SECRET_CALLBACK | ||||||||||||
| /* allocate secret buffer for wc_HpkeInitOpenContext to copy into */ | ||||||||||||
| if (ret == 0 && (ssl->tls13SecretCb != NULL | ||||||||||||
| #ifdef OPENSSL_EXTRA | ||||||||||||
| || ssl->tls13KeyLogCb != NULL | ||||||||||||
| #endif | ||||||||||||
| )) { | ||||||||||||
| ret = wc_HpkeInitEchSecret(ech->hpke); | ||||||||||||
| } | ||||||||||||
| #endif /* HAVE_SECRET_CALLBACK */ | ||||||||||||
| /* init the context for opening */ | ||||||||||||
| if (ret == 0) { | ||||||||||||
| ret = wc_HpkeInitOpenContext(ech->hpke, ech->hpkeContext, | ||||||||||||
|
|
@@ -14332,16 +14379,44 @@ static int TLSX_ExtractEch(WOLFSSL_ECH* ech, WOLFSSL_EchConfig* echConfig, | |||||||||||
| ech->outerClientPayload, ech->innerClientHelloLen, | ||||||||||||
| ech->innerClientHello + HANDSHAKE_HEADER_SZ); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| #ifdef HAVE_SECRET_CALLBACK | ||||||||||||
| if (ret == 0 && ech->hpke->echSecret != NULL) { | ||||||||||||
| /* server does not store raw configs, so it needs to be built here */ | ||||||||||||
| byte* echConfigRaw = NULL; | ||||||||||||
| word32 echConfigRawSz = 0; | ||||||||||||
| ret = GetEchConfig(echConfig, NULL, &echConfigRawSz); | ||||||||||||
| if (ret == WC_NO_ERR_TRACE(LENGTH_ONLY_E)) | ||||||||||||
| ret = 0; | ||||||||||||
| if (ret == 0) { | ||||||||||||
| echConfigRaw = (byte*)XMALLOC(echConfigRawSz, ssl->heap, | ||||||||||||
| DYNAMIC_TYPE_TMP_BUFFER); | ||||||||||||
| if (echConfigRaw == NULL) | ||||||||||||
| ret = MEMORY_E; | ||||||||||||
| } | ||||||||||||
| if (ret == 0) | ||||||||||||
| ret = GetEchConfig(echConfig, echConfigRaw, &echConfigRawSz); | ||||||||||||
| if (ret == 0) { | ||||||||||||
| ret = EchWriteKeyLog(ssl, ech->hpke->echSecret, ech->hpke->Nsecret, | ||||||||||||
| echConfigRaw, echConfigRawSz); | ||||||||||||
| } | ||||||||||||
| XFREE(echConfigRaw, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); | ||||||||||||
| } | ||||||||||||
| #endif /* HAVE_SECRET_CALLBACK */ | ||||||||||||
|
|
||||||||||||
| /* free the hpke and context on failure */ | ||||||||||||
| if (ret != 0) { | ||||||||||||
| XFREE(ech->hpke, heap, DYNAMIC_TYPE_TMP_BUFFER); | ||||||||||||
| #ifdef HAVE_SECRET_CALLBACK | ||||||||||||
| wc_HpkeFreeEchSecret(ech->hpke); | ||||||||||||
| #endif | ||||||||||||
| XFREE(ech->hpke, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); | ||||||||||||
| ech->hpke = NULL; | ||||||||||||
| XFREE(ech->hpkeContext, heap, DYNAMIC_TYPE_TMP_BUFFER); | ||||||||||||
| XFREE(ech->hpkeContext, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); | ||||||||||||
| ech->hpkeContext = NULL; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if (info != NULL) | ||||||||||||
| XFREE(info, heap, DYNAMIC_TYPE_TMP_BUFFER); | ||||||||||||
| XFREE(info, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); | ||||||||||||
|
|
||||||||||||
| return ret; | ||||||||||||
| } | ||||||||||||
|
|
@@ -14512,8 +14587,8 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size, | |||||||||||
| echConfig = ssl->ctx->echConfigs; | ||||||||||||
| while (echConfig != NULL) { | ||||||||||||
| if (echConfig->configId == ech->configId) { | ||||||||||||
| ret = TLSX_ExtractEch(ech, echConfig, aadCopy, ech->aadLen, | ||||||||||||
| ssl->heap); | ||||||||||||
| ret = TLSX_ExtractEch(ssl, ech, echConfig, aadCopy, | ||||||||||||
| ech->aadLen); | ||||||||||||
| break; | ||||||||||||
| } | ||||||||||||
| echConfig = echConfig->next; | ||||||||||||
|
|
@@ -14522,8 +14597,8 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size, | |||||||||||
| if (echConfig == NULL || ret != 0) { | ||||||||||||
| echConfig = ssl->ctx->echConfigs; | ||||||||||||
| while (echConfig != NULL) { | ||||||||||||
| ret = TLSX_ExtractEch(ech, echConfig, aadCopy, ech->aadLen, | ||||||||||||
| ssl->heap); | ||||||||||||
| ret = TLSX_ExtractEch(ssl, ech, echConfig, aadCopy, | ||||||||||||
| ech->aadLen); | ||||||||||||
| if (ret == 0) | ||||||||||||
| break; | ||||||||||||
| echConfig = echConfig->next; | ||||||||||||
|
|
@@ -14558,10 +14633,14 @@ static void TLSX_ECH_Free(WOLFSSL_ECH* ech, void* heap) | |||||||||||
| if (ech->ephemeralKey != NULL) | ||||||||||||
| wc_HpkeFreeKey(ech->hpke, ech->hpke->kem, ech->ephemeralKey, | ||||||||||||
| ech->hpke->heap); | ||||||||||||
| #ifdef HAVE_SECRET_CALLBACK | ||||||||||||
| wc_HpkeFreeEchSecret(ech->hpke); | ||||||||||||
| #endif | ||||||||||||
| XFREE(ech->hpke, heap, DYNAMIC_TYPE_TMP_BUFFER); | ||||||||||||
| } | ||||||||||||
| if (ech->hpkeContext != NULL) | ||||||||||||
| if (ech->hpkeContext != NULL) { | ||||||||||||
| XFREE(ech->hpkeContext, heap, DYNAMIC_TYPE_TMP_BUFFER); | ||||||||||||
| } | ||||||||||||
| if (ech->privateName != NULL) | ||||||||||||
| XFREE((char*)ech->privateName, heap, DYNAMIC_TYPE_TMP_BUFFER); | ||||||||||||
|
|
||||||||||||
|
|
@@ -14571,13 +14650,15 @@ static void TLSX_ECH_Free(WOLFSSL_ECH* ech, void* heap) | |||||||||||
|
|
||||||||||||
| /* encrypt the client hello and store it in ech->outerClientPayload, return | ||||||||||||
| * status */ | ||||||||||||
| int TLSX_FinalizeEch(WOLFSSL_ECH* ech, byte* aad, word32 aadLen) | ||||||||||||
| int TLSX_FinalizeEch(WOLFSSL* ssl, WOLFSSL_ECH* ech, byte* aad, word32 aadLen) | ||||||||||||
| { | ||||||||||||
| int ret = 0; | ||||||||||||
| void* receiverPubkey = NULL; | ||||||||||||
| byte* info = NULL; | ||||||||||||
| int infoLen = 0; | ||||||||||||
| byte* aadCopy = NULL; | ||||||||||||
| if (ssl == NULL || ech == NULL || aad == NULL) | ||||||||||||
| return BAD_FUNC_ARG; | ||||||||||||
| /* setup hpke context to seal, should be done at most once per connection */ | ||||||||||||
| if (ech->hpkeContext == NULL) { | ||||||||||||
| /* import the server public key */ | ||||||||||||
|
|
@@ -14605,6 +14686,18 @@ int TLSX_FinalizeEch(WOLFSSL_ECH* ech, byte* aad, word32 aadLen) | |||||||||||
| TLS_INFO_CONST_STRING_SZ + 1); | ||||||||||||
| XMEMCPY(info + TLS_INFO_CONST_STRING_SZ + 1, | ||||||||||||
| ech->echConfig->raw, ech->echConfig->rawLen); | ||||||||||||
| } | ||||||||||||
| #ifdef HAVE_SECRET_CALLBACK | ||||||||||||
| /* allocate secret buffer for wc_HpkeInitSealContext to copy into */ | ||||||||||||
| if (ret == 0 && (ssl->tls13SecretCb != NULL | ||||||||||||
| #ifdef OPENSSL_EXTRA | ||||||||||||
| || ssl->tls13KeyLogCb != NULL | ||||||||||||
| #endif | ||||||||||||
| )) { | ||||||||||||
| ret = wc_HpkeInitEchSecret(ech->hpke); | ||||||||||||
| } | ||||||||||||
| #endif /* HAVE_SECRET_CALLBACK */ | ||||||||||||
| if (ret == 0) { | ||||||||||||
| /* init the context for seal with info and keys */ | ||||||||||||
| ret = wc_HpkeInitSealContext(ech->hpke, ech->hpkeContext, | ||||||||||||
| ech->ephemeralKey, receiverPubkey, info, infoLen); | ||||||||||||
|
|
@@ -14625,6 +14718,14 @@ int TLSX_FinalizeEch(WOLFSSL_ECH* ech, byte* aad, word32 aadLen) | |||||||||||
| aadLen, ech->innerClientHello, | ||||||||||||
| ech->innerClientHelloLen - ech->hpke->Nt, ech->outerClientPayload); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| #ifdef HAVE_SECRET_CALLBACK | ||||||||||||
| if (ret == 0 && ech->hpke->echSecret != NULL) { | ||||||||||||
| ret = EchWriteKeyLog(ssl, ech->hpke->echSecret, ech->hpke->Nsecret, | ||||||||||||
| ech->echConfig->raw, ech->echConfig->rawLen); | ||||||||||||
|
||||||||||||
| ech->echConfig->raw, ech->echConfig->rawLen); | |
| ech->echConfig->raw, ech->echConfig->rawLen); | |
| if (ret == 0) { | |
| wc_HpkeFreeEchSecret(ech->hpke); | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14157,6 +14157,12 @@ static int test_wolfSSL_Tls12_Key_Logging_test(void) | |
|
|
||
| #if defined(WOLFSSL_TLS13) && defined(OPENSSL_EXTRA) && \ | ||
| defined(HAVE_SECRET_CALLBACK) | ||
| #ifdef HAVE_ECH | ||
| static int test_ech_server_ctx_ready(WOLFSSL_CTX* ctx); | ||
| static int test_ech_server_ssl_ready(WOLFSSL* ssl); | ||
| static int test_ech_client_ssl_ready(WOLFSSL* ssl); | ||
| #endif | ||
|
|
||
| static int test_wolfSSL_Tls13_Key_Logging_client_ctx_ready(WOLFSSL_CTX* ctx) | ||
| { | ||
| /* set keylog callback */ | ||
|
|
@@ -14176,11 +14182,21 @@ static int test_wolfSSL_Tls13_Key_Logging_test(void) | |
| test_ssl_cbf server_cbf; | ||
| test_ssl_cbf client_cbf; | ||
| XFILE fp = XBADFILE; | ||
| #ifdef HAVE_ECH | ||
| const int label_count = 7; | ||
| #else | ||
| const int label_count = 5; | ||
| #endif | ||
|
Comment on lines
+14185
to
+14189
|
||
|
|
||
| XMEMSET(&server_cbf, 0, sizeof(test_ssl_cbf)); | ||
| XMEMSET(&client_cbf, 0, sizeof(test_ssl_cbf)); | ||
| server_cbf.method = wolfTLSv1_3_server_method; /* TLS1.3 */ | ||
| client_cbf.ctx_ready = &test_wolfSSL_Tls13_Key_Logging_client_ctx_ready; | ||
| #ifdef HAVE_ECH | ||
| server_cbf.ctx_ready = &test_ech_server_ctx_ready; | ||
| server_cbf.ssl_ready = &test_ech_server_ssl_ready; | ||
| client_cbf.ssl_ready = &test_ech_client_ssl_ready; | ||
| #endif | ||
|
|
||
| /* clean up keylog file */ | ||
| ExpectTrue((fp = XFOPEN("./MyKeyLog.txt", "w")) != XBADFILE); | ||
|
|
@@ -14195,7 +14211,7 @@ static int test_wolfSSL_Tls13_Key_Logging_test(void) | |
| /* check if the keylog file exists */ | ||
| { | ||
| char buff[300] = {0}; | ||
| int found[4] = {0}; | ||
| int found[7] = {0}; | ||
| int numfnd = 0; | ||
| int i; | ||
|
|
||
|
|
@@ -14223,14 +14239,31 @@ static int test_wolfSSL_Tls13_Key_Logging_test(void) | |
| found[3] = 1; | ||
| continue; | ||
| } | ||
| else if (0 == strncmp(buff, "EXPORTER_SECRET ", | ||
| sizeof("EXPORTER_SECRET ")-1)) { | ||
| found[4] = 1; | ||
| continue; | ||
| } | ||
| #ifdef HAVE_ECH | ||
| else if (0 == strncmp(buff, "ECH_SECRET ", | ||
| sizeof("ECH_SECRET ")-1)) { | ||
| found[5] = 1; | ||
| continue; | ||
| } | ||
| else if (0 == strncmp(buff, "ECH_CONFIG ", | ||
| sizeof("ECH_CONFIG ")-1)) { | ||
| found[6] = 1; | ||
| continue; | ||
| } | ||
| #endif | ||
| } | ||
| if (fp != XBADFILE) | ||
| XFCLOSE(fp); | ||
| for (i = 0; i < 4; i++) { | ||
| for (i = 0; i < label_count; i++) { | ||
| if (found[i] != 0) | ||
| numfnd++; | ||
| } | ||
| ExpectIntEQ(numfnd, 4); | ||
| ExpectIntEQ(numfnd, label_count); | ||
| } | ||
| #endif /* OPENSSL_EXTRA && HAVE_SECRET_CALLBACK && WOLFSSL_TLS13 */ | ||
| return EXPECT_RESULT(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TLSX_ExtractEch() logs the HPKE shared secret when ech->hpke->echSecret is set, but leaves echSecret populated afterward. Consider wiping/freeing ech->hpke->echSecret immediately after EchWriteKeyLog() succeeds (wc_HpkeFreeEchSecret) to reduce the amount of time key material stays resident in memory.