diff --git a/src/dtls13.c b/src/dtls13.c index 995d144119..299dafca92 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -2720,17 +2720,27 @@ static int Dtls13RtxIsTrackedByRn(const Dtls13RtxRecord* r, w64wrapper epoch, static int Dtls13KeyUpdateAckReceived(WOLFSSL* ssl) { int ret; + /* Validate on a local copy so ssl->dtls13Epoch is left untouched when a + * check fails. */ + w64wrapper newEpoch = ssl->dtls13Epoch; ret = DeriveTls13Keys(ssl, update_traffic_key, ENCRYPT_SIDE_ONLY, 1); if (ret != 0) return ret; - w64Increment(&ssl->dtls13Epoch); + w64Increment(&newEpoch); /* Epoch wrapped up */ - if (w64IsZero(ssl->dtls13Epoch)) + if (w64IsZero(newEpoch)) return BAD_STATE_E; + /* RFC 9147 Section 4.2.1: the epoch must not exceed 2^48-1. */ + if (w64GT(newEpoch, + w64From32(DTLS13_EPOCH_MAX_HI32, DTLS13_EPOCH_MAX_LO32))) + return BAD_STATE_E; + + ssl->dtls13Epoch = newEpoch; + return Dtls13SetEpochKeys(ssl, ssl->dtls13Epoch, ENCRYPT_SIDE_ONLY); } diff --git a/src/tls13.c b/src/tls13.c index 8d7efb9df4..23fa8e25d2 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -12172,8 +12172,16 @@ int SendTls13KeyUpdate(WOLFSSL* ssl) WOLFSSL_ENTER("SendTls13KeyUpdate"); #ifdef WOLFSSL_DTLS13 - if (ssl->options.dtls) + if (ssl->options.dtls) { + /* RFC 9147 Section 4.2.1: do not send a KeyUpdate that would advance + * the sending epoch beyond 2^48-1. */ + if (w64GTE(ssl->dtls13Epoch, + w64From32(DTLS13_EPOCH_MAX_HI32, DTLS13_EPOCH_MAX_LO32))) { + WOLFSSL_MSG("DTLS 1.3 sending epoch at maximum; refusing KeyUpdate"); + return BAD_STATE_E; + } i = Dtls13GetRlHeaderLength(ssl, 1) + DTLS_HANDSHAKE_HEADER_SZ; + } #endif /* WOLFSSL_DTLS13 */ outputSz = OPAQUE8_LEN + MAX_MSG_EXTRA; @@ -12307,7 +12315,18 @@ static int DoTls13KeyUpdate(WOLFSSL* ssl, const byte* input, word32* inOutIdx, #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { - w64Increment(&ssl->dtls13PeerEpoch); + w64wrapper newEpoch = ssl->dtls13PeerEpoch; + w64Increment(&newEpoch); + + /* RFC 9147 Section 4.2.1: the epoch must not exceed 2^48-1. Reject a + * peer KeyUpdate that would advance the receiving epoch past the + * limit. Validate on a local copy so ssl->dtls13PeerEpoch is left + * untouched when the check fails. */ + if (w64GT(newEpoch, + w64From32(DTLS13_EPOCH_MAX_HI32, DTLS13_EPOCH_MAX_LO32))) + return BAD_STATE_E; + + ssl->dtls13PeerEpoch = newEpoch; ret = Dtls13SetEpochKeys(ssl, ssl->dtls13PeerEpoch, DECRYPT_SIDE_ONLY); if (ret != 0) diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 794808f38b..fb5f8a3233 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -5948,6 +5948,11 @@ enum { DTLS13_EPOCH_TRAFFIC0 = 3 }; +/* RFC 9147 Section 4.2.1: the DTLS 1.3 epoch is a 48-bit value and must not + * exceed 2^48-1. Expressed as the high/low 32-bit halves of a w64wrapper. */ +#define DTLS13_EPOCH_MAX_HI32 0x0000FFFFU +#define DTLS13_EPOCH_MAX_LO32 0xFFFFFFFFU + /* 64-bit epoch + 64-bit sequence number */ #define DTLS13_RN_SIZE (OPAQUE64_LEN + OPAQUE64_LEN) /* Maximum number of ACK records allowed in an ACK message */