diff --git a/.github/workflows/os-check.yml b/.github/workflows/os-check.yml index 4baea6b830..772b6c86e0 100644 --- a/.github/workflows/os-check.yml +++ b/.github/workflows/os-check.yml @@ -98,6 +98,7 @@ jobs: '--enable-curve25519=nonblock --enable-ecc=nonblock --enable-sp=yes,nonblock CPPFLAGS="-DWOLFSSL_PUBLIC_MP -DWOLFSSL_DEBUG_NONBLOCK"', '--enable-certreq --enable-certext --enable-certgen --disable-secure-renegotiation-info CPPFLAGS="-DNO_TLS"', '--enable-ocsp --enable-ocsp-responder --enable-ocspstapling CPPFLAGS="-DWOLFSSL_NONBLOCK_OCSP" --enable-maxfragment', + '--enable-all --enable-writedup', ] name: make check if: github.repository_owner == 'wolfssl' diff --git a/src/dtls13.c b/src/dtls13.c index 5af638645d..7598bf0e9b 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -2034,8 +2034,21 @@ int Dtls13HandshakeSend(WOLFSSL* ssl, byte* message, word16 outputSize, maxFrag = wolfssl_local_GetMaxPlaintextSize(ssl); maxLen = length; - if (handshakeType == key_update) + if (handshakeType == key_update) { ssl->dtls13WaitKeyUpdateAck = 1; +#ifdef HAVE_WRITE_DUP + /* Notify the read side so it can watch for the ACK on our behalf. */ + if (ssl->dupWrite != NULL && ssl->dupSide == WRITE_DUP_SIDE) { + if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) + return BAD_MUTEX_E; + ssl->dupWrite->keyUpdateEpoch = ssl->dtls13Epoch; + ssl->dupWrite->keyUpdateSeq = + ssl->dtls13EncryptEpoch->nextSeqNumber; + ssl->dupWrite->keyUpdateWaiting = 1; + wc_UnLockMutex(&ssl->dupWrite->dupMutex); + } +#endif /* HAVE_WRITE_DUP */ + } if (maxLen < maxFrag) { ret = Dtls13SendOneFragmentRtx(ssl, handshakeType, outputSize, message, @@ -2656,7 +2669,7 @@ static void Dtls13PrintRtxRecord(Dtls13RtxRecord* r) } #endif /* WOLFSSL_DEBUG_TLS */ -static void Dtls13RtxRemoveRecord(WOLFSSL* ssl, w64wrapper epoch, +void Dtls13RtxRemoveRecord(WOLFSSL* ssl, w64wrapper epoch, w64wrapper seq) { Dtls13RtxRecord *r, **prevNext; @@ -2706,9 +2719,28 @@ int Dtls13DoScheduledWork(WOLFSSL* ssl) ret = wc_UnLockMutex(&ssl->dtls13Rtx.mutex); #endif if (sendAcks) { - ret = SendDtls13Ack(ssl); - if (ret != 0) - return ret; +#ifdef HAVE_WRITE_DUP + /* The read side cannot encrypt. Transfer the seenRecords list to the + * shared WriteDup struct so the write side sends the ACK instead. */ + if (ssl->dupWrite != NULL && ssl->dupSide == READ_DUP_SIDE) { + struct Dtls13RecordNumber** tail = NULL; + if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) + return BAD_MUTEX_E; + tail = (struct Dtls13RecordNumber**)&ssl->dupWrite->sendAckList; + while (*tail != NULL) + tail = &(*tail)->next; + *tail = ssl->dtls13Rtx.seenRecords; + ssl->dtls13Rtx.seenRecords = NULL; + ssl->dupWrite->sendAcks = 1; + wc_UnLockMutex(&ssl->dupWrite->dupMutex); + } + else +#endif /* HAVE_WRITE_DUP */ + { + ret = SendDtls13Ack(ssl); + if (ret != 0) + return ret; + } } if (ssl->dtls13Rtx.retransmit) { @@ -2824,6 +2856,22 @@ int DoDtls13Ack(WOLFSSL* ssl, const byte* input, word32 inputSize, ato64(ackMessage + i + OPAQUE64_LEN, &seq); WOLFSSL_MSG_EX("epoch %d seq %d", epoch, seq); Dtls13RtxRemoveRecord(ssl, epoch, seq); +#ifdef HAVE_WRITE_DUP + /* Read side: check if this ACK covers the write side's pending KeyUpdate. + * Match on both epoch AND seq to avoid false positives from data records + * in the same epoch (sent while dtls13WaitKeyUpdateAck == 1). */ + if (ssl->dupWrite != NULL && ssl->dupSide == READ_DUP_SIDE) { + if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) + return BAD_MUTEX_E; + if (ssl->dupWrite->keyUpdateWaiting && + w64Equal(epoch, ssl->dupWrite->keyUpdateEpoch) && + w64Equal(seq, ssl->dupWrite->keyUpdateSeq)) { + ssl->dupWrite->keyUpdateAcked = 1; + ssl->dupWrite->keyUpdateWaiting = 0; + } + wc_UnLockMutex(&ssl->dupWrite->dupMutex); + } +#endif /* HAVE_WRITE_DUP */ } /* last client flight was completely acknowledged by the server. Handshake diff --git a/src/internal.c b/src/internal.c index fa42145104..ee83eaf778 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7462,43 +7462,49 @@ int InitHandshakeHashes(WOLFSSL* ssl) return ret; } -void FreeHandshakeHashes(WOLFSSL* ssl) +void Free_HS_Hashes(HS_Hashes* hsHashes, void* heap) { - if (ssl->hsHashes) { + if (hsHashes) { #if !defined(NO_MD5) && !defined(NO_OLD_TLS) - wc_Md5Free(&ssl->hsHashes->hashMd5); + wc_Md5Free(&hsHashes->hashMd5); #endif #if !defined(NO_SHA) && (!defined(NO_OLD_TLS) || \ defined(WOLFSSL_ALLOW_TLS_SHA1)) - wc_ShaFree(&ssl->hsHashes->hashSha); + wc_ShaFree(&hsHashes->hashSha); #endif #ifndef NO_SHA256 - wc_Sha256Free(&ssl->hsHashes->hashSha256); + wc_Sha256Free(&hsHashes->hashSha256); #endif #ifdef WOLFSSL_SHA384 - wc_Sha384Free(&ssl->hsHashes->hashSha384); + wc_Sha384Free(&hsHashes->hashSha384); #endif #ifdef WOLFSSL_SHA512 - wc_Sha512Free(&ssl->hsHashes->hashSha512); + wc_Sha512Free(&hsHashes->hashSha512); #endif #ifdef WOLFSSL_SM3 - wc_Sm3Free(&ssl->hsHashes->hashSm3); + wc_Sm3Free(&hsHashes->hashSm3); #endif #if (defined(HAVE_ED25519) || defined(HAVE_ED448) || \ (defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3))) && \ !defined(WOLFSSL_NO_CLIENT_AUTH) - if (ssl->hsHashes->messages != NULL) { - ForceZero(ssl->hsHashes->messages, (word32)ssl->hsHashes->length); - XFREE(ssl->hsHashes->messages, ssl->heap, DYNAMIC_TYPE_HASHES); - ssl->hsHashes->messages = NULL; + if (hsHashes->messages != NULL) { + ForceZero(hsHashes->messages, (word32)hsHashes->length); + XFREE(hsHashes->messages, heap, DYNAMIC_TYPE_HASHES); + hsHashes->messages = NULL; } #endif - XFREE(ssl->hsHashes, ssl->heap, DYNAMIC_TYPE_HASHES); - ssl->hsHashes = NULL; + XFREE(hsHashes, heap, DYNAMIC_TYPE_HASHES); + hsHashes = NULL; } } +void FreeHandshakeHashes(WOLFSSL* ssl) +{ + Free_HS_Hashes(ssl->hsHashes, ssl->heap); + ssl->hsHashes = NULL; +} + /* copy the hashes from source to a newly made destination return status */ int InitHandshakeHashesAndCopy(WOLFSSL* ssl, HS_Hashes* source, HS_Hashes** destination) @@ -7509,15 +7515,8 @@ int InitHandshakeHashesAndCopy(WOLFSSL* ssl, HS_Hashes* source, return BAD_FUNC_ARG; /* If *destination is already allocated, its constituent hashes need to be - * freed, else they would leak. To keep things simple, we reuse - * FreeHandshakeHashes(), which deallocates *destination. - */ - if (*destination != NULL) { - HS_Hashes* tmp = ssl->hsHashes; - ssl->hsHashes = *destination; - FreeHandshakeHashes(ssl); - ssl->hsHashes = tmp; - } + * freed, else they would leak. */ + Free_HS_Hashes(*destination, ssl->heap); /* allocate handshake hashes */ *destination = (HS_Hashes*)XMALLOC(sizeof(HS_Hashes), ssl->heap, @@ -8065,6 +8064,24 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup) } ssl->options.dtls = ssl->version.major == DTLS_MAJOR; + +#ifdef WOLFSSL_DTLS13 + /* setup 0 (un-protected) epoch */ + ssl->dtls13Epochs[0].isValid = 1; + ssl->dtls13Epochs[0].side = ENCRYPT_AND_DECRYPT_SIDE; + ssl->dtls13EncryptEpoch = &ssl->dtls13Epochs[0]; + ssl->dtls13DecryptEpoch = &ssl->dtls13Epochs[0]; + ssl->options.dtls13SendMoreAcks = WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT; + ssl->dtls13Rtx.rtxRecordTailPtr = &ssl->dtls13Rtx.rtxRecords; + +#ifdef WOLFSSL_RW_THREADED + ret = wc_InitMutex(&ssl->dtls13Rtx.mutex); + if (ret < 0) { + return ret; + } +#endif +#endif /* WOLFSSL_DTLS13 */ + #ifdef HAVE_WRITE_DUP if (writeDup) { /* all done */ @@ -8176,24 +8193,6 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup) } #endif /* HAVE_SECURE_RENEGOTIATION */ - -#ifdef WOLFSSL_DTLS13 - /* setup 0 (un-protected) epoch */ - ssl->dtls13Epochs[0].isValid = 1; - ssl->dtls13Epochs[0].side = ENCRYPT_AND_DECRYPT_SIDE; - ssl->dtls13EncryptEpoch = &ssl->dtls13Epochs[0]; - ssl->dtls13DecryptEpoch = &ssl->dtls13Epochs[0]; - ssl->options.dtls13SendMoreAcks = WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT; - ssl->dtls13Rtx.rtxRecordTailPtr = &ssl->dtls13Rtx.rtxRecords; - -#ifdef WOLFSSL_RW_THREADED - ret = wc_InitMutex(&ssl->dtls13Rtx.mutex); - if (ret < 0) { - return ret; - } -#endif -#endif /* WOLFSSL_DTLS13 */ - #ifdef WOLFSSL_QUIC if (ctx->quic.method) { ret = wolfSSL_set_quic_method(ssl, ctx->quic.method); @@ -26082,6 +26081,10 @@ static int CheckTLS13AEADSendLimit(WOLFSSL* ssl) } #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { + if (ssl->dtls13EncryptEpoch == NULL) { + WOLFSSL_MSG("DTLS 1.3 encrypt epoch not set"); + return BAD_STATE_E; + } seq = ssl->dtls13EncryptEpoch->nextSeqNumber; } else @@ -26312,6 +26315,8 @@ int SendData(WOLFSSL* ssl, const void* data, size_t sz) else { /* advance sent to previous sent + plain size just sent */ sent = ssl->buffers.prevSent + ssl->buffers.plainSz; + ssl->buffers.prevSent = 0; + ssl->buffers.plainSz = 0; WOLFSSL_MSG("sent write buffered data"); if (sent > (word32)sz) { diff --git a/src/ssl.c b/src/ssl.c index 4bcba10b5f..901a79ab2a 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -842,9 +842,28 @@ void FreeWriteDup(WOLFSSL* ssl) } if (doFree) { - WOLFSSL_MSG("Doing WriteDup full free, count to zero"); +#ifdef WOLFSSL_DTLS13 + struct Dtls13RecordNumber* rn = ssl->dupWrite->sendAckList; + while (rn != NULL) { + struct Dtls13RecordNumber* next = rn->next; + XFREE(rn, ssl->heap, DYNAMIC_TYPE_DTLS_MSG); + rn = next; + } +#endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) + Free_HS_Hashes(ssl->dupWrite->postHandshakeHashState, ssl->heap); + { + CertReqCtx* ctx = ssl->dupWrite->postHandshakeCertReqCtx; + while (ctx != NULL) { + CertReqCtx* nxt = ctx->next; + XFREE(ctx, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); + ctx = nxt; + } + } +#endif /* WOLFSSL_TLS13 && WOLFSSL_POST_HANDSHAKE_AUTH */ wc_FreeMutex(&ssl->dupWrite->dupMutex); XFREE(ssl->dupWrite, ssl->heap, DYNAMIC_TYPE_WRITEDUP); + WOLFSSL_MSG("Did WriteDup full free, count to zero"); } } @@ -902,6 +921,40 @@ static int DupSSL(WOLFSSL* dup, WOLFSSL* ssl) /* dup side now owns encrypt/write ciphers */ XMEMSET(&ssl->encrypt, 0, sizeof(Ciphers)); +#ifdef WOLFSSL_TLS13 + if (IsAtLeastTLSv1_3(ssl->version)) { + /* Copy TLS 1.3 application traffic secrets so the write side can + * derive updated keys when wolfSSL_update_keys() is called. */ + XMEMCPY(dup->clientSecret, ssl->clientSecret, SECRET_LEN); + XMEMCPY(dup->serverSecret, ssl->serverSecret, SECRET_LEN); + +#ifdef WOLFSSL_DTLS13 + if (ssl->options.dtls) { + /* Copy epoch array (contains only value types -- safe to memcpy). */ + XMEMCPY(dup->dtls13Epochs, ssl->dtls13Epochs, + sizeof(ssl->dtls13Epochs)); + + /* Re-point dtls13EncryptEpoch into dup's own epoch array. */ + if (ssl->dtls13EncryptEpoch != NULL) { + dup->dtls13EncryptEpoch = + &dup->dtls13Epochs[ssl->dtls13EncryptEpoch - + ssl->dtls13Epochs]; + } + + /* Copy current write epoch number. */ + dup->dtls13Epoch = ssl->dtls13Epoch; + + /* Transfer record-number encryption cipher ownership to dup. */ + XMEMCPY(&dup->dtlsRecordNumberEncrypt, + &ssl->dtlsRecordNumberEncrypt, sizeof(RecordNumberCiphers)); + XMEMSET(&ssl->dtlsRecordNumberEncrypt, + 0, sizeof(RecordNumberCiphers)); + } +#endif /* WOLFSSL_DTLS13 */ + } +#endif /* WOLFSSL_TLS13 */ + + dup->IOCB_WriteCtx = ssl->IOCB_WriteCtx; dup->CBIOSend = ssl->CBIOSend; #ifdef OPENSSL_EXTRA @@ -2509,7 +2562,7 @@ int wolfSSL_GetDhKey_Sz(WOLFSSL* ssl) static int wolfSSL_write_internal(WOLFSSL* ssl, const void* data, size_t sz) { - int ret; + int ret = 0; WOLFSSL_ENTER("wolfSSL_write"); @@ -2524,32 +2577,143 @@ static int wolfSSL_write_internal(WOLFSSL* ssl, const void* data, size_t sz) #endif #ifdef HAVE_WRITE_DUP - { /* local variable scope */ + if (ssl->dupSide == READ_DUP_SIDE) { + WOLFSSL_MSG("Read dup side cannot write"); + return WRITE_DUP_WRITE_E; + } + /* Only enter special dupWrite logic when error is cleared. This will help + * with handling async data and other edge case errors. */ + if (ssl->dupWrite != NULL && ssl->error == 0) { int dupErr = 0; /* local copy */ + /* Lock ssl->dupWrite to gather what needs to be done. */ + if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) + return BAD_MUTEX_E; + dupErr = ssl->dupWrite->dupErr; +#ifdef WOLFSSL_TLS13 + if (IsAtLeastTLSv1_3(ssl->version)) { + /* TLS 1.3: if the read side received a KeyUpdate(update_requested) + * it cannot respond; send the response from here. */ + ssl->keys.keyUpdateRespond |= ssl->dupWrite->keyUpdateRespond; + ssl->dupWrite->keyUpdateRespond = 0; +#ifdef WOLFSSL_POST_HANDSHAKE_AUTH + ssl->postHandshakeAuthPending |= + ssl->dupWrite->postHandshakeAuthPending; + ssl->dupWrite->postHandshakeAuthPending = 0; + if (ssl->postHandshakeAuthPending) { + /* Take ownership of the delegated auth state. */ + CertReqCtx** tail = &ssl->dupWrite->postHandshakeCertReqCtx; + while (*tail != NULL) + tail = &(*tail)->next; + *tail = ssl->certReqCtx; + ssl->certReqCtx = ssl->dupWrite->postHandshakeCertReqCtx; + ssl->dupWrite->postHandshakeCertReqCtx = NULL; + FreeHandshakeHashes(ssl); + ssl->hsHashes = ssl->dupWrite->postHandshakeHashState; + ssl->dupWrite->postHandshakeHashState = NULL; + ssl->options.sendVerify = ssl->dupWrite->postHandshakeSendVerify; + ssl->options.sigAlgo = ssl->dupWrite->postHandshakeSigAlgo; + ssl->options.hashAlgo = ssl->dupWrite->postHandshakeHashAlgo; + } +#endif /* WOLFSSL_POST_HANDSHAKE_AUTH */ +#ifdef WOLFSSL_DTLS13 + if (ssl->options.dtls) { + /* Schedule key update to be sent. */ + if (ssl->keys.keyUpdateRespond) + ssl->dtls13DoKeyUpdate = 1; + + /* Copy over ACKs */ + ssl->dtls13Rtx.sendAcks |= ssl->dupWrite->sendAcks; + if (ssl->dupWrite->sendAcks) { + /* Insert each record number so the + * ACK message is properly ordered. */ + struct Dtls13RecordNumber* rn; + for (rn = ssl->dupWrite->sendAckList; rn != NULL; + rn = rn->next) { + ret = Dtls13RtxAddAck(ssl, rn->epoch, rn->seq); + if (ret != 0) + break; + } + /* Clear only on success so no ACKs get dropped */ + if (ret == 0) { + rn = ssl->dupWrite->sendAckList; + ssl->dupWrite->sendAckList = NULL; + ssl->dupWrite->sendAcks = 0; + while (rn != NULL) { + struct Dtls13RecordNumber* next = rn->next; + XFREE(rn, ssl->heap, DYNAMIC_TYPE_DTLS_MSG); + rn = next; + } + } + } - ret = 0; - - if (ssl->dupWrite && ssl->dupSide == READ_DUP_SIDE) { - WOLFSSL_MSG("Read dup side cannot write"); - return WRITE_DUP_WRITE_E; - } - if (ssl->dupWrite) { - if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) { - return BAD_MUTEX_E; + /* Remove KeyUpdate record from RTX list. */ + if (ssl->dupWrite->keyUpdateAcked) { + Dtls13RtxRemoveRecord(ssl, ssl->dupWrite->keyUpdateEpoch, + ssl->dupWrite->keyUpdateSeq); + } + /* Store if KeyUpdate was ACKed. */ + ssl->dtls13KeyUpdateAcked |= ssl->dupWrite->keyUpdateAcked; + ssl->dupWrite->keyUpdateAcked = 0; } - dupErr = ssl->dupWrite->dupErr; - ret = wc_UnLockMutex(&ssl->dupWrite->dupMutex); +#endif /* WOLFSSL_DTLS13 */ } +#endif /* WOLFSSL_TLS13 */ + wc_UnLockMutex(&ssl->dupWrite->dupMutex); - if (ret != 0) { - ssl->error = ret; /* high priority fatal error */ - return WOLFSSL_FATAL_ERROR; - } if (dupErr != 0) { WOLFSSL_MSG("Write dup error from other side"); ssl->error = dupErr; return WOLFSSL_FATAL_ERROR; } + if (ret != 0) { + ssl->error = ret; + return WOLFSSL_FATAL_ERROR; + } + + +#ifdef WOLFSSL_TLS13 + if (IsAtLeastTLSv1_3(ssl->version)) { +#ifdef WOLFSSL_POST_HANDSHAKE_AUTH + /* Read side received a CertificateRequest but couldn't write; + * send Certificate+CertificateVerify+Finished from the write side. */ + if (ssl->postHandshakeAuthPending) { + /* reset handshake states */ + ssl->postHandshakeAuthPending = 0; + ssl->options.clientState = CLIENT_HELLO_COMPLETE; + ssl->options.connectState = FIRST_REPLY_DONE; + ssl->options.handShakeState = CLIENT_HELLO_COMPLETE; + ssl->options.processReply = 0; /* doProcessInit */ + if (wolfSSL_connect_TLSv13(ssl) != WOLFSSL_SUCCESS) { + if (ssl->error != WC_NO_ERR_TRACE(WANT_WRITE) && + ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E)) { + WOLFSSL_MSG("Post-handshake auth send failed"); + ssl->error = POST_HAND_AUTH_ERROR; + } + return WOLFSSL_FATAL_ERROR; + } + } +#endif /* WOLFSSL_POST_HANDSHAKE_AUTH */ +#ifdef WOLFSSL_DTLS13 + if (ssl->options.dtls) { + if (ssl->dtls13KeyUpdateAcked) + ret = DoDtls13KeyUpdateAck(ssl); + ssl->dtls13KeyUpdateAcked = 0; + if (ret == 0) + ret = Dtls13DoScheduledWork(ssl); + } + else +#endif /* WOLFSSL_DTLS13 */ + if (ssl->keys.keyUpdateRespond) /* cleared in SendTls13KeyUpdate */ + ret = Tls13UpdateKeys(ssl); + if (ret != 0) { + ssl->error = ret; + return WOLFSSL_FATAL_ERROR; + } + /* WANT_WRITE is safe to clear. Data is buffered in output buffer + * or in DTLS RTX queue */ + ret = 0; + } +#endif /* WOLFSSL_TLS13 */ } #endif diff --git a/src/tls13.c b/src/tls13.c index 50192f763c..21fb5f569a 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -11819,6 +11819,18 @@ static int DoTls13KeyUpdate(WOLFSSL* ssl, const byte* input, word32* inOutIdx, } #endif /* WOLFSSL_DTLS13 */ +#if defined(HAVE_WRITE_DUP) && defined(WOLFSSL_TLS13) + /* Read side cannot write; delegate the response to the write side. */ + if (ssl->dupWrite != NULL && ssl->dupSide == READ_DUP_SIDE) { + if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) + return BAD_MUTEX_E; + ssl->dupWrite->keyUpdateRespond = 1; + wc_UnLockMutex(&ssl->dupWrite->dupMutex); + ssl->keys.keyUpdateRespond = 0; + return 0; + } +#endif /* HAVE_WRITE_DUP && WOLFSSL_TLS13 */ + #ifndef WOLFSSL_RW_THREADED return SendTls13KeyUpdate(ssl); #else @@ -13286,25 +13298,61 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, #ifdef WOLFSSL_POST_HANDSHAKE_AUTH if (type == certificate_request && ssl->options.handShakeState == HANDSHAKE_DONE) { - /* reset handshake states */ - ssl->options.clientState = CLIENT_HELLO_COMPLETE; - ssl->options.connectState = FIRST_REPLY_DONE; - ssl->options.handShakeState = CLIENT_HELLO_COMPLETE; - ssl->options.processReply = 0; /* doProcessInit */ - - /* - DTLSv1.3 note: We can't reset serverState to - SERVER_FINISHED_COMPLETE with the goal that this connect - blocks until the cert/cert_verify/finished flight gets ACKed - by the server. The problem is that we will invoke - ProcessReplyEx() in that case, but we came here from - ProcessReplyEx() and it is not re-entrant safe (the input - buffer would still have the certificate_request message). */ - - if (wolfSSL_connect_TLSv13(ssl) != WOLFSSL_SUCCESS) { - ret = ssl->error; - if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) - ret = POST_HAND_AUTH_ERROR; +#if defined(HAVE_WRITE_DUP) + /* Read side cannot write; delegate the cert response to the + * write side by saving auth state in the shared WriteDup. */ + if (ssl->dupSide == READ_DUP_SIDE) { + if (ssl->dupWrite == NULL) + return BAD_STATE_E; + if (wc_LockMutex(&ssl->dupWrite->dupMutex) != 0) + return BAD_MUTEX_E; + /* Copy the current transcript so the write side can + * compute the correct Finished MAC. */ + ret = InitHandshakeHashesAndCopy(ssl, ssl->hsHashes, + &ssl->dupWrite->postHandshakeHashState); + if (ret == 0) { + /* Copy the cert request context. */ + CertReqCtx** tail = &ssl->certReqCtx; + while (*tail != NULL) + tail = &(*tail)->next; + *tail = ssl->dupWrite->postHandshakeCertReqCtx; + ssl->dupWrite->postHandshakeCertReqCtx = ssl->certReqCtx; + ssl->certReqCtx = NULL; + ssl->dupWrite->postHandshakeSendVerify = + ssl->options.sendVerify; + ssl->dupWrite->postHandshakeSigAlgo = + ssl->options.sigAlgo; + ssl->dupWrite->postHandshakeHashAlgo = + ssl->options.hashAlgo; + ssl->dupWrite->postHandshakeAuthPending = 1; + } + wc_UnLockMutex(&ssl->dupWrite->dupMutex); + /* Leave ssl->options unchanged: read side must not reset + * its states or call wolfSSL_connect_TLSv13. */ + } + else +#endif /* HAVE_WRITE_DUP */ + { + /* reset handshake states */ + ssl->options.clientState = CLIENT_HELLO_COMPLETE; + ssl->options.connectState = FIRST_REPLY_DONE; + ssl->options.handShakeState = CLIENT_HELLO_COMPLETE; + ssl->options.processReply = 0; /* doProcessInit */ + + /* + DTLSv1.3 note: We can't reset serverState to + SERVER_FINISHED_COMPLETE with the goal that this connect + blocks until the cert/cert_verify/finished flight gets ACKed + by the server. The problem is that we will invoke + ProcessReplyEx() in that case, but we came here from + ProcessReplyEx() and it is not re-entrant safe (the input + buffer would still have the certificate_request message). */ + + if (wolfSSL_connect_TLSv13(ssl) != WOLFSSL_SUCCESS) { + ret = ssl->error; + if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) + ret = POST_HAND_AUTH_ERROR; + } } } #endif diff --git a/tests/api.c b/tests/api.c index 063f358bc3..c0d52bdced 100644 --- a/tests/api.c +++ b/tests/api.c @@ -32795,9 +32795,12 @@ static int test_write_dup(void) { EXPECT_DECLS; #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(HAVE_WRITE_DUP) - size_t i, j; + size_t i, j, k; char hiWorld[] = "dup message"; char readData[sizeof(hiWorld) + 5]; +#ifdef WOLFSSL_TLS13 + int required; +#endif struct { method_provider client_meth; method_provider server_meth; @@ -32806,9 +32809,11 @@ static int test_write_dup(void) } methods[] = { #ifndef WOLFSSL_NO_TLS12 {wolfTLSv1_2_client_method, wolfTLSv1_2_server_method, "TLS 1.2", WOLFSSL_TLSV1_2}, + {wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, "DTLS 1.2", WOLFSSL_TLSV1_2}, #endif #ifdef WOLFSSL_TLS13 {wolfTLSv1_3_client_method, wolfTLSv1_3_server_method, "TLS 1.3", WOLFSSL_TLSV1_3}, + {wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method, "DTLS 1.3", WOLFSSL_TLSV1_3}, #endif }; struct { @@ -32874,6 +32879,18 @@ static int test_write_dup(void) #endif }; +/* Macro capturing local variables for concise bidirectional data exchange. */ +#define EXCHANGE_DATA do { \ + ExpectIntEQ(wolfSSL_write(ssl_s, hiWorld, sizeof(hiWorld)), \ + sizeof(hiWorld)); \ + ExpectIntEQ(wolfSSL_read(ssl_c, readData, sizeof(readData)), \ + sizeof(hiWorld)); \ + ExpectIntEQ(wolfSSL_write(ssl_c2, hiWorld, sizeof(hiWorld)), \ + sizeof(hiWorld)); \ + ExpectIntEQ(wolfSSL_read(ssl_s, readData, sizeof(readData)), \ + sizeof(hiWorld)); \ +} while (0) + for (i = 0; i < XELEM_CNT(methods); i++) { for (j = 0; j < XELEM_CNT(ciphers) && !EXPECT_FAIL(); j++) { struct test_memio_ctx test_ctx; @@ -32896,23 +32913,77 @@ static int test_write_dup(void) ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, methods[i].client_meth, methods[i].server_meth), 0); +#ifdef WOLFSSL_POST_HANDSHAKE_AUTH + if (methods[i].version == WOLFSSL_TLSV1_3) { + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_allow_post_handshake_auth(ssl_c), 0); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, caCertFile, + NULL), WOLFSSL_SUCCESS); + } +#endif ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); ExpectNotNull(ssl_c2 = wolfSSL_write_dup(ssl_c)); ExpectIntEQ(wolfSSL_write(ssl_c, hiWorld, sizeof(hiWorld)), WC_NO_ERR_TRACE(WRITE_DUP_WRITE_E)); - ExpectIntEQ(wolfSSL_write(ssl_c2, hiWorld, sizeof(hiWorld)), - sizeof(hiWorld)); - - ExpectIntEQ(wolfSSL_read(ssl_s, readData, sizeof(readData)), - sizeof(hiWorld)); - ExpectIntEQ(wolfSSL_write(ssl_s, hiWorld, sizeof(hiWorld)), - sizeof(hiWorld)); - + EXCHANGE_DATA; ExpectIntEQ(wolfSSL_read(ssl_c2, readData, sizeof(readData)), WC_NO_ERR_TRACE(WRITE_DUP_READ_E)); - ExpectIntEQ(wolfSSL_read(ssl_c, readData, sizeof(readData)), - sizeof(hiWorld)); +#ifdef WOLFSSL_DTLS13 + /* The initial EXCHANGE_DATA above processes the post-handshake + * NewSessionTicket (S2C part delegates ACK) and triggers the + * ACK (C2S part sends it). Verify it completed. */ + if (methods[i].client_meth == wolfDTLSv1_3_client_method) { + ExpectNotNull(ssl_c->dupWrite); + ExpectIntEQ(ssl_c->dupWrite->sendAcks, 0); + ExpectNull(ssl_s->dtls13Rtx.rtxRecords); + } +#endif + for (k = 0; k < 10; k++) + EXCHANGE_DATA; + +#ifdef WOLFSSL_TLS13 + if (methods[i].version == WOLFSSL_TLSV1_3) { + /* Client-initiated key update. */ + ExpectIntEQ(wolfSSL_update_keys(ssl_c2), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_key_update_response(ssl_c2, &required), 0); + ExpectIntEQ(required, 1); + for (k = 0; k < 10; k++) + EXCHANGE_DATA; + } + + if (methods[i].version == WOLFSSL_TLSV1_3) { + /* Server-initiated key update. */ + ExpectIntEQ(wolfSSL_update_keys(ssl_s), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_key_update_response(ssl_s, &required), 0); + ExpectIntEQ(required, 1); + for (k = 0; k < 10; k++) + EXCHANGE_DATA; + ExpectIntEQ(wolfSSL_key_update_response(ssl_s, &required), 0); + ExpectIntEQ(required, 0); + } +#endif /* WOLFSSL_TLS13 */ + +#ifdef WOLFSSL_POST_HANDSHAKE_AUTH + if (methods[i].version == WOLFSSL_TLSV1_3) { + WOLFSSL_X509_CHAIN* chain = NULL; + ExpectNotNull(chain = wolfSSL_get_peer_chain(ssl_s)); + ExpectIntEQ(wolfSSL_get_chain_count(chain), 0); + ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS); + EXCHANGE_DATA; + ExpectNotNull(chain = wolfSSL_get_peer_chain(ssl_s)); + ExpectIntEQ(wolfSSL_get_chain_count(chain), 1); + for (k = 0; k < 10; k++) + EXCHANGE_DATA; + } +#endif if (EXPECT_SUCCESS()) printf("ok\n"); @@ -32926,6 +32997,245 @@ static int test_write_dup(void) wolfSSL_CTX_free(ctx_s); } } +#undef EXCHANGE_DATA +#endif + return EXPECT_RESULT(); +} + +static int test_write_dup_want_write(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(HAVE_WRITE_DUP) + size_t i, k; + char hiWorld[] = "dup message"; + char readData[sizeof(hiWorld) + 5]; +#ifdef WOLFSSL_TLS13 + int required; +#endif + struct { + method_provider client_meth; + method_provider server_meth; + const char* version_name; + int version; + } methods[] = { +#ifndef WOLFSSL_NO_TLS12 + {wolfTLSv1_2_client_method, wolfTLSv1_2_server_method, "TLS 1.2", WOLFSSL_TLSV1_2}, + {wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, "DTLS 1.2", WOLFSSL_TLSV1_2}, +#endif +#ifdef WOLFSSL_TLS13 + {wolfTLSv1_3_client_method, wolfTLSv1_3_server_method, "TLS 1.3", WOLFSSL_TLSV1_3}, + {wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method, "DTLS 1.3", WOLFSSL_TLSV1_3}, +#endif + }; + +/* Same as test_write_dup's EXCHANGE_DATA but every client (write-dup side) + * write is preceded by a simulated WANT_WRITE that must be retried. */ +#define EXCHANGE_DATA do { \ + ExpectIntEQ(wolfSSL_write(ssl_s, hiWorld, sizeof(hiWorld)), \ + sizeof(hiWorld)); \ + ExpectIntEQ(wolfSSL_read(ssl_c, readData, sizeof(readData)), \ + sizeof(hiWorld)); \ + test_memio_simulate_want_write(&test_ctx, 1, 1); \ + ExpectIntEQ(wolfSSL_write(ssl_c2, hiWorld, sizeof(hiWorld)), \ + WOLFSSL_FATAL_ERROR); \ + ExpectIntEQ(wolfSSL_get_error(ssl_c2, WOLFSSL_FATAL_ERROR), \ + WOLFSSL_ERROR_WANT_WRITE); \ + test_memio_simulate_want_write(&test_ctx, 1, 0); \ + ExpectIntEQ(wolfSSL_write(ssl_c2, hiWorld, sizeof(hiWorld)), \ + sizeof(hiWorld)); \ + ExpectIntEQ(wolfSSL_read(ssl_s, readData, sizeof(readData)), \ + sizeof(hiWorld)); \ +} while (0) + + for (i = 0; i < XELEM_CNT(methods) && !EXPECT_FAIL(); i++) { + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL *ssl_c2 = NULL; + + if (i == 0) + printf("\n"); + printf("Testing write_dup WANT_WRITE %s... ", + methods[i].version_name); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + methods[i].client_meth, methods[i].server_meth), 0); +#ifdef WOLFSSL_POST_HANDSHAKE_AUTH + if (methods[i].version == WOLFSSL_TLSV1_3) { + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_allow_post_handshake_auth(ssl_c), 0); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, caCertFile, + NULL), WOLFSSL_SUCCESS); + } +#endif + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + ExpectNotNull(ssl_c2 = wolfSSL_write_dup(ssl_c)); + + for (k = 0; k < 10 && !EXPECT_FAIL(); k++) + EXCHANGE_DATA; + +#ifdef WOLFSSL_TLS13 + if (methods[i].version == WOLFSSL_TLSV1_3) { + /* Client-initiated key update with WANT_WRITE. */ + ExpectIntEQ(wolfSSL_update_keys(ssl_c2), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_key_update_response(ssl_c2, &required), 0); + ExpectIntEQ(required, 1); + for (k = 0; k < 10 && !EXPECT_FAIL(); k++) + EXCHANGE_DATA; + + /* Server-initiated key update: response goes through write side + * with WANT_WRITE. */ + ExpectIntEQ(wolfSSL_update_keys(ssl_s), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_key_update_response(ssl_s, &required), 0); + ExpectIntEQ(required, 1); + for (k = 0; k < 10 && !EXPECT_FAIL(); k++) + EXCHANGE_DATA; + ExpectIntEQ(wolfSSL_key_update_response(ssl_s, &required), 0); + ExpectIntEQ(required, 0); + } +#endif /* WOLFSSL_TLS13 */ + +#ifdef WOLFSSL_POST_HANDSHAKE_AUTH + if (methods[i].version == WOLFSSL_TLSV1_3) { + WOLFSSL_X509_CHAIN* chain = NULL; + ExpectNotNull(chain = wolfSSL_get_peer_chain(ssl_s)); + ExpectIntEQ(wolfSSL_get_chain_count(chain), 0); + ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS); + EXCHANGE_DATA; + ExpectNotNull(chain = wolfSSL_get_peer_chain(ssl_s)); + ExpectIntEQ(wolfSSL_get_chain_count(chain), 1); + for (k = 0; k < 10 && !EXPECT_FAIL(); k++) + EXCHANGE_DATA; + } +#endif /* WOLFSSL_POST_HANDSHAKE_AUTH */ + + if (EXPECT_SUCCESS()) + printf("ok\n"); + else + printf("failed\n"); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_c2); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + } +#undef EXCHANGE_DATA +#endif + return EXPECT_RESULT(); +} + +/* Simultaneous key update and cert req */ +static int test_write_dup_want_write_simul(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(HAVE_WRITE_DUP) && \ + defined(WOLFSSL_POST_HANDSHAKE_AUTH) && defined(WOLFSSL_TLS13) + size_t i, k; + char hiWorld[] = "dup message"; + char readData[sizeof(hiWorld) + 5]; + int required; + struct { + method_provider client_meth; + method_provider server_meth; + const char* version_name; + } methods[] = { +#ifdef WOLFSSL_TLS13 + {wolfTLSv1_3_client_method, wolfTLSv1_3_server_method, "TLS 1.3"}, + {wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method, "DTLS 1.3"}, +#endif + }; + +/* Same as test_write_dup's EXCHANGE_DATA but every client (write-dup side) + * write is preceded by a simulated WANT_WRITE that must be retried. */ +#define EXCHANGE_DATA do { \ + ExpectIntEQ(wolfSSL_write(ssl_s, hiWorld, sizeof(hiWorld)), \ + sizeof(hiWorld)); \ + ExpectIntEQ(wolfSSL_read(ssl_c, readData, sizeof(readData)), \ + sizeof(hiWorld)); \ + test_memio_simulate_want_write(&test_ctx, 1, 1); \ + ExpectIntEQ(wolfSSL_write(ssl_c2, hiWorld, sizeof(hiWorld)), \ + WOLFSSL_FATAL_ERROR); \ + ExpectIntEQ(wolfSSL_get_error(ssl_c2, WOLFSSL_FATAL_ERROR), \ + WOLFSSL_ERROR_WANT_WRITE); \ + test_memio_simulate_want_write(&test_ctx, 1, 0); \ + ExpectIntEQ(wolfSSL_write(ssl_c2, hiWorld, sizeof(hiWorld)), \ + sizeof(hiWorld)); \ + ExpectIntEQ(wolfSSL_read(ssl_s, readData, sizeof(readData)), \ + sizeof(hiWorld)); \ +} while (0) + + for (i = 0; i < XELEM_CNT(methods) && !EXPECT_FAIL(); i++) { + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL *ssl_c2 = NULL; + WOLFSSL_X509_CHAIN* chain = NULL; + + if (i == 0) + printf("\n"); + printf("Testing write_dup WANT_WRITE %s... ", + methods[i].version_name); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + methods[i].client_meth, methods[i].server_meth), 0); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_allow_post_handshake_auth(ssl_c), 0); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, caCertFile, + NULL), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + ExpectNotNull(ssl_c2 = wolfSSL_write_dup(ssl_c)); + + for (k = 0; k < 10 && !EXPECT_FAIL(); k++) + EXCHANGE_DATA; + + /* Server-initiated key update and cert req: response goes through + * write side with WANT_WRITE. */ + ExpectNotNull(chain = wolfSSL_get_peer_chain(ssl_s)); + ExpectIntEQ(wolfSSL_get_chain_count(chain), 0); + ExpectIntEQ(wolfSSL_update_keys(ssl_s), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_key_update_response(ssl_s, &required), 0); + ExpectIntEQ(required, 1); + ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS); + for (k = 0; k < 10 && !EXPECT_FAIL(); k++) + EXCHANGE_DATA; + ExpectNotNull(chain = wolfSSL_get_peer_chain(ssl_s)); + ExpectIntEQ(wolfSSL_get_chain_count(chain), 1); + ExpectIntEQ(wolfSSL_key_update_response(ssl_s, &required), 0); + ExpectIntEQ(required, 0); + + if (EXPECT_SUCCESS()) + printf("ok\n"); + else + printf("failed\n"); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_c2); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + } +#undef EXCHANGE_DATA #endif return EXPECT_RESULT(); } @@ -34600,6 +34910,8 @@ TEST_CASE testCases[] = { TEST_DTLS_DECLS, TEST_DECL(test_tls_multi_handshakes_one_record), TEST_DECL(test_write_dup), + TEST_DECL(test_write_dup_want_write), + TEST_DECL(test_write_dup_want_write_simul), TEST_DECL(test_read_write_hs), TEST_DECL(test_get_signature_nid), #ifndef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 4e91e95e18..f18dabc07d 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -5770,9 +5770,47 @@ typedef struct BuildMsgArgs { #define READ_DUP_SIDE 2 typedef struct WriteDup { - wolfSSL_Mutex dupMutex; /* reference count mutex */ + wolfSSL_Mutex dupMutex; /* field access mutex */ int dupCount; /* reference count */ int dupErr; /* under dupMutex, pass to other side */ +#ifdef WOLFSSL_DTLS13 + struct Dtls13RecordNumber* sendAckList; /* ownership transferred */ + /* Key update ACK tracking: write side stores the (epoch, seq) of its + * in-flight KeyUpdate; read side sets keyUpdateAcked when the ACK for + * that exact record arrives. Both epoch and seq are checked to avoid + * false positives from data records in the same epoch. */ + w64wrapper keyUpdateEpoch; /* epoch of the KeyUpdate */ + w64wrapper keyUpdateSeq; /* seq num of the KeyUpdate */ +#endif /* WOLFSSL_DTLS13 */ +#ifdef WOLFSSL_TLS13 +#ifdef WOLFSSL_POST_HANDSHAKE_AUTH + /* Post-handshake certificate request delegation: the read side received + * a CertificateRequest but cannot write; it saves state here and the + * write side sends Certificate+CertificateVerify+Finished. */ + struct HS_Hashes* postHandshakeHashState; /* transcript at CR time */ + struct CertReqCtx* postHandshakeCertReqCtx; /* context from CR */ + byte postHandshakeSendVerify; /* ssl->options.sendVerify */ + byte postHandshakeSigAlgo; /* ssl->options.sigAlgo */ + byte postHandshakeHashAlgo; /* ssl->options.hashAlgo */ +#endif /* WOLFSSL_POST_HANDSHAKE_AUTH */ +#endif /* WOLFSSL_TLS13 */ + + /* Flags */ +#ifdef WOLFSSL_DTLS13 + WC_BITFIELD keyUpdateWaiting:1; /* write side has an unACKed KeyUpdate */ + WC_BITFIELD keyUpdateAcked:1; /* read side confirmed the ACK arrived */ + /* DTLS 1.3: read side cannot write, so it passes ACK work to the + * write side. */ + WC_BITFIELD sendAcks:1; +#endif /* WOLFSSL_DTLS13 */ +#ifdef WOLFSSL_TLS13 + /* TLS 1.3 (and DTLS 1.3): read side received a KeyUpdate(update_requested) + * but cannot send the response; write side handles it. */ + WC_BITFIELD keyUpdateRespond:1; /* write side must send a KeyUpdate response */ +#ifdef WOLFSSL_POST_HANDSHAKE_AUTH + WC_BITFIELD postHandshakeAuthPending:1; /* write side must respond */ +#endif /* WOLFSSL_POST_HANDSHAKE_AUTH */ +#endif /* WOLFSSL_TLS13 */ } WriteDup; WOLFSSL_LOCAL void FreeWriteDup(WOLFSSL* ssl); @@ -6177,6 +6215,9 @@ struct WOLFSSL { byte dtls13SendingFragments:1; byte dtls13SendingAckOrRtx; byte dtls13FastTimeout:1; +#ifdef HAVE_WRITE_DUP + byte dtls13KeyUpdateAcked:1; +#endif byte dtls13WaitKeyUpdateAck; byte dtls13DoKeyUpdate; word32 dtls13MessageLength; @@ -6453,6 +6494,13 @@ struct WOLFSSL { defined(WOLFSSL_HARDEN_TLS) && !defined(WOLFSSL_HARDEN_TLS_NO_SCR_CHECK) WC_BITFIELD scr_check_enabled:1; /* enable/disable SCR check */ #endif +#ifdef HAVE_WRITE_DUP +#ifdef WOLFSSL_TLS13 +#ifdef WOLFSSL_POST_HANDSHAKE_AUTH + WC_BITFIELD postHandshakeAuthPending:1; +#endif +#endif +#endif }; #if defined(WOLFSSL_SYS_CRYPTO_POLICY) @@ -7087,6 +7135,7 @@ WOLFSSL_LOCAL int SetDhExternal(WOLFSSL_DH *dh); #endif WOLFSSL_LOCAL int InitHandshakeHashes(WOLFSSL* ssl); +WOLFSSL_LOCAL void Free_HS_Hashes(HS_Hashes* hsHashes, void* heap); WOLFSSL_LOCAL void FreeHandshakeHashes(WOLFSSL* ssl); WOLFSSL_LOCAL int InitHandshakeHashesAndCopy(WOLFSSL* ssl, HS_Hashes* source, HS_Hashes** destination); @@ -7186,6 +7235,8 @@ WOLFSSL_LOCAL int Dtls13SetEpochKeys(WOLFSSL* ssl, w64wrapper epochNumber, enum encrypt_side side); WOLFSSL_LOCAL int Dtls13GetSeq(WOLFSSL* ssl, int order, word32* seq, byte increment); +WOLFSSL_LOCAL void Dtls13RtxRemoveRecord(WOLFSSL* ssl, w64wrapper epoch, + w64wrapper seq); WOLFSSL_LOCAL int Dtls13DoScheduledWork(WOLFSSL* ssl); WOLFSSL_LOCAL int Dtls13DeriveSnKeys(WOLFSSL* ssl, int provision); WOLFSSL_LOCAL int Dtls13SetRecordNumberKeys(WOLFSSL* ssl,