@@ -807,6 +807,31 @@ static int DupSSL(WOLFSSL* dup, WOLFSSL* ssl)
807807 /* dup side now owns encrypt/write ciphers */
808808 XMEMSET(&ssl->encrypt, 0, sizeof(Ciphers));
809809
810+ #ifdef WOLFSSL_DTLS13
811+ if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) {
812+ /* Copy epoch array (contains only value types — safe to memcpy). */
813+ XMEMCPY(dup->dtls13Epochs, ssl->dtls13Epochs, sizeof(ssl->dtls13Epochs));
814+
815+ /* Re-point dtls13EncryptEpoch into dup's own epoch array. */
816+ if (ssl->dtls13EncryptEpoch != NULL) {
817+ dup->dtls13EncryptEpoch =
818+ &dup->dtls13Epochs[ssl->dtls13EncryptEpoch - ssl->dtls13Epochs];
819+ }
820+ /* dtls13DecryptEpoch is not needed by the write-only side; leave NULL. */
821+
822+ /* Copy current write epoch number (checked in Dtls13SendMessage). */
823+ dup->dtls13Epoch = ssl->dtls13Epoch;
824+
825+ /* Transfer record-number encryption cipher ownership to dup.
826+ * FreeCiphers() frees the aes/chacha pointer, so sharing it would
827+ * cause a double-free; use the same ownership-transfer pattern as
828+ * for ssl->encrypt above. */
829+ XMEMCPY(&dup->dtlsRecordNumberEncrypt, &ssl->dtlsRecordNumberEncrypt,
830+ sizeof(RecordNumberCiphers));
831+ XMEMSET(&ssl->dtlsRecordNumberEncrypt, 0, sizeof(RecordNumberCiphers));
832+ }
833+ #endif /* WOLFSSL_DTLS13 */
834+
810835 dup->IOCB_WriteCtx = ssl->IOCB_WriteCtx;
811836 dup->CBIOSend = ssl->CBIOSend;
812837#ifdef OPENSSL_EXTRA
0 commit comments