Skip to content

Commit e57971e

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 8169780 commit e57971e

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
@@ -92,6 +92,7 @@ jobs:
9292
'--enable-curve25519=nonblock --enable-ecc=nonblock --enable-sp=yes,nonblock CPPFLAGS="-DWOLFSSL_PUBLIC_MP -DWOLFSSL_DEBUG_NONBLOCK"',
9393
'--enable-certreq --enable-certext --enable-certgen --disable-secure-renegotiation-info CPPFLAGS="-DNO_TLS"',
9494
'--enable-ocsp --enable-ocsp-responder --enable-ocspstapling',
95+
'--enable-all --enable-writedup',
9596
]
9697
name: make check
9798
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
@@ -7401,43 +7401,49 @@ int InitHandshakeHashes(WOLFSSL* ssl)
74017401
return ret;
74027402
}
74037403

7404-
void FreeHandshakeHashes(WOLFSSL* ssl)
7404+
void Free_HS_Hashes(HS_Hashes* hsHashes, void* heap)
74057405
{
7406-
if (ssl->hsHashes) {
7406+
if (hsHashes) {
74077407
#if !defined(NO_MD5) && !defined(NO_OLD_TLS)
7408-
wc_Md5Free(&ssl->hsHashes->hashMd5);
7408+
wc_Md5Free(&hsHashes->hashMd5);
74097409
#endif
74107410
#if !defined(NO_SHA) && (!defined(NO_OLD_TLS) || \
74117411
defined(WOLFSSL_ALLOW_TLS_SHA1))
7412-
wc_ShaFree(&ssl->hsHashes->hashSha);
7412+
wc_ShaFree(&hsHashes->hashSha);
74137413
#endif
74147414
#ifndef NO_SHA256
7415-
wc_Sha256Free(&ssl->hsHashes->hashSha256);
7415+
wc_Sha256Free(&hsHashes->hashSha256);
74167416
#endif
74177417
#ifdef WOLFSSL_SHA384
7418-
wc_Sha384Free(&ssl->hsHashes->hashSha384);
7418+
wc_Sha384Free(&hsHashes->hashSha384);
74197419
#endif
74207420
#ifdef WOLFSSL_SHA512
7421-
wc_Sha512Free(&ssl->hsHashes->hashSha512);
7421+
wc_Sha512Free(&hsHashes->hashSha512);
74227422
#endif
74237423
#ifdef WOLFSSL_SM3
7424-
wc_Sm3Free(&ssl->hsHashes->hashSm3);
7424+
wc_Sm3Free(&hsHashes->hashSm3);
74257425
#endif
74267426
#if (defined(HAVE_ED25519) || defined(HAVE_ED448) || \
74277427
(defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3))) && \
74287428
!defined(WOLFSSL_NO_CLIENT_AUTH)
7429-
if (ssl->hsHashes->messages != NULL) {
7430-
ForceZero(ssl->hsHashes->messages, (word32)ssl->hsHashes->length);
7431-
XFREE(ssl->hsHashes->messages, ssl->heap, DYNAMIC_TYPE_HASHES);
7432-
ssl->hsHashes->messages = NULL;
7429+
if (hsHashes->messages != NULL) {
7430+
ForceZero(hsHashes->messages, (word32)hsHashes->length);
7431+
XFREE(hsHashes->messages, heap, DYNAMIC_TYPE_HASHES);
7432+
hsHashes->messages = NULL;
74337433
}
74347434
#endif
74357435

7436-
XFREE(ssl->hsHashes, ssl->heap, DYNAMIC_TYPE_HASHES);
7437-
ssl->hsHashes = NULL;
7436+
XFREE(hsHashes, heap, DYNAMIC_TYPE_HASHES);
7437+
hsHashes = NULL;
74387438
}
74397439
}
74407440

