Skip to content

Commit 24aa260

Browse files
committed
DTLS 1.3 write dup support
- Copy TLS 1.3 traffic secrets and DTLS 1.3 epoch/cipher state to the write-dup side in DupSSL so key updates can be performed. - Delegate KeyUpdate responses from the read side to the write side via the shared WriteDup struct, for both peer-initiated and local key updates. - Delegate DTLS 1.3 ACK sending from the read side to the write side. - Track DTLS 1.3 KeyUpdate ACKs: write side records the in-flight KeyUpdate epoch/seq, read side sets keyUpdateAcked when the matching ACK arrives. - Delegate post-handshake certificate authentication (CertificateRequest processing) from the read side to the write side, transferring transcript hashes, cert context, and signature parameters. - Reset prevSent/plainSz to prevent stale values from SendData to think that data was already sent. - Refactor FreeHandshakeHashes into Free_HS_Hashes for reuse. - Move DTLS 1.3 epoch initialization earlier in InitSSL so the write-dup early-return path has valid epoch state. - Add tests for write dup with all protocol versions, key update, post-handshake auth, and WANT_WRITE recovery. - Add --enable-all --enable-writedup to CI os-check matrix.
1 parent 5b9d0a1 commit 24aa260

7 files changed

Lines changed: 724 additions & 95 deletions

File tree

.github/workflows/os-check.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ jobs:
9898
'--enable-curve25519=nonblock --enable-ecc=nonblock --enable-sp=yes,nonblock CPPFLAGS="-DWOLFSSL_PUBLIC_MP -DWOLFSSL_DEBUG_NONBLOCK"',
9999
'--enable-certreq --enable-certext --enable-certgen --disable-secure-renegotiation-info CPPFLAGS="-DNO_TLS"',
100100
'--enable-ocsp --enable-ocsp-responder --enable-ocspstapling CPPFLAGS="-DWOLFSSL_NONBLOCK_OCSP" --enable-maxfragment',
101+
'--enable-all --enable-writedup',
101102
]
102103
name: make check
103104
if: github.repository_owner == 'wolfssl'

