mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-05 12:20:52 +02:00
Merge pull request #10259 from sebastian-carpenter/tls-ech-keylog
TLS ECH keylogging
This commit is contained in:
@@ -535,6 +535,12 @@ void wolfssl_priv_der_unblind_free(DerBuffer* key)
|
||||
#define SSC_TLS13_EES "EARLY_EXPORTER_SECRET"
|
||||
/* Label string for exporter secret. */
|
||||
#define SSC_TLS13_ES "EXPORTER_SECRET"
|
||||
#ifdef HAVE_ECH
|
||||
/* Label string for ECH KEM shared secret. */
|
||||
#define SSC_TLS13_ECH_S "ECH_SECRET"
|
||||
/* Label string for ECHConfig used to construct ECH. */
|
||||
#define SSC_TLS13_ECH_C "ECH_CONFIG"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This function builds up string for key-logging then call user's
|
||||
@@ -594,6 +600,18 @@ void wolfssl_priv_der_unblind_free(DerBuffer* key)
|
||||
label = SSC_TLS13_ES;
|
||||
break;
|
||||
|
||||
#ifdef HAVE_ECH
|
||||
case ECH_SECRET:
|
||||
labelSz = sizeof(SSC_TLS13_ECH_S);
|
||||
label = SSC_TLS13_ECH_S;
|
||||
break;
|
||||
|
||||
case ECH_CONFIG:
|
||||
labelSz = sizeof(SSC_TLS13_ECH_C);
|
||||
label = SSC_TLS13_ECH_C;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
@@ -14017,6 +14017,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.
|
||||
@@ -14426,8 +14462,8 @@ 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;
|
||||
@@ -14435,7 +14471,7 @@ static int TLSX_ExtractEch(WOLFSSL_ECH* ech, WOLFSSL_EchConfig* echConfig,
|
||||
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)
|
||||
@@ -14453,13 +14489,14 @@ static int TLSX_ExtractEch(WOLFSSL_ECH* ech, WOLFSSL_EchConfig* echConfig,
|
||||
/* check if hpke already exists, may if HelloRetryRequest */
|
||||
if (ech->hpke == NULL) {
|
||||
allocatedHpke = 1;
|
||||
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 */
|
||||
@@ -14477,7 +14514,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;
|
||||
@@ -14488,6 +14525,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,
|
||||
@@ -14501,17 +14548,29 @@ 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) {
|
||||
ret = EchWriteKeyLog(ssl, ech->hpke->echSecret, ech->hpke->Nsecret,
|
||||
info + TLS_INFO_CONST_STRING_SZ + 1, rawConfigLen);
|
||||
}
|
||||
wc_HpkeFreeEchSecret(ech->hpke);
|
||||
#endif /* HAVE_SECRET_CALLBACK */
|
||||
|
||||
/* only free hpke/hpkeContext if allocated in this call; otherwise preserve
|
||||
* them for clientHello2 */
|
||||
if (ret != 0 && allocatedHpke) {
|
||||
XFREE(ech->hpke, heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(ech->hpke, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ech->hpke = NULL;
|
||||
XFREE(ech->hpkeContext, heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ech->hpkeContext = NULL;
|
||||
if (ech->hpkeContext != NULL) {
|
||||
ForceZero(ech->hpkeContext, sizeof(HpkeBaseContext));
|
||||
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;
|
||||
}
|
||||
@@ -14715,9 +14774,9 @@ 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);
|
||||
if (ret == 0)
|
||||
ret = TLSX_ExtractEch(ssl, ech, echConfig, aadCopy,
|
||||
ech->aadLen);
|
||||
if (ret == 0 || ret == WC_NO_ERR_TRACE(TLS13_SECRET_CB_E))
|
||||
break;
|
||||
}
|
||||
echConfig = echConfig->next;
|
||||
@@ -14727,42 +14786,46 @@ 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);
|
||||
if (ret == 0)
|
||||
ret = TLSX_ExtractEch(ssl, ech, echConfig, aadCopy,
|
||||
ech->aadLen);
|
||||
if (ret == 0 || ret == WC_NO_ERR_TRACE(TLS13_SECRET_CB_E))
|
||||
break;
|
||||
}
|
||||
echConfig = echConfig->next;
|
||||
}
|
||||
}
|
||||
/* if we failed to extract/expand */
|
||||
if (ret != 0 || echConfig == NULL) {
|
||||
WOLFSSL_MSG("ECH rejected");
|
||||
/* TLS13_SECRET_CB_E isn't correlated with ECH acceptance so skip both
|
||||
* paths */
|
||||
if (ret != WC_NO_ERR_TRACE(TLS13_SECRET_CB_E)) {
|
||||
/* if we failed to extract/expand */
|
||||
if (ret != 0 || echConfig == NULL) {
|
||||
WOLFSSL_MSG("ECH rejected");
|
||||
|
||||
if (ssl->options.echAccepted == 1) {
|
||||
/* on SH2 this is fatal */
|
||||
SendAlert(ssl, alert_fatal, decrypt_error);
|
||||
WOLFSSL_ERROR_VERBOSE(DECRYPT_ERROR);
|
||||
ret = DECRYPT_ERROR;
|
||||
if (ssl->options.echAccepted == 0) {
|
||||
/* on SH1 prepare to write retry configs */
|
||||
XFREE(ech->innerClientHello, ssl->heap,
|
||||
DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ech->innerClientHello = NULL;
|
||||
ech->state = ECH_WRITE_RETRY_CONFIGS;
|
||||
ret = 0;
|
||||
}
|
||||
else {
|
||||
/* on SH2 failure to decrypt is fatal */
|
||||
SendAlert(ssl, alert_fatal, decrypt_error);
|
||||
WOLFSSL_ERROR_VERBOSE(DECRYPT_ERROR);
|
||||
ret = DECRYPT_ERROR;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* on SH1 prepare to write retry configs */
|
||||
XFREE(ech->innerClientHello, ssl->heap,
|
||||
DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ech->innerClientHello = NULL;
|
||||
ech->state = ECH_WRITE_RETRY_CONFIGS;
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
WOLFSSL_MSG("ECH accepted");
|
||||
ssl->options.echAccepted = 1;
|
||||
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);
|
||||
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) {
|
||||
@@ -14784,10 +14847,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);
|
||||
/* wc_HpkeFreeEchSecret is intentionally not here, free it in
|
||||
* TLSX_ExtractEch / TLSX_FinalizeEch */
|
||||
XFREE(ech->hpke, heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
}
|
||||
if (ech->hpkeContext != NULL)
|
||||
if (ech->hpkeContext != NULL) {
|
||||
ForceZero(ech->hpkeContext, sizeof(HpkeBaseContext));
|
||||
XFREE(ech->hpkeContext, heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
}
|
||||
if (ech->privateName != NULL)
|
||||
XFREE((char*)ech->privateName, heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
|
||||
@@ -14797,13 +14864,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 */
|
||||
@@ -14831,6 +14900,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);
|
||||
@@ -14851,6 +14932,15 @@ 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);
|
||||
}
|
||||
wc_HpkeFreeEchSecret(ech->hpke);
|
||||
#endif /* HAVE_SECRET_CALLBACK */
|
||||
|
||||
if (info != NULL)
|
||||
XFREE(info, ech->hpke->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (aadCopy != NULL)
|
||||
@@ -14869,7 +14959,7 @@ int TLSX_FinalizeEch(WOLFSSL_ECH* ech, byte* aad, word32 aadLen)
|
||||
#define ECH_PARSE TLSX_ECH_Parse
|
||||
#define ECH_FREE TLSX_ECH_Free
|
||||
|
||||
#endif
|
||||
#endif /* WOLFSSL_TLS13 && HAVE_ECH */
|
||||
|
||||
/** Releases all extensions in the provided list. */
|
||||
void TLSX_FreeAll(TLSX* list, void* heap)
|
||||
|
||||
+12
-5
@@ -5096,7 +5096,7 @@ int SendTls13ClientHello(WOLFSSL* ssl)
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
ret = TLSX_FinalizeEch(args->ech,
|
||||
ret = TLSX_FinalizeEch(ssl, args->ech,
|
||||
args->output + RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ,
|
||||
(word32)(args->sendSz - (RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ)));
|
||||
|
||||
@@ -5878,7 +5878,8 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
/* use the inner random for client random */
|
||||
if (args->extMsgType != hello_retry_request) {
|
||||
if (args->extMsgType != hello_retry_request &&
|
||||
ssl->options.echAccepted) {
|
||||
XMEMCPY(ssl->arrays->clientRandom,
|
||||
ssl->arrays->clientRandomInner, RAN_LEN);
|
||||
}
|
||||
@@ -7455,7 +7456,8 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
|
||||
|
||||
/* From here on we are a TLS 1.3 ClientHello. */
|
||||
|
||||
/* Client random */
|
||||
/* Client random
|
||||
* ECH Accepted -> This will fill with the innerClientRandom */
|
||||
XMEMCPY(ssl->arrays->clientRandom, input + args->idx, RAN_LEN);
|
||||
args->idx += RAN_LEN;
|
||||
|
||||
@@ -13689,8 +13691,7 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx,
|
||||
* Only on first inner ClientHello (before HRR), not CH2. */
|
||||
if (copyRandom) {
|
||||
XMEMCPY(ssl->arrays->clientRandomInner,
|
||||
((WOLFSSL_ECH*)echX->data)->innerClientHello +
|
||||
HANDSHAKE_HEADER_SZ + VERSION_SZ, RAN_LEN);
|
||||
ssl->arrays->clientRandom, RAN_LEN);
|
||||
}
|
||||
*inOutIdx += size;
|
||||
}
|
||||
@@ -16102,6 +16103,12 @@ int tls13ShowSecrets(WOLFSSL* ssl, int id, const unsigned char* secret,
|
||||
str = "SERVER_TRAFFIC_SECRET_0"; break;
|
||||
case EXPORTER_SECRET:
|
||||
str = "EXPORTER_SECRET"; break;
|
||||
#ifdef HAVE_ECH
|
||||
case ECH_SECRET:
|
||||
str = "ECH_SECRET"; break;
|
||||
case ECH_CONFIG:
|
||||
str = "ECH_CONFIG"; break;
|
||||
#endif
|
||||
default:
|
||||
#ifdef WOLFSSL_SSLKEYLOGFILE_OUTPUT
|
||||
XFCLOSE(fp);
|
||||
|
||||
+205
-19
@@ -13329,7 +13329,7 @@ static int test_wolfSSL_Tls12_Key_Logging_test(void)
|
||||
|
||||
XMEMSET(buff, 0, sizeof(buff));
|
||||
while (EXPECT_SUCCESS() && XFGETS(buff, (int)sizeof(buff), fp) != NULL) {
|
||||
if (0 == strncmp(buff,"CLIENT_RANDOM ", sizeof("CLIENT_RANDOM ")-1)) {
|
||||
if (0 == XSTRNCMP(buff,"CLIENT_RANDOM ", sizeof("CLIENT_RANDOM ")-1)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
@@ -13347,12 +13347,57 @@ 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 */
|
||||
wolfSSL_CTX_set_keylog_callback(ctx, keyLog_callback);
|
||||
return TEST_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_wolfSSL_Tls13_Key_Logging_server_ctx_ready(WOLFSSL_CTX* ctx)
|
||||
{
|
||||
#ifdef HAVE_ECH
|
||||
if (test_ech_server_ctx_ready(ctx) != TEST_SUCCESS)
|
||||
return TEST_FAIL;
|
||||
#endif
|
||||
/* set keylog callback */
|
||||
wolfSSL_CTX_set_keylog_callback(ctx, keyLog_callback);
|
||||
return TEST_SUCCESS;
|
||||
}
|
||||
|
||||
static int test_wolfSSL_Tls13_Key_Logging_client_ssl_ready(WOLFSSL* ssl)
|
||||
{
|
||||
#ifdef HAVE_KEYING_MATERIAL
|
||||
/* retain arrays so EXPORTER_SECRET is logged */
|
||||
wolfSSL_KeepArrays(ssl);
|
||||
#endif
|
||||
#ifdef HAVE_ECH
|
||||
return test_ech_client_ssl_ready(ssl);
|
||||
#else
|
||||
(void)ssl;
|
||||
return TEST_SUCCESS;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int test_wolfSSL_Tls13_Key_Logging_server_ssl_ready(WOLFSSL* ssl)
|
||||
{
|
||||
#ifdef HAVE_KEYING_MATERIAL
|
||||
/* retain arrays so EXPORTER_SECRET is logged */
|
||||
wolfSSL_KeepArrays(ssl);
|
||||
#endif
|
||||
#ifdef HAVE_ECH
|
||||
return test_ech_server_ssl_ready(ssl);
|
||||
#else
|
||||
(void)ssl;
|
||||
return TEST_SUCCESS;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static int test_wolfSSL_Tls13_Key_Logging_test(void)
|
||||
@@ -13361,7 +13406,7 @@ static int test_wolfSSL_Tls13_Key_Logging_test(void)
|
||||
#if defined(WOLFSSL_TLS13) && defined(OPENSSL_EXTRA) && \
|
||||
defined(HAVE_SECRET_CALLBACK)
|
||||
/* This test is intended for checking whether keylog callback is called
|
||||
* in client during TLS handshake between the client and a server.
|
||||
* in the client/server during a TLS handshake.
|
||||
*/
|
||||
test_ssl_cbf server_cbf;
|
||||
test_ssl_cbf client_cbf;
|
||||
@@ -13371,6 +13416,9 @@ static int test_wolfSSL_Tls13_Key_Logging_test(void)
|
||||
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;
|
||||
client_cbf.ssl_ready = &test_wolfSSL_Tls13_Key_Logging_client_ssl_ready;
|
||||
server_cbf.ctx_ready = &test_wolfSSL_Tls13_Key_Logging_server_ctx_ready;
|
||||
server_cbf.ssl_ready = &test_wolfSSL_Tls13_Key_Logging_server_ssl_ready;
|
||||
|
||||
/* clean up keylog file */
|
||||
ExpectTrue((fp = XFOPEN("./MyKeyLog.txt", "w")) != XBADFILE);
|
||||
@@ -13385,44 +13433,181 @@ static int test_wolfSSL_Tls13_Key_Logging_test(void)
|
||||
/* check if the keylog file exists */
|
||||
{
|
||||
char buff[300] = {0};
|
||||
int found[4] = {0};
|
||||
int numfnd = 0;
|
||||
int i;
|
||||
int found[7] = {0};
|
||||
#ifdef HAVE_ECH
|
||||
char echRandom[RAN_LEN * 2] = {0};
|
||||
char chtsRandom[RAN_LEN * 2] = {0};
|
||||
#endif
|
||||
|
||||
ExpectTrue((fp = XFOPEN("./MyKeyLog.txt", "rb")) != XBADFILE);
|
||||
|
||||
while (EXPECT_SUCCESS() &&
|
||||
XFGETS(buff, (int)sizeof(buff), fp) != NULL) {
|
||||
if (0 == strncmp(buff, "CLIENT_HANDSHAKE_TRAFFIC_SECRET ",
|
||||
if (0 == XSTRNCMP(buff, "CLIENT_HANDSHAKE_TRAFFIC_SECRET ",
|
||||
sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET ")-1)) {
|
||||
found[0] = 1;
|
||||
found[0]++;
|
||||
#ifdef HAVE_ECH
|
||||
XMEMCPY(chtsRandom,
|
||||
buff + sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET ")-1,
|
||||
RAN_LEN * 2);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
else if (0 == strncmp(buff, "SERVER_HANDSHAKE_TRAFFIC_SECRET ",
|
||||
else if (0 == XSTRNCMP(buff, "SERVER_HANDSHAKE_TRAFFIC_SECRET ",
|
||||
sizeof("SERVER_HANDSHAKE_TRAFFIC_SECRET ")-1)) {
|
||||
found[1] = 1;
|
||||
found[1]++;
|
||||
continue;
|
||||
}
|
||||
else if (0 == strncmp(buff, "CLIENT_TRAFFIC_SECRET_0 ",
|
||||
else if (0 == XSTRNCMP(buff, "CLIENT_TRAFFIC_SECRET_0 ",
|
||||
sizeof("CLIENT_TRAFFIC_SECRET_0 ")-1)) {
|
||||
found[2] = 1;
|
||||
found[2]++;
|
||||
continue;
|
||||
}
|
||||
else if (0 == strncmp(buff, "SERVER_TRAFFIC_SECRET_0 ",
|
||||
else if (0 == XSTRNCMP(buff, "SERVER_TRAFFIC_SECRET_0 ",
|
||||
sizeof("SERVER_TRAFFIC_SECRET_0 ")-1)) {
|
||||
found[3] = 1;
|
||||
found[3]++;
|
||||
continue;
|
||||
}
|
||||
#ifdef HAVE_KEYING_MATERIAL
|
||||
else if (0 == XSTRNCMP(buff, "EXPORTER_SECRET ",
|
||||
sizeof("EXPORTER_SECRET ")-1)) {
|
||||
found[4]++;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_ECH
|
||||
else if (0 == XSTRNCMP(buff, "ECH_SECRET ",
|
||||
sizeof("ECH_SECRET ")-1)) {
|
||||
found[5]++;
|
||||
XMEMCPY(echRandom, buff + sizeof("ECH_SECRET ")-1,
|
||||
RAN_LEN * 2);
|
||||
continue;
|
||||
}
|
||||
else if (0 == XSTRNCMP(buff, "ECH_CONFIG ",
|
||||
sizeof("ECH_CONFIG ")-1)) {
|
||||
found[6]++;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (fp != XBADFILE)
|
||||
XFCLOSE(fp);
|
||||
/* the four traffic secrets are derived by both client and server, so
|
||||
* each label should appear twice with the callback set on both sides */
|
||||
ExpectIntEQ(found[0], 2);
|
||||
ExpectIntEQ(found[1], 2);
|
||||
ExpectIntEQ(found[2], 2);
|
||||
ExpectIntEQ(found[3], 2);
|
||||
#ifdef HAVE_KEYING_MATERIAL
|
||||
/* both sides retain arrays via KeepArrays, so EXPORTER_SECRET fires
|
||||
* on each */
|
||||
ExpectIntEQ(found[4], 2);
|
||||
#endif
|
||||
#ifdef HAVE_ECH
|
||||
/* both sides also log ECH_SECRET and ECH_CONFIG (seal on client,
|
||||
* open on server) */
|
||||
ExpectIntEQ(found[5], 2);
|
||||
ExpectIntEQ(found[6], 2);
|
||||
/* both lines must have been logged */
|
||||
ExpectIntNE(echRandom[0], 0);
|
||||
ExpectIntNE(chtsRandom[0], 0);
|
||||
/* ECH_SECRET MUST be logged against the outer random while
|
||||
* CLIENT_HANDSHAKE_TRAFFIC_SECRET uses the inner random when ECH is
|
||||
* accepted */
|
||||
ExpectIntNE(XSTRNCMP(echRandom, chtsRandom, RAN_LEN * 2), 0);
|
||||
#endif
|
||||
}
|
||||
#endif /* OPENSSL_EXTRA && HAVE_SECRET_CALLBACK && WOLFSSL_TLS13 */
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* When ECH is rejected the inner random is never swapped in, so ECH_SECRET and
|
||||
* CLIENT_HANDSHAKE_TRAFFIC_SECRET are both logged against the outer random. */
|
||||
static int test_wolfSSL_Tls13_Key_Logging_ech_rejected(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(WOLFSSL_TLS13) && defined(OPENSSL_EXTRA) && \
|
||||
defined(HAVE_SECRET_CALLBACK) && defined(HAVE_ECH) && \
|
||||
defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES)
|
||||
test_ssl_memio_ctx test_ctx;
|
||||
WOLFSSL_CTX* tempCtx = NULL;
|
||||
byte badConfig[128];
|
||||
word32 badConfigLen = sizeof(badConfig);
|
||||
XFILE fp = XBADFILE;
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
test_ctx.s_cb.method = wolfTLSv1_3_server_method;
|
||||
test_ctx.c_cb.method = wolfTLSv1_3_client_method;
|
||||
/* server generates its real ECH config and sets the keylog callback */
|
||||
test_ctx.s_cb.ctx_ready = test_wolfSSL_Tls13_Key_Logging_server_ctx_ready;
|
||||
/* client sets the keylog callback */
|
||||
test_ctx.c_cb.ctx_ready = test_wolfSSL_Tls13_Key_Logging_client_ctx_ready;
|
||||
|
||||
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
|
||||
|
||||
/* generate a throwaway ECH config the server cannot decrypt */
|
||||
ExpectNotNull(tempCtx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
||||
ExpectIntEQ(wolfSSL_CTX_GenerateEchConfig(tempCtx, "ech-public-name.com",
|
||||
0, 0, 0), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_CTX_GetEchConfigs(tempCtx, badConfig, &badConfigLen),
|
||||
WOLFSSL_SUCCESS);
|
||||
wolfSSL_CTX_free(tempCtx);
|
||||
tempCtx = NULL;
|
||||
|
||||
/* client uses the bad config so the server rejects ECH */
|
||||
ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, badConfig, badConfigLen),
|
||||
WOLFSSL_SUCCESS);
|
||||
/* set inner SNI */
|
||||
ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME,
|
||||
"ech-private-name.com", (word16)XSTRLEN("ech-private-name.com")),
|
||||
WOLFSSL_SUCCESS);
|
||||
/* client sends empty cert on rejection, server should not ask for one */
|
||||
wolfSSL_set_verify(test_ctx.s_ssl, WOLFSSL_VERIFY_NONE, NULL);
|
||||
|
||||
/* clean up keylog file */
|
||||
ExpectTrue((fp = XFOPEN("./MyKeyLog.txt", "w")) != XBADFILE);
|
||||
if (fp != XBADFILE) {
|
||||
XFCLOSE(fp);
|
||||
fp = XBADFILE;
|
||||
}
|
||||
|
||||
/* handshake fails because ECH was rejected */
|
||||
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
|
||||
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);
|
||||
|
||||
{
|
||||
char buff[300] = {0};
|
||||
char echRandom[RAN_LEN * 2] = {0};
|
||||
char chtsRandom[RAN_LEN * 2] = {0};
|
||||
|
||||
ExpectTrue((fp = XFOPEN("./MyKeyLog.txt", "rb")) != XBADFILE);
|
||||
while (EXPECT_SUCCESS() &&
|
||||
XFGETS(buff, (int)sizeof(buff), fp) != NULL) {
|
||||
if (0 == XSTRNCMP(buff, "CLIENT_HANDSHAKE_TRAFFIC_SECRET ",
|
||||
sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET ")-1)) {
|
||||
XMEMCPY(chtsRandom,
|
||||
buff + sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET ")-1,
|
||||
RAN_LEN * 2);
|
||||
}
|
||||
else if (0 == XSTRNCMP(buff, "ECH_SECRET ",
|
||||
sizeof("ECH_SECRET ")-1)) {
|
||||
XMEMCPY(echRandom, buff + sizeof("ECH_SECRET ")-1, RAN_LEN * 2);
|
||||
}
|
||||
}
|
||||
if (fp != XBADFILE)
|
||||
XFCLOSE(fp);
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (found[i] != 0)
|
||||
numfnd++;
|
||||
}
|
||||
ExpectIntEQ(numfnd, 4);
|
||||
/* both lines must have been logged */
|
||||
ExpectIntNE(echRandom[0], 0);
|
||||
ExpectIntNE(chtsRandom[0], 0);
|
||||
/* ECH was rejected so both should be the outer random */
|
||||
ExpectIntEQ(XSTRNCMP(echRandom, chtsRandom, RAN_LEN * 2), 0);
|
||||
}
|
||||
#endif /* OPENSSL_EXTRA && HAVE_SECRET_CALLBACK && WOLFSSL_TLS13 */
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
@@ -34832,6 +35017,7 @@ TEST_CASE testCases[] = {
|
||||
TEST_DECL(test_wolfSSL_Tls12_Key_Logging_test),
|
||||
/* Can't memory test as server hangs. */
|
||||
TEST_DECL(test_wolfSSL_Tls13_Key_Logging_test),
|
||||
TEST_DECL(test_wolfSSL_Tls13_Key_Logging_ech_rejected),
|
||||
TEST_DECL(test_wolfSSL_Tls13_postauth),
|
||||
TEST_DECL(test_wolfSSL_set_ecdh_auto),
|
||||
TEST_DECL(test_wolfSSL_CTX_set_ecdh_auto),
|
||||
|
||||
@@ -906,6 +906,11 @@ static int wc_HpkeSetupBaseSender(Hpke* hpke, HpkeBaseContext* context,
|
||||
infoSz);
|
||||
}
|
||||
|
||||
#if defined(HAVE_SECRET_CALLBACK) && defined(HAVE_ECH)
|
||||
if (ret == 0 && hpke->echSecret != NULL) {
|
||||
XMEMCPY(hpke->echSecret, sharedSecret, hpke->Nsecret);
|
||||
}
|
||||
#endif
|
||||
ForceZero(sharedSecret, hpke->Nsecret);
|
||||
WC_FREE_VAR_EX(sharedSecret, hpke->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
|
||||
@@ -1148,6 +1153,11 @@ static int wc_HpkeSetupBaseReceiver(Hpke* hpke, HpkeBaseContext* context,
|
||||
infoSz);
|
||||
}
|
||||
|
||||
#if defined(HAVE_SECRET_CALLBACK) && defined(HAVE_ECH)
|
||||
if (ret == 0 && hpke->echSecret != NULL) {
|
||||
XMEMCPY(hpke->echSecret, sharedSecret, hpke->Nsecret);
|
||||
}
|
||||
#endif
|
||||
ForceZero(sharedSecret, hpke->Nsecret);
|
||||
WC_FREE_VAR_EX(sharedSecret, hpke->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
|
||||
@@ -1345,4 +1355,26 @@ WOLFSSL_LOCAL int wc_HpkeAeadIsSupported(word16 aeadId)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HAVE_SECRET_CALLBACK) && defined(HAVE_ECH)
|
||||
WOLFSSL_LOCAL int wc_HpkeInitEchSecret(Hpke* hpke)
|
||||
{
|
||||
if (hpke == NULL)
|
||||
return BAD_FUNC_ARG;
|
||||
hpke->echSecret = (byte*)XMALLOC(hpke->Nsecret, hpke->heap,
|
||||
DYNAMIC_TYPE_SECRET);
|
||||
if (hpke->echSecret == NULL)
|
||||
return MEMORY_E;
|
||||
return 0;
|
||||
}
|
||||
|
||||
WOLFSSL_LOCAL void wc_HpkeFreeEchSecret(Hpke* hpke)
|
||||
{
|
||||
if (hpke == NULL || hpke->echSecret == NULL)
|
||||
return;
|
||||
ForceZero(hpke->echSecret, hpke->Nsecret);
|
||||
XFREE(hpke->echSecret, hpke->heap, DYNAMIC_TYPE_SECRET);
|
||||
hpke->echSecret = NULL;
|
||||
}
|
||||
#endif /* HAVE_SECRET_CALLBACK && HAVE_ECH */
|
||||
|
||||
#endif /* HAVE_HPKE && (HAVE_ECC || HAVE_CURVE25519) && HAVE_AESGCM */
|
||||
|
||||
+2
-1
@@ -3181,7 +3181,8 @@ typedef struct WOLFSSL_ECH {
|
||||
|
||||
WOLFSSL_LOCAL int EchConfigGetSupportedCipherSuite(WOLFSSL_EchConfig* config);
|
||||
|
||||
WOLFSSL_LOCAL int TLSX_FinalizeEch(WOLFSSL_ECH* ech, byte* aad, word32 aadLen);
|
||||
WOLFSSL_LOCAL int TLSX_FinalizeEch(WOLFSSL* ssl, WOLFSSL_ECH* ech, byte* aad,
|
||||
word32 aadLen);
|
||||
|
||||
|
||||
WOLFSSL_LOCAL int SetEchConfigsEx(WOLFSSL_EchConfig** outputConfigs, void* heap,
|
||||
|
||||
+5
-1
@@ -1076,7 +1076,11 @@ enum Tls13Secret {
|
||||
CLIENT_TRAFFIC_SECRET,
|
||||
SERVER_TRAFFIC_SECRET,
|
||||
EARLY_EXPORTER_SECRET,
|
||||
EXPORTER_SECRET
|
||||
EXPORTER_SECRET,
|
||||
#if defined(HAVE_ECH)
|
||||
ECH_SECRET,
|
||||
ECH_CONFIG,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@@ -96,6 +96,9 @@ typedef struct {
|
||||
word16 aead;
|
||||
byte kem_suite_id[KEM_SUITE_ID_LEN];
|
||||
byte hpke_suite_id[HPKE_SUITE_ID_LEN];
|
||||
#if defined(HAVE_SECRET_CALLBACK) && defined(HAVE_ECH)
|
||||
byte* echSecret;
|
||||
#endif
|
||||
} Hpke;
|
||||
|
||||
typedef struct {
|
||||
@@ -135,6 +138,11 @@ WOLFSSL_LOCAL int wc_HpkeKemIsSupported(word16 kemId);
|
||||
WOLFSSL_LOCAL int wc_HpkeKdfIsSupported(word16 kdfId);
|
||||
WOLFSSL_LOCAL int wc_HpkeAeadIsSupported(word16 aeadId);
|
||||
|
||||
#if defined(HAVE_SECRET_CALLBACK) && defined(HAVE_ECH)
|
||||
WOLFSSL_LOCAL int wc_HpkeInitEchSecret(Hpke* hpke);
|
||||
WOLFSSL_LOCAL void wc_HpkeFreeEchSecret(Hpke* hpke);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* HAVE_HPKE && (HAVE_ECC || HAVE_CURVE25519) && HAVE_AESGCM */
|
||||
|
||||
Reference in New Issue
Block a user