7441+
void FreeHandshakeHashes(WOLFSSL* ssl)
7442+
{
7443+
Free_HS_Hashes(ssl->hsHashes, ssl->heap);
7444+
ssl->hsHashes = NULL;
7445+
}
7446+
74417447
/* copy the hashes from source to a newly made destination return status */
74427448
int InitHandshakeHashesAndCopy(WOLFSSL* ssl, HS_Hashes* source,
74437449
HS_Hashes** destination)
@@ -7448,15 +7454,8 @@ int InitHandshakeHashesAndCopy(WOLFSSL* ssl, HS_Hashes* source,
74487454
return BAD_FUNC_ARG;
74497455

74507456
/* If *destination is already allocated, its constituent hashes need to be
7451-
* freed, else they would leak. To keep things simple, we reuse
7452-
* FreeHandshakeHashes(), which deallocates *destination.
7453-
*/
7454-
if (*destination != NULL) {
7455-
HS_Hashes* tmp = ssl->hsHashes;
7456-
ssl->hsHashes = *destination;
7457-
FreeHandshakeHashes(ssl);
7458-
ssl->hsHashes = tmp;
7459-
}
7457+
* freed, else they would leak. */
7458+
Free_HS_Hashes(*destination, ssl->heap);
74607459

74617460
/* allocate handshake hashes */
74627461
*destination = (HS_Hashes*)XMALLOC(sizeof(HS_Hashes), ssl->heap,
@@ -8004,6 +8003,24 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
80048003
}
80058004
ssl->options.dtls = ssl->version.major == DTLS_MAJOR;
80068005

8006+
8007+
#ifdef WOLFSSL_DTLS13
8008+
/* setup 0 (un-protected) epoch */
8009+
ssl->dtls13Epochs[0].isValid = 1;
8010+
ssl->dtls13Epochs[0].side = ENCRYPT_AND_DECRYPT_SIDE;
8011+
ssl->dtls13EncryptEpoch = &ssl->dtls13Epochs[0];
8012+
ssl->dtls13DecryptEpoch = &ssl->dtls13Epochs[0];
8013+
ssl->options.dtls13SendMoreAcks = WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT;
8014+
ssl->dtls13Rtx.rtxRecordTailPtr = &ssl->dtls13Rtx.rtxRecords;
8015+
8016+
#ifdef WOLFSSL_RW_THREADED
8017+
ret = wc_InitMutex(&ssl->dtls13Rtx.mutex);
8018+
if (ret < 0) {
8019+
return ret;
8020+
}
8021+
#endif
8022+
#endif /* WOLFSSL_DTLS13 */
8023+
80078024
#ifdef HAVE_WRITE_DUP
80088025
if (writeDup) {
80098026
/* all done */
@@ -8115,24 +8132,6 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
81158132
}
81168133
#endif /* HAVE_SECURE_RENEGOTIATION */
81178134

8118-
8119-
#ifdef WOLFSSL_DTLS13
8120-
/* setup 0 (un-protected) epoch */
8121-
ssl->dtls13Epochs[0].isValid = 1;
8122-
ssl->dtls13Epochs[0].side = ENCRYPT_AND_DECRYPT_SIDE;
8123-
ssl->dtls13EncryptEpoch = &ssl->dtls13Epochs[0];
8124-
ssl->dtls13DecryptEpoch = &ssl->dtls13Epochs[0];
8125-
ssl->options.dtls13SendMoreAcks = WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT;
8126-
ssl->dtls13Rtx.rtxRecordTailPtr = &ssl->dtls13Rtx.rtxRecords;
8127-
8128-
#ifdef WOLFSSL_RW_THREADED
8129-
ret = wc_InitMutex(&ssl->dtls13Rtx.mutex);
8130-
if (ret < 0) {
8131-
return ret;
8132-
}
8133-
#endif
8134-
#endif /* WOLFSSL_DTLS13 */
8135-
81368135
#ifdef WOLFSSL_QUIC
81378136
if (ctx->quic.method) {
81388137
ret = wolfSSL_set_quic_method(ssl, ctx->quic.method);
@@ -26016,6 +26015,10 @@ static int CheckTLS13AEADSendLimit(WOLFSSL* ssl)
2601626015
}
2601726016
#ifdef WOLFSSL_DTLS13
2601826017
if (ssl->options.dtls) {
26018+
if (ssl->dtls13EncryptEpoch == NULL) {
26019+
WOLFSSL_MSG("DTLS 1.3 encrypt epoch not set");
26020+
return BAD_STATE_E;
26021+
}
2601926022
seq = ssl->dtls13EncryptEpoch->nextSeqNumber;
2602026023
}
2602126024
else
@@ -26246,6 +26249,8 @@ int SendData(WOLFSSL* ssl, const void* data, size_t sz)
2624626249
else {
2624726250
/* advance sent to previous sent + plain size just sent */
2624826251
sent = ssl->buffers.prevSent + ssl->buffers.plainSz;
26252+
ssl->buffers.prevSent = 0;
26253+
ssl->buffers.plainSz = 0;
2624926254
WOLFSSL_MSG("sent write buffered data");
2625026255

2625126256
if (sent > (word32)sz) {

0 commit comments

Comments
 (0)