src/dtls13.c

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2034,8 +2034,21 @@ int Dtls13HandshakeSend(WOLFSSL* ssl, byte* message, word16 outputSize,
20342034
maxFrag = wolfssl_local_GetMaxPlaintextSize(ssl);
20352035
maxLen = length;
20362036

2037-
if (handshakeType == key_update)
2037+
if (handshakeType == key_update) {
20382038
ssl->dtls13WaitKeyUpdateAck = 1;
2039+
#ifdef HAVE_WRITE_DUP
2040+
/* Notify the read side so it can watch for the ACK on our behalf. */
2041+
if (ssl->dupWrite != NULL && ssl->dupSide == WRITE_DUP_SIDE) {
2042+
if (wc_LockMutex(&ssl->dupWrite->dupMutex) == 0) {
2043+
ssl->dupWrite->keyUpdateEpoch = ssl->dtls13Epoch;
2044+
ssl->dupWrite->keyUpdateSeq =
2045+
ssl->dtls13EncryptEpoch->nextSeqNumber;
2046+
ssl->dupWrite->keyUpdateWaiting = 1;
2047+
wc_UnLockMutex(&ssl->dupWrite->dupMutex);
2048+
}
2049+
}
2050+
#endif /* HAVE_WRITE_DUP */
2051+
}
20392052

20402053
if (maxLen < maxFrag) {
20412054
ret = Dtls13SendOneFragmentRtx(ssl, handshakeType, outputSize, message,
@@ -2656,7 +2669,7 @@ static void Dtls13PrintRtxRecord(Dtls13RtxRecord* r)
26562669
}
26572670
#endif /* WOLFSSL_DEBUG_TLS */
26582671

2659-
static void Dtls13RtxRemoveRecord(WOLFSSL* ssl, w64wrapper epoch,
2672+
void Dtls13RtxRemoveRecord(WOLFSSL* ssl, w64wrapper epoch,
26602673
w64wrapper seq)
26612674
{
26622675
Dtls13RtxRecord *r, **prevNext;
@@ -2706,9 +2719,28 @@ int Dtls13DoScheduledWork(WOLFSSL* ssl)
27062719
ret = wc_UnLockMutex(&ssl->dtls13Rtx.mutex);
27072720
#endif
27082721
if (sendAcks) {
2709-
ret = SendDtls13Ack(ssl);
2710-
if (ret != 0)
2711-
return ret;
2722+
#ifdef HAVE_WRITE_DUP
2723+
/* The read side cannot encrypt. Transfer the seenRecords list to the
2724+
* shared WriteDup struct so the write side sends the ACK instead. */
2725+
if (ssl->dupWrite != NULL && ssl->dupSide == READ_DUP_SIDE) {
2726+
if (wc_LockMutex(&ssl->dupWrite->dupMutex) == 0) {
2727+
struct Dtls13RecordNumber** tail =
2728+
(struct Dtls13RecordNumber**)&ssl->dupWrite->sendAckList;
2729+
while (*tail != NULL)
2730+
tail = &(*tail)->next;
2731+
*tail = ssl->dtls13Rtx.seenRecords;
2732+
ssl->dtls13Rtx.seenRecords = NULL;
2733+
ssl->dupWrite->sendAcks = 1;
2734+
wc_UnLockMutex(&ssl->dupWrite->dupMutex);
2735+
}
2736+
}
2737+
else
2738+
#endif /* HAVE_WRITE_DUP */
2739+
{
2740+
ret = SendDtls13Ack(ssl);
2741+
if (ret != 0)
2742+
return ret;
2743+
}
27122744
}
27132745

27142746
if (ssl->dtls13Rtx.retransmit) {
@@ -2824,6 +2856,22 @@ int DoDtls13Ack(WOLFSSL* ssl, const byte* input, word32 inputSize,
28242856
ato64(ackMessage + i + OPAQUE64_LEN, &seq);
28252857
WOLFSSL_MSG_EX("epoch %d seq %d", epoch, seq);
28262858
Dtls13RtxRemoveRecord(ssl, epoch, seq);
2859+
#ifdef HAVE_WRITE_DUP
2860+
/* Read side: check if this ACK covers the write side's pending KeyUpdate.
2861+
* Match on both epoch AND seq to avoid false positives from data records
2862+
* in the same epoch (sent while dtls13WaitKeyUpdateAck == 1). */
2863+
if (ssl->dupWrite != NULL && ssl->dupSide == READ_DUP_SIDE) {
2864+
if (wc_LockMutex(&ssl->dupWrite->dupMutex) == 0) {
2865+
if (ssl->dupWrite->keyUpdateWaiting &&
2866+
w64Equal(epoch, ssl->dupWrite->keyUpdateEpoch) &&
2867+
w64Equal(seq, ssl->dupWrite->keyUpdateSeq)) {
2868+
ssl->dupWrite->keyUpdateAcked = 1;
2869+
ssl->dupWrite->keyUpdateWaiting = 0;
2870+
}
2871+
wc_UnLockMutex(&ssl->dupWrite->dupMutex);
2872+
}
2873+
}
2874+
#endif /* HAVE_WRITE_DUP */
28272875
}
28282876

28292877
/* last client flight was completely acknowledged by the server. Handshake

src/internal.c

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7462,43 +7462,49 @@ int InitHandshakeHashes(WOLFSSL* ssl)
74627462
return ret;
74637463
}
74647464

7465-
void FreeHandshakeHashes(WOLFSSL* ssl)
7465+
void Free_HS_Hashes(HS_Hashes* hsHashes, void* heap)
74667466
{
7467-
if (ssl->hsHashes) {
7467+
if (hsHashes) {
74687468
#if !defined(NO_MD5) && !defined(NO_OLD_TLS)
7469-
wc_Md5Free(&ssl->hsHashes->hashMd5);
7469+
wc_Md5Free(&hsHashes->hashMd5);
74707470
#endif
74717471
#if !defined(NO_SHA) && (!defined(NO_OLD_TLS) || \
74727472
defined(WOLFSSL_ALLOW_TLS_SHA1))
7473-
wc_ShaFree(&ssl->hsHashes->hashSha);
7473+
wc_ShaFree(&hsHashes->hashSha);
74747474
#endif
74757475
#ifndef NO_SHA256
7476-
wc_Sha256Free(&ssl->hsHashes->hashSha256);
7476+
wc_Sha256Free(&hsHashes->hashSha256);
74777477
#endif
74787478
#ifdef WOLFSSL_SHA384
7479-
wc_Sha384Free(&ssl->hsHashes->hashSha384);
7479+
wc_Sha384Free(&hsHashes->hashSha384);
74807480
#endif
74817481
#ifdef WOLFSSL_SHA512
7482-
wc_Sha512Free(&ssl->hsHashes->hashSha512);
7482+
wc_Sha512Free(&hsHashes->hashSha512);
74837483
#endif
74847484
#ifdef WOLFSSL_SM3
7485-
wc_Sm3Free(&ssl->hsHashes->hashSm3);
7485+
wc_Sm3Free(&hsHashes->hashSm3);
74867486
#endif
74877487
#if (defined(HAVE_ED25519) || defined(HAVE_ED448) || \
74887488
(defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3))) && \
74897489
!defined(WOLFSSL_NO_CLIENT_AUTH)
7490-
if (ssl->hsHashes->messages != NULL) {
7491-
ForceZero(ssl->hsHashes->messages, (word32)ssl->hsHashes->length);
7492-
XFREE(ssl->hsHashes->messages, ssl->heap, DYNAMIC_TYPE_HASHES);
7493-
ssl->hsHashes->messages = NULL;
7490+
if (hsHashes->messages != NULL) {
7491+
ForceZero(hsHashes->messages, (word32)hsHashes->length);
7492+
XFREE(hsHashes->messages, heap, DYNAMIC_TYPE_HASHES);
7493+
hsHashes->messages = NULL;
74947494
}
74957495
#endif
74967496

7497-
XFREE(ssl->hsHashes, ssl->heap, DYNAMIC_TYPE_HASHES);
7498-
ssl->hsHashes = NULL;
7497+
XFREE(hsHashes, heap, DYNAMIC_TYPE_HASHES);
7498+
hsHashes = NULL;
74997499
}
75007500
}
75017501

7502+
void FreeHandshakeHashes(WOLFSSL* ssl)
7503+
{
7504+
Free_HS_Hashes(ssl->hsHashes, ssl->heap);
7505+
ssl->hsHashes = NULL;
7506+
}
7507+
75027508
/* copy the hashes from source to a newly made destination return status */
75037509
int InitHandshakeHashesAndCopy(WOLFSSL* ssl, HS_Hashes* source,
75047510
HS_Hashes** destination)
@@ -7509,15 +7515,8 @@ int InitHandshakeHashesAndCopy(WOLFSSL* ssl, HS_Hashes* source,
75097515
return BAD_FUNC_ARG;
75107516

75117517
/* If *destination is already allocated, its constituent hashes need to be
7512-
* freed, else they would leak. To keep things simple, we reuse
7513-
* FreeHandshakeHashes(), which deallocates *destination.
7514-
*/
7515-
if (*destination != NULL) {
7516-
HS_Hashes* tmp = ssl->hsHashes;
7517-
ssl->hsHashes = *destination;
7518-
FreeHandshakeHashes(ssl);
7519-
ssl->hsHashes = tmp;
7520-
}
7518+
* freed, else they would leak. */
7519+
Free_HS_Hashes(*destination, ssl->heap);
75217520

75227521
/* allocate handshake hashes */
75237522
*destination = (HS_Hashes*)XMALLOC(sizeof(HS_Hashes), ssl->heap,
@@ -8065,6 +8064,24 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
80658064
}
80668065
ssl->options.dtls = ssl->version.major == DTLS_MAJOR;
80678066

8067+
8068+
#ifdef WOLFSSL_DTLS13
8069+
/* setup 0 (un-protected) epoch */
8070+
ssl->dtls13Epochs[0].isValid = 1;
8071+
ssl->dtls13Epochs[0].side = ENCRYPT_AND_DECRYPT_SIDE;
8072+
ssl->dtls13EncryptEpoch = &ssl->dtls13Epochs[0];
8073+
ssl->dtls13DecryptEpoch = &ssl->dtls13Epochs[0];
8074+
ssl->options.dtls13SendMoreAcks = WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT;
8075+
ssl->dtls13Rtx.rtxRecordTailPtr = &ssl->dtls13Rtx.rtxRecords;
8076+
8077+
#ifdef WOLFSSL_RW_THREADED
8078+
ret = wc_InitMutex(&ssl->dtls13Rtx.mutex);
8079+
if (ret < 0) {
8080+
return ret;
8081+
}
8082+
#endif
8083+
#endif /* WOLFSSL_DTLS13 */
8084+
80688085
#ifdef HAVE_WRITE_DUP
80698086
if (writeDup) {
80708087
/* all done */
@@ -8176,24 +8193,6 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
81768193
}
81778194
#endif /* HAVE_SECURE_RENEGOTIATION */
81788195

8179-
8180-
#ifdef WOLFSSL_DTLS13
8181-
/* setup 0 (un-protected) epoch */
8182-
ssl->dtls13Epochs[0].isValid = 1;
8183-
ssl->dtls13Epochs[0].side = ENCRYPT_AND_DECRYPT_SIDE;
8184-
ssl->dtls13EncryptEpoch = &ssl->dtls13Epochs[0];
8185-
ssl->dtls13DecryptEpoch = &ssl->dtls13Epochs[0];
8186-
ssl->options.dtls13SendMoreAcks = WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT;
8187-
ssl->dtls13Rtx.rtxRecordTailPtr = &ssl->dtls13Rtx.rtxRecords;
8188-
8189-
#ifdef WOLFSSL_RW_THREADED
8190-
ret = wc_InitMutex(&ssl->dtls13Rtx.mutex);
8191-
if (ret < 0) {
8192-
return ret;
8193-
}
8194-
#endif
8195-
#endif /* WOLFSSL_DTLS13 */
8196-
81978196
#ifdef WOLFSSL_QUIC
81988197
if (ctx->quic.method) {
81998198
ret = wolfSSL_set_quic_method(ssl, ctx->quic.method);
@@ -26082,6 +26081,10 @@ static int CheckTLS13AEADSendLimit(WOLFSSL* ssl)
2608226081
}
2608326082
#ifdef WOLFSSL_DTLS13
2608426083
if (ssl->options.dtls) {
26084+
if (ssl->dtls13EncryptEpoch == NULL) {
26085+
WOLFSSL_MSG("DTLS 1.3 encrypt epoch not set");
26086+
return BAD_STATE_E;
26087+
}
2608526088
seq = ssl->dtls13EncryptEpoch->nextSeqNumber;
2608626089
}
2608726090
else
@@ -26312,6 +26315,8 @@ int SendData(WOLFSSL* ssl, const void* data, size_t sz)
2631226315
else {
2631326316
/* advance sent to previous sent + plain size just sent */
2631426317
sent = ssl->buffers.prevSent + ssl->buffers.plainSz;
26318+
ssl->buffers.prevSent = 0;
26319+
ssl->buffers.plainSz = 0;
2631526320
WOLFSSL_MSG("sent write buffered data");
2631626321

2631726322
if (sent > (word32)sz) {

0 commit comments

Comments
 (0)